2 changed files with 279 additions and 0 deletions
@ -0,0 +1,278 @@ |
|||||||
|
#!/usr/bin/env tclsh |
||||||
|
|
||||||
|
# Copyright (C) 2021, Maxim Lihachev, <envrm@yandex.ru> |
||||||
|
# |
||||||
|
# This program is free software: you can redistribute it and/or modify it |
||||||
|
# under the terms of the GNU General Public License as published |
||||||
|
# by the Free Software Foundation, version 3. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
# |
||||||
|
# ------------------------------------------------------------------------------ |
||||||
|
# |
||||||
|
# DESCRIPTION |
||||||
|
# |
||||||
|
# DIY project that implement basic `ipcalc' functions for IP Address |
||||||
|
# calculating. |
||||||
|
# |
||||||
|
# |
||||||
|
# USAGE |
||||||
|
# |
||||||
|
# ipcalc ip/mask |
||||||
|
# e.g. ipcalc 192.168.0.1/24 |
||||||
|
# ipcalc 192.168.0.1/255.255.255.0 |
||||||
|
# |
||||||
|
# ------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
|
||||||
|
# Convert integer to binary |
||||||
|
proc int2bin {int} { |
||||||
|
binary scan [binary format c $int] B* bin |
||||||
|
return $bin |
||||||
|
} |
||||||
|
|
||||||
|
# Convert binary to integer |
||||||
|
proc bin2int {bin} { |
||||||
|
expr {"0b$bin"} |
||||||
|
} |
||||||
|
|
||||||
|
# Convert binary form of IP address to decimal one |
||||||
|
proc binary2decimal {binary_ip} { |
||||||
|
join [lmap x [split_binary_ip $binary_ip] {bin2int $x}] {.} |
||||||
|
} |
||||||
|
|
||||||
|
# Convert decimal form of IP addrecc to binary one |
||||||
|
proc decimal2binary {decimal_ip} { |
||||||
|
return [join [lmap x [split $decimal_ip {.}] {int2bin $x}] {.}] |
||||||
|
} |
||||||
|
|
||||||
|
# Normalize binary form of IP address |
||||||
|
# E.g.: |
||||||
|
# 11111111.11111111.11111111.00000000 => 11111111111111111111111100000000 |
||||||
|
# 11111111 11111111 11111111 00000000 => 11111111111111111111111100000000 |
||||||
|
proc normalize_binary_ip {ip} { |
||||||
|
string map {{.} {} { } {}} $ip |
||||||
|
} |
||||||
|
|
||||||
|
# Split normal form of binary IP address to chunks |
||||||
|
# E.g. |
||||||
|
# 11111111111111111111111100000000 => 11111111 11111111 11111111 00000000 |
||||||
|
proc split_binary_ip {ip} { |
||||||
|
regexp -all -inline .{8} [normalize_binary_ip $ip] |
||||||
|
} |
||||||
|
|
||||||
|
# Make a dotted form of binary IP address |
||||||
|
# E.g. |
||||||
|
# 11111111111111111111111100000000 => 11111111.11111111.11111111.00000000 |
||||||
|
proc format_binary_ip {binary_ip} { |
||||||
|
join [split_binary_ip $binary_ip] {.} |
||||||
|
} |
||||||
|
|
||||||
|
# Zip two lists with function |
||||||
|
proc zipwith {list1 list2 fx} { |
||||||
|
set l1 [regexp -all -inline {.} $list1] |
||||||
|
set l2 [regexp -all -inline {.} $list2] |
||||||
|
|
||||||
|
lmap x $l1 y $l2 {apply $fx $x $y} |
||||||
|
} |
||||||
|
|
||||||
|
# Bitwise AND digit by digit |
||||||
|
# E.g.: |
||||||
|
# 11011011 |
||||||
|
# 11011000 |
||||||
|
# |||||||| |
||||||
|
# 11011000 |
||||||
|
proc bitwise_and {ip1 ip2} { |
||||||
|
join [zipwith $ip1 $ip2 {{x y} {expr {$x & $y}}}] {} |
||||||
|
} |
||||||
|
|
||||||
|
# Bitwise OR digit by digit |
||||||
|
# E.g.: |
||||||
|
# 11011011 |
||||||
|
# 11011000 |
||||||
|
# |||||||| |
||||||
|
# 11011011 |
||||||
|
proc bitwise_or {ip1 ip2} { |
||||||
|
join [zipwith $ip1 $ip2 {{x y} {expr {$x | $y}}}] {} |
||||||
|
} |
||||||
|
|
||||||
|
# Print data in default ipcalc format |
||||||
|
proc show {label {decimal {}} {binary {}}} { |
||||||
|
puts [format "%-11s%-21s%s" $label $decimal $binary] |
||||||
|
} |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
# Get network class |
||||||
|
proc network_class {ip} { |
||||||
|
set first_octet [lindex [split $ip "."] 0] |
||||||
|
|
||||||
|
if {$first_octet <= 127} { |
||||||
|
set network_class "Class A" |
||||||
|
} elseif {$first_octet <= 191} { |
||||||
|
set network_class "Class B" |
||||||
|
} elseif {$first_octet <= 223} { |
||||||
|
set network_class "Class C" |
||||||
|
} elseif {$first_octet <= 239} { |
||||||
|
set network_class "Class D" |
||||||
|
} elseif {$first_octet <= 247} { |
||||||
|
set network_class "Class E" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Calculate wildcard |
||||||
|
proc wildcard {ip netmask} { |
||||||
|
set netmask [integer_netmask $netmask] |
||||||
|
set binary_wildcard [format_binary_ip [string repeat 0 $netmask][string repeat 1 [expr {32-$netmask}]]] |
||||||
|
set decimal_wildcard [binary2decimal $binary_wildcard] |
||||||
|
|
||||||
|
return [list $decimal_wildcard $binary_wildcard] |
||||||
|
} |
||||||
|
|
||||||
|
# Get count of available hosts |
||||||
|
proc hosts_count {netmask} { |
||||||
|
set netmask [integer_netmask $netmask] |
||||||
|
expr {2 ** (32 - $netmask) - ($netmask >= 30 ? 0 : 2)} |
||||||
|
} |
||||||
|
|
||||||
|
# 190.0.0.1 10111110.00000000.00000000.00000001 |
||||||
|
# 255.255.240.0 11111111.11111111.11110000.00000000 |
||||||
|
# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
||||||
|
# 190.0.0.0 10111110.00000000.00000000.00000000 |
||||||
|
proc network {ip netmask} { |
||||||
|
set binary_address [normalize_binary_ip [decimal2binary $ip]] |
||||||
|
set binary_netmask [normalize_binary_ip [binary_netmask $netmask]] |
||||||
|
set binary_network [format_binary_ip [bitwise_and $binary_address $binary_netmask]] |
||||||
|
set network [binary2decimal $binary_network] |
||||||
|
|
||||||
|
return [list "${network}/[integer_netmask $netmask]" $binary_network] |
||||||
|
} |
||||||
|
|
||||||
|
# 255.255.240.0 11111111.11111111.11110000.00000000 |
||||||
|
# bit mask 00000000.00000000.00001111.11111111 |
||||||
|
# 190.0.0.1 10111110.00000000.00000000.00000001 |
||||||
|
# ||||||||||||||||||||||||||||||||||| |
||||||
|
# 190.0.15.255 10111110.00000000.00001111.11111111 |
||||||
|
proc host_broadcast {ip netmask} { |
||||||
|
set binary_address [normalize_binary_ip [decimal2binary $ip]] |
||||||
|
set binary_netmask [normalize_binary_ip [binary_netmask $netmask]] |
||||||
|
|
||||||
|
# 1 => 0, 0 => 1 |
||||||
|
set binary_bitmask [string map {1 0 0 1} $binary_netmask] |
||||||
|
|
||||||
|
set binary_broadcast [format_binary_ip [bitwise_or $binary_address $binary_bitmask]] |
||||||
|
set host_broadcast [binary2decimal $binary_broadcast] |
||||||
|
|
||||||
|
return [list $host_broadcast $binary_broadcast] |
||||||
|
} |
||||||
|
|
||||||
|
# broadcast-1 |
||||||
|
# |
||||||
|
# 190.0.0.1 10111110.00000000.00000000.00000001 |
||||||
|
# 255.255.240.0 11111111.11111111.11110000.00000000 |
||||||
|
# 190.0.15.255 10111110.00000000.00001111.11111111 |
||||||
|
# |
||||||
|
# 190.0.15.255-1 10111110.00000000.00001111.11111111-1 |
||||||
|
# 190.0.15.254 10111110.00000000.00001111.11111110 |
||||||
|
proc host_max {ip netmask} { |
||||||
|
set binary_broadcast [lindex [host_broadcast $ip $netmask] 1] |
||||||
|
set binary_max_host [format_binary_ip [expr {[normalize_binary_ip [lindex [host_broadcast $ip $netmask] 1]]-1}]] |
||||||
|
set max_host [binary2decimal $binary_max_host] |
||||||
|
|
||||||
|
return [list $max_host $binary_max_host] |
||||||
|
} |
||||||
|
|
||||||
|
# network+1 |
||||||
|
# |
||||||
|
# 190.0.0.1 10111110.00000000.00000000.00000001 |
||||||
|
# 255.255.240.0 11111111.11111111.11110000.00000000 |
||||||
|
# 190.0.0.0 10111110.00000000.00000000.00000000 |
||||||
|
# |
||||||
|
# 190.0.0.0+1 10111110.00000000.00000000.00000000 |
||||||
|
# 190.0.0.1 10111110.00000000.00000000.00000001 |
||||||
|
proc host_min {ip netmask} { |
||||||
|
set network [lindex [network $ip $netmask] 1] |
||||||
|
|
||||||
|
set min_host [expr {[normalize_binary_ip $network]+1}] |
||||||
|
set binary_min_host [format_binary_ip $min_host] |
||||||
|
set decimal_min_host [binary2decimal $min_host] |
||||||
|
|
||||||
|
return [list $decimal_min_host $binary_min_host] |
||||||
|
} |
||||||
|
|
||||||
|
proc decimal_netmask {netmask} { |
||||||
|
lindex $netmask 0 |
||||||
|
} |
||||||
|
|
||||||
|
proc integer_netmask {netmask} { |
||||||
|
lindex $netmask 1 |
||||||
|
} |
||||||
|
|
||||||
|
proc binary_netmask {netmask} { |
||||||
|
lindex $netmask 2 |
||||||
|
} |
||||||
|
|
||||||
|
# Calculate netmask in decimal, binary and integer formats |
||||||
|
proc netmask {mask} { |
||||||
|
proc make_binary_netmask {netmask} { |
||||||
|
format_binary_ip [string repeat 1 $netmask][string repeat 0 [expr {32-$netmask}]] |
||||||
|
} |
||||||
|
|
||||||
|
proc make_integer_netmask {netmask} { |
||||||
|
expr {[llength [split [decimal2binary $netmask] {1}]]-1} |
||||||
|
} |
||||||
|
|
||||||
|
if {[string is integer -strict $mask]} { |
||||||
|
set integer_netmask $mask |
||||||
|
set binary_netmask [make_binary_netmask $mask] |
||||||
|
set decimal_netmask [binary2decimal $binary_netmask] |
||||||
|
} else { |
||||||
|
set integer_netmask [make_integer_netmask $mask] |
||||||
|
set binary_netmask [make_binary_netmask $integer_netmask] |
||||||
|
set decimal_netmask $mask |
||||||
|
} |
||||||
|
|
||||||
|
return [list $decimal_netmask $integer_netmask $binary_netmask] |
||||||
|
} |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
set script_name [file tail $argv0] |
||||||
|
|
||||||
|
if {$argc < 1} { |
||||||
|
puts "USAGE: $script_name ip/mask" |
||||||
|
puts " e.g. $script_name 192.168.0.1/24" |
||||||
|
puts " $script_name 192.168.0.1/255.255.255.0" |
||||||
|
exit 1 |
||||||
|
} |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
lassign [split [string map {{ } {/}} $argv] {/}] address mask |
||||||
|
|
||||||
|
if {$mask != {}} { |
||||||
|
set netmask [netmask $mask] |
||||||
|
} else { |
||||||
|
set netmask [netmask 24] |
||||||
|
} |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
# Default `ipcalc' output format |
||||||
|
show Address: $address [decimal2binary $address] |
||||||
|
show Netmask: "[decimal_netmask $netmask] = [integer_netmask $netmask]" [binary_netmask $netmask] |
||||||
|
show Wildcard: {*}[wildcard $address $netmask] |
||||||
|
show => |
||||||
|
show Network: {*}[network $address $netmask] |
||||||
|
show HostMin: {*}[host_min $address $netmask] |
||||||
|
show HostMax: {*}[host_max $address $netmask] |
||||||
|
show Broadcast: {*}[host_broadcast $address $netmask] |
||||||
|
show Host/Net: [hosts_count $netmask] [network_class $address] |
||||||
|
show {} |
||||||
|
|
Loading…
Reference in new issue