[mydocker]---网络net/netlink api 使用解析

1. 前言

由于原理在前面几篇文章已经介绍过了, 如果直接用go语言直接执行命令也可以达到目的, 目前有直接可以用的api, 所以接下来就看看这些api如何操作的.

2. 关于网桥的API 测试

使用github.com/vishvananda/netlink相关的api.

2.1 添加一个网桥

// 等于 ip link add name testbridge type bridge
func TestNet001(t *testing.T) {
    bridgeName := "testbridge"
    _, err := net.InterfaceByName(bridgeName)
    if err == nil || !strings.Contains(err.Error(), "no such network interface") {
        log.Printf("error:%v\n", err)
    }
    // create *netlink.Bridge object
    la := netlink.NewLinkAttrs()
    la.Name = bridgeName

    br := &netlink.Bridge{la}
    // 等于 ip link add name testbridge type bridge
    if err := netlink.LinkAdd(br); err != nil {
        fmt.Errorf("Bridge creation failed for bridge %s: %v", bridgeName, err)
    }
}

测试, 可以看到经过运行后增加一个testbridge设备, 但是目前是DOWN状态并且没有ip地址.

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:58:9a:c3 brd ff:ff:ff:ff:ff:ff
3: docker0:  mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# go test -v api_test.go -test.run TestNet001
=== RUN   TestNet001
--- PASS: TestNet001 (0.00s)
PASS
ok      command-line-arguments  0.003s
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:58:9a:c3 brd ff:ff:ff:ff:ff:ff
3: docker0:  mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
32: testbridge:  mtu 1500 qdisc noop state DOWN mode DEFAULT group default 
    link/ether e6:e5:e4:62:ee:a8 brd ff:ff:ff:ff:ff:ff

2.2 设置网桥地址 状态

  1. 根据设备名找到设备testbridge
  2. ip addr add 13. 92.168.0.1/24 dev testbridge
  3. ip link set testbridge up
func TestNet002(t *testing.T) {
    name := "testbridge"
    rawIP := "192.168.0.1/24"
    retries := 2
    var iface netlink.Link
    var err error
    for i := 0; i < retries; i++ {
        // 根据名字找到设备
        iface, err = netlink.LinkByName(name)
        if err == nil {
            break
        }
        log.Printf("error retrieving new bridge netlink link [ %s ]... retrying", name)
        time.Sleep(2 * time.Second)
    }
    if err != nil {
        fmt.Errorf("Abandoning retrieving the new bridge link from netlink, Run [ ip link ] to troubleshoot the error: %v", err)
    }
    // 将原始ip转换成*net.IPNet类型
    ipNet, err := netlink.ParseIPNet(rawIP)
    if err != nil {
        log.Printf("ParseIPNet error:%v\n", err)
    }

    log.Printf("ipNet:%v\n", ipNet)
    addr := &netlink.Addr{ipNet, "", 0, 0, nil}

    // 等于 ip addr add 192.168.0.1/24 dev testbridge
    err = netlink.AddrAdd(iface, addr)
    log.Printf("AddrAdd error:%v\n", err)

    // 等于 ip link set testbridge up
    if err := netlink.LinkSetUp(iface); err != nil {
        fmt.Errorf("Error enabling interface for %s: %v", name, err)
    }
}

测试, 可以看到testbridge的地址为192.168.0.1, 地址为UNDOWN.

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# go test -v api_test.go -test.run TestNet002
=== RUN   TestNet002
2019/05/05 20:57:44 ipNet:192.168.0.1/24
2019/05/05 20:57:44 AddrAdd error:
--- PASS: TestNet002 (0.00s)
PASS
ok      command-line-arguments  0.003s
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:58:9a:c3 brd ff:ff:ff:ff:ff:ff
3: docker0:  mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
32: testbridge:  mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/ether e6:e5:e4:62:ee:a8 brd ff:ff:ff:ff:ff:ff
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ifconfig
docker0   Link encap:Ethernet  HWaddr 56:84:7a:fe:97:99  
          inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:68 errors:0 dropped:0 overruns:0 frame:0
          TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:3990 (3.9 KB)  TX bytes:3599 (3.5 KB)

