peer_xmit(
	struct peer *peer	/* peer structure pointer */
	)
{
	struct pkt xpkt;	/* transmit packet */
	size_t	sendlen, authlen;
	keyid_t	xkeyid = 0;	/* transmit key ID */
	l_fp	xmt_tx, xmt_ty;

	if (!peer->dstadr)	/* drop peers without interface */
		return;

	xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, peer->version,
	    peer->hmode);
	xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
	xpkt.ppoll = peer->hpoll;
	xpkt.precision = sys_precision;
	xpkt.refid = sys_refid;
	xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
	xpkt.rootdisp =  HTONS_FP(DTOUFP(sys_rootdisp));
	HTONL_FP(&sys_reftime, &xpkt.reftime);
	HTONL_FP(&peer->rec, &xpkt.org);
	HTONL_FP(&peer->dst, &xpkt.rec);

	/*
	 * If the received packet contains a MAC, the transmitted packet
	 * is authenticated and contains a MAC. If not, the transmitted
	 * packet is not authenticated.
	 *
	 * It is most important when autokey is in use that the local
	 * interface IP address be known before the first packet is
	 * sent. Otherwise, it is not possible to compute a correct MAC
	 * the recipient will accept. Thus, the I/O semantics have to do
	 * a little more work. In particular, the wildcard interface
	 * might not be usable.
	 */
	sendlen = LEN_PKT_NOMAC;
	if (
#ifdef AUTOKEY
	    !(peer->flags & FLAG_SKEY) &&
#endif	/* !AUTOKEY */
	    peer->keyid == 0) {

		/*
		 * Transmit a-priori timestamps
		 */
		get_systime(&xmt_tx);
		if (peer->flip == 0) {	/* basic mode */
			peer->aorg = xmt_tx;
			HTONL_FP(&xmt_tx, &xpkt.xmt);
		} else {		/* interleaved modes */
			if (peer->hmode == MODE_BROADCAST) { /* bcst */
				HTONL_FP(&xmt_tx, &xpkt.xmt);
				if (peer->flip > 0)
					HTONL_FP(&peer->borg,
					    &xpkt.org);
				else
					HTONL_FP(&peer->aorg,
					    &xpkt.org);
			} else {	/* symmetric */
				if (peer->flip > 0)
					HTONL_FP(&peer->borg,
					    &xpkt.xmt);
				else
					HTONL_FP(&peer->aorg,
					    &xpkt.xmt);
			}
		}
		peer->t21_bytes = sendlen;
		sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl],
		    &xpkt, sendlen);
		peer->sent++;
		peer->throttle += (1 << peer->minpoll) - 2;

		/*
		 * Capture a-posteriori timestamps
		 */
		get_systime(&xmt_ty);
		if (peer->flip != 0) {		/* interleaved modes */
			if (peer->flip > 0)
				peer->aorg = xmt_ty;
			else
				peer->borg = xmt_ty;
			peer->flip = -peer->flip;
		}
		L_SUB(&xmt_ty, &xmt_tx);
		LFPTOD(&xmt_ty, peer->xleave);
		DPRINTF(1, ("peer_xmit: at %ld %s->%s mode %d len %zu xmt %#010x.%08x\n",
			    current_time,
			    peer->dstadr ? stoa(&peer->dstadr->sin) : "-",
		            stoa(&peer->srcadr), peer->hmode, sendlen,
			    xmt_tx.l_ui, xmt_tx.l_uf));
		return;
	}

	/*
	 * Authentication is enabled, so the transmitted packet must be
	 * authenticated. If autokey is enabled, fuss with the various
	 * modes; otherwise, symmetric key cryptography is used.
	 */
