Skip to content

Commit 9efdb68

Browse files
yue-fred-gaoravaliyel
authored andcommitted
vpp: Workaround scapy bfd issue (sonic-net#22644)
Approach What is the motivation for this PR? After upgrading scapy in ptf container, a bug is introduced. BFDResponder generates BFD packet with auth field even auth flag is not enabled. The authentication field is appended to the end of the BFD packet without adjusting UDP header length. This causes udp checksum verification failed. Here is the packet from PTF: 18:16:27.682014 IP6 fddd:a100:a0::a37:10.49157 > fc00:1::32.4784: UDP, bad length 35 > 24 0x0000: 225d a77e b78e 1e44 8b06 c367 86dd 6000 0x0010: 0000 0020 11ff fddd a100 00a0 0000 0000 0x0020: 0000 0a37 0010 fc00 0001 0000 0000 0000 0x0030: 0000 0000 0032 c005 12b0 002b 9c68 2080 0x0040: 0a18 cdba 0001 c349 ff6a 000f 4240 000f 0x0050: 4240 0000 0001 010b 0170 6173 7377 6f72 0x0060: 64 Here is the issue about scapy bfd issue: secdev/scapy#4937 How did you do it? Set optional_auth to None to get around the bug How did you verify/test it? Verified with sonic-mgmt test Signed-off-by: Yue Gao <yuega2@cisco.com>
1 parent 57dbd8d commit 9efdb68

1 file changed

Lines changed: 32 additions & 12 deletions

File tree

ansible/roles/test/files/ptftests/py3/bfd_responder.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import ptf.packet as scapy
99
from ptf.base_tests import BaseTest
1010
from scapy.contrib.bfd import BFD
11+
from scapy.layers.inet import IP
12+
from scapy.layers.inet6 import IPv6
1113
from ptf.testutils import (send_packet, test_params_get)
1214
from ipaddress import ip_address, IPv4Address, IPv6Address
1315
session_timeout = 1
@@ -161,19 +163,37 @@ def craft_bfd_packet(self,
161163
bfd_remote_disc,
162164
bfd_state):
163165
ethpart = scapy.Ether(data)
164-
bfdpart = BFD(bytes(ethpart.payload.payload.payload))
166+
167+
# Parse only the mandatory 24-byte BFD header; ignore any
168+
# trailing data such as Simple Password authentication.
169+
bfdpart = BFD(bytes(ethpart.payload.payload.payload)[:24])
165170
bfdpart.my_discriminator = my_discriminator
166171
bfdpart.your_discriminator = bfd_remote_disc
167172
bfdpart.sta = bfd_state
168173

169-
ethpart.payload.payload.payload = bfdpart
170-
ethpart.src = mac_dst
171-
ethpart.dst = mac_src
172-
ethpart.payload.src = ip_dst
173-
ethpart.payload.dst = ip_src
174-
175-
# recompute UDP checksum
176-
ethpart.payload.payload.chksum = None
177-
ethpart.show()
178-
179-
return ethpart
174+
# If the A (Auth Present) flag is set, scapy auto-creates a
175+
# phantom OptionalAuth with default auth_key=b'password' even
176+
# when no auth bytes exist on the wire. Clear both to prevent
177+
# the 11-byte auth trailer from being serialized into the
178+
# response packet.
179+
bfdpart.flags = bfdpart.flags & ~0x04
180+
bfdpart.optional_auth = None
181+
182+
udp_sport = ethpart[scapy.UDP].sport
183+
udp_dport = ethpart[scapy.UDP].dport
184+
185+
# Build the response packet from scratch so no cached raw
186+
# bytes (e.g. auth trailing data) leak from the parsed packet.
187+
if ethpart.type == 0x86dd: # IPv6
188+
pkt = (scapy.Ether(src=mac_dst, dst=mac_src, type=0x86dd) /
189+
IPv6(src=ip_dst, dst=ip_src, hlim=255) /
190+
scapy.UDP(sport=udp_sport, dport=udp_dport) /
191+
bfdpart)
192+
else: # IPv4
193+
pkt = (scapy.Ether(src=mac_dst, dst=mac_src, type=0x0800) /
194+
IP(src=ip_dst, dst=ip_src, ttl=255) /
195+
scapy.UDP(sport=udp_sport, dport=udp_dport) /
196+
bfdpart)
197+
198+
pkt.show()
199+
return pkt

0 commit comments

Comments
 (0)