diff --git a/scapy/contrib/roce.py b/scapy/contrib/roce.py index cc9a2a37ae1..96cad2314c1 100644 --- a/scapy/contrib/roce.py +++ b/scapy/contrib/roce.py @@ -9,7 +9,7 @@ """ RoCE: RDMA over Converged Ethernet """ - +from scapy.layers.inet6 import IPv6 from scapy.packet import Packet, bind_layers, Raw from scapy.fields import ByteEnumField, ByteField, XByteField, \ ShortField, XShortField, XLongField, BitField, XBitField, FCSField @@ -157,30 +157,38 @@ def compute_icrc(self, p): warning("Expecting UDP underlayer to compute checksum. Got %s.", udp and udp.name) return self.pack_icrc(0) + ip = udp.underlayer - if isinstance(ip, IP): - # pseudo-LRH / IP / UDP / BTH / payload - pshdr = Raw(b'\xff' * 8) / ip.copy() + # pseudo-LRH / IP / UDP / BTH / payload + pshdr = Raw(b'\xff' * 8) / ip.copy() + if isinstance(ip, IP): # IPv4 pshdr.chksum = 0xffff pshdr.ttl = 0xff pshdr.tos = 0xff - pshdr[UDP].chksum = 0xffff - pshdr[BTH].fecn = 1 - pshdr[BTH].becn = 1 - pshdr[BTH].resv6 = 0xff - bth = pshdr[BTH].self_build() - payload = raw(pshdr[BTH].payload) - # add ICRC placeholder just to get the right IP.totlen and - # UDP.length - icrc_placeholder = b'\xff\xff\xff\xff' - pshdr[UDP].payload = Raw(bth + payload + icrc_placeholder) - icrc = crc32(raw(pshdr)[:-4]) & 0xffffffff - return self.pack_icrc(icrc) + + elif isinstance(ip, IPv6): # IPv6 + pshdr.fl = 0xfffff + pshdr.tc = 0xff + pshdr.hlim = 0xff + else: - # TODO support IPv6 - warning("The underlayer protocol %s is not supported.", + warning(f"The underlayer protocol %s is not supported.", ip and ip.name) - return self.pack_icrc(0) + return struct.pack("!I", 0 & 0xffffffff)[::-1] + + pshdr[UDP].chksum = 0xffff + pshdr[BTH].fecn = 1 + pshdr[BTH].becn = 1 + pshdr[BTH].resv6 = 0xff + bth = pshdr[BTH].self_build() + payload = raw(pshdr[BTH].payload) + + # add ICRC placeholder just to get the right IP.totlen and + # UDP.length + icrc_placeholder = b'\xff\xff\xff\xff' + pshdr[UDP].payload = Raw(bth + payload + icrc_placeholder) + icrc = crc32(raw(pshdr)[:-4]) & 0xffffffff + return self.pack_icrc(icrc) # RoCE packets end with ICRC - a 32-bit CRC of the packet payload and # pseudo-header. Add the ICRC header if it is missing and calculate its diff --git a/test/contrib/roce.uts b/test/contrib/roce.uts index 757163d3049..b659657f2fa 100644 --- a/test/contrib/roce.uts +++ b/test/contrib/roce.uts @@ -76,6 +76,7 @@ assert pkt.opcode == CNP_OPCODE del pkt.icrc pkt = Ether(pkt.build()) assert pkt.icrc == 0x82fd002a +assert int(pkt[BTH].compute_icrc(pkt).hex(), 16) == pkt.icrc = RoCE v1 RC RDMA WRITE ONLY @@ -124,3 +125,29 @@ assert not pkt[BTH].ackreq assert pkt[AETH].syndrome == 0 assert pkt[AETH].msn == 5 assert pkt.icrc == 0x25f0c038 + += RoCE v2 RC ACKNOWLEDGE + +pkt = Ether(import_hexcap('''\ +0000 98 03 9b 99 51 3e 98 03 9b 99 51 42 81 00 60 00 +0010 86 dd 60 e0 00 00 00 1c 11 80 20 00 00 11 02 37 +0020 00 00 9a 03 9b ff fe 99 51 42 20 00 00 11 02 37 +0030 00 00 9a 03 9b ff fe 99 51 3e c1 a2 12 b7 00 1c +0040 00 00 11 40 ff ff 00 00 0b af 00 08 0c 7b 00 00 +0050 00 01 84 06 28 9a +''')) + +assert BTH in pkt.layers() +assert AETH in pkt.layers() +assert pkt[IPv6].version == 6 +assert pkt[IPv6].tc == 14 +assert pkt[IPv6].fl == 0 +assert pkt[IPv6].plen == 28 +assert pkt[BTH].opcode == 0x11 +assert pkt[BTH].padcount == 0 +assert pkt[BTH].dqpn == 0x000baf +assert not pkt[BTH].ackreq +assert pkt[AETH].syndrome == 0 +assert pkt[AETH].msn == 1 +assert pkt.icrc == 0x8406289a +assert int(pkt[BTH].compute_icrc(pkt).hex(), 16) == pkt.icrc