Example #1
0
/* Optimization for unmarshalling 32 bit integers */
int
rx_ReadProc32(struct rx_call *call, afs_int32 * value)
{
    int bytes;
    int tcurlen;
    int tnLeft;
    char *tcurpos;
    SPLVAR;

    /* Free any packets from the last call to ReadvProc/WritevProc */
    if (!queue_IsEmpty(&call->iovq)) {
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->iovq);
    }

    /*
     * Most common case, all of the data is in the current iovec.
     * We are relying on nLeft being zero unless the call is in receive mode.
     */
    tcurlen = call->curlen;
    tnLeft = call->nLeft;
    if (!call->error && tcurlen >= sizeof(afs_int32)
	&& tnLeft >= sizeof(afs_int32)) {
	tcurpos = call->curpos;

        memcpy((char *)value, tcurpos, sizeof(afs_int32));

        call->curpos = tcurpos + sizeof(afs_int32);
	call->curlen = (u_short)(tcurlen - sizeof(afs_int32));
	call->nLeft = (u_short)(tnLeft - sizeof(afs_int32));
        if (!call->nLeft && call->currentPacket != NULL) {
            /* out of packet.  Get another one. */
            rxi_FreePacket(call->currentPacket);
            call->currentPacket = (struct rx_packet *)0;
        }
	return sizeof(afs_int32);
    }

    NETPRI;
    bytes = rxi_ReadProc(call, (char *)value, sizeof(afs_int32));
    USERPRI;

    return bytes;
}
Example #2
0
int
rx_ReadProc(struct rx_call *call, char *buf, int nbytes)
{
    int bytes;
    int tcurlen;
    int tnLeft;
    char *tcurpos;
    SPLVAR;

    /* Free any packets from the last call to ReadvProc/WritevProc */
    if (!queue_IsEmpty(&call->iovq)) {
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->iovq);
    }

    /*
     * Most common case, all of the data is in the current iovec.
     * We are relying on nLeft being zero unless the call is in receive mode.
     */
    tcurlen = call->curlen;
    tnLeft = call->nLeft;
    if (!call->error && tcurlen > nbytes && tnLeft > nbytes) {
	tcurpos = call->curpos;
        memcpy(buf, tcurpos, nbytes);

	call->curpos = tcurpos + nbytes;
	call->curlen = tcurlen - nbytes;
	call->nLeft = tnLeft - nbytes;

        if (!call->nLeft && call->currentPacket != NULL) {
            /* out of packet.  Get another one. */
            rxi_FreePacket(call->currentPacket);
            call->currentPacket = (struct rx_packet *)0;
        }
	return nbytes;
    }

    NETPRI;
    bytes = rxi_ReadProc(call, buf, nbytes);
    USERPRI;
    return bytes;
}
Example #3
0
void
rx_upcall(socket_t so, void *arg, __unused int waitflag)
{
    mbuf_t m;
    int error = 0;
    int i, flags = 0;
    struct msghdr msg;
    struct sockaddr_storage ss;
    struct sockaddr *sa = NULL;
    struct sockaddr_in from;
    struct rx_packet *p;
    afs_int32 rlen;
    afs_int32 tlen;
    afs_int32 savelen;          /* was using rlen but had aliasing problems */
    size_t nbytes, resid, noffset;

    p = rxi_AllocPacket(RX_PACKET_CLASS_RECEIVE);
    rx_computelen(p, tlen);
    rx_SetDataSize(p, tlen);    /* this is the size of the user data area */
    tlen += RX_HEADER_SIZE;     /* now this is the size of the entire packet */
    rlen = rx_maxJumboRecvSize; /* this is what I am advertising.  Only check
				 * it once in order to avoid races.  */
    tlen = rlen - tlen;
    if (tlen > 0) {
	tlen = rxi_AllocDataBuf(p, tlen, RX_PACKET_CLASS_RECV_CBUF);
	if (tlen > 0) {
	    tlen = rlen - tlen;
	} else
	    tlen = rlen;
    } else
	tlen = rlen;
    /* add some padding to the last iovec, it's just to make sure that the
     * read doesn't return more data than we expect, and is done to get around
     * our problems caused by the lack of a length field in the rx header. */
    savelen = p->wirevec[p->niovecs - 1].iov_len;
    p->wirevec[p->niovecs - 1].iov_len = savelen + RX_EXTRABUFFERSIZE;

    resid = nbytes = tlen + sizeof(afs_int32);

    memset(&msg, 0, sizeof(struct msghdr));
    msg.msg_name = &ss;
    msg.msg_namelen = sizeof(struct sockaddr_storage);
    sa =(struct sockaddr *) &ss;

    do {
	m = NULL;
	error = sock_receivembuf(so, &msg, &m, MSG_DONTWAIT, &nbytes);
	if (!error) {
	    size_t sz, offset = 0;
	    noffset = 0;
	    resid = nbytes;
	    for (i=0;i<p->niovecs && resid;i++) {
		sz=MIN(resid, p->wirevec[i].iov_len);
		error = mbuf_copydata(m, offset, sz, p->wirevec[i].iov_base);
		if (error)
		    break;
		resid-=sz;
		offset+=sz;
		noffset += sz;
	    }
	}
    } while (0);

    mbuf_freem(m);

    /* restore the vec to its correct state */
    p->wirevec[p->niovecs - 1].iov_len = savelen;

    if (error == EWOULDBLOCK && noffset > 0)
	error = 0;

    if (!error) {
	int host, port;

	nbytes -= resid;

	if (sa->sa_family == AF_INET)
	    from = *(struct sockaddr_in *)sa;

	p->length = nbytes - RX_HEADER_SIZE;;
	if ((nbytes > tlen) || (p->length & 0x8000)) {  /* Bogus packet */
	    if (nbytes <= 0) {
		if (rx_stats_active) {
		    MUTEX_ENTER(&rx_stats_mutex);
		    rx_atomic_inc(&rx_stats.bogusPacketOnRead);
		    rx_stats.bogusHost = from.sin_addr.s_addr;
		    MUTEX_EXIT(&rx_stats_mutex);
		}
		dpf(("B: bogus packet from [%x,%d] nb=%d",
		     from.sin_addr.s_addr, from.sin_port, nbytes));
	    }
	    return;
	} else {
	    /* Extract packet header. */
	    rxi_DecodePacketHeader(p);

	    host = from.sin_addr.s_addr;
	    port = from.sin_port;
	    if (p->header.type > 0 && p->header.type < RX_N_PACKET_TYPES) {
		if (rx_stats_active) {
		    rx_atomic_inc(&rx_stats.packetsRead[p->header.type - 1]);
		}
	    }

#ifdef RX_TRIMDATABUFS
	    /* Free any empty packet buffers at the end of this packet */
	    rxi_TrimDataBufs(p, 1);
#endif
	    /* receive pcket */
	    p = rxi_ReceivePacket(p, so, host, port, 0, 0);
	}
    }
    /* free packet? */
    if (p)
	rxi_FreePacket(p);

    return;
}
Example #4
0
static int
rxi_GetNextPacket(struct rx_call *call) {
    struct rx_packet *rp;
    int error;

    if (call->app.currentPacket != NULL) {
#ifdef RX_TRACK_PACKETS
	call->app.currentPacket->flags |= RX_PKTFLAG_CP;
#endif
	rxi_FreePacket(call->app.currentPacket);
	call->app.currentPacket = NULL;
    }

    if (opr_queue_IsEmpty(&call->rq))
	return 0;

    /* Check that next packet available is next in sequence */
    rp = opr_queue_First(&call->rq, struct rx_packet, entry);
    if (rp->header.seq != call->rnext)
	return 0;

    opr_queue_Remove(&rp->entry);
#ifdef RX_TRACK_PACKETS
    rp->flags &= ~RX_PKTFLAG_RQ;
#endif
#ifdef RXDEBUG_PACKET
    call->rqc--;
#endif /* RXDEBUG_PACKET */

    /* RXS_CheckPacket called to undo RXS_PreparePacket's work.  It may
     * reduce the length of the packet by up to conn->maxTrailerSize,
     * to reflect the length of the data + the header. */
    if ((error = RXS_CheckPacket(call->conn->securityObject, call, rp))) {
	/* Used to merely shut down the call, but now we shut down the whole
	 * connection since this may indicate an attempt to hijack it */

	MUTEX_EXIT(&call->lock);
	rxi_ConnectionError(call->conn, error);
	MUTEX_ENTER(&call->conn->conn_data_lock);
	rp = rxi_SendConnectionAbort(call->conn, rp, 0, 0);
	MUTEX_EXIT(&call->conn->conn_data_lock);
	rxi_FreePacket(rp);

	return error;
     }

    call->rnext++;
    call->app.currentPacket = rp;
#ifdef RX_TRACK_PACKETS
    call->app.currentPacket->flags |= RX_PKTFLAG_CP;
#endif
    call->app.curvec = 1;	/* 0th vec is always header */

    /* begin at the beginning [ more or less ], continue on until the end,
     * then stop. */
    call->app.curpos = (char *)call->app.currentPacket->wirevec[1].iov_base +
		   call->conn->securityHeaderSize;
    call->app.curlen = call->app.currentPacket->wirevec[1].iov_len -
		   call->conn->securityHeaderSize;

    call->app.nLeft = call->app.currentPacket->length;
    call->app.bytesRcvd += call->app.currentPacket->length;

    call->nHardAcks++;

    return 0;
}
Example #5
0
static int
rxi_WritevAlloc(struct rx_call *call, struct iovec *iov, int *nio, int maxio,
		int nbytes)
{
    struct rx_connection *conn = call->conn;
    struct rx_packet *cp;
    int requestCount;
    int nextio;
    /* Temporary values, real work is done in rxi_WritevProc */
    int tnFree;
    unsigned int tcurvec;
    char *tcurpos;
    int tcurlen;

    requestCount = nbytes;
    nextio = 0;

    /* Free any packets from the last call to ReadvProc/WritevProc */
    if (!opr_queue_IsEmpty(&call->app.iovq)) {
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->app.iovq);
    }

    if (call->app.mode != RX_MODE_SENDING) {
	if ((conn->type == RX_SERVER_CONNECTION)
	    && (call->app.mode == RX_MODE_RECEIVING)) {
	    call->app.mode = RX_MODE_SENDING;
	    if (call->app.currentPacket) {
#ifdef RX_TRACK_PACKETS
		call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
		rxi_FreePacket(call->app.currentPacket);
		call->app.currentPacket = NULL;
		call->app.nLeft = 0;
		call->app.nFree = 0;
	    }
	} else {
	    return 0;
	}
    }

    /* Set up the iovec to point to data in packet buffers. */
    tnFree = call->app.nFree;
    tcurvec = call->app.curvec;
    tcurpos = call->app.curpos;
    tcurlen = call->app.curlen;
    cp = call->app.currentPacket;
    do {
	int t;

	if (tnFree == 0) {
	    /* current packet is full, allocate a new one */
	    MUTEX_ENTER(&call->lock);
	    cp = rxi_AllocSendPacket(call, nbytes);
	    MUTEX_EXIT(&call->lock);
	    if (cp == NULL) {
		/* out of space, return what we have */
		*nio = nextio;
		return requestCount - nbytes;
	    }
#ifdef RX_TRACK_PACKETS
	    cp->flags |= RX_PKTFLAG_IOVQ;
#endif
	    opr_queue_Append(&call->app.iovq, &cp->entry);
#ifdef RXDEBUG_PACKET
            call->iovqc++;
#endif /* RXDEBUG_PACKET */
	    tnFree = cp->length;
	    tcurvec = 1;
	    tcurpos =
		(char *)cp->wirevec[1].iov_base +
		call->conn->securityHeaderSize;
	    tcurlen = cp->wirevec[1].iov_len - call->conn->securityHeaderSize;
	}

	if (tnFree < nbytes) {
	    /* try to extend the current packet */
	    int len, mud;
	    len = cp->length;
	    mud = rx_MaxUserDataSize(call);
	    if (mud > len) {
		int want;
		want = MIN(nbytes - tnFree, mud - len);
		rxi_AllocDataBuf(cp, want, RX_PACKET_CLASS_SEND_CBUF);
		if (cp->length > (unsigned)mud)
		    cp->length = mud;
		tnFree += (cp->length - len);
		if (cp == call->app.currentPacket) {
		    call->app.nFree += (cp->length - len);
		}
	    }
	}

	/* fill in the next entry in the iovec */
	t = MIN(tcurlen, nbytes);
	t = MIN(tnFree, t);
	iov[nextio].iov_base = tcurpos;
	iov[nextio].iov_len = t;
	nbytes -= t;
	tcurpos += t;
	tcurlen -= t;
	tnFree -= t;
	nextio++;

	if (!tcurlen) {
	    /* need to get another struct iov */
	    if (++tcurvec >= cp->niovecs) {
		/* current packet is full, extend it or move on to next packet */
		tnFree = 0;
	    } else {
		tcurpos = (char *)cp->wirevec[tcurvec].iov_base;
		tcurlen = cp->wirevec[tcurvec].iov_len;
	    }
	}
    } while (nbytes && nextio < maxio);
    *nio = nextio;
    return requestCount - nbytes;
}
Example #6
0
int
rxi_WriteProc(struct rx_call *call, char *buf,
	      int nbytes)
{
    struct rx_connection *conn = call->conn;
    unsigned int t;
    int requestCount = nbytes;

    /* Free any packets from the last call to ReadvProc/WritevProc */
    if (!opr_queue_IsEmpty(&call->app.iovq)) {
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->app.iovq);
    }

    if (call->app.mode != RX_MODE_SENDING) {
	if ((conn->type == RX_SERVER_CONNECTION)
	    && (call->app.mode == RX_MODE_RECEIVING)) {
	    call->app.mode = RX_MODE_SENDING;
	    if (call->app.currentPacket) {
#ifdef RX_TRACK_PACKETS
		call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
		rxi_FreePacket(call->app.currentPacket);
		call->app.currentPacket = NULL;
		call->app.nLeft = 0;
		call->app.nFree = 0;
	    }
	} else {
	    return 0;
	}
    }

    /* Loop condition is checked at end, so that a write of 0 bytes
     * will force a packet to be created--specially for the case where
     * there are 0 bytes on the stream, but we must send a packet
     * anyway. */
    do {
	if (call->app.nFree == 0) {
	    MUTEX_ENTER(&call->lock);
            if (call->error)
                call->app.mode = RX_MODE_ERROR;
	    if (!call->error && call->app.currentPacket) {
		clock_NewTime();	/* Bogus:  need new time package */
		/* The 0, below, specifies that it is not the last packet:
		 * there will be others. PrepareSendPacket may
		 * alter the packet length by up to
		 * conn->securityMaxTrailerSize */
		call->app.bytesSent += call->app.currentPacket->length;
		rxi_PrepareSendPacket(call, call->app.currentPacket, 0);
                /* PrepareSendPacket drops the call lock */
                rxi_WaitforTQBusy(call);
#ifdef RX_TRACK_PACKETS
		call->app.currentPacket->flags |= RX_PKTFLAG_TQ;
#endif
		opr_queue_Append(&call->tq,
				 &call->app.currentPacket->entry);
#ifdef RXDEBUG_PACKET
                call->tqc++;
#endif /* RXDEBUG_PACKET */
#ifdef RX_TRACK_PACKETS
                call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
                call->app.currentPacket = NULL;

		/* If the call is in recovery, let it exhaust its current
		 * retransmit queue before forcing it to send new packets
		 */
		if (!(call->flags & (RX_CALL_FAST_RECOVER))) {
		    rxi_Start(call, 0);
		}
	    } else if (call->app.currentPacket) {
#ifdef RX_TRACK_PACKETS
		call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
		rxi_FreePacket(call->app.currentPacket);
		call->app.currentPacket = NULL;
	    }
	    /* Wait for transmit window to open up */
	    while (!call->error
		   && call->tnext + 1 > call->tfirst + (2 * call->twind)) {
		clock_NewTime();
		call->startWait = clock_Sec();

#ifdef	RX_ENABLE_LOCKS
		CV_WAIT(&call->cv_twind, &call->lock);
#else
		call->flags |= RX_CALL_WAIT_WINDOW_ALLOC;
		osi_rxSleep(&call->twind);
#endif

		call->startWait = 0;
#ifdef RX_ENABLE_LOCKS
		if (call->error) {
                    call->app.mode = RX_MODE_ERROR;
		    MUTEX_EXIT(&call->lock);
		    return 0;
		}
#endif /* RX_ENABLE_LOCKS */
	    }
	    if ((call->app.currentPacket = rxi_AllocSendPacket(call, nbytes))) {
#ifdef RX_TRACK_PACKETS
		call->app.currentPacket->flags |= RX_PKTFLAG_CP;
#endif
		call->app.nFree = call->app.currentPacket->length;
		call->app.curvec = 1;	/* 0th vec is always header */
		/* begin at the beginning [ more or less ], continue
		 * on until the end, then stop. */
		call->app.curpos =
		    (char *) call->app.currentPacket->wirevec[1].iov_base +
		    call->conn->securityHeaderSize;
		call->app.curlen =
		    call->app.currentPacket->wirevec[1].iov_len -
		    call->conn->securityHeaderSize;
	    }
	    if (call->error) {
                call->app.mode = RX_MODE_ERROR;
		if (call->app.currentPacket) {
#ifdef RX_TRACK_PACKETS
		    call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
		    rxi_FreePacket(call->app.currentPacket);
		    call->app.currentPacket = NULL;
		}
		MUTEX_EXIT(&call->lock);
		return 0;
	    }
	    MUTEX_EXIT(&call->lock);
	}

	if (call->app.currentPacket && (int)call->app.nFree < nbytes) {
	    /* Try to extend the current buffer */
	    int len, mud;
	    len = call->app.currentPacket->length;
	    mud = rx_MaxUserDataSize(call);
	    if (mud > len) {
		int want;
		want = MIN(nbytes - (int)call->app.nFree, mud - len);
		rxi_AllocDataBuf(call->app.currentPacket, want,
				 RX_PACKET_CLASS_SEND_CBUF);
		if (call->app.currentPacket->length > (unsigned)mud)
		    call->app.currentPacket->length = mud;
		call->app.nFree += (call->app.currentPacket->length - len);
	    }
	}

	/* If the remaining bytes fit in the buffer, then store them
	 * and return.  Don't ship a buffer that's full immediately to
	 * the peer--we don't know if it's the last buffer yet */

	if (!call->app.currentPacket) {
	    call->app.nFree = 0;
	}

	while (nbytes && call->app.nFree) {

	    t = MIN((int)call->app.curlen, nbytes);
	    t = MIN((int)call->app.nFree, t);
	    memcpy(call->app.curpos, buf, t);
	    buf += t;
	    nbytes -= t;
	    call->app.curpos += t;
	    call->app.curlen -= (u_short)t;
	    call->app.nFree -= (u_short)t;

	    if (!call->app.curlen) {
		/* need to get another struct iov */
		if (++call->app.curvec >= call->app.currentPacket->niovecs) {
		    /* current packet is full, extend or send it */
		    call->app.nFree = 0;
		} else {
		    call->app.curpos =
			call->app.currentPacket->wirevec[call->app.curvec].iov_base;
		    call->app.curlen =
			call->app.currentPacket->wirevec[call->app.curvec].iov_len;
		}
	    }
	}			/* while bytes to send and room to send them */

	/* might be out of space now */
	if (!nbytes) {
	    return requestCount;
	} else;			/* more data to send, so get another packet and keep going */
    } while (nbytes);

    return requestCount - nbytes;
}
Example #7
0
/* rxi_ReadProc -- internal version.
 *
 * LOCKS USED -- called at netpri
 */
