|
|
researchv10 Norman
/*
* tcp_device.c
*/
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/inio.h"
#include "sys/ttyio.h"
#include "sys/conf.h"
#include "sys/user.h"
#include "sys/inet/in.h"
#include "sys/inet/ip_var.h"
#include "sys/inet/tcp.h"
#include "sys/inet/tcp_timer.h"
#include "sys/inet/tcp_seq.h"
#include "sys/inet/tcp_var.h"
#include "sys/inet/tcpip.h"
#include "sys/inet/tcp_user.h"
#include "sys/inet/tcp_fsm.h"
extern int tcp_busy; /* set to discourage timers & ensuing panics */
extern int tcp_maxseg;
long tcpdopen();
int tcpdclose(), tcpdput();
int tcpdosrv(), tcpdisrv();
static struct qinit tcpdrinit = { noput, tcpdisrv, tcpdopen, tcpdclose, 2048, 64 };
struct qinit tcpdwinit = { tcpdput, tcpdosrv, tcpdopen, tcpdclose, 2048, 64};
struct streamtab tcpdinfo = { &tcpdrinit, &tcpdwinit };
struct cdevsw tcpcdev = cstrinit(&tcpdinfo);
extern int tcpcnt;
extern struct tcpcb tcpcb[];
long
tcpdopen(q, dev)
register struct queue *q;
dev_t dev;
{
struct tcpcb *tp;
dev = minor(dev);
if(dev >= tcpcnt)
return(0);
tp = &tcpcb[dev];
if(dev&01){
/* outgoing channel */
/*
* disallow if active
*/
if(tp->t_state != TCPS_CLOSED)
return(0);
/*
* only one opener at a time for outgoing channels
*/
if(q->ptr)
return(0);
} else {
/* incoming channel */
/*
* disallow if inactive
*/
if(tp->t_state == TCPS_CLOSED) {
printf("tcp%d closed\n", dev);
return(0);
}
/*
* disallow if it's not open or waiting to be opened
*/
if((tp->so_state&(SS_OPEN|SS_PLEASEOPEN))==0) {
printf("tcp%d state %x\n", dev, tp->so_state);
return(0);
}
/*
* if already open, just accept
*/
if(q->ptr)
return(1);
}
tcp_busy++;
if((tp->so_state & SS_PLEASEOPEN) == 0){
tp->so_rcount = tp->so_wcount = tp->so_options = 0;
tp->so_laddr = tp->so_faddr = (in_addr)0;
tp->so_lport = tp->so_fport = (tcp_port)0;
tp->so_oobmark = tp->so_delimcnt = 0;
tp->so_head = (struct tcpcb *)0;
tp->so_state = SS_WAITING;
tp->t_maxseg = TCP_DEFMAXSEG; /* until changed by other end */
}
tp->so_state |= SS_OPEN;
tp->so_dev = dev;
tp->so_rq = q;
tp->so_wq = WR(q);
q->ptr = (caddr_t)tp;
WR(q)->flag |= QBIGB;
WR(q)->ptr = (caddr_t)tp;
--tcp_busy;
if(tp->so_state & SS_PLEASEOPEN){
tp->so_state &= ~SS_PLEASEOPEN;
qenable(WR(q)); /* to force out rcv wnd update */
}
return(1);
}
tcpdclose(q)
register struct queue *q;
{
struct tcpcb *tp;
tp = (struct tcpcb *)q->ptr;
tcp_busy++;
if(tp == 0){
--tcp_busy;
return;
}
tp->so_state &= ~(SS_OPEN|SS_WAITING);
tp->so_rq = tp->so_wq = (struct queue *) 0;
tp->so_wcount = tp->so_rcount = 0;
if(tp->t_state > TCPS_LISTEN)
tcp_disconnect(tp);
else
tcp_close(tp);
--tcp_busy;
}
tcpdput(q, bp)
register struct queue *q;
register struct block *bp;
{
int s, x;
struct tcpcb *tp;
struct block *nbp;
struct foo {
int com;
struct tcpuser rep;
};
struct foo *fp;
tp = (struct tcpcb *)q->ptr;
switch(bp->type){
case M_IOCTL:
bp->type = M_IOCACK;
switch(stiocom(bp)){
case TIOCSETP:
case TIOCSETN:
x = ((struct ttydevb *)stiodata(bp))->ispeed;
bp->wptr = bp->rptr;
bp->type = M_IOCACK;
qreply(q, bp);
if(x == 0)
qpctl(OTHERQ(q), M_HANGUP);
return;
case TIOCGETP:
((struct ttydevb *)stiodata(bp))->ispeed =
((struct ttydevb *)stiodata(bp))->ospeed = B9600;
break;
case TCPGETADDR:
nbp = allocb(sizeof(struct foo));
if (nbp == NULL) {
bp->type = M_IOCNAK;
break;
}
nbp->type = M_IOCACK;
fp = (struct foo *)nbp->rptr;
fp->com = stiocom(bp);
fp->rep.lport = tp->so_lport;
fp->rep.fport = tp->so_fport;
fp->rep.laddr = tp->so_laddr;
fp->rep.faddr = tp->so_faddr;
nbp->wptr = nbp->rptr + sizeof(struct foo);
freeb(bp);
bp = nbp;
break;
case TCPIOHUP:
tp->so_state |= SS_HANGUP;
bp->wptr = bp->rptr;
bp->type = M_IOCACK;
qreply(q, bp);
return;
default:
bp->type = M_IOCNAK;
}
qreply(q, bp);
return;
case M_DATA:
if(socantsendmore(tp)){
freeb(bp);
return;
}
s = spl6();
if (bp->wptr!=bp->rptr)
tp->so_delimcnt = 0;
if (bp->class&S_DELIM)
tp->so_delimcnt++; /* 2 delims are logical EOF */
if (bp->wptr==bp->rptr){
freeb(bp);
} else {
tp->so_wcount += BLEN(bp); /* BEFORE the putq */
putq(q, bp);
}
splx(s);
if(tp->so_delimcnt
|| (tp->so_options&(SO_ACCEPTCONN|SS_WAITING)) == 0)
qenable(q);
break;
default:
freeb(bp);
break;
}
}
tcpdosrv(q)
struct queue *q;
{
register struct tcpcb *tp;
if((tp = (struct tcpcb *)q->ptr) == 0)
return;
if (tp->so_state&SS_WCLOSED)
return;
tcp_busy++;
if(tp->so_delimcnt > 1){
tp->so_state |= SS_WCLOSED;
tp = tcp_usrclosed(tp);
if(tp)
tcp_output(tp);
} else if ((tp->so_options & SO_ACCEPTCONN) == 0
&& (tp->so_state & SS_WAITING) == 0)
tcp_output(tp);
else
tcpduser(tp);
--tcp_busy;
}
tcpdrint(bp, tp)
register struct block *bp;
register struct tcpcb *tp;
{
register struct block *bp1;
if (tp->so_rq == NULL) {
printf("tcpdrint but no tp->so_rq\n");
bp_free(bp);
return -1;
}
while(bp){
bp1 = bp->next;
tp->so_rcount += bp->wptr - bp->rptr;
if(bp->wptr <= bp->rptr)
freeb(bp);
else
putq(tp->so_rq, bp);
bp = bp1;
}
return 0;
}
tcpdisrv(q)
register struct queue *q;
{
register struct tcpcb *tp = (struct tcpcb *)(q->ptr);
register struct block *bp;
while ((q->next->flag&QFULL) == 0 && (bp = getq(q)) != NULL) {
if(bp->type == M_DATA)
tp->so_rcount -= bp->wptr - bp->rptr;
if(tp->so_rcount < 0){
printf("so_rcount %d\n", tp->so_rcount);
if(tp->so_state & SS_OPEN)
panic("so_rcount");
}
(*q->next->qinfo->putp)(q->next, bp);
}
if(q->count <= q->qinfo->lolimit)
qenable(OTHERQ(q)); /* update remote send window */
}
/*
* here if channel isn't connected yet:
* output `data' is a user command
* grab all the data written so far,
* even though we'll discard the part we don't use
*/
tcpduser(tp)
register struct tcpcb *tp;
{
extern struct ipif *ip_ifwithaddr();
struct tcpuser *tu;
struct block *bp, *tail, *head;
tail = head = NULL;
while(bp = getq(tp->so_wq)){
if(bp->type != M_DATA){
freeb(bp);
continue;
}
bp->next = NULL;
if (head == NULL)
head = bp;
else
tail->next = bp;
tail = bp;
}
if(head == NULL)
return;
tp->so_wcount = 0;
bp = head;
if(BLEN(bp) < sizeof(struct tcpuser)){
bp_free(bp);
return;
}
/*
* tu->code is a request to bind this channel
*/
tu = (struct tcpuser *)bp->rptr;
switch(tu->code){
case TCPC_CONNECT:
if(tp->t_state != TCPS_CLOSED){
tu->code = TCPC_BADDEV;
goto bad;
}
if (tu->laddr != INADDR_ANY) {
/* has the user specified a legal local address? */
if (ip_ifwithaddr(tu->laddr) == 0) {
tu->code = TCPC_BADLOCAL;
goto bad;
}
} else {
/* pick a local address related to the destination */
tu->laddr = ip_hoston(tu->faddr);
if(tu->laddr == INADDR_ANY) {
tu->code = TCPC_NOROUTE;
goto bad;
}
}
tp->so_options = tu->param & ~SO_ACCEPTCONN;
if(tcpcb_bind(tp, tu->laddr, tu->lport)) {
tu->code = TCPC_BOUND;
goto bad;
}
tp->so_fport = tu->fport;
tp->so_faddr = tu->faddr;
tcp_template(tp);
if(tp->t_template == (struct block *)0) {
printf("no template - ");
tu->code = TCPC_BADDEV;
goto bad;
}
if (tp->so_options & SO_KEEPALIVE)
tcp_timers(tp, TCPT_KEEP);
tp->t_state = TCPS_SYN_SENT;
tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
tcp_sendseqinit(tp);
tp->so_state &= ~SS_WAITING;
tcp_output(tp);
break;
case TCPC_LISTEN:
if(tp->t_state != TCPS_CLOSED){
tu->code = TCPC_BADDEV;
goto bad;
}
if (tu->laddr != INADDR_ANY) {
/* has the user specified a legal local address? */
if (ip_ifwithaddr(tu->laddr) == 0) {
tu->code = TCPC_BADLOCAL;
goto bad;
}
}
tp->so_options = tu->param & ~SO_ACCEPTCONN;
if(tcpcb_bind(tp, tu->laddr, tu->lport)) {
tu->code = TCPC_BOUND;
goto bad;
}
tp->t_state = TCPS_LISTEN;
tp->so_options |= SO_ACCEPTCONN;
tp->so_fport = tu->fport==0 ? TCPPORT_ANY : tu->fport;
tp->so_faddr = tu->faddr;
tcp_template(tp);
if(tp->t_template == 0) {
tu->code = TCPC_BADDEV;
printf("no template - ");
goto bad;
}
tp->so_state &= ~SS_WAITING;
if (tcp_isconnected(tp)<0) {
tu->code = TCPC_BADDEV;
goto bad;
}
break;
default:
tu->code = TCPC_SORRY;
goto bad;
}
bp_free(bp);
return;
bad:
/*
* send reason for failure back to user.
*/
if(bp->next) {
bp_free(bp->next);
bp->next = 0;
}
(*tp->so_rq->next->qinfo->putp)(tp->so_rq->next, bp);
tcp_hungup(tp);
}
struct tcpcb *
tcp_disconnect(tp)
register struct tcpcb *tp;
{
if(tp->t_state < TCPS_ESTABLISHED)
tp = tcp_close(tp);
else {
tp->so_state &= ~SS_PLEASEOPEN;
tcp_hungup(tp); /* sends M_HANGUP */
tp = tcp_usrclosed(tp);
if(tp)
tcp_output(tp);
}
return(tp);
}
struct tcpcb *
tcp_usrclosed(tp)
register struct tcpcb *tp;
{
switch(tp->t_state){
case TCPS_CLOSED:
case TCPS_LISTEN:
case TCPS_SYN_SENT:
tp->t_state = TCPS_CLOSED;
tp = tcp_close(tp);
break;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
tp->t_state = TCPS_FIN_WAIT_1;
tp->so_options |= SO_KEEPALIVE;
tcp_timers(tp, TCPT_KEEP);
break;
case TCPS_CLOSE_WAIT:
tp->t_state = TCPS_LAST_ACK;
tp->so_options |= SO_KEEPALIVE;
tcp_timers(tp, TCPT_KEEP);
break;
}
if(tp && tp->t_state >= TCPS_FIN_WAIT_2) {
tp->so_state &= ~(SS_PLEASEOPEN|SS_RCVATMARK);
if(tp->so_state&SS_OPEN && !(tp->so_state&SS_HUNGUP)) {
tp->so_state |= SS_HUNGUP;
tcp_hungup(tp);
}
}
return(tp);
}
tcp_isconnected(tp)
struct tcpcb *tp;
{
struct block *bp;
struct tcpuser *tu;
struct tcpcb *rtp;
if(tp->so_head)
rtp = tp->so_head;
else
rtp = tp;
if((rtp->so_state & SS_OPEN) == 0){
printf("isconnected, no fd ref\n");
return -1;
}
bp = allocb(sizeof(struct tcpuser));
if(bp == 0)
return -1;
bp->wptr += sizeof(struct tcpuser);
tu = (struct tcpuser *)bp->rptr;
tu->code = TCPC_OK;
tu->fport = tp->so_fport;
tu->faddr = tp->so_faddr;
tu->lport = tp->so_lport;
tu->laddr = tp->so_laddr;
tu->param = tp->so_dev;
bp->class |= S_DELIM;
return tcpdrint(bp, rtp);
}
tcp_hungup(tp)
register struct tcpcb *tp;
{
register struct queue *q;
q = tp->so_rq;
if(q == 0)
return;
qpctl(q, M_HANGUP);
}
/*
* find a spare even-numbered tcp device for a new passive-end
* connection.
*/
struct tcpcb *
tcp_newconn(tp)
register struct tcpcb *tp;
{
register struct tcpcb *ntp;
register struct tcpcb *tend;
if(tp->so_rq && (tp->so_rq->flag&QFULL)){
printf("listen %d q full\n", tp->so_lport);
return(0);
}
tend = &tcpcb[tcpcnt];
for(ntp = &tcpcb[0]; ntp < tend; ntp += 2){
if(ntp->t_state == TCPS_CLOSED){
bzero(ntp, sizeof(struct tcpcb));
ntp->t_maxseg = TCP_DEFMAXSEG;
ntp->so_options = tp->so_options & SO_KEEPALIVE;
ntp->so_head = tp;
ntp->so_dev = ntp - tcpcb;
return(ntp);
}
}
return(0);
}
struct tcpcb *
tcpcb_lookup(faddr, fport, laddr, lport)
register in_addr faddr, laddr;
register tcp_port fport, lport;
{
register struct tcpcb *tp;
int highscore = 0, score;
struct tcpcb *match = 0;
struct tcpcb *tend;
tend = &tcpcb[tcpcnt];
for(tp = &tcpcb[0]; tp < tend; tp++){
if(tp->t_state == TCPS_CLOSED)
continue;
score = 22;
if(tp->so_faddr != faddr) {
if(tp->so_faddr != INADDR_ANY)
continue;
else
score -=8;
}
if(tp->so_fport != fport) {
if(tp->so_fport != TCPPORT_ANY)
continue;
else
score -=8;
}
if(tp->so_laddr != laddr) {
if(tp->so_laddr == in_netof(laddr))
score -=1;
else if(tp->so_laddr != INADDR_ANY)
continue;
else
score -=2;
}
if(tp->so_lport != lport) {
if(tp->so_lport != TCPPORT_ANY)
continue;
else
score -=4;
}
if (score==22)
return tp;
if (score<highscore)
continue;
match = tp;
highscore = score;
}
return(match);
}
/* n chars were acked; drop them now */
sbsnddrop(tp, n)
register struct tcpcb *tp;
register int n;
{
register struct queue *q;
register int i;
register struct block *bp;
q = tp->so_wq;
if(q == 0)
return;
bp = 0;
while(n > 0 && (bp = getq(q))){
i = MIN(BLEN(bp), n);
bp->rptr += i;
n -= i;
tp->so_wcount -= i;
if(bp->rptr >= bp->wptr){
freeb(bp);
bp = 0;
} else if(n > 0){
panic("sbsnddrop");
}
}
if(bp)
putbq(q, bp);
}
static tcp_port portnext[] = { 600, 1024 };
static tcp_port portlow[] = { 600, 1024 };
static tcp_port porthigh[] = { 1024, 2048 };
tcpcb_bind(tp, addr, port)
register struct tcpcb *tp;
register in_addr addr;
register tcp_port port;
{
register int i;
tp->so_lport = 0;
if(port){
/*
* Don't let just anyone get a trusted port.
*/
if(port<porthigh[0] && u.u_uid!=0)
return(1);
tp->so_lport = port;
tp->so_laddr = addr;
return(0);
}
/*
* No specific port, pick one. Root only gets a trusted
* port if explicitly requested, so privileged programs
* can make non-privileged calls.
*/
i = (u.u_uid==0 && (tp->so_options&SO_TRUSTED)) ? 0 : 1;
if(portnext[i] >= porthigh[i])
portnext[i] = portlow[i];
port = portnext[i];
while(1){
if(tcpcb_bind(tp, addr, portnext[i]) == 0){
portnext[i]++;
return(0);
}
portnext[i]++;
if(portnext[i] >= porthigh[i])
portnext[i] = portlow[i];
if(portnext[i] == port) /* tried them all */
break;
}
return(1);
}
tcp_cantrcvmore(tp)
register struct tcpcb *tp;
{
register struct queue *q;
q = tp->so_rq;
if(q == NULL)
return;
if(tp->so_state & SS_HANGUP)
qpctl(q, M_HANGUP);
else {
/* two delims ensure a zero length read at the process */
qpctld(q, M_DATA);
qpctld(q, M_DATA);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.