#ifdef AUTOKEY
	if (peer->flags & FLAG_SKEY) {
		struct exten *exten;	/* extension field */

		/*
		 * The Public Key Dance (PKD): Cryptographic credentials
		 * are contained in extension fields, each including a
		 * 4-octet length/code word followed by a 4-octet
		 * association ID and optional additional data. Optional
		 * data includes a 4-octet data length field followed by
		 * the data itself. Request messages are sent from a
		 * configured association; response messages can be sent
		 * from a configured association or can take the fast
		 * path without ever matching an association. Response
		 * messages have the same code as the request, but have
		 * a response bit and possibly an error bit set. In this
		 * implementation, a message may contain no more than
		 * one command and one or more responses.
		 *
		 * Cryptographic session keys include both a public and
		 * a private componet. Request and response messages
		 * using extension fields are always sent with the
		 * private component set to zero. Packets without
		 * extension fields indlude the private component when
		 * the session key is generated.
		 */
		while (1) {

			/*
			 * Allocate and initialize a keylist if not
			 * already done. Then, use the list in inverse
			 * order, discarding keys once used. Keep the
			 * latest key around until the next one, so
			 * clients can use client/server packets to
			 * compute propagation delay.
			 *
			 * Note that once a key is used from the list,
			 * it is retained in the key cache until the
			 * next key is used. This is to allow a client
			 * to retrieve the encrypted session key
			 * identifier to verify authenticity.
			 *
			 * If for some reason a key is no longer in the
			 * key cache, a birthday has happened or the key
			 * has expired, so the pseudo-random sequence is
			 * broken. In that case, purge the keylist and
			 * regenerate it.
			 */
			if (peer->keynumber == 0)
				make_keylist(peer, peer->dstadr);
			else
				peer->keynumber--;
			xkeyid = peer->keylist[peer->keynumber];
			if (authistrusted(xkeyid))
				break;
			else
				key_expire(peer);
		}
		peer->keyid = xkeyid;
		exten = NULL;
		switch (peer->hmode) {

		/*
		 * In broadcast server mode the autokey values are
		 * required by the broadcast clients. Push them when a
		 * new keylist is generated; otherwise, push the
		 * association message so the client can request them at
		 * other times.
		 */
		case MODE_BROADCAST:
			if (peer->flags & FLAG_ASSOC)
				exten = crypto_args(peer, CRYPTO_AUTO |
				    CRYPTO_RESP, peer->associd, NULL);
			else
				exten = crypto_args(peer, CRYPTO_ASSOC |
				    CRYPTO_RESP, peer->associd, NULL);
			break;

		/*
		 * In symmetric modes the parameter, certificate,
		 * identity, cookie and autokey exchanges are
		 * required. The leapsecond exchange is optional. But, a
		 * peer will not believe the other peer until the other
		 * peer has synchronized, so the certificate exchange
		 * might loop until then. If a peer finds a broken
		 * autokey sequence, it uses the autokey exchange to
		 * retrieve the autokey values. In any case, if a new
		 * keylist is generated, the autokey values are pushed.
		 */
		case MODE_ACTIVE:
		case MODE_PASSIVE:

			/*
			 * Parameter, certificate and identity.
			 */
			if (!peer->crypto)
				exten = crypto_args(peer, CRYPTO_ASSOC,
				    peer->associd, hostval.ptr);
			else if (!(peer->crypto & CRYPTO_FLAG_CERT))
				exten = crypto_args(peer, CRYPTO_CERT,
				    peer->associd, peer->issuer);
			else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
				exten = crypto_args(peer,
				    crypto_ident(peer), peer->associd,
				    NULL);

			/*
			 * Cookie and autokey. We request the cookie
			 * only when the this peer and the other peer
			 * are synchronized. But, this peer needs the
			 * autokey values when the cookie is zero. Any
			 * time we regenerate the key list, we offer the
			 * autokey values without being asked. If for
			 * some reason either peer finds a broken
			 * autokey sequence, the autokey exchange is
			 * used to retrieve the autokey values.
			 */
			else if (   sys_leap != LEAP_NOTINSYNC
				 && peer->leap != LEAP_NOTINSYNC
				 && !(peer->crypto & CRYPTO_FLAG_COOK))
				exten = crypto_args(peer, CRYPTO_COOK,
				    peer->associd, NULL);
			else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
				exten = crypto_args(peer, CRYPTO_AUTO,
				    peer->associd, NULL);
			else if (   peer->flags & FLAG_ASSOC
				 && peer->crypto & CRYPTO_FLAG_SIGN)
				exten = crypto_args(peer, CRYPTO_AUTO |
				    CRYPTO_RESP, peer->assoc, NULL);

			/*
			 * Wait for clock sync, then sign the
			 * certificate and retrieve the leapsecond
			 * values.
			 */
			else if (sys_leap == LEAP_NOTINSYNC)
				break;

			else if (!(peer->crypto & CRYPTO_FLAG_SIGN))
				exten = crypto_args(peer, CRYPTO_SIGN,
				    peer->associd, hostval.ptr);
			else if (!(peer->crypto & CRYPTO_FLAG_LEAP))
				exten = crypto_args(peer, CRYPTO_LEAP,
				    peer->associd, NULL);
			break;

		/*
		 * In client mode the parameter, certificate, identity,
		 * cookie and sign exchanges are required. The
		 * leapsecond exchange is optional. If broadcast client
		 * mode the same exchanges are required, except that the
		 * autokey exchange is substitutes for the cookie
		 * exchange, since the cookie is always zero. If the
		 * broadcast client finds a broken autokey sequence, it
		 * uses the autokey exchange to retrieve the autokey
		 * values.
		 */
		case MODE_CLIENT:

			/*
			 * Parameter, certificate and identity.
			 */
			if (!peer->crypto)
				exten = crypto_args(peer, CRYPTO_ASSOC,
				    peer->associd, hostval.ptr);
			else if (!(peer->crypto & CRYPTO_FLAG_CERT))
				exten = crypto_args(peer, CRYPTO_CERT,
				    peer->associd, peer->issuer);
			else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
				exten = crypto_args(peer,
				    crypto_ident(peer), peer->associd,
				    NULL);

			/*
			 * Cookie and autokey. These are requests, but
			 * we use the peer association ID with autokey
			 * rather than our own.
			 */
			else if (!(peer->crypto & CRYPTO_FLAG_COOK))
				exten = crypto_args(peer, CRYPTO_COOK,
				    peer->associd, NULL);
			else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
				exten = crypto_args(peer, CRYPTO_AUTO,
				    peer->assoc, NULL);

			/*
			 * Wait for clock sync, then sign the
			 * certificate and retrieve the leapsecond
			 * values.
			 */
			else if (sys_leap == LEAP_NOTINSYNC)
				break;

			else if (!(peer->crypto & CRYPTO_FLAG_SIGN))
				exten = crypto_args(peer, CRYPTO_SIGN,
				    peer->associd, hostval.ptr);
			else if (!(peer->crypto & CRYPTO_FLAG_LEAP))
				exten = crypto_args(peer, CRYPTO_LEAP,
				    peer->associd, NULL);
			break;
		}

		/*
		 * Add a queued extension field if present. This is
		 * always a request message, so the reply ID is already
		 * in the message. If an error occurs, the error bit is
		 * lit in the response.
		 */
		if (peer->cmmd != NULL) {
			u_int32 temp32;

			temp32 = CRYPTO_RESP;
			peer->cmmd->opcode |= htonl(temp32);
			sendlen += crypto_xmit(peer, &xpkt, NULL,
			    sendlen, peer->cmmd, 0);
			free(peer->cmmd);
			peer->cmmd = NULL;
		}

		/*
		 * Add an extension field created above. All but the
		 * autokey response message are request messages.
		 */
		if (exten != NULL) {
			if (exten->opcode != 0)
				sendlen += crypto_xmit(peer, &xpkt,
				    NULL, sendlen, exten, 0);
			free(exten);
		}

		/*
		 * Calculate the next session key. Since extension
		 * fields are present, the cookie value is zero.
		 */
		if (sendlen > (int)LEN_PKT_NOMAC) {
			session_key(&peer->dstadr->sin, &peer->srcadr,
			    xkeyid, 0, 2);
		}
	}
