forked from faucetsdn/ryu
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathforwarding_consistency_1_to_many.py
167 lines (137 loc) · 7.35 KB
/
forwarding_consistency_1_to_many.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
'''
STATEFUL TABLE 0
Lookup-scope=IPV4_DST,IPV4_SRC,TCP_DST,TCP_SRC
Update-scope=IPV4_DST,IPV4_SRC,TCP_DST,TCP_SRC
_______
| |--h2
h1--| S1 |--h3
|_______|--h4
h1 is the client 10.0.0.1
h1 connects to an EchoServer 10.0.0.2:80
h2, h3, h4 are 3 replicas of the server:
h2 is listening at 10.0.0.2:200
h3 is listening at 10.0.0.3:300
h4 is listening at 10.0.0.4:400
$ ryu-manager ryu/ryu/app/beba/forwarding_consistency_1_to_many.py
$ sudo mn --topo single,4 --switch user --mac --controller remote
mininet> xterm h1 h1 h1 h2 h3 h4
h2# python ryu/ryu/app/beba/echo_server.py 200
h3# python ryu/ryu/app/beba/echo_server.py 300
h4# python ryu/ryu/app/beba/echo_server.py 400
Let's try to connect from h1 to the EchoServer and send some message:
h1# nc 10.0.0.2 80
If we keep the connection open, the responding EchoServer is always the same.
If we open another connection (from the 2nd terminal of h1) maybe we get connected to another replica.
If we close it and re-connect, maybe we are connected to another replica.
'''
import logging
import struct
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
import ryu.ofproto.ofproto_v1_3 as ofproto
import ryu.ofproto.ofproto_v1_3_parser as ofparser
import ryu.ofproto.beba_v1_0 as bebaproto
import ryu.ofproto.beba_v1_0_parser as bebaparser
LOG = logging.getLogger('app.beba.forwarding_consistency_1_to_many')
SWITCH_PORTS = 4
LOG.info("Beba Forwarding Consistency sample app initialized")
LOG.info("Supporting MAX %d ports per switch" % SWITCH_PORTS)
class BebaLoadBalancing(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(BebaLoadBalancing, self).__init__(*args, **kwargs)
self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
LOG.info("Configuring switch %d..." % datapath.id)
""" Set table 0 as stateful """
req = bebaparser.OFPExpMsgConfigureStatefulTable(datapath=datapath,
table_id=0,
stateful=1)
datapath.send_msg(req)
""" Set lookup extractor = {ip_src, ip_dst, tcp_src, tcp_dst} """
req = bebaparser.OFPExpMsgKeyExtract(datapath=datapath,
command=bebaproto.OFPSC_EXP_SET_L_EXTRACTOR,
fields=[ofproto.OXM_OF_IPV4_SRC,ofproto.OXM_OF_IPV4_DST,ofproto.OXM_OF_TCP_SRC,ofproto.OXM_OF_TCP_DST],
table_id=0)
datapath.send_msg(req)
""" Set update extractor = {ip_src, ip_dst, tcp_src, tcp_dst} (same as lookup) """
req = bebaparser.OFPExpMsgKeyExtract(datapath=datapath,
command=bebaproto.OFPSC_EXP_SET_U_EXTRACTOR,
fields=[ofproto.OXM_OF_IPV4_SRC,ofproto.OXM_OF_IPV4_DST,ofproto.OXM_OF_TCP_SRC,ofproto.OXM_OF_TCP_DST],
table_id=0)
datapath.send_msg(req)
""" Group table setup """
buckets = []
# Action Bucket: <PWD port_i , SetState(i-1)
for port in range(2,SWITCH_PORTS+1):
max_len = 2000
dest_ip=self.int_to_ip_str(port)
dest_eth=self.int_to_mac_str(port)
dest_tcp=(port)*100
actions = [ bebaparser.OFPExpActionSetState(state=port, table_id=0),
ofparser.OFPActionSetField(ipv4_dst=dest_ip),
ofparser.OFPActionSetField(eth_dst=dest_eth),
ofparser.OFPActionSetField(tcp_dst=dest_tcp),
ofparser.OFPActionOutput(port=port, max_len=max_len) ]
buckets.append(ofparser.OFPBucket(weight=100,
watch_port=ofproto.OFPP_ANY,
watch_group=ofproto.OFPG_ANY,
actions=actions))
req = ofparser.OFPGroupMod(datapath=datapath,
command=ofproto.OFPGC_ADD,
type_=ofproto.OFPGT_SELECT,
group_id=1,
buckets=buckets)
datapath.send_msg(req)
""" ARP packets flooding """
match = ofparser.OFPMatch(eth_type=0x0806)
actions = [ofparser.OFPActionOutput(port=ofproto.OFPP_FLOOD)]
self.add_flow(datapath=datapath, table_id=0, priority=100,
match=match, actions=actions)
""" Reverse path flow """
for in_port in range(2, SWITCH_PORTS + 1):
src_ip=self.int_to_ip_str(in_port)
src_eth=self.int_to_mac_str(in_port)
src_tcp=in_port*100
# we need to match an IPv4 (0x800) TCP (6) packet to do SetField()
match = ofparser.OFPMatch(in_port=in_port, eth_type=0x800, ip_proto=6, ipv4_src=src_ip,eth_src=src_eth,tcp_src=src_tcp)
actions = [ofparser.OFPActionSetField(ipv4_src="10.0.0.2"),
ofparser.OFPActionSetField(eth_src="00:00:00:00:00:02"),
ofparser.OFPActionSetField(tcp_src=80),
ofparser.OFPActionOutput(port=1, max_len=0)]
self.add_flow(datapath=datapath, table_id=0, priority=100,
match=match, actions=actions)
""" Forwarding consistency rules"""
match = ofparser.OFPMatch(in_port=1, state=0, eth_type=0x800, ip_proto=6)
actions = [ofparser.OFPActionGroup(1)]
self.add_flow(datapath=datapath, table_id=0, priority=100,
match=match, actions=actions)
for state in range(2,SWITCH_PORTS+1):
dest_ip=self.int_to_ip_str(state)
dest_eth=self.int_to_mac_str(state)
dest_tcp=(state)*100
match = ofparser.OFPMatch(in_port=1, state=state, eth_type=0x800, ip_proto=6)
actions = [ ofparser.OFPActionSetField(ipv4_dst=dest_ip),
ofparser.OFPActionSetField(eth_dst=dest_eth),
ofparser.OFPActionSetField(tcp_dst=dest_tcp),
ofparser.OFPActionOutput(port=state, max_len=0)]
self.add_flow(datapath=datapath, table_id=0, priority=100,
match=match, actions=actions)
def add_flow(self, datapath, table_id, priority, match, actions):
inst = [ofparser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = ofparser.OFPFlowMod(datapath=datapath, table_id=table_id,
priority=priority, match=match, instructions=inst)
datapath.send_msg(mod)
# returns "xx:xx:xx:xx:xx:xx"
def int_to_mac_str(self, host_number):
mac_str = "{0:0{1}x}".format(int(host_number),12) # converts to hex with zero pad to 48bit
return ':'.join(mac_str[i:i+2] for i in range(0, len(mac_str), 2)) # adds ':'
# returns "10.x.x.x"
def int_to_ip_str(self, host_number):
ip = (10<<24) + int(host_number)
return ".".join(map(lambda n: str(ip>>n & 0xFF), [24,16,8,0]))