activegs-ios/kegs/Src/marinetti.cpp
2016-03-26 17:16:01 +01:00

882 lines
28 KiB
C++
Raw Blame History

/*
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
This code is covered by the GNU GPL licence
*/
#include "defc.h"
#include "sim65816.h"
#include "marinetti.h"
#ifdef NO_MARINETTI_SUPPORT
int process_marinetti_command(int _command,word32* param, word32& _regx, word32& _regy)
{
return TCPIP_ERR_NO_DEVICE;
}
void marinetti_init()
{
printf("no marinetti support\n");
}
void marinetti_shutdown()
{
}
#else
/*
05/6187: SEGMENT ARRIVE
05/6905: ACK_FINWAIT2
05/6945: ACK_CLOSING
05/6cd5: PROCESSTHIS
05/505f: TCPIPMangleDomainName
05/4c70: TCPIPValidateI
05/3f4d: PASC2HEXIP
05/6DE0: BASICACK
*/
// Constantes
MAC_STRUCT server_mac_addr;
IP_STRUCT server_ip_addr;
IP_STRUCT server_identifier;
IP_STRUCT gateway_ip_addr;
MAC_STRUCT gateway_mac_addr;
IP_STRUCT gateway_dns_addr;
IP_STRUCT gateway_mask_addr;
IP_STRUCT client_ip_addr;
IP_STRUCT local_ip_addr;
IP_STRUCT host_ip_addr;
// Globales
MAC_STRUCT client_mac_addr;
int eth_first,eth_last;
eth_queue ethqueues[MAX_ETH_QUEUE];
tcp_queue tcpqueues[MAX_TCP_QUEUE];
PConnection PConnections[MAX_TCP_CONNECTION];
double start_tcp_time;
void marinetti_init()
{
MAC_SET(server_mac_addr,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB );
IP_SET(server_ip_addr, 100,100,0,1 );
IP_SET(server_identifier, 9,9,9,9 );
IP_SET(gateway_ip_addr,128,0,0,254 );
MAC_SET(gateway_mac_addr,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA );
IP_SET(gateway_dns_addr, 208,67,222,222); // OPENDNS
IP_SET(gateway_mask_addr, 255,255,255,0 );
IP_SET(client_ip_addr,128,0,0,100 );
IP_SET(local_ip_addr,127,0,0,1 );
IP_SET(host_ip_addr,128,0,0,1 );
}
void marinetti_shutdown()
{
for(int i=0;i<MAX_TCP_CONNECTION;i++)
PConnections[i].Reset();
}
int send_dhcp_response(tcp_queue* p)
{
printf("building dhcp response\n");
tcp_queue* packet = findTCPQueue();
if (!packet)
return 0;
DHCP_HEADER* _dhcp = (DHCP_HEADER*)((const byte*)&p->udp+sizeof(UDP_HEADER));
int dhcplen = ntohs(p->udp.udp_len)-sizeof(UDP_HEADER);
packet->udp.udp_dst = p->udp.udp_src; // inverse
packet->udp.udp_src = p->udp.udp_dst; // inverse
DHCP_HEADER* target = (DHCP_HEADER*)((byte*)&packet->udp + sizeof(UDP_HEADER));
// construit la r<>ponse en UDP
byte* response_str = (byte*)target;
unsigned int pos= sizeof(DHCP_HEADER)-DHCP_OPTION_LEN;
unsigned int parse= 0;
const byte* dhcp_request = _dhcp->options;
// v<>rifie le magic cookie
if (
dhcp_request[0]!=99
|| dhcp_request[1]!=130
|| dhcp_request[2]!=83
|| dhcp_request[3]!=99)
{
printf("invalid magic cookie\n");
return 0;
}
memcpy(&response_str[pos],dhcp_request,4);
parse+=4;
pos+=4;
while( parse < ( dhcplen - ( sizeof(DHCP_HEADER) - DHCP_OPTION_LEN) ) )
{
int cmd = dhcp_request[parse++];
if (cmd==255) //-1
break;
int len = dhcp_request[parse++];
switch(cmd)
{
case 53: // messagetype
{
int type = dhcp_request[parse];
response_str[pos++]=53;
response_str[pos++]=1;
switch(type)
{
case dhcpdiscover:
printf(" ### [DHCP] dhcpdiscover");
response_str[pos++]=dhcpoffer;
break;
case dhcprequest: // request
printf(" ### [DHCP] dhcprequest");
response_str[pos++]=dhcppack;
break;
}
}
break;
case 55: // options
{
for(int l=0;l<len;l++)
{
int c = dhcp_request[parse+l];
switch(c)
{
case 1: // subnetmask
response_str[pos++]=c;
response_str[pos++]=4;
memcpy(&response_str[pos],&GET_INT_FROM_IP(gateway_mask_addr),4);
printf(" subnetmask:");
print_ip(gateway_mask_addr);
pos+=4;
break;
case 3: // routers
response_str[pos++]=c;
response_str[pos++]=4;
memcpy(&response_str[pos],&GET_INT_FROM_IP(gateway_ip_addr),4);
printf(" routers:");
print_ip(gateway_ip_addr);
pos+=4;
break;
case 6: // dns
response_str[pos++]=c;
response_str[pos++]=4;
memcpy(&response_str[pos],&GET_INT_FROM_IP(gateway_dns_addr),4);
printf(" dns:");
print_ip(gateway_dns_addr);
pos+=4;
break;
case 26: // mtu
response_str[pos++]=c;
response_str[pos++]=2;
response_str[pos++]=MTU/256;
response_str[pos++]=MTU%256;
break;
}
}
}
break;
case 54: // server
response_str[pos++]=54;
response_str[pos++]=4;
printf(" server:");
print_ip(server_identifier);
memcpy(&response_str[pos],&GET_INT_FROM_IP(server_identifier),4);
pos+=4;
break;
case 51: // lease
response_str[pos++]=54;
response_str[pos++]=4;
response_str[pos++]=0;
response_str[pos++]=0;
response_str[pos++]=0;
response_str[pos++]=0;
break;
default:
break;
}
parse+=len;
}
response_str[pos++]=0xFF; // end
packet->udp.udp_len = htons(sizeof(UDP_HEADER) + pos);
packet->ip.protocol = 17; // udp
packet->ip.ver = 4;
packet->ip.ihl = 5;
packet->ip.ttl = 64;
packet->ip.ucSource = server_ip_addr;
packet->ip.ucDestination = client_ip_addr;
packet->ip.length = htons(sizeof(IP_HEADER)+sizeof(UDP_HEADER) + pos);
packet->eth.type = htons(0x0800); // IP
packet->eth.dest_mac = client_mac_addr ;
packet->eth.source_mac = server_mac_addr ;
target->yiaddr = client_ip_addr;
*(MAC_STRUCT*)&target->chaddr = client_mac_addr;
target->xid = _dhcp->xid;
packet->queuelen = sizeof(ETH_HEADER)+sizeof(IP_HEADER)+sizeof(UDP_HEADER) + pos;
packet->iplen = sizeof(UDP_HEADER) + pos;
packet->state = QS_TOSEND;
return 1;
}
int process_marinetti_command(int _command,word32* _param, word32& _regx, word32& _regy)
{
switch(_command)
{
case TCPIP_CMD_STARTUP:
{
printf("TCPIP_CMD_STARTUP\n");
#ifdef _WIN32
int err;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup failed with error: %d\n", err);
return TCPIP_ERR_STARTUP_FAILED;
}
#endif
return 0;
}
break;
case TCPIP_CMD_GET_DATAGRAM:
{
_param[1]=0;
server_loop();
// if (eth_first == eth_last)
for(int i=0;i<MAX_TCP_CONNECTION;i++)
{
PConnection* p = &PConnections[i];
if (p && p->tcpsocket>=0)
{
if (p->socktype == PCT_HOST && p->type==RT_UDP )// && p->udp_query)
{
// regarde si il y a des donn<6E>es de dispo
fd_set input_set;
timeval timeout;
timeout.tv_usec = 1;
timeout.tv_sec = 0;
FD_ZERO(&input_set);
FD_SET(p->tcpsocket,&input_set);
int s = select(p->tcpsocket+1,&input_set,NULL,NULL,&timeout);
if (s>0)
{
byte recvbuf[MAX_UDP_SIZE];
int iResult = recv(p->tcpsocket, (char*)recvbuf, MAX_UDP_SIZE, 0);
if ( iResult > 0 )
{
_LOG("Bytes received: %d\n", iResult);
// p->udp_query--;
p->sendudppacket(iResult,recvbuf);
}
else if ( iResult == 0 )
printf("Connection closed\n");
else
printf("recv failed: %d\n", SockLastError());
}
else
if (s==SOCKET_ERROR)
printf("select failed: %d\n", SockLastError());
}
else
if ( p->type==RT_TCP && p->state == PCS_CLEARINGQUEUE)
{
// regarde si il y a encore des donn<6E>es <20> envoyer pour cette connection
int empty=1;
for(int q=0;q<MAX_TCP_QUEUE;q++)
{
if (tcpqueues[q].state != QS_IDLE && tcpqueues[q].connection == p )
{
printf("*** tcp data remaing ***");
empty=0;
break;
}
}
if (eth_first != eth_last)
{
printf("*** eth data remaininng ***");
empty=0;
break;
}
if (empty)
{
// on peut envoyer le dernier ack et fermer la connection
p->state = PCS_LASTACK;
p->sendtcppacket(TCP_ACK|TCP_FIN,0,NULL);
p->state_seq = p->send_seq;
printf("\nLAST ACK SENT\n");
}
}
else
if ( p->type==RT_TCP&& p->state == PCS_ESTABLISHED && (p->already_sent<(p->window-MAX_TCP_SIZE)))
{
// regarde si il y a des donn<6E>es TCP en entr<74>e
// regarde si il y a des donn<6E>es de dispo
fd_set input_set;
timeval timeout;
timeout.tv_usec = 1;
timeout.tv_sec = 0;
FD_ZERO(&input_set);
FD_SET(p->tcpsocket,&input_set);
int s = select(p->tcpsocket+1,&input_set,NULL,NULL,&timeout);
if (s>0)
{
byte recv_data[MAX_TCP_SIZE]; // window max size
int lr =recv(p->tcpsocket,(char*)recv_data,MAX_TCP_SIZE,0);
if (lr>0)
{
// p->udp_query--;
if (lr>p->window)
printf("*** overflow ***\n");
p->sendtcppacket(TCP_ACK,lr,recv_data);
}
}
}
}
}
// ajout les TCP <20> la liste des datagrammes <20> envoyer
for(int q=0;q<MAX_TCP_QUEUE;q++)
{
tcp_queue* tcp = &tcpqueues[q];
if (tcp->state == QS_TOSEND||tcp->state == QS_TOSENDANDRETRY )
{
printf("\n<<TCP -- ");
tcp->print();
static int identification=1;
int sent = 0;
identification++;
while(sent < tcp->iplen)
{
int tosend = tcp->iplen - sent;
int frag = 0;
eth_queue* qdest = findETHQueue();
if (!qdest)
{
printf("\n *** no more queue ***\n");
// not sure how it will recover???
break;
}
qdest->eth = tcp->eth;
qdest->ip = tcp->ip;
if (tosend>ETH_MAX_SIZE)
{
frag = FLAG_MF;
tosend = ETH_MAX_SIZE;
}
qdest->ip.flag_offset = htons(frag + sent/8);
qdest->ip.identification = identification;
qdest->ip.length = htons(tosend+sizeof(IP_HEADER));
qdest->ip.checksum = 0;
qdest->queuelen = tosend+sizeof(IP_HEADER)+sizeof(ETH_HEADER);
memcpy(((byte*)&qdest->ip) + sizeof(IP_HEADER),((const byte*)&tcp->padding) + sent,tosend);
u16 chkip= ip_sum_calc(sizeof(IP_HEADER),(const byte*)&qdest->ip);
qdest->ip.checksum = htons(chkip);
sent += tosend;
}
if (tcp->state == QS_TOSENDANDRETRY)
{
tcp->state = QS_SENT;
tcp->timeout =get_dtime();
}
else
{
tcp->state = QS_IDLE; // pas d'ack
}
}
else
if (tcp->state == QS_SENT)
{
double tick = get_dtime();
if (tick - tcp->timeout>30.0) // 5 000ms
{
if (tcp->connection->state != PCS_ESTABLISHED)
{
tcp->state = QS_IDLE;
printf("syn did not succeed...\n");
tcp->connection->Reset();
}
else
{
printf("\nretrying...");
tcp->state = QS_TOSENDANDRETRY;
}
}
}
}
// envoie les datagrammes (dans l'ordre)
if (eth_first != eth_last)
{
eth_queue& q = ethqueues[eth_first];
eth_first = (eth_first+1)% MAX_ETH_QUEUE;
q.print();
word32 p = _param[0];
byte* pdata = (byte*)&g_memory_ptr[p];
_param[1] = q.queuelen;
memcpy((void*)pdata,&q.eth,_param[1]);
*((byte*)pdata+q.queuelen) = 0;
}
return 0;
}
break;
case TCPIP_CMD_SEND_DATAGRAM:
{
word32 len = _param[1] & 0xFFFF;
byte* pdata = ( byte*)&g_memory_ptr[_param[0]];
*(pdata+len) = 0; // odd-even
eth_queue* p = (eth_queue*)pdata;
#ifdef DEBUG_ETH
int d = (int)((get_dtime()-start_tcp_time)*1000);
printf("\n>>>[%07d]\n",d);
#endif
int t = ntohs(p->eth.type);
switch(t)
{
case 0x0806: // arp
{
print_arp(&p->arp);
eth_queue* packet = findETHQueue();
if (!packet)
return TCPIP_ERR_UNSUPPORTED;
MAC_STRUCT target_mac;
IP_STRUCT target_ip = p->arp.dst_protoaddr;
if (GET_INT_FROM_IP(p->arp.dst_protoaddr) == GET_INT_FROM_IP(gateway_ip_addr))
target_mac = gateway_mac_addr;
else
target_mac.SetFromIP(target_ip);
packet->eth.type = ntohs(0x0806);
packet->eth.dest_mac = client_mac_addr;
packet->eth.source_mac = target_mac; //gateway_mac_addr;
// copie la source en dest
packet->arp.dst_hwaddr = p->arp.src_hwaddr;
packet->arp.dst_protoaddr = p->arp.src_protoaddr;
// remplis le destinataire
packet->arp.src_hwaddr =target_mac; //gateway_mac_addr;
packet->arp.src_protoaddr = target_ip; //gateway_ip_addr;
packet->arp.hwaddr_format = p->arp.hwaddr_format;
packet->arp.hwaddr_length = p->arp.hwaddr_length;
packet->arp.opcode = htons(2); // reply
packet->queuelen = sizeof(ARP_HEADER)+sizeof(ETH_HEADER);
}
break;
case 0x0800: // IP
{
print_ip_packet(&p->ip);
// ajoute les donn<6E>es dans unfragmented
add_fragment(p);
}
break;
default:
printf("unsupported protocol");
return TCPIP_ERR_UNSUPPORTED;
}
}
return 0;
case TCPIP_CMD_CONNECT:
for(int i=0;i<MAX_TCP_CONNECTION;i++)
PConnections[i].Reset();
for(int i=0;i<MAX_TCP_QUEUE;i++)
tcpqueues[i].Reset();
eth_first=eth_last = 0;
start_tcp_time = get_dtime();
init_server();
return 0;
// Disconnects from the network
case TCPIP_CMD_DISCONNECT:
for(int i=0;i<MAX_TCP_CONNECTION;i++)
PConnections[i].Reset();
return 0;
case TCPIP_CMD_SHUTDOWN:
#ifdef WIN32
WSACleanup();
#endif
return 0;
case TCPIP_SET_IP_ADDRESS:
client_mac_addr = (const byte*)_param;
return 0;
// returns the Macs IP Address in X and Y if IP Address could not be obtained will return 0
case TCPIP_GETIPADDRESS:
_regx=0;
_regy=0;
return 0;
default:
break;
}
return TCPIP_ERR_UNSUPPORTED;
}
eth_queue* findETHQueue()
{
int l = eth_last;
l = (l+1)%MAX_ETH_QUEUE;
if (l==eth_first)
{
printf("\n*** no more ETH queues****\n");
return NULL;
}
eth_queue* q = &ethqueues[eth_last];
memset(q,0,sizeof(eth_queue));
eth_last = l;
return q;
}
tcp_queue* findTCPQueueByFragment(eth_queue* f)
{
int free_frag = -1;
tcp_queue* tcp= NULL;
for(int q=0;q<MAX_TCP_QUEUE;q++)
{
tcp = &tcpqueues[q];
if (tcp->state == QS_IDLE)
free_frag = q;
if (tcp->state == QS_FRAGMENT && tcp->ip.identification == f->ip.identification)
return tcp;
}
if (free_frag == -1)
{
printf("\n*** no more TCP queues****\n");
return NULL;
}
tcp = &tcpqueues[free_frag];
memset(tcp,0,sizeof(tcp_queue));
tcp->state = QS_FRAGMENT;
tcp->eth = f->eth;
tcp->ip = f->ip;
return tcp;
}
tcp_queue* findTCPQueue()
{
tcp_queue* q = NULL;
for(int i=0;i<MAX_TCP_QUEUE;i++)
{
if (tcpqueues[i].state == QS_IDLE)
{
tcp_queue* q = &tcpqueues[i];
memset(q,0,sizeof(tcp_queue));
q->state = QS_IDLE;
return q;
}
}
printf("\n*** no more TCP queues****\n");
return q;
}
int add_fragment(eth_queue* p)
{
tcp_queue* f = findTCPQueueByFragment(p);
if (!f)
return 0;
// ajoute les donn<6E>es
f->ip = p->ip;
f->eth = p->eth;
int ipdatalen = ntohs(p->ip.length) - sizeof(IP_HEADER);
memcpy(f->padding+f->iplen,(const byte*)&p->ip+sizeof(IP_HEADER),ipdatalen);
f->iplen += ipdatalen;
int fo = ntohs(p->ip.flag_offset);
if (!(fo & FLAG_MF))
{
process_unfragmented(f);
f->state=QS_IDLE;
}
return 1;
}
int process_unfragmented(tcp_queue* p)
{
int ip_protocol = p->ip.protocol;
p->print();
if (ip_protocol==17) // udp
{
int dest_port = ntohs(p->udp.udp_dst);
int src_port = ntohs(p->udp.udp_src);
if (p->isDHCP())
{
send_dhcp_response(p);
}
else
{
print_udp(&p->udp);
// cr<63>e une nouvelle connection udp
PConnection* pco = init_connection(PCT_HOST,RT_UDP, src_port,p->ip.ucDestination, dest_port, p->eth.dest_mac,0);
if (pco==NULL)
{
printf("\ncannot init connection\n");
return TCPIP_ERR_SEND;
}
// envoie la commande
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr = p->ip.ucDestination;
server.sin_port = htons( dest_port );
int len = ntohs(p->udp.udp_len)-sizeof(UDP_HEADER);
const char* data = (const char*)&p->udp+sizeof(UDP_HEADER);
// pr<70>pare la r<>ponse
int ret = sendto(pco->tcpsocket, data, len, 0, (struct sockaddr *) &server,sizeof(server) );
if (ret< 0)
{
#ifdef _DEBUG
int err = SockLastError();
printf("socket send failed with err:%d\n",err);
#endif
return TCPIP_ERR_SEND;
}
}
}
else
if (ip_protocol==6) // tcp
{
#ifdef _DEBUG
int chk = p->check_tcp();
if (!chk)
{
printf("ignoring packet\n");
return 0;
}
#endif
int offset =p->tcp.data_offset*4;
int tcplen = p->iplen - offset;
int dest_port = ntohs(p->tcp.dest_port);
int source_port = ntohs(p->tcp.src_port);
struct in_addr dest_ip;
dest_ip= p->ip.ucDestination;
const byte *tcpdata = (const byte*)&p->tcp +offset;
if (p->tcp.flags & TCP_RST)
{
printf("\n*** CONNECTION RESET ***\n");
const char* reset_str = "HTTP/1.0 500";
::send(p->connection->tcpsocket, (const char*)reset_str, strlen(reset_str), 0);
reset_connection(PCT_HOST,RT_TCP,source_port,dest_ip,dest_port);
}
if (p->tcp.flags & TCP_ACK)
{
PConnection* ptcp = find_connection(PCT_HOST,RT_TCP,source_port,dest_ip, dest_port);
if (ptcp)
{
// lib<69>re les query correspondantes
int ack_seq = ntohl(p->tcp.ack_number);
for(int q=0;q<MAX_TCP_QUEUE;q++)
{
if ( tcpqueues[q].state == QS_SENT
&& tcpqueues[q].queueseq <= ack_seq
&& tcpqueues[q].connection == ptcp)
{
printf("- clearing %x (%d)",tcpqueues[q].queueseq,tcpqueues[q].tcplen);
ptcp->already_sent -= tcpqueues[q].tcplen;
if (ptcp->already_sent<0)
{
printf("too many acked!***\n");
ptcp->already_sent=0; // pour essayer de se r<>cup<75>rer
}
tcpqueues[q].state = QS_IDLE;
}
}
if (ptcp->state == PCS_BEINGESTABLISHED && (ptcp->state_seq+1) == ack_seq)
{
ptcp->state = PCS_ESTABLISHED;
ptcp->send_seq++;
printf("\nCONNECTION ESTABLISHED (SERVER)\n");
}
else
if (ptcp->state == PCS_SYNSENT && (ptcp->state_seq+1) == ack_seq)
{
ptcp->sendtcppacket(TCP_ACK , 0, NULL);
ptcp->state = PCS_ESTABLISHED;
ptcp->send_seq++;
printf("\nCONNECTION ESTABLISHED2 (CLIENT)\n");
}
else
if (ptcp->state == PCS_SYNCHING && (ptcp->state_seq+1) == ack_seq)
{
ptcp->state = PCS_ESTABLISHED;
printf("\nCONNECTION ESTABLISHED (CLIENT)\n");
ptcp->send_seq++;
}
else
if (ptcp->state == PCS_CLOSING && ptcp->state_seq == ack_seq)
{
// envoie notre data de fin
ptcp->state = PCS_CLEARINGQUEUE;
}
else
if (ptcp->state == PCS_LASTACK && ( ptcp->state_seq + 1) == ack_seq)
{
// envoie notre data de fin, mais n'attend pas de r<>ponse
ptcp->last_ack = ntohl(p->tcp.seq_number)+1;
ptcp->sendtcppacket(TCP_ACK,0,NULL);
ptcp->Reset();
printf("\nCONNECTION CLOSED\n");
}
}
}
if (p->tcp.flags & TCP_FIN)
{
PConnection* ptcp = find_connection(PCT_HOST,RT_TCP,source_port,dest_ip, dest_port);
if (ptcp)
{
printf("\nclose_wait\n");
ptcp->last_ack = ntohl(p->tcp.seq_number)+2;
ptcp->sendtcppacket(TCP_ACK , 0, NULL);
ptcp->state_seq = ptcp->send_seq;
ptcp->state = PCS_CLOSING;
}
}
if (p->tcp.flags & TCP_SYN && !(p->tcp.flags & TCP_ACK))
{
PConnection* ptcp = init_connection(PCT_HOST,RT_TCP,source_port,dest_ip, dest_port,p->eth.dest_mac, ntohs(p->tcp.window));
if (ptcp==NULL)
return TCPIP_ERR_NO_CONNECTION;
ptcp->last_ack = ntohl(p->tcp.seq_number)+1;
ptcp->state = PCS_SYNCHING;
ptcp->state_seq = ptcp->send_seq;
ptcp->sendtcppacket(TCP_ACK|TCP_SYN, 0, NULL);
}
if (tcplen)
{
PConnection* ptcp = find_connection(PCT_HOST,RT_TCP,source_port,dest_ip, dest_port);
if (ptcp==NULL)
return TCPIP_ERR_NO_CONNECTION;
int ret = send(ptcp->tcpsocket, (const char*)tcpdata, tcplen, 0);
if (ret< 0)
{
#ifdef _DEBUG
int err = SockLastError();
printf("socket send failed with err:%d\n",err);
#endif
return TCPIP_ERR_SEND;
}
// envoie l'ack
ptcp->last_ack = ntohl( p->tcp.seq_number) +tcplen ;
ptcp->sendtcppacket(TCP_ACK , 0, NULL);
}
}
else
{
printf("unsupported ip protocol\n");
return TCPIP_ERR_UNSUPPORTED;
}
return 1;
}
#endif