Skip to content

feat(cisco): model ASA DNS server groups and domain-lookup - #10133

Merged
dhalperi merged 4 commits into
batfish:masterfrom
Murt2005:feature/asa-dns-extended-parsing
Jul 31, 2026
Merged

feat(cisco): model ASA DNS server groups and domain-lookup#10133
dhalperi merged 4 commits into
batfish:masterfrom
Murt2005:feature/asa-dns-extended-parsing

Conversation

@Murt2005

Copy link
Copy Markdown
Contributor

Summary

Add ASA-native extraction for the Cisco ASA dns domain-lookup, dns server-group (and its name-server, domain-name, timeout, retries, poll-timer, expire-entry-timer sub-commands), dns name-server, dns-group, and dns-group-map/dns-to-domain commands to capture DNS-resolver state: which interfaces have name lookup enabled, the configured DNS server groups with their servers and tuning parameters, the default server group, and domain-to-group mappings. This is captured in the vendor-specific cisco_asa model; interface references are recorded so DNS domain-lookup and name-server source interfaces are not reported as unused. It also removes the inherited IOS-style DNS grammar/extraction that never applied to real ASA configurations and was never tested.

Changes

  • Add lexer tokens DNS_GROUP, DNS_GROUP_MAP, DNS_TO_DOMAIN, EXPIRE_ENTRY_TIMER, MINUTES, and POLL_TIMER, and remove DNS from the null_single ignored alternation so top-level dns ... dispatches to a real stanza.

  • Add the s_dns grammar rule dispatching to dns_domain_lookup (dns domain-lookup <interface>), dns_name_server (dns name-server <ip>...), and a dns_server_group submode (name-server <ip>... [interface], domain-name, timeout, retries, poll-timer minutes, expire-entry-timer minutes), plus a closed dns_null fallback for unmodeled dns globals. Add s_dns_group (dns-group <name>) and the s_dns_group_map submode with dns_to_domain (dns-to-domain <group> <domain>). Remove the IOS-only s_domain, s_ip_domain, s_ip_domain_name, and s_ip_name_server rules and their stanza dispatch entries; keep the real ASA global domain-name (s_domain_name).

  • Add two new VS model classes in representation/cisco_asa: DnsServerGroup.java (name, ordered NameServer list, and the group's domainName, timeoutSeconds, retries, pollTimerMinutes, expireEntryTimerMinutes tuning parameters) and NameServer.java (server IP plus optional per-line sourceInterface), both with equals/hashCode/toString.

  • Add DNS state to AsaConfiguration.java: a per-interface dnsDomainLookupInterfaces set (DNS lookup is a per-interface setting, not a single global flag), an insertion-ordered dnsServerGroups map with a getOrCreateDnsServerGroup accessor, a dnsGroupMap (domain -> group), and a nullable defaultDnsServerGroup. The vendor-independent Configuration.dnsServers set is now derived from every group's name servers. Remove the now-unused dnsServers and dnsSourceInterface fields (the latter had no ASA equivalent).

  • Add DNS_DOMAIN_LOOKUP_INTERFACE and DNS_SERVER_GROUP_NAME_SERVER_INTERFACE structure usages and remove the IOS-only DOMAIN_LOOKUP_SOURCE_INTERFACE and IP_DOMAIN_LOOKUP_INTERFACE. Populate the new handlers in AsaControlPlaneExtractor.java: exitDns_domain_lookup, exitDns_name_server (into the default DefaultDNS group), enter/exitDns_server_group with the dnsg_* sub-command methods, exitS_dns_group, and exitDns_to_domain. Both the domain-lookup interface and each name-server source interface record an INTERFACE structure reference (matching the existing source-interface patterns), and nameif logical names are referenced by raw text since nameif registers the interface as a structure. Server-group tuning values are validated against their documented ranges (timeout 1-30, retries 0-10, poll-timer/expire-entry-timer 1-65535) via toIntegerInSpace, so out-of-range values are warned and dropped rather than stored.

Testing

  • Add two cisco_asa testconfigs: asa_dns (per-interface dns domain-lookup on three nameif interfaces, a full DefaultDNS group with a multi-IP name-server line carrying a trailing interface plus a second name-server line, domain-name/timeout/retries/poll-timer/expire-entry-timer, a second InsideDNS group, a global dns name-server, dns-group-map/dns-to-domain mappings, dns-group, and a global domain-name) and asa_dns_bad_ranges (out-of-range tuning values).

  • Add unit tests confirming extraction works correctly: the per-interface domain-lookup set, server-group contents (ordered name servers with per-line source interface and all tuning parameters across both groups), the DefaultDNS unification of dns name-server, the domain-to-group map and default group, rejection of out-of-range tuning values, the vendor-independent DNS server set and domain name after conversion, and a reference test asserting each DNS-referenced interface carries the expected referrer count.

  • All tests pass locally, formatting (google-java-format) passes, and the full cisco_asa grammar suite runs clean.

References:

Replace the inherited IOS-style DNS grammar and extraction with an
ASA-native model. Parse "dns domain-lookup", "dns server-group" (with
name-server, domain-name, timeout, retries, poll-timer, and
expire-entry-timer), "dns name-server", "dns-group", and
"dns-group-map"/"dns-to-domain".

DNS lookup is now tracked per interface instead of as a single global
flag. DNS servers are modeled as DnsServerGroup objects (each holding
NameServer entries and tuning parameters) keyed by group name, and the
vendor-independent DNS server set is derived from all groups. Server
group tuning values are validated against their documented ranges, and
out-of-range values are dropped with a warning.

Remove the IOS-only "domain", "ip domain", "ip domain-name", and
"ip name-server" rules along with the now-unused dnsServers and
dnsSourceInterface fields.
@batfish-bot

Copy link
Copy Markdown

This change is Reviewable

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.51515% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.23%. Comparing base (b3189f0) to head (e9d3150).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
...tfish/representation/cisco_asa/DnsServerGroup.java 53.33% 12 Missing and 9 partials ⚠️
...g/batfish/representation/cisco_asa/NameServer.java 41.17% 7 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10133      +/-   ##
==========================================
+ Coverage   74.22%   74.23%   +0.01%     
==========================================
  Files        3482     3484       +2     
  Lines      179024   179137     +113     
  Branches    21962    21975      +13     
==========================================
+ Hits       132876   132983     +107     
+ Misses      36289    36280       -9     
- Partials     9859     9874      +15     
Files with missing lines Coverage Δ
...sh/grammar/cisco_asa/AsaControlPlaneExtractor.java 20.28% <100.00%> (+0.99%) ⬆️
...ish/representation/cisco_asa/AsaConfiguration.java 53.31% <100.00%> (+0.58%) ⬆️
...ish/representation/cisco_asa/AsaStructureType.java 100.00% <100.00%> (ø)
...sh/representation/cisco_asa/AsaStructureUsage.java 100.00% <100.00%> (ø)
...g/batfish/representation/cisco_asa/NameServer.java 41.17% <41.17%> (ø)
...tfish/representation/cisco_asa/DnsServerGroup.java 53.33% <53.33%> (ø)

... and 25 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Assert the extracted DnsServerGroup objects as a set in the grammar
test so DnsServerGroup and NameServer equals/hashCode are exercised,
raising patch coverage above the target.
@Murt2005

Copy link
Copy Markdown
Contributor Author

Happy to add the last 0.06% of the code coverage to make the test pass if necessary :)

