cisco_acl_intp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: