-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathmain.py
More file actions
172 lines (127 loc) · 5.78 KB
/
main.py
File metadata and controls
172 lines (127 loc) · 5.78 KB
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
168
169
170
171
172
"""NApp that solve the L2 Learning Switch algorithm."""
import requests
from kytos.core import KytosEvent, KytosNApp, log
from kytos.core.helpers import listen_to
from pyof.foundation.network_types import Ethernet
# OpenFlow structures that differ will be imported versionwise.
from pyof.v0x01.asynchronous.packet_in import PacketInReason
from pyof.v0x01.common.action import ActionOutput as Output10
from pyof.v0x01.common.phy_port import Port as Port10
from pyof.v0x01.common.phy_port import PortConfig as PortConfig10
from pyof.v0x01.controller2switch.packet_out import PacketOut as PacketOut10
from pyof.v0x04.common.action import ActionOutput as Output13
from pyof.v0x04.common.port import PortConfig as PortConfig13
from pyof.v0x04.common.port import PortNo as Port13
from pyof.v0x04.controller2switch.packet_out import PacketOut as PacketOut13
from napps.kytos.of_l2ls import settings
class Main(KytosNApp):
"""Main class of a KytosNApp, responsible for OpenFlow operations."""
def setup(self):
"""App initialization (used instead of ``__init__``).
The setup method is automatically called by the run method.
Users shouldn't call this method directly.
"""
def execute(self):
"""Run once on app 'start' or in a loop.
The execute method is called by the run method of KytosNApp class.
Users shouldn't call this method directly.
"""
@listen_to('kytos/topology.switch.enabled')
def install_table_miss_flow(self, event):
"""Install the TableMiss Flow in OF1.3 switches.
This is needed because those drop packets by default.
"""
dpid = event.content['dpid']
switch = self.controller.get_switch_by_dpid(dpid)
try:
version = switch.connection.protocol.version
except AttributeError:
version = None
log.debug(f'The OpenFlow version was not found for switch {dpid}.')
if version != 0x04:
return
flow = {}
flow['priority'] = 0
flow['table_id'] = settings.TABLE_ID
flow['actions'] = [{'action_type': 'output',
'port': Port13.OFPP_CONTROLLER}]
destination = switch.id
endpoint = f'{settings.FLOW_MANAGER_URL}/flows/{destination}'
data = {'flows': [flow]}
requests.post(endpoint, json=data)
@staticmethod
def _create_flow(packet, port):
"""Create a Flow message."""
flow = {}
match = {}
flow['priority'] = settings.FLOW_PRIORITY
flow['table_id'] = settings.TABLE_ID
match['dl_src'] = packet.source.value
match['dl_dst'] = packet.destination.value
match['dl_type'] = packet.ether_type.value
flow['match'] = match
flow['actions'] = [{'action_type': 'output',
'port': port}]
return flow
@staticmethod
def _create_packet_out(version, packet, ports, switch):
"""Create a PacketOut message with the appropriate version and data."""
if version == '0x01':
port = ports[0] if ports else Port10.OFPP_FLOOD
iface = switch.get_interface_by_port_no(port)
if iface and (iface.config & PortConfig10.OFPPC_NO_FWD ==
PortConfig10.OFPPC_NO_FWD):
return None
packet_out = PacketOut10()
packet_out.actions.append(Output10(port=port))
else:
port = ports[0] if ports else Port13.OFPP_FLOOD
iface = switch.get_interface_by_port_no(port)
if iface and (iface.config & PortConfig13.OFPPC_NO_FWD ==
PortConfig13.OFPPC_NO_FWD):
return None
packet_out = PacketOut13()
packet_out.actions.append(Output13(port=port))
packet_out.buffer_id = packet.buffer_id
packet_out.in_port = packet.in_port
packet_out.data = packet.data
return packet_out
@listen_to('kytos/of_core.v0x0[14].messages.in.ofpt_packet_in')
def handle_packet_in(self, event):
"""Handle PacketIn Event.
Install flows allowing communication between switch ports.
Args:
event (KytosPacketIn): Received Event
"""
log.debug("PacketIn Received")
packet_in = event.content['message']
ethernet = Ethernet()
ethernet.unpack(packet_in.data.value)
# Ignore LLDP packets or packets not generated by table-miss flows
if (ethernet.destination in settings.LLDP_MACS or
packet_in.reason != PacketInReason.OFPR_NO_MATCH):
return
switch = event.source.switch
version = switch.ofp_version
# Learn the port where the sender is connected
in_port = getattr(packet_in.in_port, 'value', packet_in.in_port)
switch.update_mac_table(ethernet.source, in_port)
ports = switch.where_is_mac(ethernet.destination)
# Add a flow to the switch if the destination is known
if ports:
flow = self._create_flow(ethernet, ports[0])
destination = switch.id
endpoint = f'{settings.FLOW_MANAGER_URL}/flows/{destination}'
data = {'flows': [flow]}
requests.post(endpoint, json=data)
# Send the packet to correct destination or flood it
packet_out = self._create_packet_out(version, packet_in, ports, switch)
if packet_out is None:
return
event_out = KytosEvent(name=('kytos/of_l2ls.messages.out.'
'ofpt_packet_out'),
content={'destination': event.source,
'message': packet_out})
self.controller.buffers.msg_out.put(event_out)
def shutdown(self):
"""Too simple to have a shutdown procedure."""