乐于分享
好东西不私藏

Linux 网络子系统源码剖析(十二):网络命名空间 – 容器网络的基石

Linux 网络子系统源码剖析(十二):网络命名空间 – 容器网络的基石

从内核隔离到容器网络 – 揭秘 Docker 和 Kubernetes 的网络基础

系列:Linux 网络子系统源码剖析篇号:第 12 篇内核版本:Linux 5.10 LTS重点模块:网络命名空间、veth pair、容器网络、网络隔离


📋 本篇导读

你将学到

  • • 网络命名空间的核心原理和实现
  • • struct net 数据结构的完整解析
  • • 命名空间的创建、切换和销毁流程
  • • 网络资源隔离机制(设备、路由、iptables、socket)
  • • veth pair 的实现原理和数据传输
  • • Docker 和 Kubernetes 的网络架构
  • • 容器网络的创建和通信机制
  • • 网络命名空间的性能优化和调试技巧

前置知识

  • • 已阅读第 1-11 篇文章
  • • 了解 Linux 命名空间概念
  • • 理解网络协议栈基础
  • • 熟悉容器技术(Docker/Kubernetes)

阅读时间

约 90-100 分钟


🎯 概述

1.1 什么是网络命名空间?

网络命名空间(Network Namespace)是 Linux 内核提供的一种网络资源隔离机制,它允许在同一个物理主机上创建多个相互隔离的网络环境。每个命名空间拥有独立的:

  • • 网络设备(网卡)
  • • IP 地址
  • • 路由表
  • • iptables 规则
  • • 端口号空间
  • • /proc/net 目录
  • • socket

1.2 应用场景

容器技术

  • • Docker 容器隔离
  • • Kubernetes Pod 网络
  • • LXC 容器

网络虚拟化

  • • 多租户网络隔离
  • • VPN 隧道
  • • 网络测试环境

安全隔离

  • • 进程网络隔离
  • • 沙箱环境
  • • 网络安全测试

1.3 与其他命名空间的关系

Linux 提供了多种命名空间:

命名空间类型          隔离资源              创建标志----------------------------------------------------------------Mount Namespace      文件系统挂载点        CLONE_NEWNSUTS Namespace        主机名和域名          CLONE_NEWUTSIPC Namespace        进程间通信            CLONE_NEWIPCPID Namespace        进程 ID               CLONE_NEWPIDNetwork Namespace    网络资源              CLONE_NEWNETUser Namespace       用户和组 ID           CLONE_NEWUSERCgroup Namespace     Cgroup 根目录         CLONE_NEWCGROUP

网络命名空间通常与其他命名空间配合使用,实现完整的容器隔离。

📊 网络命名空间架构

2.1 整体架构

2.2 命名空间层次结构

struct net (网络命名空间)    │    ├── struct net_device *dev_base_head    (网络设备链表)    │       │    │       ├── eth0    │       ├── lo    │       └── veth0    │    ├── struct netns_ipv4                   (IPv4 相关)    │       │    │       ├── 路由表 (FIB)    │       ├── IP 地址    │       └── sysctl 参数    │    ├── struct netns_ipv6                   (IPv6 相关)    │    ├── struct netns_xt                     (iptables/netfilter)    │       │    │       ├── filter 表    │       ├── nat 表    │       └── mangle 表    │    ├── struct sock *rtnl                   (netlink socket)    │    └── struct proc_dir_entry *proc_net     (/proc/net 目录)

2.3 命名空间切换流程

用户空间进程    │    │ setns(fd, CLONE_NEWNET)    │    ▼sys_setns()    │    ▼set_task_net()    │    ▼task->nsproxy->net_ns = new_net    │    ▼后续网络操作使用新命名空间

💾 核心数据结构

3.1 struct net

网络命名空间的核心结构,定义在 include/net/net_namespace.h

源码位置include/net/net_namespace.h

/** * struct net - 网络命名空间 *  * 每个网络命名空间包含独立的网络资源 */structnet {/* ========== 1. 引用计数和生命周期 ========== */refcount_t              passive;        /* 被动引用计数 */refcount_t              count;          /* 主动引用计数 */spinlock_t              rules_mod_lock; /* 规则修改锁 */atomic_t                dev_unreg_count; /* 设备注销计数 *//* ========== 2. 命名空间标识 ========== */unsignedint            proc_inum;      /* /proc 中的 inode 号 */structns_commonns;/* 通用命名空间 *//* ========== 3. 网络设备 ========== */structlist_headdev_base_head;/* 网络设备链表 */structhlist_head       *dev_name_head;/* 按名称索引的设备哈希表 */structhlist_head       *dev_index_head;/* 按索引的设备哈希表 *//* ========== 4. 协议栈相关 ========== */structnetns_ipv4ipv4;/* IPv4 相关 */structnetns_ipv6ipv6;/* IPv6 相关 */structnetns_unixunx;/* Unix domain socket *//* ========== 5. Netfilter 相关 ========== */structnetns_xtxt;/* xtables (iptables) */structnetns_ctct;/* 连接跟踪 */structnetns_nfnf;/* netfilter */structnetns_nf_fragnf_frag;/* 分片处理 *//* ========== 6. 路由相关 ========== */structnetns_ipv4ipv4;/* ========== 7. Netlink socket ========== */structsock             *rtnl;/* rtnetlink socket */structsock             *genl_sock;/* generic netlink socket *//* ========== 8. /proc 文件系统 ========== */structproc_dir_entry   *proc_net;/* /proc/net 目录 */structproc_dir_entry   *proc_net_stat;/* /proc/net/stat 目录 *//* ========== 9. sysctl 参数 ========== */structctl_table_setsysctls;/* sysctl 表 *//* ========== 10. 用户信息 ========== */structuser_namespace   *user_ns;/* 所属用户命名空间 */structucounts          *ucounts;/* 资源计数 *//* ========== 11. 其他 ========== */structlist_headexit_list;/* 退出链表 */structllist_nodecleanup_list;/* 清理链表 */structwork_structwork;/* 清理工作队列 */};

关键字段说明

/* 1. 引用计数 */refcount_t passive;     /* 被动引用:网络设备、socket 等持有 */refcount_t count;       /* 主动引用:进程、文件描述符持有 *//* 2. 设备管理 */structlist_headdev_base_head;/* 所有网络设备 */structhlist_head *dev_name_head;/* 按名称查找:dev_name_head["eth0"] */structhlist_head *dev_index_head;/* 按索引查找:dev_index_head[1] *//* 3. 协议栈 */structnetns_ipv4ipv4;/* 包含路由表、IP 地址等 */structnetns_ipv6ipv6;/* IPv6 协议栈 *//* 4. Netfilter */structnetns_xtxt;/* iptables 规则 */structnetns_ctct;/* 连接跟踪表 */

3.2 struct netns_ipv4

IPv4 协议栈在命名空间中的数据。

源码位置include/net/netns/ipv4.h

