golang基于Linux命名空间构建复杂网络拓扑的测试工具插件Gont的使用

Golang基于Linux命名空间构建复杂网络拓扑的测试工具插件Gont的使用

Gont Logo

Go分布式应用测试框架

Gont是一个Go包,用于支持网络和分布式应用程序的开发。它可以使用Linux网络命名空间在单台机器(VM、云或本地)上构建虚拟网络,模拟交换机、路由器、NAT和端点。此外,它还提供了用于跟踪和调试分布式应用程序的工具。

Gont深受Mininet的启发。它允许用户在Go代码中构建虚拟网络拓扑。在底层,网络是使用Linux虚拟桥和网络命名空间构建的。

Gont需要Linux系统和NET_ADMIN能力(或root访问权限)。

功能特性

  • 各种常见网络节点:
    • 标准主机
    • 第3层路由器
    • 第2层交换机
    • 第3层NAT路由器
    • 到主机网络的第3层NAT
  • 测试节点的主机名解析(/etc/hosts覆盖)
  • 在测试节点的网络命名空间中执行子进程、Go代码和函数
  • 同时设置多个隔离网络
  • 适合Go单元测试
  • 可以在GitHub的runner上运行
  • 完整的IPv6支持
  • 通过Netem和TBF排队规则实现每链路网络仿真和带宽限制
  • 使用现有网络命名空间作为节点
  • 配置每主机nftables防火墙规则
  • 内置Ping和Traceroute诊断工具
  • 内置数据包跟踪功能,支持PCAPng输出
  • 分布式事件跟踪
  • 内置Delve调试器

示例代码

以下是一个使用Gont构建简单网络拓扑的完整示例:

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/cunicu/gont/v2"
)

func main() {
	// 创建一个新的网络
	n, err := gont.NewNetwork("example")
	if err != nil {
		log.Fatalf("Failed to create network: %v", err)
	}
	defer n.Close()

	// 添加一个交换机
	sw, err := n.AddSwitch("sw")
	if err != nil {
		log.Fatalf("Failed to add switch: %v", err)
	}

	// 添加两个主机
	h1, err := n.AddHost("h1")
	if err != nil {
		log.Fatalf("Failed to add host h1: %v", err)
	}

	h2, err := n.AddHost("h2")
	if err != nil {
		log.Fatalf("Failed to add host h2: %v", err)
	}

	// 将主机连接到交换机
	if _, err := n.AddLink(
		gont.NewInterface("eth0", h1),
		gont.NewInterface("eth0", sw),
	); err != nil {
		log.Fatalf("Failed to connect h1 to switch: %v", err)
	}

	if _, err := n.AddLink(
		gont.NewInterface("eth0", h2),
		gont.NewInterface("eth1", sw),
	); err != nil {
		log.Fatalf("Failed to connect h2 to switch: %v", err)
	}

	// 设置IP地址
	h1.SetAddress("10.0.0.1/24")
	h2.SetAddress("10.0.0.2/24")

	// 在h1上ping h2
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 捕获中断信号以优雅地关闭
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sig
		cancel()
	}()

	// 在h1上执行ping命令
	if err := h1.Run(ctx, "ping", "-c", "4", "10.0.0.2"); err != nil {
		log.Printf("Ping failed: %v", err)
	}
}

更复杂的网络拓扑示例

下面是一个更复杂的示例,展示如何构建包含路由器和多个子网的网络:

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/cunicu/gont/v2"
)

