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 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 def _accept_to_table(table, actions): inst = [table.goto_this()] if actions is not None: inst.append(valve_of.apply_actions(actions)) return inst
[docs] 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] 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] 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] 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 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)) # antispoof for FAUCET's MAC address # TODO: antispoof for controller IPs on this VLAN, too. if self.dp.drop_spoofed_faucet_mac: if self.dp.stack_ports: vlan_macs = {vlan.faucet_mac for vlan in self.dp.vlans.values()} for port in self.dp.ports.values(): if not port.stack: for mac in vlan_macs: ofmsgs.extend(self.filter_packets( {'eth_src': mac, 'in_port': port.number})) else: for vlan in list(self.dp.vlans.values()): ofmsgs.extend(self.filter_packets( {'eth_src': vlan.faucet_mac})) 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(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=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)]