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", 151280849Scy 1, "sync_pps", 152280849Scy 2, "sync_lf_clock", 15354359Sroberto 3, "sync_hf_clock", 154280849Scy 4, "sync_uhf_clock", 155280849Scy 5, "sync_local_proto", 156280849Scy 6, "sync_ntp", 157280849Scy 7, "sync_udp/time", 158280849Scy 8, "sync_wristwatch", 159280849Scy 9, "sync_telephone", 16054359Sroberto "-", "ClockSource", 16154359Sroberto ); 16254359Sroberto 16354359Sroberto%SystemEvent = (0, "event_unspec", 164280849Scy 1, "event_freq_not_set", 165280849Scy 2, "event_freq_set", 166280849Scy 3, "event_spike_detect", 167280849Scy 4, "event_freq_mode", 168280849Scy 5, "event_clock_sync", 169280849Scy 6, "event_restart", 170280849Scy 7, "event_panic_stop", 171280849Scy 8, "event_no_sys_peer", 172280849Scy 9, "event_leap_armed", 173280849Scy 10, "event_leap_disarmed", 174280849Scy 11, "event_leap_event", 175280849Scy 12, "event_clock_step", 176280849Scy 13, "event_kern", 177280849Scy 14, "event_loaded_leaps", 178280849Scy 15, "event_stale_leaps", 17954359Sroberto "-", "event", 18054359Sroberto ); 18154359Srobertosub LI 18254359Sroberto{ 18354359Sroberto &getval(&ssw_LI($_[$[]),*LI); 18454359Sroberto} 18554359Srobertosub ClockSource 18654359Sroberto{ 18754359Sroberto &getval(&ssw_CS($_[$[]),*ClockSource); 18854359Sroberto} 18954359Sroberto 19054359Srobertosub SystemEvent 19154359Sroberto{ 19254359Sroberto &getval(&ssw_SECode($_[$[]),*SystemEvent); 19354359Sroberto} 19454359Sroberto 19554359Srobertosub system_status 19654359Sroberto{ 19754359Sroberto return sprintf("%s, %s, %d event%s, %s", &LI($_[$[]), &ClockSource($_[$[]), 19854359Sroberto &ssw_SECnt($_[$[]), ((&ssw_SECnt($_[$[])==1) ? "" : "s"), 19954359Sroberto &SystemEvent($_[$[])); 20054359Sroberto} 20154359Sroberto;#--------------------------------- 20254359Sroberto;# peer status 20354359Sroberto;# 20454359Sroberto;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit 20554359Srobertosub psw_PStat_config { return ($_[$[] & 0x8000) == 0x8000; } 20654359Srobertosub psw_PStat_authenable { return ($_[$[] & 0x4000) == 0x4000; } 20754359Srobertosub psw_PStat_authentic { return ($_[$[] & 0x2000) == 0x2000; } 20854359Srobertosub psw_PStat_reach { return ($_[$[] & 0x1000) == 0x1000; } 209280849Scysub psw_PStat_bcast { return ($_[$[] & 0x0800) == 0x0800; } 21054359Srobertosub psw_PStat { return ($_[$[] >> 10) & 0x3f; } 21154359Srobertosub psw_PSel { return ($_[$[] >> 8) & 0x3; } 21254359Srobertosub psw_PCnt { return ($_[$[] >> 4) & 0xf; } 21354359Srobertosub psw_PCode { return $_[$[] & 0xf; } 21454359Sroberto 21554359Sroberto%PeerSelection = (0, "sel_reject", 216280849Scy 1, "sel_falsetick", 217280849Scy 2, "sel_excess", 218280849Scy 3, "sel_outlier", 219280849Scy 4, "sel_candidate", 220280849Scy 5, "sel_backup", 221280849Scy 6, "sel_sys.peer", 222280849Scy 6, "sel_pps.peer", 22354359Sroberto "-", "PeerSel", 22454359Sroberto ); 22554359Sroberto%PeerEvent = (0, "event_unspec", 226280849Scy 1, "event_mobilize", 227280849Scy 2, "event_demobilize", 22854359Sroberto 3, "event_unreach", 22954359Sroberto 4, "event_reach", 230280849Scy 5, "event_restart", 231280849Scy 6, "event_no_reply", 232280849Scy 7, "event_rate_exceed", 233280849Scy 8, "event_denied", 234280849Scy 9, "event_leap_armed", 235280849Scy 10, "event_sys_peer", 236280849Scy 11, "event_clock_event", 237280849Scy 12, "event_bad_auth", 238280849Scy 13, "event_popcorn", 239280849Scy 14, "event_intlv_mode", 240280849Scy 15, "event_intlv_err", 24154359Sroberto "-", "event", 24254359Sroberto ); 24354359Sroberto 24454359Srobertosub PeerSelection 24554359Sroberto{ 24654359Sroberto &getval(&psw_PSel($_[$[]),*PeerSelection); 24754359Sroberto} 24882498Sroberto 24954359Srobertosub PeerEvent 25054359Sroberto{ 25154359Sroberto &getval(&psw_PCode($_[$[]),*PeerEvent); 25254359Sroberto} 25354359Sroberto 25454359Srobertosub peer_status 25554359Sroberto{ 25654359Sroberto local($x) = (""); 25754359Sroberto $x .= "config," if &psw_PStat_config($_[$[]); 25854359Sroberto $x .= "authenable," if &psw_PStat_authenable($_[$[]); 25954359Sroberto $x .= "authentic," if &psw_PStat_authentic($_[$[]); 26054359Sroberto $x .= "reach," if &psw_PStat_reach($_[$[]); 261280849Scy $x .= "bcast," if &psw_PStat_bcast($_[$[]); 26254359Sroberto 26354359Sroberto $x .= sprintf(" %s, %d event%s, %s", &PeerSelection($_[$[]), 26454359Sroberto &psw_PCnt($_[$[]), ((&psw_PCnt($_[$[]) == 1) ? "" : "s"), 26554359Sroberto &PeerEvent($_[$[])); 26654359Sroberto return $x; 26754359Sroberto} 26854359Sroberto 26954359Sroberto;#--------------------------------- 27054359Sroberto;# clock status 27154359Sroberto;# 27254359Sroberto;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit 27354359Srobertosub csw_CStat { return ($_[$[] >> 8) & 0xff; } 27454359Srobertosub csw_CEvnt { return $_[$[] & 0xff; } 27554359Sroberto 27654359Sroberto%ClockStatus = (0, "clk_nominal", 27754359Sroberto 1, "clk_timeout", 27854359Sroberto 2, "clk_badreply", 27954359Sroberto 3, "clk_fault", 280280849Scy 4, "clk_badsig", 28154359Sroberto 5, "clk_baddate", 28254359Sroberto 6, "clk_badtime", 28354359Sroberto "-", "clk", 28454359Sroberto ); 28554359Sroberto 28654359Srobertosub clock_status 28754359Sroberto{ 28854359Sroberto return sprintf("%s, last %s", 28954359Sroberto &getval(&csw_CStat($_[$[]),*ClockStatus), 29054359Sroberto &getval(&csw_CEvnt($_[$[]),*ClockStatus)); 29154359Sroberto} 29254359Sroberto 29354359Sroberto;#--------------------------------- 29454359Sroberto;# error status 29554359Sroberto;# 29654359Sroberto;# format: |Err|reserved| Err=8bit 29754359Sroberto;# 29854359Srobertosub esw_Err { return ($_[$[] >> 8) & 0xff; } 29954359Sroberto 30054359Sroberto%ErrorStatus = (0, "err_unspec", 30154359Sroberto 1, "err_auth_fail", 30254359Sroberto 2, "err_invalid_fmt", 30354359Sroberto 3, "err_invalid_opcode", 30454359Sroberto 4, "err_unknown_assoc", 30554359Sroberto 5, "err_unknown_var", 30654359Sroberto 6, "err_invalid_value", 30754359Sroberto 7, "err_adm_prohibit", 30854359Sroberto ); 30954359Sroberto 31054359Srobertosub error_status 31154359Sroberto{ 31254359Sroberto return sprintf("%s", &getval(&esw_Err($_[$[]),*ErrorStatus)); 31354359Sroberto} 31454359Sroberto 31554359Sroberto;#----------------------------------------------------------------------------- 31654359Sroberto;# 31754359Sroberto;# cntrl op name translation 31854359Sroberto 319280849Scy%CntrlOpName = (0, "reserved", 320280849Scy 1, "read_status", 32154359Sroberto 2, "read_variables", 32254359Sroberto 3, "write_variables", 32354359Sroberto 4, "read_clock_variables", 32454359Sroberto 5, "write_clock_variables", 32554359Sroberto 6, "set_trap", 32654359Sroberto 7, "trap_response", 327280849Scy 8, "configure", 328280849Scy 9, "saveconf", 329280849Scy 10, "read_mru", 330280849Scy 11, "read_ordlist", 331280849Scy 12, "rqst_nonce", 33254359Sroberto 31, "unset_trap", # !!! unofficial !!! 33354359Sroberto "-", "cntrlop", 33454359Sroberto ); 33554359Sroberto 33654359Srobertosub cntrlop_name 33754359Sroberto{ 33854359Sroberto return &getval($_[$[],*CntrlOpName); 33954359Sroberto} 34054359Sroberto 34154359Sroberto;#----------------------------------------------------------------------------- 34254359Sroberto 34354359Sroberto$STAT_short_pkt = 0; 34454359Sroberto$STAT_pkt = 0; 34554359Sroberto 34654359Sroberto;# process a NTP control message (response) packet 34754359Sroberto;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid) 34854359Sroberto;# $ret: undef --> not yet complete 34954359Sroberto;# "" --> complete packet received 35054359Sroberto;# "ERROR" --> error during receive, bad packet, ... 35154359Sroberto;# else --> error packet - list may contain useful info 35254359Sroberto 35354359Sroberto 35454359Srobertosub handle_packet 35554359Sroberto{ 35654359Sroberto local($pkt,$from) = @_; # parameters 35754359Sroberto local($len_pkt) = (length($pkt)); 35854359Sroberto;# local(*FRAGS,*lastseen); 35954359Sroberto local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data); 36054359Sroberto local($autch_keyid,$auth_cksum); 36154359Sroberto 36254359Sroberto $STAT_pkt++; 36354359Sroberto if ($len_pkt < 12) 36454359Sroberto { 36554359Sroberto $STAT_short_pkt++; 36654359Sroberto return ("ERROR","short packet received"); 36754359Sroberto } 36854359Sroberto 36954359Sroberto ;# now break packet apart 37054359Sroberto ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) = 37154359Sroberto unpack("C2n5a".($len_pkt-12),$pkt); 37254359Sroberto $data=substr($data,$[,$count); 37354359Sroberto if ((($len_pkt - 12) - &pad($count,4)) >= 12) 37454359Sroberto { 37554359Sroberto ;# looks like an authenticator 37654359Sroberto ($auth_keyid,$auth_cksum) = 37754359Sroberto unpack("Na8",substr($pkt,$len_pkt-12+$[,12)); 37854359Sroberto $STAT_auth++; 37954359Sroberto ;# no checking of auth_cksum (yet ?) 38054359Sroberto } 38154359Sroberto 38254359Sroberto if (&pkt_VN($li_vn_mode) != $NTP_version) 38354359Sroberto { 38454359Sroberto $STAT_bad_version++; 38554359Sroberto return ("ERROR","version ".&pkt_VN($li_vn_mode)."packet ignored"); 38654359Sroberto } 38754359Sroberto 38854359Sroberto if (&pkt_MODE($li_vn_mode) != $ctrl_mode) 38954359Sroberto { 39054359Sroberto $STAT_bad_mode++; 39154359Sroberto return ("ERROR", "mode ".&pkt_MODE($li_vn_mode)." packet ignored"); 39254359Sroberto } 39354359Sroberto 39454359Sroberto ;# handle single fragment fast 39554359Sroberto if ($offset == 0 && &pkt_M($r_e_m_op) == 0) 39654359Sroberto { 39754359Sroberto $STAT_single_frag++; 39854359Sroberto if (&pkt_E($r_e_m_op)) 39954359Sroberto { 40054359Sroberto $STAT_err_pkt++; 40154359Sroberto return (&error_status($status), 40254359Sroberto $data,$status,$associd,&pkt_OP($r_e_m_op),$seq, 40354359Sroberto $auth_keyid); 40454359Sroberto } 40554359Sroberto else 40654359Sroberto { 40754359Sroberto return ("", 40854359Sroberto $data,$status,$associd,&pkt_OP($r_e_m_op),$seq, 40954359Sroberto $auth_keyid); 41054359Sroberto } 41154359Sroberto } 41254359Sroberto else 41354359Sroberto { 41454359Sroberto ;# fragment - set up local name space 41554359Sroberto $id = "$from$seq".&pkt_OP($r_e_m_op); 41654359Sroberto $ID{$id} = 1; 41754359Sroberto *FRAGS = "$id FRAGS"; 41854359Sroberto *lastseen = "$id lastseen"; 41954359Sroberto 42054359Sroberto $STAT_frag++; 42154359Sroberto 42254359Sroberto $lastseen = 1 if !&pkt_M($r_e_m_op); 423280849Scy if (!%FRAGS) 42454359Sroberto { 42582498Sroberto print((&pkt_M($r_e_m_op) ? " more" : "")."\n"); 42654359Sroberto $FRAGS{$offset} = $data; 42754359Sroberto ;# save other info 42854359Sroberto @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op); 42954359Sroberto } 43054359Sroberto else 43154359Sroberto { 43282498Sroberto print((&pkt_M($r_e_m_op) ? " more" : "")."\n"); 43354359Sroberto ;# add frag to previous - combine on the fly 43454359Sroberto if (defined($FRAGS{$offset})) 43554359Sroberto { 43654359Sroberto $STAT_dup_frag++; 43754359Sroberto return ("ERROR","duplicate fragment at $offset seq=$seq"); 43854359Sroberto } 43954359Sroberto 44054359Sroberto $FRAGS{$offset} = $data; 44154359Sroberto 44254359Sroberto undef($loff); 44354359Sroberto foreach $off (sort numerical keys(%FRAGS)) 44454359Sroberto { 44554359Sroberto next unless defined($FRAGS{$off}); 44654359Sroberto if (defined($loff) && 44754359Sroberto ($loff + length($FRAGS{$loff})) == $off) 44854359Sroberto { 44954359Sroberto $FRAGS{$loff} .= $FRAGS{$off}; 45054359Sroberto delete $FRAGS{$off}; 45154359Sroberto last; 45254359Sroberto } 45354359Sroberto $loff = $off; 45454359Sroberto } 45554359Sroberto 45654359Sroberto ;# return packet if all frags arrived 45754359Sroberto ;# at most two frags with possible padding ??? 45854359Sroberto if ($lastseen && defined($FRAGS{0}) && 45954359Sroberto (((scalar(@x=sort numerical keys(%FRAGS)) == 2) && 46054359Sroberto (length($FRAGS{0}) + 8) > $x[$[+1]) || 46154359Sroberto (scalar(@x=sort numerical keys(%FRAGS)) < 2))) 46254359Sroberto { 46354359Sroberto @x=((&pkt_E($r_e_m_op) ? &error_status($status) : ""), 46454359Sroberto $FRAGS{0},@FRAGS); 46554359Sroberto &pkt_E($r_e_m_op) ? $STAT_err_frag++ : $STAT_frag_all++; 46654359Sroberto undef(%FRAGS); 46754359Sroberto undef(@FRAGS); 46854359Sroberto undef($lastseen); 46954359Sroberto delete $ID{$id}; 47054359Sroberto &main'clear_timeout($id); 47154359Sroberto return @x; 47254359Sroberto } 47354359Sroberto else 47454359Sroberto { 47554359Sroberto &main'set_timeout($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'"; 47654359Sroberto } 47754359Sroberto } 47854359Sroberto return (undef); 47954359Sroberto } 48054359Sroberto} 48154359Sroberto 48254359Srobertosub handle_packet_timeout 48354359Sroberto{ 48454359Sroberto local($id) = @_; 48554359Sroberto local($r_e_m_op,*FRAGS,*lastseen,@x) = (@FRAGS[$[+5]); 48654359Sroberto 48754359Sroberto *FRAGS = "$id FRAGS"; 48854359Sroberto *lastseen = "$id lastseen"; 48954359Sroberto 49054359Sroberto @x=((&pkt_E($r_e_m_op) ? &error_status($status) : "TIMEOUT"), 49154359Sroberto $FRAGS{0},@FRAGS[$[ .. $[+4]); 49254359Sroberto $STAT_frag_timeout++; 49354359Sroberto undef(%FRAGS); 49454359Sroberto undef(@FRAGS); 49554359Sroberto undef($lastseen); 49654359Sroberto delete $ID{$id}; 49754359Sroberto return @x; 49854359Sroberto} 49954359Sroberto 50054359Sroberto 50154359Srobertosub pad 50254359Sroberto{ 50354359Sroberto return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]); 50454359Sroberto} 50554359Sroberto 50654359Sroberto1; 507