int
rxi_ReadProc(struct rx_call *call, char *buf,
	     int nbytes)
{
    int requestCount;
    int code;
    unsigned int t;

/* XXXX took out clock_NewTime from here.  Was it needed? */
    requestCount = nbytes;

    /* Free any packets from the last call to ReadvProc/WritevProc */
    if (!opr_queue_IsEmpty(&call->app.iovq)) {
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->app.iovq);
    }

    do {
	if (call->app.nLeft == 0) {
	    /* Get next packet */
	    MUTEX_ENTER(&call->lock);
	    for (;;) {
		if (call->error || (call->app.mode != RX_MODE_RECEIVING)) {
		    if (call->error) {
                        call->app.mode = RX_MODE_ERROR;
			MUTEX_EXIT(&call->lock);
			return 0;
		    }
		    if (call->app.mode == RX_MODE_SENDING) {
                        MUTEX_EXIT(&call->lock);
			rxi_FlushWrite(call);
                        MUTEX_ENTER(&call->lock);
			continue;
		    }
		}

		code = rxi_GetNextPacket(call);
		if (code)
		     return 0;

		if (call->app.currentPacket) {
		    if (!(call->flags & RX_CALL_RECEIVE_DONE)) {
			if (call->nHardAcks > (u_short) rxi_HardAckRate) {
			    rxi_CancelDelayedAckEvent(call);
			    rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
			} else {
			    /* Delay to consolidate ack packets */
			    rxi_PostDelayedAckEvent(call, &rx_hardAckDelay);
			}
		    }
		    break;
		}

                /*
                 * If we reach this point either we have no packets in the
                 * receive queue or the next packet in the queue is not the
                 * one we are looking for.  There is nothing else for us to
                 * do but wait for another packet to arrive.
                 */

		/* Are there ever going to be any more packets? */
		if (call->flags & RX_CALL_RECEIVE_DONE) {
		    MUTEX_EXIT(&call->lock);
		    return requestCount - nbytes;
		}
		/* Wait for in-sequence packet */
		call->flags |= RX_CALL_READER_WAIT;
		clock_NewTime();
		call->startWait = clock_Sec();
		while (call->flags & RX_CALL_READER_WAIT) {
#ifdef	RX_ENABLE_LOCKS
		    CV_WAIT(&call->cv_rq, &call->lock);
#else
		    osi_rxSleep(&call->rq);
#endif
		}

		call->startWait = 0;
#ifdef RX_ENABLE_LOCKS
		if (call->error) {
		    MUTEX_EXIT(&call->lock);
		    return 0;
		}
#endif /* RX_ENABLE_LOCKS */
	    }
	    MUTEX_EXIT(&call->lock);
	} else
	    /* osi_Assert(cp); */
	    /* MTUXXX  this should be replaced by some error-recovery code before shipping */
	    /* yes, the following block is allowed to be the ELSE clause (or not) */
	    /* It's possible for call->app.nLeft to be smaller than any particular
	     * iov_len.  Usually, recvmsg doesn't change the iov_len, since it
	     * reflects the size of the buffer.  We have to keep track of the
	     * number of bytes read in the length field of the packet struct.  On
	     * the final portion of a received packet, it's almost certain that
	     * call->app.nLeft will be smaller than the final buffer. */
	    while (nbytes && call->app.currentPacket) {
		t = MIN((int)call->app.curlen, nbytes);
		t = MIN(t, (int)call->app.nLeft);
		memcpy(buf, call->app.curpos, t);
		buf += t;
		nbytes -= t;
		call->app.curpos += t;
		call->app.curlen -= t;
		call->app.nLeft -= t;

		if (!call->app.nLeft) {
		    /* out of packet.  Get another one. */
#ifdef RX_TRACK_PACKETS
		    call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
		    rxi_FreePacket(call->app.currentPacket);
		    call->app.currentPacket = NULL;
		} else if (!call->app.curlen) {
		    /* need to get another struct iov */
		    if (++call->app.curvec >= call->app.currentPacket->niovecs) {
			/* current packet is exhausted, get ready for another */
			/* don't worry about curvec and stuff, they get set somewhere else */
#ifdef RX_TRACK_PACKETS
			call->app.currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
			rxi_FreePacket(call->app.currentPacket);
			call->app.currentPacket = NULL;
			call->app.nLeft = 0;
		    } else {
			call->app.curpos =
			    call->app.currentPacket->wirevec[call->app.curvec].iov_base;
			call->app.curlen =
			    call->app.currentPacket->wirevec[call->app.curvec].iov_len;
		    }
		}
	    }
	if (!nbytes) {
	    /* user buffer is full, return */
	    return requestCount;
	}

    } while (nbytes);

    return requestCount;
}
Example #8
0
static void
rxi_ListenerProc(fd_set * rfds, int *tnop, struct rx_call **newcallp)
{
    afs_uint32 host;
    u_short port;
    struct rx_packet *p = (struct rx_packet *)0;
    osi_socket socket;
    struct clock cv;
    afs_int32 nextPollTime;	/* time to next poll FD before sleeping */
    int lastPollWorked, doingPoll;	/* true iff last poll was useful */
    struct timeval tv, *tvp;
    int code;
#ifdef AFS_NT40_ENV
    int i;
#endif
    PROCESS pid;
    char name[MAXTHREADNAMELENGTH] = "srv_0";

    clock_NewTime();
    lastPollWorked = 0;
    nextPollTime = 0;
    code = LWP_CurrentProcess(&pid);
    if (code) {
	fprintf(stderr, "rxi_Listener: Can't get my pid.\n");
	exit(1);
    }
    rx_listenerPid = pid;
    if (swapNameProgram)
	(*swapNameProgram) (pid, "listener", &name[0]);

    for (;;) {
	/* Grab a new packet only if necessary (otherwise re-use the old one) */
	if (p) {
	    rxi_RestoreDataBufs(p);
	} else {
	    if (!(p = rxi_AllocPacket(RX_PACKET_CLASS_RECEIVE)))
		osi_Panic("rxi_ListenerProc: no packets!");	/* Shouldn't happen */
	}
	/* Wait for the next event time or a packet to arrive. */
	/* event_RaiseEvents schedules any events whose time has come and
	 * then atomically computes the time to the next event, guaranteeing
	 * that this is positive.  If there is no next event, it returns 0 */
	clock_NewTime();
	if (!rxevent_RaiseEvents(&cv))
	    tvp = NULL;
	else {
	    /* It's important to copy cv to tv, because the 4.3 documentation
	     * for select threatens that *tv may be updated after a select, in
	     * future editions of the system, to indicate how much of the time
	     * period has elapsed.  So we shouldn't rely on tv not being altered. */
	    tv.tv_sec = cv.sec;	/* Time to next event */
	    tv.tv_usec = cv.usec;
	    tvp = &tv;
	}
	rx_AtomicIncrement(rx_stats.selects, rx_stats_mutex);

	*rfds = rx_selectMask;

	if (lastPollWorked || nextPollTime < clock_Sec()) {
	    /* we're catching up, or haven't tried to for a few seconds */
	    doingPoll = 1;
	    nextPollTime = clock_Sec() + 4;	/* try again in 4 seconds no matter what */
	    tv.tv_sec = tv.tv_usec = 0;	/* make sure we poll */
	    tvp = &tv;
	    code = select((int)(rx_maxSocketNumber + 1), rfds, 0, 0, tvp);
	} else {
	    doingPoll = 0;
	    code = IOMGR_Select((int)(rx_maxSocketNumber + 1), rfds, 0, 0, tvp);
	}
	lastPollWorked = 0;	/* default is that it didn't find anything */

	if (quitListening) {
	    quitListening = 0;
	    LWP_DestroyProcess(pid);
	}

	switch (code) {
	case 0:
	    /* Timer interrupt:
	     * If it was a timer interrupt then we can assume that
	     * the time has advanced by roughly the value of the
	     * previous timeout, and that there is now at least
	     * one pending event.
	     */
	    clock_NewTime();
	    break;
	case -1:
	    /* select or IOMGR_Select returned failure */
	    debugSelectFailure++;	/* update debugging counter */
	    clock_NewTime();
	    break;
	case -2:
	    /* IOMGR_Cancel:
	     * IOMGR_Cancel is invoked whenever a new event is
	     * posted that is earlier than any existing events.
	     * So we re-evaluate the time, and then go back to
	     * reschedule events
	     */
	    clock_NewTime();
	    break;

	default:
	    /* Packets have arrived, presumably:
	     * If it wasn't a timer interrupt, then no event should have
	     * timed out yet (well some event may have, but only just...), so
	     * we don't bother looking to see if any have timed out, but just
	     * go directly to reading the data packets
	     */
	    clock_NewTime();
	    if (doingPoll)
		lastPollWorked = 1;
#ifdef AFS_NT40_ENV
	    for (i = 0; p && i < rfds->fd_count; i++) {
		socket = rfds->fd_array[i];
		if (rxi_ReadPacket(socket, p, &host, &port)) {
		    *newcallp = NULL;
		    p = rxi_ReceivePacket(p, socket, host, port, tnop,
					  newcallp);
		    if (newcallp && *newcallp) {
			if (p) {
			    rxi_FreePacket(p);
			}
			if (swapNameProgram) {
			    (*swapNameProgram) (rx_listenerPid, name, 0);
			    rx_listenerPid = 0;
			}
			return;
		    }
		}
	    }
#else
	    for (socket = rx_minSocketNumber;
		 p && socket <= rx_maxSocketNumber; socket++) {
		if (!FD_ISSET(socket, rfds))
		    continue;
		if (rxi_ReadPacket(socket, p, &host, &port)) {
		    p = rxi_ReceivePacket(p, socket, host, port, tnop,
					  newcallp);
		    if (newcallp && *newcallp) {
			if (p) {
			    rxi_FreePacket(p);
			}
			if (swapNameProgram) {
			    (*swapNameProgram) (rx_listenerPid, name, 0);
			    rx_listenerPid = 0;
			}
			return;
		    }
		}
	    }
#endif
	    break;
	}
    }
    /* NOTREACHED */
}
Example #9
0
static void
rxk_input(struct mbuf *am, int iphlen)
{
    void (*tproc) ();
    register unsigned short *tsp;
    int hdr;
    struct udphdr *tu;
    register struct ip *ti;
    struct udpiphdr *tvu;
    register int i;
    char *phandle;
    afs_int32 code;
    struct sockaddr_in taddr;
    int tlen;
    short port;
    int data_len, comp_sum;

    SPLVAR;
    NETPRI;

    /* make sure we have base ip and udp headers in first mbuf */
    if (iphlen > sizeof(struct ip)) {
	ip_stripoptions(am, NULL);
	iphlen = sizeof(struct ip);
    }

    if (am->m_len < sizeof(struct udpiphdr)) {
	am = m_pullup(am, sizeof(struct udpiphdr));
	if (!am) {
	    USERPRI;
	    return;
	}
    }

    ti = mtod(am, struct ip *);
    /* skip basic ip hdr */
    tu = (struct udphdr *)(((char *)ti) + sizeof(struct ip));

    /* now read the port out */
    port = tu->uh_dport;

    if (port) {
	for (tsp = rxk_ports, i = 0; i < MAXRXPORTS; i++) {
	    if (*tsp++ == port) {
		/* checksum the packet */
		/*
		 * Make mbuf data length reflect UDP length.
		 * If not enough data to reflect UDP length, drop.
		 */
		tvu = (struct udpiphdr *)ti;
		tlen = ntohs((u_short) tvu->ui_ulen);
		if ((int)ti->ip_len != tlen) {
		    if (tlen > (int)ti->ip_len) {
			m_free(am);
			USERPRI;
			return;
		    }
		    m_adj(am, tlen - (int)ti->ip_len);
		}
		/* deliver packet to rx */
		taddr.sin_family = AF_INET;	/* compute source address */
		taddr.sin_port = tu->uh_sport;
		taddr.sin_addr.s_addr = ti->ip_src.s_addr;
		taddr.sin_len = sizeof(taddr);
		tvu = (struct udpiphdr *)ti;	/* virtual udp structure, for cksum */
		/* handle the checksum.  Note that this code damages the actual ip
		 * header (replacing it with the virtual one, which is the same size),
		 * so we must ensure we get everything out we need, first */
		if (tu->uh_sum != 0) {
		    /* if the checksum is there, always check it. It's crazy not
		     * to, unless you can really be sure that your
		     * underlying network (and interfaces and drivers and
		     * DMA hardware, etc!) is error-free. First, fill
		     * in entire virtual ip header. */
		    memset(tvu->ui_i.ih_x1, 0, 9);
		    tvu->ui_len = tvu->ui_ulen;
		    tlen = ntohs((unsigned short)(tvu->ui_ulen));
		    if (in_cksum(am, sizeof(struct ip) + tlen)) {
			/* checksum, including cksum field, doesn't come out 0, so
			 * this packet is bad */
			m_freem(am);
			USERPRI;
			return;
		    }
		}

		/*
		 * 28 is IP (20) + UDP (8) header.  ulen includes
		 * udp header, and we *don't* tell RX about udp
		 * header either.  So, we remove those 8 as well.
		 */
		data_len = ntohs(tu->uh_ulen);
		data_len -= 8;
		if (!(*rxk_GetPacketProc) (&phandle, data_len)) {
		    if (rx_mb_to_packet(am, m_freem, 28, data_len, phandle)) {
			/* XXX should just increment counter here.. */
			printf("rx: truncated UDP packet\n");
			rxi_FreePacket(phandle);
		    } else
			(*rxk_PacketArrivalProc) (phandle, &taddr,
						  rxk_portRocks[i], data_len);
		} else
		    m_freem(am);
		USERPRI;
		return;
	    }
	}
    }

    /* if we get here, try to deliver packet to udp */
    if (tproc = parent_proto.pr_input)
	(*tproc) (am, iphlen);
    USERPRI;
    return;
}
Example #10
0
/* rxi_ReadProc -- internal version.
 *
 * LOCKS USED -- called at netpri
 */
