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

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

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

问题

我有以下代码,应该使用RTNETLINK套接字监视网络变化。然而,当我为接口设置新的IP地址时,“New Addr”或“Del Addr”没有显示。可能的问题是什么?

  1. package main
  2. import (
  3. "fmt"
  4. "syscall"
  5. )
  6. func main() {
  7. l, _ := ListenNetlink()
  8. for {
  9. msgs, err := l.ReadMsgs()
  10. if err != nil {
  11. fmt.Println("Could not read netlink: %s", err)
  12. }
  13. for _, m := range msgs {
  14. if IsNewAddr(&m) {
  15. fmt.Println("New Addr")
  16. }
  17. if IsDelAddr(&m) {
  18. fmt.Println("Del Addr")
  19. }
  20. }
  21. }
  22. }
  23. type NetlinkListener struct {
  24. fd int
  25. sa *syscall.SockaddrNetlink
  26. }
  27. func ListenNetlink() (*NetlinkListener, error) {
  28. groups := syscall.RTNLGRP_LINK |
  29. syscall.RTNLGRP_IPV4_IFADDR |
  30. syscall.RTNLGRP_IPV6_IFADDR
  31. s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM,
  32. syscall.NETLINK_ROUTE)
  33. if err != nil {
  34. return nil, fmt.Errorf("socket: %s", err)
  35. }
  36. saddr := &syscall.SockaddrNetlink{
  37. Family: syscall.AF_NETLINK,
  38. Pid: uint32(0),
  39. Groups: uint32(groups),
  40. }
  41. err = syscall.Bind(s, saddr)
  42. if err != nil {
  43. return nil, fmt.Errorf("bind: %s", err)
  44. }
  45. return &NetlinkListener{fd: s, sa: saddr}, nil
  46. }
  47. func (l *NetlinkListener) ReadMsgs() ([]syscall.NetlinkMessage, error) {
  48. defer func() {
  49. recover()
  50. }()
  51. pkt := make([]byte, 2048)
  52. n, err := syscall.Read(l.fd, pkt)
  53. if err != nil {
  54. return nil, fmt.Errorf("read: %s", err)
  55. }
  56. msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
  57. if err != nil {
  58. return nil, fmt.Errorf("parse: %s", err)
  59. }
  60. return msgs, nil
  61. }
  62. func IsNewAddr(msg *syscall.NetlinkMessage) bool {
  63. if msg.Header.Type == syscall.RTM_NEWADDR {
  64. return true
  65. }
  66. return false
  67. }
  68. func IsDelAddr(msg *syscall.NetlinkMessage) bool {
  69. if msg.Header.Type == syscall.RTM_DELADDR {
  70. return true
  71. }
  72. return false
  73. }
  74. func IsRelevant(msg *syscall.IfAddrmsg) bool {
  75. if msg.Scope == syscall.RT_SCOPE_UNIVERSE ||
  76. msg.Scope == syscall.RT_SCOPE_SITE {
  77. return true
  78. }
  79. return false
  80. }

以上是要翻译的代码。

