(usagi-users 03467) Re: IPv6 Multicast question - connect fails

Bas Vermeulen <[email protected]>
Newsgroups gmane.linux.ipv6.usagi.users
Message-ID <[email protected]>
On Tue, 2005-08-09 at 13:54 +0200, Jeroen Massar wrote:
> On Mon, 2005-08-08 at 15:58 +0200, Bas Vermeulen wrote:
> > Hi,
> > 
> > I've got some code that does the following:
> > 
> > Takes an IPv6 multicast address, and opens an UDP socket with
> > socket(). It then sets the SO_REUSABLE sockopt, and binds the socket to
> > in6addr_any and port 5060. I then connect() to the IPv6 multicast
> > address (FF35::d:d44).
> > 
> > This works when the default interface has a global IPv6 address, but
> > fails with a site-local (FEC0::1) or link-local (FE80::1) address.
> > 
> > Does anyone know why, and where I can find more information on this
> > behaviour? I've included my test program. Any comments are welcome.
> 
> You definitely should be using getaddrinfo() See google(eva ipv6) =
> gsyc.escet.urjc.es/~eva/IPv6-web/ipv6.html for the details.

Right. I've changed the code slightly to use getaddrinfo(). I still get
the same result. What I do now:

Get the correct address for "ff35::d:d44" with getaddrinfo (using
get_addr() from Eva's).

Open an UDP socket for real with the address from get_addr().

bind() to the address from get_addr() (also tried in6addr_any).

connect() to the address from get_addr().

The connect will fail with EADDRNOTAVAIL if there are only link-local
addresses on the interfaces. If there is a global IPv6 IP on the link,
things work. I am trying to find out why.

> site-locals should work, then again they are deprecated and thus you
> shouuld not be using them.

I know. At the moment my boxes only have link-local addresses, but fail
to connect() to FF35::d:d44 with a "Cannot assign requested address"
error (EADDRNOTAVAIL). If I give the outgoing interface a global IPv6
address (2001:888:1941::3) things work. I'm not sure what's wrong with
that setup.

> Link-locals not working usually means that you bound to the wrong
> interface.

I usually let the kernel figure that out (by binding to in6addr_any).
bind() works, but connect does not.

> Peeking at the code reveals that you think that the interface index
> matches the scope id. Which is a very wrong assumption.
> Check Eva's site for the solution to this and a lot of related info's.

Read it, still having problems. See above... I don't see anything about
that particular problem on that page. I've updated the code to
incorporate Eva's stuff. Still no go though.

Thanks so far,

-- 
Bas Vermeulen <[email protected]>
socket_test.c (text/x-csrc, 2.1 KB)
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

int get_addr(const char* hostname, const char* service, int family, int socktype, struct sockaddr_storage* addr)
{
	struct addrinfo hints, *res, *ressave;
	int error, sockfd = -1, retval = -1;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = family;
	hints.ai_socktype = socktype;

	error = getaddrinfo(hostname, service, &hints, &res);

	if (error != 0) {
		fprintf(stderr, "getaddrinfo error: %s\n", strerror(error));
		return retval;
	}

	ressave = res;

	while(res) {
		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

		if (!(sockfd < 0)) {
			if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
				close(sockfd);
				memcpy(addr, res->ai_addr, sizeof(struct sockaddr_storage));
				retval = 0;
				break;
			} 

			close(sockfd);
			sockfd = -1;
		}
		res = res->ai_next;
	}

	freeaddrinfo(ressave);

	return retval;
}

int main(int argc, char **argv)
{
	struct sockaddr_storage addr6;
	char* interface;
	char letter;
	int err = 0;
	printf("Trying to connect to %s: ", argv[1]);

	if (get_addr(argv[1], "5060", PF_INET6, SOCK_DGRAM, &addr6) == 0)
	{
		int s, i = 1, ttl = 16;

		if ((s = socket(addr6.ss_family, SOCK_DGRAM, 0)) < 0) { err = 1; goto done; }

		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { close(s); err = 2; goto done; }

		if (bind(s, &addr6, sizeof(addr6)) < 0) { close(s); err = 3; goto done; }

		if (connect(s, &addr6, sizeof(addr6)) < 0) { close(s); err = 4; goto done; }

		setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));

		printf("success.\n");
		return 0;
	}
done:
	switch (err)
	{
	case 0: printf("failed - not an IPv6 address.\n"); break;
	case 1: printf("failed - socket() - %s.\n", strerror(errno)); break;
	case 2: printf("failed - setsockopt(SO_REUSEADDR) - %s.\n", strerror(errno)); break;
	case 3: printf("failed - bind() - %s.\n", strerror(errno)); break;
	case 4: printf("failed - connect() - %s.\n", strerror(errno)); break;
	case 5: printf("failed - setsockopt(JOIN_GROUP) - %s.\n", strerror(errno)); break;
	}
	return 0;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.