C Language Examples of IPv4 and IPv6 Raw Sockets for Linux

I have recently been investigating raw socket programming in C for linux and I decided to provide a collection of routines I have prepared. The intention here is to be able to arbitrarily set the various parameters within a packet.

Rather than use command-line arguments, each example has hard-coded values, so you need to modify each example to suit your preferences.

You must run these as root to obtain raw sockets. You don't need pcap.

An implementation of all these routines with a GUI (using GTK+) can be found here.

IPv4

Three combinations of the Domain, Type, and Protocol arguments are shown here. There are other possible combinations you could try. The packet parameters that can be modified are determined by which combination you choose.

In the Table 1 examples below, we tell the kernal the IP header is included (by us) by using setsockopt() and the IP_HDRINCL flag, and we can modify all values within the packet, but the kernal fills out the layer 2 (data link) information (source and next-hop MAC addresses) for us.

Table 1: sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
The kernel fills out layer 2 (data link) information (MAC addresses) for us.
tcp4.c Send SYN packet (an example with no TCP data).
get4.c Send HTTP GET (an example with TCP data) (note).
icmp4.c Send ICMP Echo Request with data.
udp4.c Send UDP packet with data.

In the Table 2 examples, we fill out all values, including the layer 2 (data link) information (source and next-hop MAC addresses). To do this, we must know the MAC address of the router/host the frames will be routed to next (more explanation), as well as the MAC address of the network interface ("network card") we're sending the packet from.

Table 2: sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL));
We provide layer 2 (data link) information. i.e., we specify ethernet frame header with MAC addresses.
tcp4_ll.c Send SYN packet (an example with no TCP data).
get4_ll.c Send HTTP GET (an example with TCP data) (note).
icmp4_ll.c Send ICMP Echo Request with data.
ping4_ll.c Send ICMP Echo Request with data and receive reply. i.e., ping
udp4_ll.c Send UDP packet with data.

In the Table 3 examples, we fill out all values, but only including the destination (i.e., next-hop) layer 2 (data link) information (not source MAC address). This is called a "cooked packet." To do this, we must know the MAC address of the router/host the frames will be routed to next (more explanation).

Table 3: sd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
We provide a "cooked" packet with destination MAC address in struct sockaddr_ll.
tcp4_cooked.c Send SYN packet (an example with no TCP data).
get4_cooked.c Send HTTP GET (an example with TCP data) (note).
icmp4_cooked.c Send ICMP Echo Request with data.
udp4_cooked.c Send UDP packet with data.

To learn the next-hop's MAC address for use in the Table 2 and 3 examples above, you must use the Address Resolution Protocol (ARP). I have included an example which sends an ARP request ethernet frame as well as an example that receives an ARP reply ethernet frame. Additionally, I have included some router solicitation and advertisement routines.

Table 4: Miscellaneous
arp.c Send an ARP request ethernet frame.
receive_arp.c Receive an ARP reply ethernet frame.
rs4.c Send a router solicitation.
ra4.c Send a router advertisement.
receive_ra4.c Receive a router advertisement.
tr4_ll.c TCP/ICMP/UDP traceroute

Table 5 below provides some examples of packet fragmentation. The first file, called "data", contains a list of numbers. The following three routines use it as data for the upper layer protocols. Feel free to provide to the routines your own data in any manner you prefer.

Table 5: Fragmentation
data 12390-byte file to use as upper layer protocol data
tcp4_frag.c Send TCP packet with enough data to require fragmentation.
icmp4_frag.c Send ICMP packet with enough data to require fragmentation.
udp4_frag.c Send UDP packet with enough data to require fragmentation.

Table 6 below presents examples of packets with IP and TCP options.

Table 6: IP and TCP Options
tcp4_maxseg.c Send TCP packet with a TCP option which sets maximum segment size.
tcp4_maxseg_tsopt.c Send TCP packet with two TCP options: set maximum segment size, and provide time stamp.
tcp4_maxseg-timestamp.c Send TCP packet with IP option to send time stamp, and TCP option to set maximum segment size.
tcp4_maxseg-security.c Send TCP packet with security IP option and TCP option to set maximum segment size.
tcp4_2ip-opts_2tcp_opts.c Send TCP packet with two IP options and two TCP options.

IPv6

In IPv6, we have less options at our disposal for modifying packet values (see RFC 3542 and RFC 3493). In particular, IPv6 has no equivalent to using setsockopt() with the IP_HDRINCL flag (see Table 1 in IPv4 section above). Without doing something special (using neighbor discovery), we can only change the hop limit and traffic class to arbitrary values. Neighbor discovery is the IPv6 replacement for ARP in IPv4.

Before we try some neighbor discovery, let's take a quick look at a couple of examples where we don't use neighbor discovery, and thus can only change the hop limit and traffic class values in the IPv6 header.

You can use either the ancillary data method, or a call to setsockopt() with option level IPPROTO_IPV6 and option names IPV6_TCLASS, IPV6_UNICAST_HOPS, or IPV6_MULTICAST_HOPS. Note that changes made to the properties of the socket with setsockopt() will remain in effect for all packets sent through the socket, whereas ancillary data is associated with a particular packet.

Examples icmp6_ancillary1.c and icmp6_ancillary2.c use the bind() function to bind the socket to the source IP address. Example icmp6_ancillary3.c sets the source IP address using ancillary data. In either case, the supplied source address must actually be assigned to the interface or else the sendto() call will fail and the packet won't be sent.

Table 7: Without Using Neighbor Discovery (ND)
Ancillary data method
icmp6_ancillary1.c Change hop limit using ancillary data. Source IP address set using bind().
icmp6_ancillary2.c Change hop limit and specify source interface using ancillary data. Source IP address set using bind().
icmp6_ancillary3.c Change hop limit, specify source interface, and source IP address using ancillary data.

If we wish to have the ability to change any parameter in the IPv6 header, we need to have the source and destination MAC addresses available (more explanation). In this case we have the same sort of options available to us as we did in Tables 2 and 3 above for IPv4. To recap, these are:

Table 2
  • sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL));
  • We provide layer 2 (data link) information. i.e., we specify ethernet frame header with MAC addresses.
Table 3
  • sd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
  • We provide a "cooked" packet with destination MAC address in struct sockaddr_ll.

The neighbor discovery process is used to obtain the MAC address of a link-local node's interface card (could be the MAC address of a link-local router or host's interface the frames will be routed through). First we send a neighbor solicitation with our MAC address to the target node, and then it replies with a neighbor advertisement that contains its MAC address. The neighbor solicitation is sent to the target node's solicited-node multicast address.

Some router discovery routines are also included. Router solicitations are issued by a host looking for local routers, and router advertisements are issued by routers announcing their presence on the LAN.

Table 8: Neighbor Discovery and Router Discovery
ns.c Send a neighbor solicitation.
na.c Send a neighbor advertisement (this example doesn't detect and respond to a solicitation).
receive_na.c Receive a neighbor advertisement and extract lots of info including MAC address.
rs6.c Send a router solicitation.
ra6.c Send a router advertisement (this example doesn't detect and respond to a solicitation).
receive_ra6.c Receive a router advertisement and extract lots of info including MAC address.

Now that we have used neighbor discovery to determine the MAC address of a link-local router or host, we can go ahead and modify all parameters within the ethernet frame.

Table 9: sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL));
tcp6_ll.c Send SYN packet (an example with no TCP data).
get6_ll.c Send HTTP GET (an example with TCP data) (note).
icmp6_ll.c Send ICMP Echo Request with data.
ping6_ll.c Send ICMP Echo Request with data and receive reply. i.e., ping
udp6_ll.c Send UDP packet with data.

As in the IPv4 examples of Table 3, in Table 10 below we fill out all values, but only including the destination (i.e., next hop) layer 2 (data link) information and not the source MAC address. This is called a "cooked packet." As in Table 9 above, we must know the MAC address of the router/host the frames will be routed to next (more explanation).

Table 10: sd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
tcp6_cooked.c Send SYN packet (an example with no TCP data).
get6_cooked.c Send HTTP GET (an example with TCP data) (note).
icmp6_cooked.c Send ICMP Echo Request with data.
udp6_cooked.c Send UDP packet with data.

For the transition from IPv4 to IPv6, a mechanism of tunneling IPv6 over IPv4 (6to4) has been established. Table 11 presents some 6to4 examples.

Table 11: Tunneling IPv6 over IPv4 (6to4) - RFC 4213
tcp6_6to4.c Send SYN packet (an example with no TCP data).
get6_6to4.c Send HTTP GET (an example with TCP data) (note).
icmp6_6to4.c Send ICMP Echo Request with data.
ping6_6to4.c Send ICMP Echo Request with data and receive reply. i.e., ping
udp6_6to4.c Send UDP packet with data.

The following table provides some examples of packet fragmentation. In IPv6, fragmentation requires the introduction of a fragment extension header. The first file, called "data", contains a list of numbers, and the following routines use it as data for the upper layer protocols. Feel free to provide to the routines your own data in any manner you prefer.

Table 12: Fragmentation - Section 4.5 of RFC 2460
data 12390-byte file to use as upper layer protocol data
tcp6_frag.c Send TCP packet with enough data to require fragmentation.
icmp6_frag.c Send ICMP packet with enough data to require fragmentation.
udp6_frag.c Send UDP packet with enough data to require fragmentation.
tcp6_6to4_frag.c Send IPv6 TCP packet through IPv4 tunnel with enough data to require fragmentation.
icmp6_6to4_frag.c Send IPv6 ICMP packet through IPv4 tunnel with enough data to require fragmentation.
udp6_6to4_frag.c Send IPv6 UDP packet through IPv4 tunnel with enough data to require fragmentation.

Table 13 below provides examples of sending TCP packets with TCP options.

