DNAT创建的iptables规则如下:(重写目的IP和端口)

PREROUTING, OUTPUT: --dst-type local -j CNI-HOSTPORT_DNAT    // PREROUTING和OUTPUT链中目的地址类型为local的跳转至CNI-HOSTPORT-DNAT进行处理

CNI-HOSTPORT-DNAT: -j CNI-DN-abcd123    // 进入相应容器的chain进行处理

CNI-DN-abcd123: -p tcp --dport 8080 -j DNAT --to-destination 192.0.2.33:80

CNI-DN-abcd123: -p tcp --dport 8081 -j DNAT ....

SNAT创建的iptables规则如下:(在dnat之后,重写来自于localhost的源地址)

POSTROUTING:-s 127.0.0.1 ! -d 127.0.0.1 -j CNI-HOSTPORT-SNAT

CNI-HOSTPORT-SNAT:-j CNI-SN-abcd123

CNI-SN-abcd123:-p tcp -s 127.0.0.1 -d 192.0.2.33 --dport 80 -j MASQUERADE

CNI-SN-abcd123:-p tcp -s 127.0.0.1 -d 192.0.2.33 --dport 90 -j MASQUERADE

// plugins/plugins/meta/portmap/main.go

1、func cmdAdd(args *skel.CmdArgs) error

1、首先调用netConf, err := parseConfig(args.StdinData, args.IfName)对配置进行解析

2、因为portmap只能作为chained plugin存在,因此当netConf.PrevResult为nil时,报错

3、当len(netConf.RuntimeConfig.PortMaps)为0时,说明没有配置portmap,直接返回PrevResult

4、当netConf.ConfIPv4不为nil时,调用forwardPorts(netConf, netConf.ContIPv4)

5、当netConf.ConfIPv6不为nil时,调用forwardPorts(netConf, netConf.ConfIPv6)

PortMapConf的结构如下所示:

type PortMapConf struct {types.NetConfSNAT  *boolConditionsV4 *[]stringConditions V6 *[]stringRuntimeConfig struct {PortMaps  [ ]PortMapEntry}RawPrevResult map[string]interface{}PrevResult  *current.Result// These are fields parsed out of the config or the environment;// included here for convenienceContainerID  stringContIPv4    net.IPContIPv6     net.IP
}

PortMapEntry的结构如下所示:

type PortMapEntry struct {HostPort     intContainerPort  intProtocol      stringHostIP      string
}

  

// plugins/plugins/meta/portmap/main.go

2、func parseConfig(stdin []byte, ifName string) (*PortMapConf, error)

1、首先创建conf := PortMapConf{},并调用json.Unmarshal(stdin, &conf)进行解析

2、如果conf.RawPrevResult不为nil,则先将其转换为conf.PrevResult

3、如果解析之后conf.SNAT为nil,则设置conf.SNAT赋值为true

4、遍历conf.RuntimeConfig.PortMaps,对每个pm进行检测,如果pm.ContainerPort <= 0或者pm.HostPort<=0,则报错

5、如果conf.PrevResult不为nil,则对conf.PrevResult.IPs进行遍历,将conf.ContIPv4和conf.ContIPv6分别设置为第一个遇到的container interface的地址

// plugins/plugins/meta/portmap/portmap.go

3、func forwardPorts(config *PortMapConf, containerIP net.IP) error

1、当containerIP为IPv4时,调用ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)和conditions = config.ConditionsV4获取iptable

2、调用toplevelDnatChain := genToplevelDnatChain()和toplevelDnatChain.setup(ipt, nil)创建top level chain,

3、调用dnatChain := genDnatChain(config.Name, config.ContainerID, conditions)创建dnat chain,并调用_ = dnatChain.teardown(ipt)进行清理,如果有冲突的话

4、调用dnatRules := dnatRules(config.RuntimeConfig.PortMaps, containerIP)和dnatChain.setup(ipt, dnatRules)在dnat chain中创建规则

