Net2/usr/src/contrib/isode/others/ntp/ntp_sock.c

Compare this file to the similar file:
Show the results in this format:

#ifndef	lint
static char *RCSid = "$Header: /f/osi/others/ntp/RCS/ntp_sock.c,v 7.1 91/02/22 09:33:53 mrose Interim $";
#endif

/*
 * Ntp UDP specific code (mainly) based on the 3.4 ntp but heavily modified.
 * $Log:	ntp_sock.c,v $
 * Revision 7.1  91/02/22  09:33:53  mrose
 * Interim 6.8
 * 
 * Revision 7.0  90/12/10  17:21:33  mrose
 * *** empty log message ***
 * 
 * Revision 1.2  90/08/14  10:13:58  jpo
 * new protocol version
 * 
 * Revision 1.1  89/06/15  20:36:59  jpo
 * Initial revision
 * 
 * 
 */

#include "ntp.h"

struct intf *addrs;
int nintf = 0;

#ifdef	TEST
extern int errno;

main() {
	int i, cc, val;
	char foo[10];

	advise(LLOG_DEBUG, NULLCP, "ifconfig test");
	create_sockets(htons(43242));
	for (i = 0; i < nintf; i++) {
		printf("%d: %s fd %d  addr %s  mask %x ",
		       i, addrs[i].name, addrs[i].fd,
		       paddr (addrs[i].addr),
		       ntohl(addrs[i].mask.sin_addr.s_addr));
		cc = sizeof(val);
		if (getsockopt(addrs[0].fd, SOL_SOCKET, SO_BROADCAST,
			       (char*)&val, &cc)) {
			perror("getsockopt");
			exit(1);
		}
		printf("BCAST opt %d", val);
		cc = sizeof(val);
		if (getsockopt(addrs[0].fd, SOL_SOCKET, SO_RCVBUF,
			       (char*)&val, &cc)) {
			perror("getsockopt");
			exit(1);
		}
		printf("sockbuf size = %d ", val);
		putchar('\n');
	}

	for (i=0; i < nintf; i++) {
		fprintf(stderr, "Read fd %d.. ", addrs[i].fd);
		cc = read(addrs[i].fd, foo, 10);
		fprintf(stderr, " returns %d ", cc);
		perror("read errno");
	}
}
#endif

#ifndef	SIOCGIFCONF
/*
 *  If we can't determine the interface configuration, just listen with one
 *  socket at the INADDR_ANY address.
 */
