最近在 Ryu 的 mailing list 上看到這個問題,就順便分享一下如何在 Ryu Controller 下製造一個客製化封包傳給 OpenFlow Switch (device)

應用情境

假設你在 Controller 收到某個封包之後又或是在某個條件下,需要先產生一個封包並且使用 OpenFlow 的 Packet-Out message 傳給 Switch

這裏有兩種方法,分別是利用 Ryu 與 Scapy 皆可以產生客製化封包,以下提供你參考

Ryu Framework

幾個步驟需要注意:

  1. 利用 ryu.lib.packet.packet.Packet 產生一個物件 pkt
  2. 利用 pkt.add_protocol() 堆疊各種網路協定像是(ethernet, ipv4, tcp),並把每一層需要填入的資訊塞進去
  3. 最後用 pkt.serialize() 包裝封包,checksum 會在這時候算好不用擔心
  4. pkt.data 就會是整個封包的 Binary
  5. 要確認有沒有把封包打包完整,可以用 pcaplib 存成 pcap 檔,可以用 Wireshark 做確認
    pcaplib 是我之前寫的 patch 現在已經收錄進 Ryu project 使用方式可以參考之前寫這篇 Saving captured packets in Ryu controller
build_pkt_ryu.py
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
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ipv4
from ryu.lib.packet import tcp
from ryu.lib import pcaplib


def build_pkt_ryu():
# Creat pcap file writer instance
pcap_pen = pcaplib.Writer(open('pkt_build_from_ryu.pcap', 'wb'))

# Creat an empty Packet instance
pkt = packet.Packet()

# Add ethernet protocol with ether type IP protocol and mac addresses
pkt.add_protocol(ethernet.ethernet(ethertype='0x0800',
dst='70:56:81:12:34:56',
src='70:56:81:65:43:21'))


# Add ipv4 protocol with IP addresses and TCP protocol which is 6
pkt.add_protocol(ipv4.ipv4(dst='192.168.8.70',
src='192.168.8.50',
proto=6))

# Add tcp protocol with port numbers and sequence number
pkt.add_protocol(tcp.tcp(src_port=5566,
dst_port=8080,
seq=1234566))

# Packet serializing
pkt.serialize()

bin_packet = pkt.data

# Write to pcap file
pcap_pen.write_pkt(bin_packet)

if __name__ == '__main__':
build_pkt_ryu()

Scapy Framework

這是最簡單的方式作出封包,但是需要安裝 Scapy 的 package

安裝方法

1
sudo pip install scapy

緊接著來做封包,用了 Scapy 十分容易

build_pkt_scapy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from ryu.lib import pcaplib
from scapy.all import Ether, IP, TCP

def build_pkt_scapy():
# Creat pcap file writer instance
pcap_pen = pcaplib.Writer(open('pkt_build_from_scapy.pcap', 'wb'))


eth = Ether(src='70:56:81:12:34:56', dst='84:94:56:12:34:44')

ip = IP(src='192.168.8.70', dst='192.168.8.50')

tcp_rst = TCP(sport= 5566, dport=8080, seq= 1234566)

# Combine
pkt = eth/ip/tcp_rst

# Write to pcap file
pcap_pen.write_pkt(pkt)

if __name__ == '__main__':
build_pkt_scapy()

以上兩種方法皆可以做出封包,並且輕易的存成 PCAP file 讓你用 Wireshark 去檢查是不是有問題,所以好 Ryu 不用嗎? XD