golang:如何使net.Dialer()从指定的'LocalAddr'发送数据包而不是环回?绑定在哪里?

问题描述 投票:0回答:1

(环境:裸机 x86_64 全新未修改 Ubuntu 22.04LTS)

我想做 golang net.Dialer 相当于以下 Python 代码。此 Python 确保以太网设备“enp1s0f1”的 MAC 地址出现在 TCP 数据包的源部分中,以便每个传输的数据包都会增加 tx_unicast 计数,如

ethtool -S enp1s0f1
所示。 tcpdump 永远不会显示离开环回设备的数据包,即“lo In ifindex 1 00:00:00:00:00:00...”:

#!/usr/bin/python3

import time
import socket

def send_tcp_packet(dev, packet):
    # Create a raw socket
    s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.SOCK_RAW)

    # Bind the socket to the specific Ethernet device
    s.bind((dev, 0)) <--- MAGIC CALL HERE
    . . .
send_tcp_packet("enp1s0f1", ...)

这是我编写的 golang 等效代码:

func main() {  
  // Lookup ethernet device 'enp1s0f1'                                                                                                         
  ief, err := net.InterfaceByName("enp1s0f1")                                                                           
  if err!=nil {                                                                                                         
    panic(err)                                                                                                          
  }                                                                                                                     
  fmt.Printf("ief : %+v\n", ief)                                                                                        
                                                                                                                        
  addr, err := ief.Addrs()                                                                                              
  if err!=nil || len(addr)==0 {                                                                                         
    panic(err)                                                                                                          
  }                                                                                                                     
  fmt.Printf("addr: %v\n", addr)                                                                                        
                                                                                                                        
  // for now hardcode 192.168.0.2; later use addr[0] since
  // I know addr[0]=='192.168.0.2/28'                                                                   
  bindAddr, err := net.ResolveIPAddr("ip", "192.168.0.2")                                                               
  if err!=nil || len(addr)==0 {                                                                                         
    panic(err)                                                                                                          
  }                                                                                                                     
                                                                                                                        
  // Make dialer s.t. it sends packets from 'bindAddr'                                                                  
  var d = &net.Dialer{LocalAddr: bindAddr,}                                                                             
  fmt.Printf("dialer bound to %v...\n", d.LocalAddr)                                                                    
                                                                                                                        
  fmt.Printf("dialing 192.168.0.2:2000 ...\n")                                                                                           
  conn, err1 := d.Dial("udp", "192.168.0.2:2000") 

当我运行此代码时:

ief : &{Index:3 MTU:1500 Name:enp1s0f1 HardwareAddr:08:c0:eb:d4:d1:6f Flags:...}
addr: [192.168.0.2/28 fe80::ac0:ebff:fed4:d16f/64]
dialer bound to 192.168.0.2...
dialing ...
2024/01/03 18:09:03 Failed to dial: <nil>

如果我没有在拨号器中设置

LocalAddr
,连接可以工作,但 tcpdump 报告数据包通过环回设备。

我需要做什么才能让Dialer发送MAC地址为“enp1s0f1”的UDP数据包?我尝试过使用源和目标的 DialUDP,但没有成功。

看来有问题,

  • 我错过了绑定调用?这可能需要 root;对我来说没问题,例如绑定。但它要做的只是使用 BINDTODEVICE 调用 socketopt()。 Python 有一个等价的东西,比如 golang 这对 w.r.t 没有任何影响。到环回。这不是解决办法。
  • 或者拨号器的解析器不喜欢“bindAddr”(端口丢失)
  • 或者拨号器的 LocalAddr 从未适合我的用例。 “LocalAddr”似乎仅用于保存源 IP 和端口。源MAC地址及其从哪个以太网设备传输的问题与'LocalAddr'无关
go sockets udp
1个回答
0
投票

听起来您正在寻找的实际上并不是创建原始套接字,而是将套接字绑定到特定设备。这是特定于平台的操作,但在 Linux 上您可以使用

SO_BINDTODEVICE
sockopt。

特定于平台,仅在

syscall
包中公开,您可以在侦听或拨号连接时通过
Control
选项挂钩。由于普通的 UDP 套接字处于侦听状态,因此您需要使用侦听器来实现此目的:

listener := &net.ListenConfig{
    Control: func(network, addres string, c syscall.RawConn) error {
        var err error
        ctrlErr := c.Control(func(fd uintptr) {
            err = syscall.BindToDevice(int(fd), "enp1s0f1")
        })
        if ctrlErr != nil {
            return ctrlErr
        }
        return err
    },
}

pc, err := listener.ListenPacket(context.Background(), "udp", "192.168.0.2:0")
if err != nil {
    log.Fatal(err)
}

udpConn := pc.(*net.UDPConn)
...
© www.soinside.com 2019 - 2024. All rights reserved.