cisco_acl_intp 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.travis.yml +2 -0
  4. data/.yardopts +1 -1
  5. data/Gemfile +5 -1
  6. data/README.md +76 -22
  7. data/Rakefile +18 -6
  8. data/cisco_acl_intp.gemspec +5 -5
  9. data/lib/cisco_acl_intp.rb +8 -0
  10. data/lib/cisco_acl_intp/ace.rb +36 -39
  11. data/lib/cisco_acl_intp/ace_ip.rb +73 -31
  12. data/lib/cisco_acl_intp/ace_other_qualifiers.rb +2 -2
  13. data/lib/cisco_acl_intp/ace_port.rb +114 -28
  14. data/lib/cisco_acl_intp/ace_proto.rb +160 -73
  15. data/lib/cisco_acl_intp/ace_srcdst.rb +57 -25
  16. data/lib/cisco_acl_intp/ace_tcp_flags.rb +1 -1
  17. data/lib/cisco_acl_intp/acl.rb +81 -35
  18. data/lib/cisco_acl_intp/acl_base.rb +65 -66
  19. data/lib/cisco_acl_intp/parser.rb +914 -1037
  20. data/lib/cisco_acl_intp/parser.ry +68 -215
  21. data/lib/cisco_acl_intp/parser_api.rb +248 -0
  22. data/lib/cisco_acl_intp/scanner.rb +58 -63
  23. data/lib/cisco_acl_intp/scanner_special_token_handler.rb +2 -4
  24. data/lib/cisco_acl_intp/version.rb +9 -1
  25. data/spec/cisco_acl_intp/ace_ip_spec.rb +45 -0
  26. data/spec/cisco_acl_intp/ace_port_spec.rb +119 -0
  27. data/spec/cisco_acl_intp/ace_proto_spec.rb +40 -1
  28. data/spec/cisco_acl_intp/ace_spec.rb +3 -3
  29. data/spec/cisco_acl_intp/ace_srcdst_spec.rb +95 -1
  30. data/spec/cisco_acl_intp/acl_base_spec.rb +60 -0
  31. data/spec/cisco_acl_intp/acl_spec.rb +2 -0
  32. data/spec/cisco_acl_intp/cisco_acl_intp_spec.rb +8 -0
  33. data/spec/cisco_acl_intp/parser_spec.rb +80 -2
  34. data/spec/cisco_acl_intp/scanner_spec.rb +0 -1
  35. data/spec/conf/extended_acl.yml +18 -18
  36. data/spec/parser_fullfill_patterns.rb +3 -3
  37. data/spec/spec_helper.rb +6 -0
  38. data/tools/check_acl.rb +23 -5
  39. metadata +27 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca5d93222e696b7d5cbe8af83c2993454a7ce56f
4
- data.tar.gz: e6d9b5ce240ae6a494b11f46a461bdc6ac90ac27
3
+ metadata.gz: d664d74e52b4738c1835532f0fecefe1ee162ece
4
+ data.tar.gz: 56a40d167de808a4bf0ef778d8f443b01c408a22
5
5
  SHA512:
6
- metadata.gz: 0e7d8aa8410bd44bc5406424548936d6a245f76c4c473c64dfe9bb2834e9f8348ddb797b513317a699e3615b455a68a3e201e6a60ef464faf0c4a2df9303e921
7
- data.tar.gz: 37a36a2086e4106d327b08d6c157d829af96b2dfe807fd8fb008c52be07ddd6be0a6b221446d79e975f76a89a8428b1a34610f5f56b161cb94f95e1afffd3b9d
6
+ metadata.gz: 63c07906a0c2910a1cdbb6cb53b02cb9ef36c32b4138223c400147d8447f101b9c44fb9af211363a9a79be6f4dde533f448483975cb132fd6461a4e49a7cf463
7
+ data.tar.gz: c5208c436298dde049720eb9b5f15f41b09d482b696aa592c95220ec5fc6f779f2e8b4aebd2dda7539946d8eded55cdbfc4ba1a1b871afebefb1daf88b5d9cc2
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  language: ruby
2
+ script: 'rake travis'
2
3
  rvm:
4
+ - 1.9.3
3
5
  - 2.0.0