5、如果config.SNAT为真,且不是地址不是IPv6,则首先调用toplevelSnatChain := genToplevelSnatChain(isV6)和toplevelSnatChain.setup(ipt, nil)创建top level snat chain

6、调用snatChain := genSnatChain(config.Name, config.ContainerID)和_ = snatChain.teardown(ipt)获取相应的snat chain,如果chain已存在,则进行清理

7、调用snatRules := snatRules(config.RuntimeConfig.PortMaps, containerIP)和snatChain.setup(ipt, snatRules)创建规则

8、如果isV6为false,则设置host interface的route_localnet bit,从而让127/8能cross a routing boundary,先调用hostIfName := getRoutableHostIF(containerIP)

如果hostIfName不为"",则调用enableLocalnetRouting(hostIfName)

// plugins/plugins/meta/portmap/portmap.go

4、func genToplevelDnatChain() chain

该函数仅仅返回一个chain{}结构,该chain为top-levle summary chain,其他所有的dnat chain都通过它进行调用,具体代码如下:

return chain{table:  "nat",name:  TopLevelDNATChainName,entryRule:  [ ]string{"-m", "addrtype","--dst-type", "LOCAL",},entryChains: []string{"PREROUTING", "OUTPUT"},}

  

// plugins/plugins/meta/portmap/chain.go

5、func (c *chain) setup(ipt *iptables.IPTables, rules [][]string) error

1、调用exists, err := chainExists(ipt, c.table, c.name)判断chain是否存在,不在则调用ipt.NewChain(c.table, c.name)进行创建

2、倒序遍历rules,调用prependUnique(ipt, c.table, c.name, rules[i])将rule添加至chain中

3、调用entryRule := append(c.entryRule, "-j", c.name),并遍历c.entryChains,调用prependUnique(ipt, c.table, entryChain, entryRule)将rule加入其他chain中

// plugins/plugins/meta/portmap/chain.go

6、func genDnatChain(netName, containerID string, conditions *[]string) chain

1、首先调用name := formatChainName("DN-", netName, containerID)创建新的dnat chain名字

2、调用comment := fmt.Sprintf(...)创建comment

3、创建的chain如下所示:

ch := chain{table:  "nat",name:  name,entryRule:  []string{"-m", "comment","--comment", comment},entryChains:  []string{TopLevelDNATChainName},
}

4、如果conditions不为nil,且len(*conditions)不为0,则调用ch.entryRule = append(ch.entryRule, *conditions...)

// plugins/plugins/meta/portmap/chain.go

7、func (c *chain) teardown(ipt *iptables.IPTables) error

teardown用于删除一个chain,如果chain不存在的话并不会报错,并且它先删除所有entryChains对该chain的引用

1、首先调用ipt.ClearChain(c.table, c.name),如果该chain不存在的话,创建该chain,如果存在,则先对该chain进行flush

2、遍历c.entryChains,调用entryChainRules, err := ipt.List(c.table, entryChain)获取该chain上的所有rule

3、遍历entryChainRules[1:],当entryChainRule中存在"-j"+c.name的后缀时,调用chainParts, err := shellwords.Parse(entryChainRule)和chainParts = chainParts[2:]获取rule的内容

再调用ipt.Delete(c.table, entryChain, chainParts...)删除对应的reference

4、最后调用ipt.DeleteChain(c.table, c.name)删除该chain

// plugins/plugins/meta/portmap/portmap.go

8、func dnatRules(entries []PortMapEntry, containerIP net.IP) [ ][ ]string

1、遍历entries,添加的iptables规则为"-p entry.Protocol --dport strconv.Itoa(entry.HostPort) -j DNAT --to-destination fmtIpPort(containerIP, entry.ContainerPort)"

如果entry.HostIP不为"",则进一步扩展"-d entry.HostIP"

// plugins/plugins/meta/portmap/portmap.go

9、func genToplevelSnatChain(isV6 bool) chain

1、创建的top level chain如下所示:

