]>
Commit | Line | Data |
---|---|---|
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 | 25 | VLOG_DEFINE_THIS_MODULE(ct_dpif); |
b77d9629 | 26 | |
3948eb54 DDP |
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 | |
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, | |
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 | |
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'. */ | |
116 | int | |
117 | ct_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 |
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=("); | |
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 | ||
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" | |
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 | ||
217 | static void | |
218 | ct_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 | ||
226 | static void | |
227 | ct_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 | ||
240 | static void | |
b269a122 | 241 | ct_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 | ||
247 | static void | |
248 | ct_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 | ||
254 | void | |
b269a122 | 255 | ct_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 | ||
280 | static void | |
281 | ct_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 | ||
295 | static 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 | ||
302 | const 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 | ||
308 | static void | |
309 | ct_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 | ||
325 | static uint8_t | |
326 | coalesce_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 | ||
342 | static void | |
343 | ct_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 | ||
357 | static void | |
358 | ct_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 | ||
377 | static void | |
378 | ct_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 | ||
401 | static void | |
402 | ct_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 | } |