diff options
author | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
commit | ef64b99e8412f2273dd2e8b3291c2f78ffc4667f (patch) | |
tree | fc0cfa1aab0ff6b228f511b410733ef4f35d1ead /contrib/ntp/ntpd/ntp_request.c | |
download | FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.zip FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.tar.gz |
Virgin import of ntpd 4.0.98f
Diffstat (limited to 'contrib/ntp/ntpd/ntp_request.c')
-rw-r--r-- | contrib/ntp/ntpd/ntp_request.c | 2334 |
1 files changed, 2334 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/ntp_request.c b/contrib/ntp/ntpd/ntp_request.c new file mode 100644 index 0000000..3743118 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_request.c @@ -0,0 +1,2334 @@ +/* + * ntp_request.c - respond to information requests + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <signal.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_request.h" +#include "ntp_control.h" +#include "ntp_refclock.h" +#include "ntp_if.h" +#include "ntp_stdlib.h" +#include "recvbuff.h" + +#ifdef KERNEL_PLL +#include "ntp_syscall.h" +#endif /* KERNEL_PLL */ + +/* + * Structure to hold request procedure information + */ +#define NOAUTH 0 +#define AUTH 1 + +#define NO_REQUEST (-1) + +struct req_proc { + short request_code; /* defined request code */ + short needs_auth; /* true when authentication needed */ + short sizeofitem; /* size of request data item */ + void (*handler) P((struct sockaddr_in *, struct interface *, + struct req_pkt *)); /* routine to handle request */ +}; + +/* + * Universal request codes + */ +static struct req_proc univ_codes[] = { + { NO_REQUEST, NOAUTH, 0, 0 } +}; + +static void req_ack P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static char * prepare_pkt P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_int)); +static char * more_pkt P((void)); +static void flush_pkt P((void)); +static void peer_list P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void peer_list_sum P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void peer_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void peer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void sys_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void sys_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void mem_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void io_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void timer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void loop_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_conf P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_unconf P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void set_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void clr_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void setclr_flags P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_long)); +static void list_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_resaddflags P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_ressubflags P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_unrestrict P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static void mon_getlist_0 P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void mon_getlist_1 P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void reset_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void reset_peer P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_key_reread P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void trust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void untrust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_trustkey P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static void get_auth_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void reset_auth_stats P((void)); +static void req_get_traps P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void req_set_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void req_clr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_setclr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static void set_request_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void set_control_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void get_ctl_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#ifdef KERNEL_PLL +static void get_kernel_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#endif /* KERNEL_PLL */ +#ifdef REFCLOCK +static void get_clock_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void set_clock_fudge P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#endif /* REFCLOCK */ +#ifdef REFCLOCK +static void get_clkbug_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#endif /* REFCLOCK */ + +/* + * ntpd request codes + */ +static struct req_proc ntp_codes[] = { + { REQ_PEER_LIST, NOAUTH, 0, peer_list }, + { REQ_PEER_LIST_SUM, NOAUTH, 0, peer_list_sum }, + { REQ_PEER_INFO, NOAUTH, sizeof(struct info_peer_list), peer_info }, + { REQ_PEER_STATS, NOAUTH, sizeof(struct info_peer_list), peer_stats }, + { REQ_SYS_INFO, NOAUTH, 0, sys_info }, + { REQ_SYS_STATS, NOAUTH, 0, sys_stats }, + { REQ_IO_STATS, NOAUTH, 0, io_stats }, + { REQ_MEM_STATS, NOAUTH, 0, mem_stats }, + { REQ_LOOP_INFO, NOAUTH, 0, loop_info }, + { REQ_TIMER_STATS, NOAUTH, 0, timer_stats }, + { REQ_CONFIG, AUTH, sizeof(struct conf_peer), do_conf }, + { REQ_UNCONFIG, AUTH, sizeof(struct conf_unpeer), do_unconf }, + { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), set_sys_flag }, + { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), clr_sys_flag }, + { REQ_GET_RESTRICT, NOAUTH, 0, list_restrict }, + { REQ_RESADDFLAGS, AUTH, sizeof(struct conf_restrict), do_resaddflags }, + { REQ_RESSUBFLAGS, AUTH, sizeof(struct conf_restrict), do_ressubflags }, + { REQ_UNRESTRICT, AUTH, sizeof(struct conf_restrict), do_unrestrict }, + { REQ_MON_GETLIST, NOAUTH, 0, mon_getlist_0 }, + { REQ_MON_GETLIST_1, NOAUTH, 0, mon_getlist_1 }, + { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), reset_stats }, + { REQ_RESET_PEER, AUTH, sizeof(struct conf_unpeer), reset_peer }, + { REQ_REREAD_KEYS, AUTH, 0, do_key_reread }, + { REQ_TRUSTKEY, AUTH, sizeof(u_long), trust_key }, + { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), untrust_key }, + { REQ_AUTHINFO, NOAUTH, 0, get_auth_info }, + { REQ_TRAPS, NOAUTH, 0, req_get_traps }, + { REQ_ADD_TRAP, AUTH, sizeof(struct conf_trap), req_set_trap }, + { REQ_CLR_TRAP, AUTH, sizeof(struct conf_trap), req_clr_trap }, + { REQ_REQUEST_KEY, AUTH, sizeof(u_long), set_request_keyid }, + { REQ_CONTROL_KEY, AUTH, sizeof(u_long), set_control_keyid }, + { REQ_GET_CTLSTATS, NOAUTH, 0, get_ctl_stats }, +#ifdef KERNEL_PLL + { REQ_GET_KERNEL, NOAUTH, 0, get_kernel_info }, +#endif +#ifdef REFCLOCK + { REQ_GET_CLOCKINFO, NOAUTH, sizeof(u_int32), get_clock_info }, + { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge), set_clock_fudge }, + { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(u_int32), get_clkbug_info }, +#endif + { NO_REQUEST, NOAUTH, 0, 0 } +}; + + +/* + * Authentication keyid used to authenticate requests. Zero means we + * don't allow writing anything. + */ +u_long info_auth_keyid; + +/* + * Statistic counters to keep track of requests and responses. + */ +u_long numrequests; /* number of requests we've received */ +u_long numresppkts; /* number of resp packets sent with data */ + +u_long errorcounter[INFO_ERR_AUTH+1]; /* lazy way to count errors, indexed */ +/* by the error code */ + +/* + * A hack. To keep the authentication module clear of ntp-ism's, we + * include a time reset variable for its stats here. + */ +static u_long auth_timereset; + +/* + * Response packet used by these routines. Also some state information + * so that we can handle packet formatting within a common set of + * subroutines. Note we try to enter data in place whenever possible, + * but the need to set the more bit correctly means we occasionally + * use the extra buffer and copy. + */ +static struct resp_pkt rpkt; +static int reqver; +static int seqno; +static int nitems; +static int itemsize; +static int databytes; +static char exbuf[RESP_DATA_SIZE]; +static int usingexbuf; +static struct sockaddr_in *toaddr; +static struct interface *frominter; + +/* + * init_request - initialize request data + */ +void +init_request (void) +{ + int i; + + numrequests = 0; + numresppkts = 0; + auth_timereset = 0; + info_auth_keyid = 0; /* by default, can't do this */ + + for (i = 0; i < sizeof(errorcounter)/sizeof(errorcounter[0]); i++) + errorcounter[i] = 0; +} + + +/* + * req_ack - acknowledge request with no data + */ +static void +req_ack( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int errcode + ) +{ + /* + * fill in the fields + */ + rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver); + rpkt.auth_seq = AUTH_SEQ(0, 0); + rpkt.implementation = inpkt->implementation; + rpkt.request = inpkt->request; + rpkt.err_nitems = ERR_NITEMS(errcode, 0); + rpkt.mbz_itemsize = MBZ_ITEMSIZE(0); + + /* + * send packet and bump counters + */ + sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE); + errorcounter[errcode]++; +} + + +/* + * prepare_pkt - prepare response packet for transmission, return pointer + * to storage for data item. + */ +static char * +prepare_pkt( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *pkt, + u_int structsize + ) +{ +#ifdef DEBUG + if (debug > 3) + printf("request: preparing pkt\n"); +#endif + + /* + * Fill in the implementation, reqest and itemsize fields + * since these won't change. + */ + rpkt.implementation = pkt->implementation; + rpkt.request = pkt->request; + rpkt.mbz_itemsize = MBZ_ITEMSIZE(structsize); + + /* + * Compute the static data needed to carry on. + */ + toaddr = srcadr; + frominter = inter; + seqno = 0; + nitems = 0; + itemsize = structsize; + databytes = 0; + usingexbuf = 0; + + /* + * return the beginning of the packet buffer. + */ + return &rpkt.data[0]; +} + + +/* + * more_pkt - return a data pointer for a new item. + */ +static char * +more_pkt(void) +{ + /* + * If we were using the extra buffer, send the packet. + */ + if (usingexbuf) { +#ifdef DEBUG + if (debug > 2) + printf("request: sending pkt\n"); +#endif + rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT, reqver); + rpkt.auth_seq = AUTH_SEQ(0, seqno); + rpkt.err_nitems = htons((u_short)nitems); + sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt, + RESP_HEADER_SIZE+databytes); + numresppkts++; + + /* + * Copy data out of exbuf into the packet. + */ + memmove(&rpkt.data[0], exbuf, (unsigned)itemsize); + seqno++; + databytes = 0; + nitems = 0; + usingexbuf = 0; + } + + databytes += itemsize; + nitems++; + if (databytes + itemsize <= RESP_DATA_SIZE) { +#ifdef DEBUG + if (debug > 3) + printf("request: giving him more data\n"); +#endif + /* + * More room in packet. Give him the + * next address. + */ + return &rpkt.data[databytes]; + } else { + /* + * No room in packet. Give him the extra + * buffer unless this was the last in the sequence. + */ +#ifdef DEBUG + if (debug > 3) + printf("request: into extra buffer\n"); +#endif + if (seqno == MAXSEQ) + return (char *)0; + else { + usingexbuf = 1; + return exbuf; + } + } +} + + +/* + * flush_pkt - we're done, return remaining information. + */ +static void +flush_pkt(void) +{ +#ifdef DEBUG + if (debug > 2) + printf("request: flushing packet, %d items\n", nitems); +#endif + /* + * Must send the last packet. If nothing in here and nothing + * has been sent, send an error saying no data to be found. + */ + if (seqno == 0 && nitems == 0) + req_ack(toaddr, frominter, (struct req_pkt *)&rpkt, + INFO_ERR_NODATA); + else { + rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver); + rpkt.auth_seq = AUTH_SEQ(0, seqno); + rpkt.err_nitems = htons((u_short)nitems); + sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt, + RESP_HEADER_SIZE+databytes); + numresppkts++; + } +} + + + +/* + * process_private - process private mode (7) packets + */ +void +process_private( + struct recvbuf *rbufp, + int mod_okay + ) +{ + struct req_pkt *inpkt; + struct sockaddr_in *srcadr; + struct interface *inter; + struct req_proc *proc; + + /* + * Initialize pointers, for convenience + */ + inpkt = (struct req_pkt *)&rbufp->recv_pkt; + srcadr = &rbufp->recv_srcadr; + inter = rbufp->dstadr; + +#ifdef DEBUG + if (debug > 2) + printf("prepare_pkt: impl %d req %d\n", + inpkt->implementation, inpkt->request); +#endif + + /* + * Do some sanity checks on the packet. Return a format + * error if it fails. + */ + if (ISRESPONSE(inpkt->rm_vn_mode) + || ISMORE(inpkt->rm_vn_mode) + || INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION + || INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION + || INFO_SEQ(inpkt->auth_seq) != 0 + || INFO_ERR(inpkt->err_nitems) != 0 + || INFO_MBZ(inpkt->mbz_itemsize) != 0 + || rbufp->recv_length > REQ_LEN_MAC + || rbufp->recv_length < REQ_LEN_NOMAC) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + reqver = INFO_VERSION(inpkt->rm_vn_mode); + + /* + * Get the appropriate procedure list to search. + */ + if (inpkt->implementation == IMPL_UNIV) + proc = univ_codes; + else if (inpkt->implementation == IMPL_XNTPD) + proc = ntp_codes; + else { + req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL); + return; + } + + + /* + * Search the list for the request codes. If it isn't one + * we know, return an error. + */ + while (proc->request_code != NO_REQUEST) { + if (proc->request_code == (short) inpkt->request) + break; + proc++; + } + if (proc->request_code == NO_REQUEST) { + req_ack(srcadr, inter, inpkt, INFO_ERR_REQ); + return; + } + +#ifdef DEBUG + if (debug > 3) + printf("found request in tables\n"); +#endif + + /* + * If we need to authenticate, do so. Note that an + * authenticatable packet must include a mac field, must + * have used key info_auth_keyid and must have included + * a time stamp in the appropriate field. The time stamp + * must be within INFO_TS_MAXSKEW of the receive + * time stamp. + */ + if (proc->needs_auth && sys_authenticate) { + l_fp ftmp; + double dtemp; + + /* + * If this guy is restricted from doing this, don't let him + * If wrong key was used, or packet doesn't have mac, return. + */ + if (!INFO_IS_AUTH(inpkt->auth_seq) || info_auth_keyid == 0 + || ntohl(inpkt->keyid) != info_auth_keyid) { +#ifdef DEBUG + if (debug > 4) + printf("failed auth %d info_auth_keyid %lu pkt keyid %lu\n", + INFO_IS_AUTH(inpkt->auth_seq), + (u_long)info_auth_keyid, + (u_long)ntohl(inpkt->keyid)); +#endif + req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH); + return; + } + if (rbufp->recv_length > REQ_LEN_MAC) { +#ifdef DEBUG + if (debug > 4) + printf("bad pkt length %d\n", + rbufp->recv_length); +#endif + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + if (!mod_okay || !authhavekey(info_auth_keyid)) { +#ifdef DEBUG + if (debug > 4) + printf("failed auth mod_okay %d\n", mod_okay); +#endif + req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH); + return; + } + + /* + * calculate absolute time difference between xmit time stamp + * and receive time stamp. If too large, too bad. + */ + NTOHL_FP(&inpkt->tstamp, &ftmp); + L_SUB(&ftmp, &rbufp->recv_time); + LFPTOD(&ftmp, dtemp); + if (fabs(dtemp) >= INFO_TS_MAXSKEW) { + /* + * He's a loser. Tell him. + */ + req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH); + return; + } + + /* + * So far so good. See if decryption works out okay. + */ + if (!authdecrypt(info_auth_keyid, (u_int32 *)inpkt, + REQ_LEN_NOMAC, (int)(rbufp->recv_length - REQ_LEN_NOMAC))) { + req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH); + return; + } + } + + /* + * If we need data, check to see if we have some. If we + * don't, check to see that there is none (picky, picky). + */ + if (INFO_ITEMSIZE(inpkt->mbz_itemsize) != proc->sizeofitem) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + if (proc->sizeofitem != 0) + if (proc->sizeofitem*INFO_NITEMS(inpkt->err_nitems) + > sizeof(inpkt->data)) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + +#ifdef DEBUG + if (debug > 3) + printf("process_private: all okay, into handler\n"); +#endif + + /* + * Packet is okay. Call the handler to send him data. + */ + (proc->handler)(srcadr, inter, inpkt); +} + + +/* + * peer_list - send a list of the peers + */ +static void +peer_list( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_peer_list *ip; + register struct peer *pp; + register int i; + + ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer_list)); + for (i = 0; i < HASH_SIZE && ip != 0; i++) { + pp = peer_hash[i]; + while (pp != 0 && ip != 0) { + ip->address = pp->srcadr.sin_addr.s_addr; + ip->port = pp->srcadr.sin_port; + ip->hmode = pp->hmode; + ip->flags = 0; + if (pp->flags & FLAG_CONFIG) + ip->flags |= INFO_FLAG_CONFIG; + if (pp == sys_peer) + ip->flags |= INFO_FLAG_SYSPEER; + if (pp->status == CTL_PST_SEL_SYNCCAND) + ip->flags |= INFO_FLAG_SEL_CANDIDATE; + if (pp->status >= CTL_PST_SEL_SYSPEER) + ip->flags |= INFO_FLAG_SHORTLIST; + ip = (struct info_peer_list *)more_pkt(); + pp = pp->next; + } + } + flush_pkt(); +} + + +/* + * peer_list_sum - return extended peer list + */ +static void +peer_list_sum( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_peer_summary *ips; + register struct peer *pp; + register int i; + l_fp ltmp; + +#ifdef DEBUG + if (debug > 2) + printf("wants peer list summary\n"); +#endif + + ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer_summary)); + for (i = 0; i < HASH_SIZE && ips != 0; i++) { + pp = peer_hash[i]; + while (pp != 0 && ips != 0) { +#ifdef DEBUG + if (debug > 3) + printf("sum: got one\n"); +#endif + ips->dstadr = (pp->processed) ? + pp->cast_flags == MDF_BCAST ? + pp->dstadr->bcast.sin_addr.s_addr: + pp->cast_flags ? + pp->dstadr->sin.sin_addr.s_addr ? + pp->dstadr->sin.sin_addr.s_addr: + pp->dstadr->bcast.sin_addr.s_addr: + 1 : 5; + ips->srcadr = pp->srcadr.sin_addr.s_addr; + ips->srcport = pp->srcadr.sin_port; + ips->stratum = pp->stratum; + ips->hpoll = pp->hpoll; + ips->ppoll = pp->ppoll; + ips->reach = pp->reach; + ips->flags = 0; + if (pp == sys_peer) + ips->flags |= INFO_FLAG_SYSPEER; + if (pp->flags & FLAG_CONFIG) + ips->flags |= INFO_FLAG_CONFIG; + if (pp->flags & FLAG_REFCLOCK) + ips->flags |= INFO_FLAG_REFCLOCK; + if (pp->flags & FLAG_AUTHENABLE) + ips->flags |= INFO_FLAG_AUTHENABLE; + if (pp->flags & FLAG_PREFER) + ips->flags |= INFO_FLAG_PREFER; + if (pp->flags & FLAG_BURST) + ips->flags |= INFO_FLAG_BURST; + if (pp->status == CTL_PST_SEL_SYNCCAND) + ips->flags |= INFO_FLAG_SEL_CANDIDATE; + if (pp->status >= CTL_PST_SEL_SYSPEER) + ips->flags |= INFO_FLAG_SHORTLIST; + ips->hmode = pp->hmode; + ips->delay = HTONS_FP(DTOFP(pp->delay)); + DTOLFP(pp->offset, <mp); + HTONL_FP(<mp, &ips->offset); + ips->dispersion = HTONS_FP(DTOUFP(pp->disp)); + + pp = pp->next; + ips = (struct info_peer_summary *)more_pkt(); + } + } + flush_pkt(); +} + + +/* + * peer_info - send information for one or more peers + */ +static void +peer_info ( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_peer_list *ipl; + register struct peer *pp; + register struct info_peer *ip; + register int items; + register int i, j; + struct sockaddr_in addr; + extern struct peer *sys_peer; + l_fp ltmp; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + items = INFO_NITEMS(inpkt->err_nitems); + ipl = (struct info_peer_list *) inpkt->data; + ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer)); + while (items-- > 0 && ip != 0) { + addr.sin_port = ipl->port; + addr.sin_addr.s_addr = ipl->address; + ipl++; + if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0) + continue; + ip->dstadr = (pp->processed) ? + pp->cast_flags == MDF_BCAST ? + pp->dstadr->bcast.sin_addr.s_addr: + pp->cast_flags ? + pp->dstadr->sin.sin_addr.s_addr ? + pp->dstadr->sin.sin_addr.s_addr: + pp->dstadr->bcast.sin_addr.s_addr: + 2 : 6; + ip->srcadr = NSRCADR(&pp->srcadr); + ip->srcport = NSRCPORT(&pp->srcadr); + ip->flags = 0; + if (pp == sys_peer) + ip->flags |= INFO_FLAG_SYSPEER; + if (pp->flags & FLAG_CONFIG) + ip->flags |= INFO_FLAG_CONFIG; + if (pp->flags & FLAG_REFCLOCK) + ip->flags |= INFO_FLAG_REFCLOCK; + if (pp->flags & FLAG_AUTHENABLE) + ip->flags |= INFO_FLAG_AUTHENABLE; + if (pp->flags & FLAG_PREFER) + ip->flags |= INFO_FLAG_PREFER; + if (pp->flags & FLAG_BURST) + ip->flags |= INFO_FLAG_BURST; + if (pp->status == CTL_PST_SEL_SYNCCAND) + ip->flags |= INFO_FLAG_SEL_CANDIDATE; + if (pp->status >= CTL_PST_SEL_SYSPEER) + ip->flags |= INFO_FLAG_SHORTLIST; + ip->leap = pp->leap; + ip->hmode = pp->hmode; + ip->keyid = pp->keyid; + ip->stratum = pp->stratum; + ip->ppoll = pp->ppoll; + ip->hpoll = pp->hpoll; + ip->precision = pp->precision; + ip->version = pp->version; + ip->valid = pp->valid; + ip->reach = pp->reach; + ip->unreach = pp->unreach; + ip->flash = (u_char)pp->flash; + ip->flash2 = pp->flash; + ip->estbdelay = HTONS_FP(DTOFP(pp->estbdelay)); + ip->ttl = pp->ttl; + ip->associd = htons(pp->associd); + ip->rootdelay = HTONS_FP(DTOUFP(pp->rootdelay)); + ip->rootdispersion = HTONS_FP(DTOUFP(pp->rootdispersion)); + ip->refid = pp->refid; + HTONL_FP(&pp->reftime, &ip->reftime); + HTONL_FP(&pp->org, &ip->org); + HTONL_FP(&pp->rec, &ip->rec); + HTONL_FP(&pp->xmt, &ip->xmt); + j = pp->filter_nextpt - 1; + for (i = 0; i < NTP_SHIFT; i++, j--) { + if (j < 0) + j = NTP_SHIFT-1; + ip->filtdelay[i] = HTONS_FP(DTOFP(pp->filter_delay[j])); + DTOLFP(pp->filter_offset[j], <mp); + HTONL_FP(<mp, &ip->filtoffset[i]); + ip->order[i] = (pp->filter_nextpt+NTP_SHIFT-1) + - pp->filter_order[i]; + if (ip->order[i] >= NTP_SHIFT) + ip->order[i] -= NTP_SHIFT; + } + DTOLFP(pp->offset, <mp); + HTONL_FP(<mp, &ip->offset); + ip->delay = HTONS_FP(DTOFP(pp->delay)); + ip->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp))); + ip->selectdisp = HTONS_FP(DTOUFP(SQRT(pp->variance))); + ip = (struct info_peer *)more_pkt(); + } + flush_pkt(); +} + + +/* + * peer_stats - send statistics for one or more peers + */ +static void +peer_stats ( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_peer_list *ipl; + register struct peer *pp; + register struct info_peer_stats *ip; + register int items; + struct sockaddr_in addr; + extern struct peer *sys_peer; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + items = INFO_NITEMS(inpkt->err_nitems); + ipl = (struct info_peer_list *) inpkt->data; + ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer_stats)); + while (items-- > 0 && ip != 0) { + addr.sin_port = ipl->port; + addr.sin_addr.s_addr = ipl->address; + ipl++; + if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0) + continue; + ip->dstadr = (pp->processed) ? + pp->cast_flags == MDF_BCAST ? + pp->dstadr->bcast.sin_addr.s_addr: + pp->cast_flags ? + pp->dstadr->sin.sin_addr.s_addr ? + pp->dstadr->sin.sin_addr.s_addr: + pp->dstadr->bcast.sin_addr.s_addr: + 3 : 7; + ip->srcadr = NSRCADR(&pp->srcadr); + ip->srcport = NSRCPORT(&pp->srcadr); + ip->flags = 0; + if (pp == sys_peer) + ip->flags |= INFO_FLAG_SYSPEER; + if (pp->flags & FLAG_CONFIG) + ip->flags |= INFO_FLAG_CONFIG; + if (pp->flags & FLAG_REFCLOCK) + ip->flags |= INFO_FLAG_REFCLOCK; + if (pp->flags & FLAG_AUTHENABLE) + ip->flags |= INFO_FLAG_AUTHENABLE; + if (pp->flags & FLAG_PREFER) + ip->flags |= INFO_FLAG_PREFER; + if (pp->flags & FLAG_BURST) + ip->flags |= INFO_FLAG_BURST; + if (pp->status == CTL_PST_SEL_SYNCCAND) + ip->flags |= INFO_FLAG_SEL_CANDIDATE; + if (pp->status >= CTL_PST_SEL_SYSPEER) + ip->flags |= INFO_FLAG_SHORTLIST; + ip->timereceived = htonl((u_int32)(current_time - pp->timereceived)); + ip->timetosend = htonl(pp->nextdate - current_time); + ip->timereachable = htonl((u_int32)(current_time - pp->timereachable)); + ip->sent = htonl((u_int32)(pp->sent)); + ip->processed = htonl((u_int32)(pp->processed)); + ip->badauth = htonl((u_int32)(pp->badauth)); + ip->bogusorg = htonl((u_int32)(pp->bogusorg)); + ip->oldpkt = htonl((u_int32)(pp->oldpkt)); + ip->seldisp = htonl((u_int32)(pp->seldisptoolarge)); + ip->selbroken = htonl((u_int32)(pp->selbroken)); + ip->candidate = pp->status; + ip = (struct info_peer_stats *)more_pkt(); + } + flush_pkt(); +} + + +/* + * sys_info - return system info + */ +static void +sys_info( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_sys *is; + + /* + * Importations from the protocol module + */ + extern u_char sys_leap; + extern u_char sys_stratum; + extern s_char sys_precision; + extern double sys_rootdelay; + extern double sys_rootdispersion; + extern u_int32 sys_refid; + extern l_fp sys_reftime; + extern u_char sys_poll; + extern struct peer *sys_peer; + extern int sys_bclient; + extern double sys_bdelay; + extern l_fp sys_authdelay; + extern double clock_stability; + extern double sys_error; + + is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_sys)); + + if (sys_peer != 0) { + is->peer = NSRCADR(&sys_peer->srcadr); + is->peer_mode = sys_peer->hmode; + } else { + is->peer = 0; + is->peer_mode = 0; + } + is->leap = sys_leap; + is->stratum = sys_stratum; + is->precision = sys_precision; + is->rootdelay = htonl(DTOFP(sys_rootdelay)); + is->rootdispersion = htonl(DTOUFP(sys_rootdispersion)); + is->frequency = htonl(DTOFP(sys_error)); + is->stability = htonl(DTOUFP(clock_stability * 1e6)); + is->refid = sys_refid; + HTONL_FP(&sys_reftime, &is->reftime); + + is->poll = sys_poll; + + is->flags = 0; + if (sys_bclient) + is->flags |= INFO_FLAG_BCLIENT; + if (sys_authenticate) + is->flags |= INFO_FLAG_AUTHENTICATE; + if (kern_enable) + is->flags |= INFO_FLAG_KERNEL; + if (ntp_enable) + is->flags |= INFO_FLAG_NTP; + if (pll_control) + is->flags |= INFO_FLAG_PLL_SYNC; + if (pps_control) + is->flags |= INFO_FLAG_PPS_SYNC; + if (mon_enabled != MON_OFF) + is->flags |= INFO_FLAG_MONITOR; + if (stats_control) + is->flags |= INFO_FLAG_FILEGEN; + is->bdelay = HTONS_FP(DTOFP(sys_bdelay)); + HTONL_UF(sys_authdelay.l_f, &is->authdelay); + + (void) more_pkt(); + flush_pkt(); +} + + +/* + * sys_stats - return system statistics + */ +static void +sys_stats( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_sys_stats *ss; + + /* + * Importations from the protocol module + */ + extern u_long sys_stattime; + extern u_long sys_badstratum; + extern u_long sys_oldversionpkt; + extern u_long sys_newversionpkt; + extern u_long sys_unknownversion; + extern u_long sys_badlength; + extern u_long sys_processed; + extern u_long sys_badauth; + extern u_long sys_limitrejected; + + ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_sys_stats)); + + ss->timeup = htonl((u_int32)current_time); + ss->timereset = htonl((u_int32)(current_time - sys_stattime)); + ss->badstratum = htonl((u_int32)sys_badstratum); + ss->oldversionpkt = htonl((u_int32)sys_oldversionpkt); + ss->newversionpkt = htonl((u_int32)sys_newversionpkt); + ss->unknownversion = htonl((u_int32)sys_unknownversion); + ss->badlength = htonl((u_int32)sys_badlength); + ss->processed = htonl((u_int32)sys_processed); + ss->badauth = htonl((u_int32)sys_badauth); + ss->limitrejected = htonl((u_int32)sys_limitrejected); + (void) more_pkt(); + flush_pkt(); +} + + +/* + * mem_stats - return memory statistics + */ +static void +mem_stats( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_mem_stats *ms; + register int i; + + /* + * Importations from the peer module + */ + extern int peer_hash_count[HASH_SIZE]; + extern int peer_free_count; + extern u_long peer_timereset; + extern u_long findpeer_calls; + extern u_long peer_allocations; + extern u_long peer_demobilizations; + extern int total_peer_structs; + + ms = (struct info_mem_stats *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_mem_stats)); + + ms->timereset = htonl((u_int32)(current_time - peer_timereset)); + ms->totalpeermem = htons((u_short)total_peer_structs); + ms->freepeermem = htons((u_short)peer_free_count); + ms->findpeer_calls = htonl((u_int32)findpeer_calls); + ms->allocations = htonl((u_int32)peer_allocations); + ms->demobilizations = htonl((u_int32)peer_demobilizations); + + for (i = 0; i < HASH_SIZE; i++) { + if (peer_hash_count[i] > 255) + ms->hashcount[i] = 255; + else + ms->hashcount[i] = (u_char)peer_hash_count[i]; + } + + (void) more_pkt(); + flush_pkt(); +} + + +/* + * io_stats - return io statistics + */ +static void +io_stats( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_io_stats *io; + + /* + * Importations from the io module + */ + extern u_long io_timereset; + + io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_io_stats)); + + io->timereset = htonl((u_int32)(current_time - io_timereset)); + io->totalrecvbufs = htons((u_short) total_recvbuffs()); + io->freerecvbufs = htons((u_short) free_recvbuffs()); + io->fullrecvbufs = htons((u_short) full_recvbuffs()); + io->lowwater = htons((u_short) lowater_additions()); + io->dropped = htonl((u_int32)packets_dropped); + io->ignored = htonl((u_int32)packets_ignored); + io->received = htonl((u_int32)packets_received); + io->sent = htonl((u_int32)packets_sent); + io->notsent = htonl((u_int32)packets_notsent); + io->interrupts = htonl((u_int32)handler_calls); + io->int_received = htonl((u_int32)handler_pkts); + + (void) more_pkt(); + flush_pkt(); +} + + +/* + * timer_stats - return timer statistics + */ +static void +timer_stats( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_timer_stats *ts; + + /* + * Importations from the timer module + */ + extern u_long timer_timereset; + extern u_long timer_overflows; + extern u_long timer_xmtcalls; + + ts = (struct info_timer_stats *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_timer_stats)); + + ts->timereset = htonl((u_int32)(current_time - timer_timereset)); + ts->alarms = htonl((u_int32)alarm_overflow); + ts->overflows = htonl((u_int32)timer_overflows); + ts->xmtcalls = htonl((u_int32)timer_xmtcalls); + + (void) more_pkt(); + flush_pkt(); +} + + +/* + * loop_info - return the current state of the loop filter + */ +static void +loop_info( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_loop *li; + l_fp ltmp; + + /* + * Importations from the loop filter module + */ + extern double last_offset; + extern double drift_comp; + extern int tc_counter; + extern u_long last_time; + + li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_loop)); + + DTOLFP(last_offset, <mp); + HTONL_FP(<mp, &li->last_offset); + DTOLFP(drift_comp * 1e6, <mp); + HTONL_FP(<mp, &li->drift_comp); + li->compliance = htonl((u_int32)(tc_counter)); + li->watchdog_timer = htonl((u_int32)(current_time - last_time)); + + (void) more_pkt(); + flush_pkt(); +} + + +/* + * do_conf - add a peer to the configuration list + */ +static void +do_conf( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_peer *cp; + register int items; + struct sockaddr_in peeraddr; + int fl; + + /* + * Do a check of everything to see that it looks + * okay. If not, complain about it. Note we are + * very picky here. + */ + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_peer *)inpkt->data; + + fl = 0; + while (items-- > 0 && !fl) { + if (((cp->version) > NTP_VERSION) + || ((cp->version) < NTP_OLDVERSION)) + fl = 1; + if (cp->hmode != MODE_ACTIVE + && cp->hmode != MODE_CLIENT + && cp->hmode != MODE_BROADCAST) + fl = 1; + if (cp->flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER + | CONF_FLAG_NOSELECT | CONF_FLAG_BURST | CONF_FLAG_SKEY)) + fl = 1; + cp++; + } + + if (fl) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + /* + * Looks okay, try it out + */ + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_peer *)inpkt->data; + memset((char *)&peeraddr, 0, sizeof(struct sockaddr_in)); + peeraddr.sin_family = AF_INET; + peeraddr.sin_port = htons(NTP_PORT); + + /* + * Make sure the address is valid + */ + if ( +#ifdef REFCLOCK + !ISREFCLOCKADR(&peeraddr) && +#endif + ISBADADR(&peeraddr)) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + while (items-- > 0) { + fl = 0; + if (cp->flags & CONF_FLAG_AUTHENABLE) + fl |= FLAG_AUTHENABLE; + if (cp->flags & CONF_FLAG_PREFER) + fl |= FLAG_PREFER; + if (cp->flags & CONF_FLAG_NOSELECT) + fl |= FLAG_NOSELECT; + if (cp->flags & CONF_FLAG_BURST) + fl |= FLAG_BURST; + if (cp->flags & CONF_FLAG_SKEY) + fl |= FLAG_SKEY; + peeraddr.sin_addr.s_addr = cp->peeraddr; + /* XXX W2DO? minpoll/maxpoll arguments ??? */ + if (peer_config(&peeraddr, (struct interface *)0, + cp->hmode, cp->version, cp->minpoll, cp->maxpoll, + fl, cp->ttl, cp->keyid) == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + cp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * do_unconf - remove a peer from the configuration list + */ +static void +do_unconf( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_unpeer *cp; + register int items; + register struct peer *peer; + struct sockaddr_in peeraddr; + int bad, found; + + /* + * This is a bit unstructured, but I like to be careful. + * We check to see that every peer exists and is actually + * configured. If so, we remove them. If not, we return + * an error. + */ + peeraddr.sin_family = AF_INET; + peeraddr.sin_port = htons(NTP_PORT); + + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_unpeer *)inpkt->data; + + bad = 0; + while (items-- > 0 && !bad) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + found = 0; + peer = (struct peer *)0; + while (!found) { + peer = findexistingpeer(&peeraddr, peer, -1); + if (peer == (struct peer *)0) + break; + if (peer->flags & FLAG_CONFIG) + found = 1; + } + if (!found) + bad = 1; + cp++; + } + + if (bad) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + /* + * Now do it in earnest. + */ + + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_unpeer *)inpkt->data; + while (items-- > 0) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + peer_unconfig(&peeraddr, (struct interface *)0, -1); + cp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * set_sys_flag - set system flags + */ +static void +set_sys_flag( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + setclr_flags(srcadr, inter, inpkt, 1); +} + + +/* + * clr_sys_flag - clear system flags + */ +static void +clr_sys_flag( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + setclr_flags(srcadr, inter, inpkt, 0); +} + + +/* + * setclr_flags - do the grunge work of flag setting/clearing + */ +static void +setclr_flags( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + u_long set + ) +{ + register u_long flags; + + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + flags = ((struct conf_sys_flags *)inpkt->data)->flags; + + if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_AUTHENTICATE | + SYS_FLAG_NTP | SYS_FLAG_KERNEL | SYS_FLAG_MONITOR | + SYS_FLAG_FILEGEN)) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + if (flags & SYS_FLAG_BCLIENT) + proto_config(PROTO_BROADCLIENT, set, 0.); + if (flags & SYS_FLAG_AUTHENTICATE) + proto_config(PROTO_AUTHENTICATE, set, 0.); + if (flags & SYS_FLAG_NTP) + proto_config(PROTO_NTP, set, 0.); + if (flags & SYS_FLAG_KERNEL) + proto_config(PROTO_KERNEL, set, 0.); + if (flags & SYS_FLAG_MONITOR) + proto_config(PROTO_MONITOR, set, 0.); + if (flags & SYS_FLAG_FILEGEN) + proto_config(PROTO_FILEGEN, set, 0.); + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * list_restrict - return the restrict list + */ +static void +list_restrict( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_restrict *ir; + register struct restrictlist *rl; + extern struct restrictlist *restrictlist; + +#ifdef DEBUG + if (debug > 2) + printf("wants peer list summary\n"); +#endif + + ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_restrict)); + for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) { + ir->addr = htonl(rl->addr); + ir->mask = htonl(rl->mask); + ir->count = htonl((u_int32)rl->count); + ir->flags = htons(rl->flags); + ir->mflags = htons(rl->mflags); + ir = (struct info_restrict *)more_pkt(); + } + flush_pkt(); +} + + + +/* + * do_resaddflags - add flags to a restrict entry (or create one) + */ +static void +do_resaddflags( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_restrict(srcadr, inter, inpkt, RESTRICT_FLAGS); +} + + + +/* + * do_ressubflags - remove flags from a restrict entry + */ +static void +do_ressubflags( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_restrict(srcadr, inter, inpkt, RESTRICT_UNFLAG); +} + + +/* + * do_unrestrict - remove a restrict entry from the list + */ +static void +do_unrestrict( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_restrict(srcadr, inter, inpkt, RESTRICT_REMOVE); +} + + + + + +/* + * do_restrict - do the dirty stuff of dealing with restrictions + */ +static void +do_restrict( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int op + ) +{ + register struct conf_restrict *cr; + register int items; + struct sockaddr_in matchaddr; + struct sockaddr_in matchmask; + int bad; + + /* + * Do a check of the flags to make sure that only + * the NTPPORT flag is set, if any. If not, complain + * about it. Note we are very picky here. + */ + items = INFO_NITEMS(inpkt->err_nitems); + cr = (struct conf_restrict *)inpkt->data; + + bad = 0; + while (items-- > 0 && !bad) { + if (cr->mflags & ~(RESM_NTPONLY)) + bad = 1; + if (cr->flags & ~(RES_ALLFLAGS)) + bad = 1; + if (cr->addr == htonl(INADDR_ANY) && cr->mask != htonl(INADDR_ANY)) + bad = 1; + cr++; + } + + if (bad) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + /* + * Looks okay, try it out + */ + items = INFO_NITEMS(inpkt->err_nitems); + cr = (struct conf_restrict *)inpkt->data; + memset((char *)&matchaddr, 0, sizeof(struct sockaddr_in)); + memset((char *)&matchmask, 0, sizeof(struct sockaddr_in)); + matchaddr.sin_family = AF_INET; + matchmask.sin_family = AF_INET; + + while (items-- > 0) { + matchaddr.sin_addr.s_addr = cr->addr; + matchmask.sin_addr.s_addr = cr->mask; + hack_restrict(op, &matchaddr, &matchmask, cr->mflags, + cr->flags); + cr++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * mon_getlist - return monitor data + */ +static void +mon_getlist_0( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_monitor *im; + register struct mon_data *md; + extern struct mon_data mon_mru_list; + extern int mon_enabled; + +#ifdef DEBUG + if (debug > 2) + printf("wants monitor 0 list\n"); +#endif + if (!mon_enabled) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + im = (struct info_monitor *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_monitor)); + for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0; + md = md->mru_next) { + im->lasttime = htonl((u_int32)(current_time - md->lasttime)); + im->firsttime = htonl((u_int32)(current_time - md->firsttime)); + if (md->lastdrop) + im->lastdrop = htonl((u_int32)(current_time - md->lastdrop)); + else + im->lastdrop = 0; + im->count = htonl((u_int32)(md->count)); + im->addr = md->rmtadr; + im->port = md->rmtport; + im->mode = md->mode; + im->version = md->version; + im = (struct info_monitor *)more_pkt(); + } + flush_pkt(); +} + +/* + * mon_getlist - return monitor data + */ +static void +mon_getlist_1( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_monitor_1 *im; + register struct mon_data *md; + extern struct mon_data mon_mru_list; + extern int mon_enabled; + +#ifdef DEBUG + if (debug > 2) + printf("wants monitor 1 list\n"); +#endif + if (!mon_enabled) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_monitor_1)); + for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0; + md = md->mru_next) { + im->lasttime = htonl((u_int32)(current_time - md->lasttime)); + im->firsttime = htonl((u_int32)(current_time - md->firsttime)); + if (md->lastdrop) + im->lastdrop = htonl((u_int32)(current_time - md->lastdrop)); + else + im->lastdrop = 0; + im->count = htonl((u_int32)md->count); + im->addr = md->rmtadr; + im->daddr = + (md->cast_flags == MDF_BCAST) + ? md->interface->bcast.sin_addr.s_addr + : (md->cast_flags + ? (md->interface->sin.sin_addr.s_addr + ? md->interface->sin.sin_addr.s_addr + : md->interface->bcast.sin_addr.s_addr + ) + : 4); + im->flags = md->cast_flags; + im->port = md->rmtport; + im->mode = md->mode; + im->version = md->version; + im = (struct info_monitor_1 *)more_pkt(); + } + flush_pkt(); +} + +/* + * Module entry points and the flags they correspond with + */ +struct reset_entry { + int flag; /* flag this corresponds to */ + void (*handler) P((void)); /* routine to handle request */ +}; + +struct reset_entry reset_entries[] = { + { RESET_FLAG_ALLPEERS, peer_all_reset }, + { RESET_FLAG_IO, io_clr_stats }, + { RESET_FLAG_SYS, proto_clr_stats }, + { RESET_FLAG_MEM, peer_clr_stats }, + { RESET_FLAG_TIMER, timer_clr_stats }, + { RESET_FLAG_AUTH, reset_auth_stats }, + { RESET_FLAG_CTL, ctl_clr_stats }, + { 0, 0 } +}; + +/* + * reset_stats - reset statistic counters here and there + */ +static void +reset_stats( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + u_long flags; + struct reset_entry *rent; + + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + flags = ((struct reset_flags *)inpkt->data)->flags; + + if (flags & ~RESET_ALLFLAGS) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + for (rent = reset_entries; rent->flag != 0; rent++) { + if (flags & rent->flag) + (rent->handler)(); + } + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * reset_peer - clear a peer's statistics + */ +static void +reset_peer( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_unpeer *cp; + register int items; + register struct peer *peer; + struct sockaddr_in peeraddr; + int bad; + + /* + * We check first to see that every peer exists. If not, + * we return an error. + */ + peeraddr.sin_family = AF_INET; + peeraddr.sin_port = htons(NTP_PORT); + + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_unpeer *)inpkt->data; + + bad = 0; + while (items-- > 0 && !bad) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + peer = findexistingpeer(&peeraddr, (struct peer *)0, -1); + if (peer == (struct peer *)0) + bad++; + cp++; + } + + if (bad) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + /* + * Now do it in earnest. + */ + + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_unpeer *)inpkt->data; + while (items-- > 0) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + peer = findexistingpeer(&peeraddr, (struct peer *)0, -1); + while (peer != 0) { + peer_reset(peer); + peer = findexistingpeer(&peeraddr, (struct peer *)peer, -1); + } + cp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * do_key_reread - reread the encryption key file + */ +static void +do_key_reread( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + rereadkeys(); + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * trust_key - make one or more keys trusted + */ +static void +trust_key( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_trustkey(srcadr, inter, inpkt, 1); +} + + +/* + * untrust_key - make one or more keys untrusted + */ +static void +untrust_key( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_trustkey(srcadr, inter, inpkt, 0); +} + + +/* + * do_trustkey - make keys either trustable or untrustable + */ +static void +do_trustkey( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int trust + ) +{ + register u_long *kp; + register int items; + + items = INFO_NITEMS(inpkt->err_nitems); + kp = (u_long *)inpkt->data; + while (items-- > 0) { + authtrust(*kp, trust); + kp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * get_auth_info - return some stats concerning the authentication module + */ +static void +get_auth_info( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_auth *ia; + + /* + * Importations from the authentication module + */ + extern u_long authnumkeys; + extern int authnumfreekeys; + extern u_long authkeylookups; + extern u_long authkeynotfound; + extern u_long authencryptions; + extern u_long authdecryptions; + extern u_long authkeyuncached; + extern u_long authkeyexpired; + + ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_auth)); + + ia->numkeys = htonl((u_int32)authnumkeys); + ia->numfreekeys = htonl((u_int32)authnumfreekeys); + ia->keylookups = htonl((u_int32)authkeylookups); + ia->keynotfound = htonl((u_int32)authkeynotfound); + ia->encryptions = htonl((u_int32)authencryptions); + ia->decryptions = htonl((u_int32)authdecryptions); + ia->keyuncached = htonl((u_int32)authkeyuncached); + ia->expired = htonl((u_int32)authkeyexpired); + ia->timereset = htonl((u_int32)(current_time - auth_timereset)); + + (void) more_pkt(); + flush_pkt(); +} + + + +/* + * reset_auth_stats - reset the authentication stat counters. Done here + * to keep ntp-isms out of the authentication module + */ +static void +reset_auth_stats(void) +{ + /* + * Importations from the authentication module + */ + extern u_long authkeylookups; + extern u_long authkeynotfound; + extern u_long authencryptions; + extern u_long authdecryptions; + extern u_long authkeyuncached; + + authkeylookups = 0; + authkeynotfound = 0; + authencryptions = 0; + authdecryptions = 0; + authkeyuncached = 0; + auth_timereset = current_time; +} + + +/* + * req_get_traps - return information about current trap holders + */ +static void +req_get_traps( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_trap *it; + register struct ctl_trap *tr; + register int i; + + /* + * Imported from the control module + */ + extern struct ctl_trap ctl_trap[]; + extern int num_ctl_traps; + + if (num_ctl_traps == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + it = (struct info_trap *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_trap)); + + for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) { + if (tr->tr_flags & TRAP_INUSE) { + if (tr->tr_localaddr == any_interface) + it->local_address = 0; + else + it->local_address + = NSRCADR(&tr->tr_localaddr->sin); + it->trap_address = NSRCADR(&tr->tr_addr); + it->trap_port = NSRCPORT(&tr->tr_addr); + it->sequence = htons(tr->tr_sequence); + it->settime = htonl((u_int32)(current_time - tr->tr_settime)); + it->origtime = htonl((u_int32)(current_time - tr->tr_origtime)); + it->resets = htonl((u_int32)tr->tr_resets); + it->flags = htonl((u_int32)tr->tr_flags); + it = (struct info_trap *)more_pkt(); + } + } + flush_pkt(); +} + + +/* + * req_set_trap - configure a trap + */ +static void +req_set_trap( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_setclr_trap(srcadr, inter, inpkt, 1); +} + + + +/* + * req_clr_trap - unconfigure a trap + */ +static void +req_clr_trap( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + do_setclr_trap(srcadr, inter, inpkt, 0); +} + + + +/* + * do_setclr_trap - do the grunge work of (un)configuring a trap + */ +static void +do_setclr_trap( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int set + ) +{ + register struct conf_trap *ct; + register struct interface *linter; + int res; + struct sockaddr_in laddr; + + /* + * Prepare sockaddr_in structure + */ + memset((char *)&laddr, 0, sizeof laddr); + laddr.sin_family = AF_INET; + laddr.sin_port = ntohs(NTP_PORT); + + /* + * Restrict ourselves to one item only. This eliminates + * the error reporting problem. + */ + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + ct = (struct conf_trap *)inpkt->data; + + /* + * Look for the local interface. If none, use the default. + */ + if (ct->local_address == 0) { + linter = any_interface; + } else { + laddr.sin_addr.s_addr = ct->local_address; + linter = findinterface(&laddr); + if (linter == NULL) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + } + + laddr.sin_addr.s_addr = ct->trap_address; + if (ct->trap_port != 0) + laddr.sin_port = ct->trap_port; + else + laddr.sin_port = htons(TRAPPORT); + + if (set) { + res = ctlsettrap(&laddr, linter, 0, + INFO_VERSION(inpkt->rm_vn_mode)); + } else { + res = ctlclrtrap(&laddr, linter, 0); + } + + if (!res) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + } else { + req_ack(srcadr, inter, inpkt, INFO_OKAY); + } + return; +} + + + +/* + * set_request_keyid - set the keyid used to authenticate requests + */ +static void +set_request_keyid( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + u_long keyid; + + /* + * Restrict ourselves to one item only. + */ + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + keyid = ntohl(*((u_int32 *)(inpkt->data))); + info_auth_keyid = keyid; + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + + +/* + * set_control_keyid - set the keyid used to authenticate requests + */ +static void +set_control_keyid( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + u_long keyid; + extern u_long ctl_auth_keyid; + + /* + * Restrict ourselves to one item only. + */ + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + keyid = ntohl(*((u_int32 *)(inpkt->data))); + ctl_auth_keyid = keyid; + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + + +/* + * get_ctl_stats - return some stats concerning the control message module + */ +static void +get_ctl_stats( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_control *ic; + + /* + * Importations from the control module + */ + extern u_long ctltimereset; + extern u_long numctlreq; + extern u_long numctlbadpkts; + extern u_long numctlresponses; + extern u_long numctlfrags; + extern u_long numctlerrors; + extern u_long numctltooshort; + extern u_long numctlinputresp; + extern u_long numctlinputfrag; + extern u_long numctlinputerr; + extern u_long numctlbadoffset; + extern u_long numctlbadversion; + extern u_long numctldatatooshort; + extern u_long numctlbadop; + extern u_long numasyncmsgs; + + ic = (struct info_control *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_control)); + + ic->ctltimereset = htonl((u_int32)(current_time - ctltimereset)); + ic->numctlreq = htonl((u_int32)numctlreq); + ic->numctlbadpkts = htonl((u_int32)numctlbadpkts); + ic->numctlresponses = htonl((u_int32)numctlresponses); + ic->numctlfrags = htonl((u_int32)numctlfrags); + ic->numctlerrors = htonl((u_int32)numctlerrors); + ic->numctltooshort = htonl((u_int32)numctltooshort); + ic->numctlinputresp = htonl((u_int32)numctlinputresp); + ic->numctlinputfrag = htonl((u_int32)numctlinputfrag); + ic->numctlinputerr = htonl((u_int32)numctlinputerr); + ic->numctlbadoffset = htonl((u_int32)numctlbadoffset); + ic->numctlbadversion = htonl((u_int32)numctlbadversion); + ic->numctldatatooshort = htonl((u_int32)numctldatatooshort); + ic->numctlbadop = htonl((u_int32)numctlbadop); + ic->numasyncmsgs = htonl((u_int32)numasyncmsgs); + + (void) more_pkt(); + flush_pkt(); +} + + +#ifdef KERNEL_PLL +/* + * get_kernel_info - get kernel pll/pps information + */ +static void +get_kernel_info( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_kernel *ik; + struct timex ntx; + + if (!pll_control) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + memset((char *)&ntx, 0, sizeof(ntx)); + if (ntp_adjtime(&ntx) < 0) + msyslog(LOG_ERR, "get_kernel_info: ntp_adjtime() failed: %m"); + ik = (struct info_kernel *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_kernel)); + + /* + * pll variables + */ + ik->offset = htonl((u_int32)ntx.offset); + ik->freq = htonl((u_int32)ntx.freq); + ik->maxerror = htonl((u_int32)ntx.maxerror); + ik->esterror = htonl((u_int32)ntx.esterror); + ik->status = htons(ntx.status); + ik->constant = htonl((u_int32)ntx.constant); + ik->precision = htonl((u_int32)ntx.precision); + ik->tolerance = htonl((u_int32)ntx.tolerance); + + /* + * pps variables + */ + ik->ppsfreq = htonl((u_int32)ntx.ppsfreq); + ik->jitter = htonl((u_int32)ntx.jitter); + ik->shift = htons(ntx.shift); + ik->stabil = htonl((u_int32)ntx.stabil); + ik->jitcnt = htonl((u_int32)ntx.jitcnt); + ik->calcnt = htonl((u_int32)ntx.calcnt); + ik->errcnt = htonl((u_int32)ntx.errcnt); + ik->stbcnt = htonl((u_int32)ntx.stbcnt); + + (void) more_pkt(); + flush_pkt(); +} +#endif /* KERNEL_PLL */ + + +#ifdef REFCLOCK +/* + * get_clock_info - get info about a clock + */ +static void +get_clock_info( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_clock *ic; + register u_int32 *clkaddr; + register int items; + struct refclockstat clock_stat; + struct sockaddr_in addr; + l_fp ltmp; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(NTP_PORT); + items = INFO_NITEMS(inpkt->err_nitems); + clkaddr = (u_int32 *) inpkt->data; + + ic = (struct info_clock *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_clock)); + + while (items-- > 0) { + addr.sin_addr.s_addr = *clkaddr++; + if (!ISREFCLOCKADR(&addr) || + findexistingpeer(&addr, (struct peer *)0, -1) == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + clock_stat.kv_list = (struct ctl_var *)0; + + refclock_control(&addr, (struct refclockstat *)0, &clock_stat); + + ic->clockadr = addr.sin_addr.s_addr; + ic->type = clock_stat.type; + ic->flags = clock_stat.flags; + ic->lastevent = clock_stat.lastevent; + ic->currentstatus = clock_stat.currentstatus; + ic->polls = htonl((u_int32)clock_stat.polls); + ic->noresponse = htonl((u_int32)clock_stat.noresponse); + ic->badformat = htonl((u_int32)clock_stat.badformat); + ic->baddata = htonl((u_int32)clock_stat.baddata); + ic->timestarted = htonl((u_int32)clock_stat.timereset); + DTOLFP(clock_stat.fudgetime1, <mp); + HTONL_FP(<mp, &ic->fudgetime1); + DTOLFP(clock_stat.fudgetime1, <mp); + HTONL_FP(<mp, &ic->fudgetime2); + ic->fudgeval1 = htonl((u_int32)clock_stat.fudgeval1); + ic->fudgeval2 = htonl((u_int32)clock_stat.fudgeval2); + + free_varlist(clock_stat.kv_list); + + ic = (struct info_clock *)more_pkt(); + } + flush_pkt(); +} + + + +/* + * set_clock_fudge - get a clock's fudge factors + */ +static void +set_clock_fudge( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_fudge *cf; + register int items; + struct refclockstat clock_stat; + struct sockaddr_in addr; + l_fp ltmp; + + memset((char *)&addr, 0, sizeof addr); + memset((char *)&clock_stat, 0, sizeof clock_stat); + addr.sin_family = AF_INET; + addr.sin_port = htons(NTP_PORT); + items = INFO_NITEMS(inpkt->err_nitems); + cf = (struct conf_fudge *) inpkt->data; + + while (items-- > 0) { + addr.sin_addr.s_addr = cf->clockadr; + if (!ISREFCLOCKADR(&addr) || + findexistingpeer(&addr, (struct peer *)0, -1) == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + switch(ntohl(cf->which)) { + case FUDGE_TIME1: + NTOHL_FP(&cf->fudgetime, <mp); + LFPTOD(<mp, clock_stat.fudgetime1); + clock_stat.haveflags = CLK_HAVETIME1; + break; + case FUDGE_TIME2: + NTOHL_FP(&cf->fudgetime, <mp); + LFPTOD(<mp, clock_stat.fudgetime2); + clock_stat.haveflags = CLK_HAVETIME2; + break; + case FUDGE_VAL1: + clock_stat.fudgeval1 = ntohl(cf->fudgeval_flags); + clock_stat.haveflags = CLK_HAVEVAL1; + break; + case FUDGE_VAL2: + clock_stat.fudgeval2 = ntohl(cf->fudgeval_flags); + clock_stat.haveflags = CLK_HAVEVAL2; + break; + case FUDGE_FLAGS: + clock_stat.flags = (u_char) ntohl(cf->fudgeval_flags) & 0xf; + clock_stat.haveflags = + (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4); + break; + default: + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + refclock_control(&addr, &clock_stat, (struct refclockstat *)0); + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} +#endif + +#ifdef REFCLOCK +/* + * get_clkbug_info - get debugging info about a clock + */ +static void +get_clkbug_info( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register int i; + register struct info_clkbug *ic; + register u_int32 *clkaddr; + register int items; + struct refclockbug bug; + struct sockaddr_in addr; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(NTP_PORT); + items = INFO_NITEMS(inpkt->err_nitems); + clkaddr = (u_int32 *) inpkt->data; + + ic = (struct info_clkbug *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_clkbug)); + + while (items-- > 0) { + addr.sin_addr.s_addr = *clkaddr++; + if (!ISREFCLOCKADR(&addr) || + findexistingpeer(&addr, (struct peer *)0, -1) == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + memset((char *)&bug, 0, sizeof bug); + refclock_buginfo(&addr, &bug); + if (bug.nvalues == 0 && bug.ntimes == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + ic->clockadr = addr.sin_addr.s_addr; + i = bug.nvalues; + if (i > NUMCBUGVALUES) + i = NUMCBUGVALUES; + ic->nvalues = (u_char)i; + ic->svalues = htons((u_short) (bug.svalues & ((1<<i)-1))); + while (--i >= 0) + ic->values[i] = htonl(bug.values[i]); + + i = bug.ntimes; + if (i > NUMCBUGTIMES) + i = NUMCBUGTIMES; + ic->ntimes = (u_char)i; + ic->stimes = htonl(bug.stimes); + while (--i >= 0) { + HTONL_FP(&bug.times[i], &ic->times[i]); + } + + ic = (struct info_clkbug *)more_pkt(); + } + flush_pkt(); +} +#endif |