static struct rx_connection
*rxi_FindConnection(osi_socket socket, afs_uint32 host, u_short port,
u_short serviceId, afs_uint32 cid,
- afs_uint32 epoch, int type, u_int securityIndex);
+ afs_uint32 epoch, int type, u_int securityIndex,
+ int *unknownService);
static struct rx_packet
*rxi_ReceiveDataPacket(struct rx_call *call, struct rx_packet *np,
int istack, osi_socket socket,
#define CLEAR_CALL_QUEUE_LOCK(C)
#endif /* RX_ENABLE_LOCKS */
struct rx_serverQueueEntry *rx_waitForPacket = 0;
-struct rx_serverQueueEntry *rx_waitingForPacket = 0;
/* ------------Exported Interfaces------------- */
#ifndef AFS_NT40_ENV
static
#endif
-int rxinit_status = 1;
-#ifdef AFS_PTHREAD_ENV
-/*
- * This mutex protects the following global variables:
- * rxinit_status
- */
-
-#define LOCK_RX_INIT MUTEX_ENTER(&rx_init_mutex)
-#define UNLOCK_RX_INIT MUTEX_EXIT(&rx_init_mutex)
-#else
-#define LOCK_RX_INIT
-#define UNLOCK_RX_INIT
-#endif
+rx_atomic_t rxinit_status = RX_ATOMIC_INIT(1);
int
rx_InitHost(u_int host, u_int port)
struct timeval tv;
#endif /* KERNEL */
char *htable, *ptable;
- int tmp_status;
SPLVAR;
INIT_PTHREAD_LOCKS;
- LOCK_RX_INIT;
- if (rxinit_status == 0) {
- tmp_status = rxinit_status;
- UNLOCK_RX_INIT;
- return tmp_status; /* Already started; return previous error code. */
- }
+ if (!rx_atomic_test_and_clear_bit(&rxinit_status, 0))
+ return 0; /* already started */
+
#ifdef RXDEBUG
rxi_DebugInit();
#endif
rx_socket = rxi_GetHostUDPSocket(host, (u_short) port);
if (rx_socket == OSI_NULLSOCKET) {
- UNLOCK_RX_INIT;
return RX_ADDRINUSE;
}
#if defined(RX_ENABLE_LOCKS) && defined(KERNEL)
rx_GetIFInfo();
#endif
-#if defined(RXK_LISTENER_ENV) || !defined(KERNEL)
/* Start listener process (exact function is dependent on the
* implementation environment--kernel or user space) */
rxi_StartListener();
-#endif
USERPRI;
- tmp_status = rxinit_status = 0;
- UNLOCK_RX_INIT;
- return tmp_status;
+ rx_atomic_clear_bit(&rxinit_status, 0);
+ return 0;
}
int
void
rx_SetBusyChannelError(afs_int32 onoff)
{
- osi_Assert(rxinit_status != 0);
+ osi_Assert(rx_atomic_test_bit(&rxinit_status, 0));
rxi_busyChannelError = onoff ? 1 : 0;
}
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);
struct rx_connection **conn_ptr, **conn_end;
INIT_PTHREAD_LOCKS;
- LOCK_RX_INIT;
- if (rxinit_status == 1) {
- UNLOCK_RX_INIT;
+ if (rx_atomic_test_and_set_bit(&rxinit_status, 0))
return; /* Already shutdown. */
- }
+
rxi_DeleteCachedConnections();
if (rx_connHashTable) {
MUTEX_ENTER(&rx_connHashTable_lock);
afs_winsockCleanup();
#endif
- rxinit_status = 1;
- UNLOCK_RX_INIT;
}
#endif
#ifdef AFS_RXERRQ_ENV
static void
-rxi_SetPeerDead(afs_uint32 host, afs_uint16 port)
+rxi_SetPeerDead(struct sock_extended_err *err, afs_uint32 host, afs_uint16 port)
{
int hashIndex = PEER_HASH(host, port);
struct rx_peer *peer;
for (peer = rx_peerHashTable[hashIndex]; peer; peer = peer->next) {
if (peer->host == host && peer->port == port) {
+ peer->refCount++;
break;
}
}
+ MUTEX_EXIT(&rx_peerHashTable_lock);
+
if (peer) {
rx_atomic_inc(&peer->neterrs);
- }
+ MUTEX_ENTER(&peer->peer_lock);
+ peer->last_err_origin = RX_NETWORK_ERROR_ORIGIN_ICMP;
+ peer->last_err_type = err->ee_type;
+ peer->last_err_code = err->ee_code;
+ MUTEX_EXIT(&peer->peer_lock);
- MUTEX_EXIT(&rx_peerHashTable_lock);
+ MUTEX_ENTER(&rx_peerHashTable_lock);
+ peer->refCount--;
+ MUTEX_EXIT(&rx_peerHashTable_lock);
+ }
}
void
case ICMP_PORT_UNREACH:
case ICMP_NET_ANO:
case ICMP_HOST_ANO:
- rxi_SetPeerDead(addr, port);
+ rxi_SetPeerDead(err, addr, port);
break;
}
}
}
+
+static const char *
+rxi_TranslateICMP(int type, int code)
+{
+ switch (type) {
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_NET_UNREACH:
+ return "Destination Net Unreachable";
+ case ICMP_HOST_UNREACH:
+ return "Destination Host Unreachable";
+ case ICMP_PROT_UNREACH:
+ return "Destination Protocol Unreachable";
+ case ICMP_PORT_UNREACH:
+ return "Destination Port Unreachable";
+ case ICMP_NET_ANO:
+ return "Destination Net Prohibited";
+ case ICMP_HOST_ANO:
+ return "Destination Host Prohibited";
+ }
+ break;
+ }
+ return NULL;
+}
#endif /* AFS_RXERRQ_ENV */
+/**
+ * Get the last network error for a connection
+ *
+ * A "network error" here means an error retrieved from ICMP, or some other
+ * mechanism outside of Rx that informs us of errors in network reachability.
+ *
+ * If a peer associated with the given Rx connection has received a network
+ * error recently, this function allows the caller to know what error
+ * specifically occurred. This can be useful to know, since e.g. ICMP errors
+ * can cause calls to that peer to be quickly aborted. So, this function can
+ * help see why a call was aborted due to network errors.
+ *
+ * If we have received traffic from a peer since the last network error, we
+ * treat that peer as if we had not received an network error for it.
+ *
+ * @param[in] conn The Rx connection to examine
+ * @param[out] err_origin The origin of the last network error (e.g. ICMP);
+ * one of the RX_NETWORK_ERROR_ORIGIN_* constants
+ * @param[out] err_type The type of the last error
+ * @param[out] err_code The code of the last error
+ * @param[out] msg Human-readable error message, if applicable; NULL otherwise
+ *
+ * @return If we have an error
+ * @retval -1 No error to get; 'out' params are undefined
+ * @retval 0 We have an error; 'out' params contain the last error
+ */
+int
+rx_GetNetworkError(struct rx_connection *conn, int *err_origin, int *err_type,
+ int *err_code, const char **msg)
+{
+#ifdef AFS_RXERRQ_ENV
+ struct rx_peer *peer = conn->peer;
+ if (rx_atomic_read(&peer->neterrs)) {
+ MUTEX_ENTER(&peer->peer_lock);
+ *err_origin = peer->last_err_origin;
+ *err_type = peer->last_err_type;
+ *err_code = peer->last_err_code;
+ MUTEX_EXIT(&peer->peer_lock);
+
+ *msg = NULL;
+ if (*err_origin == RX_NETWORK_ERROR_ORIGIN_ICMP) {
+ *msg = rxi_TranslateICMP(*err_type, *err_code);
+ }
+
+ return 0;
+ }
+#endif
+ return -1;
+}
+
/* Find the peer process represented by the supplied (host,port)
* combination. If there is no appropriate active peer structure, a
* new one will be allocated and initialized
static struct rx_connection *
rxi_FindConnection(osi_socket socket, afs_uint32 host,
u_short port, u_short serviceId, afs_uint32 cid,
- afs_uint32 epoch, int type, u_int securityIndex)
+ afs_uint32 epoch, int type, u_int securityIndex,
+ int *unknownService)
{
int hashindex, flag, i;
struct rx_connection *conn;
+ *unknownService = 0;
hashindex = CONN_HASH(host, port, cid, epoch, type);
MUTEX_ENTER(&rx_connHashTable_lock);
rxLastConn ? (conn = rxLastConn, flag = 0) : (conn =
if (!service || (securityIndex >= service->nSecurityObjects)
|| (service->securityObjects[securityIndex] == 0)) {
MUTEX_EXIT(&rx_connHashTable_lock);
+ *unknownService = 1;
return (struct rx_connection *)0;
}
conn = rxi_AllocConnection(); /* This bzero's the connection */
* flag is cleared.
*/
#ifdef RX_ENABLE_LOCKS
- if (call->state == RX_STATE_ACTIVE) {
- int old_error = call->error;
+ if (call->state == RX_STATE_ACTIVE && !call->error) {
rxi_WaitforTQBusy(call);
/* If we entered error state while waiting,
* must call rxi_CallError to permit rxi_ResetCall
* to processed when the tqWaiter count hits zero.
*/
- if (call->error && call->error != old_error) {
+ if (call->error) {
rxi_CallError(call, call->error);
MUTEX_EXIT(&call->lock);
return NULL;
struct rx_call *call;
struct rx_connection *conn;
int type;
+ int unknownService = 0;
#ifdef RXDEBUG
char *packetType;
#endif
conn =
rxi_FindConnection(socket, host, port, np->header.serviceId,
np->header.cid, np->header.epoch, type,
- np->header.securityIndex);
+ np->header.securityIndex, &unknownService);
/* To avoid having 2 connections just abort at each other,
don't abort an abort. */
if (!conn) {
- if (np->header.type != RX_PACKET_TYPE_ABORT)
+ if (unknownService && (np->header.type != RX_PACKET_TYPE_ABORT))
rxi_SendRawAbort(socket, host, port, RX_INVALID_OPERATION,
np, 0);
return np;
MUTEX_ENTER(&conn->conn_data_lock);
- if (event) {
- rxevent_Put(conn->checkReachEvent);
- conn->checkReachEvent = NULL;
- }
+ if (event)
+ rxevent_Put(&conn->checkReachEvent);
waiting = conn->flags & RX_CONN_ATTACHWAIT;
if (event) {
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,
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 {
#ifdef RX_ENABLE_LOCKS
if (event) {
MUTEX_ENTER(&call->lock);
- if (event == call->delayedAckEvent) {
- rxevent_Put(call->delayedAckEvent);
- call->delayedAckEvent = NULL;
- }
+ if (event == call->delayedAckEvent)
+ rxevent_Put(&call->delayedAckEvent);
CALL_RELE(call, RX_CALL_REFCOUNT_DELAY);
}
(void)rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
if (event)
MUTEX_EXIT(&call->lock);
#else /* RX_ENABLE_LOCKS */
- if (event) {
- rxevent_Put(call->delayedAckEvent);
- call->delayedAckEvent = NULL;
- }
+ if (event)
+ rxevent_Put(&call->delayedAckEvent);
(void)rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
#endif /* RX_ENABLE_LOCKS */
}
struct rx_packet *packet, int istack, int force)
{
afs_int32 error;
- struct clock when, now;
if (!conn->error)
return packet;
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;
}
* event pending. */
if (event == call->resendEvent) {
CALL_RELE(call, RX_CALL_REFCOUNT_RESEND);
- rxevent_Put(call->resendEvent);
- call->resendEvent = NULL;
+ rxevent_Put(&call->resendEvent);
}
rxi_CheckPeerDead(call);
MUTEX_ENTER(&rx_refcnt_mutex);
/* Only reschedule ourselves if the connection would not be destroyed */
if (conn->refCount <= 1) {
- rxevent_Put(conn->natKeepAliveEvent);
- conn->natKeepAliveEvent = NULL;
+ rxevent_Put(&conn->natKeepAliveEvent);
MUTEX_EXIT(&rx_refcnt_mutex);
MUTEX_EXIT(&conn->conn_data_lock);
rx_DestroyConnection(conn); /* drop the reference for this */
} else {
conn->refCount--; /* drop the reference for this */
MUTEX_EXIT(&rx_refcnt_mutex);
- rxevent_Put(conn->natKeepAliveEvent);
- conn->natKeepAliveEvent = NULL;
+ rxevent_Put(&conn->natKeepAliveEvent);
rxi_ScheduleNatKeepAliveEvent(conn);
MUTEX_EXIT(&conn->conn_data_lock);
}
CALL_RELE(call, RX_CALL_REFCOUNT_ALIVE);
MUTEX_ENTER(&call->lock);
- if (event == call->keepAliveEvent) {
- rxevent_Put(call->keepAliveEvent);
- call->keepAliveEvent = NULL;
- }
+ if (event == call->keepAliveEvent)
+ rxevent_Put(&call->keepAliveEvent);
now = clock_Sec();
CALL_RELE(call, RX_CALL_REFCOUNT_MTU);
MUTEX_ENTER(&call->lock);
- if (event == call->growMTUEvent) {
- rxevent_Put(call->growMTUEvent);
- call->growMTUEvent = NULL;
- }
+ if (event == call->growMTUEvent)
+ rxevent_Put(&call->growMTUEvent);
if (rxi_CheckCall(call, 0)) {
MUTEX_EXIT(&call->lock);
struct rx_packet *packet;
MUTEX_ENTER(&conn->conn_data_lock);
- rxevent_Put(conn->delayedAbortEvent);
- conn->delayedAbortEvent = NULL;
+ rxevent_Put(&conn->delayedAbortEvent);
error = htonl(conn->error);
conn->abortCount++;
MUTEX_EXIT(&conn->conn_data_lock);
struct rx_packet *packet;
MUTEX_ENTER(&call->lock);
- rxevent_Put(call->delayedAbortEvent);
- call->delayedAbortEvent = NULL;
+ rxevent_Put(&call->delayedAbortEvent);
error = htonl(call->error);
call->abortCount++;
packet = rxi_AllocPacket(RX_PACKET_CLASS_SPECIAL);
{
struct rx_connection *conn = arg0;
- if (event) {
- rxevent_Put(conn->challengeEvent);
- conn->challengeEvent = NULL;
- }
+ if (event)
+ rxevent_Put(&conn->challengeEvent);
+
+ /* If there are no active calls it is not worth re-issuing the
+ * challenge. If the client issues another call on this connection
+ * the challenge can be requested at that time.
+ */
+ if (!rxi_HasActiveCalls(conn))
+ return;
if (RXS_CheckAuthentication(conn->securityObject, conn) != 0) {
struct rx_packet *packet;
int unused3)
{
struct clock now, when;
+ struct rxevent *event;
clock_GetTime(&now);
/* Find server connection structures that haven't been used for
when = now;
when.sec += RX_REAP_TIME; /* Check every RX_REAP_TIME seconds */
- rxevent_Put(rxevent_Post(&when, &now, rxi_ReapConnections, 0, NULL, 0));
+ event = rxevent_Post(&when, &now, rxi_ReapConnections, 0, NULL, 0);
+ rxevent_Put(&event);
}
struct rx_serverQueueEntry *sq;
#endif /* KERNEL */
- LOCK_RX_INIT;
- if (rxinit_status == 1) {
- UNLOCK_RX_INIT;
+ if (rx_atomic_test_and_set_bit(&rxinit_status, 0))
return; /* Already shutdown. */
- }
+
#ifndef KERNEL
rx_port = 0;
#ifndef AFS_PTHREAD_ENV
rxi_dataQuota = RX_MAX_QUOTA;
rxi_availProcs = rxi_totalMin = rxi_minDeficit = 0;
MUTEX_EXIT(&rx_quota_mutex);
- rxinit_status = 1;
- UNLOCK_RX_INIT;
}
#ifndef KERNEL
for (opr_queue_Scan(&processStats, cursor)) {
unsigned int num_funcs = 0, i;
struct rx_interface_stat *rpc_stat
- = opr_queue_Entry(rpc_stat, struct rx_interface_stat, entry);
+ = opr_queue_Entry(cursor, struct rx_interface_stat, entry);
num_funcs = rpc_stat->stats[0].func_total;
for (i = 0; i < num_funcs; i++) {