/src/openvswitch/lib/tc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2009-2017 Nicira, Inc. |
3 | | * Copyright (c) 2016 Mellanox Technologies, Ltd. |
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" |
20 | | |
21 | | #include <errno.h> |
22 | | #include <linux/if_ether.h> |
23 | | #include <linux/if_packet.h> |
24 | | #include <linux/rtnetlink.h> |
25 | | #include <linux/tc_act/tc_csum.h> |
26 | | #include <linux/tc_act/tc_gact.h> |
27 | | #include <linux/tc_act/tc_mirred.h> |
28 | | #include <linux/tc_act/tc_mpls.h> |
29 | | #include <linux/tc_act/tc_pedit.h> |
30 | | #include <linux/tc_act/tc_skbedit.h> |
31 | | #include <linux/tc_act/tc_tunnel_key.h> |
32 | | #include <linux/tc_act/tc_vlan.h> |
33 | | #include <linux/tc_act/tc_ct.h> |
34 | | #include <linux/gen_stats.h> |
35 | | #include <net/if.h> |
36 | | #include <unistd.h> |
37 | | |
38 | | #include "byte-order.h" |
39 | | #include "coverage.h" |
40 | | #include "netlink-socket.h" |
41 | | #include "netlink.h" |
42 | | #include "odp-util.h" |
43 | | #include "openvswitch/ofpbuf.h" |
44 | | #include "openvswitch/util.h" |
45 | | #include "openvswitch/vlog.h" |
46 | | #include "packets.h" |
47 | | #include "timeval.h" |
48 | | #include "unaligned.h" |
49 | | |
50 | 0 | #define MAX_PEDIT_OFFSETS 32 |
51 | | |
52 | | #ifndef TCM_IFINDEX_MAGIC_BLOCK |
53 | | #define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU) |
54 | | #endif |
55 | | |
56 | | #ifndef TCA_DUMP_FLAGS_TERSE |
57 | 0 | #define TCA_DUMP_FLAGS_TERSE (1 << 0) |
58 | | #endif |
59 | | |
60 | | #if TCA_MAX < 15 |
61 | 0 | #define TCA_CHAIN 11 |
62 | 0 | #define TCA_INGRESS_BLOCK 13 |
63 | 0 | #define TCA_DUMP_FLAGS 15 |
64 | | #endif |
65 | | |
66 | | #ifndef RTM_GETCHAIN |
67 | | #define RTM_GETCHAIN 102 |
68 | | #endif |
69 | | |
70 | | VLOG_DEFINE_THIS_MODULE(tc); |
71 | | |
72 | | COVERAGE_DEFINE(tc_netlink_malformed_reply); |
73 | | |
74 | | static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5); |
75 | | |
76 | | static enum tc_offload_policy tc_policy = TC_POLICY_NONE; |
77 | | |
78 | | struct tc_pedit_key_ex { |
79 | | enum pedit_header_type htype; |
80 | | enum pedit_cmd cmd; |
81 | | }; |
82 | | |
83 | | struct flower_key_to_pedit { |
84 | | enum pedit_header_type htype; |
85 | | int offset; |
86 | | int flower_offset; |
87 | | int size; |
88 | | int boundary_shift; |
89 | | }; |
90 | | |
91 | | struct tc_flow_stats { |
92 | | uint64_t n_packets; |
93 | | uint64_t n_bytes; |
94 | | }; |
95 | | |
96 | | static struct flower_key_to_pedit flower_pedit_map[] = { |
97 | | { |
98 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP4, |
99 | | 12, |
100 | | offsetof(struct tc_flower_key, ipv4.ipv4_src), |
101 | | MEMBER_SIZEOF(struct tc_flower_key, ipv4.ipv4_src), |
102 | | 0 |
103 | | }, { |
104 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP4, |
105 | | 16, |
106 | | offsetof(struct tc_flower_key, ipv4.ipv4_dst), |
107 | | MEMBER_SIZEOF(struct tc_flower_key, ipv4.ipv4_dst), |
108 | | 0 |
109 | | }, { |
110 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP4, |
111 | | 8, |
112 | | offsetof(struct tc_flower_key, ipv4.rewrite_ttl), |
113 | | MEMBER_SIZEOF(struct tc_flower_key, ipv4.rewrite_ttl), |
114 | | 0 |
115 | | }, { |
116 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP4, |
117 | | 1, |
118 | | offsetof(struct tc_flower_key, ipv4.rewrite_tos), |
119 | | MEMBER_SIZEOF(struct tc_flower_key, ipv4.rewrite_tos), |
120 | | 0 |
121 | | }, { |
122 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP6, |
123 | | 7, |
124 | | offsetof(struct tc_flower_key, ipv6.rewrite_hlimit), |
125 | | MEMBER_SIZEOF(struct tc_flower_key, ipv6.rewrite_hlimit), |
126 | | 0 |
127 | | }, { |
128 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP6, |
129 | | 8, |
130 | | offsetof(struct tc_flower_key, ipv6.ipv6_src), |
131 | | MEMBER_SIZEOF(struct tc_flower_key, ipv6.ipv6_src), |
132 | | 0 |
133 | | }, { |
134 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP6, |
135 | | 24, |
136 | | offsetof(struct tc_flower_key, ipv6.ipv6_dst), |
137 | | MEMBER_SIZEOF(struct tc_flower_key, ipv6.ipv6_dst), |
138 | | 0 |
139 | | }, { |
140 | | TCA_PEDIT_KEY_EX_HDR_TYPE_IP6, |
141 | | 0, |
142 | | offsetof(struct tc_flower_key, ipv6.rewrite_tclass), |
143 | | MEMBER_SIZEOF(struct tc_flower_key, ipv6.rewrite_tclass), |
144 | | 4 |
145 | | }, { |
146 | | TCA_PEDIT_KEY_EX_HDR_TYPE_ETH, |
147 | | 6, |
148 | | offsetof(struct tc_flower_key, src_mac), |
149 | | MEMBER_SIZEOF(struct tc_flower_key, src_mac), |
150 | | 0 |
151 | | }, { |
152 | | TCA_PEDIT_KEY_EX_HDR_TYPE_ETH, |
153 | | 0, |
154 | | offsetof(struct tc_flower_key, dst_mac), |
155 | | MEMBER_SIZEOF(struct tc_flower_key, dst_mac), |
156 | | 0 |
157 | | }, { |
158 | | TCA_PEDIT_KEY_EX_HDR_TYPE_ETH, |
159 | | 12, |
160 | | offsetof(struct tc_flower_key, eth_type), |
161 | | MEMBER_SIZEOF(struct tc_flower_key, eth_type), |
162 | | 0 |
163 | | }, { |
164 | | TCA_PEDIT_KEY_EX_HDR_TYPE_TCP, |
165 | | 0, |
166 | | offsetof(struct tc_flower_key, tcp_src), |
167 | | MEMBER_SIZEOF(struct tc_flower_key, tcp_src), |
168 | | 0 |
169 | | }, { |
170 | | TCA_PEDIT_KEY_EX_HDR_TYPE_TCP, |
171 | | 2, |
172 | | offsetof(struct tc_flower_key, tcp_dst), |
173 | | MEMBER_SIZEOF(struct tc_flower_key, tcp_dst), |
174 | | 0 |
175 | | }, { |
176 | | TCA_PEDIT_KEY_EX_HDR_TYPE_UDP, |
177 | | 0, |
178 | | offsetof(struct tc_flower_key, udp_src), |
179 | | MEMBER_SIZEOF(struct tc_flower_key, udp_src), |
180 | | 0 |
181 | | }, { |
182 | | TCA_PEDIT_KEY_EX_HDR_TYPE_UDP, |
183 | | 2, |
184 | | offsetof(struct tc_flower_key, udp_dst), |
185 | | MEMBER_SIZEOF(struct tc_flower_key, udp_dst), |
186 | | 0 |
187 | | }, |
188 | | }; |
189 | | |
190 | | static inline int |
191 | | csum_update_flag(struct tc_flower *flower, |
192 | | enum pedit_header_type htype); |
193 | | |
194 | | struct tcmsg * |
195 | | tc_make_request(int ifindex, int type, unsigned int flags, |
196 | | struct ofpbuf *request) |
197 | 0 | { |
198 | 0 | struct tcmsg *tcmsg; |
199 | |
|
200 | 0 | ofpbuf_init(request, 512); |
201 | 0 | nl_msg_put_nlmsghdr(request, sizeof *tcmsg, type, NLM_F_REQUEST | flags); |
202 | 0 | tcmsg = ofpbuf_put_zeros(request, sizeof *tcmsg); |
203 | 0 | tcmsg->tcm_family = AF_UNSPEC; |
204 | 0 | tcmsg->tcm_ifindex = ifindex; |
205 | | /* Caller should fill in tcmsg->tcm_handle. */ |
206 | | /* Caller should fill in tcmsg->tcm_parent. */ |
207 | |
|
208 | 0 | return tcmsg; |
209 | 0 | } |
210 | | |
211 | | struct tcamsg * |
212 | | tc_make_action_request(int type, unsigned int flags, |
213 | | struct ofpbuf *request) |
214 | 0 | { |
215 | 0 | struct tcamsg *tcamsg; |
216 | |
|
217 | 0 | ofpbuf_init(request, 512); |
218 | 0 | nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type, NLM_F_REQUEST | flags); |
219 | 0 | tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg); |
220 | 0 | tcamsg->tca_family = AF_UNSPEC; |
221 | |
|
222 | 0 | return tcamsg; |
223 | 0 | } |
224 | | |
225 | | static void request_from_tcf_id(struct tcf_id *id, uint16_t eth_type, |
226 | | int type, unsigned int flags, |
227 | | struct ofpbuf *request) |
228 | 0 | { |
229 | 0 | int ifindex = id->block_id ? TCM_IFINDEX_MAGIC_BLOCK : id->ifindex; |
230 | 0 | uint32_t ingress_parent = id->block_id ? : TC_INGRESS_PARENT; |
231 | 0 | struct tcmsg *tcmsg; |
232 | |
|
233 | 0 | tcmsg = tc_make_request(ifindex, type, flags, request); |
234 | 0 | tcmsg->tcm_parent = (id->hook == TC_EGRESS) ? |
235 | 0 | TC_EGRESS_PARENT : ingress_parent; |
236 | 0 | tcmsg->tcm_info = tc_make_handle(id->prio, eth_type); |
237 | 0 | tcmsg->tcm_handle = id->handle; |
238 | |
|
239 | 0 | if (id->chain) { |
240 | 0 | nl_msg_put_u32(request, TCA_CHAIN, id->chain); |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | | int |
245 | | tc_transact(struct ofpbuf *request, struct ofpbuf **replyp) |
246 | 0 | { |
247 | 0 | int error = nl_transact(NETLINK_ROUTE, request, replyp); |
248 | 0 | ofpbuf_uninit(request); |
249 | 0 | return error; |
250 | 0 | } |
251 | | |
252 | | /* Adds or deletes a root qdisc on device with specified ifindex. |
253 | | * |
254 | | * The tc_qdisc_hook parameter determines if the qdisc is added on device |
255 | | * ingress or egress. |
256 | | * |
257 | | * If tc_qdisc_hook is TC_INGRESS, this function is equivalent to running the |
258 | | * following when 'add' is true: |
259 | | * /sbin/tc qdisc add dev <devname> handle ffff: ingress |
260 | | * |
261 | | * This function is equivalent to running the following when 'add' is false: |
262 | | * /sbin/tc qdisc del dev <devname> handle ffff: ingress |
263 | | * |
264 | | * If tc_qdisc_hook is TC_EGRESS, this function is equivalent to: |
265 | | * /sbin/tc qdisc (add|del) dev <devname> handle ffff: clsact |
266 | | * |
267 | | * Where dev <devname> is the device with specified ifindex name. |
268 | | * |
269 | | * The configuration and stats may be seen with the following command: |
270 | | * /sbin/tc -s qdisc show dev <devname> |
271 | | * |
272 | | * If block_id is greater than 0, then the ingress qdisc is added to a block. |
273 | | * In this case, it is equivalent to running (when 'add' is true): |
274 | | * /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress |
275 | | * |
276 | | * Returns 0 if successful, otherwise a positive errno value. |
277 | | */ |
278 | | int |
279 | | tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id, |
280 | | enum tc_qdisc_hook hook) |
281 | 0 | { |
282 | 0 | struct ofpbuf request; |
283 | 0 | struct tcmsg *tcmsg; |
284 | 0 | int error; |
285 | 0 | int type = add ? RTM_NEWQDISC : RTM_DELQDISC; |
286 | 0 | int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0; |
287 | |
|
288 | 0 | tcmsg = tc_make_request(ifindex, type, flags, &request); |
289 | |
|
290 | 0 | if (hook == TC_EGRESS) { |
291 | 0 | tcmsg->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); |
292 | 0 | tcmsg->tcm_parent = TC_H_CLSACT; |
293 | 0 | nl_msg_put_string(&request, TCA_KIND, "clsact"); |
294 | 0 | } else { |
295 | 0 | tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0); |
296 | 0 | tcmsg->tcm_parent = TC_H_INGRESS; |
297 | 0 | nl_msg_put_string(&request, TCA_KIND, "ingress"); |
298 | 0 | } |
299 | |
|
300 | 0 | nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0); |
301 | 0 | if (hook == TC_INGRESS && block_id) { |
302 | 0 | nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id); |
303 | 0 | } |
304 | |
|
305 | 0 | error = tc_transact(&request, NULL); |
306 | 0 | if (error) { |
307 | | /* If we're deleting the qdisc, don't worry about some of the |
308 | | * error conditions. */ |
309 | 0 | if (!add && (error == ENOENT || error == EINVAL)) { |
310 | 0 | return 0; |
311 | 0 | } |
312 | 0 | return error; |
313 | 0 | } |
314 | | |
315 | 0 | return 0; |
316 | 0 | } |
317 | | |
318 | | static const struct nl_policy tca_policy[] = { |
319 | | [TCA_KIND] = { .type = NL_A_STRING, .optional = false, }, |
320 | | [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false, }, |
321 | | [TCA_CHAIN] = { .type = NL_A_U32, .optional = true, }, |
322 | | [TCA_STATS] = { .type = NL_A_UNSPEC, |
323 | | .min_len = sizeof(struct tc_stats), .optional = true, }, |
324 | | [TCA_STATS2] = { .type = NL_A_NESTED, .optional = true, }, |
325 | | }; |
326 | | |
327 | | static const struct nl_policy tca_chain_policy[] = { |
328 | | [TCA_CHAIN] = { .type = NL_A_U32, .optional = false, }, |
329 | | }; |
330 | | |
331 | | static const struct nl_policy tca_flower_policy[] = { |
332 | | [TCA_FLOWER_CLASSID] = { .type = NL_A_U32, .optional = true, }, |
333 | | [TCA_FLOWER_INDEV] = { .type = NL_A_STRING, .max_len = IFNAMSIZ, |
334 | | .optional = true, }, |
335 | | [TCA_FLOWER_KEY_ETH_SRC] = { .type = NL_A_UNSPEC, |
336 | | .min_len = ETH_ALEN, .optional = true, }, |
337 | | [TCA_FLOWER_KEY_ETH_DST] = { .type = NL_A_UNSPEC, |
338 | | .min_len = ETH_ALEN, .optional = true, }, |
339 | | [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .type = NL_A_UNSPEC, |
340 | | .min_len = ETH_ALEN, |
341 | | .optional = true, }, |
342 | | [TCA_FLOWER_KEY_ETH_DST_MASK] = { .type = NL_A_UNSPEC, |
343 | | .min_len = ETH_ALEN, |
344 | | .optional = true, }, |
345 | | [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NL_A_U16, .optional = false, }, |
346 | | [TCA_FLOWER_KEY_ARP_SIP] = { .type = NL_A_U32, .optional = true, }, |
347 | | [TCA_FLOWER_KEY_ARP_TIP] = { .type = NL_A_U32, .optional = true, }, |
348 | | [TCA_FLOWER_KEY_ARP_SHA] = { .type = NL_A_UNSPEC, |
349 | | .min_len = ETH_ALEN, |
350 | | .optional = true, }, |
351 | | [TCA_FLOWER_KEY_ARP_THA] = { .type = NL_A_UNSPEC, |
352 | | .min_len = ETH_ALEN, |
353 | | .optional = true, }, |
354 | | [TCA_FLOWER_KEY_ARP_OP] = { .type = NL_A_U8, .optional = true, }, |
355 | | [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NL_A_U32, .optional = true, }, |
356 | | [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NL_A_U32, .optional = true, }, |
357 | | [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .type = NL_A_UNSPEC, |
358 | | .min_len = ETH_ALEN, |
359 | | .optional = true, }, |
360 | | [TCA_FLOWER_KEY_ARP_THA_MASK] = { .type = NL_A_UNSPEC, |
361 | | .min_len = ETH_ALEN, |
362 | | .optional = true, }, |
363 | | [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NL_A_U8, .optional = true, }, |
364 | | [TCA_FLOWER_FLAGS] = { .type = NL_A_U32, .optional = false, }, |
365 | | [TCA_FLOWER_ACT] = { .type = NL_A_NESTED, .optional = false, }, |
366 | | [TCA_FLOWER_KEY_IP_PROTO] = { .type = NL_A_U8, .optional = true, }, |
367 | | [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NL_A_U32, .optional = true, }, |
368 | | [TCA_FLOWER_KEY_IPV4_DST] = {.type = NL_A_U32, .optional = true, }, |
369 | | [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NL_A_U32, .optional = true, }, |
370 | | [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NL_A_U32, .optional = true, }, |
371 | | [TCA_FLOWER_KEY_IPV6_SRC] = { .type = NL_A_UNSPEC, |
372 | | .min_len = sizeof(struct in6_addr), |
373 | | .optional = true, }, |
374 | | [TCA_FLOWER_KEY_IPV6_DST] = { .type = NL_A_UNSPEC, |
375 | | .min_len = sizeof(struct in6_addr), |
376 | | .optional = true, }, |
377 | | [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC, |
378 | | .min_len = sizeof(struct in6_addr), |
379 | | .optional = true, }, |
380 | | [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .type = NL_A_UNSPEC, |
381 | | .min_len = sizeof(struct in6_addr), |
382 | | .optional = true, }, |
383 | | [TCA_FLOWER_KEY_TCP_SRC] = { .type = NL_A_U16, .optional = true, }, |
384 | | [TCA_FLOWER_KEY_TCP_DST] = { .type = NL_A_U16, .optional = true, }, |
385 | | [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NL_A_U16, .optional = true, }, |
386 | | [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NL_A_U16, .optional = true, }, |
387 | | [TCA_FLOWER_KEY_UDP_SRC] = { .type = NL_A_U16, .optional = true, }, |
388 | | [TCA_FLOWER_KEY_UDP_DST] = { .type = NL_A_U16, .optional = true, }, |
389 | | [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NL_A_U16, .optional = true, }, |
390 | | [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NL_A_U16, .optional = true, }, |
391 | | [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NL_A_U16, .optional = true, }, |
392 | | [TCA_FLOWER_KEY_SCTP_DST] = { .type = NL_A_U16, .optional = true, }, |
393 | | [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NL_A_U16, .optional = true, }, |
394 | | [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NL_A_U16, .optional = true, }, |
395 | | [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NL_A_U8, .optional = true, }, |
396 | | [TCA_FLOWER_KEY_MPLS_TC] = { .type = NL_A_U8, .optional = true, }, |
397 | | [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NL_A_U8, .optional = true, }, |
398 | | [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, }, |
399 | | [TCA_FLOWER_KEY_VLAN_ID] = { .type = NL_A_U16, .optional = true, }, |
400 | | [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NL_A_U8, .optional = true, }, |
401 | | [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, }, |
402 | | [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NL_A_U32, .optional = true, }, |
403 | | [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, }, |
404 | | [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, }, |
405 | | [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NL_A_U32, |
406 | | .optional = true, }, |
407 | | [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NL_A_U32, |
408 | | .optional = true, }, |
409 | | [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC, |
410 | | .min_len = sizeof(struct in6_addr), |
411 | | .optional = true, }, |
412 | | [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC, |
413 | | .min_len = sizeof(struct in6_addr), |
414 | | .optional = true, }, |
415 | | [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC, |
416 | | .min_len = sizeof(struct in6_addr), |
417 | | .optional = true, }, |
418 | | [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .type = NL_A_UNSPEC, |
419 | | .min_len = sizeof(struct in6_addr), |
420 | | .optional = true, }, |
421 | | [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NL_A_U16, |
422 | | .optional = true, }, |
423 | | [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NL_A_U16, |
424 | | .optional = true, }, |
425 | | [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NL_A_U16, |
426 | | .optional = true, }, |
427 | | [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NL_A_U16, |
428 | | .optional = true, }, |
429 | | [TCA_FLOWER_KEY_FLAGS] = { .type = NL_A_BE32, .optional = true, }, |
430 | | [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NL_A_BE32, .optional = true, }, |
431 | | [TCA_FLOWER_KEY_IP_TTL] = { .type = NL_A_U8, |
432 | | .optional = true, }, |
433 | | [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NL_A_U8, |
434 | | .optional = true, }, |
435 | | [TCA_FLOWER_KEY_IP_TOS] = { .type = NL_A_U8, |
436 | | .optional = true, }, |
437 | | [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NL_A_U8, |
438 | | .optional = true, }, |
439 | | [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NL_A_U16, |
440 | | .optional = true, }, |
441 | | [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16, |
442 | | .optional = true, }, |
443 | | [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, }, |
444 | | [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, }, |
445 | | [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, }, |
446 | | [TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NL_A_U8, |
447 | | .optional = true, }, |
448 | | [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NL_A_U8, |
449 | | .optional = true, }, |
450 | | [TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NL_A_U8, |
451 | | .optional = true, }, |
452 | | [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NL_A_U8, |
453 | | .optional = true, }, |
454 | | [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, }, |
455 | | [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NL_A_NESTED, |
456 | | .optional = true, }, |
457 | | [TCA_FLOWER_KEY_ENC_FLAGS] = { .type = NL_A_BE32, .optional = true, }, |
458 | | [TCA_FLOWER_KEY_ENC_FLAGS_MASK] = { .type = NL_A_BE32, |
459 | | .optional = true, }, |
460 | | [TCA_FLOWER_KEY_CT_STATE] = { .type = NL_A_U16, .optional = true, }, |
461 | | [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NL_A_U16, .optional = true, }, |
462 | | [TCA_FLOWER_KEY_CT_ZONE] = { .type = NL_A_U16, .optional = true, }, |
463 | | [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NL_A_U16, .optional = true, }, |
464 | | [TCA_FLOWER_KEY_CT_MARK] = { .type = NL_A_U32, .optional = true, }, |
465 | | [TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NL_A_U32, .optional = true, }, |
466 | | [TCA_FLOWER_KEY_CT_LABELS] = { .type = NL_A_U128, .optional = true, }, |
467 | | [TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NL_A_U128, |
468 | | .optional = true, }, |
469 | | [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NL_A_U8, |
470 | | .optional = true, }, |
471 | | [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NL_A_U8, |
472 | | .optional = true, }, |
473 | | [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NL_A_U8, |
474 | | .optional = true, }, |
475 | | [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NL_A_U8, |
476 | | .optional = true, }, |
477 | | [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NL_A_U8, |
478 | | .optional = true, }, |
479 | | [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NL_A_U8, |
480 | | .optional = true, }, |
481 | | [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NL_A_U8, |
482 | | .optional = true, }, |
483 | | [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NL_A_U8, |
484 | | .optional = true, }, |
485 | | }; |
486 | | |
487 | | static const struct nl_policy tca_flower_terse_policy[] = { |
488 | | [TCA_FLOWER_FLAGS] = { .type = NL_A_U32, .optional = false, }, |
489 | | [TCA_FLOWER_ACT] = { .type = NL_A_NESTED, .optional = false, }, |
490 | | }; |
491 | | |
492 | | static void |
493 | | nl_parse_flower_arp(struct nlattr **attrs, struct tc_flower *flower) |
494 | 0 | { |
495 | 0 | const struct eth_addr *eth; |
496 | |
|
497 | 0 | if (attrs[TCA_FLOWER_KEY_ARP_SIP_MASK]) { |
498 | 0 | flower->key.arp.spa = |
499 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_SIP]); |
500 | 0 | flower->mask.arp.spa = |
501 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_SIP_MASK]); |
502 | 0 | } |
503 | 0 | if (attrs[TCA_FLOWER_KEY_ARP_TIP_MASK]) { |
504 | 0 | flower->key.arp.tpa = |
505 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_TIP]); |
506 | 0 | flower->mask.arp.tpa = |
507 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_TIP_MASK]); |
508 | 0 | } |
509 | 0 | if (attrs[TCA_FLOWER_KEY_ARP_SHA_MASK]) { |
510 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_SHA], ETH_ALEN); |
511 | 0 | memcpy(&flower->key.arp.sha, eth, sizeof flower->key.arp.sha); |
512 | |
|
513 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_SHA_MASK], ETH_ALEN); |
514 | 0 | memcpy(&flower->mask.arp.sha, eth, sizeof flower->mask.arp.sha); |
515 | 0 | } |
516 | 0 | if (attrs[TCA_FLOWER_KEY_ARP_THA_MASK]) { |
517 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_THA], ETH_ALEN); |
518 | 0 | memcpy(&flower->key.arp.tha, eth, sizeof flower->key.arp.tha); |
519 | |
|
520 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_THA_MASK], ETH_ALEN); |
521 | 0 | memcpy(&flower->mask.arp.tha, eth, sizeof flower->mask.arp.tha); |
522 | 0 | } |
523 | 0 | if (attrs[TCA_FLOWER_KEY_ARP_OP_MASK]) { |
524 | 0 | flower->key.arp.opcode = |
525 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ARP_OP]); |
526 | 0 | flower->mask.arp.opcode = |
527 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ARP_OP_MASK]); |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | static void |
532 | | nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower) |
533 | 0 | { |
534 | 0 | const struct eth_addr *eth; |
535 | |
|
536 | 0 | if (attrs[TCA_FLOWER_KEY_ETH_SRC_MASK]) { |
537 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC], ETH_ALEN); |
538 | 0 | memcpy(&flower->key.src_mac, eth, sizeof flower->key.src_mac); |
539 | |
|
540 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC_MASK], ETH_ALEN); |
541 | 0 | memcpy(&flower->mask.src_mac, eth, sizeof flower->mask.src_mac); |
542 | 0 | } |
543 | 0 | if (attrs[TCA_FLOWER_KEY_ETH_DST_MASK]) { |
544 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST], ETH_ALEN); |
545 | 0 | memcpy(&flower->key.dst_mac, eth, sizeof flower->key.dst_mac); |
546 | |
|
547 | 0 | eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST_MASK], ETH_ALEN); |
548 | 0 | memcpy(&flower->mask.dst_mac, eth, sizeof flower->mask.dst_mac); |
549 | 0 | } |
550 | 0 | } |
551 | | |
552 | | static void |
553 | | nl_parse_flower_mpls(struct nlattr **attrs, struct tc_flower *flower) |
554 | 0 | { |
555 | 0 | uint8_t ttl, tc, bos; |
556 | 0 | uint32_t label; |
557 | |
|
558 | 0 | if (!eth_type_mpls(flower->key.eth_type)) { |
559 | 0 | return; |
560 | 0 | } |
561 | | |
562 | 0 | flower->key.encap_eth_type[0] = |
563 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]); |
564 | 0 | flower->key.mpls_lse = 0; |
565 | 0 | flower->mask.mpls_lse = 0; |
566 | |
|
567 | 0 | if (attrs[TCA_FLOWER_KEY_MPLS_TTL]) { |
568 | 0 | ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_MPLS_TTL]); |
569 | 0 | set_mpls_lse_ttl(&flower->key.mpls_lse, ttl); |
570 | 0 | set_mpls_lse_ttl(&flower->mask.mpls_lse, 0xff); |
571 | 0 | } |
572 | |
|
573 | 0 | if (attrs[TCA_FLOWER_KEY_MPLS_BOS]) { |
574 | 0 | bos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_MPLS_BOS]); |
575 | 0 | set_mpls_lse_bos(&flower->key.mpls_lse, bos); |
576 | 0 | set_mpls_lse_bos(&flower->mask.mpls_lse, 0xff); |
577 | 0 | } |
578 | |
|
579 | 0 | if (attrs[TCA_FLOWER_KEY_MPLS_TC]) { |
580 | 0 | tc = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_MPLS_TC]); |
581 | 0 | set_mpls_lse_tc(&flower->key.mpls_lse, tc); |
582 | 0 | set_mpls_lse_tc(&flower->mask.mpls_lse, 0xff); |
583 | 0 | } |
584 | |
|
585 | 0 | if (attrs[TCA_FLOWER_KEY_MPLS_LABEL]) { |
586 | 0 | label = nl_attr_get_u32(attrs[TCA_FLOWER_KEY_MPLS_LABEL]); |
587 | 0 | set_mpls_lse_label(&flower->key.mpls_lse, htonl(label)); |
588 | 0 | set_mpls_lse_label(&flower->mask.mpls_lse, OVS_BE32_MAX); |
589 | 0 | } |
590 | 0 | } |
591 | | |
592 | | static void |
593 | | nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower) |
594 | 0 | { |
595 | 0 | ovs_be16 encap_ethtype; |
596 | |
|
597 | 0 | if (!eth_type_vlan(flower->key.eth_type)) { |
598 | 0 | return; |
599 | 0 | } |
600 | | |
601 | 0 | flower->key.encap_eth_type[0] = |
602 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]); |
603 | 0 | flower->mask.encap_eth_type[0] = CONSTANT_HTONS(0xffff); |
604 | |
|
605 | 0 | if (attrs[TCA_FLOWER_KEY_VLAN_ID]) { |
606 | 0 | flower->key.vlan_id[0] = |
607 | 0 | nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]); |
608 | 0 | flower->mask.vlan_id[0] = VLAN_VID_MASK >> VLAN_VID_SHIFT; |
609 | 0 | } |
610 | 0 | if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) { |
611 | 0 | flower->key.vlan_prio[0] = |
612 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]); |
613 | 0 | flower->mask.vlan_prio[0] = VLAN_PCP_MASK >> VLAN_PCP_SHIFT; |
614 | 0 | } |
615 | |
|
616 | 0 | if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { |
617 | 0 | return; |
618 | 0 | } |
619 | | |
620 | 0 | encap_ethtype = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); |
621 | 0 | if (!eth_type_vlan(encap_ethtype)) { |
622 | 0 | return; |
623 | 0 | } |
624 | | |
625 | 0 | flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0]; |
626 | 0 | flower->mask.encap_eth_type[1] = CONSTANT_HTONS(0xffff); |
627 | 0 | flower->key.encap_eth_type[0] = encap_ethtype; |
628 | |
|
629 | 0 | if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) { |
630 | 0 | flower->key.vlan_id[1] = |
631 | 0 | nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]); |
632 | 0 | flower->mask.vlan_id[1] = VLAN_VID_MASK >> VLAN_VID_SHIFT; |
633 | 0 | } |
634 | 0 | if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) { |
635 | 0 | flower->key.vlan_prio[1] = |
636 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]); |
637 | 0 | flower->mask.vlan_prio[1] = VLAN_PCP_MASK >> VLAN_PCP_SHIFT; |
638 | 0 | } |
639 | 0 | } |
640 | | |
641 | | static int |
642 | | nl_parse_geneve_key(const struct nlattr *in_nlattr, |
643 | | struct tun_metadata *metadata) |
644 | 0 | { |
645 | 0 | struct geneve_opt *opt = NULL; |
646 | 0 | const struct ofpbuf *msg; |
647 | 0 | uint16_t last_opt_type; |
648 | 0 | struct nlattr *nla; |
649 | 0 | struct ofpbuf buf; |
650 | 0 | size_t left; |
651 | 0 | int cnt; |
652 | |
|
653 | 0 | nl_attr_get_nested(in_nlattr, &buf); |
654 | 0 | msg = &buf; |
655 | |
|
656 | 0 | last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC; |
657 | 0 | cnt = 0; |
658 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) { |
659 | 0 | uint16_t type = nl_attr_type(nla); |
660 | |
|
661 | 0 | switch (type) { |
662 | 0 | case TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS: |
663 | 0 | if (cnt && last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA) { |
664 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tun options class"); |
665 | 0 | return EINVAL; |
666 | 0 | } |
667 | | |
668 | 0 | opt = &metadata->opts.gnv[cnt]; |
669 | 0 | opt->opt_class = nl_attr_get_be16(nla); |
670 | 0 | cnt += sizeof(struct geneve_opt) / 4; |
671 | 0 | metadata->present.len += sizeof(struct geneve_opt); |
672 | 0 | last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS; |
673 | 0 | break; |
674 | 0 | case TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE: |
675 | 0 | if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS) { |
676 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tun options type"); |
677 | 0 | return EINVAL; |
678 | 0 | } |
679 | | |
680 | 0 | opt->type = nl_attr_get_u8(nla); |
681 | 0 | last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE; |
682 | 0 | break; |
683 | 0 | case TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA: |
684 | 0 | if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE) { |
685 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tun options data"); |
686 | 0 | return EINVAL; |
687 | 0 | } |
688 | | |
689 | 0 | opt->length = nl_attr_get_size(nla) / 4; |
690 | 0 | memcpy(opt + 1, nl_attr_get_unspec(nla, 1), opt->length * 4); |
691 | 0 | cnt += opt->length; |
692 | 0 | metadata->present.len += opt->length * 4; |
693 | 0 | last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA; |
694 | 0 | break; |
695 | 0 | } |
696 | 0 | } |
697 | | |
698 | 0 | if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA) { |
699 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tun options without data"); |
700 | 0 | return EINVAL; |
701 | 0 | } |
702 | | |
703 | 0 | return 0; |
704 | 0 | } |
705 | | |
706 | | static int |
707 | | nl_parse_vxlan_key(const struct nlattr *in_nlattr, |
708 | | struct tc_flower_tunnel *tunnel) |
709 | 0 | { |
710 | 0 | const struct ofpbuf *msg; |
711 | 0 | struct nlattr *nla; |
712 | 0 | struct ofpbuf buf; |
713 | 0 | uint32_t gbp_raw; |
714 | 0 | size_t left; |
715 | |
|
716 | 0 | nl_attr_get_nested(in_nlattr, &buf); |
717 | 0 | msg = &buf; |
718 | |
|
719 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) { |
720 | 0 | uint16_t type = nl_attr_type(nla); |
721 | |
|
722 | 0 | switch (type) { |
723 | 0 | case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP: |
724 | 0 | gbp_raw = nl_attr_get_u32(nla); |
725 | 0 | odp_decode_gbp_raw(gbp_raw, &tunnel->gbp.id, |
726 | 0 | &tunnel->gbp.flags); |
727 | 0 | tunnel->gbp.id_present = true; |
728 | 0 | break; |
729 | 0 | default: |
730 | 0 | VLOG_WARN_RL(&error_rl, "failed to parse vxlan tun options"); |
731 | 0 | return EINVAL; |
732 | 0 | } |
733 | 0 | } |
734 | | |
735 | 0 | return 0; |
736 | 0 | } |
737 | | |
738 | | static int |
739 | | nl_parse_flower_tunnel_opts(struct nlattr *options, |
740 | | struct tc_flower_tunnel *tunnel) |
741 | 0 | { |
742 | 0 | const struct ofpbuf *msg; |
743 | 0 | struct nlattr *nla; |
744 | 0 | struct ofpbuf buf; |
745 | 0 | size_t left; |
746 | 0 | int err; |
747 | |
|
748 | 0 | nl_attr_get_nested(options, &buf); |
749 | 0 | msg = &buf; |
750 | |
|
751 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) { |
752 | 0 | uint16_t type = nl_attr_type(nla); |
753 | 0 | switch (type) { |
754 | 0 | case TCA_FLOWER_KEY_ENC_OPTS_GENEVE: |
755 | 0 | err = nl_parse_geneve_key(nla, &tunnel->metadata); |
756 | 0 | if (err) { |
757 | 0 | return err; |
758 | 0 | } |
759 | | |
760 | 0 | break; |
761 | 0 | case TCA_FLOWER_KEY_ENC_OPTS_VXLAN: |
762 | 0 | err = nl_parse_vxlan_key(nla, tunnel); |
763 | 0 | if (err) { |
764 | 0 | return err; |
765 | 0 | } |
766 | | |
767 | 0 | break; |
768 | 0 | } |
769 | 0 | } |
770 | | |
771 | 0 | return 0; |
772 | 0 | } |
773 | | |
774 | | static int |
775 | | flower_tun_geneve_opt_check_len(struct tun_metadata *key, |
776 | | struct tun_metadata *mask) |
777 | 0 | { |
778 | 0 | const struct geneve_opt *opt, *opt_mask; |
779 | 0 | int len, cnt = 0; |
780 | |
|
781 | 0 | if (key->present.len != mask->present.len) { |
782 | 0 | goto bad_length; |
783 | 0 | } |
784 | | |
785 | 0 | len = key->present.len; |
786 | 0 | while (len) { |
787 | 0 | opt = &key->opts.gnv[cnt]; |
788 | 0 | opt_mask = &mask->opts.gnv[cnt]; |
789 | |
|
790 | 0 | if (opt->length != opt_mask->length) { |
791 | 0 | goto bad_length; |
792 | 0 | } |
793 | | |
794 | 0 | cnt += sizeof(struct geneve_opt) / 4 + opt->length; |
795 | 0 | len -= sizeof(struct geneve_opt) + opt->length * 4; |
796 | 0 | } |
797 | | |
798 | 0 | return 0; |
799 | | |
800 | 0 | bad_length: |
801 | 0 | VLOG_ERR_RL(&error_rl, |
802 | 0 | "failed to parse tun options; key/mask length differ"); |
803 | 0 | return EINVAL; |
804 | 0 | } |
805 | | |
806 | | static int |
807 | | nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower) |
808 | 0 | { |
809 | 0 | int err; |
810 | |
|
811 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_KEY_ID]) { |
812 | 0 | ovs_be32 id = nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_KEY_ID]); |
813 | |
|
814 | 0 | flower->key.tunnel.id = be32_to_be64(id); |
815 | 0 | flower->mask.tunnel.id = OVS_BE64_MAX; |
816 | 0 | } |
817 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK]) { |
818 | 0 | flower->mask.tunnel.ipv4.ipv4_src = |
819 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK]); |
820 | 0 | flower->key.tunnel.ipv4.ipv4_src = |
821 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC]); |
822 | 0 | } |
823 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK]) { |
824 | 0 | flower->mask.tunnel.ipv4.ipv4_dst = |
825 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK]); |
826 | 0 | flower->key.tunnel.ipv4.ipv4_dst = |
827 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_DST]); |
828 | 0 | } |
829 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]) { |
830 | 0 | flower->mask.tunnel.ipv6.ipv6_src = |
831 | 0 | nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]); |
832 | 0 | flower->key.tunnel.ipv6.ipv6_src = |
833 | 0 | nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC]); |
834 | 0 | } |
835 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]) { |
836 | 0 | flower->mask.tunnel.ipv6.ipv6_dst = |
837 | 0 | nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]); |
838 | 0 | flower->key.tunnel.ipv6.ipv6_dst = |
839 | 0 | nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_DST]); |
840 | 0 | } |
841 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK]) { |
842 | 0 | flower->mask.tunnel.tp_src = |
843 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK]); |
844 | 0 | flower->key.tunnel.tp_src = |
845 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT]); |
846 | 0 | } |
847 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK]) { |
848 | 0 | flower->mask.tunnel.tp_dst = |
849 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK]); |
850 | 0 | flower->key.tunnel.tp_dst = |
851 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]); |
852 | 0 | } |
853 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_IP_TOS_MASK]) { |
854 | 0 | flower->key.tunnel.tos = |
855 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TOS]); |
856 | 0 | flower->mask.tunnel.tos = |
857 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TOS_MASK]); |
858 | 0 | } |
859 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]) { |
860 | 0 | flower->key.tunnel.ttl = |
861 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TTL]); |
862 | 0 | flower->mask.tunnel.ttl = |
863 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]); |
864 | 0 | } |
865 | |
|
866 | 0 | if (!is_all_zeros(&flower->mask.tunnel, sizeof flower->mask.tunnel) || |
867 | 0 | !is_all_zeros(&flower->key.tunnel, sizeof flower->key.tunnel)) { |
868 | 0 | flower->tunnel = true; |
869 | 0 | } |
870 | |
|
871 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_FLAGS_MASK]) { |
872 | 0 | flower->key.tunnel.tc_enc_flags = ntohl( |
873 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_FLAGS])); |
874 | 0 | flower->mask.tunnel.tc_enc_flags = ntohl( |
875 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_FLAGS_MASK])); |
876 | 0 | } |
877 | |
|
878 | 0 | if (attrs[TCA_FLOWER_KEY_ENC_OPTS] && |
879 | 0 | attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK]) { |
880 | 0 | err = nl_parse_flower_tunnel_opts(attrs[TCA_FLOWER_KEY_ENC_OPTS], |
881 | 0 | &flower->key.tunnel); |
882 | 0 | if (err) { |
883 | 0 | return err; |
884 | 0 | } |
885 | | |
886 | 0 | err = nl_parse_flower_tunnel_opts(attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK], |
887 | 0 | &flower->mask.tunnel); |
888 | 0 | if (err) { |
889 | 0 | return err; |
890 | 0 | } |
891 | | |
892 | 0 | err = flower_tun_geneve_opt_check_len(&flower->key.tunnel.metadata, |
893 | 0 | &flower->mask.tunnel.metadata); |
894 | 0 | if (err) { |
895 | 0 | return err; |
896 | 0 | } |
897 | 0 | } else if (attrs[TCA_FLOWER_KEY_ENC_OPTS]) { |
898 | 0 | VLOG_ERR_RL(&error_rl, |
899 | 0 | "failed to parse tun options; no mask supplied"); |
900 | 0 | return EINVAL; |
901 | 0 | } else if (attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK]) { |
902 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tun options; no key supplied"); |
903 | 0 | return EINVAL; |
904 | 0 | } |
905 | | |
906 | 0 | return 0; |
907 | 0 | } |
908 | | |
909 | | static void |
910 | 0 | nl_parse_flower_ct_match(struct nlattr **attrs, struct tc_flower *flower) { |
911 | 0 | struct tc_flower_key *key = &flower->key; |
912 | 0 | struct tc_flower_key *mask = &flower->mask; |
913 | 0 | struct nlattr *attr_key, *attr_mask; |
914 | |
|
915 | 0 | attr_key = attrs[TCA_FLOWER_KEY_CT_STATE]; |
916 | 0 | attr_mask = attrs[TCA_FLOWER_KEY_CT_STATE_MASK]; |
917 | 0 | if (attr_mask) { |
918 | 0 | key->ct_state = nl_attr_get_u16(attr_key); |
919 | 0 | mask->ct_state = nl_attr_get_u16(attr_mask); |
920 | 0 | } |
921 | |
|
922 | 0 | attr_key = attrs[TCA_FLOWER_KEY_CT_ZONE]; |
923 | 0 | attr_mask = attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]; |
924 | 0 | if (attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]) { |
925 | 0 | key->ct_zone = nl_attr_get_u16(attr_key); |
926 | 0 | mask->ct_zone = nl_attr_get_u16(attr_mask); |
927 | 0 | } |
928 | |
|
929 | 0 | attr_key = attrs[TCA_FLOWER_KEY_CT_MARK]; |
930 | 0 | attr_mask = attrs[TCA_FLOWER_KEY_CT_MARK_MASK]; |
931 | 0 | if (attrs[TCA_FLOWER_KEY_CT_MARK_MASK]) { |
932 | 0 | key->ct_mark = nl_attr_get_u32(attr_key); |
933 | 0 | mask->ct_mark = nl_attr_get_u32(attr_mask); |
934 | 0 | } |
935 | |
|
936 | 0 | attr_key = attrs[TCA_FLOWER_KEY_CT_LABELS]; |
937 | 0 | attr_mask = attrs[TCA_FLOWER_KEY_CT_LABELS_MASK]; |
938 | 0 | if (attrs[TCA_FLOWER_KEY_CT_LABELS_MASK]) { |
939 | 0 | key->ct_label = nl_attr_get_u128(attr_key); |
940 | 0 | mask->ct_label = nl_attr_get_u128(attr_mask); |
941 | 0 | } |
942 | 0 | } |
943 | | |
944 | | static void |
945 | 0 | nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) { |
946 | 0 | uint8_t ip_proto = 0; |
947 | 0 | struct tc_flower_key *key = &flower->key; |
948 | 0 | struct tc_flower_key *mask = &flower->mask; |
949 | |
|
950 | 0 | if (attrs[TCA_FLOWER_KEY_IP_PROTO]) { |
951 | 0 | ip_proto = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_PROTO]); |
952 | 0 | key->ip_proto = ip_proto; |
953 | 0 | mask->ip_proto = UINT8_MAX; |
954 | 0 | } |
955 | |
|
956 | 0 | if (attrs[TCA_FLOWER_KEY_FLAGS_MASK]) { |
957 | 0 | key->flags = ntohl(nl_attr_get_be32(attrs[TCA_FLOWER_KEY_FLAGS])); |
958 | 0 | mask->flags = |
959 | 0 | ntohl(nl_attr_get_be32(attrs[TCA_FLOWER_KEY_FLAGS_MASK])); |
960 | 0 | } |
961 | |
|
962 | 0 | if (attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]) { |
963 | 0 | key->ipv4.ipv4_src = |
964 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC]); |
965 | 0 | mask->ipv4.ipv4_src = |
966 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]); |
967 | 0 | } |
968 | 0 | if (attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]) { |
969 | 0 | key->ipv4.ipv4_dst = |
970 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST]); |
971 | 0 | mask->ipv4.ipv4_dst = |
972 | 0 | nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]); |
973 | 0 | } |
974 | 0 | if (attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK]) { |
975 | 0 | struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_SRC]; |
976 | 0 | struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK]; |
977 | |
|
978 | 0 | key->ipv6.ipv6_src = nl_attr_get_in6_addr(attr); |
979 | 0 | mask->ipv6.ipv6_src = nl_attr_get_in6_addr(attr_mask); |
980 | 0 | } |
981 | 0 | if (attrs[TCA_FLOWER_KEY_IPV6_DST_MASK]) { |
982 | 0 | struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_DST]; |
983 | 0 | struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_DST_MASK]; |
984 | |
|
985 | 0 | key->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr); |
986 | 0 | mask->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr_mask); |
987 | 0 | } |
988 | |
|
989 | 0 | if (ip_proto == IPPROTO_TCP) { |
990 | 0 | if (attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]) { |
991 | 0 | key->tcp_src = |
992 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC]); |
993 | 0 | mask->tcp_src = |
994 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]); |
995 | 0 | } |
996 | 0 | if (attrs[TCA_FLOWER_KEY_TCP_DST_MASK]) { |
997 | 0 | key->tcp_dst = |
998 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST]); |
999 | 0 | mask->tcp_dst = |
1000 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST_MASK]); |
1001 | 0 | } |
1002 | 0 | if (attrs[TCA_FLOWER_KEY_TCP_FLAGS_MASK]) { |
1003 | 0 | key->tcp_flags = |
1004 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_FLAGS]); |
1005 | 0 | mask->tcp_flags = |
1006 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_FLAGS_MASK]); |
1007 | 0 | } |
1008 | 0 | } else if (ip_proto == IPPROTO_UDP) { |
1009 | 0 | if (attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]) { |
1010 | 0 | key->udp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC]); |
1011 | 0 | mask->udp_src = |
1012 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]); |
1013 | 0 | } |
1014 | 0 | if (attrs[TCA_FLOWER_KEY_UDP_DST_MASK]) { |
1015 | 0 | key->udp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST]); |
1016 | 0 | mask->udp_dst = |
1017 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST_MASK]); |
1018 | 0 | } |
1019 | 0 | } else if (ip_proto == IPPROTO_SCTP) { |
1020 | 0 | if (attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]) { |
1021 | 0 | key->sctp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC]); |
1022 | 0 | mask->sctp_src = |
1023 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]); |
1024 | 0 | } |
1025 | 0 | if (attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]) { |
1026 | 0 | key->sctp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST]); |
1027 | 0 | mask->sctp_dst = |
1028 | 0 | nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]); |
1029 | 0 | } |
1030 | 0 | } else if (ip_proto == IPPROTO_ICMP) { |
1031 | 0 | if (attrs[TCA_FLOWER_KEY_ICMPV4_CODE_MASK]) { |
1032 | 0 | key->icmp_code = |
1033 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE]); |
1034 | 0 | mask->icmp_code = |
1035 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE_MASK]); |
1036 | 0 | } |
1037 | 0 | if (attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]) { |
1038 | 0 | key->icmp_type = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE]); |
1039 | 0 | mask->icmp_type = |
1040 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]); |
1041 | 0 | } |
1042 | 0 | } else if (ip_proto == IPPROTO_ICMPV6) { |
1043 | 0 | if (attrs[TCA_FLOWER_KEY_ICMPV6_CODE_MASK]) { |
1044 | 0 | key->icmp_code = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE]); |
1045 | 0 | mask->icmp_code = |
1046 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE_MASK]); |
1047 | 0 | } |
1048 | 0 | if (attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]) { |
1049 | 0 | key->icmp_type = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE]); |
1050 | 0 | mask->icmp_type = |
1051 | 0 | nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]); |
1052 | 0 | } |
1053 | 0 | } |
1054 | |
|
1055 | 0 | if (attrs[TCA_FLOWER_KEY_IP_TTL_MASK]) { |
1056 | 0 | key->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL]); |
1057 | 0 | mask->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL_MASK]); |
1058 | 0 | } |
1059 | |
|
1060 | 0 | if (attrs[TCA_FLOWER_KEY_IP_TOS_MASK]) { |
1061 | 0 | key->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS]); |
1062 | 0 | mask->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS_MASK]); |
1063 | 0 | } |
1064 | |
|
1065 | 0 | nl_parse_flower_ct_match(attrs, flower); |
1066 | 0 | } |
1067 | | |
1068 | | static enum tc_offloaded_state |
1069 | | nl_get_flower_offloaded_state(struct nlattr **attrs) |
1070 | 0 | { |
1071 | 0 | uint32_t flower_flags = 0; |
1072 | |
|
1073 | 0 | if (attrs[TCA_FLOWER_FLAGS]) { |
1074 | 0 | flower_flags = nl_attr_get_u32(attrs[TCA_FLOWER_FLAGS]); |
1075 | 0 | if (flower_flags & TCA_CLS_FLAGS_NOT_IN_HW) { |
1076 | 0 | return TC_OFFLOADED_STATE_NOT_IN_HW; |
1077 | 0 | } else if (flower_flags & TCA_CLS_FLAGS_IN_HW) { |
1078 | 0 | return TC_OFFLOADED_STATE_IN_HW; |
1079 | 0 | } |
1080 | 0 | } |
1081 | 0 | return TC_OFFLOADED_STATE_UNDEFINED; |
1082 | 0 | } |
1083 | | |
1084 | | static void |
1085 | | nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower) |
1086 | 0 | { |
1087 | 0 | flower->offloaded_state = nl_get_flower_offloaded_state(attrs); |
1088 | 0 | } |
1089 | | |
1090 | | static void |
1091 | | nl_parse_action_pc(uint32_t action_pc, struct tc_action *action) |
1092 | 0 | { |
1093 | 0 | if (action_pc == TC_ACT_STOLEN) { |
1094 | 0 | action->jump_action = JUMP_ACTION_STOP; |
1095 | 0 | } else if (action_pc & TC_ACT_JUMP) { |
1096 | 0 | action->jump_action = action_pc & TC_ACT_EXT_VAL_MASK; |
1097 | 0 | } else { |
1098 | 0 | action->jump_action = 0; |
1099 | 0 | } |
1100 | 0 | } |
1101 | | |
1102 | | static const struct nl_policy pedit_policy[] = { |
1103 | | [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC, |
1104 | | .min_len = sizeof(struct tc_pedit), |
1105 | | .optional = false, }, |
1106 | | [TCA_PEDIT_KEYS_EX] = { .type = NL_A_NESTED, |
1107 | | .optional = false, }, |
1108 | | }; |
1109 | | |
1110 | | static int |
1111 | | nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower) |
1112 | 0 | { |
1113 | 0 | struct tc_action *action = &flower->actions[flower->action_count++]; |
1114 | 0 | struct nlattr *pe_attrs[ARRAY_SIZE(pedit_policy)]; |
1115 | 0 | const struct tc_pedit *pe; |
1116 | 0 | const struct tc_pedit_key *keys; |
1117 | 0 | const struct nlattr *nla, *keys_ex, *ex_type; |
1118 | 0 | const void *keys_attr; |
1119 | 0 | char *rewrite_key = (void *) &action->rewrite.key; |
1120 | 0 | char *rewrite_mask = (void *) &action->rewrite.mask; |
1121 | 0 | size_t keys_ex_size, left; |
1122 | 0 | int type, i = 0, err; |
1123 | |
|
1124 | 0 | if (!nl_parse_nested(options, pedit_policy, pe_attrs, |
1125 | 0 | ARRAY_SIZE(pedit_policy))) { |
1126 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse pedit action options"); |
1127 | 0 | return EPROTO; |
1128 | 0 | } |
1129 | | |
1130 | 0 | pe = nl_attr_get_unspec(pe_attrs[TCA_PEDIT_PARMS_EX], sizeof *pe); |
1131 | 0 | keys = pe->keys; |
1132 | 0 | keys_attr = pe_attrs[TCA_PEDIT_KEYS_EX]; |
1133 | 0 | keys_ex = nl_attr_get(keys_attr); |
1134 | 0 | keys_ex_size = nl_attr_get_size(keys_attr); |
1135 | |
|
1136 | 0 | NL_ATTR_FOR_EACH (nla, left, keys_ex, keys_ex_size) { |
1137 | 0 | if (i >= pe->nkeys) { |
1138 | 0 | break; |
1139 | 0 | } |
1140 | | |
1141 | 0 | if (nl_attr_type(nla) != TCA_PEDIT_KEY_EX) { |
1142 | 0 | VLOG_ERR_RL(&error_rl, "unable to parse legacy pedit type: %d", |
1143 | 0 | nl_attr_type(nla)); |
1144 | 0 | return EOPNOTSUPP; |
1145 | 0 | } |
1146 | | |
1147 | 0 | ex_type = nl_attr_find_nested(nla, TCA_PEDIT_KEY_EX_HTYPE); |
1148 | 0 | if (!ex_type) { |
1149 | 0 | return EOPNOTSUPP; |
1150 | 0 | } |
1151 | | |
1152 | 0 | type = nl_attr_get_u16(ex_type); |
1153 | |
|
1154 | 0 | err = csum_update_flag(flower, type); |
1155 | 0 | if (err) { |
1156 | 0 | return err; |
1157 | 0 | } |
1158 | | |
1159 | 0 | for (int j = 0; j < ARRAY_SIZE(flower_pedit_map); j++) { |
1160 | 0 | struct flower_key_to_pedit *m = &flower_pedit_map[j]; |
1161 | 0 | int flower_off = m->flower_offset; |
1162 | 0 | int sz = m->size; |
1163 | 0 | int mf = m->offset; |
1164 | 0 | int ef = ROUND_UP(mf, 4); |
1165 | |
|
1166 | 0 | if (m->htype != type) { |
1167 | 0 | continue; |
1168 | 0 | } |
1169 | | |
1170 | | /* check overlap between current pedit key, which is always |
1171 | | * 4 bytes (range [off, off + 3]), and a map entry in |
1172 | | * flower_pedit_map sf = ROUND_DOWN(mf, 4) |
1173 | | * (range [sf|mf, (mf + sz - 1)|ef]) */ |
1174 | 0 | if ((keys->off >= mf && keys->off < mf + sz) |
1175 | 0 | || (keys->off + 3 >= mf && keys->off + 3 < ef)) { |
1176 | 0 | int diff = flower_off + (keys->off - mf); |
1177 | 0 | ovs_be32 *dst = (void *) (rewrite_key + diff); |
1178 | 0 | ovs_be32 *dst_m = (void *) (rewrite_mask + diff); |
1179 | 0 | ovs_be32 mask, mask_word, data_word, val; |
1180 | 0 | uint32_t zero_bits; |
1181 | |
|
1182 | 0 | mask_word = htonl(ntohl(keys->mask) << m->boundary_shift); |
1183 | 0 | data_word = htonl(ntohl(keys->val) << m->boundary_shift); |
1184 | 0 | mask = ~(mask_word); |
1185 | |
|
1186 | 0 | if (keys->off < mf) { |
1187 | 0 | zero_bits = 8 * (mf - keys->off); |
1188 | 0 | mask &= htonl(UINT32_MAX >> zero_bits); |
1189 | 0 | } else if (keys->off + 4 > mf + m->size) { |
1190 | 0 | zero_bits = 8 * (keys->off + 4 - mf - m->size); |
1191 | 0 | mask &= htonl(UINT32_MAX << zero_bits); |
1192 | 0 | } |
1193 | |
|
1194 | 0 | val = get_unaligned_be32(dst_m); |
1195 | 0 | val |= mask; |
1196 | 0 | put_unaligned_be32(dst_m, val); |
1197 | |
|
1198 | 0 | val = get_unaligned_be32(dst); |
1199 | 0 | val |= data_word & mask; |
1200 | 0 | put_unaligned_be32(dst, val); |
1201 | 0 | } |
1202 | 0 | } |
1203 | |
|
1204 | 0 | keys++; |
1205 | 0 | i++; |
1206 | 0 | } |
1207 | | |
1208 | 0 | action->type = TC_ACT_PEDIT; |
1209 | |
|
1210 | 0 | nl_parse_action_pc(pe->action, action); |
1211 | 0 | return 0; |
1212 | 0 | } |
1213 | | |
1214 | | static const struct nl_policy tunnel_key_policy[] = { |
1215 | | [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC, |
1216 | | .min_len = sizeof(struct tc_tunnel_key), |
1217 | | .optional = false, }, |
1218 | | [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, }, |
1219 | | [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, }, |
1220 | | [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC, |
1221 | | .min_len = sizeof(struct in6_addr), |
1222 | | .optional = true, }, |
1223 | | [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC, |
1224 | | .min_len = sizeof(struct in6_addr), |
1225 | | .optional = true, }, |
1226 | | [TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NL_A_U32, .optional = true, }, |
1227 | | [TCA_TUNNEL_KEY_ENC_DST_PORT] = { .type = NL_A_U16, .optional = true, }, |
1228 | | [TCA_TUNNEL_KEY_ENC_TOS] = { .type = NL_A_U8, .optional = true, }, |
1229 | | [TCA_TUNNEL_KEY_ENC_TTL] = { .type = NL_A_U8, .optional = true, }, |
1230 | | [TCA_TUNNEL_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, }, |
1231 | | [TCA_TUNNEL_KEY_NO_CSUM] = { .type = NL_A_U8, .optional = true, }, |
1232 | | [TCA_TUNNEL_KEY_NO_FRAG] = { .type = NL_A_FLAG, .optional = true, }, |
1233 | | }; |
1234 | | |
1235 | | static int |
1236 | | nl_parse_act_geneve_opts(const struct nlattr *in_nlattr, |
1237 | | struct tc_action *action) |
1238 | 0 | { |
1239 | 0 | struct geneve_opt *opt = NULL; |
1240 | 0 | const struct ofpbuf *msg; |
1241 | 0 | uint16_t last_opt_type; |
1242 | 0 | struct nlattr *nla; |
1243 | 0 | struct ofpbuf buf; |
1244 | 0 | size_t left; |
1245 | 0 | int cnt; |
1246 | |
|
1247 | 0 | nl_attr_get_nested(in_nlattr, &buf); |
1248 | 0 | msg = &buf; |
1249 | |
|
1250 | 0 | last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_UNSPEC; |
1251 | 0 | cnt = 0; |
1252 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) { |
1253 | 0 | uint16_t type = nl_attr_type(nla); |
1254 | |
|
1255 | 0 | switch (type) { |
1256 | 0 | case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS: |
1257 | 0 | if (cnt && last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA) { |
1258 | 0 | VLOG_ERR_RL(&error_rl, |
1259 | 0 | "failed to parse action geneve options class"); |
1260 | 0 | return EINVAL; |
1261 | 0 | } |
1262 | | |
1263 | 0 | opt = &action->encap.data.opts.gnv[cnt]; |
1264 | 0 | opt->opt_class = nl_attr_get_be16(nla); |
1265 | 0 | cnt += sizeof(struct geneve_opt) / 4; |
1266 | 0 | action->encap.data.present.len += sizeof(struct geneve_opt); |
1267 | 0 | last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS; |
1268 | 0 | break; |
1269 | 0 | case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE: |
1270 | 0 | if (last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS) { |
1271 | 0 | VLOG_ERR_RL(&error_rl, |
1272 | 0 | "failed to parse action geneve options type"); |
1273 | 0 | return EINVAL; |
1274 | 0 | } |
1275 | | |
1276 | 0 | opt->type = nl_attr_get_u8(nla); |
1277 | 0 | last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE; |
1278 | 0 | break; |
1279 | 0 | case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA: |
1280 | 0 | if (last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE) { |
1281 | 0 | VLOG_ERR_RL(&error_rl, |
1282 | 0 | "failed to parse action geneve options data"); |
1283 | 0 | return EINVAL; |
1284 | 0 | } |
1285 | | |
1286 | 0 | opt->length = nl_attr_get_size(nla) / 4; |
1287 | 0 | memcpy(opt + 1, nl_attr_get_unspec(nla, 1), opt->length * 4); |
1288 | 0 | cnt += opt->length; |
1289 | 0 | action->encap.data.present.len += opt->length * 4; |
1290 | 0 | last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA; |
1291 | 0 | break; |
1292 | 0 | } |
1293 | 0 | } |
1294 | | |
1295 | 0 | if (last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA) { |
1296 | 0 | VLOG_ERR_RL(&error_rl, |
1297 | 0 | "failed to parse action geneve options without data"); |
1298 | 0 | return EINVAL; |
1299 | 0 | } |
1300 | | |
1301 | 0 | return 0; |
1302 | 0 | } |
1303 | | |
1304 | | static int |
1305 | | nl_parse_act_vxlan_opts(struct nlattr *in_nlattr, struct tc_action *action) |
1306 | 0 | { |
1307 | 0 | const struct ofpbuf *msg; |
1308 | 0 | struct nlattr *nla; |
1309 | 0 | struct ofpbuf buf; |
1310 | 0 | size_t left; |
1311 | |
|
1312 | 0 | nl_attr_get_nested(in_nlattr, &buf); |
1313 | 0 | msg = &buf; |
1314 | |
|
1315 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) { |
1316 | 0 | uint16_t type = nl_attr_type(nla); |
1317 | 0 | int32_t gbp_raw; |
1318 | |
|
1319 | 0 | switch (type) { |
1320 | 0 | case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP: |
1321 | 0 | gbp_raw = nl_attr_get_u32(nla); |
1322 | 0 | odp_decode_gbp_raw(gbp_raw, &action->encap.gbp.id, |
1323 | 0 | &action->encap.gbp.flags); |
1324 | 0 | action->encap.gbp.id_present = true; |
1325 | |
|
1326 | 0 | break; |
1327 | 0 | } |
1328 | 0 | } |
1329 | | |
1330 | 0 | return 0; |
1331 | 0 | } |
1332 | | |
1333 | | static int |
1334 | | nl_parse_act_tunnel_opts(struct nlattr *options, struct tc_action *action) |
1335 | 0 | { |
1336 | 0 | const struct ofpbuf *msg; |
1337 | 0 | struct nlattr *nla; |
1338 | 0 | struct ofpbuf buf; |
1339 | 0 | size_t left; |
1340 | 0 | int err; |
1341 | |
|
1342 | 0 | if (!options) { |
1343 | 0 | return 0; |
1344 | 0 | } |
1345 | | |
1346 | 0 | nl_attr_get_nested(options, &buf); |
1347 | 0 | msg = &buf; |
1348 | |
|
1349 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) { |
1350 | 0 | uint16_t type = nl_attr_type(nla); |
1351 | 0 | switch (type) { |
1352 | 0 | case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE: |
1353 | 0 | err = nl_parse_act_geneve_opts(nla, action); |
1354 | 0 | if (err) { |
1355 | 0 | return err; |
1356 | 0 | } |
1357 | 0 | break; |
1358 | 0 | case TCA_TUNNEL_KEY_ENC_OPTS_VXLAN: |
1359 | 0 | err = nl_parse_act_vxlan_opts(nla, action); |
1360 | 0 | if (err) { |
1361 | 0 | return err; |
1362 | 0 | } |
1363 | 0 | break; |
1364 | 0 | } |
1365 | 0 | } |
1366 | | |
1367 | 0 | return 0; |
1368 | 0 | } |
1369 | | |
1370 | | static int |
1371 | | nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower) |
1372 | 0 | { |
1373 | 0 | struct nlattr *tun_attrs[ARRAY_SIZE(tunnel_key_policy)]; |
1374 | 0 | const struct nlattr *tun_parms; |
1375 | 0 | const struct tc_tunnel_key *tun; |
1376 | 0 | struct tc_action *action; |
1377 | 0 | int err; |
1378 | |
|
1379 | 0 | if (!nl_parse_nested(options, tunnel_key_policy, tun_attrs, |
1380 | 0 | ARRAY_SIZE(tunnel_key_policy))) { |
1381 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tunnel_key action options"); |
1382 | 0 | return EPROTO; |
1383 | 0 | } |
1384 | | |
1385 | 0 | tun_parms = tun_attrs[TCA_TUNNEL_KEY_PARMS]; |
1386 | 0 | tun = nl_attr_get_unspec(tun_parms, sizeof *tun); |
1387 | 0 | if (tun->t_action == TCA_TUNNEL_KEY_ACT_SET) { |
1388 | 0 | struct nlattr *id = tun_attrs[TCA_TUNNEL_KEY_ENC_KEY_ID]; |
1389 | 0 | struct nlattr *dst_port = tun_attrs[TCA_TUNNEL_KEY_ENC_DST_PORT]; |
1390 | 0 | struct nlattr *ipv4_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_SRC]; |
1391 | 0 | struct nlattr *ipv4_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_DST]; |
1392 | 0 | struct nlattr *ipv6_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_SRC]; |
1393 | 0 | struct nlattr *ipv6_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_DST]; |
1394 | 0 | struct nlattr *tos = tun_attrs[TCA_TUNNEL_KEY_ENC_TOS]; |
1395 | 0 | struct nlattr *ttl = tun_attrs[TCA_TUNNEL_KEY_ENC_TTL]; |
1396 | 0 | struct nlattr *tun_opt = tun_attrs[TCA_TUNNEL_KEY_ENC_OPTS]; |
1397 | 0 | struct nlattr *no_csum = tun_attrs[TCA_TUNNEL_KEY_NO_CSUM]; |
1398 | 0 | struct nlattr *no_frag = tun_attrs[TCA_TUNNEL_KEY_NO_FRAG]; |
1399 | |
|
1400 | 0 | action = &flower->actions[flower->action_count++]; |
1401 | 0 | action->type = TC_ACT_ENCAP; |
1402 | 0 | action->encap.ipv4.ipv4_src = ipv4_src ? nl_attr_get_be32(ipv4_src) : 0; |
1403 | 0 | action->encap.ipv4.ipv4_dst = ipv4_dst ? nl_attr_get_be32(ipv4_dst) : 0; |
1404 | 0 | if (ipv6_src) { |
1405 | 0 | action->encap.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src); |
1406 | 0 | } |
1407 | 0 | if (ipv6_dst) { |
1408 | 0 | action->encap.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst); |
1409 | 0 | } |
1410 | 0 | action->encap.id = id ? be32_to_be64(nl_attr_get_be32(id)) : 0; |
1411 | 0 | action->encap.id_present = id ? true : false; |
1412 | 0 | action->encap.tp_dst = dst_port ? nl_attr_get_be16(dst_port) : 0; |
1413 | 0 | action->encap.tos = tos ? nl_attr_get_u8(tos) : 0; |
1414 | 0 | action->encap.ttl = ttl ? nl_attr_get_u8(ttl) : 0; |
1415 | 0 | action->encap.no_csum = no_csum ? nl_attr_get_u8(no_csum) : 0; |
1416 | 0 | action->encap.dont_fragment = no_frag ? true : false; |
1417 | |
|
1418 | 0 | err = nl_parse_act_tunnel_opts(tun_opt, action); |
1419 | 0 | if (err) { |
1420 | 0 | return err; |
1421 | 0 | } |
1422 | 0 | nl_parse_action_pc(tun->action, action); |
1423 | 0 | } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) { |
1424 | 0 | flower->tunnel = true; |
1425 | 0 | } else { |
1426 | 0 | VLOG_ERR_RL(&error_rl, "unknown tunnel actions: %d, %d", |
1427 | 0 | tun->action, tun->t_action); |
1428 | 0 | return EINVAL; |
1429 | 0 | } |
1430 | 0 | return 0; |
1431 | 0 | } |
1432 | | |
1433 | | static const struct nl_policy gact_policy[] = { |
1434 | | [TCA_GACT_PARMS] = { .type = NL_A_UNSPEC, |
1435 | | .min_len = sizeof(struct tc_gact), |
1436 | | .optional = false, }, |
1437 | | [TCA_GACT_TM] = { .type = NL_A_UNSPEC, |
1438 | | .min_len = sizeof(struct tcf_t), |
1439 | | .optional = false, }, |
1440 | | }; |
1441 | | |
1442 | | static int |
1443 | | get_user_hz(void) |
1444 | 0 | { |
1445 | 0 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
1446 | 0 | static int user_hz = 100; |
1447 | |
|
1448 | 0 | if (ovsthread_once_start(&once)) { |
1449 | 0 | user_hz = sysconf(_SC_CLK_TCK); |
1450 | 0 | ovsthread_once_done(&once); |
1451 | 0 | } |
1452 | |
|
1453 | 0 | return user_hz; |
1454 | 0 | } |
1455 | | |
1456 | | static void |
1457 | | nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower) |
1458 | 0 | { |
1459 | 0 | uint64_t lastused; |
1460 | | |
1461 | | /* On creation both tm->install and tm->lastuse are set to jiffies |
1462 | | * by the kernel. So if both values are the same, the flow has not been |
1463 | | * used yet. |
1464 | | * |
1465 | | * Note that tm->firstuse can not be used due to some kernel bug, i.e., |
1466 | | * hardware offloaded flows do not update tm->firstuse. */ |
1467 | 0 | if (tm->lastuse == tm->install) { |
1468 | 0 | lastused = 0; |
1469 | 0 | } else { |
1470 | 0 | lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz()); |
1471 | 0 | } |
1472 | |
|
1473 | 0 | if (flower->lastused < lastused) { |
1474 | 0 | flower->lastused = lastused; |
1475 | 0 | } |
1476 | 0 | } |
1477 | | |
1478 | | static int |
1479 | | nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower) |
1480 | 0 | { |
1481 | 0 | struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)]; |
1482 | 0 | const struct tc_gact *p; |
1483 | 0 | struct nlattr *gact_parms; |
1484 | 0 | struct tc_action *action; |
1485 | 0 | struct tcf_t tm; |
1486 | |
|
1487 | 0 | if (!nl_parse_nested(options, gact_policy, gact_attrs, |
1488 | 0 | ARRAY_SIZE(gact_policy))) { |
1489 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse gact action options"); |
1490 | 0 | return EPROTO; |
1491 | 0 | } |
1492 | | |
1493 | 0 | gact_parms = gact_attrs[TCA_GACT_PARMS]; |
1494 | 0 | p = nl_attr_get_unspec(gact_parms, sizeof *p); |
1495 | |
|
1496 | 0 | if (TC_ACT_EXT_CMP(p->action, TC_ACT_GOTO_CHAIN)) { |
1497 | 0 | action = &flower->actions[flower->action_count++]; |
1498 | 0 | action->chain = p->action & TC_ACT_EXT_VAL_MASK; |
1499 | 0 | action->type = TC_ACT_GOTO; |
1500 | 0 | nl_parse_action_pc(p->action, action); |
1501 | 0 | } else if (p->action != TC_ACT_SHOT) { |
1502 | 0 | VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action); |
1503 | 0 | return EINVAL; |
1504 | 0 | } |
1505 | | |
1506 | 0 | memcpy(&tm, nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof tm), |
1507 | 0 | sizeof tm); |
1508 | 0 | nl_parse_tcf(&tm, flower); |
1509 | |
|
1510 | 0 | return 0; |
1511 | 0 | } |
1512 | | |
1513 | | static const struct nl_policy police_policy[] = { |
1514 | | [TCA_POLICE_TBF] = { .type = NL_A_UNSPEC, |
1515 | | .min_len = sizeof(struct tc_police), |
1516 | | .optional = false, }, |
1517 | | [TCA_POLICE_RATE] = { .type = NL_A_UNSPEC, |
1518 | | .min_len = 1024, |
1519 | | .optional = true, }, |
1520 | | [TCA_POLICE_RATE64] = { .type = NL_A_U32, |
1521 | | .optional = true, }, |
1522 | | [TCA_POLICE_PEAKRATE] = { .type = NL_A_UNSPEC, |
1523 | | .min_len = 1024, |
1524 | | .optional = true, }, |
1525 | | [TCA_POLICE_AVRATE] = { .type = NL_A_U32, |
1526 | | .optional = true, }, |
1527 | | [TCA_POLICE_RESULT] = { .type = NL_A_U32, |
1528 | | .optional = true, }, |
1529 | | [TCA_POLICE_TM] = { .type = NL_A_UNSPEC, |
1530 | | .min_len = sizeof(struct tcf_t), |
1531 | | .optional = true, }, |
1532 | | }; |
1533 | | |
1534 | | static int |
1535 | | nl_parse_act_police(const struct nlattr *options, struct tc_flower *flower) |
1536 | 0 | { |
1537 | 0 | struct nlattr *police_attrs[ARRAY_SIZE(police_policy)] = {}; |
1538 | 0 | const struct tc_police *police; |
1539 | 0 | struct nlattr *police_result; |
1540 | 0 | struct tc_action *action; |
1541 | 0 | struct nlattr *police_tm; |
1542 | 0 | struct tcf_t tm; |
1543 | |
|
1544 | 0 | if (!nl_parse_nested(options, police_policy, police_attrs, |
1545 | 0 | ARRAY_SIZE(police_policy))) { |
1546 | 0 | VLOG_ERR_RL(&error_rl, "Failed to parse police action options"); |
1547 | 0 | return EPROTO; |
1548 | 0 | } |
1549 | | |
1550 | 0 | police = nl_attr_get_unspec(police_attrs[TCA_POLICE_TBF], sizeof *police); |
1551 | 0 | action = &flower->actions[flower->action_count++]; |
1552 | |
|
1553 | 0 | police_result = police_attrs[TCA_POLICE_RESULT]; |
1554 | 0 | if (police_result && !tc_is_meter_index(police->index)) { |
1555 | 0 | action->type = TC_ACT_POLICE_MTU; |
1556 | 0 | action->police.mtu = police->mtu; |
1557 | |
|
1558 | 0 | uint32_t action_pc = nl_attr_get_u32(police_result); |
1559 | 0 | if (action_pc & TC_ACT_JUMP) { |
1560 | 0 | action->police.result_jump = action_pc & TC_ACT_EXT_VAL_MASK; |
1561 | 0 | } else { |
1562 | 0 | action->police.result_jump = JUMP_ACTION_STOP; |
1563 | 0 | } |
1564 | 0 | } else { |
1565 | 0 | action->type = TC_ACT_POLICE; |
1566 | 0 | action->police.index = police->index; |
1567 | 0 | } |
1568 | |
|
1569 | 0 | police_tm = police_attrs[TCA_POLICE_TM]; |
1570 | 0 | if (police_tm) { |
1571 | 0 | memcpy(&tm, nl_attr_get_unspec(police_tm, sizeof tm), sizeof tm); |
1572 | 0 | nl_parse_tcf(&tm, flower); |
1573 | 0 | } |
1574 | |
|
1575 | 0 | nl_parse_action_pc(police->action, action); |
1576 | 0 | return 0; |
1577 | 0 | } |
1578 | | |
1579 | | static const struct nl_policy mirred_policy[] = { |
1580 | | [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC, |
1581 | | .min_len = sizeof(struct tc_mirred), |
1582 | | .optional = false, }, |
1583 | | [TCA_MIRRED_TM] = { .type = NL_A_UNSPEC, |
1584 | | .min_len = sizeof(struct tcf_t), |
1585 | | .optional = false, }, |
1586 | | }; |
1587 | | |
1588 | | static int |
1589 | | nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower) |
1590 | 0 | { |
1591 | |
|
1592 | 0 | struct nlattr *mirred_attrs[ARRAY_SIZE(mirred_policy)]; |
1593 | 0 | const struct tc_mirred *m; |
1594 | 0 | const struct nlattr *mirred_parms; |
1595 | 0 | struct nlattr *mirred_tm; |
1596 | 0 | struct tc_action *action; |
1597 | 0 | struct tcf_t tm; |
1598 | |
|
1599 | 0 | if (!nl_parse_nested(options, mirred_policy, mirred_attrs, |
1600 | 0 | ARRAY_SIZE(mirred_policy))) { |
1601 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse mirred action options"); |
1602 | 0 | return EPROTO; |
1603 | 0 | } |
1604 | | |
1605 | 0 | mirred_parms = mirred_attrs[TCA_MIRRED_PARMS]; |
1606 | 0 | m = nl_attr_get_unspec(mirred_parms, sizeof *m); |
1607 | |
|
1608 | 0 | if (m->eaction != TCA_EGRESS_REDIR && m->eaction != TCA_EGRESS_MIRROR && |
1609 | 0 | m->eaction != TCA_INGRESS_REDIR && m->eaction != TCA_INGRESS_MIRROR) { |
1610 | 0 | VLOG_ERR_RL(&error_rl, "unknown mirred action: %d, %d, %d", |
1611 | 0 | m->action, m->eaction, m->ifindex); |
1612 | 0 | return EINVAL; |
1613 | 0 | } |
1614 | | |
1615 | 0 | action = &flower->actions[flower->action_count++]; |
1616 | 0 | action->out.ifindex_out = m->ifindex; |
1617 | 0 | if (m->eaction == TCA_INGRESS_REDIR || m->eaction == TCA_INGRESS_MIRROR) { |
1618 | 0 | action->out.ingress = true; |
1619 | 0 | } else { |
1620 | 0 | action->out.ingress = false; |
1621 | 0 | } |
1622 | 0 | action->type = TC_ACT_OUTPUT; |
1623 | |
|
1624 | 0 | mirred_tm = mirred_attrs[TCA_MIRRED_TM]; |
1625 | 0 | memcpy(&tm, nl_attr_get_unspec(mirred_tm, sizeof tm), sizeof tm); |
1626 | |
|
1627 | 0 | nl_parse_tcf(&tm, flower); |
1628 | 0 | nl_parse_action_pc(m->action, action); |
1629 | 0 | return 0; |
1630 | 0 | } |
1631 | | |
1632 | | static const struct nl_policy ct_policy[] = { |
1633 | | [TCA_CT_PARMS] = { .type = NL_A_UNSPEC, |
1634 | | .min_len = sizeof(struct tc_ct), |
1635 | | .optional = false, }, |
1636 | | [TCA_CT_ACTION] = { .type = NL_A_U16, |
1637 | | .optional = true, }, |
1638 | | [TCA_CT_ZONE] = { .type = NL_A_U16, |
1639 | | .optional = true, }, |
1640 | | [TCA_CT_MARK] = { .type = NL_A_U32, |
1641 | | .optional = true, }, |
1642 | | [TCA_CT_MARK_MASK] = { .type = NL_A_U32, |
1643 | | .optional = true, }, |
1644 | | [TCA_CT_LABELS] = { .type = NL_A_UNSPEC, |
1645 | | .optional = true, }, |
1646 | | [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC, |
1647 | | .optional = true, }, |
1648 | | [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32, |
1649 | | .optional = true, }, |
1650 | | [TCA_CT_NAT_IPV4_MAX] = { .type = NL_A_U32, |
1651 | | .optional = true, }, |
1652 | | [TCA_CT_NAT_IPV6_MIN] = { .min_len = sizeof(struct in6_addr), |
1653 | | .type = NL_A_UNSPEC, |
1654 | | .optional = true }, |
1655 | | [TCA_CT_NAT_IPV6_MAX] = { .min_len = sizeof(struct in6_addr), |
1656 | | .type = NL_A_UNSPEC, |
1657 | | .optional = true }, |
1658 | | [TCA_CT_NAT_PORT_MIN] = { .type = NL_A_U16, |
1659 | | .optional = true, }, |
1660 | | [TCA_CT_NAT_PORT_MAX] = { .type = NL_A_U16, |
1661 | | .optional = true, }, |
1662 | | [TCA_CT_TM] = { .type = NL_A_UNSPEC, |
1663 | | .min_len = sizeof(struct tcf_t), |
1664 | | .optional = true, }, |
1665 | | }; |
1666 | | |
1667 | | static int |
1668 | | nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower) |
1669 | 0 | { |
1670 | 0 | struct nlattr *ct_attrs[ARRAY_SIZE(ct_policy)]; |
1671 | 0 | const struct nlattr *ct_parms; |
1672 | 0 | struct tc_action *action; |
1673 | 0 | const struct tc_ct *ct; |
1674 | 0 | uint16_t ct_action = 0; |
1675 | 0 | struct tcf_t tm; |
1676 | |
|
1677 | 0 | if (!nl_parse_nested(options, ct_policy, ct_attrs, |
1678 | 0 | ARRAY_SIZE(ct_policy))) { |
1679 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse ct action options"); |
1680 | 0 | return EPROTO; |
1681 | 0 | } |
1682 | | |
1683 | 0 | ct_parms = ct_attrs[TCA_CT_PARMS]; |
1684 | 0 | ct = nl_attr_get_unspec(ct_parms, sizeof *ct); |
1685 | |
|
1686 | 0 | if (ct_attrs[TCA_CT_ACTION]) { |
1687 | 0 | ct_action = nl_attr_get_u16(ct_attrs[TCA_CT_ACTION]); |
1688 | 0 | } |
1689 | |
|
1690 | 0 | action = &flower->actions[flower->action_count++]; |
1691 | 0 | action->ct.clear = ct_action & TCA_CT_ACT_CLEAR; |
1692 | 0 | if (!action->ct.clear) { |
1693 | 0 | struct nlattr *zone = ct_attrs[TCA_CT_ZONE]; |
1694 | 0 | struct nlattr *mark = ct_attrs[TCA_CT_MARK]; |
1695 | 0 | struct nlattr *mark_mask = ct_attrs[TCA_CT_MARK_MASK]; |
1696 | 0 | struct nlattr *label = ct_attrs[TCA_CT_LABELS]; |
1697 | 0 | struct nlattr *label_mask = ct_attrs[TCA_CT_LABELS_MASK]; |
1698 | |
|
1699 | 0 | action->ct.commit = ct_action & TCA_CT_ACT_COMMIT; |
1700 | 0 | action->ct.force = ct_action & TCA_CT_ACT_FORCE; |
1701 | |
|
1702 | 0 | action->ct.zone = zone ? nl_attr_get_u16(zone) : 0; |
1703 | 0 | action->ct.mark = mark ? nl_attr_get_u32(mark) : 0; |
1704 | 0 | action->ct.mark_mask = mark_mask ? nl_attr_get_u32(mark_mask) : 0; |
1705 | 0 | action->ct.label = label? nl_attr_get_u128(label) : OVS_U128_ZERO; |
1706 | 0 | action->ct.label_mask = label_mask ? |
1707 | 0 | nl_attr_get_u128(label_mask) : OVS_U128_ZERO; |
1708 | |
|
1709 | 0 | if (ct_action & TCA_CT_ACT_NAT) { |
1710 | 0 | struct nlattr *ipv4_min = ct_attrs[TCA_CT_NAT_IPV4_MIN]; |
1711 | 0 | struct nlattr *ipv4_max = ct_attrs[TCA_CT_NAT_IPV4_MAX]; |
1712 | 0 | struct nlattr *ipv6_min = ct_attrs[TCA_CT_NAT_IPV6_MIN]; |
1713 | 0 | struct nlattr *ipv6_max = ct_attrs[TCA_CT_NAT_IPV6_MAX]; |
1714 | 0 | struct nlattr *port_min = ct_attrs[TCA_CT_NAT_PORT_MIN]; |
1715 | 0 | struct nlattr *port_max = ct_attrs[TCA_CT_NAT_PORT_MAX]; |
1716 | |
|
1717 | 0 | action->ct.nat_type = TC_NAT_RESTORE; |
1718 | 0 | if (ct_action & TCA_CT_ACT_NAT_SRC) { |
1719 | 0 | action->ct.nat_type = TC_NAT_SRC; |
1720 | 0 | } else if (ct_action & TCA_CT_ACT_NAT_DST) { |
1721 | 0 | action->ct.nat_type = TC_NAT_DST; |
1722 | 0 | } |
1723 | |
|
1724 | 0 | if (ipv4_min) { |
1725 | 0 | action->ct.range.ip_family = AF_INET; |
1726 | 0 | action->ct.range.ipv4.min = nl_attr_get_be32(ipv4_min); |
1727 | 0 | if (ipv4_max) { |
1728 | 0 | ovs_be32 addr = nl_attr_get_be32(ipv4_max); |
1729 | |
|
1730 | 0 | if (action->ct.range.ipv4.min != addr) { |
1731 | 0 | action->ct.range.ipv4.max = addr; |
1732 | 0 | } |
1733 | 0 | } |
1734 | 0 | } else if (ipv6_min) { |
1735 | 0 | action->ct.range.ip_family = AF_INET6; |
1736 | 0 | action->ct.range.ipv6.min |
1737 | 0 | = nl_attr_get_in6_addr(ipv6_min); |
1738 | 0 | if (ipv6_max) { |
1739 | 0 | struct in6_addr addr = nl_attr_get_in6_addr(ipv6_max); |
1740 | |
|
1741 | 0 | if (!ipv6_addr_equals(&action->ct.range.ipv6.min, &addr)) { |
1742 | 0 | action->ct.range.ipv6.max = addr; |
1743 | 0 | } |
1744 | 0 | } |
1745 | 0 | } |
1746 | |
|
1747 | 0 | if (port_min) { |
1748 | 0 | action->ct.range.port.min = nl_attr_get_be16(port_min); |
1749 | 0 | if (port_max) { |
1750 | 0 | action->ct.range.port.max = nl_attr_get_be16(port_max); |
1751 | 0 | if (action->ct.range.port.min == |
1752 | 0 | action->ct.range.port.max) { |
1753 | 0 | action->ct.range.port.max = 0; |
1754 | 0 | } |
1755 | 0 | } |
1756 | 0 | } |
1757 | 0 | } |
1758 | 0 | } |
1759 | 0 | action->type = TC_ACT_CT; |
1760 | |
|
1761 | 0 | if (ct_attrs[TCA_CT_TM]) { |
1762 | 0 | memcpy(&tm, nl_attr_get_unspec(ct_attrs[TCA_CT_TM], sizeof tm), |
1763 | 0 | sizeof tm); |
1764 | 0 | nl_parse_tcf(&tm, flower); |
1765 | 0 | } |
1766 | 0 | nl_parse_action_pc(ct->action, action); |
1767 | 0 | return 0; |
1768 | 0 | } |
1769 | | |
1770 | | static const struct nl_policy vlan_policy[] = { |
1771 | | [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC, |
1772 | | .min_len = sizeof(struct tc_vlan), |
1773 | | .optional = false, }, |
1774 | | [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, }, |
1775 | | [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, }, |
1776 | | [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, }, |
1777 | | }; |
1778 | | |
1779 | | static int |
1780 | | nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower) |
1781 | 0 | { |
1782 | 0 | struct nlattr *vlan_attrs[ARRAY_SIZE(vlan_policy)]; |
1783 | 0 | const struct tc_vlan *v; |
1784 | 0 | const struct nlattr *vlan_parms; |
1785 | 0 | struct tc_action *action; |
1786 | |
|
1787 | 0 | if (!nl_parse_nested(options, vlan_policy, vlan_attrs, |
1788 | 0 | ARRAY_SIZE(vlan_policy))) { |
1789 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse vlan action options"); |
1790 | 0 | return EPROTO; |
1791 | 0 | } |
1792 | | |
1793 | 0 | action = &flower->actions[flower->action_count++]; |
1794 | 0 | vlan_parms = vlan_attrs[TCA_VLAN_PARMS]; |
1795 | 0 | v = nl_attr_get_unspec(vlan_parms, sizeof *v); |
1796 | 0 | if (v->v_action == TCA_VLAN_ACT_PUSH) { |
1797 | 0 | struct nlattr *vlan_tpid = vlan_attrs[TCA_VLAN_PUSH_VLAN_PROTOCOL]; |
1798 | 0 | struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID]; |
1799 | 0 | struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY]; |
1800 | |
|
1801 | 0 | action->vlan.vlan_push_tpid = nl_attr_get_be16(vlan_tpid); |
1802 | 0 | action->vlan.vlan_push_id = nl_attr_get_u16(vlan_id); |
1803 | 0 | action->vlan.vlan_push_prio = vlan_prio ? nl_attr_get_u8(vlan_prio) : 0; |
1804 | 0 | action->type = TC_ACT_VLAN_PUSH; |
1805 | 0 | } else if (v->v_action == TCA_VLAN_ACT_POP) { |
1806 | 0 | action->type = TC_ACT_VLAN_POP; |
1807 | 0 | } else { |
1808 | 0 | VLOG_ERR_RL(&error_rl, "unknown vlan action: %d, %d", |
1809 | 0 | v->action, v->v_action); |
1810 | 0 | return EINVAL; |
1811 | 0 | } |
1812 | | |
1813 | 0 | nl_parse_action_pc(v->action, action); |
1814 | 0 | return 0; |
1815 | 0 | } |
1816 | | |
1817 | | static const struct nl_policy mpls_policy[] = { |
1818 | | [TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC, |
1819 | | .min_len = sizeof(struct tc_mpls), |
1820 | | .optional = false, }, |
1821 | | [TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, }, |
1822 | | [TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, }, |
1823 | | [TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, }, |
1824 | | [TCA_MPLS_TTL] = { .type = NL_A_U8, .optional = true, }, |
1825 | | [TCA_MPLS_BOS] = { .type = NL_A_U8, .optional = true, }, |
1826 | | }; |
1827 | | |
1828 | | static int |
1829 | | nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower) |
1830 | 0 | { |
1831 | 0 | struct nlattr *mpls_attrs[ARRAY_SIZE(mpls_policy)]; |
1832 | 0 | const struct nlattr *mpls_parms; |
1833 | 0 | struct nlattr *mpls_proto; |
1834 | 0 | struct nlattr *mpls_label; |
1835 | 0 | struct tc_action *action; |
1836 | 0 | const struct tc_mpls *m; |
1837 | 0 | struct nlattr *mpls_ttl; |
1838 | 0 | struct nlattr *mpls_bos; |
1839 | 0 | struct nlattr *mpls_tc; |
1840 | |
|
1841 | 0 | if (!nl_parse_nested(options, mpls_policy, mpls_attrs, |
1842 | 0 | ARRAY_SIZE(mpls_policy))) { |
1843 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse mpls action options"); |
1844 | 0 | return EPROTO; |
1845 | 0 | } |
1846 | | |
1847 | 0 | action = &flower->actions[flower->action_count++]; |
1848 | 0 | mpls_parms = mpls_attrs[TCA_MPLS_PARMS]; |
1849 | 0 | m = nl_attr_get_unspec(mpls_parms, sizeof *m); |
1850 | |
|
1851 | 0 | switch (m->m_action) { |
1852 | 0 | case TCA_MPLS_ACT_POP: |
1853 | 0 | mpls_proto = mpls_attrs[TCA_MPLS_PROTO]; |
1854 | 0 | if (mpls_proto) { |
1855 | 0 | action->mpls.proto = nl_attr_get_be16(mpls_proto); |
1856 | 0 | } |
1857 | 0 | action->type = TC_ACT_MPLS_POP; |
1858 | 0 | break; |
1859 | 0 | case TCA_MPLS_ACT_PUSH: |
1860 | 0 | mpls_proto = mpls_attrs[TCA_MPLS_PROTO]; |
1861 | 0 | if (mpls_proto) { |
1862 | 0 | action->mpls.proto = nl_attr_get_be16(mpls_proto); |
1863 | 0 | } |
1864 | 0 | mpls_label = mpls_attrs[TCA_MPLS_LABEL]; |
1865 | 0 | if (mpls_label) { |
1866 | 0 | action->mpls.label = nl_attr_get_u32(mpls_label); |
1867 | 0 | } |
1868 | 0 | mpls_tc = mpls_attrs[TCA_MPLS_TC]; |
1869 | 0 | if (mpls_tc) { |
1870 | 0 | action->mpls.tc = nl_attr_get_u8(mpls_tc); |
1871 | 0 | } |
1872 | 0 | mpls_ttl = mpls_attrs[TCA_MPLS_TTL]; |
1873 | 0 | if (mpls_ttl) { |
1874 | 0 | action->mpls.ttl = nl_attr_get_u8(mpls_ttl); |
1875 | 0 | } |
1876 | 0 | mpls_bos = mpls_attrs[TCA_MPLS_BOS]; |
1877 | 0 | if (mpls_bos) { |
1878 | 0 | action->mpls.bos = nl_attr_get_u8(mpls_bos); |
1879 | 0 | } |
1880 | 0 | action->type = TC_ACT_MPLS_PUSH; |
1881 | 0 | break; |
1882 | 0 | case TCA_MPLS_ACT_MODIFY: |
1883 | 0 | mpls_label = mpls_attrs[TCA_MPLS_LABEL]; |
1884 | 0 | if (mpls_label) { |
1885 | 0 | action->mpls.label = nl_attr_get_u32(mpls_label); |
1886 | 0 | } |
1887 | 0 | mpls_tc = mpls_attrs[TCA_MPLS_TC]; |
1888 | 0 | if (mpls_tc) { |
1889 | 0 | action->mpls.tc = nl_attr_get_u8(mpls_tc); |
1890 | 0 | } |
1891 | 0 | mpls_ttl = mpls_attrs[TCA_MPLS_TTL]; |
1892 | 0 | if (mpls_ttl) { |
1893 | 0 | action->mpls.ttl = nl_attr_get_u8(mpls_ttl); |
1894 | 0 | } |
1895 | 0 | mpls_bos = mpls_attrs[TCA_MPLS_BOS]; |
1896 | 0 | if (mpls_bos) { |
1897 | 0 | action->mpls.bos = nl_attr_get_u8(mpls_bos); |
1898 | 0 | } |
1899 | 0 | action->type = TC_ACT_MPLS_SET; |
1900 | 0 | break; |
1901 | 0 | default: |
1902 | 0 | VLOG_ERR_RL(&error_rl, "unknown mpls action: %d, %d", |
1903 | 0 | m->action, m->m_action); |
1904 | 0 | return EINVAL; |
1905 | 0 | } |
1906 | | |
1907 | 0 | nl_parse_action_pc(m->action, action); |
1908 | 0 | return 0; |
1909 | 0 | } |
1910 | | |
1911 | | static const struct nl_policy csum_policy[] = { |
1912 | | [TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC, |
1913 | | .min_len = sizeof(struct tc_csum), |
1914 | | .optional = false, }, |
1915 | | }; |
1916 | | |
1917 | | static int |
1918 | | nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower) |
1919 | 0 | { |
1920 | 0 | struct nlattr *csum_attrs[ARRAY_SIZE(csum_policy)]; |
1921 | 0 | const struct tc_csum *c; |
1922 | 0 | const struct nlattr *csum_parms; |
1923 | |
|
1924 | 0 | if (!nl_parse_nested(options, csum_policy, csum_attrs, |
1925 | 0 | ARRAY_SIZE(csum_policy))) { |
1926 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse csum action options"); |
1927 | 0 | return EPROTO; |
1928 | 0 | } |
1929 | | |
1930 | 0 | csum_parms = csum_attrs[TCA_CSUM_PARMS]; |
1931 | 0 | c = nl_attr_get_unspec(csum_parms, sizeof *c); |
1932 | | |
1933 | | /* sanity checks */ |
1934 | 0 | if (c->update_flags != flower->csum_update_flags) { |
1935 | 0 | VLOG_WARN_RL(&error_rl, |
1936 | 0 | "expected different act csum flags: 0x%x != 0x%x", |
1937 | 0 | flower->csum_update_flags, c->update_flags); |
1938 | 0 | return EINVAL; |
1939 | 0 | } |
1940 | 0 | flower->csum_update_flags = 0; /* so we know csum was handled */ |
1941 | |
|
1942 | 0 | if (flower->needs_full_ip_proto_mask |
1943 | 0 | && flower->mask.ip_proto != UINT8_MAX) { |
1944 | 0 | VLOG_WARN_RL(&error_rl, "expected full matching on flower ip_proto"); |
1945 | 0 | return EINVAL; |
1946 | 0 | } |
1947 | | |
1948 | | /* The action_pc should be set on the previous action. */ |
1949 | 0 | if (flower->action_count < TCA_ACT_MAX_NUM) { |
1950 | 0 | struct tc_action *action = &flower->actions[flower->action_count]; |
1951 | |
|
1952 | 0 | nl_parse_action_pc(c->action, action); |
1953 | 0 | } |
1954 | 0 | return 0; |
1955 | 0 | } |
1956 | | |
1957 | | static const struct nl_policy act_policy[] = { |
1958 | | [TCA_ACT_KIND] = { .type = NL_A_STRING, .optional = false, }, |
1959 | | [TCA_ACT_COOKIE] = { .type = NL_A_UNSPEC, .optional = true, }, |
1960 | | [TCA_ACT_OPTIONS] = { .type = NL_A_NESTED, .optional = true, }, |
1961 | | [TCA_ACT_STATS] = { .type = NL_A_NESTED, .optional = false, }, |
1962 | | }; |
1963 | | |
1964 | | static int |
1965 | | nl_parse_action_stats(struct nlattr *act_stats, |
1966 | | struct ovs_flow_stats *stats_sw, |
1967 | | struct ovs_flow_stats *stats_hw, |
1968 | | struct ovs_flow_stats *stats_dropped) |
1969 | 0 | { |
1970 | 0 | struct tc_flow_stats s_sw = {0}, s_hw = {0}; |
1971 | 0 | const struct gnet_stats_queue *qs = NULL; |
1972 | 0 | uint16_t prev_type = __TCA_STATS_MAX; |
1973 | 0 | const struct nlattr *nla; |
1974 | 0 | unsigned int seen = 0; |
1975 | 0 | size_t left; |
1976 | | |
1977 | | /* Cannot use nl_parse_nested due to duplicate attributes. */ |
1978 | 0 | NL_NESTED_FOR_EACH (nla, left, act_stats) { |
1979 | 0 | struct gnet_stats_basic stats_basic; |
1980 | 0 | uint16_t type = nl_attr_type(nla); |
1981 | |
|
1982 | 0 | seen |= 1 << type; |
1983 | |
|
1984 | 0 | switch (type) { |
1985 | 0 | case TCA_STATS_BASIC: |
1986 | 0 | memcpy(&stats_basic, nl_attr_get_unspec(nla, sizeof stats_basic), |
1987 | 0 | sizeof stats_basic); |
1988 | 0 | s_sw.n_packets = stats_basic.packets; |
1989 | 0 | s_sw.n_bytes = stats_basic.bytes; |
1990 | 0 | break; |
1991 | | |
1992 | 0 | case TCA_STATS_BASIC_HW: |
1993 | 0 | memcpy(&stats_basic, nl_attr_get_unspec(nla, sizeof stats_basic), |
1994 | 0 | sizeof stats_basic); |
1995 | 0 | s_hw.n_packets = stats_basic.packets; |
1996 | 0 | s_hw.n_bytes = stats_basic.bytes; |
1997 | 0 | break; |
1998 | | |
1999 | 0 | case TCA_STATS_QUEUE: |
2000 | 0 | qs = nl_attr_get_unspec(nla, sizeof *qs); |
2001 | 0 | break; |
2002 | | |
2003 | 0 | case TCA_STATS_PKT64: |
2004 | 0 | if (prev_type == TCA_STATS_BASIC) { |
2005 | 0 | s_sw.n_packets = nl_attr_get_u64(nla); |
2006 | 0 | } else if (prev_type == TCA_STATS_BASIC_HW) { |
2007 | 0 | s_hw.n_packets = nl_attr_get_u64(nla); |
2008 | 0 | } else { |
2009 | 0 | goto err; |
2010 | 0 | } |
2011 | 0 | break; |
2012 | | |
2013 | 0 | default: |
2014 | 0 | break; |
2015 | 0 | } |
2016 | 0 | prev_type = type; |
2017 | 0 | } |
2018 | | |
2019 | 0 | if (!(seen & (1 << TCA_STATS_BASIC))) { |
2020 | 0 | goto err; |
2021 | 0 | } |
2022 | | |
2023 | 0 | if (seen & (1 << TCA_STATS_BASIC_HW)) { |
2024 | 0 | s_sw.n_packets = s_sw.n_packets - s_hw.n_packets; |
2025 | 0 | s_sw.n_bytes = s_sw.n_bytes - s_hw.n_bytes; |
2026 | |
|
2027 | 0 | if (s_hw.n_packets > get_32aligned_u64(&stats_hw->n_packets)) { |
2028 | 0 | put_32aligned_u64(&stats_hw->n_packets, s_hw.n_packets); |
2029 | 0 | put_32aligned_u64(&stats_hw->n_bytes, s_hw.n_bytes); |
2030 | 0 | } |
2031 | 0 | } |
2032 | |
|
2033 | 0 | if (s_sw.n_packets > get_32aligned_u64(&stats_sw->n_packets)) { |
2034 | 0 | put_32aligned_u64(&stats_sw->n_packets, s_sw.n_packets); |
2035 | 0 | put_32aligned_u64(&stats_sw->n_bytes, s_sw.n_bytes); |
2036 | 0 | } |
2037 | |
|
2038 | 0 | if (stats_dropped && qs) { |
2039 | 0 | put_32aligned_u64(&stats_dropped->n_packets, qs->drops); |
2040 | 0 | } |
2041 | |
|
2042 | 0 | return 0; |
2043 | | |
2044 | 0 | err: |
2045 | 0 | VLOG_ERR_RL(&error_rl, "Failed to parse action stats policy"); |
2046 | 0 | return EPROTO; |
2047 | 0 | } |
2048 | | |
2049 | | static int |
2050 | | nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, |
2051 | | bool terse, bool *csum) |
2052 | 0 | { |
2053 | 0 | struct nlattr *act_options; |
2054 | 0 | struct nlattr *act_cookie; |
2055 | 0 | const char *act_kind; |
2056 | 0 | struct nlattr *action_attrs[ARRAY_SIZE(act_policy)]; |
2057 | 0 | int err = 0; |
2058 | |
|
2059 | 0 | if (!nl_parse_nested(action, act_policy, action_attrs, |
2060 | 0 | ARRAY_SIZE(act_policy)) || |
2061 | 0 | (!terse && !action_attrs[TCA_ACT_OPTIONS])) { |
2062 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse single action options"); |
2063 | 0 | return EPROTO; |
2064 | 0 | } |
2065 | | |
2066 | 0 | *csum = false; |
2067 | 0 | act_kind = nl_attr_get_string(action_attrs[TCA_ACT_KIND]); |
2068 | 0 | act_options = action_attrs[TCA_ACT_OPTIONS]; |
2069 | 0 | act_cookie = action_attrs[TCA_ACT_COOKIE]; |
2070 | |
|
2071 | 0 | if (terse) { |
2072 | | /* Terse dump doesn't provide act options attribute. */ |
2073 | 0 | } else if (!strcmp(act_kind, "gact")) { |
2074 | 0 | err = nl_parse_act_gact(act_options, flower); |
2075 | 0 | } else if (!strcmp(act_kind, "mirred")) { |
2076 | 0 | err = nl_parse_act_mirred(act_options, flower); |
2077 | 0 | } else if (!strcmp(act_kind, "vlan")) { |
2078 | 0 | err = nl_parse_act_vlan(act_options, flower); |
2079 | 0 | } else if (!strcmp(act_kind, "mpls")) { |
2080 | 0 | err = nl_parse_act_mpls(act_options, flower); |
2081 | 0 | } else if (!strcmp(act_kind, "tunnel_key")) { |
2082 | 0 | err = nl_parse_act_tunnel_key(act_options, flower); |
2083 | 0 | } else if (!strcmp(act_kind, "pedit")) { |
2084 | 0 | err = nl_parse_act_pedit(act_options, flower); |
2085 | 0 | } else if (!strcmp(act_kind, "csum")) { |
2086 | 0 | nl_parse_act_csum(act_options, flower); |
2087 | 0 | *csum = true; |
2088 | 0 | } else if (!strcmp(act_kind, "skbedit")) { |
2089 | | /* Added for TC rule only (not in OvS rule) so ignore. */ |
2090 | 0 | } else if (!strcmp(act_kind, "ct")) { |
2091 | 0 | nl_parse_act_ct(act_options, flower); |
2092 | 0 | } else if (!strcmp(act_kind, "police")) { |
2093 | 0 | nl_parse_act_police(act_options, flower); |
2094 | 0 | } else { |
2095 | 0 | VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind); |
2096 | 0 | err = EINVAL; |
2097 | 0 | } |
2098 | |
|
2099 | 0 | if (err) { |
2100 | 0 | return err; |
2101 | 0 | } |
2102 | | |
2103 | 0 | if (act_cookie) { |
2104 | 0 | flower->act_cookie.data = nl_attr_get(act_cookie); |
2105 | 0 | flower->act_cookie.len = nl_attr_get_size(act_cookie); |
2106 | 0 | } |
2107 | |
|
2108 | 0 | return nl_parse_action_stats(action_attrs[TCA_ACT_STATS], |
2109 | 0 | &flower->stats_sw, &flower->stats_hw, NULL); |
2110 | 0 | } |
2111 | | |
2112 | | int |
2113 | | tc_parse_action_stats(struct nlattr *action, struct ovs_flow_stats *stats_sw, |
2114 | | struct ovs_flow_stats *stats_hw, |
2115 | | struct ovs_flow_stats *stats_dropped) |
2116 | 0 | { |
2117 | 0 | struct nlattr *action_attrs[ARRAY_SIZE(act_policy)]; |
2118 | |
|
2119 | 0 | if (!nl_parse_nested(action, act_policy, action_attrs, |
2120 | 0 | ARRAY_SIZE(act_policy))) { |
2121 | 0 | VLOG_ERR_RL(&error_rl, "Failed to parse single action options"); |
2122 | 0 | return EPROTO; |
2123 | 0 | } |
2124 | | |
2125 | 0 | return nl_parse_action_stats(action_attrs[TCA_ACT_STATS], stats_sw, |
2126 | 0 | stats_hw, stats_dropped); |
2127 | 0 | } |
2128 | | |
2129 | 0 | #define TCA_ACT_MIN_PRIO 1 |
2130 | | |
2131 | | static int |
2132 | | nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower, |
2133 | | bool terse) |
2134 | 0 | { |
2135 | 0 | const struct nlattr *actions = attrs[TCA_FLOWER_ACT]; |
2136 | 0 | static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {}; |
2137 | 0 | struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)]; |
2138 | 0 | const int max_size = ARRAY_SIZE(actions_orders_policy); |
2139 | 0 | int previous_action_count = 0; |
2140 | 0 | bool need_jump_adjust = false; |
2141 | 0 | int jump_adjust = 0; |
2142 | 0 | bool csum = false; |
2143 | |
|
2144 | 0 | for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) { |
2145 | 0 | actions_orders_policy[i].type = NL_A_NESTED; |
2146 | 0 | actions_orders_policy[i].optional = true; |
2147 | 0 | } |
2148 | |
|
2149 | 0 | if (!nl_parse_nested(actions, actions_orders_policy, actions_orders, |
2150 | 0 | ARRAY_SIZE(actions_orders_policy))) { |
2151 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse flower order of actions"); |
2152 | 0 | return EPROTO; |
2153 | 0 | } |
2154 | | |
2155 | 0 | for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) { |
2156 | 0 | if (actions_orders[i]) { |
2157 | 0 | int err; |
2158 | |
|
2159 | 0 | if (flower->action_count >= TCA_ACT_MAX_NUM) { |
2160 | 0 | VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM); |
2161 | 0 | return EOPNOTSUPP; |
2162 | 0 | } |
2163 | 0 | err = nl_parse_single_action(actions_orders[i], flower, terse, |
2164 | 0 | &csum); |
2165 | |
|
2166 | 0 | if (flower->action_count == previous_action_count) { |
2167 | |
|
2168 | 0 | struct tc_action *action; |
2169 | | |
2170 | | /* We had no update on the TC action count, which means |
2171 | | * we had a none TC type action. So need to adjust existing |
2172 | | * jump offsets. */ |
2173 | 0 | jump_adjust++; |
2174 | |
|
2175 | 0 | if (need_jump_adjust || (csum && flower->action_count > 0)) { |
2176 | |
|
2177 | 0 | if (csum && flower->action_count > 0) { |
2178 | | /* The csum action is special as it might carry |
2179 | | * a jump count for the previous TC_ACT and therefore |
2180 | | * should be adjusted with jump_adjust as it got |
2181 | | * copied. */ |
2182 | 0 | action = &flower->actions[flower->action_count - 1]; |
2183 | 0 | if (action->jump_action |
2184 | 0 | && action->jump_action != JUMP_ACTION_STOP) { |
2185 | 0 | action->jump_action -= (jump_adjust - 1); |
2186 | 0 | } |
2187 | 0 | } |
2188 | |
|
2189 | 0 | for (int j = 0; j < flower->action_count; j++) { |
2190 | 0 | action = &flower->actions[j]; |
2191 | |
|
2192 | 0 | if (action->type == TC_ACT_POLICE_MTU |
2193 | 0 | && action->police.result_jump != JUMP_ACTION_STOP |
2194 | 0 | && (action->police.result_jump - 1) > |
2195 | 0 | flower->action_count) { |
2196 | |
|
2197 | 0 | action->police.result_jump--; |
2198 | 0 | } |
2199 | |
|
2200 | 0 | if (action->jump_action |
2201 | 0 | && action->jump_action != JUMP_ACTION_STOP |
2202 | 0 | && (action->jump_action - 1) > |
2203 | 0 | flower->action_count) { |
2204 | |
|
2205 | 0 | action->jump_action--; |
2206 | 0 | } |
2207 | 0 | } |
2208 | 0 | } |
2209 | 0 | } else { |
2210 | 0 | struct tc_action *action; |
2211 | |
|
2212 | 0 | action = &flower->actions[previous_action_count]; |
2213 | 0 | if (action->type == TC_ACT_POLICE_MTU && |
2214 | 0 | action->police.result_jump != JUMP_ACTION_STOP) { |
2215 | 0 | action->police.result_jump -= jump_adjust; |
2216 | 0 | need_jump_adjust = true; |
2217 | 0 | } |
2218 | |
|
2219 | 0 | if (action->jump_action |
2220 | 0 | && action->jump_action != JUMP_ACTION_STOP) { |
2221 | 0 | action->jump_action -= jump_adjust; |
2222 | 0 | need_jump_adjust = true; |
2223 | 0 | } |
2224 | |
|
2225 | 0 | previous_action_count = flower->action_count; |
2226 | 0 | } |
2227 | |
|
2228 | 0 | if (err) { |
2229 | 0 | return err; |
2230 | 0 | } |
2231 | 0 | } |
2232 | 0 | } |
2233 | | |
2234 | 0 | if (flower->csum_update_flags) { |
2235 | 0 | VLOG_WARN_RL(&error_rl, |
2236 | 0 | "expected act csum with flags: 0x%x", |
2237 | 0 | flower->csum_update_flags); |
2238 | 0 | return EINVAL; |
2239 | 0 | } |
2240 | | |
2241 | 0 | return 0; |
2242 | 0 | } |
2243 | | |
2244 | | static int |
2245 | | nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower, |
2246 | | bool terse) |
2247 | 0 | { |
2248 | 0 | struct nlattr *attrs[ARRAY_SIZE(tca_flower_policy)]; |
2249 | 0 | int err; |
2250 | |
|
2251 | 0 | if (terse) { |
2252 | 0 | if (!nl_parse_nested(nl_options, tca_flower_terse_policy, |
2253 | 0 | attrs, ARRAY_SIZE(tca_flower_terse_policy))) { |
2254 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse flower classifier terse options"); |
2255 | 0 | return EPROTO; |
2256 | 0 | } |
2257 | 0 | goto skip_flower_opts; |
2258 | 0 | } |
2259 | | |
2260 | 0 | if (!nl_parse_nested(nl_options, tca_flower_policy, |
2261 | 0 | attrs, ARRAY_SIZE(tca_flower_policy))) { |
2262 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse flower classifier options"); |
2263 | 0 | return EPROTO; |
2264 | 0 | } |
2265 | | |
2266 | 0 | nl_parse_flower_eth(attrs, flower); |
2267 | 0 | nl_parse_flower_arp(attrs, flower); |
2268 | 0 | nl_parse_flower_mpls(attrs, flower); |
2269 | 0 | nl_parse_flower_vlan(attrs, flower); |
2270 | 0 | nl_parse_flower_ip(attrs, flower); |
2271 | 0 | err = nl_parse_flower_tunnel(attrs, flower); |
2272 | 0 | if (err) { |
2273 | 0 | return err; |
2274 | 0 | } |
2275 | | |
2276 | 0 | skip_flower_opts: |
2277 | 0 | nl_parse_flower_flags(attrs, flower); |
2278 | 0 | return nl_parse_flower_actions(attrs, flower, terse); |
2279 | 0 | } |
2280 | | |
2281 | | int |
2282 | | parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tcf_id *id, |
2283 | | struct tc_flower *flower, bool terse) |
2284 | 0 | { |
2285 | 0 | struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size); |
2286 | 0 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); |
2287 | 0 | struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc); |
2288 | 0 | struct nlattr *ta[ARRAY_SIZE(tca_policy)]; |
2289 | 0 | const char *kind; |
2290 | |
|
2291 | 0 | if (!nlmsg || !tc) { |
2292 | 0 | COVERAGE_INC(tc_netlink_malformed_reply); |
2293 | 0 | return EPROTO; |
2294 | 0 | } |
2295 | | |
2296 | 0 | memset(flower, 0, sizeof *flower); |
2297 | |
|
2298 | 0 | flower->key.eth_type = (OVS_FORCE ovs_be16) tc_get_minor(tc->tcm_info); |
2299 | 0 | flower->mask.eth_type = OVS_BE16_MAX; |
2300 | 0 | id->prio = tc_get_major(tc->tcm_info); |
2301 | 0 | id->handle = tc->tcm_handle; |
2302 | |
|
2303 | 0 | if (id->prio == TC_RESERVED_PRIORITY_POLICE) { |
2304 | 0 | return 0; |
2305 | 0 | } |
2306 | | |
2307 | 0 | if (!id->handle) { |
2308 | 0 | return EAGAIN; |
2309 | 0 | } |
2310 | | |
2311 | 0 | if (!nl_policy_parse(&b, 0, tca_policy, ta, ARRAY_SIZE(ta))) { |
2312 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tca policy"); |
2313 | 0 | return EPROTO; |
2314 | 0 | } |
2315 | | |
2316 | 0 | if (ta[TCA_CHAIN]) { |
2317 | 0 | id->chain = nl_attr_get_u32(ta[TCA_CHAIN]); |
2318 | 0 | } |
2319 | |
|
2320 | 0 | kind = nl_attr_get_string(ta[TCA_KIND]); |
2321 | 0 | if (strcmp(kind, "flower")) { |
2322 | 0 | VLOG_DBG_ONCE("Unsupported filter: %s", kind); |
2323 | 0 | return EPROTO; |
2324 | 0 | } |
2325 | | |
2326 | 0 | return nl_parse_flower_options(ta[TCA_OPTIONS], flower, terse); |
2327 | 0 | } |
2328 | | |
2329 | | int |
2330 | | parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t *chain) |
2331 | 0 | { |
2332 | 0 | struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size); |
2333 | 0 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); |
2334 | 0 | struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc); |
2335 | 0 | struct nlattr *ta[ARRAY_SIZE(tca_chain_policy)]; |
2336 | |
|
2337 | 0 | if (!nlmsg || !tc) { |
2338 | 0 | COVERAGE_INC(tc_netlink_malformed_reply); |
2339 | 0 | return EPROTO; |
2340 | 0 | } |
2341 | | |
2342 | 0 | if (!nl_policy_parse(&b, 0, tca_chain_policy, ta, ARRAY_SIZE(ta))) { |
2343 | 0 | VLOG_ERR_RL(&error_rl, "failed to parse tca chain policy"); |
2344 | 0 | return EINVAL; |
2345 | 0 | } |
2346 | | |
2347 | 0 | *chain = nl_attr_get_u32(ta[TCA_CHAIN]); |
2348 | |
|
2349 | 0 | return 0; |
2350 | 0 | } |
2351 | | |
2352 | | int |
2353 | | tc_dump_flower_start(struct tcf_id *id, struct nl_dump *dump, bool terse) |
2354 | 0 | { |
2355 | 0 | struct ofpbuf request; |
2356 | |
|
2357 | 0 | request_from_tcf_id(id, 0, RTM_GETTFILTER, NLM_F_DUMP, &request); |
2358 | 0 | if (terse) { |
2359 | 0 | struct nla_bitfield32 dump_flags = { TCA_DUMP_FLAGS_TERSE, |
2360 | 0 | TCA_DUMP_FLAGS_TERSE }; |
2361 | |
|
2362 | 0 | nl_msg_put_unspec(&request, TCA_DUMP_FLAGS, &dump_flags, |
2363 | 0 | sizeof dump_flags); |
2364 | 0 | } |
2365 | 0 | nl_dump_start(dump, NETLINK_ROUTE, &request); |
2366 | 0 | ofpbuf_uninit(&request); |
2367 | |
|
2368 | 0 | return 0; |
2369 | 0 | } |
2370 | | |
2371 | | int |
2372 | | tc_dump_tc_chain_start(struct tcf_id *id, struct nl_dump *dump) |
2373 | 0 | { |
2374 | 0 | struct ofpbuf request; |
2375 | |
|
2376 | 0 | request_from_tcf_id(id, 0, RTM_GETCHAIN, NLM_F_DUMP, &request); |
2377 | 0 | nl_dump_start(dump, NETLINK_ROUTE, &request); |
2378 | 0 | ofpbuf_uninit(&request); |
2379 | |
|
2380 | 0 | return 0; |
2381 | 0 | } |
2382 | | |
2383 | | int |
2384 | | tc_dump_tc_action_start(char *name, struct nl_dump *dump) |
2385 | 0 | { |
2386 | 0 | size_t offset, root_offset; |
2387 | 0 | struct ofpbuf request; |
2388 | |
|
2389 | 0 | tc_make_action_request(RTM_GETACTION, NLM_F_DUMP, &request); |
2390 | 0 | root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB); |
2391 | 0 | offset = nl_msg_start_nested(&request, 1); |
2392 | 0 | nl_msg_put_string(&request, TCA_ACT_KIND, name); |
2393 | 0 | nl_msg_end_nested(&request, offset); |
2394 | 0 | nl_msg_end_nested(&request, root_offset); |
2395 | |
|
2396 | 0 | nl_dump_start(dump, NETLINK_ROUTE, &request); |
2397 | 0 | ofpbuf_uninit(&request); |
2398 | |
|
2399 | 0 | return 0; |
2400 | 0 | } |
2401 | | |
2402 | | int |
2403 | | parse_netlink_to_tc_policer(struct ofpbuf *reply, uint32_t police_idx[]) |
2404 | 0 | { |
2405 | 0 | static struct nl_policy actions_orders_policy[TCA_ACT_MAX_PRIO] = {}; |
2406 | 0 | struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size); |
2407 | 0 | struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)]; |
2408 | 0 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); |
2409 | 0 | const int max_size = ARRAY_SIZE(actions_orders_policy); |
2410 | 0 | struct tcamsg *tca = ofpbuf_try_pull(&b, sizeof *tca); |
2411 | 0 | const struct nlattr *actions; |
2412 | 0 | struct tc_flower flower; |
2413 | 0 | int i, cnt = 0; |
2414 | 0 | int err; |
2415 | |
|
2416 | 0 | if (!nlmsg || !tca) { |
2417 | 0 | COVERAGE_INC(tc_netlink_malformed_reply); |
2418 | 0 | return EPROTO; |
2419 | 0 | } |
2420 | | |
2421 | 0 | for (i = 0; i < max_size; i++) { |
2422 | 0 | actions_orders_policy[i].type = NL_A_NESTED; |
2423 | 0 | actions_orders_policy[i].optional = true; |
2424 | 0 | } |
2425 | |
|
2426 | 0 | actions = nl_attr_find(&b, 0, TCA_ACT_TAB); |
2427 | 0 | if (!actions || !nl_parse_nested(actions, actions_orders_policy, |
2428 | 0 | actions_orders, max_size)) { |
2429 | 0 | VLOG_ERR_RL(&error_rl, |
2430 | 0 | "Failed to parse actions in netlink to policer"); |
2431 | 0 | return EPROTO; |
2432 | 0 | } |
2433 | | |
2434 | 0 | for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { |
2435 | 0 | if (actions_orders[i]) { |
2436 | 0 | bool csum; |
2437 | |
|
2438 | 0 | memset(&flower, 0, sizeof(struct tc_flower)); |
2439 | 0 | err = nl_parse_single_action(actions_orders[i], &flower, false, |
2440 | 0 | &csum); |
2441 | 0 | if (err || flower.actions[0].type != TC_ACT_POLICE) { |
2442 | 0 | continue; |
2443 | 0 | } |
2444 | 0 | if (flower.actions[0].police.index) { |
2445 | 0 | police_idx[cnt++] = flower.actions[0].police.index; |
2446 | 0 | } |
2447 | 0 | } |
2448 | 0 | } |
2449 | |
|
2450 | 0 | return 0; |
2451 | 0 | } |
2452 | | |
2453 | | int |
2454 | | tc_del_filter(struct tcf_id *id, const char *kind) |
2455 | 0 | { |
2456 | 0 | struct ofpbuf request; |
2457 | |
|
2458 | 0 | request_from_tcf_id(id, 0, RTM_DELTFILTER, NLM_F_ACK, &request); |
2459 | 0 | if (kind) { |
2460 | 0 | nl_msg_put_string(&request, TCA_KIND, kind); |
2461 | 0 | } |
2462 | 0 | return tc_transact(&request, NULL); |
2463 | 0 | } |
2464 | | |
2465 | | int |
2466 | | tc_del_flower_filter(struct tcf_id *id) |
2467 | 0 | { |
2468 | 0 | return tc_del_filter(id, "flower"); |
2469 | 0 | } |
2470 | | |
2471 | | int |
2472 | | tc_get_flower(struct tcf_id *id, struct tc_flower *flower) |
2473 | 0 | { |
2474 | 0 | struct ofpbuf request; |
2475 | 0 | struct ofpbuf *reply; |
2476 | 0 | int error; |
2477 | |
|
2478 | 0 | request_from_tcf_id(id, 0, RTM_GETTFILTER, NLM_F_ECHO, &request); |
2479 | 0 | nl_msg_put_string(&request, TCA_KIND, "flower"); |
2480 | 0 | error = tc_transact(&request, &reply); |
2481 | 0 | if (error) { |
2482 | 0 | return error; |
2483 | 0 | } |
2484 | | |
2485 | 0 | error = parse_netlink_to_tc_flower(reply, id, flower, false); |
2486 | 0 | ofpbuf_delete(reply); |
2487 | 0 | return error; |
2488 | 0 | } |
2489 | | |
2490 | | static int |
2491 | | tc_get_tc_cls_policy(enum tc_offload_policy policy) |
2492 | 0 | { |
2493 | 0 | if (policy == TC_POLICY_SKIP_HW) { |
2494 | 0 | return TCA_CLS_FLAGS_SKIP_HW; |
2495 | 0 | } else if (policy == TC_POLICY_SKIP_SW) { |
2496 | 0 | return TCA_CLS_FLAGS_SKIP_SW; |
2497 | 0 | } |
2498 | | |
2499 | 0 | return 0; |
2500 | 0 | } |
2501 | | |
2502 | | static void |
2503 | | nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags, uint32_t action_pc) |
2504 | 0 | { |
2505 | 0 | size_t offset; |
2506 | |
|
2507 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "csum"); |
2508 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2509 | 0 | { |
2510 | 0 | struct tc_csum parm = { .action = action_pc, |
2511 | 0 | .update_flags = flags }; |
2512 | |
|
2513 | 0 | nl_msg_put_unspec(request, TCA_CSUM_PARMS, &parm, sizeof parm); |
2514 | 0 | } |
2515 | 0 | nl_msg_end_nested(request, offset); |
2516 | 0 | } |
2517 | | |
2518 | | static void |
2519 | | nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm, |
2520 | | struct tc_pedit_key_ex *ex, uint32_t action_pc) |
2521 | 0 | { |
2522 | 0 | size_t ksize = sizeof *parm + parm->nkeys * sizeof(struct tc_pedit_key); |
2523 | 0 | size_t offset, offset_keys_ex, offset_key; |
2524 | 0 | int i; |
2525 | |
|
2526 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "pedit"); |
2527 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2528 | 0 | { |
2529 | 0 | parm->action = action_pc; |
2530 | |
|
2531 | 0 | nl_msg_put_unspec(request, TCA_PEDIT_PARMS_EX, parm, ksize); |
2532 | 0 | offset_keys_ex = nl_msg_start_nested(request, TCA_PEDIT_KEYS_EX); |
2533 | 0 | for (i = 0; i < parm->nkeys; i++, ex++) { |
2534 | 0 | offset_key = nl_msg_start_nested(request, TCA_PEDIT_KEY_EX); |
2535 | 0 | nl_msg_put_u16(request, TCA_PEDIT_KEY_EX_HTYPE, ex->htype); |
2536 | 0 | nl_msg_put_u16(request, TCA_PEDIT_KEY_EX_CMD, ex->cmd); |
2537 | 0 | nl_msg_end_nested(request, offset_key); |
2538 | 0 | } |
2539 | 0 | nl_msg_end_nested(request, offset_keys_ex); |
2540 | 0 | } |
2541 | 0 | nl_msg_end_nested(request, offset); |
2542 | 0 | } |
2543 | | |
2544 | | static void |
2545 | | nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid, |
2546 | | uint16_t vid, uint8_t prio, uint32_t action_pc) |
2547 | 0 | { |
2548 | 0 | size_t offset; |
2549 | |
|
2550 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "vlan"); |
2551 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2552 | 0 | { |
2553 | 0 | struct tc_vlan parm = { .action = action_pc, |
2554 | 0 | .v_action = TCA_VLAN_ACT_PUSH }; |
2555 | |
|
2556 | 0 | nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm); |
2557 | 0 | nl_msg_put_be16(request, TCA_VLAN_PUSH_VLAN_PROTOCOL, tpid); |
2558 | 0 | nl_msg_put_u16(request, TCA_VLAN_PUSH_VLAN_ID, vid); |
2559 | 0 | nl_msg_put_u8(request, TCA_VLAN_PUSH_VLAN_PRIORITY, prio); |
2560 | 0 | } |
2561 | 0 | nl_msg_end_nested(request, offset); |
2562 | 0 | } |
2563 | | |
2564 | | static void |
2565 | | nl_msg_put_act_pop_vlan(struct ofpbuf *request, uint32_t action_pc) |
2566 | 0 | { |
2567 | 0 | size_t offset; |
2568 | |
|
2569 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "vlan"); |
2570 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2571 | 0 | { |
2572 | 0 | struct tc_vlan parm = { .action = action_pc, |
2573 | 0 | .v_action = TCA_VLAN_ACT_POP }; |
2574 | |
|
2575 | 0 | nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm); |
2576 | 0 | } |
2577 | 0 | nl_msg_end_nested(request, offset); |
2578 | 0 | } |
2579 | | |
2580 | | static void |
2581 | | nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto, |
2582 | | uint32_t action_pc) |
2583 | 0 | { |
2584 | 0 | size_t offset; |
2585 | |
|
2586 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); |
2587 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); |
2588 | 0 | { |
2589 | 0 | struct tc_mpls parm = { .action = action_pc, |
2590 | 0 | .m_action = TCA_MPLS_ACT_POP }; |
2591 | |
|
2592 | 0 | nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); |
2593 | 0 | nl_msg_put_be16(request, TCA_MPLS_PROTO, proto); |
2594 | 0 | } |
2595 | 0 | nl_msg_end_nested(request, offset); |
2596 | 0 | } |
2597 | | |
2598 | | static void |
2599 | | nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto, |
2600 | | uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos, |
2601 | | uint32_t action_pc) |
2602 | 0 | { |
2603 | 0 | size_t offset; |
2604 | |
|
2605 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); |
2606 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); |
2607 | 0 | { |
2608 | 0 | struct tc_mpls parm = { .action = action_pc, |
2609 | 0 | .m_action = TCA_MPLS_ACT_PUSH }; |
2610 | |
|
2611 | 0 | nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); |
2612 | 0 | nl_msg_put_be16(request, TCA_MPLS_PROTO, proto); |
2613 | 0 | nl_msg_put_u32(request, TCA_MPLS_LABEL, label); |
2614 | 0 | nl_msg_put_u8(request, TCA_MPLS_TC, tc); |
2615 | 0 | nl_msg_put_u8(request, TCA_MPLS_TTL, ttl); |
2616 | 0 | nl_msg_put_u8(request, TCA_MPLS_BOS, bos); |
2617 | 0 | } |
2618 | 0 | nl_msg_end_nested(request, offset); |
2619 | 0 | } |
2620 | | |
2621 | | static void |
2622 | | nl_msg_put_act_set_mpls(struct ofpbuf *request, uint32_t label, uint8_t tc, |
2623 | | uint8_t ttl, uint8_t bos, uint32_t action_pc) |
2624 | 0 | { |
2625 | 0 | size_t offset; |
2626 | |
|
2627 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); |
2628 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); |
2629 | 0 | { |
2630 | 0 | struct tc_mpls parm = { .action = action_pc, |
2631 | 0 | .m_action = TCA_MPLS_ACT_MODIFY }; |
2632 | |
|
2633 | 0 | nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); |
2634 | 0 | nl_msg_put_u32(request, TCA_MPLS_LABEL, label); |
2635 | 0 | nl_msg_put_u8(request, TCA_MPLS_TC, tc); |
2636 | 0 | nl_msg_put_u8(request, TCA_MPLS_TTL, ttl); |
2637 | 0 | nl_msg_put_u8(request, TCA_MPLS_BOS, bos); |
2638 | 0 | } |
2639 | 0 | nl_msg_end_nested(request, offset); |
2640 | 0 | } |
2641 | | |
2642 | | static void |
2643 | | nl_msg_put_act_tunnel_key_release(struct ofpbuf *request) |
2644 | 0 | { |
2645 | 0 | size_t offset; |
2646 | |
|
2647 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key"); |
2648 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2649 | 0 | { |
2650 | 0 | struct tc_tunnel_key tun = { .action = TC_ACT_PIPE, |
2651 | 0 | .t_action = TCA_TUNNEL_KEY_ACT_RELEASE }; |
2652 | |
|
2653 | 0 | nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun); |
2654 | 0 | } |
2655 | 0 | nl_msg_end_nested(request, offset); |
2656 | 0 | } |
2657 | | |
2658 | | static void |
2659 | | nl_msg_put_act_tunnel_geneve_option(struct ofpbuf *request, |
2660 | | struct tun_metadata *tun_metadata) |
2661 | 0 | { |
2662 | 0 | const struct geneve_opt *opt; |
2663 | 0 | size_t outer, inner; |
2664 | 0 | int len, cnt = 0; |
2665 | |
|
2666 | 0 | len = tun_metadata->present.len; |
2667 | 0 | if (!len) { |
2668 | 0 | return; |
2669 | 0 | } |
2670 | | |
2671 | 0 | outer = nl_msg_start_nested(request, TCA_TUNNEL_KEY_ENC_OPTS); |
2672 | |
|
2673 | 0 | while (len) { |
2674 | 0 | opt = &tun_metadata->opts.gnv[cnt]; |
2675 | 0 | inner = nl_msg_start_nested(request, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE); |
2676 | |
|
2677 | 0 | nl_msg_put_be16(request, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS, |
2678 | 0 | opt->opt_class); |
2679 | 0 | nl_msg_put_u8(request, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE, opt->type); |
2680 | 0 | nl_msg_put_unspec(request, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA, opt + 1, |
2681 | 0 | opt->length * 4); |
2682 | |
|
2683 | 0 | cnt += sizeof(struct geneve_opt) / 4 + opt->length; |
2684 | 0 | len -= sizeof(struct geneve_opt) + opt->length * 4; |
2685 | |
|
2686 | 0 | nl_msg_end_nested(request, inner); |
2687 | 0 | } |
2688 | |
|
2689 | 0 | nl_msg_end_nested(request, outer); |
2690 | 0 | } |
2691 | | |
2692 | | static void |
2693 | | nl_msg_put_act_tunnel_vxlan_opts(struct ofpbuf *request, |
2694 | | struct tc_action_encap *encap) |
2695 | 0 | { |
2696 | 0 | size_t outer, inner; |
2697 | 0 | uint32_t gbp_raw; |
2698 | |
|
2699 | 0 | if (!encap->gbp.id_present) { |
2700 | 0 | return; |
2701 | 0 | } |
2702 | | |
2703 | 0 | gbp_raw = odp_encode_gbp_raw(encap->gbp.flags, |
2704 | 0 | encap->gbp.id); |
2705 | 0 | outer = nl_msg_start_nested_with_flag(request, TCA_TUNNEL_KEY_ENC_OPTS); |
2706 | 0 | inner = nl_msg_start_nested_with_flag(request, |
2707 | 0 | TCA_TUNNEL_KEY_ENC_OPTS_VXLAN); |
2708 | 0 | nl_msg_put_u32(request, TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP, gbp_raw); |
2709 | 0 | nl_msg_end_nested(request, inner); |
2710 | 0 | nl_msg_end_nested(request, outer); |
2711 | 0 | } |
2712 | | |
2713 | | static void |
2714 | | nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, |
2715 | | struct tc_action_encap *encap, |
2716 | | uint32_t action_pc) |
2717 | 0 | { |
2718 | 0 | size_t offset; |
2719 | |
|
2720 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key"); |
2721 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2722 | 0 | { |
2723 | 0 | struct tc_tunnel_key tun = { .action = action_pc, |
2724 | 0 | .t_action = TCA_TUNNEL_KEY_ACT_SET }; |
2725 | |
|
2726 | 0 | nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun); |
2727 | |
|
2728 | 0 | ovs_be32 id32 = be64_to_be32(encap->id); |
2729 | 0 | if (encap->id_present) { |
2730 | 0 | nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_KEY_ID, id32); |
2731 | 0 | } |
2732 | 0 | if (encap->ipv4.ipv4_dst) { |
2733 | 0 | nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_IPV4_SRC, |
2734 | 0 | encap->ipv4.ipv4_src); |
2735 | 0 | nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_IPV4_DST, |
2736 | 0 | encap->ipv4.ipv4_dst); |
2737 | 0 | } else if (ipv6_addr_is_set(&encap->ipv6.ipv6_dst)) { |
2738 | 0 | nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_DST, |
2739 | 0 | &encap->ipv6.ipv6_dst); |
2740 | 0 | nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_SRC, |
2741 | 0 | &encap->ipv6.ipv6_src); |
2742 | 0 | } |
2743 | 0 | if (encap->tos) { |
2744 | 0 | nl_msg_put_u8(request, TCA_TUNNEL_KEY_ENC_TOS, encap->tos); |
2745 | 0 | } |
2746 | 0 | if (encap->ttl) { |
2747 | 0 | nl_msg_put_u8(request, TCA_TUNNEL_KEY_ENC_TTL, encap->ttl); |
2748 | 0 | } |
2749 | 0 | if (encap->tp_dst) { |
2750 | 0 | nl_msg_put_be16(request, TCA_TUNNEL_KEY_ENC_DST_PORT, |
2751 | 0 | encap->tp_dst); |
2752 | 0 | } |
2753 | 0 | if (encap->dont_fragment) { |
2754 | 0 | nl_msg_put_flag(request, TCA_TUNNEL_KEY_NO_FRAG); |
2755 | 0 | } |
2756 | 0 | nl_msg_put_act_tunnel_vxlan_opts(request, encap); |
2757 | 0 | nl_msg_put_act_tunnel_geneve_option(request, &encap->data); |
2758 | 0 | nl_msg_put_u8(request, TCA_TUNNEL_KEY_NO_CSUM, encap->no_csum); |
2759 | 0 | } |
2760 | 0 | nl_msg_end_nested(request, offset); |
2761 | 0 | } |
2762 | | |
2763 | | static void |
2764 | | nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain) |
2765 | 0 | { |
2766 | 0 | size_t offset; |
2767 | |
|
2768 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "gact"); |
2769 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2770 | 0 | { |
2771 | 0 | struct tc_gact p = { .action = TC_ACT_SHOT }; |
2772 | |
|
2773 | 0 | if (chain) { |
2774 | 0 | p.action = TC_ACT_GOTO_CHAIN | chain; |
2775 | 0 | } |
2776 | |
|
2777 | 0 | nl_msg_put_unspec(request, TCA_GACT_PARMS, &p, sizeof p); |
2778 | 0 | } |
2779 | 0 | nl_msg_end_nested(request, offset); |
2780 | 0 | } |
2781 | | |
2782 | | static void |
2783 | | nl_msg_put_act_police_index(struct ofpbuf *request, uint32_t police_idx, |
2784 | | uint32_t action_pc) |
2785 | 0 | { |
2786 | 0 | struct tc_police police; |
2787 | 0 | size_t offset; |
2788 | |
|
2789 | 0 | memset(&police, 0, sizeof police); |
2790 | 0 | police.index = police_idx; |
2791 | 0 | police.action = action_pc; |
2792 | |
|
2793 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "police"); |
2794 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2795 | 0 | nl_msg_put_unspec(request, TCA_POLICE_TBF, &police, sizeof police); |
2796 | 0 | nl_msg_put_u32(request, TCA_POLICE_RESULT, action_pc); |
2797 | 0 | nl_msg_end_nested(request, offset); |
2798 | 0 | } |
2799 | | |
2800 | | static void |
2801 | | nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action, |
2802 | | uint32_t action_pc) |
2803 | 0 | { |
2804 | 0 | uint16_t ct_action = 0; |
2805 | 0 | size_t offset; |
2806 | |
|
2807 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "ct"); |
2808 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); |
2809 | 0 | { |
2810 | 0 | struct tc_ct ct = { |
2811 | 0 | .action = action_pc, |
2812 | 0 | }; |
2813 | |
|
2814 | 0 | if (!action->ct.clear) { |
2815 | 0 | if (action->ct.zone) { |
2816 | 0 | nl_msg_put_u16(request, TCA_CT_ZONE, action->ct.zone); |
2817 | 0 | } |
2818 | |
|
2819 | 0 | if (!is_all_zeros(&action->ct.label_mask, |
2820 | 0 | sizeof action->ct.label_mask)) { |
2821 | 0 | nl_msg_put_u128(request, TCA_CT_LABELS, |
2822 | 0 | action->ct.label); |
2823 | 0 | nl_msg_put_u128(request, TCA_CT_LABELS_MASK, |
2824 | 0 | action->ct.label_mask); |
2825 | 0 | } |
2826 | |
|
2827 | 0 | if (action->ct.mark_mask) { |
2828 | 0 | nl_msg_put_u32(request, TCA_CT_MARK, |
2829 | 0 | action->ct.mark); |
2830 | 0 | nl_msg_put_u32(request, TCA_CT_MARK_MASK, |
2831 | 0 | action->ct.mark_mask); |
2832 | 0 | } |
2833 | |
|
2834 | 0 | if (action->ct.commit) { |
2835 | 0 | ct_action = TCA_CT_ACT_COMMIT; |
2836 | 0 | if (action->ct.force) { |
2837 | 0 | ct_action |= TCA_CT_ACT_FORCE; |
2838 | 0 | } |
2839 | 0 | } |
2840 | |
|
2841 | 0 | if (action->ct.nat_type) { |
2842 | 0 | ct_action |= TCA_CT_ACT_NAT; |
2843 | |
|
2844 | 0 | if (action->ct.nat_type == TC_NAT_SRC) { |
2845 | 0 | ct_action |= TCA_CT_ACT_NAT_SRC; |
2846 | 0 | } else if (action->ct.nat_type == TC_NAT_DST) { |
2847 | 0 | ct_action |= TCA_CT_ACT_NAT_DST; |
2848 | 0 | } |
2849 | |
|
2850 | 0 | if (action->ct.range.ip_family == AF_INET) { |
2851 | 0 | nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MIN, |
2852 | 0 | action->ct.range.ipv4.min); |
2853 | 0 | if (action->ct.range.ipv4.max) { |
2854 | 0 | nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MAX, |
2855 | 0 | action->ct.range.ipv4.max); |
2856 | 0 | } |
2857 | 0 | } else if (action->ct.range.ip_family == AF_INET6) { |
2858 | |
|
2859 | 0 | nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MIN, |
2860 | 0 | &action->ct.range.ipv6.min); |
2861 | 0 | if (ipv6_addr_is_set(&action->ct.range.ipv6.max)) { |
2862 | 0 | nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MAX, |
2863 | 0 | &action->ct.range.ipv6.max); |
2864 | 0 | } |
2865 | 0 | } |
2866 | |
|
2867 | 0 | if (action->ct.range.port.min) { |
2868 | 0 | nl_msg_put_be16(request, TCA_CT_NAT_PORT_MIN, |
2869 | 0 | action->ct.range.port.min); |
2870 | 0 | if (action->ct.range.port.max) { |
2871 | 0 | nl_msg_put_be16(request, TCA_CT_NAT_PORT_MAX, |
2872 | 0 | action->ct.range.port.max); |
2873 | 0 | } |
2874 | 0 | } |
2875 | 0 | } |
2876 | 0 | } else { |
2877 | 0 | ct_action = TCA_CT_ACT_CLEAR; |
2878 | 0 | } |
2879 | |
|
2880 | 0 | nl_msg_put_u16(request, TCA_CT_ACTION, ct_action); |
2881 | 0 | nl_msg_put_unspec(request, TCA_CT_PARMS, &ct, sizeof ct); |
2882 | 0 | } |
2883 | 0 | nl_msg_end_nested(request, offset); |
2884 | 0 | } |
2885 | | |
2886 | | static void |
2887 | | nl_msg_put_act_skbedit_to_host(struct ofpbuf *request) |
2888 | 0 | { |
2889 | 0 | size_t offset; |
2890 | |
|
2891 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "skbedit"); |
2892 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2893 | 0 | { |
2894 | 0 | struct tc_skbedit s = { .action = TC_ACT_PIPE }; |
2895 | |
|
2896 | 0 | nl_msg_put_unspec(request, TCA_SKBEDIT_PARMS, &s, sizeof s); |
2897 | 0 | nl_msg_put_be16(request, TCA_SKBEDIT_PTYPE, PACKET_HOST); |
2898 | 0 | } |
2899 | 0 | nl_msg_end_nested(request, offset); |
2900 | 0 | } |
2901 | | |
2902 | | static void |
2903 | | nl_msg_put_act_mirred(struct ofpbuf *request, int ifindex, int action, |
2904 | | int eaction) |
2905 | 0 | { |
2906 | 0 | size_t offset; |
2907 | |
|
2908 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "mirred"); |
2909 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
2910 | 0 | { |
2911 | 0 | struct tc_mirred m = { .action = action, |
2912 | 0 | .eaction = eaction, |
2913 | 0 | .ifindex = ifindex }; |
2914 | |
|
2915 | 0 | nl_msg_put_unspec(request, TCA_MIRRED_PARMS, &m, sizeof m); |
2916 | 0 | } |
2917 | 0 | nl_msg_end_nested(request, offset); |
2918 | 0 | } |
2919 | | |
2920 | | static inline void |
2921 | 0 | nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) { |
2922 | 0 | if (ck->len) { |
2923 | 0 | nl_msg_put_unspec(request, TCA_ACT_COOKIE, ck->data, ck->len); |
2924 | 0 | } |
2925 | 0 | } |
2926 | | |
2927 | | static inline void |
2928 | 0 | nl_msg_put_act_flags(struct ofpbuf *request) { |
2929 | 0 | struct nla_bitfield32 act_flags = { TCA_ACT_FLAGS_NO_PERCPU_STATS, |
2930 | 0 | TCA_ACT_FLAGS_NO_PERCPU_STATS }; |
2931 | |
|
2932 | 0 | nl_msg_put_unspec(request, TCA_ACT_FLAGS, &act_flags, sizeof act_flags); |
2933 | 0 | } |
2934 | | |
2935 | | /* Given flower, a key_to_pedit map entry, calculates the rest, |
2936 | | * where: |
2937 | | * |
2938 | | * mask, data - pointers of where read the first word of flower->key/mask. |
2939 | | * current_offset - which offset to use for the first pedit action. |
2940 | | * cnt - max pedits actions to use. |
2941 | | * first_word_mask/last_word_mask - the mask to use for the first/last read |
2942 | | * (as we read entire words). */ |
2943 | | static void |
2944 | | calc_offsets(struct tc_action *action, struct flower_key_to_pedit *m, |
2945 | | int *cur_offset, int *cnt, ovs_be32 *last_word_mask, |
2946 | | ovs_be32 *first_word_mask, ovs_be32 **mask, ovs_be32 **data) |
2947 | 0 | { |
2948 | 0 | int start_offset, max_offset, total_size; |
2949 | 0 | int diff, right_zero_bits, left_zero_bits; |
2950 | 0 | char *rewrite_key = (void *) &action->rewrite.key; |
2951 | 0 | char *rewrite_mask = (void *) &action->rewrite.mask; |
2952 | |
|
2953 | 0 | max_offset = m->offset + m->size; |
2954 | 0 | start_offset = ROUND_DOWN(m->offset, 4); |
2955 | 0 | diff = m->offset - start_offset; |
2956 | 0 | total_size = max_offset - start_offset; |
2957 | 0 | right_zero_bits = 8 * (4 - ((max_offset % 4) ? : 4)); |
2958 | 0 | left_zero_bits = 8 * (m->offset - start_offset); |
2959 | |
|
2960 | 0 | *cur_offset = start_offset; |
2961 | 0 | *cnt = (total_size / 4) + (total_size % 4 ? 1 : 0); |
2962 | 0 | *last_word_mask = htonl(UINT32_MAX << right_zero_bits); |
2963 | 0 | *first_word_mask = htonl(UINT32_MAX >> left_zero_bits); |
2964 | 0 | *data = (void *) (rewrite_key + m->flower_offset - diff); |
2965 | 0 | *mask = (void *) (rewrite_mask + m->flower_offset - diff); |
2966 | 0 | } |
2967 | | |
2968 | | static inline int |
2969 | | csum_update_flag(struct tc_flower *flower, |
2970 | 0 | enum pedit_header_type htype) { |
2971 | | /* Explictily specifiy the csum flags so HW can return EOPNOTSUPP |
2972 | | * if it doesn't support a checksum recalculation of some headers. |
2973 | | * And since OVS allows a flow such as |
2974 | | * eth(dst=<mac>),eth_type(0x0800) actions=set(ipv4(src=<new_ip>)) |
2975 | | * we need to force a more specific flow as this can, for example, |
2976 | | * need a recalculation of icmp checksum if the packet that passes |
2977 | | * is ICMPv6 and tcp checksum if its tcp. |
2978 | | * |
2979 | | * This section of the code must be kept in sync with the pre-check |
2980 | | * function in netdev-offload-tc.c, tc_will_add_l4_checksum(). */ |
2981 | |
|
2982 | 0 | switch (htype) { |
2983 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: |
2984 | 0 | flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_IPV4HDR; |
2985 | | /* Fall through. */ |
2986 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: |
2987 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: |
2988 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: |
2989 | 0 | if (flower->key.ip_proto == IPPROTO_TCP) { |
2990 | 0 | flower->needs_full_ip_proto_mask = true; |
2991 | 0 | flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_TCP; |
2992 | 0 | } else if (flower->key.ip_proto == IPPROTO_UDP) { |
2993 | 0 | flower->needs_full_ip_proto_mask = true; |
2994 | 0 | flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_UDP; |
2995 | 0 | } else if (flower->key.ip_proto == IPPROTO_ICMP || |
2996 | 0 | flower->key.ip_proto == IPPROTO_IGMP || |
2997 | 0 | flower->key.ip_proto == IPPROTO_SCTP || |
2998 | 0 | flower->key.ip_proto == IPPROTO_IPIP || |
2999 | 0 | flower->key.ip_proto == IPPROTO_GRE) { |
3000 | 0 | flower->needs_full_ip_proto_mask = true; |
3001 | 0 | } else if (flower->key.ip_proto == IPPROTO_ICMPV6) { |
3002 | 0 | flower->needs_full_ip_proto_mask = true; |
3003 | 0 | flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_ICMP; |
3004 | 0 | } else if (flower->key.ip_proto == IPPROTO_UDPLITE) { |
3005 | 0 | flower->needs_full_ip_proto_mask = true; |
3006 | 0 | flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_UDPLITE; |
3007 | 0 | } else { |
3008 | 0 | VLOG_WARN_RL(&error_rl, |
3009 | 0 | "can't offload rewrite of IP/IPV6 with ip_proto: %d", |
3010 | 0 | flower->key.ip_proto); |
3011 | 0 | break; |
3012 | 0 | } |
3013 | | /* Fall through. */ |
3014 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: |
3015 | 0 | return 0; /* success */ |
3016 | | |
3017 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: |
3018 | 0 | case __PEDIT_HDR_TYPE_MAX: |
3019 | 0 | default: |
3020 | 0 | break; |
3021 | 0 | } |
3022 | | |
3023 | 0 | return EOPNOTSUPP; |
3024 | 0 | } |
3025 | | |
3026 | | static bool |
3027 | | rewrite_pedits_need_csum_update(struct tc_action *action) |
3028 | 0 | { |
3029 | 0 | int i, j; |
3030 | |
|
3031 | 0 | for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) { |
3032 | 0 | struct flower_key_to_pedit *m = &flower_pedit_map[i]; |
3033 | 0 | ovs_be32 *mask, *data, first_word_mask, last_word_mask; |
3034 | 0 | int cnt = 0, cur_offset = 0; |
3035 | |
|
3036 | 0 | if (!m->size) { |
3037 | 0 | continue; |
3038 | 0 | } |
3039 | | |
3040 | 0 | calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask, |
3041 | 0 | &first_word_mask, &mask, &data); |
3042 | |
|
3043 | 0 | for (j = 0; j < cnt; j++, mask++) { |
3044 | 0 | ovs_be32 mask_word = get_unaligned_be32(mask); |
3045 | |
|
3046 | 0 | if (j == 0) { |
3047 | 0 | mask_word &= first_word_mask; |
3048 | 0 | } |
3049 | 0 | if (j == cnt - 1) { |
3050 | 0 | mask_word &= last_word_mask; |
3051 | 0 | } |
3052 | 0 | if (!mask_word) { |
3053 | 0 | continue; |
3054 | 0 | } |
3055 | | |
3056 | 0 | switch (m->htype) { |
3057 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: |
3058 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: |
3059 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: |
3060 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: |
3061 | 0 | return true; |
3062 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: |
3063 | 0 | case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: |
3064 | 0 | case __PEDIT_HDR_TYPE_MAX: |
3065 | 0 | break; |
3066 | 0 | } |
3067 | 0 | } |
3068 | 0 | } |
3069 | 0 | return false; |
3070 | 0 | } |
3071 | | |
3072 | | static int |
3073 | | nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request, |
3074 | | struct tc_flower *flower, |
3075 | | struct tc_action *action, |
3076 | | uint32_t action_pc) |
3077 | 0 | { |
3078 | 0 | union { |
3079 | 0 | struct tc_pedit sel; |
3080 | 0 | uint8_t buffer[sizeof(struct tc_pedit) |
3081 | 0 | + MAX_PEDIT_OFFSETS * sizeof(struct tc_pedit_key)]; |
3082 | 0 | } sel; |
3083 | 0 | struct tc_pedit_key_ex keys_ex[MAX_PEDIT_OFFSETS]; |
3084 | 0 | int i, j, err; |
3085 | |
|
3086 | 0 | memset(&sel, 0, sizeof sel); |
3087 | 0 | memset(keys_ex, 0, sizeof keys_ex); |
3088 | |
|
3089 | 0 | for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) { |
3090 | 0 | struct flower_key_to_pedit *m = &flower_pedit_map[i]; |
3091 | 0 | struct tc_pedit_key *pedit_key = NULL; |
3092 | 0 | struct tc_pedit_key_ex *pedit_key_ex = NULL; |
3093 | 0 | ovs_be32 *mask, *data, first_word_mask, last_word_mask; |
3094 | 0 | int cnt = 0, cur_offset = 0; |
3095 | |
|
3096 | 0 | if (!m->size) { |
3097 | 0 | continue; |
3098 | 0 | } |
3099 | | |
3100 | 0 | calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask, |
3101 | 0 | &first_word_mask, &mask, &data); |
3102 | |
|
3103 | 0 | for (j = 0; j < cnt; j++, mask++, data++, cur_offset += 4) { |
3104 | 0 | ovs_be32 mask_word = get_unaligned_be32(mask); |
3105 | 0 | ovs_be32 data_word = get_unaligned_be32(data); |
3106 | |
|
3107 | 0 | if (j == 0) { |
3108 | 0 | mask_word &= first_word_mask; |
3109 | 0 | } |
3110 | 0 | if (j == cnt - 1) { |
3111 | 0 | mask_word &= last_word_mask; |
3112 | 0 | } |
3113 | 0 | if (!mask_word) { |
3114 | 0 | continue; |
3115 | 0 | } |
3116 | 0 | if (sel.sel.nkeys == MAX_PEDIT_OFFSETS) { |
3117 | 0 | VLOG_WARN_RL(&error_rl, "reached too many pedit offsets: %d", |
3118 | 0 | MAX_PEDIT_OFFSETS); |
3119 | 0 | return EOPNOTSUPP; |
3120 | 0 | } |
3121 | | |
3122 | 0 | pedit_key = &sel.sel.keys[sel.sel.nkeys]; |
3123 | 0 | pedit_key_ex = &keys_ex[sel.sel.nkeys]; |
3124 | 0 | pedit_key_ex->cmd = TCA_PEDIT_KEY_EX_CMD_SET; |
3125 | 0 | pedit_key_ex->htype = m->htype; |
3126 | 0 | pedit_key->off = cur_offset; |
3127 | 0 | mask_word = htonl(ntohl(mask_word) >> m->boundary_shift); |
3128 | 0 | data_word = htonl(ntohl(data_word) >> m->boundary_shift); |
3129 | 0 | pedit_key->mask = ~mask_word; |
3130 | 0 | pedit_key->val = data_word & mask_word; |
3131 | 0 | sel.sel.nkeys++; |
3132 | |
|
3133 | 0 | err = csum_update_flag(flower, m->htype); |
3134 | 0 | if (err) { |
3135 | 0 | return err; |
3136 | 0 | } |
3137 | | |
3138 | 0 | if (flower->needs_full_ip_proto_mask) { |
3139 | 0 | flower->mask.ip_proto = UINT8_MAX; |
3140 | 0 | } |
3141 | 0 | } |
3142 | 0 | } |
3143 | 0 | nl_msg_put_act_pedit(request, &sel.sel, keys_ex, |
3144 | 0 | flower->csum_update_flags ? TC_ACT_PIPE : action_pc); |
3145 | |
|
3146 | 0 | return 0; |
3147 | 0 | } |
3148 | | |
3149 | | static void |
3150 | | nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index) |
3151 | 0 | { |
3152 | 0 | size_t act_offset; |
3153 | |
|
3154 | 0 | act_offset = nl_msg_start_nested(request, act_index); |
3155 | 0 | nl_msg_put_act_tunnel_key_release(request); |
3156 | 0 | nl_msg_put_act_flags(request); |
3157 | 0 | nl_msg_end_nested(request, act_offset); |
3158 | 0 | } |
3159 | | |
3160 | | /* Aggregates all previous successive pedit actions csum_update_flags |
3161 | | * to flower->csum_update_flags. Only append one csum action to the |
3162 | | * last pedit action. */ |
3163 | | static void |
3164 | | nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower, |
3165 | | uint32_t action_pc, uint16_t *act_index) |
3166 | 0 | { |
3167 | 0 | size_t act_offset; |
3168 | | |
3169 | | /* No pedit actions or processed already. */ |
3170 | 0 | if (!flower->csum_update_flags) { |
3171 | 0 | return; |
3172 | 0 | } |
3173 | | |
3174 | 0 | act_offset = nl_msg_start_nested(request, (*act_index)++); |
3175 | 0 | nl_msg_put_act_csum(request, flower->csum_update_flags, action_pc); |
3176 | 0 | nl_msg_put_act_flags(request); |
3177 | 0 | nl_msg_end_nested(request, act_offset); |
3178 | | |
3179 | | /* Clear it. So we can have another series of pedit actions. */ |
3180 | 0 | flower->csum_update_flags = 0; |
3181 | 0 | } |
3182 | | |
3183 | | static int |
3184 | | get_action_index_for_tc_actions(struct tc_flower *flower, uint16_t act_index, |
3185 | | struct tc_action *action, int action_count, |
3186 | | bool tunnel_key_released) |
3187 | 0 | { |
3188 | 0 | bool need_csum = false; |
3189 | |
|
3190 | 0 | if (action_count < 0) { |
3191 | | /* Only forward jumps are supported */ |
3192 | 0 | return -EINVAL; |
3193 | 0 | } |
3194 | | |
3195 | 0 | for (int i = 0; i < action_count; i++, action++) { |
3196 | 0 | if (action->type != TC_ACT_PEDIT && need_csum) { |
3197 | 0 | need_csum = false; |
3198 | 0 | act_index++; |
3199 | 0 | } |
3200 | |
|
3201 | 0 | switch (action->type) { |
3202 | | |
3203 | 0 | case TC_ACT_OUTPUT: |
3204 | 0 | if (!tunnel_key_released && flower->tunnel) { |
3205 | 0 | act_index++; |
3206 | 0 | tunnel_key_released = true; |
3207 | 0 | } |
3208 | 0 | if (action->out.ingress) { |
3209 | 0 | act_index++; |
3210 | 0 | } |
3211 | 0 | act_index++; |
3212 | 0 | break; |
3213 | | |
3214 | 0 | case TC_ACT_ENCAP: |
3215 | 0 | if (!tunnel_key_released && flower->tunnel) { |
3216 | 0 | act_index++; |
3217 | 0 | tunnel_key_released = true; |
3218 | 0 | } |
3219 | 0 | act_index++; |
3220 | 0 | break; |
3221 | | |
3222 | 0 | case TC_ACT_PEDIT: |
3223 | 0 | if (!need_csum) { |
3224 | 0 | need_csum = rewrite_pedits_need_csum_update(action); |
3225 | 0 | } |
3226 | 0 | if (i == (action_count - 1) && need_csum) { |
3227 | 0 | need_csum = false; |
3228 | 0 | act_index++; |
3229 | 0 | } |
3230 | 0 | act_index++; |
3231 | 0 | break; |
3232 | | |
3233 | 0 | case TC_ACT_POLICE: |
3234 | 0 | case TC_ACT_POLICE_MTU: |
3235 | 0 | case TC_ACT_VLAN_POP: |
3236 | 0 | case TC_ACT_VLAN_PUSH: |
3237 | 0 | case TC_ACT_MPLS_POP: |
3238 | 0 | case TC_ACT_MPLS_PUSH: |
3239 | 0 | case TC_ACT_MPLS_SET: |
3240 | 0 | case TC_ACT_GOTO: |
3241 | 0 | case TC_ACT_CT: |
3242 | | /* Increase act_index by one if we are sure this type of action |
3243 | | * will only add one tc action in the kernel. */ |
3244 | 0 | act_index++; |
3245 | 0 | break; |
3246 | | |
3247 | | /* If we can't determine how many tc actions will be added by the |
3248 | | * kernel return -EOPNOTSUPP. |
3249 | | * |
3250 | | * Please do NOT add a default case. */ |
3251 | 0 | } |
3252 | 0 | } |
3253 | | |
3254 | 0 | return act_index; |
3255 | 0 | } |
3256 | | |
3257 | | static int |
3258 | | nl_msg_put_act_police_mtu(struct ofpbuf *request, struct tc_flower *flower, |
3259 | | struct tc_action *action, uint32_t action_pc, |
3260 | | int action_index, uint16_t act_index, bool released) |
3261 | 0 | { |
3262 | 0 | uint32_t tc_action; |
3263 | 0 | size_t offset; |
3264 | |
|
3265 | 0 | if (action->police.result_jump != JUMP_ACTION_STOP) { |
3266 | 0 | int jump_index; |
3267 | 0 | int action_count = action->police.result_jump - action_index - 1; |
3268 | |
|
3269 | 0 | jump_index = get_action_index_for_tc_actions(flower, |
3270 | 0 | act_index - 1, |
3271 | 0 | action + 1, |
3272 | 0 | action_count, |
3273 | 0 | released); |
3274 | 0 | if (jump_index < 0) { |
3275 | 0 | return -jump_index; |
3276 | 0 | } |
3277 | 0 | tc_action = TC_ACT_JUMP | (jump_index & TC_ACT_EXT_VAL_MASK); |
3278 | 0 | } else { |
3279 | 0 | tc_action = TC_ACT_STOLEN; |
3280 | 0 | } |
3281 | | |
3282 | 0 | nl_msg_put_string(request, TCA_ACT_KIND, "police"); |
3283 | 0 | offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS); |
3284 | 0 | { |
3285 | 0 | struct tc_police p = { .action = action_pc, |
3286 | 0 | .mtu = action->police.mtu }; |
3287 | |
|
3288 | 0 | nl_msg_put_unspec(request, TCA_POLICE_TBF, &p, sizeof p); |
3289 | | |
3290 | | /* The value in jump_action is the total number of TC_ACT_* |
3291 | | * we need to jump, not the actual number of TCA_ACT_KIND |
3292 | | * (act_index) actions. As certain TC_ACT_* actions can be |
3293 | | * translated into multiple TCA_ACT_KIND ones. |
3294 | | * |
3295 | | * See nl_msg_put_flower_acts() below for more details. */ |
3296 | 0 | nl_msg_put_u32(request, TCA_POLICE_RESULT, tc_action); |
3297 | 0 | } |
3298 | 0 | nl_msg_end_nested(request, offset); |
3299 | 0 | return 0; |
3300 | 0 | } |
3301 | | |
3302 | | static int |
3303 | | nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) |
3304 | 0 | { |
3305 | 0 | bool ingress, released = false; |
3306 | 0 | size_t offset; |
3307 | 0 | size_t act_offset; |
3308 | 0 | uint16_t act_index = 1; |
3309 | 0 | struct tc_action *action; |
3310 | 0 | int i, ifindex = 0; |
3311 | |
|
3312 | 0 | offset = nl_msg_start_nested(request, TCA_FLOWER_ACT); |
3313 | 0 | { |
3314 | 0 | int error; |
3315 | 0 | uint32_t prev_action_pc = TC_ACT_PIPE; |
3316 | |
|
3317 | 0 | action = flower->actions; |
3318 | 0 | for (i = 0; i < flower->action_count; i++, action++) { |
3319 | 0 | uint32_t action_pc; /* Programmatic Control */ |
3320 | |
|
3321 | 0 | if (!action->jump_action) { |
3322 | 0 | if (i == flower->action_count - 1) { |
3323 | 0 | action_pc = TC_ACT_SHOT; |
3324 | 0 | } else { |
3325 | 0 | action_pc = TC_ACT_PIPE; |
3326 | 0 | } |
3327 | 0 | } else if (action->jump_action == JUMP_ACTION_STOP) { |
3328 | 0 | action_pc = TC_ACT_STOLEN; |
3329 | 0 | } else { |
3330 | | /* The value in jump_action is the total number of TC_ACT_* |
3331 | | * we need to jump, not the actual number of TCA_ACT_KIND |
3332 | | * (act_index) actions. As certain TC_ACT_* actions can be |
3333 | | * translated into multiple TCA_ACT_KIND ones. |
3334 | | * |
3335 | | * If we can determine the number of actual actions being |
3336 | | * inserted we will update the count, if not we will return |
3337 | | * -EOPNOTSUPP. |
3338 | | */ |
3339 | 0 | int jump_index; |
3340 | 0 | int act_index_start = act_index - 1; |
3341 | 0 | int action_count = (action->jump_action & |
3342 | 0 | TC_ACT_EXT_VAL_MASK) - i; |
3343 | |
|
3344 | 0 | if (flower->csum_update_flags && |
3345 | 0 | (action->type != TC_ACT_PEDIT |
3346 | 0 | || prev_action_pc & TC_ACT_JUMP)) { |
3347 | 0 | act_index_start++; |
3348 | 0 | } |
3349 | |
|
3350 | 0 | jump_index = get_action_index_for_tc_actions(flower, |
3351 | 0 | act_index_start, |
3352 | 0 | action, |
3353 | 0 | action_count, |
3354 | 0 | released); |
3355 | 0 | if (jump_index < 0) { |
3356 | 0 | return -jump_index; |
3357 | 0 | } |
3358 | | |
3359 | 0 | action_pc = TC_ACT_JUMP | jump_index; |
3360 | 0 | } |
3361 | | |
3362 | 0 | if (action->type != TC_ACT_PEDIT || prev_action_pc & TC_ACT_JUMP) { |
3363 | 0 | nl_msg_put_csum_act(request, flower, prev_action_pc, |
3364 | 0 | &act_index); |
3365 | 0 | } |
3366 | |
|
3367 | 0 | switch (action->type) { |
3368 | 0 | case TC_ACT_PEDIT: { |
3369 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3370 | 0 | error = nl_msg_put_flower_rewrite_pedits(request, flower, |
3371 | 0 | action, action_pc); |
3372 | 0 | if (error) { |
3373 | 0 | return error; |
3374 | 0 | } |
3375 | 0 | nl_msg_end_nested(request, act_offset); |
3376 | |
|
3377 | 0 | if (i == flower->action_count - 1) { |
3378 | | /* If this is the last action check csum calc again. */ |
3379 | 0 | nl_msg_put_csum_act(request, flower, action_pc, |
3380 | 0 | &act_index); |
3381 | 0 | } |
3382 | 0 | } |
3383 | 0 | break; |
3384 | 0 | case TC_ACT_ENCAP: { |
3385 | 0 | if (!released && flower->tunnel) { |
3386 | 0 | nl_msg_put_flower_acts_release(request, act_index++); |
3387 | 0 | released = true; |
3388 | 0 | } |
3389 | |
|
3390 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3391 | 0 | nl_msg_put_act_tunnel_key_set(request, &action->encap, |
3392 | 0 | action_pc); |
3393 | 0 | nl_msg_put_act_flags(request); |
3394 | 0 | nl_msg_end_nested(request, act_offset); |
3395 | 0 | } |
3396 | 0 | break; |
3397 | 0 | case TC_ACT_VLAN_POP: { |
3398 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3399 | 0 | nl_msg_put_act_pop_vlan(request, action_pc); |
3400 | 0 | nl_msg_put_act_flags(request); |
3401 | 0 | nl_msg_end_nested(request, act_offset); |
3402 | 0 | } |
3403 | 0 | break; |
3404 | 0 | case TC_ACT_VLAN_PUSH: { |
3405 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3406 | 0 | nl_msg_put_act_push_vlan(request, |
3407 | 0 | action->vlan.vlan_push_tpid, |
3408 | 0 | action->vlan.vlan_push_id, |
3409 | 0 | action->vlan.vlan_push_prio, |
3410 | 0 | action_pc); |
3411 | 0 | nl_msg_put_act_flags(request); |
3412 | 0 | nl_msg_end_nested(request, act_offset); |
3413 | 0 | } |
3414 | 0 | break; |
3415 | 0 | case TC_ACT_MPLS_POP: { |
3416 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3417 | 0 | nl_msg_put_act_pop_mpls(request, action->mpls.proto, |
3418 | 0 | action_pc); |
3419 | 0 | nl_msg_end_nested(request, act_offset); |
3420 | 0 | } |
3421 | 0 | break; |
3422 | 0 | case TC_ACT_MPLS_PUSH: { |
3423 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3424 | 0 | nl_msg_put_act_push_mpls(request, action->mpls.proto, |
3425 | 0 | action->mpls.label, action->mpls.tc, |
3426 | 0 | action->mpls.ttl, action->mpls.bos, |
3427 | 0 | action_pc); |
3428 | 0 | nl_msg_end_nested(request, act_offset); |
3429 | 0 | } |
3430 | 0 | break; |
3431 | 0 | case TC_ACT_MPLS_SET: { |
3432 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3433 | 0 | nl_msg_put_act_set_mpls(request, action->mpls.label, |
3434 | 0 | action->mpls.tc, action->mpls.ttl, |
3435 | 0 | action->mpls.bos, action_pc); |
3436 | 0 | nl_msg_end_nested(request, act_offset); |
3437 | 0 | } |
3438 | 0 | break; |
3439 | 0 | case TC_ACT_OUTPUT: { |
3440 | 0 | if (!released && flower->tunnel) { |
3441 | 0 | nl_msg_put_flower_acts_release(request, act_index++); |
3442 | 0 | released = true; |
3443 | 0 | } |
3444 | |
|
3445 | 0 | ingress = action->out.ingress; |
3446 | 0 | ifindex = action->out.ifindex_out; |
3447 | 0 | if (ifindex < 1) { |
3448 | 0 | VLOG_ERR_RL(&error_rl, "%s: invalid ifindex: %d, type: %d", |
3449 | 0 | __func__, ifindex, action->type); |
3450 | 0 | return EINVAL; |
3451 | 0 | } |
3452 | | |
3453 | 0 | if (ingress) { |
3454 | | /* If redirecting to ingress (internal port) ensure |
3455 | | * pkt_type on skb is set to PACKET_HOST. */ |
3456 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3457 | 0 | nl_msg_put_act_skbedit_to_host(request); |
3458 | 0 | nl_msg_end_nested(request, act_offset); |
3459 | 0 | } |
3460 | |
|
3461 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3462 | 0 | if (i == flower->action_count - 1) { |
3463 | 0 | if (ingress) { |
3464 | 0 | nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN, |
3465 | 0 | TCA_INGRESS_REDIR); |
3466 | 0 | } else { |
3467 | 0 | nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN, |
3468 | 0 | TCA_EGRESS_REDIR); |
3469 | 0 | } |
3470 | 0 | action->jump_action = JUMP_ACTION_STOP; |
3471 | 0 | } else { |
3472 | 0 | if (ingress) { |
3473 | 0 | nl_msg_put_act_mirred(request, ifindex, action_pc, |
3474 | 0 | TCA_INGRESS_MIRROR); |
3475 | 0 | } else { |
3476 | 0 | nl_msg_put_act_mirred(request, ifindex, action_pc, |
3477 | 0 | TCA_EGRESS_MIRROR); |
3478 | 0 | } |
3479 | 0 | } |
3480 | 0 | nl_msg_put_act_cookie(request, &flower->act_cookie); |
3481 | 0 | nl_msg_put_act_flags(request); |
3482 | 0 | nl_msg_end_nested(request, act_offset); |
3483 | 0 | } |
3484 | 0 | break; |
3485 | 0 | case TC_ACT_GOTO: { |
3486 | 0 | if (released) { |
3487 | | /* We don't support tunnel release + output + goto |
3488 | | * for now, as next chain by default will try and match |
3489 | | * the tunnel metadata that was released/unset. |
3490 | | * |
3491 | | * This will happen with tunnel + mirror ports. |
3492 | | */ |
3493 | 0 | return -EOPNOTSUPP; |
3494 | 0 | } |
3495 | | |
3496 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3497 | 0 | nl_msg_put_act_gact(request, action->chain); |
3498 | 0 | nl_msg_put_act_cookie(request, &flower->act_cookie); |
3499 | 0 | nl_msg_end_nested(request, act_offset); |
3500 | 0 | } |
3501 | 0 | break; |
3502 | 0 | case TC_ACT_CT: { |
3503 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3504 | 0 | nl_msg_put_act_ct(request, action, action_pc); |
3505 | 0 | nl_msg_put_act_cookie(request, &flower->act_cookie); |
3506 | 0 | nl_msg_end_nested(request, act_offset); |
3507 | 0 | } |
3508 | 0 | break; |
3509 | 0 | case TC_ACT_POLICE: { |
3510 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3511 | 0 | nl_msg_put_act_police_index(request, action->police.index, |
3512 | 0 | action_pc); |
3513 | 0 | nl_msg_end_nested(request, act_offset); |
3514 | 0 | } |
3515 | 0 | break; |
3516 | 0 | case TC_ACT_POLICE_MTU: { |
3517 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3518 | 0 | if (nl_msg_put_act_police_mtu(request, flower, action, |
3519 | 0 | action_pc, i, act_index, |
3520 | 0 | released)) { |
3521 | 0 | return -EOPNOTSUPP; |
3522 | 0 | } |
3523 | 0 | nl_msg_put_act_cookie(request, &flower->act_cookie); |
3524 | 0 | nl_msg_put_act_flags(request); |
3525 | 0 | nl_msg_end_nested(request, act_offset); |
3526 | 0 | } |
3527 | 0 | break; |
3528 | 0 | } |
3529 | | |
3530 | 0 | prev_action_pc = action_pc; |
3531 | 0 | } |
3532 | 0 | } |
3533 | | |
3534 | 0 | if (!flower->action_count) { |
3535 | 0 | act_offset = nl_msg_start_nested(request, act_index++); |
3536 | 0 | nl_msg_put_act_gact(request, 0); |
3537 | 0 | nl_msg_put_act_cookie(request, &flower->act_cookie); |
3538 | 0 | nl_msg_put_act_flags(request); |
3539 | 0 | nl_msg_end_nested(request, act_offset); |
3540 | 0 | } |
3541 | 0 | nl_msg_end_nested(request, offset); |
3542 | |
|
3543 | 0 | return 0; |
3544 | 0 | } |
3545 | | |
3546 | | static void |
3547 | | nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type, |
3548 | | uint16_t mask_type, const void *data, |
3549 | | const void *mask_data, size_t len) |
3550 | 0 | { |
3551 | 0 | if (mask_type != TCA_FLOWER_UNSPEC) { |
3552 | 0 | if (is_all_zeros(mask_data, len)) { |
3553 | 0 | return; |
3554 | 0 | } |
3555 | 0 | nl_msg_put_unspec(request, mask_type, mask_data, len); |
3556 | 0 | } |
3557 | 0 | nl_msg_put_unspec(request, type, data, len); |
3558 | 0 | } |
3559 | | |
3560 | | static void |
3561 | | nl_msg_put_flower_geneve(struct ofpbuf *request, |
3562 | | const struct tc_flower_tunnel *tunnel) |
3563 | 0 | { |
3564 | 0 | const struct tun_metadata *metadata = &tunnel->metadata; |
3565 | 0 | const struct geneve_opt *opt; |
3566 | 0 | int len, cnt = 0; |
3567 | 0 | size_t offset; |
3568 | |
|
3569 | 0 | len = metadata->present.len; |
3570 | 0 | while (len) { |
3571 | 0 | opt = &metadata->opts.gnv[cnt]; |
3572 | 0 | offset = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE); |
3573 | |
|
3574 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, |
3575 | 0 | opt->opt_class); |
3576 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, opt->type); |
3577 | 0 | nl_msg_put_unspec(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, opt + 1, |
3578 | 0 | opt->length * 4); |
3579 | |
|
3580 | 0 | cnt += sizeof(struct geneve_opt) / 4 + opt->length; |
3581 | 0 | len -= sizeof(struct geneve_opt) + opt->length * 4; |
3582 | |
|
3583 | 0 | nl_msg_end_nested(request, offset); |
3584 | 0 | } |
3585 | 0 | } |
3586 | | |
3587 | | static void |
3588 | | nl_msg_put_flower_vxlan_tun_opts(struct ofpbuf *request, |
3589 | | const struct tc_flower_tunnel *tunnel) |
3590 | 0 | { |
3591 | 0 | uint32_t gbp_raw; |
3592 | 0 | size_t offset; |
3593 | |
|
3594 | 0 | if (!tunnel->gbp.id_present) { |
3595 | 0 | return; |
3596 | 0 | } |
3597 | | |
3598 | 0 | gbp_raw = odp_encode_gbp_raw(tunnel->gbp.flags, tunnel->gbp.id); |
3599 | 0 | offset = nl_msg_start_nested_with_flag(request, |
3600 | 0 | TCA_FLOWER_KEY_ENC_OPTS_VXLAN); |
3601 | 0 | nl_msg_put_u32(request, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp_raw); |
3602 | 0 | nl_msg_end_nested(request, offset); |
3603 | 0 | } |
3604 | | |
3605 | | static void |
3606 | | nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type, |
3607 | | struct tc_flower_tunnel *tunnel) |
3608 | 0 | { |
3609 | 0 | size_t outer; |
3610 | |
|
3611 | 0 | if (!tunnel->metadata.present.len && !tunnel->gbp.id_present) { |
3612 | 0 | return; |
3613 | 0 | } |
3614 | | |
3615 | 0 | outer = nl_msg_start_nested(request, type); |
3616 | 0 | nl_msg_put_flower_geneve(request, tunnel); |
3617 | 0 | nl_msg_put_flower_vxlan_tun_opts(request, tunnel); |
3618 | 0 | nl_msg_end_nested(request, outer); |
3619 | 0 | } |
3620 | | |
3621 | | static void |
3622 | | nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower) |
3623 | 0 | { |
3624 | 0 | ovs_be32 ipv4_src_mask = flower->mask.tunnel.ipv4.ipv4_src; |
3625 | 0 | ovs_be32 ipv4_dst_mask = flower->mask.tunnel.ipv4.ipv4_dst; |
3626 | 0 | ovs_be32 ipv4_src = flower->key.tunnel.ipv4.ipv4_src; |
3627 | 0 | ovs_be32 ipv4_dst = flower->key.tunnel.ipv4.ipv4_dst; |
3628 | 0 | struct in6_addr *ipv6_src_mask = &flower->mask.tunnel.ipv6.ipv6_src; |
3629 | 0 | struct in6_addr *ipv6_dst_mask = &flower->mask.tunnel.ipv6.ipv6_dst; |
3630 | 0 | struct in6_addr *ipv6_src = &flower->key.tunnel.ipv6.ipv6_src; |
3631 | 0 | struct in6_addr *ipv6_dst = &flower->key.tunnel.ipv6.ipv6_dst; |
3632 | 0 | ovs_be32 id = be64_to_be32(flower->key.tunnel.id); |
3633 | 0 | ovs_be32 enc_flags = htonl(flower->key.tunnel.tc_enc_flags); |
3634 | 0 | ovs_be16 tp_src = flower->key.tunnel.tp_src; |
3635 | 0 | ovs_be16 tp_dst = flower->key.tunnel.tp_dst; |
3636 | 0 | uint8_t tos = flower->key.tunnel.tos; |
3637 | 0 | uint8_t ttl = flower->key.tunnel.ttl; |
3638 | 0 | uint8_t tos_mask = flower->mask.tunnel.tos; |
3639 | 0 | uint8_t ttl_mask = flower->mask.tunnel.ttl; |
3640 | 0 | ovs_be64 id_mask = flower->mask.tunnel.id; |
3641 | 0 | ovs_be32 enc_flags_mask = htonl(flower->mask.tunnel.tc_enc_flags); |
3642 | 0 | ovs_be16 tp_src_mask = flower->mask.tunnel.tp_src; |
3643 | 0 | ovs_be16 tp_dst_mask = flower->mask.tunnel.tp_dst; |
3644 | |
|
3645 | 0 | if (ipv4_dst_mask || ipv4_src_mask) { |
3646 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, |
3647 | 0 | ipv4_dst_mask); |
3648 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, |
3649 | 0 | ipv4_src_mask); |
3650 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_DST, ipv4_dst); |
3651 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_SRC, ipv4_src); |
3652 | 0 | } else if (ipv6_addr_is_set(ipv6_dst_mask) || |
3653 | 0 | ipv6_addr_is_set(ipv6_src_mask)) { |
3654 | 0 | nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, |
3655 | 0 | ipv6_dst_mask); |
3656 | 0 | nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, |
3657 | 0 | ipv6_src_mask); |
3658 | 0 | nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_DST, ipv6_dst); |
3659 | 0 | nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_SRC, ipv6_src); |
3660 | 0 | } |
3661 | 0 | if (tos_mask) { |
3662 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TOS, tos); |
3663 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TOS_MASK, tos_mask); |
3664 | 0 | } |
3665 | 0 | if (ttl_mask) { |
3666 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TTL, ttl); |
3667 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TTL_MASK, ttl_mask); |
3668 | 0 | } |
3669 | 0 | if (tp_src_mask) { |
3670 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, tp_src); |
3671 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, |
3672 | 0 | tp_src_mask); |
3673 | 0 | } |
3674 | 0 | if (tp_dst_mask) { |
3675 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, tp_dst); |
3676 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, |
3677 | 0 | tp_dst_mask); |
3678 | 0 | } |
3679 | 0 | if (enc_flags_mask) { |
3680 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_FLAGS, enc_flags); |
3681 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_FLAGS_MASK, |
3682 | 0 | enc_flags_mask); |
3683 | 0 | } |
3684 | 0 | if (id_mask) { |
3685 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_KEY_ID, id); |
3686 | 0 | } |
3687 | 0 | nl_msg_put_flower_tunnel_opts(request, TCA_FLOWER_KEY_ENC_OPTS, |
3688 | 0 | &flower->key.tunnel); |
3689 | 0 | nl_msg_put_flower_tunnel_opts(request, TCA_FLOWER_KEY_ENC_OPTS_MASK, |
3690 | 0 | &flower->mask.tunnel); |
3691 | 0 | } |
3692 | | |
3693 | | #define FLOWER_PUT_MASKED_VALUE(member, type) \ |
3694 | 0 | nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \ |
3695 | 0 | &flower->mask.member, sizeof flower->key.member) |
3696 | | |
3697 | | static int |
3698 | | nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower) |
3699 | 0 | { |
3700 | |
|
3701 | 0 | uint16_t host_eth_type = ntohs(flower->key.eth_type); |
3702 | 0 | bool is_vlan = eth_type_vlan(flower->key.eth_type); |
3703 | 0 | bool is_qinq = is_vlan && eth_type_vlan(flower->key.encap_eth_type[0]); |
3704 | 0 | bool is_mpls = eth_type_mpls(flower->key.eth_type); |
3705 | 0 | enum tc_offload_policy policy = flower->tc_policy; |
3706 | 0 | int err; |
3707 | | |
3708 | | /* need to parse acts first as some acts require changing the matching |
3709 | | * see csum_update_flag() */ |
3710 | 0 | err = nl_msg_put_flower_acts(request, flower); |
3711 | 0 | if (err) { |
3712 | 0 | return err; |
3713 | 0 | } |
3714 | | |
3715 | 0 | if (is_vlan) { |
3716 | 0 | if (is_qinq) { |
3717 | 0 | host_eth_type = ntohs(flower->key.encap_eth_type[1]); |
3718 | 0 | } else { |
3719 | 0 | host_eth_type = ntohs(flower->key.encap_eth_type[0]); |
3720 | 0 | } |
3721 | 0 | } |
3722 | |
|
3723 | 0 | if (is_mpls) { |
3724 | 0 | host_eth_type = ntohs(flower->key.encap_eth_type[0]); |
3725 | 0 | } |
3726 | |
|
3727 | 0 | FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST); |
3728 | 0 | FLOWER_PUT_MASKED_VALUE(src_mac, TCA_FLOWER_KEY_ETH_SRC); |
3729 | |
|
3730 | 0 | if (host_eth_type == ETH_P_ARP) { |
3731 | 0 | FLOWER_PUT_MASKED_VALUE(arp.spa, TCA_FLOWER_KEY_ARP_SIP); |
3732 | 0 | FLOWER_PUT_MASKED_VALUE(arp.tpa, TCA_FLOWER_KEY_ARP_TIP); |
3733 | 0 | FLOWER_PUT_MASKED_VALUE(arp.sha, TCA_FLOWER_KEY_ARP_SHA); |
3734 | 0 | FLOWER_PUT_MASKED_VALUE(arp.tha, TCA_FLOWER_KEY_ARP_THA); |
3735 | 0 | FLOWER_PUT_MASKED_VALUE(arp.opcode, TCA_FLOWER_KEY_ARP_OP); |
3736 | 0 | } |
3737 | |
|
3738 | 0 | if (host_eth_type == ETH_P_IP || host_eth_type == ETH_P_IPV6) { |
3739 | 0 | FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL); |
3740 | 0 | FLOWER_PUT_MASKED_VALUE(ip_tos, TCA_FLOWER_KEY_IP_TOS); |
3741 | |
|
3742 | 0 | if (flower->mask.ip_proto && flower->key.ip_proto) { |
3743 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_IP_PROTO, |
3744 | 0 | flower->key.ip_proto); |
3745 | 0 | } |
3746 | |
|
3747 | 0 | if (flower->mask.flags) { |
3748 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_FLAGS, |
3749 | 0 | htonl(flower->key.flags)); |
3750 | 0 | nl_msg_put_be32(request, TCA_FLOWER_KEY_FLAGS_MASK, |
3751 | 0 | htonl(flower->mask.flags)); |
3752 | 0 | } |
3753 | |
|
3754 | 0 | if (flower->key.ip_proto == IPPROTO_UDP) { |
3755 | 0 | FLOWER_PUT_MASKED_VALUE(udp_src, TCA_FLOWER_KEY_UDP_SRC); |
3756 | 0 | FLOWER_PUT_MASKED_VALUE(udp_dst, TCA_FLOWER_KEY_UDP_DST); |
3757 | 0 | } else if (flower->key.ip_proto == IPPROTO_TCP) { |
3758 | 0 | FLOWER_PUT_MASKED_VALUE(tcp_src, TCA_FLOWER_KEY_TCP_SRC); |
3759 | 0 | FLOWER_PUT_MASKED_VALUE(tcp_dst, TCA_FLOWER_KEY_TCP_DST); |
3760 | 0 | FLOWER_PUT_MASKED_VALUE(tcp_flags, TCA_FLOWER_KEY_TCP_FLAGS); |
3761 | 0 | } else if (flower->key.ip_proto == IPPROTO_SCTP) { |
3762 | 0 | FLOWER_PUT_MASKED_VALUE(sctp_src, TCA_FLOWER_KEY_SCTP_SRC); |
3763 | 0 | FLOWER_PUT_MASKED_VALUE(sctp_dst, TCA_FLOWER_KEY_SCTP_DST); |
3764 | 0 | } else if (flower->key.ip_proto == IPPROTO_ICMP) { |
3765 | 0 | FLOWER_PUT_MASKED_VALUE(icmp_code, TCA_FLOWER_KEY_ICMPV4_CODE); |
3766 | 0 | FLOWER_PUT_MASKED_VALUE(icmp_type, TCA_FLOWER_KEY_ICMPV4_TYPE); |
3767 | 0 | } else if (flower->key.ip_proto == IPPROTO_ICMPV6) { |
3768 | 0 | FLOWER_PUT_MASKED_VALUE(icmp_code, TCA_FLOWER_KEY_ICMPV6_CODE); |
3769 | 0 | FLOWER_PUT_MASKED_VALUE(icmp_type, TCA_FLOWER_KEY_ICMPV6_TYPE); |
3770 | 0 | } |
3771 | 0 | } |
3772 | |
|
3773 | 0 | FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE); |
3774 | 0 | FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE); |
3775 | 0 | FLOWER_PUT_MASKED_VALUE(ct_mark, TCA_FLOWER_KEY_CT_MARK); |
3776 | 0 | FLOWER_PUT_MASKED_VALUE(ct_label, TCA_FLOWER_KEY_CT_LABELS); |
3777 | |
|
3778 | 0 | if (host_eth_type == ETH_P_IP) { |
3779 | 0 | FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC); |
3780 | 0 | FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST); |
3781 | 0 | } else if (host_eth_type == ETH_P_IPV6) { |
3782 | 0 | FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_src, TCA_FLOWER_KEY_IPV6_SRC); |
3783 | 0 | FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_dst, TCA_FLOWER_KEY_IPV6_DST); |
3784 | 0 | } |
3785 | |
|
3786 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type); |
3787 | |
|
3788 | 0 | if (is_mpls) { |
3789 | 0 | if (mpls_lse_to_ttl(flower->mask.mpls_lse)) { |
3790 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_MPLS_TTL, |
3791 | 0 | mpls_lse_to_ttl(flower->key.mpls_lse)); |
3792 | 0 | } |
3793 | 0 | if (mpls_lse_to_tc(flower->mask.mpls_lse)) { |
3794 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_MPLS_TC, |
3795 | 0 | mpls_lse_to_tc(flower->key.mpls_lse)); |
3796 | 0 | } |
3797 | 0 | if (mpls_lse_to_bos(flower->mask.mpls_lse)) { |
3798 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_MPLS_BOS, |
3799 | 0 | mpls_lse_to_bos(flower->key.mpls_lse)); |
3800 | 0 | } |
3801 | 0 | if (mpls_lse_to_label(flower->mask.mpls_lse)) { |
3802 | 0 | nl_msg_put_u32(request, TCA_FLOWER_KEY_MPLS_LABEL, |
3803 | 0 | mpls_lse_to_label(flower->key.mpls_lse)); |
3804 | 0 | } |
3805 | 0 | } |
3806 | |
|
3807 | 0 | if (is_vlan) { |
3808 | 0 | if (flower->mask.vlan_id[0]) { |
3809 | 0 | nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID, |
3810 | 0 | flower->key.vlan_id[0]); |
3811 | 0 | } |
3812 | 0 | if (flower->mask.vlan_prio[0]) { |
3813 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO, |
3814 | 0 | flower->key.vlan_prio[0]); |
3815 | 0 | } |
3816 | 0 | if (flower->key.encap_eth_type[0]) { |
3817 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE, |
3818 | 0 | flower->key.encap_eth_type[0]); |
3819 | 0 | } |
3820 | |
|
3821 | 0 | if (is_qinq) { |
3822 | 0 | if (flower->mask.vlan_id[1]) { |
3823 | 0 | nl_msg_put_u16(request, TCA_FLOWER_KEY_CVLAN_ID, |
3824 | 0 | flower->key.vlan_id[1]); |
3825 | 0 | } |
3826 | 0 | if (flower->mask.vlan_prio[1]) { |
3827 | 0 | nl_msg_put_u8(request, TCA_FLOWER_KEY_CVLAN_PRIO, |
3828 | 0 | flower->key.vlan_prio[1]); |
3829 | 0 | } |
3830 | 0 | if (flower->key.encap_eth_type[1]) { |
3831 | 0 | nl_msg_put_be16(request, TCA_FLOWER_KEY_CVLAN_ETH_TYPE, |
3832 | 0 | flower->key.encap_eth_type[1]); |
3833 | 0 | } |
3834 | 0 | } |
3835 | 0 | } |
3836 | |
|
3837 | 0 | if (policy == TC_POLICY_NONE) { |
3838 | 0 | policy = tc_policy; |
3839 | 0 | } |
3840 | |
|
3841 | 0 | nl_msg_put_u32(request, TCA_FLOWER_FLAGS, tc_get_tc_cls_policy(policy)); |
3842 | |
|
3843 | 0 | if (flower->tunnel) { |
3844 | 0 | nl_msg_put_flower_tunnel(request, flower); |
3845 | 0 | } |
3846 | |
|
3847 | 0 | return 0; |
3848 | 0 | } |
3849 | | |
3850 | | static void |
3851 | | log_tc_flower_match(const char *msg, |
3852 | | const struct tc_flower *a, |
3853 | | const struct tc_flower *b) |
3854 | 0 | { |
3855 | 0 | uint8_t key_a[sizeof(struct tc_flower_key)]; |
3856 | 0 | uint8_t key_b[sizeof(struct tc_flower_key)]; |
3857 | 0 | struct ds s = DS_EMPTY_INITIALIZER; |
3858 | |
|
3859 | 0 | for (int i = 0; i < sizeof a->key; i++) { |
3860 | 0 | uint8_t mask_a = ((uint8_t *) &a->mask)[i]; |
3861 | 0 | uint8_t mask_b = ((uint8_t *) &b->mask)[i]; |
3862 | |
|
3863 | 0 | key_a[i] = ((uint8_t *) &a->key)[i] & mask_a; |
3864 | 0 | key_b[i] = ((uint8_t *) &b->key)[i] & mask_b; |
3865 | 0 | } |
3866 | 0 | ds_put_cstr(&s, "\nExpected Mask:\n"); |
3867 | 0 | ds_put_sparse_hex_dump(&s, &a->mask, sizeof a->mask, 0, false); |
3868 | 0 | ds_put_cstr(&s, "\nReceived Mask:\n"); |
3869 | 0 | ds_put_sparse_hex_dump(&s, &b->mask, sizeof b->mask, 0, false); |
3870 | 0 | ds_put_cstr(&s, "\nExpected Key:\n"); |
3871 | 0 | ds_put_sparse_hex_dump(&s, &a->key, sizeof a->key, 0, false); |
3872 | 0 | ds_put_cstr(&s, "\nReceived Key:\n"); |
3873 | 0 | ds_put_sparse_hex_dump(&s, &b->key, sizeof b->key, 0, false); |
3874 | 0 | ds_put_cstr(&s, "\nExpected Masked Key:\n"); |
3875 | 0 | ds_put_sparse_hex_dump(&s, key_a, sizeof key_a, 0, false); |
3876 | 0 | ds_put_cstr(&s, "\nReceived Masked Key:\n"); |
3877 | 0 | ds_put_sparse_hex_dump(&s, key_b, sizeof key_b, 0, false); |
3878 | |
|
3879 | 0 | if (a->action_count != b->action_count) { |
3880 | | /* If action count is not equal, we print all actions to see which |
3881 | | * ones are missing. */ |
3882 | 0 | const struct tc_action *action; |
3883 | 0 | int i; |
3884 | |
|
3885 | 0 | ds_put_cstr(&s, "\nExpected Actions:\n"); |
3886 | 0 | for (i = 0, action = a->actions; i < a->action_count; i++, action++) { |
3887 | 0 | ds_put_format(&s, " - %d -\n", i); |
3888 | 0 | ds_put_sparse_hex_dump(&s, action, sizeof *action, 0, false); |
3889 | 0 | } |
3890 | 0 | ds_put_cstr(&s, "\nReceived Actions:\n"); |
3891 | 0 | for (i = 0, action = b->actions; i < b->action_count; i++, action++) { |
3892 | 0 | ds_put_format(&s, " - %d -\n", i); |
3893 | 0 | ds_put_sparse_hex_dump(&s, action, sizeof *action, 0, false); |
3894 | 0 | } |
3895 | 0 | } else { |
3896 | | /* Only dump the delta in actions. */ |
3897 | 0 | const struct tc_action *action_a = a->actions; |
3898 | 0 | const struct tc_action *action_b = b->actions; |
3899 | |
|
3900 | 0 | for (int i = 0; i < a->action_count; i++, action_a++, action_b++) { |
3901 | 0 | if (memcmp(action_a, action_b, sizeof *action_a)) { |
3902 | 0 | ds_put_format(&s, "\nAction %d mismatch:\n" |
3903 | 0 | " - Expected Action:\n", i); |
3904 | 0 | ds_put_sparse_hex_dump(&s, action_a, sizeof *action_a, |
3905 | 0 | 0, false); |
3906 | 0 | ds_put_cstr(&s, " - Received Action:\n"); |
3907 | 0 | ds_put_sparse_hex_dump(&s, action_b, sizeof *action_b, |
3908 | 0 | 0, false); |
3909 | 0 | } |
3910 | 0 | } |
3911 | 0 | } |
3912 | 0 | VLOG_DBG_RL(&error_rl, "%s%s", msg, ds_cstr(&s)); |
3913 | 0 | ds_destroy(&s); |
3914 | 0 | } |
3915 | | |
3916 | | static bool |
3917 | | cmp_tc_flower_match_action(const struct tc_flower *a, |
3918 | | const struct tc_flower *b) |
3919 | 0 | { |
3920 | 0 | if (memcmp(&a->mask, &b->mask, sizeof a->mask)) { |
3921 | 0 | log_tc_flower_match("tc flower compare failed mask compare:", a, b); |
3922 | 0 | return false; |
3923 | 0 | } |
3924 | | |
3925 | | /* We can not memcmp() the key as some keys might be set while the mask |
3926 | | * is not.*/ |
3927 | | |
3928 | 0 | for (int i = 0; i < sizeof a->key; i++) { |
3929 | 0 | uint8_t mask = ((uint8_t *)&a->mask)[i]; |
3930 | 0 | uint8_t key_a = ((uint8_t *)&a->key)[i] & mask; |
3931 | 0 | uint8_t key_b = ((uint8_t *)&b->key)[i] & mask; |
3932 | |
|
3933 | 0 | if (key_a != key_b) { |
3934 | 0 | log_tc_flower_match("tc flower compare failed masked key compare:", |
3935 | 0 | a, b); |
3936 | 0 | return false; |
3937 | 0 | } |
3938 | 0 | } |
3939 | | |
3940 | | /* Compare the actions. */ |
3941 | 0 | const struct tc_action *action_a = a->actions; |
3942 | 0 | const struct tc_action *action_b = b->actions; |
3943 | |
|
3944 | 0 | if (a->action_count != b->action_count) { |
3945 | 0 | log_tc_flower_match("tc flower compare failed action length check", |
3946 | 0 | a, b); |
3947 | 0 | return false; |
3948 | 0 | } |
3949 | | |
3950 | 0 | for (int i = 0; i < a->action_count; i++, action_a++, action_b++) { |
3951 | 0 | if (memcmp(action_a, action_b, sizeof *action_a)) { |
3952 | 0 | log_tc_flower_match("tc flower compare failed action compare", |
3953 | 0 | a, b); |
3954 | 0 | return false; |
3955 | 0 | } |
3956 | 0 | } |
3957 | | |
3958 | 0 | return true; |
3959 | 0 | } |
3960 | | |
3961 | | int |
3962 | | tc_replace_flower(struct tcf_id *id, struct tc_flower *flower) |
3963 | 0 | { |
3964 | 0 | struct ofpbuf request; |
3965 | 0 | struct ofpbuf *reply; |
3966 | 0 | int error = 0; |
3967 | 0 | size_t basic_offset; |
3968 | 0 | uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type; |
3969 | |
|
3970 | 0 | request_from_tcf_id(id, eth_type, RTM_NEWTFILTER, |
3971 | 0 | NLM_F_CREATE | NLM_F_ECHO, &request); |
3972 | |
|
3973 | 0 | nl_msg_put_string(&request, TCA_KIND, "flower"); |
3974 | 0 | basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS); |
3975 | 0 | { |
3976 | 0 | error = nl_msg_put_flower_options(&request, flower); |
3977 | |
|
3978 | 0 | if (error) { |
3979 | 0 | ofpbuf_uninit(&request); |
3980 | 0 | return error; |
3981 | 0 | } |
3982 | 0 | } |
3983 | 0 | nl_msg_end_nested(&request, basic_offset); |
3984 | |
|
3985 | 0 | error = tc_transact(&request, &reply); |
3986 | 0 | if (!error) { |
3987 | 0 | struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size); |
3988 | 0 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); |
3989 | 0 | struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc); |
3990 | 0 | bool is_probe = id->prio == TC_RESERVED_PRIORITY_FEATURE_PROBE; |
3991 | |
|
3992 | 0 | if (!nlmsg || !tc) { |
3993 | 0 | COVERAGE_INC(tc_netlink_malformed_reply); |
3994 | 0 | ofpbuf_delete(reply); |
3995 | 0 | return EPROTO; |
3996 | 0 | } |
3997 | | |
3998 | 0 | id->prio = tc_get_major(tc->tcm_info); |
3999 | 0 | id->handle = tc->tcm_handle; |
4000 | |
|
4001 | 0 | if (id->prio != TC_RESERVED_PRIORITY_POLICE) { |
4002 | 0 | struct tc_flower flower_out; |
4003 | 0 | struct tcf_id id_out; |
4004 | 0 | int ret; |
4005 | |
|
4006 | 0 | ret = parse_netlink_to_tc_flower(reply, &id_out, &flower_out, |
4007 | 0 | false); |
4008 | |
|
4009 | 0 | if (ret || !cmp_tc_flower_match_action(flower, &flower_out)) { |
4010 | 0 | if (is_probe) { |
4011 | 0 | error = EINVAL; |
4012 | 0 | } else { |
4013 | 0 | VLOG_WARN_RL(&error_rl, "Kernel flower acknowledgment " |
4014 | 0 | "does not match request! Set " |
4015 | 0 | "dpif_netlink to dbg to see " |
4016 | 0 | "which rule caused this error."); |
4017 | 0 | } |
4018 | 0 | } |
4019 | 0 | } |
4020 | 0 | ofpbuf_delete(reply); |
4021 | 0 | } |
4022 | | |
4023 | 0 | return error; |
4024 | 0 | } |
4025 | | |
4026 | | void |
4027 | | tc_set_policy(const char *policy) |
4028 | 0 | { |
4029 | 0 | if (!policy) { |
4030 | 0 | return; |
4031 | 0 | } |
4032 | | |
4033 | 0 | if (!strcmp(policy, "skip_sw")) { |
4034 | 0 | tc_policy = TC_POLICY_SKIP_SW; |
4035 | 0 | } else if (!strcmp(policy, "skip_hw")) { |
4036 | 0 | tc_policy = TC_POLICY_SKIP_HW; |
4037 | 0 | } else if (!strcmp(policy, "none")) { |
4038 | 0 | tc_policy = TC_POLICY_NONE; |
4039 | 0 | } else { |
4040 | 0 | VLOG_WARN("tc: Invalid policy '%s'", policy); |
4041 | 0 | return; |
4042 | 0 | } |
4043 | | |
4044 | 0 | VLOG_INFO("tc: Using policy '%s'", policy); |
4045 | 0 | } |
4046 | | |
4047 | | void |
4048 | | nl_msg_put_act_tc_policy_flag(struct ofpbuf *request) |
4049 | 0 | { |
4050 | 0 | int flag = 0; |
4051 | |
|
4052 | 0 | if (!request) { |
4053 | 0 | return; |
4054 | 0 | } |
4055 | | |
4056 | 0 | if (tc_policy == TC_POLICY_SKIP_HW) { |
4057 | 0 | flag = TCA_ACT_FLAGS_SKIP_HW; |
4058 | 0 | } else if (tc_policy == TC_POLICY_SKIP_SW) { |
4059 | 0 | flag = TCA_ACT_FLAGS_SKIP_SW; |
4060 | 0 | } |
4061 | |
|
4062 | 0 | if (flag) { |
4063 | 0 | struct nla_bitfield32 flags = { flag, flag }; |
4064 | 0 | nl_msg_put_unspec(request, TCA_ACT_FLAGS, &flags, sizeof flags); |
4065 | 0 | } |
4066 | 0 | } |