/** * struct netns_ipv4 - IPv4 命名空间数据 */structnetns_ipv4 {/* ========== 1. 路由表 ========== */structfib_table __rcu  *fib_main;/* 主路由表 */structfib_table __rcu  *fib_default;/* 默认路由表 */structfib_table __rcu  *fib_local;/* 本地路由表 */structhlist_head       *fib_table_hash;/* 路由表哈希 *//* ========== 2. IP 地址 ========== */structin_device        *loopback_dev;/* loopback 设备 *//* ========== 3. sysctl 参数 ========== */int sysctl_ip_forward;                  /* IP 转发开关 */int sysctl_ip_default_ttl;              /* 默认 TTL */int sysctl_tcp_timestamps;              /* TCP 时间戳 */int sysctl_tcp_window_scaling;          /* TCP 窗口缩放 *//* ========== 4. ICMP ========== */structinet_peer_base   *peers;/* ICMP 速率限制 *//* ========== 5. TCP/UDP ========== */structinet_hashinfo    *tcp_hashinfo;/* TCP 连接哈希表 */structudp_table        *udp_table;/* UDP socket 表 *//* ========== 6. 统计信息 ========== */structipstats_mib __percpu *ip_statistics;structtcp_mib __percpu     *tcp_statistics;structudp_mib __percpu     *udp_statistics;};

3.3 初始命名空间 init_net

系统启动时创建的默认命名空间。

源码位置net/core/net_namespace.c

/** * init_net - 初始网络命名空间 *  * 系统启动时创建,所有进程默认在此命名空间中 */structnetinit_net = {    .count      = REFCOUNT_INIT(1),    .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head),    .ns         = {        .ops = &netns_operations,    },};EXPORT_SYMBOL(init_net);/* 当前进程的网络命名空间 */#define current_net() (current->nsproxy->net_ns)

🔄 命名空间生命周期

4.1 创建命名空间

命名空间通过 clone() 或 unshare() 系统调用创建。

源码位置net/core/net_namespace.c

/** * copy_net_ns - 创建新的网络命名空间 * @flags: 创建标志 * @user_ns: 用户命名空间 * @old_net: 父命名空间 *  * 返回:新创建的命名空间 */struct net *copy_net_ns(unsignedlong flags,struct user_namespace *user_ns,struct net *old_net){structnet *net;int rv;/* ========== 1. 检查权限 ========== */if (!(flags & CLONE_NEWNET))return get_net(old_net);  /* 不创建新命名空间 */if (!ns_capable(user_ns, CAP_SYS_ADMIN))return ERR_PTR(-EPERM);   /* 需要 CAP_SYS_ADMIN 权限 *//* ========== 2. 分配 net 结构 ========== */    net = net_alloc();if (!net)return ERR_PTR(-ENOMEM);/* ========== 3. 初始化引用计数 ========== */    refcount_set(&net->passive, 1);    refcount_set(&net->count, 1);/* ========== 4. 设置用户命名空间 ========== */    net->user_ns = user_ns;/* ========== 5. 初始化设备链表 ========== */    INIT_LIST_HEAD(&net->dev_base_head);/* ========== 6. 分配设备哈希表 ========== */    net->dev_name_head = netdev_create_hash();if (!net->dev_name_head)goto out_free;    net->dev_index_head = netdev_create_hash();if (!net->dev_index_head)goto out_free_name;/* ========== 7. 初始化各子系统 ========== */    rv = setup_net(net, user_ns);if (rv < 0)goto out_free_index;/* ========== 8. 创建 /proc/net 目录 ========== */    rv = proc_net_init(net);if (rv < 0)goto out_cleanup;return net;out_cleanup:    cleanup_net(net);out_free_index:    kfree(net->dev_index_head);out_free_name:    kfree(net->dev_name_head);out_free:    net_free(net);return ERR_PTR(rv);}

setup_net() – 初始化网络子系统

/** * setup_net - 初始化网络命名空间的各个子系统 * @net: 网络命名空间 * @user_ns: 用户命名空间 */static __net_init intsetup_net(struct net *net, struct user_namespace *user_ns){structpernet_operations *ops, *saved_ops;int error = 0;/* ========== 1. 初始化锁 ========== */    spin_lock_init(&net->rules_mod_lock);atomic_set(&net->dev_unreg_count, 0);/* ========== 2. 遍历所有子系统初始化函数 ========== */    list_for_each_entry(ops, &pernet_list, list) {/* 调用子系统的 init 函数 */        error = ops_init(ops, net);if (error < 0)goto out_undo;    }return0;out_undo:/* 回滚已初始化的子系统 */    saved_ops = ops;    list_for_each_entry_continue_reverse(ops, &pernet_list, list)        ops_exit(ops, net);return error;}

子系统注册示例

/* IPv4 子系统注册 */staticstructpernet_operations __net_initdataip_rt_ops = {    .init = ip_rt_init,     /* 初始化路由表 */    .exit = ip_rt_exit,     /* 清理路由表 */};staticint __init ip_rt_init_global(void){return register_pernet_subsys(&ip_rt_ops);}

4.2 切换命名空间

进程通过 setns() 系统调用切换到其他命名空间。

源码位置kernel/nsproxy.c

/** * sys_setns - 切换到指定的命名空间 * @fd: 命名空间文件描述符(/proc/PID/ns/net) * @nstype: 命名空间类型(CLONE_NEWNET) */SYSCALL_DEFINE2(setns, int, fd, int, nstype){structtask_struct *tsk = current;structnsproxy *new_nsproxy;structfile *file;structns_common *ns;int err;/* ========== 1. 获取命名空间文件 ========== */    file = proc_ns_fget(fd);if (IS_ERR(file))return PTR_ERR(file);/* ========== 2. 检查命名空间类型 ========== */    ns = get_proc_ns(file_inode(file));if (nstype && (ns->ops->type != nstype)) {        err = -EINVAL;goto out;    }/* ========== 3. 检查权限 ========== */if (!ns_capable(ns->ops->owner(ns), CAP_SYS_ADMIN)) {        err = -EPERM;goto out;    }/* ========== 4. 创建新的 nsproxy ========== */    new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);if (IS_ERR(new_nsproxy)) {        err = PTR_ERR(new_nsproxy);goto out;    }/* ========== 5. 安装新的网络命名空间 ========== */    err = ns->ops->install(new_nsproxy, ns);if (err) {        free_nsproxy(new_nsproxy);goto out;    }/* ========== 6. 切换进程的命名空间 ========== */    switch_task_namespaces(tsk, new_nsproxy);out:    fput(file);return err;}

网络命名空间安装函数

/** * netns_install - 安装网络命名空间 * @nsproxy: 命名空间代理 * @ns: 要安装的命名空间 */staticintnetns_install(struct nsproxy *nsproxy, struct ns_common *ns){structnet *net = to_net_ns(ns);/* 检查权限 */if (!ns_capable(net->user_ns, CAP_SYS_ADMIN) ||        !ns_capable(current_user_ns(), CAP_SYS_ADMIN))return -EPERM;/* 增加引用计数 */    get_net(net);/* 释放旧的命名空间 */    put_net(nsproxy->net_ns);/* 安装新的命名空间 */    nsproxy->net_ns = net;return0;}

