blob: 0d64f1fd765f73687c10829c995fbe39e5817eb3 [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
David K. Bainbridgebba65ff2018-01-19 09:26:09 -080021docker run -ti --rm -v $(pwd):/voltha --privileged voltha/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, \
khenaidoo9b9b7a32017-11-17 12:38:57 -050039 FrameIOPortProxy, FrameIOPort
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):
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070048 @inlineCallbacks
49 def make_veth_pairs_if_needed(self):
50
51 def has_iface(iface):
52 return os.system('ip link show {}'.format(iface)) == 0
53
54 def make_veth(iface):
55 os.system('ip link add type veth')
56 os.system('ip link set {} up'.format(iface))
57 peer = iface[:len('veth')] + str(int(iface[len('veth'):]) + 1)
58 os.system('ip link set {} up'.format(peer))
59 assert has_iface(iface)
60
61 for iface_number in (0, 2):
62 iface = 'veth{}'.format(iface_number)
63 if not has_iface(iface):
64 make_veth(iface)
65 yield asleep(2)
66
67 @inlineCallbacks
68 def setUp(self):
69 yield self.make_veth_pairs_if_needed()
70 self.mgr = FrameIOManager().start()
71
72 def tearDown(self):
73 self.mgr.stop()
74
75 @inlineCallbacks
76 def test_packet_send_receive(self):
77 rcvd = DeferredWithTimeout()
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -080078 p0 = self.mgr.open_port('veth0', none).up()
79 p1 = self.mgr.open_port('veth1',
khenaidoo9b9b7a32017-11-17 12:38:57 -050080 lambda p, f: rcvd.callback((p, f))).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070081
82 # sending to veth0 should result in receiving on veth1 and vice versa
83 bogus_frame = 'bogus packet'
khenaidoo9b9b7a32017-11-17 12:38:57 -050084 bogus_frame_padded = bogus_frame + '\x00' * (
85 FrameIOPort.MIN_PKT_SIZE - len(bogus_frame))
86 p0.send(bogus_frame_padded)
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070087
88 # check that we receved packet
89 port, frame = yield rcvd
90 self.assertEqual(port, p1)
khenaidoo9b9b7a32017-11-17 12:38:57 -050091 self.assertEqual(frame, bogus_frame_padded)
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070092
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070093 @inlineCallbacks
94 def test_packet_send_receive_with_filter(self):
95 rcvd = DeferredWithTimeout()
96
97 filter = BpfProgramFilter('ip dst host 123.123.123.123')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -080098 p0 = self.mgr.open_port('veth0', none).up()
99 p1 = self.mgr.open_port('veth1',
100 lambda p, f: rcvd.callback((p, f)),
101 filter=filter).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700102
103 # sending bogus packet would not be received
khenaidoo9b9b7a32017-11-17 12:38:57 -0500104 ip_packet = str(Ether() / IP(dst='123.123.123.123'))
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700105 p0.send(ip_packet)
106
107 # check that we receved packet
108 port, frame = yield rcvd
109 self.assertEqual(port, p1)
110 self.assertEqual(frame, ip_packet)
111
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700112 @inlineCallbacks
113 def test_packet_send_drop_with_filter(self):
114 rcvd = DeferredWithTimeout()
115
116 filter = BpfProgramFilter('ip dst host 123.123.123.123')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800117 p0 = self.mgr.open_port('veth0', none).up()
118 self.mgr.open_port('veth1', lambda p, f: rcvd.callback((p, f)),
119 filter=filter).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700120
121 # sending bogus packet would not be received
122 p0.send('bogus packet')
123
124 try:
125 _ = yield rcvd
126 except TimeOutError:
127 pass
128 else:
129 self.fail('not timed out')
130
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700131 @inlineCallbacks
132 def test_concurrent_packet_send_receive(self):
133
134 done = Deferred()
135 queue1 = []
136 queue2 = []
137
138 n = 100
139
140 def append(queue):
141 def _append(_, frame):
142 queue.append(frame)
143 if len(queue1) == n and len(queue2) == n:
144 done.callback(None)
khenaidoo9b9b7a32017-11-17 12:38:57 -0500145
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700146 return _append
147
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800148 p1in = self.mgr.open_port('veth0', none).up()
149 self.mgr.open_port('veth1', append(queue1)).up()
150 p2in = self.mgr.open_port('veth2', none).up()
151 self.mgr.open_port('veth3', append(queue2)).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700152
153 @inlineCallbacks
154 def send_packets(port, n):
155 for i in xrange(n):
156 port.send(str(i))
157 yield asleep(0.00001 * random.random()) # to interleave
158
159 # sending two concurrent streams
160 send_packets(p1in, n)
161 send_packets(p2in, n)
162
163 # verify that both queue got all packets
164 yield done
165
166 @inlineCallbacks
167 def test_concurrent_packet_send_receive_with_filter(self):
168
169 done = Deferred()
170 queue1 = []
171 queue2 = []
172
173 n = 100
174
175 def append(queue):
176 def _append(_, frame):
177 queue.append(frame)
178 if len(queue1) == n / 2 and len(queue2) == n / 2:
179 done.callback(None)
khenaidoo9b9b7a32017-11-17 12:38:57 -0500180
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700181 return _append
182
183 filter = BpfProgramFilter('vlan 100')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800184 p1in = self.mgr.open_port('veth0', none).up()
185 self.mgr.open_port('veth1', append(queue1), filter).up()
186 p2in = self.mgr.open_port('veth2', none).up()
187 self.mgr.open_port('veth3', append(queue2), filter).up()
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700188
189 @inlineCallbacks
190 def send_packets(port, n):
191 for i in xrange(n):
192 # packets have alternating VLAN ids 100 and 101
khenaidoo9b9b7a32017-11-17 12:38:57 -0500193 pkt = Ether() / Dot1Q(vlan=100 + i % 2)
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700194 port.send(str(pkt))
195 yield asleep(0.00001 * random.random()) # to interleave
196
197 # sending two concurrent streams
198 send_packets(p1in, n)
199 send_packets(p2in, n)
200
201 # verify that both queue got all packets
202 yield done
203
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800204 @inlineCallbacks
205 def test_shared_interface(self):
206
207 queue1 = DeferredQueue()
208 queue2 = DeferredQueue()
209
210 # two senders hooked up to the same interface (sharing it)
211 # here we test if they can both send
212 pin1 = self.mgr.open_port('veth0', none).up()
213 pin2 = self.mgr.open_port('veth0', none).up()
214
215 pout1 = self.mgr.open_port(
216 'veth1', lambda p, f: queue1.put((p, f))).up()
217 filter = BpfProgramFilter('ip dst host 123.123.123.123')
218 pout2 = self.mgr.open_port(
219 'veth1', lambda p, f: queue2.put((p, f)), filter=filter).up()
220
221 # sending from pin1, should be received by pout1
222 bogus_frame = 'bogus packet'
khenaidoo9b9b7a32017-11-17 12:38:57 -0500223 bogus_frame_padded = bogus_frame + '\x00' * (
224 FrameIOPort.MIN_PKT_SIZE - len(bogus_frame))
225 pin1.send(bogus_frame_padded)
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800226 port, frame = yield queue1.get()
227 self.assertEqual(port, pout1)
khenaidoo9b9b7a32017-11-17 12:38:57 -0500228 self.assertEqual(frame, bogus_frame_padded)
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800229 self.assertEqual(len(queue1.pending), 0)
230 self.assertEqual(len(queue2.pending), 0)
231
232 # sending from pin2, should be received by pout1
khenaidoo9b9b7a32017-11-17 12:38:57 -0500233 pin2.send(bogus_frame_padded)
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800234 port, frame = yield queue1.get()
235 self.assertEqual(port, pout1)
khenaidoo9b9b7a32017-11-17 12:38:57 -0500236 self.assertEqual(frame, bogus_frame_padded)
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800237 self.assertEqual(len(queue1.pending), 0)
238 self.assertEqual(len(queue2.pending), 0)
239
240 # sending from pin1, should be received by both pouts
khenaidoo9b9b7a32017-11-17 12:38:57 -0500241 ip_packet = str(Ether() / IP(dst='123.123.123.123'))
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800242 pin1.send(ip_packet)
243 port, frame = yield queue1.get()
244 self.assertEqual(port, pout1)
245 self.assertEqual(frame, ip_packet)
246 self.assertEqual(len(queue1.pending), 0)
247 port, frame = yield queue2.get()
248 self.assertEqual(port, pout2)
249 self.assertEqual(frame, ip_packet)
250 self.assertEqual(len(queue2.pending), 0)
251
252 # sending from pin2, should be received by pout1
khenaidoo9b9b7a32017-11-17 12:38:57 -0500253 ip_packet = str(Ether() / IP(dst='123.123.123.123'))
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800254 pin2.send(ip_packet)
255 port, frame = yield queue1.get()
256 self.assertEqual(port, pout1)
257 self.assertEqual(frame, ip_packet)
258 self.assertEqual(len(queue1.pending), 0)
259 port, frame = yield queue2.get()
260 self.assertEqual(port, pout2)
261 self.assertEqual(frame, ip_packet)
262 self.assertEqual(len(queue2.pending), 0)
263
264 self.mgr.close_port(pin1)
265 self.mgr.close_port(pin2)
266 self.mgr.close_port(pout1)
267 self.mgr.close_port(pout2)
268
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700269
270if __name__ == '__main__':
271 import unittest
khenaidoo9b9b7a32017-11-17 12:38:57 -0500272
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700273 unittest.main()