Forum Discussion

spalande's avatar
spalande
Icon for Nacreous rankNacreous
12 years ago

Need help for iRule

Need help on iRule for mapping private IP hosted on GTM (A record) to public IP (owned by customer DMZ).

 

eg. 192.168.4.242 is the private IP hosted on GTM, while sending DNS response need to map it to public IP. I have an idea of creating data group where I can map all private IPs to public, but don't have idea how to use in iRule for DNS response event.

 

4 Replies

  • The iRule would require a data-group, i.e.:

    ltm data-group internal dg_internal_external {
        records {
            10.131.131.125/32 {
                data 10.141.141.125
            }
            10.131.131.126/32 {
                data 10.141.141.126
            }
        }
        type ip
    }
    

    An LTM (!) iRule to translate a response would look as follows:

    when DNS_RESPONSE {
    
        foreach item [DNS::answer] {
            if {[DNS::type $item] eq "A"} {
                log local0. "[DNS::rdata $item]"
                log local0. "[DNS::type $item]"
                log local0. "[class match -value [DNS::rdata $item] equals dg_internal_external]"
                DNS::rdata $item [class match -value [DNS::rdata $item] equals dg_internal_external]
            } else {
    
                log local0. "[DNS::type $item]"
            }
    
        }
    }
    

    The iRule will be associated with your GMT listener (as well in the LTM context):

    modify ltm virtual vs_10_131_131_53_53_gtm rules { rule_modify_dns_response }

    Now the translation should work as I just tested:

     host -t A test.lb-net.bit 10.131.131.53
    Using domain server:
    Name: 10.131.131.53
    Address: 10.131.131.5353
    Aliases:
    
    test.lb-net.bit has address 10.141.141.126
     host -t A test.lb-net.bit 10.131.131.53
    Using domain server:
    Name: 10.131.131.53
    Address: 10.131.131.5353
    Aliases:
    
    test.lb-net.bit has address 10.141.141.125
    

    You may want to use the iRule editor to craft the data-group and to assign it to the listener. This would be required with a plain GTM license.

    Make sure to build your iRule in the LTM context. It sounds a bit odd, but the GTM listener is an object in the LTM context.

  • I´ve tried to solve it with catch in a single-line but still got TCL errors.

    (Probably due to the nesting.)

    This one works now and inserts an A record for a sorrypage in case of no match:
    when DNS_RESPONSE {
        foreach item [DNS::answer] {
            if {[DNS::type $item] eq "A"} {
                log local0. "[DNS::rdata $item]"
                log local0. "[DNS::type $item]"
                if { [class match -value [DNS::rdata $item] equals dg_internal_external] eq "" } {
                    log local0. "no match for internal A record [DNS::rdata $item]"
                    DNS::rdata $item 10.141.141.10
                } else {
                    log local0. "[class match -value [DNS::rdata $item] equals dg_internal_external]"
                    DNS::rdata $item [class match -value [DNS::rdata $item] equals dg_internal_external]
                }
            } else {
                log local0. "[DNS::type $item]"
            }
        }
    }
    
  • Testing for A records in the response is already build in and would be logged:

    when DNS_REQUEST {
            if {[DNS::question type] ne "A"} {
            log local0. "incoming query for [DNS::question name] is of type [DNS::question type]"
        }
    }
    when DNS_RESPONSE {
        foreach item [DNS::answer] {
            if {[DNS::type $item] eq "A"} {
                log local0. "[DNS::rdata $item]"
                log local0. "[DNS::type $item]"
                if { [class match -value [DNS::rdata $item] equals dg_internal_external] eq "" } {
                    log local0. "no match for internal A record [DNS::rdata $item]"
                    DNS::rdata $item 10.141.141.10
                } else {
                    log local0. "[class match -value [DNS::rdata $item] equals dg_internal_external]"
                    DNS::rdata $item [class match -value [DNS::rdata $item] equals dg_internal_external]
                }
            } else {
                log local0. "[DNS::type $item]"
            }
        }
    }
    

    The log will show now:

    incoming query for test.lb-net.bit is of type AAAA

  • No worries. Here is a updated version allowing to filter for non-A record queries and to enable / disable logging globally.

    I also used a global static variable to set a fallback in case there is no datagroup match:
    when RULE_INIT {
        set static::debug 1
         $fallback_A will be used to replace in case of no data-group match
        set static::fallback_A 10.141.141.10
    }
    when DNS_REQUEST {
        if {[DNS::question type] ne "A"} {
            if { $static::debug != 0 } {
                log local0. "incoming query for [DNS::question name] is of type [DNS::question type]"
            }
        }
    }
    when DNS_RESPONSE {
        foreach item [DNS::answer] {
            if {[DNS::type $item] eq "A"} {
                if { $static::debug != 0 } {
                    log local0. "internal ([DNS::type $item] [DNS::rdata $item])"
                }
                if { [class match -value [DNS::rdata $item] equals dg_internal_external] eq "" } {
                    if { $static::debug != 0 } {
                        log local0. "no match for internal A record [DNS::rdata $item]"
                    }
                    DNS::rdata $item $static::fallback_A
                } else {
                    if { $static::debug != 0 } {
                        log local0. "replace with [class match -value [DNS::rdata $item] equals dg_internal_external]"
                    }
                    DNS::rdata $item [class match -value [DNS::rdata $item] equals dg_internal_external]
                }
            } else {
                if { $static::debug != 0 } {
                    log local0. "internal ([DNS::type $item], RR data: [DNS::rdata $item])"
                }
            }
        }
    }
    

    Log output looks as expected:

    : internal (A 10.131.131.125)  
    : replace with 10.141.141.125  
    : internal (A 10.131.131.127)  
    : no match for internal A record 10.131.131.127  
    : incoming query for test.lb-net.bit is of type AAAA  
    : incoming query for test.lb-net.bit is of type AAAA  
    : internal (CNAME, RR data: fallback.lb-net.bit)