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 VLOG_DEFINE_THIS_MODULE(tc
);
48 static struct vlog_rate_limit error_rl
= VLOG_RATE_LIMIT_INIT(60, 5);
50 enum tc_offload_policy
{
56 static enum tc_offload_policy tc_policy
= TC_POLICY_NONE
;
58 struct tc_pedit_key_ex
{
59 enum pedit_header_type htype
;
63 struct flower_key_to_pedit
{
64 enum pedit_header_type htype
;
70 static struct flower_key_to_pedit flower_pedit_map
[] = {
72 TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
,
74 offsetof(struct tc_flower_key
, ipv4
.ipv4_src
),
75 MEMBER_SIZEOF(struct tc_flower_key
, ipv4
.ipv4_src
)
77 TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
,
79 offsetof(struct tc_flower_key
, ipv4
.ipv4_dst
),
80 MEMBER_SIZEOF(struct tc_flower_key
, ipv4
.ipv4_dst
)
82 TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
,
84 offsetof(struct tc_flower_key
, ipv4
.rewrite_ttl
),
85 MEMBER_SIZEOF(struct tc_flower_key
, ipv4
.rewrite_ttl
)
87 TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
,
89 offsetof(struct tc_flower_key
, ipv6
.ipv6_src
),
90 MEMBER_SIZEOF(struct tc_flower_key
, ipv6
.ipv6_src
)
92 TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
,
94 offsetof(struct tc_flower_key
, ipv6
.ipv6_dst
),
95 MEMBER_SIZEOF(struct tc_flower_key
, ipv6
.ipv6_dst
)
97 TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
,
99 offsetof(struct tc_flower_key
, src_mac
),
100 MEMBER_SIZEOF(struct tc_flower_key
, src_mac
)
102 TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
,
104 offsetof(struct tc_flower_key
, dst_mac
),
105 MEMBER_SIZEOF(struct tc_flower_key
, dst_mac
)
107 TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
,
109 offsetof(struct tc_flower_key
, eth_type
),
110 MEMBER_SIZEOF(struct tc_flower_key
, eth_type
)
112 TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
,
114 offsetof(struct tc_flower_key
, tcp_src
),
115 MEMBER_SIZEOF(struct tc_flower_key
, tcp_src
)
117 TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
,
119 offsetof(struct tc_flower_key
, tcp_dst
),
120 MEMBER_SIZEOF(struct tc_flower_key
, tcp_dst
)
122 TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
,
124 offsetof(struct tc_flower_key
, udp_src
),
125 MEMBER_SIZEOF(struct tc_flower_key
, udp_src
)
127 TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
,
129 offsetof(struct tc_flower_key
, udp_dst
),
130 MEMBER_SIZEOF(struct tc_flower_key
, udp_dst
)
135 csum_update_flag(struct tc_flower
*flower
,
136 enum pedit_header_type htype
);
139 tc_make_request(int ifindex
, int type
, unsigned int flags
,
140 struct ofpbuf
*request
)
144 ofpbuf_init(request
, 512);
145 nl_msg_put_nlmsghdr(request
, sizeof *tcmsg
, type
, NLM_F_REQUEST
| flags
);
146 tcmsg
= ofpbuf_put_zeros(request
, sizeof *tcmsg
);
147 tcmsg
->tcm_family
= AF_UNSPEC
;
148 tcmsg
->tcm_ifindex
= ifindex
;
149 /* Caller should fill in tcmsg->tcm_handle. */
150 /* Caller should fill in tcmsg->tcm_parent. */
156 tc_transact(struct ofpbuf
*request
, struct ofpbuf
**replyp
)
158 int error
= nl_transact(NETLINK_ROUTE
, request
, replyp
);
159 ofpbuf_uninit(request
);
163 /* Adds or deletes a root ingress qdisc on device with specified ifindex.
165 * This function is equivalent to running the following when 'add' is true:
166 * /sbin/tc qdisc add dev <devname> handle ffff: ingress
168 * This function is equivalent to running the following when 'add' is false:
169 * /sbin/tc qdisc del dev <devname> handle ffff: ingress
171 * Where dev <devname> is the device with specified ifindex name.
173 * The configuration and stats may be seen with the following command:
174 * /sbin/tc -s qdisc show dev <devname>
176 * Returns 0 if successful, otherwise a positive errno value.
179 tc_add_del_ingress_qdisc(int ifindex
, bool add
)
181 struct ofpbuf request
;
184 int type
= add
? RTM_NEWQDISC
: RTM_DELQDISC
;
185 int flags
= add
? NLM_F_EXCL
| NLM_F_CREATE
: 0;
187 tcmsg
= tc_make_request(ifindex
, type
, flags
, &request
);
188 tcmsg
->tcm_handle
= TC_H_MAKE(TC_H_INGRESS
, 0);
189 tcmsg
->tcm_parent
= TC_H_INGRESS
;
190 nl_msg_put_string(&request
, TCA_KIND
, "ingress");
191 nl_msg_put_unspec(&request
, TCA_OPTIONS
, NULL
, 0);
193 error
= tc_transact(&request
, NULL
);
195 /* If we're deleting the qdisc, don't worry about some of the
196 * error conditions. */
197 if (!add
&& (error
== ENOENT
|| error
== EINVAL
)) {
206 static const struct nl_policy tca_policy
[] = {
207 [TCA_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
208 [TCA_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
209 [TCA_STATS
] = { .type
= NL_A_UNSPEC
,
210 .min_len
= sizeof(struct tc_stats
), .optional
= true, },
211 [TCA_STATS2
] = { .type
= NL_A_NESTED
, .optional
= true, },
214 static const struct nl_policy tca_flower_policy
[] = {
215 [TCA_FLOWER_CLASSID
] = { .type
= NL_A_U32
, .optional
= true, },
216 [TCA_FLOWER_INDEV
] = { .type
= NL_A_STRING
, .max_len
= IFNAMSIZ
,
218 [TCA_FLOWER_KEY_ETH_SRC
] = { .type
= NL_A_UNSPEC
,
219 .min_len
= ETH_ALEN
, .optional
= true, },
220 [TCA_FLOWER_KEY_ETH_DST
] = { .type
= NL_A_UNSPEC
,
221 .min_len
= ETH_ALEN
, .optional
= true, },
222 [TCA_FLOWER_KEY_ETH_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
225 [TCA_FLOWER_KEY_ETH_DST_MASK
] = { .type
= NL_A_UNSPEC
,
228 [TCA_FLOWER_KEY_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= false, },
229 [TCA_FLOWER_FLAGS
] = { .type
= NL_A_U32
, .optional
= false, },
230 [TCA_FLOWER_ACT
] = { .type
= NL_A_NESTED
, .optional
= false, },
231 [TCA_FLOWER_KEY_IP_PROTO
] = { .type
= NL_A_U8
, .optional
= true, },
232 [TCA_FLOWER_KEY_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
233 [TCA_FLOWER_KEY_IPV4_DST
] = {.type
= NL_A_U32
, .optional
= true, },
234 [TCA_FLOWER_KEY_IPV4_SRC_MASK
] = { .type
= NL_A_U32
, .optional
= true, },
235 [TCA_FLOWER_KEY_IPV4_DST_MASK
] = { .type
= NL_A_U32
, .optional
= true, },
236 [TCA_FLOWER_KEY_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
237 .min_len
= sizeof(struct in6_addr
),
239 [TCA_FLOWER_KEY_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
240 .min_len
= sizeof(struct in6_addr
),
242 [TCA_FLOWER_KEY_IPV6_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
243 .min_len
= sizeof(struct in6_addr
),
245 [TCA_FLOWER_KEY_IPV6_DST_MASK
] = { .type
= NL_A_UNSPEC
,
246 .min_len
= sizeof(struct in6_addr
),
248 [TCA_FLOWER_KEY_TCP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
249 [TCA_FLOWER_KEY_TCP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
250 [TCA_FLOWER_KEY_TCP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
251 [TCA_FLOWER_KEY_TCP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
252 [TCA_FLOWER_KEY_UDP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
253 [TCA_FLOWER_KEY_UDP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
254 [TCA_FLOWER_KEY_UDP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
255 [TCA_FLOWER_KEY_UDP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
256 [TCA_FLOWER_KEY_SCTP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
257 [TCA_FLOWER_KEY_SCTP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
258 [TCA_FLOWER_KEY_SCTP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
259 [TCA_FLOWER_KEY_SCTP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
260 [TCA_FLOWER_KEY_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
261 [TCA_FLOWER_KEY_VLAN_PRIO
] = { .type
= NL_A_U8
, .optional
= true, },
262 [TCA_FLOWER_KEY_VLAN_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= true, },
263 [TCA_FLOWER_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
264 [TCA_FLOWER_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
265 [TCA_FLOWER_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
266 [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
] = { .type
= NL_A_U32
,
268 [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
] = { .type
= NL_A_U32
,
270 [TCA_FLOWER_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
271 .min_len
= sizeof(struct in6_addr
),
273 [TCA_FLOWER_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
274 .min_len
= sizeof(struct in6_addr
),
276 [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
277 .min_len
= sizeof(struct in6_addr
),
279 [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
] = { .type
= NL_A_UNSPEC
,
280 .min_len
= sizeof(struct in6_addr
),
282 [TCA_FLOWER_KEY_ENC_UDP_DST_PORT
] = { .type
= NL_A_U16
,
284 [TCA_FLOWER_KEY_FLAGS
] = { .type
= NL_A_BE32
, .optional
= true, },
285 [TCA_FLOWER_KEY_FLAGS_MASK
] = { .type
= NL_A_BE32
, .optional
= true, },
286 [TCA_FLOWER_KEY_IP_TTL
] = { .type
= NL_A_U8
,
288 [TCA_FLOWER_KEY_IP_TTL_MASK
] = { .type
= NL_A_U8
,
290 [TCA_FLOWER_KEY_TCP_FLAGS
] = { .type
= NL_A_U16
,
292 [TCA_FLOWER_KEY_TCP_FLAGS_MASK
] = { .type
= NL_A_U16
,
297 nl_parse_flower_eth(struct nlattr
**attrs
, struct tc_flower
*flower
)
299 const struct eth_addr
*eth
;
301 if (attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
]) {
302 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC
], ETH_ALEN
);
303 memcpy(&flower
->key
.src_mac
, eth
, sizeof flower
->key
.src_mac
);
305 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
], ETH_ALEN
);
306 memcpy(&flower
->mask
.src_mac
, eth
, sizeof flower
->mask
.src_mac
);
308 if (attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
]) {
309 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST
], ETH_ALEN
);
310 memcpy(&flower
->key
.dst_mac
, eth
, sizeof flower
->key
.dst_mac
);
312 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
], ETH_ALEN
);
313 memcpy(&flower
->mask
.dst_mac
, eth
, sizeof flower
->mask
.dst_mac
);
318 nl_parse_flower_vlan(struct nlattr
**attrs
, struct tc_flower
*flower
)
320 if (flower
->key
.eth_type
!= htons(ETH_TYPE_VLAN
)) {
324 flower
->key
.encap_eth_type
=
325 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ETH_TYPE
]);
327 if (attrs
[TCA_FLOWER_KEY_VLAN_ID
]) {
328 flower
->key
.vlan_id
=
329 nl_attr_get_u16(attrs
[TCA_FLOWER_KEY_VLAN_ID
]);
331 if (attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]) {
332 flower
->key
.vlan_prio
=
333 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]);
338 nl_parse_flower_tunnel(struct nlattr
**attrs
, struct tc_flower
*flower
)
340 if (attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]) {
341 ovs_be32 id
= nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]);
343 flower
->tunnel
.id
= be32_to_be64(id
);
345 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
]) {
346 flower
->tunnel
.ipv4
.ipv4_src
=
347 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC
]);
349 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
]) {
350 flower
->tunnel
.ipv4
.ipv4_dst
=
351 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST
]);
353 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
]) {
354 flower
->tunnel
.ipv6
.ipv6_src
=
355 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC
]);
357 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
]) {
358 flower
->tunnel
.ipv6
.ipv6_dst
=
359 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST
]);
361 if (attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]) {
362 flower
->tunnel
.tp_dst
=
363 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]);
368 nl_parse_flower_ip(struct nlattr
**attrs
, struct tc_flower
*flower
) {
369 uint8_t ip_proto
= 0;
370 struct tc_flower_key
*key
= &flower
->key
;
371 struct tc_flower_key
*mask
= &flower
->mask
;
373 if (attrs
[TCA_FLOWER_KEY_IP_PROTO
]) {
374 ip_proto
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_PROTO
]);
375 key
->ip_proto
= ip_proto
;
376 mask
->ip_proto
= UINT8_MAX
;
379 if (attrs
[TCA_FLOWER_KEY_FLAGS_MASK
]) {
380 key
->flags
= ntohl(nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_FLAGS
]));
382 ntohl(nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_FLAGS_MASK
]));
385 if (attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]) {
387 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC
]);
388 mask
->ipv4
.ipv4_src
=
389 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]);
391 if (attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]) {
393 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST
]);
394 mask
->ipv4
.ipv4_dst
=
395 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]);
397 if (attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
]) {
398 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_SRC
];
399 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
];
401 key
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr
);
402 mask
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr_mask
);
404 if (attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
]) {
405 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_DST
];
406 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
];
408 key
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr
);
409 mask
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr_mask
);
412 if (ip_proto
== IPPROTO_TCP
) {
413 if (attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]) {
415 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC
]);
417 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]);
419 if (attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]) {
421 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST
]);
423 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]);
425 if (attrs
[TCA_FLOWER_KEY_TCP_FLAGS_MASK
]) {
427 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_FLAGS
]);
429 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_FLAGS_MASK
]);
431 } else if (ip_proto
== IPPROTO_UDP
) {
432 if (attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]) {
433 key
->udp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC
]);
435 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]);
437 if (attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]) {
438 key
->udp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST
]);
440 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]);
442 } else if (ip_proto
== IPPROTO_SCTP
) {
443 if (attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]) {
444 key
->sctp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC
]);
446 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]);
448 if (attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]) {
449 key
->sctp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST
]);
451 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]);
455 if (attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]) {
456 key
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL
]);
457 mask
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]);
461 static enum tc_offloaded_state
462 nl_get_flower_offloaded_state(struct nlattr
**attrs
)
464 uint32_t flower_flags
= 0;
466 if (attrs
[TCA_FLOWER_FLAGS
]) {
467 flower_flags
= nl_attr_get_u32(attrs
[TCA_FLOWER_FLAGS
]);
468 if (flower_flags
& TCA_CLS_FLAGS_NOT_IN_HW
) {
469 return TC_OFFLOADED_STATE_NOT_IN_HW
;
470 } else if (flower_flags
& TCA_CLS_FLAGS_IN_HW
) {
471 return TC_OFFLOADED_STATE_IN_HW
;
474 return TC_OFFLOADED_STATE_UNDEFINED
;
478 nl_parse_flower_flags(struct nlattr
**attrs
, struct tc_flower
*flower
)
480 flower
->offloaded_state
= nl_get_flower_offloaded_state(attrs
);
483 static const struct nl_policy pedit_policy
[] = {
484 [TCA_PEDIT_PARMS_EX
] = { .type
= NL_A_UNSPEC
,
485 .min_len
= sizeof(struct tc_pedit
),
486 .optional
= false, },
487 [TCA_PEDIT_KEYS_EX
] = { .type
= NL_A_NESTED
,
488 .optional
= false, },
492 nl_parse_act_pedit(struct nlattr
*options
, struct tc_flower
*flower
)
494 struct tc_action
*action
;
495 struct nlattr
*pe_attrs
[ARRAY_SIZE(pedit_policy
)];
496 const struct tc_pedit
*pe
;
497 const struct tc_pedit_key
*keys
;
498 const struct nlattr
*nla
, *keys_ex
, *ex_type
;
499 const void *keys_attr
;
500 char *rewrite_key
= (void *) &flower
->rewrite
.key
;
501 char *rewrite_mask
= (void *) &flower
->rewrite
.mask
;
502 size_t keys_ex_size
, left
;
503 int type
, i
= 0, err
;
505 if (!nl_parse_nested(options
, pedit_policy
, pe_attrs
,
506 ARRAY_SIZE(pedit_policy
))) {
507 VLOG_ERR_RL(&error_rl
, "failed to parse pedit action options");
511 pe
= nl_attr_get_unspec(pe_attrs
[TCA_PEDIT_PARMS_EX
], sizeof *pe
);
513 keys_attr
= pe_attrs
[TCA_PEDIT_KEYS_EX
];
514 keys_ex
= nl_attr_get(keys_attr
);
515 keys_ex_size
= nl_attr_get_size(keys_attr
);
517 NL_ATTR_FOR_EACH (nla
, left
, keys_ex
, keys_ex_size
) {
518 if (i
>= pe
->nkeys
) {
522 if (nl_attr_type(nla
) != TCA_PEDIT_KEY_EX
) {
523 VLOG_ERR_RL(&error_rl
, "unable to parse legacy pedit type: %d",
528 ex_type
= nl_attr_find_nested(nla
, TCA_PEDIT_KEY_EX_HTYPE
);
529 type
= nl_attr_get_u16(ex_type
);
531 err
= csum_update_flag(flower
, type
);
536 for (int j
= 0; j
< ARRAY_SIZE(flower_pedit_map
); j
++) {
537 struct flower_key_to_pedit
*m
= &flower_pedit_map
[j
];
538 int flower_off
= m
->flower_offset
;
542 if (m
->htype
!= type
) {
546 /* check overlap between current pedit key, which is always
547 * 4 bytes (range [off, off + 3]), and a map entry in
548 * flower_pedit_map (range [mf, mf + sz - 1]) */
549 if ((keys
->off
>= mf
&& keys
->off
< mf
+ sz
)
550 || (keys
->off
+ 3 >= mf
&& keys
->off
+ 3 < mf
+ sz
)) {
551 int diff
= flower_off
+ (keys
->off
- mf
);
552 uint32_t *dst
= (void *) (rewrite_key
+ diff
);
553 uint32_t *dst_m
= (void *) (rewrite_mask
+ diff
);
554 uint32_t mask
= ~(keys
->mask
);
557 if (keys
->off
< mf
) {
558 zero_bits
= 8 * (mf
- keys
->off
);
559 mask
&= UINT32_MAX
<< zero_bits
;
560 } else if (keys
->off
+ 4 > mf
+ m
->size
) {
561 zero_bits
= 8 * (keys
->off
+ 4 - mf
- m
->size
);
562 mask
&= UINT32_MAX
>> zero_bits
;
566 *dst
|= keys
->val
& mask
;
574 action
= &flower
->actions
[flower
->action_count
++];
575 action
->type
= TC_ACT_PEDIT
;
580 static const struct nl_policy tunnel_key_policy
[] = {
581 [TCA_TUNNEL_KEY_PARMS
] = { .type
= NL_A_UNSPEC
,
582 .min_len
= sizeof(struct tc_tunnel_key
),
583 .optional
= false, },
584 [TCA_TUNNEL_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
585 [TCA_TUNNEL_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
586 [TCA_TUNNEL_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
587 .min_len
= sizeof(struct in6_addr
),
589 [TCA_TUNNEL_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
590 .min_len
= sizeof(struct in6_addr
),
592 [TCA_TUNNEL_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
593 [TCA_TUNNEL_KEY_ENC_DST_PORT
] = { .type
= NL_A_U16
, .optional
= true, },
597 nl_parse_act_tunnel_key(struct nlattr
*options
, struct tc_flower
*flower
)
599 struct nlattr
*tun_attrs
[ARRAY_SIZE(tunnel_key_policy
)];
600 const struct nlattr
*tun_parms
;
601 const struct tc_tunnel_key
*tun
;
602 struct tc_action
*action
;
604 if (!nl_parse_nested(options
, tunnel_key_policy
, tun_attrs
,
605 ARRAY_SIZE(tunnel_key_policy
))) {
606 VLOG_ERR_RL(&error_rl
, "failed to parse tunnel_key action options");
610 tun_parms
= tun_attrs
[TCA_TUNNEL_KEY_PARMS
];
611 tun
= nl_attr_get_unspec(tun_parms
, sizeof *tun
);
612 if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_SET
) {
613 struct nlattr
*id
= tun_attrs
[TCA_TUNNEL_KEY_ENC_KEY_ID
];
614 struct nlattr
*dst_port
= tun_attrs
[TCA_TUNNEL_KEY_ENC_DST_PORT
];
615 struct nlattr
*ipv4_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_SRC
];
616 struct nlattr
*ipv4_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_DST
];
617 struct nlattr
*ipv6_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_SRC
];
618 struct nlattr
*ipv6_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_DST
];
620 action
= &flower
->actions
[flower
->action_count
++];
621 action
->type
= TC_ACT_ENCAP
;
622 action
->encap
.ipv4
.ipv4_src
= ipv4_src
? nl_attr_get_be32(ipv4_src
) : 0;
623 action
->encap
.ipv4
.ipv4_dst
= ipv4_dst
? nl_attr_get_be32(ipv4_dst
) : 0;
625 action
->encap
.ipv6
.ipv6_src
= nl_attr_get_in6_addr(ipv6_src
);
628 action
->encap
.ipv6
.ipv6_dst
= nl_attr_get_in6_addr(ipv6_dst
);
630 action
->encap
.id
= id
? be32_to_be64(nl_attr_get_be32(id
)) : 0;
631 action
->encap
.tp_dst
= dst_port
? nl_attr_get_be16(dst_port
) : 0;
632 } else if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_RELEASE
) {
633 flower
->tunnel
.tunnel
= true;
635 VLOG_ERR_RL(&error_rl
, "unknown tunnel actions: %d, %d",
636 tun
->action
, tun
->t_action
);
642 static const struct nl_policy gact_policy
[] = {
643 [TCA_GACT_PARMS
] = { .type
= NL_A_UNSPEC
,
644 .min_len
= sizeof(struct tc_gact
),
645 .optional
= false, },
646 [TCA_GACT_TM
] = { .type
= NL_A_UNSPEC
,
647 .min_len
= sizeof(struct tcf_t
),
648 .optional
= false, },
654 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
655 static int user_hz
= 100;
657 if (ovsthread_once_start(&once
)) {
658 user_hz
= sysconf(_SC_CLK_TCK
);
659 ovsthread_once_done(&once
);
666 nl_parse_tcf(const struct tcf_t
*tm
, struct tc_flower
*flower
)
668 flower
->lastused
= time_msec() - (tm
->lastuse
* 1000 / get_user_hz());
672 nl_parse_act_drop(struct nlattr
*options
, struct tc_flower
*flower
)
674 struct nlattr
*gact_attrs
[ARRAY_SIZE(gact_policy
)];
675 const struct tc_gact
*p
;
676 struct nlattr
*gact_parms
;
677 const struct tcf_t
*tm
;
679 if (!nl_parse_nested(options
, gact_policy
, gact_attrs
,
680 ARRAY_SIZE(gact_policy
))) {
681 VLOG_ERR_RL(&error_rl
, "failed to parse gact action options");
685 gact_parms
= gact_attrs
[TCA_GACT_PARMS
];
686 p
= nl_attr_get_unspec(gact_parms
, sizeof *p
);
688 if (p
->action
!= TC_ACT_SHOT
) {
689 VLOG_ERR_RL(&error_rl
, "unknown gact action: %d", p
->action
);
693 tm
= nl_attr_get_unspec(gact_attrs
[TCA_GACT_TM
], sizeof *tm
);
694 nl_parse_tcf(tm
, flower
);
699 static const struct nl_policy mirred_policy
[] = {
700 [TCA_MIRRED_PARMS
] = { .type
= NL_A_UNSPEC
,
701 .min_len
= sizeof(struct tc_mirred
),
702 .optional
= false, },
703 [TCA_MIRRED_TM
] = { .type
= NL_A_UNSPEC
,
704 .min_len
= sizeof(struct tcf_t
),
705 .optional
= false, },
709 nl_parse_act_mirred(struct nlattr
*options
, struct tc_flower
*flower
)
712 struct nlattr
*mirred_attrs
[ARRAY_SIZE(mirred_policy
)];
713 const struct tc_mirred
*m
;
714 const struct nlattr
*mirred_parms
;
715 const struct tcf_t
*tm
;
716 struct nlattr
*mirred_tm
;
717 struct tc_action
*action
;
719 if (!nl_parse_nested(options
, mirred_policy
, mirred_attrs
,
720 ARRAY_SIZE(mirred_policy
))) {
721 VLOG_ERR_RL(&error_rl
, "failed to parse mirred action options");
725 mirred_parms
= mirred_attrs
[TCA_MIRRED_PARMS
];
726 m
= nl_attr_get_unspec(mirred_parms
, sizeof *m
);
728 if (m
->eaction
!= TCA_EGRESS_REDIR
&& m
->eaction
!= TCA_EGRESS_MIRROR
) {
729 VLOG_ERR_RL(&error_rl
, "unknown mirred action: %d, %d, %d",
730 m
->action
, m
->eaction
, m
->ifindex
);
734 action
= &flower
->actions
[flower
->action_count
++];
735 action
->ifindex_out
= m
->ifindex
;
736 action
->type
= TC_ACT_OUTPUT
;
738 mirred_tm
= mirred_attrs
[TCA_MIRRED_TM
];
739 tm
= nl_attr_get_unspec(mirred_tm
, sizeof *tm
);
740 nl_parse_tcf(tm
, flower
);
745 static const struct nl_policy vlan_policy
[] = {
746 [TCA_VLAN_PARMS
] = { .type
= NL_A_UNSPEC
,
747 .min_len
= sizeof(struct tc_vlan
),
748 .optional
= false, },
749 [TCA_VLAN_PUSH_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
750 [TCA_VLAN_PUSH_VLAN_PROTOCOL
] = { .type
= NL_A_U16
, .optional
= true, },
751 [TCA_VLAN_PUSH_VLAN_PRIORITY
] = { .type
= NL_A_U8
, .optional
= true, },
755 nl_parse_act_vlan(struct nlattr
*options
, struct tc_flower
*flower
)
757 struct nlattr
*vlan_attrs
[ARRAY_SIZE(vlan_policy
)];
758 const struct tc_vlan
*v
;
759 const struct nlattr
*vlan_parms
;
760 struct tc_action
*action
;
762 if (!nl_parse_nested(options
, vlan_policy
, vlan_attrs
,
763 ARRAY_SIZE(vlan_policy
))) {
764 VLOG_ERR_RL(&error_rl
, "failed to parse vlan action options");
768 action
= &flower
->actions
[flower
->action_count
++];
769 vlan_parms
= vlan_attrs
[TCA_VLAN_PARMS
];
770 v
= nl_attr_get_unspec(vlan_parms
, sizeof *v
);
771 if (v
->v_action
== TCA_VLAN_ACT_PUSH
) {
772 struct nlattr
*vlan_id
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_ID
];
773 struct nlattr
*vlan_prio
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_PRIORITY
];
775 action
->vlan
.vlan_push_id
= nl_attr_get_u16(vlan_id
);
776 action
->vlan
.vlan_push_prio
= vlan_prio
? nl_attr_get_u8(vlan_prio
) : 0;
777 action
->type
= TC_ACT_VLAN_PUSH
;
778 } else if (v
->v_action
== TCA_VLAN_ACT_POP
) {
779 action
->type
= TC_ACT_VLAN_POP
;
781 VLOG_ERR_RL(&error_rl
, "unknown vlan action: %d, %d",
782 v
->action
, v
->v_action
);
788 static const struct nl_policy csum_policy
[] = {
789 [TCA_CSUM_PARMS
] = { .type
= NL_A_UNSPEC
,
790 .min_len
= sizeof(struct tc_csum
),
791 .optional
= false, },
795 nl_parse_act_csum(struct nlattr
*options
, struct tc_flower
*flower
)
797 struct nlattr
*csum_attrs
[ARRAY_SIZE(csum_policy
)];
798 const struct tc_csum
*c
;
799 const struct nlattr
*csum_parms
;
801 if (!nl_parse_nested(options
, csum_policy
, csum_attrs
,
802 ARRAY_SIZE(csum_policy
))) {
803 VLOG_ERR_RL(&error_rl
, "failed to parse csum action options");
807 csum_parms
= csum_attrs
[TCA_CSUM_PARMS
];
808 c
= nl_attr_get_unspec(csum_parms
, sizeof *c
);
811 if (c
->update_flags
!= flower
->csum_update_flags
) {
812 VLOG_WARN_RL(&error_rl
,
813 "expected different act csum flags: 0x%x != 0x%x",
814 flower
->csum_update_flags
, c
->update_flags
);
817 flower
->csum_update_flags
= 0; /* so we know csum was handled */
819 if (flower
->needs_full_ip_proto_mask
820 && flower
->mask
.ip_proto
!= UINT8_MAX
) {
821 VLOG_WARN_RL(&error_rl
, "expected full matching on flower ip_proto");
828 static const struct nl_policy act_policy
[] = {
829 [TCA_ACT_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
830 [TCA_ACT_COOKIE
] = { .type
= NL_A_UNSPEC
, .optional
= true, },
831 [TCA_ACT_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
832 [TCA_ACT_STATS
] = { .type
= NL_A_NESTED
, .optional
= false, },
835 static const struct nl_policy stats_policy
[] = {
836 [TCA_STATS_BASIC
] = { .type
= NL_A_UNSPEC
,
837 .min_len
= sizeof(struct gnet_stats_basic
),
838 .optional
= false, },
842 nl_parse_single_action(struct nlattr
*action
, struct tc_flower
*flower
)
844 struct nlattr
*act_options
;
845 struct nlattr
*act_stats
;
846 struct nlattr
*act_cookie
;
847 const char *act_kind
;
848 struct nlattr
*action_attrs
[ARRAY_SIZE(act_policy
)];
849 struct nlattr
*stats_attrs
[ARRAY_SIZE(stats_policy
)];
850 struct ovs_flow_stats
*stats
= &flower
->stats
;
851 const struct gnet_stats_basic
*bs
;
854 if (!nl_parse_nested(action
, act_policy
, action_attrs
,
855 ARRAY_SIZE(act_policy
))) {
856 VLOG_ERR_RL(&error_rl
, "failed to parse single action options");
860 act_kind
= nl_attr_get_string(action_attrs
[TCA_ACT_KIND
]);
861 act_options
= action_attrs
[TCA_ACT_OPTIONS
];
862 act_cookie
= action_attrs
[TCA_ACT_COOKIE
];
864 if (!strcmp(act_kind
, "gact")) {
865 err
= nl_parse_act_drop(act_options
, flower
);
866 } else if (!strcmp(act_kind
, "mirred")) {
867 err
= nl_parse_act_mirred(act_options
, flower
);
868 } else if (!strcmp(act_kind
, "vlan")) {
869 err
= nl_parse_act_vlan(act_options
, flower
);
870 } else if (!strcmp(act_kind
, "tunnel_key")) {
871 err
= nl_parse_act_tunnel_key(act_options
, flower
);
872 } else if (!strcmp(act_kind
, "pedit")) {
873 err
= nl_parse_act_pedit(act_options
, flower
);
874 } else if (!strcmp(act_kind
, "csum")) {
875 nl_parse_act_csum(act_options
, flower
);
877 VLOG_ERR_RL(&error_rl
, "unknown tc action kind: %s", act_kind
);
886 flower
->act_cookie
.data
= nl_attr_get(act_cookie
);
887 flower
->act_cookie
.len
= nl_attr_get_size(act_cookie
);
890 act_stats
= action_attrs
[TCA_ACT_STATS
];
892 if (!nl_parse_nested(act_stats
, stats_policy
, stats_attrs
,
893 ARRAY_SIZE(stats_policy
))) {
894 VLOG_ERR_RL(&error_rl
, "failed to parse action stats policy");
898 bs
= nl_attr_get_unspec(stats_attrs
[TCA_STATS_BASIC
], sizeof *bs
);
899 put_32aligned_u64(&stats
->n_packets
, bs
->packets
);
900 put_32aligned_u64(&stats
->n_bytes
, bs
->bytes
);
905 #define TCA_ACT_MIN_PRIO 1
908 nl_parse_flower_actions(struct nlattr
**attrs
, struct tc_flower
*flower
)
910 const struct nlattr
*actions
= attrs
[TCA_FLOWER_ACT
];
911 static struct nl_policy actions_orders_policy
[TCA_ACT_MAX_PRIO
+ 1] = {};
912 struct nlattr
*actions_orders
[ARRAY_SIZE(actions_orders_policy
)];
913 const int max_size
= ARRAY_SIZE(actions_orders_policy
);
915 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
916 actions_orders_policy
[i
].type
= NL_A_NESTED
;
917 actions_orders_policy
[i
].optional
= true;
920 if (!nl_parse_nested(actions
, actions_orders_policy
, actions_orders
,
921 ARRAY_SIZE(actions_orders_policy
))) {
922 VLOG_ERR_RL(&error_rl
, "failed to parse flower order of actions");
926 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
927 if (actions_orders
[i
]) {
930 if (flower
->action_count
>= TCA_ACT_MAX_PRIO
) {
931 VLOG_DBG_RL(&error_rl
, "Can only support %d actions", flower
->action_count
);
934 err
= nl_parse_single_action(actions_orders
[i
], flower
);
942 if (flower
->csum_update_flags
) {
943 VLOG_WARN_RL(&error_rl
,
944 "expected act csum with flags: 0x%x",
945 flower
->csum_update_flags
);
953 nl_parse_flower_options(struct nlattr
*nl_options
, struct tc_flower
*flower
)
955 struct nlattr
*attrs
[ARRAY_SIZE(tca_flower_policy
)];
957 if (!nl_parse_nested(nl_options
, tca_flower_policy
,
958 attrs
, ARRAY_SIZE(tca_flower_policy
))) {
959 VLOG_ERR_RL(&error_rl
, "failed to parse flower classifier options");
963 nl_parse_flower_eth(attrs
, flower
);
964 nl_parse_flower_vlan(attrs
, flower
);
965 nl_parse_flower_ip(attrs
, flower
);
966 nl_parse_flower_tunnel(attrs
, flower
);
967 nl_parse_flower_flags(attrs
, flower
);
968 return nl_parse_flower_actions(attrs
, flower
);
972 parse_netlink_to_tc_flower(struct ofpbuf
*reply
, struct tc_flower
*flower
)
975 struct nlattr
*ta
[ARRAY_SIZE(tca_policy
)];
978 if (NLMSG_HDRLEN
+ sizeof *tc
> reply
->size
) {
982 memset(flower
, 0, sizeof *flower
);
984 tc
= ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
985 flower
->handle
= tc
->tcm_handle
;
986 flower
->key
.eth_type
= (OVS_FORCE ovs_be16
) tc_get_minor(tc
->tcm_info
);
987 flower
->mask
.eth_type
= OVS_BE16_MAX
;
988 flower
->prio
= tc_get_major(tc
->tcm_info
);
990 if (!flower
->handle
) {
994 if (!nl_policy_parse(reply
, NLMSG_HDRLEN
+ sizeof *tc
,
995 tca_policy
, ta
, ARRAY_SIZE(ta
))) {
996 VLOG_ERR_RL(&error_rl
, "failed to parse tca policy");
1000 kind
= nl_attr_get_string(ta
[TCA_KIND
]);
1001 if (strcmp(kind
, "flower")) {
1002 VLOG_DBG_ONCE("Unsupported filter: %s", kind
);
1006 return nl_parse_flower_options(ta
[TCA_OPTIONS
], flower
);
1010 tc_dump_flower_start(int ifindex
, struct nl_dump
*dump
)
1012 struct ofpbuf request
;
1013 struct tcmsg
*tcmsg
;
1015 tcmsg
= tc_make_request(ifindex
, RTM_GETTFILTER
, NLM_F_DUMP
, &request
);
1016 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1017 tcmsg
->tcm_info
= TC_H_UNSPEC
;
1018 tcmsg
->tcm_handle
= 0;
1020 nl_dump_start(dump
, NETLINK_ROUTE
, &request
);
1021 ofpbuf_uninit(&request
);
1027 tc_flush(int ifindex
)
1029 struct ofpbuf request
;
1030 struct tcmsg
*tcmsg
;
1032 tcmsg
= tc_make_request(ifindex
, RTM_DELTFILTER
, NLM_F_ACK
, &request
);
1033 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1034 tcmsg
->tcm_info
= TC_H_UNSPEC
;
1036 return tc_transact(&request
, NULL
);
1040 tc_del_filter(int ifindex
, int prio
, int handle
)
1042 struct ofpbuf request
;
1043 struct tcmsg
*tcmsg
;
1044 struct ofpbuf
*reply
;
1047 tcmsg
= tc_make_request(ifindex
, RTM_DELTFILTER
, NLM_F_ECHO
, &request
);
1048 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1049 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
1050 tcmsg
->tcm_handle
= handle
;
1052 error
= tc_transact(&request
, &reply
);
1054 ofpbuf_delete(reply
);
1060 tc_get_flower(int ifindex
, int prio
, int handle
, struct tc_flower
*flower
)
1062 struct ofpbuf request
;
1063 struct tcmsg
*tcmsg
;
1064 struct ofpbuf
*reply
;
1067 tcmsg
= tc_make_request(ifindex
, RTM_GETTFILTER
, NLM_F_ECHO
, &request
);
1068 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1069 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
1070 tcmsg
->tcm_handle
= handle
;
1072 error
= tc_transact(&request
, &reply
);
1077 error
= parse_netlink_to_tc_flower(reply
, flower
);
1078 ofpbuf_delete(reply
);
1083 tc_get_tc_cls_policy(enum tc_offload_policy policy
)
1085 if (policy
== TC_POLICY_SKIP_HW
) {
1086 return TCA_CLS_FLAGS_SKIP_HW
;
1087 } else if (policy
== TC_POLICY_SKIP_SW
) {
1088 return TCA_CLS_FLAGS_SKIP_SW
;
1095 nl_msg_put_act_csum(struct ofpbuf
*request
, uint32_t flags
)
1099 nl_msg_put_string(request
, TCA_ACT_KIND
, "csum");
1100 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1102 struct tc_csum parm
= { .action
= TC_ACT_PIPE
,
1103 .update_flags
= flags
};
1105 nl_msg_put_unspec(request
, TCA_CSUM_PARMS
, &parm
, sizeof parm
);
1107 nl_msg_end_nested(request
, offset
);
1111 nl_msg_put_act_pedit(struct ofpbuf
*request
, struct tc_pedit
*parm
,
1112 struct tc_pedit_key_ex
*ex
)
1114 size_t ksize
= sizeof *parm
+ parm
->nkeys
* sizeof(struct tc_pedit_key
);
1115 size_t offset
, offset_keys_ex
, offset_key
;
1118 nl_msg_put_string(request
, TCA_ACT_KIND
, "pedit");
1119 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1121 parm
->action
= TC_ACT_PIPE
;
1123 nl_msg_put_unspec(request
, TCA_PEDIT_PARMS_EX
, parm
, ksize
);
1124 offset_keys_ex
= nl_msg_start_nested(request
, TCA_PEDIT_KEYS_EX
);
1125 for (i
= 0; i
< parm
->nkeys
; i
++, ex
++) {
1126 offset_key
= nl_msg_start_nested(request
, TCA_PEDIT_KEY_EX
);
1127 nl_msg_put_u16(request
, TCA_PEDIT_KEY_EX_HTYPE
, ex
->htype
);
1128 nl_msg_put_u16(request
, TCA_PEDIT_KEY_EX_CMD
, ex
->cmd
);
1129 nl_msg_end_nested(request
, offset_key
);
1131 nl_msg_end_nested(request
, offset_keys_ex
);
1133 nl_msg_end_nested(request
, offset
);
1137 nl_msg_put_act_push_vlan(struct ofpbuf
*request
, uint16_t vid
, uint8_t prio
)
1141 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
1142 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1144 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
1145 .v_action
= TCA_VLAN_ACT_PUSH
};
1147 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
1148 nl_msg_put_u16(request
, TCA_VLAN_PUSH_VLAN_ID
, vid
);
1149 nl_msg_put_u8(request
, TCA_VLAN_PUSH_VLAN_PRIORITY
, prio
);
1151 nl_msg_end_nested(request
, offset
);
1155 nl_msg_put_act_pop_vlan(struct ofpbuf
*request
)
1159 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
1160 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1162 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
1163 .v_action
= TCA_VLAN_ACT_POP
};
1165 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
1167 nl_msg_end_nested(request
, offset
);
1171 nl_msg_put_act_tunnel_key_release(struct ofpbuf
*request
)
1175 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
1176 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1178 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
1179 .t_action
= TCA_TUNNEL_KEY_ACT_RELEASE
};
1181 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
1183 nl_msg_end_nested(request
, offset
);
1187 nl_msg_put_act_tunnel_key_set(struct ofpbuf
*request
, ovs_be64 id
,
1188 ovs_be32 ipv4_src
, ovs_be32 ipv4_dst
,
1189 struct in6_addr
*ipv6_src
,
1190 struct in6_addr
*ipv6_dst
,
1195 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
1196 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1198 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
1199 .t_action
= TCA_TUNNEL_KEY_ACT_SET
};
1201 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
1203 ovs_be32 id32
= be64_to_be32(id
);
1204 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_KEY_ID
, id32
);
1206 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_SRC
, ipv4_src
);
1207 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_DST
, ipv4_dst
);
1208 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
1209 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_DST
,
1211 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_SRC
,
1214 nl_msg_put_be16(request
, TCA_TUNNEL_KEY_ENC_DST_PORT
, tp_dst
);
1216 nl_msg_end_nested(request
, offset
);
1220 nl_msg_put_act_drop(struct ofpbuf
*request
)
1224 nl_msg_put_string(request
, TCA_ACT_KIND
, "gact");
1225 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1227 struct tc_gact p
= { .action
= TC_ACT_SHOT
};
1229 nl_msg_put_unspec(request
, TCA_GACT_PARMS
, &p
, sizeof p
);
1231 nl_msg_end_nested(request
, offset
);
1235 nl_msg_put_act_mirred(struct ofpbuf
*request
, int ifindex
, int action
,
1240 nl_msg_put_string(request
, TCA_ACT_KIND
, "mirred");
1241 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1243 struct tc_mirred m
= { .action
= action
,
1245 .ifindex
= ifindex
};
1247 nl_msg_put_unspec(request
, TCA_MIRRED_PARMS
, &m
, sizeof m
);
1249 nl_msg_end_nested(request
, offset
);
1253 nl_msg_put_act_cookie(struct ofpbuf
*request
, struct tc_cookie
*ck
) {
1255 nl_msg_put_unspec(request
, TCA_ACT_COOKIE
, ck
->data
, ck
->len
);
1259 /* Given flower, a key_to_pedit map entry, calculates the rest,
1262 * mask, data - pointers of where read the first word of flower->key/mask.
1263 * current_offset - which offset to use for the first pedit action.
1264 * cnt - max pedits actions to use.
1265 * first_word_mask/last_word_mask - the mask to use for the first/last read
1266 * (as we read entire words). */
1268 calc_offsets(struct tc_flower
*flower
, struct flower_key_to_pedit
*m
,
1269 int *cur_offset
, int *cnt
, uint32_t *last_word_mask
,
1270 uint32_t *first_word_mask
, uint32_t **mask
, uint32_t **data
)
1272 int start_offset
, max_offset
, total_size
;
1273 int diff
, right_zero_bits
, left_zero_bits
;
1274 char *rewrite_key
= (void *) &flower
->rewrite
.key
;
1275 char *rewrite_mask
= (void *) &flower
->rewrite
.mask
;
1277 max_offset
= m
->offset
+ m
->size
;
1278 start_offset
= ROUND_DOWN(m
->offset
, 4);
1279 diff
= m
->offset
- start_offset
;
1280 total_size
= max_offset
- start_offset
;
1281 right_zero_bits
= 8 * (4 - (max_offset
% 4));
1282 left_zero_bits
= 8 * (m
->offset
- start_offset
);
1284 *cur_offset
= start_offset
;
1285 *cnt
= (total_size
/ 4) + (total_size
% 4 ? 1 : 0);
1286 *last_word_mask
= UINT32_MAX
>> right_zero_bits
;
1287 *first_word_mask
= UINT32_MAX
<< left_zero_bits
;
1288 *data
= (void *) (rewrite_key
+ m
->flower_offset
- diff
);
1289 *mask
= (void *) (rewrite_mask
+ m
->flower_offset
- diff
);
1293 csum_update_flag(struct tc_flower
*flower
,
1294 enum pedit_header_type htype
) {
1295 /* Explictily specifiy the csum flags so HW can return EOPNOTSUPP
1296 * if it doesn't support a checksum recalculation of some headers.
1297 * And since OVS allows a flow such as
1298 * eth(dst=<mac>),eth_type(0x0800) actions=set(ipv4(src=<new_ip>))
1299 * we need to force a more specific flow as this can, for example,
1300 * need a recalculation of icmp checksum if the packet that passes
1301 * is ICMPv6 and tcp checksum if its tcp. */
1304 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
:
1305 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_IPV4HDR
;
1307 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
:
1308 case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
:
1309 case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
:
1310 if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
1311 flower
->needs_full_ip_proto_mask
= true;
1312 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_TCP
;
1313 } else if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
1314 flower
->needs_full_ip_proto_mask
= true;
1315 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_UDP
;
1316 } else if (flower
->key
.ip_proto
== IPPROTO_ICMP
) {
1317 flower
->needs_full_ip_proto_mask
= true;
1318 } else if (flower
->key
.ip_proto
== IPPROTO_ICMPV6
) {
1319 flower
->needs_full_ip_proto_mask
= true;
1320 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_ICMP
;
1322 VLOG_WARN_RL(&error_rl
,
1323 "can't offload rewrite of IP/IPV6 with ip_proto: %d",
1324 flower
->key
.ip_proto
);
1328 case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
:
1329 return 0; /* success */
1331 case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK
:
1332 case __PEDIT_HDR_TYPE_MAX
:
1341 nl_msg_put_flower_rewrite_pedits(struct ofpbuf
*request
,
1342 struct tc_flower
*flower
)
1345 struct tc_pedit sel
;
1346 struct tc_pedit_key keys
[MAX_PEDIT_OFFSETS
];
1347 struct tc_pedit_key_ex keys_ex
[MAX_PEDIT_OFFSETS
];
1355 for (i
= 0; i
< ARRAY_SIZE(flower_pedit_map
); i
++) {
1356 struct flower_key_to_pedit
*m
= &flower_pedit_map
[i
];
1357 struct tc_pedit_key
*pedit_key
= NULL
;
1358 struct tc_pedit_key_ex
*pedit_key_ex
= NULL
;
1359 uint32_t *mask
, *data
, first_word_mask
, last_word_mask
;
1360 int cnt
= 0, cur_offset
= 0;
1366 calc_offsets(flower
, m
, &cur_offset
, &cnt
, &last_word_mask
,
1367 &first_word_mask
, &mask
, &data
);
1369 for (j
= 0; j
< cnt
; j
++, mask
++, data
++, cur_offset
+= 4) {
1370 uint32_t mask_word
= *mask
;
1373 mask_word
&= first_word_mask
;
1376 mask_word
&= last_word_mask
;
1381 if (sel
.sel
.nkeys
== MAX_PEDIT_OFFSETS
) {
1382 VLOG_WARN_RL(&error_rl
, "reached too many pedit offsets: %d",
1387 pedit_key
= &sel
.keys
[sel
.sel
.nkeys
];
1388 pedit_key_ex
= &sel
.keys_ex
[sel
.sel
.nkeys
];
1389 pedit_key_ex
->cmd
= TCA_PEDIT_KEY_EX_CMD_SET
;
1390 pedit_key_ex
->htype
= m
->htype
;
1391 pedit_key
->off
= cur_offset
;
1392 pedit_key
->mask
= ~mask_word
;
1393 pedit_key
->val
= *data
& mask_word
;
1396 err
= csum_update_flag(flower
, m
->htype
);
1401 if (flower
->needs_full_ip_proto_mask
) {
1402 flower
->mask
.ip_proto
= UINT8_MAX
;
1406 nl_msg_put_act_pedit(request
, &sel
.sel
, sel
.keys_ex
);
1412 nl_msg_put_flower_acts(struct ofpbuf
*request
, struct tc_flower
*flower
)
1416 uint16_t act_index
= 1;
1417 struct tc_action
*action
;
1420 offset
= nl_msg_start_nested(request
, TCA_FLOWER_ACT
);
1424 if (flower
->tunnel
.tunnel
) {
1425 act_offset
= nl_msg_start_nested(request
, act_index
++);
1426 nl_msg_put_act_tunnel_key_release(request
);
1427 nl_msg_end_nested(request
, act_offset
);
1430 action
= flower
->actions
;
1431 for (i
= 0; i
< flower
->action_count
; i
++, action
++) {
1432 switch (action
->type
) {
1433 case TC_ACT_PEDIT
: {
1434 act_offset
= nl_msg_start_nested(request
, act_index
++);
1435 error
= nl_msg_put_flower_rewrite_pedits(request
, flower
);
1439 nl_msg_end_nested(request
, act_offset
);
1441 if (flower
->csum_update_flags
) {
1442 act_offset
= nl_msg_start_nested(request
, act_index
++);
1443 nl_msg_put_act_csum(request
, flower
->csum_update_flags
);
1444 nl_msg_end_nested(request
, act_offset
);
1448 case TC_ACT_ENCAP
: {
1449 act_offset
= nl_msg_start_nested(request
, act_index
++);
1450 nl_msg_put_act_tunnel_key_set(request
, action
->encap
.id
,
1451 action
->encap
.ipv4
.ipv4_src
,
1452 action
->encap
.ipv4
.ipv4_dst
,
1453 &action
->encap
.ipv6
.ipv6_src
,
1454 &action
->encap
.ipv6
.ipv6_dst
,
1455 action
->encap
.tp_dst
);
1456 nl_msg_end_nested(request
, act_offset
);
1459 case TC_ACT_VLAN_POP
: {
1460 act_offset
= nl_msg_start_nested(request
, act_index
++);
1461 nl_msg_put_act_pop_vlan(request
);
1462 nl_msg_end_nested(request
, act_offset
);
1465 case TC_ACT_VLAN_PUSH
: {
1466 act_offset
= nl_msg_start_nested(request
, act_index
++);
1467 nl_msg_put_act_push_vlan(request
,
1468 action
->vlan
.vlan_push_id
,
1469 action
->vlan
.vlan_push_prio
);
1470 nl_msg_end_nested(request
, act_offset
);
1473 case TC_ACT_OUTPUT
: {
1474 ifindex
= action
->ifindex_out
;
1476 VLOG_ERR_RL(&error_rl
, "%s: invalid ifindex: %d, type: %d",
1477 __func__
, ifindex
, action
->type
);
1480 act_offset
= nl_msg_start_nested(request
, act_index
++);
1481 if (i
== flower
->action_count
- 1) {
1482 nl_msg_put_act_mirred(request
, ifindex
, TC_ACT_STOLEN
,
1485 nl_msg_put_act_mirred(request
, ifindex
, TC_ACT_PIPE
,
1488 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
1489 nl_msg_end_nested(request
, act_offset
);
1496 act_offset
= nl_msg_start_nested(request
, act_index
++);
1497 nl_msg_put_act_drop(request
);
1498 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
1499 nl_msg_end_nested(request
, act_offset
);
1501 nl_msg_end_nested(request
, offset
);
1507 nl_msg_put_masked_value(struct ofpbuf
*request
, uint16_t type
,
1508 uint16_t mask_type
, const void *data
,
1509 const void *mask_data
, size_t len
)
1511 if (mask_type
!= TCA_FLOWER_UNSPEC
) {
1512 if (is_all_zeros(mask_data
, len
)) {
1515 nl_msg_put_unspec(request
, mask_type
, mask_data
, len
);
1517 nl_msg_put_unspec(request
, type
, data
, len
);
1521 nl_msg_put_flower_tunnel(struct ofpbuf
*request
, struct tc_flower
*flower
)
1523 ovs_be32 ipv4_src
= flower
->tunnel
.ipv4
.ipv4_src
;
1524 ovs_be32 ipv4_dst
= flower
->tunnel
.ipv4
.ipv4_dst
;
1525 struct in6_addr
*ipv6_src
= &flower
->tunnel
.ipv6
.ipv6_src
;
1526 struct in6_addr
*ipv6_dst
= &flower
->tunnel
.ipv6
.ipv6_dst
;
1527 ovs_be16 tp_dst
= flower
->tunnel
.tp_dst
;
1528 ovs_be32 id
= be64_to_be32(flower
->tunnel
.id
);
1530 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_KEY_ID
, id
);
1532 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_SRC
, ipv4_src
);
1533 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_DST
, ipv4_dst
);
1534 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
1535 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_SRC
, ipv6_src
);
1536 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_DST
, ipv6_dst
);
1538 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ENC_UDP_DST_PORT
, tp_dst
);
1541 #define FLOWER_PUT_MASKED_VALUE(member, type) \
1542 nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \
1543 &flower->mask.member, sizeof flower->key.member)
1546 nl_msg_put_flower_options(struct ofpbuf
*request
, struct tc_flower
*flower
)
1549 uint16_t host_eth_type
= ntohs(flower
->key
.eth_type
);
1550 bool is_vlan
= (host_eth_type
== ETH_TYPE_VLAN
);
1553 /* need to parse acts first as some acts require changing the matching
1554 * see csum_update_flag() */
1555 err
= nl_msg_put_flower_acts(request
, flower
);
1561 host_eth_type
= ntohs(flower
->key
.encap_eth_type
);
1564 FLOWER_PUT_MASKED_VALUE(dst_mac
, TCA_FLOWER_KEY_ETH_DST
);
1565 FLOWER_PUT_MASKED_VALUE(src_mac
, TCA_FLOWER_KEY_ETH_SRC
);
1567 if (host_eth_type
== ETH_P_IP
|| host_eth_type
== ETH_P_IPV6
) {
1568 if (flower
->mask
.ip_proto
&& flower
->key
.ip_proto
) {
1569 nl_msg_put_u8(request
, TCA_FLOWER_KEY_IP_PROTO
,
1570 flower
->key
.ip_proto
);
1573 if (flower
->mask
.flags
) {
1574 nl_msg_put_be32(request
, TCA_FLOWER_KEY_FLAGS
,
1575 htonl(flower
->key
.flags
));
1576 nl_msg_put_be32(request
, TCA_FLOWER_KEY_FLAGS_MASK
,
1577 htonl(flower
->mask
.flags
));
1580 if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
1581 FLOWER_PUT_MASKED_VALUE(udp_src
, TCA_FLOWER_KEY_UDP_SRC
);
1582 FLOWER_PUT_MASKED_VALUE(udp_dst
, TCA_FLOWER_KEY_UDP_DST
);
1583 } else if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
1584 FLOWER_PUT_MASKED_VALUE(tcp_src
, TCA_FLOWER_KEY_TCP_SRC
);
1585 FLOWER_PUT_MASKED_VALUE(tcp_dst
, TCA_FLOWER_KEY_TCP_DST
);
1586 FLOWER_PUT_MASKED_VALUE(tcp_flags
, TCA_FLOWER_KEY_TCP_FLAGS
);
1587 } else if (flower
->key
.ip_proto
== IPPROTO_SCTP
) {
1588 FLOWER_PUT_MASKED_VALUE(sctp_src
, TCA_FLOWER_KEY_SCTP_SRC
);
1589 FLOWER_PUT_MASKED_VALUE(sctp_dst
, TCA_FLOWER_KEY_SCTP_DST
);
1593 if (host_eth_type
== ETH_P_IP
) {
1594 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_src
, TCA_FLOWER_KEY_IPV4_SRC
);
1595 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_dst
, TCA_FLOWER_KEY_IPV4_DST
);
1596 FLOWER_PUT_MASKED_VALUE(ip_ttl
, TCA_FLOWER_KEY_IP_TTL
);
1597 } else if (host_eth_type
== ETH_P_IPV6
) {
1598 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_src
, TCA_FLOWER_KEY_IPV6_SRC
);
1599 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_dst
, TCA_FLOWER_KEY_IPV6_DST
);
1602 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ETH_TYPE
, flower
->key
.eth_type
);
1605 if (flower
->key
.vlan_id
|| flower
->key
.vlan_prio
) {
1606 nl_msg_put_u16(request
, TCA_FLOWER_KEY_VLAN_ID
,
1607 flower
->key
.vlan_id
);
1608 nl_msg_put_u8(request
, TCA_FLOWER_KEY_VLAN_PRIO
,
1609 flower
->key
.vlan_prio
);
1611 if (flower
->key
.encap_eth_type
) {
1612 nl_msg_put_be16(request
, TCA_FLOWER_KEY_VLAN_ETH_TYPE
,
1613 flower
->key
.encap_eth_type
);
1617 nl_msg_put_u32(request
, TCA_FLOWER_FLAGS
, tc_get_tc_cls_policy(tc_policy
));
1619 if (flower
->tunnel
.tunnel
) {
1620 nl_msg_put_flower_tunnel(request
, flower
);
1627 tc_replace_flower(int ifindex
, uint16_t prio
, uint32_t handle
,
1628 struct tc_flower
*flower
)
1630 struct ofpbuf request
;
1631 struct tcmsg
*tcmsg
;
1632 struct ofpbuf
*reply
;
1634 size_t basic_offset
;
1635 uint16_t eth_type
= (OVS_FORCE
uint16_t) flower
->key
.eth_type
;
1637 tcmsg
= tc_make_request(ifindex
, RTM_NEWTFILTER
,
1638 NLM_F_CREATE
| NLM_F_ECHO
, &request
);
1639 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1640 tcmsg
->tcm_info
= tc_make_handle(prio
, eth_type
);
1641 tcmsg
->tcm_handle
= handle
;
1643 nl_msg_put_string(&request
, TCA_KIND
, "flower");
1644 basic_offset
= nl_msg_start_nested(&request
, TCA_OPTIONS
);
1646 error
= nl_msg_put_flower_options(&request
, flower
);
1649 ofpbuf_uninit(&request
);
1653 nl_msg_end_nested(&request
, basic_offset
);
1655 error
= tc_transact(&request
, &reply
);
1658 ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
1660 flower
->prio
= tc_get_major(tc
->tcm_info
);
1661 flower
->handle
= tc
->tcm_handle
;
1662 ofpbuf_delete(reply
);
1669 tc_set_policy(const char *policy
)
1675 if (!strcmp(policy
, "skip_sw")) {
1676 tc_policy
= TC_POLICY_SKIP_SW
;
1677 } else if (!strcmp(policy
, "skip_hw")) {
1678 tc_policy
= TC_POLICY_SKIP_HW
;
1679 } else if (!strcmp(policy
, "none")) {
1680 tc_policy
= TC_POLICY_NONE
;
1682 VLOG_WARN("tc: Invalid policy '%s'", policy
);
1686 VLOG_INFO("tc: Using policy '%s'", policy
);