rxinit_status needs to be global for the kext since
[openafs.git] / src / rx / rx.c
index 0c05763..161365e 100644 (file)
@@ -73,6 +73,7 @@ extern afs_int32 afs_termState;
 #endif /* KERNEL */
 
 #include <opr/queue.h>
+#include <hcrypto/rand.h>
 
 #include "rx.h"
 #include "rx_clock.h"
@@ -157,6 +158,7 @@ static void rxi_AckAllInTransmitQueue(struct rx_call *call);
 static void rxi_CancelKeepAliveEvent(struct rx_call *call);
 static void rxi_CancelDelayedAbortEvent(struct rx_call *call);
 static void rxi_CancelGrowMTUEvent(struct rx_call *call);
+static void update_nextCid(void);
 
 #ifdef RX_ENABLE_LOCKS
 struct rx_tq_debug {
@@ -202,16 +204,6 @@ static unsigned int rxi_rpc_peer_stat_cnt;
 
 static unsigned int rxi_rpc_process_stat_cnt;
 
-/*
- * rxi_busyChannelError is a boolean.  It indicates whether or not RX_CALL_BUSY
- * errors should be reported to the application when a call channel appears busy
- * (inferred from the receipt of RX_PACKET_TYPE_BUSY packets on the channel),
- * and there are other call channels in the connection that are not busy.
- * If 0, we do not return errors upon receiving busy packets; we just keep
- * trying on the same call channel until we hit a timeout.
- */
-static afs_int32 rxi_busyChannelError = 0;
-
 rx_atomic_t rx_nWaiting = RX_ATOMIC_INIT(0);
 rx_atomic_t rx_nWaited = RX_ATOMIC_INIT(0);
 
@@ -254,6 +246,7 @@ extern afs_kmutex_t rx_packets_mutex;
 extern afs_kmutex_t rx_refcnt_mutex;
 extern afs_kmutex_t des_init_mutex;
 extern afs_kmutex_t des_random_mutex;
+#ifndef KERNEL
 extern afs_kmutex_t rx_clock_mutex;
 extern afs_kmutex_t rxi_connCacheMutex;
 extern afs_kmutex_t event_handler_mutex;
@@ -263,6 +256,7 @@ extern afs_kmutex_t rx_if_mutex;
 
 extern afs_kcondvar_t rx_event_handler_cond;
 extern afs_kcondvar_t rx_listener_cond;
+#endif /* !KERNEL */
 
 static afs_kmutex_t epoch_mutex;
 static afs_kmutex_t rx_init_mutex;
@@ -272,24 +266,28 @@ static afs_kmutex_t rx_rpc_stats;
 static void
 rxi_InitPthread(void)
 {
-    MUTEX_INIT(&rx_clock_mutex, "clock", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&rx_stats_mutex, "stats", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&rx_atomic_mutex, "atomic", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_quota_mutex, "quota", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_pthread_mutex, "pthread", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_packets_mutex, "packets", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_refcnt_mutex, "refcnts", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&epoch_mutex, "epoch", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&rx_init_mutex, "init", MUTEX_DEFAULT, 0);
-    MUTEX_INIT(&event_handler_mutex, "event handler", MUTEX_DEFAULT, 0);
+#ifndef KERNEL
+    MUTEX_INIT(&rx_clock_mutex, "clock", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rxi_connCacheMutex, "conn cache", MUTEX_DEFAULT, 0);
+    MUTEX_INIT(&event_handler_mutex, "event handler", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&listener_mutex, "listener", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_if_init_mutex, "if init", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_if_mutex, "if", MUTEX_DEFAULT, 0);
+#endif
+    MUTEX_INIT(&rx_stats_mutex, "stats", MUTEX_DEFAULT, 0);
+    MUTEX_INIT(&rx_atomic_mutex, "atomic", MUTEX_DEFAULT, 0);
+    MUTEX_INIT(&epoch_mutex, "epoch", MUTEX_DEFAULT, 0);
+    MUTEX_INIT(&rx_init_mutex, "init", MUTEX_DEFAULT, 0);
     MUTEX_INIT(&rx_debug_mutex, "debug", MUTEX_DEFAULT, 0);
 
+#ifndef KERNEL
     CV_INIT(&rx_event_handler_cond, "evhand", CV_DEFAULT, 0);
     CV_INIT(&rx_listener_cond, "rxlisten", CV_DEFAULT, 0);
+#endif
 
     osi_Assert(pthread_key_create(&rx_thread_id_key, NULL) == 0);
     osi_Assert(pthread_key_create(&rx_ts_info_key, NULL) == 0);
@@ -310,7 +308,9 @@ rxi_InitPthread(void)
     MUTEX_INIT(&rx_connHashTable_lock, "rx_connHashTable_lock", MUTEX_DEFAULT,
               0);
     MUTEX_INIT(&rx_serverPool_lock, "rx_serverPool_lock", MUTEX_DEFAULT, 0);
+#ifndef KERNEL
     MUTEX_INIT(&rxi_keyCreate_lock, "rxi_keyCreate_lock", MUTEX_DEFAULT, 0);
+#endif
 #endif /* RX_ENABLE_LOCKS */
 }
 
@@ -394,6 +394,7 @@ struct rx_connection *rxLastConn = 0;
  * tiers:
  *
  * rx_connHashTable_lock - synchronizes conn creation, rx_connHashTable access
+ *                         also protects updates to rx_nextCid
  * conn_call_lock - used to synchonize rx_EndCall and rx_NewCall
  * call->lock - locks call data fields.
  * These are independent of each other:
@@ -437,45 +438,16 @@ static int rxdb_fileID = RXDB_FILE_RX;
 #define CLEAR_CALL_QUEUE_LOCK(C)
 #endif /* RX_ENABLE_LOCKS */
 struct rx_serverQueueEntry *rx_waitForPacket = 0;
-struct rx_serverQueueEntry *rx_waitingForPacket = 0;
 
 /* ------------Exported Interfaces------------- */
 
-/* This function allows rxkad to set the epoch to a suitably random number
- * which rx_NewConnection will use in the future.  The principle purpose is to
- * get rxnull connections to use the same epoch as the rxkad connections do, at
- * least once the first rxkad connection is established.  This is important now
- * that the host/port addresses aren't used in FindConnection: the uniqueness
- * of epoch/cid matters and the start time won't do. */
-
-#ifdef AFS_PTHREAD_ENV
-/*
- * This mutex protects the following global variables:
- * rx_epoch
- */
-
-#define LOCK_EPOCH MUTEX_ENTER(&epoch_mutex)
-#define UNLOCK_EPOCH MUTEX_EXIT(&epoch_mutex)
-#else
-#define LOCK_EPOCH
-#define UNLOCK_EPOCH
-#endif /* AFS_PTHREAD_ENV */
-
-void
-rx_SetEpoch(afs_uint32 epoch)
-{
-    LOCK_EPOCH;
-    rx_epoch = epoch;
-    UNLOCK_EPOCH;
-}
-
 /* Initialize rx.  A port number may be mentioned, in which case this
  * becomes the default port number for any service installed later.
  * If 0 is provided for the port number, a random port will be chosen
  * by the kernel.  Whether this will ever overlap anything in
  * /etc/services is anybody's guess...  Returns 0 on success, -1 on
  * error. */
-#ifndef AFS_NT40_ENV
+#if !(defined(AFS_NT40_ENV) || defined(RXK_UPCALL_ENV))
 static
 #endif
 rx_atomic_t rxinit_status = RX_ATOMIC_INIT(1);
@@ -609,12 +581,12 @@ rx_InitHost(u_int host, u_int port)
 #endif
     }
     rx_stats.minRtt.sec = 9999999;
