summaryrefslogtreecommitdiffstats
path: root/src/include/radsniff.h
blob: c909ecdf0733173ce62b08bbfad3043323e4dd12 (plain)
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
 *   This program is is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License, version 2 of the
 *   License as published by the Free Software Foundation.
 *
 *   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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

/**
 * $Id$
 * @file radsniff.h
 * @brief Structures and prototypes for the RADIUS sniffer.
 *
 * @copyright 2013 Arran Cudbard-Bell <arran.cudbardb@freeradius.org>
 * @copyright 2006 The FreeRADIUS server project
 * @copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
 */

RCSIDH(radsniff_h, "$Id$")

#include <sys/types.h>

#include <freeradius-devel/libradius.h>
#include <freeradius-devel/pcap.h>
#include <freeradius-devel/event.h>

#ifdef HAVE_COLLECTDC_H
#  include <collectd/client.h>
#endif

#define RS_DEFAULT_PREFIX	"radsniff"	//!< Default instance
#define RS_DEFAULT_SECRET	"testing123"	//!< Default secret
#define RS_DEFAULT_TIMEOUT	5200		//!< Standard timeout of 5s + 300ms to cover network latency
#define RS_FORCE_YIELD		1000		//!< Service another descriptor every X number of packets
#define RS_RETRANSMIT_MAX	5		//!< Maximum number of times we expect to see a packet retransmitted
#define RS_MAX_ATTRS		50		//!< Maximum number of attributes we can filter on.
#define RS_SOCKET_REOPEN_DELAY  5000		//!< How long we delay re-opening a collectd socket.

/*
 *	Logging macros
 */
#undef DEBUG2
#define DEBUG2(fmt, ...)	if (fr_debug_lvl > 2) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
#undef DEBUG
#define DEBUG(fmt, ...)		if (fr_debug_lvl > 1) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
#undef INFO
#define INFO(fmt, ...)		if (fr_debug_lvl > 0) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)

#define ERROR(fmt, ...)		fr_perror("radsniff: " fmt, ## __VA_ARGS__)

#define RIDEBUG_ENABLED()	(conf->print_packet && (fr_debug_lvl > 0))
#define RDEBUG_ENABLED()	(conf->print_packet && (fr_debug_lvl > 1))
#define RDEBUG_ENABLED2()	(conf->print_packet && (fr_debug_lvl > 2))

#define REDEBUG(fmt, ...)	if (conf->print_packet) fr_perror("%s (%" PRIu64 ") " fmt , timestr, count, ## __VA_ARGS__)
#define RIDEBUG(fmt, ...)	if (conf->print_packet && (fr_debug_lvl > 0)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
#define RDEBUG(fmt, ...)	if (conf->print_packet && (fr_debug_lvl > 1)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
#define RDEBUG2(fmt, ...)	if (conf->print_packet && (fr_debug_lvl > 2)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)

typedef enum {
	RS_NORMAL	= 0x01,
	RS_UNLINKED	= 0x02,
	RS_RTX		= 0x04,
	RS_REUSED	= 0x08,
	RS_ERROR	= 0x10,
	RS_LOST		= 0x20
} rs_status_t;

typedef void (*rs_packet_logger_t)(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
				   struct timeval *elapsed, struct timeval *latency, bool response, bool body);
typedef enum {
#ifdef HAVE_COLLECTDC_H
	RS_STATS_OUT_COLLECTD = 1,
#endif
	RS_STATS_OUT_STDIO
} stats_out_t;

typedef struct rs rs_t;

#ifdef HAVE_COLLECTDC_H
typedef struct rs_stats_tmpl rs_stats_tmpl_t;
typedef struct rs_stats_value_tmpl rs_stats_value_tmpl_t;
#endif

typedef struct rs_counters {
	uint64_t type[PW_CODE_MAX];
} rs_counters_t;

/** Stats for a single interval
 *
 * And interval is defined as the time between a call to the stats output function.
 */
typedef struct rs_latency {
	int			intervals;			//!< Number of stats intervals.

	double			latency_smoothed;		//!< Smoothed moving average.
	uint64_t		latency_smoothed_count;		//!< Number of CMA datapoints processed.

	struct {
		uint64_t		received_total;		//!< Total received over interval.
		uint64_t		linked_total;		//!< Total request/response pairs over interval.
		uint64_t		unlinked_total;		//!< Total unlinked over interval.
		uint64_t		reused_total;		//!< Total reused over interval.
		uint64_t		lost_total;		//!< Total packets definitely lost in this interval.
		uint64_t		rt_total[RS_RETRANSMIT_MAX + 1];	//!< Number of RTX until complete
										//!< over interval.


		double			received;		//!< Number of this type of packet we've received.
		double			linked;			//!< Number of request/response pairs
		double			unlinked;		//!< Response with no request.
		double			reused;			//!< ID re-used too quickly.
		double			lost;			//!< Never got a response to a request.
		double			rt[RS_RETRANSMIT_MAX + 1];	//!< Number of times we saw the same
									//!< request packet.

		long double		latency_total;		//!< Total latency between requests/responses in the
								//!< interval.
		double			latency_average;	//!< Average latency (this iteration).

		double			latency_high;		//!< Latency high water mark.
		double			latency_low;		//!< Latency low water mark.
	} interval;
} rs_latency_t;

typedef struct rs_malformed {
	uint64_t		min_length_packet;
	uint64_t		min_length_field;
	uint64_t		min_length_mimatch;
	uint64_t		header_overflow;
	uint64_t		invalid_attribute;
	uint64_t		attribute_too_short;
	uint64_t		attribute_overflow;
	uint64_t		ma_invalid_length;
	uint64_t		attribute_underflow;
	uint64_t		too_many_attributes;
	uint64_t		ma_missing;
} rs_malformed_t;

/** One set of statistics
 *
 */
typedef struct rs_stats {
	int			intervals;		//!< Number of stats intervals.

	rs_latency_t		exchange[PW_CODE_MAX];  //!< We end up allocating ~16K, but memory is cheap so
							//!< what the hell.  This is required because instances of
							//!< FreeRADIUS delay Access-Rejects, which would artificially
							//!< increase latency stats for Access-Requests.

	struct timeval		quiet;			//!< We may need to 'mute' the stats if libpcap starts
							//!< dropping packets, or we run out of memory.
} rs_stats_t;

typedef struct rs_capture {
	struct pcap_pkthdr	*header;		//!< PCAP packet header.
	uint8_t			*data;			//!< PCAP packet data.
} rs_capture_t;

/** Wrapper for RADIUS_PACKET
 *
 * Allows an event to be associated with a request packet.  This is required because we need to disarm
 * the event timer when a response is received, so we don't erroneously log the response as lost.
 */
typedef struct rs_request {
	uint64_t		id;			//!< Monotonically increasing packet counter.
	fr_event_t		*event;			//!< Event created when we received the original request.

	bool			logged;			//!< Whether any messages regarding this request were logged.

	struct timeval		when;			//!< Time when the packet was received, or next time an event
							//!< is scheduled.
	fr_pcap_t		*in;			//!< PCAP handle the original request was received on.
	RADIUS_PACKET		*packet;		//!< The original packet.
	RADIUS_PACKET		*expect;		//!< Request/response.
	RADIUS_PACKET		*linked;		//!< The subsequent response or forwarded request the packet
							//!< was linked against.


	rs_capture_t		capture[RS_RETRANSMIT_MAX];	//!< Buffered request packets (if a response filter
								//!< has been applied).
	rs_capture_t		*capture_p;			//!< Next packet slot.

	uint64_t		rt_req;			//!< Number of times we saw the same request packet.
	uint64_t		rt_rsp;			//!< Number of times we saw a retransmitted response
							//!< packet.
	rs_latency_t		*stats_req;		//!< Latency entry for the request type.
	rs_latency_t		*stats_rsp;		//!< Latency entry for the request type.

	bool			silent_cleanup;		//!< Cleanup was forced before normal expiry period,
							//!< ignore stats about packet loss.

	VALUE_PAIR		*link_vps;		//!< VALUE_PAIRs used to link retransmissions.

	bool			in_request_tree;	//!< Whether the request is currently in the request tree.
	bool			in_link_tree;		//!< Whether the request is currently in the linked tree.
} rs_request_t;

/** Statistic write/print event
 *
 */
typedef struct rs_event {
	fr_event_list_t		*list;			//!< The event list.

	fr_pcap_t		*in;			//!< PCAP handle event occurred on.
	fr_pcap_t		*out;			//!< Where to write output.

	rs_stats_t		*stats;			//!< Where to write stats.
} rs_event_t;