@dhalperi dhalperi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhalperi reviewed 2 files and made 4 comments.
Reviewable status: 2 of 11 files reviewed, 4 unresolved discussions (waiting on Murt2005).


projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/AsaParser.g4 line 2315 at r2 (raw file):

;

s_dns

create a new file called Asa_dns.4 for s_dns and move all this to it.


projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/AsaParser.g4 line 2322 at r2 (raw file):

      | dns_name_server
      | dns_server_group
      | dns_null

we don't use dns_null catch-all rules -- that would be a rule like dns_null: DNS null_rest_of_line (or similar). If you want to ignore keywords, create one null rule per keyword.


projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/AsaParser.g4 line 2392 at r2 (raw file):

:
   DNS_GROUP_MAP NEWLINE
   dns_to_domain*

Please reread the docs about idiomatic grammar rules. this should be like dnsgm_dns_to_domain -- the dnsgm is for dns_group_map and dns_to_domain is the next token. This problem happens in here a lot.


projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/AsaParser.g4 line 2624 at r2 (raw file):

;

s_ip_name_server

is the claim that all of these are not valid ASA syntax? I didn't see it called out.

Move s_dns and its related rules (dns server-group, dns-group, dns-group-map) out of AsaParser.g4 into Asa_dns.g4.

