"""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,
)
]