]> git.proxmox.com Git - mirror_ovs.git/blame - lib/ct-dpif.c
Fix coding style and some typos.
[mirror_ovs.git] / lib / ct-dpif.c
CommitLineData
3948eb54
DDP
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>
2a7c4805 18#include "dpif-provider.h"
3948eb54
DDP
19
20#include <errno.h>
21
22#include "ct-dpif.h"
2a7c4805 23#include "openvswitch/vlog.h"
3948eb54 24
2a7c4805 25VLOG_DEFINE_THIS_MODULE(ct_dpif);
b77d9629 26
3948eb54
DDP
27/* Declarations for conntrack entry formatting. */
28struct flags {
29 uint32_t flag;
30 const char *name;
31};
32
33static void ct_dpif_format_ipproto(struct ds *, uint16_t ipproto);
34static void ct_dpif_format_counters(struct ds *,
35 const struct ct_dpif_counters *);
36static void ct_dpif_format_timestamp(struct ds *,
37 const struct ct_dpif_timestamp *);
38static void ct_dpif_format_flags(struct ds *, const char *title,
39 uint32_t flags, const struct flags *);
40static void ct_dpif_format_protoinfo(struct ds *, const char *title,
41 const struct ct_dpif_protoinfo *,
42 bool verbose);
43static void ct_dpif_format_helper(struct ds *, const char *title,
44 const struct ct_dpif_helper *);
45
46static 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. */
66int
67ct_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(). */
92int
93ct_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' */
103int
104ct_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
a0f7b6d5
DDP
113/* Flush the entries in the connection tracker used by 'dpif'.
114 *
115 * If 'zone' is not NULL, flush only the entries in '*zone'. */
116int
117ct_dpif_flush(struct dpif *dpif, const uint16_t *zone)
118{
2a7c4805
JP
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
a0f7b6d5
DDP
125 return (dpif->dpif_class->ct_flush
126 ? dpif->dpif_class->ct_flush(dpif, zone)
127 : EOPNOTSUPP);
128}
129
3948eb54
DDP
130void
131ct_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
140void
141ct_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=(");
b269a122 147 ct_dpif_format_tuple(ds, &entry->tuple_orig);
3948eb54
DDP
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=(");
b269a122 154 ct_dpif_format_tuple(ds, &entry->tuple_reply);
3948eb54
DDP
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 }
2ff8484b 179 if (!ovs_u128_is_zero(entry->labels)) {
3948eb54
DDP
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=(");
b269a122 190 ct_dpif_format_tuple(ds, &entry->tuple_master);
3948eb54
DDP
191 ds_put_cstr(ds, ")");
192 }
193}
194
195static void
196ct_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"
81f97b1e
JR
205 : (ipproto == IPPROTO_UDPLITE) ? "udplite"
206 : (ipproto == IPPROTO_DCCP) ? "dccp"
207 : (ipproto == IPPROTO_IGMP) ? "igmp"
3948eb54
DDP
208 : NULL;
209
210 if (name) {
211 ds_put_cstr(ds, name);
212 } else {
213 ds_put_format(ds, "%u", ipproto);
214 }
215}
216
217static void
218ct_dpif_format_counters(struct ds *ds, const struct ct_dpif_counters *counters)
219{
220 if (counters->packets || counters->bytes) {
221 ds_put_format(ds, ",packets=%"PRIu64",bytes=%"PRIu64,
222 counters->packets, counters->bytes);
223 }
224}
225
226static void
227ct_dpif_format_timestamp(struct ds *ds,
228 const struct ct_dpif_timestamp *timestamp)
229{
230 if (timestamp->start || timestamp->stop) {
231 ds_put_strftime_msec(ds, ",start=%Y-%m-%dT%H:%M:%S.###",
232 timestamp->start / UINT64_C(1000000), false);
233 if (timestamp->stop) {
234 ds_put_strftime_msec(ds, ",stop=%Y-%m-%dT%H:%M:%S.###",
235 timestamp->stop / UINT64_C(1000000), false);
236 }
237 }
238}
239
240static void
b269a122 241ct_dpif_format_tuple_icmp(struct ds *ds, const struct ct_dpif_tuple *tuple)
3948eb54 242{
b269a122
DDP
243 ds_put_format(ds, ",id=%u,type=%u,code=%u", ntohs(tuple->icmp_id),
244 tuple->icmp_type, tuple->icmp_code);
3948eb54
DDP
245}
246
247static void
248ct_dpif_format_tuple_tp(struct ds *ds, const struct ct_dpif_tuple *tuple)
249{
250 ds_put_format(ds, ",sport=%u,dport=%u",
251 ntohs(tuple->src_port), ntohs(tuple->dst_port));
252}
253
254void
b269a122 255ct_dpif_format_tuple(struct ds *ds, const struct ct_dpif_tuple *tuple)
3948eb54
DDP
256{
257 if (tuple->l3_type == AF_INET) {
258 ds_put_format(ds, "src="IP_FMT",dst="IP_FMT,
259 IP_ARGS(tuple->src.ip), IP_ARGS(tuple->dst.ip));
260 } else if (tuple->l3_type == AF_INET6) {
261 ds_put_cstr(ds, "src=");
262 ipv6_format_addr(&tuple->src.in6, ds);
263 ds_put_cstr(ds, ",dst=");
264 ipv6_format_addr(&tuple->dst.in6, ds);
265 } else {
266 ds_put_format(ds, "Unsupported address family: %u. HEX:\n",
267 tuple->l3_type);
268 ds_put_hex_dump(ds, tuple, sizeof *tuple, 0, false);
269 return;
270 }
271
272 if (tuple->ip_proto == IPPROTO_ICMP
273 || tuple->ip_proto == IPPROTO_ICMPV6) {
b269a122 274 ct_dpif_format_tuple_icmp(ds, tuple);
3948eb54
DDP
275 } else {
276 ct_dpif_format_tuple_tp(ds, tuple);
277 }
278}
279
280static void
281ct_dpif_format_flags(struct ds *ds, const char *title, uint32_t flags,
282 const struct flags *table)
283{
284 if (title) {
285 ds_put_cstr(ds, title);
286 }
287 for (; table->name; table++) {
288 if (flags & table->flag) {
289 ds_put_format(ds, "%s|", table->name);
290 }
291 }
292 ds_chomp(ds, '|');
293}
294
295static const struct flags tcp_flags[] = {
296#define CT_DPIF_TCP_FLAG(FLAG) { CT_DPIF_TCPF_##FLAG, #FLAG },
297 CT_DPIF_TCP_FLAGS
298#undef CT_DPIF_TCP_FLAG
299 { 0, NULL } /* End marker. */
300};
301
302const char *ct_dpif_tcp_state_string[] = {
303#define CT_DPIF_TCP_STATE(STATE) [CT_DPIF_TCPS_##STATE] = #STATE,
304 CT_DPIF_TCP_STATES
305#undef CT_DPIF_TCP_STATE
306};
307
308static void
309ct_dpif_format_enum__(struct ds *ds, const char *title, unsigned int state,
310 const char *names[], unsigned int max)
311{
312 if (title) {
313 ds_put_cstr(ds, title);
314 }
315 if (state < max) {
316 ds_put_cstr(ds, names[state]);
317 } else {
318 ds_put_format(ds, "[%u]", state);
319 }
320}
321
322#define ct_dpif_format_enum(DS, TITLE, STATE, NAMES) \
323 ct_dpif_format_enum__((DS), (TITLE), (STATE), (NAMES), ARRAY_SIZE(NAMES))
324
325static uint8_t
326coalesce_tcp_state(uint8_t state)
327{
328 /* The Linux kernel connection tracker and the userspace view the
329 * tcp states differently in some situations. If we're formatting
330 * the entry without being verbose, it is worth to adjust the
331 * differences, to ease writing testcases. */
332 switch (state) {
333 case CT_DPIF_TCPS_FIN_WAIT_2:
334 return CT_DPIF_TCPS_TIME_WAIT;
335 case CT_DPIF_TCPS_SYN_RECV:
336 return CT_DPIF_TCPS_ESTABLISHED;
337 default:
338 return state;
339 }
340}
341
342static void
343ct_dpif_format_protoinfo_tcp(struct ds *ds,
344 const struct ct_dpif_protoinfo *protoinfo)
345{
346 uint8_t tcp_state;
347
348 /* We keep two separate tcp states, but we print just one. The Linux
349 * kernel connection tracker internally keeps only one state, so
350 * 'state_orig' and 'state_reply', will be the same. */
351 tcp_state = MAX(protoinfo->tcp.state_orig, protoinfo->tcp.state_reply);
352
353 tcp_state = coalesce_tcp_state(tcp_state);
354 ct_dpif_format_enum(ds, "state=", tcp_state, ct_dpif_tcp_state_string);
355}
356
357static void
358ct_dpif_format_protoinfo_tcp_verbose(struct ds *ds,
359 const struct ct_dpif_protoinfo *protoinfo)
360{
361 ct_dpif_format_enum(ds, "state_orig=", protoinfo->tcp.state_orig,
362 ct_dpif_tcp_state_string);
363 ct_dpif_format_enum(ds, ",state_reply=", protoinfo->tcp.state_reply,
364 ct_dpif_tcp_state_string);
365
366 if (protoinfo->tcp.wscale_orig || protoinfo->tcp.wscale_reply) {
367 ds_put_format(ds, ",wscale_orig=%u,wscale_reply=%u",
368 protoinfo->tcp.wscale_orig,
369 protoinfo->tcp.wscale_reply);
370 }
371 ct_dpif_format_flags(ds, ",flags_orig=", protoinfo->tcp.flags_orig,
372 tcp_flags);
373 ct_dpif_format_flags(ds, ",flags_reply=", protoinfo->tcp.flags_reply,
374 tcp_flags);
375}
376
377static void
378ct_dpif_format_protoinfo(struct ds *ds, const char *title,
379 const struct ct_dpif_protoinfo *protoinfo,
380 bool verbose)
381{
382 if (protoinfo->proto != 0) {
383 if (title) {
384 ds_put_format(ds, "%s(", title);
385 }
386 switch (protoinfo->proto) {
387 case IPPROTO_TCP:
388 if (verbose) {
389 ct_dpif_format_protoinfo_tcp_verbose(ds, protoinfo);
390 } else {
391 ct_dpif_format_protoinfo_tcp(ds, protoinfo);
392 }
393 break;
394 }
395 if (title) {
396 ds_put_cstr(ds, ")");
397 }
398 }
399}
400
401static void
402ct_dpif_format_helper(struct ds *ds, const char *title,
403 const struct ct_dpif_helper *helper)
404{
405 if (helper->name) {
406 if (title) {
407 ds_put_cstr(ds, title);
408 }
409 ds_put_cstr(ds, helper->name);
410 }
411}