Home | History | Annotate | Line # | Download | only in ntpd
      1 /*	$NetBSD: ntpsim.c,v 1.6 2020/05/25 20:47:25 christos Exp $	*/
      2 
      3 /* ntpdsim.c
      4  *
      5  * The source code for the ntp discrete event simulator.
      6  *
      7  * Written By:	Sachin Kamboj
      8  *		University of Delaware
      9  *		Newark, DE 19711
     10  * Copyright (c) 2006
     11  * (Some code shamelessly based on the original NTP discrete event simulator)
     12  */
     13 
     14 #include <config.h>
     15 #ifdef SIM
     16 #include "ntpd.h"
     17 #include "ntp_config.h"
     18 
     19 /* forward prototypes */
     20 int determine_event_ordering(const Event *e1, const Event *e2);
     21 int determine_recv_buf_ordering(const struct recvbuf *b1,
     22 				const struct recvbuf *b2);
     23 void create_server_associations(void);
     24 void init_sim_io(void);
     25 
     26 /* Global Variable Definitions */
     27 sim_info simulation;		/* Simulation Control Variables */
     28 local_clock_info simclock;	/* Local Clock Variables */
     29 queue *event_queue;		/* Event Queue */
     30 queue *recv_queue;		/* Receive Queue */
     31 static double sys_residual = 0;	/* adjustment residue (s) */
     32 
     33 void (*event_ptr[]) (Event *) = {
     34     sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
     35 };			/* Function pointer to the events */
     36 
     37 
     38 /*
     39  * Define a function to compare two events to determine which one occurs
     40  * first.
     41  */
     42 int
     43 determine_event_ordering(
     44 	const Event *e1,
     45 	const Event *e2
     46 	)
     47 {
     48 	return (e1->time - e2->time);
     49 }
     50 
     51 
     52 /*
     53  * Define a function to compare two received packets to determine which
     54  * one is received first.
     55  */
     56 int
     57 determine_recv_buf_ordering(
     58 	const struct recvbuf *b1,
     59 	const struct recvbuf *b2
     60 	)
     61 {
     62 	double recv_time1;
     63 	double recv_time2;
     64 
     65 	/* Simply convert the time received to double and subtract */
     66 	LFPTOD(&b1->recv_time, recv_time1);
     67 	LFPTOD(&b2->recv_time, recv_time2);
     68 
     69 	return (int)(recv_time1 - recv_time2);
     70 }
     71 
     72 
     73 /* Define a function to create the server associations */
     74 void create_server_associations(void)
     75 {
     76 	int i;
     77 
     78 	for (i = 0; i < simulation.num_of_servers; ++i) {
     79 		printf("%s\n", stoa(simulation.servers[i].addr));
     80 		if (peer_config(simulation.servers[i].addr,
     81 				NULL,
     82 				loopback_interface,
     83 				MODE_CLIENT,
     84 				-1,
     85 				NTP_VERSION,
     86 				NTP_MINDPOLL,
     87 				NTP_MAXDPOLL,
     88 				0, /* peerflags */
     89 				0, /* ttl */
     90 				0, /* peerkey */
     91 				NULL /* group ident */) == 0) {
     92 			fprintf(stderr,
     93 				"ERROR!! Could not create association for: %s\n",
     94 				stoa(simulation.servers[i].addr));
     95 		}
     96 	}
     97 }
     98 
     99 
    100 /* Main Simulator Code */
    101 
    102 int
    103 ntpsim(
    104 	int	argc,
    105 	char *	argv[]
    106 	)
    107 {
    108 	Event *		curr_event;
    109 	struct timeval	seed;
    110 
    111 	/* Initialize the local Clock */
    112 	simclock.local_time = 0;
    113 	simclock.adj = 0;
    114 	simclock.slew = 500e-6;
    115 
    116 	/* Initialize the simulation */
    117 	simulation.num_of_servers = 0;
    118 	simulation.beep_delay = BEEP_DLY;
    119 	simulation.sim_time = 0;
    120 	simulation.end_time = SIM_TIME;
    121 
    122 	/* Initialize ntp modules */
    123 	initializing = TRUE;
    124 	msyslog_term = TRUE;
    125 	init_sim_io();
    126 	init_auth();
    127 	init_util();
    128 	init_restrict();
    129 	init_mon();
    130 	init_timer();
    131 	init_lib();
    132 	init_request();
    133 	init_control();
    134 	init_peer();
    135 	init_proto();
    136 	init_loopfilter();
    137 	mon_start(MON_OFF);
    138 
    139 	/* Call getconfig to parse the configuration file */
    140 	getconfig(argc, argv);
    141 	loop_config(LOOP_DRIFTINIT, 0);
    142 	initializing = FALSE;
    143 
    144 	/*
    145 	 * Watch out here, we want the real time, not the silly stuff.
    146 	 */
    147 	gettimeofday(&seed, NULL);
    148 	ntp_srandom(seed.tv_usec);
    149 
    150 	/* Initialize the event queue */
    151 	event_queue = create_priority_queue((q_order_func)
    152 	    determine_event_ordering);
    153 
    154 	/* Initialize the receive queue */
    155 	recv_queue = create_priority_queue((q_order_func)
    156 	    determine_recv_buf_ordering);
    157 
    158 	/* Push a beep and a timer on the event queue */
    159 	enqueue(event_queue, event(0, BEEP));
    160 	enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
    161 
    162 	/*
    163 	 * Pop the queue until nothing is left or time is exceeded
    164 	 */
    165 	/* maxtime = simulation.sim_time + simulation.end_time;*/
    166 	while (simulation.sim_time <= simulation.end_time &&
    167 	   (!empty(event_queue))) {
    168 		curr_event = dequeue(event_queue);
    169 		/* Update all the clocks to the time on the event */
    170 		sim_update_clocks(curr_event);
    171 
    172 		/* Execute the function associated with the event */
    173 		(*event_ptr[curr_event->function])(curr_event);
    174 		free_node(curr_event);
    175 	}
    176 	printf("sys_received: %lu\n", sys_received);
    177 	printf("sys_badlength: %lu\n", sys_badlength);
    178 	printf("sys_declined: %lu\n", sys_declined);
    179 	printf("sys_restricted: %lu\n", sys_restricted);
    180 	printf("sys_newversion: %lu\n", sys_newversion);
    181 	printf("sys_oldversion: %lu\n", sys_oldversion);
    182 	printf("sys_limitrejected: %lu\n", sys_limitrejected);
    183 	printf("sys_badauth: %lu\n", sys_badauth);
    184 
    185 	return (0);
    186 }
    187 
    188 
    189 void
    190 init_sim_io(void)
    191 {
    192 	loopback_interface = emalloc_zero(sizeof(*loopback_interface));
    193 	ep_list = loopback_interface;
    194 	strlcpy(loopback_interface->name, "IPv4loop",
    195 		sizeof(loopback_interface->name));
    196 	loopback_interface->flags = INT_UP | INT_LOOPBACK;
    197 	loopback_interface->fd = -1;
    198 	loopback_interface->bfd = -1;
    199 	loopback_interface->ifnum = 1;
    200 	loopback_interface->family = AF_INET;
    201 	AF(&loopback_interface->sin) = AF_INET;
    202 	SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
    203 	SET_PORT(&loopback_interface->sin, NTP_PORT);
    204 	AF(&loopback_interface->mask) = AF_INET;
    205 	SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
    206 }
    207 
    208 
    209 /* Define a function to create an return an Event  */
    210 
    211 Event *event(double t, funcTkn f)
    212 {
    213     Event *e;
    214 
    215     if ((e = get_node(sizeof(*e))) == NULL)
    216 	abortsim("get_node failed in event");
    217     e->time = t;
    218     e->function = f;
    219     return (e);
    220 }
    221 
    222 /* NTP SIMULATION FUNCTIONS */
    223 
    224 /* Define a function for processing a timer interrupt.
    225  * On every timer interrupt, call the NTP timer to send packets and process
    226  * the clock and then call the receive function to receive packets.
    227  */
    228 void sim_event_timer(Event *e)
    229 {
    230     struct recvbuf *rbuf;
    231 
    232     /* Call the NTP timer.
    233      * This will be responsible for actually "sending the packets."
    234      * Since this is a simulation, the packets sent over the network
    235      * will be processed by the simulate_server routine below.
    236      */
    237     timer();
    238 
    239     /* Process received buffers */
    240     while (!empty(recv_queue)) {
    241 	rbuf = (struct recvbuf *)dequeue(recv_queue);
    242 	(*rbuf->receiver)(rbuf);
    243 	free_node(rbuf);
    244     }
    245 
    246     /* Arm the next timer interrupt. */
    247     enqueue(event_queue,
    248 	    event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
    249 }
    250 
    251 
    252 
    253 /* Define a function to simulate a server.
    254  * This function processes the sent packet according to the server script,
    255  * creates a reply packet and pushes the reply packet onto the event queue
    256  */
    257 int simulate_server(
    258     sockaddr_u *serv_addr,	/* Address of the server */
    259     endpt *	inter,		/* Interface on which the reply should
    260 				   be inserted */
    261     struct pkt *rpkt		/* Packet sent to the server that
    262 				   needs to be processed. */
    263     )
    264 {
    265     struct pkt xpkt;		/* Packet to be transmitted back
    266 				   to the client */
    267     struct recvbuf rbuf;	/* Buffer for the received packet */
    268     Event *e;			/* Packet receive event */
    269     server_info *server;	/* Pointer to the server being simulated */
    270     script_info *curr_script;	/* Current script being processed */
    271     int i;
    272     double d1, d2, d3;		/* Delays while the packet is enroute */
    273     double t1, t2, t3, t4;	/* The four timestamps in the packet */
    274     l_fp lfp_host;		/* host-order l_fp */
    275 
    276     ZERO(xpkt);
    277     ZERO(rbuf);
    278 
    279     /* Search for the server with the desired address */
    280     server = NULL;
    281     for (i = 0; i < simulation.num_of_servers; ++i) {
    282 	if (memcmp(simulation.servers[i].addr, serv_addr,
    283 		   sizeof(*serv_addr)) == 0) {
    284 	    server = &simulation.servers[i];
    285 	    break;
    286 	}
    287     }
    288 
    289     fprintf(stderr, "Received packet from %s on %s\n",
    290 	    stoa(serv_addr), latoa(inter));
    291     if (server == NULL)
    292 	abortsim("Server with specified address not found!!!");
    293 
    294     /* Get the current script for the server */
    295     curr_script = server->curr_script;
    296 
    297     /* Create a server reply packet.
    298      * Masquerade the reply as a stratum-1 server with a GPS clock
    299      */
    300     xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
    301 				     MODE_SERVER);
    302     xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
    303     memcpy(&xpkt.refid, "GPS", 4);
    304     xpkt.ppoll = rpkt->ppoll;
    305     xpkt.precision = rpkt->precision;
    306     xpkt.rootdelay = 0;
    307     xpkt.rootdisp = 0;
    308 
    309     /* TIMESTAMP CALCULATIONS
    310 	    t1				 t4
    311 	     \				/
    312 	  d1  \			       / d3
    313 	       \		      /
    314 	       t2 ----------------- t3
    315 			 d2
    316     */
    317     /* Compute the delays */
    318     d1 = poisson(curr_script->prop_delay, curr_script->jitter);
    319     d2 = poisson(curr_script->proc_delay, 0);
    320     d3 = poisson(curr_script->prop_delay, curr_script->jitter);
    321 
    322     /* Note: In the transmitted packet:
    323      * 1. t1 and t4 are times in the client according to the local clock.
    324      * 2. t2 and t3 are server times according to the simulated server.
    325      * Compute t1, t2, t3 and t4
    326      * Note: This function is called at time t1.
    327      */
    328 
    329     NTOHL_FP(&rpkt->xmt, &lfp_host);
    330     LFPTOD(&lfp_host, t1);
    331     t2 = server->server_time + d1;
    332     t3 = server->server_time + d1 + d2;
    333     t4 = t1 + d1 + d2 + d3;
    334 
    335     /* Save the timestamps */
    336     xpkt.org = rpkt->xmt;
    337     DTOLFP(t2, &lfp_host);
    338     HTONL_FP(&lfp_host, &xpkt.rec);
    339     DTOLFP(t3, &lfp_host);
    340     HTONL_FP(&lfp_host, &xpkt.xmt);
    341     xpkt.reftime = xpkt.xmt;
    342 
    343     /*
    344      * Ok, we are done with the packet. Now initialize the receive
    345      * buffer for the packet.
    346      */
    347     rbuf.used = 1;
    348     rbuf.receiver = &receive;   /* callback to process the packet */
    349     rbuf.recv_length = LEN_PKT_NOMAC;
    350     rbuf.recv_pkt = xpkt;
    351     rbuf.dstadr = inter;
    352     rbuf.fd = inter->fd;
    353     memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
    354     memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
    355 
    356     /*
    357      * Create a packet event and insert it onto the event_queue at the
    358      * arrival time (t4) of the packet at the client
    359      */
    360     e = event(t4, PACKET);
    361     e->rcv_buf = rbuf;
    362     enqueue(event_queue, e);
    363 
    364     /*
    365      * Check if the time of the script has expired. If yes, delete it.
    366      */
    367     if (curr_script->duration > simulation.sim_time &&
    368 	NULL == HEAD_PFIFO(server->script)) {
    369 	printf("Hello\n");
    370 	/*
    371 	 * For some reason freeing up the curr_script memory kills the
    372 	 * simulation. Further debugging is needed to determine why.
    373 	 * free(curr_script);
    374 	 */
    375 	UNLINK_FIFO(curr_script, *server->script, link);
    376     }
    377 
    378     return (0);
    379 }
    380 
    381 
    382 /* Define a function to update all the clocks
    383  * Most of the code is modified from the systime.c file by Prof. Mills
    384  */
    385 
    386 void sim_update_clocks(Event *e)
    387 {
    388     double time_gap;
    389     double adj;
    390     int i;
    391 
    392     /* Compute the time between the last update event and this update */
    393     time_gap = e->time - simulation.sim_time;
    394 
    395     if (time_gap < 0)
    396 	    printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
    397 		   e->time, simulation.sim_time, time_gap);
    398 
    399     /* Advance the client clock */
    400     if (e->time + time_gap < simclock.local_time)
    401 	    printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
    402 		   e->time + time_gap, simclock.local_time);
    403     simclock.local_time = e->time + time_gap;
    404 
    405     /* Advance the simulation time */
    406     simulation.sim_time = e->time;
    407 
    408     /* Advance the server clocks adjusted for systematic and random frequency
    409      * errors. The random error is a random walk computed as the
    410      * integral of samples from a Gaussian distribution.
    411      */
    412     for (i = 0; i < simulation.num_of_servers; ++i) {
    413 	simulation.servers[i].curr_script->freq_offset +=
    414 	    gauss(0, time_gap * simulation.servers[i].curr_script->wander);
    415 
    416 	simulation.servers[i].server_time += time_gap *
    417 	    (1 + simulation.servers[i].curr_script->freq_offset);
    418     }
    419 
    420     /* Perform the adjtime() function. If the adjustment completed
    421      * in the previous interval, amortize the entire amount; if not,
    422      * carry the leftover to the next interval.
    423      */
    424 
    425     adj = time_gap * simclock.slew;
    426     if (adj < fabs(simclock.adj)) {
    427 	if (simclock.adj < 0) {
    428 	    simclock.adj += adj;
    429 	    simclock.local_time -= adj;
    430 	} else {
    431 	    simclock.adj -= adj;
    432 	    simclock.local_time += adj;
    433 	}
    434     } else {
    435 	simclock.local_time += simclock.adj;
    436 	simclock.adj = 0;
    437     }
    438 }
    439 
    440 
    441 /* Define a function that processes a receive packet event.
    442  * This function simply inserts the packet received onto the receive queue
    443  */
    444 
    445 void sim_event_recv_packet(Event *e)
    446 {
    447     struct recvbuf *rbuf;
    448 
    449     /* Allocate a receive buffer and copy the packet to it */
    450     if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
    451 	abortsim("get_node failed in sim_event_recv_packet");
    452     memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
    453 
    454     /* Store the local time in the received packet */
    455     DTOLFP(simclock.local_time, &rbuf->recv_time);
    456 
    457     /* Insert the packet received onto the receive queue */
    458     enqueue(recv_queue, rbuf);
    459 }
    460 
    461 
    462 
    463 /* Define a function to output simulation statistics on a beep event
    464  */
    465 
    466 /*** TODO: Need to decide on how to output for multiple servers ***/
    467 void sim_event_beep(Event *e)
    468 {
    469 #if 0
    470     static int first_time = 1;
    471     char *dash = "-----------------";
    472 #endif
    473 
    474     fprintf(stderr, "BEEP!!!\n");
    475     enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
    476 #if 0
    477     if(simulation.beep_delay > 0) {
    478 	if (first_time) {
    479 	    printf("\t%4c    T    %4c\t%4c  T+ERR  %3c\t%5cT+ERR+NTP\n",
    480 	           ' ', ' ', ' ', ' ',' ');
    481 	    printf("\t%s\t%s\t%s\n", dash, dash, dash);
    482 	    first_time = 0;
    483 
    484 	    printf("\t%16.6f\t%16.6f\t%16.6f\n",
    485 	           n->time, n->clk_time, n->ntp_time);
    486 	    return;
    487 	}
    488 	printf("\t%16.6f\t%16.6f\t%16.6f\n",
    489 	       simclock.local_time,
    490 	       n->time, n->clk_time, n->ntp_time);
    491 #endif
    492 
    493 }
    494 
    495 
    496 /* Define a function to abort the simulation on an error and spit out an
    497  * error message
    498  */
    499 
    500 void abortsim(char *errmsg)
    501 {
    502     perror(errmsg);
    503     exit(1);
    504 }
    505 
    506 
    507 
    508 /* CODE ORIGINALLY IN libntp/systime.c
    509  * -----------------------------------
    510  * This code was a part of the original NTP simulator and originally
    511  * had its home in the libntp/systime.c file.
    512  *
    513  * It has been shamelessly moved to here and has been modified for the
    514  * purposes of the current simulator.
    515  */
    516 
    517 
    518 /*
    519  * get_systime - return the system time in NTP timestamp format
    520  */
    521 void
    522 get_systime(
    523     l_fp *now		/* current system time in l_fp */        )
    524 {
    525     /*
    526      * To fool the code that determines the local clock precision,
    527      * we advance the clock a minimum of 200 nanoseconds on every
    528      * clock read. This is appropriate for a typical modern machine
    529      * with nanosecond clocks. Note we make no attempt here to
    530      * simulate reading error, since the error is so small. This may
    531      * change when the need comes to implement picosecond clocks.
    532      */
    533     if (simclock.local_time == simclock.last_read_time)
    534         simclock.local_time += 200e-9;
    535 
    536     simclock.last_read_time = simclock.local_time;
    537     DTOLFP(simclock.local_time, now);
    538 /* OLD Code
    539    if (ntp_node.ntp_time == ntp_node.last_time)
    540    ntp_node.ntp_time += 200e-9;
    541    ntp_node.last_time = ntp_node.ntp_time;
    542    DTOLFP(ntp_node.ntp_time, now);
    543 */
    544 }
    545 
    546 
    547 /*
    548  * adj_systime - advance or retard the system clock exactly like the
    549  * real thng.
    550  */
    551 int				/* always succeeds */
    552 adj_systime(
    553     double now		/* time adjustment (s) */
    554     )
    555 {
    556     struct timeval adjtv;	/* new adjustment */
    557     double	dtemp;
    558     long	ticks;
    559     int	isneg = 0;
    560 
    561     /*
    562      * Most Unix adjtime() implementations adjust the system clock
    563      * in microsecond quanta, but some adjust in 10-ms quanta. We
    564      * carefully round the adjustment to the nearest quantum, then
    565      * adjust in quanta and keep the residue for later.
    566      */
    567     dtemp = now + sys_residual;
    568     if (dtemp < 0) {
    569 	isneg = 1;
    570 	dtemp = -dtemp;
    571     }
    572     adjtv.tv_sec = (long)dtemp;
    573     dtemp -= adjtv.tv_sec;
    574     ticks = (long)(dtemp / sys_tick + .5);
    575     adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
    576     dtemp -= adjtv.tv_usec / 1e6;
    577     sys_residual = dtemp;
    578 
    579     /*
    580      * Convert to signed seconds and microseconds for the Unix
    581      * adjtime() system call. Note we purposely lose the adjtime()
    582      * leftover.
    583      */
    584     if (isneg) {
    585 	adjtv.tv_sec = -adjtv.tv_sec;
    586 	adjtv.tv_usec = -adjtv.tv_usec;
    587 	sys_residual = -sys_residual;
    588     }
    589     simclock.adj = now;
    590 /*	ntp_node.adj = now; */
    591     return (1);
    592 }
    593 
    594 
    595 /*
    596  * step_systime - step the system clock. We are religious here.
    597  */
    598 int				/* always succeeds */
    599 step_systime(
    600     double now		/* step adjustment (s) */
    601     )
    602 {
    603 #ifdef DEBUG
    604     if (debug)
    605 	printf("step_systime: time %.6f adj %.6f\n",
    606 	       simclock.local_time, now);
    607 #endif
    608     simclock.local_time += now;
    609     return (1);
    610 }
    611 
    612 /*
    613  * gauss() - returns samples from a gaussion distribution
    614  */
    615 double				/* Gaussian sample */
    616 gauss(
    617     double m,		/* sample mean */
    618     double s		/* sample standard deviation (sigma) */
    619     )
    620 {
    621     double q1, q2;
    622 
    623     /*
    624      * Roll a sample from a Gaussian distribution with mean m and
    625      * standard deviation s. For m = 0, s = 1, mean(y) = 0,
    626      * std(y) = 1.
    627      */
    628     if (s == 0)
    629         return (m);
    630     while ((q1 = drand48()) == 0)
    631 	/* empty statement */;
    632     q2 = drand48();
    633     return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
    634 }
    635 
    636 
    637 /*
    638  * poisson() - returns samples from a network delay distribution
    639  */
    640 double				/* delay sample (s) */
    641 poisson(
    642     double m,		/* fixed propagation delay (s) */
    643     double s		/* exponential parameter (mu) */
    644     )
    645 {
    646     double q1;
    647 
    648     /*
    649      * Roll a sample from a composite distribution with propagation
    650      * delay m and exponential distribution time with parameter s.
    651      * For m = 0, s = 1, mean(y) = std(y) = 1.
    652      */
    653     if (s == 0)
    654         return (m);
    655     while ((q1 = drand48()) == 0)
    656 	/* empty statement */;
    657     return (m - s * log(q1 * s));
    658 }
    659 
    660 #endif
    661