]> git.proxmox.com Git - ovs.git/blame - lib/tc.c
tc: Add ip layer ttl matching
[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, },
f98e418f
RD
203};
204
205static void
206nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower)
207{
208 const struct eth_addr *eth;
209
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);
213
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);
216 }
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);
220
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);
223 }
224}
225
226static void
227nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
228{
229 if (flower->key.eth_type != htons(ETH_TYPE_VLAN)) {
230 return;
231 }
232
233 flower->key.encap_eth_type =
234 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
235
236 if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
237 flower->key.vlan_id =
238 nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
239 }
240 if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
241 flower->key.vlan_prio =
242 nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
243 }
244}
245
246static void
247nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower)
248{
249 if (attrs[TCA_FLOWER_KEY_ENC_KEY_ID]) {
250 ovs_be32 id = nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_KEY_ID]);
251
252 flower->tunnel.id = be32_to_be64(id);
253 }
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]);
257 }
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]);
261 }
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]);
265 }
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]);
269 }
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]);
273 }
274}
275
276static void
277nl_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;
281
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;
286 }
287
288 if (attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]) {
289 key->ipv4.ipv4_src =
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]);
293 }
294 if (attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]) {
295 key->ipv4.ipv4_dst =
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]);
299 }
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];
303
304 key->ipv6.ipv6_src = nl_attr_get_in6_addr(attr);
305 mask->ipv6.ipv6_src = nl_attr_get_in6_addr(attr_mask);
306 }
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];
310
311 key->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr);
312 mask->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr_mask);
313 }
314
315 if (ip_proto == IPPROTO_TCP) {
316 if (attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]) {
2b1d9fa9 317 key->tcp_src =
f98e418f 318 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC]);
2b1d9fa9 319 mask->tcp_src =
f98e418f
RD
320 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]);
321 }
322 if (attrs[TCA_FLOWER_KEY_TCP_DST_MASK]) {
2b1d9fa9 323 key->tcp_dst =
f98e418f 324 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST]);
2b1d9fa9 325 mask->tcp_dst =
f98e418f
RD
326 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST_MASK]);
327 }
328 } else if (ip_proto == IPPROTO_UDP) {
329 if (attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]) {
2b1d9fa9
PB
330 key->udp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC]);
331 mask->udp_src =
f98e418f
RD
332 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]);
333 }
334 if (attrs[TCA_FLOWER_KEY_UDP_DST_MASK]) {
2b1d9fa9
PB
335 key->udp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST]);
336 mask->udp_dst =
f98e418f
RD
337 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST_MASK]);
338 }
4862b4e5
VB
339 } else if (ip_proto == IPPROTO_SCTP) {
340 if (attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]) {
2b1d9fa9
PB
341 key->sctp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC]);
342 mask->sctp_src =
4862b4e5
VB
343 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]);
344 }
345 if (attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]) {
2b1d9fa9
PB
346 key->sctp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST]);
347 mask->sctp_dst =
4862b4e5
VB
348 nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]);
349 }
f98e418f 350 }
0b4b5203
PB
351
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]);
355 }
f98e418f
RD
356}
357
358static 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),
366 .optional = true, },
367 [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
368 .min_len = sizeof(struct in6_addr),
369 .optional = true, },
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, },
372};
373
374static int
375nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
376{
377 struct nlattr *tun_attrs[ARRAY_SIZE(tunnel_key_policy)];
378 const struct nlattr *tun_parms;
379 const struct tc_tunnel_key *tun;
380
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");
384 return EPROTO;
385 }
386
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];
396
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;
400 if (ipv6_src) {
401 flower->set.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src);
402 }
403 if (ipv6_dst) {
404 flower->set.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst);
405 }
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;
410 } else {
411 VLOG_ERR_RL(&error_rl, "unknown tunnel actions: %d, %d",
412 tun->action, tun->t_action);
413 return EINVAL;
414 }
415 return 0;
416}
417
418static 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, },
425};
426
8c1e74d1
PB
427static int
428get_user_hz(void)
429{
430 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
431 static int user_hz = 100;
432
433 if (ovsthread_once_start(&once)) {
434 user_hz = sysconf(_SC_CLK_TCK);
435 ovsthread_once_done(&once);
436 }
437
438 return user_hz;
439}
f98e418f
RD
440
441static void
442nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
443{
8c1e74d1 444 flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
f98e418f
RD
445}
446
447static int
448nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower)
449{
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;
454
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");
458 return EPROTO;
459 }
460
461 gact_parms = gact_attrs[TCA_GACT_PARMS];
462 p = nl_attr_get_unspec(gact_parms, sizeof *p);
463
464 if (p->action != TC_ACT_SHOT) {
465 VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action);
466 return EINVAL;
467 }
468
469 tm = nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof *tm);
470 nl_parse_tcf(tm, flower);
471
472 return 0;
473}
474
475static 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, },
482};
483
484static int
485nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
486{
487
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;
493
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");
497 return EPROTO;
498 }
499
500 mirred_parms = mirred_attrs[TCA_MIRRED_PARMS];
501 m = nl_attr_get_unspec(mirred_parms, sizeof *m);
502
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);
506 return EINVAL;
507 }
508
509 flower->ifindex_out = m->ifindex;
510
511 mirred_tm = mirred_attrs[TCA_MIRRED_TM];
512 tm = nl_attr_get_unspec(mirred_tm, sizeof *tm);
513 nl_parse_tcf(tm, flower);
514
515 return 0;
516}
517
518static 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, },
525};
526
527static int
528nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
529{
530 struct nlattr *vlan_attrs[ARRAY_SIZE(vlan_policy)];
531 const struct tc_vlan *v;
532 const struct nlattr *vlan_parms;
533
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");
537 return EPROTO;
538 }
539
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];
545
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;
550 } else {
551 VLOG_ERR_RL(&error_rl, "unknown vlan action: %d, %d",
552 v->action, v->v_action);
553 return EINVAL;
554 }
555 return 0;
556}
557
558static 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, },
563};
564
565static 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, },
569};
570
571static int
572nl_parse_single_action(struct nlattr *action, struct tc_flower *flower)
573{
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;
582
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");
586 return EPROTO;
587 }
588
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];
592
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);
601 } else {
602 VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
603 return EINVAL;
604 }
605
606 if (act_cookie) {
607 flower->act_cookie.data = nl_attr_get(act_cookie);
608 flower->act_cookie.len = nl_attr_get_size(act_cookie);
609 }
610
611 act_stats = action_attrs[TCA_ACT_STATS];
612
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");
616 return EPROTO;
617 }
618
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);
622
623 return 0;
624}
625
626#define TCA_ACT_MIN_PRIO 1
627
628static int
629nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower)
630{
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);
635
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;
639 }
640
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");
644 return EPROTO;
645 }
646
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);
650
651 if (err) {
652 return err;
653 }
654 }
655 }
656
657 return 0;
658}
659
660static int
661nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower)
662{
663 struct nlattr *attrs[ARRAY_SIZE(tca_flower_policy)];
664
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");
668 return EPROTO;
669 }
670
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);
676}
677
678int
679parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
680{
681 struct tcmsg *tc;
682 struct nlattr *ta[ARRAY_SIZE(tca_policy)];
683 const char *kind;
684
685 if (NLMSG_HDRLEN + sizeof *tc > reply->size) {
686 return EPROTO;
687 }
688
689 memset(flower, 0, sizeof *flower);
690
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);
696
697 if (!flower->handle) {
698 return EAGAIN;
699 }
700
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");
704 return EPROTO;
705 }
706
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);
710 return EPROTO;
711 }
712
713 return nl_parse_flower_options(ta[TCA_OPTIONS], flower);
714}
715
716int
717tc_dump_flower_start(int ifindex, struct nl_dump *dump)
718{
719 struct ofpbuf request;
720 struct tcmsg *tcmsg;
721
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;
726
727 nl_dump_start(dump, NETLINK_ROUTE, &request);
728 ofpbuf_uninit(&request);
729
730 return 0;
731}
732
733int
734tc_flush(int ifindex)
735{
736 struct ofpbuf request;
737 struct tcmsg *tcmsg;
738
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;
742
743 return tc_transact(&request, NULL);
744}
745
746int
747tc_del_filter(int ifindex, int prio, int handle)
748{
749 struct ofpbuf request;
750 struct tcmsg *tcmsg;
751 struct ofpbuf *reply;
752 int error;
753
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;
758
759 error = tc_transact(&request, &reply);
760 if (!error) {
761 ofpbuf_delete(reply);
762 }
763 return error;
764}
765
766int
767tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower)
768{
769 struct ofpbuf request;
770 struct tcmsg *tcmsg;
771 struct ofpbuf *reply;
772 int error;
773
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;
778
779 error = tc_transact(&request, &reply);
780 if (error) {
781 return error;
782 }
783
784 error = parse_netlink_to_tc_flower(reply, flower);
785 ofpbuf_delete(reply);
786 return error;
787}
788
691d20cb
PB
789static int
790tc_get_tc_cls_policy(enum tc_offload_policy policy)
791{
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;
796 }
797
798 return 0;
799}
800
f98e418f
RD
801static void
802nl_msg_put_act_push_vlan(struct ofpbuf *request, uint16_t vid, uint8_t prio)
803{
804 size_t offset;
805
806 nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
807 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
808 {
809 struct tc_vlan parm = { .action = TC_ACT_PIPE,
810 .v_action = TCA_VLAN_ACT_PUSH };
811
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);
815 }
816 nl_msg_end_nested(request, offset);
817}
818
819static void
820nl_msg_put_act_pop_vlan(struct ofpbuf *request)
821{
822 size_t offset;
823
824 nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
825 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
826 {
827 struct tc_vlan parm = { .action = TC_ACT_PIPE,
828 .v_action = TCA_VLAN_ACT_POP };
829
830 nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
831 }
832 nl_msg_end_nested(request, offset);
833}
834
835static void
836nl_msg_put_act_tunnel_key_release(struct ofpbuf *request)
837{
838 size_t offset;
839
840 nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
841 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
842 {
843 struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
844 .t_action = TCA_TUNNEL_KEY_ACT_RELEASE };
845
846 nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
847 }
848 nl_msg_end_nested(request, offset);
849}
850
851static void
852nl_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,
856 ovs_be16 tp_dst)
857{
858 size_t offset;
859
860 nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
861 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
862 {
863 struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
864 .t_action = TCA_TUNNEL_KEY_ACT_SET };
865
866 nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
867
868 ovs_be32 id32 = be64_to_be32(id);
869 nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_KEY_ID, id32);
870 if (ipv4_dst) {
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,
875 ipv6_dst);
876 nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_SRC,
877 ipv6_src);
878 }
879 nl_msg_put_be16(request, TCA_TUNNEL_KEY_ENC_DST_PORT, tp_dst);
880 }
881 nl_msg_end_nested(request, offset);
882}
883
884static void
885nl_msg_put_act_drop(struct ofpbuf *request)
886{
887 size_t offset;
888
889 nl_msg_put_string(request, TCA_ACT_KIND, "gact");
890 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
891 {
892 struct tc_gact p = { .action = TC_ACT_SHOT };
893
894 nl_msg_put_unspec(request, TCA_GACT_PARMS, &p, sizeof p);
895 }
896 nl_msg_end_nested(request, offset);
897}
898
899static void
900nl_msg_put_act_redirect(struct ofpbuf *request, int ifindex)
901{
902 size_t offset;
903
904 nl_msg_put_string(request, TCA_ACT_KIND, "mirred");
905 offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
906 {
907 struct tc_mirred m = { .action = TC_ACT_STOLEN,
908 .eaction = TCA_EGRESS_REDIR,
909 .ifindex = ifindex };
910
911 nl_msg_put_unspec(request, TCA_MIRRED_PARMS, &m, sizeof m);
912 }
913 nl_msg_end_nested(request, offset);
914}
915
916static inline void
917nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) {
918 if (ck->len) {
919 nl_msg_put_unspec(request, TCA_ACT_COOKIE, ck->data, ck->len);
920 }
921}
922
923static void
924nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
925{
926 size_t offset;
927 size_t act_offset;
928
929 offset = nl_msg_start_nested(request, TCA_FLOWER_ACT);
930 {
931 uint16_t act_index = 1;
932
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,
940 flower->set.tp_dst);
941 nl_msg_end_nested(request, act_offset);
942 }
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);
947 }
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);
952 }
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);
959 }
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);
965 } else {
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);
970 }
971 }
972 nl_msg_end_nested(request, offset);
973}
974
975static void
976nl_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)
979{
980 if (mask_type != TCA_FLOWER_UNSPEC) {
981 if (is_all_zeros(mask_data, len)) {
982 return;
983 }
984 nl_msg_put_unspec(request, mask_type, mask_data, len);
985 }
986 nl_msg_put_unspec(request, type, data, len);
987}
988
989static void
990nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
991{
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);
998
999 nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_KEY_ID, id);
1000 if (ipv4_dst) {
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);
1006 }
1007 nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, tp_dst);
1008}
1009
bb170644
PB
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)
1013
f98e418f
RD
1014static void
1015nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
1016{
1017 uint16_t host_eth_type = ntohs(flower->key.eth_type);
1018 bool is_vlan = (host_eth_type == ETH_TYPE_VLAN);
1019
1020 if (is_vlan) {
1021 host_eth_type = ntohs(flower->key.encap_eth_type);
1022 }
1023
bb170644
PB
1024 FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST);
1025 FLOWER_PUT_MASKED_VALUE(src_mac, TCA_FLOWER_KEY_ETH_SRC);
f98e418f
RD
1026
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);
1031 }
1032
1033 if (flower->key.ip_proto == IPPROTO_UDP) {
2b1d9fa9
PB
1034 FLOWER_PUT_MASKED_VALUE(udp_src, TCA_FLOWER_KEY_UDP_SRC);
1035 FLOWER_PUT_MASKED_VALUE(udp_dst, TCA_FLOWER_KEY_UDP_DST);
f98e418f 1036 } else if (flower->key.ip_proto == IPPROTO_TCP) {
2b1d9fa9
PB
1037 FLOWER_PUT_MASKED_VALUE(tcp_src, TCA_FLOWER_KEY_TCP_SRC);
1038 FLOWER_PUT_MASKED_VALUE(tcp_dst, TCA_FLOWER_KEY_TCP_DST);
4862b4e5 1039 } else if (flower->key.ip_proto == IPPROTO_SCTP) {
2b1d9fa9
PB
1040 FLOWER_PUT_MASKED_VALUE(sctp_src, TCA_FLOWER_KEY_SCTP_SRC);
1041 FLOWER_PUT_MASKED_VALUE(sctp_dst, TCA_FLOWER_KEY_SCTP_DST);
f98e418f
RD
1042 }
1043 }
1044
1045 if (host_eth_type == ETH_P_IP) {
bb170644
PB
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);
0b4b5203 1048 FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
f98e418f 1049 } else if (host_eth_type == ETH_P_IPV6) {
bb170644
PB
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);
f98e418f
RD
1052 }
1053
1054 nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type);
1055
1056 if (is_vlan) {
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);
1062 }
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);
1066 }
1067 }
1068
691d20cb 1069 nl_msg_put_u32(request, TCA_FLOWER_FLAGS, tc_get_tc_cls_policy(tc_policy));
f98e418f
RD
1070
1071 if (flower->tunnel.tunnel) {
1072 nl_msg_put_flower_tunnel(request, flower);
1073 }
1074
1075 nl_msg_put_flower_acts(request, flower);
1076}
1077
1078int
1079tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
1080 struct tc_flower *flower)
1081{
1082 struct ofpbuf request;
1083 struct tcmsg *tcmsg;
1084 struct ofpbuf *reply;
1085 int error = 0;
1086 size_t basic_offset;
1087 uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
1088
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;
1094
1095 nl_msg_put_string(&request, TCA_KIND, "flower");
1096 basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
1097 {
1098 nl_msg_put_flower_options(&request, flower);
1099 }
1100 nl_msg_end_nested(&request, basic_offset);
1101
1102 error = tc_transact(&request, &reply);
1103 if (!error) {
1104 struct tcmsg *tc =
1105 ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc);
1106
1107 flower->prio = tc_get_major(tc->tcm_info);
1108 flower->handle = tc->tcm_handle;
1109 ofpbuf_delete(reply);
1110 }
1111
1112 return error;
1113}
691d20cb
PB
1114
1115void
1116tc_set_policy(const char *policy)
1117{
1118 if (!policy) {
1119 return;
1120 }
1121
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;
1128 } else {
1129 VLOG_WARN("tc: Invalid policy '%s'", policy);
1130 return;
1131 }
1132
1133 VLOG_INFO("tc: Using policy '%s'", policy);
1134}