@@ -74,6 +74,12 @@ def IsDefaultDeny(term):
7474 return True
7575
7676
77+
78+ def GetNextPriority (priority ):
79+ """Get the priority for the next rule."""
80+ return priority
81+
82+
7783class Term (gcp .Term ):
7884 """Creates the term for the GCE firewall."""
7985
@@ -104,10 +110,14 @@ class Term(gcp.Term):
104110 # Any protocol not in _ALLOW_PROTO_NAME must be passed by number.
105111 ALWAYS_PROTO_NUM = set (gcp .Term .PROTO_MAP .keys ()) - _ALLOW_PROTO_NAME
106112
107- def __init__ (self , term , inet_version = 'inet' ):
113+ def __init__ (self , term , inet_version = 'inet' , policy_inet_version = 'inet' ):
108114 super (Term , self ).__init__ (term )
109115 self .term = term
110116 self .inet_version = inet_version
117+ # This is to handle mixed, where the policy_inet_version is mixed,
118+ # but the term inet version is either inet/inet6.
119+ # This is only useful for term name and priority.
120+ self .policy_inet_version = policy_inet_version
111121
112122 self ._validateDirection ()
113123 if self .term .source_address_exclude and not self .term .source_address :
@@ -194,6 +204,13 @@ def ConvertToDict(self):
194204 term_dict ['network' ] = self .term .network
195205 term_dict ['name' ] = '%s-%s' % (
196206 self .term .network .split ('/' )[- 1 ], term_dict ['name' ])
207+ # Identify if this is inet6 processing for a term under a mixed policy.
208+ mixed_policy_inet6_term = False
209+ if self .policy_inet_version == 'mixed' and self .inet_version == 'inet6' :
210+ mixed_policy_inet6_term = True
211+ # Update term name to have the IPv6 suffix for the inet6 rule.
212+ if mixed_policy_inet6_term :
213+ term_dict ['name' ] = gcp .GetIpv6TermName (term_dict ['name' ])
197214
198215 # Checking counts of tags, and ports to see if they exceeded limits.
199216 if len (self .term .source_tag ) > self ._TERM_SOURCE_TAGS_LIMIT :
@@ -214,19 +231,35 @@ def ConvertToDict(self):
214231 term_dict ['targetTags' ] = self .term .destination_tag
215232 if self .term .priority :
216233 term_dict ['priority' ] = self .term .priority
234+ # Update term priority for the inet6 rule.
235+ if mixed_policy_inet6_term :
236+ term_dict ['priority' ] = GetNextPriority (term_dict ['priority' ])
217237
218238 rules = []
219- # TODO(abhindes) correctly account for 'mixed' as well
239+ # If 'mixed' ends up in indvidual term inet_version, something has gone
240+ # horribly wrong. The only valid values are inet/inet6.
220241 term_af = self .AF_MAP .get (self .inet_version )
242+ if self .inet_version == 'mixed' :
243+ raise GceFirewallError (
244+ 'GCE firewall rule has incorrect inet_version for rule: %s' %
245+ self .term .name )
246+
247+ # Exit early for inet6 processing of mixed rules that have only tags,
248+ # and no IP addresses, since this is handled in the inet processing.
249+ if mixed_policy_inet6_term :
250+ if not self .term .source_address and not self .term .destination_address :
251+ if 'targetTags' in term_dict or 'sourceTags' in term_dict :
252+ return []
253+
221254 saddrs = sorted (self .term .GetAddressOfVersion ('source_address' , term_af ),
222255 key = ipaddress .get_mixed_type_key )
223256 daddrs = sorted (
224257 self .term .GetAddressOfVersion ('destination_address' , term_af ),
225258 key = ipaddress .get_mixed_type_key )
226259
227260 # If the address got filtered out and is empty due to address family, we
228- # don't render the term. At this point of term processing, the direction has
229- # already been validated, so we can just log and return empty rule.
261+ # don't render the term. At this point of term processing, the direction
262+ # has already been validated, so we can just log and return empty rule.
230263 if self .term .source_address and not saddrs :
231264 logging .warning (
232265 'WARNING: Term %s is not being rendered for %s, '
@@ -252,7 +285,7 @@ def ConvertToDict(self):
252285 filtered_protocols = []
253286 for proto in self .term .protocol :
254287 # ICMP filtering by inet_version
255- # TODO(abhindes) deal with " mixed" correctly
288+ # Since each term has inet_version, ' mixed' is correctly processed here.
256289 # Convert protocol to number for uniformity of comparison.
257290 # PROTO_MAP always returns protocol number.
258291 if proto in self ._ALLOW_PROTO_NAME :
@@ -349,7 +382,7 @@ class GCE(gcp.GCP):
349382
350383 _PLATFORM = 'gce'
351384 SUFFIX = '.gce'
352- _SUPPORTED_AF = frozenset (('inet' , 'inet6' ))
385+ _SUPPORTED_AF = frozenset (('inet' , 'inet6' , 'mixed' ))
353386 _ANY_IP = {
354387 'inet' : nacaddr .IP ('0.0.0.0/0' ),
355388 'inet6' : nacaddr .IP ('::/0' ),
@@ -438,10 +471,19 @@ def _TranslatePolicy(self, pol, exp_info):
438471 terms [- 1 ].priority = 65534
439472 if direction == 'EGRESS' :
440473 if address_family != 'mixed' :
474+ # Default deny also gets processed as part of terms processing.
475+ # The name and priority get updated there.
441476 terms [- 1 ].destination_address = [self ._ANY_IP [address_family ]]
477+ else :
478+ terms [- 1 ].destination_address = [self ._ANY_IP ['inet' ],
479+ self ._ANY_IP ['inet6' ]]
442480 else :
443481 if address_family != 'mixed' :
444482 terms [- 1 ].source_address = [self ._ANY_IP [address_family ]]
483+ else :
484+ terms [- 1 ].source_address = [
485+ self ._ANY_IP ['inet' ], self ._ANY_IP ['inet6' ]
486+ ]
445487
446488 for term in terms :
447489 if term .stateless_reply :
@@ -456,7 +498,7 @@ def _TranslatePolicy(self, pol, exp_info):
456498 term .name += '-e'
457499 term .name = self .FixTermLength (term .name )
458500 if term .name in term_names :
459- raise GceFirewallError ('Duplicate term name' )
501+ raise GceFirewallError ('Duplicate term name %s' % term . name )
460502 term_names .add (term .name )
461503
462504 term .direction = direction
@@ -472,18 +514,26 @@ def _TranslatePolicy(self, pol, exp_info):
472514 raise GceFirewallError (
473515 'GCE firewall does not support term options.' )
474516
475- for rules in Term (term , address_family ).ConvertToDict ():
476- logging .debug ('Attribute count of rule %s is: %d' , term .name ,
477- GetAttributeCount (rules ))
478- total_attribute_count += GetAttributeCount (rules )
479- total_rule_count += 1
480- if max_attribute_count and total_attribute_count > max_attribute_count :
481- # Stop processing rules as soon as the attribute count is over the
482- # limit.
483- raise ExceededAttributeCountError (
484- 'Attribute count (%d) for %s exceeded the maximum (%d)' % (
485- total_attribute_count , filter_name , max_attribute_count ))
486- self .gce_policies .append (rules )
517+ # Handle mixed for each indvidual term as inet and inet6.
518+ # inet/inet6 are treated the same.
519+ term_address_families = []
520+ if address_family == 'mixed' :
521+ term_address_families = ['inet' , 'inet6' ]
522+ else :
523+ term_address_families = [address_family ]
524+ for term_af in term_address_families :
525+ for rules in Term (term , term_af , address_family ).ConvertToDict ():
526+ logging .debug ('Attribute count of rule %s is: %d' , term .name ,
527+ GetAttributeCount (rules ))
528+ total_attribute_count += GetAttributeCount (rules )
529+ total_rule_count += 1
530+ if max_attribute_count and total_attribute_count > max_attribute_count :
531+ # Stop processing rules as soon as the attribute count is over the
532+ # limit.
533+ raise ExceededAttributeCountError (
534+ 'Attribute count (%d) for %s exceeded the maximum (%d)' %
535+ (total_attribute_count , filter_name , max_attribute_count ))
536+ self .gce_policies .append (rules )
487537 logging .info ('Total rule count of policy %s is: %d' , filter_name ,
488538 total_rule_count )
489539 logging .info ('Total attribute count of policy %s is: %d' , filter_name ,
0 commit comments