The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
radsniff.h
Go to the documentation of this file.
1 #pragma once
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16  */
17 
18 /**
19  * $Id: 10c2de3c134cbcb676f0c24bcb98032b29ee09e7 $
20  *
21  * @file radsniff.h
22  * @brief Structures and prototypes for the RADIUS sniffer.
23  *
24  * @copyright 2013 Arran Cudbard-Bell (arran.cudbardb@freeradius.org)
25  * @copyright 2006 The FreeRADIUS server project
26  * @copyright 2006 Nicolas Baradakis (nicolas.baradakis@cegetel.net)
27  */
28 RCSIDH(radsniff_h, "$Id: 10c2de3c134cbcb676f0c24bcb98032b29ee09e7 $")
29 
30 #include <sys/types.h>
31 
32 #include <freeradius-devel/util/pcap.h>
33 #include <freeradius-devel/util/event.h>
34 #include <freeradius-devel/radius/radius.h>
35 
36 #ifdef HAVE_COLLECTDC_H
37 # include <collectd/client.h>
38 #endif
39 
40 #define RS_DEFAULT_PREFIX "radsniff" //!< Default instance
41 #define RS_DEFAULT_SECRET "testing123" //!< Default secret
42 #define RS_DEFAULT_TIMEOUT 5200 //!< Standard timeout of 5s + 300ms to cover network latency
43 #define RS_FORCE_YIELD 1000 //!< Service another descriptor every X number of packets
44 #define RS_RETRANSMIT_MAX 5 //!< Maximum number of times we expect to see a packet retransmitted
45 #define RS_MAX_ATTRS 50 //!< Maximum number of attributes we can filter on.
46 #define RS_SOCKET_REOPEN_DELAY 5000 //!< How long we delay re-opening a collectd socket.
47 
48 /*
49  * Logging macros
50  */
51 #undef DEBUG2
52 #define DEBUG2(fmt, ...) if (fr_debug_lvl > 2) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
53 #undef DEBUG
54 #define DEBUG(fmt, ...) if (fr_debug_lvl > 1) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
55 #undef INFO
56 #define INFO(fmt, ...) if (fr_debug_lvl > 0) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
57 
58 #define ERROR(fmt, ...) fr_perror("radsniff: " fmt, ## __VA_ARGS__)
59 
60 #define RIDEBUG_ENABLED() (conf->print_packet && (fr_debug_lvl > 0))
61 #define RDEBUG_ENABLED() (conf->print_packet && (fr_debug_lvl > 1))
62 #define RDEBUG_ENABLED2() (conf->print_packet && (fr_debug_lvl > 2))
63 
64 #define REDEBUG(fmt, ...) if (conf->print_packet) fr_perror("%s (%" PRIu64 ") " fmt , timestr, count, ## __VA_ARGS__)
65 #define RIDEBUG(fmt, ...) if (conf->print_packet && (fr_debug_lvl > 0)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
66 #define RDEBUG(fmt, ...) if (conf->print_packet && (fr_debug_lvl > 1)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
67 #define RDEBUG2(fmt, ...) if (conf->print_packet && (fr_debug_lvl > 2)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
68 
69 typedef enum {
70  RS_NORMAL = 0x01,
71  RS_UNLINKED = 0x02,
72  RS_RTX = 0x04,
73  RS_REUSED = 0x08,
74  RS_ERROR = 0x10,
75  RS_LOST = 0x20
77 
78 typedef void (*rs_packet_logger_t)(uint64_t count, rs_status_t status, fr_pcap_t *handle,
79  fr_packet_t *packet, fr_pair_list_t *list,
80  struct timeval *elapsed, struct timeval *latency, bool response, bool body);
81 typedef enum {
82 #ifdef HAVE_COLLECTDC_H
83  RS_STATS_OUT_COLLECTD = 1,
84 #endif
88 
89 typedef struct rs rs_t;
90 
91 #ifdef HAVE_COLLECTDC_H
92 typedef struct rs_stats_tmpl rs_stats_tmpl_t;
93 typedef struct rs_stats_value_tmpl rs_stats_value_tmpl_t;
94 #endif
95 
96 typedef struct {
97  uint64_t type[FR_RADIUS_CODE_MAX+ 1];
99 
100 typedef struct CC_HINT(__packed__) {
103  uint8_t length[2];
107 
108 /** Stats for a single interval
109  *
110  * And interval is defined as the time between a call to the stats output function.
111  */
112 typedef struct {
113  int intervals; //!< Number of stats intervals.
114 
115  double latency_smoothed; //!< Smoothed moving average.
116  uint64_t latency_smoothed_count; //!< Number of CMA datapoints processed.
117 
118  struct {
119  uint64_t received_total; //!< Total received over interval.
120  uint64_t linked_total; //!< Total request/response pairs over interval.
121  uint64_t unlinked_total; //!< Total unlinked over interval.
122  uint64_t reused_total; //!< Total reused over interval.
123  uint64_t lost_total; //!< Total packets definitely lost in this interval.
124  uint64_t rt_total[RS_RETRANSMIT_MAX + 1]; //!< Number of RTX until complete
125  //!< over interval.
126 
127 
128  double received; //!< Number of this type of packet we've received.
129  double linked; //!< Number of request/response pairs
130  double unlinked; //!< Response with no request.
131  double reused; //!< ID re-used too quickly.
132  double lost; //!< Never got a response to a request.
133  double rt[RS_RETRANSMIT_MAX + 1]; //!< Number of times we saw the same
134  //!< request packet.
135 
136  long double latency_total; //!< Total latency between requests/responses in the
137  //!< interval.
138  double latency_average; //!< Average latency (this iteration).
139 
140  double latency_high; //!< Latency high water mark.
141  double latency_low; //!< Latency low water mark.
142  } interval;
143 } rs_latency_t;
144 
145 typedef struct {
149  uint64_t header_overflow;
156  uint64_t ma_missing;
158 
159 /** One set of statistics
160  *
161  */
162 typedef struct {
163  int intervals; //!< Number of stats intervals.
164 
165  rs_latency_t exchange[FR_RADIUS_CODE_MAX+ 1]; //!< We end up allocating ~16K, but memory is cheap so
166  //!< what the hell. This is required because instances of
167  //!< FreeRADIUS delay Access-Rejects, which would artificially
168  //!< increase latency stats for Access-Requests.
169 
170  struct timeval quiet; //!< We may need to 'mute' the stats if libpcap starts
171  //!< dropping packets, or we run out of memory.
172 } rs_stats_t;
173 
174 typedef struct {
175  struct pcap_pkthdr *header; //!< PCAP packet header.
176  uint8_t *data; //!< PCAP packet data.
177 } rs_capture_t;
178 
179 /** Wrapper for fr_packet_t
180  *
181  * Allows an event to be associated with a request packet. This is required because we need to disarm
182  * the event timer when a response is received, so we don't erroneously log the response as lost.
183  */
184 typedef struct {
185  uint64_t id; //!< Monotonically increasing packet counter.
186  fr_event_timer_t const *event; //!< Event created when we received the original request.
187 
188  bool logged; //!< Whether any messages regarding this request were logged.
189 
190  struct timeval when; //!< Time when the packet was received, or next time an event
191  //!< is scheduled.
192  fr_pcap_t *in; //!< PCAP handle the original request was received on.
193  fr_packet_t *packet; //!< The original packet.
195  fr_packet_t *expect; //!< Request/response.
197  fr_packet_t *linked; //!< The subsequent response or forwarded request the packet
198  //!< was linked against.
199  fr_pair_list_t link_vps; //!< fr_pair_ts used to link retransmissions.
200 
201  rs_capture_t capture[RS_RETRANSMIT_MAX]; //!< Buffered request packets (if a response filter
202  //!< has been applied).
203  rs_capture_t *capture_p; //!< Next packet slot.
204 
205  uint64_t rt_req; //!< Number of times we saw the same request packet.
206  uint64_t rt_rsp; //!< Number of times we saw a retransmitted response
207  //!< packet.
208  rs_latency_t *stats_req; //!< Latency entry for the request type.
209  rs_latency_t *stats_rsp; //!< Latency entry for the request type.
210 
211  bool silent_cleanup; //!< Cleanup was forced before normal expiry period,
212  //!< ignore stats about packet loss.
213 
214 
217  bool in_request_tree; //!< Whether the request is currently in the request tree.
218  bool in_link_tree; //!< Whether the request is currently in the linked tree.
219 } rs_request_t;
220 
221 /** Statistic write/print event
222  *
223  */
224 typedef struct {
225  fr_event_list_t *list; //!< The event list.
226 
227  fr_pcap_t *in; //!< PCAP handle event occurred on.
228  fr_pcap_t *out; //!< Where to write output.
229 
230  rs_stats_t *stats; //!< Where to write stats.
231 } rs_event_t;
232 
233 typedef struct rs_update rs_update_t;
234 
235 /** Callback for printing stats header.
236  *
237  */
239 
240 /** Callback for printing stats values.
241  *
242  */
243 typedef void (*rs_stats_print_cb_t)(rs_update_t *this, rs_stats_t *stats, struct timeval *now);
244 
245 
246 /** FD data which gets passed to callbacks
247  *
248  */
249 struct rs_update {
250  bool done_header; //!< Have we printed the stats header?
251  fr_event_list_t *list; //!< List to insert new event into.
252 
253  fr_pcap_t *in; //!< Linked list of PCAP handles to check for drops.
254  rs_stats_t *stats; //!< Stats to process.
255  rs_stats_print_header_cb_t head; //!< Print header.
256  rs_stats_print_cb_t body; //!< Print body.
257 };
258 
259 struct rs {
260  bool from_file; //!< Were reading pcap data from files.
261  bool from_dev; //!< Were reading pcap data from devices.
262  bool from_stdin; //!< Were reading pcap data from stdin.
263  bool to_file; //!< Were writing pcap data to files.
264  bool to_stdout; //!< Were writing pcap data to stdout.
265 
266  bool to_output_dir; //!< Were writing attributes into directory.
267  char const *output_dir; //!< Where we should save the files $PATH/requests.txt and $PATH/reply.txt
268 
269  bool daemonize; //!< Daemonize and write PID out to file.
270  char const *pidfile; //!< File to write PID to.
271 
272  bool from_auto; //!< From list was auto-generated.
273  bool promiscuous; //!< Capture in promiscuous mode.
274  bool print_packet; //!< Print packet info, disabled with -W
275  bool decode_attrs; //!< Whether we should decode attributes in the request
276  //!< and response.
277  bool verify_udp_checksum; //!< Check UDP checksum in packets.
278  bool verify_radius_authenticator; //!< Check RADIUS authenticator in packets.
279 
280  char *radius_secret; //!< Secret to decode encrypted attributes.
281 
282  char *pcap_filter; //!< PCAP filter string applied to live capture devices.
283  char *pcap_filter_vlan; //!< Variant of the normal filter to apply to devices
284  ///< which support VLAN tags.
285 
286  char *list_attributes; //!< Raw attribute filter string.
287  fr_dict_attr_t const *list_da[RS_MAX_ATTRS]; //!< Output CSV with these attribute values.
289 
290  char *link_attributes; //!< Names of fr_dict_attr_ts to use for rtx.
291  fr_dict_attr_t const *link_da[RS_MAX_ATTRS]; //!< fr_dict_attr_ts to link on.
292  int link_da_num; //!< Number of rtx fr_dict_attr_ts.
293 
294  char const *filter_request; //!< Raw request filter string.
295  char const *filter_response; //!< Raw response filter string.
296 
297  fr_pair_list_t filter_request_vps; //!< Sorted filter vps.
298  fr_pair_list_t filter_response_vps; //!< Sorted filter vps.
299  fr_radius_packet_code_t filter_request_code; //!< Filter request packets by code.
300  fr_radius_packet_code_t filter_response_code; //!< Filter response packets by code.
301 
302  rs_status_t event_flags; //!< Events we log and capture on.
303  rs_packet_logger_t logger; //!< Packet logger
304 
305  int buffer_pkts; //!< Size of the ring buffer to setup for live capture.
306  uint64_t limit; //!< Maximum number of packets to capture
307 
308  struct {
309  int interval; //!< Time between stats updates in seconds.
310  stats_out_t out; //!< Where to write stats.
311  int timeout; //!< Maximum length of time we wait for a response.
312 
313 #ifdef HAVE_COLLECTDC_H
314  char const *collectd; //!< Collectd server/port/unixsocket
315  char const *prefix; //!< Prefix collectd stats with this value.
316  lcc_connection_t *handle; //!< Collectd client handle.
317  rs_stats_tmpl_t *tmpl; //!< The stats templates we created on startup.
318 #endif
319  } stats;
320 };
321 
322 #ifdef HAVE_COLLECTDC_H
323 
324 /** Callback for processing stats values.
325  *
326  */
327 typedef void (*rs_stats_cb_t)(rs_t *conf, rs_stats_value_tmpl_t *tmpl);
328 
329 struct rs_stats_value_tmpl {
330  void *src; //!< Pointer to source field in struct. Must be set by
331  //!< stats_collectdc_init caller.
332  int type; //!< Stats type.
333  rs_stats_cb_t cb; //!< Callback used to process stats
334  void *dst; //!< Pointer to dst field in value struct. Must be set
335  //!< by stats_collectdc_init caller.
336 };
337 
338 /** Stats templates
339  *
340  * This gets processed to turn radsniff stats structures into collectd lcc_value_list_t structures.
341  */
342 struct rs_stats_tmpl
343 {
344  rs_stats_value_tmpl_t *value_tmpl; //!< Value template
345  void *stats; //!< Struct containing the raw stats to process
346  lcc_value_list_t *value; //!< Collectd stats struct to populate
347 
348  rs_stats_tmpl_t *next; //!< Next...
349 };
350 
351 /*
352  * collectd.c - Registration and processing functions
353  */
354 rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
355  char const *type, rs_latency_t *stats, fr_radius_packet_code_t code);
356 void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now);
357 int rs_stats_collectd_open(rs_t *conf);
358 int rs_stats_collectd_close(rs_t *conf);
359 #endif
#define RCSIDH(h, id)
Definition: build.h:482
next
Definition: dcursor.h:178
fr_radius_packet_code_t
RADIUS packet codes.
Definition: defs.h:31
@ FR_RADIUS_CODE_MAX
Maximum possible protocol code.
Definition: defs.h:53
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
Test enumeration values.
Definition: dict_test.h:92
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
unsigned char uint8_t
Definition: merged_model.c:30
#define RADIUS_AUTH_VECTOR_LENGTH
Definition: net.h:89
uint8_t code
Definition: radsniff.h:101
uint8_t id
Definition: radsniff.h:102
static rc_stats_t stats
Definition: radclient-ng.c:74
static bool quiet
Definition: radius1_test.c:71
static rs_t * conf
Definition: radsniff.c:53
rs_stats_t * stats
Where to write stats.
Definition: radsniff.h:230
fr_radius_packet_code_t filter_response_code
Filter response packets by code.
Definition: radsniff.h:300
fr_pcap_t * in
PCAP handle event occurred on.
Definition: radsniff.h:227
fr_packet_t * expect
Request/response.
Definition: radsniff.h:195
bool to_output_dir
Were writing attributes into directory.
Definition: radsniff.h:266
fr_event_timer_t const * event
Event created when we received the original request.
Definition: radsniff.h:186
uint64_t attribute_underflow
Definition: radsniff.h:154
rs_stats_print_cb_t body
Print body.
Definition: radsniff.h:256
bool print_packet
Print packet info, disabled with -W.
Definition: radsniff.h:274
uint64_t min_length_field
Definition: radsniff.h:147
uint8_t * data
PCAP packet data.
Definition: radsniff.h:176
char const * output_dir
Where we should save the files $PATH/requests.txt and $PATH/reply.txt.
Definition: radsniff.h:267
stats_out_t
Definition: radsniff.h:81
@ RS_STATS_OUT_STDIO_CSV
Definition: radsniff.h:86
@ RS_STATS_OUT_STDIO_FANCY
Definition: radsniff.h:85
bool in_request_tree
Whether the request is currently in the request tree.
Definition: radsniff.h:217
rs_status_t event_flags
Events we log and capture on.
Definition: radsniff.h:302
uint64_t latency_smoothed_count
Number of CMA datapoints processed.
Definition: radsniff.h:116
fr_rb_node_t link_node
Definition: radsniff.h:216
int link_da_num
Number of rtx fr_dict_attr_ts.
Definition: radsniff.h:292
fr_radius_packet_code_t filter_request_code
Filter request packets by code.
Definition: radsniff.h:299
rs_stats_t * stats
Stats to process.
Definition: radsniff.h:254
int buffer_pkts
Size of the ring buffer to setup for live capture.
Definition: radsniff.h:305
char const * filter_response
Raw response filter string.
Definition: radsniff.h:295
fr_event_list_t * list
List to insert new event into.
Definition: radsniff.h:251
int list_da_num
Definition: radsniff.h:288
bool from_dev
Were reading pcap data from devices.
Definition: radsniff.h:261
uint64_t min_length_packet
Definition: radsniff.h:146
rs_packet_logger_t logger
Packet logger.
Definition: radsniff.h:303
char * pcap_filter_vlan
Variant of the normal filter to apply to devices which support VLAN tags.
Definition: radsniff.h:283
char * pcap_filter
PCAP filter string applied to live capture devices.
Definition: radsniff.h:282
int intervals
Number of stats intervals.
Definition: radsniff.h:113
#define RS_MAX_ATTRS
Maximum number of attributes we can filter on.
Definition: radsniff.h:45
bool from_auto
From list was auto-generated.
Definition: radsniff.h:272
rs_latency_t * stats_req
Latency entry for the request type.
Definition: radsniff.h:208
bool done_header
Have we printed the stats header?
Definition: radsniff.h:250
uint64_t attribute_too_short
Definition: radsniff.h:151
bool decode_attrs
Whether we should decode attributes in the request and response.
Definition: radsniff.h:275
uint64_t id
Monotonically increasing packet counter.
Definition: radsniff.h:185
void(* rs_stats_print_header_cb_t)(rs_update_t *this)
Callback for printing stats header.
Definition: radsniff.h:238
rs_latency_t * stats_rsp
Latency entry for the request type.
Definition: radsniff.h:209
bool to_file
Were writing pcap data to files.
Definition: radsniff.h:263
uint64_t ma_invalid_length
Definition: radsniff.h:153
fr_pair_list_t link_vps
fr_pair_ts used to link retransmissions.
Definition: radsniff.h:199
fr_rb_node_t request_node
Definition: radsniff.h:215
fr_packet_t * packet
The original packet.
Definition: radsniff.h:193
#define RS_RETRANSMIT_MAX
Maximum number of times we expect to see a packet retransmitted.
Definition: radsniff.h:44
rs_stats_print_header_cb_t head
Print header.
Definition: radsniff.h:255
char * list_attributes
Raw attribute filter string.
Definition: radsniff.h:286
char * radius_secret
Secret to decode encrypted attributes.
Definition: radsniff.h:280
bool from_file
Were reading pcap data from files.
Definition: radsniff.h:260
fr_pcap_t * out
Where to write output.
Definition: radsniff.h:228
uint64_t attribute_overflow
Definition: radsniff.h:152
uint64_t header_overflow
Definition: radsniff.h:149
fr_pcap_t * in
PCAP handle the original request was received on.
Definition: radsniff.h:192
uint64_t invalid_attribute
Definition: radsniff.h:150
fr_pcap_t * in
Linked list of PCAP handles to check for drops.
Definition: radsniff.h:253
rs_capture_t * capture_p
Next packet slot.
Definition: radsniff.h:203
void(* rs_stats_print_cb_t)(rs_update_t *this, rs_stats_t *stats, struct timeval *now)
Callback for printing stats values.
Definition: radsniff.h:243
bool in_link_tree
Whether the request is currently in the linked tree.
Definition: radsniff.h:218
int intervals
Number of stats intervals.
Definition: radsniff.h:163
fr_event_list_t * list
The event list.
Definition: radsniff.h:225
fr_packet_t * linked
The subsequent response or forwarded request the packet.
Definition: radsniff.h:197
bool verify_radius_authenticator
Check RADIUS authenticator in packets.
Definition: radsniff.h:278
uint64_t limit
Maximum number of packets to capture.
Definition: radsniff.h:306
char const * pidfile
File to write PID to.
Definition: radsniff.h:270
uint64_t min_length_mimatch
Definition: radsniff.h:148
uint64_t rt_rsp
Number of times we saw a retransmitted response packet.
Definition: radsniff.h:206
bool from_stdin
Were reading pcap data from stdin.
Definition: radsniff.h:262
bool daemonize
Daemonize and write PID out to file.
Definition: radsniff.h:269
double latency_smoothed
Smoothed moving average.
Definition: radsniff.h:115
uint64_t rt_req
Number of times we saw the same request packet.
Definition: radsniff.h:205
fr_pair_list_t filter_response_vps
Sorted filter vps.
Definition: radsniff.h:298
bool promiscuous
Capture in promiscuous mode.
Definition: radsniff.h:273
fr_pair_list_t expect_vps
Definition: radsniff.h:196
fr_pair_list_t packet_vps
Definition: radsniff.h:194
bool logged
Whether any messages regarding this request were logged.
Definition: radsniff.h:188
char * link_attributes
Names of fr_dict_attr_ts to use for rtx.
Definition: radsniff.h:290
bool silent_cleanup
Cleanup was forced before normal expiry period, ignore stats about packet loss.
Definition: radsniff.h:211
char const * filter_request
Raw request filter string.
Definition: radsniff.h:294
void(* rs_packet_logger_t)(uint64_t count, rs_status_t status, fr_pcap_t *handle, fr_packet_t *packet, fr_pair_list_t *list, struct timeval *elapsed, struct timeval *latency, bool response, bool body)
Definition: radsniff.h:78
rs_status_t
Definition: radsniff.h:69
@ RS_ERROR
Definition: radsniff.h:74
@ RS_UNLINKED
Definition: radsniff.h:71
@ RS_REUSED
Definition: radsniff.h:73
@ RS_LOST
Definition: radsniff.h:75
@ RS_NORMAL
Definition: radsniff.h:70
@ RS_RTX
Definition: radsniff.h:72
struct pcap_pkthdr * header
PCAP packet header.
Definition: radsniff.h:175
uint64_t ma_missing
Definition: radsniff.h:156
uint64_t too_many_attributes
Definition: radsniff.h:155
bool verify_udp_checksum
Check UDP checksum in packets.
Definition: radsniff.h:277
bool to_stdout
Were writing pcap data to stdout.
Definition: radsniff.h:264
fr_pair_list_t filter_request_vps
Sorted filter vps.
Definition: radsniff.h:297
Definition: radsniff.h:259
Statistic write/print event.
Definition: radsniff.h:224
Stats for a single interval.
Definition: radsniff.h:112
Wrapper for fr_packet_t.
Definition: radsniff.h:184
One set of statistics.
Definition: radsniff.h:162
FD data which gets passed to callbacks.
Definition: radsniff.h:249
return count
Definition: module.c:163
fr_aka_sim_id_type_t type
static fr_slen_t data
Definition: value.h:1265
static size_t char ** out
Definition: value.h:997