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