V9/sys/inet/udp_device.c

Compare this file to the similar file:
Show the results in this format:

/*
 * udp device driver.
 */

#include "udp.h"
#if NUDP
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/stream.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/inet/in.h"
#include "../h/inet/ip.h"
#include "../h/inet/ip_var.h"
#include "../h/inet/udp.h"
#include "../h/inet/udp_var.h"
#include "../h/inet/udp_user.h"

int	nodev(), udpdopen(), udpdclose(), udpdput();
int	udpdosrv();
static	struct qinit udpdrinit = { nodev, NULL, udpdopen, udpdclose, 0, 0 };
static	struct qinit udpdwinit = { udpdput, udpdosrv, udpdopen, udpdclose,
				   UDP_BODY_LIMIT, 64 };
struct	streamtab udpdinfo = { &udpdrinit, &udpdwinit };

int Nudp = NUDP;	/* for netstat */
struct udp udpconn[NUDP];

in_addr udpbaddst;

udpdopen(q, dev)
register struct queue *q;
dev_t dev;
{
	struct udp *udp;

	dev = minor(dev);
	if(dev >= NUDP)
		return(0);
	udp = &udpconn[dev];
	if(q->ptr)
		return(0);
	udp->rq = q;
	udp->flags = UDP_INIT;
	udp->head = udp->tail = 0;
	q->ptr = (caddr_t)udp;
	q->flag |= QDELIM;
	WR(q)->flag |= QBIGB;
	WR(q)->ptr = (caddr_t)udp;
	return(1);
}

udpdclose(q)
register struct queue *q;
{
	struct udp *udp;

	udp = (struct udp *)q->ptr;
	udp->rq = (struct queue *)0;
	q->ptr = (caddr_t)0;
}

udpdput(q, bp)
register struct queue *q;
register struct block *bp;
{

	switch(bp->type){
	case M_IOCTL:
		bp->type = M_IOCNAK;
		bp->wptr = bp->rptr;
		qreply(q, bp);
		return;
	case M_DATA:
		putq(q, bp);
		break;
	case M_DELIM:
		putq(q, bp);
		qenable(q);
		break;
	default:
		freeb(bp);
		break;
	}
}

udpdosrv(q)
register struct queue *q;
{
	register struct block *bp;
	register struct udp *udp;

	udp = (struct udp *)q->ptr;
	while(bp = getq(q)){
		bp->next = 0;
		if(bp->type != M_DATA) {
			freeb(bp);
			if (udp->flags & UDP_INIT)
				udpduser(udp->head, udp);
			else {
				if ((udp->flags & UDP_CONNECTED) == 0) {
					/* get user-supplied destination */
					if(udpdaddr(udp)) {
						bp_free(udp->head);
						udp->head = udp->tail = NULL;
						return;
					}
				} else if (udp->head == NULL) {
					/* zero length message */
					udp->head = allocb(0);
				}
				udp_output(udp->head, udp);
			}
			udp->head = udp->tail = NULL;
		} else if(udp->head == 0){
			udp->head = udp->tail = bp;
		} else {
			udp->tail->next = bp;
			udp->tail = bp;
		}
	}
}

/* extract address from a message */
udpdaddr(udp)
register struct udp *udp;
{
	struct udpaddr *uap;

	/* get destination */
	if((udp->head = bp_pullup(udp->head, sizeof(struct udpaddr))) == NULL)
		return(1);
	uap = (struct udpaddr *)udp->head->rptr;
	udp->dst = uap->host;
	udp->dport = uap->port;
	udp->head->rptr += sizeof(struct udpaddr);

	/* host address for datagrams depends on route */
	if ((udp->flags & UDP_CONNECTED) == 0) {
		udp->src = ip_hoston(udp->dst);
		if(udp->src == 0) {
			udpbaddst = udp->dst;
			return(1);
		}
	}
	return(0);
}

udpdrint(bp, ui)
register struct block *bp;
struct udpiphdr *ui;
{
	register struct block *bp1;
	register struct queue *q;
	register struct udp *udp;
	register struct udpaddr *uap;
	int	ps = spl6();

