2 * Copyright (c) 2009-2017 Nicira, Inc.
3 * Copyright (c) 2016 Mellanox Technologies, Ltd.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
22 #include <linux/if_ether.h>
23 #include <linux/rtnetlink.h>
24 #include <linux/tc_act/tc_csum.h>
25 #include <linux/tc_act/tc_gact.h>
26 #include <linux/tc_act/tc_mirred.h>
27 #include <linux/tc_act/tc_pedit.h>
28 #include <linux/tc_act/tc_tunnel_key.h>
29 #include <linux/tc_act/tc_vlan.h>
30 #include <linux/gen_stats.h>
34 #include "byte-order.h"
35 #include "netlink-socket.h"
37 #include "openvswitch/ofpbuf.h"
38 #include "openvswitch/util.h"
39 #include "openvswitch/vlog.h"
42 #include "unaligned.h"
44 #define MAX_PEDIT_OFFSETS 32
46 #ifndef TCM_IFINDEX_MAGIC_BLOCK
47 #define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
51 #define TCA_INGRESS_BLOCK 13
54 VLOG_DEFINE_THIS_MODULE(tc
);
56 static struct vlog_rate_limit error_rl
= VLOG_RATE_LIMIT_INIT(60, 5);
58 enum tc_offload_policy
{
64 static enum tc_offload_policy tc_policy
= TC_POLICY_NONE
;
66 struct tc_pedit_key_ex
{
67 enum pedit_header_type htype
;
71 struct flower_key_to_pedit
{
72 enum pedit_header_type htype
;
78 static struct flower_key_to_pedit flower_pedit_map
[] = {
80 TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
,
82 offsetof(struct tc_flower_key
, ipv4
.ipv4_src
),
83 MEMBER_SIZEOF(struct tc_flower_key
, ipv4
.ipv4_src
)
85 TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
,
87 offsetof(struct tc_flower_key
, ipv4
.ipv4_dst
),
88 MEMBER_SIZEOF(struct tc_flower_key
, ipv4
.ipv4_dst
)
90 TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
,
92 offsetof(struct tc_flower_key
, ipv4
.rewrite_ttl
),
93 MEMBER_SIZEOF(struct tc_flower_key
, ipv4
.rewrite_ttl
)
95 TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
,
97 offsetof(struct tc_flower_key
, ipv6
.ipv6_src
),
98 MEMBER_SIZEOF(struct tc_flower_key
, ipv6
.ipv6_src
)
100 TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
,
102 offsetof(struct tc_flower_key
, ipv6
.ipv6_dst
),
103 MEMBER_SIZEOF(struct tc_flower_key
, ipv6
.ipv6_dst
)
105 TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
,
107 offsetof(struct tc_flower_key
, src_mac
),
108 MEMBER_SIZEOF(struct tc_flower_key
, src_mac
)
110 TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
,
112 offsetof(struct tc_flower_key
, dst_mac
),
113 MEMBER_SIZEOF(struct tc_flower_key
, dst_mac
)
115 TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
,
117 offsetof(struct tc_flower_key
, eth_type
),
118 MEMBER_SIZEOF(struct tc_flower_key
, eth_type
)
120 TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
,
122 offsetof(struct tc_flower_key
, tcp_src
),
123 MEMBER_SIZEOF(struct tc_flower_key
, tcp_src
)
125 TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
,
127 offsetof(struct tc_flower_key
, tcp_dst
),
128 MEMBER_SIZEOF(struct tc_flower_key
, tcp_dst
)
130 TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
,
132 offsetof(struct tc_flower_key
, udp_src
),
133 MEMBER_SIZEOF(struct tc_flower_key
, udp_src
)
135 TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
,
137 offsetof(struct tc_flower_key
, udp_dst
),
138 MEMBER_SIZEOF(struct tc_flower_key
, udp_dst
)
143 csum_update_flag(struct tc_flower
*flower
,
144 enum pedit_header_type htype
);
147 tc_make_request(int ifindex
, int type
, unsigned int flags
,
148 struct ofpbuf
*request
)
152 ofpbuf_init(request
, 512);
153 nl_msg_put_nlmsghdr(request
, sizeof *tcmsg
, type
, NLM_F_REQUEST
| flags
);
154 tcmsg
= ofpbuf_put_zeros(request
, sizeof *tcmsg
);
155 tcmsg
->tcm_family
= AF_UNSPEC
;
156 tcmsg
->tcm_ifindex
= ifindex
;
157 /* Caller should fill in tcmsg->tcm_handle. */
158 /* Caller should fill in tcmsg->tcm_parent. */
164 tc_transact(struct ofpbuf
*request
, struct ofpbuf
**replyp
)
166 int error
= nl_transact(NETLINK_ROUTE
, request
, replyp
);
167 ofpbuf_uninit(request
);
171 /* Adds or deletes a root ingress qdisc on device with specified ifindex.
173 * This function is equivalent to running the following when 'add' is true:
174 * /sbin/tc qdisc add dev <devname> handle ffff: ingress
176 * This function is equivalent to running the following when 'add' is false:
177 * /sbin/tc qdisc del dev <devname> handle ffff: ingress
179 * Where dev <devname> is the device with specified ifindex name.
181 * The configuration and stats may be seen with the following command:
182 * /sbin/tc -s qdisc show dev <devname>
184 * If block_id is greater than 0, then the ingress qdisc is added to a block.
185 * In this case, it is equivalent to running (when 'add' is true):
186 * /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress
188 * Returns 0 if successful, otherwise a positive errno value.
191 tc_add_del_ingress_qdisc(int ifindex
, bool add
, uint32_t block_id
)
193 struct ofpbuf request
;
196 int type
= add
? RTM_NEWQDISC
: RTM_DELQDISC
;
197 int flags
= add
? NLM_F_EXCL
| NLM_F_CREATE
: 0;
199 tcmsg
= tc_make_request(ifindex
, type
, flags
, &request
);
200 tcmsg
->tcm_handle
= TC_H_MAKE(TC_H_INGRESS
, 0);
201 tcmsg
->tcm_parent
= TC_H_INGRESS
;
202 nl_msg_put_string(&request
, TCA_KIND
, "ingress");
203 nl_msg_put_unspec(&request
, TCA_OPTIONS
, NULL
, 0);
205 nl_msg_put_u32(&request
, TCA_INGRESS_BLOCK
, block_id
);
208 error
= tc_transact(&request
, NULL
);
210 /* If we're deleting the qdisc, don't worry about some of the
211 * error conditions. */
212 if (!add
&& (error
== ENOENT
|| error
== EINVAL
)) {
221 static const struct nl_policy tca_policy
[] = {
222 [TCA_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
223 [TCA_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
224 [TCA_STATS
] = { .type
= NL_A_UNSPEC
,
225 .min_len
= sizeof(struct tc_stats
), .optional
= true, },
226 [TCA_STATS2
] = { .type
= NL_A_NESTED
, .optional
= true, },
229 static const struct nl_policy tca_flower_policy
[] = {
230 [TCA_FLOWER_CLASSID
] = { .type
= NL_A_U32
, .optional
= true, },
231 [TCA_FLOWER_INDEV
] = { .type
= NL_A_STRING
, .max_len
= IFNAMSIZ
,
233 [TCA_FLOWER_KEY_ETH_SRC
] = { .type
= NL_A_UNSPEC
,
234 .min_len
= ETH_ALEN
, .optional
= true, },
235 [TCA_FLOWER_KEY_ETH_DST
] = { .type
= NL_A_UNSPEC
,
236 .min_len
= ETH_ALEN
, .optional
= true, },
237 [TCA_FLOWER_KEY_ETH_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
240 [TCA_FLOWER_KEY_ETH_DST_MASK
] = { .type
= NL_A_UNSPEC
,
243 [TCA_FLOWER_KEY_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= false, },
244 [TCA_FLOWER_FLAGS
] = { .type
= NL_A_U32
, .optional
= false, },
245 [TCA_FLOWER_ACT
] = { .type
= NL_A_NESTED
, .optional
= false, },
246 [TCA_FLOWER_KEY_IP_PROTO
] = { .type
= NL_A_U8
, .optional
= true, },
247 [TCA_FLOWER_KEY_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
248 [TCA_FLOWER_KEY_IPV4_DST
] = {.type
= NL_A_U32
, .optional
= true, },
249 [TCA_FLOWER_KEY_IPV4_SRC_MASK
] = { .type
= NL_A_U32
, .optional
= true, },
250 [TCA_FLOWER_KEY_IPV4_DST_MASK
] = { .type
= NL_A_U32
, .optional
= true, },
251 [TCA_FLOWER_KEY_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
252 .min_len
= sizeof(struct in6_addr
),
254 [TCA_FLOWER_KEY_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
255 .min_len
= sizeof(struct in6_addr
),
257 [TCA_FLOWER_KEY_IPV6_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
258 .min_len
= sizeof(struct in6_addr
),
260 [TCA_FLOWER_KEY_IPV6_DST_MASK
] = { .type
= NL_A_UNSPEC
,
261 .min_len
= sizeof(struct in6_addr
),
263 [TCA_FLOWER_KEY_TCP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
264 [TCA_FLOWER_KEY_TCP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
265 [TCA_FLOWER_KEY_TCP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
266 [TCA_FLOWER_KEY_TCP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
267 [TCA_FLOWER_KEY_UDP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
268 [TCA_FLOWER_KEY_UDP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
269 [TCA_FLOWER_KEY_UDP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
270 [TCA_FLOWER_KEY_UDP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
271 [TCA_FLOWER_KEY_SCTP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
272 [TCA_FLOWER_KEY_SCTP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
273 [TCA_FLOWER_KEY_SCTP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
274 [TCA_FLOWER_KEY_SCTP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
275 [TCA_FLOWER_KEY_MPLS_TTL
] = { .type
= NL_A_U8
, .optional
= true, },
276 [TCA_FLOWER_KEY_MPLS_TC
] = { .type
= NL_A_U8
, .optional
= true, },
277 [TCA_FLOWER_KEY_MPLS_BOS
] = { .type
= NL_A_U8
, .optional
= true, },
278 [TCA_FLOWER_KEY_MPLS_LABEL
] = { .type
= NL_A_U32
, .optional
= true, },
279 [TCA_FLOWER_KEY_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
280 [TCA_FLOWER_KEY_VLAN_PRIO
] = { .type
= NL_A_U8
, .optional
= true, },
281 [TCA_FLOWER_KEY_VLAN_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= true, },
282 [TCA_FLOWER_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
283 [TCA_FLOWER_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
284 [TCA_FLOWER_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
285 [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
] = { .type
= NL_A_U32
,
287 [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
] = { .type
= NL_A_U32
,
289 [TCA_FLOWER_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
290 .min_len
= sizeof(struct in6_addr
),
292 [TCA_FLOWER_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
293 .min_len
= sizeof(struct in6_addr
),
295 [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
296 .min_len
= sizeof(struct in6_addr
),
298 [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
] = { .type
= NL_A_UNSPEC
,
299 .min_len
= sizeof(struct in6_addr
),
301 [TCA_FLOWER_KEY_ENC_UDP_DST_PORT
] = { .type
= NL_A_U16
,
303 [TCA_FLOWER_KEY_FLAGS
] = { .type
= NL_A_BE32
, .optional
= true, },
304 [TCA_FLOWER_KEY_FLAGS_MASK
] = { .type
= NL_A_BE32
, .optional
= true, },
305 [TCA_FLOWER_KEY_IP_TTL
] = { .type
= NL_A_U8
,
307 [TCA_FLOWER_KEY_IP_TTL_MASK
] = { .type
= NL_A_U8
,
309 [TCA_FLOWER_KEY_IP_TOS
] = { .type
= NL_A_U8
,
311 [TCA_FLOWER_KEY_IP_TOS_MASK
] = { .type
= NL_A_U8
,
313 [TCA_FLOWER_KEY_TCP_FLAGS
] = { .type
= NL_A_U16
,
315 [TCA_FLOWER_KEY_TCP_FLAGS_MASK
] = { .type
= NL_A_U16
,
317 [TCA_FLOWER_KEY_CVLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
318 [TCA_FLOWER_KEY_CVLAN_PRIO
] = { .type
= NL_A_U8
, .optional
= true, },
319 [TCA_FLOWER_KEY_CVLAN_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= true, },
320 [TCA_FLOWER_KEY_ENC_IP_TOS
] = { .type
= NL_A_U8
,
322 [TCA_FLOWER_KEY_ENC_IP_TOS_MASK
] = { .type
= NL_A_U8
,
324 [TCA_FLOWER_KEY_ENC_IP_TTL
] = { .type
= NL_A_U8
,
326 [TCA_FLOWER_KEY_ENC_IP_TTL_MASK
] = { .type
= NL_A_U8
,
328 [TCA_FLOWER_KEY_ENC_OPTS
] = { .type
= NL_A_NESTED
, .optional
= true, },
329 [TCA_FLOWER_KEY_ENC_OPTS_MASK
] = { .type
= NL_A_NESTED
,
334 nl_parse_flower_eth(struct nlattr
**attrs
, struct tc_flower
*flower
)
336 const struct eth_addr
*eth
;
338 if (attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
]) {
339 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC
], ETH_ALEN
);
340 memcpy(&flower
->key
.src_mac
, eth
, sizeof flower
->key
.src_mac
);
342 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
], ETH_ALEN
);
343 memcpy(&flower
->mask
.src_mac
, eth
, sizeof flower
->mask
.src_mac
);
345 if (attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
]) {
346 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST
], ETH_ALEN
);
347 memcpy(&flower
->key
.dst_mac
, eth
, sizeof flower
->key
.dst_mac
);
349 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
], ETH_ALEN
);
350 memcpy(&flower
->mask
.dst_mac
, eth
, sizeof flower
->mask
.dst_mac
);
355 nl_parse_flower_mpls(struct nlattr
**attrs
, struct tc_flower
*flower
)
357 uint8_t ttl
, tc
, bos
;
360 if (!eth_type_mpls(flower
->key
.eth_type
)) {
364 flower
->key
.encap_eth_type
[0] =
365 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ETH_TYPE
]);
366 flower
->key
.mpls_lse
= 0;
367 flower
->mask
.mpls_lse
= 0;
369 if (attrs
[TCA_FLOWER_KEY_MPLS_TTL
]) {
370 ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_MPLS_TTL
]);
371 set_mpls_lse_ttl(&flower
->key
.mpls_lse
, ttl
);
372 set_mpls_lse_ttl(&flower
->mask
.mpls_lse
, 0xff);
375 if (attrs
[TCA_FLOWER_KEY_MPLS_BOS
]) {
376 bos
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_MPLS_BOS
]);
377 set_mpls_lse_bos(&flower
->key
.mpls_lse
, bos
);
378 set_mpls_lse_ttl(&flower
->mask
.mpls_lse
, 0xff);
381 if (attrs
[TCA_FLOWER_KEY_MPLS_TC
]) {
382 tc
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_MPLS_TC
]);
383 set_mpls_lse_tc(&flower
->key
.mpls_lse
, tc
);
384 set_mpls_lse_tc(&flower
->mask
.mpls_lse
, 0xff);
387 if (attrs
[TCA_FLOWER_KEY_MPLS_LABEL
]) {
388 label
= nl_attr_get_u32(attrs
[TCA_FLOWER_KEY_MPLS_LABEL
]);
389 set_mpls_lse_label(&flower
->key
.mpls_lse
, htonl(label
));
390 set_mpls_lse_label(&flower
->mask
.mpls_lse
, OVS_BE32_MAX
);
395 nl_parse_flower_vlan(struct nlattr
**attrs
, struct tc_flower
*flower
)
397 ovs_be16 encap_ethtype
;
399 if (!eth_type_vlan(flower
->key
.eth_type
)) {
403 flower
->key
.encap_eth_type
[0] =
404 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ETH_TYPE
]);
406 if (attrs
[TCA_FLOWER_KEY_VLAN_ID
]) {
407 flower
->key
.vlan_id
[0] =
408 nl_attr_get_u16(attrs
[TCA_FLOWER_KEY_VLAN_ID
]);
409 flower
->mask
.vlan_id
[0] = 0xffff;
411 if (attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]) {
412 flower
->key
.vlan_prio
[0] =
413 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]);
414 flower
->mask
.vlan_prio
[0] = 0xff;
417 if (!attrs
[TCA_FLOWER_KEY_VLAN_ETH_TYPE
]) {
421 encap_ethtype
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_VLAN_ETH_TYPE
]);
422 if (!eth_type_vlan(encap_ethtype
)) {
426 flower
->key
.encap_eth_type
[1] = flower
->key
.encap_eth_type
[0];
427 flower
->key
.encap_eth_type
[0] = encap_ethtype
;
429 if (attrs
[TCA_FLOWER_KEY_CVLAN_ID
]) {
430 flower
->key
.vlan_id
[1] =
431 nl_attr_get_u16(attrs
[TCA_FLOWER_KEY_CVLAN_ID
]);
432 flower
->mask
.vlan_id
[1] = 0xffff;
434 if (attrs
[TCA_FLOWER_KEY_CVLAN_PRIO
]) {
435 flower
->key
.vlan_prio
[1] =
436 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_CVLAN_PRIO
]);
437 flower
->mask
.vlan_prio
[1] = 0xff;
442 nl_parse_geneve_key(const struct nlattr
*in_nlattr
,
443 struct tun_metadata
*metadata
)
445 struct geneve_opt
*opt
= NULL
;
446 const struct ofpbuf
*msg
;
447 uint16_t last_opt_type
;
453 nl_attr_get_nested(in_nlattr
, &buf
);
456 last_opt_type
= TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC
;
458 NL_ATTR_FOR_EACH (nla
, left
, ofpbuf_at(msg
, 0, 0), msg
->size
) {
459 uint16_t type
= nl_attr_type(nla
);
462 case TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS
:
463 if (cnt
&& last_opt_type
!= TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA
) {
464 VLOG_ERR_RL(&error_rl
, "failed to parse tun options class");
468 opt
= &metadata
->opts
.gnv
[cnt
];
469 opt
->opt_class
= nl_attr_get_be16(nla
);
470 cnt
+= sizeof(struct geneve_opt
) / 4;
471 metadata
->present
.len
+= sizeof(struct geneve_opt
);
472 last_opt_type
= TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS
;
474 case TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE
:
475 if (last_opt_type
!= TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS
) {
476 VLOG_ERR_RL(&error_rl
, "failed to parse tun options type");
480 opt
->type
= nl_attr_get_u8(nla
);
481 last_opt_type
= TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE
;
483 case TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA
:
484 if (last_opt_type
!= TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE
) {
485 VLOG_ERR_RL(&error_rl
, "failed to parse tun options data");
489 opt
->length
= nl_attr_get_size(nla
) / 4;
490 memcpy(opt
+ 1, nl_attr_get_unspec(nla
, 1), opt
->length
* 4);
492 metadata
->present
.len
+= opt
->length
* 4;
493 last_opt_type
= TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA
;
498 if (last_opt_type
!= TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA
) {
499 VLOG_ERR_RL(&error_rl
, "failed to parse tun options without data");
507 nl_parse_flower_tunnel_opts(struct nlattr
*options
,
508 struct tun_metadata
*metadata
)
510 const struct ofpbuf
*msg
;
516 nl_attr_get_nested(options
, &buf
);
519 NL_ATTR_FOR_EACH (nla
, left
, ofpbuf_at(msg
, 0, 0), msg
->size
) {
520 uint16_t type
= nl_attr_type(nla
);
522 case TCA_FLOWER_KEY_ENC_OPTS_GENEVE
:
523 err
= nl_parse_geneve_key(nla
, metadata
);
536 flower_tun_geneve_opt_check_len(struct tun_metadata
*key
,
537 struct tun_metadata
*mask
)
539 const struct geneve_opt
*opt
, *opt_mask
;
542 len
= key
->present
.len
;
544 opt
= &key
->opts
.gnv
[cnt
];
545 opt_mask
= &mask
->opts
.gnv
[cnt
];
547 if (opt
->length
!= opt_mask
->length
) {
548 VLOG_ERR_RL(&error_rl
,
549 "failed to parse tun options; key/mask length differ");
553 cnt
+= sizeof(struct geneve_opt
) / 4 + opt
->length
;
554 len
-= sizeof(struct geneve_opt
) + opt
->length
* 4;
561 nl_parse_flower_tunnel(struct nlattr
**attrs
, struct tc_flower
*flower
)
565 if (attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]) {
566 ovs_be32 id
= nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]);
568 flower
->key
.tunnel
.id
= be32_to_be64(id
);
570 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
]) {
571 flower
->key
.tunnel
.ipv4
.ipv4_src
=
572 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC
]);
574 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
]) {
575 flower
->key
.tunnel
.ipv4
.ipv4_dst
=
576 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST
]);
578 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
]) {
579 flower
->key
.tunnel
.ipv6
.ipv6_src
=
580 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC
]);
582 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
]) {
583 flower
->key
.tunnel
.ipv6
.ipv6_dst
=
584 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST
]);
586 if (attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]) {
587 flower
->key
.tunnel
.tp_dst
=
588 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]);
590 if (attrs
[TCA_FLOWER_KEY_ENC_IP_TOS_MASK
]) {
591 flower
->key
.tunnel
.tos
=
592 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_ENC_IP_TOS
]);
593 flower
->mask
.tunnel
.tos
=
594 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_ENC_IP_TOS_MASK
]);
596 if (attrs
[TCA_FLOWER_KEY_ENC_IP_TTL_MASK
]) {
597 flower
->key
.tunnel
.ttl
=
598 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_ENC_IP_TTL
]);
599 flower
->mask
.tunnel
.ttl
=
600 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_ENC_IP_TTL_MASK
]);
602 if (attrs
[TCA_FLOWER_KEY_ENC_OPTS
] &&
603 attrs
[TCA_FLOWER_KEY_ENC_OPTS_MASK
]) {
604 err
= nl_parse_flower_tunnel_opts(attrs
[TCA_FLOWER_KEY_ENC_OPTS
],
605 &flower
->key
.tunnel
.metadata
);
610 err
= nl_parse_flower_tunnel_opts(attrs
[TCA_FLOWER_KEY_ENC_OPTS_MASK
],
611 &flower
->mask
.tunnel
.metadata
);
616 err
= flower_tun_geneve_opt_check_len(&flower
->key
.tunnel
.metadata
,
617 &flower
->mask
.tunnel
.metadata
);
621 } else if (attrs
[TCA_FLOWER_KEY_ENC_OPTS
]) {
622 VLOG_ERR_RL(&error_rl
,
623 "failed to parse tun options; no mask supplied");
625 } else if (attrs
[TCA_FLOWER_KEY_ENC_OPTS_MASK
]) {
626 VLOG_ERR_RL(&error_rl
, "failed to parse tun options; no key supplied");
634 nl_parse_flower_ip(struct nlattr
**attrs
, struct tc_flower
*flower
) {
635 uint8_t ip_proto
= 0;
636 struct tc_flower_key
*key
= &flower
->key
;
637 struct tc_flower_key
*mask
= &flower
->mask
;
639 if (attrs
[TCA_FLOWER_KEY_IP_PROTO
]) {
640 ip_proto
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_PROTO
]);
641 key
->ip_proto
= ip_proto
;
642 mask
->ip_proto
= UINT8_MAX
;
645 if (attrs
[TCA_FLOWER_KEY_FLAGS_MASK
]) {
646 key
->flags
= ntohl(nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_FLAGS
]));
648 ntohl(nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_FLAGS_MASK
]));
651 if (attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]) {
653 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC
]);
654 mask
->ipv4
.ipv4_src
=
655 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]);
657 if (attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]) {
659 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST
]);
660 mask
->ipv4
.ipv4_dst
=
661 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]);
663 if (attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
]) {
664 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_SRC
];
665 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
];
667 key
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr
);
668 mask
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr_mask
);
670 if (attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
]) {
671 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_DST
];
672 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
];
674 key
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr
);
675 mask
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr_mask
);
678 if (ip_proto
== IPPROTO_TCP
) {
679 if (attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]) {
681 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC
]);
683 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]);
685 if (attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]) {
687 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST
]);
689 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]);
691 if (attrs
[TCA_FLOWER_KEY_TCP_FLAGS_MASK
]) {
693 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_FLAGS
]);
695 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_FLAGS_MASK
]);
697 } else if (ip_proto
== IPPROTO_UDP
) {
698 if (attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]) {
699 key
->udp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC
]);
701 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]);
703 if (attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]) {
704 key
->udp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST
]);
706 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]);
708 } else if (ip_proto
== IPPROTO_SCTP
) {
709 if (attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]) {
710 key
->sctp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC
]);
712 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]);
714 if (attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]) {
715 key
->sctp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST
]);
717 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]);
721 if (attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]) {
722 key
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL
]);
723 mask
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]);
726 if (attrs
[TCA_FLOWER_KEY_IP_TOS_MASK
]) {
727 key
->ip_tos
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TOS
]);
728 mask
->ip_tos
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TOS_MASK
]);
732 static enum tc_offloaded_state
733 nl_get_flower_offloaded_state(struct nlattr
**attrs
)
735 uint32_t flower_flags
= 0;
737 if (attrs
[TCA_FLOWER_FLAGS
]) {
738 flower_flags
= nl_attr_get_u32(attrs
[TCA_FLOWER_FLAGS
]);
739 if (flower_flags
& TCA_CLS_FLAGS_NOT_IN_HW
) {
740 return TC_OFFLOADED_STATE_NOT_IN_HW
;
741 } else if (flower_flags
& TCA_CLS_FLAGS_IN_HW
) {
742 return TC_OFFLOADED_STATE_IN_HW
;
745 return TC_OFFLOADED_STATE_UNDEFINED
;
749 nl_parse_flower_flags(struct nlattr
**attrs
, struct tc_flower
*flower
)
751 flower
->offloaded_state
= nl_get_flower_offloaded_state(attrs
);
754 static const struct nl_policy pedit_policy
[] = {
755 [TCA_PEDIT_PARMS_EX
] = { .type
= NL_A_UNSPEC
,
756 .min_len
= sizeof(struct tc_pedit
),
757 .optional
= false, },
758 [TCA_PEDIT_KEYS_EX
] = { .type
= NL_A_NESTED
,
759 .optional
= false, },
763 nl_parse_act_pedit(struct nlattr
*options
, struct tc_flower
*flower
)
765 struct tc_action
*action
;
766 struct nlattr
*pe_attrs
[ARRAY_SIZE(pedit_policy
)];
767 const struct tc_pedit
*pe
;
768 const struct tc_pedit_key
*keys
;
769 const struct nlattr
*nla
, *keys_ex
, *ex_type
;
770 const void *keys_attr
;
771 char *rewrite_key
= (void *) &flower
->rewrite
.key
;
772 char *rewrite_mask
= (void *) &flower
->rewrite
.mask
;
773 size_t keys_ex_size
, left
;
774 int type
, i
= 0, err
;
776 if (!nl_parse_nested(options
, pedit_policy
, pe_attrs
,
777 ARRAY_SIZE(pedit_policy
))) {
778 VLOG_ERR_RL(&error_rl
, "failed to parse pedit action options");
782 pe
= nl_attr_get_unspec(pe_attrs
[TCA_PEDIT_PARMS_EX
], sizeof *pe
);
784 keys_attr
= pe_attrs
[TCA_PEDIT_KEYS_EX
];
785 keys_ex
= nl_attr_get(keys_attr
);
786 keys_ex_size
= nl_attr_get_size(keys_attr
);
788 NL_ATTR_FOR_EACH (nla
, left
, keys_ex
, keys_ex_size
) {
789 if (i
>= pe
->nkeys
) {
793 if (nl_attr_type(nla
) != TCA_PEDIT_KEY_EX
) {
794 VLOG_ERR_RL(&error_rl
, "unable to parse legacy pedit type: %d",
799 ex_type
= nl_attr_find_nested(nla
, TCA_PEDIT_KEY_EX_HTYPE
);
800 type
= nl_attr_get_u16(ex_type
);
802 err
= csum_update_flag(flower
, type
);
807 for (int j
= 0; j
< ARRAY_SIZE(flower_pedit_map
); j
++) {
808 struct flower_key_to_pedit
*m
= &flower_pedit_map
[j
];
809 int flower_off
= m
->flower_offset
;
813 if (m
->htype
!= type
) {
817 /* check overlap between current pedit key, which is always
818 * 4 bytes (range [off, off + 3]), and a map entry in
819 * flower_pedit_map (range [mf, mf + sz - 1]) */
820 if ((keys
->off
>= mf
&& keys
->off
< mf
+ sz
)
821 || (keys
->off
+ 3 >= mf
&& keys
->off
+ 3 < mf
+ sz
)) {
822 int diff
= flower_off
+ (keys
->off
- mf
);
823 uint32_t *dst
= (void *) (rewrite_key
+ diff
);
824 uint32_t *dst_m
= (void *) (rewrite_mask
+ diff
);
825 uint32_t mask
= ~(keys
->mask
);
828 if (keys
->off
< mf
) {
829 zero_bits
= 8 * (mf
- keys
->off
);
830 mask
&= UINT32_MAX
<< zero_bits
;
831 } else if (keys
->off
+ 4 > mf
+ m
->size
) {
832 zero_bits
= 8 * (keys
->off
+ 4 - mf
- m
->size
);
833 mask
&= UINT32_MAX
>> zero_bits
;
837 *dst
|= keys
->val
& mask
;
845 action
= &flower
->actions
[flower
->action_count
++];
846 action
->type
= TC_ACT_PEDIT
;
851 static const struct nl_policy tunnel_key_policy
[] = {
852 [TCA_TUNNEL_KEY_PARMS
] = { .type
= NL_A_UNSPEC
,
853 .min_len
= sizeof(struct tc_tunnel_key
),
854 .optional
= false, },
855 [TCA_TUNNEL_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
856 [TCA_TUNNEL_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
857 [TCA_TUNNEL_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
858 .min_len
= sizeof(struct in6_addr
),
860 [TCA_TUNNEL_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
861 .min_len
= sizeof(struct in6_addr
),
863 [TCA_TUNNEL_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
864 [TCA_TUNNEL_KEY_ENC_DST_PORT
] = { .type
= NL_A_U16
, .optional
= true, },
865 [TCA_TUNNEL_KEY_ENC_TOS
] = { .type
= NL_A_U8
, .optional
= true, },
866 [TCA_TUNNEL_KEY_ENC_TTL
] = { .type
= NL_A_U8
, .optional
= true, },
867 [TCA_TUNNEL_KEY_ENC_OPTS
] = { .type
= NL_A_NESTED
, .optional
= true, },
871 nl_parse_act_geneve_opts(const struct nlattr
*in_nlattr
,
872 struct tc_action
*action
)
874 struct geneve_opt
*opt
= NULL
;
875 const struct ofpbuf
*msg
;
876 uint16_t last_opt_type
;
882 nl_attr_get_nested(in_nlattr
, &buf
);
885 last_opt_type
= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_UNSPEC
;
887 NL_ATTR_FOR_EACH (nla
, left
, ofpbuf_at(msg
, 0, 0), msg
->size
) {
888 uint16_t type
= nl_attr_type(nla
);
891 case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS
:
892 if (cnt
&& last_opt_type
!= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA
) {
893 VLOG_ERR_RL(&error_rl
,
894 "failed to parse action geneve options class");
898 opt
= &action
->encap
.data
.opts
.gnv
[cnt
];
899 opt
->opt_class
= nl_attr_get_be16(nla
);
900 cnt
+= sizeof(struct geneve_opt
) / 4;
901 action
->encap
.data
.present
.len
+= sizeof(struct geneve_opt
);
902 last_opt_type
= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS
;
904 case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE
:
905 if (last_opt_type
!= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS
) {
906 VLOG_ERR_RL(&error_rl
,
907 "failed to parse action geneve options type");
911 opt
->type
= nl_attr_get_u8(nla
);
912 last_opt_type
= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE
;
914 case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA
:
915 if (last_opt_type
!= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE
) {
916 VLOG_ERR_RL(&error_rl
,
917 "failed to parse action geneve options data");
921 opt
->length
= nl_attr_get_size(nla
) / 4;
922 memcpy(opt
+ 1, nl_attr_get_unspec(nla
, 1), opt
->length
* 4);
924 action
->encap
.data
.present
.len
+= opt
->length
* 4;
925 last_opt_type
= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA
;
930 if (last_opt_type
!= TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA
) {
931 VLOG_ERR_RL(&error_rl
,
932 "failed to parse action geneve options without data");
940 nl_parse_act_tunnel_opts(struct nlattr
*options
, struct tc_action
*action
)
942 const struct ofpbuf
*msg
;
952 nl_attr_get_nested(options
, &buf
);
955 NL_ATTR_FOR_EACH (nla
, left
, ofpbuf_at(msg
, 0, 0), msg
->size
) {
956 uint16_t type
= nl_attr_type(nla
);
958 case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE
:
959 err
= nl_parse_act_geneve_opts(nla
, action
);
972 nl_parse_act_tunnel_key(struct nlattr
*options
, struct tc_flower
*flower
)
974 struct nlattr
*tun_attrs
[ARRAY_SIZE(tunnel_key_policy
)];
975 const struct nlattr
*tun_parms
;
976 const struct tc_tunnel_key
*tun
;
977 struct tc_action
*action
;
980 if (!nl_parse_nested(options
, tunnel_key_policy
, tun_attrs
,
981 ARRAY_SIZE(tunnel_key_policy
))) {
982 VLOG_ERR_RL(&error_rl
, "failed to parse tunnel_key action options");
986 tun_parms
= tun_attrs
[TCA_TUNNEL_KEY_PARMS
];
987 tun
= nl_attr_get_unspec(tun_parms
, sizeof *tun
);
988 if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_SET
) {
989 struct nlattr
*id
= tun_attrs
[TCA_TUNNEL_KEY_ENC_KEY_ID
];
990 struct nlattr
*dst_port
= tun_attrs
[TCA_TUNNEL_KEY_ENC_DST_PORT
];
991 struct nlattr
*ipv4_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_SRC
];
992 struct nlattr
*ipv4_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_DST
];
993 struct nlattr
*ipv6_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_SRC
];
994 struct nlattr
*ipv6_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_DST
];
995 struct nlattr
*tos
= tun_attrs
[TCA_TUNNEL_KEY_ENC_TOS
];
996 struct nlattr
*ttl
= tun_attrs
[TCA_TUNNEL_KEY_ENC_TTL
];
997 struct nlattr
*tun_opt
= tun_attrs
[TCA_TUNNEL_KEY_ENC_OPTS
];
999 action
= &flower
->actions
[flower
->action_count
++];
1000 action
->type
= TC_ACT_ENCAP
;
1001 action
->encap
.ipv4
.ipv4_src
= ipv4_src
? nl_attr_get_be32(ipv4_src
) : 0;
1002 action
->encap
.ipv4
.ipv4_dst
= ipv4_dst
? nl_attr_get_be32(ipv4_dst
) : 0;
1004 action
->encap
.ipv6
.ipv6_src
= nl_attr_get_in6_addr(ipv6_src
);
1007 action
->encap
.ipv6
.ipv6_dst
= nl_attr_get_in6_addr(ipv6_dst
);
1009 action
->encap
.id
= id
? be32_to_be64(nl_attr_get_be32(id
)) : 0;
1010 action
->encap
.tp_dst
= dst_port
? nl_attr_get_be16(dst_port
) : 0;
1011 action
->encap
.tos
= tos
? nl_attr_get_u8(tos
) : 0;
1012 action
->encap
.ttl
= ttl
? nl_attr_get_u8(ttl
) : 0;
1014 err
= nl_parse_act_tunnel_opts(tun_opt
, action
);
1018 } else if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_RELEASE
) {
1019 flower
->tunnel
= true;
1021 VLOG_ERR_RL(&error_rl
, "unknown tunnel actions: %d, %d",
1022 tun
->action
, tun
->t_action
);
1028 static const struct nl_policy gact_policy
[] = {
1029 [TCA_GACT_PARMS
] = { .type
= NL_A_UNSPEC
,
1030 .min_len
= sizeof(struct tc_gact
),
1031 .optional
= false, },
1032 [TCA_GACT_TM
] = { .type
= NL_A_UNSPEC
,
1033 .min_len
= sizeof(struct tcf_t
),
1034 .optional
= false, },
1040 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
1041 static int user_hz
= 100;
1043 if (ovsthread_once_start(&once
)) {
1044 user_hz
= sysconf(_SC_CLK_TCK
);
1045 ovsthread_once_done(&once
);
1052 nl_parse_tcf(const struct tcf_t
*tm
, struct tc_flower
*flower
)
1054 flower
->lastused
= time_msec() - (tm
->lastuse
* 1000 / get_user_hz());
1058 nl_parse_act_drop(struct nlattr
*options
, struct tc_flower
*flower
)
1060 struct nlattr
*gact_attrs
[ARRAY_SIZE(gact_policy
)];
1061 const struct tc_gact
*p
;
1062 struct nlattr
*gact_parms
;
1063 const struct tcf_t
*tm
;
1065 if (!nl_parse_nested(options
, gact_policy
, gact_attrs
,
1066 ARRAY_SIZE(gact_policy
))) {
1067 VLOG_ERR_RL(&error_rl
, "failed to parse gact action options");
1071 gact_parms
= gact_attrs
[TCA_GACT_PARMS
];
1072 p
= nl_attr_get_unspec(gact_parms
, sizeof *p
);
1074 if (p
->action
!= TC_ACT_SHOT
) {
1075 VLOG_ERR_RL(&error_rl
, "unknown gact action: %d", p
->action
);
1079 tm
= nl_attr_get_unspec(gact_attrs
[TCA_GACT_TM
], sizeof *tm
);
1080 nl_parse_tcf(tm
, flower
);
1085 static const struct nl_policy mirred_policy
[] = {
1086 [TCA_MIRRED_PARMS
] = { .type
= NL_A_UNSPEC
,
1087 .min_len
= sizeof(struct tc_mirred
),
1088 .optional
= false, },
1089 [TCA_MIRRED_TM
] = { .type
= NL_A_UNSPEC
,
1090 .min_len
= sizeof(struct tcf_t
),
1091 .optional
= false, },
1095 nl_parse_act_mirred(struct nlattr
*options
, struct tc_flower
*flower
)
1098 struct nlattr
*mirred_attrs
[ARRAY_SIZE(mirred_policy
)];
1099 const struct tc_mirred
*m
;
1100 const struct nlattr
*mirred_parms
;
1101 const struct tcf_t
*tm
;
1102 struct nlattr
*mirred_tm
;
1103 struct tc_action
*action
;
1105 if (!nl_parse_nested(options
, mirred_policy
, mirred_attrs
,
1106 ARRAY_SIZE(mirred_policy
))) {
1107 VLOG_ERR_RL(&error_rl
, "failed to parse mirred action options");
1111 mirred_parms
= mirred_attrs
[TCA_MIRRED_PARMS
];
1112 m
= nl_attr_get_unspec(mirred_parms
, sizeof *m
);
1114 if (m
->eaction
!= TCA_EGRESS_REDIR
&& m
->eaction
!= TCA_EGRESS_MIRROR
) {
1115 VLOG_ERR_RL(&error_rl
, "unknown mirred action: %d, %d, %d",
1116 m
->action
, m
->eaction
, m
->ifindex
);
1120 action
= &flower
->actions
[flower
->action_count
++];
1121 action
->ifindex_out
= m
->ifindex
;
1122 action
->type
= TC_ACT_OUTPUT
;
1124 mirred_tm
= mirred_attrs
[TCA_MIRRED_TM
];
1125 tm
= nl_attr_get_unspec(mirred_tm
, sizeof *tm
);
1126 nl_parse_tcf(tm
, flower
);
1131 static const struct nl_policy vlan_policy
[] = {
1132 [TCA_VLAN_PARMS
] = { .type
= NL_A_UNSPEC
,
1133 .min_len
= sizeof(struct tc_vlan
),
1134 .optional
= false, },
1135 [TCA_VLAN_PUSH_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
1136 [TCA_VLAN_PUSH_VLAN_PROTOCOL
] = { .type
= NL_A_U16
, .optional
= true, },
1137 [TCA_VLAN_PUSH_VLAN_PRIORITY
] = { .type
= NL_A_U8
, .optional
= true, },
1141 nl_parse_act_vlan(struct nlattr
*options
, struct tc_flower
*flower
)
1143 struct nlattr
*vlan_attrs
[ARRAY_SIZE(vlan_policy
)];
1144 const struct tc_vlan
*v
;
1145 const struct nlattr
*vlan_parms
;
1146 struct tc_action
*action
;
1148 if (!nl_parse_nested(options
, vlan_policy
, vlan_attrs
,
1149 ARRAY_SIZE(vlan_policy
))) {
1150 VLOG_ERR_RL(&error_rl
, "failed to parse vlan action options");
1154 action
= &flower
->actions
[flower
->action_count
++];
1155 vlan_parms
= vlan_attrs
[TCA_VLAN_PARMS
];
1156 v
= nl_attr_get_unspec(vlan_parms
, sizeof *v
);
1157 if (v
->v_action
== TCA_VLAN_ACT_PUSH
) {
1158 struct nlattr
*vlan_tpid
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_PROTOCOL
];
1159 struct nlattr
*vlan_id
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_ID
];
1160 struct nlattr
*vlan_prio
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_PRIORITY
];
1162 action
->vlan
.vlan_push_tpid
= nl_attr_get_be16(vlan_tpid
);
1163 action
->vlan
.vlan_push_id
= nl_attr_get_u16(vlan_id
);
1164 action
->vlan
.vlan_push_prio
= vlan_prio
? nl_attr_get_u8(vlan_prio
) : 0;
1165 action
->type
= TC_ACT_VLAN_PUSH
;
1166 } else if (v
->v_action
== TCA_VLAN_ACT_POP
) {
1167 action
->type
= TC_ACT_VLAN_POP
;
1169 VLOG_ERR_RL(&error_rl
, "unknown vlan action: %d, %d",
1170 v
->action
, v
->v_action
);
1176 static const struct nl_policy csum_policy
[] = {
1177 [TCA_CSUM_PARMS
] = { .type
= NL_A_UNSPEC
,
1178 .min_len
= sizeof(struct tc_csum
),
1179 .optional
= false, },
1183 nl_parse_act_csum(struct nlattr
*options
, struct tc_flower
*flower
)
1185 struct nlattr
*csum_attrs
[ARRAY_SIZE(csum_policy
)];
1186 const struct tc_csum
*c
;
1187 const struct nlattr
*csum_parms
;
1189 if (!nl_parse_nested(options
, csum_policy
, csum_attrs
,
1190 ARRAY_SIZE(csum_policy
))) {
1191 VLOG_ERR_RL(&error_rl
, "failed to parse csum action options");
1195 csum_parms
= csum_attrs
[TCA_CSUM_PARMS
];
1196 c
= nl_attr_get_unspec(csum_parms
, sizeof *c
);
1199 if (c
->update_flags
!= flower
->csum_update_flags
) {
1200 VLOG_WARN_RL(&error_rl
,
1201 "expected different act csum flags: 0x%x != 0x%x",
1202 flower
->csum_update_flags
, c
->update_flags
);
1205 flower
->csum_update_flags
= 0; /* so we know csum was handled */
1207 if (flower
->needs_full_ip_proto_mask
1208 && flower
->mask
.ip_proto
!= UINT8_MAX
) {
1209 VLOG_WARN_RL(&error_rl
, "expected full matching on flower ip_proto");
1216 static const struct nl_policy act_policy
[] = {
1217 [TCA_ACT_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
1218 [TCA_ACT_COOKIE
] = { .type
= NL_A_UNSPEC
, .optional
= true, },
1219 [TCA_ACT_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
1220 [TCA_ACT_STATS
] = { .type
= NL_A_NESTED
, .optional
= false, },
1223 static const struct nl_policy stats_policy
[] = {
1224 [TCA_STATS_BASIC
] = { .type
= NL_A_UNSPEC
,
1225 .min_len
= sizeof(struct gnet_stats_basic
),
1226 .optional
= false, },
1230 nl_parse_single_action(struct nlattr
*action
, struct tc_flower
*flower
)
1232 struct nlattr
*act_options
;
1233 struct nlattr
*act_stats
;
1234 struct nlattr
*act_cookie
;
1235 const char *act_kind
;
1236 struct nlattr
*action_attrs
[ARRAY_SIZE(act_policy
)];
1237 struct nlattr
*stats_attrs
[ARRAY_SIZE(stats_policy
)];
1238 struct ovs_flow_stats
*stats
= &flower
->stats
;
1239 const struct gnet_stats_basic
*bs
;
1242 if (!nl_parse_nested(action
, act_policy
, action_attrs
,
1243 ARRAY_SIZE(act_policy
))) {
1244 VLOG_ERR_RL(&error_rl
, "failed to parse single action options");
1248 act_kind
= nl_attr_get_string(action_attrs
[TCA_ACT_KIND
]);
1249 act_options
= action_attrs
[TCA_ACT_OPTIONS
];
1250 act_cookie
= action_attrs
[TCA_ACT_COOKIE
];
1252 if (!strcmp(act_kind
, "gact")) {
1253 err
= nl_parse_act_drop(act_options
, flower
);
1254 } else if (!strcmp(act_kind
, "mirred")) {
1255 err
= nl_parse_act_mirred(act_options
, flower
);
1256 } else if (!strcmp(act_kind
, "vlan")) {
1257 err
= nl_parse_act_vlan(act_options
, flower
);
1258 } else if (!strcmp(act_kind
, "tunnel_key")) {
1259 err
= nl_parse_act_tunnel_key(act_options
, flower
);
1260 } else if (!strcmp(act_kind
, "pedit")) {
1261 err
= nl_parse_act_pedit(act_options
, flower
);
1262 } else if (!strcmp(act_kind
, "csum")) {
1263 nl_parse_act_csum(act_options
, flower
);
1265 VLOG_ERR_RL(&error_rl
, "unknown tc action kind: %s", act_kind
);
1274 flower
->act_cookie
.data
= nl_attr_get(act_cookie
);
1275 flower
->act_cookie
.len
= nl_attr_get_size(act_cookie
);
1278 act_stats
= action_attrs
[TCA_ACT_STATS
];
1280 if (!nl_parse_nested(act_stats
, stats_policy
, stats_attrs
,
1281 ARRAY_SIZE(stats_policy
))) {
1282 VLOG_ERR_RL(&error_rl
, "failed to parse action stats policy");
1286 bs
= nl_attr_get_unspec(stats_attrs
[TCA_STATS_BASIC
], sizeof *bs
);
1287 put_32aligned_u64(&stats
->n_packets
, bs
->packets
);
1288 put_32aligned_u64(&stats
->n_bytes
, bs
->bytes
);
1293 #define TCA_ACT_MIN_PRIO 1
1296 nl_parse_flower_actions(struct nlattr
**attrs
, struct tc_flower
*flower
)
1298 const struct nlattr
*actions
= attrs
[TCA_FLOWER_ACT
];
1299 static struct nl_policy actions_orders_policy
[TCA_ACT_MAX_PRIO
+ 1] = {};
1300 struct nlattr
*actions_orders
[ARRAY_SIZE(actions_orders_policy
)];
1301 const int max_size
= ARRAY_SIZE(actions_orders_policy
);
1303 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
1304 actions_orders_policy
[i
].type
= NL_A_NESTED
;
1305 actions_orders_policy
[i
].optional
= true;
1308 if (!nl_parse_nested(actions
, actions_orders_policy
, actions_orders
,
1309 ARRAY_SIZE(actions_orders_policy
))) {
1310 VLOG_ERR_RL(&error_rl
, "failed to parse flower order of actions");
1314 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
1315 if (actions_orders
[i
]) {
1318 if (flower
->action_count
>= TCA_ACT_MAX_PRIO
) {
1319 VLOG_DBG_RL(&error_rl
, "Can only support %d actions", flower
->action_count
);
1322 err
= nl_parse_single_action(actions_orders
[i
], flower
);
1330 if (flower
->csum_update_flags
) {
1331 VLOG_WARN_RL(&error_rl
,
1332 "expected act csum with flags: 0x%x",
1333 flower
->csum_update_flags
);
1341 nl_parse_flower_options(struct nlattr
*nl_options
, struct tc_flower
*flower
)
1343 struct nlattr
*attrs
[ARRAY_SIZE(tca_flower_policy
)];
1346 if (!nl_parse_nested(nl_options
, tca_flower_policy
,
1347 attrs
, ARRAY_SIZE(tca_flower_policy
))) {
1348 VLOG_ERR_RL(&error_rl
, "failed to parse flower classifier options");
1352 nl_parse_flower_eth(attrs
, flower
);
1353 nl_parse_flower_mpls(attrs
, flower
);
1354 nl_parse_flower_vlan(attrs
, flower
);
1355 nl_parse_flower_ip(attrs
, flower
);
1356 err
= nl_parse_flower_tunnel(attrs
, flower
);
1361 nl_parse_flower_flags(attrs
, flower
);
1362 return nl_parse_flower_actions(attrs
, flower
);
1366 parse_netlink_to_tc_flower(struct ofpbuf
*reply
, struct tc_flower
*flower
)
1369 struct nlattr
*ta
[ARRAY_SIZE(tca_policy
)];
1372 if (NLMSG_HDRLEN
+ sizeof *tc
> reply
->size
) {
1376 memset(flower
, 0, sizeof *flower
);
1378 tc
= ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
1379 flower
->handle
= tc
->tcm_handle
;
1380 flower
->key
.eth_type
= (OVS_FORCE ovs_be16
) tc_get_minor(tc
->tcm_info
);
1381 flower
->mask
.eth_type
= OVS_BE16_MAX
;
1382 flower
->prio
= tc_get_major(tc
->tcm_info
);
1384 if (!flower
->handle
) {
1388 if (!nl_policy_parse(reply
, NLMSG_HDRLEN
+ sizeof *tc
,
1389 tca_policy
, ta
, ARRAY_SIZE(ta
))) {
1390 VLOG_ERR_RL(&error_rl
, "failed to parse tca policy");
1394 kind
= nl_attr_get_string(ta
[TCA_KIND
]);
1395 if (strcmp(kind
, "flower")) {
1396 VLOG_DBG_ONCE("Unsupported filter: %s", kind
);
1400 return nl_parse_flower_options(ta
[TCA_OPTIONS
], flower
);
1404 tc_dump_flower_start(int ifindex
, struct nl_dump
*dump
, uint32_t block_id
)
1406 struct ofpbuf request
;
1407 struct tcmsg
*tcmsg
;
1410 index
= block_id
? TCM_IFINDEX_MAGIC_BLOCK
: ifindex
;
1411 tcmsg
= tc_make_request(index
, RTM_GETTFILTER
, NLM_F_DUMP
, &request
);
1412 tcmsg
->tcm_parent
= block_id
? : TC_INGRESS_PARENT
;
1413 tcmsg
->tcm_info
= TC_H_UNSPEC
;
1414 tcmsg
->tcm_handle
= 0;
1416 nl_dump_start(dump
, NETLINK_ROUTE
, &request
);
1417 ofpbuf_uninit(&request
);
1423 tc_flush(int ifindex
, uint32_t block_id
)
1425 struct ofpbuf request
;
1426 struct tcmsg
*tcmsg
;
1429 index
= block_id
? TCM_IFINDEX_MAGIC_BLOCK
: ifindex
;
1430 tcmsg
= tc_make_request(index
, RTM_DELTFILTER
, NLM_F_ACK
, &request
);
1431 tcmsg
->tcm_parent
= block_id
? : TC_INGRESS_PARENT
;
1432 tcmsg
->tcm_info
= TC_H_UNSPEC
;
1434 return tc_transact(&request
, NULL
);
1438 tc_del_filter(int ifindex
, int prio
, int handle
, uint32_t block_id
)
1440 struct ofpbuf request
;
1441 struct tcmsg
*tcmsg
;
1442 struct ofpbuf
*reply
;
1446 index
= block_id
? TCM_IFINDEX_MAGIC_BLOCK
: ifindex
;
1447 tcmsg
= tc_make_request(index
, RTM_DELTFILTER
, NLM_F_ECHO
, &request
);
1448 tcmsg
->tcm_parent
= block_id
? : TC_INGRESS_PARENT
;
1449 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
1450 tcmsg
->tcm_handle
= handle
;
1452 error
= tc_transact(&request
, &reply
);
1454 ofpbuf_delete(reply
);
1460 tc_get_flower(int ifindex
, int prio
, int handle
, struct tc_flower
*flower
,
1463 struct ofpbuf request
;
1464 struct tcmsg
*tcmsg
;
1465 struct ofpbuf
*reply
;
1469 index
= block_id
? TCM_IFINDEX_MAGIC_BLOCK
: ifindex
;
1470 tcmsg
= tc_make_request(index
, RTM_GETTFILTER
, NLM_F_ECHO
, &request
);
1471 tcmsg
->tcm_parent
= block_id
? : TC_INGRESS_PARENT
;
1472 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
1473 tcmsg
->tcm_handle
= handle
;
1475 error
= tc_transact(&request
, &reply
);
1480 error
= parse_netlink_to_tc_flower(reply
, flower
);
1481 ofpbuf_delete(reply
);
1486 tc_get_tc_cls_policy(enum tc_offload_policy policy
)
1488 if (policy
== TC_POLICY_SKIP_HW
) {
1489 return TCA_CLS_FLAGS_SKIP_HW
;
1490 } else if (policy
== TC_POLICY_SKIP_SW
) {
1491 return TCA_CLS_FLAGS_SKIP_SW
;
1498 nl_msg_put_act_csum(struct ofpbuf
*request
, uint32_t flags
)
1502 nl_msg_put_string(request
, TCA_ACT_KIND
, "csum");
1503 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1505 struct tc_csum parm
= { .action
= TC_ACT_PIPE
,
1506 .update_flags
= flags
};
1508 nl_msg_put_unspec(request
, TCA_CSUM_PARMS
, &parm
, sizeof parm
);
1510 nl_msg_end_nested(request
, offset
);
1514 nl_msg_put_act_pedit(struct ofpbuf
*request
, struct tc_pedit
*parm
,
1515 struct tc_pedit_key_ex
*ex
)
1517 size_t ksize
= sizeof *parm
+ parm
->nkeys
* sizeof(struct tc_pedit_key
);
1518 size_t offset
, offset_keys_ex
, offset_key
;
1521 nl_msg_put_string(request
, TCA_ACT_KIND
, "pedit");
1522 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1524 parm
->action
= TC_ACT_PIPE
;
1526 nl_msg_put_unspec(request
, TCA_PEDIT_PARMS_EX
, parm
, ksize
);
1527 offset_keys_ex
= nl_msg_start_nested(request
, TCA_PEDIT_KEYS_EX
);
1528 for (i
= 0; i
< parm
->nkeys
; i
++, ex
++) {
1529 offset_key
= nl_msg_start_nested(request
, TCA_PEDIT_KEY_EX
);
1530 nl_msg_put_u16(request
, TCA_PEDIT_KEY_EX_HTYPE
, ex
->htype
);
1531 nl_msg_put_u16(request
, TCA_PEDIT_KEY_EX_CMD
, ex
->cmd
);
1532 nl_msg_end_nested(request
, offset_key
);
1534 nl_msg_end_nested(request
, offset_keys_ex
);
1536 nl_msg_end_nested(request
, offset
);
1540 nl_msg_put_act_push_vlan(struct ofpbuf
*request
, ovs_be16 tpid
,
1541 uint16_t vid
, uint8_t prio
)
1545 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
1546 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1548 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
1549 .v_action
= TCA_VLAN_ACT_PUSH
};
1551 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
1552 nl_msg_put_be16(request
, TCA_VLAN_PUSH_VLAN_PROTOCOL
, tpid
);
1553 nl_msg_put_u16(request
, TCA_VLAN_PUSH_VLAN_ID
, vid
);
1554 nl_msg_put_u8(request
, TCA_VLAN_PUSH_VLAN_PRIORITY
, prio
);
1556 nl_msg_end_nested(request
, offset
);
1560 nl_msg_put_act_pop_vlan(struct ofpbuf
*request
)
1564 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
1565 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1567 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
1568 .v_action
= TCA_VLAN_ACT_POP
};
1570 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
1572 nl_msg_end_nested(request
, offset
);
1576 nl_msg_put_act_tunnel_key_release(struct ofpbuf
*request
)
1580 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
1581 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1583 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
1584 .t_action
= TCA_TUNNEL_KEY_ACT_RELEASE
};
1586 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
1588 nl_msg_end_nested(request
, offset
);
1592 nl_msg_put_act_tunnel_geneve_option(struct ofpbuf
*request
,
1593 struct tun_metadata tun_metadata
)
1595 const struct geneve_opt
*opt
;
1596 size_t outer
, inner
;
1599 len
= tun_metadata
.present
.len
;
1604 outer
= nl_msg_start_nested(request
, TCA_TUNNEL_KEY_ENC_OPTS
);
1607 opt
= &tun_metadata
.opts
.gnv
[cnt
];
1608 inner
= nl_msg_start_nested(request
, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE
);
1610 nl_msg_put_be16(request
, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS
,
1612 nl_msg_put_u8(request
, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE
, opt
->type
);
1613 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA
, opt
+ 1,
1616 cnt
+= sizeof(struct geneve_opt
) / 4 + opt
->length
;
1617 len
-= sizeof(struct geneve_opt
) + opt
->length
* 4;
1619 nl_msg_end_nested(request
, inner
);
1622 nl_msg_end_nested(request
, outer
);
1626 nl_msg_put_act_tunnel_key_set(struct ofpbuf
*request
, ovs_be64 id
,
1627 ovs_be32 ipv4_src
, ovs_be32 ipv4_dst
,
1628 struct in6_addr
*ipv6_src
,
1629 struct in6_addr
*ipv6_dst
,
1630 ovs_be16 tp_dst
, uint8_t tos
, uint8_t ttl
,
1631 struct tun_metadata tun_metadata
)
1635 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
1636 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1638 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
1639 .t_action
= TCA_TUNNEL_KEY_ACT_SET
};
1641 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
1643 ovs_be32 id32
= be64_to_be32(id
);
1644 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_KEY_ID
, id32
);
1646 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_SRC
, ipv4_src
);
1647 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_DST
, ipv4_dst
);
1648 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
1649 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_DST
,
1651 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_SRC
,
1655 nl_msg_put_u8(request
, TCA_TUNNEL_KEY_ENC_TOS
, tos
);
1658 nl_msg_put_u8(request
, TCA_TUNNEL_KEY_ENC_TTL
, ttl
);
1660 nl_msg_put_be16(request
, TCA_TUNNEL_KEY_ENC_DST_PORT
, tp_dst
);
1661 nl_msg_put_act_tunnel_geneve_option(request
, tun_metadata
);
1663 nl_msg_end_nested(request
, offset
);
1667 nl_msg_put_act_drop(struct ofpbuf
*request
)
1671 nl_msg_put_string(request
, TCA_ACT_KIND
, "gact");
1672 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1674 struct tc_gact p
= { .action
= TC_ACT_SHOT
};
1676 nl_msg_put_unspec(request
, TCA_GACT_PARMS
, &p
, sizeof p
);
1678 nl_msg_end_nested(request
, offset
);
1682 nl_msg_put_act_mirred(struct ofpbuf
*request
, int ifindex
, int action
,
1687 nl_msg_put_string(request
, TCA_ACT_KIND
, "mirred");
1688 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1690 struct tc_mirred m
= { .action
= action
,
1692 .ifindex
= ifindex
};
1694 nl_msg_put_unspec(request
, TCA_MIRRED_PARMS
, &m
, sizeof m
);
1696 nl_msg_end_nested(request
, offset
);
1700 nl_msg_put_act_cookie(struct ofpbuf
*request
, struct tc_cookie
*ck
) {
1702 nl_msg_put_unspec(request
, TCA_ACT_COOKIE
, ck
->data
, ck
->len
);
1706 /* Given flower, a key_to_pedit map entry, calculates the rest,
1709 * mask, data - pointers of where read the first word of flower->key/mask.
1710 * current_offset - which offset to use for the first pedit action.
1711 * cnt - max pedits actions to use.
1712 * first_word_mask/last_word_mask - the mask to use for the first/last read
1713 * (as we read entire words). */
1715 calc_offsets(struct tc_flower
*flower
, struct flower_key_to_pedit
*m
,
1716 int *cur_offset
, int *cnt
, uint32_t *last_word_mask
,
1717 uint32_t *first_word_mask
, uint32_t **mask
, uint32_t **data
)
1719 int start_offset
, max_offset
, total_size
;
1720 int diff
, right_zero_bits
, left_zero_bits
;
1721 char *rewrite_key
= (void *) &flower
->rewrite
.key
;
1722 char *rewrite_mask
= (void *) &flower
->rewrite
.mask
;
1724 max_offset
= m
->offset
+ m
->size
;
1725 start_offset
= ROUND_DOWN(m
->offset
, 4);
1726 diff
= m
->offset
- start_offset
;
1727 total_size
= max_offset
- start_offset
;
1728 right_zero_bits
= 8 * (4 - (max_offset
% 4));
1729 left_zero_bits
= 8 * (m
->offset
- start_offset
);
1731 *cur_offset
= start_offset
;
1732 *cnt
= (total_size
/ 4) + (total_size
% 4 ? 1 : 0);
1733 *last_word_mask
= UINT32_MAX
>> right_zero_bits
;
1734 *first_word_mask
= UINT32_MAX
<< left_zero_bits
;
1735 *data
= (void *) (rewrite_key
+ m
->flower_offset
- diff
);
1736 *mask
= (void *) (rewrite_mask
+ m
->flower_offset
- diff
);
1740 csum_update_flag(struct tc_flower
*flower
,
1741 enum pedit_header_type htype
) {
1742 /* Explictily specifiy the csum flags so HW can return EOPNOTSUPP
1743 * if it doesn't support a checksum recalculation of some headers.
1744 * And since OVS allows a flow such as
1745 * eth(dst=<mac>),eth_type(0x0800) actions=set(ipv4(src=<new_ip>))
1746 * we need to force a more specific flow as this can, for example,
1747 * need a recalculation of icmp checksum if the packet that passes
1748 * is ICMPv6 and tcp checksum if its tcp. */
1751 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
:
1752 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_IPV4HDR
;
1754 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
:
1755 case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
:
1756 case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
:
1757 if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
1758 flower
->needs_full_ip_proto_mask
= true;
1759 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_TCP
;
1760 } else if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
1761 flower
->needs_full_ip_proto_mask
= true;
1762 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_UDP
;
1763 } else if (flower
->key
.ip_proto
== IPPROTO_ICMP
) {
1764 flower
->needs_full_ip_proto_mask
= true;
1765 } else if (flower
->key
.ip_proto
== IPPROTO_ICMPV6
) {
1766 flower
->needs_full_ip_proto_mask
= true;
1767 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_ICMP
;
1769 VLOG_WARN_RL(&error_rl
,
1770 "can't offload rewrite of IP/IPV6 with ip_proto: %d",
1771 flower
->key
.ip_proto
);
1775 case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
:
1776 return 0; /* success */
1778 case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK
:
1779 case __PEDIT_HDR_TYPE_MAX
:
1788 nl_msg_put_flower_rewrite_pedits(struct ofpbuf
*request
,
1789 struct tc_flower
*flower
)
1792 struct tc_pedit sel
;
1793 struct tc_pedit_key keys
[MAX_PEDIT_OFFSETS
];
1794 struct tc_pedit_key_ex keys_ex
[MAX_PEDIT_OFFSETS
];
1802 for (i
= 0; i
< ARRAY_SIZE(flower_pedit_map
); i
++) {
1803 struct flower_key_to_pedit
*m
= &flower_pedit_map
[i
];
1804 struct tc_pedit_key
*pedit_key
= NULL
;
1805 struct tc_pedit_key_ex
*pedit_key_ex
= NULL
;
1806 uint32_t *mask
, *data
, first_word_mask
, last_word_mask
;
1807 int cnt
= 0, cur_offset
= 0;
1813 calc_offsets(flower
, m
, &cur_offset
, &cnt
, &last_word_mask
,
1814 &first_word_mask
, &mask
, &data
);
1816 for (j
= 0; j
< cnt
; j
++, mask
++, data
++, cur_offset
+= 4) {
1817 uint32_t mask_word
= *mask
;
1820 mask_word
&= first_word_mask
;
1823 mask_word
&= last_word_mask
;
1828 if (sel
.sel
.nkeys
== MAX_PEDIT_OFFSETS
) {
1829 VLOG_WARN_RL(&error_rl
, "reached too many pedit offsets: %d",
1834 pedit_key
= &sel
.keys
[sel
.sel
.nkeys
];
1835 pedit_key_ex
= &sel
.keys_ex
[sel
.sel
.nkeys
];
1836 pedit_key_ex
->cmd
= TCA_PEDIT_KEY_EX_CMD_SET
;
1837 pedit_key_ex
->htype
= m
->htype
;
1838 pedit_key
->off
= cur_offset
;
1839 pedit_key
->mask
= ~mask_word
;
1840 pedit_key
->val
= *data
& mask_word
;
1843 err
= csum_update_flag(flower
, m
->htype
);
1848 if (flower
->needs_full_ip_proto_mask
) {
1849 flower
->mask
.ip_proto
= UINT8_MAX
;
1853 nl_msg_put_act_pedit(request
, &sel
.sel
, sel
.keys_ex
);
1859 nl_msg_put_flower_acts(struct ofpbuf
*request
, struct tc_flower
*flower
)
1863 uint16_t act_index
= 1;
1864 struct tc_action
*action
;
1867 offset
= nl_msg_start_nested(request
, TCA_FLOWER_ACT
);
1871 if (flower
->tunnel
) {
1872 act_offset
= nl_msg_start_nested(request
, act_index
++);
1873 nl_msg_put_act_tunnel_key_release(request
);
1874 nl_msg_end_nested(request
, act_offset
);
1877 action
= flower
->actions
;
1878 for (i
= 0; i
< flower
->action_count
; i
++, action
++) {
1879 switch (action
->type
) {
1880 case TC_ACT_PEDIT
: {
1881 act_offset
= nl_msg_start_nested(request
, act_index
++);
1882 error
= nl_msg_put_flower_rewrite_pedits(request
, flower
);
1886 nl_msg_end_nested(request
, act_offset
);
1888 if (flower
->csum_update_flags
) {
1889 act_offset
= nl_msg_start_nested(request
, act_index
++);
1890 nl_msg_put_act_csum(request
, flower
->csum_update_flags
);
1891 nl_msg_end_nested(request
, act_offset
);
1895 case TC_ACT_ENCAP
: {
1896 act_offset
= nl_msg_start_nested(request
, act_index
++);
1897 nl_msg_put_act_tunnel_key_set(request
, action
->encap
.id
,
1898 action
->encap
.ipv4
.ipv4_src
,
1899 action
->encap
.ipv4
.ipv4_dst
,
1900 &action
->encap
.ipv6
.ipv6_src
,
1901 &action
->encap
.ipv6
.ipv6_dst
,
1902 action
->encap
.tp_dst
,
1905 action
->encap
.data
);
1906 nl_msg_end_nested(request
, act_offset
);
1909 case TC_ACT_VLAN_POP
: {
1910 act_offset
= nl_msg_start_nested(request
, act_index
++);
1911 nl_msg_put_act_pop_vlan(request
);
1912 nl_msg_end_nested(request
, act_offset
);
1915 case TC_ACT_VLAN_PUSH
: {
1916 act_offset
= nl_msg_start_nested(request
, act_index
++);
1917 nl_msg_put_act_push_vlan(request
,
1918 action
->vlan
.vlan_push_tpid
,
1919 action
->vlan
.vlan_push_id
,
1920 action
->vlan
.vlan_push_prio
);
1921 nl_msg_end_nested(request
, act_offset
);
1924 case TC_ACT_OUTPUT
: {
1925 ifindex
= action
->ifindex_out
;
1927 VLOG_ERR_RL(&error_rl
, "%s: invalid ifindex: %d, type: %d",
1928 __func__
, ifindex
, action
->type
);
1931 act_offset
= nl_msg_start_nested(request
, act_index
++);
1932 if (i
== flower
->action_count
- 1) {
1933 nl_msg_put_act_mirred(request
, ifindex
, TC_ACT_STOLEN
,
1936 nl_msg_put_act_mirred(request
, ifindex
, TC_ACT_PIPE
,
1939 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
1940 nl_msg_end_nested(request
, act_offset
);
1947 act_offset
= nl_msg_start_nested(request
, act_index
++);
1948 nl_msg_put_act_drop(request
);
1949 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
1950 nl_msg_end_nested(request
, act_offset
);
1952 nl_msg_end_nested(request
, offset
);
1958 nl_msg_put_masked_value(struct ofpbuf
*request
, uint16_t type
,
1959 uint16_t mask_type
, const void *data
,
1960 const void *mask_data
, size_t len
)
1962 if (mask_type
!= TCA_FLOWER_UNSPEC
) {
1963 if (is_all_zeros(mask_data
, len
)) {
1966 nl_msg_put_unspec(request
, mask_type
, mask_data
, len
);
1968 nl_msg_put_unspec(request
, type
, data
, len
);
1972 nl_msg_put_flower_tunnel_opts(struct ofpbuf
*request
, uint16_t type
,
1973 struct tun_metadata metadata
)
1975 struct geneve_opt
*opt
;
1976 size_t outer
, inner
;
1979 len
= metadata
.present
.len
;
1984 outer
= nl_msg_start_nested(request
, type
);
1986 opt
= &metadata
.opts
.gnv
[cnt
];
1987 inner
= nl_msg_start_nested(request
, TCA_FLOWER_KEY_ENC_OPTS_GENEVE
);
1989 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS
,
1991 nl_msg_put_u8(request
, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE
, opt
->type
);
1992 nl_msg_put_unspec(request
, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA
, opt
+ 1,
1995 cnt
+= sizeof(struct geneve_opt
) / 4 + opt
->length
;
1996 len
-= sizeof(struct geneve_opt
) + opt
->length
* 4;
1998 nl_msg_end_nested(request
, inner
);
2000 nl_msg_end_nested(request
, outer
);
2004 nl_msg_put_flower_tunnel(struct ofpbuf
*request
, struct tc_flower
*flower
)
2006 ovs_be32 ipv4_src
= flower
->key
.tunnel
.ipv4
.ipv4_src
;
2007 ovs_be32 ipv4_dst
= flower
->key
.tunnel
.ipv4
.ipv4_dst
;
2008 struct in6_addr
*ipv6_src
= &flower
->key
.tunnel
.ipv6
.ipv6_src
;
2009 struct in6_addr
*ipv6_dst
= &flower
->key
.tunnel
.ipv6
.ipv6_dst
;
2010 ovs_be16 tp_dst
= flower
->key
.tunnel
.tp_dst
;
2011 ovs_be32 id
= be64_to_be32(flower
->key
.tunnel
.id
);
2012 uint8_t tos
= flower
->key
.tunnel
.tos
;
2013 uint8_t ttl
= flower
->key
.tunnel
.ttl
;
2014 uint8_t tos_mask
= flower
->mask
.tunnel
.tos
;
2015 uint8_t ttl_mask
= flower
->mask
.tunnel
.ttl
;
2018 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_SRC
, ipv4_src
);
2019 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_DST
, ipv4_dst
);
2020 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
2021 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_SRC
, ipv6_src
);
2022 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_DST
, ipv6_dst
);
2025 nl_msg_put_u8(request
, TCA_FLOWER_KEY_ENC_IP_TOS
, tos
);
2026 nl_msg_put_u8(request
, TCA_FLOWER_KEY_ENC_IP_TOS_MASK
, tos_mask
);
2029 nl_msg_put_u8(request
, TCA_FLOWER_KEY_ENC_IP_TTL
, ttl
);
2030 nl_msg_put_u8(request
, TCA_FLOWER_KEY_ENC_IP_TTL_MASK
, ttl_mask
);
2032 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ENC_UDP_DST_PORT
, tp_dst
);
2033 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_KEY_ID
, id
);
2034 nl_msg_put_flower_tunnel_opts(request
, TCA_FLOWER_KEY_ENC_OPTS
,
2035 flower
->key
.tunnel
.metadata
);
2036 nl_msg_put_flower_tunnel_opts(request
, TCA_FLOWER_KEY_ENC_OPTS_MASK
,
2037 flower
->mask
.tunnel
.metadata
);
2040 #define FLOWER_PUT_MASKED_VALUE(member, type) \
2041 nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \
2042 &flower->mask.member, sizeof flower->key.member)
2045 nl_msg_put_flower_options(struct ofpbuf
*request
, struct tc_flower
*flower
)
2048 uint16_t host_eth_type
= ntohs(flower
->key
.eth_type
);
2049 bool is_vlan
= eth_type_vlan(flower
->key
.eth_type
);
2050 bool is_qinq
= is_vlan
&& eth_type_vlan(flower
->key
.encap_eth_type
[0]);
2051 bool is_mpls
= eth_type_mpls(flower
->key
.eth_type
);
2054 /* need to parse acts first as some acts require changing the matching
2055 * see csum_update_flag() */
2056 err
= nl_msg_put_flower_acts(request
, flower
);
2063 host_eth_type
= ntohs(flower
->key
.encap_eth_type
[1]);
2065 host_eth_type
= ntohs(flower
->key
.encap_eth_type
[0]);
2070 host_eth_type
= ntohs(flower
->key
.encap_eth_type
[0]);
2073 FLOWER_PUT_MASKED_VALUE(dst_mac
, TCA_FLOWER_KEY_ETH_DST
);
2074 FLOWER_PUT_MASKED_VALUE(src_mac
, TCA_FLOWER_KEY_ETH_SRC
);
2076 if (host_eth_type
== ETH_P_IP
|| host_eth_type
== ETH_P_IPV6
) {
2077 FLOWER_PUT_MASKED_VALUE(ip_ttl
, TCA_FLOWER_KEY_IP_TTL
);
2078 FLOWER_PUT_MASKED_VALUE(ip_tos
, TCA_FLOWER_KEY_IP_TOS
);
2080 if (flower
->mask
.ip_proto
&& flower
->key
.ip_proto
) {
2081 nl_msg_put_u8(request
, TCA_FLOWER_KEY_IP_PROTO
,
2082 flower
->key
.ip_proto
);
2085 if (flower
->mask
.flags
) {
2086 nl_msg_put_be32(request
, TCA_FLOWER_KEY_FLAGS
,
2087 htonl(flower
->key
.flags
));
2088 nl_msg_put_be32(request
, TCA_FLOWER_KEY_FLAGS_MASK
,
2089 htonl(flower
->mask
.flags
));
2092 if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
2093 FLOWER_PUT_MASKED_VALUE(udp_src
, TCA_FLOWER_KEY_UDP_SRC
);
2094 FLOWER_PUT_MASKED_VALUE(udp_dst
, TCA_FLOWER_KEY_UDP_DST
);
2095 } else if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
2096 FLOWER_PUT_MASKED_VALUE(tcp_src
, TCA_FLOWER_KEY_TCP_SRC
);
2097 FLOWER_PUT_MASKED_VALUE(tcp_dst
, TCA_FLOWER_KEY_TCP_DST
);
2098 FLOWER_PUT_MASKED_VALUE(tcp_flags
, TCA_FLOWER_KEY_TCP_FLAGS
);
2099 } else if (flower
->key
.ip_proto
== IPPROTO_SCTP
) {
2100 FLOWER_PUT_MASKED_VALUE(sctp_src
, TCA_FLOWER_KEY_SCTP_SRC
);
2101 FLOWER_PUT_MASKED_VALUE(sctp_dst
, TCA_FLOWER_KEY_SCTP_DST
);
2105 if (host_eth_type
== ETH_P_IP
) {
2106 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_src
, TCA_FLOWER_KEY_IPV4_SRC
);
2107 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_dst
, TCA_FLOWER_KEY_IPV4_DST
);
2108 } else if (host_eth_type
== ETH_P_IPV6
) {
2109 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_src
, TCA_FLOWER_KEY_IPV6_SRC
);
2110 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_dst
, TCA_FLOWER_KEY_IPV6_DST
);
2113 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ETH_TYPE
, flower
->key
.eth_type
);
2116 if (mpls_lse_to_ttl(flower
->mask
.mpls_lse
)) {
2117 nl_msg_put_u8(request
, TCA_FLOWER_KEY_MPLS_TTL
,
2118 mpls_lse_to_ttl(flower
->key
.mpls_lse
));
2120 if (mpls_lse_to_tc(flower
->mask
.mpls_lse
)) {
2121 nl_msg_put_u8(request
, TCA_FLOWER_KEY_MPLS_TC
,
2122 mpls_lse_to_tc(flower
->key
.mpls_lse
));
2124 if (mpls_lse_to_bos(flower
->mask
.mpls_lse
)) {
2125 nl_msg_put_u8(request
, TCA_FLOWER_KEY_MPLS_BOS
,
2126 mpls_lse_to_bos(flower
->key
.mpls_lse
));
2128 if (mpls_lse_to_label(flower
->mask
.mpls_lse
)) {
2129 nl_msg_put_u32(request
, TCA_FLOWER_KEY_MPLS_LABEL
,
2130 mpls_lse_to_label(flower
->key
.mpls_lse
));
2135 if (flower
->mask
.vlan_id
[0]) {
2136 nl_msg_put_u16(request
, TCA_FLOWER_KEY_VLAN_ID
,
2137 flower
->key
.vlan_id
[0]);
2139 if (flower
->mask
.vlan_prio
[0]) {
2140 nl_msg_put_u8(request
, TCA_FLOWER_KEY_VLAN_PRIO
,
2141 flower
->key
.vlan_prio
[0]);
2143 if (flower
->key
.encap_eth_type
[0]) {
2144 nl_msg_put_be16(request
, TCA_FLOWER_KEY_VLAN_ETH_TYPE
,
2145 flower
->key
.encap_eth_type
[0]);
2149 if (flower
->mask
.vlan_id
[1]) {
2150 nl_msg_put_u16(request
, TCA_FLOWER_KEY_CVLAN_ID
,
2151 flower
->key
.vlan_id
[1]);
2153 if (flower
->mask
.vlan_prio
[1]) {
2154 nl_msg_put_u8(request
, TCA_FLOWER_KEY_CVLAN_PRIO
,
2155 flower
->key
.vlan_prio
[1]);
2157 if (flower
->key
.encap_eth_type
[1]) {
2158 nl_msg_put_be16(request
, TCA_FLOWER_KEY_CVLAN_ETH_TYPE
,
2159 flower
->key
.encap_eth_type
[1]);
2164 nl_msg_put_u32(request
, TCA_FLOWER_FLAGS
, tc_get_tc_cls_policy(tc_policy
));
2166 if (flower
->tunnel
) {
2167 nl_msg_put_flower_tunnel(request
, flower
);
2174 tc_replace_flower(int ifindex
, uint16_t prio
, uint32_t handle
,
2175 struct tc_flower
*flower
, uint32_t block_id
)
2177 struct ofpbuf request
;
2178 struct tcmsg
*tcmsg
;
2179 struct ofpbuf
*reply
;
2181 size_t basic_offset
;
2182 uint16_t eth_type
= (OVS_FORCE
uint16_t) flower
->key
.eth_type
;
2185 index
= block_id
? TCM_IFINDEX_MAGIC_BLOCK
: ifindex
;
2186 tcmsg
= tc_make_request(index
, RTM_NEWTFILTER
, NLM_F_CREATE
| NLM_F_ECHO
,
2188 tcmsg
->tcm_parent
= block_id
? : TC_INGRESS_PARENT
;
2189 tcmsg
->tcm_info
= tc_make_handle(prio
, eth_type
);
2190 tcmsg
->tcm_handle
= handle
;
2192 nl_msg_put_string(&request
, TCA_KIND
, "flower");
2193 basic_offset
= nl_msg_start_nested(&request
, TCA_OPTIONS
);
2195 error
= nl_msg_put_flower_options(&request
, flower
);
2198 ofpbuf_uninit(&request
);
2202 nl_msg_end_nested(&request
, basic_offset
);
2204 error
= tc_transact(&request
, &reply
);
2207 ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
2209 flower
->prio
= tc_get_major(tc
->tcm_info
);
2210 flower
->handle
= tc
->tcm_handle
;
2211 ofpbuf_delete(reply
);
2218 tc_set_policy(const char *policy
)
2224 if (!strcmp(policy
, "skip_sw")) {
2225 tc_policy
= TC_POLICY_SKIP_SW
;
2226 } else if (!strcmp(policy
, "skip_hw")) {
2227 tc_policy
= TC_POLICY_SKIP_HW
;
2228 } else if (!strcmp(policy
, "none")) {
2229 tc_policy
= TC_POLICY_NONE
;
2231 VLOG_WARN("tc: Invalid policy '%s'", policy
);
2235 VLOG_INFO("tc: Using policy '%s'", policy
);