Streaming data to a specific interface without using the routing table (Linux)

I recently created a multicast generator application to test an application that processes multicast data.  The application worked well.  However, I became frustrated with having to set up the routes on any server I used to ensure the data was sent out of the correct interface.  tcpreplay overcomes this by allowing you to give the interface name on the command line and bypasses the routing.  After doing some investigation into how this can be achieved I thought I would share a couple of techniques you can use to do this.  I haven't tested this code so I apologise if it does not compile but it should give you a rough idea about how this works.

Using the interface IP address:

#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <linux/if_ether.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <linux/ip.h>
#include <stdio.h>
#include <string.h>

int getAddressFromIP ( int fd, struct in_addr *address, const char *ip )
{
    int Ret = inet_aton (ip, address);
   
    if ( Ret == 0 )
    {
        fprintf(stderr, "Invalid IP address: %s\n", ip);
        return -1;
    }
   
    return 0;
}

int bindSocketWithIP(int fd, const char *ip)
{
    struct sockaddr_in address;

    memset(&address, 0, sizeof(address));

    if ( !getAddressFromIP(fd, &address.sin_addr, ip) )
    {
        address.sin_family = AF_INET;
        address.sin_port   = 0;
        if (bind (fd, (struct sockaddr *) &address, sizeof(address)))
        {
            fprintf(stderr, "Failed to bind to socket: %s\n", ip);
            return -1;
        }
    }
    else
    {
        /* error already reported by getAddressFromIP() */
        return -1;
    }
   
    return 0;
}

Using the interface name:

#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <linux/if_ether.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <linux/ip.h>
#include <stdio.h>
#include <string.h>


int getAddressFromName ( int fd, struct in_addr *address, const char *name )
{
    struct ifreq request;
    struct sockaddr_in *sockaddr_in;
   
    if (strlen (name) > IFNAMSIZ)
    {
        fprintf(stderr, "Invalid interface name: %s\n", name);
        return -1;
    }
   
    strncpy (request.ifr_name, name, IFNAMSIZ);
   
    if (ioctl(fd, SIOCGIFADDR, &request))
    {
        fprintf(stderr, "ioctl call failed for interface name: %s\n", name);   
        return -1;
    }
   
    sockaddr_in = (struct sockaddr_in *) &request.ifr_addr;
   
    memcpy (address, &sockaddr_in->sin_addr, sizeof(*address));
   
    return 0;
}

int bindSocketWithName(int fd, const char *name)
{
    struct sockaddr_in address;

    memset(&address, 0, sizeof(address));

    if ( !getAddressFromName(fd, &address.sin_addr, name) )
    {
        address.sin_family = AF_INET;
        address.sin_port   = 0;
        if (bind (fd, (struct sockaddr *) &address, sizeof(address)))
        {
            fprintf(stderr, "Failed to bind to socket: %s\n", name);
            return -1;
        }
    }
    else
    {
        /* error already reported by getAddressFromName() */
        return -1;
    }
   
    return 0;
}

Comments

Popular Posts