4.3 销毁命名空间

当命名空间的引用计数降为 0 时,触发清理流程。

源码位置net/core/net_namespace.c

/** * cleanup_net - 清理网络命名空间 * @work: 工作队列项 *  * 异步清理命名空间资源 */staticvoidcleanup_net(struct work_struct *work){structnet *net, *tmp;structlist_headnet_kill_list;    LIST_HEAD(net_exit_list);/* ========== 1. 收集要清理的命名空间 ========== */    INIT_LIST_HEAD(&net_kill_list);    spin_lock_irq(&cleanup_list_lock);    list_replace_init(&cleanup_list, &net_kill_list);    spin_unlock_irq(&cleanup_list_lock);/* ========== 2. 注销所有网络设备 ========== */    list_for_each_entry(net, &net_kill_list, cleanup_list) {/* 注销设备 */        unregister_netdevice_many(&net->dev_base_head);    }/* ========== 3. 等待设备注销完成 ========== */    list_for_each_entry(net, &net_kill_list, cleanup_list) {        wait_event(netdev_unregistering_wq,atomic_read(&net->dev_unreg_count) == 0);    }/* ========== 4. 调用各子系统的 exit 函数 ========== */    list_for_each_entry_reverse(ops, &pernet_list, list) {        list_for_each_entry(net, &net_kill_list, cleanup_list) {            ops_exit(ops, net);        }    }/* ========== 5. 释放资源 ========== */    list_for_each_entry_safe(net, tmp, &net_kill_list, cleanup_list) {        list_del_init(&net->cleanup_list);/* 删除 /proc/net 目录 */        proc_net_remove(net);/* 释放哈希表 */        kfree(net->dev_index_head);        kfree(net->dev_name_head);/* 释放 net 结构 */        net_free(net);    }}

引用计数管理

/* 增加引用计数 */staticinlinestruct net *get_net(struct net *net){    refcount_inc(&net->count);return net;}/* 减少引用计数 */staticinlinevoidput_net(struct net *net){if (refcount_dec_and_test(&net->count))        __put_net(net);  /* 触发清理 */}/* 触发清理 */staticinlinevoid __put_net(struct net *net){/* 加入清理链表 */    llist_add(&net->cleanup_list, &cleanup_list);/* 调度清理工作 */    queue_work(netns_wq, &net_cleanup_work);}

🔗 网络资源隔离

5.1 网络设备隔离

每个命名空间拥有独立的网络设备列表。

设备归属检查

/** * dev_net - 获取设备所属的命名空间 * @dev: 网络设备 */staticinlinestruct net *dev_net(conststruct net_device *dev){return read_pnet(&dev->nd_net);}/** * dev_net_set - 设置设备所属的命名空间 * @dev: 网络设备 * @net: 命名空间 */staticinlinevoiddev_net_set(struct net_device *dev, struct net *net){    write_pnet(&dev->nd_net, net);}

设备查找(命名空间感知)

/** * __dev_get_by_name - 在指定命名空间中按名称查找设备 * @net: 网络命名空间 * @name: 设备名称 */structnet_device *__dev_get_by_name(structnet *netconstchar *name){structnet_device *dev;structhlist_head *head = dev_name_hash(net, name);/* 在命名空间的哈希表中查找 */    hlist_for_each_entry(dev, head, name_hlist) {if (!strncmp(dev->name, name, IFNAMSIZ))return dev;    }returnNULL;}/** * __dev_get_by_index - 在指定命名空间中按索引查找设备 * @net: 网络命名空间 * @ifindex: 设备索引 */structnet_device *__dev_get_by_index(structnet *netintifindex){structnet_device *dev;structhlist_head *head = dev_index_hash(net, ifindex);    hlist_for_each_entry(dev, head, index_hlist) {if (dev->ifindex == ifindex)return dev;    }returnNULL;}

设备移动到其他命名空间

/** * dev_change_net_namespace - 移动设备到其他命名空间 * @dev: 网络设备 * @net: 目标命名空间 * @pat: 新设备名称模式 */intdev_change_net_namespace(struct net_device *dev, struct net *net,constchar *pat){int err;/* ========== 1. 检查设备是否可移动 ========== */if (dev->features & NETIF_F_NETNS_LOCAL) {/* 设备不支持命名空间移动(如 loopback)*/return -EINVAL;    }/* ========== 2. 检查目标命名空间 ========== */if (dev_net(dev) == net)return0;  /* 已在目标命名空间 *//* ========== 3. 停止设备 ========== */    dev_close(dev);/* ========== 4. 从旧命名空间注销 ========== */    unregister_netdevice(dev);/* ========== 5. 修改设备名称(如果需要)========== */if (pat) {        err = dev_get_valid_name(net, dev, pat);if (err < 0)goto out;    }/* ========== 6. 设置新命名空间 ========== */    dev_net_set(dev, net);/* ========== 7. 在新命名空间注册 ========== */    err = register_netdevice(dev);if (err)goto out;return0;out:return err;}

5.2 路由表隔离

每个命名空间拥有独立的路由表。

路由查找(命名空间感知)

/** * fib_lookup - 在指定命名空间中查找路由 * @net: 网络命名空间 * @flp: 流信息 * @res: 查找结果 */intfib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res){structfib_table *tb;int err = -ENETUNREACH;/* ========== 1. 查找本地路由表 ========== */    tb = fib_get_table(net, RT_TABLE_LOCAL);if (tb) {        err = fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF);if (!err)return0;    }/* ========== 2. 查找主路由表 ========== */    tb = fib_get_table(net, RT_TABLE_MAIN);if (tb)        err = fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF);return err;}

路由表初始化

/** * ip_fib_net_init - 初始化命名空间的路由表 * @net: 网络命名空间 */staticint __net_init ip_fib_net_init(struct net *net){int err;/* ========== 1. 创建本地路由表 ========== */    err = fib4_rules_init(net);if (err < 0)return err;/* ========== 2. 创建主路由表 ========== */    net->ipv4.fib_main = fib_trie_table(RT_TABLE_MAIN, NULL);if (!net->ipv4.fib_main) {        err = -ENOMEM;goto fail_main;    }/* ========== 3. 创建默认路由表 ========== */    net->ipv4.fib_default = fib_trie_table(RT_TABLE_DEFAULT, NULL);if (!net->ipv4.fib_default) {        err = -ENOMEM;goto fail_default;    }/* ========== 4. 创建本地路由表 ========== */    net->ipv4.fib_local = fib_trie_table(RT_TABLE_LOCAL, NULL);if (!net->ipv4.fib_local) {        err = -ENOMEM;goto fail_local;    }return0;fail_local:    fib_trie_free(net->ipv4.fib_default);fail_default:    fib_trie_free(net->ipv4.fib_main);fail_main:    fib4_rules_exit(net);return err;}

5.3 iptables 规则隔离

每个命名空间拥有独立的 iptables 规则。

