V10/lsys/inet/udp_device.c
/*
* udp device driver.
*/
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/conf.h"
#include "sys/inet/in.h"
#include "sys/inet/ip.h"
#include "sys/inet/ip_var.h"
#include "sys/inet/udp.h"
#include "sys/inet/udp_var.h"
#include "sys/inet/udp_user.h"
int udpprintfs;
long udpdopen();
int udpdclose(), udpdput();
int udpdosrv();
static struct qinit udpdrinit = { noput, NULL, udpdopen, udpdclose, 0, 0 };
static struct qinit udpdwinit = { udpdput, udpdosrv, udpdopen, udpdclose,
UDP_BODY_LIMIT, 64 };
struct streamtab udpdinfo = { &udpdrinit, &udpdwinit };
struct cdevsw udpcdev = cstrinit(&udpdinfo);
extern int udpcnt; /* for netstat */
extern struct udp udpconn[];
in_addr udpbaddst;
long
udpdopen(q, dev)
register struct queue *q;
dev_t dev;
{
register struct udp *udp;
dev = minor(dev);
if(dev >= udpcnt)
return(0);
udp = &udpconn[dev];
if(q->ptr)
return(0);
udp->rq = q;
udp->flags = UDP_INIT;
udp->head = udp->tail = NULL;
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);
if (bp->class&S_DELIM)
qenable(q);
return;
default:
freeb(bp);
return;
}
}
udpdosrv(q)
register struct queue *q;
{
register struct block *bp;
register struct udp *udp;
udp = (struct udp *)q->ptr;
while(bp = getq(q)){
if (bp->type != M_DATA)
panic("udpdosrv");
bp->next = NULL;
if (udp->head == NULL)
udp->head = bp;
else
udp->tail->next = bp;
udp->tail = bp;
if ((bp->class & S_DELIM) == 0)
continue;
/*
* have entire datagram
*/
bp->class &=~ S_DELIM;
if (udp->flags & UDP_INIT)
udpduser(udp->head, udp);
else {
if ((udp->flags & UDP_CONNECTED) == 0
&& udpdaddr(udp) == 0)
continue; /* couldn't get an addr */
udp_output(udp->head, udp);
}
udp->head = udp->tail = NULL;
}
}
/*
* extract address from an outbound datagram
* return 0 if can't find one
* -- if return 0, have freed bad message and cleared udp->head
*/
udpdaddr(udp)
register struct udp *udp;
{
register struct udpaddr *uap;
/* get destination */
if((udp->head = bp_pullup(udp->head, sizeof(struct udpaddr))) == NULL)
return(0);
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;
bp_free(udp->head);
udp->head = NULL;
return(0);
}
}
return(1);
}
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;
register struct udp *udpend;
int ps = spl6();
/* find the udp port */
udpend = &udpconn[udpcnt];
for (udp = udpconn; udp < udpend; ++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 >= udpend) {
if(udpprintfs)
printf("no udpconn: %d\n", ui->ui_dport);
udpstat.udps_badport++;
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) {
udpstat.udps_inqfull++;
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("udpdrint: 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;
(*q->next->qinfo->putp)(q->next, bp1);
}
while(bp){
bp1 = bp->next;
if (bp1==NULL)
bp->class |= S_DELIM;
(*q->next->qinfo->putp)(q->next, bp);
bp = bp1;
}
}
/*
* first write on this device:
* data is a command from the user
*/
udpduser(bp, udp)
register struct block *bp;
register struct udp *udp;
{
register 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));
putctld(q->next, M_DATA);
}
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[udpcnt]; 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);
}