-------------------[ Readings ]------------------- In the first half of the class, we had a guest lecture about Activision's Call of Duty network servers and architecture. In the second half, we looked at IP Raw Sockets in Linux and Darwin/MacOS. -------------------[ Raw Sockets ]------------------- Code examples of raw sockets can be found in lab3/ directory. Raw sockets for IP packets in Linux are an interface for a userland program to receive and send full IP packets as byte buffers, rather than just the packet's TCP and UDP payloads. There are other OS interfaces that provide the same function for _receiving_ "raw" packets, such the Libpcap packet capture library used by Wireshark and tcpdump---but raw sockets are the primary was of _sending_ raw IP packets. Raw sockets are created with AF_INET and SOCK_RAW (instead of the more familiar SOCK_STREAM or SOCK_DGRAM). There is an additional argument you can use here to limit the packets you get from the socket: the protocol from /usr/include/netinet/in.h. Today and for Lab 3, we will be interested in IPPROTO_ICMP, hence our socket will be: sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); After this, we can read raw IP packets from the kernel by calling recvfrom() on this socket. (Or recv() would do, but in that case we won't be told which interface the packet came in on---whereas recvfrom() fill that information into the struct sockaddr_in passed to it). See icmp4-rawrecv.c for example code. We must tell the raw socket that we intend to write fully constructed IP packets into it. We do so by setting the socket option IP_HDRINCL: const int on = 1; setsockopt (sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)); Now we can send packets with sendto(). Note that sendto() needs to be passed a pointer to a struct sockaddr_in with the IP address of the correct interface---otherwise, the packet may be sent on lo, or may refuse to go out. Raw sending often requires specifying the interface with either its IP directly or with the interface number, because the kernel's normal routing and connection tracking doesn't apply to it. See icmp4-rawsend.c for details (adapted from icmp4.c example at http://www.pdbuchan.com/rawsock/rawsock.html). ----------------------------[ Demo ]----------------------------- On one of my CS computers (where I have root; otherwise sending or receiving via raw sockets is off limits), I have icmp4-rawsend.c with SRC_IP of 8.8.8.8 and DST_IP of cs60.cs.dartmouth.edu . So when I run the icmp4-rawsend (as root) on that machine, on cs60.cs.dartmouth.edu I see this: # tcpdump -v -i eth0 -n icmp // -v adds verbosity so that we see more packet details 18:32:35.073855 IP (tos 0x0, ttl 255, id 0, offset 0, flags [DF], proto ICMP (1), length 32) 8.8.8.8 > 129.170.213.53: ICMP echo request, id 1000, seq 0, length 12 18:32:35.074728 IP (tos 0x0, ttl 64, id 18405, offset 0, flags [none], proto ICMP (1), length 32) 129.170.213.53 > 8.8.8.8: ICMP echo reply, id 1000, seq 0, length 12 18:33:11.291630 IP (tos 0x0, ttl 255, id 0, offset 0, flags [DF], proto ICMP (1), length 32) 8.8.8.8 > 129.170.213.53: ICMP echo request, id 1000, seq 0, length 12 18:33:11.292401 IP (tos 0x0, ttl 64, id 18568, offset 0, flags [none], proto ICMP (1), length 32) 129.170.213.53 > 8.8.8.8: ICMP echo reply, id 1000, seq 0, length 12 That is, 129.170.213.53 believes that it just got a ping request from Google's nameserver 8.8.8.8, and responds to it with a pong. In reality, though, these packets weren't sent by Google at all, but by my other machine! Their IP source is "spoofed" to be Google's, and the receiving machine doesn't know differently. This trick, alas, does not work so cleanly if there are NATs in the way, such as from Dartmouth Public or a home network with a NAT router. The results are still interesting and vary by the NAT implementation :) Note that some ICMP traffic just arrives out of the blue. For example, the switch my machine is plugged in is not supposed to send me unicast packets to 129.170.212.132, but does so, for whatever reason: 18:33:30.177975 IP (tos 0x0, ttl 52, id 10, offset 0, flags [none], proto ICMP (1), length 28) 46.166.148.176 > 129.170.212.132: ICMP echo reply, id 9844, seq 8330, length 8 And these packets aren't even prepared right: note the incorrect checksum and ICMP id and seq number being all zeros: 18:34:21.760972 IP (tos 0x0, ttl 51, id 62121, offset 0, flags [none], proto ICMP (1), length 48) 51.254.85.30 > 129.170.212.132: ICMP echo reply, id 0, seq 0, length 28 (wrong icmp cksum 0 (->ffff)!) I wonder what they are after. -----------------[ Raw sockets on MacOS/Darwin ]----------------- Raw sockets on Darwin are still powerful for sending constructed packets, but their functionality for receiving packets has been severely reduced compared to Linux. So the demo with receiving ICMP packets that hit the host (in lab3/icmp4-rawrecv-macos.c) works only partially: we never see echo requests hitting us, even through they are responded to by the kernel. On the other hand, echo replies are visible (as we saw in class), and so are other kinds of ICMP such as "time exceeded" (test this---you know a program that causes such packets to come your way!). Note that the names of the fields in the "ip" struct that describes the layout of the IP header are different between Darwin/MacOS and Linux. Check out /usr/include/netinet/ip.h on the respective systems, or compare field names in icmp4-rawrecv.c and icmp4-rawrecv-macos.c. This is another sign that raw sockets are, alas, not portable across systems. On MacOS and FreeBSD, it was decided, other methods such as Libpcap, would be the way to get raw TCP and UDP (and ICMP) packets. http://sock-raw.org/papers/sock_raw has excellent coverage of raw socket details across systems.