[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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



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 <bvermeul@xxxxxxxxxxxx>
#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;
}