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,1397 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # cisco IOS access list parser
4
+ class Parser
5
+
6
+ start expr
7
+
8
+ rule
9
+ expr: ipacl
10
+ | ipacl expr
11
+
12
+ ipacl: numbered_acl
13
+ | named_acl
14
+ | objgrp
15
+
16
+ eos: EOS
17
+ {
18
+ # end of line or "empty line"
19
+ yyerrok
20
+ }
21
+
22
+ # standard access-list
23
+
24
+ numbered_acl: NUMD_STD_ACL std_acl eos
25
+ {
26
+ dputs "## numd std acl: #{val[0]}/#{val[1]}"
27
+ @curr_acl_name = val[0]
28
+ unless @acl_table[@curr_acl_name]
29
+ @acl_table[@curr_acl_name] = NumberedStdAcl.new(@curr_acl_name)
30
+ @line_number = 0
31
+ end
32
+ @acl_table[@curr_acl_name].add_entry(val[1])
33
+ @line_number += 1
34
+ }
35
+ | NUMD_EXT_ACL ext_acl eos
36
+ {
37
+ dputs "## numd ext acl: #{val[0]}/#{val[1]}"
38
+ @curr_acl_name = val[0]
39
+ unless @acl_table[@curr_acl_name]
40
+ @acl_table[@curr_acl_name] = NumberedExtAcl.new(@curr_acl_name)
41
+ @line_number = 0
42
+ end
43
+ @acl_table[@curr_acl_name].add_entry(val[1])
44
+ @line_number += 1
45
+ }
46
+ | error eos
47
+ | numbered_acl eos
48
+
49
+ std_acl: action ip_spec std_acl_log_spec
50
+ {
51
+ dputs "## std_acl: #{val[0]}/#{val[1]}/#{val[2]}"
52
+ result = StandardAce.new(
53
+ :action => val[0],
54
+ :src => AceSrcDstSpec.new(:ip_spec => val[1]),
55
+ :log => val[2]
56
+ )
57
+ }
58
+ | 'remark' STRING
59
+ {
60
+ dputs "## std_acl: remark/#{val[1]}"
61
+ result = RemarkAce.new(val[1])
62
+ }
63
+
64
+ std_acl_log_spec:
65
+ | 'log' log_cookie
66
+ {
67
+ result = AceLogSpec.new(val[1])
68
+ }
69
+
70
+ # extended access-list
71
+
72
+ ext_acl: dynamic_spec ext_acl_body other_qualifier_list
73
+ {
74
+ ## NOT implemented yet:
75
+ ## dynamic_spec,
76
+ ## other_qualifier_list (partially implemented)
77
+ dputs '## ext_acl'
78
+ val[1].tcp_other_qualifiers = val[2]
79
+ result = val[1]
80
+ }
81
+ | 'remark' STRING
82
+ {
83
+ dputs "## ext_acl: remark/#{val[1]}"
84
+ result = RemarkAce.new(val[1])
85
+ }
86
+
87
+ ext_acl_body: action ip_proto ip_spec ip_spec
88
+ {
89
+ dputs "## ext_acl_body ip: #{val[0]}/#{val[1]}/#{val[2]}/#{val[3]}"
90
+ result = ExtendedAce.new(
91
+ :action => val[0],
92
+ :protocol => val[1],
93
+ :src => AceSrcDstSpec.new(:ip_spec => val[2]),
94
+ :dst => AceSrcDstSpec.new(:ip_spec => val[3])
95
+ )
96
+ }
97
+ | action icmp_proto ip_spec ip_spec icmp_qualifier
98
+ {
99
+ result = ExtendedAce.new(
100
+ :action => val[0],
101
+ :protocol => val[1],
102
+ :src => AceSrcDstSpec.new(:ip_spec => val[2]),
103
+ :dst => AceSrcDstSpec.new(:ip_spec => val[3])
104
+ ## TBD: icmp_qualifier: val[4]
105
+ )
106
+ }
107
+ | action tcp_proto tcp_srcdst_spec tcp_srcdst_spec tcp_flags_qualifier
108
+ {
109
+ result = ExtendedAce.new(
110
+ :action => val[0],
111
+ :protocol => val[1],
112
+ :src => val[2],
113
+ :dst => val[3],
114
+ :tcp_flags_qualifier => val[4]
115
+ )
116
+ }
117
+ | action udp_proto udp_srcdst_spec udp_srcdst_spec
118
+ {
119
+ result = ExtendedAce.new(
120
+ :action => val[0],
121
+ :protocol => val[1],
122
+ :src => val[2],
123
+ :dst => val[3]
124
+ )
125
+ }
126
+ | action objgrp_service_spec objgrp_srcdst_spec objgrp_srcdst_spec
127
+ {
128
+ ## TBD: object group
129
+ }
130
+
131
+ # protocols
132
+
133
+ ip_proto: 'ahp'
134
+ {
135
+ result = AceIpProtoSpec.new(
136
+ :name => val[0], :number => 51
137
+ )
138
+ }
139
+ | 'eigrp'
140
+ {
141
+ result = AceIpProtoSpec.new(
142
+ :name => val[0], :number => 88
143
+ )
144
+ }
145
+ | 'esp'
146
+ {
147
+ result = AceIpProtoSpec.new(
148
+ :name => val[0], :number => 50
149
+ )
150
+ }
151
+ | 'gre'
152
+ {
153
+ result = AceIpProtoSpec.new(
154
+ :name => val[0], :number => 47
155
+ )
156
+ }
157
+ | 'igmp'
158
+ {
159
+ result = AceIpProtoSpec.new(
160
+ :name => val[0], :number => 2
161
+ )
162
+ }
163
+ | 'igrp'
164
+ {
165
+ result = AceIpProtoSpec.new(
166
+ :name => val[0], :number => 9
167
+ )
168
+ }
169
+ | 'ip'
170
+ {
171
+ result = AceIpProtoSpec.new(
172
+ :name => val[0]
173
+ )
174
+ } # IS NOT 0! (no number)
175
+ | 'ipinip'
176
+ {
177
+ result = AceIpProtoSpec.new(
178
+ :name => val[0], :number => 94
179
+ )
180
+ }
181
+ | 'nos'
182
+ {
183
+ result = AceIpProtoSpec.new(
184
+ :name => val[0], :number => 4
185
+ )
186
+ }
187
+ | 'ospf'
188
+ {
189
+ result = AceIpProtoSpec.new(
190
+ :name => val[0], :number => 89
191
+ )
192
+ }
193
+ | 'pcp'
194
+ {
195
+ result = AceIpProtoSpec.new(
196
+ :name => val[0], :number => 108
197
+ )
198
+ }
199
+ | 'pim'
200
+ {
201
+ result = AceIpProtoSpec.new(
202
+ :name => val[0], :number => 103
203
+ )
204
+ }
205
+ | NUMBER # ip protocol number (0-255)
206
+ {
207
+ begin
208
+ dputs "## ip_proto number: #{val[0]}"
209
+ result = AceIpProtoSpec.new(
210
+ :number => val[0].to_i
211
+ )
212
+ rescue => err
213
+ yyerror_with err.message
214
+ end
215
+ }
216
+
217
+ icmp_proto: 'icmp'
218
+ {
219
+ dputs '## icmp_proto'
220
+ result = AceIpProtoSpec.new(
221
+ :name => val[0], :number => 1
222
+ )
223
+ }
224
+
225
+ tcp_proto: 'tcp'
226
+ {
227
+ dputs '## tcp_proto'
228
+ result = AceIpProtoSpec.new(
229
+ :name => val[0], :number => 6
230
+ )
231
+ }
232
+
233
+ udp_proto: 'udp'
234
+ {
235
+ dputs '## udp_proto'
236
+ result = AceIpProtoSpec.new(
237
+ :name => val[0], :number => 17
238
+ )
239
+ }
240
+
241
+ tcp_srcdst_spec: objgrp_srcdst_spec tcp_port_spec
242
+ {
243
+ result = AceSrcDstSpec.new(
244
+ :ip_spec => val[0],
245
+ :port_spec => val[1]
246
+ )
247
+ }
248
+
249
+ udp_srcdst_spec: objgrp_srcdst_spec udp_port_spec
250
+ {
251
+ result = AceSrcDstSpec.new(
252
+ :ip_spec => val[0],
253
+ :port_spec => val[1]
254
+ )
255
+ }
256
+
257
+ # ip named access-list
258
+
259
+ named_acl: std_named_acl_header std_named_acl_entry_list
260
+ | ext_named_acl_header ext_named_acl_entry_list
261
+ # Notice:
262
+ # acl header でエラーがあると、そのあとのエントリ追加用のハコ(object)が
263
+ # つくれないので、エラーリカバリしようがない。
264
+ # acl_entry_list の中身については可能な範囲で parse して追加。
265
+
266
+ std_named_acl_header: NAMED_ACL 'standard' STRING eos
267
+ {
268
+ dputs "## std named acl: #{val[2]}"
269
+ @curr_acl_name = val[2]
270
+ @acl_table[@curr_acl_name] = NamedStdAcl.new(@curr_acl_name)
271
+ dputs "## make NamedStdAcl obj, name = #{ @curr_acl_name }"
272
+ }
273
+
274
+ std_named_acl_entry_list:
275
+ | std_named_acl_entry_list std_named_acl_entry
276
+ {
277
+ @acl_table[@curr_acl_name].add_entry(val[1])
278
+ @line_number += 1
279
+ }
280
+ | std_named_acl_entry_list error eos
281
+ # when a line contains syntax error (error recovery)
282
+ | std_named_acl_entry_list eos
283
+ # acl ends when empty-line "eos"
284
+
285
+ std_named_acl_entry: seq_number std_acl eos
286
+ {
287
+ # std_acl returns StandardAce/RemarkAce object
288
+ val[1].seq_number = val[0].to_i
289
+ result = val[1]
290
+ }
291
+
292
+ ext_named_acl_header: NAMED_ACL 'extended' STRING eos
293
+ {
294
+ dputs "## ext named acl: #{val[2]}"
295
+ @curr_acl_name = val[2]
296
+ @acl_table[@curr_acl_name] = NamedExtAcl.new(@curr_acl_name)
297
+ dputs "## make NamedExtAcl obj, name = #{ @curr_acl_name }"
298
+ }
299
+
300
+ ext_named_acl_entry_list:
301
+ | ext_named_acl_entry_list ext_named_acl_entry
302
+ {
303
+ @acl_table[@curr_acl_name].add_entry(val[1])
304
+ @line_number += 1
305
+ }
306
+ | ext_named_acl_entry_list error eos
307
+ # when a line contains syntax error (error recovery)
308
+ | ext_named_acl_entry_list eos
309
+ # acl ends when empty-line "eos"
310
+
311
+ ext_named_acl_entry: seq_number ext_acl eos
312
+ {
313
+ # ext_acl returns ExtendedAce/RemarkAce object
314
+ val[1].seq_number = val[0].to_i
315
+ result = val[1]
316
+ }
317
+ | seq_number 'evaluate' STRING eos
318
+ {
319
+ result = EvaluateAce.new(
320
+ :number => val[0].to_i,
321
+ :recursive_name => val[2]
322
+ )
323
+ }
324
+
325
+ seq_number:
326
+ | NUMBER # (1-2147483647)
327
+ {
328
+ result = val[0].to_i
329
+ }
330
+
331
+ # access-list common components
332
+
333
+ action: 'permit'
334
+ {
335
+ result = val[0]
336
+ }
337
+ | 'deny'
338
+ {
339
+ result = val[0]
340
+ }
341
+
342
+
343
+ ip_spec: 'host' IPV4_ADDR
344
+ {
345
+ begin
346
+ dputs "## ip_spec host: #{val[0]}/#{val[1]}"
347
+ result = AceIpSpec.new(
348
+ :ipaddr => val[1], :wildcard => '0.0.0.0'
349
+ )
350
+ rescue => err
351
+ yyerror_with err.message
352
+ end
353
+ }
354
+ | IPV4_ADDR IPV4_ADDR # ipaddr wildcard
355
+ {
356
+ begin
357
+ dputs "## ip_spec #{val[0]}/#{val[1]}"
358
+ result = AceIpSpec.new(
359
+ :ipaddr => val[0], :wildcard => val[1]
360
+ )
361
+ rescue => err
362
+ yyerror_with err.message
363
+ end
364
+ }
365
+ | 'any'
366
+ {
367
+ dputs "## ip_spec any: #{val[0]}"
368
+ result = AceIpSpec.new(
369
+ :ipaddr => '0.0.0.0', :wildcard => '255.255.255.255'
370
+ )
371
+ }
372
+
373
+ dynamic_spec:
374
+ | 'dynamic' STRING timeout_spec
375
+
376
+ timeout_spec:
377
+ | 'timeout' NUMBER
378
+
379
+ # object-group
380
+
381
+ ## TBD
382
+ # object group syntax was not implemented
383
+ # 'eos' termination
384
+
385
+ objgrp: objgrp_service
386
+ | objgrp_network
387
+
388
+ objgrp_service_spec: 'object-group' STRING # service object group
389
+ {
390
+ }
391
+
392
+ objgrp_srcdst_spec: objgrp_network_spec
393
+ | ip_spec
394
+ {
395
+ result = val[0]
396
+ }
397
+
398
+ objgrp_network_spec: 'object-group' STRING # network object group
399
+ {
400
+ }
401
+
402
+ objgrp_network: objgrp_network_header objgrp_network_entry_list
403
+
404
+ objgrp_network_header: 'object-group' 'network' STRING
405
+ {
406
+ }
407
+
408
+ objgrp_network_entry_list:
409
+ | objgrp_network_entry_list objgrp_network_entry
410
+ {
411
+ }
412
+
413
+ objgrp_network_entry: 'description' STRING
414
+ {
415
+ }
416
+ | 'host' IPV4_ADDR
417
+ {
418
+ }
419
+ | IPV4_ADDR IPV4_ADDR
420
+ {
421
+ }
422
+ | IPV4_ADDR '/' NUMBER # 0-32
423
+ {
424
+ }
425
+ | 'range' IPV4_ADDR IPV4_ADDR
426
+ {
427
+ }
428
+ | 'group-object' STRING # nested object-group
429
+ {
430
+ }
431
+
432
+ objgrp_service: objgrp_service_header objgrp_service_entry_list
433
+
434
+ objgrp_service_header: 'object-group' 'service' STRING
435
+ {
436
+ }
437
+
438
+ objgrp_service_entry_list:
439
+ | objgrp_service_entry_list objgrp_service_entry
440
+ {
441
+ }
442
+
443
+ objgrp_service_entry: 'description' STRING
444
+ | ip_proto
445
+ {
446
+ }
447
+ | icmp_proto icmp_qualifier
448
+ {
449
+ }
450
+ | tcp_proto objgrp_tcp_proto
451
+ {
452
+ }
453
+ | udp_proto objgrp_udp_proto
454
+ {
455
+ }
456
+ | 'tcp-udp' objgrp_tcpudp_proto
457
+ {
458
+ }
459
+ | 'group-object' STRING # nested object-group
460
+ {
461
+ }
462
+
463
+ objgrp_tcp_proto: objgrp_tcp_proto_spec
464
+ {
465
+ }
466
+ | 'source' objgrp_tcp_proto_spec
467
+ {
468
+ }
469
+
470
+ objgrp_tcp_proto_spec:
471
+ | unary_operator tcp_port_qualifier
472
+ {
473
+ }
474
+ | 'range' tcp_port_qualifier tcp_port_qualifier
475
+ {
476
+ }
477
+ | tcp_port_qualifier
478
+ {
479
+ }
480
+
481
+ objgrp_udp_proto: objgrp_udp_proto_spec
482
+ {
483
+ }
484
+ | 'source' objgrp_udp_proto_spec
485
+ {
486
+ }
487
+
488
+ objgrp_udp_proto_spec:
489
+ | unary_operator udp_port_qualifier
490
+ {
491
+ }
492
+ | 'range' udp_port_qualifier udp_port_qualifier
493
+ {
494
+ }
495
+ | udp_port_qualifier
496
+ {
497
+ }
498
+
499
+ objgrp_tcpudp_proto: objgrp_tcpudp_proto_spec
500
+ {
501
+ }
502
+ | 'source' objgrp_tcpudp_proto_spec
503
+ {
504
+ }
505
+
506
+ objgrp_tcpudp_proto_spec:
507
+ | unary_operator tcpudp_port_qualifier
508
+ {
509
+ }
510
+ | 'range' tcpudp_port_qualifier tcpudp_port_qualifier
511
+ {
512
+ }
513
+ | tcpudp_port_qualifier
514
+ {
515
+ }
516
+
517
+ tcpudp_port_qualifier: NUMBER # port number (0-65535)
518
+ | 'discard'
519
+ | 'domain'
520
+ | 'echo'
521
+ | 'pim-auto-rp'
522
+ | 'sunrpc'
523
+ | 'syslog'
524
+ | 'tacacs'
525
+ | 'talk'
526
+
527
+ # icmp qualifier
528
+
529
+ icmp_qualifier:
530
+ | 'administratively-prohibited'
531
+ | 'alternate-address'
532
+ | 'conversion-error'
533
+ | 'dod-host-prohibited'
534
+ | 'dod-net-prohibited'
535
+ | 'echo'
536
+ | 'echo-reply'
537
+ | 'general-parameter-problem'
538
+ | 'host-isolated'
539
+ | 'mobile-redirect'
540
+ | 'net-redirect'
541
+ | 'net-tos-redirect'
542
+ | 'net-unreachable'
543
+ | 'net-unknown'
544
+ | 'no-room-for-option'
545
+ | 'option-missing'
546
+ | 'packet-too-big'
547
+ | 'parameter-problem'
548
+ | 'port-unreachable'
549
+ | 'precedence-unreachable'
550
+ | 'protocol-unreachable'
551
+ | 'host-precedence-unreachable'
552
+ | 'host-redirect'
553
+ | 'host-tos-redirect'
554
+ | 'host-unknown'
555
+ | 'host-unreachable'
556
+ | 'information-reply'
557
+ | 'information-request'
558
+ | 'mask-reply'
559
+ | 'mask-request'
560
+ | 'reassembly-timeout'
561
+ | 'redirect'
562
+ | 'router-advertisement'
563
+ | 'router-solicitation'
564
+ | 'source-quench'
565
+ | 'source-route-failed'
566
+ | 'time-exceeded'
567
+ | 'timestamp-reply'
568
+ | 'timestamp-request'
569
+ | 'traceroute'
570
+ | 'ttl-exceeded'
571
+ | 'unreachable'
572
+ | icmp_numtype icmp_numcode
573
+
574
+ icmp_numtype: NUMBER # icmp message type (0-255)
575
+
576
+ icmp_numcode:
577
+ | NUMBER # icmp message code (0-255)
578
+
579
+
580
+
581
+ # tcp/udp port spec
582
+
583
+ tcp_port_spec:
584
+ {
585
+ # tcp any
586
+ dputs '## tcp port any'
587
+ result = AcePortSpec.new(:operator => 'any')
588
+ }
589
+ | unary_operator tcp_port_qualifier
590
+ {
591
+ dputs "## tcp port spec: #{val[0]}/#{val[1]}"
592
+ result = AcePortSpec.new(
593
+ :operator => val[0],
594
+ :port => val[1]
595
+ )
596
+ }
597
+ | 'range' tcp_port_qualifier tcp_port_qualifier
598
+ {
599
+ dputs "## tcp port spec: #{val[0]}/#{val[1]}/#{val[2]}"
600
+ result = AcePortSpec.new(
601
+ :operator => val[0],
602
+ :begin_port => val[1],
603
+ :end_port => val[2]
604
+ )
605
+ }
606
+
607
+ udp_port_spec:
608
+ {
609
+ # udp any
610
+ dputs '## udp port: any'
611
+ result = AcePortSpec.new(:operator => 'any')
612
+ }
613
+ | unary_operator udp_port_qualifier
614
+ {
615
+ dputs "## udp port spec: #{val[0]}/#{val[1]}"
616
+ result = AcePortSpec.new(
617
+ :operator => val[0],
618
+ :port => val[1]
619
+ )
620
+ }
621
+ | 'range' udp_port_qualifier udp_port_qualifier
622
+ {
623
+ dputs "## udp port spec: #{val[0]}/#{val[1]}/#{val[2]}"
624
+ result = AcePortSpec.new(
625
+ :operator => val[0],
626
+ :begin_port => val[1],
627
+ :end_port => val[2]
628
+ )
629
+ }
630
+
631
+ unary_operator: 'gt'
632
+ | 'lt'
633
+ | 'eq'
634
+ | 'neq'
635
+
636
+ tcp_port_qualifier: NUMBER
637
+ {
638
+ begin
639
+ # port number (0-65535)
640
+ result = AceTcpProtoSpec.new(
641
+ :number => val[0]
642
+ )
643
+ rescue => err
644
+ yyerror_with err.message
645
+ end
646
+ }
647
+ | 'bgp'
648
+ {
649
+ result = AceTcpProtoSpec.new(
650
+ :name => val[0], :number => 179
651
+ )
652
+ }
653
+ | 'chargen'
654
+ {
655
+ result = AceTcpProtoSpec.new(
656
+ :name => val[0], :number => 19
657
+ )
658
+ }
659
+ | 'cmd'
660
+ {
661
+ result = AceTcpProtoSpec.new(
662
+ :name => val[0], :number => 514
663
+ )
664
+ }
665
+ | 'daytime'
666
+ {
667
+ result = AceTcpProtoSpec.new(
668
+ :name => val[0], :number => 13
669
+ )
670
+ }
671
+ | 'discard'
672
+ {
673
+ result = AceTcpProtoSpec.new(
674
+ :name => val[0], :number => 9
675
+ )
676
+ }
677
+ | 'domain'
678
+ {
679
+ result = AceTcpProtoSpec.new(
680
+ :name => val[0], :number => 53
681
+ )
682
+ }
683
+ | 'drip'
684
+ {
685
+ result = AceTcpProtoSpec.new(
686
+ :name => val[0], :number => 3949
687
+ )
688
+ }
689
+ | 'echo'
690
+ {
691
+ result = AceTcpProtoSpec.new(
692
+ :name => val[0], :number => 7
693
+ )
694
+ }
695
+ | 'exec'
696
+ {
697
+ result = AceTcpProtoSpec.new(
698
+ :name => val[0], :number => 512
699
+ )
700
+ }
701
+ | 'finger'
702
+ {
703
+ result = AceTcpProtoSpec.new(
704
+ :name => val[0], :number => 79
705
+ )
706
+ }
707
+ | 'ftp'
708
+ {
709
+ result = AceTcpProtoSpec.new(
710
+ :name => val[0], :number => 21
711
+ )
712
+ }
713
+ | 'ftp-data'
714
+ {
715
+ result = AceTcpProtoSpec.new(
716
+ :name => val[0], :number => 20
717
+ )
718
+ }
719
+ | 'gopher'
720
+ {
721
+ result = AceTcpProtoSpec.new(
722
+ :name => val[0], :number => 70
723
+ )
724
+ }
725
+ | 'hostname'
726
+ {
727
+ result = AceTcpProtoSpec.new(
728
+ :name => val[0], :number => 101
729
+ )
730
+ }
731
+ | 'ident'
732
+ {
733
+ result = AceTcpProtoSpec.new(
734
+ :name => val[0], :number => 113
735
+ )
736
+ }
737
+ | 'irc'
738
+ {
739
+ result = AceTcpProtoSpec.new(
740
+ :name => val[0], :number => 194
741
+ )
742
+ }
743
+ | 'klogin'
744
+ {
745
+ result = AceTcpProtoSpec.new(
746
+ :name => val[0], :number => 543
747
+ )
748
+ }
749
+ | 'kshell'
750
+ {
751
+ result = AceTcpProtoSpec.new(
752
+ :name => val[0], :number => 544
753
+ )
754
+ }
755
+ | 'login'
756
+ {
757
+ result = AceTcpProtoSpec.new(
758
+ :name => val[0], :number => 513
759
+ )
760
+ }
761
+ | 'lpd'
762
+ {
763
+ result = AceTcpProtoSpec.new(
764
+ :name => val[0], :number => 515
765
+ )
766
+ }
767
+ | 'nntp'
768
+ {
769
+ result = AceTcpProtoSpec.new(
770
+ :name => val[0], :number => 119
771
+ )
772
+ }
773
+ | 'pim-auto-rp'
774
+ {
775
+ result = AceTcpProtoSpec.new(
776
+ :name => val[0], :number => 496
777
+ )
778
+ }
779
+ | 'pop2'
780
+ {
781
+ result = AceTcpProtoSpec.new(
782
+ :name => val[0], :number => 109
783
+ )
784
+ }
785
+ | 'pop3'
786
+ {
787
+ result = AceTcpProtoSpec.new(
788
+ :name => val[0], :number => 110
789
+ )
790
+ }
791
+ | 'smtp'
792
+ {
793
+ result = AceTcpProtoSpec.new(
794
+ :name => val[0], :number => 25
795
+ )
796
+ }
797
+ | 'sunrpc'
798
+ {
799
+ result = AceTcpProtoSpec.new(
800
+ :name => val[0], :number => 111
801
+ )
802
+ }
803
+ | 'syslog'
804
+ {
805
+ result = AceTcpProtoSpec.new(
806
+ :name => val[0], :number => 514
807
+ )
808
+ }
809
+ | 'tacacs'
810
+ {
811
+ result = AceTcpProtoSpec.new(
812
+ :name => val[0], :number => 49
813
+ )
814
+ }
815
+ | 'talk'
816
+ {
817
+ result = AceTcpProtoSpec.new(
818
+ :name => val[0], :number => 517
819
+ )
820
+ }
821
+ | 'telnet'
822
+ {
823
+ result = AceTcpProtoSpec.new(
824
+ :name => val[0], :number => 23
825
+ )
826
+ }
827
+ | 'time'
828
+ {
829
+ result = AceTcpProtoSpec.new(
830
+ :name => val[0], :number => 37
831
+ )
832
+ }
833
+ | 'uucp'
834
+ {
835
+ result = AceTcpProtoSpec.new(
836
+ :name => val[0], :number => 540
837
+ )
838
+ }
839
+ | 'whois'
840
+ {
841
+ result = AceTcpProtoSpec.new(
842
+ :name => val[0], :number => 43
843
+ )
844
+ }
845
+ | 'www'
846
+ {
847
+ result = AceTcpProtoSpec.new(
848
+ :name => val[0], :number => 80
849
+ )
850
+ }
851
+
852
+ udp_port_qualifier: NUMBER
853
+ {
854
+ # port number (0-65535)
855
+ result = AceUdpProtoSpec.new(
856
+ :number => val[0]
857
+ )
858
+ }
859
+ | 'biff'
860
+ {
861
+ result = AceUdpProtoSpec.new(
862
+ :name => val[0], :number => 512
863
+ )
864
+ }
865
+ | 'bootpc'
866
+ {
867
+ result = AceUdpProtoSpec.new(
868
+ :name => val[0], :number => 68
869
+ )
870
+ }
871
+ | 'bootps'
872
+ {
873
+ result = AceUdpProtoSpec.new(
874
+ :name => val[0], :number => 67
875
+ )
876
+ }
877
+ | 'discard'
878
+ {
879
+ result = AceUdpProtoSpec.new(
880
+ :name => val[0], :number => 9
881
+ )
882
+ }
883
+ | 'dnsix'
884
+ {
885
+ result = AceUdpProtoSpec.new(
886
+ :name => val[0], :number => 195
887
+ )
888
+ }
889
+ | 'domain'
890
+ {
891
+ result = AceUdpProtoSpec.new(
892
+ :name => val[0], :number => 53
893
+ )
894
+ }
895
+ | 'echo'
896
+ {
897
+ result = AceUdpProtoSpec.new(
898
+ :name => val[0], :number => 7
899
+ )
900
+ }
901
+ | 'isakmp'
902
+ {
903
+ result = AceUdpProtoSpec.new(
904
+ :name => val[0], :number => 500
905
+ )
906
+ }
907
+ | 'mobile-ip'
908
+ {
909
+ result = AceUdpProtoSpec.new(
910
+ :name => val[0], :number => 434
911
+ )
912
+ }
913
+ | 'nameserver'
914
+ {
915
+ result = AceUdpProtoSpec.new(
916
+ :name => val[0], :number => 42
917
+ )
918
+ }
919
+ | 'netbios-dgm'
920
+ {
921
+ result = AceUdpProtoSpec.new(
922
+ :name => val[0], :number => 138
923
+ )
924
+ }
925
+ | 'netbios-ns'
926
+ {
927
+ result = AceUdpProtoSpec.new(
928
+ :name => val[0], :number => 137
929
+ )
930
+ }
931
+ | 'netbios-ss'
932
+ {
933
+ result = AceUdpProtoSpec.new(
934
+ :name => val[0], :number => 139
935
+ )
936
+ }
937
+ | 'non500-isakmp'
938
+ {
939
+ result = AceUdpProtoSpec.new(
940
+ :name => val[0], :number => 4500
941
+ )
942
+ }
943
+ | 'ntp'
944
+ {
945
+ result = AceUdpProtoSpec.new(
946
+ :name => val[0], :number => 123
947
+ )
948
+ }
949
+ | 'pim-auto-rp'
950
+ {
951
+ result = AceUdpProtoSpec.new(
952
+ :name => val[0], :number => 496
953
+ )
954
+ }
955
+ | 'rip'
956
+ {
957
+ result = AceUdpProtoSpec.new(
958
+ :name => val[0], :number => 520
959
+ )
960
+ }
961
+ | 'snmp'
962
+ {
963
+ result = AceUdpProtoSpec.new(
964
+ :name => val[0], :number => 161
965
+ )
966
+ }
967
+ | 'snmptrap'
968
+ {
969
+ result = AceUdpProtoSpec.new(
970
+ :name => val[0], :number => 162
971
+ )
972
+ }
973
+ | 'sunrpc'
974
+ {
975
+ result = AceUdpProtoSpec.new(
976
+ :name => val[0], :number => 111
977
+ )
978
+ }
979
+ | 'syslog'
980
+ {
981
+ result = AceUdpProtoSpec.new(
982
+ :name => val[0], :number => 514
983
+ )
984
+ }
985
+ | 'tacacs'
986
+ {
987
+ result = AceUdpProtoSpec.new(
988
+ :name => val[0], :number => 49
989
+ )
990
+ }
991
+ | 'talk'
992
+ {
993
+ result = AceUdpProtoSpec.new(
994
+ :name => val[0], :number => 517
995
+ )
996
+ }
997
+ | 'tftp'
998
+ {
999
+ result = AceUdpProtoSpec.new(
1000
+ :name => val[0], :number => 69
1001
+ )
1002
+ }
1003
+ | 'time'
1004
+ {
1005
+ result = AceUdpProtoSpec.new(
1006
+ :name => val[0], :number => 37
1007
+ )
1008
+ }
1009
+ | 'who'
1010
+ {
1011
+ result = AceUdpProtoSpec.new(
1012
+ :name => val[0], :number => 513
1013
+ )
1014
+ }
1015
+ | 'xdmcp'
1016
+ {
1017
+ result = AceUdpProtoSpec.new(
1018
+ :name => val[0], :number => 177
1019
+ )
1020
+ }
1021
+
1022
+ # tcp flags list
1023
+
1024
+ tcp_flags_qualifier:
1025
+ | tcp_flags_list
1026
+ | 'match-all' pm_tcp_flags_list
1027
+ | 'match-any' pm_tcp_flags_list
1028
+
1029
+ tcp_flags_list: tcp_flags
1030
+ {
1031
+ list = AceTcpFlagList.new
1032
+ list.push(val[0])
1033
+ result = list
1034
+ }
1035
+ | tcp_flags_list tcp_flags
1036
+ {
1037
+ val[0].push(val[1])
1038
+ result = val[0]
1039
+ }
1040
+
1041
+ tcp_flags: 'established'
1042
+ {
1043
+ dputs "## tcp_flags, established: #{val[0]}"
1044
+ result = AceTcpFlag.new(val[0])
1045
+ }
1046
+ | 'ack'
1047
+ {
1048
+ result = AceTcpFlag.new(val[0])
1049
+ }
1050
+ | 'syn'
1051
+ {
1052
+ result = AceTcpFlag.new(val[0])
1053
+ }
1054
+ | 'fin'
1055
+ {
1056
+ result = AceTcpFlag.new(val[0])
1057
+ }
1058
+ | 'psh'
1059
+ {
1060
+ result = AceTcpFlag.new(val[0])
1061
+ }
1062
+ | 'urg'
1063
+ {
1064
+ result = AceTcpFlag.new(val[0])
1065
+ }
1066
+ | 'rst'
1067
+ {
1068
+ result = AceTcpFlag.new(val[0])
1069
+ }
1070
+
1071
+ pm_tcp_flags_list: pm_tcp_flags
1072
+ | pm_tcp_flags_list pm_tcp_flags
1073
+
1074
+ pm_tcp_flags: '+ack'
1075
+ | '-ack'
1076
+ | '+syn'
1077
+ | '-syn'
1078
+ | '+fin'
1079
+ | '-fin'
1080
+ | '+psh'
1081
+ | '-psh'
1082
+ | '+urg'
1083
+ | '-urg'
1084
+ | '+rst'
1085
+ | '-rst'
1086
+
1087
+ # oether qualifier list
1088
+
1089
+ other_qualifier_list:
1090
+ | other_qualifier_list other_qualifier
1091
+ {
1092
+ dputs "## other qualifier list, #{val[0]}/#{val[1]}"
1093
+ if val[0]
1094
+ list = val[0]
1095
+ else
1096
+ list = AceOtherQualifierList.new
1097
+ end
1098
+ list.push(val[1])
1099
+ result = list
1100
+ }
1101
+
1102
+ other_qualifier: dscp_rule
1103
+ | 'fragments'
1104
+ | logging
1105
+ {
1106
+ dputs "## other qualifier, logging, #{val[0]})"
1107
+ result = val[0]
1108
+ }
1109
+ | tos_qualifier
1110
+ | precedence_qualifier
1111
+ | time_range_spec
1112
+ | recursive_qualifier
1113
+ {
1114
+ result = val[0]
1115
+ }
1116
+ | ttl_qualifier # IOS 12.4
1117
+ | option_qualifier # IOS 12.3(4)T,12.2(25)S, IP Options
1118
+
1119
+ dscp_rule: 'dscp' dscp_spec
1120
+ ;
1121
+
1122
+ dscp_spec: NUMBER # 0-63
1123
+ | 'af11' # 001010
1124
+ | 'af12' # 001100
1125
+ | 'af13' # 001110
1126
+ | 'af21' # 010010
1127
+ | 'af22' # 010100
1128
+ | 'af23' # 010110
1129
+ | 'af31' # 011010
1130
+ | 'af32' # 011100
1131
+ | 'af33' # 011110
1132
+ | 'af41' # 100010
1133
+ | 'af42' # 100100
1134
+ | 'af43' # 100110
1135
+ | 'CS1' # 001000
1136
+ | 'CS2' # 010000
1137
+ | 'CS3' # 011000
1138
+ | 'CS4' # 100000
1139
+ | 'CS5' # 101000
1140
+ | 'CS6' # 110000
1141
+ | 'CS7' # 111000
1142
+ | 'default' # 000000
1143
+ | 'ef' # 101110
1144
+
1145
+ logging: 'log-input' log_cookie
1146
+ {
1147
+ result = AceLogSpec.new(val[1], true)
1148
+ }
1149
+ | 'log' log_cookie
1150
+ {
1151
+ result = AceLogSpec.new(val[1])
1152
+ }
1153
+
1154
+ log_cookie:
1155
+ | STRING
1156
+ {
1157
+ result = val[0]
1158
+ }
1159
+
1160
+ tos_qualifier: 'tos' tos_string
1161
+ | 'tos' NUMBER
1162
+
1163
+ tos_string: 'max-reliability'
1164
+ | 'max-throughput'
1165
+ | 'min-delay'
1166
+ | 'min-monetary-cost'
1167
+ | 'normal'
1168
+
1169
+ precedence_qualifier: 'precedence' precedence_string
1170
+ | 'precedence' NUMBER # 0-7
1171
+
1172
+ precedence_string: 'critical' # 5
1173
+ | 'flash' # 3
1174
+ | 'flash-override' # 4
1175
+ | 'immediate' # 2
1176
+ | 'internet' # 6
1177
+ | 'network' # 7
1178
+ | 'priority' # 1
1179
+ | 'routine' # 0
1180
+
1181
+ time_range_spec: 'time-range' STRING
1182
+
1183
+ recursive_qualifier: 'reflect' STRING timeout_spec
1184
+ {
1185
+ ## TBD: timeout_spec not implemented yet.
1186
+ result = AceRecursiveQualifier.new(val[1])
1187
+ }
1188
+
1189
+ ttl_qualifier: 'ttl' unary_operator NUMBER # 0-255
1190
+ | 'ttl' 'range' NUMBER NUMBER
1191
+
1192
+ option_qualifier: 'option' option_spec
1193
+
1194
+ option_spec: 'add-ext' # opt 147
1195
+ | 'any-options'
1196
+ | 'com-security' # opt 134
1197
+ | 'dps' # opt 151
1198
+ | 'encode' # opt 15
1199
+ | 'eool' # opt 0
1200
+ | 'ext-ip' # opt 145
1201
+ | 'ext-security' # opt 133
1202
+ | 'finn' # opt 205
1203
+ | 'imitd' # opt 144
1204
+ | 'lsr' # opt 131
1205
+ | 'mtup' # opt 11
1206
+ | 'mtur' # opt 12
1207
+ | 'no-op' # opt 1
1208
+ | 'nsapa' # opt 150
1209
+ | 'record-route' # opt 7
1210
+ | 'route-alert' # opt 148
1211
+ | 'sdb' # opt 149
1212
+ | 'security' # opt 130
1213
+ | 'ssr' # opt 137
1214
+ | 'stream-id' # opt 136
1215
+ | 'timestamp' # opt 68
1216
+ | 'traceroute' # opt 82
1217
+ | 'ump' # opt 152
1218
+ | 'visa' # opt 142
1219
+ | 'zsu' # opt 10
1220
+ | NUMBER # ip options vlaue (0-255)
1221
+
1222
+ ---- header
1223
+
1224
+ require 'term/ansicolor'
1225
+ require 'cisco_acl_intp/scanner'
1226
+ require 'cisco_acl_intp/acl'
1227
+
1228
+ module CiscoAclIntp
1229
+
1230
+ ---- inner
1231
+
1232
+ # @return [Hash] ACL Table by ACL name key
1233
+ attr_reader :acl_table, :error_count
1234
+
1235
+ # Constructor
1236
+ # @param [Hash] opts Options
1237
+ # @option [Boolean] :yydebug Enable Racc debug print.
1238
+ # (default: false)
1239
+ # @option [Boolean] :debug Enable debug print.
1240
+ # (default: false)
1241
+ # @option [Boolean] :color Enable Term ANSI Color.
1242
+ # (default: false)
1243
+ # @option [Boolean] :silent Enable all parser syntax error
1244
+ # (default: false)
1245
+ # @return [CiscoACLParser]
1246
+ def initialize opts
1247
+ @yydebug = opts[:yydebug] || false
1248
+ @debug_print = opts[:debug] || false
1249
+ @color_mode = opts[:color] || false
1250
+ @silent_mode = @debug_print || opts[:silent] || false
1251
+
1252
+ if @color_mode
1253
+ AclContainerBase.enable_color
1254
+ else
1255
+ AclContainerBase.disable_color
1256
+ end
1257
+
1258
+ @acl_table = {}
1259
+ @curr_acl_name = ''
1260
+ @line_number = 0
1261
+ @error_count = 0
1262
+ end
1263
+
1264
+ # Scan ACL from file to parse
1265
+ # @param [String] file File name
1266
+ # @param [IO] file IO Object
1267
+ # @return [Hash] ACL Table
1268
+ def parse_file filename
1269
+ begin
1270
+ file = nil
1271
+ case filename
1272
+ when String
1273
+ file = File.new(filename)
1274
+ when IO, StringIO
1275
+ file = filename
1276
+ end
1277
+
1278
+ if file
1279
+ scanner = Scanner.new
1280
+ @queue = scanner.scan_file(file)
1281
+ @error_count = 0 # reset error count
1282
+ do_parse
1283
+ else
1284
+ @error_count = 1
1285
+ fail AclError, "File: #{filename} not found."
1286
+ end
1287
+ rescue Racc::ParseError => err
1288
+ eputs(
1289
+ ["Parse aborted. Found syntax error:",
1290
+ " #{err.message}"
1291
+ ].join("\n")
1292
+ )
1293
+ rescue AclArgumentError => err
1294
+ eputs(
1295
+ ["Parse aborted. Found acl argment error:",
1296
+ " #{err.message}",
1297
+ " #{err_pos_str}"
1298
+ ].join("\n")
1299
+ )
1300
+ rescue AclError => err
1301
+ eputs(
1302
+ ["Parse aborted. Found acl error:",
1303
+ " #{err.message}"
1304
+ ].join("\n")
1305
+ )
1306
+ rescue => err
1307
+ eputs(
1308
+ [ "Parse aborted. Found unknown error:",
1309
+ " #{err.message}"
1310
+ ].join("\n")
1311
+ )
1312
+ end
1313
+ @acl_table
1314
+ end
1315
+
1316
+ # Syntax error handler
1317
+ # @raise [Racc::ParseError]
1318
+ def on_error tok, val, vstack
1319
+ errstr = [
1320
+ err_pos_str,
1321
+ "near value: #{val}",
1322
+ "(token: #{token_to_str(tok)})",
1323
+ ].join(', ')
1324
+
1325
+ # raise Racc::ParseError, errstr
1326
+ eputs errstr
1327
+ end
1328
+
1329
+ # Parsed data contains error or not?
1330
+ # @return [Boolean]
1331
+ def contains_error?
1332
+ @error_count > 0 ? true : false
1333
+ end
1334
+
1335
+ private
1336
+
1337
+ # count syntax error
1338
+ def count_error
1339
+ @error_count = @error_count + 1
1340
+ end
1341
+
1342
+ # print error message and enter error recovery mode
1343
+ # @param [String] str Error message
1344
+ def yyerror_with str
1345
+ eputs [err_pos_str, str].join(', ')
1346
+ yyerror
1347
+ end
1348
+
1349
+ # normal err print
1350
+ # @param [String] str Message string
1351
+ def eputs str
1352
+ count_error
1353
+ puts c_err(str) unless @silent_mode
1354
+ end
1355
+
1356
+ # debug print
1357
+ # @param [String] str String to print
1358
+ def dputs str
1359
+ puts str if @debug_print
1360
+ end
1361
+
1362
+ # Get next token
1363
+ # @return [Array] Next token array
1364
+ def next_token
1365
+ @queue.shift
1366
+ end
1367
+
1368
+ # Coloring error string
1369
+ # @param [String] str Message string
1370
+ # @return [String] Colored message string
1371
+ def c_err str
1372
+ if @color_mode
1373
+ c = Term::ANSIColor
1374
+ str = [c.red, c.bold, str, c.clear].join
1375
+ end
1376
+ str
1377
+ end
1378
+
1379
+ # Generate error string
1380
+ # @return [String] error position string
1381
+ def err_pos_str
1382
+ line_num = @acl_table[@curr_acl_name] ?
1383
+ @acl_table[@curr_acl_name].length + 1 : ''
1384
+ ["in acl: #{@curr_acl_name}",
1385
+ "line: #{line_num}"
1386
+ ].join(', ')
1387
+ end
1388
+
1389
+ ---- footer
1390
+
1391
+ end # module
1392
+
1393
+ ### Local variables:
1394
+ ### mode: Racc
1395
+ ### coding: utf-8-unix
1396
+ ### indent-tabs-mode: nil
1397
+ ### End: