xref: /netbsd-src/external/bsd/ntp/dist/scripts/monitoring/ntp.pl (revision b8ecfcfef0e343ad71faea7a54fb5fcb42ad4e27)
1abb0f93cSkardel#!/usr/bin/perl -w
2abb0f93cSkardel;#
3abb0f93cSkardel;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
4abb0f93cSkardel;#
5abb0f93cSkardel;# process loop filter statistics file and either
6abb0f93cSkardel;#     - show statistics periodically using gnuplot
7abb0f93cSkardel;#     - or print a single plot
8abb0f93cSkardel;#
9abb0f93cSkardel;#  Copyright (c) 1992
10abb0f93cSkardel;#  Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
11abb0f93cSkardel;#
12abb0f93cSkardel;#
13abb0f93cSkardel;#############################################################
14abb0f93cSkardel
15abb0f93cSkardelpackage ntp;
16abb0f93cSkardel
17abb0f93cSkardel$NTP_version = 2;
18abb0f93cSkardel$ctrl_mode=6;
19abb0f93cSkardel
20abb0f93cSkardel$byte1 = (($NTP_version & 0x7)<< 3) & 0x34 | ($ctrl_mode & 0x7);
21abb0f93cSkardel$MAX_DATA = 468;
22abb0f93cSkardel
23abb0f93cSkardel$sequence = 0;			# initial sequence number incred before used
24abb0f93cSkardel$pad=4;
25abb0f93cSkardel$do_auth=0;			# no possibility today
26abb0f93cSkardel$keyid=0;
27abb0f93cSkardel;#list if known keys (passwords)
28abb0f93cSkardel%KEYS = ( 0, "\200\200\200\200\200\200\200\200",
29abb0f93cSkardel	 );
30abb0f93cSkardel
31abb0f93cSkardel;#-----------------------------------------------------------------------------
32abb0f93cSkardel;# access routines for ntp control packet
33abb0f93cSkardel    ;# NTP control message format
34abb0f93cSkardel    ;#  C  LI|VN|MODE  LI 2bit=00  VN 3bit=2(3) MODE 3bit=6 : $byte1
35abb0f93cSkardel    ;#  C  R|E|M|Op    R response  E error    M more   Op opcode
36abb0f93cSkardel    ;#  n  sequence
37abb0f93cSkardel    ;#  n  status
38abb0f93cSkardel    ;#  n  associd
39abb0f93cSkardel    ;#  n  offset
40abb0f93cSkardel    ;#  n  count
41abb0f93cSkardel    ;#  a+ data (+ padding)
42abb0f93cSkardel    ;#  optional authentication data
43abb0f93cSkardel    ;#  N  key
44abb0f93cSkardel    ;#  N2 checksum
45abb0f93cSkardel
46abb0f93cSkardel;# first byte of packet
47abb0f93cSkardelsub pkt_LI   { return ($_[$[] >> 6) & 0x3; }
48abb0f93cSkardelsub pkt_VN   { return ($_[$[] >> 3) & 0x7; }
49abb0f93cSkardelsub pkt_MODE { return ($_[$[]     ) & 0x7; }
50abb0f93cSkardel
51abb0f93cSkardel;# second byte of packet
52abb0f93cSkardelsub pkt_R  { return ($_[$[] & 0x80) == 0x80; }
53abb0f93cSkardelsub pkt_E  { return ($_[$[] & 0x40) == 0x40; }
54abb0f93cSkardelsub pkt_M  { return ($_[$[] & 0x20) == 0x20; }
55abb0f93cSkardelsub pkt_OP { return $_[$[] & 0x1f; }
56abb0f93cSkardel
57abb0f93cSkardel;#-----------------------------------------------------------------------------
58abb0f93cSkardel
59abb0f93cSkardelsub setkey
60abb0f93cSkardel{
61abb0f93cSkardel    local($id,$key) = @_;
62abb0f93cSkardel
63abb0f93cSkardel    $KEYS{$id} = $key if (defined($key));
64abb0f93cSkardel    if (! defined($KEYS{$id}))
65abb0f93cSkardel    {
66abb0f93cSkardel	warn "Key $id not yet specified - key not changed\n";
67abb0f93cSkardel	return undef;
68abb0f93cSkardel    }
69abb0f93cSkardel    return ($keyid,$keyid = $id)[$[];
70abb0f93cSkardel}
71abb0f93cSkardel
72abb0f93cSkardel;#-----------------------------------------------------------------------------
73abb0f93cSkardelsub numerical { $a <=> $b; }
74abb0f93cSkardel
75abb0f93cSkardel;#-----------------------------------------------------------------------------
76abb0f93cSkardel
77abb0f93cSkardelsub send	#'
78abb0f93cSkardel{
79abb0f93cSkardel    local($fh,$opcode, $associd, $data,$address) = @_;
80abb0f93cSkardel    $fh = caller(0)."'$fh";
81abb0f93cSkardel
82abb0f93cSkardel    local($junksize,$junk,$packet,$offset,$ret);
83abb0f93cSkardel    $offset = 0;
84abb0f93cSkardel
85abb0f93cSkardel    $sequence++;
86abb0f93cSkardel    while(1)
87abb0f93cSkardel    {
88abb0f93cSkardel	$junksize = length($data);
89abb0f93cSkardel	$junksize = $MAX_DATA if $junksize > $MAX_DATA;
90abb0f93cSkardel
91abb0f93cSkardel	($junk,$data) = $data =~ /^(.{$junksize})(.*)$/;
92abb0f93cSkardel	$packet
93abb0f93cSkardel	    = pack("C2n5a".(($junk eq "") ? 0 : &pad($junksize+12,$pad)-12),
94abb0f93cSkardel		   $byte1,
95abb0f93cSkardel		   ($opcode & 0x1f) | ($data ? 0x20 : 0),
96abb0f93cSkardel		   $sequence,
97abb0f93cSkardel		   0, $associd,
98abb0f93cSkardel		   $offset, $junksize, $junk);
99abb0f93cSkardel	if ($do_auth)
100abb0f93cSkardel	{
101abb0f93cSkardel	    ;# not yet
102abb0f93cSkardel	}
103abb0f93cSkardel	$offset += $junksize;
104abb0f93cSkardel
105abb0f93cSkardel	if (defined($address))
106abb0f93cSkardel	{
107abb0f93cSkardel	    $ret = send($fh, $packet, 0, $address);
108abb0f93cSkardel	}
109abb0f93cSkardel	else
110abb0f93cSkardel	{
111abb0f93cSkardel	    $ret = send($fh, $packet, 0);
112abb0f93cSkardel	}
113abb0f93cSkardel
114abb0f93cSkardel	if (! defined($ret))
115abb0f93cSkardel	{
116abb0f93cSkardel	    warn "send failed: $!\n";
117abb0f93cSkardel	    return undef;
118abb0f93cSkardel	}
119abb0f93cSkardel	elsif ($ret != length($packet))
120abb0f93cSkardel	{
121abb0f93cSkardel	    warn "send failed: sent only $ret from ".length($packet). "bytes\n";
122abb0f93cSkardel	    return undef;
123abb0f93cSkardel	}
124abb0f93cSkardel	return $sequence unless $data;
125abb0f93cSkardel    }
126abb0f93cSkardel}
127abb0f93cSkardel
128abb0f93cSkardel;#-----------------------------------------------------------------------------
129abb0f93cSkardel;# status interpretation
130abb0f93cSkardel;#
131abb0f93cSkardelsub getval
132abb0f93cSkardel{
133abb0f93cSkardel    local($val,*list) = @_;
134abb0f93cSkardel
135abb0f93cSkardel    return $list{$val} if defined($list{$val});
136abb0f93cSkardel    return sprintf("%s#%d",$list{"-"},$val) if defined($list{"-"});
137abb0f93cSkardel    return "unknown-$val";
138abb0f93cSkardel}
139abb0f93cSkardel
140abb0f93cSkardel;#---------------------------------
141abb0f93cSkardel;# system status
142abb0f93cSkardel;#
143abb0f93cSkardel;# format: |LI|CS|SECnt|SECode| LI=2bit CS=6bit SECnt=4bit SECode=4bit
144abb0f93cSkardelsub ssw_LI     { return ($_[$[] >> 14) & 0x3; }
145abb0f93cSkardelsub ssw_CS     { return ($_[$[] >> 8)  & 0x3f; }
146abb0f93cSkardelsub ssw_SECnt  { return ($_[$[] >> 4)  & 0xf; }
147abb0f93cSkardelsub ssw_SECode { return $_[$[] & 0xf; }
148abb0f93cSkardel
149abb0f93cSkardel%LI = ( 0, "leap_none",  1, "leap_add_sec", 2, "leap_del_sec", 3, "sync_alarm", "-", "leap");
150abb0f93cSkardel%ClockSource = (0, "sync_unspec",
151*b8ecfcfeSchristos		1, "sync_pps",
152*b8ecfcfeSchristos		2, "sync_lf_clock",
153abb0f93cSkardel		3, "sync_hf_clock",
154*b8ecfcfeSchristos		4, "sync_uhf_clock",
155*b8ecfcfeSchristos		5, "sync_local_proto",
156*b8ecfcfeSchristos		6, "sync_ntp",
157*b8ecfcfeSchristos		7, "sync_udp/time",
158*b8ecfcfeSchristos		8, "sync_wristwatch",
159*b8ecfcfeSchristos		9, "sync_telephone",
160abb0f93cSkardel		"-", "ClockSource",
161abb0f93cSkardel		);
162abb0f93cSkardel
163abb0f93cSkardel%SystemEvent = (0, "event_unspec",
164*b8ecfcfeSchristos		1, "event_freq_not_set",
165*b8ecfcfeSchristos		2, "event_freq_set",
166*b8ecfcfeSchristos		3, "event_spike_detect",
167*b8ecfcfeSchristos		4, "event_freq_mode",
168*b8ecfcfeSchristos		5, "event_clock_sync",
169*b8ecfcfeSchristos		6, "event_restart",
170*b8ecfcfeSchristos		7, "event_panic_stop",
171*b8ecfcfeSchristos		8, "event_no_sys_peer",
172*b8ecfcfeSchristos		9, "event_leap_armed",
173*b8ecfcfeSchristos		10, "event_leap_disarmed",
174*b8ecfcfeSchristos		11, "event_leap_event",
175*b8ecfcfeSchristos		12, "event_clock_step",
176*b8ecfcfeSchristos		13, "event_kern",
177*b8ecfcfeSchristos		14, "event_loaded_leaps",
178*b8ecfcfeSchristos		15, "event_stale_leaps",
179abb0f93cSkardel		"-", "event",
180abb0f93cSkardel		);
181abb0f93cSkardelsub LI
182abb0f93cSkardel{
183abb0f93cSkardel    &getval(&ssw_LI($_[$[]),*LI);
184abb0f93cSkardel}
185abb0f93cSkardelsub ClockSource
186abb0f93cSkardel{
187abb0f93cSkardel    &getval(&ssw_CS($_[$[]),*ClockSource);
188abb0f93cSkardel}
189abb0f93cSkardel
190abb0f93cSkardelsub SystemEvent
191abb0f93cSkardel{
192abb0f93cSkardel    &getval(&ssw_SECode($_[$[]),*SystemEvent);
193abb0f93cSkardel}
194abb0f93cSkardel
195abb0f93cSkardelsub system_status
196abb0f93cSkardel{
197abb0f93cSkardel    return sprintf("%s, %s, %d event%s, %s", &LI($_[$[]), &ClockSource($_[$[]),
198abb0f93cSkardel		   &ssw_SECnt($_[$[]), ((&ssw_SECnt($_[$[])==1) ? "" : "s"),
199abb0f93cSkardel		   &SystemEvent($_[$[]));
200abb0f93cSkardel}
201abb0f93cSkardel;#---------------------------------
202abb0f93cSkardel;# peer status
203abb0f93cSkardel;#
204abb0f93cSkardel;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit
205abb0f93cSkardelsub psw_PStat_config     { return ($_[$[] & 0x8000) == 0x8000; }
206abb0f93cSkardelsub psw_PStat_authenable { return ($_[$[] & 0x4000) == 0x4000; }
207abb0f93cSkardelsub psw_PStat_authentic  { return ($_[$[] & 0x2000) == 0x2000; }
208abb0f93cSkardelsub psw_PStat_reach      { return ($_[$[] & 0x1000) == 0x1000; }
209*b8ecfcfeSchristossub psw_PStat_bcast      { return ($_[$[] & 0x0800) == 0x0800; }
210abb0f93cSkardelsub psw_PStat { return ($_[$[] >> 10) & 0x3f; }
211abb0f93cSkardelsub psw_PSel  { return ($_[$[] >> 8)  & 0x3;  }
212abb0f93cSkardelsub psw_PCnt  { return ($_[$[] >> 4)  & 0xf; }
213abb0f93cSkardelsub psw_PCode { return $_[$[] & 0xf; }
214abb0f93cSkardel
215abb0f93cSkardel%PeerSelection = (0, "sel_reject",
216*b8ecfcfeSchristos		  1, "sel_falsetick",
217*b8ecfcfeSchristos		  2, "sel_excess",
218*b8ecfcfeSchristos		  3, "sel_outlier",
219*b8ecfcfeSchristos		  4, "sel_candidate",
220*b8ecfcfeSchristos		  5, "sel_backup",
221*b8ecfcfeSchristos		  6, "sel_sys.peer",
222*b8ecfcfeSchristos		  6, "sel_pps.peer",
223abb0f93cSkardel		  "-", "PeerSel",
224abb0f93cSkardel		  );
225abb0f93cSkardel%PeerEvent = (0, "event_unspec",
226*b8ecfcfeSchristos	      1, "event_mobilize",
227*b8ecfcfeSchristos	      2, "event_demobilize",
228abb0f93cSkardel	      3, "event_unreach",
229abb0f93cSkardel	      4, "event_reach",
230*b8ecfcfeSchristos	      5, "event_restart",
231*b8ecfcfeSchristos	      6, "event_no_reply",
232*b8ecfcfeSchristos	      7, "event_rate_exceed",
233*b8ecfcfeSchristos	      8, "event_denied",
234*b8ecfcfeSchristos	      9, "event_leap_armed",
235*b8ecfcfeSchristos	      10, "event_sys_peer",
236*b8ecfcfeSchristos	      11, "event_clock_event",
237*b8ecfcfeSchristos	      12, "event_bad_auth",
238*b8ecfcfeSchristos	      13, "event_popcorn",
239*b8ecfcfeSchristos	      14, "event_intlv_mode",
240*b8ecfcfeSchristos	      15, "event_intlv_err",
241abb0f93cSkardel	      "-", "event",
242abb0f93cSkardel	      );
243abb0f93cSkardel
244abb0f93cSkardelsub PeerSelection
245abb0f93cSkardel{
246abb0f93cSkardel    &getval(&psw_PSel($_[$[]),*PeerSelection);
247abb0f93cSkardel}
248abb0f93cSkardel
249abb0f93cSkardelsub PeerEvent
250abb0f93cSkardel{
251abb0f93cSkardel    &getval(&psw_PCode($_[$[]),*PeerEvent);
252abb0f93cSkardel}
253abb0f93cSkardel
254abb0f93cSkardelsub peer_status
255abb0f93cSkardel{
256abb0f93cSkardel    local($x) = ("");
257abb0f93cSkardel    $x .= "config,"     if &psw_PStat_config($_[$[]);
258abb0f93cSkardel    $x .= "authenable," if &psw_PStat_authenable($_[$[]);
259abb0f93cSkardel    $x .= "authentic,"  if &psw_PStat_authentic($_[$[]);
260abb0f93cSkardel    $x .= "reach,"      if &psw_PStat_reach($_[$[]);
261*b8ecfcfeSchristos    $x .= "bcast,"      if &psw_PStat_bcast($_[$[]);
262abb0f93cSkardel
263abb0f93cSkardel    $x .= sprintf(" %s, %d event%s, %s", &PeerSelection($_[$[]),
264abb0f93cSkardel		  &psw_PCnt($_[$[]), ((&psw_PCnt($_[$[]) == 1) ? "" : "s"),
265abb0f93cSkardel		  &PeerEvent($_[$[]));
266abb0f93cSkardel    return $x;
267abb0f93cSkardel}
268abb0f93cSkardel
269abb0f93cSkardel;#---------------------------------
270abb0f93cSkardel;# clock status
271abb0f93cSkardel;#
272abb0f93cSkardel;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit
273abb0f93cSkardelsub csw_CStat { return ($_[$[] >> 8) & 0xff; }
274abb0f93cSkardelsub csw_CEvnt { return $_[$[] & 0xff; }
275abb0f93cSkardel
276abb0f93cSkardel%ClockStatus = (0, "clk_nominal",
277abb0f93cSkardel		1, "clk_timeout",
278abb0f93cSkardel		2, "clk_badreply",
279abb0f93cSkardel		3, "clk_fault",
280*b8ecfcfeSchristos		4, "clk_badsig",
281abb0f93cSkardel		5, "clk_baddate",
282abb0f93cSkardel		6, "clk_badtime",
283abb0f93cSkardel		"-", "clk",
284abb0f93cSkardel	       );
285abb0f93cSkardel
286abb0f93cSkardelsub clock_status
287abb0f93cSkardel{
288abb0f93cSkardel    return sprintf("%s, last %s",
289abb0f93cSkardel		   &getval(&csw_CStat($_[$[]),*ClockStatus),
290abb0f93cSkardel		   &getval(&csw_CEvnt($_[$[]),*ClockStatus));
291abb0f93cSkardel}
292abb0f93cSkardel
293abb0f93cSkardel;#---------------------------------
294abb0f93cSkardel;# error status
295abb0f93cSkardel;#
296abb0f93cSkardel;# format: |Err|reserved|  Err=8bit
297abb0f93cSkardel;#
298abb0f93cSkardelsub esw_Err { return ($_[$[] >> 8) & 0xff; }
299abb0f93cSkardel
300abb0f93cSkardel%ErrorStatus = (0, "err_unspec",
301abb0f93cSkardel		1, "err_auth_fail",
302abb0f93cSkardel		2, "err_invalid_fmt",
303abb0f93cSkardel		3, "err_invalid_opcode",
304abb0f93cSkardel		4, "err_unknown_assoc",
305abb0f93cSkardel		5, "err_unknown_var",
306abb0f93cSkardel		6, "err_invalid_value",
307abb0f93cSkardel		7, "err_adm_prohibit",
308abb0f93cSkardel		);
309abb0f93cSkardel
310abb0f93cSkardelsub error_status
311abb0f93cSkardel{
312abb0f93cSkardel    return sprintf("%s", &getval(&esw_Err($_[$[]),*ErrorStatus));
313abb0f93cSkardel}
314abb0f93cSkardel
315abb0f93cSkardel;#-----------------------------------------------------------------------------
316abb0f93cSkardel;#
317abb0f93cSkardel;# cntrl op name translation
318abb0f93cSkardel
319*b8ecfcfeSchristos%CntrlOpName = (0, "reserved",
320*b8ecfcfeSchristos		1, "read_status",
321abb0f93cSkardel		2, "read_variables",
322abb0f93cSkardel		3, "write_variables",
323abb0f93cSkardel		4, "read_clock_variables",
324abb0f93cSkardel		5, "write_clock_variables",
325abb0f93cSkardel		6, "set_trap",
326abb0f93cSkardel		7, "trap_response",
327*b8ecfcfeSchristos		8, "configure",
328*b8ecfcfeSchristos		9, "saveconf",
329*b8ecfcfeSchristos		10, "read_mru",
330*b8ecfcfeSchristos		11, "read_ordlist",
331*b8ecfcfeSchristos		12, "rqst_nonce",
332abb0f93cSkardel		31, "unset_trap", # !!! unofficial !!!
333abb0f93cSkardel		"-", "cntrlop",
334abb0f93cSkardel		);
335abb0f93cSkardel
336abb0f93cSkardelsub cntrlop_name
337abb0f93cSkardel{
338abb0f93cSkardel    return &getval($_[$[],*CntrlOpName);
339abb0f93cSkardel}
340abb0f93cSkardel
341abb0f93cSkardel;#-----------------------------------------------------------------------------
342abb0f93cSkardel
343abb0f93cSkardel$STAT_short_pkt = 0;
344abb0f93cSkardel$STAT_pkt = 0;
345abb0f93cSkardel
346abb0f93cSkardel;# process a NTP control message (response) packet
347abb0f93cSkardel;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid)
348abb0f93cSkardel;#      $ret: undef     --> not yet complete
349abb0f93cSkardel;#            ""        --> complete packet received
350abb0f93cSkardel;#            "ERROR"   --> error during receive, bad packet, ...
351abb0f93cSkardel;#          else        --> error packet - list may contain useful info
352abb0f93cSkardel
353abb0f93cSkardel
354abb0f93cSkardelsub handle_packet
355abb0f93cSkardel{
356abb0f93cSkardel    local($pkt,$from) = @_;	# parameters
357abb0f93cSkardel    local($len_pkt) = (length($pkt));
358abb0f93cSkardel;#    local(*FRAGS,*lastseen);
359abb0f93cSkardel    local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data);
360abb0f93cSkardel    local($autch_keyid,$auth_cksum);
361abb0f93cSkardel
362abb0f93cSkardel    $STAT_pkt++;
363abb0f93cSkardel    if ($len_pkt < 12)
364abb0f93cSkardel    {
365abb0f93cSkardel	$STAT_short_pkt++;
366abb0f93cSkardel	return ("ERROR","short packet received");
367abb0f93cSkardel    }
368abb0f93cSkardel
369abb0f93cSkardel    ;# now break packet apart
370abb0f93cSkardel    ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) =
371abb0f93cSkardel	unpack("C2n5a".($len_pkt-12),$pkt);
372abb0f93cSkardel    $data=substr($data,$[,$count);
373abb0f93cSkardel    if ((($len_pkt - 12) - &pad($count,4)) >= 12)
374abb0f93cSkardel    {
375abb0f93cSkardel	;# looks like an authenticator
376abb0f93cSkardel	($auth_keyid,$auth_cksum) =
377abb0f93cSkardel	    unpack("Na8",substr($pkt,$len_pkt-12+$[,12));
378abb0f93cSkardel	$STAT_auth++;
379abb0f93cSkardel	;# no checking of auth_cksum (yet ?)
380abb0f93cSkardel    }
381abb0f93cSkardel
382abb0f93cSkardel    if (&pkt_VN($li_vn_mode) != $NTP_version)
383abb0f93cSkardel    {
384abb0f93cSkardel	$STAT_bad_version++;
385abb0f93cSkardel	return ("ERROR","version ".&pkt_VN($li_vn_mode)."packet ignored");
386abb0f93cSkardel    }
387abb0f93cSkardel
388abb0f93cSkardel    if (&pkt_MODE($li_vn_mode) != $ctrl_mode)
389abb0f93cSkardel    {
390abb0f93cSkardel	$STAT_bad_mode++;
391abb0f93cSkardel	return ("ERROR", "mode ".&pkt_MODE($li_vn_mode)." packet ignored");
392abb0f93cSkardel    }
393abb0f93cSkardel
394abb0f93cSkardel    ;# handle single fragment fast
395abb0f93cSkardel    if ($offset == 0 && &pkt_M($r_e_m_op) == 0)
396abb0f93cSkardel    {
397abb0f93cSkardel	$STAT_single_frag++;
398abb0f93cSkardel	if (&pkt_E($r_e_m_op))
399abb0f93cSkardel	{
400abb0f93cSkardel	    $STAT_err_pkt++;
401abb0f93cSkardel	    return (&error_status($status),
402abb0f93cSkardel		    $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
403abb0f93cSkardel		    $auth_keyid);
404abb0f93cSkardel	}
405abb0f93cSkardel	else
406abb0f93cSkardel	{
407abb0f93cSkardel	    return ("",
408abb0f93cSkardel		    $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
409abb0f93cSkardel		    $auth_keyid);
410abb0f93cSkardel	}
411abb0f93cSkardel    }
412abb0f93cSkardel    else
413abb0f93cSkardel    {
414abb0f93cSkardel	;# fragment - set up local name space
415abb0f93cSkardel	$id = "$from$seq".&pkt_OP($r_e_m_op);
416abb0f93cSkardel	$ID{$id} = 1;
417abb0f93cSkardel	*FRAGS = "$id FRAGS";
418abb0f93cSkardel	*lastseen = "$id lastseen";
419abb0f93cSkardel
420abb0f93cSkardel	$STAT_frag++;
421abb0f93cSkardel
422abb0f93cSkardel	$lastseen = 1 if !&pkt_M($r_e_m_op);
423*b8ecfcfeSchristos	if (!%FRAGS)
424abb0f93cSkardel	{
425abb0f93cSkardel	    print((&pkt_M($r_e_m_op) ? " more" : "")."\n");
426abb0f93cSkardel	    $FRAGS{$offset} = $data;
427abb0f93cSkardel	    ;# save other info
428abb0f93cSkardel	    @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
429abb0f93cSkardel	}
430abb0f93cSkardel	else
431abb0f93cSkardel	{
432abb0f93cSkardel	    print((&pkt_M($r_e_m_op) ? " more" : "")."\n");
433abb0f93cSkardel	    ;# add frag to previous - combine on the fly
434abb0f93cSkardel	    if (defined($FRAGS{$offset}))
435abb0f93cSkardel	    {
436abb0f93cSkardel		$STAT_dup_frag++;
437abb0f93cSkardel		return ("ERROR","duplicate fragment at $offset seq=$seq");
438abb0f93cSkardel	    }
439abb0f93cSkardel
440abb0f93cSkardel	    $FRAGS{$offset} = $data;
441abb0f93cSkardel
442abb0f93cSkardel	    undef($loff);
443abb0f93cSkardel	    foreach $off (sort numerical keys(%FRAGS))
444abb0f93cSkardel	    {
445abb0f93cSkardel		next unless defined($FRAGS{$off});
446abb0f93cSkardel		if (defined($loff) &&
447abb0f93cSkardel		    ($loff + length($FRAGS{$loff})) == $off)
448abb0f93cSkardel		{
449abb0f93cSkardel		    $FRAGS{$loff} .= $FRAGS{$off};
450abb0f93cSkardel		    delete $FRAGS{$off};
451abb0f93cSkardel		    last;
452abb0f93cSkardel		}
453abb0f93cSkardel		$loff = $off;
454abb0f93cSkardel	    }
455abb0f93cSkardel
456abb0f93cSkardel	    ;# return packet if all frags arrived
457abb0f93cSkardel	    ;# at most two frags with possible padding ???
458abb0f93cSkardel	    if ($lastseen && defined($FRAGS{0}) &&
459abb0f93cSkardel		(((scalar(@x=sort numerical keys(%FRAGS)) == 2) &&
460abb0f93cSkardel		  (length($FRAGS{0}) + 8) > $x[$[+1]) ||
461abb0f93cSkardel		  (scalar(@x=sort numerical keys(%FRAGS)) < 2)))
462abb0f93cSkardel	    {
463abb0f93cSkardel		@x=((&pkt_E($r_e_m_op) ? &error_status($status) : ""),
464abb0f93cSkardel		    $FRAGS{0},@FRAGS);
465abb0f93cSkardel		&pkt_E($r_e_m_op) ? $STAT_err_frag++ : $STAT_frag_all++;
466abb0f93cSkardel		undef(%FRAGS);
467abb0f93cSkardel		undef(@FRAGS);
468abb0f93cSkardel		undef($lastseen);
469abb0f93cSkardel		delete $ID{$id};
470abb0f93cSkardel		&main'clear_timeout($id);
471abb0f93cSkardel		return @x;
472abb0f93cSkardel	    }
473abb0f93cSkardel	    else
474abb0f93cSkardel	    {
475abb0f93cSkardel		&main'set_timeout($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'";
476abb0f93cSkardel	    }
477abb0f93cSkardel	}
478abb0f93cSkardel	return (undef);
479abb0f93cSkardel    }
480abb0f93cSkardel}
481abb0f93cSkardel
482abb0f93cSkardelsub handle_packet_timeout
483abb0f93cSkardel{
484abb0f93cSkardel    local($id) = @_;
485abb0f93cSkardel    local($r_e_m_op,*FRAGS,*lastseen,@x) = (@FRAGS[$[+5]);
486abb0f93cSkardel
487abb0f93cSkardel    *FRAGS = "$id FRAGS";
488abb0f93cSkardel    *lastseen = "$id lastseen";
489abb0f93cSkardel
490abb0f93cSkardel    @x=((&pkt_E($r_e_m_op) ? &error_status($status) : "TIMEOUT"),
491abb0f93cSkardel	$FRAGS{0},@FRAGS[$[ .. $[+4]);
492abb0f93cSkardel    $STAT_frag_timeout++;
493abb0f93cSkardel    undef(%FRAGS);
494abb0f93cSkardel    undef(@FRAGS);
495abb0f93cSkardel    undef($lastseen);
496abb0f93cSkardel    delete $ID{$id};
497abb0f93cSkardel    return @x;
498abb0f93cSkardel}
499abb0f93cSkardel
500abb0f93cSkardel
501abb0f93cSkardelsub pad
502abb0f93cSkardel{
503abb0f93cSkardel    return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]);
504abb0f93cSkardel}
505abb0f93cSkardel
506abb0f93cSkardel1;
507