[转载]Libnet 1.1 tutorial for beginners
[ 2009-12-09 12:50:42 | 作者: dklkt ]
来源:http://repura.livejournal.com/23112.html
A few weeks ago I had to learn how to use libpcap and libnet for my computer networks class. Although google promptly pointed me to this great pcap tutorial, all I could find for libnet were tutorials for version 1.0 (not useful for 1.1 at all) and some very advanced stuff. Being an unexperienced programmer, it was pretty difficult for me to get started using only libnet's man pages and other people's code over the web. For that reason, I decided to write a humble libnet 1.1 tutorial for beginners in order to help other beginners, like me.
Index
Please refer to this post for the whole index, a download link for the examples, copyright info and acknowledgments.
Part 1:
Introduction
Compiling
Checking your packets
libnet_init() and libnet_destroy()
libnet_geterror()
Addresses
Keep on reading
Introduction
Before you try any of this, you need to have libnet installed and you need to know how to use su (or sudo). If you haven't done those things yet, do them now. I'll wait. Done? Good.
Here's what you need to do to start injecting packets:
1) Fire up libnet with libnet_init()
2) Build all headers, from the highest layer to the lowest. Say you'd like to build an UDP packet over IPv4 over Ethernet, with full control over all headers. You would need to call libnet_build_udp(), libnet_build_ipv4() and libnet_build_ethernet(), in this particular order.
3) Write the packet with libnet_write().
4a) Clear the packet with libnet_clear_packet(). Go back to 2) and write a different packet.
4b) Go back to 2) and update the packet using the same build functions, but feeding them the tags they returned on the last call.
5) Clean up with libnet_destroy().
Understanding that logic is the hardest part. Now all you need to do is take a closer look at each function. man libnet-functions.h and man libnet-headers.h are going to be your best friends at this point.
Compiling
To compile the following examples and also your own programs, you'll need to at least link with "-lnet". For reference, here's what I use:
gcc -ggdb -Wall `libnet-config --defines` `libnet-config --libs` example.c -o example
Checking your packets
After you are injecting some packets into your network, you'll probably want to look at them and check whether they are being built as expected. For that purpose, you'll need a sniffer. I use tcpdump to capture traffic and sometimes wireshark (with my regular non-root account) to take a look at those captures. You can use whatever works best for you, though.
libnet_init() and libnet_destroy()
libnet-functions.h gives us the prototype for libnet_init():
libnet_t * libnet_init (int injection_type, char *device, char *err_buf)
From last to first: err_buf is a string which will hold an error message if something goes wrong. device is the device's name (as in "eth0") or its IP address (as in "10.0.0.1"). If device is set to NULL, libnet will try to find a device for you. injection_type is the injection type, as in "from the link layer up" or "from the network layer up". We'll use LIBNET_RAW4 (IPv4 and above) and LIBNET_LINK (link layer and above).
Let's try it out:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 1: init.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
int main() {
libnet_t *l; // the libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Or we can provide a device name or IP address as an argument:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 2: init_devname.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
int main(int argc, char **argv) {
libnet_t *l; // the libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
if ( argc == 1 ) {
fprintf(stderr,"Usage: %s device\n", argv[0]);
exit(EXIT_FAILURE);
}
l = libnet_init(LIBNET_RAW4, argv[1], errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Please note that we will need the libnet context (l in those examples) for almost every libnet function we call.
libnet_geterror()
When calling libnet_init(), you need a string (errbuf) to learn more about an error. After libnet_init() has been successfully called, though, you will get your error messages from a different source:
char * libnet_geterror (libnet_t *l)
All it needs is the context. Call it when you want to know more about the last error you've got.
Addresses
If you are going to handle IPv4 and Ethernet addresses, you will be dealing with a u_int32_t (unsigned 32 bits [4 bytes] integer) for each IP address and a u_int8_t[6] array (unsigned 8 bits [1 byte] integer) for Ethernet. When you get one of this addresses from libnet, you will get them in network byte order. Even if your architecture stores integers in little-endian order (e.g., x86 and x86_64), you will get your addresses in memory with most significant bytes first (lower addresses) and least significant bytes last (higher addresses). Hopefully, the next example will illustrate that.
The functions you need for dealing with IPv4 address to string, string to IPv4 address and string to Ethernet address are:
char* libnet_addr2name4 (u_int32_t in, u_int8_t use_name)
u_int32_t libnet_name2addr4 (libnet_t *l, char *host_name, u_int8_t use_name)
u_int8_t* libnet_hex_aton (int8_t * s, int * len)
libnet_addr2name4() will take the 4 bytes address "in" and return a string with its dotted decimal representation (e.g., 192.168.0.1) if use_name == LIBNET_DONT_RESOLVE, or its DNS name (e.g., google.com) if use_name == LIBNET_RESOLVE.
libnet_name2addr4() will do the exact opposite of libnet_addr2name4(), and will also need the libnet context as its first argument.
libnet_hex_aton() will take a string (int8_t* == char*) of two digits hexadecimal numbers separated by colons (e.g., 00:30:0A:67:A6:5C) and return that address in a u_int8_t array. The array's length is stored in "len" (for Ethernet, it's usually 6). As we can see on man libnet-functions.h, libnet_hex_aton() implicitly calls malloc() and that memory needs to be freed after you are done with it. Remember this.
To accomplish the opposite effect of libnet_hex_aton() you can call printf with "%02X" for each byte, separating them with colons. We'll do that in the following example.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 3: addr.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; // the libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
char ip_addr_str[16], mac_addr_str[18];
u_int32_t ip_addr;
u_int8_t *ip_addr_p, *mac_addr;
int i, length; // for libnet_hex_aton()
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* IP address */
printf("IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr != -1 ) {
/*
* ip_addr is ready to be used in a build function.
* We'll print its contents to stdout to check if everything
* went fine.
*/
// libnet_name2addr4 returns the address in network order (big
// endian).
ip_addr_p = (u_int8_t*)(&ip_addr);
/* Check your system's endianess: */
//printf("ip_addr: %08X\n", ip_addr);
//printf("ip_addr_p: %02X%02X%02X%02X\n", ip_addr_p[0],\
// ip_addr_p[1], ip_addr_p[2], ip_addr_p[3]);
printf("Address read: %d.%d.%d.%d\n", ip_addr_p[0],\
ip_addr_p[1], ip_addr_p[2], ip_addr_p[3]);
/* This would output the same thing, but I wanted to show you
* how the address is stored in memory. */
//printf("Address read: %s\n", libnet_addr2name4(ip_addr,\
// LIBNET_DONT_RESOLVE));
}
else
fprintf(stderr, "Error converting IP address.\n");
/* MAC address */
printf("MAC address: ");
scanf("%17s", mac_addr_str);
mac_addr = libnet_hex_aton((int8_t*)mac_addr_str, &length);
if (mac_addr != NULL) {
/*
* mac_addr is ready to be used in a build function.
* We'll print its contents to stdout to check if everything
* went fine.
*/
printf("Address read: ");
for ( i=0; i < length; i++) {
printf("%02X", mac_addr[i]);
if ( i < length-1 )
printf(":");
}
printf("\n");
// Remember to free the memory allocated by libnet_hex_aton()
free(mac_addr);
}
else
fprintf(stderr, "Error converting MAC address.\n");
libnet_destroy(l);
return 0;
}
/* EOF */
Here you can see one of many casting tricks you might need in the future. On line 42, we'll cast &ip_addr into a u_int8_t* and store it in ip_addr_p. We are turning what would be a pointer to a 4 byte integer into a pointer to an array of 4 single bytes. The reason we do that is so that even if your system uses little-endian integers, we'll be able to read the bytes in the correct order (most significant first). If you are on a PC, try uncommenting lines 44-46; you should get the same thing except that all bytes are swapped (2 hex digits == 1 byte). You should also note that we could have accomplished the same result with libnet_addr2name4(). Uncomment lines 53-54 to check.
It may also be very useful to get our own IP and MAC addresses from libnet. For that, we'll need:
u_int32_t libnet_get_ipaddr4 (libnet_t *l)
libnet_ether_addr * libnet_get_hwaddr (libnet_t *l)
No explanation needed, except for the libnet_ether_addr type:
struct libnet_ether_addr {
u_char ether_addr_octet[6]; /* Ethernet address */
};
It's just a struct with an array of 6 unsigned bytes like the one we used above.
Here's an example of their usage:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 4: get_own_addr.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; // libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
u_int32_t ip_addr;
struct libnet_ether_addr *mac_addr;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
ip_addr = libnet_get_ipaddr4(l);
if ( ip_addr != -1 )
printf("IP address: %s\n", libnet_addr2name4(ip_addr, LIBNET_DONT_RESOLVE));
else
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
mac_addr = libnet_get_hwaddr(l);
if ( mac_addr != NULL )
printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",\
mac_addr->ether_addr_octet[0],\
mac_addr->ether_addr_octet[1],\
mac_addr->ether_addr_octet[2],\
mac_addr->ether_addr_octet[3],\
mac_addr->ether_addr_octet[4],\
mac_addr->ether_addr_octet[5]);
else
fprintf(stderr, "Couldn't get own MAC address: %s\n", libnet_geterror(l));
libnet_destroy(l);
return 0;
}
/* EOF */
Part 2:
libnet_build functions
Tags and libnet_clear_packet()
Keep on reading
libnet_build functions
Libnet makes available a build function for each type of header you may want to use in your packet. As you might notice, some of these functions have a smaller version of themselves, called autobuild. The build functions will let you control every piece of information the header will carry. Usually, however, you will only want to fill out the most important fields and let libnet handle the rest, therefore we have the autobuild functions.
In all subsequent examples, I'll use the autobuild funtions wherever possible. Do not hesitate to try out their build counterparts, though. You'll just need to know your headers (i.e., now is a good time to read those RFCs :D).
Let's build and sent an ICMP echo request to an address read from stdin. That packet will carry a 10 byte payload that could be any size (up to (64K - 28) bytes) and anything. In this case, it's a string that goes "libnet :D". When we pass it to libnet_build_icmpv4_echo(), we cast it as u_int8_t*. If we were reading the packet on the other end or even reading its reply, all we would need to do is cast it back to char*. When you do not wish to send any data, simply pass NULL as the payload and 0 as its length.
We'll need these:
libnet_ptag_t libnet_build_icmpv4_echo (u_int8_t type, u_int8_t code, u_int16_t sum, u_int16_t id, u_int16_t seq, u_int8_t * payload, u_int32_t payload_s, libnet_t * l, libnet_ptag_t ptag)
libnet_ptag_t libnet_autobuild_ipv4 (u_int16_t len, u_int8_t prot, u_int32_t dst, libnet_t *l)
For libnet_build_icmpv4_echo(), we'll be interested in seq (sequence number) and id (identification number), payload (the extra data we're sending), payload_s (the payload's size). l is the libnet context we initialized with libnet_init(). Don't worry about the libnet_ptag_t type and ptag, we will talk about them later when we are sending multiple packets.
libnet_autobuild_ipv4() is straightforward. Go take a look at both functions' descriptions in man libnet-functions.h.
We will use libnet's pseudo-random number generating abilities for the first time. It's pretty easy: seed the generator with libnet_seed_prand(), and then get as many numbers as you like with libnet_get_prand(). Go take a look at those functions' descriptions in man libnet-functions.h also.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 1: ping.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];
u_int32_t ip_addr;
u_int16_t id, seq;
char payload[] = "libnet :D";
int bytes_written;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Generating a random id */
libnet_seed_prand (l);
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* Getting destination IP address */
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ICMP header */
seq = 1;
if (libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq,\
(u_int8_t*)payload,sizeof(payload), l, 0) == -1)
{
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building IP header */
if ( libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H +\
sizeof(payload), IPPROTO_ICMP, ip_addr, l) == -1 )
{
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
return 0;
}
/* EOF */
Note that we built libnet_build_icmpv4_echo() and libnet_autobuild_ipv4() in this particular order.
In both functions we needed some macros for the headers sizes (LIBNET_IPV4_H, LIBNET_ICMPV4_ECHO_H), upper layer (not really, but IP thinks it is) protocol (IPPROTO_ICMP) and ICMP packet type (ICMP_ECHO). All of these can be found in man libnet-headers.h. Actually, IPPROTO_ICMP is not there and I'm not really sure where it is, but you can write it down now. You might also want to write down its cousins IPPROTO_TCP and IPPROTO_UDP.
Here's what tcpdump sniffed when I (10.0.0.3) used that program to ping my adsl modem (10.0.0.1).
# tcpdump -n -ttt icmp
000000 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 3276, seq 1, length 18
000936 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 3276, seq 1, length 18
length refers to the data carried by IP which is 10 bytes from the payload and 8 bytes from the ICMP header. When I ran the program it outputted "38 bytes written." That's because we called libnet_init() with LIBNET_RAW4, so it is only telling us how many bytes were written to the link layer, not to the wire.
Here's the payload in wireshark:
Now let's do something similar on the link layer. The following example sends an ARP request for the IP address read from stdin. We'll need:
libnet_ptag_t libnet_autobuild_arp (u_int16_t op, u_int8_t * sha, u_int8_t * spa, u_int8_t * tha, u_int8_t * tpa, libnet_t * l)
libnet_ptag_t libnet_autobuild_ethernet (u_int8_t * dst, u_int16_t type, libnet_t * l)
Read their descriptions in man libnet-functions.h. Read the respective RFCs if you need more information.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 2: arp.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; /* the libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], target_ip_addr_str[16];
u_int32_t target_ip_addr, src_ip_addr;
u_int8_t mac_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
mac_zero_addr[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
struct libnet_ether_addr *src_mac_addr;
int bytes_written;
l = libnet_init(LIBNET_LINK, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Getting our own MAC and IP addresses */
src_ip_addr = libnet_get_ipaddr4(l);
if ( src_ip_addr == -1 ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
src_mac_addr = libnet_get_hwaddr(l);
if ( src_mac_addr == NULL ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting target IP address */
printf("Target IP address: ");
scanf("%15s",target_ip_addr_str);
target_ip_addr = libnet_name2addr4(l, target_ip_addr_str,\
LIBNET_DONT_RESOLVE);
if ( target_ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ARP header */
if ( libnet_autobuild_arp (ARPOP_REQUEST,\
src_mac_addr->ether_addr_octet,\
(u_int8_t*)(&src_ip_addr), mac_zero_addr,\
(u_int8_t*)(&target_ip_addr), l) == -1)
{
fprintf(stderr, "Error building ARP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building Ethernet header */
if ( libnet_autobuild_ethernet (mac_broadcast_addr, ETHERTYPE_ARP, l)\
== -1 )
{
fprintf(stderr, "Error building Ethernet header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
return 0;
}
/* EOF */
As previously mentioned, we need to call libnet_init() with LIBNET_LINK. Note that libnet_autobuild_arp() (libnet_build_arp() too) expects IPv4 addresses as an array of 4 u_int8_t, instead of the u_int32_t that libnet_autobuild_ipv4()/libnet_build_ipv4() expected. You can see above that a simple cast solves the problem.
Here's what tcpdump sniffed when I used that example to request my modem's (10.0.0.1) MAC address:
# tcpdump -n -ttt arp
000000 arp who-has 10.0.0.1 tell 10.0.0.3
000456 arp reply 10.0.0.1 is-at 00:30:0a:67:a6:5c
The MAC address has been altered to protect my modem's secret identity.
The output I got when running it was "42 bytes written." This time, these are bytes written to the wire and not the link layer (the ARP header is always 28 bytes long; the Ethernet header, 14).
Tags and libnet_clear_packet()
The last thing I'm going to cover in this tutorial is sending multiple packets in a row. You can choose between using tags to modify already built headers, or calling libnet_clear_packet() and rebuilding all headers from scratch.
Tags are integers with a libnet_ptag_t type. When you call a build function, it will return a tag. That tag identifies the header built inside libnet's context. When you are calling a build function for the first time, you can pass 0 as the tag, or a tag initialized with tag = LIBNET_PTAG_INITIALIZER. That will make the function build a new header, and wrap it around what is already built. When calling a build function again, you should pass the tag it returned the previous time, so that the header will be modified instead of a new one being built.
If you do not wish to modify an existing header, you need to erase it with libnet_clear_packet(). After you've done that you can build new headers passing 0 as the tag. Remember that you will need to rebuild all headers, and that you will need to do it from the upper layers to the lower ones just like the first time. You should only do this when you have built a packet (e.g., a UDP packet) and now want to build a different kind of packet (e.g., an ICMP error message), otherwise you will be just wasting time and resources. Since I'm lazy, however, that's exactly what I'll do below on my libnet_clear_packet() example.
Here's how you should use tags:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 3: reping_tags.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
#include <unistd.h>
int main() {
libnet_t *l; /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];
u_int32_t ip_addr;
libnet_ptag_t icmp_tag, ip_tag;
u_int16_t id, seq;
int i;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
icmp_tag = ip_tag = LIBNET_PTAG_INITIALIZER;
/* Generating a random id */
libnet_seed_prand(l);
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* Getting destination IP address */
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ICMP header */
seq = 1;
icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq, NULL,\
0, l, 0);
if (icmp_tag == -1) {
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building IP header */
ip_tag = libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H,\
IPPROTO_ICMP, ip_addr, l);
if (ip_tag == -1) {
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing 4 packets */
for ( i = 0; i < 4; i++ ) {
/* Updating the ICMP header */
icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id,\
(seq + i), NULL, 0, l, icmp_tag);
if (icmp_tag == -1) {
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
if ( libnet_write(l) == -1 )
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
/* Waiting 1 second between each packet */
sleep(1);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Just remember that when we are calling libnet_build_icmpv4_echo() for any time other than the first, we are not building it, just modifying the already built header.
Here's tcpdump's output:
# tcpdump -n -ttt icmp
000000 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 1, length 8
000486 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 1, length 8
999623 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 2, length 8
000479 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 2, length 8
999568 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 3, length 8
000533 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 3, length 8
999519 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 4, length 8
000805 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 4, length 8
Now let's try the same thing using libnet_clear_packet(). As stated above, this is a waste of time and resources.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 4: reping_clear.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
#include <unistd.h>
int main() {
libnet_t *l; /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];
u_int32_t ip_addr;
u_int16_t id, seq;
int i;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Generating a random id */
libnet_seed_prand(l);
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* Getting destination IP address */
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing 4 packets */
seq = 1;
for ( i = 0; i < 4; i++ ) {
/* Building the ICMP header */
if ( libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id,\
(seq + i), NULL, 0, l, 0) == -1 ) {
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building the IP header */
if ( libnet_autobuild_ipv4(LIBNET_IPV4_H + \
LIBNET_ICMPV4_ECHO_H, IPPROTO_ICMP,\
ip_addr, l) == -1 ) {
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
if ( libnet_write(l) == -1 )
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
/* Clearing the packet */
/* Comment this to see what happens when you rebuild headers
* without calling libnet_clear_packet() */
libnet_clear_packet(l);
/* Waiting 1 second between each packet */
sleep(1);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Note that you have to rebuild the whole packet again, and that you must do it in the correct order (upper layer to lower layer) again.
Well, that wasn't interesting at all. Let's try commenting out libnet_clear_packet() and see what happens:
# tcpdump -n -ttt icmp
000000 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 1, length 8
000486 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 1, length 8
999605 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 2, length 36
000483 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 2, length 36
999596 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 3, length 64
000500 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 3, length 64
999585 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 4, length 92
000558 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 4, length 92
You should remember from the first time we tried sending an ICMP echo request that it is 20 (IP header) + 8 (ICMP header) + 0 (payload size) bytes long when going to the link layer. You should also remember that the length outputted by tcpdump refers to the data carried by IP. Not let's try this: 8 bytes (ICMP header for the second packet) + 28 bytes (previous packet as seen by the libnet context) == 36 bytes. Now add 20 bytes (IP header for the second packet) and 8 more bytes (ICMP header for the third packet), and we get 64. Do this again and you will get 92 for the last packet.
What we can see here is that when we forget to call libnet_clear_packet(), all headers we build again will go before the ones already built, and carry them as if they were data you meant to send in that packet. I don't think I really need to say this, but don't do it. This can lead to bandwidth waste, exposure of sensitive data to a third party, etc.
One of the comments I received in this tutorial asked about sending a packet bigger than the MTU using libnet. At first I thought libnet would automatically fragment the packet if it was in RAW4 mode. That didn't work. Then I thought there might be an option to turn on auto-fragmentation. If it exists, I haven't found it yet.
So I moved on to trying to fragment the packet myself. The biggest problem I found was that libnet has no idea I'm trying to build a fragmented packet, which means the transport layer checksum is computed for the first fragment only, leading to the whole thing getting discarded as corrupt at the destination. To get the right sum I had to look into libnet's advanced mode, so that's what I'll be talking about here.
Index
Please refer to this post for the whole index, a download link for the examples, copyright info and acknowledgments.
Part 3:
Advanced mode
IP fragmentation with libnet
Advanced mode
Libnet has a few advanced functions, which are only available when you open it in advanced mode. If you remember when we looked into libnet_init(), we could choose to deal with the link layer or not by using LIBNET_LINK and LIBNET_RAW4 respectively as the injection type. For advanced mode, we would use LIBNET_LINK_ADV and LIBNET_RAW4_ADV in the same way. Except that we won't actually use LIBNET_RAW4_ADV since all advanced functions seem to require LIBNET_LINK_ADV.
Anyway, in advanced mode, you'll have access to:
int libnet_adv_cull_header (libnet_t * l, libnet_ptag_t ptag, u_int8_t ** header, u_int32_t * header_s)
This function will give you a pointer (in u_int8_t ** header) to the header referenced by ptag. header_s will point to an u_int32_t containing the size of the header.
Here's an example:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 3 - example 1: cull_header.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
/* Builds an IP header and prints it */
libnet_t *l;
char errbuf[LIBNET_ERRBUF_SIZE];
libnet_ptag_t ip_tag;
u_int8_t *ip_header;
u_int32_t ip_header_size;
int i;
l = libnet_init(LIBNET_LINK_ADV, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Building IP header, size = 20 bytes, dest address = 0.0.0.0, upper
* layer protocol = 0 */
ip_tag = libnet_autobuild_ipv4(20, 0, 0, l);
if ( ip_tag == -1 ) {
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting a pointer to the header */
if (libnet_adv_cull_header(l, ip_tag, &ip_header, &ip_header_size)\
== -1) {
fprintf(stderr, "libnet_adv_cull_header() failed: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Printing the header */
for (i=0; i < ip_header_size; i++) {
printf("%02X ", ip_header[i]);
}
printf("\n");
libnet_destroy(l);
return 0;
}
/* EOF */
# ./cull_header
45 00 00 14 00 00 00 00 40 00 00 00 0A 00 00 03 00 00 00 00
Similarly, there's:
int libnet_adv_cull_packet (libnet_t * l, u_int8_t ** packet, u_int32_t * packet_s)
This one works the same way, but gives you a pointer to whole packet which you should have already built. If you call it, you'll need to free the memory later with
void libnet_adv_free_packet (libnet_t * l, u_int8_t * packet)
If you modify the packet from libnet_adv_cull_packet() or build your own packet from scratch, you can send it with
int libnet_adv_write_link (libnet_t * l, u_int8_t * packet, u_int32_t packet_s)
To exemplify, I'll rewrite the ARP example I used when talking about the build_functions, but I'll build the ARP header without the target IP address and add it directly into the packet:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 3 - example 2: cull_packet.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
/*
* Builds an ARP request, then pulls it from libnet, changes the target
* IP address and writes it.
*/
libnet_t *l;
char errbuf[LIBNET_ERRBUF_SIZE], target_ip_addr_str[16];
u_int32_t src_ip_addr, target_ip_addr = 0;
u_int8_t mac_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
mac_zero_addr[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
struct libnet_ether_addr *src_mac_addr;
int i;
u_int8_t *packet, *target_ip_addr_p;
u_int32_t packet_size;
l = libnet_init(LIBNET_LINK_ADV, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Getting our own MAC and IP addresses */
src_ip_addr = libnet_get_ipaddr4(l);
if ( src_ip_addr == -1 ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
src_mac_addr = libnet_get_hwaddr(l);
if ( src_mac_addr == NULL ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ARP header with target IP address = 0.0.0.0 */
if ( libnet_autobuild_arp (ARPOP_REQUEST,\
src_mac_addr->ether_addr_octet,\
(u_int8_t*)(&src_ip_addr), mac_zero_addr,\
(u_int8_t*)(&target_ip_addr), l) == -1)
{
fprintf(stderr, "Error building ARP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building Ethernet header */
if ( libnet_autobuild_ethernet (mac_broadcast_addr, ETHERTYPE_ARP, l)\
== -1 )
{
fprintf(stderr, "Error building Ethernet header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Pulling the packet */
if (libnet_adv_cull_packet(l, &packet, &packet_size) == -1) {
fprintf(stderr, "libnet_adv_cull_packet() failed: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting target IP address */
printf("Target IP address: ");
scanf("%15s",target_ip_addr_str);
target_ip_addr = libnet_name2addr4(l, target_ip_addr_str,\
LIBNET_DONT_RESOLVE);
if ( target_ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Changing the target */
/* We want to change the 39th, 40th, 41st and 42nd bytes:
* Ethernet header (14) + ARP's hw(2), proto(2), hw size(1), proto
* size(1), opcode(2), src hw addr(6), src ip add(4), target hw addr(6)
* = 38 */
if (packet_size >= 42) {
target_ip_addr_p = (u_int8_t *)&target_ip_addr;
for (i=0; i < 4; i++) {
packet[38+i] = target_ip_addr_p[i];
}
}
/* Writing packet */
if (libnet_adv_write_link(l, packet, packet_size) == -1) {
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
}
/* Freeing up the memory */
libnet_adv_free_packet(l, packet);
libnet_destroy(l);
return 0;
}
/* EOF */
# tcpdump -n -ttt arp
000000 arp who-has 10.0.0.1 tell 10.0.0.3
000459 arp reply 10.0.0.1 is-at 00:30:0a:67:a6:5c
Now, let's move on to the useful stuff.
IP fragmentation with libnet
As stated above, the only way I was able to fragment packets with libnet was implementing the fragmentation myself. In this example, I'll send an ICMP echo request with arbitrarily big random payload. The MTU I chose is Ethernet's 1500 bytes, since that's what I have here. I hope it will be simple to adapt the code to send a TCP or UDP packet with any kind of payload.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 3 - example 3: frag_ping.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
#define MTU 1500
libnet_t *l; /* libnet context */
void frag_and_send(u_int8_t *payload, u_int32_t total_pload_size);
u_int16_t get_sum(u_int8_t *payload, u_int32_t total_pload_size, \
u_int16_t id, u_int16_t seq);
int main() {
int i;
char errbuf[LIBNET_ERRBUF_SIZE];
/* It's a good idea to have the payload as an array of bytes. If yours
* isn't, make a pointer to it and cast it.*/
u_int8_t payload[3000];
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed (raw4, 1st call): %s\n", \
errbuf);
exit(EXIT_FAILURE);
}
/* Generating random payload */
libnet_seed_prand (l);
for (i = 0; i < sizeof(payload); i++) {
payload[i] = libnet_get_prand(LIBNET_PR8);
}
/* Building and sending the fragments */
frag_and_send(payload, sizeof(payload));
libnet_destroy(l);
return 0;
}
void frag_and_send(u_int8_t *payload, u_int32_t total_pload_size) {
/*
* Builds and sends the first packet, calling get_sum() to get the
* correct checksum for the ICMP packet (with the whole payload). Then
* builds and sends IP fragments until all the payload is sent.
*/
char ip_addr_str[16];
u_int32_t ip_addr, src_addr;
u_int16_t id, seq, ip_id;
/* hdr_offset = fragmentation flags + offset (in bytes) divided by 8 */
int pload_offset, hdr_offset;
int bytes_written, max_pload_size, packet_pload_size;
libnet_ptag_t ip_tag;
/* Generating random IDs */
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* We need a non-zero id number for the IP headers, otherwise libnet
* will increase it after each build_ipv4, breaking the fragments */
ip_id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
seq = 1;
/* Getting IP addresses */
src_addr = libnet_get_ipaddr4(l);
if ( src_addr == -1 ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting max payload size */
max_pload_size = (MTU - LIBNET_IPV4_H);
/* making it a multiple of 8 */
max_pload_size -= (max_pload_size % 8);
pload_offset = 0;
/* Building the first packet, which carries the ICMP header */
/*
* We're doing (payload size - icmp header size) and not
* checking if it's a multiple of 8 because we know the header is 8
* bytes long
*/
if ( total_pload_size > (max_pload_size - LIBNET_ICMPV4_ECHO_H) ) {
hdr_offset = IP_MF;
packet_pload_size = max_pload_size - LIBNET_ICMPV4_ECHO_H;
}
else {
hdr_offset = 0;
packet_pload_size = total_pload_size;
}
/* ICMP header */
if ( libnet_build_icmpv4_echo(ICMP_ECHO, 0, get_sum(payload, \
total_pload_size, id, seq), id, seq, \
payload, packet_pload_size, l, 0) == -1 )
{
fprintf(stderr, "Error building ICMP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* First IP header (no payload, offset == 0) */
if ( libnet_build_ipv4((LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H \
+ packet_pload_size), 0, ip_id, \
hdr_offset, 255, IPPROTO_ICMP, 0, src_addr, \
ip_addr, NULL, 0, l, 0) == -1 )
{
fprintf(stderr, "Error building IP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n", \
libnet_geterror(l));
/* Updating the offset */
pload_offset += packet_pload_size;
/* Clearing
*
* We need to get rid of the ICMP header to build the other
* fragments
*/
libnet_clear_packet(l);
ip_tag = LIBNET_PTAG_INITIALIZER;
/* Looping until all the payload is sent */
while ( total_pload_size > pload_offset ) {
/* Building IP header */
/* checking if there will be more fragments */
if ( (total_pload_size - pload_offset) > max_pload_size ) {
/* In IP's eyes, the ICMP header in the first packet
* needs to be in the offset, so we add its size to the
* payload offset here */
hdr_offset = IP_MF + (pload_offset + \
LIBNET_ICMPV4_ECHO_H)/8;
packet_pload_size = max_pload_size;
}
else {
/* See above */
hdr_offset = (pload_offset + LIBNET_ICMPV4_ECHO_H)/8;
packet_pload_size = total_pload_size - pload_offset;
}
ip_tag = libnet_build_ipv4((LIBNET_IPV4_H + max_pload_size), \
0, ip_id, hdr_offset, 255, IPPROTO_ICMP, 0, \
src_addr, ip_addr, (payload + pload_offset), \
packet_pload_size, l, ip_tag);
if ( ip_tag == -1 ) {
fprintf(stderr, "Error building IP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n", \
libnet_geterror(l));
/* Updating the offset */
pload_offset += packet_pload_size;
}
}
u_int16_t get_sum(u_int8_t *payload, u_int32_t total_pload_size, \
u_int16_t id, u_int16_t seq) {
/*
* Builds the ICMP header with the whole payload, gets the checksum from
* it and returns it (in host order).
*/
char errbuf[LIBNET_ERRBUF_SIZE];
libnet_ptag_t icmp_tag;
u_int8_t *packet;
u_int32_t packet_size;
u_int16_t *sum_p, sum;
u_int8_t dummy_dst[6] = {0, 0, 0, 0, 0, 0};
icmp_tag = LIBNET_PTAG_INITIALIZER;
/* Switching to advanced link mode */
/* Nothing should be built yet and all random numbers should be already
* generated. */
libnet_destroy(l);
l = libnet_init(LIBNET_LINK_ADV, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed (link_adv): %s\n", \
errbuf);
exit(EXIT_FAILURE);
}
/* Building the header */
icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq, \
payload, total_pload_size, l, icmp_tag);
if ( icmp_tag == -1 ) {
fprintf(stderr, "Error building ICMP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building dummy IP header */
if ( libnet_autobuild_ipv4((LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H + \
total_pload_size), \
IPPROTO_ICMP, 0, l) == -1 ) {
fprintf(stderr, "Error building dummy IP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building dummy Ethernet header */
if ( libnet_autobuild_ethernet (dummy_dst, ETHERTYPE_IP, l) == -1 ) {
fprintf(stderr, "Error building dummy Ethernet header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Pulling the packet */
if (libnet_adv_cull_packet(l, &packet, &packet_size) == -1) {
fprintf(stderr, "Error pulling the packet: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Grabbing the checksum */
/* We want the 37th and 38th bytes: eth header (14) + ip header (20) +
* icmp type and code (2) = 36 */
sum_p = (u_int16_t*)(packet + 36);
sum = ntohs(*sum_p);
/* Freeing memory */
libnet_adv_free_packet(l, packet);
/* Clearing the header */
libnet_clear_packet(l);
/* Switching back to IPv4 raw socket mode */
libnet_destroy(l);
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed (raw4, 2nd call): %s\n", errbuf);
exit(EXIT_FAILURE);
}
return sum;
}
/* EOF */
I've trimmed and formated tcpdump's output as much as possible for clarity.
# tcpdump -n -ttt -vv icmp
000000 IP (id 28896, offset 0, flags [+], length 1500) 10.0.0.3 > 10.0.0.1: ICMP echo request, id 3371, seq 1
000034 IP (id 28896, offset 1480, flags [+], length 1500) 10.0.0.3 > 10.0.0.1
000012 IP (id 28896, offset 2960, flags [none], length 68) 10.0.0.3 > 10.0.0.1
001657 IP (id 41612, offset 0, flags [+], length 1500) 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 3371, seq 1
000215 IP (id 41612, offset 1480, flags [+], length 1500) 10.0.0.1 > 10.0.0.3
000007 IP (id 41612, offset 2960, flags [none], length 68) 10.0.0.1 > 10.0.0.3
Keep on reading
Index
Part 1
Part 2
If you have any suggestions, criticism or corrections, please leave a comment below.
Edit: I've changed the coloring on the last example. When I made this post for the first time, it was dangerously close to livejournal's post size limit. This was my way of reducing that size (by 24 KB if you're wondering). I had to do it because:
That last example was not working properly until I fixed it a few moments ago (November 08, 2008).
Please re-download the examples tarball at the index page.
Edit2: I'm pretty sure it works flawlessly now. I've tested it with a lot of different payload sizes (from 1 to 65527) and I've outputted the payload and compared it (by md5sum) to the payload exported from wireshark and they're identical.
Also, in case you are curious, libnet will let you build a packet with up to 65535 bytes (64K-1) on top of IP, but my modem discards it if it's over 65515 bytes (64K-21 [possibly because of IP's header]).
A few weeks ago I had to learn how to use libpcap and libnet for my computer networks class. Although google promptly pointed me to this great pcap tutorial, all I could find for libnet were tutorials for version 1.0 (not useful for 1.1 at all) and some very advanced stuff. Being an unexperienced programmer, it was pretty difficult for me to get started using only libnet's man pages and other people's code over the web. For that reason, I decided to write a humble libnet 1.1 tutorial for beginners in order to help other beginners, like me.
Index
Please refer to this post for the whole index, a download link for the examples, copyright info and acknowledgments.
Part 1:
Introduction
Compiling
Checking your packets
libnet_init() and libnet_destroy()
libnet_geterror()
Addresses
Keep on reading
Introduction
Before you try any of this, you need to have libnet installed and you need to know how to use su (or sudo). If you haven't done those things yet, do them now. I'll wait. Done? Good.
Here's what you need to do to start injecting packets:
1) Fire up libnet with libnet_init()
2) Build all headers, from the highest layer to the lowest. Say you'd like to build an UDP packet over IPv4 over Ethernet, with full control over all headers. You would need to call libnet_build_udp(), libnet_build_ipv4() and libnet_build_ethernet(), in this particular order.
3) Write the packet with libnet_write().
4a) Clear the packet with libnet_clear_packet(). Go back to 2) and write a different packet.
4b) Go back to 2) and update the packet using the same build functions, but feeding them the tags they returned on the last call.
5) Clean up with libnet_destroy().
Understanding that logic is the hardest part. Now all you need to do is take a closer look at each function. man libnet-functions.h and man libnet-headers.h are going to be your best friends at this point.
Compiling
To compile the following examples and also your own programs, you'll need to at least link with "-lnet". For reference, here's what I use:
gcc -ggdb -Wall `libnet-config --defines` `libnet-config --libs` example.c -o example
Checking your packets
After you are injecting some packets into your network, you'll probably want to look at them and check whether they are being built as expected. For that purpose, you'll need a sniffer. I use tcpdump to capture traffic and sometimes wireshark (with my regular non-root account) to take a look at those captures. You can use whatever works best for you, though.
libnet_init() and libnet_destroy()
libnet-functions.h gives us the prototype for libnet_init():
libnet_t * libnet_init (int injection_type, char *device, char *err_buf)
From last to first: err_buf is a string which will hold an error message if something goes wrong. device is the device's name (as in "eth0") or its IP address (as in "10.0.0.1"). If device is set to NULL, libnet will try to find a device for you. injection_type is the injection type, as in "from the link layer up" or "from the network layer up". We'll use LIBNET_RAW4 (IPv4 and above) and LIBNET_LINK (link layer and above).
Let's try it out:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 1: init.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
int main() {
libnet_t *l; // the libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Or we can provide a device name or IP address as an argument:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 2: init_devname.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
int main(int argc, char **argv) {
libnet_t *l; // the libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
if ( argc == 1 ) {
fprintf(stderr,"Usage: %s device\n", argv[0]);
exit(EXIT_FAILURE);
}
l = libnet_init(LIBNET_RAW4, argv[1], errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Please note that we will need the libnet context (l in those examples) for almost every libnet function we call.
libnet_geterror()
When calling libnet_init(), you need a string (errbuf) to learn more about an error. After libnet_init() has been successfully called, though, you will get your error messages from a different source:
char * libnet_geterror (libnet_t *l)
All it needs is the context. Call it when you want to know more about the last error you've got.
Addresses
If you are going to handle IPv4 and Ethernet addresses, you will be dealing with a u_int32_t (unsigned 32 bits [4 bytes] integer) for each IP address and a u_int8_t[6] array (unsigned 8 bits [1 byte] integer) for Ethernet. When you get one of this addresses from libnet, you will get them in network byte order. Even if your architecture stores integers in little-endian order (e.g., x86 and x86_64), you will get your addresses in memory with most significant bytes first (lower addresses) and least significant bytes last (higher addresses). Hopefully, the next example will illustrate that.
The functions you need for dealing with IPv4 address to string, string to IPv4 address and string to Ethernet address are:
char* libnet_addr2name4 (u_int32_t in, u_int8_t use_name)
u_int32_t libnet_name2addr4 (libnet_t *l, char *host_name, u_int8_t use_name)
u_int8_t* libnet_hex_aton (int8_t * s, int * len)
libnet_addr2name4() will take the 4 bytes address "in" and return a string with its dotted decimal representation (e.g., 192.168.0.1) if use_name == LIBNET_DONT_RESOLVE, or its DNS name (e.g., google.com) if use_name == LIBNET_RESOLVE.
libnet_name2addr4() will do the exact opposite of libnet_addr2name4(), and will also need the libnet context as its first argument.
libnet_hex_aton() will take a string (int8_t* == char*) of two digits hexadecimal numbers separated by colons (e.g., 00:30:0A:67:A6:5C) and return that address in a u_int8_t array. The array's length is stored in "len" (for Ethernet, it's usually 6). As we can see on man libnet-functions.h, libnet_hex_aton() implicitly calls malloc() and that memory needs to be freed after you are done with it. Remember this.
To accomplish the opposite effect of libnet_hex_aton() you can call printf with "%02X" for each byte, separating them with colons. We'll do that in the following example.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 3: addr.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; // the libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
char ip_addr_str[16], mac_addr_str[18];
u_int32_t ip_addr;
u_int8_t *ip_addr_p, *mac_addr;
int i, length; // for libnet_hex_aton()
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* IP address */
printf("IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr != -1 ) {
/*
* ip_addr is ready to be used in a build function.
* We'll print its contents to stdout to check if everything
* went fine.
*/
// libnet_name2addr4 returns the address in network order (big
// endian).
ip_addr_p = (u_int8_t*)(&ip_addr);
/* Check your system's endianess: */
//printf("ip_addr: %08X\n", ip_addr);
//printf("ip_addr_p: %02X%02X%02X%02X\n", ip_addr_p[0],\
// ip_addr_p[1], ip_addr_p[2], ip_addr_p[3]);
printf("Address read: %d.%d.%d.%d\n", ip_addr_p[0],\
ip_addr_p[1], ip_addr_p[2], ip_addr_p[3]);
/* This would output the same thing, but I wanted to show you
* how the address is stored in memory. */
//printf("Address read: %s\n", libnet_addr2name4(ip_addr,\
// LIBNET_DONT_RESOLVE));
}
else
fprintf(stderr, "Error converting IP address.\n");
/* MAC address */
printf("MAC address: ");
scanf("%17s", mac_addr_str);
mac_addr = libnet_hex_aton((int8_t*)mac_addr_str, &length);
if (mac_addr != NULL) {
/*
* mac_addr is ready to be used in a build function.
* We'll print its contents to stdout to check if everything
* went fine.
*/
printf("Address read: ");
for ( i=0; i < length; i++) {
printf("%02X", mac_addr[i]);
if ( i < length-1 )
printf(":");
}
printf("\n");
// Remember to free the memory allocated by libnet_hex_aton()
free(mac_addr);
}
else
fprintf(stderr, "Error converting MAC address.\n");
libnet_destroy(l);
return 0;
}
/* EOF */
Here you can see one of many casting tricks you might need in the future. On line 42, we'll cast &ip_addr into a u_int8_t* and store it in ip_addr_p. We are turning what would be a pointer to a 4 byte integer into a pointer to an array of 4 single bytes. The reason we do that is so that even if your system uses little-endian integers, we'll be able to read the bytes in the correct order (most significant first). If you are on a PC, try uncommenting lines 44-46; you should get the same thing except that all bytes are swapped (2 hex digits == 1 byte). You should also note that we could have accomplished the same result with libnet_addr2name4(). Uncomment lines 53-54 to check.
It may also be very useful to get our own IP and MAC addresses from libnet. For that, we'll need:
u_int32_t libnet_get_ipaddr4 (libnet_t *l)
libnet_ether_addr * libnet_get_hwaddr (libnet_t *l)
No explanation needed, except for the libnet_ether_addr type:
struct libnet_ether_addr {
u_char ether_addr_octet[6]; /* Ethernet address */
};
It's just a struct with an array of 6 unsigned bytes like the one we used above.
Here's an example of their usage:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 1 - example 4: get_own_addr.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; // libnet context
char errbuf[LIBNET_ERRBUF_SIZE];
u_int32_t ip_addr;
struct libnet_ether_addr *mac_addr;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
ip_addr = libnet_get_ipaddr4(l);
if ( ip_addr != -1 )
printf("IP address: %s\n", libnet_addr2name4(ip_addr, LIBNET_DONT_RESOLVE));
else
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
mac_addr = libnet_get_hwaddr(l);
if ( mac_addr != NULL )
printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",\
mac_addr->ether_addr_octet[0],\
mac_addr->ether_addr_octet[1],\
mac_addr->ether_addr_octet[2],\
mac_addr->ether_addr_octet[3],\
mac_addr->ether_addr_octet[4],\
mac_addr->ether_addr_octet[5]);
else
fprintf(stderr, "Couldn't get own MAC address: %s\n", libnet_geterror(l));
libnet_destroy(l);
return 0;
}
/* EOF */
Part 2:
libnet_build functions
Tags and libnet_clear_packet()
Keep on reading
libnet_build functions
Libnet makes available a build function for each type of header you may want to use in your packet. As you might notice, some of these functions have a smaller version of themselves, called autobuild. The build functions will let you control every piece of information the header will carry. Usually, however, you will only want to fill out the most important fields and let libnet handle the rest, therefore we have the autobuild functions.
In all subsequent examples, I'll use the autobuild funtions wherever possible. Do not hesitate to try out their build counterparts, though. You'll just need to know your headers (i.e., now is a good time to read those RFCs :D).
Let's build and sent an ICMP echo request to an address read from stdin. That packet will carry a 10 byte payload that could be any size (up to (64K - 28) bytes) and anything. In this case, it's a string that goes "libnet :D". When we pass it to libnet_build_icmpv4_echo(), we cast it as u_int8_t*. If we were reading the packet on the other end or even reading its reply, all we would need to do is cast it back to char*. When you do not wish to send any data, simply pass NULL as the payload and 0 as its length.
We'll need these:
libnet_ptag_t libnet_build_icmpv4_echo (u_int8_t type, u_int8_t code, u_int16_t sum, u_int16_t id, u_int16_t seq, u_int8_t * payload, u_int32_t payload_s, libnet_t * l, libnet_ptag_t ptag)
libnet_ptag_t libnet_autobuild_ipv4 (u_int16_t len, u_int8_t prot, u_int32_t dst, libnet_t *l)
For libnet_build_icmpv4_echo(), we'll be interested in seq (sequence number) and id (identification number), payload (the extra data we're sending), payload_s (the payload's size). l is the libnet context we initialized with libnet_init(). Don't worry about the libnet_ptag_t type and ptag, we will talk about them later when we are sending multiple packets.
libnet_autobuild_ipv4() is straightforward. Go take a look at both functions' descriptions in man libnet-functions.h.
We will use libnet's pseudo-random number generating abilities for the first time. It's pretty easy: seed the generator with libnet_seed_prand(), and then get as many numbers as you like with libnet_get_prand(). Go take a look at those functions' descriptions in man libnet-functions.h also.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 1: ping.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];
u_int32_t ip_addr;
u_int16_t id, seq;
char payload[] = "libnet :D";
int bytes_written;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Generating a random id */
libnet_seed_prand (l);
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* Getting destination IP address */
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ICMP header */
seq = 1;
if (libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq,\
(u_int8_t*)payload,sizeof(payload), l, 0) == -1)
{
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building IP header */
if ( libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H +\
sizeof(payload), IPPROTO_ICMP, ip_addr, l) == -1 )
{
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
return 0;
}
/* EOF */
Note that we built libnet_build_icmpv4_echo() and libnet_autobuild_ipv4() in this particular order.
In both functions we needed some macros for the headers sizes (LIBNET_IPV4_H, LIBNET_ICMPV4_ECHO_H), upper layer (not really, but IP thinks it is) protocol (IPPROTO_ICMP) and ICMP packet type (ICMP_ECHO). All of these can be found in man libnet-headers.h. Actually, IPPROTO_ICMP is not there and I'm not really sure where it is, but you can write it down now. You might also want to write down its cousins IPPROTO_TCP and IPPROTO_UDP.
Here's what tcpdump sniffed when I (10.0.0.3) used that program to ping my adsl modem (10.0.0.1).
# tcpdump -n -ttt icmp
000000 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 3276, seq 1, length 18
000936 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 3276, seq 1, length 18
length refers to the data carried by IP which is 10 bytes from the payload and 8 bytes from the ICMP header. When I ran the program it outputted "38 bytes written." That's because we called libnet_init() with LIBNET_RAW4, so it is only telling us how many bytes were written to the link layer, not to the wire.
Here's the payload in wireshark:
Now let's do something similar on the link layer. The following example sends an ARP request for the IP address read from stdin. We'll need:
libnet_ptag_t libnet_autobuild_arp (u_int16_t op, u_int8_t * sha, u_int8_t * spa, u_int8_t * tha, u_int8_t * tpa, libnet_t * l)
libnet_ptag_t libnet_autobuild_ethernet (u_int8_t * dst, u_int16_t type, libnet_t * l)
Read their descriptions in man libnet-functions.h. Read the respective RFCs if you need more information.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 2: arp.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
libnet_t *l; /* the libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], target_ip_addr_str[16];
u_int32_t target_ip_addr, src_ip_addr;
u_int8_t mac_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
mac_zero_addr[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
struct libnet_ether_addr *src_mac_addr;
int bytes_written;
l = libnet_init(LIBNET_LINK, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Getting our own MAC and IP addresses */
src_ip_addr = libnet_get_ipaddr4(l);
if ( src_ip_addr == -1 ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
src_mac_addr = libnet_get_hwaddr(l);
if ( src_mac_addr == NULL ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting target IP address */
printf("Target IP address: ");
scanf("%15s",target_ip_addr_str);
target_ip_addr = libnet_name2addr4(l, target_ip_addr_str,\
LIBNET_DONT_RESOLVE);
if ( target_ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ARP header */
if ( libnet_autobuild_arp (ARPOP_REQUEST,\
src_mac_addr->ether_addr_octet,\
(u_int8_t*)(&src_ip_addr), mac_zero_addr,\
(u_int8_t*)(&target_ip_addr), l) == -1)
{
fprintf(stderr, "Error building ARP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building Ethernet header */
if ( libnet_autobuild_ethernet (mac_broadcast_addr, ETHERTYPE_ARP, l)\
== -1 )
{
fprintf(stderr, "Error building Ethernet header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
return 0;
}
/* EOF */
As previously mentioned, we need to call libnet_init() with LIBNET_LINK. Note that libnet_autobuild_arp() (libnet_build_arp() too) expects IPv4 addresses as an array of 4 u_int8_t, instead of the u_int32_t that libnet_autobuild_ipv4()/libnet_build_ipv4() expected. You can see above that a simple cast solves the problem.
Here's what tcpdump sniffed when I used that example to request my modem's (10.0.0.1) MAC address:
# tcpdump -n -ttt arp
000000 arp who-has 10.0.0.1 tell 10.0.0.3
000456 arp reply 10.0.0.1 is-at 00:30:0a:67:a6:5c
The MAC address has been altered to protect my modem's secret identity.
The output I got when running it was "42 bytes written." This time, these are bytes written to the wire and not the link layer (the ARP header is always 28 bytes long; the Ethernet header, 14).
Tags and libnet_clear_packet()
The last thing I'm going to cover in this tutorial is sending multiple packets in a row. You can choose between using tags to modify already built headers, or calling libnet_clear_packet() and rebuilding all headers from scratch.
Tags are integers with a libnet_ptag_t type. When you call a build function, it will return a tag. That tag identifies the header built inside libnet's context. When you are calling a build function for the first time, you can pass 0 as the tag, or a tag initialized with tag = LIBNET_PTAG_INITIALIZER. That will make the function build a new header, and wrap it around what is already built. When calling a build function again, you should pass the tag it returned the previous time, so that the header will be modified instead of a new one being built.
If you do not wish to modify an existing header, you need to erase it with libnet_clear_packet(). After you've done that you can build new headers passing 0 as the tag. Remember that you will need to rebuild all headers, and that you will need to do it from the upper layers to the lower ones just like the first time. You should only do this when you have built a packet (e.g., a UDP packet) and now want to build a different kind of packet (e.g., an ICMP error message), otherwise you will be just wasting time and resources. Since I'm lazy, however, that's exactly what I'll do below on my libnet_clear_packet() example.
Here's how you should use tags:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 3: reping_tags.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
#include <unistd.h>
int main() {
libnet_t *l; /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];
u_int32_t ip_addr;
libnet_ptag_t icmp_tag, ip_tag;
u_int16_t id, seq;
int i;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
icmp_tag = ip_tag = LIBNET_PTAG_INITIALIZER;
/* Generating a random id */
libnet_seed_prand(l);
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* Getting destination IP address */
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ICMP header */
seq = 1;
icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq, NULL,\
0, l, 0);
if (icmp_tag == -1) {
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building IP header */
ip_tag = libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H,\
IPPROTO_ICMP, ip_addr, l);
if (ip_tag == -1) {
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing 4 packets */
for ( i = 0; i < 4; i++ ) {
/* Updating the ICMP header */
icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id,\
(seq + i), NULL, 0, l, icmp_tag);
if (icmp_tag == -1) {
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
if ( libnet_write(l) == -1 )
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
/* Waiting 1 second between each packet */
sleep(1);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Just remember that when we are calling libnet_build_icmpv4_echo() for any time other than the first, we are not building it, just modifying the already built header.
Here's tcpdump's output:
# tcpdump -n -ttt icmp
000000 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 1, length 8
000486 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 1, length 8
999623 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 2, length 8
000479 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 2, length 8
999568 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 3, length 8
000533 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 3, length 8
999519 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 26873, seq 4, length 8
000805 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 26873, seq 4, length 8
Now let's try the same thing using libnet_clear_packet(). As stated above, this is a waste of time and resources.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 2 - example 4: reping_clear.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
#include <unistd.h>
int main() {
libnet_t *l; /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE], ip_addr_str[16];
u_int32_t ip_addr;
u_int16_t id, seq;
int i;
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Generating a random id */
libnet_seed_prand(l);
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* Getting destination IP address */
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing 4 packets */
seq = 1;
for ( i = 0; i < 4; i++ ) {
/* Building the ICMP header */
if ( libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id,\
(seq + i), NULL, 0, l, 0) == -1 ) {
fprintf(stderr, "Error building ICMP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building the IP header */
if ( libnet_autobuild_ipv4(LIBNET_IPV4_H + \
LIBNET_ICMPV4_ECHO_H, IPPROTO_ICMP,\
ip_addr, l) == -1 ) {
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
if ( libnet_write(l) == -1 )
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
/* Clearing the packet */
/* Comment this to see what happens when you rebuild headers
* without calling libnet_clear_packet() */
libnet_clear_packet(l);
/* Waiting 1 second between each packet */
sleep(1);
}
libnet_destroy(l);
return 0;
}
/* EOF */
Note that you have to rebuild the whole packet again, and that you must do it in the correct order (upper layer to lower layer) again.
Well, that wasn't interesting at all. Let's try commenting out libnet_clear_packet() and see what happens:
# tcpdump -n -ttt icmp
000000 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 1, length 8
000486 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 1, length 8
999605 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 2, length 36
000483 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 2, length 36
999596 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 3, length 64
000500 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 3, length 64
999585 IP 10.0.0.3 > 10.0.0.1: ICMP echo request, id 9701, seq 4, length 92
000558 IP 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 9701, seq 4, length 92
You should remember from the first time we tried sending an ICMP echo request that it is 20 (IP header) + 8 (ICMP header) + 0 (payload size) bytes long when going to the link layer. You should also remember that the length outputted by tcpdump refers to the data carried by IP. Not let's try this: 8 bytes (ICMP header for the second packet) + 28 bytes (previous packet as seen by the libnet context) == 36 bytes. Now add 20 bytes (IP header for the second packet) and 8 more bytes (ICMP header for the third packet), and we get 64. Do this again and you will get 92 for the last packet.
What we can see here is that when we forget to call libnet_clear_packet(), all headers we build again will go before the ones already built, and carry them as if they were data you meant to send in that packet. I don't think I really need to say this, but don't do it. This can lead to bandwidth waste, exposure of sensitive data to a third party, etc.
One of the comments I received in this tutorial asked about sending a packet bigger than the MTU using libnet. At first I thought libnet would automatically fragment the packet if it was in RAW4 mode. That didn't work. Then I thought there might be an option to turn on auto-fragmentation. If it exists, I haven't found it yet.
So I moved on to trying to fragment the packet myself. The biggest problem I found was that libnet has no idea I'm trying to build a fragmented packet, which means the transport layer checksum is computed for the first fragment only, leading to the whole thing getting discarded as corrupt at the destination. To get the right sum I had to look into libnet's advanced mode, so that's what I'll be talking about here.
Index
Please refer to this post for the whole index, a download link for the examples, copyright info and acknowledgments.
Part 3:
Advanced mode
IP fragmentation with libnet
Advanced mode
Libnet has a few advanced functions, which are only available when you open it in advanced mode. If you remember when we looked into libnet_init(), we could choose to deal with the link layer or not by using LIBNET_LINK and LIBNET_RAW4 respectively as the injection type. For advanced mode, we would use LIBNET_LINK_ADV and LIBNET_RAW4_ADV in the same way. Except that we won't actually use LIBNET_RAW4_ADV since all advanced functions seem to require LIBNET_LINK_ADV.
Anyway, in advanced mode, you'll have access to:
int libnet_adv_cull_header (libnet_t * l, libnet_ptag_t ptag, u_int8_t ** header, u_int32_t * header_s)
This function will give you a pointer (in u_int8_t ** header) to the header referenced by ptag. header_s will point to an u_int32_t containing the size of the header.
Here's an example:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 3 - example 1: cull_header.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
/* Builds an IP header and prints it */
libnet_t *l;
char errbuf[LIBNET_ERRBUF_SIZE];
libnet_ptag_t ip_tag;
u_int8_t *ip_header;
u_int32_t ip_header_size;
int i;
l = libnet_init(LIBNET_LINK_ADV, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Building IP header, size = 20 bytes, dest address = 0.0.0.0, upper
* layer protocol = 0 */
ip_tag = libnet_autobuild_ipv4(20, 0, 0, l);
if ( ip_tag == -1 ) {
fprintf(stderr, "Error building IP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting a pointer to the header */
if (libnet_adv_cull_header(l, ip_tag, &ip_header, &ip_header_size)\
== -1) {
fprintf(stderr, "libnet_adv_cull_header() failed: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Printing the header */
for (i=0; i < ip_header_size; i++) {
printf("%02X ", ip_header[i]);
}
printf("\n");
libnet_destroy(l);
return 0;
}
/* EOF */
# ./cull_header
45 00 00 14 00 00 00 00 40 00 00 00 0A 00 00 03 00 00 00 00
Similarly, there's:
int libnet_adv_cull_packet (libnet_t * l, u_int8_t ** packet, u_int32_t * packet_s)
This one works the same way, but gives you a pointer to whole packet which you should have already built. If you call it, you'll need to free the memory later with
void libnet_adv_free_packet (libnet_t * l, u_int8_t * packet)
If you modify the packet from libnet_adv_cull_packet() or build your own packet from scratch, you can send it with
int libnet_adv_write_link (libnet_t * l, u_int8_t * packet, u_int32_t packet_s)
To exemplify, I'll rewrite the ARP example I used when talking about the build_functions, but I'll build the ARP header without the target IP address and add it directly into the packet:
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 3 - example 2: cull_packet.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
int main() {
/*
* Builds an ARP request, then pulls it from libnet, changes the target
* IP address and writes it.
*/
libnet_t *l;
char errbuf[LIBNET_ERRBUF_SIZE], target_ip_addr_str[16];
u_int32_t src_ip_addr, target_ip_addr = 0;
u_int8_t mac_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
mac_zero_addr[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
struct libnet_ether_addr *src_mac_addr;
int i;
u_int8_t *packet, *target_ip_addr_p;
u_int32_t packet_size;
l = libnet_init(LIBNET_LINK_ADV, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/* Getting our own MAC and IP addresses */
src_ip_addr = libnet_get_ipaddr4(l);
if ( src_ip_addr == -1 ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
src_mac_addr = libnet_get_hwaddr(l);
if ( src_mac_addr == NULL ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building ARP header with target IP address = 0.0.0.0 */
if ( libnet_autobuild_arp (ARPOP_REQUEST,\
src_mac_addr->ether_addr_octet,\
(u_int8_t*)(&src_ip_addr), mac_zero_addr,\
(u_int8_t*)(&target_ip_addr), l) == -1)
{
fprintf(stderr, "Error building ARP header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building Ethernet header */
if ( libnet_autobuild_ethernet (mac_broadcast_addr, ETHERTYPE_ARP, l)\
== -1 )
{
fprintf(stderr, "Error building Ethernet header: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Pulling the packet */
if (libnet_adv_cull_packet(l, &packet, &packet_size) == -1) {
fprintf(stderr, "libnet_adv_cull_packet() failed: %s\n",\
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting target IP address */
printf("Target IP address: ");
scanf("%15s",target_ip_addr_str);
target_ip_addr = libnet_name2addr4(l, target_ip_addr_str,\
LIBNET_DONT_RESOLVE);
if ( target_ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Changing the target */
/* We want to change the 39th, 40th, 41st and 42nd bytes:
* Ethernet header (14) + ARP's hw(2), proto(2), hw size(1), proto
* size(1), opcode(2), src hw addr(6), src ip add(4), target hw addr(6)
* = 38 */
if (packet_size >= 42) {
target_ip_addr_p = (u_int8_t *)&target_ip_addr;
for (i=0; i < 4; i++) {
packet[38+i] = target_ip_addr_p[i];
}
}
/* Writing packet */
if (libnet_adv_write_link(l, packet, packet_size) == -1) {
fprintf(stderr, "Error writing packet: %s\n",\
libnet_geterror(l));
}
/* Freeing up the memory */
libnet_adv_free_packet(l, packet);
libnet_destroy(l);
return 0;
}
/* EOF */
# tcpdump -n -ttt arp
000000 arp who-has 10.0.0.1 tell 10.0.0.3
000459 arp reply 10.0.0.1 is-at 00:30:0a:67:a6:5c
Now, let's move on to the useful stuff.
IP fragmentation with libnet
As stated above, the only way I was able to fragment packets with libnet was implementing the fragmentation myself. In this example, I'll send an ICMP echo request with arbitrarily big random payload. The MTU I chose is Ethernet's 1500 bytes, since that's what I have here. I hope it will be simple to adapt the code to send a TCP or UDP packet with any kind of payload.
/* libnet 1.1 tutorial (http://repura.livejournal.com/31673.html) sample code
* Part 3 - example 3: frag_ping.c */
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <stdint.h>
#define MTU 1500
libnet_t *l; /* libnet context */
void frag_and_send(u_int8_t *payload, u_int32_t total_pload_size);
u_int16_t get_sum(u_int8_t *payload, u_int32_t total_pload_size, \
u_int16_t id, u_int16_t seq);
int main() {
int i;
char errbuf[LIBNET_ERRBUF_SIZE];
/* It's a good idea to have the payload as an array of bytes. If yours
* isn't, make a pointer to it and cast it.*/
u_int8_t payload[3000];
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed (raw4, 1st call): %s\n", \
errbuf);
exit(EXIT_FAILURE);
}
/* Generating random payload */
libnet_seed_prand (l);
for (i = 0; i < sizeof(payload); i++) {
payload[i] = libnet_get_prand(LIBNET_PR8);
}
/* Building and sending the fragments */
frag_and_send(payload, sizeof(payload));
libnet_destroy(l);
return 0;
}
void frag_and_send(u_int8_t *payload, u_int32_t total_pload_size) {
/*
* Builds and sends the first packet, calling get_sum() to get the
* correct checksum for the ICMP packet (with the whole payload). Then
* builds and sends IP fragments until all the payload is sent.
*/
char ip_addr_str[16];
u_int32_t ip_addr, src_addr;
u_int16_t id, seq, ip_id;
/* hdr_offset = fragmentation flags + offset (in bytes) divided by 8 */
int pload_offset, hdr_offset;
int bytes_written, max_pload_size, packet_pload_size;
libnet_ptag_t ip_tag;
/* Generating random IDs */
id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
/* We need a non-zero id number for the IP headers, otherwise libnet
* will increase it after each build_ipv4, breaking the fragments */
ip_id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
seq = 1;
/* Getting IP addresses */
src_addr = libnet_get_ipaddr4(l);
if ( src_addr == -1 ) {
fprintf(stderr, "Couldn't get own IP address: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
printf("Destination IP address: ");
scanf("%15s",ip_addr_str);
ip_addr = libnet_name2addr4(l, ip_addr_str, LIBNET_DONT_RESOLVE);
if ( ip_addr == -1 ) {
fprintf(stderr, "Error converting IP address.\n");
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Getting max payload size */
max_pload_size = (MTU - LIBNET_IPV4_H);
/* making it a multiple of 8 */
max_pload_size -= (max_pload_size % 8);
pload_offset = 0;
/* Building the first packet, which carries the ICMP header */
/*
* We're doing (payload size - icmp header size) and not
* checking if it's a multiple of 8 because we know the header is 8
* bytes long
*/
if ( total_pload_size > (max_pload_size - LIBNET_ICMPV4_ECHO_H) ) {
hdr_offset = IP_MF;
packet_pload_size = max_pload_size - LIBNET_ICMPV4_ECHO_H;
}
else {
hdr_offset = 0;
packet_pload_size = total_pload_size;
}
/* ICMP header */
if ( libnet_build_icmpv4_echo(ICMP_ECHO, 0, get_sum(payload, \
total_pload_size, id, seq), id, seq, \
payload, packet_pload_size, l, 0) == -1 )
{
fprintf(stderr, "Error building ICMP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* First IP header (no payload, offset == 0) */
if ( libnet_build_ipv4((LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H \
+ packet_pload_size), 0, ip_id, \
hdr_offset, 255, IPPROTO_ICMP, 0, src_addr, \
ip_addr, NULL, 0, l, 0) == -1 )
{
fprintf(stderr, "Error building IP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n", \
libnet_geterror(l));
/* Updating the offset */
pload_offset += packet_pload_size;
/* Clearing
*
* We need to get rid of the ICMP header to build the other
* fragments
*/
libnet_clear_packet(l);
ip_tag = LIBNET_PTAG_INITIALIZER;
/* Looping until all the payload is sent */
while ( total_pload_size > pload_offset ) {
/* Building IP header */
/* checking if there will be more fragments */
if ( (total_pload_size - pload_offset) > max_pload_size ) {
/* In IP's eyes, the ICMP header in the first packet
* needs to be in the offset, so we add its size to the
* payload offset here */
hdr_offset = IP_MF + (pload_offset + \
LIBNET_ICMPV4_ECHO_H)/8;
packet_pload_size = max_pload_size;
}
else {
/* See above */
hdr_offset = (pload_offset + LIBNET_ICMPV4_ECHO_H)/8;
packet_pload_size = total_pload_size - pload_offset;
}
ip_tag = libnet_build_ipv4((LIBNET_IPV4_H + max_pload_size), \
0, ip_id, hdr_offset, 255, IPPROTO_ICMP, 0, \
src_addr, ip_addr, (payload + pload_offset), \
packet_pload_size, l, ip_tag);
if ( ip_tag == -1 ) {
fprintf(stderr, "Error building IP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Writing packet */
bytes_written = libnet_write(l);
if ( bytes_written != -1 )
printf("%d bytes written.\n", bytes_written);
else
fprintf(stderr, "Error writing packet: %s\n", \
libnet_geterror(l));
/* Updating the offset */
pload_offset += packet_pload_size;
}
}
u_int16_t get_sum(u_int8_t *payload, u_int32_t total_pload_size, \
u_int16_t id, u_int16_t seq) {
/*
* Builds the ICMP header with the whole payload, gets the checksum from
* it and returns it (in host order).
*/
char errbuf[LIBNET_ERRBUF_SIZE];
libnet_ptag_t icmp_tag;
u_int8_t *packet;
u_int32_t packet_size;
u_int16_t *sum_p, sum;
u_int8_t dummy_dst[6] = {0, 0, 0, 0, 0, 0};
icmp_tag = LIBNET_PTAG_INITIALIZER;
/* Switching to advanced link mode */
/* Nothing should be built yet and all random numbers should be already
* generated. */
libnet_destroy(l);
l = libnet_init(LIBNET_LINK_ADV, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed (link_adv): %s\n", \
errbuf);
exit(EXIT_FAILURE);
}
/* Building the header */
icmp_tag = libnet_build_icmpv4_echo(ICMP_ECHO, 0, 0, id, seq, \
payload, total_pload_size, l, icmp_tag);
if ( icmp_tag == -1 ) {
fprintf(stderr, "Error building ICMP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building dummy IP header */
if ( libnet_autobuild_ipv4((LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H + \
total_pload_size), \
IPPROTO_ICMP, 0, l) == -1 ) {
fprintf(stderr, "Error building dummy IP header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Building dummy Ethernet header */
if ( libnet_autobuild_ethernet (dummy_dst, ETHERTYPE_IP, l) == -1 ) {
fprintf(stderr, "Error building dummy Ethernet header: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Pulling the packet */
if (libnet_adv_cull_packet(l, &packet, &packet_size) == -1) {
fprintf(stderr, "Error pulling the packet: %s\n", \
libnet_geterror(l));
libnet_destroy(l);
exit(EXIT_FAILURE);
}
/* Grabbing the checksum */
/* We want the 37th and 38th bytes: eth header (14) + ip header (20) +
* icmp type and code (2) = 36 */
sum_p = (u_int16_t*)(packet + 36);
sum = ntohs(*sum_p);
/* Freeing memory */
libnet_adv_free_packet(l, packet);
/* Clearing the header */
libnet_clear_packet(l);
/* Switching back to IPv4 raw socket mode */
libnet_destroy(l);
l = libnet_init(LIBNET_RAW4, NULL, errbuf);
if ( l == NULL ) {
fprintf(stderr, "libnet_init() failed (raw4, 2nd call): %s\n", errbuf);
exit(EXIT_FAILURE);
}
return sum;
}
/* EOF */
I've trimmed and formated tcpdump's output as much as possible for clarity.
# tcpdump -n -ttt -vv icmp
000000 IP (id 28896, offset 0, flags [+], length 1500) 10.0.0.3 > 10.0.0.1: ICMP echo request, id 3371, seq 1
000034 IP (id 28896, offset 1480, flags [+], length 1500) 10.0.0.3 > 10.0.0.1
000012 IP (id 28896, offset 2960, flags [none], length 68) 10.0.0.3 > 10.0.0.1
001657 IP (id 41612, offset 0, flags [+], length 1500) 10.0.0.1 > 10.0.0.3: ICMP echo reply, id 3371, seq 1
000215 IP (id 41612, offset 1480, flags [+], length 1500) 10.0.0.1 > 10.0.0.3
000007 IP (id 41612, offset 2960, flags [none], length 68) 10.0.0.1 > 10.0.0.3
Keep on reading
Index
Part 1
Part 2
If you have any suggestions, criticism or corrections, please leave a comment below.
Edit: I've changed the coloring on the last example. When I made this post for the first time, it was dangerously close to livejournal's post size limit. This was my way of reducing that size (by 24 KB if you're wondering). I had to do it because:
That last example was not working properly until I fixed it a few moments ago (November 08, 2008).
Please re-download the examples tarball at the index page.
Edit2: I'm pretty sure it works flawlessly now. I've tested it with a lot of different payload sizes (from 1 to 65527) and I've outputted the payload and compared it (by md5sum) to the payload exported from wireshark and they're identical.
Also, in case you are curious, libnet will let you build a packet with up to 65535 bytes (64K-1) on top of IP, but my modem discards it if it's over 65515 bytes (64K-21 [possibly because of IP's header]).
这篇日志没有评论。

评论Feed: http://www.dklkt.cn/feed.asp?q=comment&id=201
引用链接: 

