aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/map/examples/gen-rules.py
blob: 7964aa9a359ac067b2588dfe0cacb6d59a8f8522 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env python3

# Copyright (c) 2015 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import ipaddress
import argparse
import sys

# map add domain ip4-pfx <pfx> ip6-pfx ::/0 ip6-src <ip6-src> ea-bits-len 0 psid-offset 6 psid-len 6
# map add rule index <0> psid <psid> ip6-dst <ip6-dst>

def_ip4_pfx = '192.0.2.0/24'
def_ip6_pfx = '2001:db8::/32'
def_ip6_src = '2001:db8::1'
def_psid_offset = 6
def_psid_len = 6
def_ea_bits_len = 0

parser = argparse.ArgumentParser(description='MAP VPP configuration generator')
parser.add_argument('-t', action="store", dest="mapmode")
parser.add_argument('-f', action="store", dest="format", default="vpp")
parser.add_argument('--ip4-prefix', action="store", dest="ip4_pfx", default=def_ip4_pfx)
parser.add_argument('--ip6-prefix', action="store", dest="ip6_pfx", default=def_ip6_pfx)
parser.add_argument('--ip6-src', action="store", dest="ip6_src", default=def_ip6_src)
parser.add_argument('--psid-len', action="store", dest="psid_len", default=def_psid_len)
parser.add_argument('--psid-offset', action="store", dest="psid_offset", default=def_psid_offset)
parser.add_argument('--ea-bits-len', action="store", dest="ea_bits_len", default=def_ea_bits_len)
args = parser.parse_args()

#
# Print domain
#
def domain_print(i, ip4_pfx, ip6_pfx, ip6_src, eabits_len, psid_offset, psid_len):
    if format == 'vpp':
        print("map add domain ip4-pfx " + ip4_pfx + " ip6-pfx", ip6_pfx, "ip6-src " + ip6_src +
              " ea-bits-len", eabits_len, "psid-offset", psid_offset, "psid-len", psid_len)
    if format == 'confd':
        print("vpp softwire softwire-instances softwire-instance", i, "br-ipv6 " + ip6_src +
              " ipv6-prefix " + ip6_pfx + " ipv4-prefix " + ip4_pfx +
              " ea-bits-len", eabits_len, "psid-offset", psid_offset, "psid-len", psid_len)
    if format == 'xml':
        print("<softwire-instance>")
        print("<id>", i, "</id>");
        print("  <br-ipv6>" + ip6_src + "</br-ipv6>")
        print("  <ipv6-prefix>" + ip6_pfx + "</ipv6-prefix>")
        print("  <ipv4-prefix>" + ip4_pfx + "</ipv4-prefix>")
        print("  <ea-len>", eabits_len, "</ea-len>")
        print("  <psid-len>", psid_len, "</psid-len>")
        print("  <psid-offset>", psid_offset, "</psid-offset>")

def domain_print_end():
    if format == 'xml':
        print("</softwire-instance>")

def rule_print(i, psid, dst):
    if format == 'vpp':
        print("map add rule index", i, "psid", psid, "ip6-dst", dst)
    if format == 'confd':
        print("binding", psid, "ipv6-addr", dst)
    if format == 'xml':
        print("  <binding>")
        print("    <psid>", psid, "</psid>")
        print("    <ipv6-addr>", dst, "</ipv6-addr>")
        print("  </binding>")

#
# Algorithmic mapping Shared IPv4 address
#
def algo(ip4_pfx_str, ip6_pfx_str, ip6_src_str, ea_bits_len, psid_offset, psid_len, ip6_src_ecmp = False):
    domain_print(0, ip4_pfx_str, ip6_pfx_str, ip6_src_str, ea_bits_len, psid_offset, psid_len)
    domain_print_end()

#
# 1:1 Full IPv4 address
#
def lw46(ip4_pfx_str, ip6_pfx_str, ip6_src_str, ea_bits_len, psid_offset, psid_len, ip6_src_ecmp = False):
    ip4_pfx = ipaddress.ip_network(ip4_pfx_str)
    ip6_src = ipaddress.ip_address(ip6_src_str)
    ip6_dst = ipaddress.ip_network(ip6_pfx_str)
    psid_len = 0
    mod = ip4_pfx.num_addresses / 1024

    for i in range(ip4_pfx.num_addresses):
        domain_print(i, str(ip4_pfx[i]) + "/32", str(ip6_dst[i]) + "/128", str(ip6_src), 0, 0, 0)
        domain_print_end()
        if ip6_src_ecmp and not i % mod:
            ip6_src = ip6_src + 1

#
# 1:1 Shared IPv4 address, shared BR (16) VPP CLI
#
def lw46_shared(ip4_pfx_str, ip6_pfx_str, ip6_src_str, ea_bits_len, psid_offset, psid_len, ip6_src_ecmp = False):
    ip4_pfx = ipaddress.ip_network(ip4_pfx_str)
    ip6_src = ipaddress.ip_address(ip6_src_str)
    ip6_dst = ipaddress.ip_network(ip6_pfx_str)
    mod = ip4_pfx.num_addresses / 1024

    for i in range(ip4_pfx.num_addresses):
        domain_print(i, str(ip4_pfx[i]) + "/32", "::/0", str(ip6_src), 0, 0, psid_len)
        for psid in range(0x1 << int(psid_len)):
            rule_print(i, psid, str(ip6_dst[(i * (0x1<<int(psid_len))) + psid]))
        domain_print_end()
        if ip6_src_ecmp and not i % mod:
            ip6_src = ip6_src + 1


