1- # Copyright (c) 2015-2016 Cisco and/or its affiliates.
1+ # Copyright (c) 2015-2018 Cisco and/or its affiliates.
22#
33# Licensed under the Apache License, Version 2.0 (the "License");
44# you may not use this file except in compliance with the License.
1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ require 'ipaddr'
1516require_relative 'node_util'
1617
1718module Cisco
@@ -73,6 +74,9 @@ def ace_get
7374 remark = Regexp . new ( '(?<seqno>\d+) remark (?<remark>.*)' ) . match ( str )
7475 return remark unless remark . nil?
7576
77+ # specialized icmp protocol handling
78+ return icmp_ace_get ( str ) if str . include? ( 'icmp' )
79+
7680 # rubocop:disable Metrics/LineLength
7781 regexp = Regexp . new ( '(?<seqno>\d+) (?<action>\S+)' \
7882 ' *(?<proto>\d+|\S+)' \
@@ -95,6 +99,60 @@ def ace_get
9599 regexp . match ( str )
96100 end
97101
102+ # icmp ace getter
103+ def icmp_ace_get ( str )
104+ # rubocop:disable Metrics/LineLength
105+ # fragments is nvgen at a different location than all other
106+ # proto_option so get rid of it so as not to mess up other fields
107+ str . sub! ( 'fragments ' , '' )
108+ regexp = Regexp . new ( '(?<seqno>\d+) (?<action>\S+)' \
109+ ' *(?<proto>\d+|\S+)' \
110+ ' *(?<src_addr>any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)' \
111+ ' *(?<dst_addr>any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)' \
112+ ' *(?<proto_option>\S+)?' \
113+ ' *(?<precedence>precedence \S+)?' \
114+ ' *(?<dscp>dscp \S+)?' \
115+ ' *(?<time_range>time-range \S+)?' \
116+ ' *(?<packet_length>packet-length (range \d+ \d+|(lt|eq|gt|neq) \d+))?' \
117+ ' *(?<ttl>ttl \d+)?' \
118+ ' *(?<vlan>vlan \d+)?' \
119+ ' *(?<set_erspan_gre_proto>set-erspan-gre-proto \d+)?' \
120+ ' *(?<set_erspan_dscp>set-erspan-dscp \d+)?' \
121+ ' *(?<redirect>redirect \S+)?' )
122+ regexp_no_proto_option = Regexp . new ( '(?<seqno>\d+) (?<action>\S+)' \
123+ ' *(?<proto>\d+|\S+)' \
124+ ' *(?<src_addr>any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)' \
125+ ' *(?<dst_addr>any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)' \
126+ ' *(?<precedence>precedence \S+)?' \
127+ ' *(?<dscp>dscp \S+)?' \
128+ ' *(?<time_range>time-range \S+)?' \
129+ ' *(?<packet_length>packet-length (range \d+ \d+|(lt|eq|gt|neq) \d+))?' \
130+ ' *(?<ttl>ttl \d+)?' \
131+ ' *(?<vlan>vlan \d+)?' \
132+ ' *(?<set_erspan_gre_proto>set-erspan-gre-proto \d+)?' \
133+ ' *(?<set_erspan_dscp>set-erspan-dscp \d+)?' \
134+ ' *(?<redirect>redirect \S+)?' )
135+ temp = regexp . match ( str )
136+ po = temp [ :proto_option ]
137+ if po . nil?
138+ return temp
139+ # redirect can be proto_option or an actual redirect to interface
140+ elsif po . strip . match ( /redirect$/ )
141+ if str . match ( /Ethernet|port-channel/ )
142+ # if proto_option is given as redirect and also redirect to intf
143+ # we need to do extra processing
144+ return temp if check_redirect_repeat ( str )
145+ return regexp_no_proto_option . match ( str )
146+ end
147+ # the reserved keywords check
148+ elsif po . strip . match ( /precedence$|dscp$|time-range$|packet-length$|ttl$|vlan$|set-erspan-gre-proto$|set-erspan-dscp$|log$/ )
149+ return regexp_no_proto_option . match ( str )
150+ else
151+ return temp
152+ end
153+ # rubocop:enable Metrics/LineLength
154+ end
155+
98156 # common ace setter. Put the values you need in a hash and pass it in.
99157 # attrs = {:action=>'permit', :proto=>'tcp', :src =>'host 1.1.1.1'}
100158 def ace_set ( attrs )
@@ -130,6 +188,10 @@ def ace_set(attrs)
130188 :tcp_option_length ,
131189 :redirect ,
132190 :log ,
191+ :proto_option ,
192+ :set_erspan_dscp ,
193+ :set_erspan_gre_proto ,
194+ :vlan ,
133195 ] . each do |p |
134196 attrs [ p ] = '' if attrs [ p ] . nil?
135197 send ( p . to_s + '=' , attrs [ p ] )
@@ -139,6 +201,21 @@ def ace_set(attrs)
139201 config_set ( 'acl' , cmd , @set_args )
140202 end
141203
204+ def valid_ipv6? ( addr )
205+ begin
206+ ret = IPAddr . new ( addr . split [ 0 ] ) . ipv6?
207+ rescue
208+ ret = false
209+ end
210+ ret
211+ end
212+
213+ def check_redirect_repeat ( str )
214+ return false unless str . include? ( 'redirect' )
215+ nstr = str . sub ( 'redirect' , '' ) . strip
216+ nstr . include? ( 'redirect' ) ? true : false
217+ end
218+
142219 # PROPERTIES
143220 # ----------
144221 def seqno
@@ -182,7 +259,7 @@ def src_addr
182259 return nil if match . nil? || !match . names . include? ( 'src_addr' )
183260 addr = match [ :src_addr ]
184261 # Normalize addr. Some platforms zero_pad ipv6 addrs.
185- addr . gsub! ( /^0*/ , '' ) . gsub! ( /:0*/ , ':' )
262+ addr . gsub! ( /^0*/ , '' ) . gsub! ( /:0*/ , ':' ) if valid_ipv6? ( addr )
186263 addr
187264 end
188265
@@ -205,7 +282,7 @@ def dst_addr
205282 return nil if match . nil? || !match . names . include? ( 'dst_addr' )
206283 addr = match [ :dst_addr ]
207284 # Normalize addr. Some platforms zero_pad ipv6 addrs.
208- addr . gsub! ( /^0*/ , '' ) . gsub! ( /:0*/ , ':' )
285+ addr . gsub! ( /^0*/ , '' ) . gsub! ( /:0*/ , ':' ) if valid_ipv6? ( addr )
209286 addr
210287 end
211288
@@ -261,6 +338,49 @@ def dscp=(dscp)
261338 @set_args [ :dscp ] = Utils . attach_prefix ( dscp , :dscp )
262339 end
263340
341+ def vlan
342+ Utils . extract_value ( ace_get , 'vlan' )
343+ end
344+
345+ def vlan = ( vlan )
346+ @set_args [ :vlan ] = Utils . attach_prefix ( vlan , :vlan )
347+ end
348+
349+ def set_erspan_dscp
350+ ret = Utils . extract_value ( ace_get , 'set_erspan_dscp' , 'set-erspan-dscp' )
351+ return ret if ret
352+ # position of set_erspan_dscp is different in older release so check again
353+ str = config_get ( 'acl' , 'ace' , @get_args )
354+ sstr = str . split
355+ return sstr [ sstr . index ( 'set-erspan-dscp' ) + 1 ] if
356+ sstr . include? ( 'set-erspan-dscp' )
357+ end
358+
359+ def set_erspan_dscp = ( set_erspan_dscp )
360+ @set_args [ :set_erspan_dscp ] = Utils . attach_prefix ( set_erspan_dscp ,
361+ :set_erspan_dscp ,
362+ 'set-erspan-dscp' )
363+ end
364+
365+ def set_erspan_gre_proto
366+ ret = Utils . extract_value ( ace_get , 'set_erspan_gre_proto' ,
367+ 'set-erspan-gre-proto' )
368+ return ret if ret
369+ # position of set_erspan_gre_proto is different in older release
370+ # so check again
371+ str = config_get ( 'acl' , 'ace' , @get_args )
372+ sstr = str . split
373+ return sstr [ sstr . index ( 'set-erspan-gre-proto' ) + 1 ] if
374+ sstr . include? ( 'set-erspan-gre-proto' )
375+ end
376+
377+ def set_erspan_gre_proto = ( set_erspan_gre_proto )
378+ @set_args [ :set_erspan_gre_proto ] =
379+ Utils . attach_prefix ( set_erspan_gre_proto ,
380+ :set_erspan_gre_proto ,
381+ 'set-erspan-gre-proto' )
382+ end
383+
264384 def time_range
265385 Utils . extract_value ( ace_get , 'time_range' , 'time-range' )
266386 end
@@ -317,12 +437,27 @@ def redirect=(redirect)
317437 @set_args [ :redirect ] = Utils . attach_prefix ( redirect , :redirect )
318438 end
319439
320- def log
440+ def proto_option
321441 match = ace_get
442+ return nil if match . nil? || proto != 'icmp' || !remark . nil?
443+ # fragments is nvgen at a different location than all other
444+ # proto_option
445+ if config_get ( 'acl' , 'ace' , @get_args ) . include? ( 'fragments' )
446+ return 'fragments'
447+ end
448+ # log is special case
449+ return nil if !match . names . include? ( 'proto_option' ) ||
450+ match [ :proto_option ] == 'log'
451+ match [ :proto_option ]
452+ end
453+
454+ def proto_option = ( proto_option )
455+ @set_args [ :proto_option ] = proto_option
456+ end
457+
458+ def log
322459 return nil unless remark . nil?
323- return false if match . nil?
324- return false unless match . names . include? ( 'log' )
325- match [ :log ] == 'log' ? true : false
460+ config_get ( 'acl' , 'ace' , @get_args ) . include? ( 'log' ) ? true : false
326461 end
327462
328463 def log = ( log )
0 commit comments