int
rxi_ReadProc(struct rx_call *call, char *buf,
	     int nbytes)
{
    struct rx_packet *cp = call->currentPacket;
    struct rx_packet *rp;
    int requestCount;
    unsigned int t;

/* XXXX took out clock_NewTime from here.  Was it needed? */
    requestCount = nbytes;

    /* Free any packets from the last call to ReadvProc/WritevProc */
    if (queue_IsNotEmpty(&call->iovq)) {
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->iovq);
    }

    do {
	if (call->nLeft == 0) {
	    /* Get next packet */
	    MUTEX_ENTER(&call->lock);
	    for (;;) {
		if (call->error || (call->mode != RX_MODE_RECEIVING)) {
		    if (call->error) {
                        call->mode = RX_MODE_ERROR;
			MUTEX_EXIT(&call->lock);
			return 0;
		    }
		    if (call->mode == RX_MODE_SENDING) {
                        MUTEX_EXIT(&call->lock);
			rxi_FlushWrite(call);
                        MUTEX_ENTER(&call->lock);
			continue;
		    }
		}
		if (queue_IsNotEmpty(&call->rq)) {
		    /* Check that next packet available is next in sequence */
		    rp = queue_First(&call->rq, rx_packet);
		    if (rp->header.seq == call->rnext) {
			afs_int32 error;
			struct rx_connection *conn = call->conn;
			queue_Remove(rp);
#ifdef RX_TRACK_PACKETS
			rp->flags &= ~RX_PKTFLAG_RQ;
#endif
#ifdef RXDEBUG_PACKET
                        call->rqc--;
#endif /* RXDEBUG_PACKET */

			/* RXS_CheckPacket called to undo RXS_PreparePacket's
			 * work.  It may reduce the length of the packet by up
			 * to conn->maxTrailerSize, to reflect the length of the
			 * data + the header. */
			if ((error =
			     RXS_CheckPacket(conn->securityObject, call,
					     rp))) {
			    /* Used to merely shut down the call, but now we
			     * shut down the whole connection since this may
			     * indicate an attempt to hijack it */

			    MUTEX_EXIT(&call->lock);
			    rxi_ConnectionError(conn, error);
			    MUTEX_ENTER(&conn->conn_data_lock);
			    rp = rxi_SendConnectionAbort(conn, rp, 0, 0);
			    MUTEX_EXIT(&conn->conn_data_lock);
			    rxi_FreePacket(rp);

			    return 0;
			}
			call->rnext++;
			cp = call->currentPacket = rp;
#ifdef RX_TRACK_PACKETS
			call->currentPacket->flags |= RX_PKTFLAG_CP;
#endif
			call->curvec = 1;	/* 0th vec is always header */
			/* begin at the beginning [ more or less ], continue
			 * on until the end, then stop. */
			call->curpos =
			    (char *)cp->wirevec[1].iov_base +
			    call->conn->securityHeaderSize;
			call->curlen =
			    cp->wirevec[1].iov_len -
			    call->conn->securityHeaderSize;

			/* Notice that this code works correctly if the data
			 * size is 0 (which it may be--no reply arguments from
			 * server, for example).  This relies heavily on the
			 * fact that the code below immediately frees the packet
			 * (no yields, etc.).  If it didn't, this would be a
			 * problem because a value of zero for call->nLeft
			 * normally means that there is no read packet */
			call->nLeft = cp->length;
			hadd32(call->bytesRcvd, cp->length);

			/* Send a hard ack for every rxi_HardAckRate+1 packets
			 * consumed. Otherwise schedule an event to send
			 * the hard ack later on.
			 */
			call->nHardAcks++;
			if (!(call->flags & RX_CALL_RECEIVE_DONE)) {
			    if (call->nHardAcks > (u_short) rxi_HardAckRate) {
				rxevent_Cancel(call->delayedAckEvent, call,
					       RX_CALL_REFCOUNT_DELAY);
				rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0);
			    } else {
				struct clock when, now;
				clock_GetTime(&now);
				when = now;
				/* Delay to consolidate ack packets */
				clock_Add(&when, &rx_hardAckDelay);
				if (!call->delayedAckEvent
				    || clock_Gt(&call->delayedAckEvent->
						eventTime, &when)) {
				    rxevent_Cancel(call->delayedAckEvent,
						   call,
						   RX_CALL_REFCOUNT_DELAY);
                                    MUTEX_ENTER(&rx_refcnt_mutex);
				    CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
                                    MUTEX_EXIT(&rx_refcnt_mutex);
                                    call->delayedAckEvent =
				      rxevent_PostNow(&when, &now,
						     rxi_SendDelayedAck, call,
						     0);
				}
			    }
			}
			break;
		    }
		}

                /*
                 * If we reach this point either we have no packets in the
                 * receive queue or the next packet in the queue is not the
                 * one we are looking for.  There is nothing else for us to
                 * do but wait for another packet to arrive.
                 */

		/* Are there ever going to be any more packets? */
		if (call->flags & RX_CALL_RECEIVE_DONE) {
		    MUTEX_EXIT(&call->lock);
		    return requestCount - nbytes;
		}
		/* Wait for in-sequence packet */
		call->flags |= RX_CALL_READER_WAIT;
		clock_NewTime();
		call->startWait = clock_Sec();
		while (call->flags & RX_CALL_READER_WAIT) {
#ifdef	RX_ENABLE_LOCKS
		    CV_WAIT(&call->cv_rq, &call->lock);
#else
		    osi_rxSleep(&call->rq);
#endif
		}
                cp = call->currentPacket;

		call->startWait = 0;
#ifdef RX_ENABLE_LOCKS
		if (call->error) {
		    MUTEX_EXIT(&call->lock);
		    return 0;
		}
#endif /* RX_ENABLE_LOCKS */
	    }
	    MUTEX_EXIT(&call->lock);
	} else
	    /* osi_Assert(cp); */
	    /* MTUXXX  this should be replaced by some error-recovery code before shipping */
	    /* yes, the following block is allowed to be the ELSE clause (or not) */
	    /* It's possible for call->nLeft to be smaller than any particular
	     * iov_len.  Usually, recvmsg doesn't change the iov_len, since it
	     * reflects the size of the buffer.  We have to keep track of the
	     * number of bytes read in the length field of the packet struct.  On
	     * the final portion of a received packet, it's almost certain that
	     * call->nLeft will be smaller than the final buffer. */
	    while (nbytes && cp) {
		t = MIN((int)call->curlen, nbytes);
		t = MIN(t, (int)call->nLeft);
		memcpy(buf, call->curpos, t);
		buf += t;
		nbytes -= t;
		call->curpos += t;
		call->curlen -= t;
		call->nLeft -= t;

		if (!call->nLeft) {
		    /* out of packet.  Get another one. */
#ifdef RX_TRACK_PACKETS
		    call->currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
		    rxi_FreePacket(cp);
		    cp = call->currentPacket = (struct rx_packet *)0;
		} else if (!call->curlen) {
		    /* need to get another struct iov */
		    if (++call->curvec >= cp->niovecs) {
			/* current packet is exhausted, get ready for another */
			/* don't worry about curvec and stuff, they get set somewhere else */
#ifdef RX_TRACK_PACKETS
			call->currentPacket->flags &= ~RX_PKTFLAG_CP;
#endif
			rxi_FreePacket(cp);
			cp = call->currentPacket = (struct rx_packet *)0;
			call->nLeft = 0;
		    } else {
			call->curpos =
			    (char *)cp->wirevec[call->curvec].iov_base;
			call->curlen = cp->wirevec[call->curvec].iov_len;
		    }
		}
	    }
	if (!nbytes) {
	    /* user buffer is full, return */
	    return requestCount;
	}

    } while (nbytes);

    return requestCount;
}
Example #11
0
/* rxi_FillReadVec
 *
 * Uses packets in the receive queue to fill in as much of the
 * current iovec as possible. Does not block if it runs out
 * of packets to complete the iovec. Return true if an ack packet
 * was sent, otherwise return false */