return chain {table: "nat",name: TopLevelSNATChainName,entryRule: [ ]string {"-s", localhostIP(isV6),      // localhostIP函数,如果isV6为true的话,则返回"::1",否则返回"127.0.0.1""!", "-d", localhostIP(isV6),},entryChains: [ ]string{"POSTROUTING"},}

  

// plugins/plugins/meta/portmap/portmap.go

10、func genSnatChain(netName, containerID string) chain

1、首先调用name := formatChainName("SN-", netName, containerID)创建chain的名字

2、调用comment := fmt.Sprintf(...)创建comment

3、最后返回的chain如下:

return chain {table: "nat"name: name,entryChains: [ ]string {"-m", "comment","--comment", comment},entryChains: [ ]string{TopLevelSNATChainName}}

  

// plugins/plugins/meta/portmap/portmap.go

11、func snatRules(entries [ ]PortMapEntry, containerIP net.IP) [ ][ ]string

1、遍历entries,新建的iptable的rule为"-p entry.Protocol -s localhostIP(isV6) -d containerIP.String() --dport strconv.Itoa(entry.ContainerPort) -j MASQUERADE"

// plugins/plugins/meta/portmap/utils.go

12、func getRoutableHostIF(containerIP net.IP) string

获取路由容器流量的interface,这是关闭martian filtering的第一步

1、调用routes, err := netlink.RouteGet(containerIP)获取与containerIP有关的路由

2、遍历routes,根据route.LinkIndex找到相应的link,再返回link.Attrs().Name即可

// plugins/plugins/meta/portmap/portmap.go

13、func enableLocalnetRouting(ifName string) error

1、创建routeLocalnetPath := "net.ipv4.conf." + ifName + ".route_localnet"

2、调用sysctl.Sysctl(routeLocalnetPath, "1")即可

// plugins/plugins/meta/portmap/main.go

14、func cmdDel(args *skel.CmdArgs) error

1、首先调用netConf, err := parseConfig(args.StdinData, args.IfName)获取配置

2、调用netConf.ContainerID = args.ContainerID

3、调用err := unforwardPorts(netConf)

// plugins/plugins/meta/portmap/portmap.go

15、func unforwardPorts(config *PortMapConf) error

unforwardPorts删除所有由该plugin创建的iptables rules。并且因为在DELETE的时候我们并不知道使用的是哪种协议,因此我们首先检查对应的iptables是否存在

如果不存在也不报错,除非IPv4和IPv6都不存在

1、首先调用dnatChain := genDnatChain(config.Name, config.ContainerID, nil)和snatChain := genSnatChain(config.Name, config.ContainerID)获取container对应的chain

2、调用ip4t := maybeGetIptables(false)和ip6t := maybeGetIptables(true)

3、如果ip4t不为nil,则调用dnatChain.teardown(ip4t)和snatChain.teardown(ip4t)

4、如果ip6t不为nil,则调用dnatChain.teardown(ip6t)

// plugins/plugins/meta/portmap/portmap.go

16、func maybeGetIptables(isV6 bool) *iptables.IPTables

1、首先调用proto := iptables.ProtocolIPv4,如果isV6为true,则proto = iptables.ProtocolIPv6

2、调用ipt, err := iptables.NewWithProtocol(proto)获取iptables,并调用_, err = ipt.List("nat", "OUTPUT")查看是否存在nat的OUTPUT链

3、若存在错误,则返回nil,否则return ipt

转载于:https://www.cnblogs.com/YaoDD/p/7308045.html

CNI portmap插件实现源码分析相关推荐

  1. ATS插件channel_stats源码分析解读

    简介 channel_stats插件能对每个channel收集运行时统计信息(速率,请求数,更多选项将在未来添加),这些统计信息通过http json方式输出,这些 接口代码取自stats_over_ ...

  2. 轮播插件unsilder 源码解析(一)---使用

    啰嗦几句:学习的可以直接省略,一直本着写原生的插件想法,但是前天看了吕大豹的博客觉得自己都没有正经的写个jquery插件:所以在开始写之前我会先对几个比较热门的jquery的插件进行源码分析:至于为什 ...

  3. coredns源码分析

    CoreDNS是使用go语言编写的快速灵活的DNS服务,采用链式插件模式,每个插件实现独立的功能,底层协议可以是tcp/udp,也可以是TLS,gRPC等.默认监听所有ip地址,可使用bind插件指定 ...

  4. 【Android 插件化】VirtualApp 源码分析 ( 启动应用源码分析 | HomePresenterImpl 启动应用方法 | VirtualCore 启动插件应用最终方法 )

    文章目录 一.启动应用源码分析 1.HomeActivity 启动应用点击方法 2.HomePresenterImpl 启动应用方法 3.VirtualCore 启动插件应用最终方法 一.启动应用源码 ...

  5. 【Android 插件化】VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )

    文章目录 一.目前的 API 现状 二.安装应用源码分析 1.安装按钮执行的操作 2.返回到 HomeActivity 执行的操作 一.目前的 API 现状 下图是 VirtualApp 官方给出的集 ...

  6. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  7. 【转载】SharpDevelop源码分析(四)SharpDevelop的AddInTree View 插件

    SharpDevelop的AddInTree View 插件 http://www.cnblogs.com/passos/archive/2004/10/15/52513.html 自从SharpDe ...

  8. 云客Drupal源码分析之插件系统(下)

    以下内容仅是一个预览,完整内容请见文尾: 至此本系列对插件的介绍全部完成,涵盖了系统插件的所有知识 全文目录(全文10476字): 实例化插件 插件映射Plugin mapping 插件上下文   具 ...

  9. 云客Drupal源码分析之插件系统(上)

    各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal底层原理,并缩短学习时间,但自<插件系统(上)>主题开始博 ...

最新文章

  1. blog微服务架构代码_聊聊微服务架构
  2. Java 中,类、类对象、泛型之间的转换
  3. 广告行业一些常用物料的尺寸
  4. Linux 安装 lanmp
  5. css 背景图怎么设置自动填充满_CSS属性设置 -- 背景样式
  6. 2016 pku campus/OpenJ_POJ - C16H(推公式+矩阵快速幂)
  7. oracle 119(11.2.0.4),ORACLE 从 11.2.0.1 升级到 11.2.0.4 版本之ORA-00119问题处理纪实
  8. Bailian3728 Blah数集【数学+set】
  9. 我的vim和emacs配置文件
  10. 计算机导论第4版第五章答案,《计算机导论》习题答案.doc
  11. 屏幕录制专家——录制视频没声音的解决办法
  12. xp访问共享文件夹需要重启服务器,winXP共享文件夹断开、重新连接、重设置密码的方法...
  13. 用 Rust 写一个声控小动画
  14. Mac软件破解版下载地址
  15. FA-RPN: Floating Region Proposals for Face Detection(论文阅读笔记)
  16. switch怎么切换服务器账号,任天堂eshop如何换区 switch账号如何切换其他服地区
  17. 华为od统一考试B卷【分月饼】C++ 实现
  18. AIX 学习笔记之 存储管理 LV PV VG PP
  19. Linux 中的 nl 命令详解及C/C++代码实现(文件行数)
  20. CSAPP - LAB 1 datalab

热门文章

  1. 太神奇!波士顿动力机器狗即将长出手臂,还能自己跑去充电
  2. 阿里达摩院-视觉方向(校招、社招、实习),欢迎各路大神
  3. HALCON学习之旅(六)
  4. 聚类算法详解——深度AI科普团队
  5. 毫秒级检测!你见过带GPU加速的树莓派吗?
  6. win7旗舰版+caffe+vs2013+matlab2014b(无GPU版)
  7. LQR轨迹跟踪算法Python/Matlab算法实现_LQRmatrix推导(2)
  8. 上海事职业培训软件测试高级,《软件测试人员(Java)(4级)》人力资源和社会保障部教材办公室、中国就业培训技术指导中心上海分中心、上海市职业培训研究发展中心 编_孔网...
  9. 河南科技大学计算机系宿舍,河南科技大学宿舍条件怎么样—河南科技大学宿舍图片...
  10. NETSTAT 指令详解