2 * Copyright (c) 2015 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "dpif-provider.h"
23 #include "openvswitch/vlog.h"
25 VLOG_DEFINE_THIS_MODULE(ct_dpif
);
27 /* Declarations for conntrack entry formatting. */
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
*,
43 static void ct_dpif_format_helper(struct ds
*, const char *title
,
44 const struct ct_dpif_helper
*);
46 static const struct flags ct_dpif_status_flags
[] = {
47 #define CT_DPIF_STATUS_FLAG(FLAG) { CT_DPIF_STATUS_##FLAG, #FLAG },
49 #undef CT_DPIF_STATUS_FLAG
50 { 0, NULL
} /* End marker. */
55 /* Start dumping the entries from the connection tracker used by 'dpif'.
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}().
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.
64 * If there has been a problem the function returns a non-zero value
65 * that represents the error. Otherwise it returns zero. */
67 ct_dpif_dump_start(struct dpif
*dpif
, struct ct_dpif_dump_state
**dump
,
72 err
= (dpif
->dpif_class
->ct_dump_start
73 ? dpif
->dpif_class
->ct_dump_start(dpif
, dump
, zone
)
83 /* Dump one connection from a tracker, and put it in 'entry'.
85 * 'dump' should have been initialized by ct_dpif_dump_start().
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.
91 * In both cases, the user should call ct_dpif_dump_done(). */
93 ct_dpif_dump_next(struct ct_dpif_dump_state
*dump
, struct ct_dpif_entry
*entry
)
95 struct dpif
*dpif
= dump
->dpif
;
97 return (dpif
->dpif_class
->ct_dump_next
98 ? dpif
->dpif_class
->ct_dump_next(dpif
, dump
, entry
)
102 /* Free resources used by 'dump' */
104 ct_dpif_dump_done(struct ct_dpif_dump_state
*dump
)
106 struct dpif
*dpif
= dump
->dpif
;
108 return (dpif
->dpif_class
->ct_dump_done
109 ? dpif
->dpif_class
->ct_dump_done(dpif
, dump
)
113 /* Flush the entries in the connection tracker used by 'dpif'.
115 * If 'zone' is not NULL, flush only the entries in '*zone'. */
117 ct_dpif_flush(struct dpif
*dpif
, const uint16_t *zone
)
120 VLOG_DBG("%s: ct_flush: %"PRIu16
, dpif_name(dpif
), *zone
);
122 VLOG_DBG("%s: ct_flush: <all>", dpif_name(dpif
));
125 return (dpif
->dpif_class
->ct_flush
126 ? dpif
->dpif_class
->ct_flush(dpif
, zone
)
131 ct_dpif_entry_uninit(struct ct_dpif_entry
*entry
)
134 if (entry
->helper
.name
) {
135 free(entry
->helper
.name
);
141 ct_dpif_format_entry(const struct ct_dpif_entry
*entry
, struct ds
*ds
,
142 bool verbose
, bool print_stats
)
144 ct_dpif_format_ipproto(ds
, entry
->tuple_orig
.ip_proto
);
146 ds_put_cstr(ds
, ",orig=(");
147 ct_dpif_format_tuple(ds
, &entry
->tuple_orig
);
149 ct_dpif_format_counters(ds
, &entry
->counters_orig
);
151 ds_put_cstr(ds
, ")");
153 ds_put_cstr(ds
, ",reply=(");
154 ct_dpif_format_tuple(ds
, &entry
->tuple_reply
);
156 ct_dpif_format_counters(ds
, &entry
->counters_reply
);
158 ds_put_cstr(ds
, ")");
161 ct_dpif_format_timestamp(ds
, &entry
->timestamp
);
164 ds_put_format(ds
, ",id=%"PRIu32
, entry
->id
);
167 ds_put_format(ds
, ",zone=%"PRIu16
, entry
->zone
);
170 ct_dpif_format_flags(ds
, ",status=", entry
->status
,
171 ct_dpif_status_flags
);
174 ds_put_format(ds
, ",timeout=%"PRIu32
, entry
->timeout
);
177 ds_put_format(ds
, ",mark=%"PRIu32
, entry
->mark
);
179 if (!ovs_u128_is_zero(entry
->labels
)) {
182 ds_put_cstr(ds
, ",labels=");
183 value
= hton128(entry
->labels
);
184 ds_put_hex(ds
, &value
, sizeof value
);
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
, ")");
196 ct_dpif_format_ipproto(struct ds
*ds
, uint16_t ipproto
)
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"
208 ds_put_cstr(ds
, name
);
210 ds_put_format(ds
, "%u", ipproto
);
215 ct_dpif_format_counters(struct ds
*ds
, const struct ct_dpif_counters
*counters
)
217 if (counters
->packets
|| counters
->bytes
) {
218 ds_put_format(ds
, ",packets=%"PRIu64
",bytes=%"PRIu64
,
219 counters
->packets
, counters
->bytes
);
224 ct_dpif_format_timestamp(struct ds
*ds
,
225 const struct ct_dpif_timestamp
*timestamp
)
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);
238 ct_dpif_format_tuple_icmp(struct ds
*ds
, const struct ct_dpif_tuple
*tuple
)
240 ds_put_format(ds
, ",id=%u,type=%u,code=%u", ntohs(tuple
->icmp_id
),
241 tuple
->icmp_type
, tuple
->icmp_code
);
245 ct_dpif_format_tuple_tp(struct ds
*ds
, const struct ct_dpif_tuple
*tuple
)
247 ds_put_format(ds
, ",sport=%u,dport=%u",
248 ntohs(tuple
->src_port
), ntohs(tuple
->dst_port
));
252 ct_dpif_format_tuple(struct ds
*ds
, const struct ct_dpif_tuple
*tuple
)
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
);
263 ds_put_format(ds
, "Unsupported address family: %u. HEX:\n",
265 ds_put_hex_dump(ds
, tuple
, sizeof *tuple
, 0, false);
269 if (tuple
->ip_proto
== IPPROTO_ICMP
270 || tuple
->ip_proto
== IPPROTO_ICMPV6
) {
271 ct_dpif_format_tuple_icmp(ds
, tuple
);
273 ct_dpif_format_tuple_tp(ds
, tuple
);
278 ct_dpif_format_flags(struct ds
*ds
, const char *title
, uint32_t flags
,
279 const struct flags
*table
)
282 ds_put_cstr(ds
, title
);
284 for (; table
->name
; table
++) {
285 if (flags
& table
->flag
) {
286 ds_put_format(ds
, "%s|", table
->name
);
292 static const struct flags tcp_flags
[] = {
293 #define CT_DPIF_TCP_FLAG(FLAG) { CT_DPIF_TCPF_##FLAG, #FLAG },
295 #undef CT_DPIF_TCP_FLAG
296 { 0, NULL
} /* End marker. */
299 const char *ct_dpif_tcp_state_string
[] = {
300 #define CT_DPIF_TCP_STATE(STATE) [CT_DPIF_TCPS_##STATE] = #STATE,
302 #undef CT_DPIF_TCP_STATE
306 ct_dpif_format_enum__(struct ds
*ds
, const char *title
, unsigned int state
,
307 const char *names
[], unsigned int max
)
310 ds_put_cstr(ds
, title
);
313 ds_put_cstr(ds
, names
[state
]);
315 ds_put_format(ds
, "[%u]", state
);
319 #define ct_dpif_format_enum(DS, TITLE, STATE, NAMES) \
320 ct_dpif_format_enum__((DS), (TITLE), (STATE), (NAMES), ARRAY_SIZE(NAMES))
323 coalesce_tcp_state(uint8_t state
)
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. */
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
;
340 ct_dpif_format_protoinfo_tcp(struct ds
*ds
,
341 const struct ct_dpif_protoinfo
*protoinfo
)
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
);
350 tcp_state
= coalesce_tcp_state(tcp_state
);
351 ct_dpif_format_enum(ds
, "state=", tcp_state
, ct_dpif_tcp_state_string
);
355 ct_dpif_format_protoinfo_tcp_verbose(struct ds
*ds
,
356 const struct ct_dpif_protoinfo
*protoinfo
)
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
);
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
);
368 ct_dpif_format_flags(ds
, ",flags_orig=", protoinfo
->tcp
.flags_orig
,
370 ct_dpif_format_flags(ds
, ",flags_reply=", protoinfo
->tcp
.flags_reply
,
375 ct_dpif_format_protoinfo(struct ds
*ds
, const char *title
,
376 const struct ct_dpif_protoinfo
*protoinfo
,
379 if (protoinfo
->proto
!= 0) {
381 ds_put_format(ds
, "%s(", title
);
383 switch (protoinfo
->proto
) {
386 ct_dpif_format_protoinfo_tcp_verbose(ds
, protoinfo
);
388 ct_dpif_format_protoinfo_tcp(ds
, protoinfo
);
393 ds_put_cstr(ds
, ")");
399 ct_dpif_format_helper(struct ds
*ds
, const char *title
,
400 const struct ct_dpif_helper
*helper
)
404 ds_put_cstr(ds
, title
);
406 ds_put_cstr(ds
, helper
->name
);