2013-10-24 4 views
3

Мне было интересно, можно ли на Ubuntu 12.04 отправить UDP-пакет из сокета с адресом локальной сети IPv6 на устройство в той же беспроводной сети, используя его IPv4 адрес. Я уже успел отправить UDP-пакет в этот целевой интерфейс, используя его IPv6-адрес.Отправить UDP на локальный ipv4-адрес с помощью локального сокета ipv6

У меня есть сокет с адресом IPv6, IPv6ONLY не установлен:

int fd = socket(AF_INET6, SOCK_DGRAM, 0); 
int no = 0; 
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&no, sizeof(no)); 
bind(fd, (sockaddr*)&sa, sizeof(sa)); 

Я просто отправить сверстнику с SendTo:

struct sockaddr_in6 peer = create_ipv6_sockaddr(55555, "193.156.108.67", 0, 0, true); 
sendto(sock,content,strlen(content),0,(struct sockaddr*)&peer,sizeof(peer)); 

Эта функция создает sockaddr_in6:

sockaddr_in6 create_ipv6_sockaddr(int port, string addr, uint32_t flowinfo, uint32_t scope_id, bool ipv4=false) { 
    struct sockaddr_in6 si; 
    si.sin6_family = AF_INET6; 
    if (ipv4) { 
     si.sin6_family = AF_INET; 
    } 
    si.sin6_port = htons(port); 
    si.sin6_flowinfo = flowinfo; // Should be 0 or 'random' number to distinguish this flow 
    if (ipv4) { 
     addr = "::ffff:" + addr; 
    } 
    inet_pton(AF_INET6, addr.c_str(), &si.sin6_addr); 
    if (!si.sin6_addr.s6_addr) { 
     perror("Address is wrong.."); 
    } 
    si.sin6_scope_id = scope_id; 
    return si; 
} 

В принципе, если адрес является адресом IPv4, я добавляю его ::ffff: и Я установил семейство AF_INET.

Это возможно. Если да, то что я делаю неправильно?

EDIT:

Резюмируя, сообщение IPv4 не может отправить, если сокет IPv6 связан с конкретным IP (или интерфейс, не уверен, какой). Поэтому гнездо IPv6 должно использовать подстановочный знак ::0. Семейство структуры sockaddr_in6 должно по-прежнему быть AF_INET6 с добавленным ::ffff:. Код не будет возвращать отказ, если используется AF_INET4, но по моему опыту никакого фактического сообщения не отправлено.

Действительно, если вы создаете структуру sockaddr самостоятельно, вы получаете ее из getaddrinfo, вы сможете передать ее непосредственно в гнездо подстановочного IPv6 для отправки.

ИЗМЕНИТЬ ЛЕТ СПУСТЯ:

По желанию, @Erfan, я откопал код. Я боюсь сказать, что я больше не понимаю все это, и я не могу сказать, действительно ли это работает.

sockaddr_in6 create_ipv6_sockaddr(int port, string addr, uint32_t flowinfo, uint32_t scope_id, bool ipv4=false) { 
    struct sockaddr_in6 si; 
    si.sin6_family = AF_INET6; 
    // if (ipv4) { 
    //  si.sin6_family = AF_INET; 
    // } 
    si.sin6_port = htons(port); 
    si.sin6_flowinfo = flowinfo; // Should be 0 or 'random' number to distinguish this flow 
    if (ipv4) { 
     addr = "::ffff:" + addr; 
    } 
    inet_pton(AF_INET6, addr.c_str(), &si.sin6_addr); 
    if (!si.sin6_addr.s6_addr) { 
     perror("Address is wrong.."); 
    } 
    // char s[40]; 
    // inet_ntop(AF_INET6, &(si.sin6_addr), s, sizeof(s)); 
    // fprintf(stderr, "Sockaddr %d %s\n", si.sin6_family, s); 
    si.sin6_scope_id = scope_id; 
    if (scope_id == 0 && !ipv4) { 
     si.sin6_scope_id = ipv6_to_scope_id(&si); // Interface number 
    } 
    return si; 
} 

int ipv6_to_scope_id(sockaddr_in6 *find) { 
    struct ifaddrs *addrs, *iap; 
    struct sockaddr_in6 *sa; 
    char host[NI_MAXHOST]; 

    getifaddrs(&addrs); 
    for (iap = addrs; iap != NULL; iap = iap->ifa_next) { 
     if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == AF_INET6) { 
      sa = (struct sockaddr_in6 *)(iap->ifa_addr); 
      if (memcmp(&find->sin6_addr.s6_addr, &sa->sin6_addr.s6_addr, sizeof(sa->sin6_addr.s6_addr)) == 0) { 
       getnameinfo(iap->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 
       fprintf(stderr, "Found interface %s with scope %d\n", host, sa->sin6_scope_id); 
       return sa->sin6_scope_id; 
      } 
     } 
    } 
    freeifaddrs(addrs); 
    return 0; 
} 

