《云原生网络数据面可观测性最佳实践》—— 一、容器网络内核原理——2.netfilter框架(上)

简介: 《云原生网络数据面可观测性最佳实践》—— 一、容器网络内核原理——2.netfilter框架(上)

netfilter框架是Linux操作系统中内置的,通过向内核模块提供对协议栈中的网络数据包进行修改和操作的能力,来实现流量过滤,网络地址转换等高级功能。

 

1) netfilter如何工作

对于网络数据报文,网络设备驱动通过将二层的以太网数据报文按照Linux内核定义的网络设备驱动规范,以sk_buff结构体的方式进行接收或者发送,即通常我们所描述的报文的最小单元skb。

 

内核通过将网络设备缓冲区环形队列中skb取出,并按照以太网层,网络层,传输层顺序处理后,将报文数据放置到对应Socket缓冲区中,通知用户程序进行读取,从而完成收包

内核为Socket缓冲区待发送数据封装为skb,经过传输层,网络层和以太网层依次填充对应报头后,调用网络设备驱动方法将skb发送到网络上,从而完成发包

 

netfilter工作的核心原理则是在网络层,通过在五个不同的内核处理skb数据包的位置,执行注册到netfilter框架中的回调函数,并根据回调函数的返回来选择下一步的处理,实现复杂的功能。

 

netfilter触发时机

 

netfilter在内核网络数据包的处理流程中,注册了5个可以触发的时机,详情如下:

/* IP Hooks */
/* After promisc drops, checksum checks. */
#define NF_IP_PRE_ROUTING0
/* If the packet is destined for this box. */
#define NF_IP_LOCAL_IN1
/* If the packet is destined for another interface. */
#define NF_IP_FORWARD2
/* Packets coming from a local process. */
#define NF_IP_LOCAL_OUT3
/* Packets about to hit the wire. */
#define NF_IP_POST_ROUTING4
#define NF_IP_NUMHOOKS5
#endif /* ! __KERNEL__ */

 

根据源代码中的定义,我们可以参考下图:

 image.png

 从上面的代码定义和示意图中可以知道,在以下几个地方会调用netfilter定义好的方法对数据包进行处理:

 

数据包从网卡进入协议栈后,所有报文都会走到NF_IP_PRE_ROUTING

数据包经过入向路由选择后,确认是由本机传输层进行处理,会进入到NF_IP_LOCAL_IN

数据包经过入向路由选择后,不是由本机处理,需要转发给其他机器,会进入到NF_IP_FORWARD

由本机传输层发出报文,都会经过NF_IP_LOCAL_OUT

经过出向路由选择后报文,会经过NF_IP_POST_ROUTING,包括从本机发出和需要本机转发的。

 

在以上五个时机,当数据包skb到达时,netfilter框架定义的回调方法就会被内核执行。

 

netfilter如何操作网络数据

使用netfilter框架的模块,需要按照netfilter定义的结构体来实现自己的行为,才能正确注册到netfilter框架中,并被内核调用,netfilter约束的注册结构体的定义如下:

struct nf_hook_ops {
    // 这是真正被内核执行的函数,会对skb进行读取和修改
    nf_hookfn*hook;
    struct net_device*dev;
    void*priv;
    u_int8_tpf;
    // hooknum定义了回调函数生效的位置,从上文可知有五个位置可以选择
    unsigned inthooknum;
    // priority定义了回调函数的优先级,通过每个hook时机都会有多个回调函数需要生效
    intpriority;
};

以ipvlan模块为例:

static const struct nf_hook_ops ipvl_nfops[] = {
  {
    .hook     = ipvlan_nf_input,
    .pf       = NFPROTO_IPV4,
    .hooknum  = NF_INET_LOCAL_IN,
    .priority = INT_MAX,
  },
#if IS_ENABLED(CONFIG_IPV6)
  {
    .hook     = ipvlan_nf_input,
    .pf       = NFPROTO_IPV6,
    .hooknum  = NF_INET_LOCAL_IN,
    .priority = INT_MAX,
  },
#endif
};

 

我们可以看到,ipvlan模块根据IP协议的版本定义了两个hook结构体,其中:

hookfn是核心处理逻辑,ipvlan模块注册了ipvlan_nf_input方法

pf是netfilter协议版本,ipvlan模块分别注册了IPv6和IPv4对应方法

hooknum定义了这个hook在netfilter中生效位置,ipvlan模块将自己回调方法注册到了NF_INET_LOCAL_IN,也就是完成路由之后,进入传输层之前

priotity定义了hook优先级

 

那么,hookfn作为真正核心的处理逻辑,他是如何工作的呢?

 

以ipvlan注册在NF_INET_LOCAL_IN上的hook方法ipvlan_nf_input为例:

 

unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
           const struct nf_hook_state *state)
{
    // 参数中可以看到,有前一个生效的hook结构体,当前处理的skb报文本身以及当前netfilter框架的上下文信息
  struct ipvl_addr *addr;
  unsigned int len;
  addr = ipvlan_skb_to_addr(skb, skb->dev);
  if (!addr)
    goto out;
  skb->dev = addr->master->dev;
  len = skb->len + ETH_HLEN;
  ipvlan_count_rx(addr->master, len, true, false);
out:
    // 这里返回了netfilter框架规定的返回码
  return NF_ACCEPT;
}

 从ipvlan_nf_input可以看到,注册到netfilter中的回调函数的核心在于两个约束:

● 回调函数的入参定义,可以接受协议栈中真正的报文结构体skb以及netfilter的上下文状态为参数。

● 回调函数的返回值,需要是netfilter规定好的返回值,他们的定义如下:

/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5/* Deprecated, for userspace nf_queue compatibility. */
#define NF_MAX_VERDICT NF_STOP

从以上的分析不难看出,只要满足netfilter的注册条件,就可以在回调函数中直接对数据报文skb内容进行读取和修改操作,而通过返回值的定义,则可以借助netfilter框架完成对数据包的处理,比如丢弃,接收或者传递到用户态进行处理。

 

netfilter的内核实现

在负责对所有hook方法进行遍历处理的函数中,可以看到,netfilter核心的工作流程就是在相应的触发时机调用这个时机上注册的所有方法,按照优先级进行处理,并根据每一次处理的结果进行操作,包括丢弃,传输给用户态等等:

int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
     const struct nf_hook_entries *e, unsigned int s)
{
  unsigned int verdict;
  int ret;
  for (; s < e->num_hook_entries; s++) {
        // 在这里调用对应的hook时机上的所有注册的hook回调方法,然后根据结果选择是不是继续循环
    verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
    switch (verdict & NF_VERDICT_MASK) {
    case NF_ACCEPT:
      break;
    case NF_DROP:
      kfree_skb(skb);
      ret = NF_DROP_GETERR(verdict);
      if (ret == 0)
        ret = -EPERM;
      return ret;
    case NF_QUEUE:
      ret = nf_queue(skb, state, e, s, verdict);
      if (ret == 1)
        continue;
      return ret;
    default:
      /* Implicit handling for NF_STOLEN, as well as any other
       * non conventional verdicts.
       */
      return 0;
    }
  }
  return 1;

 

更多精彩内容,欢迎观看:

《云原生网络数据面可观测性最佳实践》—— 一、容器网络内核原理——2.netfilter框架(下):https://developer.aliyun.com/article/1221729?spm=a2c6h.13148508.setting.16.15f94f0e18Oqpt

相关文章
|
2天前
|
前端开发 网络协议 安全
【网络原理】——HTTP协议、fiddler抓包
HTTP超文本传输,HTML,fiddler抓包,URL,urlencode,HTTP首行方法,GET方法,POST方法
|
2天前
|
域名解析 网络协议 关系型数据库
【网络原理】——带你认识IP~(长文~实在不知道取啥标题了)
IP协议详解,IP协议管理地址(NAT机制),IP地址分类、组成、特殊IP地址,MAC地址,数据帧格式,DNS域名解析系统
|
2天前
|
存储 JSON 缓存
【网络原理】——HTTP请求头中的属性
HTTP请求头,HOST、Content-Agent、Content-Type、User-Agent、Referer、Cookie。
|
2天前
|
安全 算法 网络协议
【网络原理】——图解HTTPS如何加密(通俗简单易懂)
HTTPS加密过程,明文,密文,密钥,对称加密,非对称加密,公钥和私钥,证书加密
|
2天前
|
XML JSON 网络协议
【网络原理】——拥塞控制,延时/捎带应答,面向字节流,异常情况
拥塞控制,延时应答,捎带应答,面向字节流(粘包问题),异常情况(心跳包)
|
18天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
161 77
|
27天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
5天前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
45 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
|
11天前
|
关系型数据库 应用服务中间件 PHP
实战~如何组织一个多容器项目docker-compose
本文介绍了如何使用Docker搭建Nginx、PHP和MySQL的环境。首先启动Nginx容器并查看IP地址,接着启动Alpine容器并安装curl测试连通性。通过`--link`方式或`docker-compose`配置文件实现服务间的通信。最后展示了Nginx配置文件和PHP代码示例,验证了各服务的正常运行。
32 3
实战~如何组织一个多容器项目docker-compose
|
20天前
|
数据建模 应用服务中间件 nginx
docker替换宿主与容器的映射端口和文件路径
通过正确配置 Docker 的端口和文件路径映射,可以有效地管理容器化应用程序,确保其高效运行和数据持久性。在生产环境中,动态替换映射配置有助于灵活应对各种需求变化。以上方法和步骤提供了一种可靠且易于操作的方案,帮助您轻松管理 Docker 容器的端口和路径映射。
63 3