-
-
Save abp/3007001 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* 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 size), for example. | |
| The array is declared as a 16-bit (short) array to allow shorter | |
| indexing of 16-bit data from the packet headers. 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. | |
| */ | |
| short s[70]; | |
| /* 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. The initialization of 'l' will | |
| produce a compiler warning, but the code works nevertheless. | |
| */ | |
| *l = s; | |
| /* 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 for(;;) | |
| statement does. | |
| */ | |
| for(;;) { | |
| /* 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 's') and at most 140 | |
| bytes. | |
| */ | |
| read(0, s, 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. | |
| In this code, we do only a limited form of sanity checking. We | |
| check that the packet is indeed an ICMP packet by inspecting | |
| the correct field in the IP header (we must mask out the TTL | |
| field in the IP header to reach it) and by looking at the type | |
| code field in the ICMP header. The ICMP type must be ICMP ECHO. | |
| The IP protocol field is found at byte 9 in the IP header, and | |
| should be 1. We get to this field by indexing four 16-bit words | |
| into the 's' array, masking out the high 8 bits (65280 = | |
| 0xff00) and seing if these high bits are 1 (256 = 1 << 8). The | |
| ICMP type field is found 20 bytes into the IP/ICMP header, and | |
| should be 8. Since we use 16-bit indexing, we find it at s[10]. | |
| */ | |
| if((s[4] & 65280) == 256 & | |
| s[10] == 8) { | |
| /* We now set the ICMP type field to be an ECHO_REPLY packet | |
| type. This is what the ping command expects in return after | |
| sending an ICMP ECHO packet. | |
| */ | |
| s[10] = 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 's' 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, s, 140); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment