////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
//
|
//
|
// CHIPS-2.0 TCP/IP SERVER
|
// CHIPS-2.0 TCP/IP SERVER
|
//
|
//
|
// :Author: Jonathan P Dawson
|
// :Author: Jonathan P Dawson
|
// :Date: 17/10/2013
|
// :Date: 17/10/2013
|
// :email: chips@jondawson.org.uk
|
// :email: chips@jondawson.org.uk
|
// :license: MIT
|
// :license: MIT
|
// :Copyright: Copyright (C) Jonathan P Dawson 2013
|
// :Copyright: Copyright (C) Jonathan P Dawson 2013
|
//
|
//
|
// A TCP/IP stack that supports a single socket connection.
|
// A TCP/IP stack that supports a single socket connection.
|
//
|
//
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// TCP-IP User Settings
|
// TCP-IP User Settings
|
//
|
//
|
|
|
unsigned local_mac_address_hi = 0x0001u;
|
const unsigned local_mac_address_hi = 0x0001u;
|
unsigned local_mac_address_med = 0x0203u;
|
const unsigned local_mac_address_med = 0x0203u;
|
unsigned local_mac_address_lo = 0x0405u;
|
const unsigned local_mac_address_lo = 0x0405u;
|
unsigned local_ip_address_hi = 0xc0A8u;//192/168
|
const unsigned local_ip_address_hi = 0xc0A8u;//192/168
|
unsigned local_ip_address_lo = 0x0101u;//1/1
|
const unsigned local_ip_address_lo = 0x0101u;//1/1
|
unsigned local_port = 80u;//http
|
const unsigned local_port = 80u;//http
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// TCP-IP GLOBALS
|
// TCP-IP GLOBALS
|
//
|
//
|
|
|
unsigned tx_packet[512];
|
unsigned tx_packet[512];
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// Checksum calculation routines
|
// Checksum calculation routines
|
//
|
//
|
|
|
//store checksum in a global variable
|
//store checksum in a global variable
|
//unsigneds are 16 bits, so use an array of 2 to hold a 32 bit number
|
//unsigneds are 16 bits, so use an array of 2 to hold a 32 bit number
|
|
|
long unsigned checksum;
|
long unsigned checksum;
|
|
|
//Reset checksum before calculation
|
//Reset checksum before calculation
|
//
|
//
|
|
|
void reset_checksum(){
|
void reset_checksum(){
|
checksum = 0;
|
checksum = 0;
|
}
|
}
|
|
|
//Add 16 bit data value to 32 bit checksum value
|
//Add 16 bit data value to 32 bit checksum value
|
//
|
//
|
|
|
void add_checksum(unsigned data){
|
void add_checksum(unsigned data){
|
checksum += data;
|
checksum += data;
|
if(checksum & 0x10000ul){
|
if(checksum & 0x10000ul){
|
checksum &= 0xffffu;
|
checksum &= 0xffffu;
|
checksum += 1;
|
checksum += 1;
|
}
|
}
|
}
|
}
|
|
|
//Retrieve the calculated checksum
|
//Retrieve the calculated checksum
|
//
|
//
|
|
|
unsigned check_checksum(){
|
unsigned check_checksum(){
|
return ~checksum;
|
return ~checksum;
|
}
|
}
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// UTILITY FUNCTIONS
|
// UTILITY FUNCTIONS
|
//
|
//
|
|
|
unsigned calc_ack(unsigned ack[], unsigned seq[], unsigned length){
|
unsigned calc_ack(unsigned ack[], unsigned seq[], unsigned length){
|
//given a two word sequence number and a one word length
|
//given a two word sequence number and a one word length
|
//calculate a two word acknowledgement number
|
//calculate a two word acknowledgement number
|
//check whether we have new data or not
|
//check whether we have new data or not
|
unsigned new_ack_0;
|
unsigned new_ack_0;
|
unsigned new_ack_1;
|
unsigned new_ack_1;
|
unsigned return_value = 0;
|
unsigned return_value = 0;
|
new_ack_0 = seq[0] + length;
|
new_ack_0 = seq[0] + length;
|
new_ack_1 = seq[1];
|
new_ack_1 = seq[1];
|
if(new_ack_0 < length) new_ack_1 = new_ack_1 + 1;
|
if(new_ack_0 < length) new_ack_1 = new_ack_1 + 1;
|
|
|
//Is this data we have allready acknowledged?
|
//Is this data we have allready acknowledged?
|
if((new_ack_0 != ack[0]) || (new_ack_1 != ack[1])){
|
if((new_ack_0 != ack[0]) || (new_ack_1 != ack[1])){
|
ack[0] = new_ack_0;
|
ack[0] = new_ack_0;
|
ack[1] = new_ack_1;
|
ack[1] = new_ack_1;
|
return_value = 1;
|
return_value = 1;
|
}
|
}
|
return return_value;
|
return return_value;
|
}
|
}
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// Data Link Layer - Ethernet
|
// Data Link Layer - Ethernet
|
//
|
//
|
|
|
void put_ethernet_packet(
|
void put_ethernet_packet(
|
unsigned packet[],
|
unsigned packet[],
|
unsigned number_of_bytes,
|
unsigned number_of_bytes,
|
unsigned destination_mac_address_hi,
|
unsigned destination_mac_address_hi,
|
unsigned destination_mac_address_med,
|
unsigned destination_mac_address_med,
|
unsigned destination_mac_address_lo,
|
unsigned destination_mac_address_lo,
|
unsigned protocol){
|
unsigned protocol){
|
|
|
unsigned byte, index;
|
unsigned byte, index;
|
report(number_of_bytes);
|
report(number_of_bytes);
|
|
|
//set up ethernet header
|
//set up ethernet header
|
packet[0] = destination_mac_address_hi;
|
packet[0] = destination_mac_address_hi;
|
packet[1] = destination_mac_address_med;
|
packet[1] = destination_mac_address_med;
|
packet[2] = destination_mac_address_lo;
|
packet[2] = destination_mac_address_lo;
|
packet[3] = local_mac_address_hi;
|
packet[3] = local_mac_address_hi;
|
packet[4] = local_mac_address_med;
|
packet[4] = local_mac_address_med;
|
packet[5] = local_mac_address_lo;
|
packet[5] = local_mac_address_lo;
|
packet[6] = protocol;
|
packet[6] = protocol;
|
|
|
put_eth(number_of_bytes);
|
put_eth(number_of_bytes);
|
index = 0;
|
index = 0;
|
for(byte=0; byte<number_of_bytes; byte+=2){
|
for(byte=0; byte<number_of_bytes; byte+=2){
|
put_eth(packet[index]);
|
put_eth(packet[index]);
|
index ++;
|
index ++;
|
}
|
}
|
}
|
}
|
|
|
//Get a packet from the ethernet interface
|
//Get a packet from the ethernet interface
|
//Will reply to arp requests
|
//Will reply to arp requests
|
//returns the number of bytes read which may be 0
|
//returns the number of bytes read which may be 0
|
unsigned get_ethernet_packet(unsigned packet[]){
|
unsigned get_ethernet_packet(unsigned packet[]){
|
|
|
unsigned number_of_bytes, index;
|
unsigned number_of_bytes, index;
|
unsigned byte;
|
unsigned byte;
|
|
|
if(!rdy_eth()) return 0;
|
if(!rdy_eth()) return 0;
|
|
|
number_of_bytes = get_eth();
|
number_of_bytes = get_eth();
|
index = 0;
|
index = 0;
|
for(byte=0; byte<number_of_bytes; byte+=2){
|
for(byte=0; byte<number_of_bytes; byte+=2){
|
packet[index] = get_eth();
|
packet[index] = get_eth();
|
index ++;
|
index ++;
|
}
|
}
|
|
|
//Filter out packets not meant for us
|
//Filter out packets not meant for us
|
if(packet[0] != local_mac_address_hi && packet[0] != 0xffffu) return 0;
|
if(packet[0] != local_mac_address_hi && packet[0] != 0xffffu) return 0;
|
if(packet[1] != local_mac_address_med && packet[1] != 0xffffu) return 0;
|
if(packet[1] != local_mac_address_med && packet[1] != 0xffffu) return 0;
|
if(packet[2] != local_mac_address_lo && packet[2] != 0xffffu) return 0;
|
if(packet[2] != local_mac_address_lo && packet[2] != 0xffffu) return 0;
|
|
|
//Process ARP requests within the data link layer
|
//Process ARP requests within the data link layer
|
if (packet[6] == 0x0806){ //ARP
|
if (packet[6] == 0x0806){ //ARP
|
//respond to requests
|
//respond to requests
|
if (packet[10] == 0x0001){
|
if (packet[10] == 0x0001){
|
//construct and send an ARP response
|
//construct and send an ARP response
|
tx_packet[7] = 0x0001; //HTYPE ethernet
|
tx_packet[7] = 0x0001; //HTYPE ethernet
|
tx_packet[8] = 0x0800; //PTYPE IPV4
|
tx_packet[8] = 0x0800; //PTYPE IPV4
|
tx_packet[9] = 0x0604; //HLEN, PLEN
|
tx_packet[9] = 0x0604; //HLEN, PLEN
|
tx_packet[10] = 0x0002; //OPER=REPLY
|
tx_packet[10] = 0x0002; //OPER=REPLY
|
tx_packet[11] = local_mac_address_hi; //SENDER_HARDWARE_ADDRESS
|
tx_packet[11] = local_mac_address_hi; //SENDER_HARDWARE_ADDRESS
|
tx_packet[12] = local_mac_address_med; //SENDER_HARDWARE_ADDRESS
|
tx_packet[12] = local_mac_address_med; //SENDER_HARDWARE_ADDRESS
|
tx_packet[13] = local_mac_address_lo; //SENDER_HARDWARE_ADDRESS
|
tx_packet[13] = local_mac_address_lo; //SENDER_HARDWARE_ADDRESS
|
tx_packet[14] = local_ip_address_hi; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[14] = local_ip_address_hi; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[15] = local_ip_address_lo; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[15] = local_ip_address_lo; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[16] = packet[11]; //TARGET_HARDWARE_ADDRESS
|
tx_packet[16] = packet[11]; //TARGET_HARDWARE_ADDRESS
|
tx_packet[17] = packet[12]; //
|
tx_packet[17] = packet[12]; //
|
tx_packet[18] = packet[13]; //
|
tx_packet[18] = packet[13]; //
|
tx_packet[19] = packet[14]; //TARGET_PROTOCOL_ADDRESS
|
tx_packet[19] = packet[14]; //TARGET_PROTOCOL_ADDRESS
|
tx_packet[20] = packet[15]; //
|
tx_packet[20] = packet[15]; //
|
put_ethernet_packet(
|
put_ethernet_packet(
|
tx_packet,
|
tx_packet,
|
64,
|
64,
|
packet[11],
|
packet[11],
|
packet[12],
|
packet[12],
|
packet[13],
|
packet[13],
|
0x0806);
|
0x0806);
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
return number_of_bytes;
|
return number_of_bytes;
|
}
|
}
|
|
|
unsigned arp_ip_hi[16];
|
unsigned arp_ip_hi[16];
|
unsigned arp_ip_lo[16];
|
unsigned arp_ip_lo[16];
|
unsigned arp_mac_0[16];
|
unsigned arp_mac_0[16];
|
unsigned arp_mac_1[16];
|
unsigned arp_mac_1[16];
|
unsigned arp_mac_2[16];
|
unsigned arp_mac_2[16];
|
unsigned arp_pounsigneder = 0;
|
unsigned arp_pounsigneder = 0;
|
|
|
//return the location of the ip address in the arp cache table
|
//return the location of the ip address in the arp cache table
|
unsigned get_arp_cache(unsigned ip_hi, unsigned ip_lo){
|
unsigned get_arp_cache(unsigned ip_hi, unsigned ip_lo){
|
|
|
unsigned number_of_bytes;
|
unsigned number_of_bytes;
|
unsigned byte;
|
unsigned byte;
|
unsigned packet[16];
|
unsigned packet[16];
|
unsigned i;
|
unsigned i;
|
|
|
//Is the requested IP in the ARP cache?
|
//Is the requested IP in the ARP cache?
|
for(i=0; i<16; i++){
|
for(i=0; i<16; i++){
|
if(arp_ip_hi[i] == ip_hi && arp_ip_lo[i] == ip_lo){
|
if(arp_ip_hi[i] == ip_hi && arp_ip_lo[i] == ip_lo){
|
return i;
|
return i;
|
}
|
}
|
}
|
}
|
|
|
//It is not, so send an arp request
|
//It is not, so send an arp request
|
tx_packet[7] = 0x0001u; //HTYPE ethernet
|
tx_packet[7] = 0x0001u; //HTYPE ethernet
|
tx_packet[8] = 0x0800u; //PTYPE IPV4
|
tx_packet[8] = 0x0800u; //PTYPE IPV4
|
tx_packet[9] = 0x0604u; //HLEN, PLEN
|
tx_packet[9] = 0x0604u; //HLEN, PLEN
|
tx_packet[10] = 0x0001u; //OPER=REQUEST
|
tx_packet[10] = 0x0001u; //OPER=REQUEST
|
tx_packet[11] = local_mac_address_hi; //SENDER_HARDWARE_ADDRESS
|
tx_packet[11] = local_mac_address_hi; //SENDER_HARDWARE_ADDRESS
|
tx_packet[12] = local_mac_address_med; //SENDER_HARDWARE_ADDRESS
|
tx_packet[12] = local_mac_address_med; //SENDER_HARDWARE_ADDRESS
|
tx_packet[13] = local_mac_address_lo; //SENDER_HARDWARE_ADDRESS
|
tx_packet[13] = local_mac_address_lo; //SENDER_HARDWARE_ADDRESS
|
tx_packet[14] = local_ip_address_hi; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[14] = local_ip_address_hi; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[15] = local_ip_address_lo; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[15] = local_ip_address_lo; //SENDER_PROTOCOL_ADDRESS
|
tx_packet[19] = ip_hi; //TARGET_PROTOCOL_ADDRESS
|
tx_packet[19] = ip_hi; //TARGET_PROTOCOL_ADDRESS
|
tx_packet[20] = ip_lo; //
|
tx_packet[20] = ip_lo; //
|
put_ethernet_packet(
|
put_ethernet_packet(
|
tx_packet,
|
tx_packet,
|
64u,
|
64u,
|
0xffffu, //broadcast via ethernet
|
0xffffu, //broadcast via ethernet
|
0xffffu,
|
0xffffu,
|
0xffffu,
|
0xffffu,
|
0x0806u);
|
0x0806u);
|
|
|
//wait for a response
|
//wait for a response
|
while(1){
|
while(1){
|
|
|
number_of_bytes = get_eth();
|
number_of_bytes = get_eth();
|
i = 0;
|
i = 0;
|
for(byte=0; byte<number_of_bytes; byte+=2){
|
for(byte=0; byte<number_of_bytes; byte+=2){
|
//only keep the part of the packet we care about
|
//only keep the part of the packet we care about
|
if(i < 16){
|
if(i < 16){
|
packet[i] = get_eth();
|
packet[i] = get_eth();
|
} else {
|
} else {
|
get_eth();
|
get_eth();
|
}
|
}
|
i++;
|
i++;
|
}
|
}
|
|
|
//Process ARP requests within the data link layer
|
//Process ARP requests within the data link layer
|
if (packet[6] == 0x0806 && packet[10] == 0x0002){
|
if (packet[6] == 0x0806 && packet[10] == 0x0002){
|
if (packet[14] == ip_hi && packet[15] == ip_lo){
|
if (packet[14] == ip_hi && packet[15] == ip_lo){
|
arp_ip_hi[arp_pounsigneder] = ip_hi;
|
arp_ip_hi[arp_pounsigneder] = ip_hi;
|
arp_ip_lo[arp_pounsigneder] = ip_lo;
|
arp_ip_lo[arp_pounsigneder] = ip_lo;
|
arp_mac_0[arp_pounsigneder] = packet[11];
|
arp_mac_0[arp_pounsigneder] = packet[11];
|
arp_mac_1[arp_pounsigneder] = packet[12];
|
arp_mac_1[arp_pounsigneder] = packet[12];
|
arp_mac_2[arp_pounsigneder] = packet[13];
|
arp_mac_2[arp_pounsigneder] = packet[13];
|
i = arp_pounsigneder;
|
i = arp_pounsigneder;
|
arp_pounsigneder++;
|
arp_pounsigneder++;
|
if(arp_pounsigneder == 16) arp_pounsigneder = 0;
|
if(arp_pounsigneder == 16) arp_pounsigneder = 0;
|
return i;
|
return i;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// Network Layer - Internet Protocol
|
// Network Layer - Internet Protocol
|
//
|
//
|
|
|
void put_ip_packet(unsigned packet[], unsigned total_length, unsigned protocol, unsigned ip_hi, unsigned ip_lo){
|
void put_ip_packet(unsigned packet[], unsigned total_length, unsigned protocol, unsigned ip_hi, unsigned ip_lo){
|
unsigned number_of_bytes, i, arp_cache;
|
unsigned number_of_bytes, i, arp_cache;
|
|
|
//see if the requested IP address is in the arp cache
|
//see if the requested IP address is in the arp cache
|
arp_cache = get_arp_cache(ip_hi, ip_lo);
|
arp_cache = get_arp_cache(ip_hi, ip_lo);
|
|
|
//Form IP header
|
//Form IP header
|
packet[7] = 0x4500; //Version 4 header length 5x32
|
packet[7] = 0x4500; //Version 4 header length 5x32
|
packet[8] = total_length; //IP data + header
|
packet[8] = total_length; //IP data + header
|
packet[9] = 0x0000; //Identification
|
packet[9] = 0x0000; //Identification
|
packet[10] = 0x4000; //don't fragment
|
packet[10] = 0x4000; //don't fragment
|
packet[11] = 0xFF00u | protocol; //ttl|protocol
|
packet[11] = 0xFF00u | protocol; //ttl|protocol
|
packet[12] = 0x0000; //checksum
|
packet[12] = 0x0000; //checksum
|
packet[13] = local_ip_address_hi;//source_high
|
packet[13] = local_ip_address_hi;//source_high
|
packet[14] = local_ip_address_lo;//source_low
|
packet[14] = local_ip_address_lo;//source_low
|
packet[15] = ip_hi; //dest_high
|
packet[15] = ip_hi; //dest_high
|
packet[16] = ip_lo; //dest_low
|
packet[16] = ip_lo; //dest_low
|
number_of_bytes = total_length + 14;
|
number_of_bytes = total_length + 14;
|
|
|
//calculate checksum
|
//calculate checksum
|
reset_checksum();
|
reset_checksum();
|
for(i=7; i<=16; i++){
|
for(i=7; i<=16; i++){
|
add_checksum(packet[i]);
|
add_checksum(packet[i]);
|
}
|
}
|
packet[12] = check_checksum();
|
packet[12] = check_checksum();
|
|
|
//enforce minimum ethernet frame size
|
//enforce minimum ethernet frame size
|
if(number_of_bytes < 64){
|
if(number_of_bytes < 64){
|
number_of_bytes = 64;
|
number_of_bytes = 64;
|
}
|
}
|
|
|
//send packet over ethernet
|
//send packet over ethernet
|
put_ethernet_packet(
|
put_ethernet_packet(
|
packet, //packet
|
packet, //packet
|
number_of_bytes, //number_of_bytes
|
number_of_bytes, //number_of_bytes
|
arp_mac_0[arp_cache], //destination mac address
|
arp_mac_0[arp_cache], //destination mac address
|
arp_mac_1[arp_cache], //
|
arp_mac_1[arp_cache], //
|
arp_mac_2[arp_cache], //
|
arp_mac_2[arp_cache], //
|
0x0800); //protocol IPv4
|
0x0800); //protocol IPv4
|
}
|
}
|
|
|
unsigned get_ip_packet(unsigned packet[]){
|
unsigned get_ip_packet(unsigned packet[]){
|
unsigned ip_payload;
|
unsigned ip_payload;
|
unsigned total_length;
|
unsigned total_length;
|
unsigned header_length;
|
unsigned header_length;
|
unsigned payload_start;
|
unsigned payload_start;
|
unsigned payload_length;
|
unsigned payload_length;
|
unsigned i, from, to;
|
unsigned i, from, to;
|
unsigned payload_end;
|
unsigned payload_end;
|
|
unsigned number_of_bytes;
|
|
|
number_of_bytes = get_ethernet_packet(packet);
|
number_of_bytes = get_ethernet_packet(packet);
|
|
|
if(number_of_bytes == 0) return 0;
|
if(number_of_bytes == 0) return 0;
|
if(packet[6] != 0x0800) return 0;
|
if(packet[6] != 0x0800) return 0;
|
if(packet[15] != local_ip_address_hi) return 0;
|
if(packet[15] != local_ip_address_hi) return 0;
|
if(packet[16] != local_ip_address_lo) return 0;
|
if(packet[16] != local_ip_address_lo) return 0;
|
if((packet[11] & 0xff) == 1){//ICMP
|
if((packet[11] & 0xff) == 1){//ICMP
|
header_length = ((packet[7] >> 8) & 0xf) << 1; //in words
|
header_length = ((packet[7] >> 8) & 0xf) << 1; //in words
|
payload_start = header_length + 7; //in words
|
payload_start = header_length + 7; //in words
|
total_length = packet[8]; //in bytes
|
total_length = packet[8]; //in bytes
|
payload_length = ((total_length+1) >> 1) - header_length; //in words
|
payload_length = ((total_length+1) >> 1) - header_length; //in words
|
payload_end = payload_start + payload_length - 1; //in words
|
payload_end = payload_start + payload_length - 1; //in words
|
|
|
if(packet[payload_start] == 0x0800){//ping request
|
if(packet[payload_start] == 0x0800){//ping request
|
|
|
//copy icmp packet to response
|
//copy icmp packet to response
|
to = 19;//assume that 17 and 18 are 0
|
to = 19;//assume that 17 and 18 are 0
|
reset_checksum();
|
reset_checksum();
|
for(from=payload_start+2; from<=payload_end; from++){
|
for(from=payload_start+2; from<=payload_end; from++){
|
i = packet[from];
|
i = packet[from];
|
add_checksum(i);
|
add_checksum(i);
|
tx_packet[to] = i;
|
tx_packet[to] = i;
|
to++;
|
to++;
|
}
|
}
|
tx_packet[17] = 0;//ping response
|
tx_packet[17] = 0;//ping response
|
tx_packet[18] = check_checksum();
|
tx_packet[18] = check_checksum();
|
|
|
//send ping response
|
//send ping response
|
put_ip_packet(
|
put_ip_packet(
|
tx_packet,
|
tx_packet,
|
total_length,
|
total_length,
|
1,//icmp
|
1,//icmp
|
packet[13], //remote ip
|
packet[13], //remote ip
|
packet[14] //remote ip
|
packet[14] //remote ip
|
);
|
);
|
}
|
}
|
return 0;
|
return 0;
|
|
|
}
|
}
|
if((packet[11] & 0xff) != 6) return 0;//TCP
|
if((packet[11] & 0xff) != 6) return 0;//TCP
|
return number_of_bytes;
|
return number_of_bytes;
|
}
|
}
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
// Transport Layer - TCP
|
// Transport Layer - TCP
|
//
|
//
|
|
|
unsigned remote_ip_hi, remote_ip_lo;
|
unsigned remote_ip_hi, remote_ip_lo;
|
|
|
unsigned tx_source=0;
|
unsigned tx_source=0;
|
unsigned tx_dest=0;
|
unsigned tx_dest=0;
|
unsigned tx_seq[2];
|
unsigned tx_seq[2];
|
unsigned next_tx_seq[2];
|
unsigned next_tx_seq[2];
|
unsigned tx_ack[2];
|
unsigned tx_ack[2];
|
unsigned tx_window=1460; //ethernet MTU - 40 bytes for TCP/IP header
|
unsigned tx_window=1460; //ethernet MTU - 40 bytes for TCP/IP header
|
|
|
unsigned tx_fin_flag=0;
|
unsigned tx_fin_flag=0;
|
unsigned tx_syn_flag=0;
|
unsigned tx_syn_flag=0;
|
unsigned tx_rst_flag=0;
|
unsigned tx_rst_flag=0;
|
unsigned tx_psh_flag=0;
|
unsigned tx_psh_flag=0;
|
unsigned tx_ack_flag=0;
|
unsigned tx_ack_flag=0;
|
unsigned tx_urg_flag=0;
|
unsigned tx_urg_flag=0;
|
|
|
unsigned rx_source=0;
|
unsigned rx_source=0;
|
unsigned rx_dest=0;
|
unsigned rx_dest=0;
|
unsigned rx_seq[2];
|
unsigned rx_seq[2];
|
unsigned rx_ack[2];
|
unsigned rx_ack[2];
|
unsigned rx_window=0;
|
unsigned rx_window=0;
|
|
|
unsigned rx_fin_flag=0;
|
unsigned rx_fin_flag=0;
|
unsigned rx_syn_flag=0;
|
unsigned rx_syn_flag=0;
|
unsigned rx_rst_flag=0;
|
unsigned rx_rst_flag=0;
|
unsigned rx_psh_flag=0;
|
unsigned rx_psh_flag=0;
|
unsigned rx_ack_flag=0;
|
unsigned rx_ack_flag=0;
|
unsigned rx_urg_flag=0;
|
unsigned rx_urg_flag=0;
|
|
|
void put_tcp_packet(unsigned tx_packet [], unsigned tx_length){
|
void put_tcp_packet(unsigned tx_packet [], unsigned tx_length){
|
|
|
unsigned payload_start = 17;
|
unsigned payload_start = 17;
|
unsigned packet_length;
|
unsigned packet_length;
|
unsigned index;
|
unsigned index;
|
|
unsigned i;
|
|
|
//encode TCP header
|
//encode TCP header
|
tx_packet[payload_start + 0] = tx_source;
|
tx_packet[payload_start + 0] = tx_source;
|
tx_packet[payload_start + 1] = tx_dest;
|
tx_packet[payload_start + 1] = tx_dest;
|
tx_packet[payload_start + 2] = tx_seq[1];
|
tx_packet[payload_start + 2] = tx_seq[1];
|
tx_packet[payload_start + 3] = tx_seq[0];
|
tx_packet[payload_start + 3] = tx_seq[0];
|
tx_packet[payload_start + 4] = tx_ack[1];
|
tx_packet[payload_start + 4] = tx_ack[1];
|
tx_packet[payload_start + 5] = tx_ack[0];
|
tx_packet[payload_start + 5] = tx_ack[0];
|
tx_packet[payload_start + 6] = 0x5000; //5 long words
|
tx_packet[payload_start + 6] = 0x5000; //5 long words
|
tx_packet[payload_start + 7] = tx_window;
|
tx_packet[payload_start + 7] = tx_window;
|
tx_packet[payload_start + 8] = 0;
|
tx_packet[payload_start + 8] = 0;
|
tx_packet[payload_start + 9] = 0;
|
tx_packet[payload_start + 9] = 0;
|
|
|
//encode flags
|
//encode flags
|
if(tx_fin_flag) tx_packet[payload_start + 6] |= 0x01;
|
if(tx_fin_flag) tx_packet[payload_start + 6] |= 0x01;
|
if(tx_syn_flag) tx_packet[payload_start + 6] |= 0x02;
|
if(tx_syn_flag) tx_packet[payload_start + 6] |= 0x02;
|
if(tx_rst_flag) tx_packet[payload_start + 6] |= 0x04;
|
if(tx_rst_flag) tx_packet[payload_start + 6] |= 0x04;
|
if(tx_psh_flag) tx_packet[payload_start + 6] |= 0x08;
|
if(tx_psh_flag) tx_packet[payload_start + 6] |= 0x08;
|
if(tx_ack_flag) tx_packet[payload_start + 6] |= 0x10;
|
if(tx_ack_flag) tx_packet[payload_start + 6] |= 0x10;
|
if(tx_urg_flag) tx_packet[payload_start + 6] |= 0x20;
|
if(tx_urg_flag) tx_packet[payload_start + 6] |= 0x20;
|
|
|
//calculate checksum
|
//calculate checksum
|
//length of payload + header + pseudo_header in words
|
//length of payload + header + pseudo_header in words
|
reset_checksum();
|
reset_checksum();
|
add_checksum(local_ip_address_hi);
|
add_checksum(local_ip_address_hi);
|
add_checksum(local_ip_address_lo);
|
add_checksum(local_ip_address_lo);
|
add_checksum(remote_ip_hi);
|
add_checksum(remote_ip_hi);
|
add_checksum(remote_ip_lo);
|
add_checksum(remote_ip_lo);
|
add_checksum(0x0006);
|
add_checksum(0x0006);
|
add_checksum(tx_length+20);//tcp_header + tcp_payload in bytes
|
add_checksum(tx_length+20);//tcp_header + tcp_payload in bytes
|
|
|
packet_length = (tx_length + 20 + 1) >> 1;
|
packet_length = (tx_length + 20 + 1) >> 1;
|
index = payload_start;
|
index = payload_start;
|
for(i=0; i<packet_length; i++){
|
for(i=0; i<packet_length; i++){
|
add_checksum(tx_packet[index]);
|
add_checksum(tx_packet[index]);
|
index++;
|
index++;
|
}
|
}
|
tx_packet[payload_start + 8] = check_checksum();
|
tx_packet[payload_start + 8] = check_checksum();
|
|
|
put_ip_packet(
|
put_ip_packet(
|
tx_packet,
|
tx_packet,
|
tx_length + 40,
|
tx_length + 40,
|
6,//tcp
|
6,//tcp
|
remote_ip_hi, //remote ip
|
remote_ip_hi, //remote ip
|
remote_ip_lo //remote ip
|
remote_ip_lo //remote ip
|
);
|
);
|
}
|
}
|
|
|
unsigned rx_length, rx_start;
|
unsigned rx_length, rx_start;
|
|
|
unsigned get_tcp_packet(unsigned rx_packet []){
|
unsigned get_tcp_packet(unsigned rx_packet []){
|
|
|
unsigned number_of_bytes, header_length, payload_start, total_length, payload_length, payload_end, tcp_header_length;
|
unsigned number_of_bytes, header_length, payload_start, total_length, payload_length, payload_end, tcp_header_length;
|
|
|
number_of_bytes = get_ip_packet(rx_packet);
|
number_of_bytes = get_ip_packet(rx_packet);
|
|
|
//decode lengths from the IP header
|
//decode lengths from the IP header
|
header_length = ((rx_packet[7] >> 8) & 0xf) << 1; //in words
|
header_length = ((rx_packet[7] >> 8) & 0xf) << 1; //in words
|
payload_start = header_length + 7; //in words
|
payload_start = header_length + 7; //in words
|
|
|
total_length = rx_packet[8]; //in bytes
|
total_length = rx_packet[8]; //in bytes
|
payload_length = total_length - (header_length << 1); //in bytes
|
payload_length = total_length - (header_length << 1); //in bytes
|
tcp_header_length = ((rx_packet[payload_start + 6] & 0xf000u)>>10); //in bytes
|
tcp_header_length = ((rx_packet[payload_start + 6] & 0xf000u)>>10); //in bytes
|
rx_length = payload_length - tcp_header_length; //in bytes
|
rx_length = payload_length - tcp_header_length; //in bytes
|
rx_start = payload_start + (tcp_header_length >> 1); //in words
|
rx_start = payload_start + (tcp_header_length >> 1); //in words
|
|
|
//decode TCP header
|
//decode TCP header
|
rx_source = rx_packet[payload_start + 0];
|
rx_source = rx_packet[payload_start + 0];
|
rx_dest = rx_packet[payload_start + 1];
|
rx_dest = rx_packet[payload_start + 1];
|
rx_seq[1] = rx_packet[payload_start + 2];
|
rx_seq[1] = rx_packet[payload_start + 2];
|
rx_seq[0] = rx_packet[payload_start + 3];
|
rx_seq[0] = rx_packet[payload_start + 3];
|
rx_ack[1] = rx_packet[payload_start + 4];
|
rx_ack[1] = rx_packet[payload_start + 4];
|
rx_ack[0] = rx_packet[payload_start + 5];
|
rx_ack[0] = rx_packet[payload_start + 5];
|
rx_window = rx_packet[payload_start + 7];
|
rx_window = rx_packet[payload_start + 7];
|
|
|
//decode flags
|
//decode flags
|
rx_fin_flag = rx_packet[payload_start + 6] & 0x01;
|
rx_fin_flag = rx_packet[payload_start + 6] & 0x01;
|
rx_syn_flag = rx_packet[payload_start + 6] & 0x02;
|
rx_syn_flag = rx_packet[payload_start + 6] & 0x02;
|
rx_rst_flag = rx_packet[payload_start + 6] & 0x04;
|
rx_rst_flag = rx_packet[payload_start + 6] & 0x04;
|
rx_psh_flag = rx_packet[payload_start + 6] & 0x08;
|
rx_psh_flag = rx_packet[payload_start + 6] & 0x08;
|
rx_ack_flag = rx_packet[payload_start + 6] & 0x10;
|
rx_ack_flag = rx_packet[payload_start + 6] & 0x10;
|
rx_urg_flag = rx_packet[payload_start + 6] & 0x20;
|
rx_urg_flag = rx_packet[payload_start + 6] & 0x20;
|
|
|
return number_of_bytes;
|
return number_of_bytes;
|
}
|
}
|
|
|
void application_put_data(unsigned packet[], unsigned start, unsigned length){
|
void application_put_data(unsigned packet[], unsigned start, unsigned length){
|
unsigned i, index;
|
unsigned i, index;
|
|
|
index = start;
|
index = start;
|
put_socket(length);
|
put_socket(length);
|
for(i=0; i<length; i+=2){
|
for(i=0; i<length; i+=2){
|
put_socket(packet[index]);
|
put_socket(packet[index]);
|
index++;
|
index++;
|
}
|
}
|
}
|
}
|
|
|
unsigned application_get_data(unsigned packet[], unsigned start){
|
unsigned application_get_data(unsigned packet[], unsigned start){
|
unsigned i, index, length;
|
unsigned i, index, length;
|
|
|
if(!ready_socket()){
|
if(!ready_socket()){
|
return 0;
|
return 0;
|
}
|
}
|
|
|
index = start;
|
index = start;
|
length = get_socket();
|
length = get_socket();
|
for(i=0; i<length; i+=2){
|
for(i=0; i<length; i+=2){
|
packet[index] = get_socket();
|
packet[index] = get_socket();
|
index++;
|
index++;
|
}
|
}
|
return length;
|
return length;
|
}
|
}
|
|
|
void server()
|
void server()
|
{
|
{
|
unsigned rx_packet[1024];
|
unsigned rx_packet[1024];
|
unsigned tx_packet[1024];
|
unsigned tx_packet[1024];
|
unsigned tx_start = 27;
|
unsigned tx_start = 27;
|
|
|
unsigned new_rx_data = 0;
|
unsigned new_rx_data = 0;
|
unsigned new_tx_data = 0;
|
unsigned new_tx_data = 0;
|
unsigned tx_length;
|
unsigned tx_length;
|
unsigned timeout;
|
unsigned timeout;
|
unsigned resend_wait;
|
unsigned resend_wait;
|
unsigned bytes;
|
unsigned bytes;
|
unsigned last_state;
|
unsigned last_state;
|
unsigned new_rx_data;
|
unsigned new_rx_data;
|
|
|
unsigned listen = 0;
|
const unsigned listen = 0;
|
unsigned open = 1;
|
const unsigned open = 1;
|
unsigned send = 2;
|
const unsigned send = 2;
|
unsigned wait_acknowledge = 3;
|
const unsigned wait_acknowledge = 3;
|
unsigned close = 4;
|
const unsigned close = 4;
|
unsigned state = listen;
|
unsigned state = listen;
|
|
|
tx_seq[0] = 0;
|
tx_seq[0] = 0;
|
tx_seq[1] = 0;
|
tx_seq[1] = 0;
|
|
|
while(1){
|
while(1){
|
|
|
if(timeout){
|
if(timeout){
|
timeout--;
|
timeout--;
|
} else {
|
} else {
|
timeout = 120; //2 mins @100 MHz
|
timeout = 120; //2 mins @100 MHz
|
state = listen;
|
state = listen;
|
tx_syn_flag = 0;
|
tx_syn_flag = 0;
|
tx_fin_flag = 0;
|
tx_fin_flag = 0;
|
tx_ack_flag = 0;
|
tx_ack_flag = 0;
|
tx_rst_flag = 1;
|
tx_rst_flag = 1;
|
put_tcp_packet(tx_packet, 0);//send reset packet
|
put_tcp_packet(tx_packet, 0);//send reset packet
|
}
|
}
|
|
|
// (optionaly) send something
|
// (optionaly) send something
|
switch(state){
|
switch(state){
|
case 0:
|
case listen:
|
tx_rst_flag = 0;
|
tx_rst_flag = 0;
|
tx_syn_flag = 0;
|
tx_syn_flag = 0;
|
tx_fin_flag = 0;
|
tx_fin_flag = 0;
|
tx_ack_flag = 0;
|
tx_ack_flag = 0;
|
break;
|
break;
|
case 1:
|
case open:
|
// set remote ip/port
|
// set remote ip/port
|
remote_ip_hi = rx_packet[13];
|
remote_ip_hi = rx_packet[13];
|
remote_ip_lo = rx_packet[14];
|
remote_ip_lo = rx_packet[14];
|
tx_dest = rx_source;
|
tx_dest = rx_source;
|
tx_source = local_port;
|
tx_source = local_port;
|
// send syn_ack
|
// send syn_ack
|
calc_ack(tx_ack, rx_seq, 1);
|
calc_ack(tx_ack, rx_seq, 1);
|
tx_syn_flag = 1;
|
tx_syn_flag = 1;
|
tx_ack_flag = 1;
|
tx_ack_flag = 1;
|
put_tcp_packet(tx_packet, 0);
|
put_tcp_packet(tx_packet, 0);
|
break;
|
break;
|
case 2:
|
case send:
|
// application -> tcp
|
// application -> tcp
|
tx_length = application_get_data(tx_packet, tx_start);
|
tx_length = application_get_data(tx_packet, tx_start);
|
tx_seq[0] = next_tx_seq[0];
|
tx_seq[0] = next_tx_seq[0];
|
tx_seq[1] = next_tx_seq[1];
|
tx_seq[1] = next_tx_seq[1];
|
calc_ack(next_tx_seq, tx_seq, tx_length);
|
calc_ack(next_tx_seq, tx_seq, tx_length);
|
tx_syn_flag = 0;
|
tx_syn_flag = 0;
|
tx_ack_flag = 1;
|
tx_ack_flag = 1;
|
put_tcp_packet(tx_packet, tx_length);
|
put_tcp_packet(tx_packet, tx_length);
|
break;
|
break;
|
case 3:
|
case wait_acknowledge:
|
// resend until acknowledge recieved
|
// resend until acknowledge recieved
|
put_tcp_packet(tx_packet, tx_length);
|
put_tcp_packet(tx_packet, tx_length);
|
break;
|
break;
|
case 4:
|
case close:
|
// send fin ack
|
// send fin ack
|
tx_fin_flag = 1;
|
tx_fin_flag = 1;
|
tx_ack_flag = 1;
|
tx_ack_flag = 1;
|
calc_ack(tx_ack, rx_seq, 1);
|
calc_ack(tx_ack, rx_seq, 1);
|
put_tcp_packet(tx_packet, 0);
|
put_tcp_packet(tx_packet, 0);
|
break;
|
break;
|
}
|
}
|
|
|
// repeatedly check for responses
|
// repeatedly check for responses
|
for(resend_wait = 10000; resend_wait; resend_wait--){ //1 second @ 100MHz
|
for(resend_wait = 10000; resend_wait; resend_wait--){ //1 second @ 100MHz
|
bytes = get_tcp_packet(rx_packet);
|
bytes = get_tcp_packet(rx_packet);
|
if(bytes && (rx_dest == local_port)){
|
if(bytes && (rx_dest == local_port)){
|
//Once connection is established ignore other connection attempts
|
//Once connection is established ignore other connection attempts
|
if(state != listen && rx_source != tx_dest) continue;
|
if(state != listen && rx_source != tx_dest) continue;
|
new_rx_data = 0;
|
new_rx_data = 0;
|
last_state = state;
|
last_state = state;
|
switch(state){
|
switch(state){
|
|
|
// If a syn packet is recieved, wait for an ack
|
// If a syn packet is recieved, wait for an ack
|
case 0:
|
case listen:
|
if(rx_syn_flag) state = open;
|
if(rx_syn_flag) state = open;
|
else{
|
else{
|
tx_rst_flag = 1;
|
tx_rst_flag = 1;
|
put_tcp_packet(tx_packet, 0);//send reset packet
|
put_tcp_packet(tx_packet, 0);//send reset packet
|
}
|
}
|
break;
|
break;
|
|
|
// If an ack is recieved the connection is established
|
// If an ack is recieved the connection is established
|
case 1:
|
case open:
|
if(rx_ack_flag){
|
if(rx_ack_flag){
|
tx_seq[1] = rx_ack[1];
|
tx_seq[1] = rx_ack[1];
|
tx_seq[0] = rx_ack[0];
|
tx_seq[0] = rx_ack[0];
|
next_tx_seq[1] = rx_ack[1];
|
next_tx_seq[1] = rx_ack[1];
|
next_tx_seq[0] = rx_ack[0];
|
next_tx_seq[0] = rx_ack[0];
|
state = send;
|
state = send;
|
}
|
}
|
break;
|
break;
|
|
|
// Send some data
|
// Send some data
|
case 2:
|
case send:
|
new_rx_data = calc_ack(tx_ack, rx_seq, rx_length);
|
new_rx_data = calc_ack(tx_ack, rx_seq, rx_length);
|
if(rx_fin_flag){
|
if(rx_fin_flag){
|
state = close;
|
state = close;
|
} else if( tx_length ){
|
} else if( tx_length ){
|
state = wait_acknowledge;
|
state = wait_acknowledge;
|
}
|
}
|
break;
|
break;
|
|
|
// Wait until data is acknowledged before sending some more.
|
// Wait until data is acknowledged before sending some more.
|
case 3:
|
case wait_acknowledge:
|
|
|
new_rx_data = calc_ack(tx_ack, rx_seq, rx_length);
|
new_rx_data = calc_ack(tx_ack, rx_seq, rx_length);
|
if(rx_fin_flag){
|
if(rx_fin_flag){
|
state = close;
|
state = close;
|
} else if( rx_ack_flag &&
|
} else if( rx_ack_flag &&
|
(next_tx_seq[1] == rx_ack[1]) &&
|
(next_tx_seq[1] == rx_ack[1]) &&
|
(next_tx_seq[0] == rx_ack[0])){
|
(next_tx_seq[0] == rx_ack[0])){
|
state = send;
|
state = send;
|
}
|
}
|
|
|
break;
|
break;
|
|
|
// wait for fin/ack.
|
// wait for fin/ack.
|
case 4:
|
case close:
|
if(rx_ack_flag) state = listen;
|
if(rx_ack_flag) state = listen;
|
break;
|
break;
|
}
|
}
|
|
|
if(rx_rst_flag) state = listen;
|
if(rx_rst_flag) state = listen;
|
|
|
// Transfer any new data to the application
|
// Transfer any new data to the application
|
if(new_rx_data){
|
if(new_rx_data){
|
application_put_data(rx_packet, rx_start, rx_length);
|
application_put_data(rx_packet, rx_start, rx_length);
|
//Ack can go in next transmission.
|
//Ack can go in next transmission.
|
if(state == last_state) put_tcp_packet(tx_packet, tx_length);
|
if(state == last_state) put_tcp_packet(tx_packet, tx_length);
|
}
|
}
|
|
|
if(state == send && ready_socket()){
|
if(state == send && ready_socket()){
|
break;
|
break;
|
}
|
}
|
|
|
if(state != last_state){
|
if(state != last_state){
|
timeout = 120;
|
timeout = 120;
|
break;
|
break;
|
}
|
}
|
|
|
} else {
|
} else {
|
wait_clocks(10000);//100 us @100 MHz
|
wait_clocks(10000);//100 us @100 MHz
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|