Version 14 of Handling internet addresses

Updated 2004-06-08 20:31:46 by schlenk

PT 2004-Jun-07 I think it would be useful to have a module in tcllib to do some manipulations of internet addresses. If we can think up a sensible API then we can provide something to help cope with both IPv4 and IPv6 addresses. For instance, how do I check that an address is within a certain range? Is 192.168.0.4 within 192.168.0.0./24 or within 192.16./16 or even within 192.168.0.0./255.255.255.0?

Here are a few helper functions I've used elsewhere. It's likely there are faster/neater implementations :) Assume at some point we have done

 namespace eval ip4 {}

Please feel free to comment and edit. I could especially use some equivalent functions to deal with IPv6 addresses.

There are some similar functions on A Little CIDR Calculator - I shall attempt to collect the fastest versions together here. PT


ip2x Convert an IPv4 address in dotted quad notation into a hexadecimal representation. This will extend truncated ip4 addresses with zeros. eg: ip2x 192.168.0.4 -> 0xc0a80004 or ip2x 127 -> 0x7f000000 This is a little faster using [binary] than using [format]

 proc ::ip4::ip2x {ip {validate 0}} {
    set octets [split $ip .]
    if {[llength $octets] != 4} {
        set octets [lrange [concat $octets 0 0 0] 0 3]
    }
    if {$validate} {
        foreach oct $octets {
            if {$oct < 0 || $oct > 255} {
                return -code error "invalid ip address"
            }
        }
    }
    binary scan [binary format c4 $octets] H8 x
    return 0x$x
 }

x2ip Turn the hex representation of an IPv4 address into dotted quad notation.

 proc ::ip4::x2ip {hex} {
    set r {}
    set bin [binary format I [expr {$hex}]]
    binary scan $bin c4 octets
    foreach octet $octets {
        lappend r [expr {$octet & 0xFF}]
    }
    return [join $r .]
 }

ipmask Returns an IPv4 address masked with subnet bits as a hexadecimal representation. For instance: [ipmask 192.168.0.4 24] -> 0xc0a80000 This makes it easy to compare addresses as described in the introduction. Is 192.168.0.4 within 192.168/16? [expr {[ipmask 192.168.0.4 16] == [ipmask 192.168 16]}]

 proc ::ip4::ipmask {ip {bits {}}} {
    if {[string length $bits] < 1} { set bits 32 }
    set ipx [ip2x $ip]
    if {[string is integer $bits]} {
        set mask [expr {(0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF}]
    } else {
        set mask [ip2x $bits]
    }
    return [format 0x%08x [expr {$ipx & $mask}]]
 }

is_ip4_addr Use the ip4x conversion proc to check that the given address is really an IPv4 address.

 proc ::ip4::is_ip4_addr {ip} {
    if {[catch {ip2x $ip true}]} {
        return 0
    }
    return 1
 }

splitspec Split an address specification into a ipadd and mask part. This doesn't validate the address portion. If a spec with no mask is provided then the mask will be 32 (all bits significant).

 proc ::ip4::splitspec {spec} {
    set bits 32
    set domain $spec
    set slash [string last / $spec]
    if {$slash != -1} {
        incr slash -1
        set domain [string range $spec 0 $slash]
        incr slash 2
        set bits [string range $spec $slash end]
    }
    return [list $domain $bits]
 }

Examples

 proc IpaddrInDomain {addr domainspec} {
    foreach {network bits} [ip4::splitspec $domainspec] {}
    set net [ip4::ipmask $network $bits]
    set ipx [ip4::ipmask $addr $bits]
    if {$ipx == $net} {
        return 1
    }
    return 0
 }

escargo 7 Jun 2004 - From my networking experience, I can think of some functions that would be useful in handling IP addresses.

  • Is an address a host address?
  • Is an address a broadcast address?
  • Are a host address and a netmask consistent?

These would all be appropriate predicates to provide in such a library.


For a widget that allows validation of dotted decimal IP addresses take a look at mentry.


[Category Internet]