| #!/usr/bin/env python3 |
| |
| import socket |
| import unittest |
| |
| from scapy.packet import Raw |
| from scapy.layers.l2 import Ether |
| from scapy.layers.ppp import PPPoE, PPPoED, PPP |
| from scapy.layers.inet import IP |
| |
| from framework import VppTestCase |
| from asfframework import VppTestRunner |
| from vpp_ip_route import VppIpRoute, VppRoutePath |
| from vpp_pppoe_interface import VppPppoeInterface |
| from util import ppp |
| from config import config |
| |
| |
| @unittest.skipIf("pppoe" in config.excluded_plugins, "Exclude PPPoE plugin tests") |
| class TestPPPoE(VppTestCase): |
| """PPPoE Test Case""" |
| |
| @classmethod |
| def setUpClass(cls): |
| super(TestPPPoE, cls).setUpClass() |
| |
| cls.session_id = 1 |
| cls.dst_ip = "100.1.1.100" |
| cls.dst_ipn = socket.inet_pton(socket.AF_INET, cls.dst_ip) |
| |
| @classmethod |
| def tearDownClass(cls): |
| super(TestPPPoE, cls).tearDownClass() |
| |
| def setUp(self): |
| super(TestPPPoE, self).setUp() |
| |
| # create 2 pg interfaces |
| self.create_pg_interfaces(range(3)) |
| |
| for i in self.pg_interfaces: |
| i.admin_up() |
| i.config_ip4() |
| i.resolve_arp() |
| |
| def tearDown(self): |
| super(TestPPPoE, self).tearDown() |
| |
| for i in self.pg_interfaces: |
| i.unconfig_ip4() |
| i.admin_down() |
| |
| def show_commands_at_teardown(self): |
| self.logger.info(self.vapi.cli("show int")) |
| self.logger.info(self.vapi.cli("show pppoe fib")) |
| self.logger.info(self.vapi.cli("show pppoe session")) |
| self.logger.info(self.vapi.cli("show ip fib")) |
| self.logger.info(self.vapi.cli("show trace")) |
| |
| def create_stream_pppoe_discovery(self, src_if, dst_if, client_mac, count=1): |
| packets = [] |
| for i in range(count): |
| # create packet info stored in the test case instance |
| info = self.create_packet_info(src_if, dst_if) |
| # convert the info into packet payload |
| payload = self.info_to_payload(info) |
| # create the packet itself |
| p = ( |
| Ether(dst=src_if.local_mac, src=client_mac) |
| / PPPoED(sessionid=0) |
| / Raw(payload) |
| ) |
| # store a copy of the packet in the packet info |
| info.data = p.copy() |
| # append the packet to the list |
| packets.append(p) |
| |
| # return the created packet list |
| return packets |
| |
| def create_stream_pppoe_lcp(self, src_if, dst_if, client_mac, session_id, count=1): |
| packets = [] |
| for i in range(count): |
| # create packet info stored in the test case instance |
| info = self.create_packet_info(src_if, dst_if) |
| # convert the info into packet payload |
| payload = self.info_to_payload(info) |
| # create the packet itself |
| p = ( |
| Ether(dst=src_if.local_mac, src=client_mac) |
| / PPPoE(sessionid=session_id) |
| / PPP(proto=0xC021) |
| / Raw(payload) |
| ) |
| # store a copy of the packet in the packet info |
| info.data = p.copy() |
| # append the packet to the list |
| packets.append(p) |
| |
| # return the created packet list |
| return packets |
| |
| def create_stream_pppoe_ip4( |
| self, src_if, dst_if, client_mac, session_id, client_ip, count=1 |
| ): |
| packets = [] |
| for i in range(count): |
| # create packet info stored in the test case instance |
| info = self.create_packet_info(src_if, dst_if) |
| # convert the info into packet payload |
| payload = self.info_to_payload(info) |
| # create the packet itself |
| p = ( |
| Ether(dst=src_if.local_mac, src=client_mac) |
| / PPPoE(sessionid=session_id) |
| / PPP(proto=0x0021) |
| / IP(src=client_ip, dst=self.dst_ip) |
| / Raw(payload) |
| ) |
| # store a copy of the packet in the packet info |
| info.data = p.copy() |
| # append the packet to the list |
| packets.append(p) |
| |
| # return the created packet list |
| return packets |
| |
| def create_stream_ip4(self, src_if, dst_if, client_ip, dst_ip, count=1): |
| pkts = [] |
| for i in range(count): |
| # create packet info stored in the test case instance |
| info = self.create_packet_info(src_if, dst_if) |
| # convert the info into packet payload |
| payload = self.info_to_payload(info) |
| # create the packet itself |
| p = ( |
| Ether(dst=src_if.local_mac, src=src_if.remote_mac) |
| / IP(src=dst_ip, dst=client_ip) |
| / Raw(payload) |
| ) |
| # store a copy of the packet in the packet info |
| info.data = p.copy() |
| # append the packet to the list |
| pkts.append(p) |
| |
| # return the created packet list |
| return pkts |
| |
| def verify_decapped_pppoe(self, src_if, capture, sent): |
| self.assertEqual(len(capture), len(sent)) |
| |
| for i in range(len(capture)): |
| try: |
| tx = sent[i] |
| rx = capture[i] |
| |
| tx_ip = tx[IP] |
| rx_ip = rx[IP] |
| |
| self.assertEqual(rx_ip.src, tx_ip.src) |
| self.assertEqual(rx_ip.dst, tx_ip.dst) |
| |
| except: |
| self.logger.error(ppp("Rx:", rx)) |
| self.logger.error(ppp("Tx:", tx)) |
| raise |
| |
| def verify_encaped_pppoe(self, src_if, capture, sent, session_id): |
| self.assertEqual(len(capture), len(sent)) |
| |
| for i in range(len(capture)): |
| try: |
| tx = sent[i] |
| rx = capture[i] |
| |
| tx_ip = tx[IP] |
| rx_ip = rx[IP] |
| |
| self.assertEqual(rx_ip.src, tx_ip.src) |
| self.assertEqual(rx_ip.dst, tx_ip.dst) |
| |
| rx_pppoe = rx[PPPoE] |
| |
| self.assertEqual(rx_pppoe.sessionid, session_id) |
| |
| except: |
| self.logger.error(ppp("Rx:", rx)) |
| self.logger.error(ppp("Tx:", tx)) |
| raise |
| |
| def test_PPPoE_Decap(self): |
| """PPPoE Decap Test""" |
| |
| self.vapi.cli("clear trace") |
| |
| # |
| # Add a route that resolves the server's destination |
| # |
| route_sever_dst = VppIpRoute( |
| self, |
| "100.1.1.100", |
| 32, |
| [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], |
| ) |
| route_sever_dst.add_vpp_config() |
| |
| # Send PPPoE Discovery |
| tx0 = self.create_stream_pppoe_discovery( |
| self.pg0, self.pg1, self.pg0.remote_mac |
| ) |
| self.pg0.add_stream(tx0) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP |
| tx1 = self.create_stream_pppoe_lcp( |
| self.pg0, self.pg1, self.pg0.remote_mac, self.session_id |
| ) |
| self.pg0.add_stream(tx1) |
| self.pg_start() |
| |
| # Create PPPoE session |
| pppoe_if = VppPppoeInterface( |
| self, self.pg0.remote_ip4, self.pg0.remote_mac, self.session_id |
| ) |
| pppoe_if.add_vpp_config() |
| pppoe_if.set_unnumbered(self.pg0.sw_if_index) |
| # |
| # Send tunneled packets that match the created tunnel and |
| # are decapped and forwarded |
| # |
| tx2 = self.create_stream_pppoe_ip4( |
| self.pg0, |
| self.pg1, |
| self.pg0.remote_mac, |
| self.session_id, |
| self.pg0.remote_ip4, |
| ) |
| self.pg0.add_stream(tx2) |
| |
| self.pg_enable_capture(self.pg_interfaces) |
| self.pg_start() |
| |
| rx2 = self.pg1.get_capture(len(tx2)) |
| self.verify_decapped_pppoe(self.pg0, rx2, tx2) |
| |
| self.logger.info(self.vapi.cli("show pppoe fib")) |
| self.logger.info(self.vapi.cli("show pppoe session")) |
| self.logger.info(self.vapi.cli("show ip fib")) |
| |
| # |
| # test case cleanup |
| # |
| |
| # Delete PPPoE session |
| pppoe_if.remove_vpp_config() |
| |
| # Delete a route that resolves the server's destination |
| route_sever_dst.remove_vpp_config() |
| |
| def test_PPPoE_Encap(self): |
| """PPPoE Encap Test""" |
| |
| self.vapi.cli("clear trace") |
| |
| # |
| # Add a route that resolves the server's destination |
| # |
| route_sever_dst = VppIpRoute( |
| self, |
| "100.1.1.100", |
| 32, |
| [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], |
| ) |
| route_sever_dst.add_vpp_config() |
| |
| # Send PPPoE Discovery |
| tx0 = self.create_stream_pppoe_discovery( |
| self.pg0, self.pg1, self.pg0.remote_mac |
| ) |
| self.pg0.add_stream(tx0) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP |
| tx1 = self.create_stream_pppoe_lcp( |
| self.pg0, self.pg1, self.pg0.remote_mac, self.session_id |
| ) |
| self.pg0.add_stream(tx1) |
| self.pg_start() |
| |
| # Create PPPoE session |
| pppoe_if = VppPppoeInterface( |
| self, self.pg0.remote_ip4, self.pg0.remote_mac, self.session_id |
| ) |
| pppoe_if.add_vpp_config() |
| pppoe_if.set_unnumbered(self.pg0.sw_if_index) |
| |
| # |
| # Send a packet stream that is routed into the session |
| # - packets are PPPoE encapped |
| # |
| self.vapi.cli("clear trace") |
| tx2 = self.create_stream_ip4( |
| self.pg1, self.pg0, self.pg0.remote_ip4, self.dst_ip, 65 |
| ) |
| self.pg1.add_stream(tx2) |
| |
| self.pg_enable_capture(self.pg_interfaces) |
| self.pg_start() |
| |
| rx2 = self.pg0.get_capture(len(tx2)) |
| self.verify_encaped_pppoe(self.pg1, rx2, tx2, self.session_id) |
| |
| self.logger.info(self.vapi.cli("show pppoe fib")) |
| self.logger.info(self.vapi.cli("show pppoe session")) |
| self.logger.info(self.vapi.cli("show ip fib")) |
| self.logger.info(self.vapi.cli("show adj")) |
| |
| # |
| # test case cleanup |
| # |
| |
| # Delete PPPoE session |
| pppoe_if.remove_vpp_config() |
| |
| # Delete a route that resolves the server's destination |
| route_sever_dst.remove_vpp_config() |
| |
| def test_PPPoE_Add_Twice(self): |
| """PPPoE Add Same Session Twice Test""" |
| |
| self.vapi.cli("clear trace") |
| |
| # |
| # Add a route that resolves the server's destination |
| # |
| route_sever_dst = VppIpRoute( |
| self, |
| "100.1.1.100", |
| 32, |
| [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], |
| ) |
| route_sever_dst.add_vpp_config() |
| |
| # Send PPPoE Discovery |
| tx0 = self.create_stream_pppoe_discovery( |
| self.pg0, self.pg1, self.pg0.remote_mac |
| ) |
| self.pg0.add_stream(tx0) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP |
| tx1 = self.create_stream_pppoe_lcp( |
| self.pg0, self.pg1, self.pg0.remote_mac, self.session_id |
| ) |
| self.pg0.add_stream(tx1) |
| self.pg_start() |
| |
| # Create PPPoE session |
| pppoe_if = VppPppoeInterface( |
| self, self.pg0.remote_ip4, self.pg0.remote_mac, self.session_id |
| ) |
| pppoe_if.add_vpp_config() |
| pppoe_if.set_unnumbered(self.pg0.sw_if_index) |
| |
| # |
| # The double create (create the same session twice) should fail, |
| # and we should still be able to use the original |
| # |
| try: |
| pppoe_if.add_vpp_config() |
| except Exception: |
| pass |
| else: |
| self.fail("Double GRE tunnel add does not fail") |
| |
| # |
| # test case cleanup |
| # |
| |
| # Delete PPPoE session |
| pppoe_if.remove_vpp_config() |
| |
| # Delete a route that resolves the server's destination |
| route_sever_dst.remove_vpp_config() |
| |
| def test_PPPoE_Del_Twice(self): |
| """PPPoE Delete Same Session Twice Test""" |
| |
| self.vapi.cli("clear trace") |
| |
| # |
| # Add a route that resolves the server's destination |
| # |
| route_sever_dst = VppIpRoute( |
| self, |
| "100.1.1.100", |
| 32, |
| [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], |
| ) |
| route_sever_dst.add_vpp_config() |
| |
| # Send PPPoE Discovery |
| tx0 = self.create_stream_pppoe_discovery( |
| self.pg0, self.pg1, self.pg0.remote_mac |
| ) |
| self.pg0.add_stream(tx0) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP |
| tx1 = self.create_stream_pppoe_lcp( |
| self.pg0, self.pg1, self.pg0.remote_mac, self.session_id |
| ) |
| self.pg0.add_stream(tx1) |
| self.pg_start() |
| |
| # Create PPPoE session |
| pppoe_if = VppPppoeInterface( |
| self, self.pg0.remote_ip4, self.pg0.remote_mac, self.session_id |
| ) |
| pppoe_if.add_vpp_config() |
| |
| # Delete PPPoE session |
| pppoe_if.remove_vpp_config() |
| |
| # |
| # The double del (del the same session twice) should fail, |
| # and we should still be able to use the original |
| # |
| try: |
| pppoe_if.remove_vpp_config() |
| except Exception: |
| pass |
| else: |
| self.fail("Double GRE tunnel del does not fail") |
| |
| # |
| # test case cleanup |
| # |
| |
| # Delete a route that resolves the server's destination |
| route_sever_dst.remove_vpp_config() |
| |
| def test_PPPoE_Decap_Multiple(self): |
| """PPPoE Decap Multiple Sessions Test""" |
| |
| self.vapi.cli("clear trace") |
| |
| # |
| # Add a route that resolves the server's destination |
| # |
| route_sever_dst = VppIpRoute( |
| self, |
| "100.1.1.100", |
| 32, |
| [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], |
| ) |
| route_sever_dst.add_vpp_config() |
| |
| # Send PPPoE Discovery 1 |
| tx0 = self.create_stream_pppoe_discovery( |
| self.pg0, self.pg1, self.pg0.remote_mac |
| ) |
| self.pg0.add_stream(tx0) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP 1 |
| tx1 = self.create_stream_pppoe_lcp( |
| self.pg0, self.pg1, self.pg0.remote_mac, self.session_id |
| ) |
| self.pg0.add_stream(tx1) |
| self.pg_start() |
| |
| # Create PPPoE session 1 |
| pppoe_if1 = VppPppoeInterface( |
| self, self.pg0.remote_ip4, self.pg0.remote_mac, self.session_id |
| ) |
| pppoe_if1.add_vpp_config() |
| pppoe_if1.set_unnumbered(self.pg0.sw_if_index) |
| |
| # Send PPPoE Discovery 2 |
| tx3 = self.create_stream_pppoe_discovery( |
| self.pg2, self.pg1, self.pg2.remote_mac |
| ) |
| self.pg2.add_stream(tx3) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP 2 |
| tx4 = self.create_stream_pppoe_lcp( |
| self.pg2, self.pg1, self.pg2.remote_mac, self.session_id + 1 |
| ) |
| self.pg2.add_stream(tx4) |
| self.pg_start() |
| |
| # Create PPPoE session 2 |
| pppoe_if2 = VppPppoeInterface( |
| self, self.pg2.remote_ip4, self.pg2.remote_mac, self.session_id + 1 |
| ) |
| pppoe_if2.add_vpp_config() |
| pppoe_if2.set_unnumbered(self.pg0.sw_if_index) |
| |
| # |
| # Send tunneled packets that match the created tunnel and |
| # are decapped and forwarded |
| # |
| tx2 = self.create_stream_pppoe_ip4( |
| self.pg0, |
| self.pg1, |
| self.pg0.remote_mac, |
| self.session_id, |
| self.pg0.remote_ip4, |
| ) |
| self.pg0.add_stream(tx2) |
| |
| self.pg_enable_capture(self.pg_interfaces) |
| self.pg_start() |
| |
| rx2 = self.pg1.get_capture(len(tx2)) |
| self.verify_decapped_pppoe(self.pg0, rx2, tx2) |
| |
| tx5 = self.create_stream_pppoe_ip4( |
| self.pg2, |
| self.pg1, |
| self.pg2.remote_mac, |
| self.session_id + 1, |
| self.pg2.remote_ip4, |
| ) |
| self.pg2.add_stream(tx5) |
| |
| self.pg_enable_capture(self.pg_interfaces) |
| self.pg_start() |
| |
| rx5 = self.pg1.get_capture(len(tx5)) |
| self.verify_decapped_pppoe(self.pg2, rx5, tx5) |
| |
| self.logger.info(self.vapi.cli("show pppoe fib")) |
| self.logger.info(self.vapi.cli("show pppoe session")) |
| self.logger.info(self.vapi.cli("show ip fib")) |
| |
| # |
| # test case cleanup |
| # |
| |
| # Delete PPPoE session |
| pppoe_if1.remove_vpp_config() |
| pppoe_if2.remove_vpp_config() |
| |
| # Delete a route that resolves the server's destination |
| route_sever_dst.remove_vpp_config() |
| |
| def test_PPPoE_Encap_Multiple(self): |
| """PPPoE Encap Multiple Sessions Test""" |
| |
| self.vapi.cli("clear trace") |
| |
| # |
| # Add a route that resolves the server's destination |
| # |
| route_sever_dst = VppIpRoute( |
| self, |
| "100.1.1.100", |
| 32, |
| [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], |
| ) |
| route_sever_dst.add_vpp_config() |
| |
| # Send PPPoE Discovery 1 |
| tx0 = self.create_stream_pppoe_discovery( |
| self.pg0, self.pg1, self.pg0.remote_mac |
| ) |
| self.pg0.add_stream(tx0) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP 1 |
| tx1 = self.create_stream_pppoe_lcp( |
| self.pg0, self.pg1, self.pg0.remote_mac, self.session_id |
| ) |
| self.pg0.add_stream(tx1) |
| self.pg_start() |
| |
| # Create PPPoE session 1 |
| pppoe_if1 = VppPppoeInterface( |
| self, self.pg0.remote_ip4, self.pg0.remote_mac, self.session_id |
| ) |
| pppoe_if1.add_vpp_config() |
| |
| # Send PPPoE Discovery 2 |
| tx3 = self.create_stream_pppoe_discovery( |
| self.pg2, self.pg1, self.pg2.remote_mac |
| ) |
| self.pg2.add_stream(tx3) |
| self.pg_start() |
| |
| # Send PPPoE PPP LCP 2 |
| tx4 = self.create_stream_pppoe_lcp( |
| self.pg2, self.pg1, self.pg2.remote_mac, self.session_id + 1 |
| ) |
| self.pg2.add_stream(tx4) |
| self.pg_start() |
| |
| # Create PPPoE session 2 |
| pppoe_if2 = VppPppoeInterface( |
| self, self.pg2.remote_ip4, self.pg2.remote_mac, self.session_id + 1 |
| ) |
| pppoe_if2.add_vpp_config() |
| |
| # |
| # Send a packet stream that is routed into the session |
| # - packets are PPPoE encapped |
| # |
| self.vapi.cli("clear trace") |
| tx2 = self.create_stream_ip4( |
| self.pg1, self.pg0, self.pg0.remote_ip4, self.dst_ip |
| ) |
| self.pg1.add_stream(tx2) |
| |
| self.pg_enable_capture(self.pg_interfaces) |
| self.pg_start() |
| |
| rx2 = self.pg0.get_capture(len(tx2)) |
| self.verify_encaped_pppoe(self.pg1, rx2, tx2, self.session_id) |
| |
| tx5 = self.create_stream_ip4( |
| self.pg1, self.pg2, self.pg2.remote_ip4, self.dst_ip |
| ) |
| self.pg1.add_stream(tx5) |
| |
| self.pg_enable_capture(self.pg_interfaces) |
| self.pg_start() |
| |
| rx5 = self.pg2.get_capture(len(tx5)) |
| self.verify_encaped_pppoe(self.pg1, rx5, tx5, self.session_id + 1) |
| |
| self.logger.info(self.vapi.cli("show pppoe fib")) |
| self.logger.info(self.vapi.cli("show pppoe session")) |
| self.logger.info(self.vapi.cli("show ip fib")) |
| |
| # |
| # test case cleanup |
| # |
| |
| # Delete PPPoE session |
| pppoe_if1.remove_vpp_config() |
| pppoe_if2.remove_vpp_config() |
| |
| # Delete a route that resolves the server's destination |
| route_sever_dst.remove_vpp_config() |
| |
| |
| if __name__ == "__main__": |
| unittest.main(testRunner=VppTestRunner) |