blob: bd8f770e1953d72a9d5ed8a5bde443576b25d8a8 [file] [log] [blame]
Zsolt Haraszti91350eb2016-11-05 15:33:53 -07001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""
19Run this test inside a docker container using the following syntax:
20
21docker run -ti --rm -v $(pwd):/voltha --privileged cord/voltha-base \
Khen Nursimulu96bb5322016-11-09 20:16:03 -080022 env PYTHONPATH=/voltha python \
Zsolt Haraszti89a27302016-12-08 16:53:06 -080023 /voltha/tests/itests/run_as_root/test_frameio.py
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070024
25"""
26
27import os
28import random
29from time import sleep
30
31from scapy.layers.inet import IP
32from scapy.layers.l2 import Ether, Dot1Q
33from twisted.internet import reactor
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -080034from twisted.internet.defer import Deferred, inlineCallbacks, DeferredQueue
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070035from twisted.internet.error import AlreadyCalled
36from twisted.trial.unittest import TestCase
37
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -080038from common.frameio.frameio import FrameIOManager, BpfProgramFilter, \
39 FrameIOPortProxy
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070040from common.utils.asleep import asleep
41from common.utils.deferred_utils import DeferredWithTimeout, TimeOutError
42
43ident = lambda frame: frame
44none = lambda *args, **kw: None
45
46
47class TestFrameIO(TestCase):
48
49 @inlineCallbacks
50 def make_veth_pairs_if_needed(self):
51
52 def has_iface(iface):
53 return os.system('ip link show {}'.format(iface)) == 0
54
55 def make_veth(iface):
56 os.system('ip link add type veth')
57 os.system('ip link set {} up'.format(iface))
58 peer = iface[:len('veth')] + str(int(iface[len('veth'):]) + 1)
59 os.system('ip link set {} up'.format(peer))
60 assert has_iface(iface)
61
62 for iface_number in (0, 2):
63 iface = 'veth{}'.format(iface_number)
64 if not has_iface(iface):
65 make_veth(iface)
66 yield asleep(2)
67
68 @inlineCallbacks
69 def setUp(self):
70 yield self.make_veth_pairs_if_needed()
71 self.mgr = FrameIOManager().start()
72
73 def tearDown(self):
74 self.mgr.stop()
75
76 @inlineCallbacks
77 def test_packet_send_receive(self):
78 rcvd = DeferredWithTimeout()
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -080079 p0 = self.mgr.open_port('veth0', none).up()
80 p1 = self.mgr.open_port('veth1',
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070081 lambda p, f: rcvd.callback((p, f))).up()
82
83 # sending to veth0 should result in receiving on veth1 and vice versa
84 bogus_frame = 'bogus packet'
85 p0.send(bogus_frame)
86
87 # check that we receved packet
88 port, frame = yield rcvd
89 self.assertEqual(port, p1)
90 self.assertEqual(frame, bogus_frame)
91
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070092 @inlineCallbacks
93 def test_packet_send_receive_with_filter(self):
94 rcvd = DeferredWithTimeout()
95
96 filter = BpfProgramFilter('ip dst host 123.123.123.123')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -080097 p0 = self.mgr.open_port('veth0', none).up()
98 p1 = self.mgr.open_port('veth1',
99 lambda p, f: rcvd.callback((p, f)),
100 filter=filter).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700101
102 # sending bogus packet would not be received
103 ip_packet = str(Ether()/IP(dst='123.123.123.123'))
104 p0.send(ip_packet)
105
106 # check that we receved packet
107 port, frame = yield rcvd
108 self.assertEqual(port, p1)
109 self.assertEqual(frame, ip_packet)
110
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700111 @inlineCallbacks
112 def test_packet_send_drop_with_filter(self):
113 rcvd = DeferredWithTimeout()
114
115 filter = BpfProgramFilter('ip dst host 123.123.123.123')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800116 p0 = self.mgr.open_port('veth0', none).up()
117 self.mgr.open_port('veth1', lambda p, f: rcvd.callback((p, f)),
118 filter=filter).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700119
120 # sending bogus packet would not be received
121 p0.send('bogus packet')
122
123 try:
124 _ = yield rcvd
125 except TimeOutError:
126 pass
127 else:
128 self.fail('not timed out')
129
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700130 @inlineCallbacks
131 def test_concurrent_packet_send_receive(self):
132
133 done = Deferred()
134 queue1 = []
135 queue2 = []
136
137 n = 100
138
139 def append(queue):
140 def _append(_, frame):
141 queue.append(frame)
142 if len(queue1) == n and len(queue2) == n:
143 done.callback(None)
144 return _append
145
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800146 p1in = self.mgr.open_port('veth0', none).up()
147 self.mgr.open_port('veth1', append(queue1)).up()
148 p2in = self.mgr.open_port('veth2', none).up()
149 self.mgr.open_port('veth3', append(queue2)).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700150
151 @inlineCallbacks
152 def send_packets(port, n):
153 for i in xrange(n):
154 port.send(str(i))
155 yield asleep(0.00001 * random.random()) # to interleave
156
157 # sending two concurrent streams
158 send_packets(p1in, n)
159 send_packets(p2in, n)
160
161 # verify that both queue got all packets
162 yield done
163
164 @inlineCallbacks
165 def test_concurrent_packet_send_receive_with_filter(self):
166
167 done = Deferred()
168 queue1 = []
169 queue2 = []
170
171 n = 100
172
173 def append(queue):
174 def _append(_, frame):
175 queue.append(frame)
176 if len(queue1) == n / 2 and len(queue2) == n / 2:
177 done.callback(None)
178 return _append
179
180 filter = BpfProgramFilter('vlan 100')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800181 p1in = self.mgr.open_port('veth0', none).up()
182 self.mgr.open_port('veth1', append(queue1), filter).up()
183 p2in = self.mgr.open_port('veth2', none).up()
184 self.mgr.open_port('veth3', append(queue2), filter).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700185
186 @inlineCallbacks
187 def send_packets(port, n):
188 for i in xrange(n):
189 # packets have alternating VLAN ids 100 and 101
190 pkt = Ether()/Dot1Q(vlan=100 + i % 2)
191 port.send(str(pkt))
192 yield asleep(0.00001 * random.random()) # to interleave
193
194 # sending two concurrent streams
195 send_packets(p1in, n)
196 send_packets(p2in, n)
197
198 # verify that both queue got all packets
199 yield done
200
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800201 @inlineCallbacks
202 def test_shared_interface(self):
203
204 queue1 = DeferredQueue()
205 queue2 = DeferredQueue()
206
207 # two senders hooked up to the same interface (sharing it)
208 # here we test if they can both send
209 pin1 = self.mgr.open_port('veth0', none).up()
210 pin2 = self.mgr.open_port('veth0', none).up()
211
212 pout1 = self.mgr.open_port(
213 'veth1', lambda p, f: queue1.put((p, f))).up()
214 filter = BpfProgramFilter('ip dst host 123.123.123.123')
215 pout2 = self.mgr.open_port(
216 'veth1', lambda p, f: queue2.put((p, f)), filter=filter).up()
217
218 # sending from pin1, should be received by pout1
219 bogus_frame = 'bogus packet'
220 pin1.send(bogus_frame)
221 port, frame = yield queue1.get()
222 self.assertEqual(port, pout1)
223 self.assertEqual(frame, bogus_frame)
224 self.assertEqual(len(queue1.pending), 0)
225 self.assertEqual(len(queue2.pending), 0)
226
227 # sending from pin2, should be received by pout1
228 bogus_frame = 'bogus packet'
229 pin2.send(bogus_frame)
230 port, frame = yield queue1.get()
231 self.assertEqual(port, pout1)
232 self.assertEqual(frame, bogus_frame)
233 self.assertEqual(len(queue1.pending), 0)
234 self.assertEqual(len(queue2.pending), 0)
235
236 # sending from pin1, should be received by both pouts
237 ip_packet = str(Ether()/IP(dst='123.123.123.123'))
238 pin1.send(ip_packet)
239 port, frame = yield queue1.get()
240 self.assertEqual(port, pout1)
241 self.assertEqual(frame, ip_packet)
242 self.assertEqual(len(queue1.pending), 0)
243 port, frame = yield queue2.get()
244 self.assertEqual(port, pout2)
245 self.assertEqual(frame, ip_packet)
246 self.assertEqual(len(queue2.pending), 0)
247
248 # sending from pin2, should be received by pout1
249 ip_packet = str(Ether()/IP(dst='123.123.123.123'))
250 pin2.send(ip_packet)
251 port, frame = yield queue1.get()
252 self.assertEqual(port, pout1)
253 self.assertEqual(frame, ip_packet)
254 self.assertEqual(len(queue1.pending), 0)
255 port, frame = yield queue2.get()
256 self.assertEqual(port, pout2)
257 self.assertEqual(frame, ip_packet)
258 self.assertEqual(len(queue2.pending), 0)
259
260 self.mgr.close_port(pin1)
261 self.mgr.close_port(pin2)
262 self.mgr.close_port(pout1)
263 self.mgr.close_port(pout2)
264
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700265
266if __name__ == '__main__':
267 import unittest
268 unittest.main()