	/* find the udp port */
	for (udp = udpconn; udp < &udpconn[NUDP]; ++udp) {
		if (udp->rq == 0)
			continue;
		if (udp->sport != ui->ui_dport)
			continue;
		if (udp->flags & UDP_LISTEN)
			break;
		if ((udp->flags & UDP_CONNECTED) == 0)
			break;
		if (udp->dport == ui->ui_sport && udp->dst == ui->ui_src)
			break;
	}

	if (udp == &udpconn[NUDP]) {
		bp_free(bp);
		splx(ps);
		return;
	}
	if (udp->flags & UDP_LISTEN) {
		udp->dport = ui->ui_sport;
		udp->dst = ui->ui_src;
		if (udp->src == 0) {
			/* use host name connecting end called us */
			udp->src = ui->ui_dst;
		}
		udp->flags |= UDP_CONNECTED;
		udp->flags &= ~UDP_LISTEN;
		udpreply(udp);
	}
	splx(ps);

	if((q = udp->rq) == NULL) {
		printf("udpdrint but no read queue\n");
		bp_free(bp);
		return;
	}
	if(q->next->flag & QFULL) {
		bp_free(bp);
		return;
	}
	if ((udp->flags & UDP_CONNECTED) == 0) {
		/* if not acting as a connected protocol, pass user source addr */
		bp1 = allocb(sizeof(struct udpaddr));
		if (bp1 == NULL) {
			printf("udprint: out of blocks\n");
			bp_free(bp);
			return;
		}
		bp1->wptr += sizeof(struct udpaddr);
		uap = (struct udpaddr *)bp1->rptr;
		uap->host = ui->ui_src;
		uap->port = ui->ui_sport;
		putq(q->next, bp1);
	}
	while(bp){
		bp1 = bp->next;
		putq(q->next, bp);
		bp = bp1;
	}
	putctl(q->next, M_DELIM);
}

/*
 * imitation udp_usrreq
 */
udpduser(bp, udp)
register struct block *bp;
register struct udp *udp;
{
	struct udpuser *uu;

	if ((bp = bp_pullup(bp, sizeof(struct udpuser))) == 0) {
		printf("udpuser short\n");
		bp_free(bp);
		return;
	}
	uu = (struct udpuser *)bp->rptr;
	switch(uu->cmd){
	case UDPC_DATAGRAM:
		if (udpbind(udp, uu->sport))
			goto bad;
		udp->flags &= ~UDP_INIT;
		udpreply(udp);
		break;
	case UDPC_CONNECT:
		if (udpbind(udp, uu->sport))
			goto bad;
		udp->dport = uu->dport;
		udp->dst = uu->dst;
		udp->flags |= UDP_CONNECTED;
		udp->flags &= ~UDP_INIT;
		udpreply(udp);
		/* pick a local address related to the destination */
		udp->src = ip_hoston(udp->dst);
		if(udp->src == 0)
			goto bad;
		break;
	case UDPC_LISTEN:
		if (udpbind(udp, uu->sport))
			goto bad;
		udp->flags |= UDP_LISTEN;
		udp->flags &= ~UDP_INIT;
		break;
	default:
bad:		putctl(udp->rq->next, M_HANGUP);
		return;
	}
	bp_free(bp);
}

udpreply(udp)
register struct udp *udp;
{
	register struct queue *q;
	struct udpreply ur;

	ur.reply = UDPR_OK;
	ur.dport = udp->dport;
	ur.dst = udp->dst;
	ur.udpdev = udp - udpconn;

	q = udp->rq;
	putcpy(q->next, &ur, sizeof(ur));
	putctl(q->next, M_DELIM);
}

udpbind(udp, port)
register struct udp *udp;
register int port;
{
	register struct udp *udp2;
	static udp_port udpport = 600;

	udp->sport = 0;
	if(port){
		for(udp2 = &udpconn[0]; udp2 < &udpconn[NUDP]; udp2++){
			if(udp2->rq == 0)
				continue;
			if(udp2->sport == port)
				return(1);
		}
		udp->sport = port;
		return(0);
	}

	/* pick one for him */
	if(udpport >= 1024)
		udpport = 600;
	port = udpport;
	while(1){
		if(udpbind(udp, udpport) == 0){
			udpport++;
			return(0);
		}
		udpport++;
		if(udpport >= 1024)
			udpport = 600;
		if(udpport == port)	/* tried them all */
			break;
	}
	return(1);
}

#endif