[TOC]
### **官方文档**
![](https://img.kancloud.cn/b0/bd/b0bd5e445711e3fb83ae965303241dc4_1311x376.png)
### **详细解释**
`MARK`这个扩展目标可以用来给数据包(Packet)打标记,由于连接追踪(Conntrack)也有标记,为了区分,约定把数据包的标记叫`nfmark`,把连接的标记叫`ctmark`,这一节我们讲的标记就是`nfmark`。
`nfmark`占四个字节32位,我们可以把它看成是一个长度为32位的无符号整数,一般用16进制来表示。
该目标有可以通过上面的五个选项中的某一个来设置数据包的标记,接下来我们来介绍一下。
* `--set-xmark value[/mask]`
上面的`value`和掩码`mask`都是32位无符号整数,一般用16进制表示。内核设置数据包nfmark值的流程分为两步:(1)首先,内核会先用mask预处理数据包原来的nfmark,处理方法是:如果mask的第N位(二进制)为1,那么将数据包的nfmark第的N位(二进制)设置为0 ,nfmark其他的位保持不变(2)接着,再用上面预处理后的nfmark和value做异或操作,得到数据包最后的nfmark值。
举个例子:假设我们设置了`--set-xmark 0x4000/0xffffffff`,掩码为`0xffffffff`,掩码表示为二进制的话32位都为`1`,那么内核首先会将数据包原来的`nfmark`所有的位都设置为`0`(相当于是先把nfmark置0),然后再和value做异或操作,那么得到的最后的nfmark值就是`0x4000`。所以,数据包经过这条规则后,它的nfmark值就是`0x4000`。
上面的掩码`mask`是个可选项,如果没有设置的话,默认为`0xffffffff`。
通过`--set-xmark value`可以快速设置数据包的nfmark值为`value`,可以自己推导一下(`0 XOR value = value`)
* `--set-mark value[/mask]`
该设置方法上面类似,也是两步。第一步预处理和上面一样,第二步不同中,该方法是将预处理的nfmark和value做或(OR)操作。
通过`--set-mark value`可以快速设置数据包的nfmark值为`value`,可以自己推导一下(`0 OR value = value`)。
* `--and-mark bits`
`bits`相当于就是上面的`value`,也是32位,16进制表示。该选项的意思就是:直接拿`bits`和数据包原来的nfmark做与(AND)操作,得到数据包新的nfmark。
它的效果和`--set-xmark 0/invbits`的效果是一样的,我们可以来推导一下。
`invbits`就是把`bits`中的位反过来,`1`的位变为`0`,`0`的位变为`1`。假设`bits`有n位为1,分别为第`X1,...,Xn`位,那么`--and-mark bits`的效果就是,**nfmark的第`X1,...,Xn`位保持不变,其他位都变为0**。然后再来看`--set-xmark 0/invbits`的效果:`invbits`的第`X1,...,Xn`位都为0,其他位都为1,那么nfmark经过`invbits`的预处理后,**nfmark的第`X1,...,Xn`位都保持不变,其他位都变成了0**,然后再与0做异或操作,而0与任何值做异或都是该值本身,所以nfmark的最终值就是经过invbits预处理之后的值,和`--and-mark bits`的效果是一样的。
* `--or-mark bits`
该选项的意思就是:直接拿`bits`和数据包原来的nfmark做或(OR)操作,得到数据包新的nfmark。
它的效果和`--set-xmark bits/bits`是一样的,可自己推导一下
* `--xor-mark bits`
该选项的意思就是:直接拿`bits`和数据包原来的nfmark做异或(XOR)操作,得到数据包新的nfmark。
它的效果和`--set-xmark bits/0`是一样的,可自己推导一下
### **Terminating Or Non-Terminating**
Non-Terminating
### **示例1**
上面我们有五种方法来设置数据包的nfmark。比如说,我们添加如下一条规则,把源地址为1.1.1.1的数据包的nfmark值设置为`0x1`,然后查看这条规则
```
$ iptables -t mangle -A PREROUTING -s 1.1.1.1 -j MARK --set-mark 0x1
$ iptables -t mangle -nL PREROUTING
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
...
MARK all -- 1.1.1.1 0.0.0.0/0 MARK set 0x1
```
`--set-mark 0x1`的效果就是不管原来的nfmark,直接把nfmark重新设置为`0x1`;所以我们在查看这条规则时,显示的`MARK set 0x1`就是这个意思。
接着,我们再添加如下一条规则,然后再查看
```
$ iptables -t mangle -A PREROUTING -s 1.1.1.2 -j MARK --set-xmark 0x1/0x1
$ iptables -t mangle -nL PREROUTING
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
...
MARK all -- 1.1.1.2 0.0.0.0/0 MARK or 0x1
```
我们知道,`--set-xmark bits/bits`的效果和`--or-mark bits`是一样的,所在我们通过`--set-xmark 0x1/0x1`去设置nfmark,只不过显示的时候是`or 0x1`。
### **示例2**
* 问题
在k8s中,当我们发布一个Pod和一个Service,然后这个从这个Pod中通过CusterIP去访问这个Service,那么请求最终会转发到这个Pod本身。假设Pod的IP为`172.26.190.194`,Pod内服务的监听端口为`80`,ClusterIP为`10.110.161.77`,ServicePort为`80`,当Pod发出这个请求时,数据包四元组为`172.26.0.22:40000 -> 10.110.161.77:80`,那么**期望的回复包应该是`10.110.161.77:80 -> 172.26.0.22:40000`**。当Pod接收到这个请求时,数据包的四元组是`SourceIP:SourcePort -> 172.26.190.194:80`。那么**它的回复包是`172.26.190.194:80 -> SourceIP:SourcePort`**,很明显,实际的回复包与期望的回复包的源地址不一样。
那么问题来了,上面的`SourceIP:SourcePort`是什么?实际回复包`172.26.190.194:80 -> SourceIP:SourcePort`是如何转换成期望的回复包`10.110.161.77:80 -> 172.26.0.22:40000`的?
* 验证
我们知道,当从pod里面访问clusterip时,数据包从Pod出来后,会进入到Pod所在的主机,那么此时会经过主机的PREROUTING链,通过查看我们发现在NAT表的PREROUTING链通过引用`KUBE-SERVICES`链最终会引用到如下一条链,该链有如下两条规则:
```
$ iptables -t nat -nL KUBE-SEP-RFYOGXIZRFRZCFLS
Chain KUBE-SEP-RFYOGXIZRFRZCFLS (1 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 172.26.190.195 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to: random persistent random persistent
```
第一条规则的意思是做如果源地址是Pod的IP,就转到`KUBE-MARK-MASQ`链,我们来看一下这条链的内容,如下:
```
$ iptables -t nat -nL KUBE-MARK-MASQ
Chain KUBE-MARK-MASQ (8 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
```
可以看出,这条链对数据包进行了MARK:`--or-mark 0x4000`。这个动作的意思是:用数据包原来的nfmark值和`0x4000`进行或操作,那么效果就是nfmark的第18位(二进制从左到右)被设置为了1。这里可以提前说一下,在后面判断的时候,其实就是根据这一位是否为1来判断是否要做Masqurade。
然后我再看DNAT那条规则,它的意思就是做DNAT转换,把
### **参考**
* https://ipset.netfilter.org/iptables-extensions.man.html#lbDD
- 常用命令
- 用户与用户组
- 创建用户与用户组
- 快速脚本
- umask
- Yum源
- 基础Yum源
- Epel源
- 制作Yum源
- 同步Yum源
- 为Yum源配置代理
- 下载RPM及依赖
- 系统与内核
- 获取内核的rpm包
- 升级内核
- Iptables
- 基本语法
- 匹配条件
- 基础匹配条件
- 扩展匹配条件
- Addrtype
- Set
- TCP
- Mark
- Multiport
- 目标
- 基本目标
- 扩展目标
- DNAT
- LOG
- CT
- NOTRACK
- MARK
- IP set
- 连接追踪
- 初识连接追踪
- 连接追踪详解
- NAT
- 思路与参考汇总
- 数据结构
- FAQ
- Keepalived
- 单网卡多VIP
- 安装Keepalived
- 双网卡绑VIP
- 别名VIP和辅助VIP
- LVS
- 安装LVS
- Ipvsadm命令
- 磁盘与分区
- 基础知识
- 创建分区
- 格式化与挂载
- Fstab
- LVM
- LVM扩容
- Swap分区
- Tmpfs
- 网络相关
- 重命名网卡
- resolv.conf
- Tcpdump
- nslookup与dig
- ifcg-xxx
- 主机名
- 软件安装
- NFS
- Squid
- Redsocks
- Shadowsocks
- 时钟同步
- Chrony
- FTP
- 文件句柄
- 简介
- 设置文件句柄
- 其他
- SSH密钥登录
- 进程组-会话-终端
- X11转发
- 环境变量
- 常见问题
- 系统进程数
- 系统调用
- 系统调用FAQ
- 用户程序如何进行系统调用