Source code for faucet.valve_pipeline

"""Manages movement of packets through the faucet pipeline."""

# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2015 Brad Cowie, Christopher Lorier and Joe Stringer.
# Copyright (C) 2015 Research and Education Advanced Network New Zealand Ltd.
# Copyright (C) 2015--2019 The Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import copy
import functools
import faucet.faucet_metadata as faucet_md
from faucet import valve_of
from faucet.valve_manager_base import ValveManagerBase


[docs] class ValvePipeline(ValveManagerBase): """Responsible for maintaing the integrity of the Faucet pipeline for a single valve. Controls what packets a module sees in its tables and how it can pass packets through the pipeline. Responsible for installing flows in the vlan, egress and classification tables""" def __init__(self, dp): self.dp = dp self.vlan_table = dp.tables["vlan"] self.classification_table = dp.classification_table() self.output_table = dp.output_table() self.egress_table = None self.egress_acl_table = None if dp.egress_pipeline: self.egress_table = dp.tables["egress"] self.egress_acl_table = dp.tables.get("egress_acl") self.filter_priority = self._FILTER_PRIORITY self.select_priority = self._HIGH_PRIORITY @staticmethod @functools.lru_cache(maxsize=1024) def _accept_to_table(table, actions): inst = [table.goto_this()] if actions: inst.append(valve_of.apply_actions(actions)) return tuple(inst)
[docs] @functools.lru_cache(maxsize=1024) def accept_to_vlan(self, actions=None): """Get instructions to forward packet through the pipeline to vlan table. args: actions: (optional) list of actions to apply to packet. returns: list of instructions """ return self._accept_to_table(self.vlan_table, actions)
[docs] @functools.lru_cache(maxsize=1024) def accept_to_classification(self, actions=None): """Get instructions to forward packet through the pipeline to classification table. args: actions: (optional) list of actions to apply to packet. returns: list of instructions """ return self._accept_to_table(self.classification_table, actions)
[docs] @functools.lru_cache(maxsize=1024) def accept_to_l2_forwarding(self, actions=None): """Get instructions to forward packet through the pipeline to l2 forwarding. args: actions: (optional) list of actions to apply to packet. returns: list of instructions """ return self._accept_to_table(self.output_table, actions)
[docs] @functools.lru_cache(maxsize=1024) def accept_to_egress(self, actions=None): """Get instructions to forward packet through the pipeline to egress table Raises an assertion error if egress pipeline is not configured args: actions: (optional) list of actions to apply to the packet returns: list of instructions: """ assert self.egress_table is not None return self._accept_to_table(self.egress_table, actions)
[docs] def output(self, port, vlan, hairpin=False, external_forwarding_requested=None): """Get instructions list to output a packet through the regular pipeline. args: port: Port object of port to output packet to vlan: Vlan object of vlan to output packet on hairpin: if True, hairpinning is required apply_egress_acl: if True the packet will be sent to the egress acl table before being output returns: list of Instructions """ instructions = [] if self.egress_table: metadata, metadata_mask = faucet_md.get_egress_metadata( port.number, vlan.vid ) if self.egress_acl_table: instructions.extend( valve_of.metadata_goto_table( metadata, metadata_mask, self.egress_acl_table ) ) else: instructions.extend( valve_of.metadata_goto_table( metadata, metadata_mask, self.egress_table ) ) else: instructions.append( valve_of.apply_actions( vlan.output_port( port, hairpin=hairpin, output_table=self.output_table, external_forwarding_requested=external_forwarding_requested, ) ) ) return tuple(instructions)
[docs] def initialise_tables(self): """Install rules to initialise the classification_table""" ofmsgs = [] # drop broadcast sources if self.dp.drop_broadcast_source_address: ofmsgs.extend(self.filter_packets({"eth_src": valve_of.mac.BROADCAST_STR})) ofmsgs.extend( self.filter_packets( {"eth_type": valve_of.ECTP_ETH_TYPE}, priority_offset=10 ) ) return ofmsgs
def _add_egress_table_rule(self, port, vlan, pop_vlan=True): metadata, metadata_mask = faucet_md.get_egress_metadata(port.number, vlan.vid) actions = copy.copy(port.mirror_actions()) if pop_vlan: actions.append(valve_of.pop_vlan()) actions.append(valve_of.output_port(port.number)) inst = (valve_of.apply_actions(tuple(actions)),) return self.egress_table.flowmod( self.egress_table.match( vlan=vlan, metadata=metadata, metadata_mask=metadata_mask ), priority=self.dp.high_priority, inst=inst, )
[docs] def add_port(self, port): ofmsgs = [] if self.egress_table is None: return ofmsgs for vlan in port.tagged_vlans: ofmsgs.append(self._add_egress_table_rule(port, vlan, pop_vlan=False)) if port.native_vlan is not None: ofmsgs.append(self._add_egress_table_rule(port, port.native_vlan)) return ofmsgs
[docs] def del_port(self, port): ofmsgs = [] if self.egress_table: mask = faucet_md.PORT_METADATA_MASK ofmsgs.append( self.egress_table.flowdel( self.egress_table.match( metadata=port.number & mask, metadata_mask=mask ) ) ) return ofmsgs
[docs] def filter_packets(self, match_dict, priority_offset=0): """get a list of flow modification messages to filter packets from the pipeline. args: match_dict: a dictionary specifying the match fields priority_offset: used to prevent overlapping entries """ return [ self.classification_table.flowdrop( self.classification_table.match(**match_dict), priority=self.filter_priority + priority_offset, ) ]
[docs] def select_packets(self, target_table, match_dict, actions=None, priority_offset=0): """retrieve rules to redirect packets matching match_dict to table""" inst = [target_table.goto_this()] if actions is not None: inst.append(valve_of.apply_actions(actions)) return [ self.classification_table.flowmod( self.classification_table.match(**match_dict), priority=self.select_priority + priority_offset, inst=tuple(inst), ) ]
[docs] def remove_filter(self, match_dict, strict=True, priority_offset=0): """retrieve flow mods to remove a filter from the classification table""" priority = None if strict: priority = self.filter_priority + priority_offset return [ self.classification_table.flowdel( self.classification_table.match(**match_dict), priority=priority, strict=strict, ) ]