cisco_acl_intp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +3 -0
  6. data/.yardopts +4 -0
  7. data/Gemfile +19 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +132 -0
  10. data/Rakefile +78 -0
  11. data/acl_examples/err-acl.txt +49 -0
  12. data/acl_examples/named-ext-acl.txt +12 -0
  13. data/acl_examples/named-std-acl.txt +6 -0
  14. data/acl_examples/numd-acl.txt +21 -0
  15. data/cisco_acl_intp.gemspec +31 -0
  16. data/lib/cisco_acl_intp/ace.rb +432 -0
  17. data/lib/cisco_acl_intp/ace_ip.rb +136 -0
  18. data/lib/cisco_acl_intp/ace_other_qualifiers.rb +102 -0
  19. data/lib/cisco_acl_intp/ace_port.rb +146 -0
  20. data/lib/cisco_acl_intp/ace_proto.rb +319 -0
  21. data/lib/cisco_acl_intp/ace_srcdst.rb +114 -0
  22. data/lib/cisco_acl_intp/ace_tcp_flags.rb +65 -0
  23. data/lib/cisco_acl_intp/acl.rb +272 -0
  24. data/lib/cisco_acl_intp/acl_base.rb +111 -0
  25. data/lib/cisco_acl_intp/parser.rb +3509 -0
  26. data/lib/cisco_acl_intp/parser.ry +1397 -0
  27. data/lib/cisco_acl_intp/scanner.rb +176 -0
  28. data/lib/cisco_acl_intp/scanner_special_token_handler.rb +66 -0
  29. data/lib/cisco_acl_intp/version.rb +5 -0
  30. data/lib/cisco_acl_intp.rb +9 -0
  31. data/spec/cisco_acl_intp/ace_ip_spec.rb +111 -0
  32. data/spec/cisco_acl_intp/ace_other_qualifier_spec.rb +63 -0
  33. data/spec/cisco_acl_intp/ace_port_spec.rb +214 -0
  34. data/spec/cisco_acl_intp/ace_proto_spec.rb +200 -0
  35. data/spec/cisco_acl_intp/ace_spec.rb +605 -0
  36. data/spec/cisco_acl_intp/ace_srcdst_spec.rb +296 -0
  37. data/spec/cisco_acl_intp/ace_tcp_flags_spec.rb +38 -0
  38. data/spec/cisco_acl_intp/acl_spec.rb +523 -0
  39. data/spec/cisco_acl_intp/cisco_acl_intp_spec.rb +7 -0
  40. data/spec/cisco_acl_intp/parser_spec.rb +53 -0
  41. data/spec/cisco_acl_intp/scanner_spec.rb +122 -0
  42. data/spec/conf/extacl_objgrp_token_seq.yml +36 -0
  43. data/spec/conf/extacl_token_seq.yml +88 -0
  44. data/spec/conf/extended_acl.yml +226 -0
  45. data/spec/conf/scanner_spec_data.yml +120 -0
  46. data/spec/conf/single_tokens.yml +235 -0
  47. data/spec/conf/stdacl_token_seq.yml +8 -0
  48. data/spec/conf/tokens1.yml +158 -0
  49. data/spec/conf/tokens2.yml +206 -0
  50. data/spec/parser_fullfill_patterns.rb +145 -0
  51. data/spec/spec_helper.rb +54 -0
  52. data/tools/check_acl.rb +48 -0
  53. metadata +159 -0
