PageRenderTime 106ms CodeModel.GetById 40ms app.highlight 49ms RepoModel.GetById 12ms app.codeStats 0ms

/ip_arp_udp_tcp.c

https://bitbucket.org/kshdeo/avr-ethernet
C | 497 lines | 286 code | 31 blank | 180 comment | 26 complexity | 7132685c301bd74d9c09942bc726ce01 MD5 | raw file
  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
 24static uint8_t wwwport=80;
 25static uint8_t macaddr[6];
 26static uint8_t ipaddr[4];
 27static int16_t info_hdr_len=0;
 28static int16_t info_data_len=0;
 29static 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
 53uint16_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 & (((uint32_t)*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 += ((uint32_t)(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:
 94void 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
108uint8_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
127uint8_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		}
148        return(1);
149}
150// make a return eth header from a received eth packet
151void make_eth(uint8_t *buf)
152{
153        uint8_t i=0;
154        //
155        //copy the destination mac from the source and fill my mac into src
156        while(i<6){
157                buf[ETH_DST_MAC +i]=buf[ETH_SRC_MAC +i];
158                buf[ETH_SRC_MAC +i]=macaddr[i];
159                i++;
160        }
161}
162void fill_ip_hdr_checksum(uint8_t *buf)
163{
164        uint16_t ck;
165        // clear the 2 byte checksum
166        buf[IP_CHECKSUM_P]=0;
167        buf[IP_CHECKSUM_P+1]=0;
168        buf[IP_FLAGS_P]=0x40; // don't fragment
169        buf[IP_FLAGS_P+1]=0;  // fragement offset
170        buf[IP_TTL_P]=64; // ttl
171        // calculate the checksum:
172        ck=checksum(&buf[IP_P], IP_HEADER_LEN,0);
173        buf[IP_CHECKSUM_P]=ck>>8;
174        buf[IP_CHECKSUM_P+1]=ck& 0xff;
175}
176
177// make a return ip header from a received ip packet
178void make_ip(uint8_t *buf)
179{
180        uint8_t i=0;
181        while(i<4){
182                buf[IP_DST_P+i]=buf[IP_SRC_P+i];
183                buf[IP_SRC_P+i]=ipaddr[i];
184                i++;
185        }
186		
187		i=0;
188		
189		while(i<4){
190			USART0_Transmit(buf[IP_DST_P+i]);
191			USART0_Transmit();
192			i++;
193		}
194        
195		fill_ip_hdr_checksum(buf);
196
197}
198
199// make a return tcp header from a received tcp packet
200// rel_ack_num is how much we must step the seq number received from the
201// other side. We do not send more than 255 bytes of text (=data) in the tcp packet.
202// If mss=1 then mss is included in the options list
203//
204// After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4
205// If cp_seq=0 then an initial sequence number is used (should be use in synack)
206// otherwise it is copied from the packet we received
207void make_tcphead(uint8_t *buf,uint16_t rel_ack_num,uint8_t mss,uint8_t cp_seq)
208{
209        uint8_t i=0;
210        uint8_t tseq;
211        while(i<2){
212                buf[TCP_DST_PORT_H_P+i]=buf[TCP_SRC_PORT_H_P+i];
213                buf[TCP_SRC_PORT_H_P+i]=0; // clear source port
214                i++;
215        }
216        // set source port  (http):
217        buf[TCP_SRC_PORT_L_P]=wwwport;
218        i=4;
219        // sequence numbers:
220        // add the rel ack num to SEQACK
221        while(i>0){
222                rel_ack_num=buf[TCP_SEQ_H_P+i-1]+rel_ack_num;
223                tseq=buf[TCP_SEQACK_H_P+i-1];
224                buf[TCP_SEQACK_H_P+i-1]=0xff&rel_ack_num;
225                if (cp_seq){
226                        // copy the acknum sent to us into the sequence number
227                        buf[TCP_SEQ_H_P+i-1]=tseq;
228                }else{
229                        buf[TCP_SEQ_H_P+i-1]= 0; // some preset vallue
230                }
231                rel_ack_num=rel_ack_num>>8;
232                i--;
233        }
234        if (cp_seq==0){
235                // put inital seq number
236                buf[TCP_SEQ_H_P+0]= 0;
237                buf[TCP_SEQ_H_P+1]= 0;
238                // we step only the second byte, this allows us to send packts 
239                // with 255 bytes or 512 (if we step the initial seqnum by 2)
240                buf[TCP_SEQ_H_P+2]= seqnum; 
241                buf[TCP_SEQ_H_P+3]= 0;
242                // step the inititial seq num by something we will not use
243                // during this tcp session:
244                seqnum+=2;
245        }
246        // zero the checksum
247        buf[TCP_CHECKSUM_H_P]=0;
248        buf[TCP_CHECKSUM_L_P]=0;
249
250        // The tcp header length is only a 4 bit field (the upper 4 bits).
251        // It is calculated in units of 4 bytes. 
252        // E.g 24 bytes: 24/4=6 => 0x60=header len field
253        //buf[TCP_HEADER_LEN_P]=(((TCP_HEADER_LEN_PLAIN+4)/4)) <<4; // 0x60
254        if (mss){
255                // the only option we set is MSS to 1408:
256                // 1408 in hex is 0x580
257                buf[TCP_OPTIONS_P]=2;
258                buf[TCP_OPTIONS_P+1]=4;
259                buf[TCP_OPTIONS_P+2]=0x05; 
260                buf[TCP_OPTIONS_P+3]=0x80;
261                // 24 bytes:
262                buf[TCP_HEADER_LEN_P]=0x60;
263        }else{
264                // no options:
265                // 20 bytes:
266                buf[TCP_HEADER_LEN_P]=0x50;
267        }
268}
269
270void make_arp_answer_from_request(uint8_t *buf)
271{
272        uint8_t i=0;
273        //
274        make_eth(buf);
275        buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V;
276        buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V;
277        // fill the mac addresses:
278        while(i<6){
279                buf[ETH_ARP_DST_MAC_P+i]=buf[ETH_ARP_SRC_MAC_P+i];
280                buf[ETH_ARP_SRC_MAC_P+i]=macaddr[i];
281                i++;
282        }
283        i=0;
284        while(i<4){
285                buf[ETH_ARP_DST_IP_P+i]=buf[ETH_ARP_SRC_IP_P+i];
286                buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i];
287                i++;
288        }
289        // eth+arp is 42 bytes:
290        enc28j60PacketSend(42,buf); 
291}
292
293void make_echo_reply_from_request(uint8_t *buf,uint16_t len)
294{
295        make_eth(buf);
296        make_ip(buf);
297        buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V;
298        // we changed only the icmp.type field from request(=8) to reply(=0).
299        // we can therefore easily correct the checksum:
300        if (buf[ICMP_CHECKSUM_P] > (0xff-0x08)){
301                buf[ICMP_CHECKSUM_P+1]++;
302        }
303        buf[ICMP_CHECKSUM_P]+=0x08;
304        //
305        enc28j60PacketSend(len,buf);
306}
307
308/*
309// you can send a max of 220 bytes of data
310void make_udp_reply_from_request(uint8_t *buf,char *data,uint8_t datalen,uint16_t port)
311{
312        uint8_t i=0;
313        uint16_t ck;
314        make_eth(buf);
315        if (datalen>220){
316                datalen=220;
317        }
318        // total length field in the IP header must be set:
319        buf[IP_TOTLEN_H_P]=0;
320        buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen;
321        make_ip(buf);
322        // send to port:
323        //buf[UDP_DST_PORT_H_P]=port>>8;
324        //buf[UDP_DST_PORT_L_P]=port & 0xff;
325        // sent to port of sender and use "port" as own source:
326        buf[UDP_DST_PORT_H_P]=buf[UDP_SRC_PORT_H_P];
327        buf[UDP_DST_PORT_L_P]= buf[UDP_SRC_PORT_L_P];
328        buf[UDP_SRC_PORT_H_P]=port>>8;
329        buf[UDP_SRC_PORT_L_P]=port & 0xff;
330        // calculte the udp length:
331        buf[UDP_LEN_H_P]=0;
332        buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen;
333        // zero the checksum
334        buf[UDP_CHECKSUM_H_P]=0;
335        buf[UDP_CHECKSUM_L_P]=0;
336        // copy the data:
337        while(i<datalen){
338                buf[UDP_DATA_P+i]=data[i];
339                i++;
340        }
341        ck=checksum(&buf[IP_SRC_P], 16 + datalen,1);
342        buf[UDP_CHECKSUM_H_P]=ck>>8;
343        buf[UDP_CHECKSUM_L_P]=ck& 0xff;
344        enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf);
345}
346*/
347
348void make_tcp_synack_from_syn(uint8_t *buf)
349{
350        uint16_t ck;
351        make_eth(buf);
352        // total length field in the IP header must be set:
353        // 20 bytes IP + 24 bytes (20tcp+4tcp options)
354        buf[IP_TOTLEN_H_P]=0;
355        buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4;
356        make_ip(buf);
357        buf[TCP_FLAGS_P]=TCP_FLAGS_SYNACK_V;
358        make_tcphead(buf,1,1,0);
359        // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + 4 (one option: mss)
360        ck=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+4,2);
361        buf[TCP_CHECKSUM_H_P]=ck>>8;
362        buf[TCP_CHECKSUM_L_P]=ck& 0xff;
363        // add 4 for option mss:
364        enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN,buf);
365}
366
367// get a pointer to the start of tcp data in buf
368// Returns 0 if there is no data
369// You must call init_len_info once before calling this function
370uint16_t get_tcp_data_pointer(void)
371{
372        if (info_data_len){
373                return((uint16_t)TCP_SRC_PORT_H_P+info_hdr_len);
374        }else{
375                return(0);
376        }
377}
378
379// do some basic length calculations and store the result in static varibales
380void init_len_info(uint8_t *buf)
381{
382        info_data_len=(((int16_t)buf[IP_TOTLEN_H_P])<<8)|(buf[IP_TOTLEN_L_P]&0xff);
383        info_data_len-=IP_HEADER_LEN;
384        info_hdr_len=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes;
385        info_data_len-=info_hdr_len;
386        if (info_data_len<=0){
387                info_data_len=0;
388        }
389}
390
391// fill in tcp data at position pos. pos=0 means start of
392// tcp data. Returns the position at which the string after
393// this string could be filled.
394uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s)
395{
396        char c;
397        // fill in tcp data at position pos
398        //
399        // with no options the data starts after the checksum + 2 more bytes (urgent ptr)
400        while ((c = pgm_read_byte(progmem_s++))) {
401                buf[TCP_CHECKSUM_L_P+3+pos]=c;
402                pos++;
403        }
404        return(pos);
405}
406
407// fill in tcp data at position pos. pos=0 means start of
408// tcp data. Returns the position at which the string after
409// this string could be filled.
410uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s)
411{
412        // fill in tcp data at position pos
413        //
414        // with no options the data starts after the checksum + 2 more bytes (urgent ptr)
415        while (*s) {
416                buf[TCP_CHECKSUM_L_P+3+pos]=*s;
417                pos++;
418                s++;
419        }
420        return(pos);
421}
422
423uint16_t fill_tcp_data_var(uint8_t *buf,uint16_t pos, char *s)
424{
425        // fill in tcp data at position pos
426        //
427        // with no options the data starts after the checksum + 2 more bytes (urgent ptr)
428        while (*s) {
429                buf[TCP_CHECKSUM_L_P+3+pos]=*s;
430                pos++;
431                s++;
432        }
433        return(pos);
434}
435
436
437
438
439// Make just an ack packet with no tcp data inside
440// This will modify the eth/ip/tcp header 
441void make_tcp_ack_from_any(uint8_t *buf)
442{
443        uint16_t j;
444        make_eth(buf);
445        // fill the header:
446        buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V;
447        if (info_data_len==0){
448                // if there is no data then we must still acknoledge one packet
449                make_tcphead(buf,1,0,1); // no options
450        }else{
451                make_tcphead(buf,info_data_len,0,1); // no options
452        }
453
454        // total length field in the IP header must be set:
455        // 20 bytes IP + 20 bytes tcp (when no options) 
456        j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN;
457        buf[IP_TOTLEN_H_P]=j>>8;
458        buf[IP_TOTLEN_L_P]=j& 0xff;
459        make_ip(buf);
460        // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
461        j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN,2);
462        buf[TCP_CHECKSUM_H_P]=j>>8;
463        buf[TCP_CHECKSUM_L_P]=j& 0xff;
464        enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN,buf);
465}
466
467// you must have called init_len_info at some time before calling this function
468// dlen is the amount of tcp data (http data) we send in this packet
469// You can use this function only immediately after make_tcp_ack_from_any
470// This is because this function will NOT modify the eth/ip/tcp header except for
471// length and checksum
472void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen)
473{
474        uint16_t j;
475        // fill the header:
476        // This code requires that we send only one data packet
477        // because we keep no state information. We must therefore set
478        // the fin here:
479        buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V;
480
481        // total length field in the IP header must be set:
482        // 20 bytes IP + 20 bytes tcp (when no options) + len of data
483        j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen;
484        buf[IP_TOTLEN_H_P]=j>>8;
485        buf[IP_TOTLEN_L_P]=j& 0xff;
486        fill_ip_hdr_checksum(buf);
487        // zero the checksum
488        buf[TCP_CHECKSUM_H_P]=0;
489        buf[TCP_CHECKSUM_L_P]=0;
490        // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
491        j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2);
492        buf[TCP_CHECKSUM_H_P]=j>>8;
493        buf[TCP_CHECKSUM_L_P]=j& 0xff;
494        enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf);
495}
496
497/* end of ip_arp_udp.c */