Disable self-reception of UDP multicasts - c

I am having a real bear of time trying to understand IP multicasting. In particular, I want to disable packets from being looped back into the multicast group.
I just threw this code together to example what I am seeing.
/*
example.c
*/
#include <sys/types.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#define EXAMPLE_PORT 6000
#define EXAMPLE_GROUP "224.0.0.1"
const long MYSENDER = 0; // send thread ID
const long MYRECVR = 1; // recv thread ID
int status;
int sock;
void *sender(void *threadid);
void *receiver(void *threadid);
main(int argc) {
pthread_t threads[2];
struct sockaddr_in addr;
/* Set up socket */
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == -1) {
perror("socket");
exit(1);
}
status = pthread_create(&threads[MYRECVR], NULL, receiver, (void *) MYRECVR); // Start the server thread and check for error
if (status) {
fprintf(stderr, "Error: pthread_create(receiver) returned %d\n", status);
exit(-1);
}
status = pthread_create(&threads[MYSENDER], NULL, sender, (void *) MYSENDER); // Start the client thread and check for error
if (status) {
fprintf(stderr, "Error: pthread_create(sender) returned %d\n", status);
exit(-1);
}
pthread_join(threads[MYRECVR], NULL); // main() will wait for the server thread to complete
pthread_join(threads[MYSENDER], NULL); // main() will wait for the client thread to complete
}
void *sender(void *threadid) {
int c;
char message[50];
struct sockaddr_in addr;
int addrlen, cnt;
memset((char *) &addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_addr.s_addr = inet_addr(EXAMPLE_GROUP);
addr.sin_port = htons(EXAMPLE_PORT);
addrlen = sizeof (addr);
fprintf(stderr, "Starting sender thread!\n");
/* Send */
while (1) {
time_t t = time(0);
sprintf(message, "time is %-24.24s", ctime(&t));
printf("sending: %s\n", message);
cnt = sendto(sock, message, sizeof (message), 0,
(struct sockaddr *) &addr, addrlen);
if (cnt < 0) {
perror("sendto");
exit(1);
}
sleep(5);
}
return 0;
}
void *receiver(void *threadid) {
int opt = 1;
char message[50];
struct sockaddr_in addr;
int addrlen, cnt;
struct ip_mreq mreq;
fprintf(stderr, "Starting receiver thread!\n");
memset((char *) &addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(EXAMPLE_PORT);
addrlen = sizeof (addr);
/* Receive */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
if (bind(sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
perror("bind");
exit(1);
}
mreq.imr_multiaddr.s_addr = inet_addr(EXAMPLE_GROUP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof (mreq)) < 0) {
perror("setsockopt mreq");
exit(1);
}
while (1) {
cnt = recvfrom(sock, message, sizeof (message), 0,
(struct sockaddr *) &addr, &addrlen);
if (cnt < 0) {
perror("recvfrom");
exit(1);
} else if (cnt == 0) {
break;
}
printf("%s: message = \"%s\"\n", inet_ntoa(addr.sin_addr), message);
}
return 0;
}
It runs fine and produces the following output...
picozed#ubuntu:/tmp$ ./test
Starting receiver thread!
Starting sender thread!
sending: time is Wed Aug 16 19:23:29 2017
10.249.27.51: message = "time is Wed Aug 16 19:23:29 2017"
sending: time is Wed Aug 16 19:23:34 2017
10.249.27.51: message = "time is Wed Aug 16 19:23:34 2017"
^C
picozed#ubuntu:/tmp$
But, I would like to prevent the messages from being looped back in...

By default, most OS's will loop outgoing multicast traffic back to the local machine. If you want to disable this behavior, you need to set the IP_MULTICAST_LOOP socket option to 0.
int opt=0;
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &opt, sizeof (opt)) < 0) {
perror("setsockopt");
exit(1);
}

Related

Could not able to do bidirectional multicast connection using UDP

Have to make it server & client as bidirectional where I should able to send & receive data in console.
I can able to send data from server to client but not able to receive any data from client.Stuck in this for long time could not know how to resolve it.
I just started working on networking any lead on this really helpful.
Here is my code.
Server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
struct in_addr localInterface;
struct sockaddr_in groupSock, rcv_addr;
int sd;
char databuf[1024];
int datalen = sizeof(databuf);
int main(int argc, char *argv[])
{
/* Create a datagram socket on which to send. */
socklen_t rcv_addr_size = sizeof(struct sockaddr_in);
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening the datagram socket...OK.\n");
memset((char*) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
groupSock.sin_port = htons(4321);
localInterface.s_addr = inet_addr("127.0.0.1");
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char*) &localInterface,
sizeof(localInterface)) < 0) {
perror("Setting local interface error");
exit(1);
} else
printf("Setting the local interface...OK\n");
int read_size;
while (1) {
memset(databuf, 0x00, sizeof(databuf));
scanf("%s", databuf);
datalen = strlen(databuf) + 1;
if (sendto(sd, databuf, datalen, 0, (struct sockaddr*) &groupSock,
sizeof(groupSock)) < 0)
datalen = 1024;
memset(databuf, 0x00, sizeof(databuf));
read_size = recvfrom(sd, databuf, datalen, 0,
(struct sockaddr*) &rcv_addr, &rcv_addr_size);
printf("The message from multicast ckient is: \"%s\"\n", databuf);
}
return 0;
}
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
struct sockaddr_in localSock, rcv_addr;
struct ip_mreq group;
int sd;
int datalen;
char databuf[1024];
int main(int argc, char *argv[]) {
/* Create a datagram socket on which to receive. */
socklen_t rcv_addr_size = sizeof(struct sockaddr_in);
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening datagram socket....OK.\n");
{
int reuse = 1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse,
sizeof(reuse)) < 0) {
perror("Setting SO_REUSEADDR error");
close(sd);
exit(1);
} else
printf("Setting SO_REUSEADDR...OK.\n");
}
memset((char*) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(4321);
localSock.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*) &localSock, sizeof(localSock))) {
perror("Binding datagram socket error");
close(sd);
exit(1);
} else
printf("Binding datagram socket...OK.\n");
group.imr_multiaddr.s_addr = inet_addr("226.1.1.1");
group.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &group,
sizeof(group)) < 0) {
perror("Adding multicast group error");
close(sd);
exit(1);
} else
printf("Adding multicast group...OK.\n");
int read_size;
while (1) {
datalen = 1024;
memset(databuf, 0x00, sizeof(databuf));
read_size = recvfrom(sd, databuf, datalen, 0,
(struct sockaddr*) &rcv_addr, &rcv_addr_size);
printf("The message from multicast server is: \"%s\"\n", databuf);
memset(databuf, 0x00, sizeof(databuf));
scanf("%s", databuf);
datalen = strlen(databuf) + 1;
if (sendto(sd, databuf, datalen, 0, (struct sockaddr*) &localSock,
sizeof(localSock)) < 0)
break;
}
return 0;
}
Your server is sending to the wrong address.
The server is using localSock as the destination address for sendto:
if(sendto(sd, databuf, datalen, 0, (struct sockaddr*)&localSock, sizeof(localSock)) < 0)
But you have that address set to 0:
localSock.sin_family = AF_INET;
localSock.sin_port = htons(4321);
localSock.sin_addr.s_addr = INADDR_ANY;
You want to instead use rcv_addr, which was populated as the source address from recvfrom. Using this address as the destination will send the response back where it came from:
if(sendto(sd, databuf, datalen, 0, (struct sockaddr*)&rcv_addr, sizeof(rcv_addr)) < 0)
Multicast connection is like broadcast communication, you send the packet to a forum of receivers, awaiting to read it, and they consume it (if it arrives). This normally means that you have to arrange for bidirectional connection in a way that allows all people skeaking in a room and selecting yourself the responses, as everybody is talking to everybody in a multicast channel. Imagine you are chatting in a IRC channel, and you have to arrange with somebody how to direct messages only to that person, but with the anarchy of anybody being able to respond to such messages in the way they want. You have always to think that what you say, you say to everybody susbscribed in that multicast channel, so normally you will receive several response packets, from which you will have to select...

Multicast recv from another device connected to same switch not working despite traffic shown on tcpdump