Netfilter 命名空间数据

/** * struct netns_xt - iptables 命名空间数据 */structnetns_xt {/* 各个表 */structxt_table *filter_table;/* filter 表 */structxt_table *nat_table;/* nat 表 */structxt_table *mangle_table;/* mangle 表 */structxt_table *raw_table;/* raw 表 */structxt_table *security_table;/* security 表 *//* 规则链表 */structlist_headtables[NFPROTO_NUMPROTO];};

规则查找(命名空间感知)

/** * xt_find_table - 在指定命名空间中查找 iptables 表 * @net: 网络命名空间 * @af: 地址族(AF_INET/AF_INET6) * @name: 表名称 */struct xt_table *xt_find_table(struct net *net, u8 af, constchar *name){structxt_table *t;/* 在命名空间的表链表中查找 */    list_for_each_entry(t, &net->xt.tables[af], list) {if (strcmp(t->name, name) == 0)return t;    }returnNULL;}

5.4 Socket 隔离

Socket 绑定到创建它的命名空间。

Socket 命名空间

/** * sock_net - 获取 socket 所属的命名空间 * @sk: socket */staticinlinestruct net *sock_net(conststruct sock *sk){return read_pnet(&sk->sk_net);}/** * sock_net_set - 设置 socket 所属的命名空间 * @sk: socket * @net: 命名空间 */staticinlinevoidsock_net_set(struct sock *sk, struct net *net){    write_pnet(&sk->sk_net, net);}

Socket 创建时绑定命名空间

/** * __sock_create - 创建 socket * @net: 网络命名空间 * @family: 协议族 * @type: socket 类型 * @protocol: 协议 * @res: 返回的 socket * @kern: 是否内核 socket */int __sock_create(struct net *net, int family, int type, int protocol,struct socket **res, int kern){structsocket *sock;conststructnet_proto_family *pf;int err;/* ========== 1. 分配 socket 结构 ========== */    sock = sock_alloc();if (!sock)return -ENOMEM;    sock->type = type;/* ========== 2. 查找协议族 ========== */    pf = rcu_dereference(net_families[family]);if (!pf) {        err = -EAFNOSUPPORT;goto out_release;    }/* ========== 3. 创建协议相关的 sock ========== */    err = pf->create(net, sock, protocol, kern);if (err < 0)goto out_release;/* ========== 4. Socket 绑定到命名空间 ========== */    sock_net_set(sock->sk, net);    *res = sock;return0;out_release:    sock_release(sock);return err;}

5.5 /proc/net 隔离

每个命名空间拥有独立的 /proc/net 目录。

创建 /proc/net 目录

/** * proc_net_init - 初始化命名空间的 /proc/net 目录 * @net: 网络命名空间 */static __net_init intproc_net_init(struct net *net){structproc_dir_entry *netd, *net_statd;int err;/* ========== 1. 创建 /proc/net 目录 ========== */    netd = proc_net_mkdir(net, "net", net->proc_net);if (!netd)return -ENOMEM;    net->proc_net = netd;/* ========== 2. 创建 /proc/net/stat 目录 ========== */    net_statd = proc_net_mkdir(net, "stat", netd);if (!net_statd) {        err = -ENOMEM;goto out;    }    net->proc_net_stat = net_statd;return0;out:    remove_proc_entry("net", net->proc_net);return err;}

在 /proc/net 中创建文件

/** * proc_create_net - 在命名空间的 /proc/net 中创建文件 * @name: 文件名 * @mode: 权限 * @parent: 父目录 * @ops: 文件操作 * @data: 私有数据 */struct proc_dir_entry *proc_create_net(constchar *name, umode_t mode,struct proc_dir_entry *parent,conststruct seq_operations *ops,void *data){structproc_dir_entry *p;    p = proc_create_reg(name, mode, &parent, data);if (!p)returnNULL;    p->proc_fops = &proc_net_seq_fops;    p->seq_ops = ops;return p;}/* 示例:创建 /proc/net/tcp */staticint __net_init tcp4_proc_init_net(struct net *net){return tcp_proc_register(net, &tcp4_seq_afinfo);}

🔌 veth pair 实现

6.1 veth 概述

veth(Virtual Ethernet)是成对出现的虚拟网络设备,用于连接不同的网络命名空间。

veth pair 特点

  • • 成对创建,数据从一端进入,从另一端出来
  • • 常用于容器网络
  • • 可以跨命名空间通信

架构图

6.2 veth 数据结构

源码位置drivers/net/veth.c

/** * struct veth_priv - veth 设备私有数据 */structveth_priv {structnet_device __rcu *peer;/* 对端设备 */atomic64_t              dropped; /* 丢包计数 */structbpf_prog __rcu   *xdp_prog;/* XDP 程序 */structnet_device       *dev;/* 本端设备 */structlist_headlist;/* 链表节点 */};

6.3 veth 创建

创建 veth pair

/** * veth_newlink - 创建 veth pair * @src_net: 源命名空间 * @dev: 本端设备 * @tb: netlink 属性 * @data: 配置数据 * @extack: 错误信息 */staticintveth_newlink(struct net *src_net, struct net_device *dev,struct nlattr *tb[], struct nlattr *data[],struct netlink_ext_ack *extack){structnet_device *peer;structveth_priv *priv;char ifname[IFNAMSIZ];structnet *net;int err;/* ========== 1. 解析对端设备名称 ========== */if (data && data[VETH_INFO_PEER]) {/* 从 netlink 消息中获取对端名称 */        nla_strlcpy(ifname, data[VETH_INFO_PEER], IFNAMSIZ);    } else {/* 自动生成对端名称 */snprintf(ifname, IFNAMSIZ, "veth%%d");    }/* ========== 2. 确定对端命名空间 ========== */if (data && data[VETH_INFO_PEER_NS]) {        net = get_net_ns_by_fd(nla_get_u32(data[VETH_INFO_PEER_NS]));if (IS_ERR(net))return PTR_ERR(net);    } else {        net = src_net;  /* 默认在同一命名空间 */    }/* ========== 3. 分配对端设备 ========== */    peer = rtnl_create_link(net, ifname, NET_NAME_UNKNOWN,                           &veth_link_ops, tb, extack);if (IS_ERR(peer)) {        put_net(net);return PTR_ERR(peer);    }/* ========== 4. 设置 veth pair 关联 ========== */    priv = netdev_priv(dev);    rcu_assign_pointer(priv->peer, peer);    priv = netdev_priv(peer);    rcu_assign_pointer(priv->peer, dev);/* ========== 5. 注册设备 ========== */    err = register_netdevice(dev);if (err < 0)goto err_register_dev;    err = rtnl_configure_link(dev, NULL);if (err < 0)goto err_configure_dev;/* ========== 6. 注册对端设备 ========== */    err = register_netdevice(peer);if (err < 0)goto err_register_peer;    netif_carrier_on(dev);    netif_carrier_on(peer);return0;err_register_peer:    unregister_netdevice(dev);return err;err_configure_dev:    unregister_netdevice(dev);err_register_dev:    free_netdev(peer);    put_net(net);return err;}