data/.yardopts CHANGED
@@ -1,4 +1,4 @@
1
1
  --exclude parser.rb
2
- --exclude spec/data/*
2
+ --exclude spec/**/*.rb
3
3
  --private
4
4
  --protected
data/Gemfile CHANGED
@@ -1,13 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in cisco_acl_intp.gemspec
4
6
  gemspec
5
7
 
6
8
  group :development, :test do
9
+ gem 'coveralls', require: false
7
10
  gem 'racc', '~> 1.4.11'
8
11
  gem 'rake', '~> 10.1.1'
12
+ gem 'reek', '~> 1.3.6'
9
13
  gem 'rspec', '~> 2.14.1'
10
- gem 'rubocop', '~> 0.16.0' if RUBY_VERSION >= '1.9.0'
14
+ gem 'rubocop', '~> 0.18.1' if RUBY_VERSION >= '1.9.0'
11
15
  gem 'simplecov', '~> 0.8.2' if RUBY_VERSION >= '1.9.0'
12
16
  gem 'yard', '~> 0.8.7'
13
17
  end
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
1
  # CiscoAclIntp
2
+ [![Gem Version](https://badge.fury.io/rb/cisco_acl_intp.png)](http://badge.fury.io/rb/cisco_acl_intp)
3
+ [![Build Status](https://travis-ci.org/stereocat/cisco_acl_intp.png?branch=master)](https://travis-ci.org/stereocat/cisco_acl_intp)
4
+ [![Dependency Status](https://gemnasium.com/stereocat/cisco_acl_intp.png)](https://gemnasium.com/stereocat/cisco_acl_intp)
5
+ [![Coverage Status](https://coveralls.io/repos/stereocat/cisco_acl_intp/badge.png?branch=master)](https://coveralls.io/r/stereocat/cisco_acl_intp?branch=master)
2
6
 
3
7
  CiscoAclIntp is a interpreter of Cisco IOS access control list (ACL).
4
8
 
@@ -52,28 +56,70 @@ CiscoAclIntp parser and output parser results.
52
56
  In directory `acl_examples`, there are some Cisco IOS ACL sample
53
57
  files. Run `check_acl.rb` with ACL sample files, like below.
54
58
 
55
- $ ~/cisco_acl_intp$ ruby tools/check_acl.rb -c -f acl_examples/numd-acl.txt
56
- acl name : 1
57
- access-list 1 permit 192.168.0.0 0.0.255.255
58
- access-list 1 deny any log
59
- acl name : 100
60
- access-list 100 remark General Internet Access
61
- access-list 100 permit icmp any any
62
- access-list 100 permit ip 192.168.0.0 0.0.255.255 any
63
- access-list 100 remark NTP
64
- access-list 100 permit tcp any host 210.197.74.200
65
- access-list 100 permit udp any eq ntp any eq ntp
66
- access-list 100 remark 6to4
67
- access-list 100 permit 41 any host 192.88.99.1
68
- access-list 100 permit ip any host 192.88.99.1
69
- access-list 100 remark others
70
- access-list 100 permit tcp any eq 0 any eq 0
71
- access-list 100 permit udp any eq 0 any eq 0
72
- access-list 100 deny ip any any log
73
- acl name : 110
74
- access-list 110 remark SPLIT_VPN
75
- access-list 110 permit ip 192.168.0.0 0.0.255.255 any
76
- $ ~/cisco_acl_intp$
59
+ ```
60
+ $ ruby tools/check_acl.rb -c term -f acl_examples/err-acl.txt
61
+ --------------------
62
+ in acl: 100, line: 6, near value: udp, (token: "udp")
63
+ in acl: 100, line: 8, near value: 100, (token: NUMBER)
64
+ in acl: 100, line: 11, Wrong protocol number: 256
65
+ in acl: 100, line: 14, near value: hoge, (token: error)
66
+ in acl: FA8-OUT, line: 4, Wrong protocol number: 65536
67
+ in acl: FA8-OUT, line: 7, Provided wildcard mask failed validation: 0.0.240.256 is invalid (IPv4 octets should be between 0 and 255).
68
+ in acl: FA8-OUT, line: 13, near value: up, (token: error)
69
+ --------------------
70
+ acl name : 1
71
+ access-list 1 permit 192.168.0.0 0.0.255.255
72
+ access-list 1 deny any log
73
+ acl name : 100
74
+ access-list 100 remark General Internet Access
75
+ access-list 100 permit icmp any any
76
+ access-list 100 permit ip 192.168.0.0 0.0.255.255 any
77
+ access-list 100 permit tcp any host 210.197.74.200
78
+ access-list 100 remark !wrong acl number!
79
+ access-list 100 remark !------cleared------!
80
+ access-list 100 remark !wrong header! caccess-list
81
+ access-list 100 remark !------cleared------!
82
+ access-list 100 permit 41 any host 192.88.99.1
83
+ access-list 100 remark !wrong ip proto number!
84
+ access-list 100 !! error !! 192.88.99.1
85
+ access-list 100 remark !------cleared------!
86
+ access-list 100 remark !wrong ip proto!
87
+ access-list 100 !! error !! 192.88.99.1
88
+ access-list 100 remark !------cleared------!
89
+ access-list 100 permit ip any host 192.88.99.1
90
+ access-list 100 remark others
91
+ access-list 100 permit tcp any eq 0 any eq 0
92
+ access-list 100 permit udp any eq 0 any eq 0
93
+ access-list 100 deny ip any any log
94
+ acl name : 10
95
+ access-list 10 !! error !! ntp
96
+ acl name : 110
97
+ access-list 110 remark SPLIT_VPN
98
+ access-list 110 permit ip 192.168.0.0 0.0.255.255 any
99
+ acl name : FA8-OUT
100
+ ip access-list extended FA8-OUT
101
+ deny udp any any eq bootpc
102
+ deny udp any any eq bootps
103
+ remark !argment error! 65536
104
+ !! error !! 65536
105
+ remark !------cleared------!
106
+ remark !argment error! 255 => 256
107
+ !! error !! 80
108
+ remark !------cleared------!
109
+ remark network access-list remark!!
110
+ permit tcp any any established
111
+ deny tcp any any syn rst
112
+ remark !syntax error! tcp -> tp (typo)
113
+ !! error !! hoge
114
+ remark !------cleared------!
115
+ permit ip any any log
116
+ acl name : remote-ipv4
117
+ ip access-list standard remote-ipv4
118
+ permit 192.168.0.0 0.0.255.255
119
+ remark standard access-list last deny!?
120
+ deny any log
121
+ $
122
+ ```
77
123
 
78
124
  By putting `-c` (`--color`) option, `check_acl.rb` outputs
79
125
  **color-coded ACL** according to type of each word. It can parse
@@ -117,8 +163,16 @@ table is "ACL object". "ACL object" is build by ACL components. For
117
163
  example, source/destination address obj, action obj, tcp/udp protocol
118
164
  obj,... See more detail in documents (see also, Documents section)
119
165
 
166
+ ### ACL Varidator Web Frontend
167
+
168
+ Front-end of ACL Varidator is at
169
+ [github](https://github.com/stereocat/cisco_acl_web). It not only can
170
+ parse (with CLI tool, it can only parse), but also search for ACL(ACE).
171
+
120
172
  ## Documents
121
173
 
174
+ * [API document generated with YARD](http://rubydoc.info/gems/cisco_acl_intp/)
175
+
122
176
  It can generate documents with YARD.
123
177
 
124
178
  $ rake yard
data/Rakefile CHANGED
@@ -1,6 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
  require 'rake/clean'
6
+ require 'yard'
7
+ require 'yard/rake/yardoc_task'
8
+ require 'reek/rake/task'
4
9
 
5
10
  LIB_DIR = './lib'
6
11
  PACKAGE_NAME = 'cisco_acl_intp'
@@ -23,7 +28,8 @@ CLOBBER.include(
23
28
  CLASS_GRAPH_PNG
24
29
  )
25
30
 
26
- task default: [:parser, :spec]
31
+ task default: :travis
32
+ task travis: [:parser, :spec, :rubocop]
27
33
  task parser: [PARSER_RUBY]
28
34
  task spec: [SPEC_DATA_DIR]
29
35
 
@@ -47,12 +53,9 @@ RSpec::Core::RakeTask.new(fullspec: [:fullfill]) do |spec|
47
53
  spec.rspec_opts = '--format documentation --color'
48
54
  end
49
55
 
50
- # documentation by yard
51
- require 'yard'
52
- require 'yard/rake/yardoc_task'
53
56
  YARD::Rake::YardocTask.new do |task|
54
57
  # yardoc options in .yardopts
55
- task.files = ["#{LIB_DIR}/**/*.rb"]
58
+ task.files = FileList["#{LIB_DIR}/**/*.rb"]
56
59
  end
57
60
 
58
61
  task :docgraph do
@@ -61,7 +64,16 @@ task :docgraph do
61
64
  sh "dot -Tpng #{CLASS_GRAPH_DOT} -o #{CLASS_GRAPH_PNG}"
62
65
  end
63
66
 
64
- # rubocop settings
67
+ Reek::Rake::Task.new do |t|
68
+ t.fail_on_error = false
69
+ t.verbose = false
70
+ t.ruby_opts = ['-rubygems']
71
+ t.reek_opts = '--quiet'
72
+ t.source_files = FileList["#{LIB_DIR}/**/*.rb"].delete_if do |f|
73
+ f =~ /parser.rb/
74
+ end
75
+ end
76
+
65
77
  if RUBY_VERSION >= '1.9.0'
66
78
  task quality: :rubocop
67
79
  require 'rubocop/rake_task'
@@ -9,9 +9,9 @@ Gem::Specification.new do |spec|
9
9
  spec.version = CiscoAclIntp::VERSION
10
10
  spec.authors = ['stereocat']
11
11
  spec.email = ['stereocat@gmail.com']
12
- spec.description = %q{Cisco Access List Interpreter}
13
- spec.summary = %q{Cisco Access List Interpreter}
14
- spec.homepage = ''
12
+ spec.description = %q{Cisco ACL Interpreter}
13
+ spec.summary = %q{Cisco IOS Access Control List Interpreter}
14
+ spec.homepage = 'https://github.com/stereocat/cisco_acl_intp'
15
15
  spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files`.split("\n")
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ['lib']
21
21
 
22
22
  spec.add_runtime_dependency 'netaddr', '~> 1.5.0'
23
- spec.add_runtime_dependency 'term-ansicolor', '~> 1.2.2'
24
- spec.add_development_dependency 'bundler', '~> 1.3'
23
+ spec.add_runtime_dependency 'term-ansicolor', '~> 1.3.0'
24
+ spec.add_development_dependency 'bundler', '~> 1.5.3'
25
25
  end
26
26
 
27
27
  ### Local variables:
@@ -1,3 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+
1
3
  require 'rubygems'
2
4
  require 'cisco_acl_intp/acl'
3
5
  require 'cisco_acl_intp/parser'
@@ -7,3 +9,9 @@ require 'cisco_acl_intp/version'
7
9
  # define scanner functions and ACL container classes
8
10
  module CiscoAclIntp
9
11
  end
12
+
13
+ ### Local variables:
14
+ ### mode: Ruby
15
+ ### coding: utf-8-unix
16
+ ### indent-tabs-mode: nil
17
+ ### End:
@@ -47,6 +47,20 @@ module CiscoAclIntp
47
47
  end
48
48
  end
49
49
 
50
+ # Error entry container
51
+ class ErrorAce < AceBase
52
+ attr_accessor :line
53
+
54
+ def initialize(line)
55
+ super({})
56
+ @line = line
57
+ end
58
+
59
+ def to_s
60
+ tag_error(sprintf('!! error !! %s', @line))
61
+ end
62
+ end
63
+
50
64
  # Remark entry container
51
65
  class RemarkAce < AceBase
52
66
  # @param [String] value Comment string
@@ -70,7 +84,7 @@ module CiscoAclIntp
70
84
  # Generate string for Cisco IOS access list
71
85
  # @return [String] Comment string
72
86
  def to_s
73
- sprintf ' remark %s', c_rmk(@comment.to_s)
87
+ sprintf 'remark %s', tag_remark(@comment.to_s)
74
88
  end
75
89
 
76
90
  # Search matched ACE
@@ -118,14 +132,14 @@ module CiscoAclIntp
118
132
  # Generate string for Cisco IOS access list
119
133
  # @return [String]
120
134
  def to_s
121
- sprintf 'evaluate %s', c_name(@recursive_name)
135
+ sprintf 'evaluate %s', tag_name(@recursive_name)
122
136
  end
123
137
 
124
138
  # Search matched ACE
125
139
  # @param [Hash] opts Options
126
- # return [Boolean] false, Recursive does not implemented yet
140
+ # return [Boolean]
141
+ # @todo for Recursive name matching is not implemented yet
127
142
  def matches?(opts = nil)
128
- ## TODO
129
143
  false
130
144
  end
131
145
  end
@@ -170,10 +184,10 @@ module CiscoAclIntp
170
184
  # @return [String]
171
185
  def to_s
172
186
  sprintf(
173
- ' %s %s %s',
174
- c_act(@action.to_s),
187
+ '%s %s %s',
188
+ tag_action(@action.to_s),
175
189
  @src_spec,
176
- @log_spec ? @log_spec : ''
190
+ tag_other_qualifier(@log_spec ? @log_spec : '')
177
191
  )
178
192
  end
179
193
 
@@ -184,7 +198,7 @@ module CiscoAclIntp
184
198
  # @raise [AclArgumentError] Invalid src_ip
185
199
  def matches?(opts)
186
200
  if opts.key?(:src_ip)
187
- @src_spec.ip_spec.matches?(opts[:src_ip])
201
+ @src_spec.matches?(opts[:src_ip])
188
202
  else
189
203
  fail AclArgumentError, 'Invalid match target src IP address'
190
204
  end
@@ -310,13 +324,13 @@ module CiscoAclIntp
310
324
  # @return [String]
311
325
  def to_s
312
326
  sprintf(
313
- ' %s %s %s %s %s %s',
314
- c_act(@action.to_s),
315
- c_pp(@protocol.to_s),
327
+ '%s %s %s %s %s %s',
328
+ tag_action(@action.to_s),
329
+ tag_protocol(@protocol.to_s),
316
330
  @src_spec,
317
331
  @dst_spec,
318
- @tcp_flags ? @tcp_flags : '',
319
- @tcp_other_qualifiers ? @tcp_other_qualifiers : ''
332
+ @tcp_flags,
333
+ @tcp_other_qualifiers
320
334
  )
321
335
  end
322
336
 
@@ -325,21 +339,19 @@ module CiscoAclIntp
325
339
  # @option opts [String] :protocol L3/L4 protocol name
326
340
  # (allows "tcp", "udp" and "icmp")
327
341
  # @option opts [String] :src_ip Source IP Address
328
- # @option opts [Integer] :src_port Source Port No.
342
+ # @option opts [Integer,String] :src_port Source Port No./Name
329
343
  # @option opts [String] :dst_ip Destination IP Address
330
- # @option opts [Integer] :dst_port Destination Port No.
344
+ # @option opts [Integer,String] :dst_port Destination Port No./Name
331
345
  # @return [Boolean] Matched or not
332
346
  # @raise [AclArgumentError]
333
347
  def matches?(opts)
334
348
  if opts.key?(:protocol)
335
- match_proto = match_protocol?(opts[:protocol])
336
- match_src = match_addr_port?(@src_spec, opts[:src_ip], opts[:src_port])
337
- match_dst = match_addr_port?(@dst_spec, opts[:dst_ip], opts[:dst_port])
349
+ match_protocol?(opts[:protocol]) &&
350
+ @src_spec.matches?(opts[:src_ip], opts[:src_port]) &&
351
+ @dst_spec.matches?(opts[:dst_ip], opts[:dst_port])
338
352
  else
339
353
  fail AclArgumentError, 'Invalid match target protocol'
340
354
  end
341
-
342
- (match_proto && match_src && match_dst)
343
355
  end
344
356
 
345
357
  private
@@ -350,30 +362,15 @@ module CiscoAclIntp
350
362
  # @raise [AclArgumentError]
351
363
  def match_protocol?(protocol)
352
364
  protocol_str = @protocol.to_s
353
- if protocol_str == 'ip'
354
- true # allow tcp/udp
365
+ if [protocol_str, protocol].include?('ip')
366
+ true # allow any of icmp/tcp/udp
355
367
  else
356
- ## TBD
357
- ## what to do when NO name and only protocol number is specified?
358
- # In principle, it must be compared by object.
368
+ # @todo what to do when NO name and only protocol number is
369
+ # specified? In principle, it must be compared by object.
359
370
  protocol == protocol_str
360
371
  end
361
372
  end
362
373
 
363
- # check src/dst address
364
- # @option srcdst_spec [AceSrcDstSpec] src/dst address/port
365
- # @option ip [String] ip addr to compare
366
- # @option port [Integer] port number to compare
367
- # @return [Boolean] Matched or not
368
- # @raise [AclArgumentError]
369
- def match_addr_port?(srcdst_spec, ip, port)
370
- if ip
371
- srcdst_spec.matches?(ip, port)
372
- else
373
- fail AclArgumentError, 'Not specified match target IP Addr'
374
- end
375
- end
376
-
377
374
  # Set instance variables
378
375
  # return [AceIpProtoSpec] IP protocol object
379
376
  # raise [AclArgumentError]
@@ -12,20 +12,19 @@ module CiscoAclIntp
12
12
  # @param [NetAddr::CIDR] value IP address
13
13
  # (dotted decimal notation)
14
14
  # @return [NetAddr::CIDR]
15
- attr_accessor :ipaddr
15
+ attr_reader :ipaddr
16
16
 
17
17
  # @param [Integer] value Netmask length
18
18
  # @return [Integer]
19
- attr_accessor :netmask
19
+ attr_reader :netmask
20
20
 
21
21
  # @param [String] value Wildcard mask
22
22
  # (dotted decimal and bit flapped notation)
23
23
  # @return [String]
24
- attr_accessor :wildcard
24
+ attr_reader :wildcard
25
25
 
26
26
  def_delegator :@ipaddr, :ip, :ipaddr
27
- # `contained_ip?' method is cidr(ipaddr/nn) operation
28
- def_delegator :@ipaddr, :is_contained?, :contained_ip?
27
+
29
28
  # `matches?' method is wildcard mask operation
30
29
  def_delegators :@ipaddr, :matches?
31
30
 
@@ -40,7 +39,8 @@ module CiscoAclIntp
40
39
  # @return [AceIpSpec]
41
40
  def initialize(opts)
42
41
  if opts.key?(:ipaddr)
43
- define_addrinfo(opts)
42
+ @options = opts
43
+ define_addrinfo
44
44
  else
45
45
  fail AclArgumentError, 'Not specified IP address'
46
46
  end
@@ -59,13 +59,13 @@ module CiscoAclIntp
59
59
  def to_s
60
60
  if to_wmasked_ip_s == '0.0.0.0'
61
61
  # ip = '0.0.0.0' or wildcard = '255.255.255.255'
62
- c_ip('any')
62
+ tag_ip('any')
63
63
  else
64
64
  if @wildcard == '0.0.0.0'
65
65
  # /32 mask
66
- sprintf('%s %s', c_mask('host'), c_ip(@ipaddr.ip))
66
+ sprintf('%s %s', tag_mask('host'), tag_ip(@ipaddr.ip))
67
67
  else
68
- sprintf('%s %s', c_ip(to_wmasked_ip_s), c_mask(@wildcard))
68
+ sprintf('%s %s', tag_ip(to_wmasked_ip_s), tag_mask(@wildcard))
69
69
  end
70
70
  end
71
71
  end
@@ -75,54 +75,96 @@ module CiscoAclIntp
75
75
  def to_wmasked_ip_s
76
76
  ai = NetAddr.ip_to_i(@ipaddr.ip)
77
77
  mi = NetAddr.ip_to_i(@ipaddr.wildcard_mask)
78
- ami = ai & mi
79
- NetAddr.i_to_ip(ami)
78
+ NetAddr.i_to_ip(ai & mi)
79
+ end
80
+
81
+ # Check subnet contained this object or not.
82
+ # @param [String] address Subnet address string
83
+ # e.g. 192.168.0.0/24, 192.168.0.0/255.255.255.0
84
+ # @return [Boolean]
85
+ # @raise [NetAddr::ValidationError]
86
+ def contains?(address)
87
+ # `@ipaddr` contains(1), is same block(0),
88
+ # is `contained(-1), is not related(nil)
89
+ [0, 1].include?(@ipaddr.cmp(address))
80
90
  end
81
91
 
82
92
  private
83
93
 
94
+ # Convert table of IPv4 bit-flapped wildcard octet to bit length
95
+ OCTET_BIT_LENGTH = {
96
+ '255' => 0, '127' => 1, '63' => 2, '31' => 3,
97
+ '15' => 4, '7' => 5, '3' => 6, '1' => 7, '0' => 8
98
+ }
99
+
100
+ # Covnet IPv4 bit-flapped wildcard to netmask length
101
+ # @return [Fixnum] netmask length
102
+ # or `nil` when discontinuous-bits-wildcard-mask
103
+ def wildcard_bitlength
104
+ @wildcard.split(/\./).reduce(0) do |len, octet|
105
+ if len && OCTET_BIT_LENGTH.key?(octet)
106
+ len + OCTET_BIT_LENGTH[octet]
107
+ else
108
+ nil
109
+ end
110
+ end
111
+ end
112
+
84
113
  # Set instance variables
85
- # @param [Hash] opts Options of constructor
86
- def define_addrinfo(opts)
87
- case
88
- when opts[:wildcard]
89
- define_addrinfo_with_wildcard(opts)
90
- when opts[:netmask]
91
- define_addrinfo_with_netmask(opts)
114
+ def define_addrinfo
115
+ if @options.key?(:wildcard)
116
+ define_addrinfo_prefer_wildcard
117
+ else
118
+ define_addrinfo_by_netmask_or_default
119
+ end
120
+ end
121
+
122
+ # Set instance variables. assume that wildcard is primary option.
123
+ def define_addrinfo_prefer_wildcard
124
+ @wildcard = @options[:wildcard]
125
+ @netmask = wildcard_bitlength
126
+ if @netmask
127
+ @options[:netmask] = @netmask
128
+ define_addrinfo_with_netmask
129
+ else
130
+ define_addrinfo_with_wildcard
131
+ end
132
+ end
133
+
134
+ # Set instance variables. Secondary prioritize option is netmask,
135
+ # and third(last) one is default-mask
136
+ def define_addrinfo_by_netmask_or_default
137
+ if @options.key?(:netmask)
138
+ define_addrinfo_with_netmask
92
139
  else
93
- define_addrinfo_with_default_netmask(opts)
140
+ define_addrinfo_with_default_netmask
94
141
  end
95
142
  end
96
143
 
97
144
  # Set instance variables with ip/wildcard
98
- # @param [Hash] opts Options of constructor
99
- def define_addrinfo_with_wildcard(opts)
100
- @wildcard = opts[:wildcard]
145
+ def define_addrinfo_with_wildcard
101
146
  @ipaddr = NetAddr::CIDR.create(
102
- opts[:ipaddr],
147
+ @options[:ipaddr],
103
148
  WildcardMask: [@wildcard, true]
104
149
  )
105
- ## TBD : is it OK? must convert if possible?
106
150
  @netmask = nil
107
151
  end
108
152
 
109
153
  # Set instance variables with ip/netmask
110
- # @param [Hash] opts Options of constructor
111
- def define_addrinfo_with_netmask(opts)
112
- @netmask = opts[:netmask]
154
+ def define_addrinfo_with_netmask
155
+ @netmask = @options[:netmask]
113
156
  @ipaddr = NetAddr::CIDR.create(
114
- [opts[:ipaddr], @netmask].join('/')
157
+ [@options[:ipaddr], @netmask].join('/')
115
158
  )
116
159
  @wildcard = @ipaddr.wildcard_mask(true)
117
160
  end
118
161
 
119
162
  # Set instance variables with ip/default-netmask
120
- # @param [Hash] opts Options of constructor
121
- def define_addrinfo_with_default_netmask(opts)
163
+ def define_addrinfo_with_default_netmask
122
164
  # default mask
123
165
  @netmask = '255.255.255.255'
124
166
  @ipaddr = NetAddr::CIDR.create(
125
- [opts[:ipaddr], @netmask].join(' ')
167
+ [@options[:ipaddr], @netmask].join(' ')
126
168
  )
127
169
  @wildcard = @ipaddr.wildcard_mask(true)
128
170
  end