root/lang/ruby/misc/cidr2

Revision 9325, 6.0 kB (checked in by ihag, 5 years ago)

lang/ruby/misc/cidr2: propset

  • Property svn:executable set to *
  • Property svn:keywords set to Id
Line 
1#!/usr/local/bin/ruby
2# cidr2: CIDR style IPv4 address and netmask calculator
3# $Id$
4
5require 'readline'
6
7#
8# Constants
9#
10
11BITLENGTH = 32
12ALLONE = (2 ** BITLENGTH - 1)
13DEFAULT_IP = "192.168.0.0"
14
15PROMPT = {
16  :global            => 'cidr> ',
17  :expect_subnetmask => '(subnet)> ',
18}
19
20
21#
22# Global Variables
23#
24
25$flag_full = false
26$flag_verbose = 0
27$flag_dump = false
28$address = []
29
30
31
32###########################################################################
33# Procedures
34###########################################################################
35
36#
37# show usage
38#
39def usage
40  print <<-"EOD".gsub(/^    /, '')
41    cidr2: CIDR style IPv4 address and netmask calculator
42   
43    Usage: #{File.basename($0)} [-d [-f]] [IP_adress]/<prefix_length>
44    Options:
45      -d  switch to fping mode (dump IP addresses)
46      -f  sub-option of -d.  dump with broadcast/network address.
47   
48    Examples:
49      [Command line mode]
50        Basic usage
51          % cidr2 192.168.255.240/28
52          % cidr2 192.168.255.240/255.255.255.240
53       
54        IP address is ommitable (implies "192.168.0.0").
55          % cidr2 /28
56   
57        Cisco IOS style netmask (wildcard mask) capable.
58          % cidr2 192.168.255.240/0.0.0.15
59       
60        The slash is ommitable when you specifiy IP address and mask both.
61          % cidr2 192.168.255.240 0.0.0.15
62   
63        It parse arguments generously. All of below are acceptable:
64          % cidr2 192.168.255.240 /28
65          % cidr2 blahblahblah 192.168.255.240 foobarbaz /28
66          % cidr2 access-list 10 deny 192.168.255.240 0.0.0.15
67
68      [Interactive mode]
69        Basic usage
70          % cidr2
71          cidr2> 172.31.255.252 /30
72
73        It memorize last passed IP address.
74          cidr2> /29  (=> process as "172.31.255.252/29")
75          cidr2> /255.255.255.248 (=> same as above)
76          cidr2> /0.0.0.7         (=> same as above)
77          cidr2> /16  (=> process as "172.31.255.252/16")
78
79        It parse arguments generously.
80          cidr2> IP address 192.168.0.0 is mine! don't use in your network!
81          (subnet)> /24    (=> ask subnet mask if you don't specified)
82  EOD
83
84  exit(1)
85end
86
87#
88# IP address <=> String conversion
89#
90def str2ipaddr(str, delim = '.')
91  ipaddr = 0
92  str.split(delim).each {|o| ipaddr *= 256; ipaddr += o.to_i}
93
94  return ipaddr
95end
96
97def ipaddr2str(ipaddr, delim = '.')
98  octet = []
99  for i in 0..3
100    addr = ipaddr
101    addr = addr >> (i * 8)
102    addr = addr & 255
103
104    octet.unshift(addr)
105  end
106
107  str = octet.join(delim)
108  return str
109end
110
111#
112# Token class
113#
114class Token
115  def initialize(func)
116    @buf = []
117    @func = func
118  end
119  def get(mode)
120    if @buf.size == 0 then
121      line = @func.call(mode)
122      if line.nil? then
123        return nil
124      end
125
126      @buf = line.chomp.strip.split(/\s+/)
127      @buf.push ""
128    end
129    return @buf.shift
130  end
131  def push(token)
132    @buf.unshift(token)
133    self
134  end
135end
136
137#
138# Parser
139#
140def parseinput(mode, funcinput)
141  buf = funcinput.get(mode)
142
143  case mode
144  when :global
145    case buf
146    when /^(\d+\.\d+\.\d+\.\d+)?\/([0-9a-fA-Fx.]+)$/
147      ipaddr = $1
148      mask = $2
149      result(ipaddr, mask)
150    when /^(\d+\.\d+\.\d+\.\d+)$/
151      ipaddr = $1
152      result(ipaddr, parseinput(:expect_subnetmask, funcinput))
153    when /^\/([0-9a-fA-Fx.]+)$/
154      mask = $1
155      result(nil, mask)
156    when nil
157      return nil
158    end
159    return(parseinput(:global, funcinput))
160
161  when :expect_subnetmask
162    case buf
163    when /^\/?(\d+\.\d+\.\d+\.\d+)$/
164      # dotted decimal
165      return $1
166    when /^\/?(\d+)$/
167      # length
168      return $1
169    when /^\/?(0x[0-9a-fA-F]+)$/
170      # hex
171      return $1
172    when /^\./, /^end$/i
173      return nil
174    when nil
175      return nil
176    end
177    return(parseinput(:expect_subnetmask, funcinput))
178  end
179end
180
181#
182# show result
183#
184def result(ipaddr, prefixlen)
185  if ipaddr.nil? then
186    if $recent_ip then
187      ipaddr = $recent_ip
188    else
189      ipaddr = DEFAULT_IP.dup
190    end
191  end
192  $recent_ip = ipaddr
193  return if prefixlen.nil?
194
195  case prefixlen
196  when /^0x/
197    # hex
198    prefixlen = prefixlen.hex
199    l = 0
200    for i in 0...BITLENGTH
201      if (prefixlen[i] === 1) then
202        l = i
203        break
204      end
205    end
206
207    prefixlen = BITLENGTH - l
208  when /\./
209    # dotted decimal (inverse or in-order mask)
210    prefixlen = str2ipaddr(prefixlen)
211    if (prefixlen[BITLENGTH - 1] === 0) then
212      # make inverse mask into in-order mask
213      prefixlen = ALLONE ^ prefixlen
214    end
215
216    l = 0
217    for i in 0...BITLENGTH
218      if (prefixlen[i] === 1) then
219        l = i
220        break
221      end
222    end
223
224    prefixlen = BITLENGTH - l
225  else
226    # numeric (length)
227    prefixlen = prefixlen.to_i
228  end
229
230
231  mask = (ALLONE & ~(2 ** (BITLENGTH - prefixlen) - 1))
232  size = 2 ** (BITLENGTH - prefixlen)
233
234  ipaddr = str2ipaddr(ipaddr)
235  networkaddr = ipaddr & mask
236  broadcastaddr = networkaddr + size - 1
237
238
239  if (!$flag_dump) then
240    print "IP address    : ", ipaddr2str(ipaddr), "\n"
241    print "Netmask       : ", ipaddr2str(mask),
242                              " (", ipaddr2str(ALLONE ^ mask), ")", "\n"
243    print "Prefix length : /", prefixlen, "\n"
244    print "Network addr. : ", ipaddr2str(networkaddr), "\n"
245    print "Broadcast     : ", ipaddr2str(broadcastaddr), "\n"
246    print "# of IP addr. : ", size, " (#{size - 2})", "\n\n"
247  end
248
249  if ($flag_dump) then
250    for i in networkaddr..broadcastaddr
251      next if !$flag_full && i === networkaddr
252      next if !$flag_full && i === broadcastaddr
253
254      print ipaddr2str(i), "\n"
255    end
256  end
257end
258
259###########################################################################
260# Main
261###########################################################################
262
263commandline = []
264ARGV.each do |arg|
265  case arg
266  when /^-d/
267    $flag_dump = true
268  when /^-f/
269    $flag_full = true
270  when /^-h/
271    usage; exit
272  when /^-v/
273    $flag_verbose += 1
274  else
275    commandline.push arg
276  end
277end
278
279if commandline.size > 0 then
280  # commandline mode
281  parseinput(:global, Token.new(lambda {|dummy| commandline.shift }))
282else
283  # interactive mode
284  parseinput(:global,
285    Token.new(lambda {|mode|
286      Readline.readline(PROMPT[mode], true) }))
287end
Note: See TracBrowser for help on using the browser.