Send an arbitrary Ethernet frame using libpcap
Content |
Tested on |
Debian (Lenny, Squeeze) |
Ubuntu (Lucid) |
Objective
To send an arbitrary Ethernet frame using libpcap
Background
Ethernet is a link layer protocol. Most networking programs interact with the network stack at the transport layer or above, so have no need to deal with Ethernet frames directly, but there are some circumstances where interaction at a lower level may be necessary. These include:
- implementation of Ethernet-based protocols that are not built in to the network stack, and
- production of malformed or otherwise non-standard frames for testing purposes.
Scenario
Suppose that you wish to send an ARP request for a given IP address from a given Ethernet interface. You wish to use libpcap to perform the sending.
(ARP is the Address Resolution Protocol. It is used when a host needs to send a datagram to a given IP address, but does not know which MAC address corresponds to that IP address. It is described in RFC 826.)
Method
Overview
The method described here has five steps:
- Select the required EtherType.
- Construct the Ethernet frame.
- Obtain a PCAP descriptor by calling
pcap_open_live
. - Send the Ethernet frame by calling
pcap_inject
. - Close the PCAP descriptor by calling
pcap_close
.
The following header files are used:
Header | Used by |
---|---|
<stdio.h> |
fprintf |
<stdlib.h> |
exit |
<pcap.h> |
pcap_open_live , pcap_inject , pcap_close , pcap_perror
|
Be aware that:
- Not all network devices are Ethernet interfaces, or use an Ethernet-compatible frame format, or support packet injection using libpcap.
- Although a link-layer header must be supplied, libpcap does not promise to use it as-is: both the source address and the EtherType are at risk of being altered.
Programs that send raw packets, using this or any other method, are likely to require elevated privileges in order to run.
Select the required EtherType
The EtherType of an Ethernet frame specifies the type of payload that it contains. There are several sources from which EtherTypes can be obtained:
- On Linux-based systems the header file
<linux/if_ether.h>
provides constants for most commonly-used EtherTypes. Examples includeETH_P_IP
for the Internet Protocol (0x8000
),ETH_P_ARP
for the Address Resolution Protocol (0x0806
) andETH_P_8021Q
for IEEE 802.1Q VLAN tags (0x8100
). - The IEEE maintains the definitive list of registered EtherTypes.
- A semi-official list is maintained by IANA.
If you need an EtherType for experimental or private use then the values 0x88b5
and 0x88b6
have been reserved for that purpose.
Construct the Ethernet frame
Frames sent using libpcap must:
- have a link-layer header (there is no option for this to be added automatically), and
- be presented to libpcap as a single, contiguous block of memory (there is no equivalent of the scatter/gather capability provided by readmsg and sendmsg).
See the example program below for how this might be done in the specific case where you want to send an ARP request. Be aware that:
- Most network protocols require that multi-byte values be converted to network byte order.
- Structures may have padding added by the compiler (although ones provided by system headers ought to be safe).
- C and C++ place restrictions on when pointer casts can be safely used to convert data from one type to another.
You will probably need to know the MAC address of the interface from which the packet will be sent. On Linux-based systems this can be obtained using the ioctl
command SIOCGIFHWADDR
. See the microHOWTO Get the MAC address of an Ethernet interface in C using SIOCGIFHWADDR for details.
As noted previously, libpcap does not provide guarantee that the link-layer header that is sent will be identical to the one that was provided.
Obtain a PCAP descriptor by calling pcap_open_live
To access a network interface via libpcap it is necessary to have an open packet capture descriptor. This is a pointer of type pcap_t*
and can be obtained by calling pcap_open_live
:
char pcap_errbuf[PCAP_ERRBUF_SIZE]; pcap_errbuf[0]='\0'; pcap_t* pcap=pcap_open_live(if_name,96,0,0,pcap_errbuf); if (pcap_errbuf[0]!='\0') { fprintf(stderr,"%s",pcap_errbuf); } if (!pcap) { exit(1); }
The first argument to pcap_open_live
is the name of the interface from which the Ethernet frame is to be sent, for example eth0
. (Remember that not all interfaces are suitable for sending Ethernet frames.)
The second, third and fourth arguments are the snapshot length, promiscuous mode flag and timeout. These control how packets are captured, and for the task in hand it is unimportant what values are used, but if you want to capture as well as send then you will need to ensure that they have been set appropriately (especially the snapshot length).
The last argument points to a buffer for returning error messages, which must be at least PCAP_ERRBUF_SIZE
bytes long. As suggested on the pcap_open_live
manpage, this has been set to the empty string before the function call then inspected afterwards in order to detect both warnings and errors.
Send the Ethernet frame by calling pcap_inject
Given a PCAP descriptor, frames can be sent by calling the function pcap_inject
:
if (pcap_inject(pcap,&req,sizeof(req))==-1) { pcap_perror(pcap,0); pcap_close(pcap); exit(1); }
The value returned by pcap_inject
is the number of bytes sent, or -1 if there was an error. In the latter case a
human-readable error message can be obtained using pcap_geterr
or (as in this example) printed using
pcap_perror
.
Close the PCAP descriptor by calling pcap_close
The PCAP descriptor should be closed once it is no longer needed:
pcap_close(pcap)
Example program
The following example program constructs and sends an ARP request using the method described above:
It can be compiled using the command:
gcc -lpcap -o send_arp send_arp.c
When invoked it takes two arguments, the name of the Ethernet interface and the (numeric) IP address to which the ARP request should be directed:
./send_arp eth0 192.168.0.83
Alternatives
Using an AF_PACKET socket
See: | Send an arbitrary Ethernet frame using an AF_PACKET socket in C |
On Linux-based systems an alternative way to send an Ethernet frame is to use an AF_PACKET
socket. This has some advantages over the use of libpcap:
- It allows packets to be written directly to a POSIX socket descriptor, making it possible to use facilities such as scatter/gather and non-blocking output, and providing compatibility with libraries like libevent that act on file descriptors.
- It offers a choice between having the link-layer header supplied by the sender or constructed by the network stack.
- It removes a layer of indirection, and the need for libpcap to be present at compile time or run time.
The main drawback of AF_PACKET
sockets their lack of portability. They are specific to Linux (version 2.2 and later), and for this reason they are not recommended where the use of libpcap (or a raw socket) is a viable alternative.
Using a raw socket
See: | Send an arbitrary IPv4 datagram using a raw socket in C |
Raw sockets differ from packet sockets in that they operate at the network layer as opposed to the link layer. For this reason they are limited to network protocols for which raw socket support has been explicitly built into the network stack, but they also have a number of advantages which result from operating at a higher level of abstraction:
- You can write code that will work with any suitable type of network interface.
- Routing and link-layer address resolution are handled for you.
- The network layer header is constructed for you unless you request otherwise.
- The raw socket API has been partially standardised by POSIX.
For these reasons, use of a raw socket is recommended unless you specifically need the extra functionality provided by working at the link layer.
Further reading
- PCAP(3) (libpcap manpage)