Rename child rules to follow the <parent-abbrev>_<next-token> convention: dnsg_* becomes dnssg_* (dns server-group) and dns_to_domain becomes dnsgm_dns_to_domain (dns-group-map). Drop the dns_null catch-all rule; unmodeled dns subcommands now surface as normal unrecognized-line warnings instead of being silently swallowed.

Update AsaControlPlaneExtractor context imports and listener methods to match the renamed rules. No parsing or extraction behavior changes. All tests pass locally.
@Murt2005

Copy link
Copy Markdown
Contributor Author

Applied all your feedback. Moved the dns related grammar to Asa_dns.g4. Removed the dns_null catch-all rule so unmodeled subcommands don't get silently swallowed. Renamed child rules to follow <parent-abbrev><next-token> convention and updated imports and listener methods in AsaControlPlaneExtractor to match the renamed rules.

projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/AsaParser.g4 line 2624 at r2 (raw file):

;

s_ip_name_server

is the claim that all of these are not valid ASA syntax? I didn't see it called out.

Yes, these are not valid ASA syntax. The commands s_ip_name_server, s_ip_domain, s_ip_domain_name are not valid ASA syntax and they are all IOS/IOS-XE commands. s_domain is valid syntax for ASA but the grammar modeled it incorrectly using IOS/IOS-XE syntax so I removed it. I called it out in the PR here:

Summary

...
It also removes the inherited IOS-style DNS grammar/extraction that never applied to real ASA configurations and was never tested.

Changes

...
Remove the IOS-only s_domain, s_ip_domain, s_ip_domain_name, and s_ip_name_server rules and their stanza dispatch entries; keep the real ASA global domain-name (s_domain_name).

References:

@dhalperi dhalperi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! One Q before I merge.

@dhalperi reviewed 10 files and all commit messages, made 3 comments, and resolved 4 discussions.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on Murt2005).


projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/Asa_dns.g4 line 54 at r3 (raw file):

dnssg_timeout
:
   TIMEOUT secs = dec NEWLINE

can these be more precise than dec? I see you have ranges in the extrator.


