Branch data Line data Source code
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 : :
19 : : #include <errno.h>
20 : :
21 : : #include "ct-dpif.h"
22 : :
23 : : #include "dpif-provider.h"
24 : :
25 : : /* Declarations for conntrack entry formatting. */
26 : : struct flags {
27 : : uint32_t flag;
28 : : const char *name;
29 : : };
30 : :
31 : : static void ct_dpif_format_ipproto(struct ds *, uint16_t ipproto);
32 : : static void ct_dpif_format_counters(struct ds *,
33 : : const struct ct_dpif_counters *);
34 : : static void ct_dpif_format_timestamp(struct ds *,
35 : : const struct ct_dpif_timestamp *);
36 : : static void ct_dpif_format_flags(struct ds *, const char *title,
37 : : uint32_t flags, const struct flags *);
38 : : static void ct_dpif_format_protoinfo(struct ds *, const char *title,
39 : : const struct ct_dpif_protoinfo *,
40 : : bool verbose);
41 : : static void ct_dpif_format_helper(struct ds *, const char *title,
42 : : const struct ct_dpif_helper *);
43 : :
44 : : static const struct flags ct_dpif_status_flags[] = {
45 : : #define CT_DPIF_STATUS_FLAG(FLAG) { CT_DPIF_STATUS_##FLAG, #FLAG },
46 : : CT_DPIF_STATUS_FLAGS
47 : : #undef CT_DPIF_STATUS_FLAG
48 : : { 0, NULL } /* End marker. */
49 : : };
50 : :
51 : : /* Dumping */
52 : :
53 : : /* Start dumping the entries from the connection tracker used by 'dpif'.
54 : : *
55 : : * 'dump' must be the address of a pointer to a struct ct_dpif_dump_state,
56 : : * which should be passed (unaltered) to ct_dpif_dump_{next,done}().
57 : : *
58 : : * If 'zone' is not NULL, it should point to an integer identifing a
59 : : * conntrack zone to which the dump will be limited. If it is NULL,
60 : : * conntrack entries from all zones will be dumped.
61 : : *
62 : : * If there has been a problem the function returns a non-zero value
63 : : * that represents the error. Otherwise it returns zero. */
64 : : int
65 : 69 : ct_dpif_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump,
66 : : const uint16_t *zone)
67 : : {
68 : : int err;
69 : :
70 : 138 : err = (dpif->dpif_class->ct_dump_start
71 : 69 : ? dpif->dpif_class->ct_dump_start(dpif, dump, zone)
72 [ + - ]: 69 : : EOPNOTSUPP);
73 : :
74 [ + - ]: 69 : if (!err) {
75 : 69 : (*dump)->dpif = dpif;
76 : : }
77 : :
78 : 69 : return err;
79 : : }
80 : :
81 : : /* Dump one connection from a tracker, and put it in 'entry'.
82 : : *
83 : : * 'dump' should have been initialized by ct_dpif_dump_start().
84 : : *
85 : : * The function returns 0, if an entry has been dumped succesfully.
86 : : * Otherwise it returns a non-zero value which can be:
87 : : * - EOF: meaning that there are no more entries to dump.
88 : : * - an error value.
89 : : * In both cases, the user should call ct_dpif_dump_done(). */
90 : : int
91 : 503 : ct_dpif_dump_next(struct ct_dpif_dump_state *dump, struct ct_dpif_entry *entry)
92 : : {
93 : 503 : struct dpif *dpif = dump->dpif;
94 : :
95 : 503 : return (dpif->dpif_class->ct_dump_next
96 : 503 : ? dpif->dpif_class->ct_dump_next(dpif, dump, entry)
97 [ + - ]: 503 : : EOPNOTSUPP);
98 : : }
99 : :
100 : : /* Free resources used by 'dump' */
101 : : int
102 : 69 : ct_dpif_dump_done(struct ct_dpif_dump_state *dump)
103 : : {
104 : 69 : struct dpif *dpif = dump->dpif;
105 : :
106 : 69 : return (dpif->dpif_class->ct_dump_done
107 : 69 : ? dpif->dpif_class->ct_dump_done(dpif, dump)
108 [ + - ]: 69 : : EOPNOTSUPP);
109 : : }
110 : :
111 : : /* Flush the entries in the connection tracker used by 'dpif'.
112 : : *
113 : : * If 'zone' is not NULL, flush only the entries in '*zone'. */
114 : : int
115 : 8 : ct_dpif_flush(struct dpif *dpif, const uint16_t *zone)
116 : : {
117 : 8 : return (dpif->dpif_class->ct_flush
118 : 8 : ? dpif->dpif_class->ct_flush(dpif, zone)
119 [ + - ]: 8 : : EOPNOTSUPP);
120 : : }
121 : :
122 : : void
123 : 434 : ct_dpif_entry_uninit(struct ct_dpif_entry *entry)
124 : : {
125 [ + - ]: 434 : if (entry) {
126 [ + + ]: 434 : if (entry->helper.name) {
127 : 14 : free(entry->helper.name);
128 : : }
129 : : }
130 : 434 : }
131 : :
132 : : void
133 : 434 : ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds,
134 : : bool verbose, bool print_stats)
135 : : {
136 : 434 : ct_dpif_format_ipproto(ds, entry->tuple_orig.ip_proto);
137 : :
138 : 434 : ds_put_cstr(ds, ",orig=(");
139 : 434 : ct_dpif_format_tuple(ds, &entry->tuple_orig);
140 [ - + ]: 434 : if (print_stats) {
141 : 0 : ct_dpif_format_counters(ds, &entry->counters_orig);
142 : : }
143 : 434 : ds_put_cstr(ds, ")");
144 : :
145 : 434 : ds_put_cstr(ds, ",reply=(");
146 : 434 : ct_dpif_format_tuple(ds, &entry->tuple_reply);
147 [ - + ]: 434 : if (print_stats) {
148 : 0 : ct_dpif_format_counters(ds, &entry->counters_reply);
149 : : }
150 : 434 : ds_put_cstr(ds, ")");
151 : :
152 [ - + ]: 434 : if (print_stats) {
153 : 0 : ct_dpif_format_timestamp(ds, &entry->timestamp);
154 : : }
155 [ - + ]: 434 : if (verbose) {
156 : 0 : ds_put_format(ds, ",id=%"PRIu32, entry->id);
157 : : }
158 [ + + ]: 434 : if (entry->zone) {
159 : 318 : ds_put_format(ds, ",zone=%"PRIu16, entry->zone);
160 : : }
161 [ - + ]: 434 : if (verbose) {
162 : 0 : ct_dpif_format_flags(ds, ",status=", entry->status,
163 : : ct_dpif_status_flags);
164 : : }
165 [ - + ]: 434 : if (print_stats) {
166 : 0 : ds_put_format(ds, ",timeout=%"PRIu32, entry->timeout);
167 : : }
168 [ + + ]: 434 : if (entry->mark) {
169 : 29 : ds_put_format(ds, ",mark=%"PRIu32, entry->mark);
170 : : }
171 [ + + ]: 434 : if (!ovs_u128_is_zero(entry->labels)) {
172 : : ovs_be128 value;
173 : :
174 : 6 : ds_put_cstr(ds, ",labels=");
175 : 6 : value = hton128(entry->labels);
176 : 6 : ds_put_hex(ds, &value, sizeof value);
177 : : }
178 : 434 : ct_dpif_format_protoinfo(ds, ",protoinfo=", &entry->protoinfo, verbose);
179 : 434 : ct_dpif_format_helper(ds, ",helper=", &entry->helper);
180 [ - + ][ # # ]: 434 : if (verbose && entry->tuple_master.l3_type != 0) {
181 : 0 : ds_put_cstr(ds, ",master=(");
182 : 0 : ct_dpif_format_tuple(ds, &entry->tuple_master);
183 : 0 : ds_put_cstr(ds, ")");
184 : : }
185 : 434 : }
186 : :
187 : : static void
188 : 434 : ct_dpif_format_ipproto(struct ds *ds, uint16_t ipproto)
189 : : {
190 : : const char *name;
191 : :
192 : 434 : name = (ipproto == IPPROTO_ICMP) ? "icmp"
193 [ + + ][ + + ]: 434 : : (ipproto == IPPROTO_ICMPV6) ? "icmpv6"
[ + + ][ - + ]
[ # # ]
194 : : : (ipproto == IPPROTO_TCP) ? "tcp"
195 : : : (ipproto == IPPROTO_UDP) ? "udp"
196 : : : (ipproto == IPPROTO_SCTP) ? "sctp"
197 : : : NULL;
198 : :
199 [ + - ]: 434 : if (name) {
200 : 434 : ds_put_cstr(ds, name);
201 : : } else {
202 : 0 : ds_put_format(ds, "%u", ipproto);
203 : : }
204 : 434 : }
205 : :
206 : : static void
207 : 0 : ct_dpif_format_counters(struct ds *ds, const struct ct_dpif_counters *counters)
208 : : {
209 [ # # ][ # # ]: 0 : if (counters->packets || counters->bytes) {
210 : 0 : ds_put_format(ds, ",packets=%"PRIu64",bytes=%"PRIu64,
211 : : counters->packets, counters->bytes);
212 : : }
213 : 0 : }
214 : :
215 : : static void
216 : 0 : ct_dpif_format_timestamp(struct ds *ds,
217 : : const struct ct_dpif_timestamp *timestamp)
218 : : {
219 [ # # ][ # # ]: 0 : if (timestamp->start || timestamp->stop) {
220 : 0 : ds_put_strftime_msec(ds, ",start=%Y-%m-%dT%H:%M:%S.###",
221 : 0 : timestamp->start / UINT64_C(1000000), false);
222 [ # # ]: 0 : if (timestamp->stop) {
223 : 0 : ds_put_strftime_msec(ds, ",stop=%Y-%m-%dT%H:%M:%S.###",
224 : 0 : timestamp->stop / UINT64_C(1000000), false);
225 : : }
226 : : }
227 : 0 : }
228 : :
229 : : static void
230 : 36 : ct_dpif_format_tuple_icmp(struct ds *ds, const struct ct_dpif_tuple *tuple)
231 : : {
232 : 36 : ds_put_format(ds, ",id=%u,type=%u,code=%u", ntohs(tuple->icmp_id),
233 : 72 : tuple->icmp_type, tuple->icmp_code);
234 : 36 : }
235 : :
236 : : static void
237 : 832 : ct_dpif_format_tuple_tp(struct ds *ds, const struct ct_dpif_tuple *tuple)
238 : : {
239 : 832 : ds_put_format(ds, ",sport=%u,dport=%u",
240 : 832 : ntohs(tuple->src_port), ntohs(tuple->dst_port));
241 : 832 : }
242 : :
243 : : void
244 : 868 : ct_dpif_format_tuple(struct ds *ds, const struct ct_dpif_tuple *tuple)
245 : : {
246 [ + + ]: 868 : if (tuple->l3_type == AF_INET) {
247 : 850 : ds_put_format(ds, "src="IP_FMT",dst="IP_FMT,
248 : 850 : IP_ARGS(tuple->src.ip), IP_ARGS(tuple->dst.ip));
249 [ + - ]: 18 : } else if (tuple->l3_type == AF_INET6) {
250 : 18 : ds_put_cstr(ds, "src=");
251 : 18 : ipv6_format_addr(&tuple->src.in6, ds);
252 : 18 : ds_put_cstr(ds, ",dst=");
253 : 18 : ipv6_format_addr(&tuple->dst.in6, ds);
254 : : } else {
255 : 0 : ds_put_format(ds, "Unsupported address family: %u. HEX:\n",
256 : 0 : tuple->l3_type);
257 : 0 : ds_put_hex_dump(ds, tuple, sizeof *tuple, 0, false);
258 : 0 : return;
259 : : }
260 : :
261 [ + + ]: 868 : if (tuple->ip_proto == IPPROTO_ICMP
262 [ + + ]: 836 : || tuple->ip_proto == IPPROTO_ICMPV6) {
263 : 36 : ct_dpif_format_tuple_icmp(ds, tuple);
264 : : } else {
265 : 832 : ct_dpif_format_tuple_tp(ds, tuple);
266 : : }
267 : : }
268 : :
269 : : static void
270 : 0 : ct_dpif_format_flags(struct ds *ds, const char *title, uint32_t flags,
271 : : const struct flags *table)
272 : : {
273 [ # # ]: 0 : if (title) {
274 : 0 : ds_put_cstr(ds, title);
275 : : }
276 [ # # ]: 0 : for (; table->name; table++) {
277 [ # # ]: 0 : if (flags & table->flag) {
278 : 0 : ds_put_format(ds, "%s|", table->name);
279 : : }
280 : : }
281 : 0 : ds_chomp(ds, '|');
282 : 0 : }
283 : :
284 : : static const struct flags tcp_flags[] = {
285 : : #define CT_DPIF_TCP_FLAG(FLAG) { CT_DPIF_TCPF_##FLAG, #FLAG },
286 : : CT_DPIF_TCP_FLAGS
287 : : #undef CT_DPIF_TCP_FLAG
288 : : { 0, NULL } /* End marker. */
289 : : };
290 : :
291 : : const char *ct_dpif_tcp_state_string[] = {
292 : : #define CT_DPIF_TCP_STATE(STATE) [CT_DPIF_TCPS_##STATE] = #STATE,
293 : : CT_DPIF_TCP_STATES
294 : : #undef CT_DPIF_TCP_STATE
295 : : };
296 : :
297 : : static void
298 : 414 : ct_dpif_format_enum__(struct ds *ds, const char *title, unsigned int state,
299 : : const char *names[], unsigned int max)
300 : : {
301 [ + - ]: 414 : if (title) {
302 : 414 : ds_put_cstr(ds, title);
303 : : }
304 [ + - ]: 414 : if (state < max) {
305 : 414 : ds_put_cstr(ds, names[state]);
306 : : } else {
307 : 0 : ds_put_format(ds, "[%u]", state);
308 : : }
309 : 414 : }
310 : :
311 : : #define ct_dpif_format_enum(DS, TITLE, STATE, NAMES) \
312 : : ct_dpif_format_enum__((DS), (TITLE), (STATE), (NAMES), ARRAY_SIZE(NAMES))
313 : :
314 : : static uint8_t
315 : 414 : coalesce_tcp_state(uint8_t state)
316 : : {
317 : : /* The Linux kernel connection tracker and the userspace view the
318 : : * tcp states differently in some situations. If we're formatting
319 : : * the entry without being verbose, it is worth to adjust the
320 : : * differences, to ease writing testcases. */
321 [ + + + ]: 414 : switch (state) {
322 : : case CT_DPIF_TCPS_FIN_WAIT_2:
323 : 16 : return CT_DPIF_TCPS_TIME_WAIT;
324 : : case CT_DPIF_TCPS_SYN_RECV:
325 : 8 : return CT_DPIF_TCPS_ESTABLISHED;
326 : : default:
327 : 390 : return state;
328 : : }
329 : : }
330 : :
331 : : static void
332 : 414 : ct_dpif_format_protoinfo_tcp(struct ds *ds,
333 : : const struct ct_dpif_protoinfo *protoinfo)
334 : : {
335 : : uint8_t tcp_state;
336 : :
337 : : /* We keep two separate tcp states, but we print just one. The Linux
338 : : * kernel connection tracker internally keeps only one state, so
339 : : * 'state_orig' and 'state_reply', will be the same. */
340 : 414 : tcp_state = MAX(protoinfo->tcp.state_orig, protoinfo->tcp.state_reply);
341 : :
342 : 414 : tcp_state = coalesce_tcp_state(tcp_state);
343 : 414 : ct_dpif_format_enum(ds, "state=", tcp_state, ct_dpif_tcp_state_string);
344 : 414 : }
345 : :
346 : : static void
347 : 0 : ct_dpif_format_protoinfo_tcp_verbose(struct ds *ds,
348 : : const struct ct_dpif_protoinfo *protoinfo)
349 : : {
350 : 0 : ct_dpif_format_enum(ds, "state_orig=", protoinfo->tcp.state_orig,
351 : : ct_dpif_tcp_state_string);
352 : 0 : ct_dpif_format_enum(ds, ",state_reply=", protoinfo->tcp.state_reply,
353 : : ct_dpif_tcp_state_string);
354 : :
355 [ # # ][ # # ]: 0 : if (protoinfo->tcp.wscale_orig || protoinfo->tcp.wscale_reply) {
356 : 0 : ds_put_format(ds, ",wscale_orig=%u,wscale_reply=%u",
357 : 0 : protoinfo->tcp.wscale_orig,
358 : 0 : protoinfo->tcp.wscale_reply);
359 : : }
360 : 0 : ct_dpif_format_flags(ds, ",flags_orig=", protoinfo->tcp.flags_orig,
361 : : tcp_flags);
362 : 0 : ct_dpif_format_flags(ds, ",flags_reply=", protoinfo->tcp.flags_reply,
363 : : tcp_flags);
364 : 0 : }
365 : :
366 : : static void
367 : 434 : ct_dpif_format_protoinfo(struct ds *ds, const char *title,
368 : : const struct ct_dpif_protoinfo *protoinfo,
369 : : bool verbose)
370 : : {
371 [ + + ]: 434 : if (protoinfo->proto != 0) {
372 [ + - ]: 414 : if (title) {
373 : 414 : ds_put_format(ds, "%s(", title);
374 : : }
375 [ + - ]: 414 : switch (protoinfo->proto) {
376 : : case IPPROTO_TCP:
377 [ - + ]: 414 : if (verbose) {
378 : 0 : ct_dpif_format_protoinfo_tcp_verbose(ds, protoinfo);
379 : : } else {
380 : 414 : ct_dpif_format_protoinfo_tcp(ds, protoinfo);
381 : : }
382 : 414 : break;
383 : : }
384 [ + - ]: 414 : if (title) {
385 : 414 : ds_put_cstr(ds, ")");
386 : : }
387 : : }
388 : 434 : }
389 : :
390 : : static void
391 : 434 : ct_dpif_format_helper(struct ds *ds, const char *title,
392 : : const struct ct_dpif_helper *helper)
393 : : {
394 [ + + ]: 434 : if (helper->name) {
395 [ + - ]: 14 : if (title) {
396 : 14 : ds_put_cstr(ds, title);
397 : : }
398 : 14 : ds_put_cstr(ds, helper->name);
399 : : }
400 : 434 : }
|