Table 13: TCP Options - Section 3.1 of RFC 793
tcp6_maxseg.c Send TCP packet with a TCP option which sets maximum segment size.
tcp6_maxseg_tsopt.c Send TCP packet with two TCP options: set maximum segment size, and provide time stamp.

Table 14 provides an example of sending a TCP packet with a hop-by-hop extension header and enough TCP data to require fragmentation. The hop-by-hop header contains two options: a router alert, and a PadN padding option which is required to pad to the appropriate boundary. For demonstration purposes here, the router alert option provides a value which is currently unassigned by IANA (see Section 2.1 of RFC 2711).

Table 14: Hop-by-Hop Extension Header - Section 4.3 of RFC 2460
data 12390-byte file to use as upper layer protocol data
tcp6_hop_frag.c Send TCP packet with a hop-by-hop extension header with router alert option and enough data to require fragmentation.

The following few tables give examples of the authentication extension header (AH) and the encapsulating security payload extension header (ESP header). The AH provides data origin and integrity authentication. The ESP header provides confidentiality, data origin and integrity authentication, an anti-replay service, and limited traffic flow confidentiality. The main difference between the AH and ESP headers is the extent of coverage. Specifically, ESP does not protect any IP header fields unless those fields are encapsulated by ESP (tunnel mode). The respective RFCs (given below) explain the encryption requirements; no encryption is done here in the examples. For more details on how to use AH and ESP in various network environments, see the security architecture document RFC 4301. The IP security (IPsec) protocols (AH and ESP) can be used in either transport mode or tunnel mode. Section 5.1.2.2 of RFC 4301 states that in tunnel mode, the inner extension headers, if any, are not copied to become outer extension headers, although new outer extension headers can be created as desired.

Table 15 provides an example of sending a TCP packet with a hop-by-hop extension header, authentication extension header, and enough TCP data to require fragmentation. The hop-by-hop header contains two options: a router alert, and a PadN padding option which is required to pad to the appropriate boundary. For demonstration purposes here, the router alert option provides a value which is currently unassigned by IANA (see Section 2.1 of RFC 2711). Here, the authentication header carries a random bogus integrity check value (ICV) for demonstration; normally, this is computed as per Section 3 of RFC 2402. Since the authentication header can be used in transport or tunnel mode, an example is given of each.

Table 15: Authentication Extension Header - RFC 2402
data 12390-byte file to use as upper layer protocol data
tcp6_hop_auth-tr_frag.c Send TCP packet with a hop-by-hop extension header with router alert option, authentication extension header (in transport mode), and enough data to require fragmentation.
tcp6_hop_auth-tun_frag.c Send TCP packet with a hop-by-hop extension header with router alert option, authentication extension header (in tunnel mode), and enough data to require fragmentation.

Table 16 provides an example of sending a TCP packet with a hop-by-hop extension header, ecapsulating security payload (ESP) extension header, and enough TCP data to require fragmentation. The hop-by-hop header is the same as in Table 15. The authentication data portion of the ESP header is the same as the authentication data used in Table 15. Similar to the authentication header, the ESP header can be used in transport or tunnel mode, so an example is given of each.

Table 16: Encapsulating Security Payload (ESP) Extension Header - RFC 2406
data 12390-byte file to use as upper layer protocol data
tcp6_hop_esp-tr_frag.c Send TCP packet with a hop-by-hop extension header with router alert option, ESP extension header (in transport mode), and enough data to require fragmentation.
tcp6_hop_esp-tun_frag.c Send TCP packet with a hop-by-hop extension header with router alert option, ESP extension header (in tunnel mode), and enough data to require fragmentation.

Table 17 provides an example of sending a TCP packet with a hop-by-hop extension header with a router alert option, destination extension header (last) with an Identifier-Locator Network Protocol (ILNP) nonce option, and enough TCP data to require fragmentation. The hop-by-hop header is the same as in Table 15. Here "last" means a destination header that is to be processed only by the final destination node. This is relevent in terms of where in the packet the destination header is placed. A destination header can also be placed such that it is processed by devices specified within a routing header.

If you examine the code, you'll see that when multiple extension headers are introduced, a more generalized approach to chaining the headers (via Next Header fields) is going to be necessary, rather than long and awkward lists of "if" statments, as I have here.

Table 17: Destination Extension Header (last) - RFC 2460
data 12390-byte file to use as upper layer protocol data
tcp6_hop_dst_frag.c Send TCP packet with a hop-by-hop extension header, destination extension header (last), and enough data to require fragmentation.

Table 18 gives an example of a routing header. There are several possible types of routing header, specified in the Routing Type field of the header itself. Types 0 and 1 routing headers have been deprecated (IANA Parameters List). Here we use a type 3 routing header, which is a Source Routing Header for the routing protocol for low-power and lossy networks (RPL).

Table 18: Routing Extension Header (Type 3) - RFC 6554
data 12390-byte file to use as upper layer protocol data
tcp6_hop_route3_frag.c Send TCP packet with a hop-by-hop extension header, type 3 routing extension header, and enough data to require fragmentation.

P. David Buchan pdbuchan@yahoo.com

December 9, 2013