从kube-ovn中vpc网关EIP到Netfilter Hook优先级探讨
前言 博主近期工作上需要和kube-ovn打交道,后续可能会涉及到基于kube-ovn的二开,因此需要详细了解其特性。昨天在工作中接到了一个关于k8s平台中容器部署的虚拟机中,先ping着外网,然后绑定EIP成功后还是不通,需要把ping关了重新ping才生效,解绑EIP也是如此的问题分析。 VPC kube-ovn中关于VPC的设计,引用原文: VPC 主要用于有多租户网络强隔离的场景,部分 Kubernetes 网络功能在多租户网络下存在冲突。 例如节点和 Pod 互访,NodePort 功能,基于网络访问的健康检查和 DNS 能力在多租户网络场景暂不支持。 为了方便常见 Kubernetes 的使用场景,Kube-OVN 默认 VPC 做了特殊设计,该 VPC 下的 Subnet 可以满足 Kubernetes 规范。用户自定义 VPC 支持本文档介绍的静态路由,EIP 和 NAT 网关等功能。 常见隔离需求可通过默认 VPC 下的网络策略和子网 ACL 实现,在使用自定义 VPC 前请明确是否需要 VPC 级别的隔离,并了解自定义 VPC 下的限制。 在 Underlay 网络下,物理交换机负责数据面转发,VPC 无法对 Underlay 子网进行隔离。 可见kube-ovn中VPC的设计是针对Overlay网络的多租户隔离需求,像单租户下的Overlay网络、Underlay网络分别可以通过k8s中的网络策略、Underlay的Vlan实现隔离需求。 自定义VPC主要是围绕一个VPC网关实现EIP、自定义路由、自定义内部负载均衡、自定义vpc-dns等功能,本次主要设计其EIP功能,拓扑架构如图: VPC Nat Gateway 从具体的CR资源来看其对应关系: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 [root@cd56 ~]# kubectl get vpc-nat-gateways....
Linux 内核数据流概览初探
前言 博主从事云计算行业已经近两年半了,其中接触了近一年半的容器网络,愈发觉得这就是我目前的大体的职业方向。建立该站的初心是希望自己能够对目前接触到的专业知识有进一步的理解掌握,对今后工作中将要接触到的知识理解透彻,逐渐补全自身。废话不多说,网络的话题很大,本篇文章想从Linux内核的网络数据流开始聊起,博主并不擅长C语言,本次归纳只是一个简单的流程梳理,至于其中细节就待今后用到了再细细研究。 内核发送数据包到网络设备的流程 大体流程如图所示: 用户进程调用send方法开始发送数据,最终会调用到系统调用__sys_sendto 方法。在该方法中: 会根据传入的文件描述符查找内核中的socket对象。 将用户空间的待发送数据缓冲区导入到内核空间,并以此构建出msghdr对象。 调用socket_sendmsg,最终选择合适的发送函数(inet6_sendmsg或inet_sendmsg)并执行,进入内核协议栈处理。 在进入到内核协议栈之后, 找到Socket上的具体协议的发送函数,在该发送函数中会创建数据结构sk_buffer,将msghdr对象的数据拷贝到之中,并调用tcp_write_queue_tail函数获取Socket发送队列中的队尾元素,将新创建的sk_buffer添加到【内核socket对象的发送队列尾部】。(如果此时不满足发送条件【比如未发送数据没有达到TCP滑动时间窗口的一半】,则用户进程将数据拷贝到【socket的发送队列】里他的工作就算做完了,此时【内核socket对象的发送队列】里的数据会等到合适的时机进行发送) 根据协议类型(TCP或UDP),间接调用对应的sendmsg函数发送数据,tcp_sendmsg、udp_sendmsg。 调用传输层方法, 拷贝并使用sk_buffer副本,为丢包重传做服务; 并设置好tcp头部,窗口大小设置; 然后通过ip_queue_xmit发送出去; 调用网络层方法, 从本机路由表查询路由; 构建数据报的IP头信息; 调用网络过滤器netfilter 进行进一步处理; 根据MTU判断是否分片处理; 调用neigh_output发送; 调用到邻居子系统, 发送APR请求获取目标Mac地址,然后将sk_buffer中的指针移动到MAC头位置,填充MAC头。 此时的sk_buffer已经是一个完整的以太网数据帧,进入【网络设备子系统】处理。 调用到网络设备子系统, __dev_queue_xmit,初始化qdisc队列排队规则,获取网络设备的传输队列txq,选择其中一个将skb放入。这一步有可能会中断(比如用户线程内核态CPU时间用尽),后续由响应该软中断的【内核ksoftirqd线程】来调用dev_hard_start_xmit方法继续执行。 dev_hard_start_xmit,遍历网络设备传输队列进行发送数据。对于通过回环设备发送的数据,直接进入回环设备的”驱动”里的发送回调函数 loopback_xmit。对于其他设备则: 调用到网卡驱动程序, igb_xmit_frame,在RingBuffer中的数组里选取可用的位置,关联该位置和skb。然后映射数据包到DMA描述符,【网卡驱动程序】通过DMA方式将数据通过【物理网卡】发送出去。 igb_msix_ring,网卡在发送完毕的时候,会给 CPU 发送一个硬中断来通知 CPU。收到这个硬中断后会调用网卡驱动注册的硬中断回调函数触发NET_RX_SOFTIRQ类型的软中断。从而释放skb、清理RingBuffer。 内核接收数据包流程 大体流程如下: 当网卡收到数据以后,CPU发起一个中断,以通知CPU有数据到达。当CPU收到中断请求后,会去调用网络驱动注册的中断处理函数,触发软中断。ksoftirqd 检测到有软中断请求到达,开始轮询收包,收到后交由各级协议栈处理。当协议栈处理完并把数据放到接收队列的之后,唤醒用户进程。 写在最后 本文作为第一篇文章,简单梳理了一下内核发送、接收数据包的大体流程。今后会根据需要逐渐完善其中每个步骤的详细流程,加油吧~