create_sockets(port)
	unsigned int port;
{
	struct intf *ap;
	int	no;

	ap = getintf (&no);
	ap->addr.ad_inet.sin.sin_family = AF_INET;
	ap->addr.ad_inet.sin.sin_port = 0;
	ap->addr.ad_inet.sin_addr.s_addr = INADDR_ANY;
	ap->addr.ad_inet.sin_mask.s_addr = htonl(~0);
	ap->name = "wildcard";
	ap->addr.type = AF_INET;
	ap->flags = (INTF_VALID|INTF_STATIC);

	if ((ap->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		adios ("failed", "socket()");

	if (fcntl(ap->fd, F_SETFL, FNDELAY) < 0)
		adios ("failed", "fcntl(FNDELAY)");

	ap->addr.ad_inet.sin_family = AF_INET;
	ap->addr.ad_inet.sin_port = port;
	ap->if_flags = 0;
	ap->flags = (INTF_VALID|INTF_STATIC);
	if (bind(ap->fd, (struct sockaddr *)&addrs[0].addr.ad_inet,
		 sizeof(ap->addr.ad_inet)) < 0)
		adios("failed", "bind");
	return nintf;
}
#else
/*
 *  Grab interface configuration, and create a socket for each interface
 *  address.
 */
create_sockets(port)
	unsigned int port;
{
	char	buf[1024];
	struct intf *ap;
	struct	ifconf	ifc;
	struct	ifreq	ifreq, *ifr;
	int on = 1, off = 0;
	int n, i, vs;
	extern char *malloc();

	/*
	 * create pseudo-interface with wildcard address
	 */
	ap = getintf (&n);
	ap->addr.type =
		ap -> addr.inet_ad.sin_family = AF_INET;
	ap -> addr.inet_ad.sin_port = 0;
	ap -> addr.inet_ad.sin_addr.s_addr = INADDR_ANY;
	ap -> name = "wild";
	ap -> flags = (INTF_VALID|INTF_STATIC);
	ap -> mask.sin_addr.s_addr = htonl(~0);

	if ((vs = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		adios("failed", "socket(AF_INET, SOCK_DGRAM)");
	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = buf;
	if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0)
		adios ("failed", "get interface configuration");
	n = ifc.ifc_len/sizeof(struct ifreq);

	for (ifr = ifc.ifc_req; n > 0; n--, ifr++) {
		int num;

		ap = getintf (&num);
		if (ifr->ifr_addr.sa_family != AF_INET)
			continue;
		ifreq = *ifr;
		if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
			advise(LLOG_EXCEPTIONS, "failed",
			       "get interface flags");
			continue;
		}
		if ((ifreq.ifr_flags & IFF_UP) == 0)
			continue;
		ap -> if_flags = ifreq.ifr_flags;

		if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) {
			advise (LLOG_EXCEPTIONS, "failed",
				"get interface addr");
			continue;
		}
		ap -> name = strdup (ifreq.ifr_name);
		ap -> addr.inet_ad = *(struct sockaddr_in *)&ifreq.ifr_addr;
		ap -> addr.type = AF_INET;

#ifdef SIOCGIFBRDADDR
		if (ap -> if_flags & IFF_BROADCAST) {
			if (ioctl(vs, SIOCGIFBRDADDR, (char *)&ifreq) < 0)
				adios("failed", "SIOCGIFBRDADDR ");
#ifndef	SUN_3_3
			ap -> bcast =
				*(struct sockaddr_in *)&ifreq.ifr_broadaddr;
#else
			ap -> bcast =
				*(struct sockaddr_in *)&ifreq.ifr_addr;
#endif
		}
#endif /* SIOCGIFBRDADDR */
#ifdef SIOCGIFNETMASK
		if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0)
			adios ("failed", "SIOCGIFNETMASK ");

		ap -> mask = *(struct sockaddr_in *)&ifreq.ifr_addr;
#endif /* SIOCGIFNETMASK */

		/* 
		 * look for an already existing source interface address.  If
		 * the machine has multiple point to point interfaces, then 
		 * the local address may appear more than once.
		 */		   
		for (i=0; i < nintf; i++) {
			if (i != num &&
			    addr_compare (&addrs[i].addr, &ap -> addr)) {
				advise (LLOG_EXCEPTIONS, NULLCP,
					"dup interface address %s on %s\n",
					paddr (&ap -> addr),
					ifreq.ifr_name);
				goto next;
			}
		}
		ap -> flags = (INTF_VALID|INTF_STATIC);
	   next:;
	}
	(void) close(vs);

	for (i = 0; i < nintf; i++) {
		if ((addrs[i].flags & INTF_VALID) == 0)
			continue;
		/* create a datagram (UDP) socket */
		if ((addrs[i].fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
			adios ("failed", "socket()");

		/* set SO_REUSEADDR since we will be binding the same port
		   number on each interface */
		if (setsockopt(addrs[i].fd, SOL_SOCKET, SO_REUSEADDR,
			       (char *)&on, sizeof(on)))
			advise(LLOG_EXCEPTIONS, "failed",
			       "setsockopt SO_REUSEADDR");

		/*
		 * set non-blocking I/O on the descriptor
		 */
		if (fcntl(addrs[i].fd, F_SETFL, FNDELAY) < 0)
			adios ("failed", "fcntl(FNDELAY) fails");


		/*
		 * finally, bind the local address address.
		 */
		addrs[i].addr.inet_ad.sin_family = AF_INET;
		addrs[i].addr.inet_ad.sin_port = port;
		if (bind(addrs[i].fd,
			 (struct sockaddr *)&addrs[i].addr.inet_ad,
			 sizeof(addrs[i].addr.inet_ad)) < 0)
			adios ("failed", "bind on %s", paddr(&addrs[i].addr));

		/*
		 *  Turn off the SO_REUSEADDR socket option.  It apparently
		 *  causes heartburn on systems with multicast IP installed.
		 *  On normal systems it only gets looked at when the address
		 *  is being bound anyway..
		 */
		if (setsockopt(addrs[i].fd, SOL_SOCKET, SO_REUSEADDR,
			       (char *)&off, sizeof(off)))
			adios ("failed", "setsockopt SO_REUSEADDR off");

#ifdef SO_BROADCAST
		/* if this interface can support broadcast, set SO_BROADCAST */
		if (addrs[i].if_flags & IFF_BROADCAST) {
			if (setsockopt(addrs[i].fd, SOL_SOCKET, SO_BROADCAST,
				       (char *)&on, sizeof(on)))
				adios ("failed", "setsockopt(SO_BROADCAST)");
		}
#endif				/* SO_BROADCAST */
	}
	return nintf;
}

#endif

recv_inet (ap, tvp)
struct intf *ap;
struct timeval *tvp;
{
	int	dstlen;
	int	cc;
	struct Naddr peers;
	struct Naddr *peer = &peers;
	extern int debug;
	struct ntpdata pkts;
	struct ntpdata *pkt = &pkts;
	

	peer -> type = AF_INET;
	dstlen = sizeof(struct sockaddr_in);
	if ((cc = recvfrom(ap -> fd, (char *) pkt, 
			   sizeof(*pkt), 0, 
			   (struct sockaddr *) &peer->inet_ad, &dstlen)) < 0) {

		if (errno != EWOULDBLOCK) {
			advise (LLOG_EXCEPTIONS, "failed", "recvfrom");
#ifdef	DEBUG
			if(debug > 2)
				perror("recvfrom");
#endif
		}
		return -1;
	}

	if (cc < sizeof(*pkt)) {
#ifdef	DEBUG
		if (debug)
			printf("Runt packet from %s\n",
			       paddr (peer));
#endif
		return -1;
	}
	if (pkt -> stratum == INFO_QUERY ||
	    pkt -> stratum == INFO_REPLY) {
		query_mode (peer, pkt, ap);
		return 0;
	}
#ifdef	DEBUG
	if (debug > 3) {
		printf ("\nInput ");
		dump_pkt(peer, pkt, (struct ntp_peer *)NULL);
	}
#endif
	switch (PKT2VERS (pkt -> status)) {
	    case 1:
	    case 2:
		break;
	    default:
		return -1;
	}
	receive (peer, pkt, tvp, ap - addrs);
	return 0;
}

send_inet (ap, pkt, size, peer)
struct intf *ap;
char	*pkt;
int	size;
struct Naddr *peer;
{
	if (ap -> addr.type != AF_INET) {
		advise (LLOG_EXCEPTIONS, NULLCP,
			"Bad address type in sent_inet");
		return -1;
	}
	if (sendto(ap -> fd, (char *) pkt, size,
		   0, (struct sockaddr *)&peer->inet_ad,
		   sizeof(peer->inet_ad)) < 0) {
		advise (LLOG_EXCEPTIONS, "failed", "sendto: %s",
		       paddr (peer));
		return -1;
	}
	return 0;
}


#define	PKTBUF_SIZE	536

/* number of clocks per packet */
#define	N_NTP_PKTS \
      ((PKTBUF_SIZE - sizeof(struct ntpinfo))/(sizeof(struct clockinfo)))

query_mode(dst, ntp, ap)
	struct Naddr *dst;
	struct ntpdata *ntp;
struct intf *ap;
{
	extern struct list peer_list;
	extern struct sysdata sys;
	char packet[PKTBUF_SIZE];
	register struct ntpinfo *nip = (struct ntpinfo *) packet;
	register struct ntp_peer *peer;
	struct clockinfo *cip;
	int seq = 0;
	int i;

	if (ntp->stratum != INFO_QUERY)
		return;
	nip->version = NTPDC_VERSION;
	nip->type = INFO_REPLY;
	nip->seq = 0;
	nip->npkts = peer_list.members/N_NTP_PKTS;
	if (peer_list.members % N_NTP_PKTS)
		nip->npkts++;
	nip->peers = peer_list.members;
	nip->count = 0;
	cip = (struct clockinfo *)&nip[1];

	for (peer = peer_list.head; peer != NULL; peer = peer->next) {
		if (peer->src.type == AF_INET) {
			if (peer->sock < 0)
				cip->my_address = htonl(0);
			else
				cip->my_address =
					addrs[peer->sock].addr.inet_ad.sin_addr.s_addr;
			cip->port = peer->src.inet_ad.sin_port;	/* already in network order */
			cip->net_address = peer->src.inet_ad.sin_addr.s_addr;
		}
		else {
			struct in_addr sin;
			/* fake some values */
			sin = inet_makeaddr (126,1);
			cip -> net_address = sin.s_addr;
			cip -> port = htons(10123);
			sin = inet_makeaddr (126, 1);
			cip -> my_address = sin.s_addr;
		}
		cip->flags = htons(peer->flags);
		if (sys.peer == peer)
			cip->flags |= htons(PEER_FL_SELECTED);
		cip->pkt_sent = htonl(peer->pkt_sent);
		cip->pkt_rcvd = htonl(peer->pkt_rcvd);
		cip->pkt_dropped = htonl(peer->pkt_dropped);
		cip->timer = htonl(peer->timer);
		cip->leap = peer->leap;
		cip->stratum = peer->stratum;
		cip->ppoll = peer->ppoll;
		cip->precision = (int) peer->precision;
		cip->hpoll = peer->hpoll;
		cip->reach = htons(peer->reach & NTP_WINDOW_SHIFT_MASK);
		cip->estdisp = htonl((long) (peer->estdisp * 1000.0));
		cip->estdelay = htonl((long) (peer->estdelay * 1000.0));
		cip->estoffset = htonl((long) (peer->estoffset * 1000.0));
		switch (peer->refid.rid_type) {
		    case RID_STRING:
		    case RID_INET:
			cip->refid = peer->refid.rid_inet; /* XXX */
			break;
		    case RID_PSAP:
			cip -> refid = 0;
			break;
		}
		cip->reftime.int_part = htonl(peer->reftime.int_part);
		cip->reftime.fraction = htonl(peer->reftime.fraction);

		cip->info_filter.index = htons(peer->filter.samples);
		for (i = 0; i < PEER_SHIFT; i++) {
			cip->info_filter.offset[i] =
				htonl((long)(peer->filter.offset[i] * 1000.0));
			cip->info_filter.delay[i] =
				htonl((long)(peer->filter.delay[i] * 1000.0));
		}
		cip++;
		if (nip->count++ >= N_NTP_PKTS - 1) {
			nip->seq =seq++;
			(void) send_inet (ap, (char *)packet,
					  sizeof (packet), dst);
			nip->type = INFO_REPLY;
			nip->count = 0;
			cip = (struct clockinfo *)&nip[1];
		}
	}
	if (nip->count) {
		nip->seq = seq;
		(void) send_inet (ap, (char *)packet,
				  sizeof (packet), dst);
	}
}