blob: 93e102b08560720f8f67630d2f0480039dce749b [file] [log] [blame]
Zack Williams41513bf2018-07-07 20:08:35 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Nathan Knuth418fdc82016-09-16 22:51:15 -070014"""
15Mock backend for testing purposes
16"""
17
18import logging
19import os
20import sys
21from threading import Thread
22
23from hexdump import hexdump
24from scapy.all import Ether, IP, UDP, Dot1Q, sendp, sniff
25
26# VERY IMPORTANT:
Zsolt Harasztie39523b2016-10-16 19:30:34 -070027# Without the below hack, scapy will not properly receive VLAN
Nathan Knuth418fdc82016-09-16 22:51:15 -070028# header (see http://stackoverflow.com/questions/18994242/why-isnt-scapy-capturing-vlan-tag-information).
29#
30from scapy.all import conf, ETH_P_ALL
31import pcap
32conf.use_pcap = True
33import scapy.arch.pcapdnet
34assert conf.L2listen.__name__=='L2pcapListenSocket'
35
36sys.path.insert(1, os.path.join(sys.path[0], '..'))
37
38import loxi.of13 as ofp
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070039from ofagent.utils import mac_str_to_tuple
Nathan Knuth418fdc82016-09-16 22:51:15 -070040
41
42class MockBackend(object):
43
44 mfr_desc = "Ciena Corp."
45 hw_desc = "mock"
46
47 def __init__(self, store, in_out_iface=None, in_out_stag=None):
48 self.store = store
49 self.add_some_ports()
50 self.in_out_iface = in_out_iface
51 self.in_out_stag = in_out_stag
52 self.agent = None
53 self.in_out_receiver = None
54
55 def set_agent(self, agent):
56 self.agent = agent
57 if self.in_out_iface is not None:
58 self.in_out_receiver = InOutReceiver(self.in_out_iface, agent, self.in_out_stag)
59 self.in_out_receiver.start()
60
61 def stop(self):
62 if self.in_out_receiver is not None:
63 self.in_out_receiver.stop()
64
65 def get_serial_num(self):
66 return "DFG-4567-RTYU-789"
67
68 def get_dp_desc(self):
69 return "mock device"
70
71 def add_some_ports(self):
72 cap = ofp.OFPPF_1GB_FD | ofp.OFPPF_FIBER
73 for pno, mac, nam, cur, adv, sup, spe in (
74 ( 1, '00:00:00:00:00:01', 'onu1', cap, cap, cap, ofp.OFPPF_1GB_FD),
75 ( 2, '00:00:00:00:00:02', 'onu2', cap, cap, cap, ofp.OFPPF_1GB_FD),
76 (129, '00:00:00:00:00:81', 'olt', cap, cap, cap, ofp.OFPPF_1GB_FD)
77 ):
78 port = ofp.common.port_desc(pno, mac_str_to_tuple(mac), nam,
79 curr=cur, advertised=adv, supported=sup,
80 curr_speed=spe, max_speed=spe)
81 self.store.port_add(port)
82
83 def packet_out(self, in_port, out_port, data):
84 in_port = "CONTROLLER" if in_port == ofp.OFPP_CONTROLLER else in_port
85 print "PACKET OUT (%s => %s): " % (in_port, out_port)
86 hexdump(data)
87
88 if self.in_out_iface is not None:
89
90 try:
91 # disect the packet
92 pkt = Ether(data)
93
94 # remove payload from Ether frame
95 payload = pkt.payload
96 payload_type = pkt.type
97 pkt.remove_payload()
98
99 # insert Dot1Q shim with vlan_id = out_port
100
101 if self.in_out_stag is None:
102 ## WARNING -- This was changed from 0x88a8 to 0x8100 when
103 ## testing with the Intel XL710 quad 10GE boards. The
104 ## XL710 does not support the TPID for the STAG.
105 ##
106 ## Long term, it should be changed back to 0x88a8!
107 ##
108 pkt.type = 0x8100
109 new_pkt = pkt / Dot1Q(vlan=out_port, type=payload_type) / payload
110
111 else:
112 pkt.type = 0x8100
113 new_pkt = (
114 pkt /
115 Dot1Q(vlan=self.in_out_stag, type=0x8100) /
116 Dot1Q(vlan=out_port, type=payload_type) /
117 payload)
118
119 # send out the packet
120 sendp(new_pkt, iface=self.in_out_iface)
121
122 except Exception, e:
123 logging.exception("Could not parse packet-out data as scapy.Ether:\n")
124 logging.error(hexdump(data, 'return'))
125
126
127class InOutReceiver(Thread):
128
129 def __init__(self, iface, agent, in_out_stag=None):
130 Thread.__init__(self)
131 self.iface = iface
132 self.finished = False
133 self.agent = agent
134 self.in_out_stag = in_out_stag
135
136 def run(self):
137 # TODO this loop could be reconciled with the ofp Connection to become a
138 # single select loop.
139 self.sock = s = conf.L2listen(
140 type=ETH_P_ALL,
141 iface=self.iface,
142 filter='inbound'
143 )
144 while not self.finished:
145 try:
146 sniffed = sniff(1, iface=self.iface, timeout=1, opened_socket=s)
147 print 'Sniffer received %d packet(s)' % len(sniffed)
148 for pkt in sniffed:
149 self.forward_packet(pkt)
150
151 except Exception, e:
152 logging.error("scapy.sniff error: %s" % e)
153
154 def stop(self):
155 """
156 Signal the thread to exit and wait for it
157 """
158 assert not self.finished
159 logging.debug("Stop sniffing on in-out channel")
160 self.finished = True
161 self.sock.close()
162 self.join()
163
164 def forward_packet(self, pkt):
165 print "Received packet:"
166 hexdump(str(pkt))
167 pkt.show()
168
169 try:
170 assert isinstance(pkt, Ether)
171 assert isinstance(pkt.getlayer(1), Dot1Q)
172 dot1q = pkt.getlayer(1)
173 assert isinstance(dot1q, Dot1Q)
174
175 if self.in_out_stag is None:
176 payload = dot1q.payload
177 payload_type = dot1q.type
178 pkt.remove_payload()
179
180 pkt.type = payload_type
181 new_pkt = pkt / payload
182 in_port = dot1q.vlan
183
184 else:
185 if dot1q.vlan != self.in_out_stag:
186 print 'Dropping packet because outer tag %d does not match %d' % (
187 dot1q.vlan, self.in_out_stag)
188 return
189 dot1q_inner = dot1q.getlayer(1)
190 assert isinstance(dot1q_inner, Dot1Q)
191 payload = dot1q_inner.payload
192 payload_type = dot1q_inner.type
193 pkt.remove_payload()
194
195 pkt.type = payload_type
196 new_pkt = pkt / payload
197 in_port = dot1q_inner.vlan
198
199 if self.agent is not None:
200 self.agent.send_packet_in(str(new_pkt), in_port=in_port)
201 print 'new packet forwarded to controller (with in_port=%d):' % in_port
Zsolt Harasztie39523b2016-10-16 19:30:34 -0700202 new_pkt.show()
Nathan Knuth418fdc82016-09-16 22:51:15 -0700203
204 except Exception, e:
205 logging.exception("Unexpected packet format received by InOutReceiver: %s" % e)
206 logging.error(hexdump(str(pkt), 'return'))
207
208