0x00 目录
[TOC]
0x01 前言
如图1所示,OpenFlow 1.0中定义了Set动作,用于修改数据包的首部字段,这包括源宿MAC地址、源宿IP地址、源宿端口号等字段。通过将指定IP地址段与Set动作绑定作为流表项,即可在OpenFlow交换机上实现NAT的功能。
假设现在需要通过修改IP协议号,来实现一个数据包的伪装。例如:原本是ICMP的包,通过修改IP协议号(1->6)后,对外表现为TCP包。
此时OpenFlow 1.0显然不能满足需求,原因就是OpenFlow 1.0只能提供有限的数据包首部字段修改。
注:OpenFlow 1.3以后的版本,增加SET_FIELD动作,支持按照OXM TLV格式来修改任意数据包首部字段
为了在OpenFlow 1.0中实现修改IP协议号,可以利用Packet-In的消息处理机制。
0x02 基于Packet-In消息处理机制修改IP协议号
基于Packet-In消息处理机制实现IP协议号修改的基本思路:
- 显示下发流表,对指定要修改IP协议号的数据包通过Packet-In消息上传到控制器
- 修改控制器上的Packet-In消息处理函数,解析数据包首部,修改IP首部中IP协议号字段
例子:如图2所示,假设有主机H1向H2发送ICMP包,现在希望将这个ICMP包的IP协议号字段进行修改,使其对外表现为TCP包,按照上面提到的思路即可实现。
注:上图中H1通过ping发送给H2的ICMP包经过控制器修改IP协议号后变为TCP包,H2在收到这个包之后,实际上不会进行ICMP响应,因此最终H1上会显示ping失败
0x03 POX + Mininet实验验证
按照0x02中的例子,使用POX和Mininet进行验证。
实验环境
实验步骤
- H1向H2发送ICMP包(h1 ping h2 -c 1)
- SW1将ICMP包Packet-In上传到控制器
- 控制器修改IP协议号(ICMP -> TCP),变换后的TCP包(原ICMP包)Packet-Out至SW2并指示SW2转发给H2
- 在SW2与H1的链路、SW2与H2的链路上抓包,预期实验结果:前者抓到ICMP包,后者抓到TCP包,并且H1上显示ping失败
编写POX应用
为了实现IP协议号的修改,需要在POX上编写一个应用transfer_ipproto(存放在POX源码的ext文件夹下),从而实现下面两个功能:
- 下发流表给SW1,指定ICMP包通过Packet-In上传到控制器处理
- 添加Packet-In消息处理函数,解析数据包首部,修改IP协议号后通过Packet-Out消息转发至SW2,并指导SW2将修改IP协议号后的数据包转发至H2
transfer_ipproto源码如下所示:
# -*- coding: utf-8 -*-
# 功能:
# 通过Packet-In机制修改IP协议号及TCP选项
# 模块Packet-In处理函数中下发默认流表,构建路径
#
# 网络拓扑:
# h1 - sw1 - sw2 - h2
#
# @Update History:
# @Author Date Content
# @CTZ 2020-03-24 添加必要的注释
# @CTZ 2020-03-31 完成代码编写及调试
from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.packet.ethernet import ethernet
from pox.lib.packet.icmp import icmp
from pox.lib.packet.ipv4 import ipv4
from pox.lib.addresses import IPAddr
from pox.lib.util import dpid_to_str, str_to_dpid
log = core.getLogger()
class transfer_ipproto(object):
def __init__(self):
log.info("初始化模块")
# h1信息
self.h1_info = {}
self.h1_info['ip'] = "10.0.0.1"
self.h1_info['mac'] = "00:00:00:00:00:01"
# h2信息
self.h2_info = {}
self.h2_info['ip'] = "10.0.0.2"
self.h2_info['mac'] = "00:00:00:00:00:02"
# sw1信息
self.sw1_info = {}
self.sw1_info['dpid'] = str_to_dpid("0x0000000000000001")
self.sw1_info['h1'] = {}
self.sw1_info['h1']['port'] = 1
self.sw1_info['sw2'] = {}
self.sw1_info['sw2']['port'] = 2
# sw2信息
self.sw2_info = {}
self.sw2_info['dpid'] = str_to_dpid("0x0000000000000002")
self.sw2_info['sw1'] = {}
self.sw2_info['sw1']['port'] = 1
self.sw2_info['h2'] = {}
self.sw2_info['h2']['port'] = 2
# 虚拟IP协议号
#self.ipproto = ipv4.ICMP_PROTOCOL
self.ipproto = ipv4.TCP_PROTOCOL
# 统计packetin处理次数
self.packetin_count = 0
core.openflow.addListeners(self)
def _handle_PacketIn (self, event):
self.packetin_count = self.packetin_count+1
log.info(str(self.packetin_count) + " : PacketIn处理开始")
#下发初始流表
if self.packetin_count == 1:
#sw1 -> sw2
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h1_info["ip"])
msg.match.nw_dst = IPAddr(self.h2_info["ip"])
msg.actions.append(of.ofp_action_output(port=self.sw1_info['sw2']['port']))
core.openflow.sendToDPID(self.sw1_info['dpid'], msg)
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h2_info["ip"])
msg.match.nw_dst = IPAddr(self.h1_info["ip"])
msg.actions.append(of.ofp_action_output(port=self.sw1_info['h1']['port']))
core.openflow.sendToDPID(self.sw1_info['dpid'], msg)
# sw1 -> controller
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h1_info["ip"])
msg.match.nw_dst = IPAddr(self.h2_info["ip"])
msg.match.nw_proto = ipv4.TCP_PROTOCOL
msg.priority = 0x8001 # 大于默认优先级0x8000
msg.actions.append(of.ofp_action_output(port=of.ofp_port_rev_map['OFPP_CONTROLLER']))
core.openflow.sendToDPID(self.sw1_info['dpid'], msg)
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h1_info["ip"])
msg.match.nw_dst = IPAddr(self.h2_info["ip"])
msg.match.nw_proto = ipv4.ICMP_PROTOCOL
msg.priority = 0x8001 # 大于默认优先级0x8000
msg.actions.append(of.ofp_action_output(port=of.ofp_port_rev_map['OFPP_CONTROLLER']))
core.openflow.sendToDPID(self.sw1_info['dpid'], msg)
# sw2 -> sw1
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h2_info["ip"])
msg.match.nw_dst = IPAddr(self.h1_info["ip"])
msg.actions.append(of.ofp_action_output(port=self.sw2_info['sw1']['port']))
core.openflow.sendToDPID(self.sw2_info['dpid'], msg)
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h1_info["ip"])
msg.match.nw_dst = IPAddr(self.h2_info["ip"])
msg.actions.append(of.ofp_action_output(port=self.sw2_info['h2']['port']))
core.openflow.sendToDPID(self.sw2_info['dpid'], msg)
# sw2 -> controller
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h2_info["ip"])
msg.match.nw_dst = IPAddr(self.h1_info["ip"])
msg.match.nw_proto = ipv4.TCP_PROTOCOL
msg.priority = 0x8001 # 大于默认优先级0x8000
msg.actions.append(of.ofp_action_output(port=of.ofp_port_rev_map['OFPP_CONTROLLER']))
core.openflow.sendToDPID(self.sw2_info['dpid'], msg)
msg = of.ofp_flow_mod()
msg.match.dl_type = 0x800
msg.match.nw_src = IPAddr(self.h2_info["ip"])
msg.match.nw_dst = IPAddr(self.h1_info["ip"])
msg.match.nw_proto = ipv4.ICMP_PROTOCOL
msg.priority = 0x8001 # 大于默认优先级0x8000
msg.actions.append(of.ofp_action_output(port=of.ofp_port_rev_map['OFPP_CONTROLLER']))
core.openflow.sendToDPID(self.sw2_info['dpid'], msg)
log.info("已经下发初始流表")
dpid = event.connection.dpid
in_port = event.port
packet = event.parsed
if not packet.parsed:
log.warning("%i %i ignoring unparsed packet", dpid, in_port)
return
if isinstance(packet, ethernet):
log.info("解析包:eth")
eth_packet = packet
#if eth_packet.type == packet.IP_TYPE:
if isinstance(eth_packet.payload, ipv4):
log.info("解析包:ipv4")
ip_packet = eth_packet.payload
if isinstance(ip_packet.payload, icmp):
log.info("解析包:icmp")
icmp_packet = ip_packet.payload
i = ipv4(v=ip_packet.v,
hl=ip_packet.hl,
tos=ip_packet.tos,
iplen=ip_packet.iplen,
id=ip_packet.id,
flags=ip_packet.flags,
frag=ip_packet.frag,
ttl=ip_packet.ttl,
srcip=ip_packet.srcip,
dstip=ip_packet.dstip,
protocol=self.ipproto) # 修改IP协议号
#csum
i.set_payload(icmp_packet)
e = ethernet(type=ethernet.IP_TYPE,
src=eth_packet.src,
dst=eth_packet.dst
)
e.set_payload(i)
# h1 -> h2的ICMP包执行协议号变换
if ip_packet.srcip == IPAddr(self.h1_info["ip"]) and ip_packet.dstip == IPAddr(self.h2_info["ip"]):
msg = of.ofp_packet_out()
msg.data = e.pack()
msg.actions.append(of.ofp_action_output(port=self.sw2_info['h2']['port']))
core.openflow.sendToDPID(self.sw2_info['dpid'], msg)
log.info("h1 -> h2的ICMP包处理结束:IP协议号变换")
# h2 -> h1的ICMP包执行协议号变换
elif ip_packet.srcip == self.h2_info["ip"] and ip_packet.dstip == self.h1_info["ip"]:
msg = of.ofp_packet_out()
msg.data = e.pack()
msg.actions.append(of.ofp_action_output(port=self.sw1_info['h1']['port']))
core.openflow.sendToDPID(self.sw1_info['dpid'], msg)
log.info("h2 -> h1的ICMP包处理结束:IP协议号变换")
log.info("PacketIn处理结束")
return
def launch():
core.registerNew(transfer_ipproto_tcpoptions)
配置POX启动参数
注:需要用到POX的arp_responder应用实现ARP的功能
Mininet验证
1.启动POX、Mininet并打开H1和H2的终端
2.在H1和H2上启动wirehsark,分别配置显示过滤器为icmp和tcp
启动wireshark命令:sudo wireshark
3.在Mininet终端上发起Ping,H1向H2发送一个ICMP包
4.查看wireshark抓包结果
如图8和图9所示,H1上抓到ICMP包
如图10和图11所示,H2上抓到TCP包
根据图9和图11中的IP Payload(数据包原始字节流)相同可知,H2上抓到的TCP包就是修改了IP协议号的ICMP包。
至此,该实验验证了可以通过Packet-In消息处理机制来实现IP协议号的修改。该方法除了能够修改IP协议号外,也能修改数据包中首部的其他字段。
0x04 总结
修改数据包首部字段的方法:
流表Set动作
注:OpenFlow1.3以前版本仅支持修改部分首部字段,OpenFlow1.3以后版本支持修改任意首部字段
基于Packet-In消息处理机制修改
0x05 参考
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!