Match
The term match can apply in two different contexts; in the context of a single field where match type is applied to some field value; and, where a match is comprised of multiple match types and values.
It is the difference between:
field_match = Exact(Port(80))
and:
match = Match({
'hdr.tcp.dstPort': Exact(Port(80)),
'hdr.ip.srcAddr': Exact(IPv4Address('192.168.0.1'))
})
There are two match types that can be considered in terms of a mask and a
value, LongestPrefixMatch
, and Ternary
.
In turn, each of these can be thought in the context of sets: the set of all possible values within either are the bits selected for matching, and every combination of the remaining ‘don’t care’ bits.
Exact
An exact match is exactly what it sounds like. In a key field, any comparison
must on a Field
must match exactly.
API References
Masked Matches (LPM & Ternary)
A masked match is one that can be expressed in terms of a value and a mask,
which applies to Ternary
and LongestPrefixMatch
expressions.
Masked Ternary
expressions have a mask which indicates which bits
in the value are to be considered when performing comparisons.
Note
In this library, a mask bit value of 1
indicates that the bit in the same
location in the value is to be considered as a part of the mask. This is the
same as mask expressions in P4, and will be familiar to anyone who has defined
IP networks (e.g. 192.168.0.0, 255.255.255.0
).
Note
Masked matches have exact equivalents; they are where the mask is all
1’s. With LPM, this is equivalent when a prefix is the same length as the
number of bits in the value, as defined by it’s Field
.
A ternary expression’s mask bits can be set arbitrarily across the mask.
A LongestPrefixMatch
on the other hand will have bits set
contiguously up to it’s “prefix length”.
Consequently, these match kinds share a common set of bitwise and mathematical
operations. For exposition, we’ll use an IPv4Address
to
this.
Construction
Construction is as follows:
# Ternary
ternary = Ternary(
value=IPv4Address('192.168.0.0'),
mask=IPv4Address('255.255.255.0'))
# The value keyword can be omitted, as can the constructor call of the mask.
# The mask value, if not the same type as the value, an attempt will be made
# to construct the mask using the value's type:
ternary = Ternary(IPv4Address('192.168.0.0'), mask='255.255.255.0')
# LPM
lpm = LongestPrefixMatch(value=IPv4Address('192.168.0.0'), prefix=24)
# As with the ternary expression, the value keyword can be ommitted:
lpm = LongestPrefixMatch(IPv4Address('192.168.0.0'), prefix=24)
Omitting the mask will yield an object which is equivalent to an exact match,
i.e., all the mask bits are set. However, with match.Ternary
objects, you can supply a boolean dont_care
value which, if True
, will
yield an object with none of the mask bits set. This is important when used in
the context of a key, where you might not care about matching on that specific
field.
ternary = Ternary(IPv4Address('192.168.0.0'))
# print(ternary) will yield "192.168.0.0 &&& 255.255.255.255"
ternary = Ternary(IPv4Address('192.168.0.0'), dont_care=True)
# print(ternary) will yield "192.168.0.0 &&& 0.0.0.0"
Operations
The interesting thing about masked matches is that, for some operations, they
can be considered in the contexts of sets. For example, it should be fairly
obvious that the IP subnet 192.168.0.0/24
contains all the IP addresses
beginning with 192.168.0...
We can can compare on the basis of (proper?) superset/subset and intersections, detect overlaps, but we cannot make unions.
See the following for more information:
subset_of()
:<=
superset_of()
:>=
API References
Unsupported Matches
Currently, only Exact, LPM and Ternary expressions are supported.