RealtimeCompLab2

Dependencies:   mbed

Fork of PPP-Blinky by Nicolas Nackel

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ppp-blinky.cpp Source File

ppp-blinky.cpp

Go to the documentation of this file.
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 }