Netlink

概述

参考:

Netlink 是 Linux 中的用户空间程序用来与内核进行通信的界面。它可以用于添加和删除接口,设置 ip 地址和路由以及配置 ipsec。

vishvananda/netlink

Netlink 通信需要提升的权限,因此在大多数情况下,此代码需要以 root 用户身份运行。由于低级 netlink 消息充其量是难以理解的,因此该库试图提供一个以 iproute2 提供的 CLI 为松散建模的 api。ip 链接添加之类的操作将通过类似名称的函数 (例如 AddLink()) 来完成。该库的生命开始于 docker/libcontainer 中的 netlink 功能分支,但经过大量重写以提高可测试性,性能并添加 ipsec xfrm 处理等新功能。

Hello World

package main

import (
    "fmt"
    
    "github.com/vishvananda/netlink"
)

func main() {
    // 实例化一个 LinkAttrs,LinkAttrs 包含一个网络设备的绝大部分属性
    linkAttrs := netlink.NewLinkAttrs()
    // 设定 link 的名称
    linkAttrs.Name = "br0"
    // 将实例化的 LinkAttrs 信息赋值给 Bridge 结构体
    mybridge := &netlink.Bridge{LinkAttrs: linkAttrs}
    // 这里就算真正完成了一个网络设备的定义,netlink 库中包含多种网络设备结构体
    // 每种网络设备结构体都实现了 Link 接口
    // Link 接口只有两个方法,Attrs() 用来返回 LinkAttrs 结构体,Type() 用来返回该网络设备的类型。
    // 而对各种类型的网络设备实现增删改查的函数,其接受的参数就是 Link 接口类型
    // 所以 Link 接口的主要作用,就是用来区分不同类型的网络设备,以便可以在调用时统一。对网络设备的任何操作,都可以将 Link 接口作为参数互相传递。
    
    // 使用 Bridge 结构体的信息创建一个网络设备
    err := netlink.LinkAdd(mybridge)
    if err != nil {
    fmt.Printf("could not add %s: %v\n", linkAttrs.Name, err)
    }
    // eth1, _ := netlink.LinkByName("eth1")
    // netlink.LinkSetMaster(eth1, mybridge)
}

实践

package main

import (
    "fmt"
    
    "github.com/vishvananda/netlink"
)

// addBridge 创建一个桥设备
func addBridge() *netlink.Bridge {
    // 实例化一个 LinkAttrs,LinkAttrs 结构体包含一个网络设备的绝大部分属性。
    linkAttrs := netlink.NewLinkAttrs()
    // 设定 link 的名称
    linkAttrs.Name = "br0"
    // 将实例化的 link 信息赋值给 Bridge 结构体
    myBridge := &netlink.Bridge{
        LinkAttrs: linkAttrs,
    }
    
    // 使用 Bridge 结构体的信息创建一个 link
    err := netlink.LinkAdd(myBridge)
    if err != nil {
        fmt.Printf("could not add %s: %v\n", linkAttrs.Name, err)
    }
    
    return myBridge
}

// addVeth 创建一个 veth 设备
func addVeth() *netlink.Veth {
    // 实例化一个 LinkAttrs,LinkAttrs 结构体包含一个网络设备的绝大部分属性。
    linkAttrs := netlink.NewLinkAttrs()
    // 设定 link 的名称
    linkAttrs.Name = "veth1.1"
    // 将实例化的 link 信息赋值给 Veth 结构体,veth 必须指定对端设备
    myVeth := &netlink.Veth{
        LinkAttrs: linkAttrs,
        PeerName:  "veth1.2",
    }
    
    // 使用 Veth 结构体的信息创建一个 link
    err := netlink.LinkAdd(myVeth)
    if err != nil {
        fmt.Printf("could not add %s: %v\n", linkAttrs.Name, err)
    }
    
    return myVeth
}

func main() {
    // 增
    // 添加 veth 和 bridge 设备,并将 veth 设备附加到 bridge 设备上。
    if err := netlink.LinkSetMaster(addVeth(), addBridge()); err != nil {
        fmt.Println("设置网络设备主从关系出错,原因:", err)
    }

    // 删除 veth 和 bridge 设备
    br0, _ := netlink.LinkByName("br0")
    veth, _ := netlink.LinkByName("veth1.1")
    netlink.LinkDel(br0)
    netlink.LinkDel(veth)

    // 改
    
    // 查
    // 实例化一个 Handle,相当于在当前名称空间中创建一个 Socket 句柄。
    // 呼叫者可以指定句柄应支持的netlink族。如果未指定族,则将自动添加netlink软件包支持的所有族。
    handle, _ := netlink.NewHandle()
    // 获取所有网络设备,等效于 `ip link show` 命令
    links, _ := handle.LinkList()
    // 输出网络设备信息
    for _, link := range links {
        fmt.Printf("设备名称为:%v\n", link.Attrs().Name)
        // 获取一个网络设备的 IP 地址
        addrs, _ := netlink.AddrList(link, 0)
        for index, addr := range addrs {
            fmt.Printf("%v 设备的第 %v 个 IP 地址为:%v\n", link.Attrs().Name, index+1, addr.IP)
        }
    }
}