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
|