Skip to content

Commit bfc1414

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 bytes in prefix length byte array, except those who are fully masked (i.e. the ones in the middle, if exist). Issue: #146 Signed-off-by: Dor Akerman <[email protected]>
1 parent 22ed0c9 commit bfc1414

File tree

3 files changed

+102
-9
lines changed

3 files changed

+102
-9
lines changed

p4runtime_sh/shell.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -440,19 +440,36 @@ 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. We can skip bytes that are fully inside
449+
# the mask.
450+
mask = ((1 << length) - 1) # Create mask for the prefix length.
451+
mask = mask << (field_info.bitwidth - length) # Shift left to the correct position.
452+
nbytes = (field_info.bitwidth + 7) // 8
453+
bytes_mask = bytearray(mask.to_bytes(nbytes, byteorder='big'))
454+
455+
# Prefix len is aligned to num of byte needed to represent bitwidth in parsing stage.
456+
if len(bytes_mask) != len(prefix): # Should not happen, safety check.
457+
raise UserError("Invalid prefix length")
458+
459+
idxs_to_apply_mask = list(range(first_byte_masked, len(bytes_mask)))
460+
if first_byte_masked > 0:
461+
idxs_to_apply_mask.insert(0, 0) # Always apply mask to first byte.
462+
463+
transformed = False
464+
for i in idxs_to_apply_mask:
465+
if barray[i] & bytes_mask[i] != barray[i]:
451466
transformed = True
452-
barray[i] = 0
467+
barray[i] = barray[i] & bytes_mask[i]
468+
453469
if transformed:
454470
_print("LPM value was transformed to conform to the P4Runtime spec "
455471
"(trailing bits must be unset)")
472+
456473
mf.lpm.value = bytes(bytes_utils.make_canonical_if_option_set(barray))
457474
return mf
458475

p4runtime_sh/test.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,54 @@ def test_table_entry_lpm(self, input_, value, length):
379379
p4runtime_pb2.Update.INSERT, P4RuntimeEntity.table_entry, expected_entry)
380380

381381
self.servicer.Write.assert_called_once_with(ProtoCmp(expected_req), ANY)
382+
383+
@nose2.tools.params(
384+
("0xfaafe/16", "\\x0f\\xaa\\xf0", 16, "0x3/1", "\\x02", 1),
385+
("0xfaafe/20", "\\x0f\\xaa\\xfe", 20, "0x3/2", "\\x03", 2),
386+
("0xfaafe/8", "\\x0f\\xa0\\x00", 8, "0x1/1", "\\x00", 1),
387+
("0xfaafe/1", "\\x08\\x00\\x00", 1, "0x1/2", "\\x01", 2))
388+
def test_table_entry_lpm_bitwidth_not_multiply_of_8(
389+
self, input_20, value_20, length_20, input_2, value_2, length_2
390+
):
391+
te = sh.TableEntry("LpmTwo")(action="actionA")
392+
te.match["header_test.field20"] = input_20
393+
te.match["header_test.field2"] = input_2
394+
te.action["param"] = "aa:bb:cc:dd:ee:ff"
395+
te.insert()
396+
397+
# Cannot use format here because it would require escaping all braces,
398+
# which would make wiriting tests much more annoying
399+
expected_entry = """
400+
table_id: 33567647
401+
match {
402+
field_id: 1
403+
lpm {
404+
value: "%s"
405+
prefix_len: %s
406+
}
407+
}
408+
match {
409+
field_id: 2
410+
lpm {
411+
value: "%s"
412+
prefix_len: %s
413+
}
414+
}
415+
action {
416+
action {
417+
action_id: 16783703
418+
params {
419+
param_id: 1
420+
value: "\\xaa\\xbb\\xcc\\xdd\\xee\\xff"
421+
}
422+
}
423+
}
424+
""" % (value_20, length_20, value_2, length_2)
425+
426+
expected_req = self.make_write_request(
427+
p4runtime_pb2.Update.INSERT, P4RuntimeEntity.table_entry, expected_entry)
428+
429+
self.servicer.Write.assert_called_once_with(ProtoCmp(expected_req), ANY)
382430

383431
def test_table_entry_lpm_dont_care(self):
384432
te = sh.TableEntry("LpmOne")

p4runtime_sh/testdata/unittest.p4info.pb.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,34 @@ tables {
5050
}
5151
size: 512
5252
}
53+
tables {
54+
preamble {
55+
id: 33567647
56+
name: "LpmTwo"
57+
alias: "LpmTwo"
58+
}
59+
match_fields {
60+
id: 1
61+
name: "header_test.field20"
62+
bitwidth: 20
63+
match_type: LPM
64+
}
65+
match_fields {
66+
id: 2
67+
name: "header_test.field2"
68+
bitwidth: 2
69+
match_type: LPM
70+
}
71+
action_refs {
72+
id: 16783703
73+
}
74+
action_refs {
75+
id: 16800567
76+
annotations: "@defaultonly"
77+
scope: DEFAULT_ONLY
78+
}
79+
size: 512
80+
}
5381
tables {
5482
preamble {
5583
id: 33584148

0 commit comments

Comments
 (0)