/* Mark Amato 4.8.07 Changelog: */ /* CONVENTION 1: defines will be ALL CAPS with words seperated by _ CONVENTION 2: functions will be one word, with caps denoting each word (ie WriteOp) */ //Bruce defs #define begin { #define end } /* ARP defines ( info from http://www.geocities.com/SiliconValley/Vista/8672/network/arp.html ) Directly before the start of the ARP packet is the ethernet header (info from http://www.pcausa.com/resources/ndispacket_decode.htm) Real life example packet used for idiot-checking and better understanding of the packet layout: http://www.helsinki.fi/atk/yhteydet/tekniikka/paketit.html Ethernet Header: 14 bytes +--------+ |MAC | 6 bytes |Dest. | |Address | +--------+ |MAC | 6 bytes |Source | |Address | +--------+ |Ethernet| 2 bytes |Type | |Field | +--------+ Here's the ARP layout: 28 bytes +--------+ |Hardware| 2 bytes |MAC | |Address | |Type | +--------+ |Protocol| 2 bytes |Address | |Type | +--------+ |Hardware| 1 byte |MAC | |Address | |Size | +--------+ |Protocol| 1 byte |Address | |Size | +--------+ |Op | 2 bytes +--------+ |Sender | 6 bytes |MAC | |Address | +--------+ |Sender | 4 bytes |IP | |Address | +--------+ |Target | 6 bytes |MAC | |Address | +--------+ |Target | 4 bytes |IP | |Address | +--------+ ARP packets are 42 bytes and have no checksum. */ // Positioning & length defines // Ethernet header #define ETH_MAC_DEST_ADDR_START 0 #define ETH_MAC_DEST_ADDR_LENGTH 6 #define ETH_MAC_SRC_ADDR_START 6 #define ETH_MAC_SRC_ADDR_LENGTH 6 #define ETH_TYPE_START 12 #define ETH_TYPE_LENGTH 2 // ARP packet #define ARP_HW_MAC_ADDR_TYPE_START 14 #define ARP_HW_MAC_ADDR_TYPE_LENGTH 2 #define ARP_PROTOCOL_ADDR_TYPE_START 16 #define ARP_PROTOCOL_ADDR_TYPE_LENGTH 2 #define ARP_HW_MAC_ADDR_SIZE_START 18 #define ARP_HW_MAC_ADDR_SIZE_LENGTH 1 #define ARP_PROTOCOL_ADDR_SIZE_START 19 #define ARP_PROTOCOL_ADDR_SIZE_LENGTH 1 #define ARP_OP_START 20 #define ARP_OP_LENGTH 2 #define ARP_SENDER_MAC_ADDR_START 22 #define ARP_SENDER_MAC_ADDR_LENGTH 6 #define ARP_SENDER_IP_ADDR_START 28 #define ARP_SENDER_IP_ADDR_LENGTH 4 #define ARP_TARGET_MAC_ADDR_START 32 #define ARP_TARGET_MAC_ADDR_LENGTH 6 #define ARP_TARGET_IP_ADDR_START 38 #define ARP_TARGET_IP_ADDR_LENGTH 4 // Values and opcodes and somesuch // For ARP requests, Destination MAC address is ff:ff:ff:ff:ff:ff; Ethernet Type Field is 0x0806. #define ARP_DEST_ADDR 0xff #define ARP_ETH_TYPE_FIELD_L 0x06 #define ARP_ETH_TYPE_FIELD_H 0x08 // ARP Opcodes: 1 is ARP request, 2 is ARP reply #define ARP_OP_REQUEST_L 0x01 #define ARP_OP_REQUEST_H 0x00 #define ARP_OP_REPLY_L 0x02 #define ARP_OP_REPLY_H 0x00 #define IP_HEADER_START 14 #define IP_TOTAL_LENGTH_OFFSET 2 #define IP_FLAG_OFFSET 6 #define IP_TTL_OFFSET 8 #define IP_PROTOCOL_OFFSET 9 #define IP_CHECKSUM_START_OFFSET 10 #define IP_SOURCE_ADDR_OFFSET 12 #define IP_DEST_ADDR_OFFSET 16 //ICMP #define IP_ICMP_PROTOCOL 1 #define ICMP_HEADER_START 34 #define ICMP_HEADER_OFFSET 0 #define ICMP_CHECKSUM_OFFSET 2 #define ICMP_ECHO_REQUEST 8 #define ICMP_ECHO_REPLY 0 // UDP #define UDP_PROTOCOL 0x11 #define UDP_HEADER_START 34 #define UDP_DEST_PORT_OFFSET 2 #define UDP_LENGTH_OFFSET 4 #define UDP_CHECKSUM_OFFSET 6 #define UDP_HEADER_LENGTH 8 // TCP /* These defs were taken from the very detailed page at http://www.networksorcery.com/enp/protocol/tcp.htm#Control%20Bits */ #define IP_TCP_PROTOCOL 6 #define TCP_HEADER_START 34 #define TCP_SOURCE_PORT_OFFSET 0 #define TCP_SOURCE_PORT_LENGTH 2 #define TCP_DEST_PORT_OFFSET 2 #define TCP_DEST_PORT_LENGTH 2 #define TCP_SEQ_OFFSET 4 #define TCP_SEQ_OFFSET_LENGTH 4 #define TCP_ACK_OFFSET 8 #define TCP_ACK_OFFSET_LENGTH 4 #define TCP_DATA_OFFSET_OFFSET 12 #define TCP_DATA_OFFSET_LENGTH 1 #define TCP_CONTROL_BITS_OFFSET 13 //NOTE: control bits are the last 6 bits! DON'T OVERWRITE ECN BITS! #define TCP_CHECKSUM_OFFSET 16 #define TCP_CHECKSUM_LENGTH 2 #define TCP_OPTIONS_OFFSET 20 //TCP control bits #define TCP_CONTROL_BITS_MASK 0b00111111; //right shift amounts (ex 1<> 16)); end //invert checksum = checksum ^ 0xFFFF; // write checksum bits packet[IP_HEADER_START+IP_CHECKSUM_START_OFFSET] = checksum >> 8; packet[IP_HEADER_START+IP_CHECKSUM_START_OFFSET+1] = (checksum - 2 ) & 0xFF; // get out of this routine end /* IPHeaderBuild swaps the src and dest IP address, which, other than checksum, is all we need to do to make our reply packet */ void IPHeaderBuild(unsigned char *packet) begin unsigned char i; //junk var for(i=0;i<4;i++) begin // stuff a byte of the source addr into the dest addr packet[IP_HEADER_START+IP_DEST_ADDR_OFFSET+i] = packet[IP_HEADER_START+IP_SOURCE_ADDR_OFFSET+i]; // overwrite source addr with our ip packet[IP_HEADER_START+IP_SOURCE_ADDR_OFFSET+i] = ipaddr[i]; // write TTL packet[IP_HEADER_START+IP_TTL_OFFSET] = 32; //maximum hops // write IP flags : 0b01000000 = don't frag packet[IP_HEADER_START+IP_FLAG_OFFSET] = 0b01000000; // write IP flag offset: 0x00000000 packet[IP_HEADER_START+IP_FLAG_OFFSET] = 0b00000000; end end /* PingResponse: Checks to see if incoming packet is a ping, if it is, sends a pong; otherwise, it sends nothing. */ unsigned char PingResponse(unsigned int length, unsigned char *packet) begin unsigned long checksum; unsigned char i; //junk var // check IP_PROTOCOL field value if(packet[IP_HEADER_START+IP_PROTOCOL_OFFSET] != IP_ICMP_PROTOCOL) begin //PORTC = 0x00; //delay_ms(10000); // it's not ICMP, return 0 return(0); end else //it's ICMP! begin // step 1: check that it is an echo request. We don't service any other ICMP types at this time. if(packet[ICMP_HEADER_START+ICMP_HEADER_OFFSET]!= ICMP_ECHO_REQUEST) begin //it's not an echo request. dump. //PORTC = 0x77; //delay_ms(500); return(0); end else begin // it's an echo request. // PORTC = ~((~PORTC)+1); // stuff ICMP_ECHO_REPLY into original ICMP packet. packet[ICMP_HEADER_START+ICMP_HEADER_OFFSET]= ICMP_ECHO_REPLY; /* // clear checksum: packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET] = 0x00; packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET+1] = 0x00; //16 bit one's complement: for(i=0;i<(length-ICMP_HEADER_START);i=i+2) begin //algorithm taken from IPChecksum checksum = checksum + ( ( ( ( (int) (packet[((int) IP_HEADER_START)+((int) i)]) ) <<8) | packet[((int) IP_HEADER_START)+((int) i+1)]) ); checksum = 0xFFFF & (checksum + (checksum >> 16)); end //invert checksum = checksum ^ 0xFFFF; //write checksum packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET] = checksum >> 8; packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET+1] = (checksum) & 0xFF; */ // according to ethereal, switching from 8 to 1 is the same as adding 8 to the high byte of the checksum checksum = ((int)((int) packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET]) + 8); packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET] = packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET] + 8; packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET+1] = packet[ICMP_HEADER_START+ICMP_CHECKSUM_OFFSET+1] + (checksum >> 8 ); //Swap IP headers and build flags IPHeaderBuild(packet); //build IP checksum IPChecksum(packet); //swap ethernet stuffs for(i=0;i> 16)); //add IP source, IP dest, and then TCP header + data fields. //PORTC = ~(TCP_HEADER_START -8); //delay_ms(5000); //for(i=0;i> 8); // delay_ms(5000); // PORTC = ~(temp & 0xff); // delay_ms(5000); // packetspew[i>>1] = temp; checksum = checksum + temp; //add bit 17 (carry bit) to the sum and 16-bit mask to get rid of the carry. // NOTE: 0x01FF WILL FAIL THIS ALGORITHM! Thankfully, no combination of 16 bit adds will result in 0x01FF so we can get away with it. (0XFF+0XFF = 0x01FE) checksum = 0xFFFF & (checksum + (checksum >> 16)); end //invert checksum = checksum ^ 0xFFFF; // write checksum bits // packetspew[14] = checksum; packet[TCP_HEADER_START+TCP_CHECKSUM_OFFSET] = checksum >> 8; packet[TCP_HEADER_START+TCP_CHECKSUM_OFFSET+1] = (checksum - 4) & 0xFF; // get out of this routine end /* This one is painful, and I had to crib a bit off of http://www.cs.uml.edu/~kim/05_TCP.ppt and a bit from Guido Socher's work (www.tuxgraphics.org) to understand exactly what the hell is going on. This creates the packet syn/ack. Oh lordy. Side note: Why the hell is Sequence interchanged with SYNchronize, but ACK is still Acknowledge? Also, how many licks DOES it take to get to the centre of a tootsie roll pop? */ void SynAckBuild(unsigned int length,unsigned char *packet) begin unsigned char i; //junk var unsigned char temp_seq; //temporary sequence number unsigned int temp_ack; //temporary ACK number // phase one: do the usual ethernet swap. for(i=0;i>8; end packet[TCP_HEADER_START+TCP_CONTROL_BITS_OFFSET] = 1 << TCP_CONTROL_BITS_ACK | 1 << TCP_CONTROL_BITS_SYN; //now write seq with syn values. Note that even I have maintained the seq - syn differentiation. packet[TCP_HEADER_START+TCP_SEQ_OFFSET+3] = TCP_SYN_VAL_0; packet[TCP_HEADER_START+TCP_SEQ_OFFSET+1] = TCP_SYN_VAL_2; packet[TCP_HEADER_START+TCP_SEQ_OFFSET+0] = TCP_SYN_VAL_3; // the step is included here so that sequence numbers are not reused from packet to packet. This is important for single-packet TCP like we are using; // proper implementation of this using multiple packets would not use this hacky solution. // There was only one catch, and that was Catch-22. packet[TCP_HEADER_START+TCP_SEQ_OFFSET+2] = TCP_SYN_VAL_1 + step; step= step + 1; /* By setting the Maximum Segment Size (MSS) field in our SYNACK, we can tell the TCP client to not go over the maximum segment size. 1500 is the largest allowable Maximum Transmission Unit; MSS represents the maximum data size, not the maximum segment size! If we wanted to cut it close, we could subtract the total headers from 1500, but that leaves something to be desired since TCP headers can be up to 64 bytes in length. We'll use 1500 - ethernet header - IP header - Max TCP header = 1500 - 14 - 20 - 64 = 1500 - 98 = 1402. */ // set Options to MSS (http://www.networksorcery.com/enp/protocol/tcp/option002.htm) // Kind = 2 packet[TCP_HEADER_START+TCP_OPTIONS_OFFSET] = 2; //Length = 4 bytes total (kind is a byte, length is a byte, MSS is two bytes) packet[TCP_HEADER_START+TCP_OPTIONS_OFFSET+1] = 4; // MSS = 1402 = 0x057A packet[TCP_HEADER_START+TCP_OPTIONS_OFFSET+2] = 0x05; packet[TCP_HEADER_START+TCP_OPTIONS_OFFSET+3] = 0x7A; // Set TCP header length to 6. This is the number of 4 byte words in the packet. Unfortunately, this is the high four bits // of a byte that also contains a reserved triplet and a bit that is part of Explicit Congestion Notification. // Nonce sums are completely useless for our purpose, so we zero the low bits. packet[TCP_HEADER_START+TCP_DATA_OFFSET_OFFSET] = 0x70; // phase three: calculate TCP checksum TCPChecksum(packet); //Swap IP headers and build flags IPHeaderBuild(packet); //Change IP header length. This has no data, so it's just the IP header length (20) + options for this particular packet (4). //packet[IP_HEADER_START+IP_HEADER_LENGTH_OFFSET] = 0x00; //packet[IP_HEADER_START+IP_HEADER_LENGTH_OFFSET+1] = 24; //Change IP total length. This has no data, so it's just the IP header length (20) + TCP header length (20) + TCP_DATA_OFFSET. packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET] = 0x00; packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET+1] = 48; //44 + ((packet[TCP_HEADER_START+TCP_DATA_OFFSET_OFFSET]>>4)*4); //build IP checksum IPChecksum(packet); TransmitPacket(length, packet); end /* Ack response with no data change. Part two of the three-way handshake. This reuses the incoming packet. */ void NoDataAckBuild(unsigned char *packet) begin unsigned char i; //junk var unsigned char temp_syn; //temporary sequence number unsigned int temp_ack; //temporary ACK number unsigned int tcpdatalength; //tcp data length // phase one: do the usual ethernet swap. for(i=0;i>4))*4); if(tcpdatalength<=0) //no data, ack w/o data begin temp_ack = 1; for(i=0;i<4;i++) begin // byte + 1 stored into 2 byte number. HMM I WONDER WHAT WE'RE DOING WITH THAT EXTRA BIT temp_ack = packet[TCP_HEADER_START+TCP_SEQ_OFFSET+3-i] + temp_ack; // get current ACK number temp_syn = packet[TCP_HEADER_START+TCP_ACK_OFFSET+3-i]; // write ACK byte packet[TCP_HEADER_START+TCP_ACK_OFFSET+3-i] = temp_ack & 0xff; // write SYN byte packet[TCP_HEADER_START+TCP_SEQ_OFFSET+3-i] = temp_syn & 0xff; // carry the one! HEY THAT'S WHERE THE BIT COMES IN! HO HO! temp_ack = temp_ack >>8; end end // else TCP datalength is not 0, so there's data. As such, we have to add that value as the increment instead of the usual +1. else begin temp_ack = tcpdatalength; for(i=0;i<4;i++) begin // byte + 1 stored into 2 byte number. HMM I WONDER WHAT WE'RE DOING WITH THAT EXTRA BIT temp_ack = packet[TCP_HEADER_START+TCP_SEQ_OFFSET+3-i] + temp_ack; // get current ACK number temp_syn = packet[TCP_HEADER_START+TCP_ACK_OFFSET+3-i]; // write ACK byte packet[TCP_HEADER_START+TCP_ACK_OFFSET+3-i] = temp_ack & 0xff; // write SYN byte packet[TCP_HEADER_START+TCP_SEQ_OFFSET+3-i] = temp_syn & 0xff; // carry the one! HEY THAT'S WHERE THE BIT COMES IN! HO HO! temp_ack = temp_ack >>8; end end // write TCP header length = 20 bytes = 5 4 byte words packet[TCP_HEADER_START+TCP_DATA_OFFSET_OFFSET] = 0x50; //Change IP length. This has no data, so it's just the IP header length (20) + the TCP Header length (20) + options for this particular packet (0) + tcp datalength. i = 40 + tcpdatalength; packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET] = i >>8; packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET+1] = i & 0xff; // phase three: calculate TCP checksum. DOn't worry about IP address swapping since the order of addition does not matter. TCPChecksum(packet); //Swap IP headers and build flags IPHeaderBuild(packet); //build IP checksum IPChecksum(packet); //send the packet: length = ethernet length (14) + IP header length (20) + TCP header length (20) + TCP options (4) + data (tcplength) = 14+20+20+tcplength = 54 + tcplength i = 54 + tcpdatalength; TransmitPacket(i, packet); end /* Ack with data: call after NoDataAckBuild. Single packet TCP = we close the port. Sweet. */ void DataAckBuild(unsigned char *packet, unsigned int datalength) begin unsigned int i; //junk var // write TCP flags: ACK, FIN (single packet TCP means as soon as we handshake and fill our buffer packet, we close the connection), and PSH (pushes the packet as soon as network is available) packet[TCP_HEADER_START+TCP_CONTROL_BITS_OFFSET] = 1 << TCP_CONTROL_BITS_ACK | 1<< TCP_CONTROL_BITS_PSH | 1<>8; packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET+1] = i & 0xff; // write TTL packet[IP_HEADER_START+IP_TTL_OFFSET] = 32; //maximum hops // write IP flags : 0b01000000 = don't frag packet[IP_HEADER_START+IP_FLAG_OFFSET] = 0b01000000; // write IP flag offset: 0x00000000 packet[IP_HEADER_START+IP_FLAG_OFFSET] = 0b00000000; // IP checksum calc IPChecksum(packet); //TCP checksum calc TCPChecksum(packet); //send the packet TransmitPacket(i,packet); end void UDPChecksum(unsigned char *packet,unsigned int datalength) begin unsigned long checksum,temp; unsigned char i; // junk var //initialize checksum = 0; // start doing 16 bit adds. BITTTTCHHHIIINNNNNNN! checksum = UDP_PROTOCOL; // TCP protocol is 0x06 // get TCP length (in bytes) : it's the IP_LENGTH field minus the IP header (20 bytes) //PORTC = ~tcplength; //delay_ms(1000); //zero tcplength // add TCP length field checksum = checksum + (UDP_HEADER_LENGTH+datalength); //add bit 17 (carry bit) to the sum and 16-bit mask to get rid of the carry. // NOTE: 0x01FF WILL FAIL THIS ALGORITHM! Thankfully, no combination of 16 bit adds will result in 0x01FF so we can get away with it. (0XFF+0XFF = 0x01FE) checksum = 0xFFFF & (checksum + (checksum >> 16)); //add IP source, IP dest, and then TCP header + data fields. //PORTC = ~(TCP_HEADER_START -8); //delay_ms(5000); for(i=0;i<(UDP_HEADER_LENGTH+datalength)+8;i=i+2) begin //take words 16 bits at a time and add them. Start from IP_HEADER_START and increment 2 at a time until the IP header is exhausted temp = ( ( ( ( (int) (packet[((int) UDP_HEADER_START)+((int) i)-8]) ) << 8 ) | packet[((int) UDP_HEADER_START)+((int) i)-7]) ); // PORTC = ~(temp >> 8); // delay_ms(5000); // PORTC = ~(temp & 0xff); // delay_ms(5000); // packetspew[i>>1] = temp; checksum = checksum + temp; //add bit 17 (carry bit) to the sum and 16-bit mask to get rid of the carry. // NOTE: 0x01FF WILL FAIL THIS ALGORITHM! Thankfully, no combination of 16 bit adds will result in 0x01FF so we can get away with it. (0XFF+0XFF = 0x01FE) checksum = 0xFFFF & (checksum + (checksum >> 16)); end //invert checksum = checksum ^ 0xFFFF; // write checksum bits // packetspew[14] = checksum; packet[UDP_HEADER_START+UDP_CHECKSUM_OFFSET] = checksum >> 8; packet[UDP_HEADER_START+UDP_CHECKSUM_OFFSET+1] = (checksum - 2) & 0xFF; // get out of this routine end void UDPReply(unsigned char *packet, unsigned char *setpoint, unsigned char setpointindex, unsigned char *temp, unsigned char tempoutputindex, unsigned char error) begin //start with UDP header build // datalength is equal to length of string, which looks like "temp=xxx set=xxx" // note that xxx are variable length! So, 10 + lengths = total datalength unsigned char datalength; unsigned char j; unsigned int tempporth,tempportl; // write IP length: UDP_HEADER_START+UDP_HEADER_LENGTH+datalength- IP_START_OFFSET datalength = 10+ setpointindex+tempoutputindex; packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET] =0x00; packet[IP_HEADER_START+IP_TOTAL_LENGTH_OFFSET+1] = UDP_HEADER_START+UDP_HEADER_LENGTH + datalength-14; //swap source and destination port tempporth = packet[UDP_HEADER_START+UDP_DEST_PORT_OFFSET]; tempportl = packet[UDP_HEADER_START+UDP_DEST_PORT_OFFSET+1]; packet[UDP_HEADER_START+UDP_DEST_PORT_OFFSET] = packet[UDP_HEADER_START]; packet[UDP_HEADER_START+UDP_DEST_PORT_OFFSET+1] = packet[UDP_HEADER_START+1]; packet[UDP_HEADER_START] = tempporth; packet[UDP_HEADER_START+1] = tempportl; //UDP length is equal to header length + datalength packet[UDP_HEADER_START+UDP_LENGTH_OFFSET] = (UDP_HEADER_LENGTH+datalength) >> 8; packet[UDP_HEADER_START+UDP_LENGTH_OFFSET+1] = (UDP_HEADER_LENGTH+datalength) & 0xff; //no checksum for UDP! YAY! packet[UDP_HEADER_START + UDP_CHECKSUM_OFFSET] = 0; packet[UDP_HEADER_START + UDP_CHECKSUM_OFFSET+1] = 0; //stuff data if(!error) begin packet[UDP_HEADER_START+UDP_HEADER_LENGTH] = 'T'; packet[UDP_HEADER_START+UDP_HEADER_LENGTH+1] = 'e'; packet[UDP_HEADER_START+UDP_HEADER_LENGTH+2] = 'm'; packet[UDP_HEADER_START+UDP_HEADER_LENGTH+3] = 'p'; packet[UDP_HEADER_START+UDP_HEADER_LENGTH+4] = '='; for(i=0; i