@@ -0,0 +1,432 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'cisco_acl_intp/ace_srcdst'
4
+
5
+ module CiscoAclIntp
6
+ # Access control entry base model
7
+ class AceBase < AclContainerBase
8
+ include Comparable
9
+
10
+ # @param [Integer] value ACL sequence number
11
+ # @return [Integer]
12
+ attr_accessor :seq_number
13
+
14
+ # Number used when ACE does not has sequence number
15
+ NO_SEQ_NUMBER = -1
16
+
17
+ # Constructor
18
+ # @param [Hash] opts
19
+ # @option opts [Integer] :number Sequence number
20
+ # @return [AceBase]
21
+ def initialize(opts)
22
+ @seq_number = opts[:number] || NO_SEQ_NUMBER
23
+ end
24
+
25
+ # Check this object has sequence number
26
+ # @return [Boolean]
27
+ def seq_number?
28
+ @seq_number > NO_SEQ_NUMBER
29
+ end
30
+
31
+ # Compare by sequence number
32
+ # @note Using "Comparable" module, '==' operator is defined by
33
+ # '<=>' operator. But ACE object will be compared by its value
34
+ # (comparison by the equivalence), instead of sequence
35
+ # number. The '==' operator will be overriden in child class.
36
+ # @param [AceBase] other RHS object
37
+ # @return [Integer] Compare with protocol/port number
38
+ def <=>(other)
39
+ @seq_number <=> other.seq_number
40
+ end
41
+
42
+ # Search matched ACE
43
+ # @return [Boolean] Matched or not
44
+ # @abstract
45
+ def matches?
46
+ false
47
+ end
48
+ end
49
+
50
+ # Remark entry container
51
+ class RemarkAce < AceBase
52
+ # @param [String] value Comment string
53
+ # @return [String]
54
+ attr_accessor :comment
55
+
56
+ # Constructor
57
+ # @param [String] str Comment string
58
+ # @return [RemarkAce]
59
+ def initialize(str)
60
+ @comment = str.strip
61
+ @seq_number = NO_SEQ_NUMBER # remark does not takes line number
62
+ end
63
+
64
+ # Check equality
65
+ # @return [Boolean] Compare with comment string
66
+ def ==(other)
67
+ @comment == other.comment
68
+ end
69
+
70
+ # Generate string for Cisco IOS access list
71
+ # @return [String] Comment string
72
+ def to_s
73
+ sprintf ' remark %s', c_rmk(@comment.to_s)
74
+ end
75
+
76
+ # Search matched ACE
77
+ # @param [Hash] opts Options
78
+ # return [Boolean] false, Remark does not match anithyng.
79
+ def matches?(opts = nil)
80
+ false
81
+ end
82
+ end
83
+
84
+ # Evaluate entry container
85
+ class EvaluateAce < AceBase
86
+ # @param [String] value Recutsive entry name
87
+ # @return [String]
88
+ attr_accessor :recursive_name
89
+
90
+ # Constructor
91
+ # @param [Hash] opts Options
92
+ # @option opts [Integer] :number Sequence number
93
+ # @option opts [String] :recursive_name Recursive entry name
94
+ # @return [EvaluateAce]
95
+ # @raise [AclArgumentError]
96
+ def initialize(opts)
97
+ super
98
+ @options = opts
99
+ @recursive_name = define_recursive_name
100
+ end
101
+
102
+ # Set instance variables
103
+ # return [String] Recursive entry name
104
+ # raise [AclArgumentError]
105
+ def define_recursive_name
106
+ if @options.key?(:recursive_name)
107
+ @options[:recursive_name]
108
+ else
109
+ fail AclArgumentError, 'name not specified'
110
+ end
111
+ end
112
+
113
+ # @return [Boolean] Compare with recursive entry name
114
+ def ==(other)
115
+ @recursive_name == other.recursive_name
116
+ end
117
+
118
+ # Generate string for Cisco IOS access list
119
+ # @return [String]
120
+ def to_s
121
+ sprintf 'evaluate %s', c_name(@recursive_name)
122
+ end
123
+
124
+ # Search matched ACE
125
+ # @param [Hash] opts Options
126
+ # return [Boolean] false, Recursive does not implemented yet
127
+ def matches?(opts = nil)
128
+ ## TODO
129
+ false
130
+ end
131
+ end
132
+
133
+ # ACE for standard access list
134
+ class StandardAce < AceBase
135
+ # @param [String] value Action
136
+ # @return [String]
137
+ attr_accessor :action
138
+
139
+ # @param [AceSrcDstSpec] value Source spec object
140
+ # @return [AceSrcDstSpec]
141
+ attr_accessor :src_spec
142
+
143
+ # @param [AceLogSpec] value Log spec object
144
+ # @return [AceLogSpec]
145
+ attr_accessor :log_spec
146
+
147
+ # Constructor
148
+ # @param [Hash] opts Options
149
+ # @option opts [Integer] :number Sequence number
150
+ # @option opts [String] :action Action (permit/deny)
151
+ # @option opts [AceSrcDstSpec] :src Source spec object
152
+ # @option opts [Hash] :src Source spec parmeters
153
+ # @option opts [AceLogSpec] :log Log spec object
154
+ # @return [StandardAce]
155
+ # @raise [AclArgumentError]
156
+ def initialize(opts)
157
+ super
158
+ @options = opts
159
+ @action = define_action
160
+ @src_spec = define_src_spec
161
+ @log_spec = define_log_spec
162
+ end
163
+
164
+ # @return [Boolean]
165
+ def ==(other)
166
+ @action == other.action && @src_spec == other.src_spec
167
+ end
168
+
169
+ # Generate string for Cisco IOS access list
170
+ # @return [String]
171
+ def to_s
172
+ sprintf(
173
+ ' %s %s %s',
174
+ c_act(@action.to_s),
175
+ @src_spec,
176
+ @log_spec ? @log_spec : ''
177
+ )
178
+ end
179
+
180
+ # Search matched ACE
181
+ # @param [Hash] opts Options (target packet info)
182
+ # @option opts [String] :src_ip Source IP Address
183
+ # @return [Boolean] Matched or not
184
+ # @raise [AclArgumentError] Invalid src_ip
185
+ def matches?(opts)
186
+ if opts.key?(:src_ip)
187
+ @src_spec.ip_spec.matches?(opts[:src_ip])
188
+ else
189
+ fail AclArgumentError, 'Invalid match target src IP address'
190
+ end
191
+ end
192
+
193
+ private
194
+
195
+ # Set instance variables
196
+ # @return [String] Action string
197
+ # @raise [AclArgumentError]
198
+ def define_action
199
+ if @options.key?(:action)
200
+ @options[:action]
201
+ else
202
+ fail AclArgumentError, 'Not specified action'
203
+ end
204
+ end
205
+
206
+ # Set instance variables
207
+ # @return [AceSrcDstSpec] Source spec object
208
+ # @raise [AclArgumentError]
209
+ def define_src_spec
210
+ if @options.key?(:src)
211
+ src = @options[:src]
212
+ case src
213
+ when Hash
214
+ AceSrcDstSpec.new(src)
215
+ when AceSrcDstSpec
216
+ src
217
+ else
218
+ fail AclArgumentError, 'src spec: unknown class'
219
+ end
220
+ else
221
+ fail AclArgumentError, 'Not specified src spec'
222
+ end
223
+ end
224
+
225
+ # Set instance variables
226
+ # @return [String] Log spec object
227
+ # @raise [AclArgumentError]
228
+ def define_log_spec
229
+ @options[:log] || nil
230
+ end
231
+ end
232
+
233
+ # ACE for extended access list
234
+ class ExtendedAce < StandardAce
235
+ # @param [String] value L3/L4 protocol
236
+ # @return [String]
237
+ attr_accessor :protocol
238
+
239
+ # @param [AceSrcDstSpec] value Destination spec object
240
+ # @return [AceSrcDstSpec]
241
+ attr_accessor :dst_spec
242
+
243
+ # @param [AceTcpFlagList] value
244
+ # TCP flags (used when '@protocol':tcp)
245
+ # @return [AceTcpFlagList]
246
+ attr_accessor :tcp_flags
247
+
248
+ # @param [AceOtherQualifierList] value
249
+ # TCP other qualifier list object (used when '@protocol':tcp)
250
+ # @return [AceOtherQualifierList]
251
+ attr_accessor :tcp_other_qualifiers
252
+
253
+ # Option,
254
+ # :src and :dst can handle multiple types of object generation,
255
+ # so that the argments can takes hash of AceSrcDstSpec.new or
256
+ # AceSrcDstSpec instance.
257
+ # :protocol and so on. (AceIpProtoSpec Object)
258
+ #
259
+ # about :protocol, it has specification of name and number
260
+ # (specified in internal of parser).
261
+ # basically, it is OK that specify only name.
262
+ # (does it convert name <=> number each oether?)
263
+ # (does it use number?
264
+ #
265
+
266
+ # Constructor
267
+ # @param [Hash] opts Options
268
+ # @option opts [String] :protocol L3/L4 protocol
269
+ # @option opts [Integer] :number Protocol/Port number
270
+ # @option opts [String] :action Action
271
+ # @option opts [AceSrcDstSpec] :src Source spec object
272
+ # @option opts [Hash] :src Source spec parmeters
273
+ # @option opts [AceSrcDstSpec] :dst Destination spec object
274
+ # @option opts [Hash] :dst Destination spec parmeters
275
+ # @option opts [AceTcpFlagList] :tcp_port_qualifier
276
+ # TCP Flags object
277
+ # @raise [AclArgumentError]
278
+ # @return [ExtendACE]
279
+ #
280
+ # @example Construct ACE object
281
+ # ExtendACE.new(
282
+ # :protocol => 'tcp',
283
+ # :number => 10,
284
+ # :action => 'permit',
285
+ # :src => { :ipaddr => '192.168.3.0', :wildcard => '0.0.0.127' },
286
+ # :dst => { :ipaddr => '172.30.0.0', :wildcard => '0.0.7.127',
287
+ # :operator => 'eq', :begin_port => 80 })
288
+ #
289
+ def initialize(opts)
290
+ super
291
+ @options = opts
292
+ @protocol = define_protocol
293
+ @dst_spec = define_dst_spec
294
+ @tcp_flags = define_tcp_flags
295
+ @tcp_other_qualifiers = nil # not yet.
296
+ end
297
+
298
+ # @param [ExtendACE] other RHS object
299
+ # @return [Boolean]
300
+ def ==(other)
301
+ @action == other.action &&
302
+ @protocol == other.protocol &&
303
+ @src_spec == other.src_spec &&
304
+ @dst_spec == other.dst_spec &&
305
+ @tcp_flags == other.tcp_flags
306
+ ## does it need to compare? : tcp_other_qualifiers
307
+ end
308
+
309
+ # Generate string for Cisco IOS access list
310
+ # @return [String]
311
+ def to_s
312
+ sprintf(
313
+ ' %s %s %s %s %s %s',
314
+ c_act(@action.to_s),
315
+ c_pp(@protocol.to_s),
316
+ @src_spec,
317
+ @dst_spec,
318
+ @tcp_flags ? @tcp_flags : '',
319
+ @tcp_other_qualifiers ? @tcp_other_qualifiers : ''
320
+ )
321
+ end
322
+
323
+ # Search matched ACE
324
+ # @param [Hash] opts Options (target packet info)
325
+ # @option opts [String] :protocol L3/L4 protocol name
326
+ # (allows "tcp", "udp" and "icmp")
327
+ # @option opts [String] :src_ip Source IP Address
328
+ # @option opts [Integer] :src_port Source Port No.
329
+ # @option opts [String] :dst_ip Destination IP Address
330
+ # @option opts [Integer] :dst_port Destination Port No.
331
+ # @return [Boolean] Matched or not
332
+ # @raise [AclArgumentError]
333
+ def matches?(opts)
334
+ 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])
338
+ else
339
+ fail AclArgumentError, 'Invalid match target protocol'
340
+ end
341
+
342
+ (match_proto && match_src && match_dst)
343
+ end
344
+
345
+ private
346
+
347
+ # check protocol
348
+ # @option protocol [AceProtoSpecBase] protocol
349
+ # @return [Boolean] Matched or not
350
+ # @raise [AclArgumentError]
351
+ def match_protocol?(protocol)
352
+ protocol_str = @protocol.to_s
353
+ if protocol_str == 'ip'
354
+ true # allow tcp/udp
355
+ 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.
359
+ protocol == protocol_str
360
+ end
361
+ end
362
+
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
+ # Set instance variables
378
+ # return [AceIpProtoSpec] IP protocol object
379
+ # raise [AclArgumentError]
380
+ def define_protocol
381
+ if @options.key?(:protocol)
382
+ protocol = @options[:protocol]
383
+ case protocol
384
+ when AceIpProtoSpec
385
+ protocol
386
+ else
387
+ AceIpProtoSpec.new(
388
+ name: protocol,
389
+ number: @options[:protocol_num]
390
+ )
391
+ end
392
+ else
393
+ fail AclArgumentError, 'Not specified IP protocol'
394
+ end
395
+ end
396
+
397
+ # Set instance variables
398
+ # @return [AceSrcDstSpec] Destination spec object
399
+ # @raise [AclArgumentError]
400
+ def define_dst_spec
401
+ if @options.key?(:dst)
402
+ dst = @options[:dst]
403
+ case dst
404
+ when Hash
405
+ AceSrcDstSpec.new(dst)
406
+ when AceSrcDstSpec
407
+ dst
408
+ else
409
+ fail AclArgumentError, 'Dst spec: unknown class'
410
+ end
411
+ else
412
+ fail AclArgumentError, 'Not specified dst spec'
413
+ end
414
+ end
415
+
416
+ # Set instance variables
417
+ # @return [AceOtherQualifierList]
418
+ def define_tcp_flags
419
+ if @protocol.name == 'tcp' && @options.key?(:tcp_flags_qualifier)
420
+ @options[:tcp_flags_qualifier]
421
+ else
422
+ nil
423
+ end
424
+ end
425
+ end
426
+ end # module
427
+
428
+ ### Local variables:
429
+ ### mode: Ruby
430
+ ### coding: utf-8-unix
431
+ ### indent-tabs-mode: nil
432
+ ### End:
@@ -0,0 +1,136 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'forwardable'
4
+ require 'netaddr'
5
+ require 'cisco_acl_intp/acl_base'
6
+
7
+ module CiscoAclIntp
8
+ # IP Address and Wildcard mask container
9
+ class AceIpSpec < AclContainerBase
10
+ extend Forwardable
11
+
12
+ # @param [NetAddr::CIDR] value IP address
13
+ # (dotted decimal notation)
14
+ # @return [NetAddr::CIDR]
15
+ attr_accessor :ipaddr
16
+
17
+ # @param [Integer] value Netmask length
18
+ # @return [Integer]
19
+ attr_accessor :netmask
20
+
21
+ # @param [String] value Wildcard mask
22
+ # (dotted decimal and bit flapped notation)
23
+ # @return [String]
24
+ attr_accessor :wildcard
25
+
26
+ def_delegator :@ipaddr, :ip, :ipaddr
27
+ # `contained_ip?' method is cidr(ipaddr/nn) operation
28
+ def_delegator :@ipaddr, :is_contained?, :contained_ip?
29
+ # `matches?' method is wildcard mask operation
30
+ def_delegators :@ipaddr, :matches?
31
+
32
+ # Constructor
33
+ # @param [Hash] opts Options
34
+ # @option opts [String] :ipaddr IP address
35
+ # (dotted decimal notation)
36
+ # @option opts [String] :wildcard Wildcard mask
37
+ # (dotted decimal and bit flipped notation)
38
+ # @option opts [Integer] :netmask Network Mask Length
39
+ # @raise [AclArgumentError]
40
+ # @return [AceIpSpec]
41
+ def initialize(opts)
42
+ if opts.key?(:ipaddr)
43
+ define_addrinfo(opts)
44
+ else
45
+ fail AclArgumentError, 'Not specified IP address'
46
+ end
47
+ end
48
+
49
+ # @param [AceIpSpec] other RHS Object
50
+ # @return [Boolean]
51
+ def ==(other)
52
+ @ipaddr == other.ipaddr &&
53
+ @netmask == other.netmask &&
54
+ @wildcard == other.wildcard
55
+ end
56
+
57
+ # Generate string for Cisco IOS access list
58
+ # @return [String]
59
+ def to_s
60
+ if to_wmasked_ip_s == '0.0.0.0'
61
+ # ip = '0.0.0.0' or wildcard = '255.255.255.255'
62
+ c_ip('any')
63
+ else
64
+ if @wildcard == '0.0.0.0'
65
+ # /32 mask
66
+ sprintf('%s %s', c_mask('host'), c_ip(@ipaddr.ip))
67
+ else
68
+ sprintf('%s %s', c_ip(to_wmasked_ip_s), c_mask(@wildcard))
69
+ end
70
+ end
71
+ end
72
+
73
+ # Generate wildcard-masked ip address string
74
+ # @return [String] wildcard-masked ip address string
75
+ def to_wmasked_ip_s
76
+ ai = NetAddr.ip_to_i(@ipaddr.ip)
77
+ mi = NetAddr.ip_to_i(@ipaddr.wildcard_mask)
78
+ ami = ai & mi
79
+ NetAddr.i_to_ip(ami)
80
+ end
81
+
82
+ private
83
+
84
+ # 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)
92
+ else
93
+ define_addrinfo_with_default_netmask(opts)
94
+ end
95
+ end
96
+
97
+ # Set instance variables with ip/wildcard
98
+ # @param [Hash] opts Options of constructor
99
+ def define_addrinfo_with_wildcard(opts)
100
+ @wildcard = opts[:wildcard]
101
+ @ipaddr = NetAddr::CIDR.create(
102
+ opts[:ipaddr],
103
+ WildcardMask: [@wildcard, true]
104
+ )
105
+ ## TBD : is it OK? must convert if possible?
106
+ @netmask = nil
107
+ end
108
+
109
+ # Set instance variables with ip/netmask
110
+ # @param [Hash] opts Options of constructor
111
+ def define_addrinfo_with_netmask(opts)
112
+ @netmask = opts[:netmask]
113
+ @ipaddr = NetAddr::CIDR.create(
114
+ [opts[:ipaddr], @netmask].join('/')
115
+ )
116
+ @wildcard = @ipaddr.wildcard_mask(true)
117
+ end
118
+
119
+ # Set instance variables with ip/default-netmask
120
+ # @param [Hash] opts Options of constructor
121
+ def define_addrinfo_with_default_netmask(opts)
122
+ # default mask
123
+ @netmask = '255.255.255.255'
124
+ @ipaddr = NetAddr::CIDR.create(
125
+ [opts[:ipaddr], @netmask].join(' ')
126
+ )
127
+ @wildcard = @ipaddr.wildcard_mask(true)
128
+ end
129
+ end
130
+ end # module
131
+
132
+ ### Local variables:
133
+ ### mode: Ruby
134
+ ### coding: utf-8-unix
135
+ ### indent-tabs-mode: nil
136
+ ### End:
@@ -0,0 +1,102 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'forwardable'
4
+ require 'cisco_acl_intp/acl_base'
5
+
6
+ module CiscoAclIntp
7
+ # List of other-qualifiers for extended ace
8
+ class AceOtherQualifierList < AclContainerBase
9
+ extend Forwardable
10
+
11
+ # @param [Array] value List of {AceOtherQualifierList} object
12
+ # @return [Array]
13
+ attr_accessor :list
14
+
15
+ def_delegators :@list, :push, :pop, :shift, :unshift, :size, :length
16
+
17
+ # Constructor
18
+ # @return [AceOtherQualifierList]
19
+ def initialize
20
+ @list = []
21
+ end
22
+
23
+ # Generate string for Cisco IOS access list
24
+ # @return [String]
25
+ def to_s
26
+ c_pp(@list.map { | each | each.to_s }.join(' '))
27
+ end
28
+
29
+ # @param [AceOtherQualifierList] other RHS Object
30
+ # @return [Boolean]
31
+ def ==(other)
32
+ @list == other.list
33
+ end
34
+ end
35
+
36
+ # Access list entry qualifier base
37
+ class AceOtherQualifierBase < AclContainerBase
38
+ end
39
+
40
+ # Log spec container
41
+ class AceLogSpec < AceOtherQualifierBase
42
+ # @param [String] value Log cookie
43
+ # @return [String]
44
+ attr_accessor :cookie
45
+
46
+ # Specified log-input logging?
47
+ # @return [Boolean]
48
+ attr_reader :input
49
+
50
+ # alias as boolean method
51
+ # @return [Boolean]
52
+ alias_method(:input?, :input)
53
+
54
+ # Constructor
55
+ # @param [String] cookie Log cookie
56
+ # @param [Boolean] input set true 'log-input' logging
57
+ # @return [AceLogSpec]
58
+ def initialize(cookie = nil, input = false)
59
+ @input = input
60
+ @cookie = cookie
61
+ end
62
+
63
+ # Generate string for Cisco IOS access list
64
+ # @return [String]
65
+ def to_s
66
+ sprintf(
67
+ '%s %s',
68
+ @input ? 'log-input' : 'log',
69
+ @cookie ? @cookie : ''
70
+ )
71
+ end
72
+ end
73
+
74
+ # Recursive qualifier container
75
+ class AceRecursiveQualifier < AceOtherQualifierBase
76
+ # @param [String] value Recursive name
77
+ # @return [String]
78
+ attr_accessor :recursive_name
79
+
80
+ # Constructor
81
+ # @param [String] name Recursive name
82
+ def initialize(name)
83
+ if name && (!name.empty?)
84
+ @recursive_name = name
85
+ else
86
+ fail AclArgumentError, 'Not specified recursive name'
87
+ end
88
+ end
89
+
90
+ # Generate string for Cisco IOS access list
91
+ # @return [String]
92
+ def to_s
93
+ sprintf 'reflect %s', c_name(@recursive_name)
94
+ end
95
+ end
96
+ end # module
97
+
98
+ ### Local variables:
99
+ ### mode: Ruby
100
+ ### coding: utf-8-unix
101
+ ### indent-tabs-mode: nil
102
+ ### End: