虚拟网络namespace到bridge

news2024/12/28 19:32:59

0da31a72accc463392b9b9210d02d7ab.jpg


 前言

 

容器的网络是一大难点,不管是docker 还是kubernetes 都绕不开计算机网络。以下的介绍主要以计算机网络的namespace 和bridge 两个方面来展开介绍,方便深入理解容器的网络原理。

 

1.namespace分析

linux 支持六种资源的namespace :mount ns 、UTS ns 、IPC ns 、PID ns、Network ns 、User ns

为了让每一个进程从属于某一个namespace ,linux 内核为描述符增加了一个struct nsproxy 结构。

struct task_struct {
    ...
    /* namespaces */
    struct nsproxy *nsproxy;
    ...
}

struct nsproxy {
    atomic_t count;
    struct uts_namespace  *uts_ns;
    struct ipc_namespace  *ipc_ns;
    struct mnt_namespace  *mnt_ns;
    struct pid_namespace  *pid_ns;
    struct user_namespace *user_ns;
    struct net            *net_ns;
};

从上面的结构体可以看出,Linux 为每一种不同类型的资源定义了不同的命名空间的结构体进行管理。这里结构体就是上面说到的六种namespace。

这里主要以pid_namespace 为例来介绍:

首先看下pid_namespace 的结构体

struct pid_namespace {
    struct kref kref; //成员是一个引用计数器,用于记录引用这个结构的进程数
    struct pidmap pidmap[PIDMAP_ENTRIES];//成员用于快速找到可用pid的位图
    int last_pid;//成员是记录最后一个可用的pid
    struct task_struct *child_reaper; 
    struct kmem_cache *pid_cachep;
    unsigned int level;// 成员记录当前 pid命名空间 所在的层次
    struct pid_namespace *parent;//成员记录当前 pid命名空间 的父命名空间
#ifdef CONFIG_PROC_FS
    struct vfsmount *proc_mnt;
#endif
};

由于PID 命名空间是分层的,也就是说新创建一个pid命名空间时会记录父级pid 命名空间到

parent 字段中,所以随着pid 命名空间的创建,在内核中会形成一颗pid 命名空间的树。

  • 成员记录当前 pid命名空间 的父命名空间。pid 初始化的是在level0 这一层。在这个命名空间树中,低层的pid 对高层的pid 命名空间进程不可见。

  • f1a1fa749d0d4563b8d549996bb8e395.png

从level0 到 levelN 是如何保存这些进程ID 信息的。在解析完子进程pid 后,如何将信息保存在task_struct 中,看看PID相关代码,有两处保存进程ID 信息相关:

p->pid = pid_nr(pid);   //获取global pid_ns中的pid,即第0层
        … …
init_task_pid(p, PIDTYPE_PID, pid);   //将struct pid 结构地址保存到进程描述符中

首先task_struct->pid中存的是global pid namespace中的PID value,因为对于内核来说,它只需要看全局的pid namespace即可(init进程),里面包含系统全局进程的global PID。微信搜索公众号:信安黑客技术,回复:黑客 领取资料 。

在linux 中并不区分进程和线程,都是同task_struct 抽象,只不过支持多线程的进行是由一组task_struct 来抽象的。

接下来看看系统调用clone() ,传入以上 不同类型的参数实现复制不同的ns 。这里以clone_newpid 参数为例。实际是复制pid 命名空间,在新的pid 命名空间里可以使用与其他pid 命名空间相同的pid

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>

char child_stack[5000];

int child(void* arg)
{
    printf("Child - %d\n", getpid());
    return 1;
}

int main()
{
    printf("Parent - fork child\n");
    int pid = clone(child, child_stack+5000, CLONE_NEWPID, NULL);
    if (pid == -1) {
        perror("clone:");
        exit(1);
    }
    waitpid(pid, NULL, 0);
    printf("Parent - child(%d) exit\n", pid);
    return 0;
}

输入如下:

Parent - fork child
Parent - child(9054) exit
Child - 1

从运行结果来看,在子进程的PID命名空间里,当前进程的PID为1,但在父进程的pid 命名空间中子进程的pid 却是9045。

 

2.birdge

从上文中可以看到namespace 隔离了内核资源,让一些进程只能看到与自己相关的一部分资源。进程与进程之间没办法通信。但是Linux 之间需要两个或者两个以上的ns 链接。这个时候就需要veth pair 和bridge 出场了,veth pair 在这里不着重介绍,以bridge 为主来展开。

docker 中也用到了namespace 做隔离,但是docker 的隔离只是部分资源隔离,docker 的网络模式也有bridge。

2.1 网桥的实际应用

关于网桥配置ip 地址,首先先看看我自己的本机的网桥ip地址,再来看看有什么作用。

可以看到两个网桥都配置了对应的ip 地址,但是其实网桥不用配置ip的,给网桥配制ip地址,是为了方便网桥所在的主机和网桥所桥接的网卡(包括虚拟网卡)进行通信。一般情况下会配成 同一网段的ip .但是其对应的虚拟机(容器)可以配制ip地址,如果和网桥的ip是同一个网段的话,网桥所在的物理主机和这个网桥上桥接的网卡所对应的虚拟主机就可以进行通信了(能够ping通。那一下例子,我在本机ping 不同虚拟机的地址,是因为不在同一网段内。我自己本机的地址是192.168.138... 虚拟机的ip 地址是

d436d24f190b4af999ed9c28ea9c6d43.png

 而在虚拟集中我自己的IP地址是192.168.122.. 不在同一网段内,所以造成了无法通信的情况。

4bf75dee9cd642818c4125fe91f592d3.png

 2.2 网桥的工作原理

网桥是以混杂的模式工作的,混杂模式简称Promisc mode,即监听模式,常被用来诊断网络问题,混杂模式是指一个网卡会把它接受的所有的网络流量都交给cpu,而不是只想把它转交的部分交给cpu。每个桥维护了一个基于MAC地址的过滤数据库,网桥根据这个数据库,把收到的帧往相应的局域网(端口)进行转发。在过滤数据库中,列出了每个可能的目的地(目的MAC地址),以及它属于哪一条输出线路(一个端口号,即表示转发给哪个LAN),每个表项还有一个超时设置。通过下图网桥的实际作用来看网桥时候如何工作的。

6b4df6a7f6a4469eaca551b3dc831cab.png

 如上图所示,当网络接口A接收到数据包后,网桥 会将数据包复制并且发送给连接到 网桥 的其他网络接口(如上图中的网卡B和网卡C)。通过上图可以将网桥总结为以下几点:

609a66270f7d4371b2337a44eafc69cc.png

收到一个报文后,根据报文的目的MAC,选择适当的端口进行转发。对于不同的报文,起转发流程略有差异:
a. 已知单播 - 单播:对于单播报文,如果知道目的地址在哪个端口(MAC表中能找到该目的MAC),就从该端口转发出去
b. 未知单播 - 泛洪:对于单播报文,如果暂时还不知道目的地址是哪个端口,则从所有非源端口泛洪出去
c. 组播广播 - 泛洪:对于广播和组播,也需要泛洪。

网桥的这种工作方式,是为了尽可能小的代价保证报文能最终到达它的目的地。

3.3 通过命令行来看看网桥实现

增加网桥

[root@controller]# brctl addbr br0

查看网桥

[root@controller]# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.000000000000       no
docker0         8000.000000000000       no

这里回到内核源码来看看创建网桥源码:

int br_add_bridge(char *name)
{
    struct net_bridge *br;

    if ((br = new_nb(name)) == NULL) // 创建一个网桥设备对象
        return -ENOMEM;

    if (__dev_get_by_name(name) != NULL) { // 设备名是否已经注册过?
        kfree(br);
        return -EEXIST; // 返回错误, 不能重复注册相同名字的设备
    }

    // 添加到网桥列表中
    br->next = bridge_list;
    bridge_list = br;
    ...
    register_netdev(&br->dev); // 把网桥注册到网络设备中

    return 0;
}

上述代码清晰的显示了,网桥到网络设备的注册

再看看net_bridge 的数据结构

struct net_bridge
{
    struct net_bridge           *next;               // 连接内核中所有的网桥对象
    rwlock_t                    lock;                // 锁
    struct net_bridge_port      *port_list;          // 网桥端口列表
    struct net_device           dev;                 // 网桥设备信息
    struct net_device_stats     statistics;          // 信息统计
    rwlock_t                    hash_lock;           // 用于锁定CAM表
    struct net_bridge_fdb_entry *hash[BR_HASH_SIZE]; // CAM表
    struct timer_list           tick;

    /* STP */
    ...
};

网桥中比较重要的两个字段是:port_list、hash[BR_HASH_SIZE](保存着以网络接口 MAC地址 为键值,以网桥端口为值的哈希表)

网桥端口列表的数据结构:

struct net_bridge_port
{
    struct net_bridge_port  *next;   // 指向下一个端口
    struct net_bridge       *br;     // 所属网桥设备对象
    struct net_device       *dev;    // 网络接口设备对象
    int                     port_no; // 端口号

    /* STP */
    ...
};

net_bridge_fdb_entry 数据结构主要用于描述网络接口设备MAC 地址与网桥端口的对应关系:

struct net_bridge_fdb_entry
{
    struct net_bridge_fdb_entry *next_hash;
    struct net_bridge_fdb_entry **pprev_hash;
    atomic_t                    use_count;
    mac_addr                    addr;  // 网络接口设备MAC地址
    struct net_bridge_port      *dst;  // 网桥端口
    ...
};

以上三个结构,我在搜索的过程中,一张图很好的展示了三者的关系

43175649b7e545d9a243435e11240caf.png

 可见,要将 网络接口设备 绑定到一个 网桥 上,需要使用 net_bridge_port 结构来关联的。

 

小结

 

总的来说,不管是以上介绍的namespace 还是网桥,都是计算机网络的鼻祖,从docker 的网络连接方式到kubenetes的网络方式,其底层的技术原理都与之有关。理解了namespace 和网桥的连接,后续理解docker 、kubernetes网络会更加简单。

 

欢迎转发点赞收藏评论,感谢🙏

-End-


 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/656353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML日期选择器:简化日期输入与交互

Date Picker 控件 HTML提供了<input>元素的type="date"属性,用于创建日期选择器(Date Picker)控件。日期选择器允许用户从一个交互式日历中选择日期值。以下是关于HTML日期选择器的详细解释: 创建日期选择器: 使用<input>元素,并将其type属性设置…

java从零开始系统性学习完整超全资源+笔记(下)

java从零开始系统性学习完整超全资源笔记&#xff08;下&#xff09; java从零开始系统性学习完整超全资源笔记&#xff08;上&#xff09; 文章目录 java从零开始系统性学习完整超全资源笔记&#xff08;下&#xff09;第十七章 泛型与常见数据结构ArrayListCollection接口的…

linux + lnmp + tp6部署项目问题集

部署完毕后&#xff0c;访问域名&#xff0c;报错502 Gateway 1:查看防火墙&#xff0c;需要的端口是否被关闭了&#xff0c;需要开启 vi /etc/sysconfig/iptables 发现有端口没空&#xff0c;就先i,然后把该端口后面的DROP改成ACCEPT&#xff0c;然后esc :wq 再回车&#xf…

轻松搞定文件复制备份,教你用快速的时间将文件复制另一个文件夹里并自动编号。

通常情况下&#xff0c;我们会将文件复制到另一个文件夹中&#xff0c;这很容易实现。但是如果需要将大量文件复制到同一个文件夹中&#xff0c;并且需要给每个文件自动编号&#xff0c;这时就需要用到一些技巧了。下面是一个简单的办法&#xff0c; 首先&#xff0c;第一步我…

node笔记_读写excel

文章目录 ⭐前言⭐安装依赖⭐读取excel&#x1f496; 按行读取&#x1f496; 按列读取 ⭐写入excel⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于node读取excel内容 往期文章 node_windows环境变量配置 node_npm发布包 linux_配置node node_nvm安装配置…

微服务开发系列 第十一篇:XXL-JOB

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

【AIGC】baichuan-7B大模型

百川智能&#xff5c;开源可商用的大规模预训练语言模型baichuan-7B大模型 概述 baichuan-7B 是由百川智能开发的一个开源可商用的大规模预训练语言模型。基于 Transformer 结构&#xff0c;在大约1.2万亿 tokens 上训练的70亿参数模型&#xff0c;支持中英双语&#xff0c;上…

day53_spring

今日内容 零、 复习昨日 零、 复习昨日 略 一、代理设计模式 代理的设计理念是限制对象的直接访问&#xff0c;即不能通过 new 的方式得到想要的对象&#xff0c;而是访问该对象的代理类。 这样的话&#xff0c;我们就保护了内部对象&#xff0c;如果有一天内部对象因为 某个原…

MIT 6.S081 (BOOK-RISCV-REV1)教材第三章内容

MIT 6.S081 教材第三章内容 引言页表分页硬件内核地址空间物理内存分配代码&#xff08;物理内存分配&#xff09;kinit函数kfree函数kalloc函数 代码&#xff1a;创建一个地址空间kvminit 函数kvmmap函数walk函数kvminithart函数procinit函数 进程地址空间代码&#xff1a;sbr…

Lua 脚本语法学习

文章目录 Lua 基础语法单行注释和多行注释数据类型标识符运算符关系运算符if条件循环whiledorepeatuntil数值for泛型for 函数1. 固定参数函数2. 可变参函数3. 多返回值4. 函数作参数5.匿名函数 Lua 语法进阶table1. 数组2. map3. 数组-map 混合结构4. table操作函数 迭代器模块…

Makerbase SimpleFOC ESP32 例程9 在线电流检测测试

Makerbase SimpleFOC ESP32 例程9 在线电流检测测试 第一部分 硬件介绍 1.1 硬件清单 序号品名数量1 ESP32 FOC V1.0 主板 12 YT2804电机 23 12V电源适配器 14 USB 线 1 注意&#xff1a;YT2804是改装的云台无刷电机,带有AS5600编码器&#xff0c;可实现360连续运转。 主要…

java:实现用户扫码二维码自动跳转指定链接功能

0. 引言 近来接到要实现链接转二维码的需求&#xff0c;通过提供二维码给用户&#xff0c;让用户扫描后自动访问指定的H5页面&#xff0c;从而实现业务流转&#xff0c;这样的功能其实在其他很多场景也会用到&#xff0c;比如资产管理系统中&#xff0c;扫码资产二维码&#x…

uniapp实现tab切换可以滚动的效果

实现效果 当 tab 切换的内容很多时&#xff0c;需要用到滚动&#xff0c;希望在点击 tab 的时候可以自动滑动到对应的tab下 知识点 scrollIntoView&#xff1a;该scrollIntoView()方法将调用它的元素滚动到浏览器窗口的可见区域。 语法 element.scrollIntoView&#xff08…

2023 年企业 Java 面试前复习的正确姿势(已助力319人入职大厂)

作为 Java 程序员&#xff0c;选择学习什么样的技术&#xff1f;什么技术该不该学&#xff1f;去招聘网站上搜一搜、看看岗位要求就十分清楚了&#xff0c;自己具备的技术和能力&#xff0c;直接影响到你工作选择范围和能不能面试成功。 如果想进大厂&#xff0c;那就需要在 Ja…

ARM-Linux开发与MCU开发的不同之处分析

目录 一、ARM-Linux应用开发和单片机开发的不同 二、Arm-Linux基本开发环境 针对ARM-Linux程序的开发&#xff0c;主要分为三类&#xff1a;应用程序开发、驱动程序开发、系统内核开发。针对不同种类的软件开发&#xff0c;有其不同的特点。 今天&#xff0c;我们来看看ARM-L…

B-6:逆向分析及隐写

任务环境说明: 服务器场景: FTPServer20220509(关闭链接) FTP用户名:PE01密码: PE01 C语言:渗透机Windows7 (Embarcadero Dev-C++) 1,从靶机服务器的FTP上下载PE01文件,对PE01. exe二进制文件进行静态调试,将 main 函数的入口地址作为 Flag 值提交; 双击渗透机kali桌面上…

【数字图像处理】3.对比度增强

目录 3.1 灰度直方图 3.2 线性变换 3.3 直方图正规化 3.4 伽马变换 3.5 全局直方图均衡化 3.6 CLAHE 对比度增强是图像增强的一种&#xff0c;它主要解决的是图像的灰度级范围较小造成的对比度较低的问题&#xff0c;目的是将图像的灰度级增强到指定范围&#xff0c;使得…

Unity Metaverse(八)、RTC Engine 基于Agora声网SDK实现音视频通话

文章目录 简介创建应用构建应用场景API调用与回调事件测试 简介 本文介绍如何在Unity中接入声网SDK&#xff0c;它可以应用的场景有许多&#xff0c;例如直播、电商、游戏、社交等&#xff0c;音视频通话是其实时互动的基础能力。 如下图所示&#xff0c;可以在官网中选择Unit…

【Python实用基础整合(三)】儒略日计算、Pandas写Excel文件多Sheet以及datetime64[ns]时间格式处理

一、儒略日计算 儒略日&#xff08;Julian Day&#xff09;是在儒略周期内以连续的日数计算时间的计时法&#xff0c;主要用于天文学领域&#xff0c;SMOKE、CMAQ、CAMx等模型中也有使用。Linux中主要使用IOAPI库中的juldate和jul2greg来进行常规日期和儒略日的相互转化。Pyth…

MongoDB索引详解-03

MongoDB索引 索引是一种用来快速查询数据的数据结构。BTree 就是一种常用的数据库索引数据结构&#xff0c; MongoDB采用BTree 做索引 &#xff0c;索引创建在colletions上。MongoDB不使用索引的查 询&#xff0c;先扫描所有的文档&#xff0c;再匹配符合条件的文档。 使用索…