|
|
Plan 9 NeXT
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "arp.h"
#include "../port/ipdat.h"
int tcpdbg = 0;
ushort tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
#define DPRINT if(tcpdbg) print
#define LPRINT if(tcpdbg) print
char *tcpstate[] =
{
"Closed", "Listen", "Syn_sent", "Syn_received",
"Established", "Finwait1", "Finwait2", "Close_wait",
"Closing", "Last_ack", "Time_wait"
};
void
sndrst(Ipaddr source, Ipaddr dest, ushort length, Tcp *seg)
{
Block *hbp;
Port tmp;
char rflags;
Tcphdr ph;
if(seg->flags & RST)
return;
hnputl(ph.tcpsrc, dest);
hnputl(ph.tcpdst, source);
ph.proto = IP_TCPPROTO;
hnputs(ph.tcplen, TCP_HDRSIZE);
/* Swap port numbers */
tmp = seg->dest;
seg->dest = seg->source;
seg->source = tmp;
rflags = RST;
/* convince the other end that this reset is in band */
if(seg->flags & ACK) {
seg->seq = seg->ack;
seg->ack = 0;
}
else {
rflags |= ACK;
seg->ack = seg->seq;
seg->seq = 0;
if(seg->flags & SYN)
seg->ack++;
seg->ack += length;
if(seg->flags & FIN)
seg->ack++;
}
seg->flags = rflags;
seg->wnd = 0;
seg->up = 0;
seg->mss = 0;
if((hbp = htontcp(seg, 0, &ph)) == 0)
return;
ipmuxoput(0, hbp);
}
/*
* flush an incoming call; send a reset to the remote side and close the
* conversation
*/
void
tcpflushincoming(Ipconv *s)
{
Tcp seg;
Tcpctl *tcb;
uchar dst[4];
tcb = &s->tcpctl;
seg.source = s->pdst;
seg.dest = s->psrc;
seg.flags = ACK;
seg.seq = tcb->snd.ptr;
tcb->last_ack = tcb->rcv.nxt;
seg.ack = tcb->rcv.nxt;
if(s->src == 0){
hnputl(dst, s->dst);
s->src = ipgetsrc(dst);
}
sndrst(s->dst, s->src, 0, &seg);
localclose(s, 0);
}
static void
tcpmove(struct Tctl *to, struct Tctl *from)
{
memmove(to, from, sizeof(struct Tctl));
}
Ipconv*
tcpincoming(Ipifc *ifc, Ipconv *s, Tcp *segp, Ipaddr source, Ipaddr dest)
{
Ipconv *new;
qlock(s);
if(s->curlog >= s->backlog){
qunlock(s);
return 0;
}
new = ipincoming(ifc, s);
if(new == 0){
qunlock(s);
return 0;
}
s->curlog++;
qunlock(s);
new->psrc = segp->dest;
new->pdst = segp->source;
new->dst = source;
new->src = dest;
tcpmove(&new->tcpctl, &s->tcpctl);
new->tcpctl.flags &= ~CLONE;
new->tcpctl.timer.arg = new;
new->tcpctl.timer.state = TimerOFF;
new->tcpctl.acktimer.arg = new;
new->tcpctl.acktimer.state = TimerOFF;
new->newcon = s;
wakeup(&s->listenr);
return new;
}
void
tcpinput(Ipifc *ifc, Block *bp)
{
Tcp seg;
char tos;
Tcphdr *h;
int hdrlen;
Tcpctl *tcb;
ushort length;
Ipconv *spec, *gen;
Ipaddr source, dest;
Ipconv *s, **p, **etab;
h = (Tcphdr *)(bp->rptr);
dest = nhgetl(h->tcpdst);
source = nhgetl(h->tcpsrc);
tos = h->tos;
length = nhgets(h->length);
h->Unused = 0;
hnputs(h->tcplen, length - (TCP_IPLEN+TCP_PHDRSIZE));
if(ptcl_csum(bp, TCP_EHSIZE+TCP_IPLEN, length - TCP_IPLEN)) {
freeb(bp);
return;
}
if((hdrlen = ntohtcp(&seg, &bp)) < 0)
return;
/* trim the packet to the size claimed by the datagram */
length -= (hdrlen+TCP_IPLEN+TCP_PHDRSIZE);
bp = btrim(bp, hdrlen+TCP_PKT, length);
if(bp == 0)
return;
/* Look for a connection. failing that look for a listener. */
s = ip_conn(ifc, seg.dest, seg.source, source);
if(s && s->tcpctl.state == Listen)
s = 0; /* can't talk directly to a listener */
if (s == 0) {
if(seg.flags & SYN){
/*
* dump packets with bogus flags
*/
if(seg.flags & RST){
freeb(bp);
return;
}
if(seg.flags & ACK) {
freeb(bp);
sndrst(source, dest, length, &seg);
return;
}
/*
* find a listener specific to this port (spec) or,
* failing that, a general one (gen)
*/
spec = 0;
gen = 0;
etab = &ifc->conv[Nipconv];
for(p = ifc->conv; p < etab && *p; p++) {
s = *p;
if(s->tcpctl.state == Listen)
if(s->pdst == 0)
if(s->dst == 0) {
if(s->psrc == seg.dest){
spec = s;
break;
}
if(s->psrc == 0)
gen = s;
}
}
if(spec)
s = tcpincoming(ifc, spec, &seg, source, dest);
else if(gen)
s = tcpincoming(ifc, gen, &seg, source, dest);
else
s = 0;
}
if(s == 0){
freeb(bp);
sndrst(source, dest, length, &seg);
return;
}
}
/* The rest of the input state machine is run with the control block
* locked and implements the state machine directly out of the RFC
* Out-of-band data is ignored - it was always a bad idea.
*/
tcb = &s->tcpctl;
qlock(tcb);
switch(tcb->state) {
case Closed:
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
case Listen:
if((seg.flags & (SYN|RST|ACK)) != SYN) {
/* ignore bogus packets */
print("packet to channel in listen state %d <- %d\n", s->psrc, s->pdst);
freeb(bp);
goto done;
}
proc_syn(s, tos, &seg);
tcpsndsyn(tcb);
tcpsetstate(s, Syn_received);
if(length != 0 || (seg.flags & FIN))
break;
freeb(bp);
goto output;
case Syn_sent:
if(seg.flags & ACK) {
if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
}
}
if(seg.flags & RST) {
if(seg.flags & ACK)
localclose(s, Econrefused);
freeb(bp);
goto done;
}
if(seg.flags & ACK)
if(PREC(tos) != PREC(tcb->tos)){
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
}
if(seg.flags & SYN) {
proc_syn(s, tos, &seg);
if(seg.flags & ACK){
update(s, &seg);
tcpsetstate(s, Established);
}
else
tcpsetstate(s, Syn_received);
if(length != 0 || (seg.flags & FIN))
break;
freeb(bp);
goto output;
}
else
freeb(bp);
goto done;
}
/* Cut the data to fit the receive window */
if(trim(tcb, &seg, &bp, &length) == -1) {
if(!(seg.flags & RST)) {
tcb->flags |= FORCE;
goto output;
}
goto done;
}
/* Cannot accept so answer with a rst */
if(length)
if(s->readq == 0)
if(tcb->state == Closed) {
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
}
/* The segment is beyond the current receive pointer so
* queue the data in the resequence queue
*/
if(seg.seq != tcb->rcv.nxt)
if(length != 0 || (seg.flags & (SYN|FIN))) {
add_reseq(tcb, tos, &seg, bp, length);
tcb->flags |= FORCE;
goto output;
}
/*
* keep looping till we've processed this packet plus any
* adjacent packets in the resequence queue
*/
for(;;) {
if(seg.flags & RST) {
localclose(s, Econrefused);
freeb(bp);
goto done;
}
/* This tos stuff should be removed */
if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
}
if(!(seg.flags & ACK)) {
freeb(bp);
goto done;
}
switch(tcb->state) {
case Syn_received:
if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
}
update(s, &seg);
tcpsetstate(s, Established);
case Established:
case Close_wait:
update(s, &seg);
break;
case Finwait1:
update(s, &seg);
if(tcb->sndcnt == 0){
tcb->kacounter = MAXBACKOFF;
tcpsetstate(s, Finwait2);
tcb->timer.start = MSL2 * (1000 / MSPTICK);
tcpgo(&tcb->timer);
}
break;
case Finwait2:
update(s, &seg);
break;
case Closing:
update(s, &seg);
if(tcb->sndcnt == 0) {
tcpsetstate(s, Time_wait);
tcb->timer.start = MSL2 * (1000 / MSPTICK);
tcpgo(&tcb->timer);
}
break;
case Last_ack:
update(s, &seg);
if(tcb->sndcnt == 0) {
freeb(bp);
localclose(s, 0);
goto done;
}
case Time_wait:
tcb->flags |= FORCE;
tcpgo(&tcb->timer);
}
if((seg.flags&URG) && seg.up) {
if(seq_gt(seg.up + seg.seq, tcb->rcv.up)) {
tcb->rcv.up = seg.up + seg.seq;
pullb(&bp, seg.up);
}
}
else if(seq_gt(tcb->rcv.nxt, tcb->rcv.up))
tcb->rcv.up = tcb->rcv.nxt;
if(length == 0){
if(bp)
freeb(bp);
}
else {
switch(tcb->state){
default:
/* Ignore segment text */
if(bp)
freeb(bp);
break;
case Syn_received:
case Established:
case Finwait1:
/* If we still have some data place on receive queue */
tcb->rcvcnt += blen(bp);
if(bp){
if(s->readq)
PUTNEXT(s->readq, bp);
else
putb(&tcb->rcvq, bp);
bp = 0;
}
tcb->rcv.nxt += length;
tcprcvwin(s);
if(tcb->acktimer.state != TimerON)
tcpgo(&tcb->acktimer);
if(tcb->rcv.nxt-tcb->last_ack > Streamhi/2)
tcb->flags |= FORCE;
break;
case Finwait2:
/* no process to read the data, send a reset */
if(bp)
freeb(bp);
sndrst(source, dest, length, &seg);
goto done;
}
}
if(seg.flags & FIN) {
tcb->flags |= FORCE;
switch(tcb->state) {
case Syn_received:
case Established:
tcb->rcv.nxt++;
tcpsetstate(s, Close_wait);
break;
case Finwait1:
tcb->rcv.nxt++;
if(tcb->sndcnt == 0) {
tcpsetstate(s, Time_wait);
tcb->timer.start = MSL2 * (1000/MSPTICK);
tcpgo(&tcb->timer);
}
else
tcpsetstate(s, Closing);
break;
case Finwait2:
tcb->rcv.nxt++;
tcpsetstate(s, Time_wait);
tcb->timer.start = MSL2 * (1000/MSPTICK);
tcpgo(&tcb->timer);
break;
case Close_wait:
case Closing:
case Last_ack:
break;
case Time_wait:
tcpgo(&tcb->timer);
break;
}
}
/*
* get next adjacent segment from the requence queue.
* dump/trim any overlapping segments
*/
for(;;) {
if(tcb->reseq == 0)
goto output;
if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
goto output;
get_reseq(tcb, &tos, &seg, &bp, &length);
if(trim(tcb, &seg, &bp, &length) == 0)
break;
}
}
output:
tcpoutput(s);
done:
qunlock(tcb);
}
void
update(Ipconv *s, Tcp *seg)
{
int rtt;
ushort acked;
ushort expand;
Tcpctl *tcb = &s->tcpctl;
tcb->kacounter = MAXBACKOFF; /* keep alive count down */
if(seq_gt(seg->ack, tcb->snd.nxt)) {
tcb->flags |= FORCE;
return;
}
if(seq_ge(seg->ack,tcb->snd.wl2))
if(seq_gt(seg->seq,tcb->snd.wl1) || (seg->seq == tcb->snd.wl1)) {
if(seg->wnd != 0)
if(tcb->snd.wnd == 0)
tcb->snd.ptr = tcb->snd.una;
tcb->snd.wnd = seg->wnd;
tcb->snd.wl1 = seg->seq;
tcb->snd.wl2 = seg->ack;
}
if(!seq_gt(seg->ack, tcb->snd.una))
return;
/* Compute the new send window size */
acked = seg->ack - tcb->snd.una;
if(tcb->cwind < tcb->snd.wnd) {
if(tcb->cwind < tcb->ssthresh)
expand = MIN(acked,tcb->mss);
else
expand = ((long)tcb->mss * tcb->mss) / tcb->cwind;
if(tcb->cwind + expand < tcb->cwind)
expand = 65535 - tcb->cwind;
if(tcb->cwind + expand > tcb->snd.wnd)
expand = tcb->snd.wnd - tcb->cwind;
if(expand != 0)
tcb->cwind += expand;
}
/* Adjust the timers acorrding to the round trip time */
if(run_timer(&tcb->rtt_timer))
if(seq_ge(seg->ack, tcb->rttseq)) {
tcphalt(&tcb->rtt_timer);
if((tcb->flags&RETRAN) == 0) {
tcb->backoff = 0;
rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
rtt *= MSPTICK;
if(rtt > tcb->srtt &&
(tcb->state == Syn_sent || tcb->state == Syn_received))
tcb->srtt = rtt;
else {
tcb->srtt = ((AGAIN-1)*tcb->srtt + rtt) / AGAIN;
rtt = abs(rtt - tcb->srtt);
tcb->mdev = ((DGAIN-1)*tcb->mdev + rtt) / DGAIN;
}
}
}
if((tcb->flags & SYNACK) == 0){
tcb->flags |= SYNACK;
acked--;
tcb->sndcnt--;
}
pullb(&tcb->sndq, acked);
tcb->sndcnt -= acked;
tcb->snd.una = seg->ack;
if(seq_gt(seg->ack, tcb->snd.up))
tcb->snd.up = seg->ack;
tcphalt(&tcb->timer);
if(tcb->snd.una != tcb->snd.nxt)
tcpgo(&tcb->timer);
if(seq_lt(tcb->snd.ptr, tcb->snd.una))
tcb->snd.ptr = tcb->snd.una;
tcb->flags &= ~RETRAN;
tcb->backoff = 0;
if(tcb->sndfull && tcb->sndcnt < Streamhi/2){
wakeup(&tcb->sndr);
tcb->sndfull = 0;
}
}
int
in_window(Tcpctl *tcb, int seq)
{
return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
}
void
proc_syn(Ipconv *s, char tos, Tcp *seg)
{
Tcpctl *tcb = &s->tcpctl;
ushort mtu;
tcb->flags |= FORCE;
if(PREC(tos) > PREC(tcb->tos))
tcb->tos = tos;
tcb->rcv.up = tcb->rcv.nxt = seg->seq + 1;
tcb->snd.wl1 = tcb->irs = seg->seq;
tcb->snd.wnd = seg->wnd;
if(seg->mss != 0)
tcb->mss = seg->mss;
tcb->max_snd = seg->wnd;
/* FIX MTU!!! */
if((mtu = 1500) != 0) {
mtu -= TCP_HDRSIZE + TCP_EHSIZE + TCP_PHDRSIZE;
tcb->cwind = tcb->mss = MIN(mtu, tcb->mss);
}
}
/* Generate an initial sequence number and put a SYN on the send queue */
void
tcpsndsyn(Tcpctl *tcb)
{
static int start;
if(start == 0)
start = rtctime();
else
start += 250000;
tcb->iss = start;
tcb->rttseq = tcb->iss;
tcb->snd.wl2 = tcb->iss;
tcb->snd.una = tcb->iss;
tcb->snd.ptr = tcb->snd.nxt = tcb->rttseq;
tcb->sndcnt++;
tcb->flags |= FORCE;
}
void
add_reseq(Tcpctl *tcb, char tos, Tcp *seg, Block *bp, ushort length)
{
Reseq *rp, *rp1;
rp = malloc(sizeof(Reseq));
if(rp == 0){
freeb(bp); /* bp always consumed by add_reseq */
return;
}
rp->seg = *seg;
rp->tos = tos;
rp->bp = bp;
rp->length = length;
/* Place on reassembly list sorting by starting seq number */
rp1 = tcb->reseq;
if(rp1 == 0 || seq_lt(seg->seq, rp1->seg.seq)) {
rp->next = rp1;
tcb->reseq = rp;
return;
}
for(;;) {
if(rp1->next == 0 || seq_lt(seg->seq, rp1->next->seg.seq)) {
rp->next = rp1->next;
rp1->next = rp;
break;
}
rp1 = rp1->next;
}
}
void
get_reseq(Tcpctl *tcb, char *tos, Tcp *seg, Block **bp, ushort *length)
{
Reseq *rp;
rp = tcb->reseq;
if(rp == 0)
return;
tcb->reseq = rp->next;
*tos = rp->tos;
*seg = rp->seg;
*bp = rp->bp;
*length = rp->length;
free(rp);
}
int
trim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
{
Block *nbp;
long dupcnt;
long excess;
ushort len;
char accept;
accept = 0;
len = *length;
if(seg->flags & SYN)
len++;
if(seg->flags & FIN)
len++;
if(tcb->rcv.wnd == 0) {
if(len == 0)
if(seg->seq == tcb->rcv.nxt)
return 0;
}
else {
/* Some part of the segment should be in the window */
if(in_window(tcb,seg->seq))
accept++;
else
if(len != 0) {
if(in_window(tcb, seg->seq+len-1) ||
seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
accept++;
}
}
if(!accept) {
freeb(*bp);
return -1;
}
dupcnt = tcb->rcv.nxt - seg->seq;
if(dupcnt > 0){
tcb->rerecv += dupcnt;
if(seg->flags & SYN){
seg->flags &= ~SYN;
seg->seq++;
if (seg->up > 1)
seg->up--;
else
seg->flags &= ~URG;
dupcnt--;
}
if(dupcnt > 0){
pullb(bp, (ushort)dupcnt);
seg->seq += dupcnt;
*length -= dupcnt;
if (seg->up > dupcnt)
seg->up -= dupcnt;
else {
seg->flags &= ~URG;
seg->up = 0;
}
}
}
excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
if(excess > 0) {
tcb->rerecv += excess;
*length -= excess;
nbp = copyb(*bp, *length);
freeb(*bp);
*bp = nbp;
seg->flags &= ~FIN;
}
return 0;
}
int
pullb(Block **bph, int count)
{
int n, bytes;
Block *bp;
bytes = 0;
if(bph == 0)
return 0;
while(*bph && count != 0) {
bp = *bph;
n = MIN(count, BLEN(bp));
bytes += n;
count -= n;
bp->rptr += n;
if(BLEN(bp) == 0) {
*bph = bp->next;
bp->next = 0;
freeb(bp);
}
}
return bytes;
}
int
dupb(Block **hp, Block *bp, int offset, int count)
{
int i, blen, bytes = 0;
uchar *addr;
*hp = allocb(count);
if(*hp == 0)
return 0;
/* Correct to front of data area */
while(bp && offset && offset >= BLEN(bp)) {
offset -= BLEN(bp);
bp = bp->next;
}
if(bp == 0)
return 0;
addr = bp->rptr + offset;
blen = BLEN(bp) - offset;
while(count) {
i = MIN(count, blen);
memmove((*hp)->wptr, addr, i);
(*hp)->wptr += i;
bytes += i;
count -= i;
bp = bp->next;
if(!bp)
break;
blen = BLEN(bp);
addr = bp->rptr;
}
return bytes;
}
static void
cleartcp(struct Tctl *a)
{
memset(a, 0, sizeof(struct Tctl));
}
void
init_tcpctl(Ipconv *s)
{
Tcpctl *tcb = &s->tcpctl;
cleartcp(tcb);
tcb->cwind = tcb->mss = tcp_mss;
tcb->ssthresh = 65535;
tcb->srtt = tcp_irtt;
tcb->timer.start = tcb->srtt / MSPTICK;
tcb->timer.func = tcptimeout;
tcb->timer.arg = s;
tcb->rtt_timer.start = MAX_TIME;
tcb->acktimer.start = TCP_ACK / MSPTICK;
tcb->acktimer.func = tcpacktimer;
tcb->acktimer.arg = s;
}
/*
* called with tcb locked
*/
void
localclose(Ipconv *s, char *reason)
{
Reseq *rp,*rp1;
Tcpctl *tcb = &s->tcpctl;
Block *bp;
tcphalt(&tcb->timer);
tcphalt(&tcb->rtt_timer);
s->err = reason;
/* flush receive queue */
while(bp = getb(&tcb->rcvq))
freeb(bp);
/* Flush reassembly queue; nothing more can arrive */
for(rp = tcb->reseq;rp != 0;rp = rp1){
rp1 = rp->next;
freeb(rp->bp);
free(rp);
}
tcb->reseq = 0;
s->err = reason;
if(tcb->sndq != 0){
freeb(tcb->sndq);
tcb->sndq = 0;
}
tcpsetstate(s, Closed);
}
int
seq_within(ulong x, ulong low, ulong high)
{
if(low <= high){
if(low <= x && x <= high)
return 1;
}
else {
if(low >= x && x >= high)
return 1;
}
return 0;
}
int
seq_lt(ulong x, ulong y)
{
return x < y;
}
int
seq_le(ulong x, ulong y)
{
return x <= y;
}
int
seq_gt(ulong x, ulong y)
{
return x > y;
}
int
seq_ge(ulong x, ulong y)
{
return x >= y;
}
void
tcpsetstate(Ipconv *s, char newstate)
{
Tcpctl *tcb;
char oldstate;
tcb = &s->tcpctl;
oldstate = tcb->state;
tcb->state = newstate;
tcpxstate(s, oldstate, newstate);
}
Block *
htontcp(Tcp *tcph, Block *data, Tcphdr *ph)
{
int dlen;
Tcphdr *h;
Block *bp;
ushort csum;
ushort hdrlen;
hdrlen = TCP_HDRSIZE;
if(tcph->mss)
hdrlen += MSS_LENGTH;
if(data) {
dlen = blen(data);
data = padb(data, hdrlen + TCP_PKT);
if(data == 0)
return 0;
/* If we collected blocks delimit the end of the chain */
for(bp = data; bp->next; bp = bp->next)
bp->flags &= ~S_DELIM;
bp->flags |= S_DELIM;
}
else {
dlen = 0;
data = allocb(hdrlen + TCP_PKT);
if(data == 0)
return 0;
data->wptr += hdrlen + TCP_PKT;
data->flags |= S_DELIM;
}
memmove(data->rptr, ph, TCP_PKT);
h = (Tcphdr *)(data->rptr);
h->proto = IP_TCPPROTO;
h->frag[0] = 0;
h->frag[1] = 0;
hnputs(h->tcplen, hdrlen + dlen);
hnputs(h->tcpsport, tcph->source);
hnputs(h->tcpdport, tcph->dest);
hnputl(h->tcpseq, tcph->seq);
hnputl(h->tcpack, tcph->ack);
hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
hnputs(h->tcpwin, tcph->wnd);
h->tcpcksum[0] = 0;
h->tcpcksum[1] = 0;
h->Unused = 0;
hnputs(h->tcpurg, tcph->up);
if(tcph->mss != 0){
h->tcpopt[0] = MSS_KIND;
h->tcpopt[1] = MSS_LENGTH;
hnputs(h->tcpmss, tcph->mss);
}
csum = ptcl_csum(data, TCP_EHSIZE+TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE);
hnputs(h->tcpcksum, csum);
return data;
}
int
ntohtcp(Tcp *tcph, Block **bpp)
{
ushort hdrlen;
ushort i, optlen;
Tcphdr *h;
uchar *optr;
*bpp = pullup(*bpp, TCP_PKT+TCP_HDRSIZE);
if(*bpp == 0)
return -1;
h = (Tcphdr *)((*bpp)->rptr);
tcph->source = nhgets(h->tcpsport);
tcph->dest = nhgets(h->tcpdport);
tcph->seq = nhgetl(h->tcpseq);
tcph->ack = nhgetl(h->tcpack);
hdrlen = (h->tcpflag[0] & 0xf0) >> 2;
if(hdrlen < TCP_HDRSIZE) {
freeb(*bpp);
return -1;
}
tcph->flags = h->tcpflag[1];
tcph->wnd = nhgets(h->tcpwin);
tcph->up = nhgets(h->tcpurg);
tcph->mss = 0;
*bpp = pullup(*bpp, hdrlen+TCP_PKT);
if(!*bpp)
return -1;
optr = h->tcpopt;
for(i = TCP_HDRSIZE; i < hdrlen;) {
switch(*optr++){
case EOL_KIND:
return hdrlen;
case NOOP_KIND:
i++;
break;
case MSS_KIND:
optlen = *optr++;
if(optlen == MSS_LENGTH)
tcph->mss = nhgets(optr);
i += optlen;
break;
}
}
return hdrlen;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.