/** * Connects to a server by it's server address. * * @param sap Server address. * @param aport Server port. * @param acell * @param tu Connect as this user. * @param force_if_down * @param create * @param locktype Specifies type of lock to be used for this function. * * @return The new connection. */ struct afs_conn * afs_ConnBySA(struct srvAddr *sap, unsigned short aport, afs_int32 acell, struct unixuser *tu, int force_if_down, afs_int32 create, afs_int32 locktype, struct rx_connection **rxconn) { int glocked, foundvec; struct afs_conn *tc = NULL; struct sa_conn_vector *tcv = NULL; struct rx_securityClass *csec; /*Security class object */ int isec; /*Security index */ int service; *rxconn = NULL; /* find cached connection */ ObtainSharedLock(&afs_xconn, 15); foundvec = 0; for (tcv = sap->conns; tcv; tcv = tcv->next) { if (tcv->user == tu && tcv->port == aport) { /* return most eligible conn */ if (!foundvec) foundvec = 1; UpgradeSToWLock(&afs_xconn, 37); tc = find_preferred_connection(tcv, create); ConvertWToSLock(&afs_xconn); break; } } if (!tc && !create) { /* Not found and can't create a new one. */ ReleaseSharedLock(&afs_xconn); return NULL; } if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { afs_warnuser("afs_ConnBySA: disconnected\n"); ReleaseSharedLock(&afs_xconn); return NULL; } if (!foundvec && create) { /* No such connection vector exists. Create one and splice it in. * Make sure the server record has been marked as used (for the purposes * of calculating up & down times, it's now considered to be an * ``active'' server). Also make sure the server's lastUpdateEvalTime * gets set, marking the time of its ``birth''. */ UpgradeSToWLock(&afs_xconn, 37); new_conn_vector(tcv); tcv->user = tu; tcv->port = aport; tcv->srvr = sap; tcv->next = sap->conns; sap->conns = tcv; /* all struct afs_conn ptrs come from here */ tc = find_preferred_connection(tcv, create); afs_ActivateServer(sap); ConvertWToSLock(&afs_xconn); } /* end of if (!tcv) */ if (!tc) { /* Not found and no alternatives. */ ReleaseSharedLock(&afs_xconn); return NULL; } if (tu->states & UTokensBad) { /* we may still have an authenticated RPC connection here, * we'll have to create a new, unauthenticated, connection. * Perhaps a better way to do this would be to set * conn->forceConnectFS on all conns when the token first goes * bad, but that's somewhat trickier, due to locking * constraints (though not impossible). */ if (tc->id && (rx_SecurityClassOf(tc->id) != 0)) { tc->forceConnectFS = 1; /* force recreation of connection */ } tu->states &= ~UHasTokens; /* remove the authentication info */ } glocked = ISAFS_GLOCK(); if (tc->forceConnectFS) { UpgradeSToWLock(&afs_xconn, 38); csec = (struct rx_securityClass *)0; if (tc->id) { if (glocked) AFS_GUNLOCK(); rx_SetConnSecondsUntilNatPing(tc->id, 0); rx_DestroyConnection(tc->id); if (glocked) AFS_GLOCK(); } /* * Stupid hack to determine if using vldb service or file system * service. */ if (aport == sap->server->cell->vlport) service = 52; else service = 1; isec = 0; csec = afs_pickSecurityObject(tc, &isec); if (glocked) AFS_GUNLOCK(); tc->id = rx_NewConnection(sap->sa_ip, aport, service, csec, isec); if (glocked) AFS_GLOCK(); if (service == 52) { rx_SetConnHardDeadTime(tc->id, afs_rx_harddead); } /* set to a RX_CALL_TIMEOUT error to allow MTU retry to trigger */ rx_SetServerConnIdleDeadErr(tc->id, RX_CALL_DEAD); rx_SetConnIdleDeadTime(tc->id, afs_rx_idledead); /* * Only do this for the base connection, not per-user. * Will need to be revisited if/when CB gets security. */ if ((isec == 0) && (service != 52) && !(tu->states & UTokensBad) && (tu->viceId == UNDEFVID) #ifndef UKERNEL /* ukernel runs as just one uid anyway */ && (tu->uid == 0) #endif ) rx_SetConnSecondsUntilNatPing(tc->id, 20); tc->forceConnectFS = 0; /* apparently we're appropriately connected now */ if (csec) rxs_Release(csec); ConvertWToSLock(&afs_xconn); } /* end of if (tc->forceConnectFS)*/ *rxconn = tc->id; rx_GetConnection(*rxconn); ReleaseSharedLock(&afs_xconn); return tc; }
/** * Connects to a server by it's server address. * * @param sap Server address. * @param aport Server port. * @param acell * @param tu Connect as this user. * @param force_if_down * @param create * @param locktype Specifies type of lock to be used for this function. * * @return The new connection. */ struct afs_conn * afs_ConnBySA(struct srvAddr *sap, unsigned short aport, afs_int32 acell, struct unixuser *tu, int force_if_down, afs_int32 create, afs_int32 locktype) { struct afs_conn *tc = 0; struct rx_securityClass *csec; /*Security class object */ int isec; /*Security index */ int service; if (!sap || ((sap->sa_flags & SRVR_ISDOWN) && !force_if_down)) { /* sa is known down, and we don't want to force it. */ return NULL; } ObtainSharedLock(&afs_xconn, 15); /* Get conn by port and user. */ for (tc = sap->conns; tc; tc = tc->next) { if (tc->user == tu && tc->port == aport) { break; } } if (!tc && !create) { /* Not found and can't create a new one. */ ReleaseSharedLock(&afs_xconn); return NULL; } if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { afs_warnuser("afs_ConnBySA: disconnected\n"); ReleaseSharedLock(&afs_xconn); return NULL; } if (!tc) { /* No such connection structure exists. Create one and splice it in. * Make sure the server record has been marked as used (for the purposes * of calculating up & down times, it's now considered to be an * ``active'' server). Also make sure the server's lastUpdateEvalTime * gets set, marking the time of its ``birth''. */ UpgradeSToWLock(&afs_xconn, 37); tc = (struct afs_conn *)afs_osi_Alloc(sizeof(struct afs_conn)); memset(tc, 0, sizeof(struct afs_conn)); tc->user = tu; tc->port = aport; tc->srvr = sap; tc->refCount = 0; /* bumped below */ tc->forceConnectFS = 1; tc->id = (struct rx_connection *)0; tc->next = sap->conns; sap->conns = tc; afs_ActivateServer(sap); ConvertWToSLock(&afs_xconn); } /* end of if (!tc) */ tc->refCount++; if (tu->states & UTokensBad) { /* we may still have an authenticated RPC connection here, * we'll have to create a new, unauthenticated, connection. * Perhaps a better way to do this would be to set * conn->forceConnectFS on all conns when the token first goes * bad, but that's somewhat trickier, due to locking * constraints (though not impossible). */ if (tc->id && (rx_SecurityClassOf(tc->id) != 0)) { tc->forceConnectFS = 1; /* force recreation of connection */ } tu->vid = UNDEFVID; /* forcibly disconnect the authentication info */ } if (tc->forceConnectFS) { UpgradeSToWLock(&afs_xconn, 38); csec = (struct rx_securityClass *)0; if (tc->id) { AFS_GUNLOCK(); rx_DestroyConnection(tc->id); AFS_GLOCK(); } /* * Stupid hack to determine if using vldb service or file system * service. */ if (aport == sap->server->cell->vlport) service = 52; else service = 1; isec = 0; csec = afs_pickSecurityObject(tc, &isec); AFS_GUNLOCK(); tc->id = rx_NewConnection(sap->sa_ip, aport, service, csec, isec); AFS_GLOCK(); if (service == 52) { rx_SetConnHardDeadTime(tc->id, afs_rx_harddead); } /* set to a RX_CALL_TIMEOUT error to allow MTU retry to trigger */ rx_SetServerConnIdleDeadErr(tc->id, RX_CALL_DEAD); rx_SetConnIdleDeadTime(tc->id, afs_rx_idledead); rx_SetMsgsizeRetryErr(tc->id, RX_MSGSIZE); /* * Only do this for the base connection, not per-user. * Will need to be revisited if/when CB gets security. */ if ((isec == 0) && (service != 52) && !(tu->states & UTokensBad) && (tu->vid == UNDEFVID)) rx_SetConnSecondsUntilNatPing(tc->id, 20); tc->forceConnectFS = 0; /* apparently we're appropriately connected now */ if (csec) rxs_Release(csec); ConvertWToSLock(&afs_xconn); } /* end of if (tc->forceConnectFS)*/ ReleaseSharedLock(&afs_xconn); return tc; }
static void cm_CheckServersMulti(afs_uint32 flags, cm_cell_t *cellp) { /* * The goal of this function is to probe simultaneously * probe all of the up/down servers (vldb/file) as * specified by flags in the minimum number of RPCs. * Effectively that means use one multi_RXAFS_GetCapabilities() * followed by possibly one multi_RXAFS_GetTime() and * one multi_VL_ProbeServer(). * * To make this work we must construct the list of vldb * and file servers that are to be probed as well as the * associated data structures. */ int srvAddrCount = 0; struct srvAddr **addrs = NULL; cm_conn_t **conns = NULL; struct rx_connection **rxconns = NULL; cm_req_t req; afs_int32 i, nconns = 0, maxconns; afs_int32 *conntimer, *results; Capabilities *caps = NULL; cm_server_t ** serversp, *tsp; afs_uint32 isDown, wasDown; afs_uint32 code; time_t start; char hoststr[16]; cm_InitReq(&req); maxconns = max(cm_numFileServers,cm_numVldbServers); if (maxconns == 0) return; conns = (cm_conn_t **)malloc(maxconns * sizeof(cm_conn_t *)); rxconns = (struct rx_connection **)malloc(maxconns * sizeof(struct rx_connection *)); conntimer = (afs_int32 *)malloc(maxconns * sizeof (afs_int32)); results = (afs_int32 *)malloc(maxconns * sizeof (afs_int32)); serversp = (cm_server_t **)malloc(maxconns * sizeof(cm_server_t *)); caps = (Capabilities *)malloc(maxconns * sizeof(Capabilities)); memset(caps, 0, maxconns * sizeof(Capabilities)); if ((flags & CM_FLAG_CHECKFILESERVERS) || !(flags & (CM_FLAG_CHECKFILESERVERS|CM_FLAG_CHECKVLDBSERVERS))) { lock_ObtainRead(&cm_serverLock); for (nconns=0, tsp = cm_serversAllFirstp; tsp != NULL && nconns < maxconns; tsp = (cm_server_t *)osi_QNext(&tsp->allq)) { if (tsp->type != CM_SERVER_FILE || tsp->cellp == NULL || /* SetPref only */ cellp && cellp != tsp->cellp) continue; cm_GetServerNoLock(tsp); lock_ReleaseRead(&cm_serverLock); lock_ObtainMutex(&tsp->mx); isDown = tsp->flags & CM_SERVERFLAG_DOWN; if (tsp->pingCount > 0 || !((isDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) || (!isDown && (flags & CM_FLAG_CHECKUPSERVERS)))) { lock_ReleaseMutex(&tsp->mx); lock_ObtainRead(&cm_serverLock); cm_PutServerNoLock(tsp); continue; } InterlockedIncrement(&tsp->pingCount); lock_ReleaseMutex(&tsp->mx); if (cm_noIPAddr > 0) code = cm_ConnByServer(tsp, cm_rootUserp, FALSE, &conns[nconns]); else code = RX_CALL_DEAD; if (code) { lock_ObtainMutex(&tsp->mx); if (code == RX_CALL_DEAD) cm_MarkServerDown(tsp, code, isDown); InterlockedDecrement(&tsp->pingCount); lock_ReleaseMutex(&tsp->mx); lock_ObtainRead(&cm_serverLock); cm_PutServerNoLock(tsp); continue; } lock_ObtainRead(&cm_serverLock); rxconns[nconns] = cm_GetRxConn(conns[nconns]); if (conntimer[nconns] = (isDown ? 1 : 0)) rx_SetConnHardDeadTime(rxconns[nconns], 10); serversp[nconns] = tsp; nconns++; } lock_ReleaseRead(&cm_serverLock); if (nconns) { /* Perform the multi call */ start = time(NULL); multi_Rx(rxconns,nconns) { multi_RXAFS_GetCapabilities(&caps[multi_i]); results[multi_i]=multi_error; } multi_End; }
static long RunCallTest(struct clientParms *parms, long host, struct rx_securityClass *sc, long si) { long code; #ifndef rx_GetPacketCksum code = RXKST_BADARGS; afs_com_err(whoami, code, "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest"); return code; #else int i, ch; struct rx_connection *conn; long firstCall; afs_int32 callNumbers[RX_MAXCALLS]; long codes[RX_MAXCALLS]; long retCode = 0; /* ret. if nothing fatal goes wrong */ conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc, si); if (!conn) return RXKST_NEWCONNFAILED; /* First check the basic behaviour of call number handling */ code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; for (i = 0; i < RX_MAXCALLS; i++) { if (callNumbers[i] != 0) { fprintf(stderr, "Connection's initial call numbers not zero. call[%d] = %d\n", i, callNumbers[i]); return RXKST_BADCALLNUMBERS; } } code = FastCall(conn); if (code) return code; code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; firstCall = callNumbers[0]; code = FastCall(conn); if (code) return code; code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; if ((callNumbers[0] != firstCall + 1) && ((firstCall == 1) || (firstCall == 2))) { /* The call number after the first call should be one or, more likely, * two (if the call is still DALLYing). Between first and second call, * the call number should have incremented by one. */ fprintf(stderr, "Connection's first channel call number not one. call[%d] = %d\n", 0, callNumbers[0]); return RXKST_BADCALLNUMBERS; } for (i = 1; i < RX_MAXCALLS; i++) { if (callNumbers[i] != 0) { fprintf(stderr, "Connection's other channel call numbers not zero. call[%d] = %d\n", i, callNumbers[i]); return RXKST_BADCALLNUMBERS; } } code = MakeMultiChannelCall(conn, 1, 0, codes); if (code) return code; /* Now try to resend a call that's already been executed by finding a * non-zero call number on a channel other than zero and decrementing it by * one. This should appear to the server as a retransmitted call. Since * this is behaving as a broken client different strange behaviors may be * exhibited by different servers. If the response packet to the original * call is discarded by the time the "retransmitted" call arrives (perhaps * due to high server or client load) there is no way for the server to * respond at all. Further, it seems, that under some cases the connection * will be kept alive indefinitely even though the server has discarded the * "retransmitted" call and is making no effort to reexecute the call. To * handle these, accept either a timeout (-1) or and INCFAILED error here, * also set the connenction HardDeadTime to punt after a reasonable * interval. */ /* short dead time since may we expect some trouble */ rx_SetConnHardDeadTime(conn, 30); code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; for (ch = 1; ch < RX_MAXCALLS; ch++) if (callNumbers[ch] > 1) { callNumbers[ch]--; code = rxi_SetCallNumberVector(conn, callNumbers); if (code) return code; break; } if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */ return RXKST_BADCALLNUMBERS; code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes); code = CheckCallFailure(conn, codes, code, "retransmitted call"); if (code && !retCode) retCode = code; /* Get a fresh connection, becasue if the above failed as it should the * connection is dead. */ rx_DestroyConnection(conn); conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc, si); if (!conn) return RXKST_NEWCONNFAILED; /* Similarly, but decrement call number by two which should be completely * unmistakeable as a broken or malicious client. */ /* short dead time since may we expect some trouble */ rx_SetConnHardDeadTime(conn, 30); code = MakeMultiChannelCall(conn, 2, 0, codes); if (code) return code; code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; for (ch = 1; ch < RX_MAXCALLS; ch++) if (callNumbers[ch] > 2) { callNumbers[ch] -= 2; code = rxi_SetCallNumberVector(conn, callNumbers); break; } if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */ return RXKST_BADCALLNUMBERS; code = MakeMultiChannelCall(conn, 1, -1, codes); code = CheckCallFailure(conn, codes, code, "duplicate call"); if (code && !retCode) retCode = code; rx_DestroyConnection(conn); conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc, si); if (!conn) return RXKST_NEWCONNFAILED; /* Next, without waiting for the server to discard its state, we will check * to see if the Challenge/Response protocol correctly informs the server * of the client's callNumber state. We do this by artificially increasing * the call numbers of a new connection for all channels beyond zero, * making a call on channel zero, then resetting the call number for the * unused channels back to zero, then making calls on all channels. */ code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; for (i = 0; i < RX_MAXCALLS; i++) { if (callNumbers[i] != 0) return RXKST_BADCALLNUMBERS; callNumbers[i] = 51; /* an arbitrary value... */ } code = rxi_SetCallNumberVector(conn, callNumbers); if (code) return code; code = FastCall(conn); /* use channel 0 */ if (code) return code; code = rxi_GetCallNumberVector(conn, callNumbers); if (code) return code; if (callNumbers[0] != 52) return RXKST_BADCALLNUMBERS; for (i = 1; i < RX_MAXCALLS; i++) { if (callNumbers[i] != 51) return RXKST_BADCALLNUMBERS; callNumbers[i] = 37; /* back up a ways */ } code = rxi_SetCallNumberVector(conn, callNumbers); if (code) return code; /* now try calls on all channels... */ code = MakeMultiChannelCall(conn, 1, -1, codes); code = CheckCallFailure(conn, codes, code, "alternate channel call replay"); if (code && !retCode) retCode = code; rx_DestroyConnection(conn); return retCode; #endif /* rx_GetPacketCksum */ }
void cm_PingServer(cm_server_t *tsp) { long code; int wasDown = 0; cm_conn_t *connp; struct rx_connection * rxconnp; Capabilities caps = {0, 0}; char hoststr[16]; cm_req_t req; lock_ObtainMutex(&tsp->mx); if (InterlockedIncrement(&tsp->pingCount) > 1) { tsp->waitCount++; osi_SleepM((LONG_PTR)tsp, &tsp->mx); lock_ObtainMutex(&tsp->mx); InterlockedDecrement(&tsp->pingCount); if (--tsp->waitCount > 0) osi_Wakeup((LONG_PTR)tsp); lock_ReleaseMutex(&tsp->mx); return; } wasDown = tsp->flags & CM_SERVERFLAG_DOWN; afs_inet_ntoa_r(tsp->addr.sin_addr.S_un.S_addr, hoststr); lock_ReleaseMutex(&tsp->mx); if (cm_noIPAddr > 0) code = cm_ConnByServer(tsp, cm_rootUserp, FALSE, &connp); else code = RX_CALL_DEAD; /* No network */ if (code == 0) { /* now call the appropriate ping call. Drop the timeout if * the server is known to be down, so that we don't waste a * lot of time retiming out down servers. */ osi_Log4(afsd_logp, "cm_PingServer server %s (%s) was %s with caps 0x%x", osi_LogSaveString(afsd_logp, hoststr), tsp->type == CM_SERVER_VLDB ? "vldb" : "file", wasDown ? "down" : "up", tsp->capabilities); rxconnp = cm_GetRxConn(connp); if (wasDown) rx_SetConnHardDeadTime(rxconnp, 10); if (tsp->type == CM_SERVER_VLDB) { code = VL_ProbeServer(rxconnp); } else { /* file server */ code = RXAFS_GetCapabilities(rxconnp, &caps); } if (wasDown) rx_SetConnHardDeadTime(rxconnp, HardDeadtimeout); rx_PutConnection(rxconnp); cm_PutConn(connp); } /* got an unauthenticated connection to this server */ lock_ObtainMutex(&tsp->mx); if (code >= 0 || code == RXGEN_OPCODE) { /* mark server as up */ _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN); tsp->downTime = 0; /* we currently handle 32-bits of capabilities */ if (code != RXGEN_OPCODE && caps.Capabilities_len > 0) { tsp->capabilities = caps.Capabilities_val[0]; xdr_free((xdrproc_t) xdr_Capabilities, &caps); caps.Capabilities_len = 0; caps.Capabilities_val = 0; } else { tsp->capabilities = 0; } osi_Log3(afsd_logp, "cm_PingServer server %s (%s) is up with caps 0x%x", osi_LogSaveString(afsd_logp, hoststr), tsp->type == CM_SERVER_VLDB ? "vldb" : "file", tsp->capabilities); /* Now update the volume status if necessary */ if (wasDown) { cm_server_vols_t * tsrvp; cm_volume_t * volp; int i; for (tsrvp = tsp->vols; tsrvp; tsrvp = tsrvp->nextp) { for (i=0; i<NUM_SERVER_VOLS; i++) { if (tsrvp->ids[i] != 0) { cm_InitReq(&req); lock_ReleaseMutex(&tsp->mx); code = cm_FindVolumeByID(tsp->cellp, tsrvp->ids[i], cm_rootUserp, &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp); lock_ObtainMutex(&tsp->mx); if (code == 0) { cm_UpdateVolumeStatus(volp, tsrvp->ids[i]); cm_PutVolume(volp); } } } } cm_RankServer(tsp); } } else { cm_MarkServerDown(tsp, code, wasDown); osi_Log3(afsd_logp, "cm_PingServer server %s (%s) is down with caps 0x%x", osi_LogSaveString(afsd_logp, hoststr), tsp->type == CM_SERVER_VLDB ? "vldb" : "file", tsp->capabilities); } InterlockedDecrement(&tsp->pingCount); if (tsp->waitCount > 0) osi_Wakeup((LONG_PTR)tsp); lock_ReleaseMutex(&tsp->mx); }