int
rxi_FillReadVec(struct rx_call *call, afs_uint32 serial)
{
    int didConsume = 0;
    int didHardAck = 0;
    unsigned int t;
    struct rx_packet *rp;
    struct rx_packet *curp;
    struct iovec *call_iov;
    struct iovec *cur_iov = NULL;

    curp = call->currentPacket;
    if (curp) {
	cur_iov = &curp->wirevec[call->curvec];
    }
    call_iov = &call->iov[call->iovNext];

    while (!call->error && call->iovNBytes && call->iovNext < call->iovMax) {
	if (call->nLeft == 0) {
	    /* Get next packet */
	    if (queue_IsNotEmpty(&call->rq)) {
		/* Check that next packet available is next in sequence */
		rp = queue_First(&call->rq, rx_packet);
		if (rp->header.seq == call->rnext) {
		    afs_int32 error;
		    struct rx_connection *conn = call->conn;
		    queue_Remove(rp);
#ifdef RX_TRACK_PACKETS
		    rp->flags &= ~RX_PKTFLAG_RQ;
#endif
#ifdef RXDEBUG_PACKET
                    call->rqc--;
#endif /* RXDEBUG_PACKET */

		    /* RXS_CheckPacket called to undo RXS_PreparePacket's
		     * work.  It may reduce the length of the packet by up
		     * to conn->maxTrailerSize, to reflect the length of the
		     * data + the header. */
		    if ((error =
			 RXS_CheckPacket(conn->securityObject, call, rp))) {
			/* Used to merely shut down the call, but now we
			 * shut down the whole connection since this may
			 * indicate an attempt to hijack it */

			MUTEX_EXIT(&call->lock);
			rxi_ConnectionError(conn, error);
			MUTEX_ENTER(&conn->conn_data_lock);
			rp = rxi_SendConnectionAbort(conn, rp, 0, 0);
			MUTEX_EXIT(&conn->conn_data_lock);
			rxi_FreePacket(rp);
			MUTEX_ENTER(&call->lock);

			return 1;
		    }
		    call->rnext++;
		    curp = call->currentPacket = rp;
#ifdef RX_TRACK_PACKETS
		    call->currentPacket->flags |= RX_PKTFLAG_CP;
#endif
		    call->curvec = 1;	/* 0th vec is always header */
		    cur_iov = &curp->wirevec[1];
		    /* begin at the beginning [ more or less ], continue
		     * on until the end, then stop. */
		    call->curpos =
			(char *)curp->wirevec[1].iov_base +
			call->conn->securityHeaderSize;
		    call->curlen =
			curp->wirevec[1].iov_len -
			call->conn->securityHeaderSize;

		    /* Notice that this code works correctly if the data
		     * size is 0 (which it may be--no reply arguments from
		     * server, for example).  This relies heavily on the
		     * fact that the code below immediately frees the packet
		     * (no yields, etc.).  If it didn't, this would be a
		     * problem because a value of zero for call->nLeft
		     * normally means that there is no read packet */
		    call->nLeft = curp->length;
		    hadd32(call->bytesRcvd, curp->length);

		    /* Send a hard ack for every rxi_HardAckRate+1 packets
		     * consumed. Otherwise schedule an event to send
		     * the hard ack later on.
		     */
		    call->nHardAcks++;
		    didConsume = 1;
		    continue;
		}
	    }
	    break;
	}

	/* It's possible for call->nLeft to be smaller than any particular
	 * iov_len.  Usually, recvmsg doesn't change the iov_len, since it
	 * reflects the size of the buffer.  We have to keep track of the
	 * number of bytes read in the length field of the packet struct.  On
	 * the final portion of a received packet, it's almost certain that
	 * call->nLeft will be smaller than the final buffer. */
	while (call->iovNBytes && call->iovNext < call->iovMax && curp) {

	    t = MIN((int)call->curlen, call->iovNBytes);
	    t = MIN(t, (int)call->nLeft);
	    call_iov->iov_base = call->curpos;
	    call_iov->iov_len = t;
	    call_iov++;
	    call->iovNext++;
	    call->iovNBytes -= t;
	    call->curpos += t;
	    call->curlen -= t;
	    call->nLeft -= t;

	    if (!call->nLeft) {
		/* out of packet.  Get another one. */
#ifdef RX_TRACK_PACKETS
                curp->flags &= ~RX_PKTFLAG_CP;
                curp->flags |= RX_PKTFLAG_IOVQ;
#endif
		queue_Append(&call->iovq, curp);
#ifdef RXDEBUG_PACKET
                call->iovqc++;
#endif /* RXDEBUG_PACKET */
		curp = call->currentPacket = (struct rx_packet *)0;
	    } else if (!call->curlen) {
		/* need to get another struct iov */
		if (++call->curvec >= curp->niovecs) {
		    /* current packet is exhausted, get ready for another */
		    /* don't worry about curvec and stuff, they get set somewhere else */
#ifdef RX_TRACK_PACKETS
		    curp->flags &= ~RX_PKTFLAG_CP;
		    curp->flags |= RX_PKTFLAG_IOVQ;
#endif
		    queue_Append(&call->iovq, curp);
#ifdef RXDEBUG_PACKET
                    call->iovqc++;
#endif /* RXDEBUG_PACKET */
		    curp = call->currentPacket = (struct rx_packet *)0;
		    call->nLeft = 0;
		} else {
		    cur_iov++;
		    call->curpos = (char *)cur_iov->iov_base;
		    call->curlen = cur_iov->iov_len;
		}
	    }
	}
    }

    /* If we consumed any packets then check whether we need to
     * send a hard ack. */
    if (didConsume && (!(call->flags & RX_CALL_RECEIVE_DONE))) {
	if (call->nHardAcks > (u_short) rxi_HardAckRate) {
	    rxevent_Cancel(call->delayedAckEvent, call,
			   RX_CALL_REFCOUNT_DELAY);
	    rxi_SendAck(call, 0, serial, RX_ACK_DELAY, 0);
	    didHardAck = 1;
	} else {
	    struct clock when, now;
	    clock_GetTime(&now);
	    when = now;
	    /* Delay to consolidate ack packets */
	    clock_Add(&when, &rx_hardAckDelay);
	    if (!call->delayedAckEvent
		|| clock_Gt(&call->delayedAckEvent->eventTime, &when)) {
		rxevent_Cancel(call->delayedAckEvent, call,
			       RX_CALL_REFCOUNT_DELAY);
                MUTEX_ENTER(&rx_refcnt_mutex);
		CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY);
                MUTEX_EXIT(&rx_refcnt_mutex);
		call->delayedAckEvent =
		    rxevent_PostNow(&when, &now, rxi_SendDelayedAck, call, 0);
	    }
	}
    }
    return didHardAck;
}
Example #12
0
/* rxi_WritevProc -- internal version.
 *
 * Send buffers allocated in rxi_WritevAlloc.
 *
 * LOCKS USED -- called at netpri.
 */
