如何使用Go语言中的RTNETLINK套接字监视IP地址更改

huangapple go评论94阅读模式
英文:

How to monitor ip address change using RTNETLINK socket in go language

问题

我有以下代码,应该使用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
}

以上是要翻译的代码。

英文:

I have following code, which should monitor network changes using RTNETLINK socket. However when I am setting new IP address for interface "New Addr" or "Del Addr" does not showing. What can be possible problem.

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
}

答案1

得分: 2

我在syscall.go文件中找到了一个包。常量变量syscall.RTNLGRP_IPV4_IFADDR=0x5。然而,在C语言中,类似的RTMGRP_IPV4_IFADDR常量在rtnetlink.h源代码中定义的值不同,如下所示:

#define RTMGRP_IPV4_IFADDR 0x10

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

现在,您可以在您的代码中使用0x10代替0x5。它将完美地工作。

事实证明,这根本不是一个错误。他们没有重新声明rtnetlink.h源代码中的RTMGRP_*常量变量组,也不打算在未来的版本中添加此功能,因为syscall.go已经冻结。然而,他们建议使用也在rtnetlink.h源代码中声明的RTNLGRP_*。然而,这两组常量变量在以下方面有所不同。RTMGRP_*组表示位值(例如:RTMGRP_IPV4_IFADDR = 0x10),并且为用户空间向后兼容性而声明。RTNLGRP_*组表示位位置而不是位值(例如:RTNLGRP_IPV4_IFADDR=0x5),可以通过以下方式将其转换为位值:1 << (RTNLGRP_* - 1)

英文:

<s>I found bag in syscall.go file. Constant variable syscall.RTNLGRP_IPV4_IFADDR=0x5. However analog RTMGRP_IPV4_IFADDR constant in C language which is defined in rtnetlink.h source has different value as following:

#define RTMGRP_IPV4_IFADDR 0x10

I submitted issue through github.com and I hope it will fixed in upcoming releases.

For now you can use 0x10 in your code insted of 0x5. It will work perfectly.</s>

Turns out that it is not bug at all. They did not re declare RTMGRP_* constant variables group from rtnetlink.h source and do not want to add this in feature as well since syscall.go is frozen. However they suggest using RTNLGRP_* which is also declared in rtnetlink.h source. However this two groups of constant variables is different in following way. RTMGRP_* group represents bit value (i.e.: RTMGRP_IPV4_IFADDR = 0x10) and declared for userspace backward capabilities. RTLNGRP_* group represents bit position rather than bit value (i.e.: RTNLGRP_IPV4_IFADDR=0x5) which can be translated to bit value by following way 1 &lt;&lt; (RTNLGRP_* - 1)

答案2

得分: 2

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

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

As per the accepted answer, the fix it to change the groups to the following:

groups := (1 &lt;&lt; (syscall.RTNLGRP_LINK - 1)) |
(1 &lt;&lt; (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
(1 &lt;&lt; (syscall.RTNLGRP_IPV6_IFADDR - 1))

答案3

得分: 1

以下是*BSD的等效代码:

package main

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

func main() {
	netlink, err := ListenNetlink()
	if err != nil {
		log.Printf("[ERR] 无法创建 netlink 监听器:%v", err)
		return
	}

	for {
		msgs, err := netlink.ReadMsgs()
		if err != nil {
			log.Printf("[ERR] 无法读取 netlink:%v", err)
		}

		for _, msg := range msgs {
			if _, ok := msg.(*syscall.InterfaceAddrMessage); ok {
				log.Printf("地址变更!")
			}
		}
	}
}

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
}

希望对你有帮助!

英文:

Here's the equivalent code for *BSD:

package main
import (
&quot;fmt&quot;
&quot;log&quot;
&quot;syscall&quot;
)
func main() {
netlink, err := ListenNetlink()
if err != nil {
log.Printf(&quot;[ERR] Could not create netlink listener: %v&quot;, err)
return
}
for {
msgs, err := netlink.ReadMsgs()
if err != nil {
log.Printf(&quot;[ERR] Could not read netlink: %v&quot;, err)
}
for _, msg := range msgs {
if _, ok := msg.(*syscall.InterfaceAddrMessage); ok {
log.Printf(&quot;address change!&quot;)
}
}
}
}
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(&quot;socket: %s&quot;, err)
}
return &amp;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(&quot;read: %s&quot;, err)
}
msgs, err := syscall.ParseRoutingMessage(pkt[:n])
if err != nil {
return nil, fmt.Errorf(&quot;parse: %s&quot;, err)
}
return msgs, nil
}

答案4

得分: 0

更新示例应为

package main

import (
	"fmt"
	"syscall"
)

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

	for {
		msgs, err := l.ReadMsgs()
		if err != nil {
			fmt.Println("无法读取 netlink:%s", err)
		}

		for _, m := range msgs {
			if IsNewAddr(&m) {
				fmt.Println("新地址")
			}

			if IsDelAddr(&m) {
				fmt.Println("删除地址")
			}
		}
	}
}

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 是到目标的距离:
//
// RT_SCOPE_UNIVERSE   全局路由
// RT_SCOPE_SITE       本地自治系统中的内部路由
// RT_SCOPE_LINK       此链路上的路由
// RT_SCOPE_HOST       本地主机上的路由
// RT_SCOPE_NOWHERE    目标不存在
//
// RT_SCOPE_UNIVERSE 和 RT_SCOPE_SITE 之间的值可供用户使用。

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

	return false
}
英文:

the update Example should be

package main
import (
&quot;fmt&quot;
&quot;syscall&quot;
)
func main() {
l, _ := ListenNetlink()
for {
msgs, err := l.ReadMsgs()
if err != nil {
fmt.Println(&quot;Could not read netlink: %s&quot;, err)
}
for _, m := range msgs {
if IsNewAddr(&amp;m) {
fmt.Println(&quot;New Addr&quot;)
}
if IsDelAddr(&amp;m) {
fmt.Println(&quot;Del Addr&quot;)
}
}
}
}
type NetlinkListener struct {
fd int
sa *syscall.SockaddrNetlink
}
func ListenNetlink() (*NetlinkListener, error) {
groups := (1 &lt;&lt; (syscall.RTNLGRP_LINK - 1)) |
(1 &lt;&lt; (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
(1 &lt;&lt; (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(&quot;socket: %s&quot;, err)
}
saddr := &amp;syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK,
Pid:    uint32(0),
Groups: uint32(groups),
}
err = syscall.Bind(s, saddr)
if err != nil {
return nil, fmt.Errorf(&quot;bind: %s&quot;, err)
}
return &amp;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(&quot;read: %s&quot;, err)
}
msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
if err != nil {
return nil, fmt.Errorf(&quot;parse: %s&quot;, 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&#39;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
}

huangapple
  • 本文由 发表于 2016年4月1日 12:01:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/36347807.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定