英文:

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.

  1. package main
  2. import (
  3. "fmt"
  4. "syscall"
  5. )
  6. func main() {
  7. l, _ := ListenNetlink()
  8. for {
  9. msgs, err := l.ReadMsgs()
  10. if err != nil {
  11. fmt.Println("Could not read netlink: %s", err)
  12. }
  13. for _, m := range msgs {
  14. if IsNewAddr(&m) {
  15. fmt.Println("New Addr")
  16. }
  17. if IsDelAddr(&m) {
  18. fmt.Println("Del Addr")
  19. }
  20. }
  21. }
  22. }
  23. type NetlinkListener struct {
  24. fd int
  25. sa *syscall.SockaddrNetlink
  26. }
  27. func ListenNetlink() (*NetlinkListener, error) {
  28. groups := syscall.RTNLGRP_LINK |
  29. syscall.RTNLGRP_IPV4_IFADDR |
  30. syscall.RTNLGRP_IPV6_IFADDR
  31. s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM,
  32. syscall.NETLINK_ROUTE)
  33. if err != nil {
  34. return nil, fmt.Errorf("socket: %s", err)
  35. }
  36. saddr := &syscall.SockaddrNetlink{
  37. Family: syscall.AF_NETLINK,
  38. Pid: uint32(0),
  39. Groups: uint32(groups),
  40. }
  41. err = syscall.Bind(s, saddr)
  42. if err != nil {
  43. return nil, fmt.Errorf("bind: %s", err)
  44. }
  45. return &NetlinkListener{fd: s, sa: saddr}, nil
  46. }
  47. func (l *NetlinkListener) ReadMsgs() ([]syscall.NetlinkMessage, error) {
  48. defer func() {
  49. recover()
  50. }()
  51. pkt := make([]byte, 2048)
  52. n, err := syscall.Read(l.fd, pkt)
  53. if err != nil {
  54. return nil, fmt.Errorf("read: %s", err)
  55. }
  56. msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
  57. if err != nil {
  58. return nil, fmt.Errorf("parse: %s", err)
  59. }
  60. return msgs, nil
  61. }
  62. func IsNewAddr(msg *syscall.NetlinkMessage) bool {
  63. if msg.Header.Type == syscall.RTM_NEWADDR {
  64. return true
  65. }
  66. return false
  67. }
  68. func IsDelAddr(msg *syscall.NetlinkMessage) bool {
  69. if msg.Header.Type == syscall.RTM_DELADDR {
  70. return true
  71. }
  72. return false
  73. }
  74. func IsRelevant(msg *syscall.IfAddrmsg) bool {
  75. if msg.Scope == syscall.RT_SCOPE_UNIVERSE ||
  76. msg.Scope == syscall.RT_SCOPE_SITE {
  77. return true
  78. }
  79. return false
  80. }

答案1

得分: 2

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

  1. #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:

  1. #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 更改为以下内容:

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

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

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

答案3

得分: 1

以下是*BSD的等效代码:

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "syscall"
  6. )
  7. func main() {
  8. netlink, err := ListenNetlink()
  9. if err != nil {
  10. log.Printf("[ERR] 无法创建 netlink 监听器:%v", err)
  11. return
  12. }
  13. for {
  14. msgs, err := netlink.ReadMsgs()
  15. if err != nil {
  16. log.Printf("[ERR] 无法读取 netlink:%v", err)
  17. }
  18. for _, msg := range msgs {
  19. if _, ok := msg.(*syscall.InterfaceAddrMessage); ok {
  20. log.Printf("地址变更!")
  21. }
  22. }
  23. }
  24. }
  25. type NetlinkListener struct {
  26. fd int
  27. }
  28. func ListenNetlink() (*NetlinkListener, error) {
  29. s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
  30. if err != nil {
  31. return nil, fmt.Errorf("socket:%s", err)
  32. }
  33. return &NetlinkListener{fd: s}, nil
  34. }
  35. func (l *NetlinkListener) ReadMsgs() ([]syscall.RoutingMessage, error) {
  36. defer func() {
  37. recover()
  38. }()
  39. pkt := make([]byte, 2048)
  40. n, err := syscall.Read(l.fd, pkt)
  41. if err != nil {
  42. return nil, fmt.Errorf("read:%s", err)
  43. }
  44. msgs, err := syscall.ParseRoutingMessage(pkt[:n])
  45. if err != nil {
  46. return nil, fmt.Errorf("parse:%s", err)
  47. }
  48. return msgs, nil
  49. }

希望对你有帮助!

英文:

Here's the equivalent code for *BSD:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;log&quot;
  5. &quot;syscall&quot;
  6. )
  7. func main() {
  8. netlink, err := ListenNetlink()
  9. if err != nil {
  10. log.Printf(&quot;[ERR] Could not create netlink listener: %v&quot;, err)
  11. return
  12. }
  13. for {
  14. msgs, err := netlink.ReadMsgs()
  15. if err != nil {
  16. log.Printf(&quot;[ERR] Could not read netlink: %v&quot;, err)
  17. }
  18. for _, msg := range msgs {
  19. if _, ok := msg.(*syscall.InterfaceAddrMessage); ok {
  20. log.Printf(&quot;address change!&quot;)
  21. }
  22. }
  23. }
  24. }
  25. type NetlinkListener struct {
  26. fd int
  27. }
  28. func ListenNetlink() (*NetlinkListener, error) {
  29. s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
  30. if err != nil {
  31. return nil, fmt.Errorf(&quot;socket: %s&quot;, err)
  32. }
  33. return &amp;NetlinkListener{fd: s}, nil
  34. }
  35. func (l *NetlinkListener) ReadMsgs() ([]syscall.RoutingMessage, error) {
  36. defer func() {
  37. recover()
  38. }()
  39. pkt := make([]byte, 2048)
  40. n, err := syscall.Read(l.fd, pkt)
  41. if err != nil {
  42. return nil, fmt.Errorf(&quot;read: %s&quot;, err)
  43. }
  44. msgs, err := syscall.ParseRoutingMessage(pkt[:n])
  45. if err != nil {
  46. return nil, fmt.Errorf(&quot;parse: %s&quot;, err)
  47. }
  48. return msgs, nil
  49. }

答案4

得分: 0

