File: whack_briefconnectionstatus.c

package info (click to toggle)
libreswan 5.2-2.3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 81,644 kB
  • sloc: ansic: 129,988; sh: 32,018; xml: 20,646; python: 10,303; makefile: 3,022; javascript: 1,506; sed: 574; yacc: 511; perl: 264; awk: 52
file content (205 lines) | stat: -rw-r--r-- 6,238 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* ipsec briefconnectionstatus, for libreswan
 *
 * Copyright (C) 2023  Brady Johnson <bradyjoh@redhat.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

#include "whack_briefconnectionstatus.h"
#include "whack_connectionstatus.h"

#include "visit_connection.h"

#include "ike_alg.h"

#include "defs.h"
#include "connections.h"
#include "orient.h"
#include "virtual_ip.h"        /* needs connections.h */
#include "iface.h"
#include "nat_traversal.h"
#include "log.h"
#include "show.h"
#include "crypto.h"		/* for show_ike_alg_connection() */
#include "plutoalg.h"	/* for show_kernel_alg_connection() */
#include "kernel.h"		/* for enum direction */

/*
 * Brief connection status functions
 */

static uint64_t get_child_bytes(const struct connection *c, enum direction direction)
{
	uint64_t bytes = 0;

	/* Code partially copied from whack_trafficstatus.c
	 *
	 * Look for all states with C as the connection.  And then
	 * from there dump the traffic status of any children.
	 *
	 * Using .established_child_sa or .negotiating_child_sa isn't
	 * sufficient as this won't include established Child SAs that
	 * are in the process of being replaced.
	 */

	struct state_filter state_by_connection = {
	    .connection_serialno = c->serialno,
	    .search = {
		    .order = OLD2NEW,
		    .verbose.logger = &global_logger,
		    .where = HERE,
	    },
	};
	while (next_state(&state_by_connection)) {

		struct state *st = state_by_connection.st;

		if (IS_IKE_SA(st)) {
			continue;
		}

		if (!IS_IPSEC_SA_ESTABLISHED(st)) {
			continue;
		}

		/* note: this mutates *st by calling
		 * get_sa_bundle_info */
		struct child_sa *child = pexpect_child_sa(st);
		struct ipsec_proto_info *first_ipsec_proto =
		    (child->sa.st_esp.protocol == &ip_protocol_esp ? &child->sa.st_esp:
		     child->sa.st_ah.protocol == &ip_protocol_ah ? &child->sa.st_ah :
		     child->sa.st_ipcomp.protocol == &ip_protocol_ipcomp ? &child->sa.st_ipcomp :
		     NULL);
		passert(first_ipsec_proto != NULL);

		// direction should be one of [DIRECTION_INBOUND, DIRECTION_OUTBOUND]
		if (! get_ipsec_traffic(child, first_ipsec_proto, direction)) {
			continue;
		}

		if (direction == DIRECTION_INBOUND) {
			bytes += first_ipsec_proto->inbound.bytes;
		} else if (direction == DIRECTION_OUTBOUND) {
			bytes += first_ipsec_proto->outbound.bytes;
		}

	}

	return bytes;
}

static void show_one_client_brief(struct show *s,
				  const struct connection *c,
				  const struct connection_client *this,
				  const struct connection_client *that)
{
	bool local_client_eq_host = false;
	bool remote_client_eq_host = false;

	// jam_end_client() will do nothing if client == host
	if (selector_eq_address(this->client, this->host->addr)) {
		local_client_eq_host = true;
	}
	if (selector_eq_address(that->client, that->host->addr)) {
		remote_client_eq_host = true;
	}

	SHOW_JAMBUF(s, buf) {
		if (local_client_eq_host == false && remote_client_eq_host == false) {
			// subnet-to-subnet
			jam_end_client(buf, c, this, LEFT_END, NULL);
			jam_string(buf, " <==> ");
			jam_end_client(buf, c, that, RIGHT_END, NULL);
		} else if (local_client_eq_host == false) {
			// subnet-to-host
			jam_end_client(buf, c, this, LEFT_END, NULL);
			jam_string(buf, " <==> ");
			jam_end_host(buf, c, that->host);
		} else if (remote_client_eq_host == false) {
			// host-to-subnet
			jam_end_host(buf, c, this->host);
			jam_string(buf, " <==> ");
			jam_end_client(buf, c, that, RIGHT_END, NULL);
		} else {
			// if local_client_eq_host == true && remote_client_eq_host == true
			// then its a host-to-host transport tunnel
			jam_end_host(buf, c, this->host);
			jam_string(buf, " <==> ");
			jam_end_host(buf, c, that->host);
		}

		jam_string(buf, "\tfrom ");
		jam_end_host(buf, c, this->host);
		jam_string(buf, " to ");
		jam_end_host(buf, c, that->host);
		jam_humber_uintmax(buf, " (", get_child_bytes(c, DIRECTION_INBOUND), "B");
		jam_humber_uintmax(buf, "/", get_child_bytes(c, DIRECTION_OUTBOUND), "B)\t");

		jam_connection_short(buf, c);
		jam(buf, ", reqid="PRI_REQID, pri_reqid(c->child.reqid));
	}
}

static void show_brief_connection_status(struct show *s, const struct connection *c)
{
	/* Show topology. */
	show_connection_clients(s, c, show_one_client_brief);
}

static void show_brief_connection_statuses(struct show *s)
{
	int count = 0;
	int active = 0;

	struct connection **connections = sort_connections();
	if (connections != NULL) {
		/* make an array of connections, sort it, and report it */
		for (struct connection **c = connections; *c != NULL; c++) {
			count++;
			if ((*c)->routing.state == RT_ROUTED_TUNNEL ||
			    (*c)->routing.state == RT_UNROUTED_TUNNEL) {
				active++;
				show_brief_connection_status(s, *c);
			}
		}
		pfree(connections);
	}

	show(s, "# Total IPsec connections: loaded %d, active %d",
		     count, active);
}

/* Callback function from whack_briefconnectionstatus() -> whack_connections_bottom_up() */
static unsigned whack_briefconnectionstatus_cb(const struct whack_message *m UNUSED,
					struct show *s,
					struct connection *c)
{
	show_brief_connection_status(s, c);
	return 1; /* the connection counts */
}

/* Main API entry point for brief connection status */
void whack_briefconnectionstatus(const struct whack_message *m, struct show *s)
{
	if (m->name == NULL) {
		// Display all active connections
		show_brief_connection_statuses(s);
		return;
	}

	/* Iterate the connections looking for the m->name connection. Calls the
	 * whack_briefconnectionstatus_cb() callback if found, which directly calls
	 * show_brief_connection_status() */
	whack_connections_bottom_up(m, s, whack_briefconnectionstatus_cb,
				    (struct each) {
					    .log_unknown_name = true,
				    });
}