Always verify that the client is fully reachable before talking
[openafs.git] / src / rx / rx.c
index c74472d..c64bb10 100644 (file)
@@ -897,7 +897,12 @@ static void rxi_DestroyConnectionNoLock(conn)
                     * last reply packets */
                    rxevent_Cancel(call->delayedAckEvent, call,
                                   RX_CALL_REFCOUNT_DELAY);
-                   rxi_AckAll((struct rxevent *)0, call, 0);
+                   if (call->state == RX_STATE_PRECALL ||
+                       call->state == RX_STATE_ACTIVE) {
+                       rxi_SendAck(call, 0, 0, 0, 0, RX_ACK_DELAY, 0);
+                   } else {
+                       rxi_AckAll((struct rxevent *)0, call, 0);
+                   }
                }
                MUTEX_EXIT(&call->lock);
            }
@@ -952,9 +957,10 @@ static void rxi_DestroyConnectionNoLock(conn)
 
     /* Make sure the connection is completely reset before deleting it. */
     /* get rid of pending events that could zap us later */
-    if (conn->challengeEvent) {
+    if (conn->challengeEvent)
        rxevent_Cancel(conn->challengeEvent, (struct rx_call*)0, 0);
-    }
+    if (conn->checkReachEvent)
+       rxevent_Cancel(conn->checkReachEvent, (struct rx_call*)0, 0);
  
     /* Add the connection to the list of destroyed connections that
      * need to be cleaned up. This is necessary to avoid deadlocks
@@ -1001,6 +1007,21 @@ struct rx_call *rx_NewCall(conn)
     clock_GetTime(&queueTime);
     AFS_RXGLOCK();
     MUTEX_ENTER(&conn->conn_call_lock);
+
+    /*
+     * Check if there are others waiting for a new call.
+     * If so, let them go first to avoid starving them.
+     * This is a fairly simple scheme, and might not be
+     * a complete solution for large numbers of waiters.
+     */
+    if (conn->makeCallWaiters) {
+#ifdef RX_ENABLE_LOCKS
+       CV_WAIT(&conn->conn_call_cv, &conn->conn_call_lock);
+#else
+       osi_rxSleep(conn);
+#endif
+    }
+
     for (;;) {
        for (i=0; i<RX_MAXCALLS; i++) {
            call = conn->call[i];
@@ -1015,7 +1036,6 @@ struct rx_call *rx_NewCall(conn)
            }
            else {
                call = rxi_NewCall(conn, i);
-               MUTEX_ENTER(&call->lock);
                break;
            }
        }
@@ -1025,12 +1045,24 @@ struct rx_call *rx_NewCall(conn)
        MUTEX_ENTER(&conn->conn_data_lock);
        conn->flags |= RX_CONN_MAKECALL_WAITING;
        MUTEX_EXIT(&conn->conn_data_lock);
+
+       conn->makeCallWaiters++;
 #ifdef RX_ENABLE_LOCKS
        CV_WAIT(&conn->conn_call_cv, &conn->conn_call_lock);
 #else
        osi_rxSleep(conn);
 #endif
+       conn->makeCallWaiters--;
     }
+    /*
+     * Wake up anyone else who might be giving us a chance to
+     * run (see code above that avoids resource starvation).
+     */
+#ifdef RX_ENABLE_LOCKS
+    CV_BROADCAST(&conn->conn_call_cv);
+#else
+    osi_rxWakeup(conn);
+#endif
 
     CALL_HOLD(call, RX_CALL_REFCOUNT_BEGIN);
 
@@ -1216,6 +1248,7 @@ rx_NewService(port, serviceId, serviceName, securityObjects,
            service->idleDeadTime = 60;
            service->connDeadTime = rx_connDeadTime;
            service->executeRequestProc = serviceProc;
+           service->checkReach = 0;
            rx_services[i] = service;   /* not visible until now */
            AFS_RXGUNLOCK();
            USERPRI;
@@ -1918,7 +1951,7 @@ struct rx_service *rxi_FindService(socket, serviceId)
 
 /* Allocate a call structure, for the indicated channel of the
  * supplied connection.  The mode and state of the call must be set by
- * the caller. */
+ * the caller. Returns the call with mutex locked. */
 struct rx_call *rxi_NewCall(conn, channel)
     register struct rx_connection *conn;
     register int channel;
@@ -2000,7 +2033,6 @@ struct rx_call *rxi_NewCall(conn, channel)
        the call number is valid from the last time this channel was used */
     if (*call->callNumber == 0) *call->callNumber = 1;
 
-    MUTEX_EXIT(&call->lock);
     return call;
 }
 
