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_IP_TTL
] = { .type
= NL_A_U8
,
286 [TCA_FLOWER_KEY_IP_TTL_MASK
] = { .type
= NL_A_U8
,
288 [TCA_FLOWER_KEY_TCP_FLAGS
] = { .type
= NL_A_U16
,
290 [TCA_FLOWER_KEY_TCP_FLAGS_MASK
] = { .type
= NL_A_U16
,
295 nl_parse_flower_eth(struct nlattr
**attrs
, struct tc_flower
*flower
)
297 const struct eth_addr
*eth
;
299 if (attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
]) {
300 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC
], ETH_ALEN
);
301 memcpy(&flower
->key
.src_mac
, eth
, sizeof flower
->key
.src_mac
);
303 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
], ETH_ALEN
);
304 memcpy(&flower
->mask
.src_mac
, eth
, sizeof flower
->mask
.src_mac
);
306 if (attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
]) {
307 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST
], ETH_ALEN
);
308 memcpy(&flower
->key
.dst_mac
, eth
, sizeof flower
->key
.dst_mac
);
310 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
], ETH_ALEN
);
311 memcpy(&flower
->mask
.dst_mac
, eth
, sizeof flower
->mask
.dst_mac
);
316 nl_parse_flower_vlan(struct nlattr
**attrs
, struct tc_flower
*flower
)
318 if (flower
->key
.eth_type
!= htons(ETH_TYPE_VLAN
)) {
322 flower
->key
.encap_eth_type
=
323 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ETH_TYPE
]);
325 if (attrs
[TCA_FLOWER_KEY_VLAN_ID
]) {
326 flower
->key
.vlan_id
=
327 nl_attr_get_u16(attrs
[TCA_FLOWER_KEY_VLAN_ID
]);
329 if (attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]) {
330 flower
->key
.vlan_prio
=
331 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]);
336 nl_parse_flower_tunnel(struct nlattr
**attrs
, struct tc_flower
*flower
)
338 if (attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]) {
339 ovs_be32 id
= nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]);
341 flower
->tunnel
.id
= be32_to_be64(id
);
343 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
]) {
344 flower
->tunnel
.ipv4
.ipv4_src
=
345 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC
]);
347 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
]) {
348 flower
->tunnel
.ipv4
.ipv4_dst
=
349 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST
]);
351 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
]) {
352 flower
->tunnel
.ipv6
.ipv6_src
=
353 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC
]);
355 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
]) {
356 flower
->tunnel
.ipv6
.ipv6_dst
=
357 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST
]);
359 if (attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]) {
360 flower
->tunnel
.tp_dst
=
361 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]);
366 nl_parse_flower_ip(struct nlattr
**attrs
, struct tc_flower
*flower
) {
367 uint8_t ip_proto
= 0;
368 struct tc_flower_key
*key
= &flower
->key
;
369 struct tc_flower_key
*mask
= &flower
->mask
;
371 if (attrs
[TCA_FLOWER_KEY_IP_PROTO
]) {
372 ip_proto
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_PROTO
]);
373 key
->ip_proto
= ip_proto
;
374 mask
->ip_proto
= UINT8_MAX
;
377 if (attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]) {
379 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC
]);
380 mask
->ipv4
.ipv4_src
=
381 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]);
383 if (attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]) {
385 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST
]);
386 mask
->ipv4
.ipv4_dst
=
387 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]);
389 if (attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
]) {
390 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_SRC
];
391 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
];
393 key
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr
);
394 mask
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr_mask
);
396 if (attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
]) {
397 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_DST
];
398 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
];
400 key
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr
);
401 mask
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr_mask
);
404 if (ip_proto
== IPPROTO_TCP
) {
405 if (attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]) {
407 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC
]);
409 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]);
411 if (attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]) {
413 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST
]);
415 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]);
417 if (attrs
[TCA_FLOWER_KEY_TCP_FLAGS_MASK
]) {
419 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_FLAGS
]);
421 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_FLAGS_MASK
]);
423 } else if (ip_proto
== IPPROTO_UDP
) {
424 if (attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]) {
425 key
->udp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC
]);
427 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]);
429 if (attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]) {
430 key
->udp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST
]);
432 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]);
434 } else if (ip_proto
== IPPROTO_SCTP
) {
435 if (attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]) {
436 key
->sctp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC
]);
438 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]);
440 if (attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]) {
441 key
->sctp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST
]);
443 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]);
447 if (attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]) {
448 key
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL
]);
449 mask
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]);
453 static const struct nl_policy pedit_policy
[] = {
454 [TCA_PEDIT_PARMS_EX
] = { .type
= NL_A_UNSPEC
,
455 .min_len
= sizeof(struct tc_pedit
),
456 .optional
= false, },
457 [TCA_PEDIT_KEYS_EX
] = { .type
= NL_A_NESTED
,
458 .optional
= false, },
462 nl_parse_act_pedit(struct nlattr
*options
, struct tc_flower
*flower
)
464 struct nlattr
*pe_attrs
[ARRAY_SIZE(pedit_policy
)];
465 const struct tc_pedit
*pe
;
466 const struct tc_pedit_key
*keys
;
467 const struct nlattr
*nla
, *keys_ex
, *ex_type
;
468 const void *keys_attr
;
469 char *rewrite_key
= (void *) &flower
->rewrite
.key
;
470 char *rewrite_mask
= (void *) &flower
->rewrite
.mask
;
471 size_t keys_ex_size
, left
;
472 int type
, i
= 0, err
;
474 if (!nl_parse_nested(options
, pedit_policy
, pe_attrs
,
475 ARRAY_SIZE(pedit_policy
))) {
476 VLOG_ERR_RL(&error_rl
, "failed to parse pedit action options");
480 pe
= nl_attr_get_unspec(pe_attrs
[TCA_PEDIT_PARMS_EX
], sizeof *pe
);
482 keys_attr
= pe_attrs
[TCA_PEDIT_KEYS_EX
];
483 keys_ex
= nl_attr_get(keys_attr
);
484 keys_ex_size
= nl_attr_get_size(keys_attr
);
486 NL_ATTR_FOR_EACH (nla
, left
, keys_ex
, keys_ex_size
) {
487 if (i
>= pe
->nkeys
) {
491 if (nl_attr_type(nla
) != TCA_PEDIT_KEY_EX
) {
492 VLOG_ERR_RL(&error_rl
, "unable to parse legacy pedit type: %d",
497 ex_type
= nl_attr_find_nested(nla
, TCA_PEDIT_KEY_EX_HTYPE
);
498 type
= nl_attr_get_u16(ex_type
);
500 err
= csum_update_flag(flower
, type
);
505 for (int j
= 0; j
< ARRAY_SIZE(flower_pedit_map
); j
++) {
506 struct flower_key_to_pedit
*m
= &flower_pedit_map
[j
];
507 int flower_off
= m
->flower_offset
;
511 if (m
->htype
!= type
) {
515 /* check overlap between current pedit key, which is always
516 * 4 bytes (range [off, off + 3]), and a map entry in
517 * flower_pedit_map (range [mf, mf + sz - 1]) */
518 if ((keys
->off
>= mf
&& keys
->off
< mf
+ sz
)
519 || (keys
->off
+ 3 >= mf
&& keys
->off
+ 3 < mf
+ sz
)) {
520 int diff
= flower_off
+ (keys
->off
- mf
);
521 uint32_t *dst
= (void *) (rewrite_key
+ diff
);
522 uint32_t *dst_m
= (void *) (rewrite_mask
+ diff
);
523 uint32_t mask
= ~(keys
->mask
);
526 if (keys
->off
< mf
) {
527 zero_bits
= 8 * (mf
- keys
->off
);
528 mask
&= UINT32_MAX
<< zero_bits
;
529 } else if (keys
->off
+ 4 > mf
+ m
->size
) {
530 zero_bits
= 8 * (keys
->off
+ 4 - mf
- m
->size
);
531 mask
&= UINT32_MAX
>> zero_bits
;
535 *dst
|= keys
->val
& mask
;
543 flower
->rewrite
.rewrite
= true;
548 static const struct nl_policy tunnel_key_policy
[] = {
549 [TCA_TUNNEL_KEY_PARMS
] = { .type
= NL_A_UNSPEC
,
550 .min_len
= sizeof(struct tc_tunnel_key
),
551 .optional
= false, },
552 [TCA_TUNNEL_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
553 [TCA_TUNNEL_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
554 [TCA_TUNNEL_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
555 .min_len
= sizeof(struct in6_addr
),
557 [TCA_TUNNEL_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
558 .min_len
= sizeof(struct in6_addr
),
560 [TCA_TUNNEL_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
561 [TCA_TUNNEL_KEY_ENC_DST_PORT
] = { .type
= NL_A_U16
, .optional
= true, },
565 nl_parse_act_tunnel_key(struct nlattr
*options
, struct tc_flower
*flower
)
567 struct nlattr
*tun_attrs
[ARRAY_SIZE(tunnel_key_policy
)];
568 const struct nlattr
*tun_parms
;
569 const struct tc_tunnel_key
*tun
;
571 if (!nl_parse_nested(options
, tunnel_key_policy
, tun_attrs
,
572 ARRAY_SIZE(tunnel_key_policy
))) {
573 VLOG_ERR_RL(&error_rl
, "failed to parse tunnel_key action options");
577 tun_parms
= tun_attrs
[TCA_TUNNEL_KEY_PARMS
];
578 tun
= nl_attr_get_unspec(tun_parms
, sizeof *tun
);
579 if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_SET
) {
580 struct nlattr
*id
= tun_attrs
[TCA_TUNNEL_KEY_ENC_KEY_ID
];
581 struct nlattr
*dst_port
= tun_attrs
[TCA_TUNNEL_KEY_ENC_DST_PORT
];
582 struct nlattr
*ipv4_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_SRC
];
583 struct nlattr
*ipv4_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_DST
];
584 struct nlattr
*ipv6_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_SRC
];
585 struct nlattr
*ipv6_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_DST
];
587 flower
->set
.set
= true;
588 flower
->set
.ipv4
.ipv4_src
= ipv4_src
? nl_attr_get_be32(ipv4_src
) : 0;
589 flower
->set
.ipv4
.ipv4_dst
= ipv4_dst
? nl_attr_get_be32(ipv4_dst
) : 0;
591 flower
->set
.ipv6
.ipv6_src
= nl_attr_get_in6_addr(ipv6_src
);
594 flower
->set
.ipv6
.ipv6_dst
= nl_attr_get_in6_addr(ipv6_dst
);
596 flower
->set
.id
= id
? be32_to_be64(nl_attr_get_be32(id
)) : 0;
597 flower
->set
.tp_dst
= dst_port
? nl_attr_get_be16(dst_port
) : 0;
598 } else if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_RELEASE
) {
599 flower
->tunnel
.tunnel
= true;
601 VLOG_ERR_RL(&error_rl
, "unknown tunnel actions: %d, %d",
602 tun
->action
, tun
->t_action
);
608 static const struct nl_policy gact_policy
[] = {
609 [TCA_GACT_PARMS
] = { .type
= NL_A_UNSPEC
,
610 .min_len
= sizeof(struct tc_gact
),
611 .optional
= false, },
612 [TCA_GACT_TM
] = { .type
= NL_A_UNSPEC
,
613 .min_len
= sizeof(struct tcf_t
),
614 .optional
= false, },
620 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
621 static int user_hz
= 100;
623 if (ovsthread_once_start(&once
)) {
624 user_hz
= sysconf(_SC_CLK_TCK
);
625 ovsthread_once_done(&once
);
632 nl_parse_tcf(const struct tcf_t
*tm
, struct tc_flower
*flower
)
634 flower
->lastused
= time_msec() - (tm
->lastuse
* 1000 / get_user_hz());
638 nl_parse_act_drop(struct nlattr
*options
, struct tc_flower
*flower
)
640 struct nlattr
*gact_attrs
[ARRAY_SIZE(gact_policy
)];
641 const struct tc_gact
*p
;
642 struct nlattr
*gact_parms
;
643 const struct tcf_t
*tm
;
645 if (!nl_parse_nested(options
, gact_policy
, gact_attrs
,
646 ARRAY_SIZE(gact_policy
))) {
647 VLOG_ERR_RL(&error_rl
, "failed to parse gact action options");
651 gact_parms
= gact_attrs
[TCA_GACT_PARMS
];
652 p
= nl_attr_get_unspec(gact_parms
, sizeof *p
);
654 if (p
->action
!= TC_ACT_SHOT
) {
655 VLOG_ERR_RL(&error_rl
, "unknown gact action: %d", p
->action
);
659 tm
= nl_attr_get_unspec(gact_attrs
[TCA_GACT_TM
], sizeof *tm
);
660 nl_parse_tcf(tm
, flower
);
665 static const struct nl_policy mirred_policy
[] = {
666 [TCA_MIRRED_PARMS
] = { .type
= NL_A_UNSPEC
,
667 .min_len
= sizeof(struct tc_mirred
),
668 .optional
= false, },
669 [TCA_MIRRED_TM
] = { .type
= NL_A_UNSPEC
,
670 .min_len
= sizeof(struct tcf_t
),
671 .optional
= false, },
675 nl_parse_act_mirred(struct nlattr
*options
, struct tc_flower
*flower
)
678 struct nlattr
*mirred_attrs
[ARRAY_SIZE(mirred_policy
)];
679 const struct tc_mirred
*m
;
680 const struct nlattr
*mirred_parms
;
681 const struct tcf_t
*tm
;
682 struct nlattr
*mirred_tm
;
684 if (!nl_parse_nested(options
, mirred_policy
, mirred_attrs
,
685 ARRAY_SIZE(mirred_policy
))) {
686 VLOG_ERR_RL(&error_rl
, "failed to parse mirred action options");
690 mirred_parms
= mirred_attrs
[TCA_MIRRED_PARMS
];
691 m
= nl_attr_get_unspec(mirred_parms
, sizeof *m
);
693 if (m
->action
!= TC_ACT_STOLEN
|| m
->eaction
!= TCA_EGRESS_REDIR
) {
694 VLOG_ERR_RL(&error_rl
, "unknown mirred action: %d, %d, %d",
695 m
->action
, m
->eaction
, m
->ifindex
);
699 flower
->ifindex_out
= m
->ifindex
;
701 mirred_tm
= mirred_attrs
[TCA_MIRRED_TM
];
702 tm
= nl_attr_get_unspec(mirred_tm
, sizeof *tm
);
703 nl_parse_tcf(tm
, flower
);
708 static const struct nl_policy vlan_policy
[] = {
709 [TCA_VLAN_PARMS
] = { .type
= NL_A_UNSPEC
,
710 .min_len
= sizeof(struct tc_vlan
),
711 .optional
= false, },
712 [TCA_VLAN_PUSH_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
713 [TCA_VLAN_PUSH_VLAN_PROTOCOL
] = { .type
= NL_A_U16
, .optional
= true, },
714 [TCA_VLAN_PUSH_VLAN_PRIORITY
] = { .type
= NL_A_U8
, .optional
= true, },
718 nl_parse_act_vlan(struct nlattr
*options
, struct tc_flower
*flower
)
720 struct nlattr
*vlan_attrs
[ARRAY_SIZE(vlan_policy
)];
721 const struct tc_vlan
*v
;
722 const struct nlattr
*vlan_parms
;
724 if (!nl_parse_nested(options
, vlan_policy
, vlan_attrs
,
725 ARRAY_SIZE(vlan_policy
))) {
726 VLOG_ERR_RL(&error_rl
, "failed to parse vlan action options");
730 vlan_parms
= vlan_attrs
[TCA_VLAN_PARMS
];
731 v
= nl_attr_get_unspec(vlan_parms
, sizeof *v
);
732 if (v
->v_action
== TCA_VLAN_ACT_PUSH
) {
733 struct nlattr
*vlan_id
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_ID
];
734 struct nlattr
*vlan_prio
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_PRIORITY
];
736 flower
->vlan_push_id
= nl_attr_get_u16(vlan_id
);
737 flower
->vlan_push_prio
= vlan_prio
? nl_attr_get_u8(vlan_prio
) : 0;
738 } else if (v
->v_action
== TCA_VLAN_ACT_POP
) {
739 flower
->vlan_pop
= 1;
741 VLOG_ERR_RL(&error_rl
, "unknown vlan action: %d, %d",
742 v
->action
, v
->v_action
);
748 static const struct nl_policy csum_policy
[] = {
749 [TCA_CSUM_PARMS
] = { .type
= NL_A_UNSPEC
,
750 .min_len
= sizeof(struct tc_csum
),
751 .optional
= false, },
755 nl_parse_act_csum(struct nlattr
*options
, struct tc_flower
*flower
)
757 struct nlattr
*csum_attrs
[ARRAY_SIZE(csum_policy
)];
758 const struct tc_csum
*c
;
759 const struct nlattr
*csum_parms
;
761 if (!nl_parse_nested(options
, csum_policy
, csum_attrs
,
762 ARRAY_SIZE(csum_policy
))) {
763 VLOG_ERR_RL(&error_rl
, "failed to parse csum action options");
767 csum_parms
= csum_attrs
[TCA_CSUM_PARMS
];
768 c
= nl_attr_get_unspec(csum_parms
, sizeof *c
);
771 if (c
->update_flags
!= flower
->csum_update_flags
) {
772 VLOG_WARN_RL(&error_rl
,
773 "expected different act csum flags: 0x%x != 0x%x",
774 flower
->csum_update_flags
, c
->update_flags
);
777 flower
->csum_update_flags
= 0; /* so we know csum was handled */
779 if (flower
->needs_full_ip_proto_mask
780 && flower
->mask
.ip_proto
!= UINT8_MAX
) {
781 VLOG_WARN_RL(&error_rl
, "expected full matching on flower ip_proto");
788 static const struct nl_policy act_policy
[] = {
789 [TCA_ACT_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
790 [TCA_ACT_COOKIE
] = { .type
= NL_A_UNSPEC
, .optional
= true, },
791 [TCA_ACT_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
792 [TCA_ACT_STATS
] = { .type
= NL_A_NESTED
, .optional
= false, },
795 static const struct nl_policy stats_policy
[] = {
796 [TCA_STATS_BASIC
] = { .type
= NL_A_UNSPEC
,
797 .min_len
= sizeof(struct gnet_stats_basic
),
798 .optional
= false, },
802 nl_parse_single_action(struct nlattr
*action
, struct tc_flower
*flower
)
804 struct nlattr
*act_options
;
805 struct nlattr
*act_stats
;
806 struct nlattr
*act_cookie
;
807 const char *act_kind
;
808 struct nlattr
*action_attrs
[ARRAY_SIZE(act_policy
)];
809 struct nlattr
*stats_attrs
[ARRAY_SIZE(stats_policy
)];
810 struct ovs_flow_stats
*stats
= &flower
->stats
;
811 const struct gnet_stats_basic
*bs
;
813 if (!nl_parse_nested(action
, act_policy
, action_attrs
,
814 ARRAY_SIZE(act_policy
))) {
815 VLOG_ERR_RL(&error_rl
, "failed to parse single action options");
819 act_kind
= nl_attr_get_string(action_attrs
[TCA_ACT_KIND
]);
820 act_options
= action_attrs
[TCA_ACT_OPTIONS
];
821 act_cookie
= action_attrs
[TCA_ACT_COOKIE
];
823 if (!strcmp(act_kind
, "gact")) {
824 nl_parse_act_drop(act_options
, flower
);
825 } else if (!strcmp(act_kind
, "mirred")) {
826 nl_parse_act_mirred(act_options
, flower
);
827 } else if (!strcmp(act_kind
, "vlan")) {
828 nl_parse_act_vlan(act_options
, flower
);
829 } else if (!strcmp(act_kind
, "tunnel_key")) {
830 nl_parse_act_tunnel_key(act_options
, flower
);
831 } else if (!strcmp(act_kind
, "pedit")) {
832 nl_parse_act_pedit(act_options
, flower
);
833 } else if (!strcmp(act_kind
, "csum")) {
834 nl_parse_act_csum(act_options
, flower
);
836 VLOG_ERR_RL(&error_rl
, "unknown tc action kind: %s", act_kind
);
841 flower
->act_cookie
.data
= nl_attr_get(act_cookie
);
842 flower
->act_cookie
.len
= nl_attr_get_size(act_cookie
);
845 act_stats
= action_attrs
[TCA_ACT_STATS
];
847 if (!nl_parse_nested(act_stats
, stats_policy
, stats_attrs
,
848 ARRAY_SIZE(stats_policy
))) {
849 VLOG_ERR_RL(&error_rl
, "failed to parse action stats policy");
853 bs
= nl_attr_get_unspec(stats_attrs
[TCA_STATS_BASIC
], sizeof *bs
);
854 put_32aligned_u64(&stats
->n_packets
, bs
->packets
);
855 put_32aligned_u64(&stats
->n_bytes
, bs
->bytes
);
860 #define TCA_ACT_MIN_PRIO 1
863 nl_parse_flower_actions(struct nlattr
**attrs
, struct tc_flower
*flower
)
865 const struct nlattr
*actions
= attrs
[TCA_FLOWER_ACT
];
866 static struct nl_policy actions_orders_policy
[TCA_ACT_MAX_PRIO
+ 1] = {};
867 struct nlattr
*actions_orders
[ARRAY_SIZE(actions_orders_policy
)];
868 const int max_size
= ARRAY_SIZE(actions_orders_policy
);
870 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
871 actions_orders_policy
[i
].type
= NL_A_NESTED
;
872 actions_orders_policy
[i
].optional
= true;
875 if (!nl_parse_nested(actions
, actions_orders_policy
, actions_orders
,
876 ARRAY_SIZE(actions_orders_policy
))) {
877 VLOG_ERR_RL(&error_rl
, "failed to parse flower order of actions");
881 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
882 if (actions_orders
[i
]) {
883 int err
= nl_parse_single_action(actions_orders
[i
], flower
);
891 if (flower
->csum_update_flags
) {
892 VLOG_WARN_RL(&error_rl
,
893 "expected act csum with flags: 0x%x",
894 flower
->csum_update_flags
);
902 nl_parse_flower_options(struct nlattr
*nl_options
, struct tc_flower
*flower
)
904 struct nlattr
*attrs
[ARRAY_SIZE(tca_flower_policy
)];
906 if (!nl_parse_nested(nl_options
, tca_flower_policy
,
907 attrs
, ARRAY_SIZE(tca_flower_policy
))) {
908 VLOG_ERR_RL(&error_rl
, "failed to parse flower classifier options");
912 nl_parse_flower_eth(attrs
, flower
);
913 nl_parse_flower_vlan(attrs
, flower
);
914 nl_parse_flower_ip(attrs
, flower
);
915 nl_parse_flower_tunnel(attrs
, flower
);
916 return nl_parse_flower_actions(attrs
, flower
);
920 parse_netlink_to_tc_flower(struct ofpbuf
*reply
, struct tc_flower
*flower
)
923 struct nlattr
*ta
[ARRAY_SIZE(tca_policy
)];
926 if (NLMSG_HDRLEN
+ sizeof *tc
> reply
->size
) {
930 memset(flower
, 0, sizeof *flower
);
932 tc
= ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
933 flower
->handle
= tc
->tcm_handle
;
934 flower
->key
.eth_type
= (OVS_FORCE ovs_be16
) tc_get_minor(tc
->tcm_info
);
935 flower
->mask
.eth_type
= OVS_BE16_MAX
;
936 flower
->prio
= tc_get_major(tc
->tcm_info
);
938 if (!flower
->handle
) {
942 if (!nl_policy_parse(reply
, NLMSG_HDRLEN
+ sizeof *tc
,
943 tca_policy
, ta
, ARRAY_SIZE(ta
))) {
944 VLOG_ERR_RL(&error_rl
, "failed to parse tca policy");
948 kind
= nl_attr_get_string(ta
[TCA_KIND
]);
949 if (strcmp(kind
, "flower")) {
950 VLOG_ERR_RL(&error_rl
, "failed to parse filter: %s", kind
);
954 return nl_parse_flower_options(ta
[TCA_OPTIONS
], flower
);
958 tc_dump_flower_start(int ifindex
, struct nl_dump
*dump
)
960 struct ofpbuf request
;
963 tcmsg
= tc_make_request(ifindex
, RTM_GETTFILTER
, NLM_F_DUMP
, &request
);
964 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
965 tcmsg
->tcm_info
= TC_H_UNSPEC
;
966 tcmsg
->tcm_handle
= 0;
968 nl_dump_start(dump
, NETLINK_ROUTE
, &request
);
969 ofpbuf_uninit(&request
);
975 tc_flush(int ifindex
)
977 struct ofpbuf request
;
980 tcmsg
= tc_make_request(ifindex
, RTM_DELTFILTER
, NLM_F_ACK
, &request
);
981 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
982 tcmsg
->tcm_info
= TC_H_UNSPEC
;
984 return tc_transact(&request
, NULL
);
988 tc_del_filter(int ifindex
, int prio
, int handle
)
990 struct ofpbuf request
;
992 struct ofpbuf
*reply
;
995 tcmsg
= tc_make_request(ifindex
, RTM_DELTFILTER
, NLM_F_ECHO
, &request
);
996 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
997 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
998 tcmsg
->tcm_handle
= handle
;
1000 error
= tc_transact(&request
, &reply
);
1002 ofpbuf_delete(reply
);
1008 tc_get_flower(int ifindex
, int prio
, int handle
, struct tc_flower
*flower
)
1010 struct ofpbuf request
;
1011 struct tcmsg
*tcmsg
;
1012 struct ofpbuf
*reply
;
1015 tcmsg
= tc_make_request(ifindex
, RTM_GETTFILTER
, NLM_F_ECHO
, &request
);
1016 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1017 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
1018 tcmsg
->tcm_handle
= handle
;
1020 error
= tc_transact(&request
, &reply
);
1025 error
= parse_netlink_to_tc_flower(reply
, flower
);
1026 ofpbuf_delete(reply
);
1031 tc_get_tc_cls_policy(enum tc_offload_policy policy
)
1033 if (policy
== TC_POLICY_SKIP_HW
) {
1034 return TCA_CLS_FLAGS_SKIP_HW
;
1035 } else if (policy
== TC_POLICY_SKIP_SW
) {
1036 return TCA_CLS_FLAGS_SKIP_SW
;
1043 nl_msg_put_act_csum(struct ofpbuf
*request
, uint32_t flags
)
1047 nl_msg_put_string(request
, TCA_ACT_KIND
, "csum");
1048 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1050 struct tc_csum parm
= { .action
= TC_ACT_PIPE
,
1051 .update_flags
= flags
};
1053 nl_msg_put_unspec(request
, TCA_CSUM_PARMS
, &parm
, sizeof parm
);
1055 nl_msg_end_nested(request
, offset
);
1059 nl_msg_put_act_pedit(struct ofpbuf
*request
, struct tc_pedit
*parm
,
1060 struct tc_pedit_key_ex
*ex
)
1062 size_t ksize
= sizeof *parm
+ parm
->nkeys
* sizeof(struct tc_pedit_key
);
1063 size_t offset
, offset_keys_ex
, offset_key
;
1066 nl_msg_put_string(request
, TCA_ACT_KIND
, "pedit");
1067 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1069 parm
->action
= TC_ACT_PIPE
;
1071 nl_msg_put_unspec(request
, TCA_PEDIT_PARMS_EX
, parm
, ksize
);
1072 offset_keys_ex
= nl_msg_start_nested(request
, TCA_PEDIT_KEYS_EX
);
1073 for (i
= 0; i
< parm
->nkeys
; i
++, ex
++) {
1074 offset_key
= nl_msg_start_nested(request
, TCA_PEDIT_KEY_EX
);
1075 nl_msg_put_u16(request
, TCA_PEDIT_KEY_EX_HTYPE
, ex
->htype
);
1076 nl_msg_put_u16(request
, TCA_PEDIT_KEY_EX_CMD
, ex
->cmd
);
1077 nl_msg_end_nested(request
, offset_key
);
1079 nl_msg_end_nested(request
, offset_keys_ex
);
1081 nl_msg_end_nested(request
, offset
);
1085 nl_msg_put_act_push_vlan(struct ofpbuf
*request
, uint16_t vid
, uint8_t prio
)
1089 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
1090 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1092 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
1093 .v_action
= TCA_VLAN_ACT_PUSH
};
1095 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
1096 nl_msg_put_u16(request
, TCA_VLAN_PUSH_VLAN_ID
, vid
);
1097 nl_msg_put_u8(request
, TCA_VLAN_PUSH_VLAN_PRIORITY
, prio
);
1099 nl_msg_end_nested(request
, offset
);
1103 nl_msg_put_act_pop_vlan(struct ofpbuf
*request
)
1107 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
1108 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1110 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
1111 .v_action
= TCA_VLAN_ACT_POP
};
1113 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
1115 nl_msg_end_nested(request
, offset
);
1119 nl_msg_put_act_tunnel_key_release(struct ofpbuf
*request
)
1123 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
1124 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1126 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
1127 .t_action
= TCA_TUNNEL_KEY_ACT_RELEASE
};
1129 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
1131 nl_msg_end_nested(request
, offset
);
1135 nl_msg_put_act_tunnel_key_set(struct ofpbuf
*request
, ovs_be64 id
,
1136 ovs_be32 ipv4_src
, ovs_be32 ipv4_dst
,
1137 struct in6_addr
*ipv6_src
,
1138 struct in6_addr
*ipv6_dst
,
1143 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
1144 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1146 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
1147 .t_action
= TCA_TUNNEL_KEY_ACT_SET
};
1149 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
1151 ovs_be32 id32
= be64_to_be32(id
);
1152 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_KEY_ID
, id32
);
1154 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_SRC
, ipv4_src
);
1155 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_DST
, ipv4_dst
);
1156 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
1157 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_DST
,
1159 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_SRC
,
1162 nl_msg_put_be16(request
, TCA_TUNNEL_KEY_ENC_DST_PORT
, tp_dst
);
1164 nl_msg_end_nested(request
, offset
);
1168 nl_msg_put_act_drop(struct ofpbuf
*request
)
1172 nl_msg_put_string(request
, TCA_ACT_KIND
, "gact");
1173 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1175 struct tc_gact p
= { .action
= TC_ACT_SHOT
};
1177 nl_msg_put_unspec(request
, TCA_GACT_PARMS
, &p
, sizeof p
);
1179 nl_msg_end_nested(request
, offset
);
1183 nl_msg_put_act_redirect(struct ofpbuf
*request
, int ifindex
)
1187 nl_msg_put_string(request
, TCA_ACT_KIND
, "mirred");
1188 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
1190 struct tc_mirred m
= { .action
= TC_ACT_STOLEN
,
1191 .eaction
= TCA_EGRESS_REDIR
,
1192 .ifindex
= ifindex
};
1194 nl_msg_put_unspec(request
, TCA_MIRRED_PARMS
, &m
, sizeof m
);
1196 nl_msg_end_nested(request
, offset
);
1200 nl_msg_put_act_cookie(struct ofpbuf
*request
, struct tc_cookie
*ck
) {
1202 nl_msg_put_unspec(request
, TCA_ACT_COOKIE
, ck
->data
, ck
->len
);
1206 /* Given flower, a key_to_pedit map entry, calculates the rest,
1209 * mask, data - pointers of where read the first word of flower->key/mask.
1210 * current_offset - which offset to use for the first pedit action.
1211 * cnt - max pedits actions to use.
1212 * first_word_mask/last_word_mask - the mask to use for the first/last read
1213 * (as we read entire words). */
1215 calc_offsets(struct tc_flower
*flower
, struct flower_key_to_pedit
*m
,
1216 int *cur_offset
, int *cnt
, uint32_t *last_word_mask
,
1217 uint32_t *first_word_mask
, uint32_t **mask
, uint32_t **data
)
1219 int start_offset
, max_offset
, total_size
;
1220 int diff
, right_zero_bits
, left_zero_bits
;
1221 char *rewrite_key
= (void *) &flower
->rewrite
.key
;
1222 char *rewrite_mask
= (void *) &flower
->rewrite
.mask
;
1224 max_offset
= m
->offset
+ m
->size
;
1225 start_offset
= ROUND_DOWN(m
->offset
, 4);
1226 diff
= m
->offset
- start_offset
;
1227 total_size
= max_offset
- start_offset
;
1228 right_zero_bits
= 8 * (4 - (max_offset
% 4));
1229 left_zero_bits
= 8 * (m
->offset
- start_offset
);
1231 *cur_offset
= start_offset
;
1232 *cnt
= (total_size
/ 4) + (total_size
% 4 ? 1 : 0);
1233 *last_word_mask
= UINT32_MAX
>> right_zero_bits
;
1234 *first_word_mask
= UINT32_MAX
<< left_zero_bits
;
1235 *data
= (void *) (rewrite_key
+ m
->flower_offset
- diff
);
1236 *mask
= (void *) (rewrite_mask
+ m
->flower_offset
- diff
);
1240 csum_update_flag(struct tc_flower
*flower
,
1241 enum pedit_header_type htype
) {
1242 /* Explictily specifiy the csum flags so HW can return EOPNOTSUPP
1243 * if it doesn't support a checksum recalculation of some headers.
1244 * And since OVS allows a flow such as
1245 * eth(dst=<mac>),eth_type(0x0800) actions=set(ipv4(src=<new_ip>))
1246 * we need to force a more specific flow as this can, for example,
1247 * need a recalculation of icmp checksum if the packet that passes
1248 * is icmp and tcp checksum if its tcp. */
1251 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4
:
1252 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_IPV4HDR
;
1253 case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6
:
1254 case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP
:
1255 case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP
:
1256 if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
1257 flower
->needs_full_ip_proto_mask
= true;
1258 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_TCP
;
1259 } else if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
1260 flower
->needs_full_ip_proto_mask
= true;
1261 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_UDP
;
1262 } else if (flower
->key
.ip_proto
== IPPROTO_ICMP
1263 || flower
->key
.ip_proto
== IPPROTO_ICMPV6
) {
1264 flower
->needs_full_ip_proto_mask
= true;
1265 flower
->csum_update_flags
|= TCA_CSUM_UPDATE_FLAG_ICMP
;
1267 VLOG_WARN_RL(&error_rl
,
1268 "can't offload rewrite of IP/IPV6 with ip_proto: %d",
1269 flower
->key
.ip_proto
);
1272 case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH
:
1273 return 0; /* success */
1275 case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK
:
1276 case __PEDIT_HDR_TYPE_MAX
:
1285 nl_msg_put_flower_rewrite_pedits(struct ofpbuf
*request
,
1286 struct tc_flower
*flower
)
1289 struct tc_pedit sel
;
1290 struct tc_pedit_key keys
[MAX_PEDIT_OFFSETS
];
1291 struct tc_pedit_key_ex keys_ex
[MAX_PEDIT_OFFSETS
];
1299 for (i
= 0; i
< ARRAY_SIZE(flower_pedit_map
); i
++) {
1300 struct flower_key_to_pedit
*m
= &flower_pedit_map
[i
];
1301 struct tc_pedit_key
*pedit_key
= NULL
;
1302 struct tc_pedit_key_ex
*pedit_key_ex
= NULL
;
1303 uint32_t *mask
, *data
, first_word_mask
, last_word_mask
;
1304 int cnt
= 0, cur_offset
= 0;
1310 calc_offsets(flower
, m
, &cur_offset
, &cnt
, &last_word_mask
,
1311 &first_word_mask
, &mask
, &data
);
1313 for (j
= 0; j
< cnt
; j
++, mask
++, data
++, cur_offset
+= 4) {
1314 uint32_t mask_word
= *mask
;
1317 mask_word
&= first_word_mask
;
1320 mask_word
&= last_word_mask
;
1325 if (sel
.sel
.nkeys
== MAX_PEDIT_OFFSETS
) {
1326 VLOG_WARN_RL(&error_rl
, "reached too many pedit offsets: %d",
1331 pedit_key
= &sel
.keys
[sel
.sel
.nkeys
];
1332 pedit_key_ex
= &sel
.keys_ex
[sel
.sel
.nkeys
];
1333 pedit_key_ex
->cmd
= TCA_PEDIT_KEY_EX_CMD_SET
;
1334 pedit_key_ex
->htype
= m
->htype
;
1335 pedit_key
->off
= cur_offset
;
1336 pedit_key
->mask
= ~mask_word
;
1337 pedit_key
->val
= *data
& mask_word
;
1340 err
= csum_update_flag(flower
, m
->htype
);
1345 if (flower
->needs_full_ip_proto_mask
) {
1346 flower
->mask
.ip_proto
= UINT8_MAX
;
1350 nl_msg_put_act_pedit(request
, &sel
.sel
, sel
.keys_ex
);
1356 nl_msg_put_flower_acts(struct ofpbuf
*request
, struct tc_flower
*flower
)
1361 offset
= nl_msg_start_nested(request
, TCA_FLOWER_ACT
);
1363 uint16_t act_index
= 1;
1366 if (flower
->rewrite
.rewrite
) {
1367 act_offset
= nl_msg_start_nested(request
, act_index
++);
1368 error
= nl_msg_put_flower_rewrite_pedits(request
, flower
);
1372 nl_msg_end_nested(request
, act_offset
);
1374 if (flower
->csum_update_flags
) {
1375 act_offset
= nl_msg_start_nested(request
, act_index
++);
1376 nl_msg_put_act_csum(request
, flower
->csum_update_flags
);
1377 nl_msg_end_nested(request
, act_offset
);
1380 if (flower
->set
.set
) {
1381 act_offset
= nl_msg_start_nested(request
, act_index
++);
1382 nl_msg_put_act_tunnel_key_set(request
, flower
->set
.id
,
1383 flower
->set
.ipv4
.ipv4_src
,
1384 flower
->set
.ipv4
.ipv4_dst
,
1385 &flower
->set
.ipv6
.ipv6_src
,
1386 &flower
->set
.ipv6
.ipv6_dst
,
1387 flower
->set
.tp_dst
);
1388 nl_msg_end_nested(request
, act_offset
);
1390 if (flower
->tunnel
.tunnel
) {
1391 act_offset
= nl_msg_start_nested(request
, act_index
++);
1392 nl_msg_put_act_tunnel_key_release(request
);
1393 nl_msg_end_nested(request
, act_offset
);
1395 if (flower
->vlan_pop
) {
1396 act_offset
= nl_msg_start_nested(request
, act_index
++);
1397 nl_msg_put_act_pop_vlan(request
);
1398 nl_msg_end_nested(request
, act_offset
);
1400 if (flower
->vlan_push_id
) {
1401 act_offset
= nl_msg_start_nested(request
, act_index
++);
1402 nl_msg_put_act_push_vlan(request
,
1403 flower
->vlan_push_id
,
1404 flower
->vlan_push_prio
);
1405 nl_msg_end_nested(request
, act_offset
);
1407 if (flower
->ifindex_out
) {
1408 act_offset
= nl_msg_start_nested(request
, act_index
++);
1409 nl_msg_put_act_redirect(request
, flower
->ifindex_out
);
1410 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
1411 nl_msg_end_nested(request
, act_offset
);
1413 act_offset
= nl_msg_start_nested(request
, act_index
++);
1414 nl_msg_put_act_drop(request
);
1415 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
1416 nl_msg_end_nested(request
, act_offset
);
1419 nl_msg_end_nested(request
, offset
);
1425 nl_msg_put_masked_value(struct ofpbuf
*request
, uint16_t type
,
1426 uint16_t mask_type
, const void *data
,
1427 const void *mask_data
, size_t len
)
1429 if (mask_type
!= TCA_FLOWER_UNSPEC
) {
1430 if (is_all_zeros(mask_data
, len
)) {
1433 nl_msg_put_unspec(request
, mask_type
, mask_data
, len
);
1435 nl_msg_put_unspec(request
, type
, data
, len
);
1439 nl_msg_put_flower_tunnel(struct ofpbuf
*request
, struct tc_flower
*flower
)
1441 ovs_be32 ipv4_src
= flower
->tunnel
.ipv4
.ipv4_src
;
1442 ovs_be32 ipv4_dst
= flower
->tunnel
.ipv4
.ipv4_dst
;
1443 struct in6_addr
*ipv6_src
= &flower
->tunnel
.ipv6
.ipv6_src
;
1444 struct in6_addr
*ipv6_dst
= &flower
->tunnel
.ipv6
.ipv6_dst
;
1445 ovs_be16 tp_dst
= flower
->tunnel
.tp_dst
;
1446 ovs_be32 id
= be64_to_be32(flower
->tunnel
.id
);
1448 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_KEY_ID
, id
);
1450 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_SRC
, ipv4_src
);
1451 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_DST
, ipv4_dst
);
1452 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
1453 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_SRC
, ipv6_src
);
1454 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_DST
, ipv6_dst
);
1456 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ENC_UDP_DST_PORT
, tp_dst
);
1459 #define FLOWER_PUT_MASKED_VALUE(member, type) \
1460 nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \
1461 &flower->mask.member, sizeof flower->key.member)
1464 nl_msg_put_flower_options(struct ofpbuf
*request
, struct tc_flower
*flower
)
1467 uint16_t host_eth_type
= ntohs(flower
->key
.eth_type
);
1468 bool is_vlan
= (host_eth_type
== ETH_TYPE_VLAN
);
1471 /* need to parse acts first as some acts require changing the matching
1472 * see csum_update_flag() */
1473 err
= nl_msg_put_flower_acts(request
, flower
);
1479 host_eth_type
= ntohs(flower
->key
.encap_eth_type
);
1482 FLOWER_PUT_MASKED_VALUE(dst_mac
, TCA_FLOWER_KEY_ETH_DST
);
1483 FLOWER_PUT_MASKED_VALUE(src_mac
, TCA_FLOWER_KEY_ETH_SRC
);
1485 if (host_eth_type
== ETH_P_IP
|| host_eth_type
== ETH_P_IPV6
) {
1486 if (flower
->mask
.ip_proto
&& flower
->key
.ip_proto
) {
1487 nl_msg_put_u8(request
, TCA_FLOWER_KEY_IP_PROTO
,
1488 flower
->key
.ip_proto
);
1491 if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
1492 FLOWER_PUT_MASKED_VALUE(udp_src
, TCA_FLOWER_KEY_UDP_SRC
);
1493 FLOWER_PUT_MASKED_VALUE(udp_dst
, TCA_FLOWER_KEY_UDP_DST
);
1494 } else if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
1495 FLOWER_PUT_MASKED_VALUE(tcp_src
, TCA_FLOWER_KEY_TCP_SRC
);
1496 FLOWER_PUT_MASKED_VALUE(tcp_dst
, TCA_FLOWER_KEY_TCP_DST
);
1497 FLOWER_PUT_MASKED_VALUE(tcp_flags
, TCA_FLOWER_KEY_TCP_FLAGS
);
1498 } else if (flower
->key
.ip_proto
== IPPROTO_SCTP
) {
1499 FLOWER_PUT_MASKED_VALUE(sctp_src
, TCA_FLOWER_KEY_SCTP_SRC
);
1500 FLOWER_PUT_MASKED_VALUE(sctp_dst
, TCA_FLOWER_KEY_SCTP_DST
);
1504 if (host_eth_type
== ETH_P_IP
) {
1505 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_src
, TCA_FLOWER_KEY_IPV4_SRC
);
1506 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_dst
, TCA_FLOWER_KEY_IPV4_DST
);
1507 FLOWER_PUT_MASKED_VALUE(ip_ttl
, TCA_FLOWER_KEY_IP_TTL
);
1508 } else if (host_eth_type
== ETH_P_IPV6
) {
1509 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_src
, TCA_FLOWER_KEY_IPV6_SRC
);
1510 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_dst
, TCA_FLOWER_KEY_IPV6_DST
);
1513 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ETH_TYPE
, flower
->key
.eth_type
);
1516 if (flower
->key
.vlan_id
|| flower
->key
.vlan_prio
) {
1517 nl_msg_put_u16(request
, TCA_FLOWER_KEY_VLAN_ID
,
1518 flower
->key
.vlan_id
);
1519 nl_msg_put_u8(request
, TCA_FLOWER_KEY_VLAN_PRIO
,
1520 flower
->key
.vlan_prio
);
1522 if (flower
->key
.encap_eth_type
) {
1523 nl_msg_put_be16(request
, TCA_FLOWER_KEY_VLAN_ETH_TYPE
,
1524 flower
->key
.encap_eth_type
);
1528 nl_msg_put_u32(request
, TCA_FLOWER_FLAGS
, tc_get_tc_cls_policy(tc_policy
));
1530 if (flower
->tunnel
.tunnel
) {
1531 nl_msg_put_flower_tunnel(request
, flower
);
1538 tc_replace_flower(int ifindex
, uint16_t prio
, uint32_t handle
,
1539 struct tc_flower
*flower
)
1541 struct ofpbuf request
;
1542 struct tcmsg
*tcmsg
;
1543 struct ofpbuf
*reply
;
1545 size_t basic_offset
;
1546 uint16_t eth_type
= (OVS_FORCE
uint16_t) flower
->key
.eth_type
;
1548 tcmsg
= tc_make_request(ifindex
, RTM_NEWTFILTER
,
1549 NLM_F_CREATE
| NLM_F_ECHO
, &request
);
1550 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1551 tcmsg
->tcm_info
= tc_make_handle(prio
, eth_type
);
1552 tcmsg
->tcm_handle
= handle
;
1554 nl_msg_put_string(&request
, TCA_KIND
, "flower");
1555 basic_offset
= nl_msg_start_nested(&request
, TCA_OPTIONS
);
1557 error
= nl_msg_put_flower_options(&request
, flower
);
1560 ofpbuf_uninit(&request
);
1564 nl_msg_end_nested(&request
, basic_offset
);
1566 error
= tc_transact(&request
, &reply
);
1569 ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
1571 flower
->prio
= tc_get_major(tc
->tcm_info
);
1572 flower
->handle
= tc
->tcm_handle
;
1573 ofpbuf_delete(reply
);
1580 tc_set_policy(const char *policy
)
1586 if (!strcmp(policy
, "skip_sw")) {
1587 tc_policy
= TC_POLICY_SKIP_SW
;
1588 } else if (!strcmp(policy
, "skip_hw")) {
1589 tc_policy
= TC_POLICY_SKIP_HW
;
1590 } else if (!strcmp(policy
, "none")) {
1591 tc_policy
= TC_POLICY_NONE
;
1593 VLOG_WARN("tc: Invalid policy '%s'", policy
);
1597 VLOG_INFO("tc: Using policy '%s'", policy
);