Skip to content

Commit 5b983fc

Browse files
committed
LPM Masking: Fix masking for fields with bitwidth non divisable by 8.
When prefix and/or bitwidth are not aligend to size of a byte, we must make sure the mask is applied correctly to all bits in field. Until now, the logic expected to find maximum of one byte that needs special masking, and that masking is relative to the "beginning" (MSB bits) of the bytes. This caused issues with fields that are smaller than a byte. Another issue, some fields and prefixed might need some bits of a the "first" and "last" byte in the prefix to be masked, for example: Field bitwidth = 20 Prefix length = 16 Mask should be = 0b11111111111111110000 Since we are representing in byte arrays, in bytes we get -> 00001111 11111111 11110000 (24 bits, 3 bytes) Old implmentation disregarded the first 2 bytes and only masked the last byte (0b11110000). Both issues are fixed in this commit, by this flow: 1. Utilizing prefix byte array, which is aligned to number of bytes needed to represent bitwidth in bytes. 2. Creating a mask for entire field, according to prefix length. 3. Applying it to all bits in prefix length. Issue: #146
1 parent 22ed0c9 commit 5b983fc

File tree

1 file changed

+21
-10
lines changed

1 file changed

+21
-10
lines changed

p4runtime_sh/shell.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ def _sanitize_and_convert_mf_lpm(self, prefix, length, field_info):
428428
if length == 0:
429429
raise UserError(
430430
"Ignoring LPM don't care match (prefix length of 0) as per P4Runtime spec")
431-
431+
432432
mf = p4runtime_pb2.FieldMatch()
433433
mf.field_id = field_info.id
434434
mf.lpm.prefix_len = length
@@ -440,19 +440,30 @@ def _sanitize_and_convert_mf_lpm(self, prefix, length, field_info):
440440

441441
barray = bytearray(prefix)
442442
transformed = False
443-
r = length % 8
444-
byte_mask = 0xff & ((0xff << (8 - r)))
445-
if barray[first_byte_masked] & byte_mask != barray[first_byte_masked]:
446-
transformed = True
447-
barray[first_byte_masked] = barray[first_byte_masked] & byte_mask
448-
449-
for i in range(first_byte_masked + 1, len(prefix)):
450-
if barray[i] != 0:
443+
# When prefix mask and field bithwidth are not aligned to the size of a
444+
# byte, we need to make sure mask is applied to the all bytes in prefix
445+
# that should be masked.
446+
# Therefore, we will create a byte array mask for all the bits in the
447+
# field and apply it to entire prefix, zeroing out the bits that should
448+
# not be part of the prefix.
449+
mask = ((1 << length) - 1) << (field_info.bitwidth - length)
450+
nbytes = (field_info.bitwidth + 7) // 8
451+
bytes_mask = bytearray(mask.to_bytes(nbytes, byteorder='big'))
452+
453+
# Prefix len is aligned to num of byte needed to represent bitwidth in parsing stage.
454+
if len(bytes_mask) != len(prefix): # Should not happen, safety check.
455+
raise UserError("Invalid prefix length")
456+
457+
transformed = False
458+
for i in range(len(bytes_mask)):
459+
if barray[i] & bytes_mask[i] != barray[i]:
451460
transformed = True
452-
barray[i] = 0
461+
barray[i] = barray[i] & bytes_mask[i]
462+
453463
if transformed:
454464
_print("LPM value was transformed to conform to the P4Runtime spec "
455465
"(trailing bits must be unset)")
466+
456467
mf.lpm.value = bytes(bytes_utils.make_canonical_if_option_set(barray))
457468
return mf
458469

0 commit comments

Comments
 (0)