Popular Posts

Sunday, November 7, 2010

Raw socket in Linux

Contents :
  1. Introduction
  2. How to send packets using raw sockets
  3. How to receive packets using raw sockets
Introduction

Hi  this post is for those who are already familiar with basics of sockets, The raw sockets are use to access the raw data at application layer, normally tcp-ip stack in linux removes all the header and only the data is available at the application layer, but some times it is more convenient and easy to implement the new protocol or processing of some protocol at the application layer, instead of putting code in the kernel, Lets get into the practical examples.

How to send packets using raw sockets

1) create the raw socket using socket call, protocol is one of the constants defined in <netinet/in.h>, it could be IPPROTO_ICMP or anything IPPROTO_XXX, SOCK_RAW type is used to create raw socket.

int fd = socket(AF_INET,SOCK_RAW,protocol)

2) setting IP_HDRINCL socket option to construct the ip layer packet, When this option is set the ip header is also constructed by the application, kernel only adds the link layer header.

setsockopt(fd,IPPROTO_IP,IP_HDRINCL,&var,sizeof(var)

3) bind can be used to set the local ip address and connect can be use to set the destination ip address, but this is very rare

In case the raw socket is not connected then sendmsg can be used to send out the packet, sendmsg system call takes three parameters,

sendmsg(int fd,struct msghdr*,int flags)

i) fd is the valid file descriptor obtained from the socket call
ii) msgheader which is used to pass the information to kernel like destination address and the buffer
iii) the flags can be passed as 0

For us the most interesting thing is struct msghdr, the msghdr has a pointer to msg_iov which contains the buffer pointer, struct iovec  is used for scatter/gather i/o

struct iovec iov;
iov.iov_base = buf ;
iov.iov_len = pkt_len;

Once the struct iovec  is initialized it is assigned to the msghdr structure 
msg.msg_iov = &iov;

Then msghdr has other fields like msg_name and msg_namelen, which are use to fill the destination address, for inet socket we use struct sockaddr_in to fill the destination address and port

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5500);

Please find the complete code here

How to receive packets using raw sockets
 
Using raw sockets application can only receive the icmp,igmp packets, application cannot receive udp or tcp packets, incase the raw udp or tcp packets are required then application needs to access it using packet socket(datalink access), for this please wait for my another article under construction. If bind is not done then raw socket will receive the icmp or igmp packets from any interface, recvmsg can be use to receive the packet.

recvmsg(int fd, struct msghdr*, int flags)

recvmsg takes three parameter ,same as sendmsg but in recvmsg msghdr field like msg_name and msg_namelen can be set as null and 0. The msg_contol field needs to be filled incase the kernel needs to pass some extra information.

Please find complete code here

13 comments:

  1. Will try this out soon. Seems very well written, yusuf!

    - Rick C. Hodgin

    ReplyDelete
  2. Thanks Rick,

    May be the coming articles would be more interesting,i.e. datalink access in Linux.

    Yusuf

    ReplyDelete
  3. For the above raw socket, how can it be bind to specified network interface that it will send data to the network interface? Currently i using the sento function to send raw data, but the kernel start to complain this "XXX forgot to set AF_INET in raw sendmsg. Fix it!". I started googling around but no luck yet.

    Here is the example that i am using now:

    struct sockaddr_ll dst;

    memset (&dst, 0, sizeof (struct sockaddr_ll));
    dst.sll_family = AF_INET;
    dst.sll_ifindex = ifindex; // network interface index that i want to send to
    dst.sll_protocol = htons (ETH_P_ALL);

    // send into network interface
    sendto (fd, buf, len, 0, (struct sockaddr *) &dst, sizeof (struct sockaddr_ll));

    ReplyDelete
  4. Hi Jeansoon,

    Try the socket option to bind the raw socket to specific interface as below,

    ifreq Interface;
    memset(&Interface, 0, sizeof(Interface)); strncpy(Interface.ifr_ifrn.ifrn_name, "eth0", IFNAMSIZ);
    if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &Interface, sizeof(Interface)) < 0)
    {
    //error
    close(sd);
    }

    Replace the "eth0" interface name with the intended interface name, Its working for me let me know if it works fine for you.
    If you need greater control go for packet socket, please refer my another article on packet socket.

    ReplyDelete
  5. Hi Yusuf

    I am reading packet in raw socket mode. Also I am putting Time stamp in packet in kernel and printing that time stamp after reading recvfrom()RAW socket. I found a delay of 3-8 ms delay in Why is it so. Can I decease it to 30u sec.

    Thanks

    ReplyDelete
  6. how to use ioctl to get data from specified interface

    ReplyDelete
  7. i cannot find the code though..can someone help me the linksare directing me to somewhere else!!!!

    ReplyDelete
  8. Can you please upload the same code somewhere else as the domain is expired where you have uploaded the code

    ReplyDelete
  9. Hi can you give me the idea how ioctl is working with raw socket

    ReplyDelete
  10. / Interface to send packet through.
    strcpy (interface, "eth0");

    // Submit request for a socket descriptor to look up interface.
    if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
    perror ("socket() failed to get socket descriptor for using ioctl() ");
    exit (EXIT_FAILURE);
    }

    // Use ioctl() to look up interface index which we will use to
    // bind socket descriptor sd to specified interface with setsockopt() since
    // none of the other arguments of sendto() specify which interface to use.
    memset (&ifr, 0, sizeof (ifr));
    snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
    //cmd means some ioctl cmd
    if (ioctl (sd, cmd, &ifr) < 0) {
    perror ("ioctl() failed to find interface ");
    return (EXIT_FAILURE);
    }
    close (sd);

    ReplyDelete
  11. Good Stuff. But i think tcp/udp packets can also be snooped using raw sockets.
    socket(AF_INET , SOCK_RAW , IPPROTO_ICMP);

    ReplyDelete
  12. I have a need to capture tcp packets passing via a router machine where IPsec tunnel is terminated at this machine and another one setup to host. Can raw socket get the inner IP packet after decryption?

    ReplyDelete
  13. Hi, thanks for some great articles!
    Unfortunately your source links are broken.. Can you share them?

    ReplyDelete