-#ifdef KERNEL
-    rx_SetEpoch(tv.tv_sec | 0x80000000);
-#else
-    rx_SetEpoch(tv.tv_sec);    /* Start time of this package, rxkad
-                                * will provide a randomer value. */
-#endif
+    if (RAND_bytes(&rx_epoch, sizeof(rx_epoch)) != 1)
+       return -1;
+    rx_epoch  = (rx_epoch & ~0x40000000) | 0x80000000;
+    if (RAND_bytes(&rx_nextCid, sizeof(rx_nextCid)) != 1)
+       return -1;
+    rx_nextCid &= RX_CIDMASK;
     MUTEX_ENTER(&rx_quota_mutex);
     rxi_dataQuota += rx_extraQuota; /* + extra pkts caller asked to rsrv */
     MUTEX_EXIT(&rx_quota_mutex);
@@ -711,8 +683,10 @@ rxi_rto_startTimer(struct rx_call *call, int lastPacket, int istack)
 static_inline void
 rxi_rto_cancel(struct rx_call *call)
 {
-    rxevent_Cancel(&call->resendEvent);
-    CALL_RELE(call, RX_CALL_REFCOUNT_RESEND);
+    if (call->resendEvent != NULL) {
+       rxevent_Cancel(&call->resendEvent);
+       CALL_RELE(call, RX_CALL_REFCOUNT_RESEND);
+    }
 }
 
 /*!
@@ -786,20 +760,6 @@ rx_rto_setPeerTimeoutSecs(struct rx_peer *peer, int secs) {
 }
 
 /**
- * Enables or disables the busy call channel error (RX_CALL_BUSY).
- *
- * @param[in] onoff Non-zero to enable busy call channel errors.
- *
- * @pre Neither rx_Init nor rx_InitHost have been called yet
- */
-void
-rx_SetBusyChannelError(afs_int32 onoff)
-{
-    osi_Assert(rx_atomic_test_bit(&rxinit_status, 0));
-    rxi_busyChannelError = onoff ? 1 : 0;
-}
-
-/**
  * Set a delayed ack event on the specified call for the given time
  *
  * @param[in] call - the call on which to set the event
@@ -1041,7 +1001,6 @@ rx_NewConnection(afs_uint32 shost, u_short sport, u_short sservice,
                 int serviceSecurityIndex)
 {
     int hashindex, i;
-    afs_int32 cid;
     struct rx_connection *conn;
 
     SPLVAR;
@@ -1062,10 +1021,10 @@ rx_NewConnection(afs_uint32 shost, u_short sport, u_short sservice,
 #endif
     NETPRI;
     MUTEX_ENTER(&rx_connHashTable_lock);
-    cid = (rx_nextCid += RX_MAXCALLS);
     conn->type = RX_CLIENT_CONNECTION;
-    conn->cid = cid;
     conn->epoch = rx_epoch;
+    conn->cid = rx_nextCid;
+    update_nextCid();
     conn->peer = rxi_FindPeer(shost, sport, 1);
     conn->serviceId = sservice;
     conn->securityObject = securityObject;
@@ -1154,7 +1113,6 @@ void
 rx_SetConnIdleDeadTime(struct rx_connection *conn, int seconds)
 {
     conn->idleDeadTime = seconds;
-    conn->idleDeadDetection = (seconds ? 1 : 0);
     rxi_CheckConnTimeouts(conn);
 }
 
@@ -1589,7 +1547,6 @@ rx_NewCall(struct rx_connection *conn)
        }
        if (i < RX_MAXCALLS) {
            conn->lastBusy[i] = 0;
-           call->flags &= ~RX_CALL_PEER_BUSY;
            break;
        }
         if (!wait)
@@ -1820,7 +1777,6 @@ rx_NewServiceHost(afs_uint32 host, u_short port, u_short serviceId,
            service->minProcs = 0;
            service->maxProcs = 1;
            service->idleDeadTime = 60;
-           service->idleDeadErr = 0;
            service->connDeadTime = rx_connDeadTime;
            service->executeRequestProc = serviceProc;
            service->checkReach = 0;
@@ -2128,8 +2084,6 @@ rx_GetCall(int tno, struct rx_service *cur_service, osi_socket * socketp)
            opr_queue_Append(&rx_idleServerQueue, &sq->entry);
 #ifndef AFS_AIX41_ENV
            rx_waitForPacket = sq;
-#else
-           rx_waitingForPacket = sq;
 #endif /* AFS_AIX41_ENV */
            do {
                CV_WAIT(&sq->cv, &rx_serverPool_lock);
@@ -2457,8 +2411,18 @@ rx_EndCall(struct rx_call *call, afs_int32 rc)
         MUTEX_ENTER(&conn->conn_call_lock);
         MUTEX_ENTER(&call->lock);
 
-       if (!(call->flags & RX_CALL_PEER_BUSY)) {
+       if (!call->error) {
+           /* While there are some circumstances where a call with an error is
+            * obviously not on a "busy" channel, be conservative (clearing
+            * lastBusy is just best-effort to possibly speed up rx_NewCall).
+            * The call channel is definitely not busy if we just successfully
+            * completed a call on it. */
            conn->lastBusy[call->channel] = 0;
+
+       } else if (call->error == RX_CALL_TIMEOUT) {
+           /* The call is still probably running on the server side, so try to
+            * avoid this call channel in the future. */
+           conn->lastBusy[call->channel] = clock_Sec();
        }
 
        MUTEX_ENTER(&conn->conn_data_lock);
@@ -2513,6 +2477,10 @@ rx_EndCall(struct rx_call *call, afs_int32 rc)
      * Map errors to the local host's errno.h format.
      */
     error = ntoh_syserr_conv(error);
+
+    /* If the caller said the call failed with some error, we had better
+     * return an error code. */
+    osi_Assert(!rc || error);
     return error;
 }
 
@@ -2903,7 +2871,7 @@ rxi_SetPeerMtu(struct rx_peer *peer, afs_uint32 host, afs_uint32 port, int mtu)
        if (peer->ifMTU < OLD_MAX_PACKET_SIZE)
            peer->maxDgramPackets = 1;
        /* We no longer have valid peer packet information */
-       if (peer->maxPacketSize-RX_IPUDP_SIZE > peer->ifMTU)
+       if (peer->maxPacketSize + RX_HEADER_SIZE > peer->ifMTU)
            peer->maxPacketSize = 0;
         MUTEX_EXIT(&peer->peer_lock);
 
@@ -3174,8 +3142,7 @@ rxi_FindConnection(osi_socket socket, afs_uint32 host,
        conn->nSpecific = 0;
        conn->specific = NULL;
        rx_SetConnDeadTime(conn, service->connDeadTime);
-       conn->idleDeadTime = service->idleDeadTime;
-       conn->idleDeadDetection = service->idleDeadErr ? 1 : 0;
+       rx_SetConnIdleDeadTime(conn, service->idleDeadTime);
        for (i = 0; i < RX_MAXCALLS; i++) {
            conn->twind[i] = rx_initSendWindow;
            conn->rwind[i] = rx_initReceiveWindow;
@@ -3198,84 +3165,6 @@ rxi_FindConnection(osi_socket socket, afs_uint32 host,
     return conn;
 }
 
-/**
- * Timeout a call on a busy call channel if appropriate.
- *
- * @param[in] call The busy call.
- *
- * @pre 'call' is marked as busy (namely,
- *      call->conn->lastBusy[call->channel] != 0)
- *
- * @pre call->lock is held
- * @pre rxi_busyChannelError is nonzero
- *
- * @note call->lock is dropped and reacquired
- */
-static void
-rxi_CheckBusy(struct rx_call *call)
-{
-    struct rx_connection *conn = call->conn;
-    int channel = call->channel;
-    int freechannel = 0;
-    int i;
-
-    MUTEX_EXIT(&call->lock);
-
-    MUTEX_ENTER(&conn->conn_call_lock);
-
-    /* Are there any other call slots on this conn that we should try? Look for
-     * slots that are empty and are either non-busy, or were marked as busy
-     * longer than conn->secondsUntilDead seconds before this call started. */
-
-    for (i = 0; i < RX_MAXCALLS && !freechannel; i++) {
-       if (i == channel) {
-           /* only look at channels that aren't us */
-           continue;
-       }
-
-       if (conn->lastBusy[i]) {
-           /* if this channel looked busy too recently, don't look at it */
-           if (conn->lastBusy[i] >= call->startTime.sec) {
-               continue;
-           }
-           if (call->startTime.sec - conn->lastBusy[i] < conn->secondsUntilDead) {
-               continue;
-           }
-       }
-
-       if (conn->call[i]) {
-           struct rx_call *tcall = conn->call[i];
-           MUTEX_ENTER(&tcall->lock);
-           if (tcall->state == RX_STATE_DALLY) {
-               freechannel = 1;
-           }
-           MUTEX_EXIT(&tcall->lock);
-       } else {
-           freechannel = 1;
-       }
-    }
-
-    MUTEX_ENTER(&call->lock);
-
-    /* Since the call->lock has been released it is possible that the call may
-     * no longer be busy (the call channel cannot have been reallocated as we
-     * haven't dropped the conn_call_lock) Therefore, we must confirm
-     * that the call state has not changed when deciding whether or not to
-     * force this application thread to retry by forcing a Timeout error. */
-
-    if (freechannel && (call->flags & RX_CALL_PEER_BUSY)) {
-       /* Since 'freechannel' is set, there exists another channel in this
-        * rx_conn that the application thread might be able to use. We know
-        * that we have the correct call since callNumber is unchanged, and we
-        * know that the call is still busy. So, set the call error state to
-        * rxi_busyChannelError so the application can retry the request,
-        * presumably on a less-busy call channel. */
-
-       rxi_CallError(call, RX_CALL_BUSY);
-    }
-    MUTEX_EXIT(&conn->conn_call_lock);
-}
-
 /*!
  * Abort the call if the server is over the busy threshold. This
  * can be used without requiring a call structure be initialised,
@@ -3306,6 +3195,9 @@ rxi_ReceiveClientCall(struct rx_packet *np, struct rx_connection *conn)
     channel = np->header.cid & RX_CHANNELMASK;
     MUTEX_ENTER(&conn->conn_call_lock);
     call = conn->call[channel];
+    if (np->header.type == RX_PACKET_TYPE_BUSY) {
+       conn->lastBusy[channel] = clock_Sec();
+    }
     if (!call || conn->callNumber[channel] != np->header.callNumber) {
        MUTEX_EXIT(&conn->conn_call_lock);
        if (rx_stats_active)
@@ -3509,6 +3401,7 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        addr.sin_family = AF_INET;
        addr.sin_port = port;
        addr.sin_addr.s_addr = host;
+       memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
        addr.sin_len = sizeof(addr);
 #endif /* AFS_OSF_ENV */
@@ -3652,23 +3545,13 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        putConnection(conn);
        return np;              /* xmitting; drop packet */
     }
-    case RX_PACKET_TYPE_BUSY: {
-       struct clock busyTime;
-       clock_NewTime();
-       clock_GetTime(&busyTime);
-
-       MUTEX_EXIT(&call->lock);
-
-       MUTEX_ENTER(&conn->conn_call_lock);
-       MUTEX_ENTER(&call->lock);
-       conn->lastBusy[call->channel] = busyTime.sec;
-       call->flags |= RX_CALL_PEER_BUSY;
-       MUTEX_EXIT(&call->lock);
-       MUTEX_EXIT(&conn->conn_call_lock);
-
-       putConnection(conn);
-       return np;
-    }
+    case RX_PACKET_TYPE_BUSY:
+       /* Mostly ignore BUSY packets. We will update lastReceiveTime below,
+        * so we don't think the endpoint is completely dead, but otherwise
+        * just act as if we never saw anything. If all we get are BUSY packets
+        * back, then we will eventually error out with RX_CALL_TIMEOUT if the
+        * connection is configured with idle/hard timeouts. */
+       break;
 
     case RX_PACKET_TYPE_ACKALL:
        /* All packets acknowledged, so we can drop all packets previously
@@ -3687,8 +3570,6 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
      * the packet will be delivered to the user before any get time is required
      * (if not, then the time won't actually be re-evaluated here). */
     call->lastReceiveTime = clock_Sec();
-    /* we've received a legit packet, so the channel is not busy */
-    call->flags &= ~RX_CALL_PEER_BUSY;
     MUTEX_EXIT(&call->lock);
     putConnection(conn);
     return np;
@@ -4410,12 +4291,12 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
         * but we are clearly receiving.
         */
        if (!peer->maxPacketSize)
-           peer->maxPacketSize = RX_MIN_PACKET_SIZE+RX_IPUDP_SIZE;
+           peer->maxPacketSize = RX_MIN_PACKET_SIZE - RX_HEADER_SIZE;
 
        if (pktsize > peer->maxPacketSize) {
            peer->maxPacketSize = pktsize;
-           if ((pktsize-RX_IPUDP_SIZE > peer->ifMTU)) {
-               peer->ifMTU=pktsize-RX_IPUDP_SIZE;
+           if ((pktsize + RX_HEADER_SIZE > peer->ifMTU)) {
+               peer->ifMTU = pktsize + RX_HEADER_SIZE;
                peer->natMTU = rxi_AdjustIfMTU(peer->ifMTU);
                rxi_ScheduleGrowMTUEvent(call, 1);
            }
@@ -4781,6 +4662,30 @@ rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
     return np;
 }
 
+/**
+ * Schedule a connection abort to be sent after some delay.
+ *
+ * @param[in] conn The connection to send the abort on.
+ * @param[in] msec The number of milliseconds to wait before sending.
+ *
+ * @pre conn_data_lock must be held
+ */
+static void
+rxi_SendConnectionAbortLater(struct rx_connection *conn, int msec)
+{
+    struct clock when, now;
+    if (!conn->error) {
+       return;
+    }
+    if (!conn->delayedAbortEvent) {
+       clock_GetTime(&now);
+       when = now;
+       clock_Addmsec(&when, msec);
+       conn->delayedAbortEvent =
+           rxevent_Post(&when, &now, rxi_SendDelayedConnAbort, conn, NULL, 0);
+    }
+}
+
 /* Received a response to a challenge packet */
 static struct rx_packet *
 rxi_ReceiveResponsePacket(struct rx_connection *conn,
@@ -4796,17 +4701,26 @@ rxi_ReceiveResponsePacket(struct rx_connection *conn,
     if (RXS_CheckAuthentication(conn->securityObject, conn) == 0)
        return np;
 
+    if (!conn->securityChallengeSent) {
+       /* We've never sent out a challenge for this connection, so this
+        * response cannot possibly be correct; ignore it. This can happen
+        * if we sent a challenge to the client, then we were restarted, and
+        * then the client sent us a response. If we ignore the response, the
+        * client will eventually resend a data packet, causing us to send a
+        * new challenge and the client to send a new response. */
+       return np;
+    }
+
     /* Otherwise, have the security object evaluate the response packet */
     error = RXS_CheckResponse(conn->securityObject, conn, np);
     if (error) {
        /* If the response is invalid, reset the connection, sending
-        * an abort to the peer */
-#ifndef KERNEL
-       rxi_Delay(1);
-#endif
+        * an abort to the peer. Send the abort with a 1 second delay,
+        * to avoid a peer hammering us by constantly recreating a
+        * connection with bad credentials. */
        rxi_ConnectionError(conn, error);
        MUTEX_ENTER(&conn->conn_data_lock);
-       np = rxi_SendConnectionAbort(conn, np, istack, 0);
+       rxi_SendConnectionAbortLater(conn, 1000);
        MUTEX_EXIT(&conn->conn_data_lock);
        return np;
     } else {
@@ -5134,34 +5048,25 @@ static struct rx_packet *
 rxi_SendCallAbort(struct rx_call *call, struct rx_packet *packet,
                  int istack, int force)
 {
-    afs_int32 error, cerror;
+    afs_int32 error;
     struct clock when, now;
 
     if (!call->error)
        return packet;
 
-    switch (call->error) {
-    case RX_CALL_IDLE:
-    case RX_CALL_BUSY:
-        cerror = RX_CALL_TIMEOUT;
-        break;
-    default:
-        cerror = call->error;
-    }
-
     /* Clients should never delay abort messages */
     if (rx_IsClientConn(call->conn))
        force = 1;
 
-    if (call->abortCode != cerror) {
-       call->abortCode = cerror;
+    if (call->abortCode != call->error) {
+       call->abortCode = call->error;
        call->abortCount = 0;
     }
 
     if (force || rxi_callAbortThreshhold == 0
        || call->abortCount < rxi_callAbortThreshhold) {
        rxi_CancelDelayedAbortEvent(call);
-       error = htonl(cerror);
+       error = htonl(call->error);
        call->abortCount++;
        packet =
            rxi_SendSpecial(call, call->conn, packet, RX_PACKET_TYPE_ABORT,
@@ -5200,7 +5105,6 @@ rxi_SendConnectionAbort(struct rx_connection *conn,
                        struct rx_packet *packet, int istack, int force)
 {
     afs_int32 error;
-    struct clock when, now;
 
     if (!conn->error)
        return packet;
@@ -5221,12 +5125,8 @@ rxi_SendConnectionAbort(struct rx_connection *conn,
                            RX_PACKET_TYPE_ABORT, (char *)&error,
                            sizeof(error), istack);
        MUTEX_ENTER(&conn->conn_data_lock);
-    } else if (!conn->delayedAbortEvent) {
-       clock_GetTime(&now);
-       when = now;
-       clock_Addmsec(&when, rxi_connAbortDelay);
-       conn->delayedAbortEvent =
-           rxevent_Post(&when, &now, rxi_SendDelayedConnAbort, conn, NULL, 0);
+    } else {
+       rxi_SendConnectionAbortLater(conn, rxi_connAbortDelay);
     }
     return packet;
 }
@@ -5381,16 +5281,6 @@ rxi_ResetCall(struct rx_call *call, int newcall)
     }
     call->flags = 0;
 
-    if (!newcall && (flags & RX_CALL_PEER_BUSY)) {
-       /* The call channel is still busy; resetting the call doesn't change
-        * that. However, if 'newcall' is set, we are processing a call
-        * structure that has either been recycled from the free list, or has
-        * been newly allocated. So, RX_CALL_PEER_BUSY is not relevant if
-        * 'newcall' is set, since it describes a completely different call
-        * channel which we do not care about. */
-       call->flags |= RX_CALL_PEER_BUSY;
-    }
-
     rxi_ClearReceiveQueue(call);
     /* why init the queue if you just emptied it? queue_Init(&call->rq); */
 
@@ -5486,6 +5376,9 @@ rxi_ResetCall(struct rx_call *call, int newcall)
     int        reason;                  Reason an acknowledge was prompted
 */
 
+#define RX_ZEROS 1024
+static char rx_zeros[RX_ZEROS];
+
 struct rx_packet *
 rxi_SendAck(struct rx_call *call,
            struct rx_packet *optionalPacket, int serial, int reason,
@@ -5516,7 +5409,7 @@ rxi_SendAck(struct rx_call *call,
         */
        if (call->conn->peer->maxPacketSize &&
            (call->conn->peer->maxPacketSize < OLD_MAX_PACKET_SIZE
-            +RX_IPUDP_SIZE))
+            - RX_HEADER_SIZE))
            padbytes = call->conn->peer->maxPacketSize+16;
        else
            padbytes = call->conn->peer->maxMTU + 128;
@@ -5645,6 +5538,11 @@ rxi_SendAck(struct rx_call *call,
     ap->nAcks = offset;
     p->length = rx_AckDataSize(offset) + 4 * sizeof(afs_int32);
 
+    /* Must zero the 3 octets that rx_AckDataSize skips at the end of the
+     * ACK list.
+     */
+    rx_packetwrite(p, rx_AckDataSize(offset) - 3, 3, rx_zeros);
+
     /* these are new for AFS 3.3 */
     templ = rxi_AdjustMaxMTU(call->conn->peer->ifMTU, rx_maxReceiveSize);
     templ = htonl(templ);
@@ -5663,6 +5561,8 @@ rxi_SendAck(struct rx_call *call,
     rx_packetwrite(p, rx_AckDataSize(offset) + 3 * sizeof(afs_int32),
                   sizeof(afs_int32), &templ);
 
+    p->length = rx_AckDataSize(offset) + 4 * sizeof(afs_int32);
+
     p->header.serviceId = call->conn->serviceId;
     p->header.cid = (call->conn->cid | call->channel);
     p->header.callNumber = *call->callNumber;
@@ -5671,21 +5571,21 @@ rxi_SendAck(struct rx_call *call,
     p->header.epoch = call->conn->epoch;
     p->header.type = RX_PACKET_TYPE_ACK;
     p->header.flags = RX_SLOW_START_OK;
-    if (reason == RX_ACK_PING) {
+    if (reason == RX_ACK_PING)
        p->header.flags |= RX_REQUEST_ACK;
-       if (padbytes) {
-           p->length = padbytes +
-               rx_AckDataSize(call->rwind) + 4 * sizeof(afs_int32);
 
-           while (padbytes--)
-               /* not fast but we can potentially use this if truncated
-                * fragments are delivered to figure out the mtu.
-                */
-               rx_packetwrite(p, rx_AckDataSize(offset) + 4 *
-                              sizeof(afs_int32), sizeof(afs_int32),
-                              &padbytes);
+    while (padbytes > 0) {
+       if (padbytes > RX_ZEROS) {
+           rx_packetwrite(p, p->length, RX_ZEROS, rx_zeros);
+           p->length += RX_ZEROS;
+           padbytes -= RX_ZEROS;
+       } else {
+           rx_packetwrite(p, p->length, padbytes, rx_zeros);
+           p->length += padbytes;
+           padbytes = 0;
        }
     }
+
     if (call->conn->type == RX_CLIENT_CONNECTION)
        p->header.flags |= RX_CLIENT_INITIATED;
 
@@ -5846,9 +5746,6 @@ rxi_SendList(struct rx_call *call, struct xmitlist *xmit,
      * processing), and for the connection (so that we can discover
      * idle connections) */
     conn->lastSendTime = call->lastSendTime = clock_Sec();
-    /* Let a set of retransmits trigger an idle timeout */
-    if (!xmit->resending)
-       call->lastSendData = call->lastSendTime;
 }
 
 /* When sending packets we need to follow these rules:
@@ -6035,10 +5932,6 @@ rxi_Resend(struct rxevent *event, void *arg0, void *arg1, int istack)
 
     rxi_CheckPeerDead(call);
 
-    if (rxi_busyChannelError && (call->flags & RX_CALL_PEER_BUSY)) {
-       rxi_CheckBusy(call);
-    }
-
     if (opr_queue_IsEmpty(&call->tq)) {
        /* Nothing to do. This means that we've been raced, and that an
         * ACK has come in between when we were triggered, and when we
@@ -6284,12 +6177,6 @@ rxi_Send(struct rx_call *call, struct rx_packet *p,
        (p->length <= (rx_AckDataSize(call->rwind) + 4 * sizeof(afs_int32))))
     {
        conn->lastSendTime = call->lastSendTime = clock_Sec();
-       /* Don't count keepalive ping/acks here, so idleness can be tracked. */
-       if ((p->header.type != RX_PACKET_TYPE_ACK) ||
-           ((((struct rx_ackPacket *)rx_DataOf(p))->reason != RX_ACK_PING) &&
-            (((struct rx_ackPacket *)rx_DataOf(p))->reason !=
-             RX_ACK_PING_RESPONSE)))
-           call->lastSendData = call->lastSendTime;
     }
 }
 
@@ -6358,33 +6245,6 @@ rxi_CheckCall(struct rx_call *call, int haveCTLock)
      * number of seconds. */
     if (now > (call->lastReceiveTime + deadTime)) {
        if (call->state == RX_STATE_ACTIVE) {
-#ifdef AFS_ADAPT_PMTU
-# if defined(KERNEL) && defined(AFS_SUN5_ENV)
-           ire_t *ire;
-#  if defined(AFS_SUN510_ENV) && defined(GLOBAL_NETSTACKID)
-           netstack_t *ns = netstack_find_by_stackid(GLOBAL_NETSTACKID);
-           ip_stack_t *ipst = ns->netstack_ip;
-#  endif
-           ire = ire_cache_lookup(conn->peer->host
-#  if defined(AFS_SUN510_ENV) && defined(ALL_ZONES)
-                                  , ALL_ZONES
-#    if defined(ICL_3_ARG) || defined(GLOBAL_NETSTACKID)
-                                  , NULL
-#     if defined(GLOBAL_NETSTACKID)
-                                  , ipst
-#     endif
-#    endif
-#  endif
-               );
-
-           if (ire && ire->ire_max_frag > 0)
-               rxi_SetPeerMtu(NULL, conn->peer->host, 0,
-                              ire->ire_max_frag);
-#  if defined(GLOBAL_NETSTACKID)
-           netstack_rele(ns);
-#  endif
-# endif
-#endif /* AFS_ADAPT_PMTU */
            cerror = RX_CALL_DEAD;
            goto mtuout;
        } else {
@@ -6414,29 +6274,18 @@ rxi_CheckCall(struct rx_call *call, int haveCTLock)
         * attached process can die reasonably gracefully. */
     }
 
-    if (conn->idleDeadDetection) {
-        if (conn->idleDeadTime) {
-            idleDeadTime = conn->idleDeadTime + fudgeFactor;
-        }
-
-        if (idleDeadTime) {
-            /* see if we have a non-activity timeout */
-            if (call->startWait && ((call->startWait + idleDeadTime) < now) &&
-                (call->flags & RX_CALL_READER_WAIT)) {
-                if (call->state == RX_STATE_ACTIVE) {
-                    cerror = RX_CALL_TIMEOUT;
-                    goto mtuout;
-                }
-            }
+    if (conn->idleDeadTime) {
+       idleDeadTime = conn->idleDeadTime + fudgeFactor;
+    }
 
-            if (call->lastSendData && ((call->lastSendData + idleDeadTime) < now)) {
-                if (call->state == RX_STATE_ACTIVE) {
-                    cerror = conn->service ? conn->service->idleDeadErr : RX_CALL_IDLE;
-                    idle_timeout = 1;
-                    goto mtuout;
-                }
-            }
-        }
+    if (idleDeadTime) {
+       /* see if we have a non-activity timeout */
+       if (call->startWait && ((call->startWait + idleDeadTime) < now)) {
+           if (call->state == RX_STATE_ACTIVE) {
+               cerror = RX_CALL_TIMEOUT;
+               goto mtuout;
+           }
+       }
     }
 
     if (conn->hardDeadTime) {
@@ -6456,19 +6305,20 @@ mtuout:
         call->lastReceiveTime) {
        int oldMTU = conn->peer->ifMTU;
 
-       /* if we thought we could send more, perhaps things got worse */
-       if (conn->peer->maxPacketSize > conn->lastPacketSize)
-           /* maxpacketsize will be cleared in rxi_SetPeerMtu */
-           newmtu = MAX(conn->peer->maxPacketSize-RX_IPUDP_SIZE,
-                        conn->lastPacketSize-(128+RX_IPUDP_SIZE));
+       /* If we thought we could send more, perhaps things got worse.
+        * Shrink by 128 bytes and try again. */
+       if (conn->peer->maxPacketSize < conn->lastPacketSize)
+           /* maxPacketSize will be cleared in rxi_SetPeerMtu */
+           newmtu = MAX(conn->peer->maxPacketSize + RX_HEADER_SIZE,
+                        conn->lastPacketSize - 128 + RX_HEADER_SIZE);
        else
-           newmtu = conn->lastPacketSize-(128+RX_IPUDP_SIZE);
+           newmtu = conn->lastPacketSize - 128 + RX_HEADER_SIZE;
 
        /* minimum capped in SetPeerMtu */
        rxi_SetPeerMtu(conn->peer, 0, 0, newmtu);
 
        /* clean up */
-       conn->lastPacketSize = 0;
+       conn->lastPacketSize = conn->lastPacketSizeSeq = 0;
 
        /* needed so ResetCall doesn't clobber us. */
        call->MTU = conn->peer->ifMTU;
@@ -6502,6 +6352,7 @@ rxi_NatKeepAliveEvent(struct rxevent *event, void *arg1,
     taddr.sin_family = AF_INET;
     taddr.sin_port = rx_PortOf(rx_PeerOf(conn));
     taddr.sin_addr.s_addr = rx_HostOf(rx_PeerOf(conn));
+    memset(&taddr.sin_zero, 0, sizeof(taddr.sin_zero));
 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
     taddr.sin_len = sizeof(struct sockaddr_in);
 #endif
@@ -6645,7 +6496,7 @@ rxi_GrowMTUEvent(struct rxevent *event, void *arg1, void *dummy, int dummy2)
      */
     if ((conn->peer->maxPacketSize != 0) &&
        (conn->peer->natMTU < RX_MAX_PACKET_SIZE) &&
-       conn->idleDeadDetection)
+       conn->idleDeadTime)
        (void)rxi_SendAck(call, NULL, 0, RX_ACK_MTU, 0);
     rxi_ScheduleGrowMTUEvent(call, 0);
     MUTEX_EXIT(&call->lock);
@@ -6705,6 +6556,19 @@ rxi_CancelGrowMTUEvent(struct rx_call *call)
     }
 }
 
+/*
+ * Increment the counter for the next connection ID, handling overflow.
+ */
+static void
+update_nextCid(void)
+{
+    /* Overflow is technically undefined behavior; avoid it. */
+    if (rx_nextCid > MAX_AFS_INT32 - (1 << RX_CIDSHIFT))
+       rx_nextCid = -1 * ((MAX_AFS_INT32 / RX_CIDSHIFT) * RX_CIDSHIFT);
+    else
+       rx_nextCid += 1 << RX_CIDSHIFT;
+}
+
 static void
 rxi_KeepAliveOn(struct rx_call *call)
 {
@@ -6717,22 +6581,6 @@ rxi_KeepAliveOn(struct rx_call *call)
     rxi_ScheduleKeepAliveEvent(call);
 }
 
-void
-rx_KeepAliveOff(struct rx_call *call)
-{
-    MUTEX_ENTER(&call->lock);
-    rxi_CancelKeepAliveEvent(call);
-    MUTEX_EXIT(&call->lock);
-}
-
-void
-rx_KeepAliveOn(struct rx_call *call)
-{
-    MUTEX_ENTER(&call->lock);
-    rxi_KeepAliveOn(call);
-    MUTEX_EXIT(&call->lock);
-}
-
 static void
 rxi_GrowMTUOn(struct rx_call *call)
 {
@@ -6849,6 +6697,7 @@ rxi_ChallengeEvent(struct rxevent *event,
            rxi_SendSpecial((struct rx_call *)0, conn, packet,
                            RX_PACKET_TYPE_CHALLENGE, NULL, -1, 0);
            rxi_FreePacket(packet);
+           conn->securityChallengeSent = 1;
        }
        clock_GetTime(&now);
        when = now;
@@ -7499,6 +7348,7 @@ MakeDebugCall(osi_socket socket, afs_uint32 remoteAddr, afs_uint16 remotePort,
     taddr.sin_family = AF_INET;
     taddr.sin_port = remotePort;
     taddr.sin_addr.s_addr = remoteAddr;
+    memset(&taddr.sin_zero, 0, sizeof(taddr.sin_zero));
 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
     taddr.sin_len = sizeof(struct sockaddr_in);
 #endif
@@ -7929,7 +7779,7 @@ shutdown_rx(void)
     rxi_StopListener();
 #endif /* AFS_PTHREAD_ENV */
     shutdown_rxevent();
-    rx_SetEpoch(0);
+    rx_epoch = 0;
 #ifndef AFS_PTHREAD_ENV
 #ifndef AFS_USE_GETTIMEOFDAY
     clock_UnInit();
@@ -9336,7 +9186,7 @@ int rx_DumpCalls(FILE *outputFile, char *cookie)
                 "rqc=%u,%u, tqc=%u,%u, iovqc=%u,%u, "
                 "lstatus=%u, rstatus=%u, error=%d, timeout=%u, "
                 "resendEvent=%d, keepAliveEvt=%d, delayedAckEvt=%d, delayedAbortEvt=%d, abortCode=%d, abortCount=%d, "
-                "lastSendTime=%u, lastRecvTime=%u, lastSendData=%u"
+                "lastSendTime=%u, lastRecvTime=%u"
 #ifdef RX_ENABLE_LOCKS
                 ", refCount=%u"
 #endif
@@ -9350,7 +9200,7 @@ int rx_DumpCalls(FILE *outputFile, char *cookie)
                 (afs_uint32)c->rqc, (afs_uint32)rqc, (afs_uint32)c->tqc, (afs_uint32)tqc, (afs_uint32)c->iovqc, (afs_uint32)iovqc,
                 (afs_uint32)c->localStatus, (afs_uint32)c->remoteStatus, c->error, c->timeout,
                 c->resendEvent?1:0, c->keepAliveEvent?1:0, c->delayedAckEvent?1:0, c->delayedAbortEvent?1:0,
-                c->abortCode, c->abortCount, c->lastSendTime, c->lastReceiveTime, c->lastSendData
+                c->abortCode, c->abortCount, c->lastSendTime, c->lastReceiveTime
 #ifdef RX_ENABLE_LOCKS
                 , (afs_uint32)c->refCount
 #endif