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_gact.h>
25 #include <linux/tc_act/tc_mirred.h>
26 #include <linux/tc_act/tc_tunnel_key.h>
27 #include <linux/tc_act/tc_vlan.h>
28 #include <linux/gen_stats.h>
32 #include "byte-order.h"
33 #include "netlink-socket.h"
35 #include "openvswitch/ofpbuf.h"
36 #include "openvswitch/vlog.h"
39 #include "unaligned.h"
41 VLOG_DEFINE_THIS_MODULE(tc
);
43 static struct vlog_rate_limit error_rl
= VLOG_RATE_LIMIT_INIT(60, 5);
45 enum tc_offload_policy
{
51 static enum tc_offload_policy tc_policy
= TC_POLICY_NONE
;
54 tc_make_request(int ifindex
, int type
, unsigned int flags
,
55 struct ofpbuf
*request
)
59 ofpbuf_init(request
, 512);
60 nl_msg_put_nlmsghdr(request
, sizeof *tcmsg
, type
, NLM_F_REQUEST
| flags
);
61 tcmsg
= ofpbuf_put_zeros(request
, sizeof *tcmsg
);
62 tcmsg
->tcm_family
= AF_UNSPEC
;
63 tcmsg
->tcm_ifindex
= ifindex
;
64 /* Caller should fill in tcmsg->tcm_handle. */
65 /* Caller should fill in tcmsg->tcm_parent. */
71 tc_transact(struct ofpbuf
*request
, struct ofpbuf
**replyp
)
73 int error
= nl_transact(NETLINK_ROUTE
, request
, replyp
);
74 ofpbuf_uninit(request
);
78 /* Adds or deletes a root ingress qdisc on device with specified ifindex.
80 * This function is equivalent to running the following when 'add' is true:
81 * /sbin/tc qdisc add dev <devname> handle ffff: ingress
83 * This function is equivalent to running the following when 'add' is false:
84 * /sbin/tc qdisc del dev <devname> handle ffff: ingress
86 * Where dev <devname> is the device with specified ifindex name.
88 * The configuration and stats may be seen with the following command:
89 * /sbin/tc -s qdisc show dev <devname>
91 * Returns 0 if successful, otherwise a positive errno value.
94 tc_add_del_ingress_qdisc(int ifindex
, bool add
)
96 struct ofpbuf request
;
99 int type
= add
? RTM_NEWQDISC
: RTM_DELQDISC
;
100 int flags
= add
? NLM_F_EXCL
| NLM_F_CREATE
: 0;
102 tcmsg
= tc_make_request(ifindex
, type
, flags
, &request
);
103 tcmsg
->tcm_handle
= TC_H_MAKE(TC_H_INGRESS
, 0);
104 tcmsg
->tcm_parent
= TC_H_INGRESS
;
105 nl_msg_put_string(&request
, TCA_KIND
, "ingress");
106 nl_msg_put_unspec(&request
, TCA_OPTIONS
, NULL
, 0);
108 error
= tc_transact(&request
, NULL
);
110 /* If we're deleting the qdisc, don't worry about some of the
111 * error conditions. */
112 if (!add
&& (error
== ENOENT
|| error
== EINVAL
)) {
121 static const struct nl_policy tca_policy
[] = {
122 [TCA_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
123 [TCA_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
124 [TCA_STATS
] = { .type
= NL_A_UNSPEC
,
125 .min_len
= sizeof(struct tc_stats
), .optional
= true, },
126 [TCA_STATS2
] = { .type
= NL_A_NESTED
, .optional
= true, },
129 static const struct nl_policy tca_flower_policy
[] = {
130 [TCA_FLOWER_CLASSID
] = { .type
= NL_A_U32
, .optional
= true, },
131 [TCA_FLOWER_INDEV
] = { .type
= NL_A_STRING
, .max_len
= IFNAMSIZ
,
133 [TCA_FLOWER_KEY_ETH_SRC
] = { .type
= NL_A_UNSPEC
,
134 .min_len
= ETH_ALEN
, .optional
= true, },
135 [TCA_FLOWER_KEY_ETH_DST
] = { .type
= NL_A_UNSPEC
,
136 .min_len
= ETH_ALEN
, .optional
= true, },
137 [TCA_FLOWER_KEY_ETH_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
140 [TCA_FLOWER_KEY_ETH_DST_MASK
] = { .type
= NL_A_UNSPEC
,
143 [TCA_FLOWER_KEY_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= false, },
144 [TCA_FLOWER_FLAGS
] = { .type
= NL_A_U32
, .optional
= false, },
145 [TCA_FLOWER_ACT
] = { .type
= NL_A_NESTED
, .optional
= false, },
146 [TCA_FLOWER_KEY_IP_PROTO
] = { .type
= NL_A_U8
, .optional
= true, },
147 [TCA_FLOWER_KEY_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
148 [TCA_FLOWER_KEY_IPV4_DST
] = {.type
= NL_A_U32
, .optional
= true, },
149 [TCA_FLOWER_KEY_IPV4_SRC_MASK
] = { .type
= NL_A_U32
, .optional
= true, },
150 [TCA_FLOWER_KEY_IPV4_DST_MASK
] = { .type
= NL_A_U32
, .optional
= true, },
151 [TCA_FLOWER_KEY_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
152 .min_len
= sizeof(struct in6_addr
),
154 [TCA_FLOWER_KEY_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
155 .min_len
= sizeof(struct in6_addr
),
157 [TCA_FLOWER_KEY_IPV6_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
158 .min_len
= sizeof(struct in6_addr
),
160 [TCA_FLOWER_KEY_IPV6_DST_MASK
] = { .type
= NL_A_UNSPEC
,
161 .min_len
= sizeof(struct in6_addr
),
163 [TCA_FLOWER_KEY_TCP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
164 [TCA_FLOWER_KEY_TCP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
165 [TCA_FLOWER_KEY_TCP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
166 [TCA_FLOWER_KEY_TCP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
167 [TCA_FLOWER_KEY_UDP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
168 [TCA_FLOWER_KEY_UDP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
169 [TCA_FLOWER_KEY_UDP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
170 [TCA_FLOWER_KEY_UDP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
171 [TCA_FLOWER_KEY_SCTP_SRC
] = { .type
= NL_A_U16
, .optional
= true, },
172 [TCA_FLOWER_KEY_SCTP_DST
] = { .type
= NL_A_U16
, .optional
= true, },
173 [TCA_FLOWER_KEY_SCTP_SRC_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
174 [TCA_FLOWER_KEY_SCTP_DST_MASK
] = { .type
= NL_A_U16
, .optional
= true, },
175 [TCA_FLOWER_KEY_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
176 [TCA_FLOWER_KEY_VLAN_PRIO
] = { .type
= NL_A_U8
, .optional
= true, },
177 [TCA_FLOWER_KEY_VLAN_ETH_TYPE
] = { .type
= NL_A_U16
, .optional
= true, },
178 [TCA_FLOWER_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
179 [TCA_FLOWER_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
180 [TCA_FLOWER_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
181 [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
] = { .type
= NL_A_U32
,
183 [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
] = { .type
= NL_A_U32
,
185 [TCA_FLOWER_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
186 .min_len
= sizeof(struct in6_addr
),
188 [TCA_FLOWER_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
189 .min_len
= sizeof(struct in6_addr
),
191 [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
] = { .type
= NL_A_UNSPEC
,
192 .min_len
= sizeof(struct in6_addr
),
194 [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
] = { .type
= NL_A_UNSPEC
,
195 .min_len
= sizeof(struct in6_addr
),
197 [TCA_FLOWER_KEY_ENC_UDP_DST_PORT
] = { .type
= NL_A_U16
,
199 [TCA_FLOWER_KEY_IP_TTL
] = { .type
= NL_A_U8
,
201 [TCA_FLOWER_KEY_IP_TTL_MASK
] = { .type
= NL_A_U8
,
206 nl_parse_flower_eth(struct nlattr
**attrs
, struct tc_flower
*flower
)
208 const struct eth_addr
*eth
;
210 if (attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
]) {
211 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC
], ETH_ALEN
);
212 memcpy(&flower
->key
.src_mac
, eth
, sizeof flower
->key
.src_mac
);
214 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_SRC_MASK
], ETH_ALEN
);
215 memcpy(&flower
->mask
.src_mac
, eth
, sizeof flower
->mask
.src_mac
);
217 if (attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
]) {
218 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST
], ETH_ALEN
);
219 memcpy(&flower
->key
.dst_mac
, eth
, sizeof flower
->key
.dst_mac
);
221 eth
= nl_attr_get_unspec(attrs
[TCA_FLOWER_KEY_ETH_DST_MASK
], ETH_ALEN
);
222 memcpy(&flower
->mask
.dst_mac
, eth
, sizeof flower
->mask
.dst_mac
);
227 nl_parse_flower_vlan(struct nlattr
**attrs
, struct tc_flower
*flower
)
229 if (flower
->key
.eth_type
!= htons(ETH_TYPE_VLAN
)) {
233 flower
->key
.encap_eth_type
=
234 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ETH_TYPE
]);
236 if (attrs
[TCA_FLOWER_KEY_VLAN_ID
]) {
237 flower
->key
.vlan_id
=
238 nl_attr_get_u16(attrs
[TCA_FLOWER_KEY_VLAN_ID
]);
240 if (attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]) {
241 flower
->key
.vlan_prio
=
242 nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_VLAN_PRIO
]);
247 nl_parse_flower_tunnel(struct nlattr
**attrs
, struct tc_flower
*flower
)
249 if (attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]) {
250 ovs_be32 id
= nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_KEY_ID
]);
252 flower
->tunnel
.id
= be32_to_be64(id
);
254 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK
]) {
255 flower
->tunnel
.ipv4
.ipv4_src
=
256 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_SRC
]);
258 if (attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK
]) {
259 flower
->tunnel
.ipv4
.ipv4_dst
=
260 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_ENC_IPV4_DST
]);
262 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK
]) {
263 flower
->tunnel
.ipv6
.ipv6_src
=
264 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_SRC
]);
266 if (attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK
]) {
267 flower
->tunnel
.ipv6
.ipv6_dst
=
268 nl_attr_get_in6_addr(attrs
[TCA_FLOWER_KEY_ENC_IPV6_DST
]);
270 if (attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]) {
271 flower
->tunnel
.tp_dst
=
272 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT
]);
277 nl_parse_flower_ip(struct nlattr
**attrs
, struct tc_flower
*flower
) {
278 uint8_t ip_proto
= 0;
279 struct tc_flower_key
*key
= &flower
->key
;
280 struct tc_flower_key
*mask
= &flower
->mask
;
282 if (attrs
[TCA_FLOWER_KEY_IP_PROTO
]) {
283 ip_proto
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_PROTO
]);
284 key
->ip_proto
= ip_proto
;
285 mask
->ip_proto
= UINT8_MAX
;
288 if (attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]) {
290 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC
]);
291 mask
->ipv4
.ipv4_src
=
292 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_SRC_MASK
]);
294 if (attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]) {
296 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST
]);
297 mask
->ipv4
.ipv4_dst
=
298 nl_attr_get_be32(attrs
[TCA_FLOWER_KEY_IPV4_DST_MASK
]);
300 if (attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
]) {
301 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_SRC
];
302 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_SRC_MASK
];
304 key
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr
);
305 mask
->ipv6
.ipv6_src
= nl_attr_get_in6_addr(attr_mask
);
307 if (attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
]) {
308 struct nlattr
*attr
= attrs
[TCA_FLOWER_KEY_IPV6_DST
];
309 struct nlattr
*attr_mask
= attrs
[TCA_FLOWER_KEY_IPV6_DST_MASK
];
311 key
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr
);
312 mask
->ipv6
.ipv6_dst
= nl_attr_get_in6_addr(attr_mask
);
315 if (ip_proto
== IPPROTO_TCP
) {
316 if (attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]) {
318 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC
]);
320 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_SRC_MASK
]);
322 if (attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]) {
324 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST
]);
326 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_TCP_DST_MASK
]);
328 } else if (ip_proto
== IPPROTO_UDP
) {
329 if (attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]) {
330 key
->udp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC
]);
332 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_SRC_MASK
]);
334 if (attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]) {
335 key
->udp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST
]);
337 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_UDP_DST_MASK
]);
339 } else if (ip_proto
== IPPROTO_SCTP
) {
340 if (attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]) {
341 key
->sctp_src
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC
]);
343 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_SRC_MASK
]);
345 if (attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]) {
346 key
->sctp_dst
= nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST
]);
348 nl_attr_get_be16(attrs
[TCA_FLOWER_KEY_SCTP_DST_MASK
]);
352 if (attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]) {
353 key
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL
]);
354 mask
->ip_ttl
= nl_attr_get_u8(attrs
[TCA_FLOWER_KEY_IP_TTL_MASK
]);
358 static const struct nl_policy tunnel_key_policy
[] = {
359 [TCA_TUNNEL_KEY_PARMS
] = { .type
= NL_A_UNSPEC
,
360 .min_len
= sizeof(struct tc_tunnel_key
),
361 .optional
= false, },
362 [TCA_TUNNEL_KEY_ENC_IPV4_SRC
] = { .type
= NL_A_U32
, .optional
= true, },
363 [TCA_TUNNEL_KEY_ENC_IPV4_DST
] = { .type
= NL_A_U32
, .optional
= true, },
364 [TCA_TUNNEL_KEY_ENC_IPV6_SRC
] = { .type
= NL_A_UNSPEC
,
365 .min_len
= sizeof(struct in6_addr
),
367 [TCA_TUNNEL_KEY_ENC_IPV6_DST
] = { .type
= NL_A_UNSPEC
,
368 .min_len
= sizeof(struct in6_addr
),
370 [TCA_TUNNEL_KEY_ENC_KEY_ID
] = { .type
= NL_A_U32
, .optional
= true, },
371 [TCA_TUNNEL_KEY_ENC_DST_PORT
] = { .type
= NL_A_U16
, .optional
= true, },
375 nl_parse_act_tunnel_key(struct nlattr
*options
, struct tc_flower
*flower
)
377 struct nlattr
*tun_attrs
[ARRAY_SIZE(tunnel_key_policy
)];
378 const struct nlattr
*tun_parms
;
379 const struct tc_tunnel_key
*tun
;
381 if (!nl_parse_nested(options
, tunnel_key_policy
, tun_attrs
,
382 ARRAY_SIZE(tunnel_key_policy
))) {
383 VLOG_ERR_RL(&error_rl
, "failed to parse tunnel_key action options");
387 tun_parms
= tun_attrs
[TCA_TUNNEL_KEY_PARMS
];
388 tun
= nl_attr_get_unspec(tun_parms
, sizeof *tun
);
389 if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_SET
) {
390 struct nlattr
*id
= tun_attrs
[TCA_TUNNEL_KEY_ENC_KEY_ID
];
391 struct nlattr
*dst_port
= tun_attrs
[TCA_TUNNEL_KEY_ENC_DST_PORT
];
392 struct nlattr
*ipv4_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_SRC
];
393 struct nlattr
*ipv4_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV4_DST
];
394 struct nlattr
*ipv6_src
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_SRC
];
395 struct nlattr
*ipv6_dst
= tun_attrs
[TCA_TUNNEL_KEY_ENC_IPV6_DST
];
397 flower
->set
.set
= true;
398 flower
->set
.ipv4
.ipv4_src
= ipv4_src
? nl_attr_get_be32(ipv4_src
) : 0;
399 flower
->set
.ipv4
.ipv4_dst
= ipv4_dst
? nl_attr_get_be32(ipv4_dst
) : 0;
401 flower
->set
.ipv6
.ipv6_src
= nl_attr_get_in6_addr(ipv6_src
);
404 flower
->set
.ipv6
.ipv6_dst
= nl_attr_get_in6_addr(ipv6_dst
);
406 flower
->set
.id
= id
? be32_to_be64(nl_attr_get_be32(id
)) : 0;
407 flower
->set
.tp_dst
= dst_port
? nl_attr_get_be16(dst_port
) : 0;
408 } else if (tun
->t_action
== TCA_TUNNEL_KEY_ACT_RELEASE
) {
409 flower
->tunnel
.tunnel
= true;
411 VLOG_ERR_RL(&error_rl
, "unknown tunnel actions: %d, %d",
412 tun
->action
, tun
->t_action
);
418 static const struct nl_policy gact_policy
[] = {
419 [TCA_GACT_PARMS
] = { .type
= NL_A_UNSPEC
,
420 .min_len
= sizeof(struct tc_gact
),
421 .optional
= false, },
422 [TCA_GACT_TM
] = { .type
= NL_A_UNSPEC
,
423 .min_len
= sizeof(struct tcf_t
),
424 .optional
= false, },
430 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
431 static int user_hz
= 100;
433 if (ovsthread_once_start(&once
)) {
434 user_hz
= sysconf(_SC_CLK_TCK
);
435 ovsthread_once_done(&once
);
442 nl_parse_tcf(const struct tcf_t
*tm
, struct tc_flower
*flower
)
444 flower
->lastused
= time_msec() - (tm
->lastuse
* 1000 / get_user_hz());
448 nl_parse_act_drop(struct nlattr
*options
, struct tc_flower
*flower
)
450 struct nlattr
*gact_attrs
[ARRAY_SIZE(gact_policy
)];
451 const struct tc_gact
*p
;
452 struct nlattr
*gact_parms
;
453 const struct tcf_t
*tm
;
455 if (!nl_parse_nested(options
, gact_policy
, gact_attrs
,
456 ARRAY_SIZE(gact_policy
))) {
457 VLOG_ERR_RL(&error_rl
, "failed to parse gact action options");
461 gact_parms
= gact_attrs
[TCA_GACT_PARMS
];
462 p
= nl_attr_get_unspec(gact_parms
, sizeof *p
);
464 if (p
->action
!= TC_ACT_SHOT
) {
465 VLOG_ERR_RL(&error_rl
, "unknown gact action: %d", p
->action
);
469 tm
= nl_attr_get_unspec(gact_attrs
[TCA_GACT_TM
], sizeof *tm
);
470 nl_parse_tcf(tm
, flower
);
475 static const struct nl_policy mirred_policy
[] = {
476 [TCA_MIRRED_PARMS
] = { .type
= NL_A_UNSPEC
,
477 .min_len
= sizeof(struct tc_mirred
),
478 .optional
= false, },
479 [TCA_MIRRED_TM
] = { .type
= NL_A_UNSPEC
,
480 .min_len
= sizeof(struct tcf_t
),
481 .optional
= false, },
485 nl_parse_act_mirred(struct nlattr
*options
, struct tc_flower
*flower
)
488 struct nlattr
*mirred_attrs
[ARRAY_SIZE(mirred_policy
)];
489 const struct tc_mirred
*m
;
490 const struct nlattr
*mirred_parms
;
491 const struct tcf_t
*tm
;
492 struct nlattr
*mirred_tm
;
494 if (!nl_parse_nested(options
, mirred_policy
, mirred_attrs
,
495 ARRAY_SIZE(mirred_policy
))) {
496 VLOG_ERR_RL(&error_rl
, "failed to parse mirred action options");
500 mirred_parms
= mirred_attrs
[TCA_MIRRED_PARMS
];
501 m
= nl_attr_get_unspec(mirred_parms
, sizeof *m
);
503 if (m
->action
!= TC_ACT_STOLEN
|| m
->eaction
!= TCA_EGRESS_REDIR
) {
504 VLOG_ERR_RL(&error_rl
, "unknown mirred action: %d, %d, %d",
505 m
->action
, m
->eaction
, m
->ifindex
);
509 flower
->ifindex_out
= m
->ifindex
;
511 mirred_tm
= mirred_attrs
[TCA_MIRRED_TM
];
512 tm
= nl_attr_get_unspec(mirred_tm
, sizeof *tm
);
513 nl_parse_tcf(tm
, flower
);
518 static const struct nl_policy vlan_policy
[] = {
519 [TCA_VLAN_PARMS
] = { .type
= NL_A_UNSPEC
,
520 .min_len
= sizeof(struct tc_vlan
),
521 .optional
= false, },
522 [TCA_VLAN_PUSH_VLAN_ID
] = { .type
= NL_A_U16
, .optional
= true, },
523 [TCA_VLAN_PUSH_VLAN_PROTOCOL
] = { .type
= NL_A_U16
, .optional
= true, },
524 [TCA_VLAN_PUSH_VLAN_PRIORITY
] = { .type
= NL_A_U8
, .optional
= true, },
528 nl_parse_act_vlan(struct nlattr
*options
, struct tc_flower
*flower
)
530 struct nlattr
*vlan_attrs
[ARRAY_SIZE(vlan_policy
)];
531 const struct tc_vlan
*v
;
532 const struct nlattr
*vlan_parms
;
534 if (!nl_parse_nested(options
, vlan_policy
, vlan_attrs
,
535 ARRAY_SIZE(vlan_policy
))) {
536 VLOG_ERR_RL(&error_rl
, "failed to parse vlan action options");
540 vlan_parms
= vlan_attrs
[TCA_VLAN_PARMS
];
541 v
= nl_attr_get_unspec(vlan_parms
, sizeof *v
);
542 if (v
->v_action
== TCA_VLAN_ACT_PUSH
) {
543 struct nlattr
*vlan_id
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_ID
];
544 struct nlattr
*vlan_prio
= vlan_attrs
[TCA_VLAN_PUSH_VLAN_PRIORITY
];
546 flower
->vlan_push_id
= nl_attr_get_u16(vlan_id
);
547 flower
->vlan_push_prio
= vlan_prio
? nl_attr_get_u8(vlan_prio
) : 0;
548 } else if (v
->v_action
== TCA_VLAN_ACT_POP
) {
549 flower
->vlan_pop
= 1;
551 VLOG_ERR_RL(&error_rl
, "unknown vlan action: %d, %d",
552 v
->action
, v
->v_action
);
558 static const struct nl_policy act_policy
[] = {
559 [TCA_ACT_KIND
] = { .type
= NL_A_STRING
, .optional
= false, },
560 [TCA_ACT_COOKIE
] = { .type
= NL_A_UNSPEC
, .optional
= true, },
561 [TCA_ACT_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= false, },
562 [TCA_ACT_STATS
] = { .type
= NL_A_NESTED
, .optional
= false, },
565 static const struct nl_policy stats_policy
[] = {
566 [TCA_STATS_BASIC
] = { .type
= NL_A_UNSPEC
,
567 .min_len
= sizeof(struct gnet_stats_basic
),
568 .optional
= false, },
572 nl_parse_single_action(struct nlattr
*action
, struct tc_flower
*flower
)
574 struct nlattr
*act_options
;
575 struct nlattr
*act_stats
;
576 struct nlattr
*act_cookie
;
577 const char *act_kind
;
578 struct nlattr
*action_attrs
[ARRAY_SIZE(act_policy
)];
579 struct nlattr
*stats_attrs
[ARRAY_SIZE(stats_policy
)];
580 struct ovs_flow_stats
*stats
= &flower
->stats
;
581 const struct gnet_stats_basic
*bs
;
583 if (!nl_parse_nested(action
, act_policy
, action_attrs
,
584 ARRAY_SIZE(act_policy
))) {
585 VLOG_ERR_RL(&error_rl
, "failed to parse single action options");
589 act_kind
= nl_attr_get_string(action_attrs
[TCA_ACT_KIND
]);
590 act_options
= action_attrs
[TCA_ACT_OPTIONS
];
591 act_cookie
= action_attrs
[TCA_ACT_COOKIE
];
593 if (!strcmp(act_kind
, "gact")) {
594 nl_parse_act_drop(act_options
, flower
);
595 } else if (!strcmp(act_kind
, "mirred")) {
596 nl_parse_act_mirred(act_options
, flower
);
597 } else if (!strcmp(act_kind
, "vlan")) {
598 nl_parse_act_vlan(act_options
, flower
);
599 } else if (!strcmp(act_kind
, "tunnel_key")) {
600 nl_parse_act_tunnel_key(act_options
, flower
);
602 VLOG_ERR_RL(&error_rl
, "unknown tc action kind: %s", act_kind
);
607 flower
->act_cookie
.data
= nl_attr_get(act_cookie
);
608 flower
->act_cookie
.len
= nl_attr_get_size(act_cookie
);
611 act_stats
= action_attrs
[TCA_ACT_STATS
];
613 if (!nl_parse_nested(act_stats
, stats_policy
, stats_attrs
,
614 ARRAY_SIZE(stats_policy
))) {
615 VLOG_ERR_RL(&error_rl
, "failed to parse action stats policy");
619 bs
= nl_attr_get_unspec(stats_attrs
[TCA_STATS_BASIC
], sizeof *bs
);
620 put_32aligned_u64(&stats
->n_packets
, bs
->packets
);
621 put_32aligned_u64(&stats
->n_bytes
, bs
->bytes
);
626 #define TCA_ACT_MIN_PRIO 1
629 nl_parse_flower_actions(struct nlattr
**attrs
, struct tc_flower
*flower
)
631 const struct nlattr
*actions
= attrs
[TCA_FLOWER_ACT
];
632 static struct nl_policy actions_orders_policy
[TCA_ACT_MAX_PRIO
+ 1] = {};
633 struct nlattr
*actions_orders
[ARRAY_SIZE(actions_orders_policy
)];
634 const int max_size
= ARRAY_SIZE(actions_orders_policy
);
636 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
637 actions_orders_policy
[i
].type
= NL_A_NESTED
;
638 actions_orders_policy
[i
].optional
= true;
641 if (!nl_parse_nested(actions
, actions_orders_policy
, actions_orders
,
642 ARRAY_SIZE(actions_orders_policy
))) {
643 VLOG_ERR_RL(&error_rl
, "failed to parse flower order of actions");
647 for (int i
= TCA_ACT_MIN_PRIO
; i
< max_size
; i
++) {
648 if (actions_orders
[i
]) {
649 int err
= nl_parse_single_action(actions_orders
[i
], flower
);
661 nl_parse_flower_options(struct nlattr
*nl_options
, struct tc_flower
*flower
)
663 struct nlattr
*attrs
[ARRAY_SIZE(tca_flower_policy
)];
665 if (!nl_parse_nested(nl_options
, tca_flower_policy
,
666 attrs
, ARRAY_SIZE(tca_flower_policy
))) {
667 VLOG_ERR_RL(&error_rl
, "failed to parse flower classifier options");
671 nl_parse_flower_eth(attrs
, flower
);
672 nl_parse_flower_vlan(attrs
, flower
);
673 nl_parse_flower_ip(attrs
, flower
);
674 nl_parse_flower_tunnel(attrs
, flower
);
675 return nl_parse_flower_actions(attrs
, flower
);
679 parse_netlink_to_tc_flower(struct ofpbuf
*reply
, struct tc_flower
*flower
)
682 struct nlattr
*ta
[ARRAY_SIZE(tca_policy
)];
685 if (NLMSG_HDRLEN
+ sizeof *tc
> reply
->size
) {
689 memset(flower
, 0, sizeof *flower
);
691 tc
= ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
692 flower
->handle
= tc
->tcm_handle
;
693 flower
->key
.eth_type
= (OVS_FORCE ovs_be16
) tc_get_minor(tc
->tcm_info
);
694 flower
->mask
.eth_type
= OVS_BE16_MAX
;
695 flower
->prio
= tc_get_major(tc
->tcm_info
);
697 if (!flower
->handle
) {
701 if (!nl_policy_parse(reply
, NLMSG_HDRLEN
+ sizeof *tc
,
702 tca_policy
, ta
, ARRAY_SIZE(ta
))) {
703 VLOG_ERR_RL(&error_rl
, "failed to parse tca policy");
707 kind
= nl_attr_get_string(ta
[TCA_KIND
]);
708 if (strcmp(kind
, "flower")) {
709 VLOG_ERR_RL(&error_rl
, "failed to parse filter: %s", kind
);
713 return nl_parse_flower_options(ta
[TCA_OPTIONS
], flower
);
717 tc_dump_flower_start(int ifindex
, struct nl_dump
*dump
)
719 struct ofpbuf request
;
722 tcmsg
= tc_make_request(ifindex
, RTM_GETTFILTER
, NLM_F_DUMP
, &request
);
723 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
724 tcmsg
->tcm_info
= TC_H_UNSPEC
;
725 tcmsg
->tcm_handle
= 0;
727 nl_dump_start(dump
, NETLINK_ROUTE
, &request
);
728 ofpbuf_uninit(&request
);
734 tc_flush(int ifindex
)
736 struct ofpbuf request
;
739 tcmsg
= tc_make_request(ifindex
, RTM_DELTFILTER
, NLM_F_ACK
, &request
);
740 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
741 tcmsg
->tcm_info
= TC_H_UNSPEC
;
743 return tc_transact(&request
, NULL
);
747 tc_del_filter(int ifindex
, int prio
, int handle
)
749 struct ofpbuf request
;
751 struct ofpbuf
*reply
;
754 tcmsg
= tc_make_request(ifindex
, RTM_DELTFILTER
, NLM_F_ECHO
, &request
);
755 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
756 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
757 tcmsg
->tcm_handle
= handle
;
759 error
= tc_transact(&request
, &reply
);
761 ofpbuf_delete(reply
);
767 tc_get_flower(int ifindex
, int prio
, int handle
, struct tc_flower
*flower
)
769 struct ofpbuf request
;
771 struct ofpbuf
*reply
;
774 tcmsg
= tc_make_request(ifindex
, RTM_GETTFILTER
, NLM_F_ECHO
, &request
);
775 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
776 tcmsg
->tcm_info
= tc_make_handle(prio
, 0);
777 tcmsg
->tcm_handle
= handle
;
779 error
= tc_transact(&request
, &reply
);
784 error
= parse_netlink_to_tc_flower(reply
, flower
);
785 ofpbuf_delete(reply
);
790 tc_get_tc_cls_policy(enum tc_offload_policy policy
)
792 if (policy
== TC_POLICY_SKIP_HW
) {
793 return TCA_CLS_FLAGS_SKIP_HW
;
794 } else if (policy
== TC_POLICY_SKIP_SW
) {
795 return TCA_CLS_FLAGS_SKIP_SW
;
802 nl_msg_put_act_push_vlan(struct ofpbuf
*request
, uint16_t vid
, uint8_t prio
)
806 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
807 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
809 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
810 .v_action
= TCA_VLAN_ACT_PUSH
};
812 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
813 nl_msg_put_u16(request
, TCA_VLAN_PUSH_VLAN_ID
, vid
);
814 nl_msg_put_u8(request
, TCA_VLAN_PUSH_VLAN_PRIORITY
, prio
);
816 nl_msg_end_nested(request
, offset
);
820 nl_msg_put_act_pop_vlan(struct ofpbuf
*request
)
824 nl_msg_put_string(request
, TCA_ACT_KIND
, "vlan");
825 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
827 struct tc_vlan parm
= { .action
= TC_ACT_PIPE
,
828 .v_action
= TCA_VLAN_ACT_POP
};
830 nl_msg_put_unspec(request
, TCA_VLAN_PARMS
, &parm
, sizeof parm
);
832 nl_msg_end_nested(request
, offset
);
836 nl_msg_put_act_tunnel_key_release(struct ofpbuf
*request
)
840 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
841 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
843 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
844 .t_action
= TCA_TUNNEL_KEY_ACT_RELEASE
};
846 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
848 nl_msg_end_nested(request
, offset
);
852 nl_msg_put_act_tunnel_key_set(struct ofpbuf
*request
, ovs_be64 id
,
853 ovs_be32 ipv4_src
, ovs_be32 ipv4_dst
,
854 struct in6_addr
*ipv6_src
,
855 struct in6_addr
*ipv6_dst
,
860 nl_msg_put_string(request
, TCA_ACT_KIND
, "tunnel_key");
861 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
863 struct tc_tunnel_key tun
= { .action
= TC_ACT_PIPE
,
864 .t_action
= TCA_TUNNEL_KEY_ACT_SET
};
866 nl_msg_put_unspec(request
, TCA_TUNNEL_KEY_PARMS
, &tun
, sizeof tun
);
868 ovs_be32 id32
= be64_to_be32(id
);
869 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_KEY_ID
, id32
);
871 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_SRC
, ipv4_src
);
872 nl_msg_put_be32(request
, TCA_TUNNEL_KEY_ENC_IPV4_DST
, ipv4_dst
);
873 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
874 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_DST
,
876 nl_msg_put_in6_addr(request
, TCA_TUNNEL_KEY_ENC_IPV6_SRC
,
879 nl_msg_put_be16(request
, TCA_TUNNEL_KEY_ENC_DST_PORT
, tp_dst
);
881 nl_msg_end_nested(request
, offset
);
885 nl_msg_put_act_drop(struct ofpbuf
*request
)
889 nl_msg_put_string(request
, TCA_ACT_KIND
, "gact");
890 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
892 struct tc_gact p
= { .action
= TC_ACT_SHOT
};
894 nl_msg_put_unspec(request
, TCA_GACT_PARMS
, &p
, sizeof p
);
896 nl_msg_end_nested(request
, offset
);
900 nl_msg_put_act_redirect(struct ofpbuf
*request
, int ifindex
)
904 nl_msg_put_string(request
, TCA_ACT_KIND
, "mirred");
905 offset
= nl_msg_start_nested(request
, TCA_ACT_OPTIONS
);
907 struct tc_mirred m
= { .action
= TC_ACT_STOLEN
,
908 .eaction
= TCA_EGRESS_REDIR
,
909 .ifindex
= ifindex
};
911 nl_msg_put_unspec(request
, TCA_MIRRED_PARMS
, &m
, sizeof m
);
913 nl_msg_end_nested(request
, offset
);
917 nl_msg_put_act_cookie(struct ofpbuf
*request
, struct tc_cookie
*ck
) {
919 nl_msg_put_unspec(request
, TCA_ACT_COOKIE
, ck
->data
, ck
->len
);
924 nl_msg_put_flower_acts(struct ofpbuf
*request
, struct tc_flower
*flower
)
929 offset
= nl_msg_start_nested(request
, TCA_FLOWER_ACT
);
931 uint16_t act_index
= 1;
933 if (flower
->set
.set
) {
934 act_offset
= nl_msg_start_nested(request
, act_index
++);
935 nl_msg_put_act_tunnel_key_set(request
, flower
->set
.id
,
936 flower
->set
.ipv4
.ipv4_src
,
937 flower
->set
.ipv4
.ipv4_dst
,
938 &flower
->set
.ipv6
.ipv6_src
,
939 &flower
->set
.ipv6
.ipv6_dst
,
941 nl_msg_end_nested(request
, act_offset
);
943 if (flower
->tunnel
.tunnel
) {
944 act_offset
= nl_msg_start_nested(request
, act_index
++);
945 nl_msg_put_act_tunnel_key_release(request
);
946 nl_msg_end_nested(request
, act_offset
);
948 if (flower
->vlan_pop
) {
949 act_offset
= nl_msg_start_nested(request
, act_index
++);
950 nl_msg_put_act_pop_vlan(request
);
951 nl_msg_end_nested(request
, act_offset
);
953 if (flower
->vlan_push_id
) {
954 act_offset
= nl_msg_start_nested(request
, act_index
++);
955 nl_msg_put_act_push_vlan(request
,
956 flower
->vlan_push_id
,
957 flower
->vlan_push_prio
);
958 nl_msg_end_nested(request
, act_offset
);
960 if (flower
->ifindex_out
) {
961 act_offset
= nl_msg_start_nested(request
, act_index
++);
962 nl_msg_put_act_redirect(request
, flower
->ifindex_out
);
963 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
964 nl_msg_end_nested(request
, act_offset
);
966 act_offset
= nl_msg_start_nested(request
, act_index
++);
967 nl_msg_put_act_drop(request
);
968 nl_msg_put_act_cookie(request
, &flower
->act_cookie
);
969 nl_msg_end_nested(request
, act_offset
);
972 nl_msg_end_nested(request
, offset
);
976 nl_msg_put_masked_value(struct ofpbuf
*request
, uint16_t type
,
977 uint16_t mask_type
, const void *data
,
978 const void *mask_data
, size_t len
)
980 if (mask_type
!= TCA_FLOWER_UNSPEC
) {
981 if (is_all_zeros(mask_data
, len
)) {
984 nl_msg_put_unspec(request
, mask_type
, mask_data
, len
);
986 nl_msg_put_unspec(request
, type
, data
, len
);
990 nl_msg_put_flower_tunnel(struct ofpbuf
*request
, struct tc_flower
*flower
)
992 ovs_be32 ipv4_src
= flower
->tunnel
.ipv4
.ipv4_src
;
993 ovs_be32 ipv4_dst
= flower
->tunnel
.ipv4
.ipv4_dst
;
994 struct in6_addr
*ipv6_src
= &flower
->tunnel
.ipv6
.ipv6_src
;
995 struct in6_addr
*ipv6_dst
= &flower
->tunnel
.ipv6
.ipv6_dst
;
996 ovs_be16 tp_dst
= flower
->tunnel
.tp_dst
;
997 ovs_be32 id
= be64_to_be32(flower
->tunnel
.id
);
999 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_KEY_ID
, id
);
1001 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_SRC
, ipv4_src
);
1002 nl_msg_put_be32(request
, TCA_FLOWER_KEY_ENC_IPV4_DST
, ipv4_dst
);
1003 } else if (!is_all_zeros(ipv6_dst
, sizeof *ipv6_dst
)) {
1004 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_SRC
, ipv6_src
);
1005 nl_msg_put_in6_addr(request
, TCA_FLOWER_KEY_ENC_IPV6_DST
, ipv6_dst
);
1007 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ENC_UDP_DST_PORT
, tp_dst
);
1010 #define FLOWER_PUT_MASKED_VALUE(member, type) \
1011 nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \
1012 &flower->mask.member, sizeof flower->key.member)
1015 nl_msg_put_flower_options(struct ofpbuf
*request
, struct tc_flower
*flower
)
1017 uint16_t host_eth_type
= ntohs(flower
->key
.eth_type
);
1018 bool is_vlan
= (host_eth_type
== ETH_TYPE_VLAN
);
1021 host_eth_type
= ntohs(flower
->key
.encap_eth_type
);
1024 FLOWER_PUT_MASKED_VALUE(dst_mac
, TCA_FLOWER_KEY_ETH_DST
);
1025 FLOWER_PUT_MASKED_VALUE(src_mac
, TCA_FLOWER_KEY_ETH_SRC
);
1027 if (host_eth_type
== ETH_P_IP
|| host_eth_type
== ETH_P_IPV6
) {
1028 if (flower
->mask
.ip_proto
&& flower
->key
.ip_proto
) {
1029 nl_msg_put_u8(request
, TCA_FLOWER_KEY_IP_PROTO
,
1030 flower
->key
.ip_proto
);
1033 if (flower
->key
.ip_proto
== IPPROTO_UDP
) {
1034 FLOWER_PUT_MASKED_VALUE(udp_src
, TCA_FLOWER_KEY_UDP_SRC
);
1035 FLOWER_PUT_MASKED_VALUE(udp_dst
, TCA_FLOWER_KEY_UDP_DST
);
1036 } else if (flower
->key
.ip_proto
== IPPROTO_TCP
) {
1037 FLOWER_PUT_MASKED_VALUE(tcp_src
, TCA_FLOWER_KEY_TCP_SRC
);
1038 FLOWER_PUT_MASKED_VALUE(tcp_dst
, TCA_FLOWER_KEY_TCP_DST
);
1039 } else if (flower
->key
.ip_proto
== IPPROTO_SCTP
) {
1040 FLOWER_PUT_MASKED_VALUE(sctp_src
, TCA_FLOWER_KEY_SCTP_SRC
);
1041 FLOWER_PUT_MASKED_VALUE(sctp_dst
, TCA_FLOWER_KEY_SCTP_DST
);
1045 if (host_eth_type
== ETH_P_IP
) {
1046 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_src
, TCA_FLOWER_KEY_IPV4_SRC
);
1047 FLOWER_PUT_MASKED_VALUE(ipv4
.ipv4_dst
, TCA_FLOWER_KEY_IPV4_DST
);
1048 FLOWER_PUT_MASKED_VALUE(ip_ttl
, TCA_FLOWER_KEY_IP_TTL
);
1049 } else if (host_eth_type
== ETH_P_IPV6
) {
1050 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_src
, TCA_FLOWER_KEY_IPV6_SRC
);
1051 FLOWER_PUT_MASKED_VALUE(ipv6
.ipv6_dst
, TCA_FLOWER_KEY_IPV6_DST
);
1054 nl_msg_put_be16(request
, TCA_FLOWER_KEY_ETH_TYPE
, flower
->key
.eth_type
);
1057 if (flower
->key
.vlan_id
|| flower
->key
.vlan_prio
) {
1058 nl_msg_put_u16(request
, TCA_FLOWER_KEY_VLAN_ID
,
1059 flower
->key
.vlan_id
);
1060 nl_msg_put_u8(request
, TCA_FLOWER_KEY_VLAN_PRIO
,
1061 flower
->key
.vlan_prio
);
1063 if (flower
->key
.encap_eth_type
) {
1064 nl_msg_put_be16(request
, TCA_FLOWER_KEY_VLAN_ETH_TYPE
,
1065 flower
->key
.encap_eth_type
);
1069 nl_msg_put_u32(request
, TCA_FLOWER_FLAGS
, tc_get_tc_cls_policy(tc_policy
));
1071 if (flower
->tunnel
.tunnel
) {
1072 nl_msg_put_flower_tunnel(request
, flower
);
1075 nl_msg_put_flower_acts(request
, flower
);
1079 tc_replace_flower(int ifindex
, uint16_t prio
, uint32_t handle
,
1080 struct tc_flower
*flower
)
1082 struct ofpbuf request
;
1083 struct tcmsg
*tcmsg
;
1084 struct ofpbuf
*reply
;
1086 size_t basic_offset
;
1087 uint16_t eth_type
= (OVS_FORCE
uint16_t) flower
->key
.eth_type
;
1089 tcmsg
= tc_make_request(ifindex
, RTM_NEWTFILTER
,
1090 NLM_F_CREATE
| NLM_F_ECHO
, &request
);
1091 tcmsg
->tcm_parent
= TC_INGRESS_PARENT
;
1092 tcmsg
->tcm_info
= tc_make_handle(prio
, eth_type
);
1093 tcmsg
->tcm_handle
= handle
;
1095 nl_msg_put_string(&request
, TCA_KIND
, "flower");
1096 basic_offset
= nl_msg_start_nested(&request
, TCA_OPTIONS
);
1098 nl_msg_put_flower_options(&request
, flower
);
1100 nl_msg_end_nested(&request
, basic_offset
);
1102 error
= tc_transact(&request
, &reply
);
1105 ofpbuf_at_assert(reply
, NLMSG_HDRLEN
, sizeof *tc
);
1107 flower
->prio
= tc_get_major(tc
->tcm_info
);
1108 flower
->handle
= tc
->tcm_handle
;
1109 ofpbuf_delete(reply
);
1116 tc_set_policy(const char *policy
)
1122 if (!strcmp(policy
, "skip_sw")) {
1123 tc_policy
= TC_POLICY_SKIP_SW
;
1124 } else if (!strcmp(policy
, "skip_hw")) {
1125 tc_policy
= TC_POLICY_SKIP_HW
;
1126 } else if (!strcmp(policy
, "none")) {
1127 tc_policy
= TC_POLICY_NONE
;
1129 VLOG_WARN("tc: Invalid policy '%s'", policy
);
1133 VLOG_INFO("tc: Using policy '%s'", policy
);