Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
0
    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
Esempio n. 6
0
    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