"""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
def _accept_to_table(self, 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:
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)]