The complexity "under the hood" of sending a simple message
over the internet is amazing. There's a utility
called traceroute that shows how a singe
message (a packet) gets sent from one host to
another.
The following shows the route from here in the cs department
to a web-server in Austria.
bash$ traceroute www.risc.uni-linz.ac.at traceroute to www.risc.uni-linz.ac.at (193.170.37.138), 30 hops max, 40 byte packets 1 core-rt1.cs.usna.edu (131.122.88.251) 0.727 ms 0.266 ms 0.251 ms 2 internet-r1.usna.edu (192.190.228.1) 0.762 ms 0.613 ms 0.760 ms 3 sdp.usna.dren.net (138.18.45.5) 0.873 ms 0.917 ms 0.775 ms 4 ge-3-2-0.wae.dren.net (138.18.1.253) 4.452 ms 4.682 ms 4.507 ms 5 POS4-1.GW2.DCA8.ALTER.NET (157.130.49.1) 5.734 ms 5.233 ms 4.687 ms 6 0.so-3-0-0.XL1.DCA8.ALTER.NET (152.63.39.174) 5.051 ms 5.291 ms 15.561 ms 7 0.so-1-1-2.XL3.IAD8.ALTER.NET (152.63.38.129) 7.074 ms 6.022 ms 5.833 ms 8 0.xe-8-1-0.BR2.IAD8.ALTER.NET (152.63.37.149) 6.061 ms 0.xe-5-0-0.BR2.IAD8.ALTER.NET (152.63.41.229) 6.307 ms 0.xe-8-1-0.BR2.IAD8.ALTER.NET (152.63.37.149) 6.428 ms 9 xe-5-1-0.edge1.washinton4.level3.net (4.68.62.97) 6.296 ms 6.134 ms te-11-0-0.edge1.Washington4.level3.net (4.68.111.253) 5.800 ms 10 vlan89.csw3.Washington1.Level3.net (4.68.17.190) 8.472 ms 7.399 ms vlan79.csw2.Washington1.Level3.net (4.68.17.126) 6.963 ms 11 ae-62-62.ebr2.Washington1.Level3.net (4.69.134.145) 8.522 ms 8.857 ms ae-82-82.ebr2.Washington1.Level3.net (4.69.134.153) 7.327 ms 12 ae-43-43.ebr2.Frankfurt1.Level3.net (4.69.137.57) 96.266 ms 96.350 ms 98.449 ms 13 ae-82-82.csw3.Frankfurt1.Level3.net (4.69.140.26) 103.416 ms ae-72-72.csw2.Frankfurt1.Level3.net (4.69.140.22) 111.154 ms ae-62-62.csw1.Frankfurt1.Level3.net (4.69.140.18) 104.436 ms 14 ae-81-81.ebr1.Frankfurt1.Level3.net (4.69.140.9) 99.289 ms ae-61-61.ebr1.Frankfurt1.Level3.net (4.69.140.1) 94.689 ms ae-81-81.ebr1.Frankfurt1.Level3.net (4.69.140.9) 99.981 ms 15 ae-6-6.car1.Vienna1.Level3.net (4.69.135.33) 108.151 ms 107.961 ms 109.768 ms 16 ae-11-11.car2.Vienna1.Level3.net (4.69.135.30) 107.327 ms 108.842 ms 107.801 ms 17 vlan301.wien21.aco.net (212.73.203.18) 109.829 ms 111.990 ms 110.421 ms 18 vlan312.linz2.aco.net (193.171.15.6) 113.926 ms 112.485 ms 112.274 ms 19 vlan313.linz1.aco.net (193.171.15.9) 112.152 ms 183.114 ms 111.801 ms 20 jku-gw.edvz.uni-linz.ac.at (193.171.22.26) 113.047 ms 111.567 ms 112.857 ms 21 jkuc3hb1.edvz.uni-linz.ac.at (140.78.200.225) 114.143 ms 123.798 ms 122.666 ms 22 Router.RISC.Uni-Linz.AC.AT (140.78.222.31) 121.514 ms 123.248 ms 125.288 ms 23 crow.risc.uni-linz.ac.at (193.170.37.138) 122.484 ms 119.743 ms 114.332 msNotice that each host is described by a name and by four number "dotted" together.
We'll see that it works the same way with networking using the IP protocol: a host has a name (called a fully qualified domain name) and an address (a number called its IP address). The IP address is what you really need in order to communicate with a host. The name is, in and of itself, not sufficient. We require an external service called DNS to retreive an IP address from a fully qualified domain name --- in other words, DNS acts like the phonebook.
IP addresses themselves are 32-bit (unsigned) numbers. However, people seem to find things like 2205833223 to be less than catchy. So we tend to use "dotted quad" notation instead, which looks like this: 131.122.88.7. The way you interpret this is 131.122.88.7 → 131*2563 + 122*2562 + 88*256 + 7 = 2205833223. So you do need to know how to convert between these representations.
Oh, and there's a hitch: different machines represent a
4-byte integer like 2205833223 differently. It's called
byte order or "endianness". If A is an array
of four bytes, in little-endian byteorder you'd have the
integer
A[0] + A[1]*256 + A[2]*2562
+ A[3]*2563, while in big-endian
byteorder you'd have the
integer
A[0]*2563 +
A[1]*2562 + A[2]*256
+ A[3].
In the normal course of events this distinction doesn't
matter, because the machine consistently stores everything
the same way. However, when communicating across a
network that contains many different kinds of machines,
we need to agree on which byte order we want. The IP
protocol (remember, a protocol is an agreement about how
to communicate) specifies "network byte order" as
big-endian.
So we have that to worry about too.
uint32_t htonl(uint32_t hostlong); ← host byte order to network byte order (l = long = 32 bit) uint32_t ntohl(uint32_t netlong); ← network byte order to host byte order (l = long = 32 bit)The type
uint32_t is just unsigned int on our system.
It means 32-bit unsigned int. Two more that are useful for us are:
in_addr_t inet_addr(const char *cp); char *inet_ntoa(const struct in_addr in);They allow us to convert IP addresses back and forth from a string containing the dotted quad as text to 32-bit IP addresses (in network byte order, of course). The type
in_addr_t is, once again, just another name for
unsigned int on our system. So what about
argument struct in_addr in? The struct includes
the following member:
in_addr_t s_addr;
Below is a program that converts IP addresses in dotted quad
representation to IP addresses in their literal
repersentation as a single number. To compile it you must
include the -lnsl option, which links to a
system library that is not in gcc's default path. For
example:
gcc -o quad2int quad2in.c -lnsl
/*******************************************
* quad2int <dottedquad>
* Takes a dotted quad as a command-line
* argument and prints out the integer
* corresponding to the dotted quad. We
* convert that integer back to a dotted
* quad, just for run.
*******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
if (argc != 2) {
printf("%s <dottedquad>\n",argv[0]);
exit(1); }
/* Take string containing dotted quad
and get the IP address in network
byte order. */
in_addr_t a = inet_addr(argv[1]);
/* Convert to local host byte order
and print as an integer. */
printf("%u\n",ntohl(a));
/* Convert the address (in network
byte order) back to a dotted quad.
Note: it should be the same as
argv[1]! */
struct in_addr ia;
ia.s_addr = a;
printf("%s\n",inet_ntoa(ia));
return 0;
}
|