socket bind - IPv6: invalid argument

0

Witam.

Piszę prosty serwer TCP dla IPv6. kod wygląda tak:

int rc;
  struct sockaddr_in6 serverSa;
   
  int on = 1;
  int family;
  socklen_t serverSaSize;
  int c;
  char buf[INET6_ADDRSTRLEN];

  int s = socket(PF_INET6,SOCK_STREAM,0);
  if (s < 0)
  {
    fprintf(stderr, "IPv6 not active, falling back to IPv4...\n");
  }

  family = AF_INET6;
   
  
  printf("socket descriptor is: %d ", s);
  //setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&on,sizeof on);
  rc = setsockopt(s,IPPROTO_IPV6,IPV6_RECVPKTINFO,&on,sizeof(on));
  int result = 8;
  /* initialize the server's sockaddr */
  memset(&serverSa,0,sizeof(serverSa));
  serverSa.sin6_family = AF_INET6;
 // serverSa.sin6_addr = "fe80::800:27ff:fe00:0";
  serverSa.sin6_port = htons(serverPort);

  tmp = (char*)(&serverSa.sin6_addr);
  result = inet_pton6(addr, tmp);
  printf ("\n result: %d \n", result); 
  serverSaSize = sizeof(struct sockaddr_in6);
  rc = bind(s,(struct sockaddr *)&serverSa,serverSaSize);
  if (rc < 0)
  {
    printf("\n TCP bind failed\n ");
    exit(1);
  } else {
    printf("\n TCP Binding OK.\n " );
  }
   

Kod niestety nie bind-uje mi sokcet'u. Wywala bład: "Unable to bind socket: invalid argument"
Po zakomentowaniu tej linijki:

result = inet_pton6(addr, tmp); 

Jest już ok. Wygląda na to, że ta funkcja psuje coś strukturę adresową. inet_pton6 wygląda tak:

int inet_pton6(const char *src, char * dst)
{
	static char xdigits[] = "0123456789abcdef";
	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
	const char *curtok;
	int ch, saw_xdigit;
	u_int val;

	memset(tmp, '\0', NS_IN6ADDRSZ);
	tp = tmp;
	endp = tp + NS_IN6ADDRSZ;
	colonp = NULL;
	/* Leading :: requires some special handling. */
	if (*src == ':')
		if (*++src != ':')
			return (0);
	curtok = src;
	saw_xdigit = 0;
	val = 0;
	while ((ch = tolower (*src++)) != '\0') {
		char * pch;

		pch = strchr(xdigits, ch);
		if (pch != NULL) {
			val <<= 4;
			val |= (pch - xdigits);
			if (val > 0xffff)
				return (0);
			saw_xdigit = 1;
			continue;
		}
		if (ch == ':') {
			curtok = src;
			if (!saw_xdigit) {
				if (colonp)
					return (0);
				colonp = tp;
				continue;
			} else if (*src == '\0') {
				return (0);
			}
			if (tp + NS_INT16SZ > endp)
				return (0);
			*tp++ = (u_char) (val >> 8) & 0xff;
			*tp++ = (u_char) val & 0xff;
			saw_xdigit = 0;
			val = 0;
			continue;
		}
		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
		    inet_pton4(curtok, (char*)tp) > 0) {
			tp += NS_INADDRSZ;
			saw_xdigit = 0;
			break;	/* '\0' was seen by inet_pton4(). */
		}
		return (0);
	}
	if (saw_xdigit) {
		if (tp + NS_INT16SZ > endp)
			return (0);
		*tp++ = (u_char) (val >> 8) & 0xff;
		*tp++ = (u_char) val & 0xff;
	}
	if (colonp != NULL) {
		/*
		 * Since some memmove()'s erroneously fail to handle
		 * overlapping regions, we'll do the shift by hand.
		 */
		const int n = tp - colonp;
		int i;

		if (tp == endp)
			return (0);
		for (i = 1; i <= n; i++) {
			endp[- i] = colonp[n - i];
			colonp[n - i] = 0;
		}
		tp = endp;
	}
	if (tp != endp)
		return (0);
	memcpy(dst, tmp, NS_IN6ADDRSZ);
	return (1);
}

 

Czy ktoś ma jakiś pomysł, dlaczego to może nie działać ?

0

Dodam, że próbuje zrobić bind na vboxnet0 o adresie jak w komentarzu.

0

Na szybko skrobnalem, porownaj ze swoim u mnie sie nic nie crashuje, ani nie wywala.

#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PORT 9880

void ate()
{
    if(errno != 0)
        printf("%d\n", errno);
}

