отправлять пакеты на локальный многоадресный адрес ipv6 link

Я пишу пример кода на разных языках для отправки пакетов на локальный многоадресный адрес ipv6 link (ff02::fb), но это не всегда работает. версия питона:

#!/usr/bin/env python

MYPORT = 5353
MYGROUP_6 = 'ff02::fb'
MYTTL = 1 # Increase to reach other networks

import time
import struct
import socket
import sys

def main():
    sender(MYGROUP_6)


def sender(group):
    addrinfo = socket.getaddrinfo(group, None)[0]

    s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)

    # Set Time-to-live (optional)
    ttl_bin = struct.pack('@i', MYTTL)
    s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin)

    while True:
        data = repr(time.time())
        s.sendto(data + '\0', (addrinfo[4][0], MYPORT))
        time.sleep(1)

if __name__ == '__main__':
    main()

этот работает в Linux (3.13.0-24-generic), но получает ошибку «нет маршрута к хосту» в OS X (10.10 Yosemite). Затем я пишу простую программу на Go, чтобы попробовать еще раз:

package main

import (
    "fmt"
    "net"
)

func main() {
    addr, err := net.ResolveUDPAddr("udp6", "[ff02::fb]:5353")
    if err != nil {
        fmt.Printf("ResolveUDPAddr err: %v\n", err)
        return
    }

    if conn, err := net.DialUDP("udp6", nil, addr); err == nil {
        if _, err = conn.Write([]byte("hello")); err != nil {
            fmt.Printf("Write failed, %v\n", err)
        }
    } else {
        fmt.Printf("DialUDP err: %v\n", err)
    }
    return
}

этот получает ошибку «подключение: неверный аргумент» в Linux и ту же ошибку «нет маршрута к хосту» с примером выше в OS X. Наконец, я пробую низкоуровневый язык C:

 /* 
 * Examples:
 *     >sender 224.0.22.1 9210 6000 1000
 *     >sender ff15::1 2001 65000 1
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h> /* for usleep() */

#define SOCKET int

int mcast_send_socket(char* multicastIP, char* multicastPort,  int multicastTTL, struct addrinfo **multicastAddr) {

    SOCKET sock;
    struct addrinfo hints = { 0 };    /* Hints for name lookup */

    /*
      Resolve destination address for multicast datagrams 
    */
    hints.ai_family   = PF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags    = AI_NUMERICHOST;
    int status;
    if ((status = getaddrinfo(multicastIP, multicastPort, &hints, multicastAddr)) != 0 )
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        return -1;
    }

    /* 
       Create socket for sending multicast datagrams 
    */
    if ( (sock = socket((*multicastAddr)->ai_family, (*multicastAddr)->ai_socktype, 0)) < 0 ) {
        perror("socket() failed");
        freeaddrinfo(*multicastAddr);
        return -1;
    }

    /* 
       Set TTL of multicast packet 
    */
    if ( setsockopt(sock,
            (*multicastAddr)->ai_family == PF_INET6 ? IPPROTO_IPV6        : IPPROTO_IP,
            (*multicastAddr)->ai_family == PF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL,
            (char*) &multicastTTL, sizeof(multicastTTL)) != 0 ) {
        perror("setsockopt() failed");
        freeaddrinfo(*multicastAddr);
        return -1;
    }


    /* 
       set the sending interface 
    */
    if((*multicastAddr)->ai_family == PF_INET) {
        in_addr_t iface = INADDR_ANY; /* well, yeah, any */
        if(setsockopt (sock, 
                   IPPROTO_IP,
                   IP_MULTICAST_IF,
                   (char*)&iface, sizeof(iface)) != 0) { 
            perror("interface setsockopt() sending interface");
            freeaddrinfo(*multicastAddr);
            return -1;
        }

    }

    if((*multicastAddr)->ai_family == PF_INET6) {
        unsigned int ifindex = 0; /* 0 means 'default interface'*/
        if(setsockopt (sock, 
                   IPPROTO_IPV6,
                   IPV6_MULTICAST_IF,
                   (char*)&ifindex, sizeof(ifindex)) != 0) { 
            perror("interface setsockopt() sending interface");
            freeaddrinfo(*multicastAddr);
            return -1;
        }   
    }

    return sock;
}

static void DieWithError(char* errorMessage)
{
  fprintf(stderr, "%s\n", errorMessage);
  exit(EXIT_FAILURE);
}


int main(int argc, char *argv[])
{
  SOCKET sock;
  struct addrinfo *multicastAddr;
  char*     multicastIP;            /* Arg: IP Multicast address */
  char*     multicastPort;          /* Arg: Server port */
  char*     sendString;             /* Arg: String to multicast */
  int       sendStringLen;          /* Length of string to multicast */
  int       multicastTTL;           /* Arg: TTL of multicast packets */
  int       defer_ms;               /* miliseconds to defer in between sending */

  int i;

  if ( argc < 5 || argc > 6 )
    {
      fprintf(stderr, "Usage: %s <Multicast Address> <Port> <packetsize> <defer_ms> [<TTL>]\n", argv[0]);
      exit(EXIT_FAILURE);
    }


  multicastIP   = argv[1];             /* First arg:   multicast IP address */
  multicastPort = argv[2];             /* Second arg:  multicast port */
  sendStringLen = atoi(argv[3]);   
  defer_ms = atoi(argv[4]);

  /* just fill this with some byte */
  sendString = calloc(sendStringLen, sizeof(char));
  for(i = 0; i<sendStringLen; ++i)
    sendString[i]= 's';



  multicastTTL  = (argc == 6 ?         /* Fourth arg:  If supplied, use command-line */
           atoi(argv[5]) : 1); /* specified TTL, else use default TTL of 1 */



  sock = mcast_send_socket(multicastIP, multicastPort, multicastTTL, &multicastAddr);
  if(sock == -1 )
      DieWithError("mcast_send_socket() failed");


  int nr=0;
  for (;;) /* Run forever */
    {
      int*  p_nr = (int*)sendString;
      *p_nr = htonl(nr);

      if ( sendto(sock, sendString, sendStringLen, 0,
          multicastAddr->ai_addr, multicastAddr->ai_addrlen) != sendStringLen )
        DieWithError("sendto() sent a different number of bytes than expected");

      fprintf(stderr, "packet %d sent\n", nr);
      nr++;
      usleep(defer_ms*1000); 
    }

  /* NOT REACHED */
  return 0;
}

эта версия C также не работает в OS X, она жалуется «Не удается назначить запрошенный адрес» на setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*)&ifindex, sizeof(ifindex)), похоже, OS X не поддерживает автоматический выбор интерфейса.


РЕДАКТИРОВАТЬ: добавление идентификатора области (en0) заставляет версию go & C работать на OS X, но версия python (2.7.6) по-прежнему жалуется: «Нет маршрута к хосту».


person jfly    schedule 16.03.2017    source источник