更新示例应为

  1. package main
  2. import (
  3. "fmt"
  4. "syscall"
  5. )
  6. func main() {
  7. l, _ := ListenNetlink()
  8. for {
  9. msgs, err := l.ReadMsgs()
  10. if err != nil {
  11. fmt.Println("无法读取 netlink:%s", err)
  12. }
  13. for _, m := range msgs {
  14. if IsNewAddr(&m) {
  15. fmt.Println("新地址")
  16. }
  17. if IsDelAddr(&m) {
  18. fmt.Println("删除地址")
  19. }
  20. }
  21. }
  22. }
  23. type NetlinkListener struct {
  24. fd int
  25. sa *syscall.SockaddrNetlink
  26. }
  27. func ListenNetlink() (*NetlinkListener, error) {
  28. groups := (1 << (syscall.RTNLGRP_LINK - 1)) |
  29. (1 << (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
  30. (1 << (syscall.RTNLGRP_IPV6_IFADDR - 1))
  31. s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM,
  32. syscall.NETLINK_ROUTE)
  33. if err != nil {
  34. return nil, fmt.Errorf("socket:%s", err)
  35. }
  36. saddr := &syscall.SockaddrNetlink{
  37. Family: syscall.AF_NETLINK,
  38. Pid: uint32(0),
  39. Groups: uint32(groups),
  40. }
  41. err = syscall.Bind(s, saddr)
  42. if err != nil {
  43. return nil, fmt.Errorf("bind:%s", err)
  44. }
  45. return &NetlinkListener{fd: s, sa: saddr}, nil
  46. }
  47. func (l *NetlinkListener) ReadMsgs() ([]syscall.NetlinkMessage, error) {
  48. defer func() {
  49. recover()
  50. }()
  51. pkt := make([]byte, 2048)
  52. n, err := syscall.Read(l.fd, pkt)
  53. if err != nil {
  54. return nil, fmt.Errorf("read:%s", err)
  55. }
  56. msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
  57. if err != nil {
  58. return nil, fmt.Errorf("parse:%s", err)
  59. }
  60. return msgs, nil
  61. }
  62. func IsNewAddr(msg *syscall.NetlinkMessage) bool {
  63. if msg.Header.Type == syscall.RTM_NEWADDR {
  64. return true
  65. }
  66. return false
  67. }
  68. func IsDelAddr(msg *syscall.NetlinkMessage) bool {
  69. if msg.Header.Type == syscall.RTM_DELADDR {
  70. return true
  71. }
  72. return false
  73. }
  74. // rtm_scope 是到目标的距离:
  75. //
  76. // RT_SCOPE_UNIVERSE 全局路由
  77. // RT_SCOPE_SITE 本地自治系统中的内部路由
  78. // RT_SCOPE_LINK 此链路上的路由
  79. // RT_SCOPE_HOST 本地主机上的路由
  80. // RT_SCOPE_NOWHERE 目标不存在
  81. //
  82. // RT_SCOPE_UNIVERSE 和 RT_SCOPE_SITE 之间的值可供用户使用。
  83. func IsRelevant(msg *syscall.IfAddrmsg) bool {
  84. if msg.Scope == syscall.RT_SCOPE_UNIVERSE ||
  85. msg.Scope == syscall.RT_SCOPE_SITE {
  86. return true
  87. }
  88. return false
  89. }
英文:

the update Example should be

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;syscall&quot;
  5. )
  6. func main() {
  7. l, _ := ListenNetlink()
  8. for {
  9. msgs, err := l.ReadMsgs()
  10. if err != nil {
  11. fmt.Println(&quot;Could not read netlink: %s&quot;, err)
  12. }
  13. for _, m := range msgs {
  14. if IsNewAddr(&amp;m) {
  15. fmt.Println(&quot;New Addr&quot;)
  16. }
  17. if IsDelAddr(&amp;m) {
  18. fmt.Println(&quot;Del Addr&quot;)
  19. }
  20. }
  21. }
  22. }
  23. type NetlinkListener struct {
  24. fd int
  25. sa *syscall.SockaddrNetlink
  26. }
  27. func ListenNetlink() (*NetlinkListener, error) {
  28. groups := (1 &lt;&lt; (syscall.RTNLGRP_LINK - 1)) |
  29. (1 &lt;&lt; (syscall.RTNLGRP_IPV4_IFADDR - 1)) |
  30. (1 &lt;&lt; (syscall.RTNLGRP_IPV6_IFADDR - 1))
  31. s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM,
  32. syscall.NETLINK_ROUTE)
  33. if err != nil {
  34. return nil, fmt.Errorf(&quot;socket: %s&quot;, err)
  35. }
  36. saddr := &amp;syscall.SockaddrNetlink{
  37. Family: syscall.AF_NETLINK,
  38. Pid: uint32(0),
  39. Groups: uint32(groups),
  40. }
  41. err = syscall.Bind(s, saddr)
  42. if err != nil {
  43. return nil, fmt.Errorf(&quot;bind: %s&quot;, err)
  44. }
  45. return &amp;NetlinkListener{fd: s, sa: saddr}, nil
  46. }
  47. func (l *NetlinkListener) ReadMsgs() ([]syscall.NetlinkMessage, error) {
  48. defer func() {
  49. recover()
  50. }()
  51. pkt := make([]byte, 2048)
  52. n, err := syscall.Read(l.fd, pkt)
  53. if err != nil {
  54. return nil, fmt.Errorf(&quot;read: %s&quot;, err)
  55. }
  56. msgs, err := syscall.ParseNetlinkMessage(pkt[:n])
  57. if err != nil {
  58. return nil, fmt.Errorf(&quot;parse: %s&quot;, err)
  59. }
  60. return msgs, nil
  61. }
  62. func IsNewAddr(msg *syscall.NetlinkMessage) bool {
  63. if msg.Header.Type == syscall.RTM_NEWADDR {
  64. return true
  65. }
  66. return false
  67. }
  68. func IsDelAddr(msg *syscall.NetlinkMessage) bool {
  69. if msg.Header.Type == syscall.RTM_DELADDR {
  70. return true
  71. }
  72. return false
  73. }
  74. // rtm_scope is the distance to the destination:
  75. //
  76. // RT_SCOPE_UNIVERSE global route
  77. // RT_SCOPE_SITE interior route in the
  78. // local autonomous system
  79. // RT_SCOPE_LINK route on this link
  80. // RT_SCOPE_HOST route on the local host
  81. // RT_SCOPE_NOWHERE destination doesn&#39;t exist
  82. //
  83. // The values between RT_SCOPE_UNIVERSE and RT_SCOPE_SITE are
  84. // available to the user.
  85. func IsRelevant(msg *syscall.IfAddrmsg) bool {
  86. if msg.Scope == syscall.RT_SCOPE_UNIVERSE ||
  87. msg.Scope == syscall.RT_SCOPE_SITE {
  88. return true
  89. }
  90. return false
  91. }

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:

确定