int main()
{
    int sockfd;
    struct sockaddr_in6 serv;
    int tr = 1;

    atexit(ate);

    if((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
        exit(-1);

    if(setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &tr, sizeof(int)) < 0)
        exit(-1);

    memset(&serv, 0, sizeof(serv));
    serv.sin6_family = AF_INET6;
    serv.sin6_port = htons(PORT);
    if(inet_pton(AF_INET6, "0000:0000:0000:0000:0000:0000:0000:0000", &serv.sin6_port) <= 0)
        exit(-1);
    //serv.sin6_addr = in6addr_any;

    if(bind(sockfd, (struct sockaddr*)&serv, sizeof(serv)) < 0)
        exit(-1);

    if(listen(sockfd, 10) < 0)
        exit(-1);

    while(1);

    close(sockfd);

    return 0;
}
  1. Nie obslugujesz bledow (tzn. tylko drukujesz informacje diagnostyczne)

  2. Wszystko co zwiazane z Twoim "inet_pton6" to jakies potworki

  3. Jest jeszcze szansa, ze bindujesz zle lokalny adres: http://stackoverflow.com/questions/2460841/how-to-bind-a-link-local-address-to-an-ipv6-socket

0
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define PORT 9880
 
void ate()
{
    if(errno != 0)
        printf("%d\n", errno);
}
 
int main(int argc, char** argv)
{
    int sockfd, connfd;
    int n;
    const int on = 1;
    struct addrinfo hints, *res, *ressave;
    socklen_t addrlenp;
    char port[5] = "9880";
    char buffer[256];

    atexit(ate);

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if((n = getaddrinfo(NULL, port, &hints, &res)) != 0)
        exit(-1);

    ressave = res;

    do
    {
        if(res->ai_family != AF_INET6)
            continue;

        if((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
            continue;

        if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            exit(-1);

        if(bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
            break;

        if(close(sockfd) < 0)
            exit(-1);
    }
    while((res = res->ai_next) != NULL);

    if(res == NULL)
        exit(-1);

    if(listen(sockfd, 10) < 0)
        exit(-1);

    addrlenp = res->ai_addrlen;

    freeaddrinfo(ressave);
 
    while(1)
    {
        if((connfd = accept(sockfd, NULL, NULL)) < 0)
            exit(-1);
    }
 
    close(sockfd);
 
    return 0;
}

Doszedlem do czegos takiego:

  1. Program pobiera wszystkie dostepne interfejsy sieciowe (jako liste wskaznikowa)
  2. Przelatuje je w poszukiwaniu interfejsow AF_INET6
  3. Binduje dla pierwszego poprawnego adresu, przerywa i dalej leci standard

Jesli dobrze Cie zrozumialem, musialbys lekko zmodyfikowac, zeby zamiast Od razu bindowac, etc, etc. To zapisywac gdzies te struktury, a nastepnie w formie user-friendly dawac mozliwosc wyboru.

0

Zupełnie nie rozumiem po co to wszystko. Wcześniejszą wersję Twojego programu zmodyfikowałem do takiej i działa mi w tym sensie, że chociaż inet_pton nie zwraca błędu, jednak bind dalej nie działa:

 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define PORT 10000
 
void ate()
{
    if(errno != 0)
        printf("%d\n", errno);
}
 
int main() {
  
    unsigned int port=10000;
    int sockfd;
    int res=0, result = 8;
    struct sockaddr_in6 serv;
    int tr = 1;
    
    atexit(ate);
 
    if((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0){
        exit(-1);
    }
 
    if(setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &tr, sizeof(int)) < 0) {
        exit(-1);
    }
 
    memset(&serv, 0, sizeof(serv));
    serv.sin6_family = AF_INET6;
    serv.sin6_port = htons(port);
    //fe80::d2df:9aff:fe65:feea
    result = inet_pton(AF_INET6, "fe80:0000:0000:0000:d2df:9aff:fe65:feea", &serv.sin6_port);
    printf ("result: %d", result );
    
    //serv.sin6_addr = in6addr_any;
    res=bind(sockfd, (struct sockaddr*)&serv, sizeof(serv));
    printf("res: %d ", res);
    
    if(listen(sockfd, 10) < 0) {
        exit(-1);
    }
    
    getchar();
    close(sockfd);
 
    return 0;
}

Jak odpalisz ten program to bind wywali błąd: "unable to bind... ", chodzi o to, żeby znaleźć odpowiedź na pytanie dlaczego tak się dzieje, że bind zwraca kod -1.

1 użytkowników online, w tym zalogowanych: 0, gości: 1