Sending raw Ethernet packets from a specific interface in C on Linux

Lately I’ve been writing some code to send packets to a specific MAC address from a specific interface. I’m sure this will come in handy again so here is how it goes:

Includes:
(might not need all of these)

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ether.h>
#include <linux/if_packet.h>

Open the raw socket:

int sockfd;
...
/* Open RAW socket to send on */
if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) {
    perror("socket");
}

Get the index of the interface to send on:

struct ifreq if_idx;
...
memset(&if_idx, 0, sizeof(struct ifreq));
strncpy(if_idx.ifr_name, "eth0", IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFINDEX, &if_idx) < 0)
    perror("SIOCGIFINDEX");

Get the MAC address of the interface to send on:

struct ifreq if_mac;
...
memset(&if_mac, 0, sizeof(struct ifreq));
strncpy(if_mac.ifr_name, "eth0", IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFHWADDR, &if_mac) < 0)
    perror("SIOCGIFHWADDR");

Get the IP address of the interface to send on:

struct ifreq if_ip;
...
memset(&if_ip, 0, sizeof(struct ifreq));
strncpy(if_ip.ifr_name, "eth0", IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFADDR, &if_ip) < 0)
    perror("SIOCGIFADDR");

Construct the Ethernet header:

int tx_len = 0;
char sendbuf[1024];
struct ether_header *eh = (struct ether_header *) sendbuf;
...
memset(sendbuf, 0, 1024);
/* Ethernet header */
eh->ether_shost[0] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[0];
eh->ether_shost[1] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[1];
eh->ether_shost[2] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[2];
eh->ether_shost[3] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[3];
eh->ether_shost[4] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[4];
eh->ether_shost[5] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[5];
eh->ether_dhost[0] = MY_DEST_MAC0;
eh->ether_dhost[1] = MY_DEST_MAC1;
eh->ether_dhost[2] = MY_DEST_MAC2;
eh->ether_dhost[3] = MY_DEST_MAC3;
eh->ether_dhost[4] = MY_DEST_MAC4;
eh->ether_dhost[5] = MY_DEST_MAC5;
eh->ether_type = htons(ETH_P_IP);
tx_len += sizeof(struct ether_header);

Construct the IP header:

struct iphdr *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header));
...
/* IP Header */
iph->ihl = 5;
iph->version = 4;
iph->tos = 16; // Low delay
iph->id = htons(54321);
iph->ttl = ttl; // hops
iph->protocol = 17; // UDP
/* Source IP address, can be spoofed */
iph->saddr = inet_addr(inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
// iph->saddr = inet_addr("192.168.0.112");
/* Destination IP address */
iph->daddr = inet_addr("192.168.0.111");
tx_len += sizeof(struct iphdr);

Construct the UDP header:

struct udphdr *udph = (struct udphdr *) (sendbuf + sizeof(struct iphdr) + sizeof(struct ether_header));
...
/* UDP Header */
udph->source = htons(3423);
udph->dest = htons(5342);
udph->check = 0; // skip
tx_len += sizeof(struct udphdr);

Fill in UDP payload:

/* Packet data */
sendbuf[tx_len++] = 0xde;
sendbuf[tx_len++] = 0xad;
sendbuf[tx_len++] = 0xbe;
sendbuf[tx_len++] = 0xef;

Fill in remaining header info:

unsigned short csum(unsigned short *buf, int nwords)
{
    unsigned long sum;
    for(sum=0; nwords>0; nwords--)
        sum += *buf++;
    sum = (sum >> 16) + (sum &0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}
...
/* Length of UDP payload and header */
udph->len = htons(tx_len - sizeof(struct ether_header) - sizeof(struct iphdr));
/* Length of IP payload and header */
iph->tot_len = htons(tx_len - sizeof(struct ether_header));
/* Calculate IP checksum on completed header */
iph->check = csum((unsigned short *)(sendbuf+sizeof(struct ether_header)), sizeof(struct iphdr)/2);

Send the raw Ethernet packet:

/* Destination address */
struct sockaddr_ll socket_address;
...
/* Index of the network device */
socket_address.sll_ifindex = if_idx.ifr_ifindex;
/* Address length*/
socket_address.sll_halen = ETH_ALEN;
/* Destination MAC */
socket_address.sll_addr[0] = MY_DEST_MAC0;
socket_address.sll_addr[1] = MY_DEST_MAC1;
socket_address.sll_addr[2] = MY_DEST_MAC2;
socket_address.sll_addr[3] = MY_DEST_MAC3;
socket_address.sll_addr[4] = MY_DEST_MAC4;
socket_address.sll_addr[5] = MY_DEST_MAC5;
/* Send packet */
if (sendto(sock, sendbuf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
    printf("Send failed\n");

Update:
As in the comments, I’ve written a working example that can be found here: https://gist.github.com/1922600

Change the destination MAC address (e.g. 00:11:22:33:44:55) and compile:

gcc sendRawEth.c -o sendRawEth

In one terminal run tcpdump to observe the packets:

sudo tcpdump -nettti eth0 '(ether dst host 00:11:22:33:44:55)'

And in another run the program as root:

sudo ./sendRawEth eth0

References:
http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html
http://www.tenouk.com/Module43a.html
http://linux.die.net/man/3/sendto

About these ads

About austinmarton

I completed a Bachelor of Engineering (Hons) in Mechatronics at Massey University in New Zealand. In the day time I'm an Embedded Software Engineer working on Linux based networking products. Now based in Melbourne, Australia. I make things.

43 responses to “Sending raw Ethernet packets from a specific interface in C on Linux”

  1. Jonathan Andrews says :

    Thank you :-) Exactly the example I was looking for, is it possible to download the source ?

    • austinmarton says :

      Hi Jon,

      I’m stoked someone else has found it useful! Those snippits should include everything you need but I’ll try put a full compiling example up on github for you soon.

      Cheers,
      Austin.

  2. Jonathan Andrews says :

    I have it working :-)

    I’m sending 8 UDP broadcast packets each with a different IP address from a single ethernet card. Its being used to test an audio streaming application (it emulates 8 stream sources at once). Linux will let you have multiple IP addresses per card, but it seems will not let you elect to UDP broadcast from a specific one. Many thanks, great work, Jon

  3. Awais says :

    Hi All
    i just want to send ethernet packet, a frame not layer 3,4 packet. is it possible to do this in java or C ?
    Thanks

    • austinmarton says :

      Hi Awais,

      Of course it is possible to send a raw Ethernet packet in C! Just ignore the section of the above code that does the UDP and IP headers.

      I’ve written a little example here:
      https://gist.github.com/1922600

      If you compile and run that app as root you can view the packets going out on the specified interface using wireshark.

      Cheers,
      Austin.

  4. hassan says :

    Hi Austin,
    I have tried your example code https://gist.github.com/1922600 . Its compilation is fine with no errors but I’m not getting packets going out of the interface using wireshark……..Just for confirmation…. Have you run that code or you have just provided the code???….And guide me if there are more steps involved which I am not completing….And one thing more At run time what arguements is it needed..??
    Thanks

    • austinmarton says :

      I have compiled and run the code on Linux:
      gcc sendRawEth.c -o sendRawEth
      You will need to run the executable as root, or using “sudo”:
      sudo ./sendRawEth eth0
      Check that you are watching on the same interface as the packet is being sent.

      Also, because I have used the IP EtherType (0×0800) and then just put random data in, it will come up as “Malformed Packet” in Wireshark. Unlikely but possible your NIC would reject it on that basis but you should see it in Wireshark at least.

      Cheers,
      Austin.

      • Victor Ramos says :

        How did you put random data in? Could you add discrete data instead?

      • austinmarton says :

        Sorry I shouldn’t have said “random” data, I was referring to the bytes that I had hard coded as the packet data (0xde, 0xad, 0xbe, 0xef – see line 81 of the gist).

        You can easily add your own payload by copying the bytes into ‘sendbuf’ and incrementing ‘tx_len’.

        I could add some code to parse packet data from a command line argument if needed.

      • Victor Ramos says :

        Where exactly would you expect to see the data if viewing packets in Wireshark?? What I’m trying to do is send raw data to a wireless transceiver that tells a PIC chip to turn on a SSR. Any ideas? YThanks for the help!

    • Chris Miller says :

      I too witnessed “packet not going out” which had me going for some time. I was using an Asus laptop with its built-in NIC set up with six VLANs. All the VLANs were basically working in that I could ping devices on all different VLANs, and then look at the arp table and see the expected enteries against each sub-interface, but packets generated using this code were not going out – not according to tcpdump, at least.

      The same software was working on another machine in the same version of Fedora, so I was pretty sure the software was correct.

      In the end, I added a second NIC (elderly 100Mbit/s Belkin, USB-attached), reconfigured my interfaces to run over that NIC and everything started working correctly.

      My tentative conclusion is that the drivers or the hardware of the built-in NIC were not handling RAW sockets correctly over sub-interfaces.
      The suspect NIC reports as “Tigon3 [partno(BCM95764m) rev 5784100]” in a dmesg report, and its MAC is interpreted as Wistron by Wireshark. Kernel version is “2.6.35.14-106″

  5. Mehdi says :

    Hi Austin,
    I am a student, currently trying to develop an Ethernet communication link using a research protocol. But I need first to send raw ethernet packets from a machine to an other using MAC addresses. Since I’m a newbie in the field, I would need some help if you don’t mind.
    I downloaded your file but I think I need the “receive” file to run on the other machine. Can you provide me with this?

    Cheers,
    Mehdi

    • Mehdi says :

      Anyone here? I would be very grateful if could give me some help. Thanks

      • austinmarton says :

        Hi Mehdi,
        Don’t worry I haven’t forgotten about this. I will try find some time to put up the receive code soon.
        Cheers,
        Austin.

  6. Mehdi says :

    Actually, I’m trying to learn by myself how to code with RAW sockets. Do you have any kind of tutorial for newbies or some step–by-step guide? Thank you for your help :)

  7. swehack says :

    Hi Austin

    I tried your code from the github gist posting and it compiles fine but I can’t see any packet going out on eth0.

    I changed MY_DEST_MAC0 to my destination MAC in the same LAN using the format 0×001122334455.

    Then I setup tcpdump -nettti eth0 |grep 44:55 to find a packet going to that mac address but nothing comes out. I also tried without the grep but with a bpf to filter out the noise on heavily used ports but still no packet.

    So what am I doing wrong here?

    • austinmarton says :

      I observed the same problem when using grep on tcpdump, but when I ran tcpdump by itself I can see the packets (on a separate interface that isn’t flooded with other traffic).

      In short – you need to use tcpdump filters instead of grep, the following command will show packets with destination MAC address 00:11:22:33:44:55 on interface eth0:
      sudo tcpdump -nettti eth0 '(ether dst host 00:11:22:33:44:55)'

      Cheers,
      Austin.

      • swehack says :

        You’re right I do faintly remember being told never to grep tcpdump, thanks for showing how to filter out mac addresses.

        However, this time I tried it with a filter setting of ether dst host 11:22:33… on a LAN and still I see no packets. I tried pinging the same LAN host that I stole the destination mac address from and sure enough tcpdump shows an icmp echo request on that mac.

      • austinmarton says :

        Not sure what’s different with your set up. I modified the code from github like so:
        #define MY_DEST_MAC0 0x00
        #define MY_DEST_MAC1 0x11
        #define MY_DEST_MAC2 0x22
        #define MY_DEST_MAC3 0x33
        #define MY_DEST_MAC4 0x44
        #define MY_DEST_MAC5 0x55

        recompiled:
        gcc sendRawEth.c -o sendRawEth
        then ran tcpdump:
        sudo tcpdump -nettti eth0 '(ether dst host 00:11:22:33:44:55)'
        then ran the program:
        sudo sendRawEth
        and in tcpdump I can see the packets.

        Tested on Ubuntu 10.04 and 11.10.

      • swehack says :

        Oh I see Austin! This is exactly why I specified the format I used on the mac address, because I wasn’t sure it was correct. You’ve shown me now that the correct format is to use all 6 macros.

        And now it works! You’re the best Austin. <3

      • Frank says :

        Jenius! Austin! You save me. haha. For me, I use this example for wireless (e.g. ad-hoc), the both wireless cards have a same essid & channel. And run : sudo tcpdump -nettti wlan0 ‘(ether dst host 00:11:22:33:44:55)’ On the other card: sudo ./sendRawEth wlan0 everything goes well…..

      • austinmarton says :

        That’s very cool! I hadn’t thought about using it for ad-hoc wifi networks. Thanks for the info. Austin.

  8. manrico says :

    is there a similar code in C for Windows?
    thanks

  9. Chethan Pandarinath says :

    This code is fantastic, thanks so much for posting it. Tried out the linked github source and it worked flawlessly the first time. After beating my head against networking code for several hours, this made my day.

  10. Thomas Waldecker says :

    Hi Austin,

    thanks for your code. Especially for the gist. I thought it is bad to compile the MAC address in the program, so I forked your gist and wrote the bits to specify the MAC address with a command line argument.

    Kind regards,
    Thomas

  11. Eric Mackay says :

    Wow. You are a LIFESAVER! I have been searching for a clear example of using raw sockets online for weeks, but everyone always says “Don’t use them!” and provides no information. This really saves my bacon. Thanks!

  12. Yahya says :

    Hi great post and very useful, using this ive been able to send Ethernet packets over the wireless interface (802.11) just needed to know what kind of sorcery is this :P and how its even possible as ethernet (wired) and Wlan (wireless) follow different L2 protocol structures

    However Thanks for this great post!

  13. hemant says :

    hey ,
    i need this code urgent ” Sending raw Ethernet packets from a specific interface in C on Linux” can u please send it to my email
    thanks

  14. rrlagic says :

    Simple, clear, useful.

  15. NS says :

    Hi all,
    i have raw IPSEC protected packet which i have to send to my Ethernet interface,

    Dst Addr : my IP , MAC address
    Src Addr : Tunnel’s other end point host ip and MAC,

    1) Whether it will receive in the application running on my host ?

    2) Will it go through ip(ipsec) layer of my host for decrypting the packet and pass to the application ?

    While Trying this in wireshark, IPSEC packet can be captured but application is not received the packet…

    Please help me.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: