]>
Commit | Line | Data |
---|---|---|
3948eb54 | 1 | /* |
4ea96698 | 2 | * Copyright (c) 2015, 2018 Nicira, Inc. |
3948eb54 DDP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
2a7c4805 | 18 | #include "dpif-provider.h" |
3948eb54 DDP |
19 | |
20 | #include <errno.h> | |
21 | ||
22 | #include "ct-dpif.h" | |
c43a1331 | 23 | #include "openvswitch/ofp-parse.h" |
2a7c4805 | 24 | #include "openvswitch/vlog.h" |
3948eb54 | 25 | |
2a7c4805 | 26 | VLOG_DEFINE_THIS_MODULE(ct_dpif); |
b77d9629 | 27 | |
3948eb54 DDP |
28 | /* Declarations for conntrack entry formatting. */ |
29 | struct flags { | |
30 | uint32_t flag; | |
31 | const char *name; | |
32 | }; | |
33 | ||
3948eb54 DDP |
34 | static void ct_dpif_format_counters(struct ds *, |
35 | const struct ct_dpif_counters *); | |
36 | static void ct_dpif_format_timestamp(struct ds *, | |
37 | const struct ct_dpif_timestamp *); | |
38 | static void ct_dpif_format_flags(struct ds *, const char *title, | |
39 | uint32_t flags, const struct flags *); | |
40 | static void ct_dpif_format_protoinfo(struct ds *, const char *title, | |
41 | const struct ct_dpif_protoinfo *, | |
42 | bool verbose); | |
43 | static void ct_dpif_format_helper(struct ds *, const char *title, | |
44 | const struct ct_dpif_helper *); | |
45 | ||
46 | static const struct flags ct_dpif_status_flags[] = { | |
47 | #define CT_DPIF_STATUS_FLAG(FLAG) { CT_DPIF_STATUS_##FLAG, #FLAG }, | |
48 | CT_DPIF_STATUS_FLAGS | |
49 | #undef CT_DPIF_STATUS_FLAG | |
50 | { 0, NULL } /* End marker. */ | |
51 | }; | |
52 | \f | |
b77d9629 DDP |
53 | /* Dumping */ |
54 | ||
55 | /* Start dumping the entries from the connection tracker used by 'dpif'. | |
56 | * | |
57 | * 'dump' must be the address of a pointer to a struct ct_dpif_dump_state, | |
58 | * which should be passed (unaltered) to ct_dpif_dump_{next,done}(). | |
59 | * | |
60 | * If 'zone' is not NULL, it should point to an integer identifing a | |
61 | * conntrack zone to which the dump will be limited. If it is NULL, | |
62 | * conntrack entries from all zones will be dumped. | |
63 | * | |
64 | * If there has been a problem the function returns a non-zero value | |
65 | * that represents the error. Otherwise it returns zero. */ | |
66 | int | |
67 | ct_dpif_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump, | |
ded30c74 | 68 | const uint16_t *zone, int *ptot_bkts) |
b77d9629 DDP |
69 | { |
70 | int err; | |
71 | ||
72 | err = (dpif->dpif_class->ct_dump_start | |
ded30c74 | 73 | ? dpif->dpif_class->ct_dump_start(dpif, dump, zone, ptot_bkts) |
b77d9629 DDP |
74 | : EOPNOTSUPP); |
75 | ||
76 | if (!err) { | |
77 | (*dump)->dpif = dpif; | |
78 | } | |
79 | ||
80 | return err; | |
81 | } | |
82 | ||
83 | /* Dump one connection from a tracker, and put it in 'entry'. | |
84 | * | |
85 | * 'dump' should have been initialized by ct_dpif_dump_start(). | |
86 | * | |
87 | * The function returns 0, if an entry has been dumped succesfully. | |
88 | * Otherwise it returns a non-zero value which can be: | |
89 | * - EOF: meaning that there are no more entries to dump. | |
90 | * - an error value. | |
91 | * In both cases, the user should call ct_dpif_dump_done(). */ | |
92 | int | |
93 | ct_dpif_dump_next(struct ct_dpif_dump_state *dump, struct ct_dpif_entry *entry) | |
94 | { | |
95 | struct dpif *dpif = dump->dpif; | |
96 | ||
97 | return (dpif->dpif_class->ct_dump_next | |
98 | ? dpif->dpif_class->ct_dump_next(dpif, dump, entry) | |
99 | : EOPNOTSUPP); | |
100 | } | |
101 | ||
102 | /* Free resources used by 'dump' */ | |
103 | int | |
104 | ct_dpif_dump_done(struct ct_dpif_dump_state *dump) | |
105 | { | |
106 | struct dpif *dpif = dump->dpif; | |
107 | ||
108 | return (dpif->dpif_class->ct_dump_done | |
109 | ? dpif->dpif_class->ct_dump_done(dpif, dump) | |
110 | : EOPNOTSUPP); | |
111 | } | |
112 | \f | |
817a7657 YHW |
113 | /* Flush the entries in the connection tracker used by 'dpif'. The |
114 | * arguments have the following behavior: | |
a0f7b6d5 | 115 | * |
817a7657 YHW |
116 | * - If both 'zone' and 'tuple' are NULL, flush all the conntrack entries. |
117 | * - If 'zone' is not NULL, and 'tuple' is NULL, flush all the conntrack | |
118 | * entries in '*zone'. | |
119 | * - If 'tuple' is not NULL, flush the conntrack entry specified by 'tuple' | |
120 | * in '*zone'. If 'zone' is NULL, use the default zone (zone 0). */ | |
a0f7b6d5 | 121 | int |
817a7657 YHW |
122 | ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, |
123 | const struct ct_dpif_tuple *tuple) | |
a0f7b6d5 | 124 | { |
817a7657 YHW |
125 | if (tuple) { |
126 | struct ds ds = DS_EMPTY_INITIALIZER; | |
127 | ct_dpif_format_tuple(&ds, tuple); | |
128 | VLOG_DBG("%s: ct_flush: %s in zone %d", dpif_name(dpif), ds_cstr(&ds), | |
129 | zone ? *zone : 0); | |
130 | ds_destroy(&ds); | |
131 | } else if (zone) { | |
132 | VLOG_DBG("%s: ct_flush: zone %"PRIu16, dpif_name(dpif), *zone); | |
2a7c4805 JP |
133 | } else { |
134 | VLOG_DBG("%s: ct_flush: <all>", dpif_name(dpif)); | |
135 | } | |
136 | ||
a0f7b6d5 | 137 | return (dpif->dpif_class->ct_flush |
817a7657 | 138 | ? dpif->dpif_class->ct_flush(dpif, zone, tuple) |
a0f7b6d5 DDP |
139 | : EOPNOTSUPP); |
140 | } | |
141 | ||
c92339ad DB |
142 | int |
143 | ct_dpif_set_maxconns(struct dpif *dpif, uint32_t maxconns) | |
144 | { | |
145 | return (dpif->dpif_class->ct_set_maxconns | |
146 | ? dpif->dpif_class->ct_set_maxconns(dpif, maxconns) | |
147 | : EOPNOTSUPP); | |
148 | } | |
149 | ||
150 | int | |
151 | ct_dpif_get_maxconns(struct dpif *dpif, uint32_t *maxconns) | |
152 | { | |
153 | return (dpif->dpif_class->ct_get_maxconns | |
154 | ? dpif->dpif_class->ct_get_maxconns(dpif, maxconns) | |
155 | : EOPNOTSUPP); | |
156 | } | |
157 | ||
875075b3 DB |
158 | int |
159 | ct_dpif_get_nconns(struct dpif *dpif, uint32_t *nconns) | |
160 | { | |
161 | return (dpif->dpif_class->ct_get_nconns | |
162 | ? dpif->dpif_class->ct_get_nconns(dpif, nconns) | |
163 | : EOPNOTSUPP); | |
164 | } | |
165 | ||
64207120 DB |
166 | int |
167 | ct_dpif_set_tcp_seq_chk(struct dpif *dpif, bool enabled) | |
168 | { | |
169 | return (dpif->dpif_class->ct_set_tcp_seq_chk | |
170 | ? dpif->dpif_class->ct_set_tcp_seq_chk(dpif, enabled) | |
171 | : EOPNOTSUPP); | |
172 | } | |
173 | ||
174 | int | |
175 | ct_dpif_get_tcp_seq_chk(struct dpif *dpif, bool *enabled) | |
176 | { | |
177 | return (dpif->dpif_class->ct_get_tcp_seq_chk | |
178 | ? dpif->dpif_class->ct_get_tcp_seq_chk(dpif, enabled) | |
179 | : EOPNOTSUPP); | |
180 | } | |
181 | ||
cd015a11 YHW |
182 | int |
183 | ct_dpif_set_limits(struct dpif *dpif, const uint32_t *default_limit, | |
184 | const struct ovs_list *zone_limits) | |
185 | { | |
186 | return (dpif->dpif_class->ct_set_limits | |
187 | ? dpif->dpif_class->ct_set_limits(dpif, default_limit, | |
188 | zone_limits) | |
189 | : EOPNOTSUPP); | |
190 | } | |
191 | ||
192 | int | |
193 | ct_dpif_get_limits(struct dpif *dpif, uint32_t *default_limit, | |
194 | const struct ovs_list *zone_limits_in, | |
195 | struct ovs_list *zone_limits_out) | |
196 | { | |
197 | return (dpif->dpif_class->ct_get_limits | |
198 | ? dpif->dpif_class->ct_get_limits(dpif, default_limit, | |
199 | zone_limits_in, | |
200 | zone_limits_out) | |
201 | : EOPNOTSUPP); | |
202 | } | |
203 | ||
204 | int | |
205 | ct_dpif_del_limits(struct dpif *dpif, const struct ovs_list *zone_limits) | |
206 | { | |
207 | return (dpif->dpif_class->ct_del_limits | |
208 | ? dpif->dpif_class->ct_del_limits(dpif, zone_limits) | |
209 | : EOPNOTSUPP); | |
210 | } | |
211 | ||
4ea96698 DB |
212 | int |
213 | ct_dpif_ipf_set_enabled(struct dpif *dpif, bool v6, bool enable) | |
214 | { | |
215 | return (dpif->dpif_class->ipf_set_enabled | |
216 | ? dpif->dpif_class->ipf_set_enabled(dpif, v6, enable) | |
217 | : EOPNOTSUPP); | |
218 | } | |
219 | ||
220 | int | |
221 | ct_dpif_ipf_set_min_frag(struct dpif *dpif, bool v6, uint32_t min_frag) | |
222 | { | |
223 | return (dpif->dpif_class->ipf_set_min_frag | |
224 | ? dpif->dpif_class->ipf_set_min_frag(dpif, v6, min_frag) | |
225 | : EOPNOTSUPP); | |
226 | } | |
227 | ||
228 | int | |
229 | ct_dpif_ipf_set_max_nfrags(struct dpif *dpif, uint32_t max_frags) | |
230 | { | |
231 | return (dpif->dpif_class->ipf_set_max_nfrags | |
232 | ? dpif->dpif_class->ipf_set_max_nfrags(dpif, max_frags) | |
233 | : EOPNOTSUPP); | |
234 | } | |
235 | ||
236 | int ct_dpif_ipf_get_status(struct dpif *dpif, | |
237 | struct dpif_ipf_status *dpif_ipf_status) | |
238 | { | |
239 | return (dpif->dpif_class->ipf_get_status | |
240 | ? dpif->dpif_class->ipf_get_status(dpif, dpif_ipf_status) | |
241 | : EOPNOTSUPP); | |
242 | } | |
243 | ||
244 | int | |
245 | ct_dpif_ipf_dump_start(struct dpif *dpif, struct ipf_dump_ctx **dump_ctx) | |
246 | { | |
247 | return (dpif->dpif_class->ipf_dump_start | |
248 | ? dpif->dpif_class->ipf_dump_start(dpif, dump_ctx) | |
249 | : EOPNOTSUPP); | |
250 | } | |
251 | ||
252 | int | |
253 | ct_dpif_ipf_dump_next(struct dpif *dpif, void *dump_ctx, char **dump) | |
254 | { | |
255 | return (dpif->dpif_class->ipf_dump_next | |
256 | ? dpif->dpif_class->ipf_dump_next(dpif, dump_ctx, dump) | |
257 | : EOPNOTSUPP); | |
258 | } | |
259 | ||
260 | int | |
261 | ct_dpif_ipf_dump_done(struct dpif *dpif, void *dump_ctx) | |
262 | { | |
263 | return (dpif->dpif_class->ipf_dump_done | |
264 | ? dpif->dpif_class->ipf_dump_done(dpif, dump_ctx) | |
265 | : EOPNOTSUPP); | |
266 | } | |
267 | ||
3948eb54 DDP |
268 | void |
269 | ct_dpif_entry_uninit(struct ct_dpif_entry *entry) | |
270 | { | |
271 | if (entry) { | |
272 | if (entry->helper.name) { | |
273 | free(entry->helper.name); | |
274 | } | |
275 | } | |
276 | } | |
277 | \f | |
278 | void | |
279 | ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds, | |
280 | bool verbose, bool print_stats) | |
281 | { | |
282 | ct_dpif_format_ipproto(ds, entry->tuple_orig.ip_proto); | |
283 | ||
284 | ds_put_cstr(ds, ",orig=("); | |
b269a122 | 285 | ct_dpif_format_tuple(ds, &entry->tuple_orig); |
3948eb54 DDP |
286 | if (print_stats) { |
287 | ct_dpif_format_counters(ds, &entry->counters_orig); | |
288 | } | |
289 | ds_put_cstr(ds, ")"); | |
290 | ||
291 | ds_put_cstr(ds, ",reply=("); | |
b269a122 | 292 | ct_dpif_format_tuple(ds, &entry->tuple_reply); |
3948eb54 DDP |
293 | if (print_stats) { |
294 | ct_dpif_format_counters(ds, &entry->counters_reply); | |
295 | } | |
296 | ds_put_cstr(ds, ")"); | |
297 | ||
298 | if (print_stats) { | |
299 | ct_dpif_format_timestamp(ds, &entry->timestamp); | |
300 | } | |
301 | if (verbose) { | |
302 | ds_put_format(ds, ",id=%"PRIu32, entry->id); | |
303 | } | |
304 | if (entry->zone) { | |
305 | ds_put_format(ds, ",zone=%"PRIu16, entry->zone); | |
306 | } | |
307 | if (verbose) { | |
308 | ct_dpif_format_flags(ds, ",status=", entry->status, | |
309 | ct_dpif_status_flags); | |
310 | } | |
311 | if (print_stats) { | |
312 | ds_put_format(ds, ",timeout=%"PRIu32, entry->timeout); | |
313 | } | |
314 | if (entry->mark) { | |
315 | ds_put_format(ds, ",mark=%"PRIu32, entry->mark); | |
316 | } | |
2ff8484b | 317 | if (!ovs_u128_is_zero(entry->labels)) { |
3948eb54 DDP |
318 | ovs_be128 value; |
319 | ||
320 | ds_put_cstr(ds, ",labels="); | |
321 | value = hton128(entry->labels); | |
322 | ds_put_hex(ds, &value, sizeof value); | |
323 | } | |
324 | ct_dpif_format_protoinfo(ds, ",protoinfo=", &entry->protoinfo, verbose); | |
325 | ct_dpif_format_helper(ds, ",helper=", &entry->helper); | |
326 | if (verbose && entry->tuple_master.l3_type != 0) { | |
327 | ds_put_cstr(ds, ",master=("); | |
b269a122 | 328 | ct_dpif_format_tuple(ds, &entry->tuple_master); |
3948eb54 DDP |
329 | ds_put_cstr(ds, ")"); |
330 | } | |
331 | } | |
332 | ||
934f54a1 | 333 | void |
3948eb54 DDP |
334 | ct_dpif_format_ipproto(struct ds *ds, uint16_t ipproto) |
335 | { | |
336 | const char *name; | |
337 | ||
338 | name = (ipproto == IPPROTO_ICMP) ? "icmp" | |
339 | : (ipproto == IPPROTO_ICMPV6) ? "icmpv6" | |
340 | : (ipproto == IPPROTO_TCP) ? "tcp" | |
341 | : (ipproto == IPPROTO_UDP) ? "udp" | |
342 | : (ipproto == IPPROTO_SCTP) ? "sctp" | |
81f97b1e JR |
343 | : (ipproto == IPPROTO_UDPLITE) ? "udplite" |
344 | : (ipproto == IPPROTO_DCCP) ? "dccp" | |
345 | : (ipproto == IPPROTO_IGMP) ? "igmp" | |
3948eb54 DDP |
346 | : NULL; |
347 | ||
348 | if (name) { | |
349 | ds_put_cstr(ds, name); | |
350 | } else { | |
351 | ds_put_format(ds, "%u", ipproto); | |
352 | } | |
353 | } | |
354 | ||
355 | static void | |
356 | ct_dpif_format_counters(struct ds *ds, const struct ct_dpif_counters *counters) | |
357 | { | |
358 | if (counters->packets || counters->bytes) { | |
359 | ds_put_format(ds, ",packets=%"PRIu64",bytes=%"PRIu64, | |
360 | counters->packets, counters->bytes); | |
361 | } | |
362 | } | |
363 | ||
364 | static void | |
365 | ct_dpif_format_timestamp(struct ds *ds, | |
366 | const struct ct_dpif_timestamp *timestamp) | |
367 | { | |
368 | if (timestamp->start || timestamp->stop) { | |
369 | ds_put_strftime_msec(ds, ",start=%Y-%m-%dT%H:%M:%S.###", | |
370 | timestamp->start / UINT64_C(1000000), false); | |
371 | if (timestamp->stop) { | |
372 | ds_put_strftime_msec(ds, ",stop=%Y-%m-%dT%H:%M:%S.###", | |
373 | timestamp->stop / UINT64_C(1000000), false); | |
374 | } | |
375 | } | |
376 | } | |
377 | ||
378 | static void | |
b269a122 | 379 | ct_dpif_format_tuple_icmp(struct ds *ds, const struct ct_dpif_tuple *tuple) |
3948eb54 | 380 | { |
b269a122 DDP |
381 | ds_put_format(ds, ",id=%u,type=%u,code=%u", ntohs(tuple->icmp_id), |
382 | tuple->icmp_type, tuple->icmp_code); | |
3948eb54 DDP |
383 | } |
384 | ||
385 | static void | |
386 | ct_dpif_format_tuple_tp(struct ds *ds, const struct ct_dpif_tuple *tuple) | |
387 | { | |
388 | ds_put_format(ds, ",sport=%u,dport=%u", | |
389 | ntohs(tuple->src_port), ntohs(tuple->dst_port)); | |
390 | } | |
391 | ||
392 | void | |
b269a122 | 393 | ct_dpif_format_tuple(struct ds *ds, const struct ct_dpif_tuple *tuple) |
3948eb54 DDP |
394 | { |
395 | if (tuple->l3_type == AF_INET) { | |
396 | ds_put_format(ds, "src="IP_FMT",dst="IP_FMT, | |
397 | IP_ARGS(tuple->src.ip), IP_ARGS(tuple->dst.ip)); | |
398 | } else if (tuple->l3_type == AF_INET6) { | |
399 | ds_put_cstr(ds, "src="); | |
400 | ipv6_format_addr(&tuple->src.in6, ds); | |
401 | ds_put_cstr(ds, ",dst="); | |
402 | ipv6_format_addr(&tuple->dst.in6, ds); | |
403 | } else { | |
404 | ds_put_format(ds, "Unsupported address family: %u. HEX:\n", | |
405 | tuple->l3_type); | |
406 | ds_put_hex_dump(ds, tuple, sizeof *tuple, 0, false); | |
407 | return; | |
408 | } | |
409 | ||
410 | if (tuple->ip_proto == IPPROTO_ICMP | |
411 | || tuple->ip_proto == IPPROTO_ICMPV6) { | |
b269a122 | 412 | ct_dpif_format_tuple_icmp(ds, tuple); |
3948eb54 DDP |
413 | } else { |
414 | ct_dpif_format_tuple_tp(ds, tuple); | |
415 | } | |
416 | } | |
417 | ||
418 | static void | |
419 | ct_dpif_format_flags(struct ds *ds, const char *title, uint32_t flags, | |
420 | const struct flags *table) | |
421 | { | |
422 | if (title) { | |
423 | ds_put_cstr(ds, title); | |
424 | } | |
425 | for (; table->name; table++) { | |
426 | if (flags & table->flag) { | |
427 | ds_put_format(ds, "%s|", table->name); | |
428 | } | |
429 | } | |
430 | ds_chomp(ds, '|'); | |
431 | } | |
432 | ||
433 | static const struct flags tcp_flags[] = { | |
434 | #define CT_DPIF_TCP_FLAG(FLAG) { CT_DPIF_TCPF_##FLAG, #FLAG }, | |
435 | CT_DPIF_TCP_FLAGS | |
436 | #undef CT_DPIF_TCP_FLAG | |
437 | { 0, NULL } /* End marker. */ | |
438 | }; | |
439 | ||
440 | const char *ct_dpif_tcp_state_string[] = { | |
441 | #define CT_DPIF_TCP_STATE(STATE) [CT_DPIF_TCPS_##STATE] = #STATE, | |
442 | CT_DPIF_TCP_STATES | |
443 | #undef CT_DPIF_TCP_STATE | |
444 | }; | |
445 | ||
93346d88 AC |
446 | const char *ct_dpif_sctp_state_string[] = { |
447 | #define CT_DPIF_SCTP_STATE(STATE) [CT_DPIF_SCTP_STATE_##STATE] = #STATE, | |
448 | CT_DPIF_SCTP_STATES | |
449 | #undef CT_DPIF_SCTP_STATE | |
450 | }; | |
451 | ||
3948eb54 DDP |
452 | static void |
453 | ct_dpif_format_enum__(struct ds *ds, const char *title, unsigned int state, | |
454 | const char *names[], unsigned int max) | |
455 | { | |
456 | if (title) { | |
457 | ds_put_cstr(ds, title); | |
458 | } | |
459 | if (state < max) { | |
460 | ds_put_cstr(ds, names[state]); | |
461 | } else { | |
462 | ds_put_format(ds, "[%u]", state); | |
463 | } | |
464 | } | |
465 | ||
466 | #define ct_dpif_format_enum(DS, TITLE, STATE, NAMES) \ | |
467 | ct_dpif_format_enum__((DS), (TITLE), (STATE), (NAMES), ARRAY_SIZE(NAMES)) | |
468 | ||
469 | static uint8_t | |
470 | coalesce_tcp_state(uint8_t state) | |
471 | { | |
472 | /* The Linux kernel connection tracker and the userspace view the | |
473 | * tcp states differently in some situations. If we're formatting | |
474 | * the entry without being verbose, it is worth to adjust the | |
475 | * differences, to ease writing testcases. */ | |
476 | switch (state) { | |
477 | case CT_DPIF_TCPS_FIN_WAIT_2: | |
478 | return CT_DPIF_TCPS_TIME_WAIT; | |
479 | case CT_DPIF_TCPS_SYN_RECV: | |
480 | return CT_DPIF_TCPS_ESTABLISHED; | |
481 | default: | |
482 | return state; | |
483 | } | |
484 | } | |
485 | ||
486 | static void | |
487 | ct_dpif_format_protoinfo_tcp(struct ds *ds, | |
488 | const struct ct_dpif_protoinfo *protoinfo) | |
489 | { | |
490 | uint8_t tcp_state; | |
491 | ||
492 | /* We keep two separate tcp states, but we print just one. The Linux | |
493 | * kernel connection tracker internally keeps only one state, so | |
494 | * 'state_orig' and 'state_reply', will be the same. */ | |
495 | tcp_state = MAX(protoinfo->tcp.state_orig, protoinfo->tcp.state_reply); | |
496 | ||
497 | tcp_state = coalesce_tcp_state(tcp_state); | |
498 | ct_dpif_format_enum(ds, "state=", tcp_state, ct_dpif_tcp_state_string); | |
499 | } | |
500 | ||
501 | static void | |
502 | ct_dpif_format_protoinfo_tcp_verbose(struct ds *ds, | |
503 | const struct ct_dpif_protoinfo *protoinfo) | |
504 | { | |
505 | ct_dpif_format_enum(ds, "state_orig=", protoinfo->tcp.state_orig, | |
506 | ct_dpif_tcp_state_string); | |
507 | ct_dpif_format_enum(ds, ",state_reply=", protoinfo->tcp.state_reply, | |
508 | ct_dpif_tcp_state_string); | |
509 | ||
510 | if (protoinfo->tcp.wscale_orig || protoinfo->tcp.wscale_reply) { | |
511 | ds_put_format(ds, ",wscale_orig=%u,wscale_reply=%u", | |
512 | protoinfo->tcp.wscale_orig, | |
513 | protoinfo->tcp.wscale_reply); | |
514 | } | |
515 | ct_dpif_format_flags(ds, ",flags_orig=", protoinfo->tcp.flags_orig, | |
516 | tcp_flags); | |
517 | ct_dpif_format_flags(ds, ",flags_reply=", protoinfo->tcp.flags_reply, | |
518 | tcp_flags); | |
519 | } | |
520 | ||
93346d88 AC |
521 | static void |
522 | ct_dpif_format_protoinfo_sctp(struct ds *ds, | |
523 | const struct ct_dpif_protoinfo *protoinfo) | |
524 | { | |
525 | ct_dpif_format_enum(ds, "state=", protoinfo->sctp.state, | |
526 | ct_dpif_sctp_state_string); | |
527 | ds_put_format(ds, ",vtag_orig=%" PRIu32 ",vtag_reply=%" PRIu32, | |
528 | protoinfo->sctp.vtag_orig, protoinfo->sctp.vtag_reply); | |
529 | } | |
530 | ||
3948eb54 DDP |
531 | static void |
532 | ct_dpif_format_protoinfo(struct ds *ds, const char *title, | |
533 | const struct ct_dpif_protoinfo *protoinfo, | |
534 | bool verbose) | |
535 | { | |
536 | if (protoinfo->proto != 0) { | |
537 | if (title) { | |
538 | ds_put_format(ds, "%s(", title); | |
539 | } | |
540 | switch (protoinfo->proto) { | |
541 | case IPPROTO_TCP: | |
542 | if (verbose) { | |
543 | ct_dpif_format_protoinfo_tcp_verbose(ds, protoinfo); | |
544 | } else { | |
545 | ct_dpif_format_protoinfo_tcp(ds, protoinfo); | |
546 | } | |
547 | break; | |
93346d88 AC |
548 | case IPPROTO_SCTP: |
549 | ct_dpif_format_protoinfo_sctp(ds, protoinfo); | |
550 | break; | |
3948eb54 DDP |
551 | } |
552 | if (title) { | |
553 | ds_put_cstr(ds, ")"); | |
554 | } | |
555 | } | |
556 | } | |
557 | ||
558 | static void | |
559 | ct_dpif_format_helper(struct ds *ds, const char *title, | |
560 | const struct ct_dpif_helper *helper) | |
561 | { | |
562 | if (helper->name) { | |
563 | if (title) { | |
564 | ds_put_cstr(ds, title); | |
565 | } | |
566 | ds_put_cstr(ds, helper->name); | |
567 | } | |
568 | } | |
8a0d9d85 FA |
569 | |
570 | uint8_t | |
571 | ct_dpif_coalesce_tcp_state(uint8_t state) | |
572 | { | |
573 | return coalesce_tcp_state(state); | |
574 | } | |
575 | ||
576 | void | |
577 | ct_dpif_format_tcp_stat(struct ds * ds, int tcp_state, int conn_per_state) | |
578 | { | |
579 | ct_dpif_format_enum(ds, "\t [", tcp_state, ct_dpif_tcp_state_string); | |
580 | ds_put_cstr(ds, "]"); | |
581 | ds_put_format(ds, "=%u", conn_per_state); | |
582 | } | |
c43a1331 YHW |
583 | |
584 | /* Parses a specification of a conntrack 5-tuple from 's' into 'tuple'. | |
585 | * Returns true on success. Otherwise, returns false and puts the error | |
586 | * message in 'ds'. */ | |
587 | bool | |
588 | ct_dpif_parse_tuple(struct ct_dpif_tuple *tuple, const char *s, struct ds *ds) | |
589 | { | |
590 | char *pos, *key, *value, *copy; | |
591 | memset(tuple, 0, sizeof *tuple); | |
592 | ||
593 | pos = copy = xstrdup(s); | |
594 | while (ofputil_parse_key_value(&pos, &key, &value)) { | |
595 | if (!*value) { | |
596 | ds_put_format(ds, "field %s missing value", key); | |
597 | goto error; | |
598 | } | |
599 | ||
600 | if (!strcmp(key, "ct_nw_src") || !strcmp(key, "ct_nw_dst")) { | |
601 | if (tuple->l3_type && tuple->l3_type != AF_INET) { | |
602 | ds_put_cstr(ds, "L3 type set multiple times"); | |
603 | goto error; | |
604 | } else { | |
605 | tuple->l3_type = AF_INET; | |
606 | } | |
607 | if (!ip_parse(value, key[6] == 's' ? &tuple->src.ip : | |
608 | &tuple->dst.ip)) { | |
609 | goto error_with_msg; | |
610 | } | |
611 | } else if (!strcmp(key, "ct_ipv6_src") || | |
612 | !strcmp(key, "ct_ipv6_dst")) { | |
613 | if (tuple->l3_type && tuple->l3_type != AF_INET6) { | |
614 | ds_put_cstr(ds, "L3 type set multiple times"); | |
615 | goto error; | |
616 | } else { | |
617 | tuple->l3_type = AF_INET6; | |
618 | } | |
619 | if (!ipv6_parse(value, key[8] == 's' ? &tuple->src.in6 : | |
620 | &tuple->dst.in6)) { | |
621 | goto error_with_msg; | |
622 | } | |
623 | } else if (!strcmp(key, "ct_nw_proto")) { | |
624 | char *err = str_to_u8(value, key, &tuple->ip_proto); | |
625 | if (err) { | |
626 | free(err); | |
627 | goto error_with_msg; | |
628 | } | |
629 | } else if (!strcmp(key, "ct_tp_src") || !strcmp(key,"ct_tp_dst")) { | |
630 | uint16_t port; | |
631 | char *err = str_to_u16(value, key, &port); | |
632 | if (err) { | |
633 | free(err); | |
634 | goto error_with_msg; | |
635 | } | |
636 | if (key[6] == 's') { | |
637 | tuple->src_port = htons(port); | |
638 | } else { | |
639 | tuple->dst_port = htons(port); | |
640 | } | |
641 | } else if (!strcmp(key, "icmp_type") || !strcmp(key, "icmp_code") || | |
642 | !strcmp(key, "icmp_id") ) { | |
643 | if (tuple->ip_proto != IPPROTO_ICMP && | |
644 | tuple->ip_proto != IPPROTO_ICMPV6) { | |
645 | ds_put_cstr(ds, "invalid L4 fields"); | |
646 | goto error; | |
647 | } | |
648 | uint16_t icmp_id; | |
649 | char *err; | |
650 | if (key[5] == 't') { | |
651 | err = str_to_u8(value, key, &tuple->icmp_type); | |
652 | } else if (key[5] == 'c') { | |
653 | err = str_to_u8(value, key, &tuple->icmp_code); | |
654 | } else { | |
655 | err = str_to_u16(value, key, &icmp_id); | |
656 | tuple->icmp_id = htons(icmp_id); | |
657 | } | |
658 | if (err) { | |
659 | free(err); | |
660 | goto error_with_msg; | |
661 | } | |
662 | } else { | |
663 | ds_put_format(ds, "invalid conntrack tuple field: %s", key); | |
664 | goto error; | |
665 | } | |
666 | } | |
667 | ||
668 | if (ipv6_is_zero(&tuple->src.in6) || ipv6_is_zero(&tuple->dst.in6) || | |
669 | !tuple->ip_proto) { | |
670 | /* icmp_type, icmp_code, and icmp_id can be 0. */ | |
671 | if (tuple->ip_proto != IPPROTO_ICMP && | |
672 | tuple->ip_proto != IPPROTO_ICMPV6) { | |
673 | if (!tuple->src_port || !tuple->dst_port) { | |
674 | ds_put_cstr(ds, "at least one of the conntrack 5-tuple fields " | |
675 | "is missing."); | |
676 | goto error; | |
677 | } | |
678 | } | |
679 | } | |
680 | ||
681 | free(copy); | |
682 | return true; | |
683 | ||
684 | error_with_msg: | |
685 | ds_put_format(ds, "failed to parse field %s", key); | |
686 | error: | |
687 | free(copy); | |
688 | return false; | |
689 | } | |
9bc339b6 YHW |
690 | |
691 | void | |
692 | ct_dpif_push_zone_limit(struct ovs_list *zone_limits, uint16_t zone, | |
693 | uint32_t limit, uint32_t count) | |
694 | { | |
695 | struct ct_dpif_zone_limit *zone_limit = xmalloc(sizeof *zone_limit); | |
696 | zone_limit->zone = zone; | |
697 | zone_limit->limit = limit; | |
698 | zone_limit->count = count; | |
699 | ovs_list_push_back(zone_limits, &zone_limit->node); | |
700 | } | |
701 | ||
702 | void | |
703 | ct_dpif_free_zone_limits(struct ovs_list *zone_limits) | |
704 | { | |
705 | while (!ovs_list_is_empty(zone_limits)) { | |
706 | struct ovs_list *entry = ovs_list_pop_front(zone_limits); | |
707 | struct ct_dpif_zone_limit *cdzl; | |
708 | cdzl = CONTAINER_OF(entry, struct ct_dpif_zone_limit, node); | |
709 | free(cdzl); | |
710 | } | |
711 | } | |
4eeec031 YHW |
712 | |
713 | /* Parses a specification of a conntrack zone limit from 's' into '*pzone' | |
714 | * and '*plimit'. Returns true on success. Otherwise, returns false and | |
715 | * and puts the error message in 'ds'. */ | |
716 | bool | |
717 | ct_dpif_parse_zone_limit_tuple(const char *s, uint16_t *pzone, | |
718 | uint32_t *plimit, struct ds *ds) | |
719 | { | |
720 | char *pos, *key, *value, *copy, *err; | |
721 | bool parsed_limit = false, parsed_zone = false; | |
722 | ||
723 | pos = copy = xstrdup(s); | |
724 | while (ofputil_parse_key_value(&pos, &key, &value)) { | |
725 | if (!*value) { | |
726 | ds_put_format(ds, "field %s missing value", key); | |
727 | goto error; | |
728 | } | |
729 | ||
730 | if (!strcmp(key, "zone")) { | |
731 | err = str_to_u16(value, key, pzone); | |
732 | if (err) { | |
733 | free(err); | |
734 | goto error_with_msg; | |
735 | } | |
736 | parsed_zone = true; | |
737 | } else if (!strcmp(key, "limit")) { | |
738 | err = str_to_u32(value, plimit); | |
739 | if (err) { | |
740 | free(err); | |
741 | goto error_with_msg; | |
742 | } | |
743 | parsed_limit = true; | |
744 | } else { | |
745 | ds_put_format(ds, "invalid zone limit field: %s", key); | |
746 | goto error; | |
747 | } | |
748 | } | |
749 | ||
750 | if (!parsed_zone || !parsed_limit) { | |
751 | ds_put_format(ds, "failed to parse zone limit"); | |
752 | goto error; | |
753 | } | |
754 | ||
755 | free(copy); | |
756 | return true; | |
757 | ||
758 | error_with_msg: | |
759 | ds_put_format(ds, "failed to parse field %s", key); | |
760 | error: | |
761 | free(copy); | |
762 | return false; | |
763 | } | |
764 | ||
765 | void | |
766 | ct_dpif_format_zone_limits(uint32_t default_limit, | |
767 | const struct ovs_list *zone_limits, struct ds *ds) | |
768 | { | |
769 | struct ct_dpif_zone_limit *zone_limit; | |
770 | ||
771 | ds_put_format(ds, "default limit=%"PRIu32, default_limit); | |
772 | ||
773 | LIST_FOR_EACH (zone_limit, node, zone_limits) { | |
774 | ds_put_format(ds, "\nzone=%"PRIu16, zone_limit->zone); | |
775 | ds_put_format(ds, ",limit=%"PRIu32, zone_limit->limit); | |
776 | ds_put_format(ds, ",count=%"PRIu32, zone_limit->count); | |
777 | } | |
778 | } | |
1f161318 YHW |
779 | |
780 | static const char *const ct_dpif_tp_attr_string[] = { | |
781 | #define CT_DPIF_TP_TCP_ATTR(ATTR) \ | |
782 | [CT_DPIF_TP_ATTR_TCP_##ATTR] = "TCP_"#ATTR, | |
783 | CT_DPIF_TP_TCP_ATTRS | |
784 | #undef CT_DPIF_TP_TCP_ATTR | |
785 | #define CT_DPIF_TP_UDP_ATTR(ATTR) \ | |
786 | [CT_DPIF_TP_ATTR_UDP_##ATTR] = "UDP_"#ATTR, | |
787 | CT_DPIF_TP_UDP_ATTRS | |
788 | #undef CT_DPIF_TP_UDP_ATTR | |
789 | #define CT_DPIF_TP_ICMP_ATTR(ATTR) \ | |
790 | [CT_DPIF_TP_ATTR_ICMP_##ATTR] = "ICMP_"#ATTR, | |
791 | CT_DPIF_TP_ICMP_ATTRS | |
792 | #undef CT_DPIF_TP_ICMP_ATTR | |
793 | }; | |
794 | ||
795 | static bool | |
796 | ct_dpif_set_timeout_policy_attr(struct ct_dpif_timeout_policy *tp, | |
797 | uint32_t attr, uint32_t value) | |
798 | { | |
799 | if (tp->present & (1 << attr) && tp->attrs[attr] == value) { | |
800 | return false; | |
801 | } | |
802 | tp->attrs[attr] = value; | |
803 | tp->present |= 1 << attr; | |
804 | return true; | |
805 | } | |
806 | ||
807 | /* Sets a timeout value identified by '*name' to 'value'. | |
808 | * Returns true if the attribute is changed */ | |
809 | bool | |
810 | ct_dpif_set_timeout_policy_attr_by_name(struct ct_dpif_timeout_policy *tp, | |
811 | const char *name, uint32_t value) | |
812 | { | |
813 | for (uint32_t i = 0; i < CT_DPIF_TP_ATTR_MAX; ++i) { | |
814 | if (!strcasecmp(name, ct_dpif_tp_attr_string[i])) { | |
815 | return ct_dpif_set_timeout_policy_attr(tp, i, value); | |
816 | } | |
817 | } | |
818 | return false; | |
819 | } | |
820 | ||
821 | bool | |
822 | ct_dpif_timeout_policy_support_ipproto(uint8_t ipproto) | |
823 | { | |
824 | if (ipproto == IPPROTO_TCP || ipproto == IPPROTO_UDP || | |
825 | ipproto == IPPROTO_ICMP || ipproto == IPPROTO_ICMPV6) { | |
826 | return true; | |
827 | } | |
828 | return false; | |
829 | } | |
830 | ||
831 | int | |
832 | ct_dpif_set_timeout_policy(struct dpif *dpif, | |
833 | const struct ct_dpif_timeout_policy *tp) | |
834 | { | |
835 | return (dpif->dpif_class->ct_set_timeout_policy | |
836 | ? dpif->dpif_class->ct_set_timeout_policy(dpif, tp) | |
837 | : EOPNOTSUPP); | |
838 | } | |
839 | ||
840 | int | |
841 | ct_dpif_del_timeout_policy(struct dpif *dpif, uint32_t tp_id) | |
842 | { | |
843 | return (dpif->dpif_class->ct_del_timeout_policy | |
844 | ? dpif->dpif_class->ct_del_timeout_policy(dpif, tp_id) | |
845 | : EOPNOTSUPP); | |
846 | } | |
847 | ||
848 | int | |
849 | ct_dpif_get_timeout_policy(struct dpif *dpif, uint32_t tp_id, | |
850 | struct ct_dpif_timeout_policy *tp) | |
851 | { | |
852 | return (dpif->dpif_class->ct_get_timeout_policy | |
853 | ? dpif->dpif_class->ct_get_timeout_policy( | |
854 | dpif, tp_id, tp) : EOPNOTSUPP); | |
855 | } | |
856 | ||
857 | int | |
858 | ct_dpif_timeout_policy_dump_start(struct dpif *dpif, void **statep) | |
859 | { | |
860 | return (dpif->dpif_class->ct_timeout_policy_dump_start | |
861 | ? dpif->dpif_class->ct_timeout_policy_dump_start(dpif, statep) | |
862 | : EOPNOTSUPP); | |
863 | } | |
864 | ||
865 | int | |
866 | ct_dpif_timeout_policy_dump_next(struct dpif *dpif, void *state, | |
867 | struct ct_dpif_timeout_policy *tp) | |
868 | { | |
869 | return (dpif->dpif_class->ct_timeout_policy_dump_next | |
870 | ? dpif->dpif_class->ct_timeout_policy_dump_next(dpif, state, tp) | |
871 | : EOPNOTSUPP); | |
872 | } | |
873 | ||
874 | int | |
875 | ct_dpif_timeout_policy_dump_done(struct dpif *dpif, void *state) | |
876 | { | |
877 | return (dpif->dpif_class->ct_timeout_policy_dump_done | |
878 | ? dpif->dpif_class->ct_timeout_policy_dump_done(dpif, state) | |
879 | : EOPNOTSUPP); | |
880 | } | |
187bb41f YHW |
881 | |
882 | int | |
883 | ct_dpif_get_timeout_policy_name(struct dpif *dpif, uint32_t tp_id, | |
884 | uint16_t dl_type, uint8_t nw_proto, | |
885 | char **tp_name, bool *is_generic) | |
886 | { | |
887 | return (dpif->dpif_class->ct_get_timeout_policy_name | |
888 | ? dpif->dpif_class->ct_get_timeout_policy_name( | |
889 | dpif, tp_id, dl_type, nw_proto, tp_name, is_generic) | |
890 | : EOPNOTSUPP); | |
891 | } |