I'm trying to receive multicast data from an embedded device connected to the same switch as my desktop and am unable to receive multicast messages on my recv app below.
When I do a tcpdump or run Wireshark against the interface connected to the switch, I see the packets show up but for some reason they don't in my app.
I'm using INADDR_ANY for the interface address.
Could someone shed some light on why the messages would be on Wireshark/Tcpdump but not received by the application?
#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>
#define UDP_PORT 5403
#define UDP_GROUP "225.0.0.1"
#define MAX_BUFFER_SIZE 256
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, nbytes;
socklen_t addrlen;
struct ip_mreq mreq;
char msgbuf[MAX_BUFFER_SIZE];
u_int reuse_port = 1;
// Create a socket
fd = socket(AF_INET,SOCK_DGRAM,0);
if (fd < 0)
{
perror("create socket failed");
return -1;
}
// allow multiple sockets to use the same PORT number
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_port, sizeof(reuse_port)) < 0)
{
perror("Reusing port number failed");
return -1;
}
// set up destination address
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(UDP_PORT);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
return -1;
}
// Set the recvfrom timeout after 1 s
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
{
perror("Error setting recvfrom timeout\n");
return -1;
}
// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(UDP_GROUP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
perror("setsockopt");
return -1;
}
addrlen = sizeof(addr);
printf("Begin recvfrom(...) infinite loop\n");
while (true)
{
nbytes = recvfrom(fd, msgbuf, MAX_BUFFER_SIZE, 0, (struct sockaddr *)&addr, &addrlen);
if (nbytes < 0)
{
printf("recvfrom timeout\n");
}
else
{
printf("message received: %s\n", msgbuf);
}
}
return 1;
}

C, Socket, pthread: read doesn't work on a new thread