func main() {
	// 创建网络
	n, err := gont.NewNetwork("multi-subnet")
	if err != nil {
		log.Fatal(err)
	}
	defer n.Close()

	// 添加路由器
	r, err := n.AddRouter("r")
	if err != nil {
		log.Fatal(err)
	}

	// 添加两个交换机(代表两个子网)
	sw1, err := n.AddSwitch("sw1")
	if err != nil {
		log.Fatal(err)
	}

	sw2, err := n.AddSwitch("sw2")
	if err != nil {
		log.Fatal(err)
	}

	// 连接路由器到两个交换机
	if _, err := n.AddLink(
		gont.NewInterface("eth0", r, gont.AddressIP("10.0.1.1/24")),
		gont.NewInterface("eth0", sw1),
	); err != nil {
		log.Fatal(err)
	}

	if _, err := n.AddLink(
		gont.NewInterface("eth1", r, gont.AddressIP("10.0.2.1/24")),
		gont.NewInterface("eth0", sw2),
	); err != nil {
		log.Fatal(err)
	}

	// 在每个子网中添加主机
	h1, err := n.AddHost("h1",
		gont.AddressIP("10.0.1.10/24"),
		gont.GatewayIP("10.0.1.1"),
	)
	if err != nil {
		log.Fatal(err)
	}

	h2, err := n.AddHost("h2",
		gont.AddressIP("10.0.1.11/24"),
		gont.GatewayIP("10.0.1.1"),
	)
	if err != nil {
		log.Fatal(err)
	}

	h3, err := n.AddHost("h3",
		gont.AddressIP("10.0.2.10/24"),
		gont.GatewayIP("10.0.2.1"),
	)
	if err != nil {
		log.Fatal(err)
	}

	// 连接主机到交换机
	if _, err := n.AddLink(
		gont.NewInterface("eth0", h1),
		gont.NewInterface("eth1", sw1),
	); err != nil {
		log.Fatal(err)
	}

	if _, err := n.AddLink(
		gont.NewInterface("eth0", h2),
		gont.NewInterface("eth2", sw1),
	); err != nil {
		log.Fatal(err)
	}

	if _, err := n.AddLink(
		gont.NewInterface("eth0", h3),
		gont.NewInterface("eth1", sw2),
	); err != nil {
		log.Fatal(err)
	}

	// 设置上下文以处理中断信号
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sig
		cancel()
	}()

	// 测试跨子网通信
	log.Println("Testing connectivity between h1 (10.0.1.10) and h3 (10.0.2.10)...")
	if err := h1.Run(ctx, "ping", "-c", "4", "10.0.2.10"); err != nil {
		log.Printf("Ping from h1 to h3 failed: %v", err)
	} else {
		log.Println("Ping successful!")
	}
}

许可证

Gont使用Apache 2.0许可证。

  • SPDX-FileCopyrightText: 2023 Steffen Vogel post@steffenvogel.de
  • SPDX-License-Identifier: Apache-2.0

更多关于golang基于Linux命名空间构建复杂网络拓扑的测试工具插件Gont的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang基于Linux命名空间构建复杂网络拓扑的测试工具插件Gont的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Gont: 基于Linux命名空间的Go网络测试工具

Gont是一个基于Go语言开发的网络测试工具,它利用Linux命名空间(network namespace)来创建复杂的网络拓扑结构,非常适合网络协议开发、SDN测试和分布式系统验证等场景。

Gont核心特性

  1. 基于Linux命名空间实现轻量级网络隔离
  2. 支持创建复杂网络拓扑(路由器、交换机、主机等)
  3. 提供简单的API来配置网络接口、路由表和防火墙规则
  4. 支持IPv4和IPv6双栈
  5. 集成网络测试工具(ping, iperf等)

安装Gont

go get -u github.com/stv0g/gont

基础使用示例

1. 创建简单网络

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/stv0g/gont/pkg/network"
)

func main() {
	// 创建新的网络命名空间
	n, err := network.New("my-network")
	if err != nil {
		log.Fatalf("Failed to create network: %v", err)
	}
	defer n.Close()

	// 添加两个主机
	h1, err := n.AddHost("h1")
	if err != nil {
		log.Fatal(err)
	}

	h2, err := n.AddHost("h2")
	if err != nil {
		log.Fatal(err)
	}

	// 创建交换机并连接主机
	sw, err := n.AddSwitch("sw1")
	if err != nil {
		log.Fatal(err)
	}

	// 连接主机到交换机
	if err := n.AddLink(
		network.WithInterface("eth0", h1),
		network.WithInterface("eth0", sw),
	); err != nil {
		log.Fatal(err)
	}

	if err := n.AddLink(
		network.WithInterface("eth0", h2),
		network.WithInterface("eth1", sw),
	); err != nil {
		log.Fatal(err)
	}

	// 测试网络连通性
	ctx := context.Background()
	if err := h1.Ping(ctx, h2); err != nil {
		log.Fatal("Ping failed:", err)
	}

	fmt.Println("Ping successful!")
}

