SCTP multi-homing not working as expected - c

I'm facing a problem in implementing multi-homing in SCTP as the server side. The server has 2 IPs it is listening to.
I'm almost there, but there are 2 problems:
First IP returns the INIT-ACK with 2 different IPs inside the header as it should but the other IP return twice the same IP in the INIT-ACK header.
Seems like I’m not supporting 100 % in multi-homing, for example, if one of the links is down I don’t see a fail over.
So I don’t know if except the setsockopt with the option SCTP_SOCKOPT_BINDX_ADD I need anything else (maybe SCTP_PRIMARY_ADDR?) or what is wrong in my implementation.
Following is the code, I enter this code twice, first time I do bind and then save the socket and the first address, second time again bind (for the second IP) and then running setsockopt for both of the addresses and sockets.
bind(socket, &sock_addr.addr.sock_addr, sock_addr_len);
if(SHARED.num_used_entries_in_sockaddr_array == 0)
{
SHARED.saved_socket = socket;
SHARED.sockaddr_array[1] = sock_addr.addr.sock_addr;
}
else
{
SHARED.sockaddr_array[0] = sock_addr.addr.sock_addr;
}
if(SHARED.num_used_entries_in_sockaddr_array > 0)
{
sock_rc = setsockopt(SHARED.saved_socket,
IPPROTO_SCTP,
SCTP_SOCKOPT_BINDX_ADD,
(char*)SHARED.sockaddr_array,
sizeof(SCKOS_SOCK_ADDR));
sock_rc = setsockopt(socket,
IPPROTO_SCTP,
SCTP_SOCKOPT_BINDX_ADD,
(char*)SHARED.sockaddr_array,
sizeof(SCKOS_SOCK_ADDR));
}
SHARED.num_used_entries_in_sockaddr_array++;
Thanks!!!

SCTP_SOCKOPT_BINDX_ADD is ok for multi-homing. your codes have some unused lines.
if(SHARED.num_used_entries_in_sockaddr_array == 0)
{
bind(socket, &sock_addr.addr.sock_addr, sock_addr_len);
}
else
{
sock_rc = setsockopt(socket,
IPPROTO_SCTP,
SCTP_SOCKOPT_BINDX_ADD,
(char*)sock_addr.addr.sock_addr,
sizeof(SCKOS_SOCK_ADDR));
}enter code here
You can refer to linux sctp implemantation. Did you see heartbeat on all paths? Which box you test on?

thanks for the answer, in the end I used sctp_bindx which is much easier to implement

Related

Ethernet connect fails with LWIP in mbed os

I'm using an mbed enabled board for development, I need to run an Ethernet application on it.
I tried to create a connection by using the following code:
network = new EthernetInterface();
network->connect();
// Show the network address
const char *ip = network->get_ip_address();
printf("IP address is: %s\n", ip ? ip : "No IP");
Normally it should work, But it fails in the LWIPInteraface class's bringup API at osSemaphoreAcquire, returning a timeout error.
if (!netif_is_link_up(&netif)) {
if (blocking){
if (osSemaphoreAcquire(linked, 15000) != osOK){
if (ppp){
(void) ppp_lwip_disconnect(hw);
}
return NSAPI_ERROR_NO_CONNECTION;
}
}
}
Any reason why I might be getting a timeout from osSemaphoreAcquire?
I tried increasing the timeout too but in vain.
If someone could help me with it, would be appreciated.
Thanks in advance.

Will --bind-address in Wget over-ride the IP address of the interface from which packets are routed

