Skip to content

Commit

Permalink
[SAI-P4] Add security table to ingress ACLs for ToR without IPv6. (s…
Browse files Browse the repository at this point in the history
…onic-net#225)

Co-authored-by: rhalstea <[email protected]>
Co-authored-by: kishanps <[email protected]>
Co-authored-by: jonathan-dilorenzo <[email protected]>
  • Loading branch information
4 people authored Jun 22, 2024
1 parent 225f7e9 commit d7685c8
Show file tree
Hide file tree
Showing 8 changed files with 470 additions and 36 deletions.
8 changes: 4 additions & 4 deletions p4rt_app/tests/golden_outputs/p4_constraints_test.expected
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ updates {
UNKNOWN: Batch failed, individual results:
#1: INVALID_ARGUMENT: All entries must satisfy:

In @entry_restriction of table 'ingress.acl_ingress.acl_ingress_table'; at offset line 9, columns 5 to 65:
| dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
9 | ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In @entry_restriction of table 'ingress.acl_ingress.acl_ingress_table'; at offset line 10, columns 5 to 65:
| dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
10 | ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

But your entry does not.
>>>Relevant Entry Info<<<
Expand Down
129 changes: 118 additions & 11 deletions sai_p4/instantiations/google/acl_ingress.p4
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ control acl_ingress(in headers_t headers,
bit<2> ecn = 0;
// IPv4 IP protocol or IPv6 next_header (or 0, for non-IP packets)
bit<8> ip_protocol = 0;
// Captures whether to copy the packet at the end of the ingress pipeline.
bool marked_to_copy = false;
// Cancels out marked_to_copy when true.
bool cancel_copy = false;

@id(ACL_INGRESS_METER_ID)
direct_meter<MeterColor_t>(MeterType.bytes) acl_ingress_meter;
Expand All @@ -36,6 +40,9 @@ control acl_ingress(in headers_t headers,
@id(ACL_INGRESS_COUNTING_COUNTER_ID)
direct_counter(CounterType.packets_and_bytes) acl_ingress_counting_counter;

@id(ACL_INGRESS_SECURITY_COUNTER_ID)
direct_counter(CounterType.packets_and_bytes) acl_ingress_security_counter;

// Copy the packet to the CPU, and forward the original packet.
@id(ACL_INGRESS_COPY_ACTION_ID)
@sai_action(SAI_PACKET_ACTION_COPY, SAI_PACKET_COLOR_GREEN)
Expand All @@ -44,13 +51,10 @@ control acl_ingress(in headers_t headers,
action acl_copy(@sai_action_param(QOS_QUEUE) @id(1) qos_queue_t qos_queue) {
acl_ingress_counter.count();
acl_ingress_meter.read(local_metadata.color);
local_metadata.packet_in_target_egress_port = standard_metadata.egress_spec;
local_metadata.packet_in_ingress_port = standard_metadata.ingress_port;

// We model the behavior for GREEN packets only.
// TODO: Branch on color and model behavior for all colors.
clone_preserving_field_list(CloneType.I2E, COPY_TO_CPU_SESSION_ID,
PreservedFieldList.CLONE_I2E_PACKET_IN);
marked_to_copy = true;
}

// Copy the packet to the CPU. The original packet is dropped.
Expand Down Expand Up @@ -84,7 +88,6 @@ control acl_ingress(in headers_t headers,
@sai_action(SAI_PACKET_ACTION_DROP, SAI_PACKET_COLOR_YELLOW)
@sai_action(SAI_PACKET_ACTION_DROP, SAI_PACKET_COLOR_RED)
action acl_forward() {
acl_ingress_counter.count();
acl_ingress_meter.read(local_metadata.color);
// We model the behavior for GREEN packes only here.
// TODO: Branch on color and model behavior for all colors.
Expand All @@ -110,11 +113,9 @@ control acl_ingress(in headers_t headers,
}

@id(ACL_INGRESS_SET_QOS_QUEUE_AND_CANCEL_COPY_ABOVE_RATE_LIMIT_ACTION_ID)
// TODO: OA does not support SAI_PACKET_ACTION_COPY_CANCEL
// @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_GREEN)
// @sai_action(SAI_PACKET_ACTION_COPY_CANCEL, SAI_PACKET_COLOR_YELLOW)
// @sai_action(SAI_PACKET_ACTION_COPY_CANCEL, SAI_PACKET_COLOR_RED)
@sai_action(SAI_PACKET_ACTION_FORWARD)
@sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_GREEN)
@sai_action(SAI_PACKET_ACTION_COPY_CANCEL, SAI_PACKET_COLOR_YELLOW)
@sai_action(SAI_PACKET_ACTION_COPY_CANCEL, SAI_PACKET_COLOR_RED)
action set_qos_queue_and_cancel_copy_above_rate_limit(
@id(1) @sai_action_param(QOS_QUEUE) qos_queue_t qos_queue) {
acl_ingress_qos_meter.read(local_metadata.color);
Expand All @@ -140,6 +141,18 @@ control acl_ingress(in headers_t headers,
// TODO: Branch on color and model behavior for all colors.
}

// Drops the packet at the end of the the pipeline and ensures that it is not
// copied to the CPU. See `acl_drop` for more information on the mechanism of
// the drop.
@id(ACL_INGRESS_DENY_ACTION_ID)
// TODO: OA does not support SAI_PACKET_ACTION_DENY.
// @sai_action(SAI_PACKET_ACTION_DENY)
@sai_action(SAI_PACKET_ACTION_DROP)
action acl_deny() {
cancel_copy = true;
mark_to_drop(standard_metadata);
}

@p4runtime_role(P4RUNTIME_ROLE_SDN_CONTROLLER)
@id(ACL_INGRESS_TABLE_ID)
@sai_acl(INGRESS)
Expand All @@ -150,8 +163,10 @@ control acl_ingress(in headers_t headers,
dst_ip::mask != 0 -> is_ipv4 == 1;
dst_ipv6::mask != 0 -> is_ipv6 == 1;
ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
#if defined(SAI_INSTANTIATION_MIDDLEBLOCK) || defined(SAI_INSTANTIATION_FABRIC_BORDER_ROUTER)
dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
#endif
ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
// Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.
l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);
Expand Down Expand Up @@ -202,12 +217,13 @@ control acl_ingress(in headers_t headers,
// Field for v4 TTL and v6 hop_limit
ttl : ternary @name("ttl") @id(10)
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_TTL);
// Field for v4 and v6 DSCP bits.
#if defined(SAI_INSTANTIATION_MIDDLEBLOCK) || defined(SAI_INSTANTIATION_FABRIC_BORDER_ROUTER)
dscp : ternary @name("dscp") @id(11)
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_DSCP);
// Field for v4 and v6 ECN bits.
ecn : ternary @name("ecn") @id(12)
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ECN);
#endif
// Field for v4 IP protocol and v6 next header.
ip_protocol : ternary @name("ip_protocol") @id(13)
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL);
Expand Down Expand Up @@ -362,6 +378,82 @@ control acl_ingress(in headers_t headers,
size = ACL_INGRESS_COUNTING_TABLE_MINIMUM_GUARANTEED_SIZE;
}

// ACL table that only drops or denies packets, and is otherwise a no-op.
@id(ACL_INGRESS_SECURITY_TABLE_ID)
@sai_acl(INGRESS)
@p4runtime_role(P4RUNTIME_ROLE_SDN_CONTROLLER)
@entry_restriction("
// Forbid using ether_type for IP packets (by convention, use is_ip* instead).
ether_type != 0x0800 && ether_type != 0x86dd;
// Only allow IP field matches for IP packets.
dst_ip::mask != 0 -> is_ipv4 == 1;
src_ip::mask != 0 -> is_ipv4 == 1;
#if defined(SAI_INSTANTIATION_EXPERIMENTAL_TOR)
dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);
// Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.
l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);
l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);
#endif
// Forbid illegal combinations of IP_TYPE fields.
is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);
is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);
is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);
// Forbid unsupported combinations of IP_TYPE fields.
is_ipv4::mask != 0 -> (is_ipv4 == 1);
is_ipv6::mask != 0 -> (is_ipv6 == 1);
")
table acl_ingress_security_table {
key = {
headers.ipv4.isValid() || headers.ipv6.isValid() : optional
@id(1) @name("is_ip")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE/IP);
headers.ipv4.isValid() : optional
@id(2) @name("is_ipv4")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE/IPV4ANY);
headers.ipv6.isValid() : optional
@id(3) @name("is_ipv6")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE/IPV6ANY);
headers.ethernet.ether_type : ternary
@id(4) @name("ether_type")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE);
headers.ipv4.src_addr : ternary
@id(5) @name("src_ip")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_SRC_IP) @format(IPV4_ADDRESS);
headers.ipv4.dst_addr : ternary
@id(6) @name("dst_ip")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_DST_IP) @format(IPV4_ADDRESS);
dscp : ternary
@id(9) @name("dscp")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_DSCP);
ip_protocol : ternary
@id(10) @name("ip_protocol")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL);
local_metadata.l4_src_port : ternary
@id(11) @name("l4_src_port")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT);
local_metadata.l4_dst_port : ternary
@id(12) @name("l4_dst_port")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT);
local_metadata.route_metadata : ternary
@id(13) @name("route_metadata")
@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ROUTE_DST_USER_META);
// TODO: OA does not support SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META.
// local_metadata.acl_metadata : ternary
// @id(14) @name("acl_metadata")
// @sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META);
}
actions = {
@proto_id(1) acl_forward();
@proto_id(2) acl_drop(standard_metadata);
@proto_id(3) acl_deny();
@defaultonly NoAction;
}
const default_action = NoAction;
counters = acl_ingress_security_counter;
size = ACL_INGRESS_TABLE_MINIMUM_GUARANTEED_SIZE;
}

