neutron/neutron/plugins/ofagent/agent/flows.py

408 lines
15 KiB
Python

# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# 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.
"""
OpenFlow1.3 flow table for OFAgent
* requirements
** plain OpenFlow 1.3. no vendor extensions.
* legends
xxx: network id (agent internal use)
yyy: segment id (vlan id, gre key, ...)
a,b,c: tunnel port (tun_ofports, map[net_id].tun_ofports)
i,j,k: vm port (map[net_id].vif_ports[vif_id].ofport)
x,y,z: physical port (int_ofports)
N: tunnel type (0 for TYPE_GRE, 1 for TYPE_xxx, ...)
iii: unknown ip address
uuu: unicast l2 address
* tables (in order)
CHECK_IN_PORT
TUNNEL_IN+N
PHYS_IN
LOCAL_IN
ARP_PASSTHROUGH
ARP_RESPONDER
TUNNEL_OUT
LOCAL_OUT
PHYS_OUT
TUNNEL_FLOOD+N
PHYS_FLOOD
LOCAL_FLOOD
* CHECK_IN_PORT
for each vm ports:
// check_in_port_add_local_port, check_in_port_delete_port
in_port=i, write_metadata(LOCAL|xxx),goto(LOCAL_IN)
TYPE_GRE
for each tunnel ports:
// check_in_port_add_tunnel_port, check_in_port_delete_port
in_port=a, goto(TUNNEL_IN+N)
TYPE_VLAN
for each networks ports:
// provision_tenant_physnet, reclaim_tenant_physnet
in_port=x,vlan_vid=present|yyy, write_metadata(xxx),goto(PHYS_IN)
TYPE_FLAT
// provision_tenant_physnet, reclaim_tenant_physnet
in_port=x, write_metadata(xxx),goto(PHYS_IN)
default drop
* TUNNEL_IN+N (per tunnel types) tunnel -> network
for each networks:
// provision_tenant_tunnel, reclaim_tenant_tunnel
tun_id=yyy, write_metadata(xxx),goto(TUNNEL_OUT)
default drop
* PHYS_IN
default goto(TUNNEL_OUT)
* LOCAL_IN
default goto(next_table)
* ARP_PASSTHROUGH
for each unknown tpa:
// arp_passthrough
arp,arp_op=request,metadata=xxx,tpa=iii, idle_timeout=5, goto(TUNNEL_OUT)
default goto(next_table)
* ARP_RESPONDER
arp,arp_op=request, output:controller
default goto(next_table)
* TUNNEL_OUT
TYPE_GRE
// !FLOODING_ENTRY
// install_tunnel_output, delete_tunnel_output
metadata=LOCAL|xxx,eth_dst=uuu set_tunnel(yyy),output:a
default goto(next table)
* LOCAL_OUT
for each known destinations:
// local_out_add_port, local_out_delete_port
metadata=xxx,eth_dst=uuu output:i
default goto(next table)
* PHYS_OUT
NOTE(yamamoto): currently this table is always empty.
default goto(next table)
* TUNNEL_FLOOD+N. (per tunnel types)
network -> tunnel/vlan
output to tunnel/physical ports
"next table" might be LOCAL_OUT
TYPE_GRE
for each networks:
// FLOODING_ENTRY
// install_tunnel_output, delete_tunnel_output
metadata=LOCAL|xxx, set_tunnel(yyy),output:a,b,c,goto(next table)
default goto(next table)
* PHYS_FLOOD
TYPE_VLAN
for each networks:
// provision_tenant_physnet, reclaim_tenant_physnet
metadata=LOCAL|xxx, push_vlan:0x8100,set_field:present|yyy->vlan_vid,
output:x,pop_vlan,goto(next table)
TYPE_FLAT
for each networks:
// provision_tenant_physnet, reclaim_tenant_physnet
metadata=LOCAL|xxx, output:x,goto(next table)
default goto(next table)
* LOCAL_FLOOD
for each networks:
// local_flood_update, local_flood_delete
metadata=xxx, output:i,j,k
or
metadata=xxx,eth_dst=broadcast, output:i,j,k
default drop
* references
** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic
*** we use metadata instead of "internal" VLANs
*** we don't want to use NX learn action
"""
from ryu.lib.packet import arp
from ryu.ofproto import ether
from neutron.plugins.common import constants as p_const
import neutron.plugins.ofagent.agent.metadata as meta
from neutron.plugins.ofagent.agent import ofswitch
from neutron.plugins.ofagent.agent import tables
class OFAgentIntegrationBridge(ofswitch.OpenFlowSwitch):
"""ofagent br-int specific logic."""
def setup_default_table(self):
self.delete_flows()
self.install_default_drop(tables.CHECK_IN_PORT)
for t in tables.TUNNEL_IN.values():
self.install_default_drop(t)
self.install_default_goto(tables.PHYS_IN, tables.TUNNEL_OUT)
self.install_default_goto_next(tables.LOCAL_IN)
self.install_default_goto_next(tables.ARP_PASSTHROUGH)
self.install_arp_responder(tables.ARP_RESPONDER)
self.install_default_goto_next(tables.TUNNEL_OUT)
self.install_default_goto_next(tables.LOCAL_OUT)
self.install_default_goto_next(tables.PHYS_OUT)
for t in tables.TUNNEL_FLOOD.values():
self.install_default_goto_next(t)
self.install_default_goto_next(tables.PHYS_FLOOD)
self.install_default_drop(tables.LOCAL_FLOOD)
def install_arp_responder(self, table_id):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
arp_op=arp.ARP_REQUEST)
actions = [ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
msg = ofpp.OFPFlowMod(dp,
table_id=table_id,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
self.install_default_goto_next(table_id)
def install_tunnel_output(self, table_id,
network, segmentation_id,
ports, goto_next, **additional_matches):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network, meta.LOCAL),
**additional_matches)
actions = [ofpp.OFPActionSetField(tunnel_id=segmentation_id)]
actions += [ofpp.OFPActionOutput(port=p) for p in ports]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
if goto_next:
instructions += [
ofpp.OFPInstructionGotoTable(table_id=table_id + 1),
]
msg = ofpp.OFPFlowMod(dp,
table_id=table_id,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def delete_tunnel_output(self, table_id,
network, **additional_matches):
(dp, _ofp, ofpp) = self._get_dp()
self.delete_flows(table_id=table_id,
metadata=meta.mk_metadata(network, meta.LOCAL),
**additional_matches)
def provision_tenant_tunnel(self, network_type, network, segmentation_id):
(dp, _ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(tunnel_id=segmentation_id)
metadata = meta.mk_metadata(network)
instructions = [
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
metadata_mask=metadata[1]),
ofpp.OFPInstructionGotoTable(table_id=tables.TUNNEL_OUT),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.TUNNEL_IN[network_type],
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def reclaim_tenant_tunnel(self, network_type, network, segmentation_id):
table_id = tables.TUNNEL_IN[network_type]
self.delete_flows(table_id=table_id, tunnel_id=segmentation_id)
def provision_tenant_physnet(self, network_type, network,
segmentation_id, phys_port):
"""for vlan and flat."""
assert(network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT])
(dp, ofp, ofpp) = self._get_dp()
# inbound
metadata = meta.mk_metadata(network)
instructions = [
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
metadata_mask=metadata[1])
]
if network_type == p_const.TYPE_VLAN:
vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
match = ofpp.OFPMatch(in_port=phys_port, vlan_vid=vlan_vid)
actions = [ofpp.OFPActionPopVlan()]
instructions += [ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS, actions)]
else:
match = ofpp.OFPMatch(in_port=phys_port)
instructions += [ofpp.OFPInstructionGotoTable(table_id=tables.PHYS_IN)]
msg = ofpp.OFPFlowMod(dp,
priority=1,
table_id=tables.CHECK_IN_PORT,
match=match,
instructions=instructions)
self._send_msg(msg)
# outbound
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network, meta.LOCAL))
if network_type == p_const.TYPE_VLAN:
actions = [
ofpp.OFPActionPushVlan(),
ofpp.OFPActionSetField(vlan_vid=vlan_vid),
]
else:
actions = []
actions += [ofpp.OFPActionOutput(port=phys_port)]
if network_type == p_const.TYPE_VLAN:
actions += [ofpp.OFPActionPopVlan()]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
ofpp.OFPInstructionGotoTable(table_id=tables.PHYS_FLOOD + 1),
]
msg = ofpp.OFPFlowMod(dp,
priority=1,
table_id=tables.PHYS_FLOOD,
match=match,
instructions=instructions)
self._send_msg(msg)
def reclaim_tenant_physnet(self, network_type, network,
segmentation_id, phys_port):
(_dp, ofp, _ofpp) = self._get_dp()
vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
if network_type == p_const.TYPE_VLAN:
self.delete_flows(table_id=tables.CHECK_IN_PORT,
in_port=phys_port, vlan_vid=vlan_vid)
else:
self.delete_flows(table_id=tables.CHECK_IN_PORT,
in_port=phys_port)
self.delete_flows(table_id=tables.PHYS_FLOOD,
metadata=meta.mk_metadata(network))
def check_in_port_add_tunnel_port(self, network_type, port):
(dp, _ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(in_port=port)
instructions = [
ofpp.OFPInstructionGotoTable(
table_id=tables.TUNNEL_IN[network_type])
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.CHECK_IN_PORT,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def check_in_port_add_local_port(self, network, port):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(in_port=port)
metadata = meta.mk_metadata(network, meta.LOCAL)
instructions = [
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
metadata_mask=metadata[1]),
ofpp.OFPInstructionGotoTable(table_id=tables.LOCAL_IN),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.CHECK_IN_PORT,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def check_in_port_delete_port(self, port):
self.delete_flows(table_id=tables.CHECK_IN_PORT, in_port=port)
def local_flood_update(self, network, ports, flood_unicast):
(dp, ofp, ofpp) = self._get_dp()
match_all = ofpp.OFPMatch(metadata=meta.mk_metadata(network))
match_multicast = ofpp.OFPMatch(metadata=meta.mk_metadata(network),
eth_dst=('01:00:00:00:00:00',
'01:00:00:00:00:00'))
if flood_unicast:
match_add = match_all
match_del = match_multicast
else:
match_add = match_multicast
match_del = match_all
actions = [ofpp.OFPActionOutput(port=p) for p in ports]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.LOCAL_FLOOD,
priority=1,
match=match_add,
instructions=instructions)
self._send_msg(msg)
self.delete_flows(table_id=tables.LOCAL_FLOOD, strict=True,
priority=1, match=match_del)
def local_flood_delete(self, network):
self.delete_flows(table_id=tables.LOCAL_FLOOD,
metadata=meta.mk_metadata(network))
def local_out_add_port(self, network, port, mac):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network), eth_dst=mac)
actions = [ofpp.OFPActionOutput(port=port)]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.LOCAL_OUT,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def local_out_delete_port(self, network, mac):
self.delete_flows(table_id=tables.LOCAL_OUT,
metadata=meta.mk_metadata(network), eth_dst=mac)
def arp_passthrough(self, network, tpa):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network),
eth_type=ether.ETH_TYPE_ARP,
arp_op=arp.ARP_REQUEST,
arp_tpa=tpa)
instructions = [
ofpp.OFPInstructionGotoTable(table_id=tables.TUNNEL_OUT)]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.ARP_PASSTHROUGH,
priority=1,
idle_timeout=5,
match=match,
instructions=instructions)
self._send_msg(msg)