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