纯Go代码中如何获取和设置网络接口
纯Go代码中如何获取和设置网络接口 我有一台运行Ubuntu服务器的ARM物联网设备,需要在Go代码中读取和写入以下内容:
- IP地址(IPv4)
- 子网掩码
- 默认网关
- 主DNS
- 备用DNS
我知道可以使用ifconfig、route和nameserver从命令行设置这些参数,并将这些命令放在bash脚本中,然后运行/etc/init.d/networking restart来使更改永久生效。
我甚至可以编写文件处理程序直接编辑/etc/network/interfaces,但这两种方案都像是临时解决方案,因此我更希望用纯Go来实现。
有趣的是,Docker是用Go编写的,容器有自己的IP地址,所以这肯定是可行的,除非它们进行了底层内核调用。
我已经查看了os和net包,但没有发现明显的解决方案,所以在这方面我非常需要任何帮助。
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中管理网络配置的不同途径。第一种方法使用标准库,第二种使用系统命令,第三种直接操作配置文件,最后一种使用专门的网络管理库。选择哪种方法取决于你的具体需求和权限级别。