@@ -2378,7 +2410,7 @@ struct rx_packet *rxi_ReceivePacket(np, socket, host, port, tnop, newcallp)
        addr.sin_family = AF_INET;
        addr.sin_port = port;
        addr.sin_addr.s_addr = host;
-#if  defined(AFS_OSF_ENV) && defined(_KERNEL)
+#ifdef STRUCT_SOCKADDR_HAS_SA_LEN 
         addr.sin_len = sizeof(addr);
 #endif  /* AFS_OSF_ENV */
        drop = (*rx_justReceived) (np, &addr);
@@ -2408,7 +2440,7 @@ struct rx_packet *rxi_ReceivePacket(np, socket, host, port, tnop, newcallp)
 
     MUTEX_ENTER(&conn->conn_data_lock);
     if (conn->maxSerial < np->header.serial)
-      conn->maxSerial = np->header.serial;
+       conn->maxSerial = np->header.serial;
     MUTEX_EXIT(&conn->conn_data_lock);
 
     /* If the connection is in an error state, send an abort packet and ignore
@@ -2525,8 +2557,9 @@ struct rx_packet *rxi_ReceivePacket(np, socket, host, port, tnop, newcallp)
            return np;
        }
        if (!call) {
+           MUTEX_ENTER(&conn->conn_call_lock);
            call = rxi_NewCall(conn, channel);
-           MUTEX_ENTER(&call->lock);
+           MUTEX_EXIT(&conn->conn_call_lock);
            *call->callNumber = np->header.callNumber;
            call->state = RX_STATE_PRECALL;
            clock_GetTime(&call->queueTime);
@@ -2836,20 +2869,98 @@ static TooLow(ap, acall)
 }
 #endif /* KERNEL */
 
+static void rxi_CheckReachEvent(event, conn, acall)
+    struct rxevent *event;
+    struct rx_connection *conn;
+    struct rx_call *acall;
+{
+    struct rx_call *call = acall;
+    struct clock when;
+    int i, waiting;
+
+    MUTEX_ENTER(&conn->conn_call_lock);
+    MUTEX_ENTER(&conn->conn_data_lock);
+    conn->checkReachEvent = (struct rxevent *) 0;
+    waiting = conn->flags & RX_CONN_ATTACHWAIT;
+    if (event) conn->refCount--;
+    MUTEX_EXIT(&conn->conn_data_lock);
+
+    if (waiting) {
+       if (!call)
+           for (i=0; i<RX_MAXCALLS; i++) {
+               struct rx_call *tc = conn->call[i];
+               if (tc && tc->state == RX_STATE_PRECALL) {
+                   call = tc;
+                   break;
+               }
+           }
+
+       if (call) {
+           if (call != acall) MUTEX_ENTER(&call->lock);
+           rxi_SendAck(call, NULL, 0, 0, 0, RX_ACK_PING, 0);
+           if (call != acall) MUTEX_EXIT(&call->lock);
+
+           MUTEX_ENTER(&conn->conn_data_lock);
+           conn->refCount++;
+           MUTEX_EXIT(&conn->conn_data_lock);
+           clock_GetTime(&when);
+           when.sec += RX_CHECKREACH_TIMEOUT;
+           conn->checkReachEvent =
+               rxevent_Post(&when, rxi_CheckReachEvent, conn, NULL);
+       }
+    }
+    MUTEX_EXIT(&conn->conn_call_lock);
+}
+
+static int rxi_CheckConnReach(conn, call)
+    struct rx_connection *conn;
+    struct rx_call *call;
+{
+    struct rx_service *service = conn->service;
+    struct rx_peer *peer = conn->peer;
+    afs_uint32 now, lastReach;
+
+    if (service->checkReach == 0)
+       return 0;
+
+    now = clock_Sec();
+    MUTEX_ENTER(&peer->peer_lock);
+    lastReach = peer->lastReachTime;
+    MUTEX_EXIT(&peer->peer_lock);
+    if (now - lastReach < RX_CHECKREACH_TTL)
+       return 0;
+
+    MUTEX_ENTER(&conn->conn_data_lock);
+    if (conn->flags & RX_CONN_ATTACHWAIT) {
+       MUTEX_EXIT(&conn->conn_data_lock);
+       return 1;
+    }
+    conn->flags |= RX_CONN_ATTACHWAIT;
+    MUTEX_EXIT(&conn->conn_data_lock);
+    if (!conn->checkReachEvent)
+       rxi_CheckReachEvent((struct rxevent *)0, conn, call);
+
+    return 1;
+}
+
 /* try to attach call, if authentication is complete */