И связывать:

int bind_ipv6 (sockaddr_in6 sa) { 
    int fd = socket(AF_INET6, SOCK_DGRAM, 0); 
    if (fd < 0) { 
     perror("Creating socket failed"); 
    } 
    char str[40]; 
    inet_ntop(AF_INET6, &(sa.sin6_addr), str, sizeof(str)); 
    // fprintf(stderr, "Bind to %s:%d\n", str, ntohs(sa.sin6_port)); 
    int no = 0; 
    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&no, sizeof(no)) < 0) { // Only works with wildcard 
     perror("V6ONLY failed"); 
    } 
    if (bind(fd, (sockaddr*)&sa, sizeof(sa)) < 0) { 
     perror("Binding failed"); 
    } 

    return fd; 
} 
+0

есть примеры сырого сокета здесь: http://www.pdbuchan.com/rawsock/rawsock.html. inet_pton вызывается с AF_INET в случае IPV4 в примере udp6_to4.c – lucasg

ответ

4

При использовании ::ffff: отображенные адреса, то вы на самом деле отправки пакетов IPv4 через сеть, используя сокеты IPv6 в программном обеспечении. Это упрощает поддержку обоих протоколов, но не позволяет вам смешивать разные семейства адресов.

Пакет в сети представляет собой либо пакет IPv4 (с исходными и целевыми адресами IPv4), либо пакет IPv6 (с адресами источника и адреса IPv6). Адрес ::ffff: никогда не будет использоваться в фактическом пакете. Он существует только как программное представление.

Если вы используете сокеты IPv6 для связи с адресами ::ffff:, тогда на проводе они будут простыми пакетами IPv4, а ваш локальный IPv4-адрес будет использоваться на вашей стороне соединения.

+0

Так что я все равно должен создать структуру sockaddr_in6, как и сейчас. Затем сокет IPv6 превратит его в пакет IPv4 с локальным адресом IPv4 в качестве источника и адресом назначения IPv4. Итак, почему я все еще получаю ** Ошибка в сети - ошибка? –

+0

Попробуйте без 'si.sin6_family = AF_INET;' и обработайте его так же, как это IPv6 от программного обеспечения –

+0

, которое ничего не меняет, я боюсь. –

0

::ffff: адреса предназначены только для использования в IPV6_V6ONLY=0 серверах.

Если вы хотите подключиться к IPv4-адресу, создайте соответствующий сокет.

Вы можете упростить свою жизнь, если используете только getaddrinfo(): по сути, вы вставляете комбинацию хост/порт, и он даст вам список «адресных записей» для подключения. Эти записи содержат все необходимое для создания и подключения сокета.

Вот короткий фрагмент кода в качестве примера:

struct addrinfo hints = { .ai_socktype=SOCK_STREAM, .ai_flags = AI_CANONNAME }; 
struct addrinfo * ai; 
char name[100]; 
int sockfd = -1; 
int st = getaddrinfo(host, sport, &hints, &ai); 
if (st < 0) { 
    // complain and exit 
} 
// Now we have the wanted infos in ai. 
struct addrinfo * aii; 
for (aii=ai; aii; aii=aii->ai_next) { 
    /* Create a socket */ 
    if((sockfd = socket(aii->ai_family, aii->ai_socktype, aii->ai_protocol)) == -1) { 
     continue; 
    } 
    else { 
     /* Establish a connection */ 
     if(connect(sockfd, aii->ai_addr, aii->ai_addrlen) == -1) { 
      close(sockfd); 
      sockfd = -1; 
      continue; 
     } 
     else { 
      // Success. Get IP address connected to in a readable form. 
      int st = getnameinfo(aii->ai_addr, aii->ai_addrlen, name, sizeof name, 
       NULL, 0, NI_NUMERICHOST); 
      if (st != 0) name[0] = '\0'; 
      break; 
     } 
    } 
} 
freeaddrinfo(ai); 
+0

Мне любопытно. Как должна работать первая строка кода? –

+0

@Oxidator Первая строка - это инициализатор для данной 'struct '(см. [здесь] (http://en.wikipedia.org/wiki/Struct_%28C_programming_language%29#Struct_initialization) и [здесь] (http://publib.boulder.ibm.com/infocenter/ln xpcomp/v8v101/index.jsp? тема =% 2Fcom.ibm.xlcpp8l.doc% 2Flanguage% 2Fref% 2Fstrin.htm)). – glglgl

+0

Мне очень нравится синтаксис, но мой компилятор не по какой-то причине. –