/contrib/ntp/scripts/monitoring/ntp.pl

https://bitbucket.org/freebsd/freebsd-head/ · Perl · 479 lines · 418 code · 60 blank · 1 comment · 42 complexity · 9e00e2813324cd1605cead96347e1743 MD5 · raw file

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