如何使用转换语言

时间:2016-04-01 04:01:05

标签: go ip-address netlink

我有以下代码,它应该使用RTNETLINK套接字监视网络更改。但是当我为接口设置新的IP地址" New Addr"或者" Del Addr"不显示。可能出现什么问题。

package main

import (
    "fmt"
    "syscall"
)

func main() {
    l, _ := ListenNetlink()

    for {
        msgs, err := l.ReadMsgs()
        if err != nil {
            fmt.Println("Could not read netlink: %s", err)
        }

        for _, m := range msgs {
            if IsNewAddr(&m) {
                fmt.Println("New Addr")
            }

            if IsDelAddr(&m) {
                fmt.Println("Del Addr")
            }
        }
    }
}

type NetlinkListener struct {
    fd int
    sa *syscall.SockaddrNetlink
}

func ListenNetlink() (*NetlinkListener, error) {
    groups := syscall.RTNLGRP_LINK |
        syscall.RTNLGRP_IPV4_IFADDR |
        syscall.RTNLGRP_IPV6_IFADDR

    s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM,
        syscall.NETLINK_ROUTE)
    if err != nil {
        return nil, fmt.Errorf("socket: %s", err)
    }

    saddr := &syscall.SockaddrNetlink{
        Family: syscall.AF_NETLINK,
        Pid:    uint32(0),
        Groups: uint32(groups),
    }

    err = syscall.Bind(s, saddr)
    if err != nil {
        return nil, fmt.Errorf("bind: %s", err)
    }

    return &NetlinkListener{fd: s, sa: saddr}, nil
}

func (l *NetlinkListener) ReadMsgs() ([]syscall.NetlinkMessage, error) {
    defer func() {
        recover()
    }()

    pkt := make([]byte, 2048)

    n, err := syscall.Read(l.fd, pkt)
    if err != nil {
        return nil, fmt.Errorf("read: %s", err)
    }

    msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
    if err != nil {
        return nil, fmt.Errorf("parse: %s", err)
    }

    return msgs, nil
}

func IsNewAddr(msg *syscall.NetlinkMessage) bool {
    if msg.Header.Type == syscall.RTM_NEWADDR {
        return true
    }

    return false
}

func IsDelAddr(msg *syscall.NetlinkMessage) bool {
    if msg.Header.Type == syscall.RTM_DELADDR {
        return true
    }

    return false
}

func IsRelevant(msg *syscall.IfAddrmsg) bool {
    if msg.Scope == syscall.RT_SCOPE_UNIVERSE ||
        msg.Scope == syscall.RT_SCOPE_SITE {
        return true
    }

    return false
}

4 个答案:

答案 0 :(得分:2)

我在syscall.go文件中找到了包。常量变量syscall.RTNLGRP_IPV4_IFADDR=0x5。但是,在rtnetlink.h source中定义的C语言中的模拟RTMGRP_IPV4_IFADDR常量具有以下不同的值:

#define RTMGRP_IPV4_IFADDR 0x10

我通过github.com提交了issue,我希望它会在即将发布的版本中修复。

现在,您可以在0x10的代码中使用0x5。它会完美运作。

原来它根本不是bug。他们没有从rtnetlink.h source重新声明RTMGRP_*常量变量组,并且由于syscall.go被冻结,因此不希望在功能中添加此变量。但是,他们建议使用RTNLGRP_*RTMGRP_*也在rtnetlink.h source中声明。然而,这两组常数变量在以下方式上是不同的。 RTMGRP_IPV4_IFADDR = 0x10组表示位值(即:RTLNGRP_*)并为用户空间后向功能声明。 RTNLGRP_IPV4_IFADDR=0x5组表示位位置而不是位值(即:1 << (RTNLGRP_* - 1)),可以通过以下方式转换为位值M1 *

答案 1 :(得分:2)

根据接受的答案,修复它以将groups更改为以下内容:

