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 "netlink-conntrack.h"
20 : :
21 : : #include <linux/netfilter/nfnetlink.h>
22 : : #include <linux/netfilter/nfnetlink_conntrack.h>
23 : : #include <linux/netfilter/nf_conntrack_common.h>
24 : : #include <linux/netfilter/nf_conntrack_tcp.h>
25 : : #include <linux/netfilter/nf_conntrack_ftp.h>
26 : : #include <linux/netfilter/nf_conntrack_sctp.h>
27 : :
28 : : #include "byte-order.h"
29 : : #include "compiler.h"
30 : : #include "openvswitch/dynamic-string.h"
31 : : #include "netlink.h"
32 : : #include "netlink-socket.h"
33 : : #include "openvswitch/ofpbuf.h"
34 : : #include "openvswitch/vlog.h"
35 : : #include "poll-loop.h"
36 : : #include "timeval.h"
37 : : #include "unixctl.h"
38 : : #include "util.h"
39 : :
40 : 20190 : VLOG_DEFINE_THIS_MODULE(netlink_conntrack);
41 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
42 : :
43 : : /* This module works only if conntrack modules and features are enabled in the
44 : : * Linux kernel. This can be done from a root shell like this:
45 : : *
46 : : * $ modprobe ip_conntrack
47 : : * $ sysctl -w net.netfilter.nf_conntrack_acct=1
48 : : * $ sysctl -w net.netfilter.nf_conntrack_timestamp=1
49 : : *
50 : : * Also, if testing conntrack label feature without conntrack-aware OVS kernel
51 : : * module, there must be a connlabel rule in iptables for space to be reserved
52 : : * for the labels (see kernel source connlabel_mt_check()). Such a rule can be
53 : : * inserted from a root shell like this:
54 : : *
55 : : * $ iptables -A INPUT -m conntrack -m connlabel \
56 : : * --ctstate NEW,ESTABLISHED,RELATED --label 127 -j ACCEPT
57 : : */
58 : :
59 : : /* Some attributes were introduced in later kernels: with these definitions
60 : : * we should be able to compile userspace against Linux 2.6.32+. */
61 : :
62 : : #define CTA_ZONE (CTA_SECMARK + 1)
63 : : #define CTA_SECCTX (CTA_SECMARK + 2)
64 : : #define CTA_TIMESTAMP (CTA_SECMARK + 3)
65 : : #define CTA_MARK_MASK (CTA_SECMARK + 4)
66 : : #define CTA_LABELS (CTA_SECMARK + 5)
67 : : #define CTA_LABELS_MASK (CTA_SECMARK + 6)
68 : :
69 : : #define CTA_TIMESTAMP_START 1
70 : : #define CTA_TIMESTAMP_STOP 2
71 : :
72 : : #define IPS_TEMPLATE_BIT 11
73 : : #define IPS_TEMPLATE (1 << IPS_TEMPLATE_BIT)
74 : :
75 : : #define IPS_UNTRACKED_BIT 12
76 : : #define IPS_UNTRACKED (1 << IPS_UNTRACKED_BIT)
77 : :
78 : : static const struct nl_policy nfnlgrp_conntrack_policy[] = {
79 : : [CTA_TUPLE_ORIG] = { .type = NL_A_NESTED, .optional = false },
80 : : [CTA_TUPLE_REPLY] = { .type = NL_A_NESTED, .optional = false },
81 : : [CTA_ZONE] = { .type = NL_A_BE16, .optional = true },
82 : : [CTA_STATUS] = { .type = NL_A_BE32, .optional = false },
83 : : [CTA_TIMESTAMP] = { .type = NL_A_NESTED, .optional = true },
84 : : [CTA_TIMEOUT] = { .type = NL_A_BE32, .optional = true },
85 : : [CTA_COUNTERS_ORIG] = { .type = NL_A_NESTED, .optional = true },
86 : : [CTA_COUNTERS_REPLY] = { .type = NL_A_NESTED, .optional = true },
87 : : [CTA_PROTOINFO] = { .type = NL_A_NESTED, .optional = true },
88 : : [CTA_HELP] = { .type = NL_A_NESTED, .optional = true },
89 : : [CTA_MARK] = { .type = NL_A_BE32, .optional = true },
90 : : [CTA_SECCTX] = { .type = NL_A_NESTED, .optional = true },
91 : : [CTA_ID] = { .type = NL_A_BE32, .optional = false },
92 : : [CTA_USE] = { .type = NL_A_BE32, .optional = true },
93 : : [CTA_TUPLE_MASTER] = { .type = NL_A_NESTED, .optional = true },
94 : : [CTA_NAT_SEQ_ADJ_ORIG] = { .type = NL_A_NESTED, .optional = true },
95 : : [CTA_NAT_SEQ_ADJ_REPLY] = { .type = NL_A_NESTED, .optional = true },
96 : : [CTA_LABELS] = { .type = NL_A_UNSPEC, .optional = true },
97 : : /* CTA_NAT_SRC, CTA_NAT_DST, CTA_TIMESTAMP, CTA_MARK_MASK, and
98 : : * CTA_LABELS_MASK are not received from kernel. */
99 : : };
100 : :
101 : : /* Declarations for conntrack netlink dumping. */
102 : : static void nl_msg_put_nfgenmsg(struct ofpbuf *msg, size_t expected_payload,
103 : : int family, uint8_t subsystem, uint8_t cmd,
104 : : uint32_t flags);
105 : :
106 : : static bool nl_ct_parse_header_policy(struct ofpbuf *buf,
107 : : enum nl_ct_event_type *event_type,
108 : : uint8_t *nfgen_family,
109 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]);
110 : :
111 : : static bool nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry,
112 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)],
113 : : uint8_t nfgen_family);
114 : :
115 : : struct nl_ct_dump_state {
116 : : struct nl_dump dump;
117 : : struct ofpbuf buf;
118 : : bool filter_zone;
119 : : uint16_t zone;
120 : : };
121 : :
122 : : /* Conntrack netlink dumping. */
123 : :
124 : : /* Initialize a conntrack netlink dump. */
125 : : int
126 : 53 : nl_ct_dump_start(struct nl_ct_dump_state **statep, const uint16_t *zone)
127 : : {
128 : : struct nl_ct_dump_state *state;
129 : :
130 : 53 : *statep = state = xzalloc(sizeof *state);
131 : 53 : ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE);
132 : :
133 [ - + ]: 53 : if (zone) {
134 : 0 : state->filter_zone = true;
135 : 0 : state->zone = *zone;
136 : : }
137 : :
138 : 53 : nl_msg_put_nfgenmsg(&state->buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK,
139 : : IPCTNL_MSG_CT_GET, NLM_F_REQUEST);
140 : 53 : nl_dump_start(&state->dump, NETLINK_NETFILTER, &state->buf);
141 : 53 : ofpbuf_clear(&state->buf);
142 : :
143 : 53 : return 0;
144 : : }
145 : :
146 : : /* Receive the next 'entry' from the conntrack netlink dump with 'state'.
147 : : * Returns 'EOF' when no more entries are available, 0 otherwise. 'entry' may
148 : : * be uninitilized memory on entry, and must be uninitialized with
149 : : * ct_dpif_entry_uninit() afterwards by the caller. In case the same 'entry' is
150 : : * passed to this function again, the entry must also be uninitialized before
151 : : * the next call. */
152 : : int
153 : 439 : nl_ct_dump_next(struct nl_ct_dump_state *state, struct ct_dpif_entry *entry)
154 : : {
155 : : struct ofpbuf buf;
156 : :
157 : 439 : memset(entry, 0, sizeof *entry);
158 : : for (;;) {
159 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)];
160 : : enum nl_ct_event_type type;
161 : : uint8_t nfgen_family;
162 : :
163 [ + + ]: 439 : if (!nl_dump_next(&state->dump, &buf, &state->buf)) {
164 : 53 : return EOF;
165 : : }
166 : :
167 [ - + ]: 386 : if (!nl_ct_parse_header_policy(&buf, &type, &nfgen_family, attrs)) {
168 : 0 : continue;
169 : : };
170 : :
171 [ - + ]: 386 : if (state->filter_zone) {
172 [ # # ]: 0 : uint16_t entry_zone = attrs[CTA_ZONE]
173 : 0 : ? ntohs(nl_attr_get_be16(attrs[CTA_ZONE]))
174 : : : 0;
175 [ # # ]: 0 : if (entry_zone != state->zone) {
176 : 0 : continue;
177 : : }
178 : : }
179 : :
180 [ + - ]: 386 : if (nl_ct_attrs_to_ct_dpif_entry(entry, attrs, nfgen_family)) {
181 : 386 : break;
182 : : }
183 : :
184 : 0 : ct_dpif_entry_uninit(entry);
185 : 0 : memset(entry, 0, sizeof *entry);
186 : : /* Ignore the failed entry and get the next one. */
187 : 439 : }
188 : :
189 : 386 : ofpbuf_uninit(&buf);
190 : 386 : return 0;
191 : : }
192 : :
193 : : /* End a conntrack netlink dump. */
194 : : int
195 : 53 : nl_ct_dump_done(struct nl_ct_dump_state *state)
196 : : {
197 : 53 : int error = nl_dump_done(&state->dump);
198 : :
199 : 53 : ofpbuf_uninit(&state->buf);
200 : 53 : free(state);
201 : 53 : return error;
202 : : }
203 : :
204 : : /* Format conntrack event 'entry' of 'type' to 'ds'. */
205 : : void
206 : 0 : nl_ct_format_event_entry(const struct ct_dpif_entry *entry,
207 : : enum nl_ct_event_type type, struct ds *ds,
208 : : bool verbose, bool print_stats)
209 : : {
210 [ # # ]: 0 : ds_put_format(ds, "%s ",
211 : : type == NL_CT_EVENT_NEW ? "NEW"
212 : : : type == NL_CT_EVENT_UPDATE ? "UPDATE"
213 [ # # ]: 0 : : type == NL_CT_EVENT_DELETE ? "DELETE"
214 [ # # ]: 0 : : "UNKNOWN");
215 : 0 : ct_dpif_format_entry(entry, ds, verbose, print_stats);
216 : 0 : }
217 : :
218 : : int
219 : 60 : nl_ct_flush(void)
220 : : {
221 : : struct ofpbuf buf;
222 : : int err;
223 : :
224 : 60 : ofpbuf_init(&buf, NL_DUMP_BUFSIZE);
225 : :
226 : 60 : nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK,
227 : : IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST);
228 : :
229 : 60 : err = nl_transact(NETLINK_NETFILTER, &buf, NULL);
230 : 60 : ofpbuf_uninit(&buf);
231 : :
232 : : /* Expectations are flushed automatically, because they do not
233 : : * have a master connection anymore */
234 : :
235 : 60 : return err;
236 : : }
237 : :
238 : : #ifdef _WIN32
239 : : int
240 : : nl_ct_flush_zone(uint16_t flush_zone)
241 : : {
242 : : /* Windows can flush a specific zone */
243 : : struct ofpbuf buf;
244 : : int err;
245 : :
246 : : ofpbuf_init(&buf, NL_DUMP_BUFSIZE);
247 : :
248 : : nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK,
249 : : IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST);
250 : : nl_msg_put_be16(&buf, CTA_ZONE, flush_zone);
251 : :
252 : : err = nl_transact(NETLINK_NETFILTER, &buf, NULL);
253 : : ofpbuf_uninit(&buf);
254 : :
255 : : return err;
256 : : }
257 : : #else
258 : : int
259 : 0 : nl_ct_flush_zone(uint16_t flush_zone)
260 : : {
261 : : /* Apparently, there's no netlink interface to flush a specific zone.
262 : : * This code dumps every connection, checks the zone and eventually
263 : : * delete the entry.
264 : : *
265 : : * This is race-prone, but it is better than using shell scripts. */
266 : :
267 : : struct nl_dump dump;
268 : : struct ofpbuf buf, reply, delete;
269 : :
270 : 0 : ofpbuf_init(&buf, NL_DUMP_BUFSIZE);
271 : 0 : ofpbuf_init(&delete, NL_DUMP_BUFSIZE);
272 : :
273 : 0 : nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK,
274 : : IPCTNL_MSG_CT_GET, NLM_F_REQUEST);
275 : 0 : nl_dump_start(&dump, NETLINK_NETFILTER, &buf);
276 : 0 : ofpbuf_clear(&buf);
277 : :
278 : : for (;;) {
279 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)];
280 : : enum nl_ct_event_type event_type;
281 : : uint8_t nfgen_family;
282 : 0 : uint16_t zone = 0;
283 : :
284 [ # # ]: 0 : if (!nl_dump_next(&dump, &reply, &buf)) {
285 : 0 : break;
286 : : }
287 : :
288 [ # # ]: 0 : if (!nl_ct_parse_header_policy(&reply, &event_type, &nfgen_family,
289 : : attrs)) {
290 : 0 : continue;
291 : : };
292 : :
293 [ # # ]: 0 : if (attrs[CTA_ZONE]) {
294 : 0 : zone = ntohs(nl_attr_get_be16(attrs[CTA_ZONE]));
295 : : }
296 : :
297 [ # # ]: 0 : if (zone != flush_zone) {
298 : : /* The entry is not in the zone we're flushing. */
299 : 0 : continue;
300 : : }
301 : 0 : nl_msg_put_nfgenmsg(&delete, 0, nfgen_family, NFNL_SUBSYS_CTNETLINK,
302 : : IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST);
303 : :
304 : 0 : nl_msg_put_be16(&delete, CTA_ZONE, htons(zone));
305 : 0 : nl_msg_put_unspec(&delete, CTA_TUPLE_ORIG, attrs[CTA_TUPLE_ORIG] + 1,
306 : 0 : attrs[CTA_TUPLE_ORIG]->nla_len - NLA_HDRLEN);
307 : 0 : nl_msg_put_unspec(&delete, CTA_ID, attrs[CTA_ID] + 1,
308 : 0 : attrs[CTA_ID]->nla_len - NLA_HDRLEN);
309 : 0 : nl_transact(NETLINK_NETFILTER, &delete, NULL);
310 : 0 : ofpbuf_clear(&delete);
311 : 0 : }
312 : :
313 : 0 : nl_dump_done(&dump);
314 : :
315 : 0 : ofpbuf_uninit(&delete);
316 : 0 : ofpbuf_uninit(&buf);
317 : :
318 : : /* Expectations are flushed automatically, because they do not
319 : : * have a master connection anymore */
320 : 0 : return 0;
321 : : }
322 : : #endif
323 : :
324 : : /* Conntrack netlink parsing. */
325 : :
326 : : static bool
327 : 0 : nl_ct_parse_counters(struct nlattr *nla, struct ct_dpif_counters *counters)
328 : : {
329 : : static const struct nl_policy policy[] = {
330 : : [CTA_COUNTERS_PACKETS] = { .type = NL_A_BE64, .optional = false },
331 : : [CTA_COUNTERS_BYTES] = { .type = NL_A_BE64, .optional = false },
332 : : };
333 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
334 : : bool parsed;
335 : :
336 : 0 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
337 : :
338 [ # # ]: 0 : if (parsed) {
339 : : counters->packets
340 : 0 : = ntohll(nl_attr_get_be64(attrs[CTA_COUNTERS_PACKETS]));
341 : 0 : counters->bytes = ntohll(nl_attr_get_be64(attrs[CTA_COUNTERS_BYTES]));
342 : : } else {
343 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested counters. "
344 : : "Possibly incompatible Linux kernel version.");
345 : : }
346 : :
347 : 0 : return parsed;
348 : : }
349 : :
350 : : static bool
351 : 0 : nl_ct_parse_timestamp(struct nlattr *nla, struct ct_dpif_timestamp *timestamp)
352 : : {
353 : : static const struct nl_policy policy[] = {
354 : : [CTA_TIMESTAMP_START] = { .type = NL_A_BE64, .optional = false },
355 : : [CTA_TIMESTAMP_STOP] = { .type = NL_A_BE64, .optional = true },
356 : : };
357 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
358 : : bool parsed;
359 : :
360 : 0 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
361 : :
362 [ # # ]: 0 : if (parsed) {
363 : : timestamp->start
364 : 0 : = ntohll(nl_attr_get_be64(attrs[CTA_TIMESTAMP_START]));
365 [ # # ]: 0 : if (attrs[CTA_TIMESTAMP_STOP]) {
366 : : timestamp->stop
367 : 0 : = ntohll(nl_attr_get_be64(attrs[CTA_TIMESTAMP_STOP]));
368 : : }
369 : : } else {
370 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested timestamp. "
371 : : "Possibly incompatible Linux kernel version.");
372 : : }
373 : :
374 : 0 : return parsed;
375 : : }
376 : :
377 : : static bool
378 : 783 : nl_ct_parse_tuple_ip(struct nlattr *nla, struct ct_dpif_tuple *tuple)
379 : : {
380 : : static const struct nl_policy policy[] = {
381 : : [CTA_IP_V4_SRC] = { .type = NL_A_BE32, .optional = true },
382 : : [CTA_IP_V4_DST] = { .type = NL_A_BE32, .optional = true },
383 : : [CTA_IP_V6_SRC] = { NL_POLICY_FOR(struct in6_addr), .optional = true },
384 : : [CTA_IP_V6_DST] = { NL_POLICY_FOR(struct in6_addr), .optional = true },
385 : : };
386 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
387 : : bool parsed;
388 : :
389 : 783 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
390 : :
391 [ + - ]: 783 : if (parsed) {
392 [ + + ]: 783 : if (tuple->l3_type == AF_INET) {
393 [ + - ]: 769 : if (attrs[CTA_IP_V4_SRC]) {
394 : 769 : tuple->src.ip = nl_attr_get_be32(attrs[CTA_IP_V4_SRC]);
395 : : }
396 [ + - ]: 769 : if (attrs[CTA_IP_V4_DST]) {
397 : 769 : tuple->dst.ip = nl_attr_get_be32(attrs[CTA_IP_V4_DST]);
398 : : }
399 [ + - ]: 14 : } else if (tuple->l3_type == AF_INET6) {
400 [ + - ]: 14 : if (attrs[CTA_IP_V6_SRC]) {
401 : 14 : memcpy(&tuple->src.in6, nl_attr_get(attrs[CTA_IP_V6_SRC]),
402 : : sizeof tuple->src.in6);
403 : : }
404 [ + - ]: 14 : if (attrs[CTA_IP_V6_DST]) {
405 : 14 : memcpy(&tuple->dst.in6, nl_attr_get(attrs[CTA_IP_V6_DST]),
406 : : sizeof tuple->dst.in6);
407 : : }
408 : : } else {
409 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Unsupported IP protocol: %u.", tuple->l3_type);
410 : 0 : return false;
411 : : }
412 : : } else {
413 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested tuple IP options. "
414 : : "Possibly incompatible Linux kernel version.");
415 : : }
416 : :
417 : 783 : return parsed;
418 : : }
419 : :
420 : : static bool
421 : 783 : nl_ct_parse_tuple_proto(struct nlattr *nla, struct ct_dpif_tuple *tuple)
422 : : {
423 : : static const struct nl_policy policy[] = {
424 : : [CTA_PROTO_NUM] = { .type = NL_A_U8, .optional = false },
425 : : [CTA_PROTO_SRC_PORT] = { .type = NL_A_BE16, .optional = true },
426 : : [CTA_PROTO_DST_PORT] = { .type = NL_A_BE16, .optional = true },
427 : : [CTA_PROTO_ICMP_ID] = { .type = NL_A_BE16, .optional = true },
428 : : [CTA_PROTO_ICMP_TYPE] = { .type = NL_A_U8, .optional = true },
429 : : [CTA_PROTO_ICMP_CODE] = { .type = NL_A_U8, .optional = true },
430 : : [CTA_PROTO_ICMPV6_ID] = { .type = NL_A_BE16, .optional = true },
431 : : [CTA_PROTO_ICMPV6_TYPE] = { .type = NL_A_U8, .optional = true },
432 : : [CTA_PROTO_ICMPV6_CODE] = { .type = NL_A_U8, .optional = true },
433 : : };
434 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
435 : : bool parsed;
436 : :
437 : 783 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
438 : :
439 [ + - ]: 783 : if (parsed) {
440 : 783 : tuple->ip_proto = nl_attr_get_u8(attrs[CTA_PROTO_NUM]);
441 : :
442 [ + + ][ + + ]: 783 : if (tuple->l3_type == AF_INET && tuple->ip_proto == IPPROTO_ICMP) {
443 [ + - ][ + - ]: 30 : if (!attrs[CTA_PROTO_ICMP_ID] || !attrs[CTA_PROTO_ICMP_TYPE]
444 [ - + ]: 30 : || !attrs[CTA_PROTO_ICMP_CODE]) {
445 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Tuple ICMP data missing.");
446 : 0 : return false;
447 : : }
448 : 30 : tuple->icmp_id = nl_attr_get_be16(attrs[CTA_PROTO_ICMP_ID]);
449 : 30 : tuple->icmp_type = nl_attr_get_u8(attrs[CTA_PROTO_ICMP_TYPE]);
450 : 30 : tuple->icmp_code = nl_attr_get_u8(attrs[CTA_PROTO_ICMP_CODE]);
451 [ + + ][ + + ]: 753 : } else if (tuple->l3_type == AF_INET6 &&
452 : 14 : tuple->ip_proto == IPPROTO_ICMPV6) {
453 [ + - ][ + - ]: 2 : if (!attrs[CTA_PROTO_ICMPV6_ID] || !attrs[CTA_PROTO_ICMPV6_TYPE]
454 [ - + ]: 2 : || !attrs[CTA_PROTO_ICMPV6_CODE]) {
455 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Tuple ICMPv6 data missing.");
456 : 0 : return false;
457 : : }
458 : 2 : tuple->icmp_id = nl_attr_get_be16(attrs[CTA_PROTO_ICMPV6_ID]);
459 : 2 : tuple->icmp_type = nl_attr_get_u8(attrs[CTA_PROTO_ICMPV6_TYPE]);
460 : 2 : tuple->icmp_code = nl_attr_get_u8(attrs[CTA_PROTO_ICMPV6_CODE]);
461 [ + - ][ + - ]: 751 : } else if (attrs[CTA_PROTO_SRC_PORT] && attrs[CTA_PROTO_DST_PORT]) {
462 : 751 : tuple->src_port = nl_attr_get_be16(attrs[CTA_PROTO_SRC_PORT]);
463 : 751 : tuple->dst_port = nl_attr_get_be16(attrs[CTA_PROTO_DST_PORT]);
464 : : } else {
465 : : /* Unsupported IPPROTO and no ports, leave them zeroed.
466 : : * We have parsed the ip_proto, so this is not a total failure. */
467 [ # # ]: 783 : VLOG_INFO_RL(&rl, "Unsupported L4 protocol: %u.", tuple->ip_proto);
468 : : }
469 : : } else {
470 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested tuple protocol options. "
471 : : "Possibly incompatible Linux kernel version.");
472 : : }
473 : :
474 : 783 : return parsed;
475 : : }
476 : :
477 : : static bool
478 : 783 : nl_ct_parse_tuple(struct nlattr *nla, struct ct_dpif_tuple *tuple,
479 : : uint16_t l3_type)
480 : : {
481 : : static const struct nl_policy policy[] = {
482 : : [CTA_TUPLE_IP] = { .type = NL_A_NESTED, .optional = false },
483 : : [CTA_TUPLE_PROTO] = { .type = NL_A_NESTED, .optional = false },
484 : : };
485 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
486 : : bool parsed;
487 : :
488 : 783 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
489 : :
490 : 783 : memset(tuple, 0, sizeof *tuple);
491 : :
492 [ + - ]: 783 : if (parsed) {
493 : 783 : tuple->l3_type = l3_type;
494 : :
495 [ + - ]: 783 : if (!nl_ct_parse_tuple_ip(attrs[CTA_TUPLE_IP], tuple)
496 [ - + ]: 783 : || !nl_ct_parse_tuple_proto(attrs[CTA_TUPLE_PROTO], tuple)) {
497 : : struct ds ds;
498 : :
499 : 0 : ds_init(&ds);
500 : 0 : ct_dpif_format_tuple(&ds, tuple);
501 : :
502 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Failed to parse tuple: %s", ds_cstr(&ds));
503 : 0 : ds_destroy(&ds);
504 : :
505 : 0 : memset(tuple, 0, sizeof *tuple);
506 : 0 : return false;
507 : : }
508 : : } else {
509 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested tuple options. "
510 : : "Possibly incompatible Linux kernel version.");
511 : : }
512 : :
513 : 783 : return parsed;
514 : : }
515 : :
516 : : /* Translate netlink TCP state to CT_DPIF_TCP state. */
517 : : static uint8_t
518 : 368 : nl_ct_tcp_state_to_dpif(uint8_t state)
519 : : {
520 : : #ifdef _WIN32
521 : : /* Windows currently sends up CT_DPIF_TCP state */
522 : : return state;
523 : : #else
524 [ - + - + : 368 : switch (state) {
+ + - - +
- - ]
525 : : case TCP_CONNTRACK_NONE:
526 : 0 : return CT_DPIF_TCPS_CLOSED;
527 : : case TCP_CONNTRACK_SYN_SENT:
528 : 3 : return CT_DPIF_TCPS_SYN_SENT;
529 : : case TCP_CONNTRACK_SYN_SENT2:
530 : 0 : return CT_DPIF_TCPS_SYN_SENT;
531 : : case TCP_CONNTRACK_SYN_RECV:
532 : 8 : return CT_DPIF_TCPS_SYN_RECV;
533 : : case TCP_CONNTRACK_ESTABLISHED:
534 : 42 : return CT_DPIF_TCPS_ESTABLISHED;
535 : : case TCP_CONNTRACK_FIN_WAIT:
536 : 1 : return CT_DPIF_TCPS_FIN_WAIT_1;
537 : : case TCP_CONNTRACK_CLOSE_WAIT:
538 : 0 : return CT_DPIF_TCPS_CLOSE_WAIT;
539 : : case TCP_CONNTRACK_LAST_ACK:
540 : 0 : return CT_DPIF_TCPS_LAST_ACK;
541 : : case TCP_CONNTRACK_TIME_WAIT:
542 : 314 : return CT_DPIF_TCPS_TIME_WAIT;
543 : : case TCP_CONNTRACK_CLOSE:
544 : 0 : return CT_DPIF_TCPS_CLOSING;
545 : : default:
546 : 0 : return CT_DPIF_TCPS_CLOSED;
547 : : }
548 : : #endif
549 : : }
550 : :
551 : : static uint8_t
552 : 736 : ip_ct_tcp_flags_to_dpif(uint8_t flags)
553 : : {
554 : : #ifdef _WIN32
555 : : /* Windows currently sends up CT_DPIF_TCP flags */
556 : : return flags;
557 : : #else
558 : 736 : uint8_t ret = 0;
559 : : #define CT_DPIF_TCP_FLAG(FLAG) \
560 : : ret |= (flags & IP_CT_TCP_FLAG_##FLAG) ? CT_DPIF_TCPF_##FLAG : 0;
561 : 736 : CT_DPIF_TCP_FLAGS
562 : : #undef CT_DPIF_STATUS_FLAG
563 : 736 : return ret;
564 : : #endif
565 : : }
566 : :
567 : : static bool
568 : 368 : nl_ct_parse_protoinfo_tcp(struct nlattr *nla,
569 : : struct ct_dpif_protoinfo *protoinfo)
570 : : {
571 : : static const struct nl_policy policy[] = {
572 : : [CTA_PROTOINFO_TCP_STATE] = { .type = NL_A_U8, .optional = false },
573 : : [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NL_A_U8,
574 : : .optional = false },
575 : : [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NL_A_U8,
576 : : .optional = false },
577 : : [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .type = NL_A_U16,
578 : : .optional = false },
579 : : [CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .type = NL_A_U16,
580 : : .optional = false },
581 : : };
582 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
583 : : bool parsed;
584 : :
585 : 368 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
586 : :
587 [ + - ]: 368 : if (parsed) {
588 : : const struct nf_ct_tcp_flags *flags_orig, *flags_reply;
589 : : uint8_t state;
590 : 368 : protoinfo->proto = IPPROTO_TCP;
591 : 368 : state = nl_ct_tcp_state_to_dpif(
592 : 368 : nl_attr_get_u8(attrs[CTA_PROTOINFO_TCP_STATE]));
593 : : /* The connection tracker keeps only one tcp state for the
594 : : * connection, but our structures store a separate state for
595 : : * each endpoint. Here we duplicate the state. */
596 : 368 : protoinfo->tcp.state_orig = protoinfo->tcp.state_reply = state;
597 : 368 : protoinfo->tcp.wscale_orig = nl_attr_get_u8(
598 : 368 : attrs[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL]);
599 : 368 : protoinfo->tcp.wscale_reply = nl_attr_get_u8(
600 : 368 : attrs[CTA_PROTOINFO_TCP_WSCALE_REPLY]);
601 : 368 : flags_orig =
602 : 368 : nl_attr_get_unspec(attrs[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL],
603 : : sizeof *flags_orig);
604 : 368 : protoinfo->tcp.flags_orig =
605 : 368 : ip_ct_tcp_flags_to_dpif(flags_orig->flags);
606 : 368 : flags_reply =
607 : 368 : nl_attr_get_unspec(attrs[CTA_PROTOINFO_TCP_FLAGS_REPLY],
608 : : sizeof *flags_reply);
609 : 368 : protoinfo->tcp.flags_reply =
610 : 368 : ip_ct_tcp_flags_to_dpif(flags_reply->flags);
611 : : } else {
612 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested TCP protoinfo options. "
613 : : "Possibly incompatible Linux kernel version.");
614 : : }
615 : :
616 : 368 : return parsed;
617 : : }
618 : :
619 : : static bool
620 : 368 : nl_ct_parse_protoinfo(struct nlattr *nla, struct ct_dpif_protoinfo *protoinfo)
621 : : {
622 : : /* These are mutually exclusive. */
623 : : static const struct nl_policy policy[] = {
624 : : [CTA_PROTOINFO_TCP] = { .type = NL_A_NESTED, .optional = true },
625 : : [CTA_PROTOINFO_SCTP] = { .type = NL_A_NESTED, .optional = true },
626 : : };
627 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
628 : : bool parsed;
629 : :
630 : 368 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
631 : :
632 : 368 : memset(protoinfo, 0, sizeof *protoinfo);
633 : :
634 [ + - ]: 368 : if (parsed) {
635 [ + - ]: 368 : if (attrs[CTA_PROTOINFO_TCP]) {
636 : 368 : parsed = nl_ct_parse_protoinfo_tcp(attrs[CTA_PROTOINFO_TCP],
637 : : protoinfo);
638 [ # # ]: 0 : } else if (attrs[CTA_PROTOINFO_SCTP]) {
639 [ # # ]: 0 : VLOG_WARN_RL(&rl, "SCTP protoinfo not yet supported!");
640 : : } else {
641 [ # # ]: 368 : VLOG_WARN_RL(&rl, "Empty protoinfo!");
642 : : }
643 : : } else {
644 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested protoinfo options. "
645 : : "Possibly incompatible Linux kernel version.");
646 : : }
647 : :
648 : 368 : return parsed;
649 : : }
650 : :
651 : : static bool
652 : 14 : nl_ct_parse_helper(struct nlattr *nla, struct ct_dpif_helper *helper)
653 : : {
654 : : static const struct nl_policy policy[] = {
655 : : [CTA_HELP_NAME] = { .type = NL_A_STRING, .optional = false },
656 : : };
657 : : struct nlattr *attrs[ARRAY_SIZE(policy)];
658 : : bool parsed;
659 : :
660 : 14 : parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
661 : :
662 : 14 : memset(helper, 0, sizeof *helper);
663 : :
664 [ + - ]: 14 : if (parsed) {
665 : 14 : helper->name = xstrdup(nl_attr_get_string(attrs[CTA_HELP_NAME]));
666 : : } else {
667 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Could not parse nested helper options. "
668 : : "Possibly incompatible Linux kernel version.");
669 : : }
670 : :
671 : 14 : return parsed;
672 : : }
673 : :
674 : : /* Translate netlink entry status flags to CT_DPIF_TCP status flags. */
675 : : static uint32_t
676 : 386 : ips_status_to_dpif_flags(uint32_t status)
677 : : {
678 : 386 : uint32_t ret = 0;
679 : : #define CT_DPIF_STATUS_FLAG(FLAG) \
680 : : ret |= (status & IPS_##FLAG) ? CT_DPIF_STATUS_##FLAG : 0;
681 : 386 : CT_DPIF_STATUS_FLAGS
682 : : #undef CT_DPIF_STATUS_FLAG
683 : 386 : return ret;
684 : : }
685 : :
686 : : static bool
687 : 386 : nl_ct_parse_header_policy(struct ofpbuf *buf,
688 : : enum nl_ct_event_type *event_type,
689 : : uint8_t *nfgen_family,
690 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)])
691 : : {
692 : : struct nlmsghdr *nlh;
693 : : struct nfgenmsg *nfm;
694 : : uint8_t type;
695 : :
696 : 386 : nlh = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
697 : 386 : nfm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *nfm);
698 [ - + ]: 386 : if (!nfm) {
699 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Received bad nfnl message (no nfgenmsg).");
700 : 0 : return false;
701 : : }
702 [ - + ]: 386 : if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != NFNL_SUBSYS_CTNETLINK) {
703 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Received non-conntrack message (subsystem: %u).",
704 : : NFNL_SUBSYS_ID(nlh->nlmsg_type));
705 : 0 : return false;
706 : : }
707 [ - + ]: 386 : if (nfm->version != NFNETLINK_V0) {
708 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Received unsupported nfnetlink version (%u).",
709 : : NFNL_MSG_TYPE(nfm->version));
710 : 0 : return false;
711 : : }
712 : :
713 [ - + ]: 386 : if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof *nfm,
714 : : nfnlgrp_conntrack_policy, attrs,
715 : : ARRAY_SIZE(nfnlgrp_conntrack_policy))) {
716 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Received bad nfnl message (policy).");
717 : 0 : return false;
718 : : }
719 : :
720 : 386 : type = NFNL_MSG_TYPE(nlh->nlmsg_type);
721 : 386 : *nfgen_family = nfm->nfgen_family;
722 : :
723 [ + - - ]: 386 : switch (type) {
724 : : case IPCTNL_MSG_CT_NEW:
725 [ - + ]: 386 : *event_type = nlh->nlmsg_flags & NLM_F_CREATE
726 : : ? NL_CT_EVENT_NEW : NL_CT_EVENT_UPDATE;
727 : 386 : break;
728 : : case IPCTNL_MSG_CT_DELETE:
729 : 0 : *event_type = NL_CT_EVENT_DELETE;
730 : 0 : break;
731 : : default:
732 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Can't parse conntrack event type.");
733 : 0 : return false;
734 : : }
735 : :
736 : 386 : return true;
737 : : }
738 : :
739 : : static bool
740 : 386 : nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry,
741 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)],
742 : : uint8_t nfgen_family)
743 : : {
744 [ - + ]: 386 : if (!nl_ct_parse_tuple(attrs[CTA_TUPLE_ORIG], &entry->tuple_orig,
745 : : nfgen_family)) {
746 : 0 : return false;
747 : : }
748 [ - + ]: 386 : if (!nl_ct_parse_tuple(attrs[CTA_TUPLE_REPLY], &entry->tuple_reply,
749 : : nfgen_family)) {
750 : 0 : return false;
751 : : }
752 [ - + # # ]: 386 : if (attrs[CTA_COUNTERS_ORIG] &&
753 : 0 : !nl_ct_parse_counters(attrs[CTA_COUNTERS_ORIG],
754 : : &entry->counters_orig)) {
755 : 0 : return false;
756 : : }
757 [ - + # # ]: 386 : if (attrs[CTA_COUNTERS_REPLY] &&
758 : 0 : !nl_ct_parse_counters(attrs[CTA_COUNTERS_REPLY],
759 : : &entry->counters_reply)) {
760 : 0 : return false;
761 : : }
762 [ - + # # ]: 386 : if (attrs[CTA_TIMESTAMP] &&
763 : 0 : !nl_ct_parse_timestamp(attrs[CTA_TIMESTAMP], &entry->timestamp)) {
764 : 0 : return false;
765 : : }
766 [ + - ]: 386 : if (attrs[CTA_ID]) {
767 : 386 : entry->id = ntohl(nl_attr_get_be32(attrs[CTA_ID]));
768 : : }
769 [ + + ]: 386 : if (attrs[CTA_ZONE]) {
770 : 294 : entry->zone = ntohs(nl_attr_get_be16(attrs[CTA_ZONE]));
771 : : }
772 [ + - ]: 386 : if (attrs[CTA_STATUS]) {
773 : 386 : entry->status = ips_status_to_dpif_flags(
774 : 386 : ntohl(nl_attr_get_be32(attrs[CTA_STATUS])));
775 : : }
776 [ + - ]: 386 : if (attrs[CTA_TIMEOUT]) {
777 : 386 : entry->timeout = ntohl(nl_attr_get_be32(attrs[CTA_TIMEOUT]));
778 : : }
779 [ + - ]: 386 : if (attrs[CTA_MARK]) {
780 : 386 : entry->mark = ntohl(nl_attr_get_be32(attrs[CTA_MARK]));
781 : : }
782 [ + + ]: 386 : if (attrs[CTA_LABELS]) {
783 [ + - ]: 2 : memcpy(&entry->labels, nl_attr_get(attrs[CTA_LABELS]),
784 : 2 : MIN(sizeof entry->labels, nl_attr_get_size(attrs[CTA_LABELS])));
785 : : }
786 [ + + - + ]: 754 : if (attrs[CTA_PROTOINFO] &&
787 : 368 : !nl_ct_parse_protoinfo(attrs[CTA_PROTOINFO], &entry->protoinfo)) {
788 : 0 : return false;
789 : : }
790 [ + + - + ]: 400 : if (attrs[CTA_HELP] &&
791 : 14 : !nl_ct_parse_helper(attrs[CTA_HELP], &entry->helper)) {
792 : 0 : return false;
793 : : }
794 [ + + - + ]: 397 : if (attrs[CTA_TUPLE_MASTER] &&
795 : 11 : !nl_ct_parse_tuple(attrs[CTA_TUPLE_MASTER], &entry->tuple_master,
796 : : nfgen_family)) {
797 : 0 : return false;
798 : : }
799 : 386 : return true;
800 : : }
801 : :
802 : : bool
803 : 0 : nl_ct_parse_entry(struct ofpbuf *buf, struct ct_dpif_entry *entry,
804 : : enum nl_ct_event_type *event_type)
805 : : {
806 : : struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)];
807 : : uint8_t nfgen_family;
808 : :
809 : 0 : memset(entry, 0, sizeof *entry);
810 [ # # ]: 0 : if (!nl_ct_parse_header_policy(buf, event_type, &nfgen_family, attrs)) {
811 : 0 : return false;
812 : : };
813 : :
814 [ # # ]: 0 : if (!nl_ct_attrs_to_ct_dpif_entry(entry, attrs, nfgen_family)) {
815 : 0 : ct_dpif_entry_uninit(entry);
816 : 0 : memset(entry, 0, sizeof *entry);
817 : 0 : return false;
818 : : }
819 : :
820 : 0 : return true;
821 : : }
822 : :
823 : : /* NetFilter utility functions. */
824 : :
825 : : /* Puts a nlmsghdr and nfgenmsg at the beginning of 'msg', which must be
826 : : * initially empty. 'expected_payload' should be an estimate of the number of
827 : : * payload bytes to be supplied; if the size of the payload is unknown a value
828 : : * of 0 is acceptable.
829 : : *
830 : : * Non-zero 'family' is the address family of items to get (e.g. AF_INET).
831 : : *
832 : : * 'flags' is a bit-mask that indicates what kind of request is being made. It
833 : : * is often NLM_F_REQUEST indicating that a request is being made, commonly
834 : : * or'd with NLM_F_ACK to request an acknowledgement. NLM_F_DUMP flag reguests
835 : : * a dump of the table.
836 : : *
837 : : * 'subsystem' is a netfilter subsystem id, e.g., NFNL_SUBSYS_CTNETLINK.
838 : : *
839 : : * 'cmd' is an enumerated value specific to the 'subsystem'.
840 : : *
841 : : * Sets the new nlmsghdr's nlmsg_pid field to 0 for now. nl_sock_send() will
842 : : * fill it in just before sending the message.
843 : : *
844 : : * nl_msg_put_nlmsghdr() should be used to compose Netlink messages that are
845 : : * not NetFilter Netlink messages. */
846 : : static void
847 : 113 : nl_msg_put_nfgenmsg(struct ofpbuf *msg, size_t expected_payload,
848 : : int family, uint8_t subsystem, uint8_t cmd,
849 : : uint32_t flags)
850 : : {
851 : : struct nfgenmsg *nfm;
852 : :
853 : 113 : nl_msg_put_nlmsghdr(msg, sizeof *nfm + expected_payload,
854 : 113 : subsystem << 8 | cmd, flags);
855 [ - + ]: 113 : ovs_assert(msg->size == NLMSG_HDRLEN);
856 : 113 : nfm = nl_msg_put_uninit(msg, sizeof *nfm);
857 : 113 : nfm->nfgen_family = family;
858 : 113 : nfm->version = NFNETLINK_V0;
859 : 113 : nfm->res_id = 0;
860 : : #ifdef _WIN32
861 : : /* nfgenmsg contains ovsHdr padding in windows */
862 : : nfm->ovsHdr.dp_ifindex = 0;
863 : : #endif
864 : 113 : }
|