#
# 1:1 Shared IPv4 address, shared BR
#
def lw46_shared_b(ip4_pfx_str, ip6_pfx_str, ip6_src_str, ea_bits_len, psid_offset, psid_len, ip6_src_ecmp = False):
    ip4_pfx = ipaddress.ip_network(ip4_pfx_str)
    ip6_src = ipaddress.ip_address(ip6_src_str)
    ip6_dst = list(ipaddress.ip_network(ip6_pfx_str).subnets(new_prefix=56))
    mod = ip4_pfx.num_addresses / 1024

    for i in range(ip4_pfx.num_addresses):
        domain_print(i, str(ip4_pfx[i]) + "/32", "::/0", str(ip6_src), 0, 0, psid_len)
        for psid in range(0x1 << psid_len):
            enduserprefix = list(ip6_dst.pop(0).subnets(new_prefix=64))[255-1]
            rule_print(i, psid, enduserprefix[(i * (0x1<<psid_len)) + psid])
        domain_print_end()
        if ip6_src_ecmp and not i % mod:
            ip6_src = ip6_src + 1


def xml_header_print():
    print('''
<?xml version="1.0" encoding="UTF-8"?>
    <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
    <capabilities>
    <capability>urn:ietf:params:netconf:base:1.0</capability>
    </capabilities>
    </hello>
]]>]]>

<?xml version="1.0" encoding="UTF-8"?>
    <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"  message-id="1">
    <edit-config>
    <target>
    <candidate/>
    </target>
    <config>

    <vpp xmlns="http://www.cisco.com/yang/cisco-vpp">
 <softwire>
 <softwire-instances>

    ''')

def xml_footer_print():
    print('''
</softwire-instances>
</softwire>
</vpp>
    </config>
    </edit-config>
    </rpc>

]]>]]>

<?xml version="1.0" encoding="UTF-8"?>
    <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="2">
    <close-session/>
    </rpc>

]]>]]>
    ''')


format = args.format
if format == 'xml':
    xml_header_print()
globals()[args.mapmode](args.ip4_pfx, args.ip6_pfx, args.ip6_src, args.ea_bits_len, args.psid_offset, args.psid_len)
if format == 'xml':
    xml_footer_print()
