发送数据包到ipv6链接本地组播地址

时间:2017-03-16 08:18:00

标签: python c go ipv6 multicast

我用不同语言编写了一些示例代码,以便将数据包发送到ipv6链接本地多播地址(ff02 :: fb),但它并不总是有效。 python版本:

#!/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优胜美地)上获得错误“无路由到主机”。 然后我写了一个简单的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上得到错误“connect:invalid argument”,并且在OS X上出现与上面的示例相同的错误“没有路由到主机”。最后,我采用了低级语言:

    /* 
 * 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不支持自动选择界面。我不认为OS X上的某些配置会影响这一点,因为我可以看到Chrome浏览器顺利地在端口5353上向ff02 :: fb发送一些mDNS查询,但我不能发送它,更不用说接收了。

编辑:添加范围ID(en0)使得go&amp; C版本在OS X上运行,但是python(2.7.6)版本仍然抱怨:“没有到主机的路由”。我不希望一些不可移植的代码迭代本地接口列表,然后指定en0(此外,并非所有Mac都有它)。有什么简单的方法吗?

0 个答案:

没有答案