6.4 veth 数据传输

发送数据

/** * veth_xmit - veth 发送函数 * @skb: 数据包 * @dev: 发送设备 */staticnetdev_tx_tveth_xmit(struct sk_buff *skb, struct net_device *dev){structveth_priv *priv = netdev_priv(dev);structnet_device *rcv;int length = skb->len;/* ========== 1. 获取对端设备 ========== */    rcu_read_lock();    rcv = rcu_dereference(priv->peer);if (unlikely(!rcv)) {/* 对端不存在,丢弃数据包 */        kfree_skb(skb);goto drop;    }/* ========== 2. 修改数据包的设备 ========== */    skb->dev = rcv;    skb->pkt_type = PACKET_HOST;    skb->protocol = eth_type_trans(skb, rcv);/* ========== 3. 更新统计信息 ========== */if (likely(dev_forward_skb(rcv, skb) == NET_RX_SUCCESS)) {/* 发送成功 */structpcpu_vstats *stats = this_cpu_ptr(dev->vstats);        u64_stats_update_begin(&stats->syncp);        stats->bytes += length;        stats->packets++;        u64_stats_update_end(&stats->syncp);    } else {drop:/* 发送失败 */        atomic64_inc(&priv->dropped);    }    rcu_read_unlock();return NETDEV_TX_OK;}

接收数据

/** * dev_forward_skb - 转发数据包到对端设备 * @dev: 接收设备 * @skb: 数据包 */intdev_forward_skb(struct net_device *dev, struct sk_buff *skb){/* ========== 1. 检查设备状态 ========== */if (!(dev->flags & IFF_UP))return NET_RX_DROP;/* ========== 2. 检查数据包长度 ========== */if (skb->len > (dev->mtu + dev->hard_header_len + VLAN_HLEN))return NET_RX_DROP;/* ========== 3. 重置数据包状态 ========== */    skb_scrub_packet(skb, true);  /* 清除命名空间相关信息 */    skb->priority = 0;    skb->mark = 0;/* ========== 4. 提交到网络栈 ========== */return netif_rx(skb);}

6.5 用户空间操作 veth

使用 ip 命令创建 veth pair

# 创建 veth pairip link add veth0 type veth peer name veth1# 将 veth1 移动到命名空间ip linkset veth1 netns netns1# 配置 IP 地址ip addr add 10.0.0.1/24 dev veth0ip netns exec netns1 ip addr add 10.0.0.2/24 dev veth1# 启动设备ip linkset veth0 upip netns exec netns1 ip linkset veth1 up# 测试连通性ping 10.0.0.2

🐳 容器网络原理

7.1 Docker 网络架构

Docker 使用网络命名空间实现容器网络隔离。

Docker 网络模式

7.2 容器网络创建流程

Docker 创建容器网络的步骤

/* 伪代码:Docker 网络初始化 *//** * docker_network_init - 初始化容器网络 * @container_id: 容器 ID */intdocker_network_init(constchar *container_id){structnet *netns;char veth_host[IFNAMSIZ];char veth_container[IFNAMSIZ];int err;/* ========== 1. 创建网络命名空间 ========== */    netns = copy_net_ns(CLONE_NEWNET, current_user_ns(), &init_net);if (IS_ERR(netns))return PTR_ERR(netns);/* ========== 2. 生成 veth pair 名称 ========== */snprintf(veth_host, IFNAMSIZ, "veth%s", container_id);snprintf(veth_container, IFNAMSIZ, "eth0");/* ========== 3. 创建 veth pair ========== */    err = create_veth_pair(veth_host, veth_container);if (err < 0)goto out_free_ns;/* ========== 4. 将容器端 veth 移动到容器命名空间 ========== */    err = move_veth_to_netns(veth_container, netns);if (err < 0)goto out_delete_veth;/* ========== 5. 将宿主机端 veth 连接到 docker0 网桥 ========== */    err = attach_veth_to_bridge(veth_host, "docker0");if (err < 0)goto out_delete_veth;/* ========== 6. 配置容器内网络 ========== */    err = configure_container_network(netns, veth_container);if (err < 0)goto out_detach_veth;return0;out_detach_veth:    detach_veth_from_bridge(veth_host, "docker0");out_delete_veth:    delete_veth_pair(veth_host);out_free_ns:    put_net(netns);return err;}/** * configure_container_network - 配置容器内网络 * @netns: 容器命名空间 * @ifname: 网卡名称 */intconfigure_container_network(struct net *netns, constchar *ifname){/* 在容器命名空间中执行 *//* 1. 启动 loopback */    ip_link_set_up("lo");/* 2. 配置 IP 地址(从 IPAM 分配)*/    ip_addr_add(ifname, "172.17.0.2/16");/* 3. 配置默认路由 */    ip_route_add_default("172.17.0.1");/* 4. 配置 DNS */    configure_dns();/* 5. 启动网卡 */    ip_link_set_up(ifname);return0;}

7.3 容器间通信

同主机容器通信

Container A (172.17.0.2)    │    │ veth0 (容器内)    │    ▼vethXXX (宿主机)    │    ▼docker0 网桥    │    ▼vethYYY (宿主机)    │    │ veth0 (容器内)    │    ▼Container B (172.17.0.3)

跨主机容器通信(Overlay 网络)

7.4 Kubernetes Pod 网络

Pod 网络模型

CNI(Container Network Interface)插件

/* CNI 插件接口 *//** * cni_add_network - 添加容器到网络 * @netns_path: 命名空间路径 * @ifname: 接口名称 * @config: 网络配置 */intcni_add_network(constchar *netns_path, constchar *ifname,struct cni_config *config){structnet *netns;int err;/* ========== 1. 打开命名空间 ========== */    netns = get_net_ns_by_path(netns_path);if (IS_ERR(netns))return PTR_ERR(netns);/* ========== 2. 创建网络接口 ========== */    err = create_network_interface(netns, ifname, config);if (err < 0)goto out;/* ========== 3. 配置 IP 地址 ========== */    err = configure_ip_address(netns, ifname, config->ip);if (err < 0)goto out_delete_if;/* ========== 4. 配置路由 ========== */    err = configure_routes(netns, config->routes);if (err < 0)goto out_delete_if;    put_net(netns);return0;out_delete_if:    delete_network_interface(netns, ifname);out:    put_net(netns);return err;}/** * cni_del_network - 从网络中删除容器 * @netns_path: 命名空间路径 * @ifname: 接口名称 */intcni_del_network(constchar *netns_path, constchar *ifname){structnet *netns;int err;    netns = get_net_ns_by_path(netns_path);if (IS_ERR(netns))return PTR_ERR(netns);    err = delete_network_interface(netns, ifname);    put_net(netns);return err;}

7.5 Service Mesh 网络

Istio/Envoy 网络架构

💡 实战案例

8.1 案例 1:手动创建网络命名空间

创建隔离的网络环境

