I am hoping that this is a relatively simple answer to my question. I am attempting to learn how to program in sockets and I just can't seem to get a basic UDP example working. I am attempting to just send a string "hello" from one computer to another read it on the screen.
I started with this website
https://www.programminglogic.com/sockets-programming-in-c-using-udp-datagrams/
then tried:
https://www.abc.se/~m6695/udp.html
I have also looked at
https://www.cs.rutgers.edu/~pxk/417/notes/sockets/udp.html
but it seems that each time I try to code nothing happens. I get no errors, I just don't get any communication. I can ping my server computer from my client computer at least
My setup:
I currently have two computers one running Ubuntu 16.04 the other 14.04.
I was hoping to start off with something easy by connecting them directly. But I have tried connecting them through a switch. No success.
On the "server" computer I set up my network card to have by opening the terminal and
sudo ifconfig enps60 192.168.1.1 netmask 255.255.255.0
To create my server code in terminal I type
gedit udp_server.c
and in the text editor I type
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
int main()
{
int udpSocket, nBytes;
char buffer[1024];
struct sockaddr_in serverAddr, cientAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size, client_addr_size;
/* Create UDP socket */
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
/* Configure settings in address struct */
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(0);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.1");
memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
/* Bind Socket */
bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
printf("bind complete. Port number = %d\n", ntohs(serverAddr.sin_port));
addr_size = sizeof(serverStorage);
while(1)
{
nBytes = recvfrom(udpSocket, buffer, 1024, 0, (struct sockaddr *)%clientAddr, &addr_size);
printf("any bytes: %d\n, nBytes);
}
}
I then compile my code in terminal by typing
gcc -o udp_server udp_server.c
On my client computer (ubuntu 14.04) I open the terminal and type
sudo ifconfig eth0 192.168.1.2 netmask 255.255.255.0
then to create my client code in terminal I type
gedit udp_client.c
and in the text editor I type
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
int udpSocket, portNum, nBytes;
char buffer[1024] = "hello";
struct sockaddr_in serverAddr;
socklen_t addr_size;
unsigned int alen;
/*create UDP socket */
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
/* Configure settings in address struct */
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(0);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.1");
memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
addr_size = sizeof(serverAddr);
while(1)
{
printf("sending %s\n", buffer);
nBytes = strlen(buffer);
sendto(udpSocket, buffer, nBytes, 0, (struct sockaddr *)&serverAddr, addr_size);
sleep(1);
}
return 0;
}
I apologize if my code isn't that polished also.
Ok, well thanks guys for all the suggestions, but I do have some code that works.
I still am very unsure how any of this works, but for starters I was selecting an incorrect port number. According to Steve Summitt I checked my connection using Netcat, and when I would look at port 0 netcat would kindly let me know that I can't choose a port number that low. This is a bit confusing as everything I am reading says that port numbers start at 0. Also there is a thing about trying not to use well known ports which used to be 0-255 but is now 0-1023. I was trying to figure out if I know what ports I can use or not and I came across this.
https://sort.veritas.com/public/documents/vie/7.3/aix/productguides/html/sfcfsha_config/apgs04s03.htm
Although I am unsure the context is only when using IPv6 protocol?
Also I found this list.
https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
I guess there are a bunch of applications on every machine that uses these ports?
The quick solution: I set the port to 1024 and forgot to bind my client socket to my client computers network card. I also changed the way I do memset (I thought I understood it, but I might not)
for the complete solution:
Current Setup:
I have two Ubuntu machines connected together by a simple switch. The machines are what I am calling a server (ubuntu 16.04) and a client (ubuntu 14.04).
The first step I did is assign static IPs to each machine (I think this is only recommended for my server computer, but I didn't know how to get my DHCP address of my client in c).
in my server computer I opened my terminal (ctrl + alt + t)
sudo ifconfig enp6s0 192.168.1.1 netmask 255.255.255.0
in the client computer I opened my terminal and typed
sudo ifconfig eth0 192.168.1.2 netmask 255.255.255.0
now going back to the server computer in terminal I typed
gedit udp_server.c
which opened up a text editor
/************** UDP Server Code *****************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
int main()
{
int udpSocket, nBytes;
char buffer[5];
struct sockaddr_in serverAddr, clientAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size, client_addr_size;
int i;
/* Create UDP socket */
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
/* Configure settings in address struct */
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(1024);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero);
/* Bind socket with address struct */
bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
printf("bind complete port number %d\n", ntohs(serverAddr.sin_port));
/* Initialize size variable to be used later on */
addr_size = sizeof(serverStorage);
while(1)
{
nBytes = recvfrom(udpSocket, buffer, 5, (struct sockaddr *)&clientAddr, &addr_size);
printf("any bytes %d\n", nBytes);
}
return 0;
}
now for the client code I typed (on my client computer)
gedit udp_client.c
which opened up a text editor
/************ UDP client code **********/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
int main()
{
int udpSocket, portNum, nBytes;
char buffer[1024] = "hello";
struct sockaddr_in serverAddr, clientAddr;
socklen_t addr_size;
unsigned int alen;
/* Create UDP socket */
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
/* Configure client settings in address struct */
memset((char *)&clientAddr, 0, sizeof(clientAddr));
clientAddr.sin_family = AF_INET;
clientAddr.sin_addr.s_addr = inet_addr("192.168.1.2");
clientAddr.sin_port = htons(0);
if(bind(udpSocket, (struct sockaddr *)&clientAddr, sizeof(clientAddr)) < 0)
{
perror("bind failed");
return 0;
}
printf("client bound");
/* configure server settings in struct */
memset((char *)&serverAddr, '0', sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(1024);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.1");
printf("the socket ip addr is: %s\n", inet_ntoa(clientAddr.sin_addr));
addr_size = sizeof(serverAddr);
while(1)
{
sleep(2);
nBytes = strlen(buffer);
if(sendto(udpSocket, buffer, nBytes, 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
{
perror("send to");
exit(1);
}
printf("sent %s\n", buffer);
}
return 0;
}
some extra notes
I actually don't know if I need to bind my sockets on my client side? I bound it to port 0 and everything worked peachey. I am thinking might run into issues if I try to receive from the server?
I am still working on a metaphor for how to think about ports
I am going with that the IP address is the network card address (city) and the port number is the address within that city (task, application). Like many people can reside in a city, many tasks or programs can reside on a machine and each one might want to communicate to the world.
This is only the most basic information (I don't cover other things like connect(), send(), write(), sendmsg() as I don't know how to use them)
hopefully this post makes it easier on the next guy.
I'm writing a simple server code.
After running the code,I'm trying to connect the server using "telnet localhost 8000" and I get the next error: "Connection closed by foreign host" and the server closes.
this is the code I wrote:
void main(int argv,void * argc)
{
//int socket(int af, int type, int protocol);
int listen_sckt;
int new_socket;
int addrlen;
struct sockaddr_in addr;
/*
#include <netinet/in.h>
struct sockaddr_in {
short sin_family; // e.g. AF_INET
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
struct in_addr {
unsigned long s_addr; // load with inet_aton()
};
*/
listen_sckt = socket(AF_INET,SOCK_STREAM,0);
if(listen_sckt == -1){
perror("SOCKET ERR\n");
return;
}
printf("Socket succesfulyl opened\n");
{
/*
binding a docket
syntax:
int bind(int s, struct sockaddr *addr, int addrlen);
connect the socket to a logic port.
so the other side will know where to "find" the other side
*/
}
addr.sin_family = AF_INET;x`
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8000);
//binding command
if(-1 == bind(listen_sckt,(struct sockaddr*)&addr,sizeof(addr))){
perror("BINDING ERR\n");
}
printf("Binding succesfully done\n");
/* Listen()
Before any connections can be accepted, the socket must be told to listen
for connections and also the maximum number of pending connections using listen()
Includes:
#include <sys/socket.h>
Syntax:
int listen(int socket, int backlog);
socket - the socket file descriptor
backlog - the max number of pedding the socket will hold
C source:
*/
listen(listen_sckt,2);
/*
To actually tell the server to accept a connection, you have to use the function accept()
Includes:
#include <sys/socket.h>
Syntax:
int accept(int s, struct sockaddr *addr, int *addrlen);
*/
addrlen = sizeof(struct sockaddr_in);
new_socket = accept(listen_sckt,(struct sockaddr*)&addr,&addrlen);
if(new_socket<0){
perror("Accept ERR\n");
}
printf("Acept success\n");
}
Thanks.
That's because once you have accepted the connection, you exit the program which causes all descriptors (including sockets) to be closed.
If you want to do something with the socket, you should do that after the accept call. Like having a read/write loop.
And you should probably have a loop around the whole thing, so your program can accept new connections once the previous is closed.
Where am I going wrong with my experimentation:
I am trying to experimentally deduce the details of creating a simple shellcode. My first impression was I probably would not be able to use imports, as my shellcode is not linked by the compiler of the target program. I then began wondering how small I can make a simple shell command interface over a socket without using imports, so I wrote up some code; and started ignoring implicit call warnings:
// socket_family
#define AF_INET 2
#define AF_PACKET 17
// socket_type
#define SOCK_STREAM 1
typedef unsigned short sa_family_t;
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
struct in_addr {
unsigned long s_addr; // load with inet_pton()
};
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, above
char sin_zero[8]; // zero this if you want to
};
int main(void) {
int sfd;
const short family = AF_INET;
const char host[] = "127.0.0.1";
struct sockaddr addr;
struct sockaddr_in *addr_full = (struct sockaddr_in*)&addr;
if (sfd = socket(family, SOCK_STREAM, 0) < 0) return 1;
memset(&addr, 0, sizeof(struct sockaddr));
addr_full->sin_family = family;
addr_full->sin_port = htons(8000);
inet_pton(family, host, &(addr_full->sin_addr.s_addr));
if (connect(sfd, &addr, sizeof(struct sockaddr)) < 0) return 2;
close(sfd);
return 0;
}
Somewhere along the line I am not connecting to my python -m SimpleHTTPServer properly; which reports Serving HTTP on 0.0.0.0 port 8000.
$ gcc my_program.c -o my_program
$ ./my_program
$ echo $?
2
I am taking a Coursera course on software security; and a lot of this topic is new to me.
EDIT:
After removing redefinitions and adding includes:
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
int main(void) {
int sfd;
const short family = AF_INET;
const char host[] = "127.0.0.1";
struct sockaddr_in addr_full; // = (struct sockaddr_in*)&addr;
if (sfd = socket(family, SOCK_STREAM, 0) < 0) return 1;
memset(&addr_full, 0, sizeof(struct sockaddr));
addr_full.sin_family = family;
addr_full.sin_port = htons(8000);
inet_pton(family, host, &(addr_full.sin_addr.s_addr));
if (connect(sfd, (struct sockaddr*)&addr_full, sizeof(struct sockaddr)) < 0) return 2;
close(sfd);
return 0;
}
The program still exits with 2.
The socket file descriptor was not being assigned properly. Adding parenthesis, I should be assigning the descriptor, then checking its value:
if ((sfd = socket(family, SOCK_STREAM, 0)) < 0) return 1;
Motoko,
please grab C language book and check operations priority. And do it regulary, even experienced developers make errors here. And don't write this crazy style, please learn how to write for humans:
if (sfd = socket(family, SOCK_STREAM, 0) < 0) return 1;
You should instead do something like this:
sfd = socket(family, SOCK_STREAM, 0);
if (sfd < 0)
{
/* perror() is pretty old style but some good check is recommended. */
perror("socket");
return 1;
}
SPOILER: < just has higher priority here so sfd is always 0 in your code.
Your assumption about the include files is wrong. The include files only contain the function declarations, not the code itself. They don't determine how your program will get linked to shared libraries.
Include files only tell the compiler that the functions you call inside your program exist somewhere, in some external library or in another source file you wrote. After compilation the linker tries to find all the functions that your program needs, and if they are not found, linking fails.
Try compiling your code with the necessary files included (socket.h etc.). This will give you useful warnings, for example when you pass an argument of the wrong type.
The following code is a socket programming sample for a TCP client.
But when I run this, connect() is returned as Address family not supported by protocol.
I have heard, this problem will happen if the platform does not support ipv6.
But AF_INET I wrote is ipv4.
Also my server, that is CentOS6.4, is configured within an inet6 addr .
Does anyone know why?
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int
main(){
struct sockaddr_in server;
int sock;
char buf[32];
int n;
sock = socket(AF_INET,SOCK_STREAM,0);
perror("socket");
server.sin_family = AF_INET;
server.sin_port = htons(12345);
inet_pton(AF_INET,"127.0.0.1",&server,sizeof(server));
connect(sock,(struct sockaddr *)&server,sizeof(server));
perror("connect");
memset(buf,0,sizeof(buf));
n = read(sock,buf,sizeof(buf));
perror("read");
printf("%d,%s\n",n,buf);
close(sock);
return 0;
}
The code passes the wrong destination address and wrong number of arguments to inet_pton(). (For the latter the compiler should have warned you about, btw)
This line
inet_pton(AF_INET, "127.0.0.1", &server, sizeof(server));
should be
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
Verbatim from man inet_pton:
int inet_pton(int af, const char *src, void *dst);
AF_INET
[...] The address is converted to
a struct in_addr and copied to dst, which must be sizeof(struct in_addr) (4) bytes (32 bits) long.
Not related to the problem, but also an issue, is that read() returns ssize_t not int.
The following lines shall be adjusted:
int n;
[...]
printf("%d, %s\n", n, buf);
to become:
ssize_t n;
[...]
printf("%zd, %s\n", n, buf);
Set the server address like this;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(port);
I seen this error during bind. Cause was of using localhost instead of IP:
./myprogram localhost:7777
*** exception! `bind' failed for `localhost:7777' (97, Address family not supported by protocol)
./myprogram 127.0.0.1:7777
OK! Listening...
In addition: this error happens on one Linux host and does not appear on another. I check and compare network settings on this machines (lo device, /etc/hosts, /etc/host.conf, etc) and not found essential difference
I have a PC with two network cards. One (eth0) is for LAN/internet and the other for UDP communication with one microcontroller device. The microcontroller has an IP (192.168.7.2) and a MAC address. The second pc network adapter (eth1) has 192.168.7.1.
The microcontroller has a very simple IP stack, so the easiest way for the mc to send UDP packets is to broadcast them.
On the PC side I'd like to receive the broadcasts - but only from eth1. So I try to bind the UDP socket to the eth1 device.
The problems (source code below):
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device)) requires root privileges, why? (setting other options works as user)
getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)buffer, &opt_length) gives "Protocol not available". I would like to read back the device I set via setsockopt command.
Where can I find good info? I checked some Linux-programming, network books, but for example the SO_BINDTODEVICE option I've only found on the internet.
My lengthy (dirty) test program shows the problems. Setting and getting back the SO_RCVTIMEO and SO_BROADCAST options works as expected.
Running the code as user exits with:
could not set SO_BINDTODEVICE (Operation not permitted)"
Running with sudo gives:
SO_BINDTODEVICE set
./mc-test: could not get SO_BINDTODEVICE (Protocol not available)
So, setting the option seems to work but reading it back is not possible?
/* SO_BINDTODEVICE test */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#define MC_IP "192.168.7.2"
#define MC_PORT (54321)
#define MY_PORT (54321)
#define MY_DEVICE "eth1"
#define BUFFERSIZE (1000)
/* global variables */
int sock;
struct sockaddr_in MC_addr;
struct sockaddr_in my_addr;
char buffer[BUFFERSIZE];
int main(int argc, char *argv[])
{
unsigned int echolen, clientlen;
int rc, n;
char opt_buffer[1000];
struct protoent *udp_protoent;
struct timeval receive_timeout;
int optval;
socklen_t opt_length;
/* Create the UDP socket */
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
printf ("%s: failed to create UDP socket (%s) \n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("UDP socket created\n");
/* set the recvfrom timeout value */
receive_timeout.tv_sec = 5;
receive_timeout.tv_usec = 0;
rc=setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &receive_timeout,
sizeof(receive_timeout));
if (rc != 0)
{
printf ("%s: could not set SO_RCVTIMEO (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set timeout to\ntime [s]: %d\ntime [ms]: %d\n", receive_timeout.tv_sec, receive_timeout.tv_usec);
/* verify the recvfrom timeout value */
rc=getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &receive_timeout, &opt_length);
if (rc != 0)
{
printf ("%s: could not get socket options (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("timeout value\ntime [s]: %d\ntime [ms]: %d\n", receive_timeout.tv_sec, receive_timeout.tv_usec);
/* allow broadcast messages for the socket */
int true = 1;
rc=setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true));
if (rc != 0)
{
printf ("%s: could not set SO_BROADCAST (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set SO_BROADCAST\n");
/* verify SO_BROADCAST setting */
rc=getsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, &opt_length);
if (optval != 0)
{
printf("SO_BROADCAST is enabled\n");
}
/* bind the socket to one network device */
const char device[] = MY_DEVICE;
rc=setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device));
if (rc != 0)
{
printf ("%s: could not set SO_BINDTODEVICE (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("SO_BINDTODEVICE set\n");
/* verify SO_BINDTODEVICE setting */
rc = getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)buffer, &opt_length);
if (rc != 0)
{
printf ("%s: could not get SO_BINDTODEVICE (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
if (rc == 0)
{
printf("SO_BINDTODEVICE is: %s\n", buffer);
}
/* Construct the server sockaddr_in structure */
memset(&MC_addr, 0, sizeof(MC_addr)); /* Clear struct */
MC_addr.sin_family = AF_INET; /* Internet/IP */
MC_addr.sin_addr.s_addr = inet_addr(MC_IP); /* IP address */
MC_addr.sin_port = htons(MC_PORT); /* server port */
/* bind my own Port */
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY; /* INADDR_ANY all local addresses */
my_addr.sin_port = htons(MY_PORT);
rc = bind (sock, (struct sockaddr *) &my_addr, sizeof(my_addr));
if (rc < 0)
{
printf ("%s: could not bind port (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("port bound\n");
/* identify mc */
buffer[0] = (char)1;
buffer[1] = (char)0;
send_data (buffer, 2);
printf ("sent command: %d\n", (char)buffer[0]);
rc=receive_data(buffer);
printf ("%d bytes received\n", rc);
buffer[rc] = (char)0; /* string end symbol */
printf ("%d - %s\n", (int)(char)buffer[0], &buffer[1]);
close(sock);
printf ("socket closed\n");
exit(0);
}
/* send data to the MC *****************************************************/
/* buffer points to the bytes to send */
/* buf_length is the number of bytes to send */
/* returns allways 0 */
int send_data( char *buffer, int buf_length )
{
int rc;
rc = sendto (sock, buffer, buf_length, 0,
(struct sockaddr *) &MC_addr,
sizeof(MC_addr));
if (rc < 0)
{
printf ("could not send data\n");
close (sock);
exit (EXIT_FAILURE);
}
return(0);
}
/* receive data from the MC *****************************************************/
/* buffer points to the memory for the received data */
/* max BUFFERSIZE bytes can be received */
/* returns number of bytes received */
int receive_data(char *buffer)
{
int rc, MC_addr_length;
MC_addr_length = sizeof(MC_addr);
rc = recvfrom (sock, buffer, BUFFERSIZE, 0,
(struct sockaddr *) &MC_addr,
&MC_addr_length);
if (rc < 0)
{
printf ("could not receive data\n");
close (sock);
exit (EXIT_FAILURE);
}
return(rc);
}
I have been looking into this for a while after seeing conflicting answers to how SO_BINDTODEVICE is actually used. Some sources claim that the correct usage is to pass in a struct ifreq pointer, which has the device name and index obtained via an ioctl. For example:
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth0");
ioctl(fd, SIOCGIFINDEX, &ifr);
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
Where as Beej's networking tutorial says to pass the device name as a char pointer. For example:
char *devname = "eth0";
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, devname, strlen(devname));
I have tried both of these methods and they both do what is required, but I wanted to note that the device index obtained in the first method is superfluous. If you look at the kernel code in net/core/sock.c, sock_bindtodevice just copies the device name string, calls dev_get_by_name_rcu to get the device and binds to it.
The reason that the first approach works is that the device name is the first element in the ifreq structure, see http://linux.die.net/man/7/netdevice.
NOTE: SO_BINDTODEVICE requires elevated permissions:
run the executable with full root permission
after building the executable you can use sudo setcap to grant the executable permission to use this specific socket option then you can run the executable without root permission and the executable has permission to use the SO_BINDTODEVICE feature (via earlier call to setcap).
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4);
Above line of code is enough to receive messages from eth0 interface only.
I tested this on Linux.
NOTE: It won't work if there is a bridge interface controlling actual interfaces.
Best regards,
Santosh.
OK, I've looked into it a little more. SO_BINDTODEVICE was considered "near obsolete" back in 1999, and is root-only due to some unspecified "security implications" (I couldn't find out exactly what).
However, you should be able to get the behaviour you want by binding to INADDR_ANY and setting the IP_PKTINFO socketopt. This will pass an extra message on the socket that contains a pktinfo structure describing the incoming packet. This structure includes the index of the interface that the packet came in on:
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};
The ipi_ifindex matches with the ifr_ifindex from the struct ifreq returned by the netdevice ioctls like SIOCGIFCONF. So you should be able to use that to ignore packets received on interfaces other than the one you're interested in.
Doco for IP_PKTINFO is in ip(7) and for the interface ioctls in netdevice(7).
Before Linux 3.8, this socket option could be set, but could not retrieved with getsockopt(). Since Linux 3.8, it is readable. The optlen argument should contain the buffer size available to receive the device name and is recommended to be IFNAMSZ bytes. The real device name length is reported back in the optlen argument.
The problem I ran into seems to be that receiving broadcasts from a specific interface is handled differently by Linux, Windows,...
http://www.developerweb.net/forum/showthread.php?t=5722
I now decided to solve the problem (little documentation and bad portability) by changing the TCP/IP stack of the microcontroller. It will no longer send answers to the broadcast address but instead take the IP/MAC from the incoming UDP packet as the destination IP/MAC. Then I can (on the pc side) simply bind the socket to the IP of eth1.
Cheers,
Michael
Just lookup the IP address of the interface you're interested in with getifaddrs(), and bind your socket to that IP address with bind(). If you enable SO_BROADCAST on the socket you'll then only get broadcasts recieved on that interface.
Or indeed you could skip the getifaddrs() part and just directly bind() to 192.168.7.1 if you like.
I can confirm that sending multicast to specific interface works also like this. See the sample codes below.
However I can't get listener.c program working if the interface is set by SO_BINDTODEVICE to my secondary interface eth4.
I used completely different machine to send the multicast packets and the listener works from interface eth3, not from interface eth4. However, tcpdump shows the packets in both interfaces (sudo tcpdump -i eth4 |grep UDP).
These are modifications to Antony Courtney's sample code:
sender.c and listener.c:
/*
* sender.c -- multicasts "hello, world!" to a multicast group once a second
*
* Antony Courtney, 25/11/94
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define HELLO_PORT 12345
#define HELLO_GROUP "225.0.0.37"
main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, cnt;
struct ip_mreq mreq;
char *message="Hello, World!";
char com[1000];
/* create what looks like an ordinary UDP socket */
if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
perror("socket");
exit(1);
}
/* set up destination address */
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(HELLO_GROUP);
addr.sin_port=htons(HELLO_PORT);
u_char ttl=7;
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth4");
ioctl(fd, SIOCGIFINDEX, &ifr);
printf("[[%d]]\n", ifr.ifr_ifindex );
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
inet_ntop(AF_INET, &(addr), com, INET_ADDRSTRLEN);
printf("addr=%s\n", com );
/* now just sendto() our destination! */
while (1) {
if (sendto(fd,message,strlen(message),0,(struct sockaddr *) &addr,
sizeof(addr)) < 0) {
perror("sendto");
exit(1);
}
sleep(1);
}
}
listener.c :
/*
* listener.c -- joins a multicast group and echoes all data it receives from
* the group to its stdout...
*
* Antony Courtney, 25/11/94
* Modified by: Frédéric Bastien (25/03/04)
* to compile without warning and work correctly
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define HELLO_PORT 12345
#define HELLO_GROUP "225.0.0.37"
#define MSGBUFSIZE 256
main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, nbytes,addrlen;
struct ip_mreq mreq;
char msgbuf[MSGBUFSIZE];
u_int yes=1; /*** MODIFICATION TO ORIGINAL */
/* create what looks like an ordinary UDP socket */
if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
perror("socket");
exit(1);
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth4");
ioctl(fd, SIOCGIFINDEX, &ifr);
printf("[[%d]]\n", ifr.ifr_ifindex );
if( setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq)) < 0 )
{
perror("SO_BINDTODEVICE");
exit(1);
}
/**** MODIFICATION TO ORIGINAL */
/* allow multiple sockets to use the same PORT number */
if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) {
perror("Reusing ADDR failed");
exit(1);
}
/*** END OF MODIFICATION TO ORIGINAL */
/* set up destination address */
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender */
addr.sin_port=htons(HELLO_PORT);
/* bind to receive address */
if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
perror("bind");
exit(1);
}
/*
ifr.ifr_flags = IFF_UP | IFF_ALLMULTI | IFF_MULTICAST;
ioctl(fd, SIOCSIFFLAGS, &ifr );
*/
/* use setsockopt() to request that the kernel join a multicast group */
mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) {
perror("setsockopt");
exit(1);
}
/* now just enter a read-print loop */
while (1) {
addrlen=sizeof(addr);
if ((nbytes=recvfrom(fd,msgbuf,MSGBUFSIZE,0,
(struct sockaddr *) &addr,&addrlen)) < 0) {
perror("recvfrom");
exit(1);
}
msgbuf[nbytes]='\0';
puts(msgbuf);
}
}
If you are unable to receive multicast packets on the secondary interface, it could well be reverse path filtering that is blocking them. This filters out received packets if those packets would not go out on the interface they are coming in on.
To disable this feature, use the following:
sudo -i
echo 2 > /proc/sys/net/ipv4/conf/eth1/rp_filter
echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter
exit
The answer to question 2 seems to be that getsockopt is just not supported for the SO_BINDTODEVICE option. In the Linux kernel source (2.6.27) the option is only handled in the sock_setsockopt function of linux-2.6.27.25-0.1/net/core/sock.c
For question 3 it seems, lots of people recommend the "UNIX network programming" book by W. Richard Stevens.
I looked through the socket options pages of the google book online version - the SO_BINDTODEVICE option is not listed in table 7.1 and 7.2 :-(
...maybe because this option is Linux only?
setsocketopt needs device index, not name. Furthermore you should use struct ifreq to pass the index:
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth3");
ioctl(s, SIOCGIFINDEX, &ifr)
setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));
I've solved a similar problem by adding the following to /etc/sudoers (or in a file in /etc/sudoers.d):
myuser myhost=(root) NOPASSWD: /usr/bin/fping
Then instead of using fping directory, use sudo fping.