RealtimeCompLab2
Dependencies: mbed
Fork of PPP-Blinky by
ppp-blinky.cpp
00001 // PPP-Blinky - "The Most Basic Internet Thing" 00002 // A Tiny HTTP Webserver Using Windows XP/7/8/10/Linux Dial-Up Networking Over A Serial Port. 00003 // Receives UDP packets and responds to ping (ICMP Echo requests) 00004 // WebSocket Service - see https://en.wikipedia.org/wiki/WebSocket 00005 00006 // Copyright 2016/2017 Nicolas Nackel aka Nixnax. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00007 00008 // Notes and Instructions 00009 // http://bit.ly/PPP-Blinky-Instructions 00010 // http://bit.ly/win-rasdial-config 00011 00012 // Handy reading material 00013 // https://technet.microsoft.com/en-us/library/cc957992.aspx 00014 // https://en.wikibooks.org/wiki/Serial_Programming/IP_Over_Serial_Connections 00015 // http://atari.kensclassics.org/wcomlog.htm 00016 00017 // Handy tools 00018 // https://ttssh2.osdn.jp/index.html.en - Tera Term, a good terminal program to monitor the debug output from the second serial port with! 00019 // https://www.microsoft.com/en-us/download/details.aspx?id=4865 - Microsoft network monitor - real-time monitoring of PPP packets 00020 // http://pingtester.net/ - nice tool for high rate ping testing 00021 // http://www.sunshine2k.de/coding/javascript/crc/crc_js.html - Correctly calculates the 16-bit FCS (crc) on our frames (Choose CRC16_CCITT_FALSE), then custom relected-in=1, reflected-out=1 00022 // https://technet.microsoft.com/en-us/sysinternals/pstools.aspx - psping for fast testing of ICMP ping function 00023 // https://eternallybored.org/misc/netcat/ - use netcat -u 172.10.10.1 80 to send/receive UDP packets from PPP-Blinky 00024 // Windows Powershell invoke-webrequest command - use it to stress test the webserver like this: while (1){ invoke-webrequest -uri 172.10.10.1/x } 00025 00026 // Connecting PPP-Blinky to Linux 00027 // PPP-Blinky can be made to talk to Linux - tested on Fedora - the following command, which uses pppd, works: 00028 // pppd /dev/ttyACM0 115200 debug dump local passive noccp novj nodetach nocrtscts 172.10.10.1:172.10.10.2 00029 // in the above command 172.10.10.1 is the adapter IP, and 172.10.10.2 is the IP of PPP-Blinky. 00030 // See also https://en.wikipedia.org/wiki/Point-to-Point_Protocol_daemon 00031 00032 // Special pages when PPP-Blinky is running 00033 // 172.10.10.2 root page 00034 // 172.10.10.2/x returns the number of ppp frames sent - this is handy for testing 00035 // 172.10.10.2/xb also returns number of ppp frames sent, but issues a fast refresh meta command. This allows you to use your browser to benchmark page load speed 00036 // 172.10.10.2/ws a simple WebSocket demo 00037 // http://jsfiddle.net/d26cyuh2/ more complete WebSocket demo in JSFiddle, showing cross-domain access 00038 00039 // Ok, enough talking, time to check out some code!! 00040 00041 #include "ppp-blinky.h" 00042 00043 // The #define below enables/disables a second (OPTIONAL) serial port that prints out interesting diagnostic messages. 00044 // Change to SERIAL_PORT_MONITOR_YES to enable diagnostics messages. You need to wire a second serial port to your mbed hardware to monitor the debug output. 00045 // Using the second serial port will slow down packet response time 00046 // Note - the LPC11U24 does NOT have a second serial port 00047 00048 #define SERIAL_PORT_MONITOR_NO /* change to SERIAL_PORT_MONITOR_YES for debug messages */ 00049 00050 // here we define the OPTIONAL, second debug serial port for various mbed target boards 00051 #ifdef SERIAL_PORT_MONITOR_YES 00052 #if defined(TARGET_LPC1768) 00053 RawSerial xx(p9, p10); // Second serial port on LPC1768 - not required to run, if you get compile error here, change #define SERIAL_PORT_MONITOR_YES to #define SERIAL_PORT_MONITOR_NO 00054 #elif defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_L053R8) || defined(TARGET_NUCLEO_L476RG) || defined(TARGET_NUCLEO_F401RE) 00055 RawSerial xx(PC_10, PC_11); // Second serial port on NUCLEO boards - not required to run, if you get compile error here, change #define SERIAL_PORT_MONITOR_YES to #define SERIAL_PORT_MONITOR_NO 00056 #elif defined(TARGET_LPC11U24) 00057 #error The LPC11U24 does not have a second serial port to use for debugging - change SERIAL_PORT_MONITOR_YES back to SERIAL_PORT_MONITOR_NO 00058 #elif defined (TARGET_KL46Z) || (TARGET_KL25Z) 00059 RawSerial xx(PTE0,PTE1); // Second serial port on FRDM-KL46Z board 00060 #elif defined(TARGET_KW41Z) 00061 // change the next line to YOUR target board's second serial port pin definition if it's not present - and if it works, please send it to me - thanks!!! 00062 RawSerial xx(p9, p10); // change this to YOUR board second serial port pin definition - and please send it to me if it works!!! 00063 #else 00064 #error Add your target board's second serial port here if you want to use debugging - or simply change SERIAL_PORT_MONITOR_YES to SERIAL_PORT_MONITOR_NO 00065 #endif 00066 #define debugPrintf(x...) xx.printf (x) /* if we have a serial port we can print debug messages */ 00067 #define debugPutc(x...) xx.putc(x) 00068 #define debugBaudRate(x...) xx.baud(x) 00069 #else 00070 // if we don't have a debug port the debug print functions do nothing 00071 #define debugPrintf(x...) {} 00072 #define debugPutc(x...) {} 00073 #define debugBaudRate(x...) {} 00074 #endif 00075 00076 // verbosity flags used in debug printouts - change to 1 to see increasingly more detailed debug info. 00077 #define v0 1 00078 #define v1 0 00079 #define v2 0 00080 #define IP_HEADER_DUMP_YES /* YES for ip header dump */ 00081 #define TCP_HEADER_DUMP_YES /* YES for tcp header dump */ 00082 00083 // this is the webpage we serve when we get an HTTP request to root (/) 00084 // keep size under ~900 bytes to fit into a single PPP packet 00085 char body[] = \ 00086 "<html>\ 00087 <head><title>A simple Web Server</title></head>\ 00088 <body>\ 00089 <h1>COMP3215</h1>\ 00090 <form action=\"\" method=\"post\">\ 00091 <button name=\"button\" value='1' disable=\"disable\">LED ON</button>\ 00092 <button name=\"button\" value='0' disable=\"disable\">LED OFF</button>\ 00093 </form>\ 00094 </body>\ 00095 </html>"; 00096 //const static char rootWebPage[] = "\ 00097 //<!DOCTYPE html> 00098 //<html>\ 00099 //<head><title>A simple Web Server</title></head>\ 00100 //<body>\ 00101 //<h1>COMP3215</h1>\ 00102 //<form action=\"\" method=\"post\">\ 00103 //<button name=\"button\" value='1' disable=\"disable\">LED ON</button>\ 00104 //<button name=\"button\" value='0' disable=\"disable\">LED OFF</button>\ 00105 //</form>\ 00106 //</body>\ 00107 //</html>"; 00108 // size = 644 bytes plus 1 null byte = 645 bytes 00109 00110 // this is a websocket demo html page we serve when GET /ws is requested 00111 const static char webSocketPage[] = "\ 00112 <!DOCTYPE html>\ 00113 <html>\ 00114 <head>\ 00115 <title>mbed PPP-Blinky</title>\ 00116 <script>\ 00117 window.onload=function(){\ 00118 var url=\"ws://172.10.10.2\";\ 00119 var sts=document.getElementById(\"sts\");\ 00120 var btn=document.getElementById(\"btn\");\ 00121 var ctr=0;\ 00122 function show(text){sts.textContent=text;}\ 00123 btn.onclick=function(){\ 00124 if(btn.textContent==\"Connect\"){\ 00125 x=new WebSocket(url);\ 00126 x.onopen=function(){\ 00127 show(\"Connected to : \"+url);\ 00128 btn.textContent=\"Send \\\"\"+ctr+\"\\\"\";\ 00129 };\ 00130 x.onclose=function(){show(\"closed\");};\ 00131 x.onmessage=function(msg){show(\"PPP-Blinky Sent: \\\"\"+msg.data+\"\\\"\");};\ 00132 } else {\ 00133 x.send(ctr);\ 00134 ctr=ctr+1;\ 00135 btn.textContent=\"Send \\\"\"+ctr+\"\\\"\";\ 00136 }\ 00137 };\ 00138 };\ 00139 </script>\ 00140 <body style=\"font-family: sans-serif; font-size:25px; color:#807070\">\ 00141 <h1>PPP-Blinky WebSocket Test</h1>\ 00142 <div id=\"sts\">Idle</div>\ 00143 <button id=\"btn\" style=\"font-size: 100%; margin-top: 55px; margin-bottom: 55px;\">Connect</button>\ 00144 <h4><a href=\"/\">PPP-Blinky home</a></h4>\ 00145 </body>\ 00146 </html>"; // size = 916 bytes + 1 null byte = 917 bytes 00147 00148 // The serial port on your mbed hardware. Your PC should be configured to view this port as a standard dial-up networking modem. 00149 // On Windows the model type of the modem should be selected as "Communications cable between two computers" 00150 // The modem baud rate should be set to 115200 baud 00151 // See instructions at the top. 00152 // On a typical mbed hardware platform this serial port is a USB virtual com port (VCP) and the USB serial driver is supplied by the board vendor. 00153 RawSerial pc (USBTX, USBRX); // usb virtual com port for mbed hardware 00154 00155 DigitalOut led1(LED1);// this led toggles when a packet is received 00156 00157 // the standard hdlc frame start/end character. It's the tilde character "~" 00158 #define FRAME_7E (0x7e) 00159 00160 pppType ppp; // our global - definitely not thread safe 00161 00162 /// Initialize the ppp structure and clear the receive buffer 00163 void pppInitStruct() 00164 { 00165 memset( ppp.rx.buf, 0, RXBUFLEN); 00166 ppp.online=0; 00167 ppp.rx.tail=0; 00168 ppp.rx.rtail=0; 00169 ppp.rx.head=0; 00170 ppp.rx.buflevel=0; 00171 ppp.pkt.len=0; 00172 ppp.ipData.ident=10000; // easy to recognize in ip packet dumps 00173 ppp.ledState=0; 00174 ppp.hdlc.frameStartIndex=0; 00175 ppp.responseCounter=0; 00176 ppp.firstFrame=1; 00177 ppp.ppp = (pppHeaderType *)ppp.pkt.buf; // pointer to ppp header 00178 ppp.ip = (ipHeaderType *)(ppp.pkt.buf+4); // pointer to IP header 00179 } 00180 00181 /// Toggle the LED on every second PPP packet received 00182 void led1Toggle() 00183 { 00184 led1 = (ppp.ledState >> 1) & 1; // use second bit, in other words toggle LED only every second packet 00185 ppp.ledState++; 00186 } 00187 00188 /// Returns 1 after a connect message, 0 at startup or after a disconnect message 00189 int connectedPpp() 00190 { 00191 // cnt = 0; 00192 // if(cnt == 0) 00193 // led_flash(); 00194 return ppp.online; 00195 } 00196 00197 /// Previously used to check for characters in receive buffer. 00198 /// Now just a stub. 00199 void checkPc() {}; 00200 00201 /// PPP serial port receive interrupt handler. 00202 /// Check for available characters from the PC and read them into our own circular serial receive buffer at ppp.rx.buf. 00203 /// Also, if we are offline and a 0x7e frame start character is seen, we go online immediately 00204 void pppReceiveHandler() 00205 { 00206 char ch; 00207 while ( pc.readable() ) { 00208 int hd = (ppp.rx.head+1)&(RXBUFLEN-1); // increment/wrap head index 00209 if ( hd == ppp.rx.rtail ) { 00210 debugPrintf("\nReceive buffer full\n"); 00211 return; 00212 } 00213 ch = pc.getc(); // read new character 00214 ppp.rx.buf[ppp.rx.head] = ch; // insert in our receive buffer 00215 if ( ppp.online == 0 ) { 00216 if (ch == 0x7E) { 00217 ppp.online = 1; 00218 } 00219 } 00220 ppp.rx.head = hd; // update head pointer 00221 ppp.rx.buflevel++; 00222 } 00223 } 00224 00225 /// print to debug port while checking for incoming characters 00226 void putcWhileCheckingInput( char outByte ) 00227 { 00228 #ifdef SERIAL_PORT_MONITOR_YES 00229 checkPc(); 00230 debugPutc( outByte ); 00231 checkPc(); 00232 #endif 00233 } 00234 00235 /// puts to debug port while checking the PPP input stream 00236 void putsWhileCheckingInput( char * data ) 00237 { 00238 #ifdef SERIAL_PORT_MONITOR_YES 00239 char * nextChar = data; 00240 while( *nextChar != 0 ) { 00241 putcWhileCheckingInput( *nextChar ); // write one character to debug port while checking input 00242 nextChar++; 00243 } 00244 #endif 00245 } 00246 00247 /// Initialize the PPP FCS (frame check sequence) total 00248 void fcsReset() 00249 { 00250 ppp.fcs=0xffff; // crc restart 00251 } 00252 00253 /// update the cumulative PPP FCS (frame check sequence) 00254 void fcsDo(int x) 00255 { 00256 for (int i=0; i<8; i++) { 00257 ppp.fcs=((ppp.fcs&1)^(x&1))?(ppp.fcs>>1)^0x8408:ppp.fcs>>1; // crc calculator 00258 x>>=1; 00259 } 00260 checkPc(); // handle input 00261 } 00262 00263 /// calculate the PPP FCS (frame check sequence) on an entire block of memory 00264 int fcsBuf(char * buf, int size) // crc on an entire block of memory 00265 { 00266 fcsReset(); 00267 for(int i=0; i<size; i++)fcsDo(*buf++); 00268 return ppp.fcs; 00269 } 00270 00271 /// Get one character from our received PPP buffer 00272 int pc_getBuf() 00273 { 00274 int x = ppp.rx.buf[ ppp.rx.tail ]; 00275 ppp.rx.tail=(ppp.rx.tail+1)&(RXBUFLEN-1); 00276 ppp.rx.buflevel--; 00277 return x; 00278 } 00279 00280 /// Dump a PPP frame to the debug serial port 00281 /// Note - the hex output of dumpPPPFrame() can be imported into WireShark 00282 /// Capture the frame's hex output in your terminal program and save as a text file 00283 /// In WireShark, use "Import Hex File". Import options are: Offset=None, Protocol=PPP. 00284 void dumpPPPFrame() 00285 { 00286 char pbuf[30]; 00287 for(int i=0; i<ppp.pkt.len; i++) { 00288 checkPc(); 00289 sprintf(pbuf, "%02x ", ppp.pkt.buf[i]); 00290 checkPc(); 00291 putsWhileCheckingInput(pbuf); 00292 } 00293 checkPc(); 00294 sprintf(pbuf, " CRC=%04x Len=%d\n", ppp.pkt.crc, ppp.pkt.len); 00295 checkPc(); 00296 putsWhileCheckingInput(pbuf); 00297 } 00298 00299 /// Process a received PPP frame 00300 void processPPPFrame(int start, int end) 00301 { 00302 led1Toggle(); // change led1 state on every frame we receive 00303 if(start==end) { 00304 return; // empty frame 00305 } 00306 fcsReset(); 00307 char * dest = ppp.pkt.buf; 00308 ppp.pkt.len=0; 00309 int unstuff=0; 00310 int idx = start; 00311 while(1) { 00312 checkPc(); 00313 if (unstuff==0) { 00314 if (ppp.rx.buf[idx]==0x7d) unstuff=1; 00315 else { 00316 *dest = ppp.rx.buf[idx]; 00317 ppp.pkt.len++; 00318 dest++; 00319 fcsDo(ppp.rx.buf[idx]); 00320 } 00321 } else { // unstuff characters prefixed with 0x7d 00322 *dest = ppp.rx.buf[idx]^0x20; 00323 ppp.pkt.len++; 00324 dest++; 00325 fcsDo(ppp.rx.buf[idx]^0x20); 00326 unstuff=0; 00327 } 00328 idx = (idx+1) & (RXBUFLEN-1); 00329 if (idx == end) break; 00330 } 00331 ppp.pkt.crc = ppp.fcs & 0xffff; 00332 if(0) dumpPPPFrame(); // set to 1 to dump ALL ppp frames 00333 if (ppp.pkt.crc == 0xf0b8) { // check for good CRC 00334 determinePacketType(); 00335 } else { 00336 #define REPORT_FCS_ERROR_YES 00337 #ifdef REPORT_FCS_ERROR_YES 00338 char pbuf[50]; // local print buffer 00339 checkPc(); 00340 sprintf(pbuf, "\nPPP FCS(crc) Error CRC=%x Length = %d\n",ppp.pkt.crc,ppp.pkt.len); // print a debug line 00341 checkPc(); 00342 putsWhileCheckingInput( pbuf ); 00343 if(0) dumpPPPFrame(); // set to 1 to dump frames with errors in them 00344 #endif 00345 } 00346 } 00347 00348 /// output a character to the PPP port while checking for incoming characters 00349 void pcPutcWhileCheckingInput(int ch) 00350 { 00351 checkPc(); // check input 00352 pc.putc(ch); 00353 checkPc(); 00354 } 00355 00356 /// do PPP HDLC-like handling of special (flag) characters 00357 void hdlcPut(int ch) 00358 { 00359 if ( (ch<0x20) || (ch==0x7d) || (ch==0x7e) ) { 00360 pcPutcWhileCheckingInput(0x7d); 00361 pcPutcWhileCheckingInput(ch^0x20); // these characters need special handling 00362 } else { 00363 pcPutcWhileCheckingInput(ch); 00364 } 00365 } 00366 00367 /// send a PPP frame in HDLC format 00368 void sendPppFrame() 00369 { 00370 ppp.responseCounter++; // count the number of ppp frames we send 00371 int crc = fcsBuf(ppp.pkt.buf, ppp.pkt.len-2); // update crc 00372 ppp.pkt.buf[ ppp.pkt.len-2 ] = (~crc>>0); // fcs lo (crc) 00373 ppp.pkt.buf[ ppp.pkt.len-1 ] = (~crc>>8); // fcs hi (crc) 00374 pcPutcWhileCheckingInput(0x7e); // hdlc start-of-frame "flag" 00375 for(int i=0; i<ppp.pkt.len; i++) { 00376 wait_us(86); // wait one character time 00377 checkPc(); 00378 hdlcPut( ppp.pkt.buf[i] ); // send a character 00379 } 00380 pcPutcWhileCheckingInput(0x7e); // hdlc end-of-frame "flag" 00381 } 00382 00383 /// convert a network ip address in the buffer to an integer (IP adresses are big-endian, i.e most significant byte first) 00384 int bufferToIP(char * buffer) 00385 { 00386 int result=0; 00387 for(int i=0; i<4; i++) result = (result<<8)|(*buffer++ & 0xff); 00388 return result; 00389 } 00390 00391 /// convert 4-byte ip address to 32-bit 00392 unsigned int ip( int a, int b, int c, int d) 00393 { 00394 return a<<24 | b<<16 | c<<8 | d; 00395 } 00396 00397 /// handle IPCP configuration requests 00398 void ipcpConfigRequestHandler() 00399 { 00400 debugPrintf("Their IPCP Config Req, Our Ack\n"); 00401 if(ppp.ipcp->request[0]==3) { 00402 ppp.hostIP = bufferToIP(ppp.pkt.buf+10); 00403 debugPrintf("Host IP = %d.%d.%d.%d (%08x)\n", ppp.ipcp->request[2],ppp.ipcp->request[3],ppp.ipcp->request[4],ppp.ipcp->request[5],ppp.hostIP); 00404 } 00405 00406 ppp.ipcp->code=2; // change code to ack 00407 sendPppFrame(); // acknowledge everything they ask for - assume it's IP addresses 00408 00409 debugPrintf("Our IPCP Ask (no options)\n"); 00410 ppp.ipcp->code=1; // change code to request 00411 ppp.ipcp->lengthR = __REV16( 4 ); // 4 is minimum length - no options in this request 00412 ppp.pkt.len=4+4+2; // no options in this request shortest ipcp packet possible (4 ppp + 4 ipcp + 2 crc) 00413 sendPppFrame(); // send our request 00414 } 00415 00416 /// handle IPCP acknowledge (do nothing) 00417 void ipcpAckHandler() 00418 { 00419 debugPrintf("Their IPCP Grant\n"); 00420 } 00421 00422 /// Handle IPCP NACK by sending our suggested IP address if there is an IP involved. 00423 /// This is how Linux responds to an IPCP request with no options - Windows assumes any IP address on the submnet is OK. 00424 void ipcpNackHandler() 00425 { 00426 debugPrintf("Their IPCP Nack\n"); 00427 if (ppp.ipcp->request[0]==3) { // check if the NACK contains an IP address parameter 00428 ppp.ipcp->code=1; // assume the NACK contains our "suggested" IP address 00429 sendPppFrame(); // let's request this IP address as ours 00430 debugPrintf("Our IPCP ACK (received an IP)\n"); 00431 } else { // if it's not an IP nack we ignore it 00432 debugPrintf("IPCP Nack Ignored\n"); 00433 } 00434 } 00435 00436 /// handle all other IPCP requests (by ignoring them) 00437 void ipcpDefaultHandler() 00438 { 00439 debugPrintf("Their IPCP Other\n"); 00440 } 00441 00442 /// process an incoming IPCP packet 00443 void IPCPframe() 00444 { 00445 int action = ppp.ipcp->code; // packet type is here 00446 switch (action) { 00447 case 1: 00448 ipcpConfigRequestHandler(); 00449 break; 00450 case 2: 00451 ipcpAckHandler(); 00452 break; 00453 case 3: 00454 ipcpNackHandler(); 00455 break; 00456 default: 00457 ipcpDefaultHandler(); 00458 } 00459 } 00460 00461 /// perform a 16-bit checksum. if the byte count is odd, stuff in an extra zero byte. 00462 unsigned int dataCheckSum(char * ptr, int len, int restart) 00463 { 00464 unsigned int i,hi,lo; 00465 unsigned char placeHolder; 00466 if (restart) ppp.sum=0; 00467 if (len&1) { 00468 placeHolder = ptr[len]; 00469 ptr[len]=0; // if the byte count is odd, insert one extra zero byte is after the last real byte because we sum byte PAIRS 00470 } 00471 i=0; 00472 while ( i<len ) { 00473 checkPc(); 00474 hi = ptr[i++]; 00475 lo = ptr[i++]; 00476 ppp.sum = ppp.sum + ((hi<<8)|lo); 00477 } 00478 if (len&1) { 00479 ptr[len] = placeHolder; // restore the extra byte we made zero 00480 } 00481 ppp.sum = (ppp.sum & 0xffff) + (ppp.sum>>16); 00482 ppp.sum = (ppp.sum & 0xffff) + (ppp.sum>>16); // sum one more time to catch any carry from the carry 00483 return ~ppp.sum; 00484 } 00485 00486 /// perform the checksum on an IP header 00487 void IpHeaderCheckSum() 00488 { 00489 ppp.ip->checksumR=0; // zero the checsum in the IP header 00490 int len = 4 * ppp.ip->headerLength; // length of IP header in bytes 00491 unsigned int sum = dataCheckSum(ppp.ipStart,len,1); 00492 ppp.ip->checksumR = __REV16( sum ); // insert fresh checksum 00493 } 00494 00495 /// swap the IP source and destination addresses 00496 void swapIpAddresses() 00497 { 00498 unsigned int tempHold; 00499 tempHold = ppp.ip->srcAdrR; // tempHold <- source IP 00500 ppp.ip->srcAdrR = ppp.ip->dstAdrR; // source <- dest 00501 ppp.ip->dstAdrR = tempHold; // dest <- tempHold*/ 00502 } 00503 00504 /// swap the IP source and destination ports 00505 void swapIpPorts() 00506 { 00507 int headerSizeIP = 4 * (ppp.ip->headerLength); // calculate size of IP header 00508 char * ipSrcPort = ppp.ipStart + headerSizeIP + 0; // ip source port location 00509 char * ipDstPort = ppp.ipStart + headerSizeIP + 2; // ip destin port location 00510 char tempHold[2]; 00511 memcpy(tempHold, ipSrcPort,2); // tempHold <- source 00512 memcpy(ipSrcPort,ipDstPort,2); // source <- dest 00513 memcpy(ipDstPort,tempHold, 2); // dest <- tempHold 00514 } 00515 00516 /// Build the "pseudo header" required for UDP and TCP, then calculate its checksum 00517 void checkSumPseudoHeader( unsigned int packetLength ) 00518 { 00519 // this header contains the most important parts of the IP header, i.e. source and destination address, protocol number and data length. 00520 pseudoIpHeaderType pseudoHeader; // create pseudo header 00521 pseudoHeader.srcAdrR = ppp.ip->srcAdrR; // copy in ip source address 00522 pseudoHeader.dstAdrR = ppp.ip->dstAdrR; // copy in ip dest address 00523 pseudoHeader.zero = 0; // zero byte 00524 pseudoHeader.protocol = ppp.ip->protocol; // protocol number (udp or tcp) 00525 pseudoHeader.lengthR = __REV16( packetLength ); // size of tcp or udp packet 00526 dataCheckSum(pseudoHeader.start, 12, 1); // calculate this header's checksum 00527 } 00528 00529 /// initialize an IP packet to send 00530 void initIP (unsigned int srcIp, unsigned int dstIp, unsigned int srcPort, unsigned int dstPort, unsigned int protocol) 00531 { 00532 ppp.ppp->address = 0xff; 00533 ppp.ppp->control = 3; 00534 ppp.ppp->protocolR = __REV16( 0x0021 ); 00535 ppp.ip->version = 4; 00536 ppp.ip->headerLength = 5; // 5 words = 20 bytes 00537 ppp.ip->identR = __REV16(ppp.ipData.ident++); // insert our ident 00538 ppp.ip->dontFragment=1; 00539 ppp.ip->ttl=128; 00540 ppp.ip->protocol = protocol; // udp 00541 ppp.ip->srcAdrR = __REV(srcIp); 00542 ppp.ip->dstAdrR = __REV(dstIp); 00543 ppp.udpStart = ppp.ipStart + 20; // calculate start of udp header 00544 ppp.udp->srcPortR = __REV16(srcPort); // source port 00545 ppp.udp->dstPortR = __REV16(dstPort); // dest port 00546 } 00547 00548 00549 /// Build a UDP packet from scratch 00550 void sendUdp(unsigned int srcIp, unsigned int dstIp, unsigned int srcPort, unsigned int dstPort, char * message,int msgLen) 00551 { 00552 struct { 00553 unsigned int ipAll; // length of entire ip packet 00554 unsigned int ipHeader; // length of ip header 00555 unsigned int udpAll; // length of entire udp packet 00556 unsigned int udpData; // length of udp data segment 00557 } len; 00558 len.ipHeader = 20; // ip header length 00559 len.udpData = msgLen; // udp data size 00560 len.udpAll = len.udpData+8; // update local udp packet length 00561 len.ipAll = len.ipHeader + len.udpAll; // update IP Length 00562 initIP(srcIp, dstIp, srcPort, dstPort, 17); // init a UDP packet 00563 ppp.ip->lengthR = __REV16(len.ipAll); // update IP length in buffer 00564 ppp.udpStart = ppp.ipStart + len.ipHeader; // calculate start of udp header 00565 memcpy( ppp.udp->data, message, len.udpData ); // copy the message to the buffer 00566 ppp.udp->lengthR = __REV16(len.udpAll); // update UDP length in buffer 00567 ppp.pkt.len = len.ipAll+2+4; // update ppp packet length 00568 IpHeaderCheckSum(); // refresh IP header checksum 00569 checkSumPseudoHeader( len.udpAll ); // get the UDP pseudo-header checksum 00570 ppp.udp->checksumR = 0; // before TCP checksum calculations the checksum bytes must be set cleared 00571 unsigned int pseudoHeaderSum=dataCheckSum(ppp.udpStart,len.udpAll, 0); // continue the TCP checksum on the whole TCP packet 00572 ppp.udp->checksumR = __REV16( pseudoHeaderSum); // tcp checksum done, store it in the TCP header 00573 sendPppFrame(); // send the UDP message back 00574 } 00575 00576 /// Process an incoming UDP packet. 00577 /// If the packet starts with the string "echo " or "test" we echo back a special packet 00578 void UDPpacket() 00579 { 00580 struct { 00581 unsigned int all; // length of entire ip packet 00582 unsigned int header; // length of ip header 00583 } ipLength; 00584 00585 struct { 00586 unsigned int all; // length of entire udp packet 00587 unsigned int data; // length of udp data segment 00588 } udpLength; 00589 00590 ipLength.header = 4 * ppp.ip->headerLength; // length of ip header 00591 ppp.udpStart = ppp.ipStart + ipLength.header; // calculate start of udp header 00592 udpLength.all = __REV16( ppp.udp->lengthR ); // size of udp packet 00593 udpLength.data = udpLength.all - 8; // size of udp data 00594 00595 #ifdef SERIAL_PORT_MONITOR_YES 00596 char * srcIP = ppp.ip->srcAdrPtr; // IP source 00597 char * dstIP = ppp.ip->dstAdrPtr; //IP destination 00598 00599 unsigned int udpSrcPort = __REV16( ppp.udp->srcPortR ); // integer of UDP source port 00600 unsigned int udpDstPort = __REV16( ppp.udp->dstPortR ); // integer of UDP dest port 00601 00602 if(v0) { 00603 debugPrintf("UDP %d.%d.%d.%d:%d ", srcIP[0],srcIP[1],srcIP[2],srcIP[3],udpSrcPort); 00604 debugPrintf("%d.%d.%d.%d:%d ", dstIP[0],dstIP[1],dstIP[2],dstIP[3],udpDstPort); 00605 debugPrintf("Len %03d", udpLength); 00606 } 00607 if (v1) { 00608 int printSize = udpLength.data; 00609 if (printSize > 20) printSize = 20; // print only first 20 characters 00610 for (int i=0; i<printSize; i++) { 00611 char ch = ppp.udp->data[i]; 00612 if (ch>31 && ch<127) { 00613 debugPrintf("%c", ch); 00614 } else { 00615 debugPrintf("_"); 00616 } 00617 } 00618 } 00619 if (v0) debugPrintf("\n"); 00620 #endif 00621 int echoFound = !strncmp(ppp.udp->data,"echo ",5); // true if UDP message starts with "echo " 00622 int testFound = !strncmp(ppp.udp->data,"test" ,4); // true if UDP message starts with "test" 00623 if ( (echoFound) || (testFound)) { // if the UDP message starts with "echo " or "test" we answer back 00624 if (echoFound) { 00625 swapIpAddresses(); // swap IP source and destination 00626 swapIpPorts(); // swap IP source and destination ports 00627 memcpy(ppp.udp->data,"Got{",4); // in the UDP data modify "echo" to "Got:" 00628 int n=0; 00629 n=n+sprintf(n+ppp.udp->data+udpLength.data, "} UDP Server: PPP-Blinky\n"); // an appendix 00630 udpLength.data = udpLength.data + n; // update udp data size with the size of the appendix 00631 // we may have changed data length, update all the lengths 00632 udpLength.all = udpLength.data+8; // update local udp packet length 00633 ipLength.all = ipLength.header + udpLength.all; // update IP Length 00634 ppp.ip->lengthR = __REV16(ipLength.all); // update IP length in buffer 00635 ppp.udp->lengthR = __REV16(udpLength.all); // update UDP length in buffer 00636 ppp.pkt.len = ipLength.all+2+4; // update ppp packet length 00637 IpHeaderCheckSum(); // refresh IP header checksum 00638 checkSumPseudoHeader( udpLength.all ); // get the UDP pseudo-header checksum 00639 ppp.udp->checksumR = 0; // before TCP checksum calculations the checksum bytes must be set cleared 00640 unsigned int pseudoHeaderSum=dataCheckSum(ppp.udpStart,udpLength.all, 0); // continue the TCP checksum on the whole TCP packet 00641 ppp.udp->checksumR = __REV16( pseudoHeaderSum); // tcp checksum done, store it in the TCP header 00642 sendPppFrame(); // send the UDP message back 00643 } else if ( testFound ) { 00644 unsigned int sI = __REV( ppp.ip->srcAdrR ); 00645 unsigned int dI = __REV( ppp.ip->dstAdrR ); 00646 unsigned int sp = __REV16( ppp.udp->srcPortR ); 00647 unsigned int dp = __REV16( ppp.udp->dstPortR ); 00648 int n=sprintf(ppp.pkt.buf+200,"Response Count %d\n", ppp.responseCounter); 00649 sendUdp(dI,sI,dp,sp,ppp.pkt.buf+200,n); // build a udp packet from the ground up 00650 } 00651 } 00652 } 00653 00654 /// UDP demo that sends a udp packet containing a character received from the second debug serial port. 00655 /// Sends a 48 byte IP/UDP header for every 1 byte of data so line-mode would probably be better. 00656 /// If you want ip packets from ppp blinky to be routed to other networks, ensure you have ip routing enabled. 00657 /// See http://www.wikihow.com/Enable-IP-Routing. 00658 /// Also ensure that the firewall on the receiving machine has the receiving UDP port (12345 in this example) enabled. 00659 /// The netcat UDP receive command on the remote host would be: nc -ul 12345 00660 void sendUdpData() 00661 { 00662 #ifdef SERIAL_PORT_MONITOR_YES 00663 if (ppp.online) { 00664 if (xx.readable()) { 00665 char inchar = xx.getc(); 00666 xx.putc( inchar ); // echo the received character on the debug serial port 00667 sendUdp(ip(172,10,10,2), ip(192,168,0,109), 1, 12345, &inchar, 1); // send a 1 byte UDP message to a remote machine at IP 192.168.0.109:12345 00668 } 00669 } 00670 #endif 00671 } 00672 00673 /// handle a PING ICMP (internet control message protocol) packet 00674 void ICMPpacket() // internet control message protocol 00675 { 00676 struct { 00677 unsigned int all; // length of entire ip packet 00678 unsigned int header; // length of ip header 00679 } ipLength; 00680 struct { 00681 unsigned int all; // length of entire udp packet 00682 unsigned int data; // length of udp data segment 00683 } icmpLength; 00684 ipLength.all = __REV16( ppp.ip->lengthR ); // length of ip packet 00685 ipLength.header = 4 * ppp.ip->headerLength; // length of ip header 00686 ppp.icmpStart = ppp.ipStart + ipLength.header; // calculate start of udp header 00687 icmpLength.all = ipLength.all - ipLength.header; // length of icmp packet 00688 icmpLength.data = icmpLength.all - 8; // length of icmp data 00689 #define ICMP_TYPE_PING_REQUEST 8 00690 if ( ppp.icmp->type == ICMP_TYPE_PING_REQUEST ) { 00691 ppp.ip->ttl--; // decrement time to live (so we have to update header checksum) 00692 #ifdef SERIAL_PORT_MONITOR_YES 00693 char * srcAdr = ppp.ip->srcAdrPtr; 00694 char * dstAdr = ppp.ip->dstAdrPtr; 00695 int icmpIdent = __REV16( ppp.ip->identR ); // byte reversed - big endian 00696 int icmpSequence = __REV16( ppp.icmp->sequenceR ); // byte reversed - big endian 00697 if(1) { 00698 char pbuf[50]; 00699 checkPc(); 00700 sprintf(pbuf, "ICMP PING %d.%d.%d.%d %d.%d.%d.%d ", srcAdr[0],srcAdr[1],srcAdr[2],srcAdr[3],dstAdr[0],dstAdr[1],dstAdr[2],dstAdr[3]); 00701 putsWhileCheckingInput( pbuf ); 00702 checkPc(); 00703 sprintf(pbuf, "Ident %04x Sequence %04d \n",icmpIdent,icmpSequence); 00704 checkPc(); 00705 putsWhileCheckingInput( pbuf ); 00706 } 00707 #endif 00708 swapIpAddresses(); // swap the IP source and destination addresses 00709 IpHeaderCheckSum(); // new ip header checksum (required because we changed TTL) 00710 #define ICMP_TYPE_ECHO_REPLY 0 00711 ppp.icmp->type = ICMP_TYPE_ECHO_REPLY; // icmp echo reply 00712 ppp.icmp->checkSumR = 0; // zero the checksum for recalculation 00713 unsigned int sum = dataCheckSum(ppp.icmpStart, icmpLength.all, 1); // icmp checksum 00714 ppp.icmp->checkSumR = __REV16( sum ); // save big-endian icmp checksum 00715 00716 int printSize = icmpLength.data; // exclude size of icmp header 00717 if (printSize > 10) printSize = 10; // print up to 20 characters 00718 if (0) { 00719 for (int i=0; i<printSize; i++) { 00720 char ch = ppp.icmp->data[i]; 00721 if (ch>31 && ch<127) { 00722 putcWhileCheckingInput(ch); 00723 } else { 00724 putcWhileCheckingInput('_'); 00725 } 00726 } 00727 putcWhileCheckingInput('\n'); 00728 } 00729 sendPppFrame(); // reply to the ping 00730 } else { 00731 if (v0) { 00732 debugPrintf("ICMP type=%x \n", ppp.icmp->type); 00733 } 00734 } 00735 } 00736 00737 /// handle an IGMP (internet group managment protocol) packet (by ignoring it) 00738 void IGMPpacket() 00739 { 00740 if (v0) debugPrintf("IGMP type=%d \n", ppp.pkt.buf[28]); 00741 } 00742 00743 /// dump the header of an IP pakcet on the (optional) debug serial port 00744 void dumpHeaderIP (int outGoing) 00745 { 00746 #if defined(IP_HEADER_DUMP_YES) && defined(SERIAL_PORT_MONITOR_YES) 00747 checkPc(); // we are expecting the first character of the next packet 00748 int IPv4Id = __REV16(ppp.ip->identR); 00749 char pbuf[80]; // local print buffer 00750 int n=0; 00751 n=n+sprintf(pbuf+n, outGoing ? "\x1b[34m" : "\x1b[30m" ); // VT100 color code, print black for incoming, blue for outgoing headers 00752 n=n+sprintf(pbuf+n, "%05d ",IPv4Id); // IPv4Id is a good way to correlate our dumps with net monitor or wireshark traces 00753 #define DUMP_FULL_IP_ADDRESS_YES 00754 #ifdef DUMP_FULL_IP_ADDRESS_YES 00755 char * srcAdr = ppp.ip->srcAdrPtr; 00756 char * dstAdr = ppp.ip->dstAdrPtr; 00757 n=n+sprintf(pbuf+n, " %d.%d.%d.%d %d.%d.%d.%d ",srcAdr[0],srcAdr[1],srcAdr[2],srcAdr[3], dstAdr[0],dstAdr[1],dstAdr[2],dstAdr[3]); // full ip addresses 00758 #endif 00759 putsWhileCheckingInput( pbuf ); 00760 #ifndef TCP_HEADER_DUMP_YES 00761 putsWhileCheckingInput('\x1b[30m\n'); // if there's no TCP header dump we terminate the line with \n and VT100 code for black 00762 #endif 00763 #endif 00764 } 00765 00766 /// dump a TCP header on the optional debug serial port 00767 void dumpHeaderTCP(int outGoing) 00768 { 00769 #if defined(TCP_HEADER_DUMP_YES) && defined(SERIAL_PORT_MONITOR_YES) 00770 char flagString[9]; // text string presenting the 8 most important TCP flags 00771 #define PRINT_ALL_TCP_FLAGS_YES 00772 #ifdef PRINT_ALL_TCP_FLAGS_YES 00773 memset(flagString,'.', 8); // fill string with "........" 00774 if (ppp.tcp->flag.fin) flagString[7]='F'; 00775 if (ppp.tcp->flag.syn) flagString[6]='S'; 00776 if (ppp.tcp->flag.rst) flagString[5]='R'; 00777 if (ppp.tcp->flag.psh) flagString[4]='P'; 00778 if (ppp.tcp->flag.ack) flagString[3]='A'; 00779 if (ppp.tcp->flag.urg) flagString[2]='U'; 00780 if (ppp.tcp->flag.ece) flagString[1]='E'; 00781 if (ppp.tcp->flag.cwr) flagString[0]='C'; 00782 flagString[8]=0; // null terminate string 00783 #else 00784 if (ppp.tcp->flag.ack) flagString[0]='A'; // choose only the most important flag to print 00785 if (ppp.tcp->flag.syn) flagString[0]='S'; 00786 if (ppp.tcp->flag.fin) flagString[0]='F'; 00787 if (ppp.tcp->flag.psh) flagString[0]='P'; 00788 if (ppp.tcp->flag.rst) flagString[0]='R'; 00789 flagString[1]=0; // null terminate string 00790 #endif 00791 putsWhileCheckingInput( flagString ); 00792 #define EVERY_PACKET_ON_A_NEW_LINE_YES 00793 #ifdef EVERY_PACKET_ON_A_NEW_LINE_YES 00794 putsWhileCheckingInput("\x1b[30m\n"); // write a black color and newline after every packet 00795 #else 00796 putsWhileCheckingInput("\x1b[30m"); // write a black color after every packet 00797 #endif 00798 if( outGoing && ppp.tcp->flag.fin ) { // ACK/FIN - if this is an outgoing FIN it's the end of a tcp conversation 00799 putcWhileCheckingInput('\n'); // insert an extra new line to mark the end (except for final ack) of an HTTP conversation 00800 } 00801 #endif 00802 } 00803 00804 /// Encode a buffer in base-64 00805 const static char lut [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 00806 void enc64(char * in, char * out, int len) 00807 { 00808 int i,j,a,b,c; 00809 i=0; 00810 j=0; 00811 while(1) { 00812 if (i<len) { 00813 a = in[i++]; 00814 out[j++] = lut[ ( (a >> 2) & 0x3f) ]; 00815 } else break; 00816 if (i<len) { 00817 b = in[i++]; 00818 out[j++] = lut[ ( (a << 4) & 0x30) | ( (b >> 4) & 0x0f) ]; 00819 out[j++] = lut[ ( (b << 2) & 0x3c) ]; 00820 } else out[j++] = '='; 00821 if (i<len) { 00822 c = in[i++]; 00823 j--; 00824 out[j++] = lut[ ( (b << 2) & 0x3c) | ( (c >> 6) & 0x03) ]; 00825 out[j++] = lut[ ( (c >> 0) & 0x3f) ]; 00826 } else out[j++] = '='; 00827 } 00828 out[j]=0; 00829 } 00830 00831 /// Handle a request for an http websocket. 00832 /// We end up here if we enter the following javascript in a web browser console: x = new WebSocket("ws://172.10.10.2"); 00833 int webSocketHandler(char * dataStart) 00834 { 00835 int n=0; // byte counter 00836 char * key = strstr(dataStart, "Sec-WebSocket-Key:"); // search for the key in the payload 00837 if (key != NULL) { 00838 key = key + 18; // skip over the key ident string "Sec-WebSocket-Key:" 00839 if (v0) putsWhileCheckingInput("WebSocket Request\n"); 00840 while ( strchr(lut, *key) == NULL) key++; // skip non-valid base-64 characters (whitespace) 00841 char challenge [80]; 00842 int i=0; 00843 char * copyTo = challenge; 00844 while (strchr(lut, *key) != NULL) { // copy while we see valid base-64 characters 00845 if (i++ >40) break; // prevent buffer overflow 00846 *copyTo++ = *key++; // copy next valid base-64 character 00847 } 00848 strcpy(copyTo,"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); // append websocket gui code 00849 if (0) debugPrintf("Challenge is %s\n", challenge); // the string we hash for the challenge 00850 char shaOutput [20]; // sha1 output 00851 sha1( shaOutput, challenge, strlen(challenge)); // hash the challenge 00852 char encOut[50]; 00853 enc64( shaOutput, encOut, 20); // base-64 encode 00854 char * versionstring = strstr(dataStart, "Sec-WebSocket-Version:"); 00855 char * version = challenge; 00856 strncpy(version, versionstring,70); // copy their version string 00857 *strchr(version,'\r')=0; // null terminate so we can sprintf it 00858 memset(dataStart,0,500); // blank out old data befor send the websocket response header 00859 n=n+sprintf(dataStart+n, "HTTP/1.1 101 Switching Protocols\r\n"); 00860 n=n+sprintf(dataStart+n, "Upgrade: websocket\r\n"); 00861 n=n+sprintf(dataStart+n, "Connection: Upgrade\r\n"); 00862 n=n+sprintf(dataStart+n, "Sec-WebSocket-Accept: %s\r\n",encOut); 00863 n=n+sprintf(dataStart+n, "%s\r\n",version); 00864 n=n+sprintf(dataStart+n, "mbed-Code: PPP-Blinky\r\n"); 00865 n=n+sprintf(dataStart+n, "\r\n"); // websocket response header ending 00866 } 00867 return n; // this response should satisfy a web browser's websocket protocol request 00868 } 00869 00870 #define TCP_FLAG_ACK (1<<4) 00871 #define TCP_FLAG_SYN (1<<1) 00872 #define TCP_FLAG_PSH (1<<3) 00873 #define TCP_FLAG_RST (1<<2) 00874 #define TCP_FLAG_FIN (1<<0) 00875 00876 /// respond to an HTTP request 00877 int httpResponse(char * dataStart, int * flags) 00878 { 00879 int n=0; // number of bytes we have printed so far 00880 n = webSocketHandler( dataStart ); // test for and handle WebSocket upgrade requests 00881 if (n>0) return n; // if n>0 we already have a response, so return 00882 00883 int nHeader; // byte size of HTTP header 00884 int contentLengthStart; // index where HTML starts 00885 int httpGet5,httpGet6,httpGetx, httpGetRoot; 00886 int httpGetLedOff,httpGetLedOn; // temporary storage of strncmp results 00887 *flags = TCP_FLAG_ACK | TCP_FLAG_FIN; // the default case is that we close the connection 00888 00889 httpGetRoot = strncmp(dataStart, "GET / HTTP/1.", 13); // found a GET to the root directory 00890 httpGetx = strncmp(dataStart, "GET /x", 6); // found a GET to /x which we will treat special (anything starting with /x, e.g. /x, /xyz, /xABC?pqr=123 00891 httpGet5 = dataStart[5]; // the first character in the path name, we use it for special functions later on 00892 httpGet6 = dataStart[6]; // the second character in the path name, we use it for special functions later on 00893 httpGetLedOff = strncmp(dataStart,"button=0",9); 00894 httpGetLedOn = strncmp(dataStart,"button=1",9); 00895 00896 // for example, you could try this using netcat (nc): echo "GET /x" | nc 172.10.10.2 00897 if( (httpGetRoot==0) || (httpGetx==0) ) { 00898 n=n+sprintf(n+dataStart,"HTTP/1.1 200 OK\r\nServer: mbed-PPP-Blinky-v1\r\n"); // 200 OK header 00899 } else { 00900 n=n+sprintf(n+dataStart,"HTTP/1.1 404 Not Found\r\nServer: mbed-PPP-Blinky\r\n"); // 404 header 00901 } 00902 00903 n=n+sprintf(n+dataStart,"Content-Length: "); // http header 00904 contentLengthStart = n; // remember where Content-Length is in buffer 00905 n=n+sprintf(n+dataStart,"?????\r\n"); // leave five spaces for content length - will be updated later 00906 n=n+sprintf(n+dataStart,"Connection: close\r\n"); // close connection immediately 00907 n=n+sprintf(n+dataStart,"Content-Type: text/html; charset=us-ascii\r\n\r\n"); // http header must end with empty line (\r\n) 00908 nHeader=n; // size of HTTP header 00909 00910 if( httpGetRoot == 0 ) { 00911 // this is where we insert our web page into the buffer 00912 memcpy(n+dataStart,body,sizeof(body)); 00913 n = n + sizeof(body)-1; // one less than sizeof because we don't count the null byte at the end 00914 00915 } else if ( (httpGet5 == 'w') && (httpGet6 == 's') ) { // "ws" is a special page for websocket demo 00916 memcpy(n+dataStart,body,sizeof(body)); 00917 n = n + sizeof(body)-1; // one less than size 00918 *flags = TCP_FLAG_ACK | TCP_FLAG_PSH; // for a websocket page we do NOT close the connection 00919 led1=!led1; 00920 } 00921 00922 else { 00923 if (httpGetx == 0) { // the page request started with "GET /x" - here we treat anything starting with /x special: 00924 #define W3C_COMPLIANT_RESPONSE_NO 00925 // change the above to W3C_COMPLIANT_RESPONSE_YES if you want a W3C.org compliant HTTP response 00926 #ifdef W3C_COMPLIANT_RESPONSE_YES 00927 n=n+sprintf(n+dataStart,"<!DOCTYPE html><title>mbed PPP-Blinky</title>"); // html title (W3C.org required elements) 00928 n=n+sprintf(n+dataStart,"<body>%d</body>",ppp.responseCounter); // body = the http frame count 00929 #else 00930 if( httpGet6 == 'b' ) { // if the fetched page is "xb" send a meta command to let the browser continuously reload 00931 n=n+sprintf(n+dataStart, "<meta http-equiv=\"refresh\" content=\"0\">"); // reload loop - handy for benchmarking 00932 } 00933 // /x is a very short page, in fact, it is only a decimal number showing the http Page count 00934 n=n+sprintf(n+dataStart,"%d ",ppp.responseCounter); // not really valid html but most browsers and curl are ok with it 00935 #endif 00936 } else { 00937 // all other requests get 404 Not Found response with a http frame count - nice for debugging 00938 n=n+sprintf(n+dataStart,"<!DOCTYPE html><title>mbed PPP-Blinky</title>"); // html title (required element) 00939 n=n+sprintf(n+dataStart,"<body>Not Found</body>"); // not found message 00940 } 00941 } 00942 //add 00943 if(httpGetLedOn !=0) 00944 { 00945 led1 = 1; 00946 } 00947 00948 if(httpGetLedOff !=0) 00949 { 00950 led1 = 0; 00951 } 00952 #define CONTENTLENGTHSIZE 5 00953 char contentLengthString[CONTENTLENGTHSIZE+1]; 00954 snprintf(contentLengthString,CONTENTLENGTHSIZE+1,"%*d",CONTENTLENGTHSIZE,n-nHeader); // print Content-Length with leading spaces and fixed width equal to csize 00955 memcpy(dataStart+contentLengthStart, contentLengthString, CONTENTLENGTHSIZE); // copy Content-Length to it's place in the send buffer 00956 return n; // total byte size of our response 00957 } 00958 00959 /// Handle TCP data that is not an HTTP GET. 00960 /// This is handy when for example you want to use netcat (nc.exe) to talk to PPP-Blinky. 00961 /// This could also be a websocket receive event - especially if the first byte is 0x81 (websocket data push) 00962 int tcpResponse(char * dataStart, int len, int * outFlags) 00963 { 00964 int n=0; // number of bytes we have printed so far 00965 if (dataStart[0] == 0x81) { // check if this is a websocket push message 00966 char mask [4]; 00967 memcpy ( mask, dataStart+2, 4); // websocket messages are "masked", so first we obtain the 4-byte mask 00968 int websocketMessageSize = len - 6; // 1 byte prefix (0x81), 1 byte, 4 bytes mask = 6 bytes 00969 if((dataStart[1]&0x80)==0x80) // test if the mask bit is set, which means all data is xor'ed with the mask 00970 for (int i=0; i<websocketMessageSize; i++) dataStart[i+6]^= mask[i%4]; // unmask each byte with one of the mask bytes 00971 dataStart[1] = len-2; // add four extra bytes to the message length because we don't use mask bytes for the send 00972 memcpy(dataStart+2, "Got:",4); // insert our own text into the four mask bytes 00973 n = len; // our response size remains exactly the same length as what we received 00974 } else if ( (dataStart[0]==0x88) && (dataStart[1]==0x80) && (len == 6) ) { // test for a websocket close request 00975 n=2; // our close command is only two bytes long because we don't use the four mask bytes 00976 dataStart[1]=0; // we don't have mask bytes on 00977 } else { 00978 if ( len > 1 ) { // we assume a length of 1 is a keep-alive or push packet 00979 if (v1) putsWhileCheckingInput("TCP data received\n"); // all other tcp push packets 00980 } 00981 } 00982 return n; // total byte size of our response 00983 } 00984 00985 /// dump the TCP data to the debug serial port 00986 void dumpDataTCP(int outGoing) 00987 { 00988 #ifdef SERIAL_PORT_MONITOR_YES 00989 if (v2) { 00990 int packetLengthIp = __REV16(ppp.ip->lengthR ); // size of ip packet 00991 int headerSizeIp = 4 * ppp.ip->headerLength; // size of ip header 00992 ppp.tcpStart = ppp.ipStart + headerSizeIp; // calculate where the TCP header starts 00993 int headerSizeTcp = 4 * (ppp.tcp->offset); // tcp "offset" for start of data is also the header size 00994 ppp.tcpData = ppp.tcpStart + headerSizeTcp; // start of tcp data 00995 int tcpSize = packetLengthIp - headerSizeIp; // tcp size = size of ip payload 00996 int tcpDataSize = tcpSize - headerSizeTcp; // size of data block after TCP header 00997 char pbuf[80]; // local print buffer 00998 int n=0; 00999 checkPc(); 01000 n=n+sprintf(pbuf+n, outGoing ? "\x1b[34m" : "\x1b[30m" ); // VT100 color code, print black for incoming, blue for outgoing headers 01001 checkPc(); 01002 n=n+sprintf(pbuf+n, "IP:%d ipHeader:%d tcpHeader:%d tcpData:%d\n", packetLengthIp, headerSizeIp, headerSizeTcp, tcpDataSize); // 1 for more verbose 01003 if (n>70) putsWhileCheckingInput("n>pbuf overflow in dumpDataTCP()\n"); 01004 checkPc(); 01005 putsWhileCheckingInput( pbuf ); 01006 if (tcpDataSize > 0) { 01007 ppp.tcpData[tcpDataSize]=0; // insert a null after the data so debug printf stops printing after the data 01008 putsWhileCheckingInput( ppp.tcpData ); // print the tcp payload data 01009 putsWhileCheckingInput("\n"); 01010 } 01011 putsWhileCheckingInput( "\x1b[30m" ); // VT100 color code, print black 01012 } 01013 #endif 01014 } 01015 01016 /// handle an incoming TCP packet 01017 /// use the first few bytes to figure out if it's a websocket, an http request or just pure incoming TCP data 01018 void tcpHandler() 01019 { 01020 int packetLengthIp = __REV16(ppp.ip->lengthR ); // size of ip packet 01021 int headerSizeIp = 4 * ppp.ip->headerLength; // size of ip header 01022 ppp.tcpStart = ppp.ipStart + headerSizeIp; // calculate TCP header start 01023 int tcpSize = packetLengthIp - headerSizeIp; // tcp size = size of ip payload 01024 int headerSizeTcp = 4 * (ppp.tcp->offset); // tcp "offset" for start of data is also the header size 01025 char * tcpDataIn = ppp.tcpStart + headerSizeTcp; // start of data TCP data after TCP header 01026 int tcpDataSize = tcpSize - headerSizeTcp; // size of data block after TCP header 01027 01028 unsigned int seq_in = __REV(ppp.tcp->seqTcpR); 01029 unsigned int ack_in = __REV(ppp.tcp->ackTcpR); 01030 unsigned int ack_out = seq_in + tcpDataSize; 01031 unsigned int seq_out = ack_in; // use their version of our current sequence number 01032 01033 // first we shorten the TCP response header to only 20 bytes. This means we ignore all TCP option requests 01034 headerSizeIp=20; 01035 ppp.ip->headerLength = headerSizeIp/4; // ip header is 20 bytes long 01036 ppp.ip->lengthR = __REV(40); // 20 ip header + 20 tcp header 01037 //tcpSize = 20; // shorten total TCP packet size to 20 bytes (no data) 01038 headerSizeTcp = 20; // shorten outgoing TCP header size 20 bytes 01039 ppp.tcpStart = ppp.ipStart + headerSizeIp; // recalc TCP header start 01040 ppp.tcp->offset = (headerSizeTcp/4); 01041 char * tcpDataOut = ppp.tcpStart + headerSizeTcp; // start of outgoing data 01042 01043 int dataLen = 0; // most of our responses will have zero TCP data, only a header 01044 int flagsOut = TCP_FLAG_ACK; // the default case is an ACK packet 01045 01046 ppp.tcp->windowR = __REV16( 1200 ); // set tcp window size to 1200 bytes 01047 01048 // A sparse TCP flag interpreter that implements stateless TCP connections 01049 01050 switch ( ppp.tcp->flag.All ) { 01051 case TCP_FLAG_SYN: 01052 flagsOut = TCP_FLAG_SYN | TCP_FLAG_ACK; // something wants to connect - acknowledge it 01053 seq_out = seq_in+0x10000000U; // create a new sequence number using their sequence as a starting point, increase the highest digit 01054 ack_out++; // for SYN flag we have to increase the sequence by 1 01055 break; 01056 case TCP_FLAG_ACK: 01057 case TCP_FLAG_ACK | TCP_FLAG_PSH: 01058 if ( (ppp.tcp->flag.All == TCP_FLAG_ACK) && (tcpDataSize == 0)) return; // handle zero-size ack messages by ignoring them 01059 if ( (strncmp(tcpDataIn, "GET /", 5) == 0) ) { // check for an http GET command 01060 flagsOut = TCP_FLAG_ACK | TCP_FLAG_PSH; // we have data, set the PSH flag 01061 dataLen = httpResponse(tcpDataOut, &flagsOut); // send an http response 01062 } else { 01063 dataLen = tcpResponse(tcpDataOut,tcpDataSize, &flagsOut); // not an http GET, handle as a tcp connection 01064 if (dataLen > 0) flagsOut = TCP_FLAG_ACK | TCP_FLAG_PSH; // if we have any data set the PSH flag 01065 } 01066 break; 01067 case TCP_FLAG_FIN: 01068 case TCP_FLAG_FIN | TCP_FLAG_ACK: 01069 case TCP_FLAG_FIN | TCP_FLAG_PSH | TCP_FLAG_ACK: 01070 flagsOut = TCP_FLAG_ACK | TCP_FLAG_FIN; // set outgoing FIN flag to ask them to close from their side 01071 ack_out++; // for FIN flag we have to increase the sequence by 1 01072 break; 01073 default: 01074 return; // ignore all other packets 01075 } // switch 01076 01077 // The TCP flag handling is now done 01078 // first we swap source and destination TCP addresses and insert the new ack and seq numbers 01079 swapIpAddresses(); // swap IP source and destination addresses 01080 swapIpPorts(); // swap IP source and destination ports 01081 01082 ppp.tcp->ackTcpR = __REV( ack_out ); // byte reversed - tcp/ip messages are big-endian (high byte first) 01083 ppp.tcp->seqTcpR = __REV( seq_out ); // byte reversed - tcp/ip messages are big-endian (high byte first) 01084 01085 ppp.tcp->flag.All = flagsOut; // update the TCP flags 01086 01087 // recalculate all the header sizes 01088 tcpSize = headerSizeTcp + dataLen; // tcp packet size 01089 int newPacketSize = headerSizeIp + tcpSize; // calculate size of the outgoing packet 01090 ppp.ip->lengthR = __REV16 ( newPacketSize ); 01091 ppp.pkt.len = newPacketSize+4+2; // ip packet length + 4-byte ppp prefix (ff 03 00 21) + 2 fcs (crc) bytes bytes at the end of the packet 01092 01093 // the header is all set up, now do the IP and TCP checksums 01094 IpHeaderCheckSum(); // calculate new IP header checksum 01095 checkSumPseudoHeader( tcpSize ); // get the TCP pseudo-header checksum 01096 ppp.tcp->checksumR = 0; // before TCP checksum calculations the checksum bytes must be set cleared 01097 unsigned int pseudoHeaderSum=dataCheckSum(ppp.tcpStart,tcpSize, 0); // continue the TCP checksum on the whole TCP packet 01098 ppp.tcp->checksumR = __REV16( pseudoHeaderSum); // tcp checksum done, store it in the TCP header 01099 01100 dumpHeaderIP(1); // dump outgoing IP header before sending the frame 01101 dumpHeaderTCP(1); // dump outgoing TCP header before sending the frame 01102 dumpDataTCP(1); // dump outgoing TCP data before sending the frame 01103 if (0) wait_ms(45); // 45 ms delay before sending frame - a typical internet delay time 01104 sendPppFrame(); // All preparation complete - send the TCP response 01105 if(0) dumpPPPFrame(); // set to 1 to dump transmitted ppp frame 01106 memset(ppp.pkt.buf+44,0,500); // flush out traces of previous data that we may scan for 01107 } 01108 01109 /// handle an incoming TCP packet 01110 void TCPpacket() 01111 { 01112 dumpHeaderIP(0); // dump incoming packet IP header 01113 dumpHeaderTCP(0); // dump incoming packet TCP header 01114 dumpDataTCP(0); // dump incoming packet data 01115 tcpHandler(); 01116 } 01117 01118 /// handle the remaining IP protocols by ignoring them 01119 void otherProtocol() 01120 { 01121 debugPrintf("Other IP protocol"); 01122 } 01123 01124 /// process an incoming IP packet 01125 void IPframe() 01126 { 01127 int protocol = ppp.ip->protocol; 01128 switch (protocol) { 01129 case 1: 01130 ICMPpacket(); 01131 break; 01132 case 2: 01133 IGMPpacket(); 01134 break; 01135 case 17: 01136 UDPpacket(); 01137 break; 01138 case 6: 01139 TCPpacket(); 01140 break; 01141 default: 01142 otherProtocol(); 01143 } 01144 } 01145 01146 /// respond to LCP (line configuration protocol) configuration request) by allowing no options 01147 void LCPconfReq() 01148 { 01149 debugPrintf("LCP Config "); 01150 if ( ppp.lcp->lengthR != __REV16(4) ) { 01151 ppp.lcp->code=4; // allow only "no options" which means Maximum Receive Unit (MRU) is default 1500 bytes 01152 debugPrintf("Reject\n"); 01153 sendPppFrame(); 01154 } else { 01155 ppp.lcp->code=2; // ack zero conf 01156 debugPrintf("Ack\n"); 01157 sendPppFrame(); 01158 debugPrintf("LCP Ask\n"); 01159 ppp.lcp->code=1; // request no options 01160 sendPppFrame(); 01161 } 01162 } 01163 01164 /// handle LCP acknowledge packets by ignoring them 01165 void LCPconfAck() 01166 { 01167 debugPrintf("LCP Ack\n"); 01168 } 01169 01170 /// handle LCP end (disconnect) packets by acknowledging them and by setting ppp.online to false 01171 void LCPend() 01172 { 01173 ppp.lcp->code=6; // end 01174 sendPppFrame(); // acknowledge 01175 ppp.online=0; // start hunting for connect string again 01176 pppInitStruct(); // flush the receive buffer 01177 debugPrintf("LCP End (Disconnect from host)\n"); 01178 } 01179 01180 /// respond to other LCP requests by ignoring them 01181 void LCPother() 01182 { 01183 debugPrintf("LCP Other\n"); 01184 dumpPPPFrame(); 01185 } 01186 01187 /// process incoming LCP packets 01188 void LCPframe() 01189 { 01190 int code = ppp.lcp->code; 01191 switch (code) { 01192 case 1: 01193 LCPconfReq(); 01194 break; // config request 01195 case 2: 01196 LCPconfAck(); 01197 break; // config ack 01198 case 5: 01199 LCPend(); 01200 break; // end connection 01201 default: 01202 LCPother(); 01203 } 01204 } 01205 01206 /// discard packets that are not IP, IPCP, or LCP 01207 void discardedFrame() 01208 { 01209 if (v0) debugPrintf("Frame is not IP, IPCP or LCP: %02x %02x %02x %02x\n", ppp.pkt.buf[0],ppp.pkt.buf[1],ppp.pkt.buf[2],ppp.pkt.buf[3]); 01210 } 01211 01212 /// determine the packet type (IP, IPCP or LCP) of incoming packets 01213 void determinePacketType() 01214 { 01215 if ( ppp.ppp->address != 0xff ) { 01216 debugPrintf("Unexpected: PPP address != ff\n"); 01217 return; 01218 } 01219 if ( ppp.ppp->control != 3 ) { 01220 debugPrintf("Unexpected: PPP control != 3\n"); 01221 return; 01222 } 01223 unsigned int protocol = __REV16( ppp.ppp->protocolR ); 01224 switch ( protocol ) { 01225 case 0xc021: 01226 LCPframe(); 01227 break; // link control 01228 case 0x8021: 01229 IPCPframe(); 01230 break; // IP control 01231 case 0x0021: 01232 IPframe(); 01233 break; // IP itself 01234 default: 01235 discardedFrame(); 01236 } 01237 } 01238 01239 /// a sniffer tool to assist in figuring out where in the code we are having characters in the input buffer 01240 void sniff() 01241 { 01242 if ( pc.readable() ) putsWhileCheckingInput( "Sniff - Char available!\n" ); // if this prints anything it means there is a character in the serial receive buffer 01243 } 01244 01245 /// scan the PPP serial input stream for frame start markers 01246 void waitForPppFrame() 01247 { 01248 while(1) { 01249 sendUdpData(); // handle received characters from the DEBUG TERMINAL 01250 if ( ppp.rx.head != ppp.rx.tail ) { 01251 int oldTail = ppp.rx.tail; // remember where the character is located in the buffer 01252 int rx = pc_getBuf(); // get the character 01253 if (rx==FRAME_7E) { 01254 if (ppp.firstFrame) { // is this the start of the first frame start 01255 ppp.rx.rtail = ppp.rx.tail; // update real-time tail with the virtual tail 01256 ppp.hdlc.frameStartIndex = ppp.rx.tail; // remember where first frame started 01257 ppp.firstFrame=0; // clear first frame flag 01258 } else { 01259 ppp.hdlc.frameEndIndex=oldTail; // mark the frame end character 01260 processPPPFrame(ppp.hdlc.frameStartIndex, ppp.hdlc.frameEndIndex); // process the frame 01261 ppp.rx.rtail = ppp.rx.tail; // update real-time tail with the virtual tail 01262 ppp.hdlc.frameStartIndex = ppp.rx.tail; // remember where next frame started 01263 break; 01264 } 01265 } 01266 } 01267 } 01268 } 01269 01270 /// Wait for a dial-up modem connect command ("CLIENT") from the host PC, if found, we set ppp.online to true, which will start the IP packet scanner. 01271 // Note: a 0x7E in the input stream (ppp start of frame character) will also set ppp.online to true - see checkPc(); 01272 void waitForPcConnectString() 01273 { 01274 while(ppp.online == 0) { 01275 checkPc(); // gather received characters 01276 // search for Windows Dialup Networking "Direct Connection Between Two Computers" expected connect string 01277 char * found1 = strstr( (char *)ppp.rx.buf, "CLIENT" ); 01278 if (found1 != NULL) { 01279 // respond with Windows Dialup networking expected "Direct Connection Between Two Computers" response string 01280 if (v0) debugPrintf("Connected: Found connect string \"CLIENT\", sent \"CLIENTSERVER\"\n"); 01281 pc.puts("CLIENTSERVER"); 01282 ppp.online=1; // we are connected - set flag so we stop looking for the connect string 01283 checkPc(); 01284 } 01285 } 01286 } 01287 01288 /// Initialize PPP data structure and set serial port(s) baud rate(s) 01289 void initializePpp() 01290 { 01291 int i = 0; 01292 while(1) 01293 { 01294 debugPrintf("1111111111111\n"); 01295 i ++; 01296 if(i==010000) 01297 break; 01298 } 01299 debugBaudRate(115200); // baud rate for (optional) debug serial port 01300 debugPrintf("\x1b[2J\x1b[H\x1b[30m"); 01301 wait_ms(200); // a brief wait so a human can see the reset event 01302 debugPrintf("mbed PPP-Blinky HTTP & WebSocket server ready :)\n"); // VT100 codes for clear_screen, home, black_text - Tera Term is a handy VT100 terminal 01303 01304 pppInitStruct(); // initialize all the variables/properties/buffers 01305 pc.baud(115200); // pc serial port acting as a dial-up modem - for PPP traffic 01306 pc.attach(&pppReceiveHandler, RawSerial::RxIrq); // set up serial port receive interrupt handler 01307 } 01308 01309 01310 void t_print(int i, char* process) 01311 { 01312 01313 while(1) 01314 { 01315 printf("%s\n", *process); 01316 i++; 01317 led1=!led1; 01318 wait(0.5); 01319 if(i==i*3) 01320 break; 01321 } 01322 } 01323 void l_off(void) 01324 { 01325 led1 = 0; 01326 } 01327 01328 void l_on(void) 01329 { 01330 led1 = 1; 01331 } 01332 void led_flash(void) 01333 { 01334 int num = 0; 01335 for(num = 0; num < 20; num ++) 01336 { 01337 led1= !led1; 01338 wait(1); 01339 } 01340 }
Generated on Wed Jul 13 2022 22:24:08 by 1.7.2