eth0      Link encap:Ethernet  HWaddr 52:54:00:58:9a:c3  
          inet addr:172.19.16.7  Bcast:172.19.31.255  Mask:255.255.240.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11304762 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9226749 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:5638409305 (5.6 GB)  TX bytes:5757777940 (5.7 GB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:66 errors:0 dropped:0 overruns:0 frame:0
          TX packets:66 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:6180 (6.1 KB)  TX bytes:6180 (6.1 KB)

testbridge Link encap:Ethernet  HWaddr e6:e5:e4:62:ee:a8  
          inet addr:192.168.0.1  Bcast:0.0.0.0  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

2.3 往网桥上attach设备

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# bridge link
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

可以看到网桥testbridge上没有任何设备

func TestNet003(t *testing.T) {
    bridgeName := "testbridge"
    // 根据设备名找到设备testbridge
    br, err := netlink.LinkByName(bridgeName)
    if err != nil {
        log.Printf("LinkByName err:%v\n", err)
        return
    }

    la := netlink.NewLinkAttrs()
    la.Name = "12345"

    log.Printf("br.attrs().index:%d\n", br.Attrs().Index)
    // 等于 ip link set dev 12345 master testbridge
    la.MasterIndex = br.Attrs().Index

    myVeth := netlink.Veth{
        LinkAttrs: la,
        PeerName:  "cif-" + la.Name,
    }
    // 等于 ip link add 12345 type veth peer name cif-12345
    if err = netlink.LinkAdd(&myVeth); err != nil {
        fmt.Errorf("Error Add Endpoint Device: %v", err)
        return
    }

    // 等于 ip link set 12345 up
    if err = netlink.LinkSetUp(&myVeth); err != nil {
        fmt.Errorf("Error Add Endpoint Device: %v", err)
        return
    }
}

测试, 可以看到增加了一对veth设备, (12345cif-12345). 并且12345已经attachtestbridge上了.

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# go test -v api_test.go -test.run TestNet003
=== RUN   TestNet003
2019/05/05 21:16:35 br.attrs().index:32
--- PASS: TestNet003 (0.00s)
PASS
ok      command-line-arguments  0.007s
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:58:9a:c3 brd ff:ff:ff:ff:ff:ff
3: docker0:  mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
32: testbridge:  mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 86:7c:c1:7e:56:9a brd ff:ff:ff:ff:ff:ff
33: cif-12345:  mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 7e:0f:42:f5:16:2c brd ff:ff:ff:ff:ff:ff
34: 12345:  mtu 1500 qdisc pfifo_fast master testbridge state DOWN mode DEFAULT group default qlen 1000
    link/ether 86:7c:c1:7e:56:9a brd ff:ff:ff:ff:ff:ff
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# bridge link
34: 12345 state DOWN :  mtu 1500 master testbridge state disabled priority 32 cost 2 
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

2.4 设置iptables

由于iptables没有api, 所以直接用go执行命令行操作

func TestNet005(t *testing.T)  {
    subnet := "192.168.0.0/24"
    iptablesCmd := fmt.Sprintf("-t nat -A POSTROUTING -s %s -o eth0 -j MASQUERADE", subnet)
    cmd := exec.Command("iptables", strings.Split(iptablesCmd, " ")...)
    //err := cmd.Run()
    output, err := cmd.Output()
    if err != nil {
        log.Printf("iptables Output, %v", output)
    }
}

测试

root@nicktming:~/go/src/github.com/nicktming/mydocker/network#  iptables -t nat -vnL POSTROUTING --line
Chain POSTROUTING (policy ACCEPT 414 packets, 25497 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1       14   998 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# go test -v api_test.go -test.run TestNet005
=== RUN   TestNet005
--- PASS: TestNet005 (0.00s)
PASS
ok      command-line-arguments  0.003s
root@nicktming:~/go/src/github.com/nicktming/mydocker/network#  iptables -t nat -vnL POSTROUTING --line
Chain POSTROUTING (policy ACCEPT 1 packets, 60 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1       14   998 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
2        0     0 MASQUERADE  all  --  *      eth0    192.168.0.0/24       0.0.0.0/0           
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

2.5 将veth设备中的一端加入到某个namespace中

首先先在terminal中启动一个隔离的network namespace

root@nicktming:~# unshare -n sh
# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# echo $$
16694
# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# readlink /proc/16694/ns/net
net:[4026532167]
#

其次有两个辅助函数,前面已经介绍过只不过做了简单封装.

// 前面已经铺垫过
func SetInterfaceUP(interfaceName string) error {
    iface, err := netlink.LinkByName(interfaceName)
    if err != nil {
        return fmt.Errorf("Error retrieving a link named [ %s ]: %v", iface.Attrs().Name, err)
    }

    if err := netlink.LinkSetUp(iface); err != nil {
        return fmt.Errorf("Error enabling interface for %s: %v", interfaceName, err)
    }
    return nil
}

// 前面已经铺垫过
// Set the IP addr of a netlink interface
func SetInterfaceIP(name string, rawIP string) error {
    retries := 2
    var iface netlink.Link
    var err error
    for i := 0; i < retries; i++ {
        iface, err = netlink.LinkByName(name)
        if err == nil {
            break
        }
        log.Printf("error retrieving new bridge netlink link [ %s ]... retrying", name)
        time.Sleep(2 * time.Second)
    }
    if err != nil {
        return fmt.Errorf("Abandoning retrieving the new bridge link from netlink, Run [ ip link ] to troubleshoot the error: %v", err)
    }
    ipNet, err := netlink.ParseIPNet(rawIP)
    if err != nil {
        return err
    }
    addr := &netlink.Addr{ipNet, "", 0, 0, nil}
    return netlink.AddrAdd(iface, addr)
}

正式代码, 整个过程就是进入Pid16694所在的network namespace中, 将veth设备cif-12345加入到该network namespace中, 并且配置其地址为192.168.0.8, 然后添加默认路由, 网关为testbridge的地址192.168.0.1.

EnterContainerNetns: 作用是将当前运行的进程设置到Pid16694所在的network namespace中运行.
printCurrentNamespace: 是为了打印当前进程到底在哪个network namespace下运行.
ConfigEndpointIpAddressAndRoute: 配置网络.

func TestNet006(t *testing.T) {
    PeerName := "cif-12345"
    containerIP := "192.168.0.8/24"

    gwIP, ipnet, _ := net.ParseCIDR("192.168.0.1/24")
    ipnet.IP = gwIP

    if err := ConfigEndpointIpAddressAndRoute(PeerName, containerIP, ipnet); err != nil {
        log.Printf("ConfigEndpointIpAddressAndRoute error:%v\n", err)
    }
}


func EnterContainerNetns(enLink *netlink.Link) func() {
    f, err := os.OpenFile(fmt.Sprintf("/proc/%s/ns/net", "16694"), os.O_RDONLY, 0)
    if err != nil {
        logrus.Errorf("error get container net namespace, %v", err)
    }

    nsFD := f.Fd()
    runtime.LockOSThread()

    // 修改veth peer 另外一端移到容器的namespace中
    if err = netlink.LinkSetNsFd(*enLink, int(nsFD)); err != nil {
        logrus.Errorf("error set link netns , %v", err)
    }

    // 获取当前的网络namespace
    origns, err := netns.Get()
    if err != nil {
        logrus.Errorf("error get current netns, %v", err)
    }

    printCurrentNamespace()
    log.Printf("before set to new namespace \n")

    // 设置当前进程到新的网络namespace,并在函数执行完成之后再恢复到之前的namespace
    if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
        logrus.Errorf("error set netns, %v", err)
    }

    printCurrentNamespace()
    log.Printf("after set to new namespace\n")

    return func () {
        netns.Set(origns)

        printCurrentNamespace()
        log.Printf("before close\n")

        origns.Close()
        runtime.UnlockOSThread()
        f.Close()
    }
}

func printCurrentNamespace()  {
    currentNamespace, _ := netns.Get()
    log.Printf("currentNamespace:%v\n", currentNamespace)
}


// 类似于 ip netns exec network_namespace sh 然后在该network_namespace namespace中配置网络
func ConfigEndpointIpAddressAndRoute(PeerName, containerIP string, ipnet *net.IPNet) error {
    peerLink, err := netlink.LinkByName(PeerName)
    if err != nil {
        return fmt.Errorf("fail config endpoint: %v", err)
    }

    defer EnterContainerNetns(&peerLink)()

    printCurrentNamespace()
    log.Printf("config network namespace start.\n")

    if err = SetInterfaceIP(PeerName, containerIP); err != nil {
        return fmt.Errorf("set %s up error:%v", PeerName, err)
    }

    if err = SetInterfaceUP(PeerName); err != nil {
        return err
    }

    if err = SetInterfaceUP("lo"); err != nil {
        return err
    }

    _, cidr, _ := net.ParseCIDR("0.0.0.0/0")

    defaultRoute := &netlink.Route{
        LinkIndex: peerLink.Attrs().Index,
        Gw: ipnet.IP,
        Dst: cidr,
    }

    if err = netlink.RouteAdd(defaultRoute); err != nil {
        return err
    }

    printCurrentNamespace()
    log.Printf("config network namespace end.\n")

    return nil
}

测试:

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# readlink /proc/$$/ns/net
net:[4026531956]
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# go test -v api_test.go -test.run TestNet006
=== RUN   TestNet006
2019/05/05 22:45:12 currentNamespace:NS(5: 3, 4026531956)
2019/05/05 22:45:12 before set to new namespace 
2019/05/05 22:45:12 currentNamespace:NS(6: 3, 4026532167)
2019/05/05 22:45:12 after set to new namespace
2019/05/05 22:45:12 currentNamespace:NS(7: 3, 4026532167)
2019/05/05 22:45:12 config network namespace start.
2019/05/05 22:45:12 currentNamespace:NS(8: 3, 4026532167)
2019/05/05 22:45:12 config network namespace end.
2019/05/05 22:45:12 currentNamespace:NS(9: 3, 4026531956)
2019/05/05 22:45:12 before close
--- PASS: TestNet006 (0.02s)
PASS
ok      command-line-arguments  0.020s
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

可以看到先是在默认network namespace[4026531956]下工作的, 然后进入到Pid=16694所在的network namespace[4026532167]下进行配置网络, 最后又回到默认network namespace[4026531956].

可以去Pid=16694的terminal中看一下:

# readlink /proc/16694/ns/net
net:[4026532167]
# 
# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
45: cif-12345:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 1a:3f:eb:9a:17:de brd ff:ff:ff:ff:ff:ff
# ifconfig
cif-12345 Link encap:Ethernet  HWaddr 1a:3f:eb:9a:17:de  
          inet addr:192.168.0.8  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::183f:ebff:fe9a:17de/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 cif-12345
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 cif-12345
# ping -c 1 127.0.0.1      
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.030 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.030/0.030/0.030/0.000 ms
# ping -c 1 192.168.0.8
PING 192.168.0.8 (192.168.0.8) 56(84) bytes of data.
64 bytes from 192.168.0.8: icmp_seq=1 ttl=64 time=0.025 ms

--- 192.168.0.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.025/0.025/0.025/0.000 ms
# ping -c 1 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.049 ms

--- 192.168.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.049/0.049/0.049/0.000 ms
# ping -c 1 172.19.16.7
PING 172.19.16.7 (172.19.16.7) 56(84) bytes of data.
64 bytes from 172.19.16.7: icmp_seq=1 ttl=64 time=0.049 ms

--- 172.19.16.7 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.049/0.049/0.049/0.000 ms
# ping -c 1 www.baidu.com
PING www.wshifen.com (119.63.197.139) 56(84) bytes of data.
64 bytes from 119.63.197.139: icmp_seq=1 ttl=51 time=54.0 ms

--- www.wshifen.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 54.087/54.087/54.087/0.000 ms
# 

可以看到整个namespace下的网络已经配置好, 并且可以访问外部网络, 从宿主机访问该namespace.

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ping -c 1 192.168.0.8
PING 192.168.0.8 (192.168.0.8) 56(84) bytes of data.
64 bytes from 192.168.0.8: icmp_seq=1 ttl=64 time=0.036 ms

--- 192.168.0.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.036/0.036/0.036/0.000 ms
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

2.6 删除设备

func TestNet007(t *testing.T) {
    deleteDevice("testbridge")
    deleteDevice("12345")
}

func deleteDevice(name string)  {
    // 根据设备名找到该设备
    l, err := netlink.LinkByName(name)
    if err != nil {
        fmt.Errorf("Getting link with name %s failed: %v", name, err)
        return
    }

    // 删除设备
    // 删除网桥就等于 ifconfig testbridge down && ip link delete testbridge type bridge
    // 删除veth就等于  ip link delete 12345 type veth
    if err := netlink.LinkDel(l); err != nil {
        fmt.Errorf("Failed to remove bridge interface %s delete: %v", name, err)
        return
    }
    log.Printf("Delete Device %s\n", name)
}

测试, 可以看到网桥testbridge, Veth设备12345都已经被删除.

root@nicktming:~/go/src/github.com/nicktming/mydocker/network# go test -v api_test.go -test.run TestNet007
=== RUN   TestNet005
2019/05/05 21:31:09 Delete Device testbridge
2019/05/05 21:31:09 Delete Device 12345
--- PASS: TestNet005 (0.02s)
PASS
ok      command-line-arguments  0.027s
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:58:9a:c3 brd ff:ff:ff:ff:ff:ff
3: docker0:  mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
root@nicktming:~/go/src/github.com/nicktming/mydocker/network# 

3. 参考

1. 自己动手写docker.(基本参考此书,加入一些自己的理解,加深对docker的理解)

4. 全部内容

[mydocker]---网络net/netlink api 使用解析_第1张图片
mydocker.png

1. [mydocker]---环境说明
2. [mydocker]---urfave cli 理解
3. [mydocker]---Linux Namespace
4. [mydocker]---Linux Cgroup
5. [mydocker]---构造容器01-实现run命令
6. [mydocker]---构造容器02-实现资源限制01
7. [mydocker]---构造容器02-实现资源限制02
8. [mydocker]---构造容器03-实现增加管道
9. [mydocker]---通过例子理解存储驱动AUFS
10. [mydocker]---通过例子理解chroot 和 pivot_root
11. [mydocker]---一步步实现使用busybox创建容器
12. [mydocker]---一步步实现使用AUFS包装busybox
13. [mydocker]---一步步实现volume操作
14. [mydocker]---实现保存镜像
15. [mydocker]---实现容器的后台运行
16. [mydocker]---实现查看运行中容器
17. [mydocker]---实现查看容器日志
18. [mydocker]---实现进入容器Namespace
19. [mydocker]---实现停止容器
20. [mydocker]---实现删除容器
21. [mydocker]---实现容器层隔离
22. [mydocker]---实现通过容器制作镜像
23. [mydocker]---实现cp操作
24. [mydocker]---实现容器指定环境变量
25. [mydocker]---网际协议IP
26. [mydocker]---网络虚拟设备veth bridge iptables
27. [mydocker]---docker的四种网络模型与原理实现(1)
28. [mydocker]---docker的四种网络模型与原理实现(2)
29. [mydocker]---容器地址分配
30. [mydocker]---网络net/netlink api 使用解析
31. [mydocker]---网络实现
32. [mydocker]---网络实现测试

你可能感兴趣的:([mydocker]---网络net/netlink api 使用解析)