blob: 057fa28d64f470b9ea886d77a9026f7d3a5c17a2 [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
34from twisted.internet.defer import Deferred, inlineCallbacks
35from twisted.internet.error import AlreadyCalled
36from twisted.trial.unittest import TestCase
37
38from common.frameio.frameio import FrameIOManager, BpfProgramFilter
39from common.utils.asleep import asleep
40from common.utils.deferred_utils import DeferredWithTimeout, TimeOutError
41
42ident = lambda frame: frame
43none = lambda *args, **kw: None
44
45
46class TestFrameIO(TestCase):
47
48 @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()
78 p0 = self.mgr.add_interface('veth0', none).up()
79 p1 = self.mgr.add_interface('veth1',
80 lambda p, f: rcvd.callback((p, f))).up()
81
82 # sending to veth0 should result in receiving on veth1 and vice versa
83 bogus_frame = 'bogus packet'
84 p0.send(bogus_frame)
85
86 # check that we receved packet
87 port, frame = yield rcvd
88 self.assertEqual(port, p1)
89 self.assertEqual(frame, bogus_frame)
90
Zsolt Haraszti91350eb2016-11-05 15:33:53 -070091 @inlineCallbacks
92 def test_packet_send_receive_with_filter(self):
93 rcvd = DeferredWithTimeout()
94
95 filter = BpfProgramFilter('ip dst host 123.123.123.123')
96 p0 = self.mgr.add_interface('veth0', none).up()
97 p1 = self.mgr.add_interface('veth1',
98 lambda p, f: rcvd.callback((p, f)),
99 filter=filter).up()
100
101 # sending bogus packet would not be received
102 ip_packet = str(Ether()/IP(dst='123.123.123.123'))
103 p0.send(ip_packet)
104
105 # check that we receved packet
106 port, frame = yield rcvd
107 self.assertEqual(port, p1)
108 self.assertEqual(frame, ip_packet)
109
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700110 @inlineCallbacks
111 def test_packet_send_drop_with_filter(self):
112 rcvd = DeferredWithTimeout()
113
114 filter = BpfProgramFilter('ip dst host 123.123.123.123')
115 p0 = self.mgr.add_interface('veth0', none).up()
116 self.mgr.add_interface('veth1', lambda p, f: rcvd.callback((p, f)),
117 filter=filter).up()
118
119 # sending bogus packet would not be received
120 p0.send('bogus packet')
121
122 try:
123 _ = yield rcvd
124 except TimeOutError:
125 pass
126 else:
127 self.fail('not timed out')
128
Zsolt Haraszti91350eb2016-11-05 15:33:53 -0700129 @inlineCallbacks
130 def test_concurrent_packet_send_receive(self):
131
132 done = Deferred()
133 queue1 = []
134 queue2 = []
135
136 n = 100
137
138 def append(queue):
139 def _append(_, frame):
140 queue.append(frame)
141 if len(queue1) == n and len(queue2) == n:
142 done.callback(None)
143 return _append
144
145 p1in = self.mgr.add_interface('veth0', none).up()
146 self.mgr.add_interface('veth1', append(queue1)).up()
147 p2in = self.mgr.add_interface('veth2', none).up()
148 self.mgr.add_interface('veth3', append(queue2)).up()
149
150 @inlineCallbacks
151 def send_packets(port, n):
152 for i in xrange(n):
153 port.send(str(i))
154 yield asleep(0.00001 * random.random()) # to interleave
155
156 # sending two concurrent streams
157 send_packets(p1in, n)
158 send_packets(p2in, n)
159
160 # verify that both queue got all packets
161 yield done
162
163 @inlineCallbacks
164 def test_concurrent_packet_send_receive_with_filter(self):
165
166 done = Deferred()
167 queue1 = []
168 queue2 = []
169
170 n = 100
171
172 def append(queue):
173 def _append(_, frame):
174 queue.append(frame)
175 if len(queue1) == n / 2 and len(queue2) == n / 2:
176 done.callback(None)
177 return _append
178
179 filter = BpfProgramFilter('vlan 100')
180 p1in = self.mgr.add_interface('veth0', none).up()
181 self.mgr.add_interface('veth1', append(queue1), filter).up()
182 p2in = self.mgr.add_interface('veth2', none).up()
183 self.mgr.add_interface('veth3', append(queue2), filter).up()
184
185 @inlineCallbacks
186 def send_packets(port, n):
187 for i in xrange(n):
188 # packets have alternating VLAN ids 100 and 101
189 pkt = Ether()/Dot1Q(vlan=100 + i % 2)
190 port.send(str(pkt))
191 yield asleep(0.00001 * random.random()) # to interleave
192
193 # sending two concurrent streams
194 send_packets(p1in, n)
195 send_packets(p2in, n)
196
197 # verify that both queue got all packets
198 yield done
199
200
201if __name__ == '__main__':
202 import unittest
203 unittest.main()