Skip to main content

I am trying to get data out of a Cisco DMVPN hub on the spokes via NHRP database.  Specifically the ARIN addresses and if the spoke has a firewall or other NAT.

 

I was modelling off of this NQE question and it works...except for the 2nd pattern is not detecting the child line.

The above question had a parent line that was always present if the new child was present and NHRP has the new child only if there is a NAT in the path while the parent line is always present.

 

The line in question is the “(Claimed NBMA address: <IP address>)” line at the bottom of the 2nd example - it is indented 1 space from the line above ‘NBMA address’ which is always present.

 

I tried both the NHRP data in the data model as well as a custom command ‘show ip nhrp’ with identical results.

 

 

 

//NHRP Database output

//Command: show ip nhrp

//10.100.12.5/32 via 10.100.12.5

//   Tunnel1 created 1d18h, expire 01:56:31

//   Type: dynamic, Flags: unique registered nhop

//   NBMA address: 12.12.31.74

//10.100.12.6/32 via 10.100.12.6

//   Tunnel1 created 2w2d, expire 01:53:05

//   Type: dynamic, Flags: unique registered nhop

//   NBMA address: 48.22.12.199

//    (Claimed NBMA address: 192.168.64.24)


 

The NQE I can get results on all the tunnels but the 

 

NHRPnoFWpattern =

  ```

{IPandPrefix:string} via {VPNpeer: ipv4Address}

  {tunnelnum: string} created

  NBMA address: {TunPublic: ipv4Address}

```;

 

NHRPwithNATpattern =

  ```

{IPandPrefix:string} via {VPNpeer: ipv4Address}

  {tunnelnum: string} created

  NBMA address: {TunPublic: ipv4Address}

   Claimed NBMA address: {NoNATtunPublic: ipv4Address}

```;




 

foreach device in network.devices

foreach command in device.outputs.commands

  where device.name == "<DMVPN_headend_routername>"

//  where command.commandType == CommandType.NHRP_STATE

  where command.commandText == "show ip nhrp"

 

foreach x in =1]

let ParsedResponse = parseConfigBlocks(OS.UNKNOWN, command.response)

foreach r in blockMatches(ParsedResponse, NHRPnoFWpattern)

let rPlus = max(foreach match in blockMatches(ParsedResponse, NHRPwithNATpattern)

            where match.data.IPandPrefix == r.data.IPandPrefix

            select match)

 

select {

  Prefix: r.data.IPandPrefix,

  IP: r.data.VPNpeer,

  ARIN: r.data.TunPublic,

  LAN: rPlus?.data?.NoNATtunPublic

}


 

 

 

 

@CarlB The “blockMatches” has an implicit logical AND.  All lines in the pattern must be present to get a match. 

The other issue is that the round bracket is just treated as a character.  Which means you pattern needs to look more like this

NHRPwithNATpattern =

```

{IPandPrefix:string} via {VPNpeer: ipv4Address}

{tunnelnum: string} created

NBMA address: {TunPublic: ipv4Address}

(Claimed NBMA address: {NoNATtunPublic: string}

```;

Add in the left round bracket to (Claimed.  Then the type of the NoNATtunPublic will need to be String instead of ipv4Address.  This is because there is a right round bracket touching the ip address. 


@Tyson Henrie  - thanks. 

 

The followup is how to remove the parenthesis from the string - I tried the replace function but that is picking up the “)” that I am trying to replace with “” as the closing of the function as opposed to as the actual character that I want to replace.


@CarlB This is a bit tricky the way patternMatching works.

Here is a fix for your example, you need to apply the fix on the raw data before it gets parsed into ConfigBlocks

 

/**
* @intent Enter your intent here (one line, 50 characters max)
* @description Enter your description here (multiple lines are possible)
*/

data =
"""
10.100.12.5/32 via 10.100.12.5
Tunnel1 created 1d18h, expire 01:56:31
Type: dynamic, Flags: unique registered nhop
NBMA address: 12.12.31.74
10.100.12.6/32 via 10.100.12.6
Tunnel1 created 2w2d, expire 01:53:05
Type: dynamic, Flags: unique registered nhop
NBMA address: 48.22.12.199
(Claimed NBMA address: 192.168.64.24)
""";

NHRPnoFWpattern =
```
{IPandPrefix:string} via {VPNpeer:ipv4Address}
{tunnelnum:string} created
NBMA address: {TunPublic:ipv4Address}
```;

NHRPwithNATpattern =
```
{IPandPrefix:string} via {VPNpeer:ipv4Address}
{tunnelnum:string} created
NBMA address: {TunPublic:ipv4Address}
Claimed NBMA address: {NoNATtunPublic:ipv4Address}
```;

cleanParen(s) = cleanLParen(cleanRParen(s));

cleanLParen(s) = replaceMatches(s, "(", "");

cleanRParen(s) = replaceMatches(s, ")", "");

foreach x in n1]
let ParsedResponse = parseConfigBlocks(OS.UNKNOWN, cleanParen(data))
// let ParsedResponse = parseConfigBlocks(OS.UNKNOWN, command.response)
foreach r in blockMatches(ParsedResponse, NHRPnoFWpattern)
let rPlus = max(foreach match
in blockMatches(ParsedResponse, NHRPwithNATpattern)
where match.data.IPandPrefix == r.data.IPandPrefix
select match)
select {
Prefix: r.data.IPandPrefix,
IP: r.data.VPNpeer,
ARIN: r.data.TunPublic,
LAN: rPlus?.data?.NoNATtunPublic
}

Hope this helps

-g


Just to conclude, with 24.10 we can use the function replaceRegexMatches for a cleaner interface.

 

cleanParenRegex(s) = replaceRegexMatches(s, re`\(Claimed NBMA address: (\S+)\)`, "Claimed NBMA address: $1");

foreach x in 1]
let ParsedResponse = parseConfigBlocks(OS.UNKNOWN, cleanParenRegex(data))
// let ParsedResponse = parseConfigBlocks(OS.UNKNOWN, command.response)
foreach r in blockMatches(ParsedResponse, NHRPnoFWpattern)
let rPlus = max(foreach match
in blockMatches(ParsedResponse, NHRPwithNATpattern)
where match.data.IPandPrefix == r.data.IPandPrefix
select match)
select {
ParsedResponse,
r: cleanParenRegex(data),
Prefix: r.data.IPandPrefix,
IP: r.data.VPNpeer,
ARIN: r.data.TunPublic,
LAN: rPlus?.data?.NoNATtunPublic
}

 


@GaryB - thanks.  Processing the above now.


Reply