Implement firewall matching with CIDR rules
Company: Databricks
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Onsite
Quick Answer: This question evaluates understanding of IPv4 addressing and CIDR prefix matching, including bitwise reasoning, robust parsing, handling of edge cases, and algorithmic efficiency for large rule sets.
Part 1: Implement isAllowed for IPv4 firewall matching with CIDR rules
Constraints
- 0 <= len(rules) <= 10^5
- Each IPv4 octet must be an integer in [0, 255]
- Each prefix length must be an integer in [0, 32]
- Rule order matters: the first matching rule decides the result
- If any rule or the queried IP is malformed, return False
Examples
Input: ([('ALLOW', '192.168.1.0/24'), ('DENY', '0.0.0.0/0')], '192.168.1.99')
Expected Output: True
Explanation: Both rules match, but the first match is ALLOW, so the IP is allowed.
Input: ([('DENY', '10.0.0.0/8'), ('ALLOW', '10.0.0.0/16')], '10.0.5.6')
Expected Output: False
Explanation: The IP matches both CIDRs, but the earlier DENY rule wins.
Input: ([('ALLOW', '10.0.0.0/24')], '10.0.1.1')
Expected Output: False
Explanation: No rule matches, so the default result is DENY.
Input: ([('ALLOW', '1.2.3.4/32'), ('DENY', '0.0.0.0/0')], '1.2.3.4')
Expected Output: True
Explanation: A /32 rule matches exactly one IP address.
Input: ([('ALLOW', '10.0.0.0/8'), ('DENY', '300.0.0.0/8')], '10.1.2.3')
Expected Output: False
Explanation: Any malformed rule makes the whole input invalid for this problem, so return False.
Hints
- Convert dotted IPv4 strings into a single 32-bit integer so comparisons become bit operations.
- For a prefix length L, build a mask with the top L bits set and compare masked values.
Part 2: Validate coverage of a firewall matcher test suite
Constraints
- 0 <= len(test_suite) <= 10^4
- The total number of rules across all cases is at most 2 * 10^5
- Only valid cases may cover categories other than INVALID_INPUT
- IPv4 and CIDR validation rules are the same as in the firewall problem
Examples
Input: ([([('ALLOW', '10.0.0.0/24')], '10.0.0.5'), ([('ALLOW', '10.0.0.0/24')], '11.0.0.1'), ([('DENY', '10.0.0.0/8'), ('ALLOW', '10.0.0.0/16')], '10.0.1.1'), ([('ALLOW', '192.168.1.0/24')], '192.168.1.255'), ([('ALLOW', '0.0.0.0/0')], '8.8.8.8'), ([('DENY', '1.2.3.4/32')], '1.2.3.4'), ([('ALLOW', '300.1.1.0/24')], '1.1.1.1')],)
Expected Output: []
Explanation: This suite covers every required category, including invalid input.
Input: ([([('ALLOW', '10.0.0.0/24')], '10.0.0.5'), ([('ALLOW', '10.0.0.0/24')], '11.0.0.1'), ([('DENY', '10.0.0.0/8'), ('ALLOW', '10.0.0.0/16')], '10.0.1.1'), ([('ALLOW', '0.0.0.0/0')], '8.8.8.8')],)
Expected Output: ['BOUNDARY', 'EDGE_PREFIX_32', 'INVALID_INPUT']
Explanation: The suite has a simple match, a non-match, precedence, and /0, but no boundary case, no /32 case, and no invalid input case.
Input: ([],)
Expected Output: ['SIMPLE_MATCH', 'SIMPLE_NON_MATCH', 'FIRST_RULE_WINS', 'BOUNDARY', 'EDGE_PREFIX_0', 'EDGE_PREFIX_32', 'INVALID_INPUT']
Explanation: An empty proposed suite covers nothing.
Input: ([([('ALLOW', '10.0.0.0/24')], '999.1.1.1')],)
Expected Output: ['SIMPLE_MATCH', 'SIMPLE_NON_MATCH', 'FIRST_RULE_WINS', 'BOUNDARY', 'EDGE_PREFIX_0', 'EDGE_PREFIX_32']
Explanation: This suite covers only INVALID_INPUT because the queried IP is malformed.
Hints
- Reuse the same IP-to-integer and CIDR parsing logic you would use in the firewall matcher itself.
- Track each category with a boolean flag; one valid case can cover several categories at once.
Part 3: Extend firewall matching to CIDR range queries
Constraints
- 0 <= len(rules) <= 5000
- Each action must be 'ALLOW' or 'DENY'
- Each IPv4 octet must be in [0, 255] and each prefix length in [0, 32]
- Rules are evaluated in the given order
- Malformed input should return 'INVALID'
Examples
Input: ([('ALLOW', '10.0.0.0/24')], '10.0.0.0/24')
Expected Output: 'ALLOW'
Explanation: The single rule covers the whole queried block, so every IP is allowed.
Input: ([('ALLOW', '10.0.0.0/25')], '10.0.0.0/24')
Expected Output: 'MIXED'
Explanation: The first half of the /24 is allowed, while the second half is denied by default.
Input: ([('DENY', '10.0.0.0/24'), ('ALLOW', '10.0.0.0/25')], '10.0.0.0/25')
Expected Output: 'DENY'
Explanation: Even though the later ALLOW overlaps, the earlier DENY already matches the whole query block.
Input: ([], '0.0.0.0/0')
Expected Output: 'DENY'
Explanation: With no rules at all, every IP is denied by default.
Input: ([('ALLOW', '10.0.0.0/24')], '10.0.0.0/33')
Expected Output: 'INVALID'
Explanation: A prefix length of 33 is invalid.
Hints
- Convert each CIDR to an integer interval [start, end].
- Process rules in order while keeping track of only the still-unresolved parts of the query range.
Part 4: Implement the exact IPv4-to-CIDR matching condition
Constraints
- Each IPv4 octet must be an integer in [0, 255]
- The prefix length must be an integer in [0, 32]
- Return False on malformed input
- You should use bit operations rather than string-by-string binary comparison
Examples
Input: ('192.168.1.10', '192.168.1.0/24')
Expected Output: True
Explanation: The first 24 bits match.
Input: ('192.168.2.10', '192.168.1.0/24')
Expected Output: False
Explanation: The third octet differs, so the first 24 bits do not match.
Input: ('8.8.8.8', '0.0.0.0/0')
Expected Output: True
Explanation: A /0 CIDR matches every IPv4 address.
Input: ('1.2.3.4', '1.2.3.4/32')
Expected Output: True
Explanation: A /32 CIDR matches exactly one IP.
Input: ('256.1.1.1', '1.2.3.0/24')
Expected Output: False
Explanation: The IP is malformed because 256 is outside the valid IPv4 octet range.
Hints
- A prefix length of 0 means every IP matches.
- Use a 32-bit mask with the top L bits set, then compare masked values.