This is the third part of the STM32 series. For your convenience you can find other parts in the table of contents in Part 1 – DES implementation
Today we are going to handle some network packages on the STM32. Let’s go.
Libraries around
I am not going to describe how to handle SPI, LEDs and buttons on your microcontroller. First, because the code is in tutorials you get after buying the hardware, second, because I cannot post the code because of legal restrictions. However, there is no magic in this area.
Code
The code was heavily based on the work of Guido Socher. I would like to thank him for posting this code somewhere because it made the project much easier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
/********************************************* * vim:sw=8:ts=8:si:et * To use the above modeline in vim you must have "set modeline" in your .vimrc * * Author: Guido Socher * Copyright: GPL V2 * See http://www.gnu.org/licenses/gpl.html * * IP, Arp, UDP and TCP functions. * * The TCP implementation uses some size optimisations which are valid * only if all data can be sent in one single packet. This is however * not a big limitation for a microcontroller as you will anyhow use * small web-pages. The TCP stack is therefore a SDP-TCP stack (single data packet TCP). * * Chip type : ATMEGA88 with ENC28J60 *********************************************/ #include "net.h" #include "enc28j60.h" extern void Delay(__IO uint32_t nTick); #define pgm_read_byte(ptr) ((uint8_t)*(ptr)) //#define uint8_t uint8_t //#define uint8_t unisgned int static uint8_t macaddr[6]; static uint8_t ipaddr[4]; static uint16_t info_hdr_len; static uint16_t info_data_len; static uint8_t seqnum; // my initial tcp sequence number = 0xA // The Ip checksum is calculated over the ip header only starting // with the header length field and a total length of 20 bytes // unitl ip.dst // You must set the IP checksum field to zero before you start // the calculation. // len for ip is 20. // // For UDP/TCP we do not make up the required pseudo header. Instead we // use the ip.src and ip.dst fields of the real packet: // The udp checksum calculation starts with the ip.src field // Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len // In other words the len here is 8 + length over which you actually // want to calculate the checksum. // You must set the checksum field to zero before you start // the calculation. // len for udp is: 8 + 8 + data length // len for tcp is: 4+4 + 20 + option len + data length // // For more information on how this algorithm works see: // http://www.netfor2.com/checksum.html // http://www.msc.uky.edu/ken/cs471/notes/chap3.htm // The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type) { // type 0=ip // 1=udp // 2=tcp uint32_t sum = 0; //if(type==0){ // // do not add anything //} if(type==1) { sum+=IP_PROTO_UDP_V; // protocol udp // the length here is the length of udp (data+header len) // =length given to this function - (IP.scr+IP.dst length) sum+=len-8; // = real tcp len } if(type==2) { sum+=IP_PROTO_TCP_V; // the length here is the length of tcp (data+header len) // =length given to this function - (IP.scr+IP.dst length) sum+=len-8; // = real tcp len } // build the sum of 16bit words while(len >1) { sum += 0xFFFF & (*buf<<8|*(buf+1)); buf+=2; len-=2; } // if there is a byte left then add it (padded with zero) if (len) sum += (0xFF & *buf)<<8; // now calculate the sum over the bytes in the sum // until the result is only 16bit long while (sum>>16) sum = (sum & 0xFFFF)+(sum >> 16); // build 1's complement: return( (uint16_t) sum ^ 0xFFFF); } // you must call this function once before you use any of the other functions: void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip) { uint8_t i=0; info_hdr_len=0; info_data_len=0; seqnum=0xA; while(i<4) { ipaddr[i]=myip[i]; i++; } i=0; while(i<6) { macaddr[i]=mymac[i]; i++; } } uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint8_t len) { uint8_t i=0; // if (len<41) return(0); if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V) return(0); while(i<4) { if(buf[ETH_ARP_DST_IP_P+i] != ipaddr[i]) return(0); i++; } return(1); } uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint8_t len) { uint8_t i=0; //eth+ip+udp header is 42 if (len<42) return(0); if(buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V) return(0); if (buf[IP_HEADER_LEN_VER_P]!=0x45) { // must be IP V4 and 20 byte header return(0); } while(i<4) { if(buf[IP_DST_P+i]!=ipaddr[i]) return(0); i++; } return(1); } // make a return eth header from a received eth packet void make_eth(uint8_t *buf) { uint8_t i=0; // //copy the destination mac from the source and fill my mac into src while(i<6) { buf[ETH_DST_MAC +i]=buf[ETH_SRC_MAC +i]; buf[ETH_SRC_MAC +i]=macaddr[i]; i++; } } void fill_ip_hdr_checksum(uint8_t *buf) { uint16_t ck; // clear the 2 byte checksum buf[IP_CHECKSUM_P]=0; buf[IP_CHECKSUM_P+1]=0; buf[IP_FLAGS_P]=0x40; // don't fragment buf[IP_FLAGS_P+1]=0; // fragement offset buf[IP_TTL_P]=64; // ttl // calculate the checksum: ck=checksum(&buf[IP_P], IP_HEADER_LEN,0); buf[IP_CHECKSUM_P]=ck>>8; buf[IP_CHECKSUM_P+1]=ck& 0xff; } // make a return ip header from a received ip packet void make_ip(uint8_t *buf) { uint8_t i=0; while(i<4) { buf[IP_DST_P+i]=buf[IP_SRC_P+i]; buf[IP_SRC_P+i]=ipaddr[i]; i++; } fill_ip_hdr_checksum(buf); } // make a return ip header from a received ip packet for echo reply void make_ip_echo(uint8_t *buf) { uint8_t i=0; uint16_t ck; while(i<4) { buf[IP_DST_P+i]=buf[IP_SRC_P+i]; buf[IP_SRC_P+i]=ipaddr[i]; i++; } // clear the 2 byte checksum i = buf[IP_CHECKSUM_P]; buf[IP_CHECKSUM_P]=0; buf[IP_CHECKSUM_P+1]=0; buf[IP_FLAGS_P]=0x40; // don't fragment buf[IP_FLAGS_P+1]=0; // fragement offset buf[IP_TTL_P]=64; // ttl // calculate the checksum: ck=checksum(&buf[IP_P], IP_HEADER_LEN,0); buf[IP_CHECKSUM_P]=i; buf[IP_CHECKSUM_P+1]=ck& 0xff; } void make_arp_answer_from_request(uint8_t *buf) { uint8_t i=0; // make_eth(buf); buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V; buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V; // fill the mac addresses: while(i<6) { buf[ETH_ARP_DST_MAC_P+i]=buf[ETH_ARP_SRC_MAC_P+i]; buf[ETH_ARP_SRC_MAC_P+i]=macaddr[i]; i++; } i=0; while(i<4) { buf[ETH_ARP_DST_IP_P+i]=buf[ETH_ARP_SRC_IP_P+i]; buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i]; i++; } // eth+arp is 42 bytes: enc28j60PacketSend(42,buf); } void make_echo_reply_from_request(uint8_t *buf,uint16_t len) { make_eth(buf); make_ip(buf); buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V; ////////////////////////////////////////////////////////////////////////////////// // we changed only the icmp.type field from request(=8) to reply(=0). // we can therefore easily correct the checksum: if (buf[ICMP_CHECKSUM_P] > (0xff-0x08)) buf[ICMP_CHECKSUM_P+1]++; buf[ICMP_CHECKSUM_P]+=0x08; // enc28j60PacketSend(len,buf); } // you can send a max of 220 bytes of data void make_udp_reply_from_request(uint8_t *buf, uint8_t *data,uint8_t datalen,uint16_t* port) { uint8_t i=0; uint16_t ck; make_eth(buf); if (datalen>220) datalen=220; // total length field in the IP header must be set: buf[IP_TOTLEN_H_P]=0; buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen; make_ip(buf); buf[UDP_DST_PORT_H_P]=*port >> 8; buf[UDP_DST_PORT_L_P]=*port & 0xff; // source port does not matter and is what the sender used. // calculte the udp length: buf[UDP_LEN_H_P]=0; buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen; // zero the checksum buf[UDP_CHECKSUM_H_P]=0; buf[UDP_CHECKSUM_L_P]=0; // copy the data: while(i<datalen) { buf[UDP_DATA_P+i]=data[i]; i++; } ck=checksum(&buf[IP_SRC_P], 16 + datalen,1); buf[UDP_CHECKSUM_H_P]=ck>>8; buf[UDP_CHECKSUM_L_P]=ck& 0xff; enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); } // do some basic length calculations and store the result in static varibales void init_len_info(uint8_t *buf) { info_data_len=(buf[IP_TOTLEN_H_P]<<8)|(buf[IP_TOTLEN_L_P]&0xff); info_data_len-=IP_HEADER_LEN; info_hdr_len=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; info_data_len-=info_hdr_len; if (info_data_len<=0) info_data_len=0; } /* end of ip_arp_udp.c */ |
Summary
We have most of the code in place. Next time we are going to implement a server for encryption and decryption.