apply {
if (headers.ipv4.isValid()) {
ttl = headers.ipv4.ttl;
Expand All @@ -375,15 +467,30 @@ control acl_ingress(in headers_t headers,
ip_protocol = headers.ipv6.next_header;
}

// If a packet gets punted, this adds relevant metadata to it. Must happen
// here, rather than below, since `standard_metadata` can change based on
// actions taken in the various tables.
local_metadata.packet_in_target_egress_port = standard_metadata.egress_spec;
local_metadata.packet_in_ingress_port = standard_metadata.ingress_port;

#if defined(SAI_INSTANTIATION_MIDDLEBLOCK)
acl_ingress_table.apply();
#elif defined(SAI_INSTANTIATION_FABRIC_BORDER_ROUTER)
acl_ingress_table.apply();
acl_ingress_counting_table.apply();
#elif defined(SAI_INSTANTIATION_TOR)
// These tables are currently order agnostic, but we should be careful to
// ensure that the ordering is correct if we add new actions or model
// additional parts of SAI in the future.
acl_ingress_table.apply();
acl_ingress_qos_table.apply();
acl_ingress_security_table.apply();
#endif

if (marked_to_copy && !cancel_copy) {
clone_preserving_field_list(CloneType.I2E, COPY_TO_CPU_SESSION_ID,
PreservedFieldList.CLONE_I2E_PACKET_IN);
}
}
} // control ACL_INGRESS

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ tables {
alias: "acl_ingress_table"
annotations: "@p4runtime_role(\"sdn_controller\")"
annotations: "@sai_acl(INGRESS)"
annotations: "@entry_restriction(\"\n // Forbid using ether_type for IP packets (by convention, use is_ip* instead).\n ether_type != 0x0800 && ether_type != 0x86dd;\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n // Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.\n l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n\n\n\n\n\n // Only allow icmp_type matches for ICMP packets\n icmp_type::mask != 0 -> ip_protocol == 1;\n\n icmpv6_type::mask != 0 -> ip_protocol == 58;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")"
annotations: "@entry_restriction(\"\n // Forbid using ether_type for IP packets (by convention, use is_ip* instead).\n ether_type != 0x0800 && ether_type != 0x86dd;\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n\n dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n\n ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n // Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.\n l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n\n\n\n\n\n // Only allow icmp_type matches for ICMP packets\n icmp_type::mask != 0 -> ip_protocol == 1;\n\n icmpv6_type::mask != 0 -> ip_protocol == 58;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")"
}
match_fields {
id: 1
Expand Down
3 changes: 3 additions & 0 deletions sai_p4/instantiations/google/ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define ACL_INGRESS_TABLE_ID 0x02000100 // 33554688
#define ACL_INGRESS_QOS_TABLE_ID 0x02000107 // 33554695
#define ACL_INGRESS_COUNTING_TABLE_ID 0x02000109 // 33554697
#define ACL_INGRESS_SECURITY_TABLE_ID 0x0200010A // 33554698
#define ACL_PRE_INGRESS_TABLE_ID 0x02000101 // 33554689
#define ACL_PRE_INGRESS_VLAN_TABLE_ID 0x02000105 // 33554693
#define ACL_PRE_INGRESS_METADATA_TABLE_ID 0x02000106 // 33554694
Expand All @@ -44,6 +45,7 @@
0x0100010C // 16777484
#define ACL_INGRESS_SET_QOS_QUEUE_AND_DENY_ABOVE_RATE_LIMIT_ACTION_ID \
0x0100010E // 16777486
#define ACL_INGRESS_DENY_ACTION_ID 0x0100010F // 16777487
#define ACL_EGRESS_FORWARD_ACTION_ID 0x0100010D // 16777485
#define ACL_WBB_INGRESS_COPY_ACTION_ID 0x01000107 // 16777479
#define ACL_WBB_INGRESS_TRAP_ACTION_ID 0x01000108 // 16777480
Expand All @@ -61,6 +63,7 @@
#define ACL_INGRESS_COUNTER_ID 0x13000102 // 318767362
#define ACL_INGRESS_QOS_COUNTER_ID 0x13000107 // 318767367
#define ACL_INGRESS_COUNTING_COUNTER_ID 0x13000109 // 318767369
#define ACL_INGRESS_SECURITY_COUNTER_ID 0x1300010A // 318767370
#define ACL_WBB_INGRESS_COUNTER_ID 0x13000103 // 318767363
#define ACL_EGRESS_COUNTER_ID 0x13000104 // 318767364
#define ACL_EGRESS_DHCP_TO_HOST_COUNTER_ID 0x13000108 // 318767368
Expand Down
2 changes: 1 addition & 1 deletion sai_p4/instantiations/google/middleblock.p4info.pb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ tables {
alias: "acl_ingress_table"
annotations: "@p4runtime_role(\"sdn_controller\")"
annotations: "@sai_acl(INGRESS)"
annotations: "@entry_restriction(\"\n // Forbid using ether_type for IP packets (by convention, use is_ip* instead).\n ether_type != 0x0800 && ether_type != 0x86dd;\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n // Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.\n l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n\n // Only allow arp_tpa matches for ARP packets.\n arp_tpa::mask != 0 -> ether_type == 0x0806;\n\n\n\n\n\n icmpv6_type::mask != 0 -> ip_protocol == 58;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")"
annotations: "@entry_restriction(\"\n // Forbid using ether_type for IP packets (by convention, use is_ip* instead).\n ether_type != 0x0800 && ether_type != 0x86dd;\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n\n dscp::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ecn::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n\n ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n // Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.\n l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n\n // Only allow arp_tpa matches for ARP packets.\n arp_tpa::mask != 0 -> ether_type == 0x0806;\n\n\n\n\n\n icmpv6_type::mask != 0 -> ip_protocol == 58;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")"
}
match_fields {
id: 1
Expand Down
Loading

0 comments on commit d7685c8

Please sign in to comment.