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]);