2016-02-09 6 views
0

Задача заключается в том, чтобы связать сокет специально для адреса 1.0.0.2:520 (назначенный eth2), а затем прочитать многоадресные UDP-пакеты, адресованные 224.0.0.9:520.В Голанге, как получать многоадресные пакеты с сокетом, привязанным к определенному адресу/порту?

Я пытаюсь ниже код, основанный на https://godoc.org/golang.org/x/net/ipv4

К сожалению, результат этого debbuging сообщение никогда не достигается: log.Printf("udpReader: recv %d bytes from %s to %s on %s", n, cm.Src, cm.Dst, ifname)

Я знаю eth2 получает нужные пакеты, потому что у меня есть этот пакет снифер работает на это:

sudo tcpdump -n -i eth2 
18:40:28.571456 IP 1.0.0.1.520 > 224.0.0.9.520: RIPv2, Request, length: 24 
18:40:29.556503 IP 1.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 64 

Это пример кода. Можете ли вы понять, почему это не работает?

package main 

import (
    "fmt" 
    "log" 
    "net" 

    "golang.org/x/net/ipv4" 
) 

func main() { 
    if err := interfaceAdd("eth2"); err != nil { 
     log.Printf("main: error: %v", err) 
    } 

    log.Printf("main: waiting forever") 
    <-make(chan int) 
} 

func interfaceAdd(s string) error { 

    iface, err1 := net.InterfaceByName(s) 
    if err1 != nil { 
     return err1 
    } 

    addrList, err2 := iface.Addrs() 
    if err2 != nil { 
     return err2 
    } 

    for _, a := range addrList { 
     addr, _, err3 := net.ParseCIDR(a.String()) 
     if err3 != nil { 
      log.Printf("interfaceAdd: parse CIDR error for '%s' on '%s': %v", addr, s, err3) 
      continue 
     } 
     if err := join(iface, addr); err != nil { 
      log.Printf("interfaceAdd: join error for '%s' on '%s': %v", addr, s, err) 
     } 
    } 

    return nil 
} 

func join(iface *net.Interface, addr net.IP) error { 
    proto := "udp" 
    var a string 
    if addr.To4() == nil { 
     // IPv6 
     a = fmt.Sprintf("[%s]", addr.String()) 
    } else { 
     // IPv4 
     a = addr.String() 
    } 

    hostPort := fmt.Sprintf("%s:520", a) // rip multicast port 

    // open socket (connection) 
    conn, err2 := net.ListenPacket(proto, hostPort) 
    if err2 != nil { 
     return fmt.Errorf("join: %s/%s listen error: %v", proto, hostPort, err2) 
    } 

    // join multicast address 
    pc := ipv4.NewPacketConn(conn) 
    if err := pc.JoinGroup(iface, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 9)}); err != nil { 
     conn.Close() 
     return fmt.Errorf("join: join error: %v", err) 
    } 

    // request control messages 
    /* 
     if err := pc.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil { 
      // warning only 
      log.Printf("join: control message flags error: %v", err) 
     } 
    */ 

    go udpReader(pc, iface.Name, addr.String()) 

    return nil 
} 

func udpReader(c *ipv4.PacketConn, ifname, ifaddr string) { 

    log.Printf("udpReader: reading from '%s' on '%s'", ifaddr, ifname) 

    defer c.Close() 

    buf := make([]byte, 10000) 

    for { 
     n, cm, _, err := c.ReadFrom(buf) 
     if err != nil { 
      log.Printf("udpReader: ReadFrom: error %v", err) 
      break 
     } 

     // make a copy because we will overwrite buf 
     b := make([]byte, n) 
     copy(b, buf) 

     log.Printf("udpReader: recv %d bytes from %s to %s on %s", n, cm.Src, cm.Dst, ifname) 
    } 

    log.Printf("udpReader: exiting '%s'", ifname) 
} 

Выход:

2016/02/09 18:44:20 interfaceAdd: join error for 'fe80::a00:27ff:fe52:9575' on 'eth2': join: udp/[fe80::a00:27ff:fe52:9575]:520 listen error: listen udp [fe80::a00:27ff:fe52:9575]:520: bind: invalid argument 
2016/02/09 18:44:20 main: waiting forever 
2016/02/09 18:44:20 udpReader: reading from '1.0.0.2' on 'eth2' 
+0

Просто, чтобы сделать его Кристал ясно: Задача состоит в том, чтобы не связываться интерфейс, который прослушивает этот IP-адрес, но конкретно и положительно относится к самому IP-адресу? –

+0

Задача состоит в получении пакетов, адресованных 224.0.0.9, но только на определенном интерфейсе. Связывание с IP-адресом - это механизм, который, я полагаю, должен использовать для достижения этой цели. – Everton

+0

Nope. Определите интерфейс для привязки его IP-адресом, затем привяжите его и присоедините к группе многоадресной рассылки. –

ответ

1

Чтобы создать тройники специфичные для интерфейса, чтобы принимать пакеты, адресованные на тот же адрес многоадресной (224.0.0.9:520), мой оригинальный Go код не хватает трех основных вопросы:

  1. для того, чтобы связать несколько сокеты же UDP порт, установить syscall.SO_REUSEADDR
  2. для того, чтобы ограничить сокет специальный интерфейс, набор syscall.SO_BINDTODEVICE
  3. свяжи UDP сокеты 0.0.0.0:520

Найти полный образец кода здесь: http://play.golang.org/p/NprsZPHQmj