projects/batfish/src/main/java/org/batfish/grammar/cisco_asa/AsaControlPlaneExtractor.java line 4451 at r3 (raw file):

  public void exitDnsgm_dns_to_domain(Dnsgm_dns_to_domainContext ctx) {
    // "dns-to-domain <group> <domain>" maps a domain to a DNS server group. Each domain maps to
    // exactly one group.

should we define and track references to these groupo?

dhalperi added a commit that referenced this pull request Jul 31, 2026
Reviewing a vendor parsing PR by reading the diff misses the failures
that matter: a submode rule that silently swallows the next global
command, a removed rule that regresses configs no test covers, or
extracted state that nothing ever reads. The skill drives a
differential-probe workflow instead -- parse a hand-built config on the
branch and on the merge base, then diff -- and points at the docs/ that
already encode our conventions (parser_rule_conventions for LL(1) shape
and rule naming, implementation_guide for the extract-vs-_null decision
and Pattern 4 structure tracking, extraction/README for range
validation and ParseWarning vs redFlag).

The severity rubric is the load-bearing part, since the two easiest
mistakes both produce confident wrong findings. CLI submodes are not
indentation-sensitive, so a submode rule consuming a trailing global
command is usually what the real device does -- fidelity, not a bug;
and Batfish parses canonically-ordered show-run output, so an ordering
that cannot be emitted is not a realistic input. Then: grep each new
getter for callers outside the extractor, because a write-only field
that no conversion or question reads cannot carry a correctness
finding. Only after those two gates does a newly-surfaced parse warning
get ranked, and it ranks low -- error recovery is per-line and the
snapshot still converts.

references/probe-harness.md carries the mechanics, including the trap
that a javac failure prints no marker lines and so reads as empty
output rather than as a compile error.

Also gitignore Claude Code's local settings and state directories so
project config (skills) can be shared while permission allowlists and
transcripts stay local.

----

Prompt:
```
Use this to start a repo local code review skill. It should be able to
produce both your and my insights on this PR, it should include reading
or pulling the important parts of the docs/ about idiomatic grammar,
parsing, extraction, converstion, reference tracking. It should include
using local or fetching CLI reference manuals as appropriate, etc.
```

Written after reviewing #10133, where two of my three
initial findings did not survive review: one described device-faithful
submode behavior, and one corrupted a field nothing reads. Both gates
in the rubric come from those corrections. A claim that probe runs need
--nocache_test_results was also removed as false -- Bazel invalidates
on the edited test source and testconfig, and a cached result still
replays its log under --test_output=all; both verified before
committing.
Narrow the dns server-group subcommand values from the generic dec rule to sized numeric rules so the parser rejects structurally-impossible values: timeout and retries use uint8, poll-timer and expire-entry-timer use uint16 (added a uint16 rule to Asa_common). The extractor keeps its range checks for the exact valid sub-ranges. Update asa_dns_bad_ranges to use 0 for expire-entry-timer, which parses as uint16 but is still out of range, so the extractor's range-drop path stays exercised.

Track DNS server groups as a referenceable structure. Add the DNS_SERVER_GROUP structure type and DNS_GROUP / DNS_GROUP_MAP_DNS_TO_DOMAIN usages; define the structure at "dns server-group" and record references at "dns-group" and "dns-to-domain"; mark it concrete during conversion. This populates defined-structure, undefined-reference, and unused-structure analysis for DNS groups. Extend testDnsReferences to assert the group definitions and referrer counts.

All tests pass locally.
@Murt2005

Copy link
Copy Markdown
Contributor Author

projects/batfish/src/main/antlr4/org/batfish/grammar/cisco_asa/Asa_dns.g4 line 54 at r3 (raw file):

dnssg_timeout
:
   TIMEOUT secs = dec NEWLINE

can these be more precise than dec? I see you have ranges in the extrator.

Yes. I added a uint16 rule to Asa_common and changed the grammar to accept: uint8 for timeout and retries & uint16 for poll-timer and expire-entry-timer. Updated the value for expire-entry-timer minutes in asa_dns_bad_ranges config from 99999999 to 0 since the original value wouldn't fit under the updated grammar.

projects/batfish/src/main/java/org/batfish/grammar/cisco_asa/AsaControlPlaneExtractor.java line 4451 at r3 (raw file):

  public void exitDnsgm_dns_to_domain(Dnsgm_dns_to_domainContext ctx) {
    // "dns-to-domain <group> <domain>" maps a domain to a DNS server group. Each domain maps to
    // exactly one group.

should we define and track references to these groupo?

Good idea. Since a dns server-group can be referenced by dns-group and dns-group-map there should be structure tracking, i.e. some config has dns-group example but no dns server-group example was ever defined this would be bad. I added the structure tracking.

@dhalperi dhalperi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhalperi reviewed 8 files and all commit messages, and resolved 2 discussions.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on Murt2005).

@dhalperi
dhalperi merged commit aba214a into batfish:master Jul 31, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants