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.
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);
}
...
To be precise, you need the CAP_NET_RAW capability.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With