]> git.proxmox.com Git - mirror_ovs.git/blob - lib/ct-dpif.c
tests: windows ovsdb online compact
[mirror_ovs.git] / lib / ct-dpif.c
1 /*
2 * Copyright (c) 2015 Nicira, Inc.
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>
18 #include "dpif-provider.h"
19
20 #include <errno.h>
21
22 #include "ct-dpif.h"
23 #include "openvswitch/vlog.h"
24
25 VLOG_DEFINE_THIS_MODULE(ct_dpif);
26
27 /* Declarations for conntrack entry formatting. */
28 struct flags {
29 uint32_t flag;
30 const char *name;
31 };
32
33 static void ct_dpif_format_ipproto(struct ds *, uint16_t ipproto);
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
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,
68 const uint16_t *zone)
69 {
70 int err;
71
72 err = (dpif->dpif_class->ct_dump_start
73 ? dpif->dpif_class->ct_dump_start(dpif, dump, zone)
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
113 /* Flush the entries in the connection tracker used by 'dpif'.
114 *
115 * If 'zone' is not NULL, flush only the entries in '*zone'. */
116 int
117 ct_dpif_flush(struct dpif *dpif, const uint16_t *zone)
118 {
119 if (zone) {
120 VLOG_DBG("%s: ct_flush: %"PRIu16, dpif_name(dpif), *zone);
121 } else {
122 VLOG_DBG("%s: ct_flush: <all>", dpif_name(dpif));
123 }
124
125 return (dpif->dpif_class->ct_flush
126 ? dpif->dpif_class->ct_flush(dpif, zone)
127 : EOPNOTSUPP);
128 }
129
130 void
131 ct_dpif_entry_uninit(struct ct_dpif_entry *entry)
132 {
133 if (entry) {
134 if (entry->helper.name) {
135 free(entry->helper.name);
136 }
137 }
138 }
139 \f
140 void
141 ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds,
142 bool verbose, bool print_stats)
143 {
144 ct_dpif_format_ipproto(ds, entry->tuple_orig.ip_proto);
145
146 ds_put_cstr(ds, ",orig=(");
147 ct_dpif_format_tuple(ds, &entry->tuple_orig);
148 if (print_stats) {
149 ct_dpif_format_counters(ds, &entry->counters_orig);
150 }
151 ds_put_cstr(ds, ")");
152
153 ds_put_cstr(ds, ",reply=(");
154 ct_dpif_format_tuple(ds, &entry->tuple_reply);
155 if (print_stats) {
156 ct_dpif_format_counters(ds, &entry->counters_reply);
157 }
158 ds_put_cstr(ds, ")");
159
160 if (print_stats) {
161 ct_dpif_format_timestamp(ds, &entry->timestamp);
162 }
163 if (verbose) {
164 ds_put_format(ds, ",id=%"PRIu32, entry->id);
165 }
166 if (entry->zone) {
167 ds_put_format(ds, ",zone=%"PRIu16, entry->zone);
168 }
169 if (verbose) {
170 ct_dpif_format_flags(ds, ",status=", entry->status,
171 ct_dpif_status_flags);
172 }
173 if (print_stats) {
174 ds_put_format(ds, ",timeout=%"PRIu32, entry->timeout);
175 }
176 if (entry->mark) {
177 ds_put_format(ds, ",mark=%"PRIu32, entry->mark);
178 }
179 if (!ovs_u128_is_zero(entry->labels)) {
180 ovs_be128 value;
181
182 ds_put_cstr(ds, ",labels=");
183 value = hton128(entry->labels);
184 ds_put_hex(ds, &value, sizeof value);
185 }
186 ct_dpif_format_protoinfo(ds, ",protoinfo=", &entry->protoinfo, verbose);
187 ct_dpif_format_helper(ds, ",helper=", &entry->helper);
188 if (verbose && entry->tuple_master.l3_type != 0) {
189 ds_put_cstr(ds, ",master=(");
190 ct_dpif_format_tuple(ds, &entry->tuple_master);
191 ds_put_cstr(ds, ")");
192 }
193 }
194
195 static void
196 ct_dpif_format_ipproto(struct ds *ds, uint16_t ipproto)
197 {
198 const char *name;
199
200 name = (ipproto == IPPROTO_ICMP) ? "icmp"
201 : (ipproto == IPPROTO_ICMPV6) ? "icmpv6"
202 : (ipproto == IPPROTO_TCP) ? "tcp"
203 : (ipproto == IPPROTO_UDP) ? "udp"
204 : (ipproto == IPPROTO_SCTP) ? "sctp"
205 : NULL;
206
207 if (name) {
208 ds_put_cstr(ds, name);
209 } else {
210 ds_put_format(ds, "%u", ipproto);
211 }
212 }
213
214 static void
215 ct_dpif_format_counters(struct ds *ds, const struct ct_dpif_counters *counters)
216 {
217 if (counters->packets || counters->bytes) {
218 ds_put_format(ds, ",packets=%"PRIu64",bytes=%"PRIu64,
219 counters->packets, counters->bytes);
220 }
221 }
222
223 static void
224 ct_dpif_format_timestamp(struct ds *ds,
225 const struct ct_dpif_timestamp *timestamp)
226 {
227 if (timestamp->start || timestamp->stop) {
228 ds_put_strftime_msec(ds, ",start=%Y-%m-%dT%H:%M:%S.###",
229 timestamp->start / UINT64_C(1000000), false);
230 if (timestamp->stop) {
231 ds_put_strftime_msec(ds, ",stop=%Y-%m-%dT%H:%M:%S.###",
232 timestamp->stop / UINT64_C(1000000), false);
233 }
234 }
235 }
236
237 static void
238 ct_dpif_format_tuple_icmp(struct ds *ds, const struct ct_dpif_tuple *tuple)
239 {
240 ds_put_format(ds, ",id=%u,type=%u,code=%u", ntohs(tuple->icmp_id),
241 tuple->icmp_type, tuple->icmp_code);
242 }
243
244 static void
245 ct_dpif_format_tuple_tp(struct ds *ds, const struct ct_dpif_tuple *tuple)
246 {
247 ds_put_format(ds, ",sport=%u,dport=%u",
248 ntohs(tuple->src_port), ntohs(tuple->dst_port));
249 }
250
251 void
252 ct_dpif_format_tuple(struct ds *ds, const struct ct_dpif_tuple *tuple)
253 {
254 if (tuple->l3_type == AF_INET) {
255 ds_put_format(ds, "src="IP_FMT",dst="IP_FMT,
256 IP_ARGS(tuple->src.ip), IP_ARGS(tuple->dst.ip));
257 } else if (tuple->l3_type == AF_INET6) {
258 ds_put_cstr(ds, "src=");
259 ipv6_format_addr(&tuple->src.in6, ds);
260 ds_put_cstr(ds, ",dst=");
261 ipv6_format_addr(&tuple->dst.in6, ds);
262 } else {
263 ds_put_format(ds, "Unsupported address family: %u. HEX:\n",
264 tuple->l3_type);
265 ds_put_hex_dump(ds, tuple, sizeof *tuple, 0, false);
266 return;
267 }
268
269 if (tuple->ip_proto == IPPROTO_ICMP
270 || tuple->ip_proto == IPPROTO_ICMPV6) {
271 ct_dpif_format_tuple_icmp(ds, tuple);
272 } else {
273 ct_dpif_format_tuple_tp(ds, tuple);
274 }
275 }
276
277 static void
278 ct_dpif_format_flags(struct ds *ds, const char *title, uint32_t flags,
279 const struct flags *table)
280 {
281 if (title) {
282 ds_put_cstr(ds, title);
283 }
284 for (; table->name; table++) {
285 if (flags & table->flag) {
286 ds_put_format(ds, "%s|", table->name);
287 }
288 }
289 ds_chomp(ds, '|');
290 }
291
292 static const struct flags tcp_flags[] = {
293 #define CT_DPIF_TCP_FLAG(FLAG) { CT_DPIF_TCPF_##FLAG, #FLAG },
294 CT_DPIF_TCP_FLAGS
295 #undef CT_DPIF_TCP_FLAG
296 { 0, NULL } /* End marker. */
297 };
298
299 const char *ct_dpif_tcp_state_string[] = {
300 #define CT_DPIF_TCP_STATE(STATE) [CT_DPIF_TCPS_##STATE] = #STATE,
301 CT_DPIF_TCP_STATES
302 #undef CT_DPIF_TCP_STATE
303 };
304
305 static void
306 ct_dpif_format_enum__(struct ds *ds, const char *title, unsigned int state,
307 const char *names[], unsigned int max)
308 {
309 if (title) {
310 ds_put_cstr(ds, title);
311 }
312 if (state < max) {
313 ds_put_cstr(ds, names[state]);
314 } else {
315 ds_put_format(ds, "[%u]", state);
316 }
317 }
318
319 #define ct_dpif_format_enum(DS, TITLE, STATE, NAMES) \
320 ct_dpif_format_enum__((DS), (TITLE), (STATE), (NAMES), ARRAY_SIZE(NAMES))
321
322 static uint8_t
323 coalesce_tcp_state(uint8_t state)
324 {
325 /* The Linux kernel connection tracker and the userspace view the
326 * tcp states differently in some situations. If we're formatting
327 * the entry without being verbose, it is worth to adjust the
328 * differences, to ease writing testcases. */
329 switch (state) {
330 case CT_DPIF_TCPS_FIN_WAIT_2:
331 return CT_DPIF_TCPS_TIME_WAIT;
332 case CT_DPIF_TCPS_SYN_RECV:
333 return CT_DPIF_TCPS_ESTABLISHED;
334 default:
335 return state;
336 }
337 }
338
339 static void
340 ct_dpif_format_protoinfo_tcp(struct ds *ds,
341 const struct ct_dpif_protoinfo *protoinfo)
342 {
343 uint8_t tcp_state;
344
345 /* We keep two separate tcp states, but we print just one. The Linux
346 * kernel connection tracker internally keeps only one state, so
347 * 'state_orig' and 'state_reply', will be the same. */
348 tcp_state = MAX(protoinfo->tcp.state_orig, protoinfo->tcp.state_reply);
349
350 tcp_state = coalesce_tcp_state(tcp_state);
351 ct_dpif_format_enum(ds, "state=", tcp_state, ct_dpif_tcp_state_string);
352 }
353
354 static void
355 ct_dpif_format_protoinfo_tcp_verbose(struct ds *ds,
356 const struct ct_dpif_protoinfo *protoinfo)
357 {
358 ct_dpif_format_enum(ds, "state_orig=", protoinfo->tcp.state_orig,
359 ct_dpif_tcp_state_string);
360 ct_dpif_format_enum(ds, ",state_reply=", protoinfo->tcp.state_reply,
361 ct_dpif_tcp_state_string);
362
363 if (protoinfo->tcp.wscale_orig || protoinfo->tcp.wscale_reply) {
364 ds_put_format(ds, ",wscale_orig=%u,wscale_reply=%u",
365 protoinfo->tcp.wscale_orig,
366 protoinfo->tcp.wscale_reply);
367 }
368 ct_dpif_format_flags(ds, ",flags_orig=", protoinfo->tcp.flags_orig,
369 tcp_flags);
370 ct_dpif_format_flags(ds, ",flags_reply=", protoinfo->tcp.flags_reply,
371 tcp_flags);
372 }
373
374 static void
375 ct_dpif_format_protoinfo(struct ds *ds, const char *title,
376 const struct ct_dpif_protoinfo *protoinfo,
377 bool verbose)
378 {
379 if (protoinfo->proto != 0) {
380 if (title) {
381 ds_put_format(ds, "%s(", title);
382 }
383 switch (protoinfo->proto) {
384 case IPPROTO_TCP:
385 if (verbose) {
386 ct_dpif_format_protoinfo_tcp_verbose(ds, protoinfo);
387 } else {
388 ct_dpif_format_protoinfo_tcp(ds, protoinfo);
389 }
390 break;
391 }
392 if (title) {
393 ds_put_cstr(ds, ")");
394 }
395 }
396 }
397
398 static void
399 ct_dpif_format_helper(struct ds *ds, const char *title,
400 const struct ct_dpif_helper *helper)
401 {
402 if (helper->name) {
403 if (title) {
404 ds_put_cstr(ds, title);
405 }
406 ds_put_cstr(ds, helper->name);
407 }
408 }