iptables的数据包的流程介绍

参考:https://www.linuxso.com/linuxpeixun/10330.html

iptables的数据包的流程介绍

iptables 相关概念

匹配(match):符合指定的条件,比如指定的 IP 地址和端口。

丢弃(drop):当一个包到达时,简单地丢弃,不做其它任何处理。

接受(accept):和丢弃相反,接受这个包,让这个包通过。

拒绝(reject):和丢弃相似,但它还会向发送这个包的源主机发送错误消息。这个错误消息可以指定,也可以自动产生。

目标(target):指定的动作,说明如何处理一个包,比如:丢弃,接受,或拒绝。

跳转(jump):和目标类似,不过它指定的不是一个具体的动作,而是另一个链,表示要跳转到那个链上。

规则(rule):一个或多个匹配及其对应的目标。

链(chain):每条链都包含有一系列的规则,这些规则会被依次应用到每个遍历该链的数据包上。每个链都有各自专门的用途, 这一点我们下面会详细讨论。

表(table):每个表包含有若干个不同的链,比如 filter 表默认包含有 INPUT,FORWARD,OUTPUT 三个链。iptables 有四个表,分别是:raw,nat,mangle和filter,每个表都有自己专门的用处,比如最常用filter表就是专门用来做包过滤的,而 nat 表是专门用来做NAT的。

策略(police):我们在这里提到的策略是指,对于 iptables 中某条链,当所有规则都匹配不成功时其默认的处理动作。

连接跟踪(connection track):又称为动态过滤,可以根据指定连接的状态进行一些适当的过滤,是一个很强大的功能,但同时也比较消耗内存资源。

经过iptables的数据包的流程介绍

一个数据包到达时,是怎么依次穿过各个链和表的(图)。

iptables

基本步骤如下:

  1. 数据包到达网络接口,比如 eth0。

  2. 进入 raw 表的 PREROUTING 链,这个链的作用是赶在连接跟踪之前处理数据包。

  3. 如果进行了连接跟踪,在此处理。

  4. 进入 mangle 表的 PREROUTING 链,在此可以修改数据包,比如 TOS 等。

  5. 进入 nat 表的 PREROUTING 链,可以在此做DNAT,但不要做过滤。

  6. 决定路由,看是交给本地主机还是转发给其它主机。

到了这里我们就得分两种不同的情况进行讨论了,一种情况就是数据包要转发给其它主机,这时候它会依次经过:

  1. 进入 mangle 表的 FORWARD 链,这里也比较特殊,这是在第一次路由决定之后,在进行最后的路由决定之前,我们仍然可以对数据包进行某些修改。

  2. 进入 filter 表的 FORWARD 链,在这里我们可以对所有转发的数据包进行过滤。需要注意的是:经过这里的数据包是转发的,方向是双向的。

  3. 进入 mangle 表的 POSTROUTING 链,到这里已经做完了所有的路由决定,但数据包仍然在本地主机,我们还可以进行某些修改。

  4. 进入 nat 表的 POSTROUTING 链,在这里一般都是用来做 SNAT,不要在这里进行过滤。

2023-02-19    
linux cat

利用cat 给文件写内容,追加的方式

cat >> proxy.sh <<EOF
export http_proxy=http://99.0.85.1:808
export https_proxy=http://99.0.85.1:808
EOF

cat给文件写内容,覆盖的方式

cat > proxy.sh <<EOF
export http_proxy=http://99.0.85.1:808
export https_proxy=http://99.0.85.1:808
EOF
2023-02-03    
sed替换命令收集

普通操作可以使用冒号(:)井号(#)正斜杠(/)来作为分隔符

sed -i 's#abc#def#g'  a.file  #将文件a.file中的abc替换成def
sed -i 's/^abc.*/abc=def/' a.file # 将a.file中以abc开头的一行替换成abc=def
sed -i '/ABC/,$d' a.file # 将a.file中从ABC开始(包括ABC)以后的所有行删除
sed -i '$a aabbccdd' a.file # 给a.file追加aabbccdd
cat geng.file | sed  's/abc/def/g'   ## 打印文件geng,并将其中的abc替换成def

参考:https://blog.csdn.net/genghongsheng/article/details/120432010

2023-01-30    
超时控制下执行函数

go中实现超时控制下执行函数功能

func RunWithTimeout(fun func() error, timeout time.Duration) error {
	finished := make(chan struct{})
	var err error
	go func() {
		err = fun()
		finished <- struct{}{}
	}()

	select {
	case <-finished:
		return err
	case <-time.After(timeout):
		return fmt.Errorf("timeout")
	}
}
2023-01-30    
git远端删除.idea等本地文件夹

如果不配置.gitignore的文件,带push代码的时候就会把一写不必要的文件push到远程仓库,如.idea文件。如果不小心出现此文件在远程仓库可以通过一下步骤delete此文件:

1.配置.gitignore文件(新建/编辑)

vim .gitignore-->i--->添加.idea--->esc--->:wq

# 将.gitignore文件上传到远程仓库

git pull

git add .gitignore

git commit -m 'edit .gitignore'

git push origin master

3.删除git的.idea文件

git rm --cached -r .idea

4.同步到远程仓库

git commit -m 'delete .idea'
git push origin master

完成之后就可以发现git仓库中的.idea文件已经被删除,而且之后push代码也不会再把.idea文件上传

2023-01-01    
你好2023

你好2023

2022-12-31    
cgroup

参考:https://zhuanlan.zhihu.com/p/434731896、https://www.cnblogs.com/zhrx/p/16388175.html

cgroup

目前我们所提到的容器技术、虚拟化技术(不论何种抽象层次下的虚拟化技术)都能做到资源层面上的隔离和限制。

对于容器技术而言,它实现资源层面上的限制和隔离,依赖于linux内核所提供的cgroup和namespace技术。

两项技术的概括

  • cgroup主要作用:管理资源的分配、限制;
  • namespace的主要作用:封装抽象,限制,隔离,使命名空间内的进程开起来拥有他们自己的全局资源;

cgroup是Linux内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘、输入输出等)。