I am trying to simulate network client (preferably C code) that runs on a machine (laptop/PC). On start, it negotiate a new IP address through DHCP from the router of the network the machine is part of. Post obtaining IP address, it is suppose to make http requests (downloads/uploads) as per a configuration file.
The setup is primarily to spawn multiple of such clients for the purpose of stress testing the router.
I came across https://github.com/saravana815/dhtest which does the part of DHCP negotiation. It creates a random mac address & gets an IP from the router for the same.
The next step is to extend the client to have http calls. I started looking into https://github.com/jay/wget & the flag --bind-address caught my attention
--bind-address=ADDRESS bind to ADDRESS (hostname or IP) on local host.\n"),
What I understand is - purpose of this flag is to select appropriate interface on the machine . On looking closer to the source, I found the snippet below which gets kicked in for the flag
static bool
resolve_bind_address (struct sockaddr *sa)
{
struct address_list *al;
/* Make sure this is called only once. opt.bind_address doesn't
change during a Wget run. */
static bool called, should_bind;
static ip_address ip;
if (called)
{
if (should_bind)
sockaddr_set_data (sa, &ip, 0);
return should_bind;
}
called = true;
al = lookup_host (opt.bind_address, LH_BIND | LH_SILENT);
if (!al)
{
/* #### We should be able to print the error message here. */
logprintf (LOG_NOTQUIET,
_("%s: unable to resolve bind address %s; disabling bind.\n"),
exec_name, quote (opt.bind_address));
should_bind = false;
return false;
}
/* Pick the first address in the list and use it as bind address.
Perhaps we should try multiple addresses in succession, but I
don't think that's necessary in practice. */
ip = *address_list_address_at (al, 0);
address_list_release (al);
sockaddr_set_data (sa, &ip, 0);
should_bind = true;
return true;
}
The function sockaddr_set_data (sa, &ip, 0); looks as below
static void
sockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port)
{
switch (ip->family)
{
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
xzero (*sin);
sin->sin_family = AF_INET;
sin->sin_port = htons (port);
sin->sin_addr = ip->data.d4;
break;
}
#ifdef ENABLE_IPV6
case AF_INET6:
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
xzero (*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons (port);
sin6->sin6_addr = ip->data.d6;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
sin6->sin6_scope_id = ip->ipv6_scope;
#endif
break;
}
#endif /* ENABLE_IPV6 */
default:
abort ();
}
}
I am not exactly a network/socket expert. What I want to confirm is - does explicitly adding an IP address to the packets that Wget is sending will over-ride the IP address that OS would set for these packets when they leave a particular interface ?
The answer to this question would help me decide if I should create a separate logical interface for every network client I look to simulate or if I can completely do away with it (I prefer the latter).
The packets will be routed via that interface to which the socket is bound. The IP address can be virtual or the primary IP of the Ethernet interface. And the IP determines which physical interface which shall be used for communication.
You can create multiple virtual IP addresses on a given physical interface and spawn clients each binding to different IP. This presents a case of varied sources for your router (simulation) Though all will be using the same physical interface.

SDLNet Networking Not Working

I am working on a game written in C using SDL. Given that it already uses SDL, SDL_image, and SDL_ttf, I decided to add SDL_mixer and SDL_net to my engine. Getting SDL_mixer set up and working was very easy, but I am having a lot of trouble with SDL_net.
To test I created a very simple application with the following rules:
Run without arguments act as a TCP server on port 9999
Run with an argument try to connect to the server at the given IP address on port 9999
Here are some of the key lines of the program (I'm not going to post my whole event-driven SDL engine because its too long):
char *host = NULL;
if (argc > 1) host = argv[1];
and...
IPaddress ip;
TCPsocket server = NULL;
TCPsocket conn = NULL;
if (host) { /* client mode */
if (SDLNet_ResolveHost(&ip,host,port) < 0)
return NULL; //this is actually inside an engine method
if (!(conn = SDLNet_TCP_Open(&ip)))
return NULL;
} else { /* server mode */
if (SDLNet_ResolveHost(&ip,NULL,port) < 0)
return NULL;
if (!(server = SDLNet_TCP_Open(&ip)))
return NULL;
}
and... inside the event loop
if (server) {
if (!conn)
conn = SDLNet_TCP_Accept(server);
}
if (conn) {
void *buf = malloc(size); //server, conn, size are actually members of a weird struct
while (SDLNet_TCP_Recv(conn,buf,size))
onReceive(buf); //my engine uses a callback system to handle things
free(buf);
}
The program seems to start up just fine. However for some reason when I run it in client mode trying to connect to my home computer (which I have on a different IP) from my laptop I find that the call to SDLNet_TCP_Open blocks the program for awhile (5-10 seconds) then returns NULL. Can anybody see what I did wrong? Should I post more of the code? Let me know.

C websocket library, libwebsockets

I am looking through C websocket library libwebsockets client side example.
But i don't understand what the example purpose is.
Here is the example, this example have two connection (in the code wsi_dumb and wsi_mirror)which are same i think, and i don't know what second connection's purpose is.
using first conenction(in the code wsi_dumb), it seems to wait a request from server with libwebsocket_service() and then ...what with second connection(in the code wsi_mirror)?
And below is the part of the code i am saying.
wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl,
"/", argv[optind], argv[optind],
protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version);
/*
* sit there servicing the websocket context to handle incoming
* packets, and drawing random circles on the mirror protocol websocket
*/
n = 0;
while (n >= 0 && !was_closed) {
n = libwebsocket_service(context, 1000);
if (wsi_mirror == NULL) {
/* create a client websocket using mirror protocol */
wsi_mirror = libwebsocket_client_connect(context, address, port,
use_ssl, "/", argv[optind], argv[optind],
protocols[PROTOCOL_LWS_MIRROR].name, ietf_version);
mirror_lifetime = 10 + (random() & 1023);
fprintf(stderr, "opened mirror connection with %d lifetime\n", mirror_lifetime);
} else {
mirror_lifetime--;
if (mirror_lifetime == 0) {
fprintf(stderr, "closing mirror session\n");
libwebsocket_close_and_free_session(context,
wsi_mirror, LWS_CLOSE_STATUS_GOINGAWAY);
/*
* wsi_mirror will get set to NULL in
* callback when close completes
*/
}
}
}
I might mix it up but there is an example in libwebsockets where you just open a second browser (window or tab) and then see all the lines and circles you draw in the first browser mirrored and sent to the second browser.

How does gcc/cygwin get the DNS server?

I have some code I'm writing under cygwin (using GCC) that successfully uses gethostbyname(); however when I try to use the resolver directly to retrieve the IP address of the DNS server it fails (all entries in nsaddr_list[] are null and nscount is -1).
If gethostbyname() is working, then obviously it is able to connect to the DNS server.
This code...
if (res_init() == -1) {
fprintf(stderr,"res_init() failed\n");
exit(1);
}
if (_res.nscount <= 0) {
fprintf(stderr,"nscount = %d\n",_res.nscount);
}
else {
for(i=0;i<_res.nscount;i++) {
fprintf(stderr, "dnssrvr: %d.%d.%d.%d\n",
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff) >> 0,
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff00) >> 8,
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff0000) >> 16,
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff000000) >> 24);
}
}
works on unix/linux, but returns nscount=-1 on cygwin.
Is there some trick to getting the DNS server when using cygwin/gcc?
res_init does not necessarily populate _res.nsaddr_list. Instead, on Windows it directs the resolver to use DnsQuery_A, unless you have the resolv.conf file, in which case DNS servers from that file are used.
See the source here, files minires.c and minires-os-if.c.
If you need to know your DNS servers, you probably have to use DnsQueryConfig or GetNetworkParams.
NB: _res is undocumented and should not be used.
UPDATE Apparently the "newer" (ca 2010 and later) versions of cygwin do populate _res.nsaddr_list, via a call to get_dns_info and then get_registry_dns. You may want to make sure that you have the newest cygwin, and if the problem persists, try to use a debug version and trace calls to the mentioned functions.
UPDATE 2 No, _res is not populated, my mistake.
As n.m. says, on Cygwin res_init() does not populate _res.nsaddr_list if it is using the Windows resolver. It uses the Windows resolver if either /etc/resolv.conf does not exist, or /etc/resolv.conf contains options osquery.
In my opinion this is a Cygwin bug - returning a negative nscount is bogus - but nonetheless we are stuck with working around it.
The solution is to call GetNetworkParams() just as Cygwin does itself - here's what I'm doing as a fallback:
#include <windows.h>
#include <iphlpapi.h>
#include <netinet/in.h>
#include <arpa/inet.h>
if (_res.nscount < 0)
{
ULONG buflen = 0;
FIXED_INFO *buf = NULL;
if (GetNetworkParams(NULL, &buflen) == ERROR_BUFFER_OVERFLOW)
buf = malloc(buflen);
if (buf && GetNetworkParams(buf, &buflen) == NO_ERROR)
{
_res.nscount = 1;
_res.nsaddr_list[0].sin_family = AF_INET;
_res.nsaddr_list[0].sin_addr.s_addr = inet_addr(buf->DnsServerList.IpAddress.String);
_res.nsaddr_list[0].sin_port = htons(53);
}
free(buf);
}
You need to link against -liphlpapi for the GetNetworkParams() function.
This only takes the first Windows DNS address, but if you want the rest of them you can follow the linked list that GetNetworkParams() returns. GetNetworkParams() only returns IPv4 addresses, I'm not sure what you're supposed to do if the machine has an IPv6 DNS server address configured.

Resources