|
1
|
|
|
""" |
|
2
|
|
|
This module has functions used to pack and unpack the Ethernet frame |
|
3
|
|
|
used by PacketOuts and PacketIns. |
|
4
|
|
|
""" |
|
5
|
|
|
|
|
6
|
|
|
|
|
7
|
1 |
|
import dill |
|
8
|
1 |
|
from pyof.foundation.network_types import Ethernet, IPv4, VLAN |
|
9
|
1 |
|
from napps.amlight.sdntrace import constants |
|
10
|
1 |
|
from napps.amlight.sdntrace.tracing.trace_msg import TraceMsg |
|
11
|
1 |
|
from napps.amlight.sdntrace.shared.extd_nw_types import TCP, UDP |
|
12
|
1 |
|
from napps.amlight.sdntrace.shared.switches import Switches |
|
13
|
1 |
|
from napps.amlight.sdntrace.shared.colors import Colors |
|
14
|
|
|
|
|
15
|
|
|
|
|
16
|
1 |
|
def generate_trace_pkt(trace_entries, color, r_id): |
|
17
|
|
|
""" Receives the REST/PUT to generate a PacketOut |
|
18
|
|
|
data needs to be serialized. The goal is always to create |
|
19
|
|
|
a packet with data being the TraceMsg to differentiate different |
|
20
|
|
|
traces running in parallel. We will stack layers depending of |
|
21
|
|
|
the user request. If user submits just a VLAN ID, we will use |
|
22
|
|
|
ethertype 88b5 and add TraceMsg after it. Same for IP, however |
|
23
|
|
|
the protocol will be 65535. If user provides all the way to TCP/UDP |
|
24
|
|
|
we will add TraceMsg after it. First thing is to discover |
|
25
|
|
|
what it is that the user has provided. |
|
26
|
|
|
|
|
27
|
|
|
Args: |
|
28
|
|
|
trace_entries: TraceEntries provided by user or collected from PacketIn |
|
29
|
|
|
color: result from Coloring Napp for a specific DPID |
|
30
|
|
|
r_id: request ID |
|
31
|
|
|
|
|
32
|
|
|
Returns: |
|
33
|
|
|
in_port: in_port |
|
34
|
|
|
pkt: serialized Ethernet frame |
|
35
|
|
|
""" |
|
36
|
|
|
|
|
37
|
1 |
|
ethernet = _create_ethernet_frame(trace_entries, color) |
|
38
|
|
|
|
|
39
|
1 |
|
msg = TraceMsg(r_id) |
|
40
|
|
|
|
|
41
|
1 |
|
if ethernet.ether_type == constants.IPV4: |
|
42
|
1 |
|
ip_pkt = _create_ip_packet(trace_entries) |
|
43
|
1 |
|
if ip_pkt.protocol == constants.TCP: |
|
44
|
1 |
|
tp_pkt = _create_tcp_packet(trace_entries) |
|
45
|
1 |
|
tp_pkt.data = dill.dumps(msg) |
|
46
|
1 |
|
ip_pkt.data = tp_pkt.pack(ip_pkt) |
|
47
|
1 |
|
elif ip_pkt.protocol == constants.UDP: |
|
48
|
1 |
|
udp_pkt = _create_udp_packet(trace_entries) |
|
49
|
1 |
|
udp_pkt.data = dill.dumps(msg) |
|
50
|
1 |
|
ip_pkt.data = udp_pkt.pack(ip_pkt) |
|
51
|
|
|
else: |
|
52
|
1 |
|
ip_pkt.data = dill.dumps(msg) |
|
53
|
|
|
|
|
54
|
1 |
|
ethernet.data = ip_pkt.pack() |
|
55
|
|
|
else: |
|
56
|
|
|
ethernet.data = dill.dumps(msg) |
|
57
|
|
|
|
|
58
|
1 |
|
pkt = ethernet.pack() |
|
59
|
1 |
|
return trace_entries.in_port, pkt |
|
60
|
|
|
|
|
61
|
|
|
|
|
62
|
1 |
|
def prepare_next_packet(trace_entries, result, event): |
|
63
|
|
|
""" Used to support VLAN translation. Currently, it does not |
|
64
|
|
|
support translation of other fields, such as MAC addresses. |
|
65
|
|
|
|
|
66
|
|
|
Args: |
|
67
|
|
|
trace_entries: TraceEntries provided by user or collected from PacketIn |
|
68
|
|
|
result: Result of the last Trace Probe sent. |
|
69
|
|
|
event: PacketIn event |
|
70
|
|
|
|
|
71
|
|
|
Returns: |
|
72
|
|
|
trace_entries: TraceEntries customized with new VLAN |
|
73
|
|
|
color: result from Coloring Napp for a specific DPID |
|
74
|
|
|
switch: DPID |
|
75
|
|
|
""" |
|
76
|
1 |
|
dpid = result['dpid'] |
|
77
|
1 |
|
switch, color = _get_node_color_from_dpid(dpid) |
|
78
|
|
|
|
|
79
|
1 |
|
trace_entries.dpid = dpid |
|
80
|
|
|
|
|
81
|
1 |
|
if event.content['message'].header.version == 1: |
|
82
|
1 |
|
in_port = event.content['message'].in_port.value |
|
83
|
1 |
|
trace_entries.in_port = in_port |
|
84
|
|
|
else: |
|
85
|
|
|
in_port = event.message.in_port |
|
86
|
|
|
trace_entries.in_port = in_port |
|
87
|
|
|
|
|
88
|
1 |
|
vlan = _get_vlan_from_pkt(event.content['message'].data.value) |
|
89
|
1 |
|
if vlan: |
|
90
|
1 |
|
trace_entries.dl_vlan = vlan |
|
91
|
1 |
|
return trace_entries, color, switch |
|
92
|
|
|
|
|
93
|
|
|
|
|
94
|
1 |
|
def process_packet(ethernet): |
|
95
|
|
|
"""Navigates through the Ethernet payload looking for the |
|
96
|
|
|
TraceMsg(). TraceMsg is the payload after all protocols of |
|
97
|
|
|
the TCP/IP stack. |
|
98
|
|
|
|
|
99
|
|
|
Args: |
|
100
|
|
|
ethernet: ethernet frame |
|
101
|
|
|
|
|
102
|
|
|
Returns: |
|
103
|
|
|
TraceMsg in the binary format |
|
104
|
|
|
""" |
|
105
|
1 |
|
offset = 0 |
|
106
|
|
|
|
|
107
|
1 |
|
trace_msg = ethernet.data.value |
|
108
|
|
|
|
|
109
|
1 |
|
if ethernet.ether_type == constants.IPV4: |
|
110
|
1 |
|
ip_pkt = IPv4() |
|
111
|
1 |
|
ip_pkt.unpack(ethernet.data.value, offset) |
|
112
|
1 |
|
offset += ip_pkt.length |
|
113
|
1 |
|
trace_msg = ip_pkt.data |
|
114
|
|
|
|
|
115
|
1 |
|
if ip_pkt.protocol == constants.TCP: |
|
116
|
1 |
|
transport = TCP() |
|
117
|
1 |
|
transport.unpack(ip_pkt.data) |
|
118
|
1 |
|
trace_msg = transport.data |
|
119
|
|
|
|
|
120
|
1 |
|
elif ip_pkt.protocol == constants.UDP: |
|
121
|
1 |
|
transport = UDP() |
|
122
|
1 |
|
transport.unpack(ip_pkt.data) |
|
123
|
1 |
|
trace_msg = transport.data |
|
124
|
|
|
|
|
125
|
1 |
|
return trace_msg |
|
126
|
|
|
|
|
127
|
|
|
|
|
128
|
1 |
|
def _create_ethernet_frame(trace_entries, color): |
|
129
|
|
|
""" Create an Ethernet frame using TraceEntries |
|
130
|
|
|
and color (dl_src) |
|
131
|
|
|
|
|
132
|
|
|
Args: |
|
133
|
|
|
trace_entries: TraceEntries provided by user or collected from PacketIn |
|
134
|
|
|
color: field and value that indicate the color |
|
135
|
|
|
Returns: |
|
136
|
|
|
ethernet frame |
|
137
|
|
|
""" |
|
138
|
1 |
|
ethernet = Ethernet() |
|
139
|
1 |
|
ethernet.ether_type = trace_entries.dl_type |
|
140
|
|
|
|
|
141
|
1 |
|
ethernet.source = color['color_value'] |
|
142
|
1 |
|
ethernet.destination = trace_entries.dl_dst |
|
143
|
|
|
|
|
144
|
1 |
|
if trace_entries.dl_vlan: |
|
145
|
1 |
|
vlan = VLAN(vid=trace_entries.dl_vlan, |
|
146
|
|
|
pcp=trace_entries.dl_vlan_pcp) |
|
147
|
1 |
|
ethernet.vlans.append(vlan) |
|
148
|
1 |
|
return ethernet |
|
149
|
|
|
|
|
150
|
|
|
|
|
151
|
1 |
|
def _create_ip_packet(trace_entries) -> IPv4: |
|
152
|
|
|
""" Create an IP packet using TraceEntries |
|
153
|
|
|
|
|
154
|
|
|
Args: |
|
155
|
|
|
trace_entries: TraceEntries provided by user or collected from PacketIn |
|
156
|
|
|
Returns: |
|
157
|
|
|
ip packet |
|
158
|
|
|
""" |
|
159
|
1 |
|
ip_pkt = IPv4() |
|
160
|
1 |
|
ip_pkt.destination = trace_entries.nw_dst |
|
161
|
1 |
|
ip_pkt.source = trace_entries.nw_src |
|
162
|
1 |
|
ip_pkt.dscp = trace_entries.nw_tos |
|
163
|
1 |
|
ip_pkt.protocol = trace_entries.nw_proto |
|
164
|
1 |
|
return ip_pkt |
|
165
|
|
|
|
|
166
|
|
|
|
|
167
|
1 |
|
def _create_tcp_packet(trace_entries) -> TCP: |
|
168
|
|
|
""" Create a TCP packet using TraceEntries (FUTURE) |
|
169
|
|
|
|
|
170
|
|
|
Args: |
|
171
|
|
|
trace_entries: TraceEntries provided by user or collected from PacketIn |
|
172
|
|
|
Returns: |
|
173
|
|
|
tcp packet |
|
174
|
|
|
""" |
|
175
|
1 |
|
tcp_pkt = TCP() |
|
176
|
1 |
|
tcp_pkt.src_port = trace_entries.tp_src |
|
177
|
1 |
|
tcp_pkt.dst_port = trace_entries.tp_dst |
|
178
|
1 |
|
return tcp_pkt |
|
179
|
|
|
|
|
180
|
|
|
|
|
181
|
1 |
|
def _create_udp_packet(trace_entries) -> UDP: |
|
182
|
|
|
""" Create an UDP datagram using TraceEntries (FUTURE) |
|
183
|
|
|
|
|
184
|
|
|
Args: |
|
185
|
|
|
trace_entries: TraceEntries provided by user or collected from PacketIn |
|
186
|
|
|
Returns: |
|
187
|
|
|
tcp message |
|
188
|
|
|
""" |
|
189
|
1 |
|
udp_pkt = UDP() |
|
190
|
1 |
|
udp_pkt.src_port = trace_entries.tp_src |
|
191
|
1 |
|
udp_pkt.dst_port = trace_entries.tp_dst |
|
192
|
1 |
|
return udp_pkt |
|
193
|
|
|
|
|
194
|
|
|
|
|
195
|
1 |
|
def _get_node_color_from_dpid(dpid): |
|
196
|
|
|
""" Get node color from Coloring Napp |
|
197
|
|
|
|
|
198
|
|
|
Args: |
|
199
|
|
|
dpid: switch DPID |
|
200
|
|
|
Returns: |
|
201
|
|
|
switch and color |
|
202
|
|
|
0 for not Found |
|
203
|
|
|
""" |
|
204
|
1 |
|
for switch in Switches().get_switches(): |
|
205
|
1 |
|
if dpid == switch.dpid: |
|
206
|
1 |
|
return switch, Colors().get_switch_color(switch.dpid) |
|
207
|
1 |
|
return 0, 0 |
|
208
|
|
|
|
|
209
|
|
|
|
|
210
|
1 |
|
def _get_vlan_from_pkt(data): |
|
211
|
|
|
""" Get VLAN ID from frame. Used for VLAN Translation |
|
212
|
|
|
|
|
213
|
|
|
Args: |
|
214
|
|
|
data: Ethernet Frame |
|
215
|
|
|
Returns: |
|
216
|
|
|
VLAN VID |
|
217
|
|
|
""" |
|
218
|
1 |
|
ethernet = Ethernet() |
|
219
|
1 |
|
ethernet.unpack(data) |
|
220
|
1 |
|
vlans = ethernet.vlans |
|
221
|
1 |
|
if vlans: |
|
222
|
1 |
|
return vlans[0].vid |
|
223
|
|
|
return None |
|
224
|
|
|
|