#endif	/* AUTOKEY */

	/*
	 * Transmit a-priori timestamps
	 */
	get_systime(&xmt_tx);
	if (peer->flip == 0) {		/* basic mode */
		peer->aorg = xmt_tx;
		HTONL_FP(&xmt_tx, &xpkt.xmt);
	} else {			/* interleaved modes */
		if (peer->hmode == MODE_BROADCAST) { /* bcst */
			HTONL_FP(&xmt_tx, &xpkt.xmt);
			if (peer->flip > 0)
				HTONL_FP(&peer->borg, &xpkt.org);
			else
				HTONL_FP(&peer->aorg, &xpkt.org);
		} else {		/* symmetric */
			if (peer->flip > 0)
				HTONL_FP(&peer->borg, &xpkt.xmt);
			else
				HTONL_FP(&peer->aorg, &xpkt.xmt);
		}
	}
	xkeyid = peer->keyid;
	authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
	if (authlen == 0) {
		report_event(PEVNT_AUTH, peer, "no key");
		peer->flash |= TEST5;		/* auth error */
		peer->badauth++;
		return;
	}
	sendlen += authlen;
#ifdef AUTOKEY
	if (xkeyid > NTP_MAXKEY)
		authtrust(xkeyid, 0);
#endif	/* AUTOKEY */
	if (sendlen > sizeof(xpkt)) {
		msyslog(LOG_ERR, "peer_xmit: buffer overflow %zu", sendlen);
		exit (-1);
	}
	peer->t21_bytes = sendlen;
	sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt,
	    sendlen);
	peer->sent++;
	peer->throttle += (1 << peer->minpoll) - 2;

	/*
	 * Capture a-posteriori timestamps
	 */
	get_systime(&xmt_ty);
	if (peer->flip != 0) {			/* interleaved modes */
		if (peer->flip > 0)
			peer->aorg = xmt_ty;
		else
			peer->borg = xmt_ty;
		peer->flip = -peer->flip;
	}
	L_SUB(&xmt_ty, &xmt_tx);
	LFPTOD(&xmt_ty, peer->xleave);
#ifdef AUTOKEY
	DPRINTF(1, ("peer_xmit: at %ld %s->%s mode %d keyid %08x len %zu index %d\n",
		    current_time, latoa(peer->dstadr),
		    ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen,
		    peer->keynumber));
#else	/* !AUTOKEY follows */
	DPRINTF(1, ("peer_xmit: at %ld %s->%s mode %d keyid %08x len %d\n",
		    current_time, peer->dstadr ?
		    ntoa(&peer->dstadr->sin) : "-",
		    ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen));
#endif	/* !AUTOKEY */

	return;
}