/** FD data which gets passed to callbacks
 *
 */
typedef struct rs_update {
	fr_event_list_t		*list;			//!< List to insert new event into.

	fr_pcap_t		*in;			//!< Linked list of PCAP handles to check for drops.
	rs_stats_t		*stats;			//!< Stats to process.
} rs_update_t;


struct rs {
	bool			from_file;		//!< Were reading pcap data from files.
	bool			from_dev;		//!< Were reading pcap data from devices.
	bool			from_stdin;		//!< Were reading pcap data from stdin.
	bool			to_file;		//!< Were writing pcap data to files.
	bool			to_stdout;		//!< Were writing pcap data to stdout.

	bool			daemonize;		//!< Daemonize and write PID out to file.
	char const		*pidfile;		//!< File to write PID to.

	bool			from_auto;		//!< From list was auto-generated.
	bool			promiscuous;		//!< Capture in promiscuous mode.
	bool			print_packet;		//!< Print packet info, disabled with -W
	bool			decode_attrs;		//!< Whether we should decode attributes in the request
							//!< and response.
	bool			verify_udp_checksum;	//!< Check UDP checksum in packets.

	char const		*radius_secret;		//!< Secret to decode encrypted attributes.

	char			*pcap_filter;		//!< PCAP filter string applied to live capture devices.

	char			*list_attributes;	//!< Raw attribute filter string.
	DICT_ATTR const 	*list_da[RS_MAX_ATTRS]; //!< Output CSV with these attribute values.
	int			list_da_num;

	char			*link_attributes;	//!< Names of DICT_ATTRs to use for rtx.
	DICT_ATTR const		*link_da[RS_MAX_ATTRS];	//!< DICT_ATTRs to link on.
	int			link_da_num;		//!< Number of rtx DICT_ATTRs.

	char const		*filter_request;	//!< Raw request filter string.
	char const		*filter_response;	//!< Raw response filter string.

	VALUE_PAIR 		*filter_request_vps;	//!< Sorted filter vps.
	VALUE_PAIR 		*filter_response_vps;	//!< Sorted filter vps.
	PW_CODE			filter_request_code;	//!< Filter request packets by code.
	PW_CODE			filter_response_code;	//!< Filter response packets by code.

	rs_status_t		event_flags;		//!< Events we log and capture on.
	rs_packet_logger_t	logger;			//!< Packet logger

	int			buffer_pkts;		//!< Size of the ring buffer to setup for live capture.
	uint64_t		limit;			//!< Maximum number of packets to capture

	struct {
		int			interval;		//!< Time between stats updates in seconds.
		stats_out_t		out;			//!< Where to write stats.
		int			timeout;		//!< Maximum length of time we wait for a response.

#ifdef HAVE_COLLECTDC_H
		char const		*collectd;		//!< Collectd server/port/unixsocket
		char const		*prefix;		//!< Prefix collectd stats with this value.
		lcc_connection_t	*handle;		//!< Collectd client handle.
		rs_stats_tmpl_t		*tmpl;			//!< The stats templates we created on startup.
#endif
	} stats;
};

#ifdef HAVE_COLLECTDC_H

/** Callback for processing stats values.
 *
 */
typedef void (*rs_stats_cb_t)(rs_t *conf, rs_stats_value_tmpl_t *tmpl);
struct rs_stats_value_tmpl {
	void			*src;			//!< Pointer to source field in struct. Must be set by
							//!< stats_collectdc_init caller.
	int			type;			//!< Stats type.
	rs_stats_cb_t		cb;			//!< Callback used to process stats
	void			*dst;			//!< Pointer to dst field in value struct. Must be set
							//!< by stats_collectdc_init caller.
};

/** Stats templates
 *
 * This gets processed to turn radsniff stats structures into collectd lcc_value_list_t structures.
 */
struct rs_stats_tmpl
{
	rs_stats_value_tmpl_t	*value_tmpl;		//!< Value template
	void			*stats;			//!< Struct containing the raw stats to process
	lcc_value_list_t	*value;			//!< Collectd stats struct to populate

	rs_stats_tmpl_t		*next;			//!< Next...
};

/*
 *	collectd.c - Registration and processing functions
 */
rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
						char const *type, rs_latency_t *stats, PW_CODE code);
void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now);
int rs_stats_collectd_open(rs_t *conf);
int rs_stats_collectd_close(rs_t *conf);

#endif