Subversion Repositories HomeAutomation

Rev

Rev 206 | Blame | Last modification | View Log | SVN | RSS feed

  1. /*********************************************
  2.  * vim:sw=8:ts=8:si:et
  3.  * To use the above modeline in vim you must have "set modeline" in your .vimrc
  4.  *
  5.  * Author: Guido Socher
  6.  * Copyright: GPL V2
  7.  * See http://www.gnu.org/licenses/gpl.html
  8.  *
  9.  * IP, Arp, UDP and TCP functions.
  10.  *
  11.  * The TCP implementation uses some size optimisations which are valid
  12.  * only if all data can be sent in one single packet. This is however
  13.  * not a big limitation for a microcontroller as you will anyhow use
  14.  * small web-pages. The TCP stack is therefore a SDP-TCP stack (single data packet TCP).
  15.  *
  16.  * Chip type           : ATMEGA88 with ENC28J60
  17.  *********************************************/
  18. #include <avr/io.h>
  19. #include <avr/pgmspace.h>
  20. #include "avr_compat.h"
  21. #include "net.h"
  22. #include "enc28j60.h"
  23.  
  24. static uint8_t wwwport=80;
  25. static uint8_t macaddr[6];
  26. static uint8_t ipaddr[4];
  27. static int16_t info_hdr_len=0;
  28. static int16_t info_data_len=0;
  29. static uint8_t seqnum=0xa; // my initial tcp sequence number
  30.  
  31. // The Ip checksum is calculated over the ip header only starting
  32. // with the header length field and a total length of 20 bytes
  33. // unitl ip.dst
  34. // You must set the IP checksum field to zero before you start
  35. // the calculation.
  36. // len for ip is 20.
  37. //
  38. // For UDP/TCP we do not make up the required pseudo header. Instead we
  39. // use the ip.src and ip.dst fields of the real packet:
  40. // The udp checksum calculation starts with the ip.src field
  41. // Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len
  42. // In other words the len here is 8 + length over which you actually
  43. // want to calculate the checksum.
  44. // You must set the checksum field to zero before you start
  45. // the calculation.
  46. // len for udp is: 8 + 8 + data length
  47. // len for tcp is: 4+4 + 20 + option len + data length
  48. //
  49. // For more information on how this algorithm works see:
  50. // http://www.netfor2.com/checksum.html
  51. // http://www.msc.uky.edu/ken/cs471/notes/chap3.htm
  52. // The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html
  53. uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type){
  54.         // type 0=ip
  55.         //      1=udp
  56.         //      2=tcp
  57.         uint32_t sum = 0;
  58.  
  59.         //if(type==0){
  60.         //        // do not add anything
  61.         //}
  62.         if(type==1){
  63.                 sum+=IP_PROTO_UDP_V; // protocol udp
  64.                 // the length here is the length of udp (data+header len)
  65.                 // =length given to this function - (IP.scr+IP.dst length)
  66.                 sum+=len-8; // = real tcp len
  67.         }
  68.         if(type==2){
  69.                 sum+=IP_PROTO_TCP_V;
  70.                 // the length here is the length of tcp (data+header len)
  71.                 // =length given to this function - (IP.scr+IP.dst length)
  72.                 sum+=len-8; // = real tcp len
  73.         }
  74.         // build the sum of 16bit words
  75.         while(len >1){
  76.                 sum += 0xFFFF & (*buf<<8|*(buf+1));
  77.                 buf+=2;
  78.                 len-=2;
  79.         }
  80.         // if there is a byte left then add it (padded with zero)
  81.         if (len){
  82.                 sum += (0xFF & *buf)<<8;
  83.         }
  84.         // now calculate the sum over the bytes in the sum
  85.         // until the result is only 16bit long
  86.         while (sum>>16){
  87.                 sum = (sum & 0xFFFF)+(sum >> 16);
  88.         }
  89.         // build 1's complement:
  90.         return( (uint16_t) sum ^ 0xFFFF);
  91. }
  92.  
  93. // you must call this function once before you use any of the other functions:
  94. void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint8_t wwwp){
  95.         uint8_t i=0;
  96.         wwwport=wwwp;
  97.         while(i<4){
  98.                 ipaddr[i]=myip[i];
  99.                 i++;
  100.         }
  101.         i=0;
  102.         while(i<6){
  103.                 macaddr[i]=mymac[i];
  104.                 i++;
  105.         }
  106. }
  107.  
  108. uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len){
  109.         uint8_t i=0;
  110.         //  
  111.         if (len<41){
  112.                 return(0);
  113.         }
  114.         if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V ||
  115.            buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V){
  116.                 return(0);
  117.         }
  118.         while(i<4){
  119.                 if(buf[ETH_ARP_DST_IP_P+i] != ipaddr[i]){
  120.                         return(0);
  121.                 }
  122.                 i++;
  123.         }
  124.         return(1);
  125. }
  126.  
  127. uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len){
  128.         uint8_t i=0;
  129.         //eth+ip+udp header is 42
  130.         if (len<42){
  131.                 return(0);
  132.         }
  133.         if(buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V ||
  134.            buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V){
  135.                 return(0);
  136.         }
  137.         if (buf[IP_HEADER_LEN_VER_P]!=0x45){
  138.                 // must be IP V4 and 20 byte header
  139.                 return(0);
  140.         }
  141.         while(i<4){
  142.                 if(buf[IP_DST_P+i]!=ipaddr[i]){
  143.                         return(0);
  144.                 }
  145.                 i++;
  146.         }
  147.         return(1);
  148. }
  149. // make a return eth header from a received eth packet
  150. void make_eth(uint8_t *buf)
  151. {
  152.         uint8_t i=0;
  153.         //
  154.         //copy the destination mac from the source and fill my mac into src
  155.         while(i<6){
  156.                 buf[ETH_DST_MAC +i]=buf[ETH_SRC_MAC +i];
  157.                 buf[ETH_SRC_MAC +i]=macaddr[i];
  158.                 i++;
  159.         }
  160. }
  161. void fill_ip_hdr_checksum(uint8_t *buf)
  162. {      
  163.         //bits 0-3 = version (ipv4 => 4), bits 4-7 = header length (minimum 5 32bit fields => 5)
  164.         buf[IP_HEADER_LEN_VER_P]=0x45;
  165.         uint16_t ck;
  166.         // clear the 2 byte checksum
  167.         buf[IP_CHECKSUM_P]=0;
  168.         buf[IP_CHECKSUM_P+1]=0;
  169.         buf[IP_FLAGS_P]=0x40; // don't fragment
  170.         buf[IP_FLAGS_P+1]=0;  // fragement offset
  171.         buf[IP_TTL_P]=64; // ttl
  172.         // calculate the checksum:
  173.         ck=checksum(&buf[IP_P], IP_HEADER_LEN,0);
  174.         buf[IP_CHECKSUM_P]=ck>>8;
  175.         buf[IP_CHECKSUM_P+1]=ck& 0xff;
  176. }
  177.  
  178. // make a return ip header from a received ip packet
  179. void make_ip(uint8_t *buf)
  180. {
  181.         uint8_t i=0;
  182.         while(i<4){
  183.                 buf[IP_DST_P+i]=buf[IP_SRC_P+i];
  184.                 buf[IP_SRC_P+i]=ipaddr[i];
  185.                 i++;
  186.         }
  187.         fill_ip_hdr_checksum(buf);
  188. }
  189.  
  190. // make a return tcp header from a received tcp packet
  191. // rel_ack_num is how much we must step the seq number received from the
  192. // other side. We do not send more than 255 bytes of text (=data) in the tcp packet.
  193. // If mss=1 then mss is included in the options list
  194. //
  195. // After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4
  196. // If cp_seq=0 then an initial sequence number is used (should be use in synack)
  197. // otherwise it is copied from the packet we received
  198. void make_tcphead(uint8_t *buf,uint16_t rel_ack_num,uint8_t mss,uint8_t cp_seq)
  199. {
  200.         uint8_t i=0;
  201.         uint8_t tseq;
  202.         while(i<2){
  203.                 buf[TCP_DST_PORT_H_P+i]=buf[TCP_SRC_PORT_H_P+i];
  204.                 buf[TCP_SRC_PORT_H_P+i]=0; // clear source port
  205.                 i++;
  206.         }
  207.         // set source port  (http):
  208.         buf[TCP_SRC_PORT_L_P]=wwwport;
  209.         i=4;
  210.         // sequence numbers:
  211.         // add the rel ack num to SEQACK
  212.         while(i>0){
  213.                 rel_ack_num=buf[TCP_SEQ_H_P+i-1]+rel_ack_num;
  214.                 tseq=buf[TCP_SEQACK_H_P+i-1];
  215.                 buf[TCP_SEQACK_H_P+i-1]=0xff&rel_ack_num;
  216.                 if (cp_seq){
  217.                         // copy the acknum sent to us into the sequence number
  218.                         buf[TCP_SEQ_H_P+i-1]=tseq;
  219.                 }else{
  220.                         buf[TCP_SEQ_H_P+i-1]= 0; // some preset vallue
  221.                 }
  222.                 rel_ack_num=rel_ack_num>>8;
  223.                 i--;
  224.         }
  225.         if (cp_seq==0){
  226.                 // put inital seq number
  227.                 buf[TCP_SEQ_H_P+0]= 0;
  228.                 buf[TCP_SEQ_H_P+1]= 0;
  229.                 // we step only the second byte, this allows us to send packts
  230.                 // with 255 bytes or 512 (if we step the initial seqnum by 2)
  231.                 buf[TCP_SEQ_H_P+2]= seqnum;
  232.                 buf[TCP_SEQ_H_P+3]= 0;
  233.                 // step the inititial seq num by something we will not use
  234.                 // during this tcp session:
  235.                 seqnum+=2;
  236.         }
  237.         // zero the checksum
  238.         buf[TCP_CHECKSUM_H_P]=0;
  239.         buf[TCP_CHECKSUM_L_P]=0;
  240.  
  241.         // The tcp header length is only a 4 bit field (the upper 4 bits).
  242.         // It is calculated in units of 4 bytes.
  243.         // E.g 24 bytes: 24/4=6 => 0x60=header len field
  244.         //buf[TCP_HEADER_LEN_P]=(((TCP_HEADER_LEN_PLAIN+4)/4)) <<4; // 0x60
  245.         if (mss){
  246.                 // the only option we set is MSS to 1408:
  247.                 // 1408 in hex is 0x580
  248.                 buf[TCP_OPTIONS_P]=2;
  249.                 buf[TCP_OPTIONS_P+1]=4;
  250.                 buf[TCP_OPTIONS_P+2]=0x05;
  251.                 buf[TCP_OPTIONS_P+3]=0x80;
  252.                 // 24 bytes:
  253.                 buf[TCP_HEADER_LEN_P]=0x60;
  254.         }else{
  255.                 // no options:
  256.                 // 20 bytes:
  257.                 buf[TCP_HEADER_LEN_P]=0x50;
  258.         }
  259. }
  260.  
  261. void make_arp_answer_from_request(uint8_t *buf)
  262. {
  263.         uint8_t i=0;
  264.         //
  265.         make_eth(buf);
  266.         buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V;
  267.         buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V;
  268.         // fill the mac addresses:
  269.         while(i<6){
  270.                 buf[ETH_ARP_DST_MAC_P+i]=buf[ETH_ARP_SRC_MAC_P+i];
  271.                 buf[ETH_ARP_SRC_MAC_P+i]=macaddr[i];
  272.                 i++;
  273.         }
  274.         i=0;
  275.         while(i<4){
  276.                 buf[ETH_ARP_DST_IP_P+i]=buf[ETH_ARP_SRC_IP_P+i];
  277.                 buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i];
  278.                 i++;
  279.         }
  280.         // eth+arp is 42 bytes:
  281.         enc28j60PacketSend(42,buf);
  282. }
  283.  
  284. void make_echo_reply_from_request(uint8_t *buf,uint16_t len)
  285. {
  286.         make_eth(buf);
  287.         make_ip(buf);
  288.         buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V;
  289.         // we changed only the icmp.type field from request(=8) to reply(=0).
  290.         // we can therefore easily correct the checksum:
  291.         if (buf[ICMP_CHECKSUM_P] > (0xff-0x08)){
  292.                 buf[ICMP_CHECKSUM_P+1]++;
  293.         }
  294.         buf[ICMP_CHECKSUM_P]+=0x08;
  295.         //
  296.         enc28j60PacketSend(len,buf);
  297. }
  298.  
  299. // you can send a max of 220 bytes of data
  300. void make_udp_reply_from_request(uint8_t *buf,char *data,uint8_t datalen,uint16_t port)
  301. {
  302.         uint8_t i=0;
  303.         uint16_t ck;
  304.         make_eth(buf);
  305.         if (datalen>220){
  306.                 datalen=220;
  307.         }
  308.         // total length field in the IP header must be set:
  309.         buf[IP_TOTLEN_H_P]=0;
  310.         buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen;
  311.         make_ip(buf);
  312.         buf[UDP_DST_PORT_H_P]=port>>8;
  313.         buf[UDP_DST_PORT_L_P]=port & 0xff;
  314.         // source port does not matter and is what the sender used.
  315.         // calculte the udp length:
  316.         buf[UDP_LEN_H_P]=0;
  317.         buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen;
  318.         // zero the checksum
  319.         buf[UDP_CHECKSUM_H_P]=0;
  320.         buf[UDP_CHECKSUM_L_P]=0;
  321.         // copy the data:
  322.         while(i<datalen){
  323.                 buf[UDP_DATA_P+i]=data[i];
  324.                 i++;
  325.         }
  326.         ck=checksum(&buf[IP_SRC_P], 16 + datalen,1);
  327.         buf[UDP_CHECKSUM_H_P]=ck>>8;
  328.         buf[UDP_CHECKSUM_L_P]=ck& 0xff;
  329.         enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf);
  330. }
  331.  
  332. void send_udp(uint8_t *buf,char *data,uint8_t datalen,uint16_t port, uint8_t *remotemac, uint8_t *remoteip)
  333. {
  334.  
  335.         uint8_t i=0;
  336.         uint16_t ck;
  337.         //make_eth(buf);
  338.         while(i<6){
  339.             buf[ETH_DST_MAC +i]=remotemac[i];
  340.             buf[ETH_SRC_MAC +i]=macaddr[i];
  341.             i++;
  342.         }
  343.         i=0;
  344.        
  345.         buf[ETH_TYPE_H_P] = 0x08;
  346.         buf[ETH_TYPE_L_P] = 0x00;
  347.  
  348.         if (datalen>220){
  349.                 datalen=220;
  350.         }
  351.         //set protocol
  352.         buf[IP_PROTO_P]=IP_PROTO_UDP_V;
  353.         // total length field in the IP header must be set:
  354.         buf[IP_TOTLEN_H_P]=0;
  355.         buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen;
  356.         while(i<4){
  357.             buf[IP_DST_P+i]=remoteip[i];
  358.             buf[IP_SRC_P+i]=ipaddr[i];
  359.             i++;
  360.         }
  361.         i=0;
  362.         fill_ip_hdr_checksum(buf);
  363.         //make_ip(buf);
  364.         buf[UDP_DST_PORT_H_P]=port>>8;
  365.         buf[UDP_DST_PORT_L_P]=port & 0xff;
  366.         // source port does not matter and is what the sender used.
  367.         // calculte the udp length:
  368.         buf[UDP_LEN_H_P]=0;
  369.         buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen;
  370.         // zero the checksum
  371.         buf[UDP_CHECKSUM_H_P]=0;
  372.         buf[UDP_CHECKSUM_L_P]=0;
  373.         // copy the data:
  374.         while(i<datalen){
  375.                 buf[UDP_DATA_P+i]=data[i];
  376.                 i++;
  377.         }
  378.         ck=checksum(&buf[IP_SRC_P], 16 + datalen,1);
  379.         buf[UDP_CHECKSUM_H_P]=ck>>8;
  380.         buf[UDP_CHECKSUM_L_P]=ck& 0xff;
  381.         enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf);
  382. }
  383.  
  384. void make_tcp_synack_from_syn(uint8_t *buf)
  385. {
  386.         uint16_t ck;
  387.         make_eth(buf);
  388.         // total length field in the IP header must be set:
  389.         // 20 bytes IP + 24 bytes (20tcp+4tcp options)
  390.         buf[IP_TOTLEN_H_P]=0;
  391.         buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4;
  392.         make_ip(buf);
  393.         buf[TCP_FLAGS_P]=TCP_FLAGS_SYNACK_V;
  394.         make_tcphead(buf,1,1,0);
  395.         // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + 4 (one option: mss)
  396.         ck=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+4,2);
  397.         buf[TCP_CHECKSUM_H_P]=ck>>8;
  398.         buf[TCP_CHECKSUM_L_P]=ck& 0xff;
  399.         // add 4 for option mss:
  400.         enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN,buf);
  401. }
  402.  
  403. // get a pointer to the start of tcp data in buf
  404. // Returns 0 if there is no data
  405. // You must call init_len_info once before calling this function
  406. uint16_t get_tcp_data_pointer(void)
  407. {
  408.         if (info_data_len){
  409.                 return((uint16_t)TCP_SRC_PORT_H_P+info_hdr_len);
  410.         }else{
  411.                 return(0);
  412.         }
  413. }
  414.  
  415. // do some basic length calculations and store the result in static varibales
  416. void init_len_info(uint8_t *buf)
  417. {
  418.         info_data_len=(buf[IP_TOTLEN_H_P]<<8)|(buf[IP_TOTLEN_L_P]&0xff);
  419.         info_data_len-=IP_HEADER_LEN;
  420.         info_hdr_len=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes;
  421.         info_data_len-=info_hdr_len;
  422.         if (info_data_len<=0){
  423.                 info_data_len=0;
  424.         }
  425. }
  426.  
  427. // fill in tcp data at position pos. pos=0 means start of
  428. // tcp data. Returns the position at which the string after
  429. // this string could be filled.
  430. uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s)
  431. {
  432.         char c;
  433.         // fill in tcp data at position pos
  434.         //
  435.         // with no options the data starts after the checksum + 2 more bytes (urgent ptr)
  436.         while ((c = pgm_read_byte(progmem_s++))) {
  437.                 buf[TCP_CHECKSUM_L_P+3+pos]=c;
  438.                 pos++;
  439.         }
  440.         return(pos);
  441. }
  442.  
  443. // fill in tcp data at position pos. pos=0 means start of
  444. // tcp data. Returns the position at which the string after
  445. // this string could be filled.
  446. uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s)
  447. {
  448.         // fill in tcp data at position pos
  449.         //
  450.         // with no options the data starts after the checksum + 2 more bytes (urgent ptr)
  451.         while (*s) {
  452.                 buf[TCP_CHECKSUM_L_P+3+pos]=*s;
  453.                 pos++;
  454.                 s++;
  455.         }
  456.         return(pos);
  457. }
  458.  
  459. // Make just an ack packet with no tcp data inside
  460. // This will modify the eth/ip/tcp header
  461. void make_tcp_ack_from_any(uint8_t *buf)
  462. {
  463.         uint16_t j;
  464.         make_eth(buf);
  465.         // fill the header:
  466.         buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V;
  467.         if (info_data_len==0){
  468.                 // if there is no data then we must still acknoledge one packet
  469.                 make_tcphead(buf,1,0,1); // no options
  470.         }else{
  471.                 make_tcphead(buf,info_data_len,0,1); // no options
  472.         }
  473.  
  474.         // total length field in the IP header must be set:
  475.         // 20 bytes IP + 20 bytes tcp (when no options)
  476.         j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN;
  477.         buf[IP_TOTLEN_H_P]=j>>8;
  478.         buf[IP_TOTLEN_L_P]=j& 0xff;
  479.         make_ip(buf);
  480.         // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
  481.         j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN,2);
  482.         buf[TCP_CHECKSUM_H_P]=j>>8;
  483.         buf[TCP_CHECKSUM_L_P]=j& 0xff;
  484.         enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN,buf);
  485. }
  486.  
  487. // you must have called init_len_info at some time before calling this function
  488. // dlen is the amount of tcp data (http data) we send in this packet
  489. // You can use this function only immediately after make_tcp_ack_from_any
  490. // This is because this function will NOT modify the eth/ip/tcp header except for
  491. // length and checksum
  492. void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen)
  493. {
  494.         uint16_t j;
  495.         // fill the header:
  496.         // This code requires that we send only one data packet
  497.         // because we keep no state information. We must therefore set
  498.         // the fin here:
  499.         buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V;
  500.  
  501.         // total length field in the IP header must be set:
  502.         // 20 bytes IP + 20 bytes tcp (when no options) + len of data
  503.         j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen;
  504.         buf[IP_TOTLEN_H_P]=j>>8;
  505.         buf[IP_TOTLEN_L_P]=j& 0xff;
  506.         fill_ip_hdr_checksum(buf);
  507.         // zero the checksum
  508.         buf[TCP_CHECKSUM_H_P]=0;
  509.         buf[TCP_CHECKSUM_L_P]=0;
  510.         // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
  511.         j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2);
  512.         buf[TCP_CHECKSUM_H_P]=j>>8;
  513.         buf[TCP_CHECKSUM_L_P]=j& 0xff;
  514.         enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf);
  515. }
  516.  
  517. /* end of ip_arp_udp.c */
  518.