纯Go代码中如何获取和设置网络接口

纯Go代码中如何获取和设置网络接口 我有一台运行Ubuntu服务器的ARM物联网设备,需要在Go代码中读取和写入以下内容:

  1. IP地址(IPv4)
  2. 子网掩码
  3. 默认网关
  4. 主DNS
  5. 备用DNS

我知道可以使用ifconfigroutenameserver从命令行设置这些参数,并将这些命令放在bash脚本中,然后运行/etc/init.d/networking restart来使更改永久生效。

我甚至可以编写文件处理程序直接编辑/etc/network/interfaces,但这两种方案都像是临时解决方案,因此我更希望用纯Go来实现。

有趣的是,Docker是用Go编写的,容器有自己的IP地址,所以这肯定是可行的,除非它们进行了底层内核调用。

我已经查看了osnet包,但没有发现明显的解决方案,所以在这方面我非常需要任何帮助。


3 回复

请查看 Docker 在 GitHub 上的源代码,或许可以看看 libnetwork/osl/interface_linux.go 这个文件


感谢您的建议,如果找不到解决方案我本打算将其作为最后的手段。经过多次调试,我现在确实有了一个解决方案,在此分享出来供遇到类似问题的人参考。

func subnetMask() (addr string) {
	return (net.IP(net.CIDRMask(24, 32))).To4().String()
}
func ipAddress() (addr string) {
	local := "127.0.0.1"
	addrs, _ := net.InterfaceAddrs()

	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				local = ipnet.IP.String()
			}
		}
	}

	return local
}
func defaultGateway() (addr string) {
	local := "127.0.0.1"

	routeCmd := exec.Command("route", "-n")
	output, _ := routeCmd.CombinedOutput()

	lines := strings.Split(string(output), "\n")
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) >= 2 && fields[0] == "0.0.0.0" {
			ip := net.ParseIP(fields[1])
			if ip != nil {
				return ip.To4().String()
			}
		}
	}

	return local
}
func dnsServer(ordinal string) (addr string) {
	local := ""

	routeCmd := exec.Command("nmcli", "device", "list")
	output, err := routeCmd.CombinedOutput()
	if err != nil {
		routeCmd = exec.Command("nmcli", "device", "show")
		output, _ = routeCmd.CombinedOutput()
	}
	/* ********************************************************/

	lines := strings.Split(string(output), "\n")
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) >= 2 && fields[0] == "IP4.DNS["+ordinal+"]:" {
			ip := net.ParseIP(fields[1])
			if ip != nil {
				return ip.To4().String()
			}
		}
	}

	return local
}

在纯Go代码中管理网络接口配置,可以通过几种方式实现。以下是针对你需求的解决方案:

1. 使用 net 包读取当前网络配置

package main

import (
    "fmt"
    "net"
)

func getNetworkInfo() error {
    // 获取网络接口列表
    interfaces, err := net.Interfaces()
    if err != nil {
        return err
    }

    for _, iface := range interfaces {
        // 跳过非活动接口
        if iface.Flags&net.FlagUp == 0 {
            continue
        }

        addrs, err := iface.Addrs()
        if err != nil {
            continue
        }

        fmt.Printf("Interface: %s\n", iface.Name)
        for _, addr := range addrs {
            fmt.Printf("  Address: %s\n", addr.String())
        }
    }
    return nil
}

2. 使用 os/exec 执行系统命令(推荐用于设置)

package main

import (
    "fmt"
    "os/exec"
    "strings"
)

func setStaticIP(interfaceName, ip, netmask, gateway string) error {
    // 设置IP地址和子网掩码
    cmd := exec.Command("ip", "addr", "add", fmt.Sprintf("%s/%s", ip, netmask), "dev", interfaceName)
    if err := cmd.Run(); err != nil {
        return fmt.Errorf("failed to set IP: %v", err)
    }

    // 设置默认网关
    cmd = exec.Command("ip", "route", "add", "default", "via", gateway, "dev", interfaceName)
    if err := cmd.Run(); err != nil {
        return fmt.Errorf("failed to set gateway: %v", err)
    }

    return nil
}

func setDNS(primaryDNS, secondaryDNS string) error {
    // 编辑resolv.conf文件
    dnsContent := fmt.Sprintf("nameserver %s\nnameserver %s\n", primaryDNS, secondaryDNS)
    cmd := exec.Command("sh", "-c", fmt.Sprintf("echo '%s' > /etc/resolv.conf", dnsContent))
    return cmd.Run()
}

3. 直接编辑配置文件

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

func updateInterfacesFile(interfaceName, ip, netmask, gateway string) error {
    content := fmt.Sprintf(`
auto %s
iface %s inet static
    address %s
    netmask %s
    gateway %s
`, interfaceName, interfaceName, ip, netmask, gateway)

    return ioutil.WriteFile("/etc/network/interfaces", []byte(content), 0644)
}

func updateResolvConf(primaryDNS, secondaryDNS string) error {
    content := fmt.Sprintf("nameserver %s\nnameserver %s\n", primaryDNS, secondaryDNS)
    return ioutil.WriteFile("/etc/resolv.conf", []byte(content), 0644)
}

4. 完整的网络配置函数

package main

import (
    "fmt"
    "net"
    "os/exec"
)

type NetworkConfig struct {
    Interface    string
    IP           string
    Netmask      string
    Gateway      string
    PrimaryDNS   string
    SecondaryDNS string
}

func configureNetwork(cfg *NetworkConfig) error {
    // 验证IP地址格式
    if ip := net.ParseIP(cfg.IP); ip == nil {
        return fmt.Errorf("invalid IP address: %s", cfg.IP)
    }

    // 设置静态IP
    if err := setStaticIP(cfg.Interface, cfg.IP, cfg.Netmask, cfg.Gateway); err != nil {
        return err
    }

    // 设置DNS
    if err := setDNS(cfg.PrimaryDNS, cfg.SecondaryDNS); err != nil {
        return err
    }

    // 重启网络服务使更改永久生效
    cmd := exec.Command("systemctl", "restart", "networking")
    if err := cmd.Run(); err != nil {
        // 如果systemctl不可用,尝试传统方法
        cmd = exec.Command("/etc/init.d/networking", "restart")
        return cmd.Run()
    }

    return nil
}

// 使用示例
func main() {
    config := &NetworkConfig{
        Interface:    "eth0",
        IP:           "192.168.1.100",
        Netmask:      "24",
        Gateway:      "192.168.1.1",
        PrimaryDNS:   "8.8.8.8",
        SecondaryDNS: "8.8.4.4",
    }

    if err := configureNetwork(config); err != nil {
        fmt.Printf("Error configuring network: %v\n", err)
    } else {
        fmt.Println("Network configured successfully")
    }
}

5. 使用第三方库

对于更高级的网络管理,可以考虑使用第三方库:

import "github.com/vishvananda/netlink"

func setIPWithNetlink(interfaceName, ipWithCIDR string) error {
    link, err := netlink.LinkByName(interfaceName)
    if err != nil {
        return err
    }

    addr, err := netlink.ParseAddr(ipWithCIDR)
    if err != nil {
        return err
    }

    return netlink.AddrAdd(link, addr)
}

这些方法提供了在Go中管理网络配置的不同途径。第一种方法使用标准库,第二种使用系统命令,第三种直接操作配置文件,最后一种使用专门的网络管理库。选择哪种方法取决于你的具体需求和权限级别。

回到顶部