rx: update_nextCid overflow handling is broken
[openafs.git] / src / rx / rx.c
index b008400..db1f23a 100644 (file)
@@ -131,8 +131,7 @@ static struct rx_connection
 static struct rx_packet
        *rxi_ReceiveDataPacket(struct rx_call *call, struct rx_packet *np,
                               int istack, osi_socket socket,
-                              afs_uint32 host, u_short port, int *tnop,
-                              struct rx_call **newcallp);
+                              int *tnop, struct rx_call **newcallp);
 static struct rx_packet
        *rxi_ReceiveAckPacket(struct rx_call *call, struct rx_packet *np,
                              int istack, int *a_invalid);
@@ -625,9 +624,6 @@ rx_InitHost(u_int host, u_int port)
     MUTEX_ENTER(&rx_quota_mutex);
     rxi_dataQuota += rx_extraQuota; /* + extra pkts caller asked to rsrv */
     MUTEX_EXIT(&rx_quota_mutex);
-    /* *Slightly* random start time for the cid.  This is just to help
-     * out with the hashing function at the peer */
-    rx_nextCid = ((tv.tv_sec ^ tv.tv_usec) << RX_CIDSHIFT);
     rx_connHashTable = (struct rx_connection **)htable;
     rx_peerHashTable = (struct rx_peer **)ptable;
 
@@ -3110,6 +3106,44 @@ rxi_FindPeer(afs_uint32 host, u_short port, int create)
     return pp;
 }
 
