]> git.proxmox.com Git - mirror_ovs.git/blame - lib/tc.c
odp-util: Expose ovs flow key attr len table for reuse
[mirror_ovs.git] / lib / tc.c
CommitLineData
c1c5c723 1/*
ef3767f5 2 * Copyright (c) 2009-2017 Nicira, Inc.
f98e418f 3 * Copyright (c) 2016 Mellanox Technologies, Ltd.
c1c5c723
PB
4 *
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:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18#include <config.h>
19#include "tc.h"
ef3767f5 20
c1c5c723 21#include <errno.h>
f98e418f
RD
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>
29#include <net/if.h>
8c1e74d1 30#include <unistd.h>
ef3767f5 31
f98e418f 32#include "byte-order.h"
c1c5c723
PB
33#include "netlink-socket.h"
34#include "netlink.h"
35#include "openvswitch/ofpbuf.h"
36#include "openvswitch/vlog.h"
f98e418f
RD
37#include "packets.h"
38#include "timeval.h"
ef3767f5 39#include "unaligned.h"
c1c5c723
PB
40
41VLOG_DEFINE_THIS_MODULE(tc);
42
f98e418f
RD
43static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
44
691d20cb
PB
45enum tc_offload_policy {
46 TC_POLICY_NONE,
47 TC_POLICY_SKIP_SW,
48 TC_POLICY_SKIP_HW
49};
50
51static enum tc_offload_policy tc_policy = TC_POLICY_NONE;
52
c1c5c723
PB
53struct tcmsg *
54tc_make_request(int ifindex, int type, unsigned int flags,
55 struct ofpbuf *request)
56{
57 struct tcmsg *tcmsg;
58
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. */
66
67 return tcmsg;
68}
69
70int
71tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
72{
73 int error = nl_transact(NETLINK_ROUTE, request, replyp);
74 ofpbuf_uninit(request);
75 return error;
76}
77
78/* Adds or deletes a root ingress qdisc on device with specified ifindex.
79 *
80 * This function is equivalent to running the following when 'add' is true:
81 * /sbin/tc qdisc add dev <devname> handle ffff: ingress
82 *
83 * This function is equivalent to running the following when 'add' is false:
84 * /sbin/tc qdisc del dev <devname> handle ffff: ingress
85 *
86 * Where dev <devname> is the device with specified ifindex name.
87 *
88 * The configuration and stats may be seen with the following command:
89 * /sbin/tc -s qdisc show dev <devname>
90 *
91 * Returns 0 if successful, otherwise a positive errno value.
92 */
93int
94tc_add_del_ingress_qdisc(int ifindex, bool add)
95{
96 struct ofpbuf request;
97 struct tcmsg *tcmsg;
98 int error;
99 int type = add ? RTM_NEWQDISC : RTM_DELQDISC;
100 int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
101
102 tcmsg = tc_make_request(ifindex, type, flags, &request);
209832d5 103 tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
c1c5c723
PB
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);
107
108 error = tc_transact(&request, NULL);
109 if (error) {
110 /* If we're deleting the qdisc, don't worry about some of the
111 * error conditions. */
112 if (!add && (error == ENOENT || error == EINVAL)) {
113 return 0;
114 }
115 return error;
116 }
117
118 return 0;
119}
f98e418f
RD
120
121static 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, },
127};
128
129static 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,
132 .optional = true, },
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,
138 .min_len = ETH_ALEN,
139 .optional = true, },
140 [TCA_FLOWER_KEY_ETH_DST_MASK] = { .type = NL_A_UNSPEC,
141 .min_len = ETH_ALEN,
142 .optional = true, },
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),
153 .optional = true, },
154 [TCA_FLOWER_KEY_IPV6_DST] = { .type = NL_A_UNSPEC,
155 .min_len = sizeof(struct in6_addr),
156 .optional = true, },
157 [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
158 .min_len = sizeof(struct in6_addr),
159 .optional = true, },
160 [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
161 .min_len = sizeof(struct in6_addr),
162 .optional = true, },
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, },
4862b4e5
VB
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, },
f98e418f
RD
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,
182 .optional = true, },
183 [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NL_A_U32,
184 .optional = true, },
185 [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
186 .min_len = sizeof(struct in6_addr),
187 .optional = true, },
188 [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
189 .min_len = sizeof(struct in6_addr),
190 .optional = true, },
191 [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
192 .min_len = sizeof(struct in6_addr),
193 .optional = true, },
194 [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
195 .min_len = sizeof(struct in6_addr),
196 .optional = true, },
197 [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NL_A_U16,
198 .optional = true, },
0b4b5203
PB
199 [TCA_FLOWER_KEY_IP_TTL] = { .type = NL_A_U8,
200 .optional = true, },
201 [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NL_A_U8,
202 .optional = true, },
cd081043
PB
203 [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NL_A_U16,
204 .optional = true, },
205 [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16,
206 .optional = true, },
f98e418f
RD
207};
208
209static void
210nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower)
211{
212 const struct eth_addr *eth;
213
214 if (attrs[TCA_FLOWER_KEY_ETH_SRC_MASK]) {
215 eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC], ETH_ALEN);
216 memcpy(&flower->key.src_mac, eth, sizeof flower->key.src_mac);
217
218 eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC_MASK], ETH_ALEN);
219 memcpy(&flower->mask.src_mac, eth, sizeof flower->mask.src_mac);
220 }
221 if (attrs[TCA_FLOWER_KEY_ETH_DST_MASK]) {
222 eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST], ETH_ALEN);
223 memcpy(&flower->key.dst_mac, eth, sizeof flower->key.dst_mac);
224
225 eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST_MASK], ETH_ALEN);
226 memcpy(&flower->mask.dst_mac, eth, sizeof flower->mask.dst_mac);
227 }
228}
229
230static void
231nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
232{
233 if (flower->key.eth_type != htons(ETH_TYPE_VLAN)) {
234 return;
235 }
236
237 flower->key.encap_eth_type =
238 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
239
240 if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
241 flower->key.vlan_id =
242 nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
243 }
244 if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
245 flower->key.vlan_prio =
246 nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
247 }
248}
249
250static void
251nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower)
252{
253 if (attrs[TCA_FLOWER_KEY_ENC_KEY_ID]) {
254 ovs_be32 id = nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_KEY_ID]);
255
256 flower->tunnel.id = be32_to_be64(id);
257 }
258 if (attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK]) {
259 flower->tunnel.ipv4.ipv4_src =
260 nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC]);
261 }
262 if (attrs[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK]) {
263 flower->tunnel.ipv4.ipv4_dst =
264 nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_DST]);
265 }
266 if (attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]) {
267 flower->tunnel.ipv6.ipv6_src =
268 nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC]);
269 }
270 if (attrs[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]) {
271 flower->tunnel.ipv6.ipv6_dst =
272 nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_DST]);
273 }
274 if (attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]) {
275 flower->tunnel.tp_dst =
276 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]);
277 }
278}
279
280static void
281nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) {
282 uint8_t ip_proto = 0;
283 struct tc_flower_key *key = &flower->key;
284 struct tc_flower_key *mask = &flower->mask;
285
286 if (attrs[TCA_FLOWER_KEY_IP_PROTO]) {
287 ip_proto = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_PROTO]);
288 key->ip_proto = ip_proto;
289 mask->ip_proto = UINT8_MAX;
290 }
291
292 if (attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]) {
293 key->ipv4.ipv4_src =
294 nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC]);
295 mask->ipv4.ipv4_src =
296 nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]);
297 }
298 if (attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]) {
299 key->ipv4.ipv4_dst =
300 nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST]);
301 mask->ipv4.ipv4_dst =
302 nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]);
303 }
304 if (attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK]) {
305 struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_SRC];
306 struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK];
307
308 key->ipv6.ipv6_src = nl_attr_get_in6_addr(attr);
309 mask->ipv6.ipv6_src = nl_attr_get_in6_addr(attr_mask);
310 }
311 if (attrs[TCA_FLOWER_KEY_IPV6_DST_MASK]) {
312 struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_DST];
313 struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_DST_MASK];
314
315 key->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr);
316 mask->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr_mask);
317 }
318
319 if (ip_proto == IPPROTO_TCP) {
320 if (attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]) {
2b1d9fa9 321 key->tcp_src =
f98e418f 322 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC]);
2b1d9fa9 323 mask->tcp_src =
f98e418f
RD
324 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]);
325 }
326 if (attrs[TCA_FLOWER_KEY_TCP_DST_MASK]) {
2b1d9fa9 327 key->tcp_dst =
f98e418f 328 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST]);
2b1d9fa9 329 mask->tcp_dst =
f98e418f
RD
330 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST_MASK]);
331 }
cd081043
PB
332 if (attrs[TCA_FLOWER_KEY_TCP_FLAGS_MASK]) {
333 key->tcp_flags =
334 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_FLAGS]);
335 mask->tcp_flags =
336 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_FLAGS_MASK]);
337 }
f98e418f
RD
338 } else if (ip_proto == IPPROTO_UDP) {
339 if (attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]) {
2b1d9fa9
PB
340 key->udp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC]);
341 mask->udp_src =
f98e418f
RD
342 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]);
343 }
344 if (attrs[TCA_FLOWER_KEY_UDP_DST_MASK]) {
2b1d9fa9
PB
345 key->udp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST]);
346 mask->udp_dst =
f98e418f
RD
347 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST_MASK]);
348 }
4862b4e5
VB
349 } else if (ip_proto == IPPROTO_SCTP) {
350 if (attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]) {
2b1d9fa9
PB
351 key->sctp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC]);
352 mask->sctp_src =
4862b4e5
VB
353 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]);
354 }
355 if (attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]) {
2b1d9fa9
PB
356 key->sctp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST]);
357 mask->sctp_dst =
4862b4e5
VB
358 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]);
359 }
f98e418f 360 }
0b4b5203
PB
361
362 if (attrs[TCA_FLOWER_KEY_IP_TTL_MASK]) {
363 key->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL]);
364 mask->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL_MASK]);
365 }
f98e418f
RD
366}
367
368static const struct nl_policy tunnel_key_policy[] = {
369 [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
370 .min_len = sizeof(struct tc_tunnel_key),
371 .optional = false, },
372 [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
373 [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
374 [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
375 .min_len = sizeof(struct in6_addr),
376 .optional = true, },
377 [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
378 .min_len = sizeof(struct in6_addr),
379 .optional = true, },
380 [TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NL_A_U32, .optional = true, },
381 [TCA_TUNNEL_KEY_ENC_DST_PORT] = { .type = NL_A_U16, .optional = true, },
382};
383
384static int
385nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
386{
387 struct nlattr *tun_attrs[ARRAY_SIZE(tunnel_key_policy)];
388 const struct nlattr *tun_parms;
389 const struct tc_tunnel_key *tun;
390
391 if (!nl_parse_nested(options, tunnel_key_policy, tun_attrs,
392 ARRAY_SIZE(tunnel_key_policy))) {
393 VLOG_ERR_RL(&error_rl, "failed to parse tunnel_key action options");
394 return EPROTO;
395 }
396
397 tun_parms = tun_attrs[TCA_TUNNEL_KEY_PARMS];
398 tun = nl_attr_get_unspec(tun_parms, sizeof *tun);
399 if (tun->t_action == TCA_TUNNEL_KEY_ACT_SET) {
400 struct nlattr *id = tun_attrs[TCA_TUNNEL_KEY_ENC_KEY_ID];
401 struct nlattr *dst_port = tun_attrs[TCA_TUNNEL_KEY_ENC_DST_PORT];
402 struct nlattr *ipv4_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_SRC];
403 struct nlattr *ipv4_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_DST];
404 struct nlattr *ipv6_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_SRC];
405 struct nlattr *ipv6_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_DST];
406
407 flower->set.set = true;
408 flower->set.ipv4.ipv4_src = ipv4_src ? nl_attr_get_be32(ipv4_src) : 0;
409 flower->set.ipv4.ipv4_dst = ipv4_dst ? nl_attr_get_be32(ipv4_dst) : 0;
410 if (ipv6_src) {
411 flower->set.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src);
412 }
413 if (ipv6_dst) {
414 flower->set.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst);
415 }
416 flower->set.id = id ? be32_to_be64(nl_attr_get_be32(id)) : 0;
417 flower->set.tp_dst = dst_port ? nl_attr_get_be16(dst_port) : 0;
418 } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) {
419 flower->tunnel.tunnel = true;
420 } else {
421 VLOG_ERR_RL(&error_rl, "unknown tunnel actions: %d, %d",
422 tun->action, tun->t_action);
423 return EINVAL;
424 }
425 return 0;
426}
427
428static const struct nl_policy gact_policy[] = {
429 [TCA_GACT_PARMS] = { .type = NL_A_UNSPEC,
430 .min_len = sizeof(struct tc_gact),
431 .optional = false, },
432 [TCA_GACT_TM] = { .type = NL_A_UNSPEC,
433 .min_len = sizeof(struct tcf_t),
434 .optional = false, },
435};
436
8c1e74d1
PB
437static int
438get_user_hz(void)
439{
440 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
441 static int user_hz = 100;
442
443 if (ovsthread_once_start(&once)) {
444 user_hz = sysconf(_SC_CLK_TCK);
445 ovsthread_once_done(&once);
446 }
447
448 return user_hz;
449}
f98e418f
RD
450
451static void
452nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
453{
8c1e74d1 454 flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
f98e418f
RD
455}
456
457static int
458nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower)
459{
460 struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)];
461 const struct tc_gact *p;
462 struct nlattr *gact_parms;
463 const struct tcf_t *tm;
464
465 if (!nl_parse_nested(options, gact_policy, gact_attrs,
466 ARRAY_SIZE(gact_policy))) {
467 VLOG_ERR_RL(&error_rl, "failed to parse gact action options");
468 return EPROTO;
469 }
470
471 gact_parms = gact_attrs[TCA_GACT_PARMS];
472 p = nl_attr_get_unspec(gact_parms, sizeof *p);
473
474 if (p->action != TC_ACT_SHOT) {
475 VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action);
476 return EINVAL;
477 }
478
479 tm = nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof *tm);
480 nl_parse_tcf(tm, flower);
481
482 return 0;
483}
484
485static const struct nl_policy mirred_policy[] = {
486 [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC,
487 .min_len = sizeof(struct tc_mirred),
488 .optional = false, },
489 [TCA_MIRRED_TM] = { .type = NL_A_UNSPEC,
490 .min_len = sizeof(struct tcf_t),
491 .optional = false, },
492};
493
494static int
495nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
496{
497
498 struct nlattr *mirred_attrs[ARRAY_SIZE(mirred_policy)];
499 const struct tc_mirred *m;
500 const struct nlattr *mirred_parms;
501 const struct tcf_t *tm;
502 struct nlattr *mirred_tm;
503
504 if (!nl_parse_nested(options, mirred_policy, mirred_attrs,
505 ARRAY_SIZE(mirred_policy))) {
506 VLOG_ERR_RL(&error_rl, "failed to parse mirred action options");
507 return EPROTO;
508 }
509
510 mirred_parms = mirred_attrs[TCA_MIRRED_PARMS];
511 m = nl_attr_get_unspec(mirred_parms, sizeof *m);
512
513 if (m->action != TC_ACT_STOLEN || m->eaction != TCA_EGRESS_REDIR) {
514 VLOG_ERR_RL(&error_rl, "unknown mirred action: %d, %d, %d",
515 m->action, m->eaction, m->ifindex);
516 return EINVAL;
517 }
518
519 flower->ifindex_out = m->ifindex;
520
521 mirred_tm = mirred_attrs[TCA_MIRRED_TM];
522 tm = nl_attr_get_unspec(mirred_tm, sizeof *tm);
523 nl_parse_tcf(tm, flower);
524
525 return 0;
526}
527
528static const struct nl_policy vlan_policy[] = {
529 [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
530 .min_len = sizeof(struct tc_vlan),
531 .optional = false, },
532 [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
533 [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
534 [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
535};
536
537static int
538nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
539{
540 struct nlattr *vlan_attrs[ARRAY_SIZE(vlan_policy)];
541 const struct tc_vlan *v;
542 const struct nlattr *vlan_parms;
543
544 if (!nl_parse_nested(options, vlan_policy, vlan_attrs,
545 ARRAY_SIZE(vlan_policy))) {
546 VLOG_ERR_RL(&error_rl, "failed to parse vlan action options");
547 return EPROTO;
548 }
549
550 vlan_parms = vlan_attrs[TCA_VLAN_PARMS];
551 v = nl_attr_get_unspec(vlan_parms, sizeof *v);
552 if (v->v_action == TCA_VLAN_ACT_PUSH) {
553 struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID];
554 struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY];
555
556 flower->vlan_push_id = nl_attr_get_u16(vlan_id);
557 flower->vlan_push_prio = vlan_prio ? nl_attr_get_u8(vlan_prio) : 0;
558 } else if (v->v_action == TCA_VLAN_ACT_POP) {
559 flower->vlan_pop = 1;
560 } else {
561 VLOG_ERR_RL(&error_rl, "unknown vlan action: %d, %d",
562 v->action, v->v_action);
563 return EINVAL;
564 }
565 return 0;
566}
567
568static const struct nl_policy act_policy[] = {
569 [TCA_ACT_KIND] = { .type = NL_A_STRING, .optional = false, },
570 [TCA_ACT_COOKIE] = { .type = NL_A_UNSPEC, .optional = true, },
571 [TCA_ACT_OPTIONS] = { .type = NL_A_NESTED, .optional = false, },
572 [TCA_ACT_STATS] = { .type = NL_A_NESTED, .optional = false, },
573};
574
575static const struct nl_policy stats_policy[] = {
576 [TCA_STATS_BASIC] = { .type = NL_A_UNSPEC,
577 .min_len = sizeof(struct gnet_stats_basic),
578 .optional = false, },
579};
580
581static int
582nl_parse_single_action(struct nlattr *action, struct tc_flower *flower)
583{
584 struct nlattr *act_options;
585 struct nlattr *act_stats;
586 struct nlattr *act_cookie;
587 const char *act_kind;
588 struct nlattr *action_attrs[ARRAY_SIZE(act_policy)];
589 struct nlattr *stats_attrs[ARRAY_SIZE(stats_policy)];
590 struct ovs_flow_stats *stats = &flower->stats;
591 const struct gnet_stats_basic *bs;
592
593 if (!nl_parse_nested(action, act_policy, action_attrs,
594 ARRAY_SIZE(act_policy))) {
595 VLOG_ERR_RL(&error_rl, "failed to parse single action options");
596 return EPROTO;
597 }
598
599 act_kind = nl_attr_get_string(action_attrs[TCA_ACT_KIND]);
600 act_options = action_attrs[TCA_ACT_OPTIONS];
601 act_cookie = action_attrs[TCA_ACT_COOKIE];
602
603 if (!strcmp(act_kind, "gact")) {
604 nl_parse_act_drop(act_options, flower);
605 } else if (!strcmp(act_kind, "mirred")) {
606 nl_parse_act_mirred(act_options, flower);
607 } else if (!strcmp(act_kind, "vlan")) {
608 nl_parse_act_vlan(act_options, flower);
609 } else if (!strcmp(act_kind, "tunnel_key")) {
610 nl_parse_act_tunnel_key(act_options, flower);
611 } else {
612 VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
613 return EINVAL;
614 }
615
616 if (act_cookie) {
617 flower->act_cookie.data = nl_attr_get(act_cookie);
618 flower->act_cookie.len = nl_attr_get_size(act_cookie);
619 }
620
621 act_stats = action_attrs[TCA_ACT_STATS];
622
623 if (!nl_parse_nested(act_stats, stats_policy, stats_attrs,
624 ARRAY_SIZE(stats_policy))) {
625 VLOG_ERR_RL(&error_rl, "failed to parse action stats policy");
626 return EPROTO;
627 }
628
629 bs = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs);
630 put_32aligned_u64(&stats->n_packets, bs->packets);
631 put_32aligned_u64(&stats->n_bytes, bs->bytes);
632
633 return 0;
634}
635
636#define TCA_ACT_MIN_PRIO 1
637
638static int
639nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower)
640{
641 const struct nlattr *actions = attrs[TCA_FLOWER_ACT];
642 static struct nl_policy actions_orders_policy[TCA_ACT_MAX_PRIO + 1] = {};
643 struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
644 const int max_size = ARRAY_SIZE(actions_orders_policy);
645
646 for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
647 actions_orders_policy[i].type = NL_A_NESTED;
648 actions_orders_policy[i].optional = true;
649 }
650
651 if (!nl_parse_nested(actions, actions_orders_policy, actions_orders,
652 ARRAY_SIZE(actions_orders_policy))) {
653 VLOG_ERR_RL(&error_rl, "failed to parse flower order of actions");
654 return EPROTO;
655 }
656
657 for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
658 if (actions_orders[i]) {
659 int err = nl_parse_single_action(actions_orders[i], flower);
660
661 if (err) {
662 return err;
663 }
664 }
665 }
666
667 return 0;
668}
669
670static int
671nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower)
672{
673 struct nlattr *attrs[ARRAY_SIZE(tca_flower_policy)];
674
675 if (!nl_parse_nested(nl_options, tca_flower_policy,
676 attrs, ARRAY_SIZE(tca_flower_policy))) {
677 VLOG_ERR_RL(&error_rl, "failed to parse flower classifier options");
678 return EPROTO;
679 }
680
681 nl_parse_flower_eth(attrs, flower);
682 nl_parse_flower_vlan(attrs, flower);
683 nl_parse_flower_ip(attrs, flower);
684 nl_parse_flower_tunnel(attrs, flower);
685 return nl_parse_flower_actions(attrs, flower);
686}
687
688int
689parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
690{
691 struct tcmsg *tc;
692 struct nlattr *ta[ARRAY_SIZE(tca_policy)];
693 const char *kind;
694
695 if (NLMSG_HDRLEN + sizeof *tc > reply->size) {
696 return EPROTO;
697 }
698
699 memset(flower, 0, sizeof *flower);
700
701 tc = ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc);
702 flower->handle = tc->tcm_handle;
703 flower->key.eth_type = (OVS_FORCE ovs_be16) tc_get_minor(tc->tcm_info);
704 flower->mask.eth_type = OVS_BE16_MAX;
705 flower->prio = tc_get_major(tc->tcm_info);
706
707 if (!flower->handle) {
708 return EAGAIN;
709 }
710
711 if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof *tc,
712 tca_policy, ta, ARRAY_SIZE(ta))) {
713 VLOG_ERR_RL(&error_rl, "failed to parse tca policy");
714 return EPROTO;
715 }
716
717 kind = nl_attr_get_string(ta[TCA_KIND]);
718 if (strcmp(kind, "flower")) {
719 VLOG_ERR_RL(&error_rl, "failed to parse filter: %s", kind);
720 return EPROTO;
721 }
722
723 return nl_parse_flower_options(ta[TCA_OPTIONS], flower);
724}
725
726int
727tc_dump_flower_start(int ifindex, struct nl_dump *dump)
728{
729 struct ofpbuf request;
730 struct tcmsg *tcmsg;
731
732 tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_DUMP, &request);
733 tcmsg->tcm_parent = TC_INGRESS_PARENT;
734 tcmsg->tcm_info = TC_H_UNSPEC;
735 tcmsg->tcm_handle = 0;
736
737 nl_dump_start(dump, NETLINK_ROUTE, &request);
738 ofpbuf_uninit(&request);
739
740 return 0;
741}
742
743int
744tc_flush(int ifindex)
745{
746 struct ofpbuf request;
747 struct tcmsg *tcmsg;
748
749 tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ACK, &request);
750 tcmsg->tcm_parent = TC_INGRESS_PARENT;
751 tcmsg->tcm_info = TC_H_UNSPEC;
752
753 return tc_transact(&request, NULL);
754}
755
756int
757tc_del_filter(int ifindex, int prio, int handle)
758{
759 struct ofpbuf request;
760 struct tcmsg *tcmsg;
761 struct ofpbuf *reply;
762 int error;
763
764 tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ECHO, &request);
765 tcmsg->tcm_parent = TC_INGRESS_PARENT;
766 tcmsg->tcm_info = tc_make_handle(prio, 0);
767 tcmsg->tcm_handle = handle;
768
769 error = tc_transact(&request, &reply);
770 if (!error) {
771 ofpbuf_delete(reply);
772 }
773 return error;
774}
775
776int
777tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower)
778{
779 struct ofpbuf request;
780 struct tcmsg *tcmsg;
781 struct ofpbuf *reply;
782 int error;
783
784 tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_ECHO, &request);
785 tcmsg->tcm_parent = TC_INGRESS_PARENT;
786 tcmsg->tcm_info = tc_make_handle(prio, 0);
787 tcmsg->tcm_handle = handle;
788
789 error = tc_transact(&request, &reply);
790 if (error) {
791 return error;
792 }
793
794 error = parse_netlink_to_tc_flower(reply, flower);
795 ofpbuf_delete(reply);
796 return error;
797}
798
691d20cb
PB
799static int
800tc_get_tc_cls_policy(enum tc_offload_policy policy)
801{
802 if (policy == TC_POLICY_SKIP_HW) {
803 return TCA_CLS_FLAGS_SKIP_HW;
804 } else if (policy == TC_POLICY_SKIP_SW) {
805 return TCA_CLS_FLAGS_SKIP_SW;
806 }
807
808 return 0;
809}
810
f98e418f
RD
811static void
812nl_msg_put_act_push_vlan(struct ofpbuf *request, uint16_t vid, uint8_t prio)
813{
814 size_t offset;
815
816 nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
817 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
818 {
819 struct tc_vlan parm = { .action = TC_ACT_PIPE,
820 .v_action = TCA_VLAN_ACT_PUSH };
821
822 nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
823 nl_msg_put_u16(request, TCA_VLAN_PUSH_VLAN_ID, vid);
824 nl_msg_put_u8(request, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
825 }
826 nl_msg_end_nested(request, offset);
827}
828
829static void
830nl_msg_put_act_pop_vlan(struct ofpbuf *request)
831{
832 size_t offset;
833
834 nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
835 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
836 {
837 struct tc_vlan parm = { .action = TC_ACT_PIPE,
838 .v_action = TCA_VLAN_ACT_POP };
839
840 nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
841 }
842 nl_msg_end_nested(request, offset);
843}
844
845static void
846nl_msg_put_act_tunnel_key_release(struct ofpbuf *request)
847{
848 size_t offset;
849
850 nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
851 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
852 {
853 struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
854 .t_action = TCA_TUNNEL_KEY_ACT_RELEASE };
855
856 nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
857 }
858 nl_msg_end_nested(request, offset);
859}
860
861static void
862nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, ovs_be64 id,
863 ovs_be32 ipv4_src, ovs_be32 ipv4_dst,
864 struct in6_addr *ipv6_src,
865 struct in6_addr *ipv6_dst,
866 ovs_be16 tp_dst)
867{
868 size_t offset;
869
870 nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
871 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
872 {
873 struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
874 .t_action = TCA_TUNNEL_KEY_ACT_SET };
875
876 nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
877
878 ovs_be32 id32 = be64_to_be32(id);
879 nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_KEY_ID, id32);
880 if (ipv4_dst) {
881 nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_IPV4_SRC, ipv4_src);
882 nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_IPV4_DST, ipv4_dst);
883 } else if (!is_all_zeros(ipv6_dst, sizeof *ipv6_dst)) {
884 nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_DST,
885 ipv6_dst);
886 nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_SRC,
887 ipv6_src);
888 }
889 nl_msg_put_be16(request, TCA_TUNNEL_KEY_ENC_DST_PORT, tp_dst);
890 }
891 nl_msg_end_nested(request, offset);
892}
893
894static void
895nl_msg_put_act_drop(struct ofpbuf *request)
896{
897 size_t offset;
898
899 nl_msg_put_string(request, TCA_ACT_KIND, "gact");
900 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
901 {
902 struct tc_gact p = { .action = TC_ACT_SHOT };
903
904 nl_msg_put_unspec(request, TCA_GACT_PARMS, &p, sizeof p);
905 }
906 nl_msg_end_nested(request, offset);
907}
908
909static void
910nl_msg_put_act_redirect(struct ofpbuf *request, int ifindex)
911{
912 size_t offset;
913
914 nl_msg_put_string(request, TCA_ACT_KIND, "mirred");
915 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
916 {
917 struct tc_mirred m = { .action = TC_ACT_STOLEN,
918 .eaction = TCA_EGRESS_REDIR,
919 .ifindex = ifindex };
920
921 nl_msg_put_unspec(request, TCA_MIRRED_PARMS, &m, sizeof m);
922 }
923 nl_msg_end_nested(request, offset);
924}
925
926static inline void
927nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) {
928 if (ck->len) {
929 nl_msg_put_unspec(request, TCA_ACT_COOKIE, ck->data, ck->len);
930 }
931}
932
933static void
934nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
935{
936 size_t offset;
937 size_t act_offset;
938
939 offset = nl_msg_start_nested(request, TCA_FLOWER_ACT);
940 {
941 uint16_t act_index = 1;
942
943 if (flower->set.set) {
944 act_offset = nl_msg_start_nested(request, act_index++);
945 nl_msg_put_act_tunnel_key_set(request, flower->set.id,
946 flower->set.ipv4.ipv4_src,
947 flower->set.ipv4.ipv4_dst,
948 &flower->set.ipv6.ipv6_src,
949 &flower->set.ipv6.ipv6_dst,
950 flower->set.tp_dst);
951 nl_msg_end_nested(request, act_offset);
952 }
953 if (flower->tunnel.tunnel) {
954 act_offset = nl_msg_start_nested(request, act_index++);
955 nl_msg_put_act_tunnel_key_release(request);
956 nl_msg_end_nested(request, act_offset);
957 }
958 if (flower->vlan_pop) {
959 act_offset = nl_msg_start_nested(request, act_index++);
960 nl_msg_put_act_pop_vlan(request);
961 nl_msg_end_nested(request, act_offset);
962 }
963 if (flower->vlan_push_id) {
964 act_offset = nl_msg_start_nested(request, act_index++);
965 nl_msg_put_act_push_vlan(request,
966 flower->vlan_push_id,
967 flower->vlan_push_prio);
968 nl_msg_end_nested(request, act_offset);
969 }
970 if (flower->ifindex_out) {
971 act_offset = nl_msg_start_nested(request, act_index++);
972 nl_msg_put_act_redirect(request, flower->ifindex_out);
973 nl_msg_put_act_cookie(request, &flower->act_cookie);
974 nl_msg_end_nested(request, act_offset);
975 } else {
976 act_offset = nl_msg_start_nested(request, act_index++);
977 nl_msg_put_act_drop(request);
978 nl_msg_put_act_cookie(request, &flower->act_cookie);
979 nl_msg_end_nested(request, act_offset);
980 }
981 }
982 nl_msg_end_nested(request, offset);
983}
984
985static void
986nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type,
987 uint16_t mask_type, const void *data,
988 const void *mask_data, size_t len)
989{
990 if (mask_type != TCA_FLOWER_UNSPEC) {
991 if (is_all_zeros(mask_data, len)) {
992 return;
993 }
994 nl_msg_put_unspec(request, mask_type, mask_data, len);
995 }
996 nl_msg_put_unspec(request, type, data, len);
997}
998
999static void
1000nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
1001{
1002 ovs_be32 ipv4_src = flower->tunnel.ipv4.ipv4_src;
1003 ovs_be32 ipv4_dst = flower->tunnel.ipv4.ipv4_dst;
1004 struct in6_addr *ipv6_src = &flower->tunnel.ipv6.ipv6_src;
1005 struct in6_addr *ipv6_dst = &flower->tunnel.ipv6.ipv6_dst;
1006 ovs_be16 tp_dst = flower->tunnel.tp_dst;
1007 ovs_be32 id = be64_to_be32(flower->tunnel.id);
1008
1009 nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_KEY_ID, id);
1010 if (ipv4_dst) {
1011 nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_SRC, ipv4_src);
1012 nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_DST, ipv4_dst);
1013 } else if (!is_all_zeros(ipv6_dst, sizeof *ipv6_dst)) {
1014 nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_SRC, ipv6_src);
1015 nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_DST, ipv6_dst);
1016 }
1017 nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, tp_dst);
1018}
1019
bb170644
PB
1020#define FLOWER_PUT_MASKED_VALUE(member, type) \
1021 nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \
1022 &flower->mask.member, sizeof flower->key.member)
1023
f98e418f
RD
1024static void
1025nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
1026{
1027 uint16_t host_eth_type = ntohs(flower->key.eth_type);
1028 bool is_vlan = (host_eth_type == ETH_TYPE_VLAN);
1029
1030 if (is_vlan) {
1031 host_eth_type = ntohs(flower->key.encap_eth_type);
1032 }
1033
bb170644
PB
1034 FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST);
1035 FLOWER_PUT_MASKED_VALUE(src_mac, TCA_FLOWER_KEY_ETH_SRC);
f98e418f
RD
1036
1037 if (host_eth_type == ETH_P_IP || host_eth_type == ETH_P_IPV6) {
1038 if (flower->mask.ip_proto && flower->key.ip_proto) {
1039 nl_msg_put_u8(request, TCA_FLOWER_KEY_IP_PROTO,
1040 flower->key.ip_proto);
1041 }
1042
1043 if (flower->key.ip_proto == IPPROTO_UDP) {
2b1d9fa9
PB
1044 FLOWER_PUT_MASKED_VALUE(udp_src, TCA_FLOWER_KEY_UDP_SRC);
1045 FLOWER_PUT_MASKED_VALUE(udp_dst, TCA_FLOWER_KEY_UDP_DST);
f98e418f 1046 } else if (flower->key.ip_proto == IPPROTO_TCP) {
2b1d9fa9
PB
1047 FLOWER_PUT_MASKED_VALUE(tcp_src, TCA_FLOWER_KEY_TCP_SRC);
1048 FLOWER_PUT_MASKED_VALUE(tcp_dst, TCA_FLOWER_KEY_TCP_DST);
cd081043 1049 FLOWER_PUT_MASKED_VALUE(tcp_flags, TCA_FLOWER_KEY_TCP_FLAGS);
4862b4e5 1050 } else if (flower->key.ip_proto == IPPROTO_SCTP) {
2b1d9fa9
PB
1051 FLOWER_PUT_MASKED_VALUE(sctp_src, TCA_FLOWER_KEY_SCTP_SRC);
1052 FLOWER_PUT_MASKED_VALUE(sctp_dst, TCA_FLOWER_KEY_SCTP_DST);
f98e418f
RD
1053 }
1054 }
1055
1056 if (host_eth_type == ETH_P_IP) {
bb170644
PB
1057 FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC);
1058 FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST);
0b4b5203 1059 FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
f98e418f 1060 } else if (host_eth_type == ETH_P_IPV6) {
bb170644
PB
1061 FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_src, TCA_FLOWER_KEY_IPV6_SRC);
1062 FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_dst, TCA_FLOWER_KEY_IPV6_DST);
f98e418f
RD
1063 }
1064
1065 nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type);
1066
1067 if (is_vlan) {
1068 if (flower->key.vlan_id || flower->key.vlan_prio) {
1069 nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID,
1070 flower->key.vlan_id);
1071 nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO,
1072 flower->key.vlan_prio);
1073 }
1074 if (flower->key.encap_eth_type) {
1075 nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
1076 flower->key.encap_eth_type);
1077 }
1078 }
1079
691d20cb 1080 nl_msg_put_u32(request, TCA_FLOWER_FLAGS, tc_get_tc_cls_policy(tc_policy));
f98e418f
RD
1081
1082 if (flower->tunnel.tunnel) {
1083 nl_msg_put_flower_tunnel(request, flower);
1084 }
1085
1086 nl_msg_put_flower_acts(request, flower);
1087}
1088
1089int
1090tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
1091 struct tc_flower *flower)
1092{
1093 struct ofpbuf request;
1094 struct tcmsg *tcmsg;
1095 struct ofpbuf *reply;
1096 int error = 0;
1097 size_t basic_offset;
1098 uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
1099
1100 tcmsg = tc_make_request(ifindex, RTM_NEWTFILTER,
1101 NLM_F_CREATE | NLM_F_ECHO, &request);
1102 tcmsg->tcm_parent = TC_INGRESS_PARENT;
1103 tcmsg->tcm_info = tc_make_handle(prio, eth_type);
1104 tcmsg->tcm_handle = handle;
1105
1106 nl_msg_put_string(&request, TCA_KIND, "flower");
1107 basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
1108 {
1109 nl_msg_put_flower_options(&request, flower);
1110 }
1111 nl_msg_end_nested(&request, basic_offset);
1112
1113 error = tc_transact(&request, &reply);
1114 if (!error) {
1115 struct tcmsg *tc =
1116 ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc);
1117
1118 flower->prio = tc_get_major(tc->tcm_info);
1119 flower->handle = tc->tcm_handle;
1120 ofpbuf_delete(reply);
1121 }
1122
1123 return error;
1124}
691d20cb
PB
1125
1126void
1127tc_set_policy(const char *policy)
1128{
1129 if (!policy) {
1130 return;
1131 }
1132
1133 if (!strcmp(policy, "skip_sw")) {
1134 tc_policy = TC_POLICY_SKIP_SW;
1135 } else if (!strcmp(policy, "skip_hw")) {
1136 tc_policy = TC_POLICY_SKIP_HW;
1137 } else if (!strcmp(policy, "none")) {
1138 tc_policy = TC_POLICY_NONE;
1139 } else {
1140 VLOG_WARN("tc: Invalid policy '%s'", policy);
1141 return;
1142 }
1143
1144 VLOG_INFO("tc: Using policy '%s'", policy);
1145}