def group_entry_from_group_mod(mod): group = ofp.ofp_group_entry( desc=ofp.ofp_group_desc(type=mod.type, group_id=mod.group_id, buckets=mod.buckets), stats=ofp.ofp_group_stats(group_id=mod.group_id # TODO do we need to instantiate bucket bins? )) return group
def group_entry_from_group_mod(mod): group = ofp.ofp_group_entry( desc=ofp.ofp_group_desc( type=mod.type, group_id=mod.group_id, buckets=mod.buckets ), stats=ofp.ofp_group_stats( group_id=mod.group_id # TODO do we need to instantiate bucket bins? ) ) return group
def decompose_flow(self, flow, group_map): assert isinstance(flow, ofp.ofp_flow_stats) #################################################################### # # limited, heuristics based implementation # needs to be replaced, see https://jira.opencord.org/browse/CORD-841 # #################################################################### in_port_no = get_in_port(flow) out_port_no = get_out_port(flow) # may be None device_rules = {} # accumulator route = self.get_route(in_port_no, out_port_no) if route is None: log.error('no-route', in_port_no=in_port_no, out_port_no=out_port_no, comment='deleting flow') self.flow_delete(flow) return device_rules assert len(route) == 2 ingress_hop, egress_hop = route log.info('decompose-flow', in_port_no=in_port_no, out_port_no=out_port_no, ingress_hop=ingress_hop, egress_hop=egress_hop) def is_downstream(): return ingress_hop.device.root def is_upstream(): return not is_downstream() if out_port_no is not None and \ (out_port_no & 0x7fffffff) == ofp.OFPP_CONTROLLER: # UPSTREAM CONTROLLER-BOUND FLOW # we assume that the ingress device is already pushing a # customer-specific vlan (c-vid), based on its default flow # rules so there is nothing else to do on the ONU # on the olt, we need to push a new tag and set it to 4000 # which for now represents in-bound channel to the controller # (via Voltha) # TODO make the 4000 configurable fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) log.info('trap-flow', in_port_no=in_port_no, nni=self._nni_logical_port_no) if in_port_no == self._nni_logical_port_no: log.debug('trap-nni') # Trap flow for NNI port fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.egress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) ] )) else: log.debug('trap-uni') # Trap flow for UNI port # in_port_no is None for wildcard input case, do not include # upstream port for 4000 flow in input if in_port_no is None: in_ports = self.get_wildcard_input_ports(exclude_port= egress_hop.egress_port.port_no) else: in_ports = [in_port_no] for input_port in in_ports: fl_lst.append(mk_flow_stat( # Upstream flow priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), vlan_vid(ofp.OFPVID_PRESENT | input_port) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID) ], actions=[ push_vlan(0x8100), set_field(vlan_vid(ofp.OFPVID_PRESENT | 4000)), output(egress_hop.egress_port.port_no)] )) fl_lst.append(mk_flow_stat( # Downstream flow priority=flow.priority, match_fields=[ in_port(egress_hop.egress_port.port_no), vlan_vid(ofp.OFPVID_PRESENT | 4000), vlan_pcp(0), metadata(input_port) ], actions=[ pop_vlan(), output(egress_hop.ingress_port.port_no)] )) else: # NOT A CONTROLLER-BOUND FLOW if is_upstream(): log.info('decompose-flow-is-upstream') # We assume that anything that is upstream needs to get Q-in-Q # treatment and that this is expressed via two flow rules, # the first using the goto-statement. We also assume that the # inner tag is applied at the ONU, while the outer tag is # applied at the OLT if has_next_table(flow): assert out_port_no is None fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) ] + [ output(ingress_hop.egress_port.port_no) ] )) else: actions = [action.type for action in get_actions(flow)] # Transparent ONU and OLT case (No-L2-Modification flow) if len(actions) == 1 and OUTPUT in actions: child_device_flow_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) parent_device_flow_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) child_device_flow_lst.append(mk_flow_stat( priority = flow.priority, cookie = flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ output(ingress_hop.egress_port.port_no) ] )) parent_device_flow_lst.append(mk_flow_stat( priority = flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ output(egress_hop.egress_port.port_no) ] )) else: assert out_port_no is not None fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type != OUTPUT ] + [ output(egress_hop.egress_port.port_no) ] )) else: # downstream log.info('decompose-flow-is-downstream') if has_next_table(flow): assert out_port_no is None if get_metadata(flow) is not None: log.info('creating-metadata-flow', flow=flow) # For downstream flows with dual-tags, recalculate route. port_number = get_port_number_from_metadata(flow) if port_number is not None: route = self.get_route(in_port_no, port_number) if route is None: log.error('no-route-double-tag', in_port_no=in_port_no, out_port_no=port_number, comment='deleting flow', metadata=get_metadata_64_bit(flow)) self.flow_delete(flow) return device_rules assert len(route) == 2 ingress_hop, egress_hop = route inner_tag = get_inner_tag_from_metadata(flow) if inner_tag is None: log.error('no-inner-tag-double-tag', in_port_no=in_port_no, out_port_no=port_number, comment='deleting flow', metadata=get_metadata_64_bit(flow)) self.flow_delete(flow) return device_rules fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no), metadata(inner_tag) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, METADATA) ], actions=[ action for action in get_actions(flow) ] + [ output(ingress_hop.egress_port.port_no) ] )) else: log.info('creating-standard-flow', flow=flow) fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) ] + [ output(ingress_hop.egress_port.port_no) ] )) elif out_port_no is not None: # unicast case actions = [action.type for action in get_actions(flow)] # Transparent ONU and OLT case (No-L2-Modification flow) if len(actions) == 1 and OUTPUT in actions: parent_device_flow_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) child_device_flow_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) parent_device_flow_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ output(ingress_hop.egress_port.port_no) ] )) child_device_flow_lst.append(mk_flow_stat( priority = flow.priority, cookie=flow.cookie, match_fields = [ in_port(egress_hop.ingress_port.port_no), ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ output(egress_hop.egress_port.port_no) ] )) else: fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) if action.type not in (OUTPUT,) ] + [ output(egress_hop.egress_port.port_no) ] )) else: grp_id = get_group(flow) if grp_id is not None: # multicast case fl_lst_olt, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) # having no group yet is the same as having a group with # no buckets group = group_map.get(grp_id, ofp.ofp_group_entry()) for bucket in group.desc.buckets: found_pop_vlan = False other_actions = [] for action in bucket.actions: if action.type == POP_VLAN: found_pop_vlan = True elif action.type == OUTPUT: out_port_no = action.output.port else: other_actions.append(action) # re-run route request to determine egress device and # ports route2 = self.get_route(in_port_no, out_port_no) if not route2 or len(route2) != 2: log.error('mc-no-route', in_port_no=in_port_no, out_port_no=out_port_no, route2=route2, comment='deleting flow') self.flow_delete(flow) continue ingress_hop2, egress_hop = route2 if ingress_hop.ingress_port != ingress_hop2.ingress_port: log.error('mc-ingress-hop-hop2-mismatch', ingress_hop=ingress_hop, ingress_hop2=ingress_hop2, in_port_no=in_port_no, out_port_no=out_port_no, comment='ignoring flow') continue fl_lst_olt.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) if action.type not in (GROUP,) ] + [ pop_vlan(), output(egress_hop.ingress_port.port_no) ] )) fl_lst_onu, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst_onu.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID, VLAN_PCP) ], actions=other_actions + [ output(egress_hop.egress_port.port_no) ] )) else: raise NotImplementedError('undefined downstream case for flows') return device_rules
def decompose_flow(self, flow, group_map): assert isinstance(flow, ofp.ofp_flow_stats) #################################################################### # # limited, heuristics based implementation # needs to be replaced, see https://jira.opencord.org/browse/CORD-841 # #################################################################### in_port_no = get_in_port(flow) out_port_no = get_out_port(flow) # may be None device_rules = {} # accumulator route = self.get_route(in_port_no, out_port_no) if route is None: log.error('no-route', in_port_no=in_port_no, out_port_no=out_port_no, comment='ignoring flow') return device_rules assert len(route) == 2 ingress_hop, egress_hop = route def is_downstream(): return ingress_hop.device.root def is_upstream(): return not is_downstream() if out_port_no is not None and \ (out_port_no & 0x7fffffff) == ofp.OFPP_CONTROLLER: # UPSTREAM CONTROLLER-BOUND FLOW # we assume that the ingress device is already pushing a # customer-specific vlan (c-vid), based on its default flow # rules so there is nothing else to do on the ONU # on the olt, we need to push a new tag and set it to 4000 # which for now represents in-bound channel to the controller # (via Voltha) # TODO make the 4000 configurable fl_lst, _ = device_rules.setdefault(egress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[in_port(egress_hop.ingress_port.port_no)] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID) ], actions=[ push_vlan(0x8100), set_field(vlan_vid(ofp.OFPVID_PRESENT | 4000)), output(egress_hop.egress_port.port_no) ])) else: # NOT A CONTROLLER-BOUND FLOW if is_upstream(): # We assume that anything that is upstream needs to get Q-in-Q # treatment and that this is expressed via two flow rules, # the first using the goto-statement. We also assume that the # inner tag is applied at the ONU, while the outer tag is # applied at the OLT if has_next_table(flow): assert out_port_no is None fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[action for action in get_actions(flow)] + [output(ingress_hop.egress_port.port_no)])) else: assert out_port_no is not None fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type != OUTPUT ] + [output(egress_hop.egress_port.port_no)])) else: # downstream if has_next_table(flow): assert out_port_no is None fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[action for action in get_actions(flow)] + [output(ingress_hop.egress_port.port_no)])) elif out_port_no is not None: # unicast case fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type not in (OUTPUT, ) ] + [output(egress_hop.egress_port.port_no)])) else: # multicast case grp_id = get_group(flow) assert grp_id is not None fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type not in (GROUP, ) ] + [ pop_vlan(), output(ingress_hop.egress_port.port_no) ])) # having no group yet is the same as having a group with # no buckets group = group_map.get(grp_id, ofp.ofp_group_entry()) for bucket in group.desc.buckets: found_pop_vlan = False other_actions = [] for action in bucket.actions: if action.type == POP_VLAN: found_pop_vlan = True elif action.type == OUTPUT: out_port_no = action.output.port else: other_actions.append(action) # re-run route request to determine egress device and # ports route2 = self.get_route(in_port_no, out_port_no) if route2 is None: log.error('mc-no-route', in_port_no=in_port_no, out_port_no=out_port_no, route2=route2, comment='ignoring flow') continue assert len(route2) == 2 ingress_hop2, egress_hop = route2 assert ingress_hop == ingress_hop2 fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID, VLAN_PCP) ], actions=other_actions + [output(egress_hop.egress_port.port_no)])) return device_rules
def decompose_flow(self, flow, group_map): assert isinstance(flow, ofp.ofp_flow_stats) #################################################################### # # limited, heuristics based implementation # needs to be replaced, see https://jira.opencord.org/browse/CORD-841 # #################################################################### in_port_no = get_in_port(flow) out_port_no = get_out_port(flow) # may be None device_rules = {} # accumulator route = self.get_route(in_port_no, out_port_no) if route is None: log.warn('no-route', in_port_no=in_port_no, out_port_no=out_port_no, comment='deleting flow') self.flow_delete(flow) return device_rules assert len(route) == 2 ingress_hop, egress_hop = route def is_downstream(): return ingress_hop.device.root def is_upstream(): return not is_downstream() meter_id = get_meter_id_from_flow(flow) metadata_from_write_metadata = get_metadata_from_write_metadata(flow) # first identify trap flows for packets from UNI or NNI ports if out_port_no is not None and \ (out_port_no & 0x7fffffff) == ofp.OFPP_CONTROLLER: # CONTROLLER-BOUND FLOW # TODO: support in-band control as an option if in_port_no == self._nni_logical_port_no: # TODO handle multiple NNI ports log.debug('decomposing-trap-flow-from-nni', match=flow.match) # no decomposition required - it is already an OLT flow from NNI fl_lst, _ = device_rules.setdefault(ingress_hop.device.id, ([], [])) fl_lst.append(flow) else: log.debug('decomposing-trap-flow-from-uni', match=flow.match) # we assume that the ingress device is already pushing a # customer-specific vlan (c-vid) or default vlan id # so there is nothing else to do on the ONU # XXX is this a correct assumption? fl_lst, _ = device_rules.setdefault(egress_hop.device.id, ([], [])) # wildcarded input port matching is not handled if in_port_no is None: log.error('wildcarded-input-not-handled', flow=flow, comment='deleting flow') self.flow_delete(flow) return device_rules # need to map the input UNI port to the corresponding PON port fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[action for action in get_actions(flow)], meter_id=meter_id, metadata=metadata_from_write_metadata)) else: # NOT A CONTROLLER-BOUND FLOW # we assume that the controller has already ensured the right # actions for cases where # a) vlans are pushed or popped at onu and olt # b) C-vlans are transparently forwarded if is_upstream(): if flow.table_id == 0 and has_next_table(flow): # This is an ONU flow in upstream direction assert out_port_no is None log.debug('decomposing-onu-flow-in-upstream', match=flow.match) fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[action for action in get_actions(flow)] + [output(ingress_hop.egress_port.port_no)], meter_id=meter_id, metadata=metadata_from_write_metadata)) elif flow.table_id == 0 and not has_next_table(flow) and \ out_port_no is None: # This is an ONU drop flow for untagged packets at the UNI log.debug('decomposing-onu-drop-flow-upstream', match=flow.match) fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ vlan_vid(0) # OFPVID_NONE indicating untagged ], actions=[] # no action is drop )) elif flow.table_id == 1 and out_port_no is not None: # This is OLT flow in upstream direction log.debug('decomposing-olt-flow-in-upstream', match=flow.match) fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type != OUTPUT ] + [output(egress_hop.egress_port.port_no)], meter_id=meter_id, metadata=metadata_from_write_metadata)) else: # unknown upstream flow log.error('unknown-upstream-flow', flow=flow, comment='deleting flow') self.flow_delete(flow) return device_rules else: # downstream if flow.table_id == 0 and has_next_table(flow): # OLT flow in downstream direction (unicast traffic) assert out_port_no is None log.debug('decomposing-olt-flow-in-downstream', match=flow.match) # For downstream flows without output port action we need to # recalculate route with the output extracted from the metadata # to determine the PON port to send to the correct ONU/UNI egress_port_number = get_egress_port_number_from_metadata( flow) if egress_port_number is not None: route = self.get_route(in_port_no, egress_port_number) if route is None: log.error('no-route-downstream', in_port_no=in_port_no, egress_port_number=egress_port_number, comment='deleting flow') self.flow_delete(flow) return device_rules assert len(route) == 2 ingress_hop, egress_hop = route fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[action for action in get_actions(flow)] + [output(ingress_hop.egress_port.port_no)], meter_id=meter_id, metadata=metadata_from_write_metadata)) elif flow.table_id == 1 and out_port_no is not None: # ONU flow in downstream direction (unicast traffic) log.debug('decomposing-onu-flow-in-downstream', match=flow.match) fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type not in (OUTPUT, ) ] + [output(egress_hop.egress_port.port_no)], meter_id=meter_id, metadata=metadata_from_write_metadata)) elif flow.table_id == 0 and has_group(flow): # Multicast Flow log.debug('decomposing-multicast-flow') grp_id = get_group(flow) fl_lst_olt, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) # having no group yet is the same as having a group with # no buckets group = group_map.get(grp_id, ofp.ofp_group_entry()) for bucket in group.desc.buckets: found_pop_vlan = False other_actions = [] for action in bucket.actions: if action.type == POP_VLAN: found_pop_vlan = True elif action.type == OUTPUT: out_port_no = action.output.port else: other_actions.append(action) # re-run route request to determine egress device and # ports route2 = self.get_route(in_port_no, out_port_no) if not route2 or len(route2) != 2: log.error('mc-no-route', in_port_no=in_port_no, out_port_no=out_port_no, route2=route2, comment='deleting flow') self.flow_delete(flow) continue ingress_hop2, egress_hop = route2 if ingress_hop.ingress_port != ingress_hop2.ingress_port: log.error('mc-ingress-hop-hop2-mismatch', ingress_hop=ingress_hop, ingress_hop2=ingress_hop2, in_port_no=in_port_no, out_port_no=out_port_no, comment='ignoring flow') continue fl_lst_olt.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type not in (GROUP, ) ] + [ pop_vlan(), output(egress_hop.ingress_port.port_no) ])) fl_lst_onu, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst_onu.append( mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID, VLAN_PCP) ], actions=other_actions + [output(egress_hop.egress_port.port_no)])) else: log.error('unknown-downstream-flow', flow=flow, comment='deleting flow') self.flow_delete(flow) return device_rules
def decompose_flow(self, flow, group_map): assert isinstance(flow, ofp.ofp_flow_stats) #################################################################### # # limited, heuristics based implementation # needs to be replaced, see https://jira.opencord.org/browse/CORD-841 # #################################################################### in_port_no = get_in_port(flow) out_port_no = get_out_port(flow) # may be None device_rules = {} # accumulator route = self.get_route(in_port_no, out_port_no) if route is None: log.error('no-route', in_port_no=in_port_no, out_port_no=out_port_no, comment='ignoring flow') return device_rules assert len(route) == 2 ingress_hop, egress_hop = route def is_downstream(): return ingress_hop.device.root def is_upstream(): return not is_downstream() if out_port_no is not None and \ (out_port_no & 0x7fffffff) == ofp.OFPP_CONTROLLER: # UPSTREAM CONTROLLER-BOUND FLOW # we assume that the ingress device is already pushing a # customer-specific vlan (c-vid), based on its default flow # rules so there is nothing else to do on the ONU # on the olt, we need to push a new tag and set it to 4000 # which for now represents in-bound channel to the controller # (via Voltha) # TODO make the 4000 configurable fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) # in_port_no is None for wildcard input case, do not include # upstream port for 4000 flow in input if in_port_no is None: in_ports = self.get_wildcard_input_ports(exclude_port= egress_hop.egress_port.port_no) else: in_ports = [in_port_no] for input_port in in_ports: fl_lst.append(mk_flow_stat( # Upstream flow priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), vlan_vid(ofp.OFPVID_PRESENT | input_port) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID) ], actions=[ push_vlan(0x8100), set_field(vlan_vid(ofp.OFPVID_PRESENT | 4000)), output(egress_hop.egress_port.port_no)] )) fl_lst.append(mk_flow_stat( # Downstream flow priority=flow.priority, match_fields=[ in_port(egress_hop.egress_port.port_no), vlan_vid(ofp.OFPVID_PRESENT | 4000), vlan_pcp(0), metadata(input_port) ], actions=[ pop_vlan(), output(egress_hop.ingress_port.port_no)] )) else: # NOT A CONTROLLER-BOUND FLOW if is_upstream(): # We assume that anything that is upstream needs to get Q-in-Q # treatment and that this is expressed via two flow rules, # the first using the goto-statement. We also assume that the # inner tag is applied at the ONU, while the outer tag is # applied at the OLT if has_next_table(flow): assert out_port_no is None fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) ] + [ output(ingress_hop.egress_port.port_no) ] )) else: assert out_port_no is not None fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no), ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, ) ], actions=[ action for action in get_actions(flow) if action.type != OUTPUT ] + [ output(egress_hop.egress_port.port_no) ] )) else: # downstream if has_next_table(flow): assert out_port_no is None # For downstream flows with dual-tags, recalculate route with # inner-tag as logical output port. Otherwise PON-0 is always # selected. inner_tag = get_metadata(flow) if inner_tag is not None: route = self.get_route(in_port_no, inner_tag) if route is None: log.error('no-route-double-tag', in_port_no=in_port_no, out_port_no=inner_tag, comment='ignoring flow') return device_rules assert len(route) == 2 ingress_hop, egress_hop = route fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) ] + [ output(ingress_hop.egress_port.port_no) ] )) elif out_port_no is not None: # unicast case fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) if action.type not in (OUTPUT,) ] + [ output(egress_hop.egress_port.port_no) ] )) else: # multicast case grp_id = get_group(flow) assert grp_id is not None fl_lst, _ = device_rules.setdefault( ingress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(ingress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT,) ], actions=[ action for action in get_actions(flow) if action.type not in (GROUP,) ] + [ pop_vlan(), output(ingress_hop.egress_port.port_no) ] )) # having no group yet is the same as having a group with # no buckets group = group_map.get(grp_id, ofp.ofp_group_entry()) for bucket in group.desc.buckets: found_pop_vlan = False other_actions = [] for action in bucket.actions: if action.type == POP_VLAN: found_pop_vlan = True elif action.type == OUTPUT: out_port_no = action.output.port else: other_actions.append(action) # re-run route request to determine egress device and # ports route2 = self.get_route(in_port_no, out_port_no) if route2 is None: log.error('mc-no-route', in_port_no=in_port_no, out_port_no=out_port_no, route2=route2, comment='ignoring flow') continue assert len(route2) == 2 ingress_hop2, egress_hop = route2 assert ingress_hop.ingress_port == ingress_hop2.ingress_port fl_lst, _ = device_rules.setdefault( egress_hop.device.id, ([], [])) fl_lst.append(mk_flow_stat( priority=flow.priority, cookie=flow.cookie, match_fields=[ in_port(egress_hop.ingress_port.port_no) ] + [ field for field in get_ofb_fields(flow) if field.type not in (IN_PORT, VLAN_VID, VLAN_PCP) ], actions=other_actions + [ output(egress_hop.egress_port.port_no) ] )) return device_rules