#!/bin/bash# ========== 1. 创建网络命名空间 ==========ip netns add ns1ip netns add ns2# ========== 2. 创建 veth pair ==========ip link add veth0 type veth peer name veth1ip link add veth2 type veth peer name veth3# ========== 3. 将 veth 移动到命名空间 ==========ip linkset veth1 netns ns1ip linkset veth3 netns ns2# ========== 4. 创建网桥 ==========ip link add br0 type bridge# ========== 5. 将 veth 连接到网桥 ==========ip linkset veth0 master br0ip linkset veth2 master br0# ========== 6. 配置 IP 地址 ==========# 网桥ip addr add 192.168.1.1/24 dev br0# ns1ip netns exec ns1 ip addr add 192.168.1.2/24 dev veth1ip netns exec ns1 ip route add default via 192.168.1.1# ns2ip netns exec ns2 ip addr add 192.168.1.3/24 dev veth3ip netns exec ns2 ip route add default via 192.168.1.1# ========== 7. 启动设备 ==========ip linkset br0 upip linkset veth0 upip linkset veth2 upip netns exec ns1 ip linkset lo upip netns exec ns1 ip linkset veth1 upip netns exec ns2 ip linkset lo upip netns exec ns2 ip linkset veth3 up# ========== 8. 测试连通性 ==========echo"Testing ns1 -> ns2:"ip netns exec ns1 ping -c 3 192.168.1.3echo"Testing ns2 -> ns1:"ip netns exec ns2 ping -c 3 192.168.1.2

8.2 案例 2:容器网络模拟

模拟 Docker bridge 网络

#!/bin/bash# ========== 1. 创建 docker0 网桥 ==========ip link add docker0 type bridgeip addr add 172.17.0.1/16 dev docker0ip linkset docker0 up# ========== 2. 启用 IP 转发 ==========sysctl -w net.ipv4.ip_forward=1# ========== 3. 配置 NAT ==========iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE# ========== 4. 创建容器函数 ==========create_container() {local name=$1local ip=$2# 创建命名空间    ip netns add $name# 创建 veth pairlocal veth_host="veth_${name}"local veth_container="eth0"    ip link add $veth_hosttype veth peer name $veth_container# 移动到命名空间    ip linkset$veth_container netns $name# 连接到网桥    ip linkset$veth_host master docker0    ip linkset$veth_host up# 配置容器内网络    ip netns exec$name ip linkset lo up    ip netns exec$name ip addr add $ip/16 dev $veth_container    ip netns exec$name ip linkset$veth_container up    ip netns exec$name ip route add default via 172.17.0.1echo"Container $name created with IP $ip"}# ========== 5. 创建多个容器 ==========create_container container1 172.17.0.2create_container container2 172.17.0.3create_container container3 172.17.0.4# ========== 6. 测试容器间通信 ==========echo"Testing container1 -> container2:"ip netns exec container1 ping -c 3 172.17.0.3echo"Testing container2 -> container3:"ip netns exec container2 ping -c 3 172.17.0.4# ========== 7. 测试容器访问外网 ==========echo"Testing container1 -> Internet:"ip netns exec container1 ping -c 3 8.8.8.8

8.3 案例 3:网络命名空间性能测试

测试命名空间间的网络性能

#!/bin/bash# ========== 1. 创建测试环境 ==========ip netns add perf_ns1ip netns add perf_ns2ip link add veth0 type veth peer name veth1ip linkset veth1 netns perf_ns1ip link add veth2 type veth peer name veth3ip linkset veth3 netns perf_ns2ip link add br0 type bridgeip linkset veth0 master br0ip linkset veth2 master br0ip addr add 10.0.0.1/24 dev br0ip netns exec perf_ns1 ip addr add 10.0.0.2/24 dev veth1ip netns exec perf_ns2 ip addr add 10.0.0.3/24 dev veth3ip linkset br0 upip linkset veth0 upip linkset veth2 upip netns exec perf_ns1 ip linkset lo upip netns exec perf_ns1 ip linkset veth1 upip netns exec perf_ns2 ip linkset lo upip netns exec perf_ns2 ip linkset veth3 up# ========== 2. 启动 iperf3 服务器 ==========ip netns exec perf_ns2 iperf3 -s -D# ========== 3. 运行性能测试 ==========echo"========== TCP 吞吐量测试 =========="ip netns exec perf_ns1 iperf3 -c 10.0.0.3 -t 10echo"========== UDP 吞吐量测试 =========="ip netns exec perf_ns1 iperf3 -c 10.0.0.3 -u -b 1G -t 10echo"========== 延迟测试 =========="ip netns exec perf_ns1 ping -c 100 10.0.0.3 | tail -1# ========== 4. 清理 ==========pkill iperf3ip netns del perf_ns1ip netns del perf_ns2ip link del br0

8.4 案例 4:调试网络命名空间

调试工具和技巧

#!/bin/bash# ========== 1. 查看所有命名空间 ==========ip netns list# ========== 2. 查看命名空间中的网络设备 ==========ip netns exec ns1 ip link show# ========== 3. 查看命名空间中的路由表 ==========ip netns exec ns1 ip route show# ========== 4. 查看命名空间中的 iptables 规则 ==========ip netns exec ns1 iptables -L -n -v# ========== 5. 查看命名空间中的连接状态 ==========ip netns exec ns1 ss -tunap# ========== 6. 在命名空间中抓包 ==========ip netns exec ns1 tcpdump -i veth1 -w /tmp/ns1.pcap# ========== 7. 查看命名空间的 /proc/net ==========nsenter --net=/var/run/netns/ns1 cat /proc/net/dev# ========== 8. 进入命名空间执行 shell ==========ip netns exec ns1 bash# ========== 9. 查看 veth pair 对端 ==========get_veth_peer() {local ifname=$1local ifindex=$(ip link show $ifname | head -1 | awk '{print $1}' | tr -d ':')local peer_ifindex=$(ip link show $ifname | grep -oP 'link-netns \K\w+' || \                        ethtool -S $ifname 2>/dev/null | grep peer_ifindex | awk '{print $2}')if [ -n "$peer_ifindex" ]; then        ip link | grep "^${peer_ifindex}:" | awk '{print $2}' | tr -d ':'fi}# ========== 10. 监控命名空间网络流量 ==========watch -n 1 'ip netns exec ns1 cat /proc/net/dev'

📝 总结

9.1 核心要点

网络命名空间架构

  • • struct net:命名空间核心结构,包含所有网络资源
  • • 引用计数:passive 和 count 两种引用计数
  • • 初始命名空间:init_net,系统默认命名空间

资源隔离

  • • 网络设备:每个命名空间独立的设备列表和哈希表
  • • 路由表:独立的 FIB 表(main、local、default)
  • • iptables 规则:独立的 filter、nat、mangle 表
  • • Socket:绑定到创建时的命名空间
  • • /proc/net:独立的 proc 文件系统

生命周期管理

  • • 创建:copy_net_ns() 通过 clone(CLONE_NEWNET) 或 unshare()
  • • 切换:setns() 系统调用切换进程的命名空间
  • • 销毁:引用计数为 0 时异步清理(cleanup_net)