groups := (1 << (syscall.RTNLGRP_LINK - 1)) |
    (1 << (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
    (1 << (syscall.RTNLGRP_IPV6_IFADDR - 1))

答案 2 :(得分:0)

这是* BSD的等效代码:

package main

import (
    "fmt"
    "log"
    "syscall"
)

func main() {
    netlink, err := ListenNetlink()
    if err != nil {
        log.Printf("[ERR] Could not create netlink listener: %v", err)
        return
    }

    for {
        msgs, err := netlink.ReadMsgs()
        if err != nil {
            log.Printf("[ERR] Could not read netlink: %v", err)
        }

        for _, msg := range msgs {
            if _, ok := msg.(*syscall.InterfaceAddrMessage); ok {
                log.Printf("address change!")
            }
        }
    }
}

type NetlinkListener struct {
    fd int
}

func ListenNetlink() (*NetlinkListener, error) {
    s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
    if err != nil {
        return nil, fmt.Errorf("socket: %s", err)
    }
    return &NetlinkListener{fd: s}, nil
}

func (l *NetlinkListener) ReadMsgs() ([]syscall.RoutingMessage, error) {
    defer func() {
        recover()
    }()

    pkt := make([]byte, 2048)

    n, err := syscall.Read(l.fd, pkt)
    if err != nil {
        return nil, fmt.Errorf("read: %s", err)
    }

    msgs, err := syscall.ParseRoutingMessage(pkt[:n])
    if err != nil {
        return nil, fmt.Errorf("parse: %s", err)
    }

    return msgs, nil
}

答案 3 :(得分:0)

更新示例应为

package main

import (
    "fmt"
    "syscall"
)

func main() {
    l, _ := ListenNetlink()

    for {
        msgs, err := l.ReadMsgs()
        if err != nil {
            fmt.Println("Could not read netlink: %s", err)
        }

        for _, m := range msgs {
            if IsNewAddr(&m) {
                fmt.Println("New Addr")
            }

            if IsDelAddr(&m) {
                fmt.Println("Del Addr")
            }
        }
    }
}

type NetlinkListener struct {
    fd int
    sa *syscall.SockaddrNetlink
}

func ListenNetlink() (*NetlinkListener, error) {
    groups := (1 << (syscall.RTNLGRP_LINK - 1)) |
        (1 << (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
        (1 << (syscall.RTNLGRP_IPV6_IFADDR - 1))

    s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM,
        syscall.NETLINK_ROUTE)
    if err != nil {
        return nil, fmt.Errorf("socket: %s", err)
    }

    saddr := &syscall.SockaddrNetlink{
        Family: syscall.AF_NETLINK,
        Pid:    uint32(0),
        Groups: uint32(groups),
    }

    err = syscall.Bind(s, saddr)
    if err != nil {
        return nil, fmt.Errorf("bind: %s", err)
    }

    return &NetlinkListener{fd: s, sa: saddr}, nil
}

func (l *NetlinkListener) ReadMsgs() ([]syscall.NetlinkMessage, error) {
    defer func() {
        recover()
    }()

    pkt := make([]byte, 2048)

    n, err := syscall.Read(l.fd, pkt)
    if err != nil {
        return nil, fmt.Errorf("read: %s", err)
    }

    msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
    if err != nil {
        return nil, fmt.Errorf("parse: %s", err)
    }

    return msgs, nil
}

func IsNewAddr(msg *syscall.NetlinkMessage) bool {
    if msg.Header.Type == syscall.RTM_NEWADDR {
        return true
    }

    return false
}

func IsDelAddr(msg *syscall.NetlinkMessage) bool {
    if msg.Header.Type == syscall.RTM_DELADDR {
        return true
    }

    return false
}

// rtm_scope is the distance to the destination:
//
// RT_SCOPE_UNIVERSE   global route
// RT_SCOPE_SITE       interior route in the
// local autonomous system
// RT_SCOPE_LINK       route on this link
// RT_SCOPE_HOST       route on the local host
// RT_SCOPE_NOWHERE    destination doesn't exist
//
// The values between RT_SCOPE_UNIVERSE and RT_SCOPE_SITE are
// available to the user.

func IsRelevant(msg *syscall.IfAddrmsg) bool {
    if msg.Scope == syscall.RT_SCOPE_UNIVERSE ||
        msg.Scope == syscall.RT_SCOPE_SITE {
        return true
    }

    return false
}