cgroup需要限制的资源是:

  • CPU
  • 内存
  • 网络
  • 磁盘I/O

作用:

  • 资源限制:可以配置cgroup,从而限制进程可以对特性资源的使用量
  • 优先级:当资源发生冲突时,可以控制一个进程相比另一个cgroup中的进程可以使用的资源量(CPU、磁盘或网络等)
  • 记录:在cgroup级别监控和报告资源限制
  • 控制:可以使用单个命令更改cgroup中所有进程的状态(冻结、停止或重新启动)

依赖的四个核心概念

  • 子系统
  • 控制组
  • 层技树
  • 任务

控制组(group)

表示一组进程和一组带有参数的子系统的关联关系。例如,一个进程使用了CPU子系统来限制CPU的使用时间,则这个进程和CPU子系统的关联关系称为控制组。

层级树

有 一系列的控制组按照树状结构排列组成的。这种排列方式可以使得控制组拥有父子关系,子控制组默认拥有父控制组的属性,也就是子控制组会继承父控制组。

比如,系统中定义了一组控制组c1,限制了CPU可以使用1核,然后另一个控制组c2想实现既限制CPU使用1核,同时限制内存使用2G,那么c2就可以直接继承c1,无需重复定义CPU限制。

子系统

一个内核的组件,一个系统代表一类资源调度控制器。例如内存子系统可以限制内存的使用量,CPU子系统可以限制CPU的使用时间。

子系统是真正实现某类资源的限制的基础。

subsystems(子系统)cgroups中的子系统就是一个资源调度控制器(又叫controllers)。 在/sys/fs/cgroup/这个目录下可以看到cgroup子系统

[root@centos ~]# ll /sys/fs/cgroup/
total 0
dr-xr-xr-x 5 root root  0 Oct 23 06:38 blkio # 为块设备设定输入输出限制,比如物理驱动设备
lrwxrwxrwx 1 root root 11 Oct 23 06:38 cpu -> cpu,cpuacct # 使用调度控制程序控制对CPU的使用
lrwxrwxrwx 1 root root 11 Oct 23 06:38 cpuacct -> cpu,cpuacct # 自动生成cgroup中任务对cpu资源使用情况的报告
dr-xr-xr-x 3 root root  0 Oct 23 06:38 cpuset # 可以为cgroup中的任务分配独立的cpu和内存
dr-xr-xr-x 5 root root  0 Oct 23 06:38 devices # 可以开启或关闭cgroup中任务对设备的访问
dr-xr-xr-x 3 root root  0 Oct 23 06:38 freezer # 可以挂起或恢复cgroup中的任务
dr-xr-xr-x 3 root root  0 Oct 23 06:38 hugetlb
dr-xr-xr-x 6 root root  0 Oct 23 06:38 memory # 可以设定cgroup中任务对内存使用量的限定,并且自动生成这些任务对内存资源使用情况的报告
lrwxrwxrwx 1 root root 16 Oct 23 06:38 net_cls -> net_cls,net_prio # docker没有直接使用它,它通过使用等级识别符标记网络数据包,从而允许linux流量控制程序识别从具体cgroup中生成的数据包
lrwxrwxrwx 1 root root 16 Oct 23 06:38 net_prio -> net_cls,net_prio
dr-xr-xr-x 3 root root  0 Oct 23 06:38 perf_event # 使用后使cgroup中的任务可以进行统一的性能测试
dr-xr-xr-x 5 root root  0 Oct 23 06:38 pids # 限制任务数量
dr-xr-xr-x 3 root root  0 Oct 23 06:38 rdma
dr-xr-xr-x 6 root root  0 Oct 23 06:38 systemd

这篇文章好强,可以抽空研究复现下:https://www.cnblogs.com/zhrx/p/16388175.html

2022-12-10    
CNI插件

参考文章:https://developer.aliyun.com/learning/course/572/detail/7866

如何开发自己的CNI插件

CNI插件的实现通常包含两个部分:

  1. 一个二进制的CNI插件去配置Pod网卡和IP地址。这一步配置完成后相当于给Pod插上了一条网线:有了自己的IP、自己的网卡;
  2. 一个Daemon进程去管理Pod之间的网络打通。这一步相当于将Pod真正连上网络,让Pod之间能够互通。

给Pod插上网线

  1. 给Pod准备虚拟网卡
    • 创建“veth”虚拟网卡对
    • 将一端的网卡挪到Pod中
  2. 给Pod分配IP地址
    • 给Pod分配集群中唯一的IP地址
    • 一般把Pod网段按Node分段
    • 每个Pod再从Node段中分配IP
  3. 配置Pod的IP和路由
    • 给Pod的虚拟网卡网址分配到的IP
    • 给Pod的网卡上配置集群网段的路由
    • 在宿主机上配置到Pod的IP地址的路由到对端虚拟网卡上

给Pod连上网络

刚才是给Pod插上网线,也就是说分配了IP地址和路由表。接下来说明怎么让每一个Pod的IP地址在集群里都能被访问到。

一般是在CNI的daemon进程中去做这些网络打通的事情。

  • 首先CNI在每个节点上运行的daemon进程会学习到集群所有Pod的IP地址及其所在节点的信息。学习的方式通过监听K8s APIserver,拿到现有Pod的IP地址以及节点,并且新的节点和新的Pod在创建的时候也能通知到每个daemon;
  • 拿到Pod以及Node相关信息后,再去配置网络进行打通。
    • 首先daemon回创建到整个集群所有节点的通道。这里的通道是个抽象的概念, 具体实现一般是通过overlay隧道等。
    • 第二部是将所有Pod的IP地址跟上一步创建的通道关联起来。关联也是个抽象的概念,具体实现通常是通过linux路由、fdb转发表或者ovs流表完成的。
2022-12-10    
CRI

参考文章:https://zhuanlan.zhihu.com/p/102897620

CRI是什么

CRI: Container Runtime Interface,容器运行时接口;

CRI包括Protocol Buffers、gRPC API、运行库支持及开发中的标准规范和工具,是以容器为中心设计的API,设计CRI的初衷是不希望向容器(比如docker)暴露pod信息或pod的api。

CRI工作在kubelet与container runtime之间,目前常见的runtime有:

  • docker
  • containerd: 可以通过shim对接不同的low-level runtime
  • cri-o:一种轻量级的runtime,支持runc和Clear container作为low-level runtimes。

CRI是如何工作的

CRI大体包含三部分接口:Sandbox、Container和Image,其中提供了一些操作容器的通用接口,包括Create、Delete、List等。

Sanbox为container提供一定的运行环境,这其中包括pod的网络等。Container包括容器生命周期的具体操作,Image则提供对镜像的操作。

kubelet回通过gRPC调用CRI接口,首先去创建一个环境,也就是所谓的PodSandbox。当Podsandbox可用后,继续调用image或container接口去拉取镜像和创建容器。

PodSandbox

从虚拟机和容器化两方面看,两者都是用了cgroups做资源配额,而且概念上都抽离出一个隔离的运行时环境,只是区别在于资源隔离的实现。 因此sandbox是k8s为兼容不同运行时环境所预留的空间,也就是说k8s允许low-level runtime依据不同的实现去创建不同的podsandbox,对于kata来说podsandbox就是虚拟机,对于docker来说就是linxe namespace。 当pod sandbox建立起来后,kubelet就可以在里面创建用户容器。当删除pod时,kubelet会先移除pod sandbox然后再停止里面的所有容器,对于container来说,当sandbox运行后,只需将新的container的namespace加入到已有的sandbox的 namespace中。

在默认情况下,cri体系里,pod sandbox其实就是pause容器。kubelet代码引用的defaultSandboxImage就是官方提供的gcr.io/google_containers/pause-amd64 镜像

2022-12-10    
复原 IP 地址

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

原题链接复原 IP 地址

var res []string

func restoreIpAddresses(s string) []string {
    res = make([]string, 0)
    path := make([]string, 0)
    tracingBack(s, 0, path)
    return res
}


func tracingBack(s string, startIndex int, path []string){
    if startIndex >= len(s) && len(path) == 4{
        // 遍历结束且候选长度为4,则为一种解法
        res = append(res, strings.Join(path, ".")) 
        return
    }
    for index:= startIndex+1; index < len(s)+1; index++{
        if isValidIpAddress(s[startIndex:index]){
            // 该子序列为有效的序列,则加入候选进行遍历
            path = append(path, s[startIndex:index])
            tracingBack(s, index, path)
        }
        if len(path) > 0{
            // case 1: 本次遍历的子序列为无效子序列,需将上一次分割结果抛弃
            // case 2: 上一次分割结束,已经产生一种可能结果,需将上一次分割结果抛弃,开始回溯下一种解法

            // 不管上次结果如何,需进行回溯,抛弃上一次的结果。
            path = path[0:len(path)-1]
        }
    }
}

func isValidIpAddress(s string)bool{
    a, _ := strconv.Atoi(s)
    if a <= 255 && strconv.Itoa(a) == s{
        return true
    }
    return false
}
2022-12-05