187d24db6SDave Barach#!/usr/bin/env python3
287d24db6SDave Barach
387d24db6SDave Barachimport unittest
487d24db6SDave Barach
587d24db6SDave Barachfrom framework import VppTestCase, VppTestRunner, running_extended_tests
687d24db6SDave Barachfrom vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
787d24db6SDave Barach
86461b7a3SPaul Vinciguerrafrom scapy.contrib.geneve import GENEVE
98d27fa73SBenoît Gannefrom scapy.packet import Raw
108d27fa73SBenoît Gannefrom scapy.layers.l2 import Ether
118d27fa73SBenoît Gannefrom scapy.layers.inet import IP, UDP
128d27fa73SBenoît Gannefrom scapy.layers.vxlan import VXLAN
138d27fa73SBenoît Gannefrom scapy.compat import raw
148d27fa73SBenoît Ganne
1587d24db6SDave Barach
1687d24db6SDave Barachclass TestTracefilter(VppTestCase):
1787d24db6SDave Barach    """ Packet Tracer Filter Test """
1887d24db6SDave Barach
1987d24db6SDave Barach    @classmethod
2087d24db6SDave Barach    def setUpClass(cls):
2187d24db6SDave Barach        super(TestTracefilter, cls).setUpClass()
2287d24db6SDave Barach
2387d24db6SDave Barach    @classmethod
2487d24db6SDave Barach    def tearDownClass(cls):
2587d24db6SDave Barach        super(TestTracefilter, cls).tearDownClass()
2687d24db6SDave Barach
2787d24db6SDave Barach    def setUp(self):
2887d24db6SDave Barach        super(TestTracefilter, self).setUp()
298d27fa73SBenoît Ganne        self.create_pg_interfaces(range(1))
308d27fa73SBenoît Ganne        for i in self.pg_interfaces:
318d27fa73SBenoît Ganne            i.admin_up()
328d27fa73SBenoît Ganne            i.config_ip4()
3387d24db6SDave Barach
3487d24db6SDave Barach    def tearDown(self):
3587d24db6SDave Barach        super(TestTracefilter, self).tearDown()
368d27fa73SBenoît Ganne        for i in self.pg_interfaces:
378d27fa73SBenoît Ganne            i.unconfig()
388d27fa73SBenoît Ganne            i.admin_down()
398d27fa73SBenoît Ganne
408d27fa73SBenoît Ganne    def cli(self, cmd):
418d27fa73SBenoît Ganne        r = self.vapi.cli_return_response(cmd)
428d27fa73SBenoît Ganne        if r.retval != 0:
438d27fa73SBenoît Ganne            if hasattr(r, 'reply'):
448d27fa73SBenoît Ganne                self.logger.info(cmd + " FAIL reply " + r.reply)
458d27fa73SBenoît Ganne            else:
468d27fa73SBenoît Ganne                self.logger.info(cmd + " FAIL retval " + str(r.retval))
478d27fa73SBenoît Ganne        return r
488d27fa73SBenoît Ganne
498d27fa73SBenoît Ganne    # check number of hits for classifier
508d27fa73SBenoît Ganne    def assert_hits(self, n):
518d27fa73SBenoît Ganne        r = self.cli("show classify table verbose 2")
528d27fa73SBenoît Ganne        self.assertTrue(r.retval == 0)
538d27fa73SBenoît Ganne        self.assertTrue(hasattr(r, 'reply'))
548d27fa73SBenoît Ganne        self.assertTrue(r.reply.find("hits %i" % n) != -1)
5587d24db6SDave Barach
5687d24db6SDave Barach    def test_mactime_unitTest(self):
5787d24db6SDave Barach        """ Packet Tracer Filter Test """
5887d24db6SDave Barach        cmds = ["loopback create",
5987d24db6SDave Barach                "set int ip address loop0",
6087d24db6SDave Barach                "set int state loop0 up",
6187d24db6SDave Barach                "packet-generator new {\n"
6287d24db6SDave Barach                " name classifyme\n"
6387d24db6SDave Barach                " limit 100\n"
6487d24db6SDave Barach                " size 300-300\n"
6587d24db6SDave Barach                " interface loop0\n"
6687d24db6SDave Barach                " node ethernet-input\n"
6787d24db6SDave Barach                " data { \n"
6887d24db6SDave Barach                "      IP4: 1.2.3 -> 4.5.6\n"
6987d24db6SDave Barach                "      UDP: - ->\n"
7087d24db6SDave Barach                "      UDP: 1234 -> 2345\n"
7187d24db6SDave Barach                "      incrementing 286\n"
7287d24db6SDave Barach                "     }\n"
7387d24db6SDave Barach                "}\n",
7487d24db6SDave Barach                "classify filter trace mask l3 ip4 src\n"
7587d24db6SDave Barach                " match l3 ip4 src",
7687d24db6SDave Barach                "trace add pg-input 100 filter",
778d27fa73SBenoît Ganne                "pa en classifyme"]
7887d24db6SDave Barach
7987d24db6SDave Barach        for cmd in cmds:
808d27fa73SBenoît Ganne            self.cli(cmd)
8187d24db6SDave Barach
8287d24db6SDave Barach        # Check for 9 classifier hits, which is the right answer
838d27fa73SBenoît Ganne        self.assert_hits(9)
848d27fa73SBenoît Ganne
858d27fa73SBenoît Ganne        # cleanup
868d27fa73SBenoît Ganne        self.cli("pa de classifyme")
878d27fa73SBenoît Ganne        self.cli("classify filter trace del mask l3 ip4 src "
888d27fa73SBenoît Ganne                 "match l3 ip4 src")
898d27fa73SBenoît Ganne
908d27fa73SBenoît Ganne    # install a classify rule, inject traffic and check for hits
918d27fa73SBenoît Ganne    def assert_classify(self, mask, match, packets, n=None):
928d27fa73SBenoît Ganne        r = self.cli(
938d27fa73SBenoît Ganne            "classify filter trace mask hex %s match hex %s" %
948d27fa73SBenoît Ganne            (mask, match))
9587d24db6SDave Barach        self.assertTrue(r.retval == 0)
968d27fa73SBenoît Ganne        r = self.cli("trace add pg-input %i filter" % len(packets))
978d27fa73SBenoît Ganne        self.assertTrue(r.retval == 0)
988d27fa73SBenoît Ganne        self.pg0.add_stream(packets)
998d27fa73SBenoît Ganne        self.cli("pa en")
1008d27fa73SBenoît Ganne        self.assert_hits(n if n is not None else len(packets))
1018d27fa73SBenoît Ganne        self.cli("clear trace")
1028d27fa73SBenoît Ganne        self.cli(
1038d27fa73SBenoît Ganne            "classify filter trace del mask hex %s match hex %s" %
1048d27fa73SBenoît Ganne            (mask, match))
1058d27fa73SBenoît Ganne
1068d27fa73SBenoît Ganne    def test_encap(self):
1078d27fa73SBenoît Ganne        """ Packet Tracer Filter Test with encap """
1088d27fa73SBenoît Ganne
1098d27fa73SBenoît Ganne        # the packet we are trying to match
1108d27fa73SBenoît Ganne        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
1118d27fa73SBenoît Ganne             IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1128d27fa73SBenoît Ganne             UDP() /
1138d27fa73SBenoît Ganne             VXLAN() /
1148d27fa73SBenoît Ganne             Ether() /
1158d27fa73SBenoît Ganne             IP() /
1168d27fa73SBenoît Ganne             UDP() /
1178d27fa73SBenoît Ganne             GENEVE(vni=1234) /
1188d27fa73SBenoît Ganne             Ether() /
1198d27fa73SBenoît Ganne             IP(src='') /
1208d27fa73SBenoît Ganne             UDP() /
1218d27fa73SBenoît Ganne             Raw('\xa5' * 100))
1228d27fa73SBenoît Ganne
1238d27fa73SBenoît Ganne        #
1248d27fa73SBenoît Ganne        # compute filter mask & value
1258d27fa73SBenoît Ganne        # we compute it by XOR'ing a template packet with a modified packet
1268d27fa73SBenoît Ganne        # we need to set checksums to 0 to make sure scapy will not recompute
1278d27fa73SBenoît Ganne        # them
1288d27fa73SBenoît Ganne        #
1298d27fa73SBenoît Ganne        tmpl = (Ether() /
1308d27fa73SBenoît Ganne                IP(chksum=0) /
1318d27fa73SBenoît Ganne                UDP(chksum=0) /
1328d27fa73SBenoît Ganne                VXLAN() /
1338d27fa73SBenoît Ganne                Ether() /
1348d27fa73SBenoît Ganne                IP(chksum=0) /
1358d27fa73SBenoît Ganne                UDP(chksum=0) /
1368d27fa73SBenoît Ganne                GENEVE(vni=0) /
1378d27fa73SBenoît Ganne                Ether() /
1388d27fa73SBenoît Ganne                IP(src='', chksum=0))
1398d27fa73SBenoît Ganne        ori = raw(tmpl)
1408d27fa73SBenoît Ganne
1418d27fa73SBenoît Ganne        # the mask
1428d27fa73SBenoît Ganne        tmpl[GENEVE].vni = 0xffffff
1438d27fa73SBenoît Ganne        user = tmpl[GENEVE].payload
1448d27fa73SBenoît Ganne        user[IP].src = ''
1458d27fa73SBenoît Ganne        new = raw(tmpl)
1468d27fa73SBenoît Ganne        mask = "".join(("{:02x}".format(o ^ n) for o, n in zip(ori, new)))
1478d27fa73SBenoît Ganne
1488d27fa73SBenoît Ganne        # this does not match (wrong vni)
1498d27fa73SBenoît Ganne        tmpl[GENEVE].vni = 1
1508d27fa73SBenoît Ganne        user = tmpl[GENEVE].payload
1518d27fa73SBenoît Ganne        user[IP].src = ''
1528d27fa73SBenoît Ganne        new = raw(tmpl)
1538d27fa73SBenoît Ganne        match = "".join(("{:02x}".format(o ^ n) for o, n in zip(ori, new)))
1548d27fa73SBenoît Ganne        self.assert_classify(mask, match, [p] * 11, 0)
1558d27fa73SBenoît Ganne
1568d27fa73SBenoît Ganne        # this must match
1578d27fa73SBenoît Ganne        tmpl[GENEVE].vni = 1234
1588d27fa73SBenoît Ganne        new = raw(tmpl)
1598d27fa73SBenoît Ganne        match = "".join(("{:02x}".format(o ^ n) for o, n in zip(ori, new)))
1608d27fa73SBenoît Ganne        self.assert_classify(mask, match, [p] * 17)
1618d27fa73SBenoît Ganne
16287d24db6SDave Barach
16387d24db6SDave Barachif __name__ == '__main__':
16487d24db6SDave Barach    unittest.main(testRunner=VppTestRunner)