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