crypt_algo=params.crypt_algo, crypt_key=params.crypt_key, auth_algo=params.auth_algo, auth_key=params.auth_key) params.vpp_tra_sa = SecurityAssociation(cls.encryption_type, spi=params.scapy_tra_spi, crypt_algo=params.crypt_algo, crypt_key=params.crypt_key, auth_algo=params.auth_algo, auth_key=params.auth_key) class IpsecTcpTests(object): def test_tcp_checksum(self): """ verify checksum correctness for vpp generated packets """ self.vapi.cli("test http server") p = self.params[socket.AF_INET] vpp_tun_sa, scapy_tun_sa = self.configure_sa_tun(p) send = (Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) / scapy_tun_sa.encrypt(IP(src=p.remote_tun_if_host, dst=self.tun_if.local_ip4) / TCP(flags='S', dport=80))) self.logger.debug(ppp("Sending packet:", send)) recv = self.send_and_expect(self.tun_if, [send], self.tun_if) recv = recv[0] decrypted = vpp_tun_sa.decrypt(recv[IP]) self.assert_packet_checksums_valid(decrypted) class IpsecTraTests(object): def test_tra_anti_replay(self, count=1): """ ipsec v4 transport anti-reply test """ p = self.params[socket.AF_INET] # fire in a packet with seq number 1 pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=1)) recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if) # now move the window over to 235 pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=235)) recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if) # the window size is 64 packets # in window are still accepted pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=172)) recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if) # out of window are dropped pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=17)) self.send_and_assert_no_replies(self.tra_if, pkt * 17) err = self.statistics.get_counter( '/err/%s/SA replayed packet' % self.tra4_decrypt_node_name) self.assertEqual(err, 17) # a packet that does not decrypt does not move the window forward bogus_sa = SecurityAssociation(self.encryption_type, p.vpp_tra_spi) pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / bogus_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=350)) self.send_and_assert_no_replies(self.tra_if, pkt * 17) err = self.statistics.get_counter( '/err/%s/Integrity check failed' % self.tra4_decrypt_node_name) self.assertEqual(err, 17) # which we can determine since this packet is still in the window pkt = (Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac) / p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(), seq_num=234)) recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if) # move the security-associations seq number on to the last we used p.scapy_tra_sa.seq_num = 351 p.vpp_tra_sa.seq_num = 351 def test_tra_basic(self, count=1): """ ipsec v4 transport basic test """ self.vapi.cli("clear errors") try: p = self.params[socket.AF_INET] send_pkts = self.gen_encrypt_pkts(p.scapy_tra_sa, self.tra_if, src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4, count=count) recv_pkts = self.send_and_expect(self.tra_if, send_pkts, self.tra_if) for rx in recv_pkts: try: decrypted = p.vpp_tra_sa.decrypt(rx[IP]) self.assert_packet_checksums_valid(decrypted) except: self.logger.debug(ppp("Unexpected packet:", rx)) raise finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec")) self.assert_packet_counter_equal(self.tra4_encrypt_node_name, count) self.assert_packet_counter_equal(self.tra4_decrypt_node_name, count) def test_tra_burst(self): """ ipsec v4 transport burst test """ self.test_tra_basic(count=257) def test_tra_basic6(self, count=1): """ ipsec v6 transport basic test """ self.vapi.cli("clear errors") try: p = self.params[socket.AF_INET6] send_pkts = self.gen_encrypt_pkts6(p.scapy_tra_sa, self.tra_if, src=self.tra_if.remote_ip6, dst=self.tra_if.local_ip6, count=count) recv_pkts = self.send_and_expect(self.tra_if, send_pkts, self.tra_if) for rx in recv_pkts: try: decrypted = p.vpp_tra_sa.decrypt(rx[IPv6]) self.assert_packet_checksums_valid(decrypted) except: self.logger.debug(ppp("Unexpected packet:", rx)) raise finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec")) self.assert_packet_counter_equal(self.tra6_encrypt_node_name, count) self.assert_packet_counter_equal(self.tra6_decrypt_node_name, count) def test_tra_burst6(self): """ ipsec v6 transport burst test """ self.test_tra_basic6(count=257) class IpsecTun4Tests(object): def test_tun_basic44(self, count=1): """ ipsec 4o4 tunnel basic test """ self.vapi.cli("clear errors") try: p = self.params[socket.AF_INET] vpp_tun_sa, scapy_tun_sa = self.configure_sa_tun(p) send_pkts = self.gen_encrypt_pkts(scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip4, count=count) recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1) for recv_pkt in recv_pkts: self.assert_equal(recv_pkt[IP].src, p.remote_tun_if_host) self.assert_equal(recv_pkt[IP].dst, self.pg1.remote_ip4) self.assert_packet_checksums_valid(recv_pkt) send_pkts = self.gen_pkts(self.pg1, src=self.pg1.remote_ip4, dst=p.remote_tun_if_host, count=count) recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if) for recv_pkt in recv_pkts: try: decrypt_pkt = vpp_tun_sa.decrypt(recv_pkt[IP]) if not decrypt_pkt.haslayer(IP): decrypt_pkt = IP(decrypt_pkt[Raw].load) self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip4) self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host) self.assert_packet_checksums_valid(decrypt_pkt) except: self.logger.debug(ppp("Unexpected packet:", recv_pkt)) try: self.logger.debug( ppp("Decrypted packet:", decrypt_pkt)) except: pass raise finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec")) self.assert_packet_counter_equal(self.tun4_encrypt_node_name, count) self.assert_packet_counter_equal(self.tun4_decrypt_node_name, count) def test_tun_burst44(self): """ ipsec 4o4 tunnel burst test """ self.test_tun_basic44(count=257) class IpsecTun6Tests(object): def test_tun_basic66(self, count=1): """ ipsec 6o6 tunnel basic test """ self.vapi.cli("clear errors") try: p = self.params[socket.AF_INET6] vpp_tun_sa, scapy_tun_sa = self.configure_sa_tun(p) send_pkts = self.gen_encrypt_pkts6(scapy_tun_sa, self.tun_if, src=p.remote_tun_if_host, dst=self.pg1.remote_ip6, count=count) recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1) for recv_pkt in recv_pkts: self.assert_equal(recv_pkt[IPv6].src, p.remote_tun_if_host) self.assert_equal(recv_pkt[IPv6].dst, self.pg1.remote_ip6) self.assert_packet_checksums_valid(recv_pkt) send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6, dst=p.remote_tun_if_host, count=count) recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if) for recv_pkt in recv_pkts: try: decrypt_pkt = vpp_tun_sa.decrypt(recv_pkt[IPv6]) if not decrypt_pkt.haslayer(IPv6): decrypt_pkt = IPv6(decrypt_pkt[Raw].load) self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip6) self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host) self.assert_packet_checksums_valid(decrypt_pkt) except: self.logger.debug(ppp("Unexpected packet:", recv_pkt)) try: self.logger.debug( ppp("Decrypted packet:", decrypt_pkt)) except: pass raise finally: self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec")) self.assert_packet_counter_equal(self.tun6_encrypt_node_name, count) self.assert_packet_counter_equal(self.tun6_decrypt_node_name, count) def test_tun_burst66(self): """ ipsec 6o6 tunnel burst test """ self.test_tun_basic66(count=257) class IpsecTunTests(IpsecTun4Tests, IpsecTun6Tests): pass if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)