-static void TryAttach(acall, socket, tnop, newcallp)
-register struct rx_call *acall;
-register osi_socket socket;
-register int *tnop;
-register struct rx_call **newcallp; {
-    register struct rx_connection *conn;
-    conn = acall->conn;
-    if ((conn->type==RX_SERVER_CONNECTION) && (acall->state == RX_STATE_PRECALL)) {
+static void TryAttach(acall, socket, tnop, newcallp, reachOverride)
+    register struct rx_call *acall;
+    register osi_socket socket;
+    register int *tnop;
+    register struct rx_call **newcallp;
+    int reachOverride;
+{
+    struct rx_connection *conn = acall->conn;
+
+    if (conn->type==RX_SERVER_CONNECTION && acall->state==RX_STATE_PRECALL) {
        /* Don't attach until we have any req'd. authentication. */
        if (RXS_CheckAuthentication(conn->securityObject, conn) == 0) {
-           rxi_AttachServerProc(acall, socket, tnop, newcallp);
-           /* Note:  this does not necessarily succeed; there
-              may not any proc available */
+           if (reachOverride || rxi_CheckConnReach(conn, acall) == 0)
+               rxi_AttachServerProc(acall, socket, tnop, newcallp);
+               /* Note:  this does not necessarily succeed; there
+                * may not any proc available
+                */
        }
        else {
            rxi_ChallengeOn(acall->conn);
@@ -3024,7 +3135,7 @@ struct rx_packet *rxi_ReceiveDataPacket(call, np, istack, socket, host,
             * server thread is available, this thread becomes a server
             * thread and the server thread becomes a listener thread. */
            if (isFirst) {
-               TryAttach(call, socket, tnop, newcallp);
+               TryAttach(call, socket, tnop, newcallp, 0);
            }
        }       
        /* This is not the expected next packet. */
@@ -3212,6 +3323,37 @@ nextloop:;
 static void rxi_ComputeRate();
 #endif
 
+static void rxi_UpdatePeerReach(conn, acall)
+    struct rx_connection *conn;
+    struct rx_call *acall;
+{
+    struct rx_peer *peer = conn->peer;
+
+    MUTEX_ENTER(&peer->peer_lock);
+    peer->lastReachTime = clock_Sec();
+    MUTEX_EXIT(&peer->peer_lock);
+
+    MUTEX_ENTER(&conn->conn_call_lock);
+    MUTEX_ENTER(&conn->conn_data_lock);
+    if (conn->flags & RX_CONN_ATTACHWAIT) {
+       int i;
+
+       conn->flags &= ~RX_CONN_ATTACHWAIT;
+       MUTEX_EXIT(&conn->conn_data_lock);
+
+       for (i=0; i<RX_MAXCALLS; i++) {
+           struct rx_call *call = conn->call[i];
+           if (call) {
+               if (call != acall) MUTEX_ENTER(&call->lock);
+               TryAttach(call, -1, NULL, NULL, 1);
+               if (call != acall) MUTEX_EXIT(&call->lock);
+           }
+       }
+    } else
+       MUTEX_EXIT(&conn->conn_data_lock);
+    MUTEX_EXIT(&conn->conn_call_lock);
+}
+
 /* The real smarts of the whole thing.  */
 struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
     register struct rx_call *call;
@@ -3259,6 +3401,9 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
     if (np->header.flags & RX_SLOW_START_OK) {
        call->flags |= RX_CALL_SLOW_START_OK;
     }
+
+    if (ap->reason == RX_ACK_PING_RESPONSE)
+       rxi_UpdatePeerReach(conn, call);
     
 #ifdef RXDEBUG
     if (rx_Log) {
@@ -3460,7 +3605,7 @@ struct rx_packet *rxi_ReceiveAckPacket(call, np, istack)
 
     /* if the ack packet has a receivelen field hanging off it,
      * update our state */
-    if ( np->length >= rx_AckDataSize(ap->nAcks) +sizeof(afs_int32)) {
+    if ( np->length >= rx_AckDataSize(ap->nAcks) + 2*sizeof(afs_int32)) {
       afs_uint32 tSize;
 
       /* If the ack packet has a "recommended" size that is less than 
@@ -3714,8 +3859,9 @@ struct rx_packet *rxi_ReceiveResponsePacket(conn, np, istack)
     }
     else {
        /* If the response is valid, any calls waiting to attach
-         * servers can now do so */
+        * servers can now do so */
        int i;
+
        for (i=0; i<RX_MAXCALLS; i++) {
            struct rx_call *call = conn->call[i];
            if (call) {
@@ -3725,6 +3871,12 @@ struct rx_packet *rxi_ReceiveResponsePacket(conn, np, istack)
                MUTEX_EXIT(&call->lock);
            }
        }
+
+       /* Update the peer reachability information, just in case
+        * some calls went into attach-wait while we were waiting
+        * for authentication..
+        */
+       rxi_UpdatePeerReach(conn, NULL);
     }
     return np;
 }
@@ -3775,10 +3927,10 @@ rxi_ReceiveChallengePacket(conn, np, istack)
  * call so it eventually gets one */
 void 
 rxi_AttachServerProc(call, socket, tnop, newcallp)
-register struct rx_call *call;
-register osi_socket socket;
-register int *tnop;
-register struct rx_call **newcallp;
+    register struct rx_call *call;
+    register osi_socket socket;
+    register int *tnop;
+    register struct rx_call **newcallp;
 {
     register struct rx_serverQueueEntry *sq;
     register struct rx_service *service = call->conn->service;
@@ -4130,8 +4282,15 @@ void rxi_ConnectionError(conn, error)
 {
     if (error) {
        register int i;
+       MUTEX_ENTER(&conn->conn_data_lock);
        if (conn->challengeEvent)
            rxevent_Cancel(conn->challengeEvent, (struct rx_call*)0, 0);
+       if (conn->checkReachEvent) {
+           rxevent_Cancel(conn->checkReachEvent, (struct rx_call*)0, 0);
+           conn->checkReachEvent = 0;
+           conn->refCount--;
+       }
+       MUTEX_EXIT(&conn->conn_data_lock);
        for (i=0; i<RX_MAXCALLS; i++) {
            struct rx_call *call = conn->call[i];
            if (call) {
@@ -5263,15 +5422,40 @@ void rxi_SendDelayedCallAbort(event, call, dummy)
  * seconds) to ask the client to authenticate itself.  The routine
  * issues a challenge to the client, which is obtained from the
  * security object associated with the connection */
-void rxi_ChallengeEvent(event, conn, dummy)
+void rxi_ChallengeEvent(event, conn, atries)
     struct rxevent *event;
     register struct rx_connection *conn;
-    char *dummy;
+    void *atries;
 {
+    int tries = (int) atries;
     conn->challengeEvent = (struct rxevent *) 0;
     if (RXS_CheckAuthentication(conn->securityObject, conn) != 0) {
        register struct rx_packet *packet;
        struct clock when;
+
+       if (tries <= 0) {
+           /* We've failed to authenticate for too long.
+            * Reset any calls waiting for authentication;
+            * they are all in RX_STATE_PRECALL.
+            */
+           int i;
+
+           MUTEX_ENTER(&conn->conn_call_lock);
+           for (i=0; i<RX_MAXCALLS; i++) {
+               struct rx_call *call = conn->call[i];
+               if (call) {
+                   MUTEX_ENTER(&call->lock);
+                   if (call->state == RX_STATE_PRECALL) {
+                       rxi_CallError(call, RX_CALL_DEAD);
+                       rxi_SendCallAbort(call, NULL, 0, 0);
+                   }
+                   MUTEX_EXIT(&call->lock);
+               }
+           }
+           MUTEX_EXIT(&conn->conn_call_lock);
+           return;
+       }
+
        packet = rxi_AllocPacket(RX_PACKET_CLASS_SPECIAL);
        if (packet) {
            /* If there's no packet available, do this later. */
@@ -5282,7 +5466,8 @@ void rxi_ChallengeEvent(event, conn, dummy)
        }
        clock_GetTime(&when);
        when.sec += RX_CHALLENGE_TIMEOUT;
-       conn->challengeEvent = rxevent_Post(&when, rxi_ChallengeEvent, conn, 0);
+       conn->challengeEvent =
+           rxevent_Post(&when, rxi_ChallengeEvent, conn, (void *) (tries-1));
     }
 }
 
@@ -5297,7 +5482,7 @@ void rxi_ChallengeOn(conn)
 {
     if (!conn->challengeEvent) {
        RXS_CreateChallenge(conn->securityObject, conn);
-       rxi_ChallengeEvent((struct rxevent *)0, conn, NULL);
+       rxi_ChallengeEvent(NULL, conn, (void *) RX_CHALLENGE_MAXTRIES);
     };
 }
 
@@ -5977,6 +6162,9 @@ static int MakeDebugCall(
     taddr.sin_family = AF_INET;
     taddr.sin_port = remotePort;
     taddr.sin_addr.s_addr = remoteAddr;
+#ifdef STRUCT_SOCKADDR_HAS_SA_LEN
+    taddr.sin_len = sizeof(struct sockaddr_in);
+#endif
     while(1) {
        memset(&theader, 0, sizeof(theader));
        theader.epoch = htonl(999);