veth pair

  • • 成对虚拟设备,连接不同命名空间
  • • 数据从一端进入,从另一端出来
  • • 容器网络的基础设施

容器网络

  • • Docker bridge 模式:veth + bridge + NAT
  • • Kubernetes Pod 网络:共享命名空间 + CNI 插件
  • • Service Mesh:iptables 流量劫持 + Sidecar 代理

9.2 最佳实践

命名空间管理

# 1. 创建命名空间ip netns add myns# 2. 在命名空间中执行命令ip netns exec myns <command># 3. 进入命名空间nsenter --net=/var/run/netns/myns bash# 4. 删除命名空间ip netns del myns

网络配置

# 1. 创建 veth pair 连接命名空间ip link add veth0 type veth peer name veth1ip linkset veth1 netns myns# 2. 配置 IP 地址ip addr add 10.0.0.1/24 dev veth0ip netns exec myns ip addr add 10.0.0.2/24 dev veth1# 3. 启动设备ip linkset veth0 upip netns exec myns ip linkset veth1 upip netns exec myns ip linkset lo up# 4. 配置路由ip netns exec myns ip route add default via 10.0.0.1

性能优化

# 1. 增大 veth 队列长度ip linkset veth0 txqueuelen 10000# 2. 启用 GRO/GSOethtool -K veth0 gro on gso on# 3. 调整 MTUip linkset veth0 mtu 9000# 4. 绑定 CPUecho 1 > /sys/class/net/veth0/queues/tx-0/xps_cpus

9.3 常见问题排查

连通性问题

# 1. 检查设备状态ip netns exec ns1 ip link show# 2. 检查 IP 地址ip netns exec ns1 ip addr show# 3. 检查路由ip netns exec ns1 ip route show# 4. 检查 iptablesip netns exec ns1 iptables -L -n -v# 5. 抓包分析ip netns exec ns1 tcpdump -i veth1 -n

性能问题

# 1. 查看网络统计ip netns exec ns1 cat /proc/net/dev# 2. 查看丢包ip netns exec ns1 netstat -s | grep -i drop# 3. 查看队列状态tc -s qdisc show dev veth0# 4. 性能测试ip netns exec ns1 iperf3 -c <server_ip>

9.4 安全考虑

权限控制

  • • 创建命名空间需要 CAP_SYS_ADMIN 权限
  • • 切换命名空间需要 CAP_SYS_ADMIN 权限
  • • 用户命名空间可以降低权限要求

资源限制

# 1. 限制命名空间数量sysctl -w user.max_net_namespaces=100# 2. 使用 cgroup 限制网络带宽cgcreate -g net_cls:/mycontainerecho 0x00100001 > /sys/fs/cgroup/net_cls/mycontainer/net_cls.classid# 3. 配置 tc 规则tc qdisc add dev veth0 root handle 1: htb default 10tc class add dev veth0 parent 1: classid 1:10 htb rate 100mbittc filter add dev veth0 protocol ip parent 1:0 prio 1 handle 1: cgroup

网络隔离

# 1. 禁止命名空间间通信iptables -A FORWARD -i docker0 -o docker0 -j DROP# 2. 限制容器访问宿主机iptables -A INPUT -i docker0 -j DROPiptables -A INPUT -i docker0 -m state --state ESTABLISHED,RELATED -j ACCEPT# 3. 限制容器访问外网iptables -A FORWARD -i docker0 ! -o docker0 -j DROP

9.5 下一篇预告

下一篇《网络性能优化技术》将深入分析:

  • • 零拷贝技术(sendfile、splice、MSG_ZEROCOPY)
  • • 硬件 Offload(TSO/GSO、GRO/LRO、RSS/RPS/RFS)
  • • 中断优化(中断合并、中断亲和性、NAPI 调优)
  • • 内存优化(sk_buff 池化、页面回收、NUMA 感知)
  • • 性能测试和分析方法

❓ 常见问题(FAQ)

10.1 网络命名空间和虚拟机有什么区别?

网络命名空间

  • • 轻量级隔离,共享内核
  • • 启动快速(毫秒级)
  • • 资源开销小
  • • 隔离级别较低

虚拟机

  • • 完全隔离,独立内核
  • • 启动较慢(秒级)
  • • 资源开销大
  • • 隔离级别高

选择建议

  • • 容器化应用:使用命名空间
  • • 需要完全隔离:使用虚拟机
  • • 混合场景:虚拟机 + 容器

10.2 如何查看进程所在的网络命名空间?

# 方法 1:查看 /proc/PID/ns/netls -l /proc/$PID/ns/net# 方法 2:使用 ip netns identifyip netns identify $PID# 方法 3:比较 inode 号stat -L /proc/$PID/ns/netstat -L /var/run/netns/myns# 方法 4:使用 lsnslsns -t net -p $PID

10.3 veth pair 的性能如何?

性能特点

  • • 吞吐量:约为物理网卡的 50-70%
  • • 延迟:增加约 10-20 微秒
  • • CPU 开销:较高(软件转发)

性能对比(10Gbps 网卡):

场景                    吞吐量        延迟----------------------------------------------物理网卡直连            9.5 Gbps     10 μsveth pair              6-7 Gbps     25 μsveth + bridge          5-6 Gbps     30 μsveth + NAT             4-5 Gbps     40 μs

优化建议

# 1. 增大队列长度ip linkset veth0 txqueuelen 10000# 2. 启用 offload 特性ethtool -K veth0 tso on gso on gro on# 3. 调整 MTUip linkset veth0 mtu 9000# 4. 使用 XDP 加速

10.4 如何在命名空间之间共享文件?

方法 1:使用 Mount 命名空间

# 在两个命名空间中挂载同一目录mount --bind /shared /var/run/netns/ns1/sharedmount --bind /shared /var/run/netns/ns2/shared

方法 2:使用 Unix Domain Socket

# 创建 socket 在共享目录ip netns exec ns1 nc -lU /shared/socket.sockip netns exec ns2 nc -U /shared/socket.sock

方法 3:使用网络传输

# 通过 veth pair 传输文件ip netns exec ns1 nc -l 8080 > fileip netns exec ns2 nc 10.0.0.2 8080 < file

10.5 命名空间删除后资源会立即释放吗?

不会立即释放,需要满足条件:

  1. 1. 所有进程退出命名空间
  2. 2. 所有文件描述符关闭(/proc/PID/ns/net)
  3. 3. 所有网络设备移出或删除
  4. 4. 引用计数降为 0

检查命名空间引用

# 查看命名空间的引用lsns -t net# 查看哪些进程在使用lsof | grep netns# 强制清理ip netns del myns# 如果失败,查找并杀死相关进程ps aux | grep myns

10.6 如何实现命名空间的网络监控?

方法 1:使用 netns exec

# 监控脚本whiletruedofor ns in $(ip netns list); doecho"=== $ns ==="        ip netns exec$nscat /proc/net/devdonesleep 1done