2. 创建复杂网络拓扑

package main

import (
	"context"
	"log"

	"github.com/stv0g/gont/pkg/network"
)

func main() {
	n, err := network.New("complex-network")
	if err != nil {
		log.Fatal(err)
	}
	defer n.Close()

	// 创建核心交换机
	coreSw, err := n.AddSwitch("core")
	if err != nil {
		log.Fatal(err)
	}

	// 创建两个区域交换机
	sw1, err := n.AddSwitch("sw1")
	if err != nil {
		log.Fatal(err)
	}

	sw2, err := n.AddSwitch("sw2")
	if err != nil {
		log.Fatal(err)
	}

	// 连接区域交换机到核心
	if err := n.AddLink(
		network.WithInterface("uplink", sw1),
		network.WithInterface("sw1-link", coreSw),
	); err != nil {
		log.Fatal(err)
	}

	if err := n.AddLink(
		network.WithInterface("uplink", sw2),
		network.WithInterface("sw2-link", coreSw),
	); err != nil {
		log.Fatal(err)
	}

	// 在每个区域添加主机
	for i := 0; i < 3; i++ {
		h, err := n.AddHost(
			network.HostName(fmt.Sprintf("h%d-%d", 1, i)),
			network.Interface("eth0", sw1),
		)
		if err != nil {
			log.Fatal(err)
		}

		// 配置IP地址
		if err := h.AddAddress("10.0.1.%d/24", i+1); err != nil {
			log.Fatal(err)
		}
	}

	for i := 0; i < 3; i++ {
		h, err := n.AddHost(
			network.HostName(fmt.Sprintf("h%d-%d", 2, i)),
			network.Interface("eth0", sw2),
		)
		if err != nil {
			log.Fatal(err)
		}

		if err := h.AddAddress("10.0.2.%d/24", i+1); err != nil {
			log.Fatal(err)
		}
	}

	// 测试跨区域连通性
	h1_0 := n.Host("h1-0")
	h2_0 := n.Host("h2-0")

	ctx := context.Background()
	if err := h1_0.Ping(ctx, h2_0); err != nil {
		log.Fatal("Cross-area ping failed:", err)
	}

	log.Println("Complex network setup successful!")
}

高级功能

1. 配置路由

// 在主机上添加默认路由
if err := h1.AddDefaultRoute("10.0.1.254"); err != nil {
    log.Fatal(err)
}

// 添加静态路由
if err := h1.AddRoute("192.168.1.0/24", "10.0.1.1"); err != nil {
    log.Fatal(err)
}

2. 使用网络命名空间

// 获取主机的网络命名空间句柄
ns, err := h1.NetNS()
if err != nil {
    log.Fatal(err)
}

// 在命名空间中执行命令
cmd := exec.Command("ip", "addr")
cmd.SysProcAttr = &syscall.SysProcAttr{
    Cloneflags: syscall.CLONE_NEWNET,
    NetNS:     int(ns.Fd()),
}

output, err := cmd.CombinedOutput()
if err != nil {
    log.Fatal(err)
}

fmt.Println(string(output))

3. 性能测试

// 在h1上启动iperf服务器
server, err := h1.StartServer("iperf", "-s")
if err != nil {
    log.Fatal(err)
}
defer server.Close()

// 在h2上运行iperf客户端
client, err := h2.Start("iperf", "-c", h1.IP("eth0").String())
if err != nil {
    log.Fatal(err)
}

output, err := client.Wait(context.Background())
if err != nil {
    log.Fatal(err)
}

fmt.Println("Iperf results:", string(output))

清理网络

Gont创建的测试网络会在程序退出时自动清理,但也可以手动清理:

// 关闭并清理整个网络
n.Close()

// 或者单独清理某个组件
if err := h1.Close(); err != nil {
    log.Println(err)
}

实际应用场景

  1. SDN控制器测试:创建复杂拓扑验证控制器行为
  2. 网络协议开发:测试新协议在不同网络条件下的表现
  3. 分布式系统测试:模拟真实网络环境测试分布式算法
  4. 网络安全测试:验证防火墙规则和网络安全策略

Gont通过简单的API提供了强大的网络模拟能力,是Go语言开发者进行网络相关测试的利器。

回到顶部