📄 pctcp.c
字号:
/*
* PCTCP - the true worker of Waterloo TCP
* - contains all opens, closes, major read/write routines and
* basic IP handler for incomming packets
* - NOTE: much of the TCP/UDP/IP layering is done at the data
* structure level, not in separate routines or tasks
*
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <dos.h>
#include "copyrigh.h"
#include "wattcp.h"
#include "wattcpd.h"
#include "chksum.h"
#include "strings.h"
#include "language.h"
#include "udp_dom.h"
#include "bsdname.h"
#include "pcstat.h"
#include "pcconfig.h"
#include "pcqueue.h"
#include "pcsed.h"
#include "pcpkt.h"
#include "pcicmp.h"
#include "pcigmp.h"
#include "pcmulti.h"
#include "pcdbug.h"
#include "pcdhcp.h"
#include "pcbsd.h"
#include "pcarp.h"
#include "ip_out.h"
#include "misc.h"
#include "fragment.h"
#include "pppoe.h"
#include "tcp_fsm.h"
#include "pctcp.h"
#if defined(USE_BSD_FUNC)
#include "socket.h"
#endif
#ifndef __inline /* normally in <sys/cdefs.h> */
#define __inline
#endif
/*
* These are hooks to prevent the BSD-socket API being linked in
* by default. These function pointers are only set from the BSD
* functions when needed; `_raw_ip_hook' is set to filter SOCK_RAW
* packets, `_tcp_syn_hook' is set to filter incoming SYN packets
* for SOCK_STREAM packets used in `accept()'. And '_tcp_find_hook'
* is set to `sock_find_tcp()' when allocating SOCK_STREAM sockets.
*/
int (*_raw_ip_hook) (const in_Header *) = NULL;
int (*_tcp_syn_hook) (tcp_Socket **) = NULL;
void *(*_tcp_find_hook) (const tcp_Socket*) = NULL;
char hostname[MAX_HOSTLEN+1] = "random-pc";
int mss = ETH_MAX_DATA - sizeof(tcp_Header) - sizeof(in_Header);
int mtu = ETH_MAX_DATA;
int mtu_discover = 0; /* to-do */
int block_tcp = 0; /* when application handles tcp itself */
int block_udp = 0; /* udp itself */
int block_icmp = 0; /* icmp itself */
int block_ip = 0; /* ip itself */
DWORD my_ip_addr = 0L; /* our IP address */
DWORD sin_mask = 0xFFFFFF00L; /* our net-mask, 255.255.255.0 */
DebugProc _dbugxmit = NULL;
DebugProc _dbugrecv = NULL;
udp_Socket *_udp_allsocs = NULL; /* list of udp-sockets */
/*
* Prototypes and local data
*/
#if !defined(USE_UDP_ONLY) /* not used for UDP/IP */
/* TCP timer values
*/
int tcp_OPEN_TO = DEF_OPEN_TO;
int tcp_CLOSE_TO = DEF_CLOSE_TO;
int tcp_RTO_ADD = DEF_RTO_ADD;
int tcp_RTO_BASE = DEF_RTO_BASE;
int tcp_RST_TIME = DEF_RST_TIME;
int tcp_RETRAN_TIME = DEF_RETRAN_TIME;
/* TCP option config flags.
*/
int tcp_opt_timstmp = 0;
int tcp_opt_wscale = 0;
int tcp_opt_sackok = 0;
/* Misc TCP values
*/
int tcp_nagle = 1;
int tcp_keepalive = 30;
tcp_Socket *_tcp_allsocs = NULL; /* list of tcp-sockets */
static tcp_Socket *tcp_findseq (const in_Header *ip, const tcp_Header *tcp);
static void tcp_sockreset(tcp_Socket *s, int proxy);
static void tcp_rtt_wind (tcp_Socket *s);
static void tcp_upd_wind (tcp_Socket *s, unsigned line);
static int tcp_chksum (const in_Header *ip, const tcp_Header *tcp, int len);
static void tcp_rtt_add (tcp_Socket *s, UINT rto);
static void tcp_rtt_clr (tcp_Socket *s);
static UINT tcp_rtt_get (tcp_Socket *s);
#endif
static void udp_close (udp_Socket *s);
static void (*system_yield)(void) = NULL;
/*
* Passive open: listen for a connection on a particular port
*/
int udp_listen (udp_Socket *s, WORD lport, DWORD ina, WORD port, ProtoHandler handler)
{
udp_close (s);
watt_largecheck (s, sizeof(*s), __FILE__, __LINE__);
memset (s, 0, sizeof(*s));
s->rdata = &s->rddata[0];
s->maxrdatalen = udp_MaxBufSize;
s->ip_type = UDP_PROTO;
s->myport = findfreeport (lport, 0); /* get a nonzero port val */
s->hisport = port;
s->hisaddr = ina;
s->ttl = _default_ttl;
s->protoHandler = handler;
s->usr_yield = system_yield;
s->safetysig = SAFETYUDP; /* insert into chain */
s->next = _udp_allsocs;
_udp_allsocs = s;
return (1);
}
/*
* Active open: open a connection on a particular port
*/
int udp_open (udp_Socket *s, WORD lport, DWORD ip, WORD port, ProtoHandler handler)
{
BOOL bcast = (ip == (DWORD)-1) ||
(~ip & ~sin_mask) == 0;
udp_close (s);
watt_largecheck (s, sizeof(*s), __FILE__, __LINE__);
memset (s, 0, sizeof(*s));
if (ip - my_ip_addr <= multihomes)
return (0);
s->rdata = &s->rddata[0];
s->maxrdatalen = udp_MaxBufSize;
s->ip_type = UDP_PROTO;
s->myport = findfreeport (lport, 0);
s->myaddr = my_ip_addr;
s->ttl = _default_ttl;
if (bcast || !ip) /* check for broadcast */
{
memset (s->hisethaddr, 0xFF, sizeof(eth_address));
if (!ip)
ip = (DWORD)-1; /* make s->hisaddr = 255.255.255.255 */
}
#if defined(USE_MULTICAST)
else if (is_multicast(ip)) /* check for multicast */
{
multi_to_eth (ip, (BYTE*)&s->hisethaddr[0]);
s->ttl = 1; /* so we don't send worldwide as default */
}
#endif
else if (!_arp_resolve(ip,&s->hisethaddr,0))
return (0);
s->hisaddr = ip;
s->hisport = port;
s->protoHandler = handler;
s->usr_yield = system_yield;
s->safetysig = SAFETYUDP;
s->next = _udp_allsocs;
_udp_allsocs = s;
return (1);
}
/*
* Since UDP is stateless, simply reclaim the local-port and
* unthread the socket from the list.
*/
static void udp_close (udp_Socket *ds)
{
udp_Socket *s, *prev;
for (s = prev = _udp_allsocs; s; prev = s, s = s->next)
{
if (ds != s)
continue;
(void) reuse_localport (s->myport);
if (s == _udp_allsocs)
_udp_allsocs = s->next;
else prev->next = s->next;
if (s->err_msg == NULL)
s->err_msg = _LANG("UDP Close called");
}
}
/*
* Set the TTL on an outgoing UDP datagram.
*/
void udp_SetTTL (udp_Socket *s, BYTE ttl)
{
s->ttl = ttl;
}
#if !defined(USE_UDP_ONLY)
/*
* Actively open a TCP connection to a particular destination.
* - 0 on error
*
* 'lport' is local port to associate with the connection.
* 'rport' is remote port for same connection
*/
int tcp_open (tcp_Socket *s, WORD lport, DWORD ina, WORD rport, ProtoHandler handler)
{
UINT rtt;
watt_largecheck (s, sizeof(*s), __FILE__, __LINE__);
(void) _tcp_unthread (s); /* just in case not totally closed */
memset (s, 0, sizeof(*s));
if ((ina - my_ip_addr <= multihomes) || is_multicast(ina))
return (0);
if (!_arp_resolve(ina,&s->hisethaddr,0))
return (0);
s->rdata = &s->rddata[0];
s->maxrdatalen = tcp_MaxBufSize;
s->ip_type = TCP_PROTO;
s->max_seg = mss; /* to-do !!: use mss from setsockopt() */
s->state = tcp_StateSYNSENT;
s->timeout = set_timeout (tcp_LONGTIMEOUT);
/* to-do !!: use TCP_NODELAY set in setsockopt()
*/
s->sockmode = tcp_nagle ? TCP_MODE_NAGLE : TCP_MODE_NONAGLE;
s->cwindow = 1;
s->wwindow = 0; /* slow start VJ algorithm */
s->vj_sa = INIT_VJSA;
s->rto = tcp_OPEN_TO; /* added 14-Dec 1999, GV */
s->myaddr = my_ip_addr;
s->myport = findfreeport (lport,1); /* get a nonzero port val */
s->locflags = LF_LINGER; /* close via TIMEWT state */
if (tcp_opt_timstmp)
s->locflags |= LF_REQ_TSTMP; /* use timestamp option */
s->ttl = _default_ttl;
s->hisaddr = ina;
s->hisport = rport;
s->seqnum = INIT_SEQ();
s->flags = tcp_FlagSYN;
s->unhappy = TRUE;
s->protoHandler = handler;
s->usr_yield = system_yield;
s->safetysig = SAFETYTCP; /* marker signatures */
s->safetytcp = SAFETYTCP;
s->next = _tcp_allsocs; /* insert into chain */
_tcp_allsocs = s;
(void) TCP_SEND (s); /* send opening SYN */
/* find previous RTT replacing RTT set in tcp_send() above
*/
if ((rtt = tcp_rtt_get(s)) > 0)
s->rtt_time = set_timeout (rtt);
else s->rtt_time = set_timeout (tcp_OPEN_TO);
return (1);
}
/*
* Passive open: listen for a connection on a particular port
*/
int tcp_listen (tcp_Socket *s, WORD lport, DWORD ina, WORD port, ProtoHandler handler, WORD timeout)
{
watt_largecheck (s, sizeof(*s), __FILE__, __LINE__);
(void) _tcp_unthread (s); /* just in case not totally closed */
memset (s, 0, sizeof(*s));
if (is_multicast(ina))
return (0);
s->rdata = &s->rddata[0];
s->maxrdatalen = tcp_MaxBufSize;
s->ip_type = TCP_PROTO;
s->max_seg = mss; /* to-do !!: use mss from setsockopt() */
s->cwindow = 1;
s->wwindow = 0; /* slow start VJ algorithm */
s->vj_sa = INIT_VJSA;
s->state = tcp_StateLISTEN;
s->locflags = LF_LINGER;
s->myport = findfreeport (lport, 0);
s->hisport = port;
s->hisaddr = ina;
s->seqnum = INIT_SEQ();
s->unhappy = FALSE;
s->ttl = _default_ttl;
s->protoHandler = handler;
s->usr_yield = system_yield;
s->safetysig = SAFETYTCP; /* marker signatures */
s->safetytcp = SAFETYTCP;
s->next = _tcp_allsocs; /* insert into chain */
_tcp_allsocs = s;
if (timeout != 0)
s->timeout = set_timeout (1000 * timeout);
return (1);
}
/*
* Send a FIN on a particular port -- only works if it is open.
* Must still allow receives
*/
void _tcp_close (tcp_Socket *s)
{
if (s->ip_type != TCP_PROTO)
return;
if (s->state == tcp_StateESTAB ||
s->state == tcp_StateESTCL ||
s->state == tcp_StateSYNREC)
{
if (s->datalen) /* must first flush all Tx data */
{
s->flags |= (tcp_FlagPUSH | tcp_FlagACK);
if (s->state < tcp_StateESTCL)
{
s->state = tcp_StateESTCL;
TCP_SENDSOON (s);
}
}
else /* really closing */
{
s->flags = (tcp_FlagACK | tcp_FlagFIN);
if (s->err_msg == NULL)
s->err_msg = _LANG("Connection closed normally");
s->state = tcp_StateFINWT1;
s->timeout = set_timeout (tcp_TIMEOUT);
s->rtt_time = 0UL; /* stop RTT timer */
(void) TCP_SEND (s);
}
s->unhappy = TRUE;
}
else if (s->state == tcp_StateCLOSWT)
{
/* need to ACK the FIN and get on with it
*/
s->timeout = set_timeout (tcp_LASTACK_TIME); /* Added AGW 6 Jan 2001 */
s->state = tcp_StateLASTACK;
s->flags |= tcp_FlagFIN;
(void) TCP_SEND (s);
s->unhappy = TRUE;
}
else if (s->state == tcp_StateSYNSENT) /* unlink failed connection */
{
s->state = tcp_StateCLOSED;
maybe_reuse_lport (s);
(void) _tcp_unthread (s);
}
}
/*
* Abort a tcp connection
*/
void tcp_abort (tcp_Socket *s)
{
if (s->err_msg == NULL)
s->err_msg = _LANG("TCP Abort");
if (s->state != tcp_StateLISTEN && s->state != tcp_StateCLOSED)
{
s->flags = (tcp_FlagRST | tcp_FlagACK);
s->unhappy = TRUE;
if (s->state <= tcp_StateSYNREC)
{
s->rtt_time = 0UL; /* Stop RTT timer */
tcp_rtt_clr (s); /* Clear cached RTT */
}
(void) TCP_SEND (s);
}
s->unhappy = FALSE;
s->datalen = 0; /* discard Tx buffer, but not Rx buffer */
s->ip_type = 0;
maybe_reuse_lport (s);
(void) _tcp_unthread (s);
}
/*
* _tcp_sendsoon - schedule a transmission pretty soon.
* - this one has an imperfection at midnight, but it
* is not significant to the connection performance.
*
* gv: Added - 5 May 2000: Relax retransmission period to
* tcp_CLOSE_TO when CLOSEWT state is entered.
* Relax retransmission period to tcp_OPEN_TO in
* SYNSENT state.
*/
int _tcp_sendsoon (tcp_Socket *s, char *file, unsigned line)
{
DWORD timeout;
if (s->ip_type != TCP_PROTO)
return (0);
if (s->state >= tcp_StateCLOSWT)
timeout = set_timeout (tcp_CLOSE_TO);
else timeout = set_timeout (tcp_RTO_BASE);
if (s->rto <= tcp_RTO_BASE && s->recent == 0 &&
cmp_timers(s->rtt_time,timeout) <= 0)
{ /* !! was == */
int rc;
s->karn_count = 0;
rc = _tcp_send (s, file, line);
s->recent = 1;
return (rc);
}
if ((s->unhappy || s->datalen > 0 || s->karn_count == 1) &&
(s->rtt_time && cmp_timers(s->rtt_time,timeout) < 0))
return (0);
if (s->state == tcp_StateSYNSENT) /* relaxed in SYNSENT state */
s->rtt_time = set_timeout (tcp_OPEN_TO);
else s->rtt_time = set_timeout (tcp_RTO_BASE + (s->rto >> 4));
s->karn_count = 1;
return (0);
}
/*
* Unthread a socket from the tcp socket list, if it's there
*/
tcp_Socket *_tcp_unthread (tcp_Socket *ds)
{
tcp_Socket *s, *prev;
tcp_Socket *next = NULL;
for (s = prev = _tcp_allsocs; s; prev = s, s = s->next)
{
if (ds != s)
continue;
if (s == _tcp_allsocs)
_tcp_allsocs = s->next;
else prev->next = s->next;
next = s->next;
}
if (ds->rdatalen == 0 || (ds->state > tcp_StateESTCL))
ds->ip_type = 0; /* fail further I/O */
ds->state = tcp_StateCLOSED; /* tcp_tick needs this */
return (next);
}
/*
* Returns 1 if connection is established
*/
int tcp_established (tcp_Socket *s)
{
return (s->state >= tcp_StateESTAB);
}
/*
* tcp_handler - All tcp input processing is done from here.
*/
static tcp_Socket *tcp_handler (const in_Header *ip, BOOL broadcast)
{
tcp_Header *tcp;
tcp_Socket *s;
int len;
BYTE flags;
DWORD source = intel (ip->source);
DWORD destin = intel (ip->destination);
WORD dstPort, srcPort;
if (broadcast || block_tcp ||
!is_local_addr(destin) || is_multicast(source))
{
DEBUG_RX (NULL, ip);
if (!block_tcp)
STAT (tcpstats.tcps_drops++);
return (NULL);
}
len = in_GetHdrLen (ip); /* len of IP header */
tcp = (tcp_Header*) ((BYTE*)ip + len); /* tcp frame pointer */
len = intel16 (ip->length) - len; /* len of tcp+data */
flags = tcp->flags & tcp_FlagMASK; /* get TCP flags */
if (!tcp_chksum(ip,tcp,len))
{
DEBUG_RX (NULL, ip);
return (NULL);
}
dstPort = intel16 (tcp->dstPort);
srcPort = intel16 (tcp->srcPort);
/* demux to active sockets
*/
for (s = _tcp_allsocs; s; s = s->next)
{
if (s->safetysig != SAFETYTCP || s->safetytcp != SAFETYTCP)
{
outsnl (_LANG("tcp-socket error in tcp_handler()"));
DEBUG_RX (s, ip);
return (NULL);
}
if (s->hisport && /* not a listening socket */
destin == s->myaddr && /* addressed to my IP */
source == s->hisaddr && /* and from my peer address */
dstPort == s->myport && /* addressed to my local port */
srcPort == s->hisport) /* and from correct remote port */
break;
}
if (!s && (flags & tcp_FlagSYN))
{
/* demux to passive (listening) sockets, must be a new session
*/
for (s = _tcp_allsocs; s; s = s->next)
if (s->hisport == 0 && /* =0, listening socket */
dstPort == s->myport) /* addressed to my local port */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -