问题
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:
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.
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 << (RTNLGRP_* - 1)
回答2:
As per the accepted answer, the fix it to change the groups
to the following:
groups := (1 << (syscall.RTNLGRP_LINK - 1)) |
(1 << (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
(1 << (syscall.RTNLGRP_IPV6_IFADDR - 1))
回答3:
Here's the equivalent code for *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
}
来源:https://stackoverflow.com/questions/36347807/how-to-monitor-ip-address-change-using-rtnetlink-socket-in-go-language