/* The whole point of the twIP stack is to respond to pings. This is
   done by reading one IP packet at a time, hoping that it is an IP
   ping packet (no check is made!), changing the packet type to a ping
   reply packet, updating the ICMP checksum, swapping the IP source
   and destination addresses, and sending the packet back. That's
   it.
 */
 
/* This is the packet buffer. I chose the size of the array so that
   the maximum packet size that twIP would support would be the same
   as the largest twitter message: 140 bytes. This array could have
   been larger, and still make the code fit into a tweet: 576 bytes
   (the smallest maximum packet size a real IPv4 stack must support),
   or 1500 bytes (the maximum Ethernet siez), for example.
 */
char b[140];
 
/* The 's' variable is a pointer to an unsigned short, which is 16
   bits on most platforms. This is needed because the ICMP checksum,
   which is a 16 bit quantity, needs to be updated. It is shorter to
   use a 16-bit pointer to reach into the packet to update the
   checksum. The 's' variable is intialized to point to the first byte
   of the packet buffer (the 'b' variable). This will produce a
   warning at compilation, but the code works.
 */
unsigned short *s = b;
 
/* The 'l' variable is a pointer to an int. The 'int' keyword can be
   omitted because C implitictly treats the variable as an int then
   (this is an old legacy of C). An int is 32 bits on many platforms,
   and this variable is used when swapping the two 32-bit IPv4
   addresses in the packet header.
 */
*l = b;
 
/* The 't' variable is a temporary int variable which is used when
   swapping IP addresses. The variable is implicitly defined as an int
   by the C compiler.
 */
 
t;
 
/* This is the main function. To save space, its type and arguments
   are omitted. This is valid C (but looks weird nowadays).
 */
main() {
 
  /* The program runs in an infinite loop. This is what the while(1)
     statement does.
   */
  while(1) {
 
    /* Here we read the IP packets. We read them from file descriptor
       0, which should correspond to STDIN_FILENO. This works only if
       we have redirected the input from the tun0 device file. We read
       the data into the packet buffer (variable 'b') and at most 140
       bytes.
    */
    read(0, b, 140);
 
    /* At this point, we should do a lot of sanity checking of the
       incoming packet. We *should* check its length (to see that it
       is at least longer than a packet header), we should check that
       it is a valid IPv4 packet header, we should check that it is an
       ICMP packet, and we should check that the ICMP type is ECHO,
       which is the type used for ping packets.
 
       But we don't do any of that, because doing so would make the
       code not fit inside a Twitter message. Instead, we just hope
       that the packet is valid, that it is an IP packet, and that it
       is an ICMP ECHO message.
 
       What we do is to set the packet type to ECHO_REPLY, which is
       0. If this is an ICMP packet, its packet type is found 20 bytes
       into the packet, at b[20].
    */
    b[20] = 0;
 
    /* Now that we've altered the packet, the ICMP checksum is no
       longer valid and we must update it. Fortunately, we did a very
       small change to the packet and we can simply update the
       checksum accordingly. The code below works only on
       little-endian machines.
 
       With the code below, there is still a small chance that the
       ICMP checksum update fails, however. If the checksum is 0xfff7
       or larger, we would really need to add 9 instead of 8. But the
       program wouldn't fit in a tweet if we'd check for this
       condition. And with a risk of only 9 out of 65536, we are
       willing to chance it this time.

       The ICMP checksum is found 22 bytes into the packet, but since
       we are using the 's' pointer, which is a 16-bit pointer, we
       must use 22/2 = 11 as the array index.
    */
    s[11] += 8;
 
    /* Next, we swap the IP destination and source addresses before
       sending the packet. Swapping the IP addresses will return the
       packet back to the sender of the ping packet. Since we only
       swap bytes in the IP header, we do not need to update the IP
       header checksum.
 
       We make use of the temporary variable 't' and the 32-bit
       pointer 'l'. The IP addresses are found 12 and 16 bytes into
       the packet, but since the 'l' pointer is a 32-bit pointer we
       need to use the indicies 3 and 4.
    */
    t = l[4];
    l[4] = l[3];
    l[3] = t;
 
    /* We are now done with manipulating the packet. If the packet was
       what we hoped it was - an incoming ping packet - we should now
       have a suitable ping reply in the packet buffer, and we can
       send the packet out again. We write the packet from the packet
       buffer (the 'b' variable) and to file descriptor 1. This file
       descriptor is the STDOUT_FILENO, which we have redirected to
       point to the /dev/tun0 file, and the corresponding tun
       device. We write 140 bytes - the maximum packet size that we
       support. The incoming packet may have been smaller than 140
       bytes, but most IP stacks handles packets that are longer than
       what their IP headers say. (This commonly is referred to as the
       Jon Postel quote "Be liberal in what you accept, and
       conservative in what you send.")
    
       Once the packet has been written to the file, the packet is
       sent to the network interface, and our ping has been
       successfully replied to.
    */
    write(1, b, 140);
  }
}