I'm making a client-server program in C using threads.
I've got this problem: on the server, on thread #1 (number_one), function "read" works fine. But when I create another thread #2 (number_two), on this one something goes wrong. Parameters are passed in the right way (I think).
-->thread number_one
...
char message[256];
int new_connection=accept(master_sock,NULL,NULL);
pthread_t temp
if(pthread_create(&temp , NULL , number_two , (void*) &new_connection))
{
perror("pthread_create failed");
exit(-2);
}
else
{
puts("number_two created");
if(read(new_connection, message, 256) > 0)
printf("Message from client is %s", message);
}
if(pthread_detach(temp))
{
perror("detach failed");
exit(-3);
}
...
---> thread number_two
void *number_two(void *sock_desc)
{
int sock = *(int*)sock_desc;
int read_size;
char client_message[2000];
read_size=read(sock, client_message, 256);
client_message[read_size]='\0';
return 0;
}
In "number_one", read waits an input from the client, and then it sets correctly the buffer "message".
In "number_two", read does not wait the client and does not set the buffer "client_message".
Thank you.
Please try my code? it works, I think it is the same with your code.
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#define INVALID_SOCKET_FD (-1)
int create_tcp_server_socket(unsigned short port, bool bind_local, int backlog,
char *caller_name)
{
int socket_fd = INVALID_SOCKET_FD;
struct sockaddr_storage server_addr;
unsigned int yes = 1;
// just try ipv4
if (socket_fd < 0 && (socket_fd = socket(PF_INET, SOCK_STREAM, 0)) >= 0) {
struct sockaddr_in *s4 = (struct sockaddr_in *)&server_addr;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
memset(&server_addr, 0, sizeof(server_addr));
s4->sin_family = AF_INET;
s4->sin_port = htons(port);
if (bind_local)
s4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else
s4->sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(socket_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
close(socket_fd);
printf("Server: Failed to bind ipv4 server socket.\n");
return INVALID_SOCKET_FD;
}
}
else if (socket_fd < 0) {
printf("Server: Failed to create server socket.\n");
return INVALID_SOCKET_FD;
}
if (listen(socket_fd, backlog) < 0) {
close(socket_fd);
printf("Server: Failed to set listen.\n");
return INVALID_SOCKET_FD;
}
return socket_fd;
}
pthread_t temp;
void *number_two(void *sock)
{
char buf[1024];
int fd = *(int *)sock;
int nread = read(fd, buf, 1024);
write(STDOUT_FILENO, buf, nread);
return NULL;
}
int main()
{
pid_t pid;
if ((pid = fork()) < 0) {
}
else if (pid > 0) { // parent, server
char buf[1024];
int fd = create_tcp_server_socket(8787, false, 10, "zz");
int new_fd = accept(fd, NULL, 0);
pthread_create(&temp, NULL, number_two, (void *)&new_fd);
}
else { // child, client
uint32_t ip;
struct hostent *hp = gethostbyname("localhost");
memcpy(&ip, hp->h_addr_list[0], hp->h_length);
struct sockaddr_in server_addr;
memset((char *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = ip;
server_addr.sin_port = htons(8787);
int fd = socket(AF_INET, SOCK_STREAM, 0);
connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
write(fd, "abcd", 4);
}
pause();
return 0;
}

Client and server sockets in c

I've can't seem to figure out how to send and receive data between my existing sockets. right now I am just trying to send one string back and forth and be able to see it. Any help as to what i am doing wrong would be greatly appreciated.
the problem parts are here:
char hostm[10];
printw("you are host!\n");
printw("Type the number you would like to pick %s: ", req.hostname);//gets row1
scanw("%s", &hostm);
printw("%s\n", hostm);
write( (struct sockaddr *)&req.sa, &hostm, sizeof(hostm));
char response[80];
read( (struct sockaddr *)&resp.sa, &response, sizeof(response));
printw("Response: %s\n", response);
recvfrom(sock, (void *)&resp, sizeof (matchrequest), 0, (struct sockaddr *)&sa, &fromlen);
and
char response[80];
char response2[80];
read( (struct sockaddr *) &host.sa, &response, sizeof(response));
read( (struct sockaddr *) &req.sa, &response2, sizeof(response2));
printf("\n\nresponse was %s\n%s\n", &response, &response2);
write( (struct sockaddr *) &req.sa, &response, sizeof(response));
write((struct sockaddr *) &host.sa, &response2, sizeof(response2));
I don't know if its in the right place either, so here is all the code:
peer.c
#include <ncurses.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "match.h"
int sock;
struct sockaddr_in serveraddr;
matchrequest req;
int matched = 0;
void *thread_start(void *args) {
int bytes_sent;
while(!matched) {
printf("waiting for peers...\n");
bytes_sent = sendto(sock, &req, sizeof(matchrequest), 0,(struct sockaddr*)&serveraddr, sizeof serveraddr);
if (bytes_sent < 0) {
printf("Error sending packet: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
sleep(3);
}
}
void writeResponse(char * response) {
write(sock, &response, sizeof(response));
}
int main(int argc, char *argv[])
{
pthread_t th;
struct sockaddr_in sa;
matchrequest resp;
ssize_t recsize;
socklen_t fromlen;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (argc != 3) {
printf("specify name and game type for this peer!\n");
return;
}
req.gametype = atoi(argv[2]);
req.state = 0;
strncpy(req.peername, argv[1], 100);
printf("using name: %s\n", req.peername);
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = 0;
if (-1 == bind(sock,(struct sockaddr *) &sa, sizeof(sa)))
{
perror("error bind failed");
close(sock);
exit(EXIT_FAILURE);
}
memset(&serveraddr, 0, sizeof serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serveraddr.sin_port = htons(7652);
pthread_create(&th, NULL, &thread_start, NULL);
for (;;)
{
fromlen = sizeof(sa);
recsize = recvfrom(sock, (void *)&resp, sizeof (matchrequest), 0, (struct sockaddr *)&sa, &fromlen);
if (recsize < 0) {
fprintf(stderr, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (recsize != sizeof(matchrequest)) {
fprintf(stderr, "invalid datagram received");
continue;
}
initscr();
printw("matched with peer %s. Game can start!\n", resp.peername);
if(strcmp(resp.hostname, req.peername) == 0)
{
char hostm[10];
printw("you are host!\n");
printw("Type the number you would like to pick %s: ", req.hostname);//gets row1
scanw("%s", &hostm);
printw("%s\n", hostm);
write( (struct sockaddr *)&req.sa, &hostm, sizeof(hostm));
char response[80];
read( (struct sockaddr *)&resp.sa, &response, sizeof(response));
printw("Response: %s\n", response);
recvfrom(sock, (void *)&resp, sizeof (matchrequest), 0, (struct sockaddr *)&sa, &fromlen);
}
else
{
char peerm[10];
printw("you are peer!\n");
printw("Type the number you would like to pick %s: ", req.peername);//gets row1
scanw("%s", &peerm);
printw("%s\n", &peerm);
// send to client, transmitted fine
write(sock, &peerm, sizeof(peerm));
char response[80];
read(sock, &response, sizeof(response));
// Prints the missing character symbol, and 'random' letters
printw("Response: %s\n", response);
}
endwin();
}
close(sock); /* close the socket */
return 0;
}
matchd.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "match.h"
#define MAX_REQUESTS 100
matchrequest ready[MAX_REQUESTS];
int outsock;
void handlerequest(struct sockaddr_in sa, matchrequest req) {
matchrequest host;
int idx = req.gametype % MAX_REQUESTS;
req.state = READY;
req.sa = sa;
printf("%s on %s:%d requested game type %d \n", req.peername, inet_ntoa(req.sa.sin_addr), req.sa.sin_port, req.gametype);
// if room, place in ready position
if (strncmp(ready[idx].peername, req.peername, 100) == 0) {
// peer is just checking in
} else if (ready[idx].state == MATCHED) {
ready[idx] = req;
printf("%s selected as host. waiting for peers...\n", req.peername);
strcpy(req.hostname, req.peername);
} else {
// match with existing peer
printf("%s matched with existing host\n", req.peername);
req.state = MATCHED;
host = ready[idx];
host.state = HOST;
ready[idx] = req;
strcpy(req.hostname, host.peername);
req.peermove = req.hostmove;
req.hostmove = req.peermove;
char response[80];
char response2[80];
read( (struct sockaddr *) &host.sa, &response, sizeof(response));
read( (struct sockaddr *) &req.sa, &response2, sizeof(response2));
printf("\n\nresponse was %s\n%s\n", &response, &response2);
write( (struct sockaddr *) &req.sa, &response, sizeof(response));
write((struct sockaddr *) &host.sa, &response2, sizeof(response2));
// read(outsock, &response, sizeof(response));
// printf("\n\nresponse was %s\n\n", &response);
// write(outsock, &response, sizeof(response));
printf("sending response to %s at %s:%d\n", host.peername, inet_ntoa(host.sa.sin_addr), host.sa.sin_port);
sendto(outsock, (void *) &req, sizeof(matchrequest), 0, (struct sockaddr *) &host.sa, sizeof(host.sa));
printf("sending response to %s at %s:%d\n", req.peername, inet_ntoa(req.sa.sin_addr), req.sa.sin_port);
sendto(outsock, (void *) &host, sizeof(matchrequest), 0, (struct sockaddr *) &req.sa, sizeof(req.sa));
fprintf(stderr, "%s\n", strerror(errno));
}
}
int main(void)
{
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in sa;
matchrequest req;
ssize_t recsize;
socklen_t fromlen;
outsock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(7652);
if (-1 == bind(sock,(struct sockaddr *)&sa, sizeof(sa)))
{
perror("error bind failed");
close(sock);
exit(EXIT_FAILURE);
}
for (;;)
{
fromlen = sizeof(sa);
printf ("receiving....\n");
recsize = recvfrom(sock, (void *)&req, sizeof (matchrequest), 0, (struct sockaddr *)&sa, &fromlen);
if (recsize < 0) {
fprintf(stderr, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (recsize != sizeof(matchrequest)) {
fprintf(stderr, "invalid datagram received");
continue;
}
handlerequest(sa, req);
}
}
match.h
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef enum matchstate {MATCHED = 0, READY, HOST} matchstate;
typedef struct matchrequest {
matchstate state;
int gametype;
char board[4][4];
char covboard[4][4];
int player1pts;
int player2pts;
int gameover;
int turn;
int hostmove;
int peermove;
char peername[100];
char hostname[100];
struct sockaddr_in sa;
} matchrequest;
I think the problem is that read and write expect file descriptors as the first argument, but you're providing something else entirely. See the documentation for write and note that you're not calling it the way you should be. Also, I didn't read all of your code but it appears as though it would generate a lot of warnings that you shouldn't ignore.
Also note, that read may return prematurely or it may block while it waits for more data to arrive. You have to cater for these situations yourself.
Also, keep in mind that with UDP, there is no guarantee or acknowledgement of delivery of data, unlike TCP. So although your program has sent the data, it may be lost in transmission to the clients.

SCTP Multihoming

I've been developing this simple client - server application with C where the client is just sending random data to the server and the server just listens to what the client sends. The protocol I'm using is SCTP and I'm interested on how to implement the multihoming feature to it.
I've been searching through the internet about SCTP and multihoming and haven't been able to find any examples about how to instruct SCTP to use multiple addresses for communication. I've only managed to find what commands one should use when trying to setup SCTP with multihoming and it should be quite straightforward.
I've created a client and a server which both use my computers two WLAN interfaces as their connection points. Both adapters are connected to the same AP. The server is listening for data from the client from these interfaces and the client sends data through them. The problem is that when I disconnect the primary WLAN adapter the client is sending data to, the transmission just halts when it should fallback to the secondary connection. I've traced the packets with Wireshark and the first INIT and INIT_ACK packets report that both the client and the server are using the WLAN adapters as their communication links.
When I reconnect the primary WLAN connection the transmission continues after a little while and bursts a huge load of packets to the server which isn't right. The packets should have been transmitted over the secondary connection. On many sites it is said that SCTP switches between connections automagically but in my case that's not happening. So do you guys have any clues why the transmission doesn't fallback to the secondary connection when the primary link is down even though the client and the server knows each others addresses including the secondary address?
About the server:
The server creates a SOCK_SEQPACKET type socket and binds all interfaces found with INADDR_ANY. getladdrs reports that the server is bounded to 3 addresses (including 127.0.0.1). After that the server listens to the socket and waits the client to send data. Server reads the data with sctp_recvmsg call.
About the client:
The client creates also a SEQPACKET socket and connects to an IP-address specified by a commandline argument. getladdrs in this case returns also 3 addresses like in the servers case. After that the client just starts to send data to the server with one second delay to the server until the user interrupts the send with Ctrl-C.
Here's some source code:
Server:
#define BUFFER_SIZE (1 << 16)
#define PORT 10000
int sock, ret, flags;
int i;
int addr_count = 0;
char buffer[BUFFER_SIZE];
socklen_t from_len;
struct sockaddr_in addr;
struct sockaddr_in *laddr[10];
struct sockaddr_in *paddrs[10];
struct sctp_sndrcvinfo sinfo;
struct sctp_event_subscribe event;
struct sctp_prim prim_addr;
struct sctp_paddrparams heartbeat;
struct sigaction sig_handler;
void handle_signal(int signum);
int main(void)
{
if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0)
perror("socket");
memset(&addr, 0, sizeof(struct sockaddr_in));
memset((void*)&event, 1, sizeof(struct sctp_event_subscribe));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
from_len = (socklen_t)sizeof(struct sockaddr_in);
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
if(setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0)
perror("setsockopt");
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))< 0)
perror("setsockopt");
if(bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
perror("bind");
if(listen(sock, 2) < 0)
perror("listen");
addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddr);
printf("Addresses binded: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddr)[i].sin_addr), (*laddr)[i].sin_port);
sctp_freeladdrs((struct sockaddr*)*laddr);
while(1)
{
flags = 0;
ret = sctp_recvmsg(sock, buffer, BUFFER_SIZE, (struct sockaddr*)&addr, &from_len, NULL, &flags);
if(flags & MSG_NOTIFICATION)
printf("Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
printf("%d bytes received from %s:%u\n", ret, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
if(close(sock) < 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
And the Client:
#define PORT 10000
#define MSG_SIZE 1000
#define NUMBER_OF_MESSAGES 1000
#define PPID 1234
int sock;
struct sockaddr_in *paddrs[10];
struct sockaddr_in *laddrs[10];
void handle_signal(int signum);
int main(int argc, char **argv)
{
int i;
int counter = 1;
int ret;
int addr_count;
char address[16];
char buffer[MSG_SIZE];
sctp_assoc_t id;
struct sockaddr_in addr;
struct sctp_status status;
struct sctp_initmsg initmsg;
struct sctp_event_subscribe events;
struct sigaction sig_handler;
memset((void*)&buffer, 'j', MSG_SIZE);
memset((void*)&initmsg, 0, sizeof(initmsg));
memset((void*)&addr, 0, sizeof(struct sockaddr_in));
memset((void*)&events, 1, sizeof(struct sctp_event_subscribe));
if(argc != 2 || (inet_addr(argv[1]) == -1))
{
puts("Usage: client [IP ADDRESS in form xxx.xxx.xxx.xxx] ");
return 0;
}
strncpy(address, argv[1], 15);
address[15] = 0;
addr.sin_family = AF_INET;
inet_aton(address, &(addr.sin_addr));
addr.sin_port = htons(PORT);
initmsg.sinit_num_ostreams = 2;
initmsg.sinit_max_instreams = 2;
initmsg.sinit_max_attempts = 5;
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0)
perror("socket");
if((setsockopt(sock, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg))) != 0)
perror("setsockopt");
if((setsockopt(sock, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events))) != 0)
perror("setsockopt");
if(sendto(sock, buffer, MSG_SIZE, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == -1)
perror("sendto");
addr_count = sctp_getpaddrs(sock, 0, (struct sockaddr**)paddrs);
printf("\nPeer addresses: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*paddrs)[i].sin_addr), (*paddrs)[i].sin_port);
sctp_freepaddrs((struct sockaddr*)*paddrs);
addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddrs);
printf("\nLocal addresses: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddrs)[i].sin_addr), (*laddrs)[i].sin_port);
sctp_freeladdrs((struct sockaddr*)*laddrs);
i = sizeof(status);
if((ret = getsockopt(sock, SOL_SCTP, SCTP_STATUS, &status, (socklen_t *)&i)) != 0)
perror("getsockopt");
printf("\nSCTP Status:\n--------\n");
printf("assoc id = %d\n", status.sstat_assoc_id);
printf("state = %d\n", status.sstat_state);
printf("instrms = %d\n", status.sstat_instrms);
printf("outstrms = %d\n--------\n\n", status.sstat_outstrms);
for(i = 0; i < NUMBER_OF_MESSAGES; i++)
{
counter++;
printf("Sending data chunk #%d...", counter);
if((ret = sendto(sock, buffer, MSG_SIZE, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr))) == -1)
perror("sendto");
printf("Sent %d bytes to peer\n",ret);
sleep(1);
}
if(close(sock) != 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
So do you guys have any clues what I'm doing wrong?
Ok I resolved the multihoming problem finally. Here's what I did.
I adjusted the heartbeat value to 5000 ms with sctp_paddrparams struct. The flags variable located in the struct has to in SPP_HB_ENABLE mode because otherwise SCTP ignores the heartbeat value when trying to set the value with setsockopt().
That was the reason why SCTP didn't send heartbeats as often as I wanted. The reason, why I didn't notice the flag variable, was the obsolete reference guide to SCTP I was reading, which stated that there didn't exist a flags variable inside the struct! Newer reference revealed that there was. So heartbeat problem solved!
Another thing was to modify the rto_max value to, for example, 2000 ms or so. Lowering the value tells SCTP to change the path much sooner. The default value was 60 000 ms which was too high (1 minute before it starts to change the path). rto_max value can be adjusted with sctp_rtoinfo struct.
With these two modifications the multihoming started to work. Oh and a another thing. Client has to be in STREAM mode when the Server is in SEQPACKET mode. Client sends data to server with normal send() command and Server read data with sctp_recvmsg() where addr struct is set to NULL.
I hope that this information helps other guys struggling with the multihoming of SCTP. Cheers guys for your opinions, those were a big help for me! Here is some code example so this maybe the first multihoming simple example in the net if you ask me (didnt find any examples than multistreaming examples)
Server:
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdlib.h>
#include <pthread.h>
#define BUFFER_SIZE (1 << 16)
#define PORT 10000
int sock, ret, flags;
int i, reuse = 1;
int addr_count = 0;
char buffer[BUFFER_SIZE];
socklen_t from_len;
struct sockaddr_in addr;
struct sockaddr_in *laddr[10];
struct sockaddr_in *paddrs[10];
struct sctp_sndrcvinfo sinfo;
struct sctp_event_subscribe event;
struct sctp_prim prim_addr;
struct sctp_paddrparams heartbeat;
struct sigaction sig_handler;
struct sctp_rtoinfo rtoinfo;
void handle_signal(int signum);
int main(void)
{
if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0)
perror("socket");
memset(&addr, 0, sizeof(struct sockaddr_in));
memset(&event, 1, sizeof(struct sctp_event_subscribe));
memset(&heartbeat, 0, sizeof(struct sctp_paddrparams));
memset(&rtoinfo, 0, sizeof(struct sctp_rtoinfo));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
from_len = (socklen_t)sizeof(struct sockaddr_in);
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
heartbeat.spp_flags = SPP_HB_ENABLE;
heartbeat.spp_hbinterval = 5000;
heartbeat.spp_pathmaxrxt = 1;
rtoinfo.srto_max = 2000;
/*Set Heartbeats*/
if(setsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS , &heartbeat, sizeof(heartbeat)) != 0)
perror("setsockopt");
/*Set rto_max*/
if(setsockopt(sock, SOL_SCTP, SCTP_RTOINFO , &rtoinfo, sizeof(rtoinfo)) != 0)
perror("setsockopt");
/*Set Signal Handler*/
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
/*Set Events */
if(setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0)
perror("setsockopt");
/*Set the Reuse of Address*/
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))< 0)
perror("setsockopt");
/*Bind the Addresses*/
if(bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
perror("bind");
if(listen(sock, 2) < 0)
perror("listen");
/*Get Heartbeat Value*/
i = (sizeof heartbeat);
getsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeat, (socklen_t*)&i);
printf("Heartbeat interval %d\n", heartbeat.spp_hbinterval);
/*Print Locally Binded Addresses*/
addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddr);
printf("Addresses binded: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddr)[i].sin_addr), (*laddr)[i].sin_port);
sctp_freeladdrs((struct sockaddr*)*laddr);
while(1)
{
flags = 0;
ret = sctp_recvmsg(sock, buffer, BUFFER_SIZE, NULL, 0, NULL, &flags);
if(flags & MSG_NOTIFICATION)
printf("Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
printf("%d bytes received from %s:%u\n", ret, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
if(close(sock) < 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
Client:
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define PORT 11000
#define MSG_SIZE 1000
#define NUMBER_OF_MESSAGES 1000
int sock;
struct sockaddr_in *paddrs[5];
struct sockaddr_in *laddrs[5];
void handle_signal(int signum);
int main(int argc, char **argv)
{
int i;
int counter = 0;
int asconf = 1;
int ret;
int addr_count;
char address[16];
char buffer[MSG_SIZE];
sctp_assoc_t id;
struct sockaddr_in addr;
struct sctp_status status;
struct sctp_initmsg initmsg;
struct sctp_event_subscribe events;
struct sigaction sig_handler;
struct sctp_paddrparams heartbeat;
struct sctp_rtoinfo rtoinfo;
memset(&buffer, 'j', MSG_SIZE);
memset(&initmsg, 0, sizeof(struct sctp_initmsg));
memset(&addr, 0, sizeof(struct sockaddr_in));
memset(&events, 1, sizeof(struct sctp_event_subscribe));
memset(&status, 0, sizeof(struct sctp_status));
memset(&heartbeat, 0, sizeof(struct sctp_paddrparams));
memset(&rtoinfo, 0, sizeof(struct sctp_rtoinfo))
if(argc != 2 || (inet_addr(argv[1]) == -1))
{
puts("Usage: client [IP ADDRESS in form xxx.xxx.xxx.xxx] ");
return 0;
}
strncpy(address, argv[1], 15);
address[15] = 0;
addr.sin_family = AF_INET;
inet_aton(address, &(addr.sin_addr));
addr.sin_port = htons(PORT);
initmsg.sinit_num_ostreams = 2;
initmsg.sinit_max_instreams = 2;
initmsg.sinit_max_attempts = 1;
heartbeat.spp_flags = SPP_HB_ENABLE;
heartbeat.spp_hbinterval = 5000;
heartbeat.spp_pathmaxrxt = 1;
rtoinfo.srto_max = 2000;
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
/*Handle SIGINT in handle_signal Function*/
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
/*Create the Socket*/
if((ret = (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP))) < 0)
perror("socket");
/*Configure Heartbeats*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS , &heartbeat, sizeof(heartbeat))) != 0)
perror("setsockopt");
/*Set rto_max*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_RTOINFO , &rtoinfo, sizeof(rtoinfo))) != 0)
perror("setsockopt");
/*Set SCTP Init Message*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg))) != 0)
perror("setsockopt");
/*Enable SCTP Events*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_EVENTS, (void *)&events, sizeof(events))) != 0)
perror("setsockopt");
/*Get And Print Heartbeat Interval*/
i = (sizeof heartbeat);
getsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeat, (socklen_t*)&i);
printf("Heartbeat interval %d\n", heartbeat.spp_hbinterval);
/*Connect to Host*/
if(((ret = connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)))) < 0)
{
perror("connect");
close(sock);
exit(0);
}
/*Get Peer Addresses*/
addr_count = sctp_getpaddrs(sock, 0, (struct sockaddr**)paddrs);
printf("\nPeer addresses: %d\n", addr_count);
/*Print Out Addresses*/
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*paddrs)[i].sin_addr), (*paddrs)[i].sin_port);
sctp_freepaddrs((struct sockaddr*)*paddrs);
/*Start to Send Data*/
for(i = 0; i < NUMBER_OF_MESSAGES; i++)
{
counter++;
printf("Sending data chunk #%d...", counter);
if((ret = send(sock, buffer, MSG_SIZE, 0)) == -1)
perror("write");
printf("Sent %d bytes to peer\n",ret);
sleep(1);
}
if(close(sock) != 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
You client opens an assosiation by using sendto() call. It is OK.
But after that you should not use sendto() anymore. Because sendto() will propably force SCTP to use interface of the given IP.
So, use write() or send() instead of sendto():
counter++;
printf("Sending data chunk #%d...", counter);
if((ret = write(sock, buffer, MSG_SIZE) == -1)
perror("write");
I have never tried SCTP but according to this site it supports two models.
one-to-one style and one-to-many style.
As far as I understand multihoming works for one-to-one style.
In order to open a one-to-one style socket you need a call like :
connSock = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
Note that SOCK_STREAM is different than your SOCK_SEQPACKET
When you do
sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)
it seems to open one-to-many style socket which I could not understand if it supports multihoming or not.
So try your code with SOCK_STREAM parameter.
Also here is an example that uses SOCK_STREAM.

Resources