方法 2:使用 eBPF

/* eBPF 程序跟踪所有命名空间的网络事件 */SEC("kprobe/dev_queue_xmit")inttrace_dev_queue_xmit(struct pt_regs *ctx){structsk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);structnet_device *dev;structnet *net;    u32 netns_inum;    bpf_probe_read(&dev, sizeof(dev), &skb->dev);    bpf_probe_read(&net, sizeof(net), &dev->nd_net.net);    bpf_probe_read(&netns_inum, sizeof(netns_inum), &net->ns.inum);/* 记录命名空间的网络活动 */    bpf_trace_printk("netns=%u len=%d\n", netns_inum, skb->len);return0;}

方法 3:使用 Prometheus + cAdvisor

# 监控容器网络指标scrape_configs:-job_name:'cadvisor'static_configs:-targets: ['localhost:8080']

10.7 Docker 和 Kubernetes 如何使用网络命名空间?

Docker

# 查看容器的命名空间docker inspect <container_id> | grep SandboxKey# 进入容器的网络命名空间nsenter --net=/var/run/docker/netns/<netns_id> bash# 查看容器网络配置docker exec <container_id> ip addr show

Kubernetes

# 查看 Pod 的命名空间crictl inspect <pod_id> | grep namespace# 进入 Pod 的网络命名空间nsenter --net=/var/run/netns/<netns_id> bash# 使用 kubectl 调试kubectl debug <pod_name> --image=nicolaka/netshoot

10.8 如何实现跨主机的命名空间通信?

方法 1:VXLAN Overlay

# 主机 Aip link add vxlan0 type vxlan id 42 remote <host_b_ip> dstport 4789 dev eth0ip addr add 10.0.0.1/24 dev vxlan0ip linkset vxlan0 up# 主机 Bip link add vxlan0 type vxlan id 42 remote <host_a_ip> dstport 4789 dev eth0ip addr add 10.0.0.2/24 dev vxlan0ip linkset vxlan0 up

方法 2:GRE 隧道

# 主机 Aip tunnel add gre0 mode gre remote <host_b_ip> local <host_a_ip>ip addr add 10.0.0.1/24 dev gre0ip linkset gre0 up# 主机 Bip tunnel add gre0 mode gre remote <host_a_ip> local <host_b_ip>ip addr add 10.0.0.2/24 dev gre0ip linkset gre0 up

方法 3:WireGuard VPN

# 主机 Aip link add wg0 type wireguardwg setconf wg0 /etc/wireguard/wg0.confip addr add 10.0.0.1/24 dev wg0ip linkset wg0 up# 主机 Bip link add wg0 type wireguardwg setconf wg0 /etc/wireguard/wg0.confip addr add 10.0.0.2/24 dev wg0ip linkset wg0 up

10.9 网络命名空间的性能开销有多大?

CPU 开销

  • • 命名空间切换:约 1-2 微秒
  • • veth 转发:约 10-20% CPU
  • • 网桥转发:约 15-25% CPU

内存开销

  • • 每个命名空间:约 1-2 MB
  • • veth pair:约 100 KB
  • • 路由表:约 500 KB

性能测试结果

场景                    吞吐量        CPU 使用率------------------------------------------------物理网卡                9.5 Gbps     5%单命名空间              9.0 Gbps     8%veth pair              6.5 Gbps     25%veth + bridge          5.5 Gbps     35%

10.10 如何调试命名空间网络问题?

调试工具

# 1. 查看设备和地址ip netns exec ns1 ip addr showip netns exec ns1 ip link show# 2. 查看路由ip netns exec ns1 ip route showip netns exec ns1 ip route get 8.8.8.8# 3. 查看 ARP 表ip netns exec ns1 ip neigh show# 4. 查看连接状态ip netns exec ns1 ss -tunap# 5. 抓包分析ip netns exec ns1 tcpdump -i veth1 -w /tmp/capture.pcap# 6. 测试连通性ip netns exec ns1 ping -c 3 10.0.0.2ip netns exec ns1 traceroute 10.0.0.2# 7. 查看 iptablesip netns exec ns1 iptables -L -n -vip netns exec ns1 iptables -t nat -L -n -v# 8. 查看内核日志dmesg | grep -i net# 9. 使用 strace 跟踪系统调用strace -e trace=network ip netns exec ns1 ping 10.0.0.2# 10. 使用 perf 分析性能perf record -a -g -- ip netns exec ns1 iperf3 -c 10.0.0.2perf report

📚 参考资料

11.1 内核源码文件

命名空间核心文件

net/core/net_namespace.c        # 命名空间管理include/net/net_namespace.h     # 命名空间定义kernel/nsproxy.c                # 命名空间代理fs/nsfs.c                       # 命名空间文件系统

veth 实现

drivers/net/veth.c              # veth 驱动

网络设备管理

net/core/dev.c                  # 设备注册和管理net/core/rtnetlink.c            # rtnetlink 接口

路由和 iptables

net/ipv4/fib_frontend.c         # 路由前端net/netfilter/core.c            # netfilter 核心

11.2 系统调用和工具

系统调用

man 2 clone# 创建命名空间man 2 unshare      # 取消共享命名空间man 2 setns        # 切换命名空间

用户空间工具

man 8 ip-netns     # ip netns 命令man 8 nsenter      # 进入命名空间man 1 unshare      # unshare 命令man 8 ip-link      # ip link 命令

11.3 内核文档

官方文档

Documentation/networking/veth.txt           # veth 文档Documentation/admin-guide/namespaces/       # 命名空间管理指南

在线资源

  • • Linux Namespaces: https://man7.org/linux/man-pages/man7/namespaces.7.html
  • • Network Namespaces: https://man7.org/linux/man-pages/man7/network_namespaces.7.html

11.4 经典书籍

容器和网络

  1. 1. 《Docker 容器与容器云》(第2版)
    • • 详细介绍 Docker 网络实现
  2. 2. 《Kubernetes 网络权威指南》
    • • 深入讲解 K8s 网络原理
  3. 3. 《Linux 容器技术》
    • • 全面介绍 Linux 命名空间

内核网络4. 《深入理解 Linux 网络技术内幕》

  • • 包含命名空间实现细节
  1. 5. 《Linux 内核源代码情景分析》
    • • 网络命名空间源码分析

11.5 调试和性能工具

网络工具

# 命名空间管理ip netns, nsenter, unshare# 网络配置ip, ifconfig, route# 调试工具tcpdump, wireshark, ss, netstat# 性能测试iperf3, netperf, qperf# 跟踪工具strace, ltrace, perf, bpftrace

容器工具

# Dockerdocker network, docker inspect# Kuberneteskubectl, crictl# 网络调试容器nicolaka/netshoot, wbitt/network-multitool

作者:肇中内核版本:Linux 5.10 LTS

系列文章

  • • 上一篇:《第11篇:套接字层实现》
  • • 下一篇:《第13篇:网络性能优化技术》

勘误和建议:欢迎勘误和指出改进建议。