1# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
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
12# implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import unittest
17import logging
18try:
19    import mock  # Python 2
20except ImportError:
21    from unittest import mock  # Python 3
22
23from nose.tools import eq_
24
25from ryu.lib.packet import bgp
26from ryu.services.protocols.bgp import peer
27
28
29LOG = logging.getLogger(__name__)
30
31
32class Test_Peer(unittest.TestCase):
33    """
34    Test case for peer.Peer
35    """
36
37    @mock.patch.object(
38        peer.Peer, '__init__', mock.MagicMock(return_value=None))
39    def _test_construct_as_path_attr(
40            self, input_as_path, input_as4_path, expected_as_path):
41        # Prepare input data
42        input_as_path_attr = bgp.BGPPathAttributeAsPath(input_as_path)
43        input_as4_path_attr = bgp.BGPPathAttributeAs4Path(input_as4_path)
44        _peer = peer.Peer(None, None, None, None, None)
45
46        # TEST
47        output_as_path_attr = _peer._construct_as_path_attr(
48            input_as_path_attr, input_as4_path_attr)
49
50        eq_(bgp.BGP_ATTR_TYPE_AS_PATH, output_as_path_attr.type)
51        eq_(expected_as_path, output_as_path_attr.path_seg_list)
52
53    def test_construct_as_path_attr_sequence_only(self):
54        # Test Data
55        # Input:
56        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
57        input_as4_path = [[400000, 300000, 40001]]
58        # Expected:
59        expected_as_path = [[65000, 4000, 400000, 300000, 40001]]
60
61        self._test_construct_as_path_attr(
62            input_as_path, input_as4_path, expected_as_path)
63
64    def test_construct_as_path_attr_aggregated_as_path_1(self):
65        # Test Data
66        # Input:
67        input_as_path = [[65000, 4000], {10, 20, 30}, [23456, 23456, 40001]]
68        input_as4_path = [[400000, 300000, 40001]]
69        # Expected:
70        expected_as_path = [[65000, 4000], {10, 20, 30}, [400000, 300000, 40001]]
71
72        self._test_construct_as_path_attr(
73            input_as_path, input_as4_path, expected_as_path)
74
75    def test_construct_as_path_attr_aggregated_as_path_2(self):
76        # Test Data
77        # Input:
78        input_as_path = [[65000, 4000], {10, 20, 30}, [23456, 23456, 40001]]
79        input_as4_path = [[3000, 400000, 300000, 40001]]
80        # Expected:
81        expected_as_path = [[65000, 4000, 3000, 400000, 300000, 40001]]
82
83        self._test_construct_as_path_attr(
84            input_as_path, input_as4_path, expected_as_path)
85
86    def test_construct_as_path_attr_aggregated_path_3(self):
87        # Test Data
88        # Input:
89        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
90        input_as4_path = [[400000, 300000, 40001], {10, 20, 30}]
91        # Expected:
92        expected_as_path = [[65000, 400000, 300000, 40001], {10, 20, 30}]
93
94        self._test_construct_as_path_attr(
95            input_as_path, input_as4_path, expected_as_path)
96
97    def test_construct_as_path_attr_aggregated_as4_path(self):
98        # Test Data
99        # Input:
100        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
101        input_as4_path = [{10, 20, 30}, [400000, 300000, 40001]]
102        # Expected:
103        expected_as_path = [[65000], {10, 20, 30}, [400000, 300000, 40001]]
104
105        self._test_construct_as_path_attr(
106            input_as_path, input_as4_path, expected_as_path)
107
108    def test_construct_as_path_attr_too_short_as_path(self):
109        # Test Data
110        # Input:
111        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
112        input_as4_path = [[100000, 65000, 4000, 400000, 300000, 40001]]
113        # Expected:
114        expected_as_path = [[65000, 4000, 23456, 23456, 40001]]
115
116        self._test_construct_as_path_attr(
117            input_as_path, input_as4_path, expected_as_path)
118
119    def test_construct_as_path_attr_too_short_as4_path(self):
120        # Test Data
121        # Input:
122        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
123        input_as4_path = [[300000, 40001]]
124        # Expected:
125        expected_as_path = [[65000, 4000, 23456, 300000, 40001]]
126
127        self._test_construct_as_path_attr(
128            input_as_path, input_as4_path, expected_as_path)
129
130    def test_construct_as_path_attr_empty_as4_path(self):
131        # Test Data
132        # Input:
133        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
134        input_as4_path = [[]]
135        # Expected:
136        expected_as_path = [[65000, 4000, 23456, 23456, 40001]]
137
138        self._test_construct_as_path_attr(
139            input_as_path, input_as4_path, expected_as_path)
140
141    @mock.patch.object(
142        peer.Peer, '__init__', mock.MagicMock(return_value=None))
143    def test_construct_as_path_attr_as4_path_None(self):
144        # Test Data
145        # Input:
146        input_as_path = [[65000, 4000, 23456, 23456, 40001]]
147        # input_as4_path = None
148        # Expected:
149        expected_as_path = [[65000, 4000, 23456, 23456, 40001]]
150
151        # Prepare input data
152        input_as_path_attr = bgp.BGPPathAttributeAsPath(input_as_path)
153        input_as4_path_attr = None
154        _peer = peer.Peer(None, None, None, None, None)
155
156        # TEST
157        output_as_path_attr = _peer._construct_as_path_attr(
158            input_as_path_attr, input_as4_path_attr)
159
160        eq_(bgp.BGP_ATTR_TYPE_AS_PATH, output_as_path_attr.type)
161        eq_(expected_as_path, output_as_path_attr.path_seg_list)
162
163    @mock.patch.object(
164        peer.Peer, '__init__', mock.MagicMock(return_value=None))
165    def _test_trans_as_path(
166            self, input_as_path, expected_as_path, expected_as4_path):
167        # Prepare input data
168        _peer = peer.Peer(None, None, None, None, None)
169
170        # TEST
171        output_as_path, output_as4_path = _peer._trans_as_path(input_as_path)
172
173        eq_(expected_as_path, output_as_path)
174        eq_(expected_as4_path, output_as4_path)
175
176    @mock.patch.object(
177        peer.Peer, 'is_four_octet_as_number_cap_valid',
178        mock.MagicMock(return_value=True))
179    def test_trans_as_path_as4_path_is_supported(self):
180        # Test Data
181        # Input:
182        input_as_path = [[65000, 4000, 400000, 300000, 40001]]
183        # Expected:
184        expected_as_path = [[65000, 4000, 400000, 300000, 40001]]
185        expected_as4_path = None
186
187        self._test_trans_as_path(
188            input_as_path, expected_as_path, expected_as4_path)
189
190    @mock.patch.object(
191        peer.Peer, 'is_four_octet_as_number_cap_valid',
192        mock.MagicMock(return_value=False))
193    def test_trans_as_path_sequence_only(self):
194        # Test Data
195        # Input:
196        input_as_path = [[65000, 4000, 400000, 300000, 40001]]
197        # Expected:
198        expected_as_path = [[65000, 4000, 23456, 23456, 40001]]
199        expected_as4_path = [[65000, 4000, 400000, 300000, 40001]]
200
201        self._test_trans_as_path(
202            input_as_path, expected_as_path, expected_as4_path)
203
204    @mock.patch.object(
205        peer.Peer, 'is_four_octet_as_number_cap_valid',
206        mock.MagicMock(return_value=False))
207    def test_trans_as_path_no_trans(self):
208        # Test Data
209        # Input:
210        input_as_path = [[65000, 4000, 40000, 30000, 40001]]
211        # Expected:
212        expected_as_path = [[65000, 4000, 40000, 30000, 40001]]
213        expected_as4_path = None
214
215        self._test_trans_as_path(
216            input_as_path, expected_as_path, expected_as4_path)
217
218    @mock.patch.object(
219        peer.Peer, '__init__', mock.MagicMock(return_value=None))
220    def _test_extract_and_reconstruct_as_path(
221            self, path_attributes, ex_as_path_value,
222            ex_aggregator_as_number, ex_aggregator_addr):
223        # Prepare test data
224        update_msg = bgp.BGPUpdate(path_attributes=path_attributes)
225        _peer = peer.Peer(None, None, None, None, None)
226
227        # Test
228        _peer._extract_and_reconstruct_as_path(update_msg)
229
230        umsg_pattrs = update_msg.pathattr_map
231        as_path_attr = umsg_pattrs.get(
232            bgp.BGP_ATTR_TYPE_AS_PATH, None)
233        as4_path_attr = umsg_pattrs.get(
234            bgp.BGP_ATTR_TYPE_AS4_PATH, None)
235        aggregator_attr = umsg_pattrs.get(
236            bgp.BGP_ATTR_TYPE_AGGREGATOR, None)
237        as4_aggregator_attr = umsg_pattrs.get(
238            bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR, None)
239
240        eq_(ex_as_path_value, as_path_attr.value)
241        eq_(None, as4_path_attr)
242        eq_(ex_aggregator_as_number, aggregator_attr.as_number)
243        eq_(ex_aggregator_addr, aggregator_attr.addr)
244        eq_(None, as4_aggregator_attr)
245
246    @mock.patch.object(
247        peer.Peer, '__init__', mock.MagicMock(return_value=None))
248    def test_extract_and_reconstruct_as_path_with_no_as4_attr(self):
249        # Input values
250        in_as_path_value = [[1000, 2000, 3000]]
251        # in_as4_path_value
252        in_aggregator_as_number = 4000
253        in_aggregator_addr = '10.0.0.1'
254        # in_as4_aggregator_as_number
255        # in_as4_aggregator_addr
256
257        # Expected values
258        ex_as_path_value = [[1000, 2000, 3000]]
259        ex_aggregator_as_number = 4000
260        ex_aggregator_addr = '10.0.0.1'
261
262        # Prepare test data
263        path_attributes = [
264            bgp.BGPPathAttributeAsPath(
265                value=in_as_path_value),
266            bgp.BGPPathAttributeAggregator(
267                as_number=in_aggregator_as_number, addr=in_aggregator_addr),
268        ]
269
270        # Test
271        self._test_extract_and_reconstruct_as_path(
272            path_attributes, ex_as_path_value,
273            ex_aggregator_as_number, ex_aggregator_addr)
274
275    @mock.patch.object(
276        peer.Peer, '__init__', mock.MagicMock(return_value=None))
277    def test_extract_and_reconstruct_as_path_with_as4_attr(self):
278        # Input values
279        in_as_path_value = [[1000, 23456, 3000]]
280        in_as4_path_value = [[2000, 3000]]
281        in_aggregator_as_number = 23456
282        in_aggregator_addr = '10.0.0.1'
283        in_as4_aggregator_as_number = 4000
284        in_as4_aggregator_addr = '10.0.0.1'
285
286        # Expected values
287        ex_as_path_value = [[1000, 2000, 3000]]
288        ex_aggregator_as_number = 4000
289        ex_aggregator_addr = '10.0.0.1'
290
291        # Prepare test data
292        path_attributes = [
293            bgp.BGPPathAttributeAsPath(
294                value=in_as_path_value),
295            bgp.BGPPathAttributeAs4Path(
296                value=in_as4_path_value),
297            bgp.BGPPathAttributeAggregator(
298                as_number=in_aggregator_as_number,
299                addr=in_aggregator_addr),
300            bgp.BGPPathAttributeAs4Aggregator(
301                as_number=in_as4_aggregator_as_number,
302                addr=in_as4_aggregator_addr),
303        ]
304
305        # Test
306        self._test_extract_and_reconstruct_as_path(
307            path_attributes, ex_as_path_value,
308            ex_aggregator_as_number, ex_aggregator_addr)
309
310    @mock.patch.object(
311        peer.Peer, '__init__', mock.MagicMock(return_value=None))
312    def test_extract_and_reconstruct_as_path_with_not_trans_as_aggr(self):
313        # Input values
314        in_as_path_value = [[1000, 23456, 3000]]
315        in_as4_path_value = [[2000, 3000]]
316        in_aggregator_as_number = 4000  # not AS_TRANS
317        in_aggregator_addr = '10.0.0.1'
318        in_as4_aggregator_as_number = 4000
319        in_as4_aggregator_addr = '10.0.0.1'
320
321        # Expected values
322        ex_as_path_value = [[1000, 23456, 3000]]
323        ex_aggregator_as_number = 4000
324        ex_aggregator_addr = '10.0.0.1'
325
326        # Prepare test data
327        path_attributes = [
328            bgp.BGPPathAttributeAsPath(
329                value=in_as_path_value),
330            bgp.BGPPathAttributeAs4Path(
331                value=in_as4_path_value),
332            bgp.BGPPathAttributeAggregator(
333                as_number=in_aggregator_as_number,
334                addr=in_aggregator_addr),
335            bgp.BGPPathAttributeAs4Aggregator(
336                as_number=in_as4_aggregator_as_number,
337                addr=in_as4_aggregator_addr),
338        ]
339
340        # Test
341        self._test_extract_and_reconstruct_as_path(
342            path_attributes, ex_as_path_value,
343            ex_aggregator_as_number, ex_aggregator_addr)
344
345    @mock.patch.object(
346        peer.Peer, '__init__', mock.MagicMock(return_value=None))
347    def test_extract_and_reconstruct_as_path_with_short_as_path(self):
348        # Input values
349        in_as_path_value = [[1000, 23456, 3000]]
350        in_as4_path_value = [[2000, 3000, 4000, 5000]]  # longer than AS_PATH
351        in_aggregator_as_number = 4000
352        in_aggregator_addr = '10.0.0.1'
353        # in_as4_aggregator_as_number
354        # in_as4_aggregator_addr
355
356        # Expected values
357        ex_as_path_value = [[1000, 23456, 3000]]
358        ex_aggregator_as_number = 4000
359        ex_aggregator_addr = '10.0.0.1'
360
361        # Prepare test data
362        path_attributes = [
363            bgp.BGPPathAttributeAsPath(
364                value=in_as_path_value),
365            bgp.BGPPathAttributeAs4Path(
366                value=in_as4_path_value),
367            bgp.BGPPathAttributeAggregator(
368                as_number=in_aggregator_as_number,
369                addr=in_aggregator_addr),
370        ]
371
372        # Test
373        self._test_extract_and_reconstruct_as_path(
374            path_attributes, ex_as_path_value,
375            ex_aggregator_as_number, ex_aggregator_addr)
376