int
rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes)
{
    struct rx_packet *cp = NULL;
#ifdef RX_TRACK_PACKETS
    struct rx_packet *p, *np;
#endif
    int nextio;
    int requestCount;
    struct rx_queue tmpq;
#ifdef RXDEBUG_PACKET
    u_short tmpqc;
#endif

    requestCount = nbytes;
    nextio = 0;

    MUTEX_ENTER(&call->lock);
    if (call->error) {
        call->mode = RX_MODE_ERROR;
    } else if (call->mode != RX_MODE_SENDING) {
	call->error = RX_PROTOCOL_ERROR;
    }
#ifdef AFS_GLOBAL_RXLOCK_KERNEL
    rxi_WaitforTQBusy(call);
#endif /* AFS_GLOBAL_RXLOCK_KERNEL */
    cp = call->currentPacket;

    if (call->error) {
        call->mode = RX_MODE_ERROR;
	MUTEX_EXIT(&call->lock);
	if (cp) {
#ifdef RX_TRACK_PACKETS
            cp->flags &= ~RX_PKTFLAG_CP;
            cp->flags |= RX_PKTFLAG_IOVQ;
#endif
	    queue_Prepend(&call->iovq, cp);
#ifdef RXDEBUG_PACKET
            call->iovqc++;
#endif /* RXDEBUG_PACKET */
	    call->currentPacket = (struct rx_packet *)0;
	}
#ifdef RXDEBUG_PACKET
        call->iovqc -=
#endif /* RXDEBUG_PACKET */
            rxi_FreePackets(0, &call->iovq);
	return 0;
    }

    /* Loop through the I/O vector adjusting packet pointers.
     * Place full packets back onto the iovq once they are ready
     * to send. Set RX_PROTOCOL_ERROR if any problems are found in
     * the iovec. We put the loop condition at the end to ensure that
     * a zero length write will push a short packet. */
    nextio = 0;
    queue_Init(&tmpq);
#ifdef RXDEBUG_PACKET
    tmpqc = 0;
#endif /* RXDEBUG_PACKET */
    do {
	if (call->nFree == 0 && cp) {
	    clock_NewTime();	/* Bogus:  need new time package */
	    /* The 0, below, specifies that it is not the last packet:
	     * there will be others. PrepareSendPacket may
	     * alter the packet length by up to
	     * conn->securityMaxTrailerSize */
	    hadd32(call->bytesSent, cp->length);
	    rxi_PrepareSendPacket(call, cp, 0);
	    queue_Append(&tmpq, cp);
#ifdef RXDEBUG_PACKET
            tmpqc++;
#endif /* RXDEBUG_PACKET */
            cp = call->currentPacket = (struct rx_packet *)0;

	    /* The head of the iovq is now the current packet */
	    if (nbytes) {
		if (queue_IsEmpty(&call->iovq)) {
                    MUTEX_EXIT(&call->lock);
		    call->error = RX_PROTOCOL_ERROR;
#ifdef RXDEBUG_PACKET
                    tmpqc -=
#endif /* RXDEBUG_PACKET */
                        rxi_FreePackets(0, &tmpq);
		    return 0;
		}
		cp = queue_First(&call->iovq, rx_packet);
		queue_Remove(cp);
#ifdef RX_TRACK_PACKETS
                cp->flags &= ~RX_PKTFLAG_IOVQ;
#endif
#ifdef RXDEBUG_PACKET
                call->iovqc--;
#endif /* RXDEBUG_PACKET */
#ifdef RX_TRACK_PACKETS
                cp->flags |= RX_PKTFLAG_CP;
#endif
		call->currentPacket = cp;
		call->nFree = cp->length;
		call->curvec = 1;
		call->curpos =
		    (char *)cp->wirevec[1].iov_base +
		    call->conn->securityHeaderSize;
		call->curlen =
		    cp->wirevec[1].iov_len - call->conn->securityHeaderSize;
	    }
	}

	if (nbytes) {
	    /* The next iovec should point to the current position */
	    if (iov[nextio].iov_base != call->curpos
		|| iov[nextio].iov_len > (int)call->curlen) {
		call->error = RX_PROTOCOL_ERROR;
                MUTEX_EXIT(&call->lock);
		if (cp) {
#ifdef RX_TRACK_PACKETS
		    cp->flags &= ~RX_PKTFLAG_CP;
#endif
                    queue_Prepend(&tmpq, cp);
#ifdef RXDEBUG_PACKET
                    tmpqc++;
#endif /* RXDEBUG_PACKET */
                    cp = call->currentPacket = (struct rx_packet *)0;
		}
#ifdef RXDEBUG_PACKET
                tmpqc -=
#endif /* RXDEBUG_PACKET */
                    rxi_FreePackets(0, &tmpq);
		return 0;
	    }
	    nbytes -= iov[nextio].iov_len;
	    call->curpos += iov[nextio].iov_len;
	    call->curlen -= iov[nextio].iov_len;
	    call->nFree -= iov[nextio].iov_len;
	    nextio++;
	    if (call->curlen == 0) {
		if (++call->curvec > cp->niovecs) {
		    call->nFree = 0;
		} else {
		    call->curpos = (char *)cp->wirevec[call->curvec].iov_base;
		    call->curlen = cp->wirevec[call->curvec].iov_len;
		}
	    }
	}
    } while (nbytes && nextio < nio);

    /* Move the packets from the temporary queue onto the transmit queue.
     * We may end up with more than call->twind packets on the queue. */

#ifdef RX_TRACK_PACKETS
    for (queue_Scan(&tmpq, p, np, rx_packet))
    {
        p->flags |= RX_PKTFLAG_TQ;
    }
#endif

    if (call->error)
        call->mode = RX_MODE_ERROR;

    queue_SpliceAppend(&call->tq, &tmpq);

    if (!(call->flags & (RX_CALL_FAST_RECOVER | RX_CALL_FAST_RECOVER_WAIT))) {
	rxi_Start(0, call, 0, 0);
    }

    /* Wait for the length of the transmit queue to fall below call->twind */
    while (!call->error && call->tnext + 1 > call->tfirst + (2 * call->twind)) {
	clock_NewTime();
	call->startWait = clock_Sec();
#ifdef	RX_ENABLE_LOCKS
	CV_WAIT(&call->cv_twind, &call->lock);
#else
	call->flags |= RX_CALL_WAIT_WINDOW_ALLOC;
	osi_rxSleep(&call->twind);
#endif
	call->startWait = 0;
    }

    /* cp is no longer valid since we may have given up the lock */
    cp = call->currentPacket;

    if (call->error) {
        call->mode = RX_MODE_ERROR;
        call->currentPacket = NULL;
        MUTEX_EXIT(&call->lock);
	if (cp) {
#ifdef RX_TRACK_PACKETS
	    cp->flags &= ~RX_PKTFLAG_CP;
#endif
	    rxi_FreePacket(cp);
	}
	return 0;
    }
    MUTEX_EXIT(&call->lock);

    return requestCount - nbytes;
}