3 ;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
5 ;# process loop filter statistics file and either
6 ;# - show statistics periodically using gnuplot
7 ;# - or print a single plot
10 ;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
13 ;#############################################################
20 $byte1 = (($NTP_version & 0x7)<< 3) & 0x34 | ($ctrl_mode & 0x7);
23 $sequence = 0; # initial sequence number incred before used
25 $do_auth=0; # no possibility today
27 ;#list if known keys (passwords)
28 %KEYS = ( 0, "\200\200\200\200\200\200\200\200",
31 ;#-----------------------------------------------------------------------------
32 ;# access routines for ntp control packet
33 ;# NTP control message format
34 ;# C LI|VN|MODE LI 2bit=00 VN 3bit=2(3) MODE 3bit=6 : $byte1
35 ;# C R|E|M|Op R response E error M more Op opcode
41 ;# a+ data (+ padding)
42 ;# optional authentication data
46 ;# first byte of packet
47 sub pkt_LI
{ return ($_[$[] >> 6) & 0x3; }
48 sub pkt_VN
{ return ($_[$[] >> 3) & 0x7; }
49 sub pkt_MODE
{ return ($_[$[] ) & 0x7; }
51 ;# second byte of packet
52 sub pkt_R
{ return ($_[$[] & 0x80) == 0x80; }
53 sub pkt_E
{ return ($_[$[] & 0x40) == 0x40; }
54 sub pkt_M
{ return ($_[$[] & 0x20) == 0x20; }
55 sub pkt_OP
{ return $_[$[] & 0x1f; }
57 ;#-----------------------------------------------------------------------------
63 $KEYS{$id} = $key if (defined($key));
64 if (! defined($KEYS{$id}))
66 warn "Key $id not yet specified - key not changed\n";
69 return ($keyid,$keyid = $id)[$[];
72 ;#-----------------------------------------------------------------------------
73 sub numerical
{ $a <=> $b; }
75 ;#-----------------------------------------------------------------------------
79 local($fh,$opcode, $associd, $data,$address) = @_;
80 $fh = caller(0)."'$fh";
82 local($junksize,$junk,$packet,$offset,$ret);
88 $junksize = length($data);
89 $junksize = $MAX_DATA if $junksize > $MAX_DATA;
91 ($junk,$data) = $data =~ /^(.{$junksize})(.*)$/;
93 = pack("C2n5a".(($junk eq "") ?
0 : &pad
($junksize+12,$pad)-12),
95 ($opcode & 0x1f) | ($data ?
0x20 : 0),
98 $offset, $junksize, $junk);
103 $offset += $junksize;
105 if (defined($address))
107 $ret = send($fh, $packet, 0, $address);
111 $ret = send($fh, $packet, 0);
116 warn "send failed: $!\n";
119 elsif ($ret != length($packet))
121 warn "send failed: sent only $ret from ".length($packet). "bytes\n";
124 return $sequence unless $data;
128 ;#-----------------------------------------------------------------------------
129 ;# status interpretation
133 local($val,*list
) = @_;
135 return $list{$val} if defined($list{$val});
136 return sprintf("%s#%d",$list{"-"},$val) if defined($list{"-"});
137 return "unknown-$val";
140 ;#---------------------------------
143 ;# format: |LI|CS|SECnt|SECode| LI=2bit CS=6bit SECnt=4bit SECode=4bit
144 sub ssw_LI
{ return ($_[$[] >> 14) & 0x3; }
145 sub ssw_CS
{ return ($_[$[] >> 8) & 0x3f; }
146 sub ssw_SECnt
{ return ($_[$[] >> 4) & 0xf; }
147 sub ssw_SECode
{ return $_[$[] & 0xf; }
149 %LI = ( 0, "leap_none", 1, "leap_add_sec", 2, "leap_del_sec", 3, "sync_alarm", "-", "leap");
150 %ClockSource = (0, "sync_unspec",
154 4, "sync_local_proto",
157 7, "sync_wristwatch",
161 %SystemEvent = (0, "event_unspec",
165 4, "event_sync/strat_chg",
166 5, "event_clock_reset",
168 7, "event_clock_excptn",
173 &getval
(&ssw_LI
($_[$[]),*LI
);
177 &getval
(&ssw_CS
($_[$[]),*ClockSource
);
182 &getval
(&ssw_SECode
($_[$[]),*SystemEvent
);
187 return sprintf("%s, %s, %d event%s, %s", &LI
($_[$[]), &ClockSource
($_[$[]),
188 &ssw_SECnt
($_[$[]), ((&ssw_SECnt
($_[$[])==1) ?
"" : "s"),
189 &SystemEvent
($_[$[]));
191 ;#---------------------------------
194 ;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit
195 sub psw_PStat_config
{ return ($_[$[] & 0x8000) == 0x8000; }
196 sub psw_PStat_authenable
{ return ($_[$[] & 0x4000) == 0x4000; }
197 sub psw_PStat_authentic
{ return ($_[$[] & 0x2000) == 0x2000; }
198 sub psw_PStat_reach
{ return ($_[$[] & 0x1000) == 0x1000; }
199 sub psw_PStat_sane
{ return ($_[$[] & 0x0800) == 0x0800; }
200 sub psw_PStat_dispok
{ return ($_[$[] & 0x0400) == 0x0400; }
201 sub psw_PStat
{ return ($_[$[] >> 10) & 0x3f; }
202 sub psw_PSel
{ return ($_[$[] >> 8) & 0x3; }
203 sub psw_PCnt
{ return ($_[$[] >> 4) & 0xf; }
204 sub psw_PCode
{ return $_[$[] & 0xf; }
206 %PeerSelection = (0, "sel_reject",
212 %PeerEvent = (0, "event_unspec",
217 5, "event_clock_excptn",
218 6, "event_stratum_chg",
224 &getval
(&psw_PSel
($_[$[]),*PeerSelection
);
229 &getval
(&psw_PCode
($_[$[]),*PeerEvent
);
235 $x .= "config," if &psw_PStat_config
($_[$[]);
236 $x .= "authenable," if &psw_PStat_authenable
($_[$[]);
237 $x .= "authentic," if &psw_PStat_authentic
($_[$[]);
238 $x .= "reach," if &psw_PStat_reach
($_[$[]);
239 $x .= &psw_PStat_sane
($_[$[]) ?
"sane," : "insane,";
240 $x .= "hi_disp," unless &psw_PStat_dispok
($_[$[]);
242 $x .= sprintf(" %s, %d event%s, %s", &PeerSelection
($_[$[]),
243 &psw_PCnt
($_[$[]), ((&psw_PCnt
($_[$[]) == 1) ?
"" : "s"),
248 ;#---------------------------------
251 ;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit
252 sub csw_CStat
{ return ($_[$[] >> 8) & 0xff; }
253 sub csw_CEvnt
{ return $_[$[] & 0xff; }
255 %ClockStatus = (0, "clk_nominal",
267 return sprintf("%s, last %s",
268 &getval
(&csw_CStat
($_[$[]),*ClockStatus
),
269 &getval
(&csw_CEvnt
($_[$[]),*ClockStatus
));
272 ;#---------------------------------
275 ;# format: |Err|reserved| Err=8bit
277 sub esw_Err
{ return ($_[$[] >> 8) & 0xff; }
279 %ErrorStatus = (0, "err_unspec",
281 2, "err_invalid_fmt",
282 3, "err_invalid_opcode",
283 4, "err_unknown_assoc",
284 5, "err_unknown_var",
285 6, "err_invalid_value",
286 7, "err_adm_prohibit",
291 return sprintf("%s", &getval
(&esw_Err
($_[$[]),*ErrorStatus
));
294 ;#-----------------------------------------------------------------------------
296 ;# cntrl op name translation
298 %CntrlOpName = (1, "read_status",
300 3, "write_variables",
301 4, "read_clock_variables",
302 5, "write_clock_variables",
305 31, "unset_trap", # !!! unofficial !!!
311 return &getval
($_[$[],*CntrlOpName
);
314 ;#-----------------------------------------------------------------------------
319 ;# process a NTP control message (response) packet
320 ;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid)
321 ;# $ret: undef --> not yet complete
322 ;# "" --> complete packet received
323 ;# "ERROR" --> error during receive, bad packet, ...
324 ;# else --> error packet - list may contain useful info
329 local($pkt,$from) = @_; # parameters
330 local($len_pkt) = (length($pkt));
331 ;# local(*FRAGS,*lastseen);
332 local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data);
333 local($autch_keyid,$auth_cksum);
339 return ("ERROR","short packet received");
342 ;# now break packet apart
343 ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) =
344 unpack("C2n5a".($len_pkt-12),$pkt);
345 $data=substr($data,$[,$count);
346 if ((($len_pkt - 12) - &pad
($count,4)) >= 12)
348 ;# looks like an authenticator
349 ($auth_keyid,$auth_cksum) =
350 unpack("Na8",substr($pkt,$len_pkt-12+$[,12));
352 ;# no checking of auth_cksum (yet ?)
355 if (&pkt_VN
($li_vn_mode) != $NTP_version)
358 return ("ERROR","version ".&pkt_VN
($li_vn_mode)."packet ignored");
361 if (&pkt_MODE
($li_vn_mode) != $ctrl_mode)
364 return ("ERROR", "mode ".&pkt_MODE
($li_vn_mode)." packet ignored");
367 ;# handle single fragment fast
368 if ($offset == 0 && &pkt_M
($r_e_m_op) == 0)
371 if (&pkt_E
($r_e_m_op))
374 return (&error_status
($status),
375 $data,$status,$associd,&pkt_OP
($r_e_m_op),$seq,
381 $data,$status,$associd,&pkt_OP
($r_e_m_op),$seq,
387 ;# fragment - set up local name space
388 $id = "$from$seq".&pkt_OP
($r_e_m_op);
390 *FRAGS
= "$id FRAGS";
391 *lastseen
= "$id lastseen";
395 $lastseen = 1 if !&pkt_M
($r_e_m_op);
396 if (!defined(%FRAGS))
398 print((&pkt_M
($r_e_m_op) ?
" more" : "")."\n");
399 $FRAGS{$offset} = $data;
401 @FRAGS = ($status,$associd,&pkt_OP
($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
405 print((&pkt_M
($r_e_m_op) ?
" more" : "")."\n");
406 ;# add frag to previous - combine on the fly
407 if (defined($FRAGS{$offset}))
410 return ("ERROR","duplicate fragment at $offset seq=$seq");
413 $FRAGS{$offset} = $data;
416 foreach $off (sort numerical
keys(%FRAGS))
418 next unless defined($FRAGS{$off});
419 if (defined($loff) &&
420 ($loff + length($FRAGS{$loff})) == $off)
422 $FRAGS{$loff} .= $FRAGS{$off};
429 ;# return packet if all frags arrived
430 ;# at most two frags with possible padding ???
431 if ($lastseen && defined($FRAGS{0}) &&
432 (((scalar(@x=sort numerical
keys(%FRAGS)) == 2) &&
433 (length($FRAGS{0}) + 8) > $x[$[+1]) ||
434 (scalar(@x=sort numerical
keys(%FRAGS)) < 2)))
436 @x=((&pkt_E
($r_e_m_op) ?
&error_status
($status) : ""),
438 &pkt_E
($r_e_m_op) ?
$STAT_err_frag++ : $STAT_frag_all++;
443 &main
'clear_timeout($id);
448 &main'set_timeout
($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'";
455 sub handle_packet_timeout
458 local($r_e_m_op,*FRAGS
,*lastseen
,@x) = (@FRAGS[$[+5]);
460 *FRAGS
= "$id FRAGS";
461 *lastseen
= "$id lastseen";
463 @x=((&pkt_E
($r_e_m_op) ?
&error_status
($status) : "TIMEOUT"),
464 $FRAGS{0},@FRAGS[$[ .. $[+4]);
465 $STAT_frag_timeout++;
476 return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]);