/* * Basic/interleaved protocol simulator * * This program simulates the NTP on-wire protocol operating in either * the current basic (1-step) mode or proposed interleaved (2-step) * mode. Basic mode delivers one set of timestamps for each symmetric * peer on each round. It is compatible with the NTP protocol * specification and all past and present NTP reference implementations. * Interleaved mode delivers two sets of timestamps for each symmetric * peer in two successive rounds. To operate in interleaved mode, both * symmetric peers must support this mode. * * In either basic or interleaved mode The protocol can recover from * most error conditions involving a lost or duplicated packet. However, * a crossover error in interleaved mode requires a protocol reset, * which requires a delay of two rounds. The program can automatically * synchronize to a preferred mode. If one peer can operate in either * basic or interleaved mode and the other can ooperate only in basic * mode, the interleaved peer reverts to basic mode automatically. * * The timestamp numbering is designed to detect an incorrect timetamp * set. Timestamps are numbered sequentially so that transmit timestamps * have odd numbers while receive timestamps have even numbers. There * are four timetamps in a set: t1, t2, t3 and t4. A correct timestamp * set has t2 = t1 + 1 and t4 = t3 + 1. Any other numbering is an error. * * The program runs the protcol until both peers are synchronized, then * executes an optional scenario represening an error condition, then * continues until both peers are again synchronized. The program is * controlled by three flags. The -s option selects the scenario as an * integer: 0 normal operation, 1 network duplicate, 2 peer duplicate, 3 * peer reset, 4 broadcast. The -a and -b options set the mode and * initial state of peer A and B, respectively: 0 force basic mode, 1 * force interleaved mode, 2 negotiate starting in basic mode, 3 * negotiate starting in interleaved mode. */ #include /* * Scenarios */ #define S_NORM 0 /* normal */ #define S_NETD 1 /* net duplicate */ #define S_PEED 2 /* peer duplicate */ #define S_REST 3 /* crossover error */ #define S_BCST 4 /* broadcast */ /* * Modes */ #define BASIC 0x0 /* force basic mode */ #define XLEAV 0x1 /* force interleaved mode */ #define BNEGO 0x2 /* start basic; will do either */ #define XNEGOT ox3 /* start interleaved; will do either */ /* * Error codes */ #define R_OK 0 /* okay */ #define R_DUPE 1 /* duplicate packet */ #define R_LOOP 2 /* loopback error */ #define R_SYNC 3 /* syncrhonization error */ /* * Peer state variables */ struct peer { int mode; /* mode */ int err; /* last error */ int rec; /* receive timestamp */ int xmt; /* transmit timestamp */ int dst; /* destination timestamp */ int aorg; /* a origin */ int borg; /* b origin */ int x; /* transmit origin switch */ int r; /* t/r switch */ int b; /* broadcast switch */ char id; /* ident */ }; /* * Packet header variables */ struct { int torg; /* origin timestamp */ int trec; /* receive timestamp */ int txmt; /* transmit timestamp */ } pkt; /* * Global variables, etc. */ int n = 1; /* timestamp counter */ int tcnt; /* successful rounds */ int sw; /* scenario */ void trans(struct peer *); /* transmit routine */ void bcast(struct peer *); /* broadcast routine */ void recv(struct peer *); /* receive routine */ /* * Main program */ main( int argc, /* number of arguments */ char **argv /* vector of argument pointers */ ) { struct peer *a; /* peer A structure */ struct peer *b; /* peer B structure */ struct peer *c; /* peer C structure */ int i, temp; /* * Default basic A and B normal mode. Default C interleaved * mode. It will be used as a broadcast server. */ a = malloc(sizeof(struct peer)); memset(a, 0, sizeof(struct peer)); a->id = 'A'; b = malloc(sizeof(struct peer)); memset(b, 0, sizeof(struct peer)); b->id = 'B'; c = malloc(sizeof(struct peer)); memset(c, 0, sizeof(struct peer)); c->x = 1; c->id = 'C'; /* * Preset modes. */ while ((temp = getopt(argc, argv, "a:b:s:")) != -1) { switch(temp) { case 'a': /* peer A */ a->mode = atoi(optarg); a->x = a->mode & 0x1; break; case 'b': /* peer B */ b->mode = atoi(optarg); b->x = b->mode & 0x1; break; case 's': /* scenario */ sw = atoi(optarg); break; default: printf("invalid option\n"); } } printf("sw %d A %d B %d\n", sw, a->mode, b->mode); printf(" rec dst aorg borg torg trec txmt rec dst aorg borg\n"); printf(" +---+---+---+---+ +---+---+---+ +---+---+---+---+ \n"); /* * Main loop */ for (i = 1; i < 7; i++) { switch (sw) { /* * No error. This is the normal case. */ case S_NORM: trans(a); /* transmit A to B */ recv(b); trans(b); /* transmit B to A */ recv(a); break; /* * Net duplicate. The packet has either been duplicated * in the network or retransmitted by the other peer. */ case S_NETD: trans(a); /* transmit A to B */ recv(b); if (tcnt == 2) { recv(b); /* receive duplicate */ tcnt++; } trans(b); /* transmit B to A */ recv(a); break; /* * Peer duplicate. The other peer has not been heard * before the next poll. */ case S_PEED: trans(a); /* transmit A to B */ recv(b); if (tcnt == 2) { trans(a); /* transmit A to B again */ recv(b); tcnt++; } trans(b); /* transmit B to A */ recv(a); break; /* * Crossover error. Packets from A and B are on the wire * at the same time and cross in flight. This is not a * perfect test, as only as single backet buffer is * used; however, even when simulated with two buffers, * the numbering scheme doesn't work and some scenarios * might in fact be valid. */ case S_REST: trans(a); /* transmit A to B */ recv(b); if (tcnt == 2) { trans(a); /* transmit A to B */ trans(b); /* transmit B to A */ recv(a); recv(b); tcnt++; } trans(b); /* transmit B to A */ recv(a); break; /* * Broadcast mode. This simulates the case where the * client has just received a broadcast packet and needs * to calibrate the delay. */ case S_BCST: bcast(c); /* broadcast to A */ recv(a); trans(a); /* transmit A to B */ recv(b); trans(b); /* transmit B to A */ recv(a); break; default: printf("Invalid scenario %d\n", sw); break; } } } /* * Transmit routine */ void trans(struct peer *peer) /* peer structure */ { pkt.torg = peer->rec; pkt.trec = peer->dst; /* * Basic mode. Do not alternate the origin timestamp. */ if (peer->x == 0) { peer->aorg = n++; pkt.txmt = peer->aorg; /* * Interleaved mode. If no receive packet after last transmit * packet, transmit a duplicate. */ } else if (peer->r != 0) { if (peer->x > 0) { pkt.txmt = peer->aorg; } else { pkt.txmt = peer->borg; } /* * Receive packet after last transmit packet. Alternate the * origin timestamp. */ } else { if (peer->x > 0) { peer->aorg = n++; pkt.txmt = peer->borg; } else { peer->borg = n++; pkt.txmt = peer->aorg; } peer->x = -peer->x; } peer->r = 1; printf("%cx | %2d %2d %2d %2d| -> | %2d %2d %2d|", peer->id, peer->rec, peer->dst, peer->aorg, peer->borg, pkt.torg, pkt.trec, pkt.txmt); } /* * Broadcast routine */ void bcast(struct peer *peer) /* peer structure */ { /* * Alternate the origin on each packet. */ if (peer->x > 0) { peer->aorg = n++; pkt.txmt = peer->aorg; pkt.torg = peer->borg; } else { peer->borg = n++; pkt.txmt = peer->borg; pkt.torg = peer->aorg; } pkt.trec = 0; peer->x = -peer->x; printf("%cx | %2d %2d %2d %2d| -> | %2d %2d %2d|", peer->id, peer->rec, peer->dst, peer->aorg, peer->borg, pkt.torg, pkt.trec, pkt.txmt); } /* * Receive routine */ void recv(struct peer *peer) /* peer structure */ { int t1, t2, t3, t4; int err; err = R_OK; /* * Discard duplicate packets before any further processing. */ if (pkt.txmt != 0 && peer->xmt == pkt.txmt) { err = R_DUPE; } else if (peer->x == 0) { /* * Basic mode * * An interleaved broadcast mode packet is recognized * when the orgin timestamp is nonzero and the receive * timestamp is zero. An ordinary broadcast packet is * not simulate here. */ if (pkt.torg != 0 && pkt.trec == 0) { peer->b = 1; t1 = pkt.torg; t2 = peer->borg; t3 = peer->aorg; t4 = peer->dst; /* * The hardware origin timestamp must be later * than the software orign timestamp by no more * than a small amount; otherwise, a previous * broadcast packet has been lost. */ if (t1 != pkt.torg) err = R_LOOP; peer->rec = pkt.txmt; peer->dst = n++; peer->borg = peer->dst; /* * Discard unsynchronized packets. */ if (t1 == 0 || t2 == 0) err = R_SYNC; /* * Ordinary basic mode packet. In interleaved broadcast * mode the destination timestamp is overriden by the * receive timestamp, which is actually the hardware * origin timestamp. */ } else { peer->rec = pkt.txmt; peer->dst = n++; t1 = pkt.torg; t2 = pkt.trec; t3 = pkt.txmt; t4 = peer->dst; if (peer->b != 0) { peer->dst = pkt.trec; peer->b = 0; } /* * Discard unsynchronized and bogus packets. */ if (t1 == 0 || t2 == 0) err = R_SYNC; else if (t1 != peer->aorg) err = R_LOOP; } } else { /* * Interleaved mode */ if (peer->x > 0) t1 = peer->aorg; else if (peer->x < 0) t1 = peer->borg; t2 = peer->rec; t3 = pkt.txmt; t4 = peer->dst; peer->rec = pkt.trec; peer->dst = n++; /* * Discard unsynchronized packets. Reset if a bogus * packet. */ if (t1 == 0 || t2 == 0) { err = R_SYNC; } else if (pkt.torg != 0 && pkt.torg != t4) { peer->rec = peer->dst = 0; peer->aorg = peer->borg = 0; err = R_LOOP; } } peer->xmt = pkt.txmt; peer->r = 0; printf(" -> %cr | %2d %2d %2d %2d|", peer->id, peer->rec, peer->dst, peer->aorg, peer->borg, peer->x); printf(" %2d\n", peer->x); printf(" +---------------+ +-----------+ +---------------+\n"); printf("%c timestamp %2d %2d %2d %2d tcnt %2d", peer->id, t1, t2, t3, t4, tcnt); /* * Document error conditions. Correct behavior requires the * receive timestamp (t2) be one greater than the origin * timestamp (t1) and the destination timestamp (t4) be one * greater than the transmit timestamp (t3). */ switch (err) { case R_OK: /* no apparent error */ if (t1 + 1 != t2 || t3 + 1 != t4) printf( " ERROR\n"); else printf("\n"); tcnt++; break; case R_DUPE: /* duplicate packet */ printf(" dupe\n"); break; case R_LOOP: /* bogus packet */ if (peer->err == R_SYNC && peer->mode & 0x2) { if (peer->x == 0) peer->x = 1; else peer->x = 0; } printf(" bogus\n"); break; case R_SYNC: /* unsynchronized packet */ printf(" sync\n"); break; } printf(" +---------------+ +-----------+ +---------------+\n"); peer->err = err; }