+static_inline int
+rxi_ConnectionMatch(struct rx_connection *conn,
+                   afs_uint32 host, u_short port, afs_uint32 cid,
+                   afs_uint32 epoch, int type, u_int securityIndex,
+                   int *a_badSecurityIndex)
+{
+    struct rx_peer *pp;
+    if (conn->type != type) {
+       return 0;
+    }
+    if (conn->cid != (cid & RX_CIDMASK)) {
+       return 0;
+    }
+    if (conn->epoch != epoch) {
+       return 0;
+    }
+    if (conn->securityIndex != securityIndex) {
+       if (a_badSecurityIndex) {
+           *a_badSecurityIndex = 1;
+       }
+       return 0;
+    }
+    pp = conn->peer;
+    if (pp->host == host && pp->port == port) {
+       return 1;
+    }
+    if (type == RX_CLIENT_CONNECTION && pp->port == port) {
+       /* For client conns, we allow packets from any host to be associated
+        * with the conn. */
+       return 1;
+    }
+    if ((conn->epoch & 0x80000000)) {
+       /* If the epoch high bit is set, we ignore the host/port of any packets
+        * coming in for the conn. */
+       return 1;
+    }
+    return 0;
+}
 
 /* Find the connection at (host, port) started at epoch, and with the
  * given connection id.  Creates the server connection if necessary.
@@ -3139,25 +3173,18 @@ rxi_FindConnection(osi_socket socket, afs_uint32 host,
                                                  rx_connHashTable[hashindex],
                                                  flag = 1);
     for (; conn;) {
-       if ((conn->type == type) && ((cid & RX_CIDMASK) == conn->cid)
-           && (epoch == conn->epoch)) {
-           struct rx_peer *pp = conn->peer;
-           if (securityIndex != conn->securityIndex) {
-               /* this isn't supposed to happen, but someone could forge a packet
-                * like this, and there seems to be some CM bug that makes this
-                * happen from time to time -- in which case, the fileserver
-                * asserts. */
-               MUTEX_EXIT(&rx_connHashTable_lock);
-               return (struct rx_connection *)0;
-           }
-           if (pp->host == host && pp->port == port)
-               break;
-           if (type == RX_CLIENT_CONNECTION && pp->port == port)
-               break;
-           /* So what happens when it's a callback connection? */
-           if (                /*type == RX_CLIENT_CONNECTION && */
-                  (conn->epoch & 0x80000000))
-               break;
+       int bad_sec = 0;
+       if (rxi_ConnectionMatch(conn, host, port, cid, epoch, type,
+                               securityIndex, &bad_sec)) {
+           break;
+       }
+       if (bad_sec) {
+           /*
+            * This isn't supposed to happen, but someone could forge a packet
+            * like this, and bugs causing such packets are not unheard of.
+            */
+           MUTEX_EXIT(&rx_connHashTable_lock);
+           return NULL;
        }
        if (!flag) {
            /* the connection rxLastConn that was used the last time is not the
@@ -3294,6 +3321,36 @@ rxi_ReceiveServerCall(osi_socket socket, struct rx_packet *np,
     call = conn->call[channel];
 
     if (!call) {
+       if (np->header.type != RX_PACKET_TYPE_DATA) {
+           /*
+            * Clients must send DATA packets at some point to create a new
+            * call. If the first packet we saw for this call channel is
+            * something else, then either the DATA packets got lost/delayed,
+            * or we were restarted and this is an existing call from before we
+            * were restarted. In the latter case, some clients get confused if
+            * we respond to such requests, so just drop the packet to make
+            * things easier for them.
+            */
+           MUTEX_EXIT(&conn->conn_call_lock);
+           if (rx_stats_active)
+               rx_atomic_inc(&rx_stats.spuriousPacketsRead);
+           return NULL;
+       }
+
+       if (np->header.seq > rx_maxReceiveWindow) {
+           /*
+            * This is a DATA packet for further along in the call than is
+            * possible for a new call. This is probably from an existing call
+            * that was in the middle of running when we were restarted; ignore
+            * it to avoid confusing clients. (See above comment about non-DATA
+            * packets.)
+            */
+           MUTEX_EXIT(&conn->conn_call_lock);
+           if (rx_stats_active)
+               rx_atomic_inc(&rx_stats.spuriousPacketsRead);
+           return NULL;
+       }
+
        if (rxi_AbortIfServerBusy(socket, conn, np)) {
            MUTEX_EXIT(&conn->conn_call_lock);
            return NULL;
@@ -3583,8 +3640,7 @@ rxi_ReceivePacket(struct rx_packet *np, osi_socket socket,
        if (type == RX_CLIENT_CONNECTION && !opr_queue_IsEmpty(&call->tq))
            rxi_AckAllInTransmitQueue(call);
 
-       np = rxi_ReceiveDataPacket(call, np, 1, socket, host, port, tnop,
-                                  newcallp);
+       np = rxi_ReceiveDataPacket(call, np, 1, socket, tnop, newcallp);
        break;
     case RX_PACKET_TYPE_ACK:
        /* Respond immediately to ack packets requesting acknowledgement
@@ -3866,8 +3922,7 @@ TryAttach(struct rx_call *acall, osi_socket socket,
 static struct rx_packet *
 rxi_ReceiveDataPacket(struct rx_call *call,
                      struct rx_packet *np, int istack,
-                     osi_socket socket, afs_uint32 host, u_short port,
-                     int *tnop, struct rx_call **newcallp)
+                     osi_socket socket, int *tnop, struct rx_call **newcallp)
 {
     int ackNeeded = 0;         /* 0 means no, otherwise ack_reason */
     int newPackets = 0;
@@ -3926,7 +3981,7 @@ rxi_ReceiveDataPacket(struct rx_call *call,
        /* The RX_JUMBO_PACKET is set in all but the last packet in each
         * AFS 3.5 jumbogram. */
        if (flags & RX_JUMBO_PACKET) {
-           tnp = rxi_SplitJumboPacket(np, host, port, isFirst);
+           tnp = rxi_SplitJumboPacket(np);
        } else {
            tnp = NULL;
        }
@@ -6740,9 +6795,8 @@ 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;
+       rx_nextCid = 0;
+    rx_nextCid += 1 << RX_CIDSHIFT;
 }
 
 static void