db9599011c97a2628485bffe756941b37c3ee74e
[csit.git] / GPL / traffic_scripts / srv6_encap.py
1 #!/usr/bin/env python3
2
3 # Copyright (c) 2020 Cisco and/or its affiliates.
4 #
5 # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6 #
7 # Licensed under the Apache License 2.0 or
8 # GNU General Public License v2.0 or later;  you may not use this file
9 # except in compliance with one of these Licenses. You
10 # may obtain a copy of the Licenses at:
11 #
12 #     http://www.apache.org/licenses/LICENSE-2.0
13 #     https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
14 #
15 # Note: If this file is linked with Scapy, which is GPLv2+, your use of it
16 # must be under GPLv2+.  If at any point in the future it is no longer linked
17 # with Scapy (or other GPLv2+ licensed software), you are free to choose Apache 2.
18 #
19 # Unless required by applicable law or agreed to in writing, software
20 # distributed under the License is distributed on an "AS IS" BASIS,
21 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 # See the License for the specific language governing permissions and
23 # limitations under the License.
24
25 """Traffic script for SRv6 verification."""
26
27 import sys
28
29 from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, IPv6ExtHdrSegmentRouting,\
30     ipv6nh
31 from scapy.layers.l2 import Ether
32 from scapy.packet import Raw
33
34 from .PacketVerifier import RxQueue, TxQueue
35 from .TrafficScriptArg import TrafficScriptArg
36
37
38 def check_srv6(
39         pkt_recv, src_mac, dst_mac, src_ip, dst_ip, dir_srcsid, dir_dstsid1,
40         dir_dstsid2, dir_dstsid3, segleft_corr, static_proxy=False):
41     """Check received SRv6 packet.
42
43     :param pkt_recv: Received packet to verify.
44     :param src_mac: Source MAC address.
45     :param dst_mac: Destination MAC address.
46     :param src_ip: Source IP/IPv6 address of original IP/IPv6 packet.
47     :param dst_ip: Destination IP/IPv6 address of original IP/IPv6 packet.
48     :param dir_srcsid: Source SID for SR in desired direction.
49     :param dir_dstsid1: Destination SID1 in desired direction.
50     :param dir_dstsid2: Destination SID2 in desired direction.
51     :param dir_dstsid3: Destination SID3 in desired direction.
52     :param segleft_corr: Correction for expected segleft value of SRH.
53     :type pkt_recv: scapy.Ether
54     :type src_mac: str
55     :type dst_mac: str
56     :type src_ip: str
57     :type dst_ip: str
58     :type dir_srcsid: str
59     :type dir_dstsid1: str
60     :type dir_dstsid2: str
61     :type dir_dstsid3: str
62     :type segleft_corr: int
63     :type static_proxy; bool
64     :raises RuntimeError: If received packet is invalid.
65     """
66     if pkt_recv[Ether].src != src_mac:
67         raise RuntimeError(
68             f"Received frame has invalid source MAC address: "
69             f"{pkt_recv[Ether].src} should be: {src_mac}"
70         )
71     if pkt_recv[Ether].dst != dst_mac:
72         raise RuntimeError(
73             f"Received frame has invalid destination MAC address: "
74             f"{pkt_recv[Ether].dst} should be: {dst_mac}"
75         )
76     if not pkt_recv.haslayer(IPv6):
77         raise RuntimeError(
78             f"Not an IPv6 packet received: {pkt_recv!r}"
79         )
80     ip6_pkt = pkt_recv[IPv6]
81     if ip6_pkt.src != dir_srcsid:
82         raise RuntimeError(
83             f"Outer IPv6 packet has invalid source address: "
84             f"{ip6_pkt.src} should be: {dir_srcsid}"
85         )
86     dir_dstsids = [dir_dstsid2, dir_dstsid1] if dir_dstsid3 == u"None" \
87         else [dir_dstsid3, dir_dstsid2, dir_dstsid1] if not static_proxy \
88         else [dir_dstsid3, dir_dstsid2]
89     segleft = len(dir_dstsids) - segleft_corr if not static_proxy \
90         else len(dir_dstsids) - segleft_corr + 1
91     if ip6_pkt.dst != dir_dstsids[segleft]:
92         raise RuntimeError(
93             f"Outer IPv6 packet has invalid destination address: "
94             f"{ip6_pkt.dst} should be: {dir_dstsids[segleft]}"
95         )
96     if dir_dstsid2 == u"None":
97         if ip6_pkt.haslayer(IPv6ExtHdrSegmentRouting):
98             raise RuntimeError(
99                 f"Segment Routing Header in received packet: {pkt_recv!r}"
100             )
101         if ip6_pkt.nh != 41:  # ipv6nh[41] == IPv6
102             raise RuntimeError(
103                 f"Outer IPv6 packet has invalid next header: "
104                 f"{ip6_pkt.nh} should be: 41 ({ipv6nh[41]})"
105             )
106         ip6_pkt = ip6_pkt[IPv6][1]
107     else:
108         if not pkt_recv.haslayer(IPv6ExtHdrSegmentRouting):
109             raise RuntimeError(
110                 f"No Segment Routing Header in received packet: {pkt_recv!r}"
111             )
112         if ip6_pkt.nh != 43:  # ipv6nh[43] == Routing Header
113             raise RuntimeError(
114                 f"Outer IPv6 packet has invalid next header: "
115                 f"{pkt_recv[IPv6][0].nh} should be: 43 ({ipv6nh[43]})"
116             )
117         ip6_pkt = ip6_pkt[IPv6ExtHdrSegmentRouting]
118         if ip6_pkt.addresses != dir_dstsids:
119             raise RuntimeError(
120                 f"Segment Routing Header has invalid addresses: "
121                 f"{ip6_pkt.addresses} should be: {dir_dstsids}"
122             )
123         if ip6_pkt.segleft != segleft:
124             raise RuntimeError(
125                 f"Segment Routing Header has invalid segleft value: "
126                 f"{ip6_pkt.segleft} should be: {segleft}"
127             )
128         if ip6_pkt.nh != 41:  # ipv6nh[41] == IPv6
129             raise RuntimeError(
130                 f"Segment Routing Header has invalid next header: "
131                 f"{ip6_pkt.nh} should be: 41 ({ipv6nh[41]})"
132             )
133         ip6_pkt = ip6_pkt[IPv6]
134     if ip6_pkt.src != src_ip:
135         raise RuntimeError(
136             f"Inner IPv6 packet has invalid source address: "
137             f"{ip6_pkt.src} should be: {src_ip}"
138         )
139     if ip6_pkt.dst != dst_ip:
140         raise RuntimeError(
141             f"Inner IPv6 packet has invalid destination address: "
142             f"{ip6_pkt.dst} should be: {dst_ip}"
143         )
144     if ip6_pkt.nh != 59:  # ipv6nh[59] == No Next Header
145         raise RuntimeError(
146             f"Inner IPv6 packet has invalid next header: "
147             f"{ip6_pkt.nh} should be: 59 ({ipv6nh[59]})"
148         )
149
150
151 def check_ip(pkt_recv, src_mac, dst_mac, src_ip, dst_ip):
152     """Check received IPv6 packet.
153
154     :param pkt_recv: Received packet to verify.
155     :param src_mac: Source MAC address.
156     :param dst_mac: Destination MAC address.
157     :param src_ip: Source IP/IPv6 address.
158     :param dst_ip: Destination IP/IPv6 address.
159     :type pkt_recv: scapy.Ether
160     :type src_mac: str
161     :type dst_mac: str
162     :type src_ip: str
163     :type dst_ip: str
164     :raises RuntimeError: If received packet is invalid.
165     """
166     if pkt_recv[Ether].src != src_mac:
167         raise RuntimeError(
168             f"Received frame has invalid source MAC address: "
169             f"{pkt_recv[Ether].src} should be: {src_mac}"
170         )
171
172     if pkt_recv[Ether].dst != dst_mac:
173         raise RuntimeError(
174             f"Received frame has invalid destination MAC address: "
175             f"{pkt_recv[Ether].dst} should be: {dst_mac}"
176         )
177
178     if not pkt_recv.haslayer(IPv6):
179         raise RuntimeError(
180             f"Not an {IPv6.name} packet received: {pkt_recv!r}"
181         )
182
183     if pkt_recv[IPv6].dst != dst_ip:
184         raise RuntimeError(
185             f"Received packet has invalid destination address: "
186             f"{pkt_recv[IPv6.name].dst} should be: {dst_ip}"
187         )
188
189     if pkt_recv[IPv6].src != src_ip:
190         raise RuntimeError(
191             f"Received packet has invalid destination address: "
192             f"{pkt_recv[IPv6.name].dst} should be: {src_ip}"
193         )
194
195     if pkt_recv[IPv6].nh != 59:  # ipv6nh[59] == No Next Header
196         raise RuntimeError(
197             f"Received IPv6 packet has invalid next header: "
198             f"{IPv6.nh} should be: 59 ({ipv6nh[59]})"
199         )
200
201
202 def main():
203     """Send, receive and check IPv6 and IPv6ExtHdrSegmentRouting packets."""
204
205     args = TrafficScriptArg(
206         [
207             u"tx_src_mac", u"tx_dst_mac", u"rx_src_mac", u"rx_dst_mac",
208             u"src_ip", u"dst_ip", u"dir0_srcsid", u"dir0_dstsid1",
209             u"dir0_dstsid2", u"dir1_srcsid", u"dir1_dstsid1", u"dir1_dstsid2",
210             u"decap", u"dir0_dstsid3", u"dir1_dstsid3", u"static_proxy"
211         ]
212     )
213
214     tx_txq = TxQueue(args.get_arg(u"tx_if"))
215     tx_rxq = RxQueue(args.get_arg(u"tx_if"))
216     rx_txq = TxQueue(args.get_arg(u"rx_if"))
217     rx_rxq = RxQueue(args.get_arg(u"rx_if"))
218
219     tx_src_mac = args.get_arg(u"tx_src_mac")
220     tx_dst_mac = args.get_arg(u"tx_dst_mac")
221     rx_src_mac = args.get_arg(u"rx_src_mac")
222     rx_dst_mac = args.get_arg(u"rx_dst_mac")
223     src_ip = args.get_arg(u"src_ip")
224     dst_ip = args.get_arg(u"dst_ip")
225
226     dir0_srcsid = args.get_arg(u"dir0_srcsid")
227     dir0_dstsid1 = args.get_arg(u"dir0_dstsid1")
228     dir0_dstsid2 = args.get_arg(u"dir0_dstsid2")
229     dir1_srcsid = args.get_arg(u"dir1_srcsid")
230     dir1_dstsid1 = args.get_arg(u"dir1_dstsid1")
231     dir1_dstsid2 = args.get_arg(u"dir1_dstsid2")
232     decap = args.get_arg(u"decap")
233     dir0_dstsid3 = args.get_arg(u"dir0_dstsid3")
234     dir1_dstsid3 = args.get_arg(u"dir1_dstsid3")
235     static_proxy = args.get_arg(u"static_proxy")
236
237     ip_pkt = IPv6(src=src_ip, dst=dst_ip)
238
239     sent_packets = list()
240     tx_pkt_send = (Ether(src=tx_src_mac, dst=tx_dst_mac) / ip_pkt)
241     tx_pkt_send /= Raw()
242     size_limit = 78
243     if len(tx_pkt_send) < size_limit:
244         tx_pkt_send[Raw].load += (b"\0" * (size_limit - len(tx_pkt_send)))
245     sent_packets.append(tx_pkt_send)
246     tx_txq.send(tx_pkt_send)
247
248     while True:
249         rx_pkt_recv = rx_rxq.recv(2)
250
251         if rx_pkt_recv is None:
252             raise RuntimeError(f"{IPv6.name} packet Rx timeout")
253
254         if rx_pkt_recv.haslayer(ICMPv6ND_NS):
255             # read another packet in the queue if the current one is ICMPv6ND_NS
256             continue
257         else:
258             # otherwise process the current packet
259             break
260
261     check_srv6(
262         rx_pkt_recv, rx_src_mac, rx_dst_mac, src_ip, dst_ip, dir0_srcsid,
263         dir0_dstsid1, dir0_dstsid2, dir0_dstsid3, 1
264     )
265
266     ip_pkt = IPv6(src=dst_ip, dst=src_ip)
267     ip_pkt /= Raw()
268     if len(ip_pkt) < (size_limit - 14):
269         ip_pkt[Raw].load += (b"\0" * (size_limit - 14 - len(ip_pkt)))
270
271     rx_pkt_send = (
272             Ether(src=rx_dst_mac, dst=rx_src_mac) /
273             IPv6(src=dir1_srcsid, dst=dir1_dstsid1) /
274             IPv6ExtHdrSegmentRouting(
275                 segleft=1 if dir1_dstsid3 == u"None" else 2,
276                 lastentry=1 if dir1_dstsid3 == u"None" else 2,
277                 addresses=[dir1_dstsid2, dir1_dstsid1]
278                 if dir1_dstsid3 == u"None"
279                 else [dir1_dstsid3, dir1_dstsid2, dir1_dstsid1]
280             ) /
281             ip_pkt
282     ) if dir1_dstsid2 != u"None" else (
283             Ether(src=rx_dst_mac, dst=rx_src_mac) /
284             IPv6(src=dir1_srcsid, dst=dir1_dstsid1) /
285             ip_pkt
286     )
287     rx_txq.send(rx_pkt_send)
288
289     while True:
290         tx_pkt_recv = tx_rxq.recv(2, ignore=sent_packets)
291
292         if tx_pkt_recv is None:
293             raise RuntimeError(f"{IPv6.name} packet Rx timeout")
294
295         if tx_pkt_recv.haslayer(ICMPv6ND_NS):
296             # read another packet in the queue if the current one is ICMPv6ND_NS
297             continue
298         else:
299             # otherwise process the current packet
300             break
301
302     if decap == u"True":
303         check_ip(tx_pkt_recv, tx_dst_mac, tx_src_mac, dst_ip, src_ip)
304     else:
305         check_srv6(
306             tx_pkt_recv, tx_dst_mac, tx_src_mac, dst_ip, src_ip, dir1_srcsid,
307             dir1_dstsid1, dir1_dstsid2, dir1_dstsid3, 2,
308             bool(static_proxy == u"True")
309         )
310
311     sys.exit(0)
312
313
314 if __name__ == u"__main__":
315     main()