154359Sroberto/*
2132451Sroberto * ntp_monitor - monitor ntpd statistics
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
582498Sroberto# include <config.h>
654359Sroberto#endif
754359Sroberto
854359Sroberto#include "ntpd.h"
954359Sroberto#include "ntp_io.h"
1054359Sroberto#include "ntp_if.h"
11290001Sglebius#include "ntp_lists.h"
1254359Sroberto#include "ntp_stdlib.h"
13182007Sroberto#include <ntp_random.h>
1454359Sroberto
1582498Sroberto#include <stdio.h>
1682498Sroberto#include <signal.h>
1782498Sroberto#ifdef HAVE_SYS_IOCTL_H
1882498Sroberto# include <sys/ioctl.h>
1982498Sroberto#endif
2082498Sroberto
2154359Sroberto/*
22290001Sglebius * Record statistics based on source address, mode and version. The
23290001Sglebius * receive procedure calls us with the incoming rbufp before it does
24290001Sglebius * anything else. While at it, implement rate controls for inbound
25290001Sglebius * traffic.
2654359Sroberto *
27290001Sglebius * Each entry is doubly linked into two lists, a hash table and a most-
28290001Sglebius * recently-used (MRU) list. When a packet arrives it is looked up in
29290001Sglebius * the hash table. If found, the statistics are updated and the entry
30132451Sroberto * relinked at the head of the MRU list. If not found, a new entry is
31132451Sroberto * allocated, initialized and linked into both the hash table and at the
32132451Sroberto * head of the MRU list.
3354359Sroberto *
34132451Sroberto * Memory is usually allocated by grabbing a big chunk of new memory and
35132451Sroberto * cutting it up into littler pieces. The exception to this when we hit
36132451Sroberto * the memory limit. Then we free memory by grabbing entries off the
37132451Sroberto * tail for the MRU list, unlinking from the hash table, and
3854359Sroberto * reinitializing.
3954359Sroberto *
40290001Sglebius * INC_MONLIST is the default allocation granularity in entries.
41290001Sglebius * INIT_MONLIST is the default initial allocation in entries.
4254359Sroberto */
43290001Sglebius#ifdef MONMEMINC		/* old name */
44290001Sglebius# define	INC_MONLIST	MONMEMINC
45290001Sglebius#elif !defined(INC_MONLIST)
46290001Sglebius# define	INC_MONLIST	(4 * 1024 / sizeof(mon_entry))
4754359Sroberto#endif
48290001Sglebius#ifndef INIT_MONLIST
49290001Sglebius# define	INIT_MONLIST	(4 * 1024 / sizeof(mon_entry))
5054359Sroberto#endif
51290001Sglebius#ifndef MRU_MAXDEPTH_DEF
52290001Sglebius# define MRU_MAXDEPTH_DEF	(1024 * 1024 / sizeof(mon_entry))
53290001Sglebius#endif
5454359Sroberto
5554359Sroberto/*
5654359Sroberto * Hashing stuff
5754359Sroberto */
58290001Sglebiusu_char	mon_hash_bits;
5954359Sroberto
6054359Sroberto/*
61290001Sglebius * Pointers to the hash table and the MRU list.  Memory for the hash
62290001Sglebius * table is allocated only if monitoring is enabled.
6354359Sroberto */
64290001Sglebiusmon_entry **	mon_hash;	/* MRU hash table */
65290001Sglebiusmon_entry	mon_mru_list;	/* mru listhead */
66132451Sroberto
6754359Sroberto/*
68290001Sglebius * List of free structures structures, and counters of in-use and total
69290001Sglebius * structures. The free structures are linked with the hash_next field.
7054359Sroberto */
71290001Sglebiusstatic  mon_entry *mon_free;		/* free list or null if none */
72290001Sglebius	u_int mru_alloc;		/* mru list + free list count */
73290001Sglebius	u_int mru_entries;		/* mru list count */
74290001Sglebius	u_int mru_peakentries;		/* highest mru_entries seen */
75290001Sglebius	u_int mru_initalloc = INIT_MONLIST;/* entries to preallocate */
76290001Sglebius	u_int mru_incalloc = INC_MONLIST;/* allocation batch factor */
77290001Sglebiusstatic	u_int mon_mem_increments;	/* times called malloc() */
7854359Sroberto
7954359Sroberto/*
80290001Sglebius * Parameters of the RES_LIMITED restriction option. We define headway
81290001Sglebius * as the idle time between packets. A packet is discarded if the
82290001Sglebius * headway is less than the minimum, as well as if the average headway
83290001Sglebius * is less than eight times the increment.
84290001Sglebius */
85290001Sglebiusint	ntp_minpkt = NTP_MINPKT;	/* minimum (log 2 s) */
86290001Sglebiusu_char	ntp_minpoll = NTP_MINPOLL;	/* increment (log 2 s) */
87290001Sglebius
88290001Sglebius/*
8954359Sroberto * Initialization state.  We may be monitoring, we may not.  If
9054359Sroberto * we aren't, we may not even have allocated any memory yet.
9154359Sroberto */
92290001Sglebius	u_int	mon_enabled;		/* enable switch */
93290001Sglebius	u_int	mru_mindepth = 600;	/* preempt above this */
94290001Sglebius	int	mru_maxage = 64;	/* for entries older than */
95290001Sglebius	u_int	mru_maxdepth = 		/* MRU count hard limit */
96290001Sglebius			MRU_MAXDEPTH_DEF;
97290001Sglebius	int	mon_age = 3000;		/* preemption limit */
9854359Sroberto
99290001Sglebiusstatic	void		mon_getmoremem(void);
100290001Sglebiusstatic	void		remove_from_hash(mon_entry *);
101290001Sglebiusstatic	inline void	mon_free_entry(mon_entry *);
102290001Sglebiusstatic	inline void	mon_reclaim_entry(mon_entry *);
103290001Sglebius
104290001Sglebius
10554359Sroberto/*
10654359Sroberto * init_mon - initialize monitoring global data
10754359Sroberto */
10854359Srobertovoid
10954359Srobertoinit_mon(void)
11054359Sroberto{
11154359Sroberto	/*
11254359Sroberto	 * Don't do much of anything here.  We don't allocate memory
113290001Sglebius	 * until mon_start().
11454359Sroberto	 */
11554359Sroberto	mon_enabled = MON_OFF;
116290001Sglebius	INIT_DLIST(mon_mru_list, mru);
117290001Sglebius}
11854359Sroberto
119290001Sglebius
120290001Sglebius/*
121290001Sglebius * remove_from_hash - removes an entry from the address hash table and
122290001Sglebius *		      decrements mru_entries.
123290001Sglebius */
124290001Sglebiusstatic void
125290001Sglebiusremove_from_hash(
126290001Sglebius	mon_entry *mon
127290001Sglebius	)
128290001Sglebius{
129290001Sglebius	u_int hash;
130290001Sglebius	mon_entry *punlinked;
131290001Sglebius
132290001Sglebius	mru_entries--;
133290001Sglebius	hash = MON_HASH(&mon->rmtadr);
134290001Sglebius	UNLINK_SLIST(punlinked, mon_hash[hash], mon, hash_next,
135290001Sglebius		     mon_entry);
136290001Sglebius	ENSURE(punlinked == mon);
13754359Sroberto}
13854359Sroberto
13954359Sroberto
140290001Sglebiusstatic inline void
141290001Sglebiusmon_free_entry(
142290001Sglebius	mon_entry *m
143290001Sglebius	)
144290001Sglebius{
145290001Sglebius	ZERO(*m);
146290001Sglebius	LINK_SLIST(mon_free, m, hash_next);
147290001Sglebius}
148290001Sglebius
149290001Sglebius
15054359Sroberto/*
151290001Sglebius * mon_reclaim_entry - Remove an entry from the MRU list and from the
152290001Sglebius *		       hash array, then zero-initialize it.  Indirectly
153290001Sglebius *		       decrements mru_entries.
154290001Sglebius
155290001Sglebius * The entry is prepared to be reused.  Before return, in
156290001Sglebius * remove_from_hash(), mru_entries is decremented.  It is the caller's
157290001Sglebius * responsibility to increment it again.
158290001Sglebius */
159290001Sglebiusstatic inline void
160290001Sglebiusmon_reclaim_entry(
161290001Sglebius	mon_entry *m
162290001Sglebius	)
163290001Sglebius{
164290001Sglebius	DEBUG_INSIST(NULL != m);
165290001Sglebius
166290001Sglebius	UNLINK_DLIST(m, mru);
167290001Sglebius	remove_from_hash(m);
168290001Sglebius	ZERO(*m);
169290001Sglebius}
170290001Sglebius
171290001Sglebius
172290001Sglebius/*
173290001Sglebius * mon_getmoremem - get more memory and put it on the free list
174290001Sglebius */
175290001Sglebiusstatic void
176290001Sglebiusmon_getmoremem(void)
177290001Sglebius{
178290001Sglebius	mon_entry *chunk;
179290001Sglebius	u_int entries;
180290001Sglebius
181290001Sglebius	entries = (0 == mon_mem_increments)
182290001Sglebius		      ? mru_initalloc
183290001Sglebius		      : mru_incalloc;
184290001Sglebius
185290001Sglebius	if (entries) {
186290001Sglebius		chunk = eallocarray(entries, sizeof(*chunk));
187290001Sglebius		mru_alloc += entries;
188290001Sglebius		for (chunk += entries; entries; entries--)
189290001Sglebius			mon_free_entry(--chunk);
190290001Sglebius
191290001Sglebius		mon_mem_increments++;
192290001Sglebius	}
193290001Sglebius}
194290001Sglebius
195290001Sglebius
196290001Sglebius/*
19754359Sroberto * mon_start - start up the monitoring software
19854359Sroberto */
19954359Srobertovoid
20054359Srobertomon_start(
20154359Sroberto	int mode
20254359Sroberto	)
20354359Sroberto{
204290001Sglebius	size_t octets;
205290001Sglebius	u_int min_hash_slots;
20654359Sroberto
207290001Sglebius	if (MON_OFF == mode)		/* MON_OFF is 0 */
208290001Sglebius		return;
209290001Sglebius	if (mon_enabled) {
21054359Sroberto		mon_enabled |= mode;
21154359Sroberto		return;
21254359Sroberto	}
213290001Sglebius	if (0 == mon_mem_increments)
21454359Sroberto		mon_getmoremem();
215290001Sglebius	/*
216290001Sglebius	 * Select the MRU hash table size to limit the average count
217290001Sglebius	 * per bucket at capacity (mru_maxdepth) to 8, if possible
218290001Sglebius	 * given our hash is limited to 16 bits.
219290001Sglebius	 */
220290001Sglebius	min_hash_slots = (mru_maxdepth / 8) + 1;
221290001Sglebius	mon_hash_bits = 0;
222290001Sglebius	while (min_hash_slots >>= 1)
223290001Sglebius		mon_hash_bits++;
224290001Sglebius	mon_hash_bits = max(4, mon_hash_bits);
225290001Sglebius	mon_hash_bits = min(16, mon_hash_bits);
226290001Sglebius	octets = sizeof(*mon_hash) * MON_HASH_SIZE;
227290001Sglebius	mon_hash = erealloc_zero(mon_hash, octets, 0);
22854359Sroberto
22954359Sroberto	mon_enabled = mode;
23054359Sroberto}
23154359Sroberto
23254359Sroberto
23354359Sroberto/*
23454359Sroberto * mon_stop - stop the monitoring software
23554359Sroberto */
23654359Srobertovoid
23754359Srobertomon_stop(
23854359Sroberto	int mode
23954359Sroberto	)
24054359Sroberto{
241290001Sglebius	mon_entry *mon;
24254359Sroberto
243290001Sglebius	if (MON_OFF == mon_enabled)
244290001Sglebius		return;
24554359Sroberto	if ((mon_enabled & mode) == 0 || mode == MON_OFF)
246290001Sglebius		return;
24754359Sroberto
24854359Sroberto	mon_enabled &= ~mode;
24954359Sroberto	if (mon_enabled != MON_OFF)
250290001Sglebius		return;
25154359Sroberto
25254359Sroberto	/*
253290001Sglebius	 * Move everything on the MRU list to the free list quickly,
254290001Sglebius	 * without bothering to remove each from either the MRU list or
255290001Sglebius	 * the hash table.
25654359Sroberto	 */
257290001Sglebius	ITER_DLIST_BEGIN(mon_mru_list, mon, mru, mon_entry)
258290001Sglebius		mon_free_entry(mon);
259290001Sglebius	ITER_DLIST_END()
26054359Sroberto
261290001Sglebius	/* empty the MRU list and hash table. */
262290001Sglebius	mru_entries = 0;
263290001Sglebius	INIT_DLIST(mon_mru_list, mru);
264290001Sglebius	zero_mem(mon_hash, sizeof(*mon_hash) * MON_HASH_SIZE);
26554359Sroberto}
26654359Sroberto
267290001Sglebius
268290001Sglebius/*
269290001Sglebius * mon_clearinterface -- remove mru entries referring to a local address
270290001Sglebius *			 which is going away.
271290001Sglebius */
272182007Srobertovoid
273290001Sglebiusmon_clearinterface(
274290001Sglebius	endpt *lcladr
275290001Sglebius	)
276182007Sroberto{
277290001Sglebius	mon_entry *mon;
27854359Sroberto
279290001Sglebius	/* iterate mon over mon_mru_list */
280290001Sglebius	ITER_DLIST_BEGIN(mon_mru_list, mon, mru, mon_entry)
281290001Sglebius		if (mon->lcladr == lcladr) {
282290001Sglebius			/* remove from mru list */
283290001Sglebius			UNLINK_DLIST(mon, mru);
284290001Sglebius			/* remove from hash list, adjust mru_entries */
285290001Sglebius			remove_from_hash(mon);
286290001Sglebius			/* put on free list */
287290001Sglebius			mon_free_entry(mon);
288290001Sglebius		}
289290001Sglebius	ITER_DLIST_END()
290182007Sroberto}
291182007Sroberto
292290001Sglebius
29354359Sroberto/*
29454359Sroberto * ntp_monitor - record stats about this packet
295182007Sroberto *
296290001Sglebius * Returns supplied restriction flags, with RES_LIMITED and RES_KOD
297290001Sglebius * cleared unless the packet should not be responded to normally
298290001Sglebius * (RES_LIMITED) and possibly should trigger a KoD response (RES_KOD).
299290001Sglebius * The returned flags are saved in the MRU entry, so that it reflects
300290001Sglebius * whether the last packet from that source triggered rate limiting,
301290001Sglebius * and if so, possible KoD response.  This implies you can not tell
302290001Sglebius * whether a given address is eligible for rate limiting/KoD from the
303290001Sglebius * monlist restrict bits, only whether or not the last packet triggered
304290001Sglebius * such responses.  ntpdc -c reslist lets you see whether RES_LIMITED
305290001Sglebius * or RES_KOD is lit for a particular address before ntp_monitor()'s
306290001Sglebius * typical dousing.
30754359Sroberto */
308290001Sglebiusu_short
30954359Srobertontp_monitor(
310290001Sglebius	struct recvbuf *rbufp,
311290001Sglebius	u_short	flags
31254359Sroberto	)
31354359Sroberto{
314290001Sglebius	l_fp		interval_fp;
315290001Sglebius	struct pkt *	pkt;
316290001Sglebius	mon_entry *	mon;
317290001Sglebius	mon_entry *	oldest;
318290001Sglebius	int		oldest_age;
319290001Sglebius	u_int		hash;
320290001Sglebius	u_short		restrict_mask;
321290001Sglebius	u_char		mode;
322290001Sglebius	u_char		version;
323290001Sglebius	int		interval;
324290001Sglebius	int		head;		/* headway increment */
325290001Sglebius	int		leak;		/* new headway */
326290001Sglebius	int		limit;		/* average threshold */
32754359Sroberto
328290001Sglebius	REQUIRE(rbufp != NULL);
329290001Sglebius
33054359Sroberto	if (mon_enabled == MON_OFF)
331290001Sglebius		return ~(RES_LIMITED | RES_KOD) & flags;
33254359Sroberto
33354359Sroberto	pkt = &rbufp->recv_pkt;
334290001Sglebius	hash = MON_HASH(&rbufp->recv_srcadr);
33554359Sroberto	mode = PKT_MODE(pkt->li_vn_mode);
336290001Sglebius	version = PKT_VERSION(pkt->li_vn_mode);
337290001Sglebius	mon = mon_hash[hash];
338132451Sroberto
339290001Sglebius	/*
340290001Sglebius	 * We keep track of all traffic for a given IP in one entry,
341290001Sglebius	 * otherwise cron'ed ntpdate or similar evades RES_LIMITED.
342290001Sglebius	 */
343290001Sglebius
344290001Sglebius	for (; mon != NULL; mon = mon->hash_next)
345290001Sglebius		if (SOCK_EQ(&mon->rmtadr, &rbufp->recv_srcadr))
346290001Sglebius			break;
347290001Sglebius
348290001Sglebius	if (mon != NULL) {
349290001Sglebius		interval_fp = rbufp->recv_time;
350290001Sglebius		L_SUB(&interval_fp, &mon->last);
351290001Sglebius		/* add one-half second to round up */
352290001Sglebius		L_ADDUF(&interval_fp, 0x80000000);
353290001Sglebius		interval = interval_fp.l_i;
354290001Sglebius		mon->last = rbufp->recv_time;
355290001Sglebius		NSRCPORT(&mon->rmtadr) = NSRCPORT(&rbufp->recv_srcadr);
356290001Sglebius		mon->count++;
357290001Sglebius		restrict_mask = flags;
358290001Sglebius		mon->vn_mode = VN_MODE(version, mode);
359290001Sglebius
360290001Sglebius		/* Shuffle to the head of the MRU list. */
361290001Sglebius		UNLINK_DLIST(mon, mru);
362290001Sglebius		LINK_DLIST(mon_mru_list, mon, mru);
363290001Sglebius
364132451Sroberto		/*
365290001Sglebius		 * At this point the most recent arrival is first in the
366290001Sglebius		 * MRU list.  Decrease the counter by the headway, but
367290001Sglebius		 * not less than zero.
368132451Sroberto		 */
369290001Sglebius		mon->leak -= interval;
370290001Sglebius		mon->leak = max(0, mon->leak);
371290001Sglebius		head = 1 << ntp_minpoll;
372290001Sglebius		leak = mon->leak + head;
373290001Sglebius		limit = NTP_SHIFT * head;
37454359Sroberto
375290001Sglebius		DPRINTF(2, ("MRU: interval %d headway %d limit %d\n",
376290001Sglebius			    interval, leak, limit));
377290001Sglebius
378290001Sglebius		/*
379290001Sglebius		 * If the minimum and average thresholds are not
380290001Sglebius		 * exceeded, douse the RES_LIMITED and RES_KOD bits and
381290001Sglebius		 * increase the counter by the headway increment.  Note
382290001Sglebius		 * that we give a 1-s grace for the minimum threshold
383290001Sglebius		 * and a 2-s grace for the headway increment.  If one or
384290001Sglebius		 * both thresholds are exceeded and the old counter is
385290001Sglebius		 * less than the average threshold, set the counter to
386290001Sglebius		 * the average threshold plus the increment and leave
387290001Sglebius		 * the RES_LIMITED and RES_KOD bits lit. Otherwise,
388290001Sglebius		 * leave the counter alone and douse the RES_KOD bit.
389290001Sglebius		 * This rate-limits the KoDs to no less than the average
390290001Sglebius		 * headway.
391290001Sglebius		 */
392290001Sglebius		if (interval + 1 >= ntp_minpkt && leak < limit) {
393290001Sglebius			mon->leak = leak - 2;
394290001Sglebius			restrict_mask &= ~(RES_LIMITED | RES_KOD);
395290001Sglebius		} else if (mon->leak < limit)
396290001Sglebius			mon->leak = limit + head;
397290001Sglebius		else
398290001Sglebius			restrict_mask &= ~RES_KOD;
399290001Sglebius
400290001Sglebius		mon->flags = restrict_mask;
401290001Sglebius
402290001Sglebius		return mon->flags;
40354359Sroberto	}
40454359Sroberto
40554359Sroberto	/*
40654359Sroberto	 * If we got here, this is the first we've heard of this
40754359Sroberto	 * guy.  Get him some memory, either from the free list
40854359Sroberto	 * or from the tail of the MRU list.
409290001Sglebius	 *
410290001Sglebius	 * The following ntp.conf "mru" knobs come into play determining
411290001Sglebius	 * the depth (or count) of the MRU list:
412290001Sglebius	 * - mru_mindepth ("mru mindepth") is a floor beneath which
413290001Sglebius	 *   entries are kept without regard to their age.  The
414290001Sglebius	 *   default is 600 which matches the longtime implementation
415290001Sglebius	 *   limit on the total number of entries.
416290001Sglebius	 * - mru_maxage ("mru maxage") is a ceiling on the age in
417290001Sglebius	 *   seconds of entries.  Entries older than this are
418290001Sglebius	 *   reclaimed once mon_mindepth is exceeded.  64s default.
419290001Sglebius	 *   Note that entries older than this can easily survive
420290001Sglebius	 *   as they are reclaimed only as needed.
421290001Sglebius	 * - mru_maxdepth ("mru maxdepth") is a hard limit on the
422290001Sglebius	 *   number of entries.
423290001Sglebius	 * - "mru maxmem" sets mru_maxdepth to the number of entries
424290001Sglebius	 *   which fit in the given number of kilobytes.  The default is
425290001Sglebius	 *   1024, or 1 megabyte.
426290001Sglebius	 * - mru_initalloc ("mru initalloc" sets the count of the
427290001Sglebius	 *   initial allocation of MRU entries.
428290001Sglebius	 * - "mru initmem" sets mru_initalloc in units of kilobytes.
429290001Sglebius	 *   The default is 4.
430290001Sglebius	 * - mru_incalloc ("mru incalloc" sets the number of entries to
431290001Sglebius	 *   allocate on-demand each time the free list is empty.
432290001Sglebius	 * - "mru incmem" sets mru_incalloc in units of kilobytes.
433290001Sglebius	 *   The default is 4.
434290001Sglebius	 * Whichever of "mru maxmem" or "mru maxdepth" occurs last in
435290001Sglebius	 * ntp.conf controls.  Similarly for "mru initalloc" and "mru
436290001Sglebius	 * initmem", and for "mru incalloc" and "mru incmem".
43754359Sroberto	 */
438290001Sglebius	if (mru_entries < mru_mindepth) {
439290001Sglebius		if (NULL == mon_free)
440290001Sglebius			mon_getmoremem();
441290001Sglebius		UNLINK_HEAD_SLIST(mon, mon_free, hash_next);
44254359Sroberto	} else {
443290001Sglebius		oldest = TAIL_DLIST(mon_mru_list, mru);
444290001Sglebius		oldest_age = 0;		/* silence uninit warning */
445290001Sglebius		if (oldest != NULL) {
446290001Sglebius			interval_fp = rbufp->recv_time;
447290001Sglebius			L_SUB(&interval_fp, &oldest->last);
448290001Sglebius			/* add one-half second to round up */
449290001Sglebius			L_ADDUF(&interval_fp, 0x80000000);
450290001Sglebius			oldest_age = interval_fp.l_i;
451290001Sglebius		}
452290001Sglebius		/* note -1 is legal for mru_maxage (disables) */
453290001Sglebius		if (oldest != NULL && mru_maxage < oldest_age) {
454290001Sglebius			mon_reclaim_entry(oldest);
455290001Sglebius			mon = oldest;
456290001Sglebius		} else if (mon_free != NULL || mru_alloc <
457290001Sglebius			   mru_maxdepth) {
458290001Sglebius			if (NULL == mon_free)
459290001Sglebius				mon_getmoremem();
460290001Sglebius			UNLINK_HEAD_SLIST(mon, mon_free, hash_next);
461290001Sglebius		/* Preempt from the MRU list if old enough. */
462290001Sglebius		} else if (ntp_random() / (2. * FRAC) >
463290001Sglebius			   (double)oldest_age / mon_age) {
464290001Sglebius			return ~(RES_LIMITED | RES_KOD) & flags;
465290001Sglebius		} else {
466290001Sglebius			mon_reclaim_entry(oldest);
467290001Sglebius			mon = oldest;
468290001Sglebius		}
46954359Sroberto	}
47054359Sroberto
471290001Sglebius	INSIST(mon != NULL);
472290001Sglebius
47354359Sroberto	/*
47454359Sroberto	 * Got one, initialize it
47554359Sroberto	 */
476290001Sglebius	mru_entries++;
477290001Sglebius	mru_peakentries = max(mru_peakentries, mru_entries);
478290001Sglebius	mon->last = rbufp->recv_time;
479290001Sglebius	mon->first = mon->last;
480290001Sglebius	mon->count = 1;
481290001Sglebius	mon->flags = ~(RES_LIMITED | RES_KOD) & flags;
482290001Sglebius	mon->leak = 0;
483290001Sglebius	memcpy(&mon->rmtadr, &rbufp->recv_srcadr, sizeof(mon->rmtadr));
484290001Sglebius	mon->vn_mode = VN_MODE(version, mode);
485290001Sglebius	mon->lcladr = rbufp->dstadr;
486290001Sglebius	mon->cast_flags = (u_char)(((rbufp->dstadr->flags &
487290001Sglebius	    INT_MCASTOPEN) && rbufp->fd == mon->lcladr->fd) ? MDF_MCAST
488290001Sglebius	    : rbufp->fd == mon->lcladr->bfd ? MDF_BCAST : MDF_UCAST);
48954359Sroberto
49054359Sroberto	/*
491132451Sroberto	 * Drop him into front of the hash table. Also put him on top of
492132451Sroberto	 * the MRU list.
49354359Sroberto	 */
494290001Sglebius	LINK_SLIST(mon_hash[hash], mon, hash_next);
495290001Sglebius	LINK_DLIST(mon_mru_list, mon, mru);
49654359Sroberto
497290001Sglebius	return mon->flags;
49854359Sroberto}
49954359Sroberto
50054359Sroberto
501