ntp.pl revision 82498
182498Sroberto#!/usr/bin/perl -w
254359Sroberto;#
354359Sroberto;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
454359Sroberto;#
554359Sroberto;# process loop filter statistics file and either
654359Sroberto;#     - show statistics periodically using gnuplot
754359Sroberto;#     - or print a single plot
854359Sroberto;#
954359Sroberto;#  Copyright (c) 1992
1054359Sroberto;#  Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
1154359Sroberto;#
1254359Sroberto;#
1354359Sroberto;#############################################################
1454359Sroberto
1554359Srobertopackage ntp;
1654359Sroberto
1754359Sroberto$NTP_version = 2;
1854359Sroberto$ctrl_mode=6;
1954359Sroberto
2054359Sroberto$byte1 = (($NTP_version & 0x7)<< 3) & 0x34 | ($ctrl_mode & 0x7);
2154359Sroberto$MAX_DATA = 468;
2254359Sroberto
2354359Sroberto$sequence = 0;			# initial sequence number incred before used
2454359Sroberto$pad=4;
2554359Sroberto$do_auth=0;			# no possibility today
2654359Sroberto$keyid=0;
2754359Sroberto;#list if known keys (passwords)
2854359Sroberto%KEYS = ( 0, "\200\200\200\200\200\200\200\200",
2954359Sroberto	 );
3054359Sroberto
3154359Sroberto;#-----------------------------------------------------------------------------
3254359Sroberto;# access routines for ntp control packet
3354359Sroberto    ;# NTP control message format
3454359Sroberto    ;#  C  LI|VN|MODE  LI 2bit=00  VN 3bit=2(3) MODE 3bit=6 : $byte1
3554359Sroberto    ;#  C  R|E|M|Op    R response  E error    M more   Op opcode
3654359Sroberto    ;#  n  sequence
3754359Sroberto    ;#  n  status
3854359Sroberto    ;#  n  associd
3954359Sroberto    ;#  n  offset
4054359Sroberto    ;#  n  count
4154359Sroberto    ;#  a+ data (+ padding)
4254359Sroberto    ;#  optional authentication data
4354359Sroberto    ;#  N  key
4454359Sroberto    ;#  N2 checksum
4554359Sroberto
4682498Sroberto;# first byte of packet
4754359Srobertosub pkt_LI   { return ($_[$[] >> 6) & 0x3; }
4854359Srobertosub pkt_VN   { return ($_[$[] >> 3) & 0x7; }
4954359Srobertosub pkt_MODE { return ($_[$[]     ) & 0x7; }
5054359Sroberto
5154359Sroberto;# second byte of packet
5254359Srobertosub pkt_R  { return ($_[$[] & 0x80) == 0x80; }
5354359Srobertosub pkt_E  { return ($_[$[] & 0x40) == 0x40; }
5454359Srobertosub pkt_M  { return ($_[$[] & 0x20) == 0x20; }
5554359Srobertosub pkt_OP { return $_[$[] & 0x1f; }
5654359Sroberto
5754359Sroberto;#-----------------------------------------------------------------------------
5854359Sroberto
5954359Srobertosub setkey
6054359Sroberto{
6154359Sroberto    local($id,$key) = @_;
6254359Sroberto
6354359Sroberto    $KEYS{$id} = $key if (defined($key));
6454359Sroberto    if (! defined($KEYS{$id}))
6554359Sroberto    {
6654359Sroberto	warn "Key $id not yet specified - key not changed\n";
6754359Sroberto	return undef;
6854359Sroberto    }
6954359Sroberto    return ($keyid,$keyid = $id)[$[];
7054359Sroberto}
7154359Sroberto
7254359Sroberto;#-----------------------------------------------------------------------------
7354359Srobertosub numerical { $a <=> $b; }
7454359Sroberto
7554359Sroberto;#-----------------------------------------------------------------------------
7654359Sroberto
7754359Srobertosub send	#'
7854359Sroberto{
7954359Sroberto    local($fh,$opcode, $associd, $data,$address) = @_;
8054359Sroberto    $fh = caller(0)."'$fh";
8154359Sroberto
8254359Sroberto    local($junksize,$junk,$packet,$offset,$ret);
8354359Sroberto    $offset = 0;
8454359Sroberto
8554359Sroberto    $sequence++;
8654359Sroberto    while(1)
8754359Sroberto    {
8854359Sroberto	$junksize = length($data);
8954359Sroberto	$junksize = $MAX_DATA if $junksize > $MAX_DATA;
9054359Sroberto
9154359Sroberto	($junk,$data) = $data =~ /^(.{$junksize})(.*)$/;
9254359Sroberto	$packet
9354359Sroberto	    = pack("C2n5a".(($junk eq "") ? 0 : &pad($junksize+12,$pad)-12),
9454359Sroberto		   $byte1,
9554359Sroberto		   ($opcode & 0x1f) | ($data ? 0x20 : 0),
9654359Sroberto		   $sequence,
9754359Sroberto		   0, $associd,
9854359Sroberto		   $offset, $junksize, $junk);
9954359Sroberto	if ($do_auth)
10054359Sroberto	{
10154359Sroberto	    ;# not yet
10254359Sroberto	}
10354359Sroberto	$offset += $junksize;
10454359Sroberto
10554359Sroberto	if (defined($address))
10654359Sroberto	{
10754359Sroberto	    $ret = send($fh, $packet, 0, $address);
10854359Sroberto	}
10954359Sroberto	else
11054359Sroberto	{
11154359Sroberto	    $ret = send($fh, $packet, 0);
11254359Sroberto	}
11354359Sroberto
11454359Sroberto	if (! defined($ret))
11554359Sroberto	{
11654359Sroberto	    warn "send failed: $!\n";
11754359Sroberto	    return undef;
11854359Sroberto	}
11954359Sroberto	elsif ($ret != length($packet))
12054359Sroberto	{
12154359Sroberto	    warn "send failed: sent only $ret from ".length($packet). "bytes\n";
12254359Sroberto	    return undef;
12354359Sroberto	}
12454359Sroberto	return $sequence unless $data;
12554359Sroberto    }
12654359Sroberto}
12754359Sroberto
12854359Sroberto;#-----------------------------------------------------------------------------
12954359Sroberto;# status interpretation
13054359Sroberto;#
13154359Srobertosub getval
13254359Sroberto{
13354359Sroberto    local($val,*list) = @_;
13454359Sroberto
13554359Sroberto    return $list{$val} if defined($list{$val});
13654359Sroberto    return sprintf("%s#%d",$list{"-"},$val) if defined($list{"-"});
13754359Sroberto    return "unknown-$val";
13854359Sroberto}
13954359Sroberto
14054359Sroberto;#---------------------------------
14154359Sroberto;# system status
14254359Sroberto;#
14354359Sroberto;# format: |LI|CS|SECnt|SECode| LI=2bit CS=6bit SECnt=4bit SECode=4bit
14454359Srobertosub ssw_LI     { return ($_[$[] >> 14) & 0x3; }
14554359Srobertosub ssw_CS     { return ($_[$[] >> 8)  & 0x3f; }
14654359Srobertosub ssw_SECnt  { return ($_[$[] >> 4)  & 0xf; }
14754359Srobertosub ssw_SECode { return $_[$[] & 0xf; }
14854359Sroberto
14954359Sroberto%LI = ( 0, "leap_none",  1, "leap_add_sec", 2, "leap_del_sec", 3, "sync_alarm", "-", "leap");
15054359Sroberto%ClockSource = (0, "sync_unspec",
15154359Sroberto		1, "sync_lf_clock",
15254359Sroberto		2, "sync_uhf_clock",
15354359Sroberto		3, "sync_hf_clock",
15454359Sroberto		4, "sync_local_proto",
15554359Sroberto		5, "sync_ntp",
15654359Sroberto		6, "sync_udp/time",
15754359Sroberto		7, "sync_wristwatch",
15854359Sroberto		"-", "ClockSource",
15954359Sroberto		);
16054359Sroberto
16154359Sroberto%SystemEvent = (0, "event_unspec",
16254359Sroberto		1, "event_restart",
16354359Sroberto		2, "event_fault",
16454359Sroberto		3, "event_sync_chg",
16554359Sroberto		4, "event_sync/strat_chg",
16654359Sroberto		5, "event_clock_reset",
16754359Sroberto		6, "event_bad_date",
16854359Sroberto		7, "event_clock_excptn",
16954359Sroberto		"-", "event",
17054359Sroberto		);
17154359Srobertosub LI
17254359Sroberto{
17354359Sroberto    &getval(&ssw_LI($_[$[]),*LI);
17454359Sroberto}
17554359Srobertosub ClockSource
17654359Sroberto{
17754359Sroberto    &getval(&ssw_CS($_[$[]),*ClockSource);
17854359Sroberto}
17954359Sroberto
18054359Srobertosub SystemEvent
18154359Sroberto{
18254359Sroberto    &getval(&ssw_SECode($_[$[]),*SystemEvent);
18354359Sroberto}
18454359Sroberto
18554359Srobertosub system_status
18654359Sroberto{
18754359Sroberto    return sprintf("%s, %s, %d event%s, %s", &LI($_[$[]), &ClockSource($_[$[]),
18854359Sroberto		   &ssw_SECnt($_[$[]), ((&ssw_SECnt($_[$[])==1) ? "" : "s"),
18954359Sroberto		   &SystemEvent($_[$[]));
19054359Sroberto}
19154359Sroberto;#---------------------------------
19254359Sroberto;# peer status
19354359Sroberto;#
19454359Sroberto;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit
19554359Srobertosub psw_PStat_config     { return ($_[$[] & 0x8000) == 0x8000; }
19654359Srobertosub psw_PStat_authenable { return ($_[$[] & 0x4000) == 0x4000; }
19754359Srobertosub psw_PStat_authentic  { return ($_[$[] & 0x2000) == 0x2000; }
19854359Srobertosub psw_PStat_reach      { return ($_[$[] & 0x1000) == 0x1000; }
19954359Srobertosub psw_PStat_sane       { return ($_[$[] & 0x0800) == 0x0800; }
20054359Srobertosub psw_PStat_dispok     { return ($_[$[] & 0x0400) == 0x0400; }
20154359Srobertosub psw_PStat { return ($_[$[] >> 10) & 0x3f; }
20254359Srobertosub psw_PSel  { return ($_[$[] >> 8)  & 0x3;  }
20354359Srobertosub psw_PCnt  { return ($_[$[] >> 4)  & 0xf; }
20454359Srobertosub psw_PCode { return $_[$[] & 0xf; }
20554359Sroberto
20654359Sroberto%PeerSelection = (0, "sel_reject",
20754359Sroberto		  1, "sel_candidate",
20854359Sroberto		  2, "sel_selcand",
20954359Sroberto		  3, "sel_sys.peer",
21054359Sroberto		  "-", "PeerSel",
21154359Sroberto		  );
21254359Sroberto%PeerEvent = (0, "event_unspec",
21354359Sroberto	      1, "event_ip_err",
21454359Sroberto	      2, "event_authen",
21554359Sroberto	      3, "event_unreach",
21654359Sroberto	      4, "event_reach",
21754359Sroberto	      5, "event_clock_excptn",
21854359Sroberto	      6, "event_stratum_chg",
21954359Sroberto	      "-", "event",
22054359Sroberto	      );
22154359Sroberto
22254359Srobertosub PeerSelection
22354359Sroberto{
22454359Sroberto    &getval(&psw_PSel($_[$[]),*PeerSelection);
22554359Sroberto}
22682498Sroberto
22754359Srobertosub PeerEvent
22854359Sroberto{
22954359Sroberto    &getval(&psw_PCode($_[$[]),*PeerEvent);
23054359Sroberto}
23154359Sroberto
23254359Srobertosub peer_status
23354359Sroberto{
23454359Sroberto    local($x) = ("");
23554359Sroberto    $x .= "config,"     if &psw_PStat_config($_[$[]);
23654359Sroberto    $x .= "authenable," if &psw_PStat_authenable($_[$[]);
23754359Sroberto    $x .= "authentic,"  if &psw_PStat_authentic($_[$[]);
23854359Sroberto    $x .= "reach,"      if &psw_PStat_reach($_[$[]);
23954359Sroberto    $x .= &psw_PStat_sane($_[$[]) ? "sane," : "insane,";
24054359Sroberto    $x .= "hi_disp," unless &psw_PStat_dispok($_[$[]);
24154359Sroberto
24254359Sroberto    $x .= sprintf(" %s, %d event%s, %s", &PeerSelection($_[$[]),
24354359Sroberto		  &psw_PCnt($_[$[]), ((&psw_PCnt($_[$[]) == 1) ? "" : "s"),
24454359Sroberto		  &PeerEvent($_[$[]));
24554359Sroberto    return $x;
24654359Sroberto}
24754359Sroberto
24854359Sroberto;#---------------------------------
24954359Sroberto;# clock status
25054359Sroberto;#
25154359Sroberto;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit
25254359Srobertosub csw_CStat { return ($_[$[] >> 8) & 0xff; }
25354359Srobertosub csw_CEvnt { return $_[$[] & 0xff; }
25454359Sroberto
25554359Sroberto%ClockStatus = (0, "clk_nominal",
25654359Sroberto		1, "clk_timeout",
25754359Sroberto		2, "clk_badreply",
25854359Sroberto		3, "clk_fault",
25954359Sroberto		4, "clk_prop",
26054359Sroberto		5, "clk_baddate",
26154359Sroberto		6, "clk_badtime",
26254359Sroberto		"-", "clk",
26354359Sroberto	       );
26454359Sroberto
26554359Srobertosub clock_status
26654359Sroberto{
26754359Sroberto    return sprintf("%s, last %s",
26854359Sroberto		   &getval(&csw_CStat($_[$[]),*ClockStatus),
26954359Sroberto		   &getval(&csw_CEvnt($_[$[]),*ClockStatus));
27054359Sroberto}
27154359Sroberto
27254359Sroberto;#---------------------------------
27354359Sroberto;# error status
27454359Sroberto;#
27554359Sroberto;# format: |Err|reserved|  Err=8bit
27654359Sroberto;#
27754359Srobertosub esw_Err { return ($_[$[] >> 8) & 0xff; }
27854359Sroberto
27954359Sroberto%ErrorStatus = (0, "err_unspec",
28054359Sroberto		1, "err_auth_fail",
28154359Sroberto		2, "err_invalid_fmt",
28254359Sroberto		3, "err_invalid_opcode",
28354359Sroberto		4, "err_unknown_assoc",
28454359Sroberto		5, "err_unknown_var",
28554359Sroberto		6, "err_invalid_value",
28654359Sroberto		7, "err_adm_prohibit",
28754359Sroberto		);
28854359Sroberto
28954359Srobertosub error_status
29054359Sroberto{
29154359Sroberto    return sprintf("%s", &getval(&esw_Err($_[$[]),*ErrorStatus));
29254359Sroberto}
29354359Sroberto
29454359Sroberto;#-----------------------------------------------------------------------------
29554359Sroberto;#
29654359Sroberto;# cntrl op name translation
29754359Sroberto
29854359Sroberto%CntrlOpName = (1, "read_status",
29954359Sroberto		2, "read_variables",
30054359Sroberto		3, "write_variables",
30154359Sroberto		4, "read_clock_variables",
30254359Sroberto		5, "write_clock_variables",
30354359Sroberto		6, "set_trap",
30454359Sroberto		7, "trap_response",
30554359Sroberto		31, "unset_trap", # !!! unofficial !!!
30654359Sroberto		"-", "cntrlop",
30754359Sroberto		);
30854359Sroberto
30954359Srobertosub cntrlop_name
31054359Sroberto{
31154359Sroberto    return &getval($_[$[],*CntrlOpName);
31254359Sroberto}
31354359Sroberto
31454359Sroberto;#-----------------------------------------------------------------------------
31554359Sroberto
31654359Sroberto$STAT_short_pkt = 0;
31754359Sroberto$STAT_pkt = 0;
31854359Sroberto
31954359Sroberto;# process a NTP control message (response) packet
32054359Sroberto;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid)
32154359Sroberto;#      $ret: undef     --> not yet complete
32254359Sroberto;#            ""        --> complete packet received
32354359Sroberto;#            "ERROR"   --> error during receive, bad packet, ...
32454359Sroberto;#          else        --> error packet - list may contain useful info
32554359Sroberto
32654359Sroberto
32754359Srobertosub handle_packet
32854359Sroberto{
32954359Sroberto    local($pkt,$from) = @_;	# parameters
33054359Sroberto    local($len_pkt) = (length($pkt));
33154359Sroberto;#    local(*FRAGS,*lastseen);
33254359Sroberto    local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data);
33354359Sroberto    local($autch_keyid,$auth_cksum);
33454359Sroberto
33554359Sroberto    $STAT_pkt++;
33654359Sroberto    if ($len_pkt < 12)
33754359Sroberto    {
33854359Sroberto	$STAT_short_pkt++;
33954359Sroberto	return ("ERROR","short packet received");
34054359Sroberto    }
34154359Sroberto
34254359Sroberto    ;# now break packet apart
34354359Sroberto    ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) =
34454359Sroberto	unpack("C2n5a".($len_pkt-12),$pkt);
34554359Sroberto    $data=substr($data,$[,$count);
34654359Sroberto    if ((($len_pkt - 12) - &pad($count,4)) >= 12)
34754359Sroberto    {
34854359Sroberto	;# looks like an authenticator
34954359Sroberto	($auth_keyid,$auth_cksum) =
35054359Sroberto	    unpack("Na8",substr($pkt,$len_pkt-12+$[,12));
35154359Sroberto	$STAT_auth++;
35254359Sroberto	;# no checking of auth_cksum (yet ?)
35354359Sroberto    }
35454359Sroberto
35554359Sroberto    if (&pkt_VN($li_vn_mode) != $NTP_version)
35654359Sroberto    {
35754359Sroberto	$STAT_bad_version++;
35854359Sroberto	return ("ERROR","version ".&pkt_VN($li_vn_mode)."packet ignored");
35954359Sroberto    }
36054359Sroberto
36154359Sroberto    if (&pkt_MODE($li_vn_mode) != $ctrl_mode)
36254359Sroberto    {
36354359Sroberto	$STAT_bad_mode++;
36454359Sroberto	return ("ERROR", "mode ".&pkt_MODE($li_vn_mode)." packet ignored");
36554359Sroberto    }
36654359Sroberto
36754359Sroberto    ;# handle single fragment fast
36854359Sroberto    if ($offset == 0 && &pkt_M($r_e_m_op) == 0)
36954359Sroberto    {
37054359Sroberto	$STAT_single_frag++;
37154359Sroberto	if (&pkt_E($r_e_m_op))
37254359Sroberto	{
37354359Sroberto	    $STAT_err_pkt++;
37454359Sroberto	    return (&error_status($status),
37554359Sroberto		    $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
37654359Sroberto		    $auth_keyid);
37754359Sroberto	}
37854359Sroberto	else
37954359Sroberto	{
38054359Sroberto	    return ("",
38154359Sroberto		    $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
38254359Sroberto		    $auth_keyid);
38354359Sroberto	}
38454359Sroberto    }
38554359Sroberto    else
38654359Sroberto    {
38754359Sroberto	;# fragment - set up local name space
38854359Sroberto	$id = "$from$seq".&pkt_OP($r_e_m_op);
38954359Sroberto	$ID{$id} = 1;
39054359Sroberto	*FRAGS = "$id FRAGS";
39154359Sroberto	*lastseen = "$id lastseen";
39254359Sroberto
39354359Sroberto	$STAT_frag++;
39454359Sroberto
39554359Sroberto	$lastseen = 1 if !&pkt_M($r_e_m_op);
39654359Sroberto	if (!defined(%FRAGS))
39754359Sroberto	{
39882498Sroberto	    print((&pkt_M($r_e_m_op) ? " more" : "")."\n");
39954359Sroberto	    $FRAGS{$offset} = $data;
40054359Sroberto	    ;# save other info
40154359Sroberto	    @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
40254359Sroberto	}
40354359Sroberto	else
40454359Sroberto	{
40582498Sroberto	    print((&pkt_M($r_e_m_op) ? " more" : "")."\n");
40654359Sroberto	    ;# add frag to previous - combine on the fly
40754359Sroberto	    if (defined($FRAGS{$offset}))
40854359Sroberto	    {
40954359Sroberto		$STAT_dup_frag++;
41054359Sroberto		return ("ERROR","duplicate fragment at $offset seq=$seq");
41154359Sroberto	    }
41254359Sroberto
41354359Sroberto	    $FRAGS{$offset} = $data;
41454359Sroberto
41554359Sroberto	    undef($loff);
41654359Sroberto	    foreach $off (sort numerical keys(%FRAGS))
41754359Sroberto	    {
41854359Sroberto		next unless defined($FRAGS{$off});
41954359Sroberto		if (defined($loff) &&
42054359Sroberto		    ($loff + length($FRAGS{$loff})) == $off)
42154359Sroberto		{
42254359Sroberto		    $FRAGS{$loff} .= $FRAGS{$off};
42354359Sroberto		    delete $FRAGS{$off};
42454359Sroberto		    last;
42554359Sroberto		}
42654359Sroberto		$loff = $off;
42754359Sroberto	    }
42854359Sroberto
42954359Sroberto	    ;# return packet if all frags arrived
43054359Sroberto	    ;# at most two frags with possible padding ???
43154359Sroberto	    if ($lastseen && defined($FRAGS{0}) &&
43254359Sroberto		(((scalar(@x=sort numerical keys(%FRAGS)) == 2) &&
43354359Sroberto		  (length($FRAGS{0}) + 8) > $x[$[+1]) ||
43454359Sroberto		  (scalar(@x=sort numerical keys(%FRAGS)) < 2)))
43554359Sroberto	    {
43654359Sroberto		@x=((&pkt_E($r_e_m_op) ? &error_status($status) : ""),
43754359Sroberto		    $FRAGS{0},@FRAGS);
43854359Sroberto		&pkt_E($r_e_m_op) ? $STAT_err_frag++ : $STAT_frag_all++;
43954359Sroberto		undef(%FRAGS);
44054359Sroberto		undef(@FRAGS);
44154359Sroberto		undef($lastseen);
44254359Sroberto		delete $ID{$id};
44354359Sroberto		&main'clear_timeout($id);
44454359Sroberto		return @x;
44554359Sroberto	    }
44654359Sroberto	    else
44754359Sroberto	    {
44854359Sroberto		&main'set_timeout($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'";
44954359Sroberto	    }
45054359Sroberto	}
45154359Sroberto	return (undef);
45254359Sroberto    }
45354359Sroberto}
45454359Sroberto
45554359Srobertosub handle_packet_timeout
45654359Sroberto{
45754359Sroberto    local($id) = @_;
45854359Sroberto    local($r_e_m_op,*FRAGS,*lastseen,@x) = (@FRAGS[$[+5]);
45954359Sroberto
46054359Sroberto    *FRAGS = "$id FRAGS";
46154359Sroberto    *lastseen = "$id lastseen";
46254359Sroberto
46354359Sroberto    @x=((&pkt_E($r_e_m_op) ? &error_status($status) : "TIMEOUT"),
46454359Sroberto	$FRAGS{0},@FRAGS[$[ .. $[+4]);
46554359Sroberto    $STAT_frag_timeout++;
46654359Sroberto    undef(%FRAGS);
46754359Sroberto    undef(@FRAGS);
46854359Sroberto    undef($lastseen);
46954359Sroberto    delete $ID{$id};
47054359Sroberto    return @x;
47154359Sroberto}
47254359Sroberto
47354359Sroberto
47454359Srobertosub pad
47554359Sroberto{
47654359Sroberto    return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]);
47754359Sroberto}
47854359Sroberto
47954359Sroberto1;
480