Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Operation not permitted : Server-setsockopt() error for SO_BINDTODEVICE

I have this c executable file that is responsible for processing packets. I am trying to use setsockopt to bind an interface to my socket sockfd

#define MULTICAST

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <net/if.h>

#define TS_PACKET_SIZE 188

long long int usecDiff(struct timeval* time_stop, struct timeval* time_start)
{
    long long int temp = 0;
    long long int utemp = 0;

    if (time_stop && time_start) {
        if (time_stop->tv_usec >= time_start->tv_usec) {
            utemp = time_stop->tv_usec - time_start->tv_usec;    
            temp = time_stop->tv_sec - time_start->tv_sec;
        } else {
            utemp = time_stop->tv_usec + 1000000 - time_start->tv_usec;       
            temp = time_stop->tv_sec - 1 - time_start->tv_sec;
        }
        if (temp >= 0 && utemp >= 0) {
            temp = (temp * 1000000) + utemp;
            } else {
            fprintf(stderr, "start time %ld.%ld is after stop time %ld.%ld\n", time_start->tv_sec, time_start->tv_usec, time_stop->tv_sec, time_stop->tv_usec);
            temp = -1;
        }
    } else {
        fprintf(stderr, "memory is garbaged?\n");
        temp = -1;
    }
        return temp;
}


int main (int argc, char *argv[]) {

    int sockfd;
    int len;
    int sent;
    int transport_fd;
    struct sockaddr_in addr;
    unsigned long int packet_size;   
    char* tsfile;
    unsigned char* send_buf;
    unsigned int bitrate;
    unsigned long long int packet_time;
    unsigned long long int real_time;    
    struct timeval time_start;
    struct timeval time_stop;
    struct timespec nano_sleep_packet;
    char *opt; // "eth0" or "eth1"

    memset(&addr, 0, sizeof(addr));
    memset(&time_start, 0, sizeof(time_start));
    memset(&time_stop, 0, sizeof(time_stop));
    memset(&nano_sleep_packet, 0, sizeof(nano_sleep_packet));

    if(argc < 6 ) {
    fprintf(stderr, "Usage: %s file.ts ipaddr port bitrate [ts_packet_per_ip_packet]\n", argv[0]);
    fprintf(stderr, "ts_packet_per_ip_packet default is 7\n");
    fprintf(stderr, "bit rate refers to transport stream bit rate\n");
    fprintf(stderr, "zero bitrate is 100.000.000 bps\n");
    fprintf(stderr, "This is the modified file\n");
    return 0;
    } else {
    tsfile = argv[1];
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[2]);
    addr.sin_port = htons(atoi(argv[3]));
    bitrate = atoi(argv[4]);
    opt = argv[5];
    if (bitrate <= 0) {
        bitrate = 100000000;
    }
    //To be modified : original arguments are 5 and have to be increased by one
    if (argc >= 7) {
        packet_size = strtoul(argv[5], 0, 0) * TS_PACKET_SIZE;
    } else {
        packet_size = 7 * TS_PACKET_SIZE;
    }
    }

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0) {
    perror("socket(): error ");
    return 0;
    } 
    struct ifreq ifr;

    memset(&ifr, 0, sizeof(ifr));
    //snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), opt); //
    setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)); // 6 is the size of MAC address in bytes
    ioctl(sockfd, SIOCGIFINDEX, &ifr);
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
    perror("Server-setsockopt() error for SO_BINDTODEVICE");
    close(sockfd);
    return 0;
    }

    transport_fd = open(tsfile, O_RDONLY);
    if(transport_fd < 0) {
    fprintf(stderr, "can't open file %s\n", tsfile);
    close(sockfd);
    return 0;
    } 

    int completed = 0;
    send_buf = malloc(packet_size);
    packet_time = 0;
    real_time = 0;

    gettimeofday(&time_start, 0);
    while (!completed) {

        gettimeofday(&time_stop, 0);
        real_time = usecDiff(&time_stop, &time_start);
        if (real_time * bitrate > packet_time * 1000000) { /* theorical bits against sent bits */
        len = read(transport_fd, send_buf, packet_size);
        if(len < 0) {
            fprintf(stderr, "ts file read error \n");
            completed = 1;
        } else if (len == 0) {
            fprintf(stderr, "ts sent done\n");
                completed = 1;
        } else {
            sent = sendto(sockfd, send_buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
            if(sent <= 0) {
            perror("send(): error ");
            completed = 1;
            } else {
            packet_time += packet_size * 8;
            }
        }
        } else {
            nanosleep(&nano_sleep_packet, 0);
        }
    }

    close(transport_fd);
    close(sockfd);
    free(send_buf);
    return 0;    
}

I call this file using subprocess.Popen() in a Python script as follows

self.proc.append(subprocess.Popen(["tsudpsend", "/dev/stdin", out_address, str(port), "10000000", "eth0"], stdin=self.proc[-1].stdout, stderr=open("/home/cayman/log.txt", "w")))

When I check the pipe output I found the following error:

Server-setsockopt() error for SO_BINDTODEVICE: Operation not permitted

I spent some time looking into this thread and I saw few suggestions but in vain. Can anyone please guide me how to resolve this. Thanks.


2 Answers

The issue with SO_BINDTODEVICE is that you must be root to use it. If I run your code as root, I do not get that error.

What you need to do instead is get the list of interfaces via getifaddrs and loop through the interface list until the name matches the one you want. Then you pull out the IPv4 address in question and use that to populate a sockaddr_in in a call to bind.

struct sockaddr_in bind_addr;
memset(&bind_addr, 0, sizeof(bind_addr);
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons(atoi(argv[3]));

...

struct ifaddrs *ifa, *ifa_tmp;
if (getifaddrs(&ifa) == -1) {
    perror("getifaddrs failed");
    exit(1);
}
ifa_tmp = ifa;
while (ifa_tmp) {
    if (!strcmp(ifa_tmp->ifa_name, opt) && 
            ifa_tmp->ifa_addr && (ifa_tmp->ifa_addr->sa_family == AF_INET)) {
        printf("addr = %s\n", 
            inet_ntoa(((struct sockaddr_in *)ifa_tmp->ifa_addr)->sin_addr);
        bind_addr.sin_addr.s_addr = 
            ((struct sockaddr_in *)ifa_tmp->ifa_addr)->sin_addr.s_addr;
        break;
    }
    ifa_tmp = ifa_tmp->ifa_next;
}
freeifaddrs(ifa);

if (bind_addr.sin_addr.s_addr == 0) {
    printf("interface not found\n");
    exit(1);
}

...

if (bind(sockfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) {
    perror("bind failed");
    exit(1);
}

...
like image 116
dbush Avatar answered Sep 09 '25 13:09

dbush


To be precise, you need the CAP_NET_RAW capability.

like image 22
Trần Việt Hoàng Avatar answered Sep 09 '25 13:09

Trần Việt Hoàng