summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCory Wright <cory.wright@rackspace.com>2011-06-01 07:26:40 +0000
committerTarmac <>2011-06-01 07:26:40 +0000
commit81857037710d2bab23d151184912c194edf9018d (patch)
tree8469baf89289287ba33c42db5cfba31c0fbf5513
parenta8113ae0dcc15171d138f6333203d0d16a24c8ea (diff)
parent2bd6e5561339a6755709461dab9aa6cad4a1cf81 (diff)
downloadnova-diablo-1.tar.gz
Adds hooks for applying ovs flows when vifs are created and destroyed for XenServer instances.diablo-1
-rwxr-xr-xplugins/xenserver/networking/etc/init.d/openvswitch-nova96
-rw-r--r--plugins/xenserver/networking/etc/sysconfig/openvswitch-nova1
-rw-r--r--plugins/xenserver/networking/etc/udev/rules.d/xen-openvswitch-nova.rules3
-rw-r--r--plugins/xenserver/networking/etc/xensource/scripts/novalib.py40
-rwxr-xr-xplugins/xenserver/networking/etc/xensource/scripts/ovs_configure_base_flows.py62
-rwxr-xr-xplugins/xenserver/networking/etc/xensource/scripts/ovs_configure_vif_flows.py180
-rwxr-xr-xplugins/xenserver/networking/etc/xensource/scripts/vif_rules.py30
7 files changed, 393 insertions, 19 deletions
diff --git a/plugins/xenserver/networking/etc/init.d/openvswitch-nova b/plugins/xenserver/networking/etc/init.d/openvswitch-nova
new file mode 100755
index 0000000000..8672a69b88
--- /dev/null
+++ b/plugins/xenserver/networking/etc/init.d/openvswitch-nova
@@ -0,0 +1,96 @@
+#!/bin/bash
+#
+# openvswitch-nova
+#
+# chkconfig: 2345 96 89
+# description: Apply initial OVS flows for Nova
+
+# Copyright 2011 OpenStack LLC.
+# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# 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.
+
+# source function library
+if [ -f /etc/init.d/functions ]; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ]; then
+ . /etc/rc.d/init.d/functions
+elif [ -f /lib/lsb/init-functions ]; then
+ . /lib/lsb/init-functions
+else
+ echo "$0: missing LSB shell function library" >&2
+ exit 1
+fi
+
+OVS_CONFIGURE_BASE_FLOWS=/etc/xensource/scripts/ovs_configure_base_flows.py
+
+if test -e /etc/sysconfig/openvswitch-nova; then
+ . /etc/sysconfig/openvswitch-nova
+else
+ echo "$0: missing configuration file: /etc/sysconfig/openvswitch-nova"
+ exit 1
+fi
+
+if test -e /etc/xensource/network.conf; then
+ NETWORK_MODE=$(cat /etc/xensource/network.conf)
+fi
+
+case ${NETWORK_MODE:=openvswitch} in
+ vswitch|openvswitch)
+ ;;
+ bridge)
+ exit 0
+ ;;
+ *)
+ echo "Open vSwitch disabled (/etc/xensource/network.conf is invalid)" >&2
+ exit 0
+ ;;
+esac
+
+function run_ovs_conf_base_flows {
+ # expected format: DEVICE_BRIDGES="eth0:xenbr0 eth1:xenbr1"
+ for pair in $DEVICE_BRIDGES; do
+ # below in $info, physical device is [0], bridge name is [1]
+ info=${pair//:/ }
+ /usr/bin/python $OVS_CONFIGURE_BASE_FLOWS $1 ${info[0]} ${info[1]}
+ done
+}
+
+function start {
+ run_ovs_conf_base_flows online
+}
+
+function stop {
+ run_ovs_conf_base_flows offline
+}
+
+function restart {
+ run_ovs_conf_base_flows reset
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ *)
+ echo "usage: openvswitch-nova [start|stop|restart]"
+ exit 1
+ ;;
+esac
diff --git a/plugins/xenserver/networking/etc/sysconfig/openvswitch-nova b/plugins/xenserver/networking/etc/sysconfig/openvswitch-nova
new file mode 100644
index 0000000000..829782fb60
--- /dev/null
+++ b/plugins/xenserver/networking/etc/sysconfig/openvswitch-nova
@@ -0,0 +1 @@
+#DEVICE_BRIDGES="eth0:xenbr0 eth1:xenbr1"
diff --git a/plugins/xenserver/networking/etc/udev/rules.d/xen-openvswitch-nova.rules b/plugins/xenserver/networking/etc/udev/rules.d/xen-openvswitch-nova.rules
new file mode 100644
index 0000000000..b179f0847a
--- /dev/null
+++ b/plugins/xenserver/networking/etc/udev/rules.d/xen-openvswitch-nova.rules
@@ -0,0 +1,3 @@
+SUBSYSTEM=="xen-backend", KERNEL=="vif*", RUN+="/etc/xensource/scripts/ovs_configure_vif_flows.py $env{ACTION} %k all"
+# is this one needed?
+#SUBSYSTEM=="net", KERNEL=="tap*", RUN+="/etc/xensource/scripts/ovs_configure_vif_flows.py $env{ACTION} %k all"
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/novalib.py b/plugins/xenserver/networking/etc/xensource/scripts/novalib.py
new file mode 100644
index 0000000000..dcbee3dedb
--- /dev/null
+++ b/plugins/xenserver/networking/etc/xensource/scripts/novalib.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+
+
+import os
+import subprocess
+
+
+def execute_get_output(*command):
+ """Execute and return stdout"""
+ devnull = open(os.devnull, 'w')
+ command = map(str, command)
+ proc = subprocess.Popen(command, close_fds=True,
+ stdout=subprocess.PIPE, stderr=devnull)
+ devnull.close()
+ return proc.stdout.read().strip()
+
+
+def execute(*command):
+ """Execute without returning stdout"""
+ devnull = open(os.devnull, 'w')
+ command = map(str, command)
+ proc = subprocess.Popen(command, close_fds=True,
+ stdout=subprocess.PIPE, stderr=devnull)
+ devnull.close()
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/ovs_configure_base_flows.py b/plugins/xenserver/networking/etc/xensource/scripts/ovs_configure_base_flows.py
new file mode 100755
index 0000000000..514a43a2df
--- /dev/null
+++ b/plugins/xenserver/networking/etc/xensource/scripts/ovs_configure_base_flows.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+
+"""
+This script is used to configure base openvswitch flows for XenServer hosts.
+"""
+
+import os
+import sys
+
+
+from novalib import execute, execute_get_output
+
+
+def main(command, phys_dev_name, bridge_name):
+ ovs_ofctl = lambda *rule: execute('/usr/bin/ovs-ofctl', *rule)
+
+ # always clear all flows first
+ ovs_ofctl('del-flows', bridge_name)
+
+ if command in ('online', 'reset'):
+ pnic_ofport = execute_get_output('/usr/bin/ovs-vsctl', 'get',
+ 'Interface', phys_dev_name, 'ofport')
+
+ # these flows are lower priority than all VM-specific flows.
+
+ # allow all traffic from the physical NIC, as it is trusted (i.e.,
+ # from a filtered vif, or from the physical infrastructure)
+ ovs_ofctl('add-flow', bridge_name,
+ "priority=2,in_port=%s,actions=normal" % pnic_ofport)
+
+ # default drop
+ ovs_ofctl('add-flow', bridge_name, 'priority=1,actions=drop')
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 4 or sys.argv[1] not in ('online', 'offline', 'reset'):
+ print sys.argv
+ script_name = os.path.basename(sys.argv[0])
+ print "This script configures base ovs flows."
+ print "usage: %s [online|offline|reset] phys-dev-name bridge-name" \
+ % script_name
+ print " ex: %s online eth0 xenbr0" % script_name
+ sys.exit(1)
+ else:
+ command, phys_dev_name, bridge_name = sys.argv[1:4]
+ main(command, phys_dev_name, bridge_name)
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/ovs_configure_vif_flows.py b/plugins/xenserver/networking/etc/xensource/scripts/ovs_configure_vif_flows.py
new file mode 100755
index 0000000000..accd08b916
--- /dev/null
+++ b/plugins/xenserver/networking/etc/xensource/scripts/ovs_configure_vif_flows.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+
+"""
+This script is used to configure openvswitch flows on XenServer hosts.
+"""
+
+import os
+import sys
+
+# This is written to Python 2.4, since that is what is available on XenServer
+import netaddr
+import simplejson as json
+
+from novalib import execute, execute_get_output
+
+
+OVS_OFCTL = '/usr/bin/ovs-ofctl'
+
+
+class OvsFlow(object):
+ def __init__(self, bridge, params):
+ self.bridge = bridge
+ self.params = params
+
+ def add(self, rule):
+ execute(OVS_OFCTL, 'add-flow', self.bridge, rule % self.params)
+
+ def clear_flows(self, ofport):
+ execute(OVS_OFCTL, 'del-flows', self.bridge, "in_port=%s" % ofport)
+
+
+def main(command, vif_raw, net_type):
+ if command not in ('online', 'offline'):
+ return
+
+ vif_name, dom_id, vif_index = vif_raw.split('-')
+ vif = "%s%s.%s" % (vif_name, dom_id, vif_index)
+ bridge = "xenbr%s" % vif_index
+
+ xsls = execute_get_output('/usr/bin/xenstore-ls',
+ '/local/domain/%s/vm-data/networking' % dom_id)
+ macs = [line.split("=")[0].strip() for line in xsls.splitlines()]
+
+ for mac in macs:
+ xsread = execute_get_output('/usr/bin/xenstore-read',
+ '/local/domain/%s/vm-data/networking/%s' %
+ (dom_id, mac))
+ data = json.loads(xsread)
+ if data["label"] == "public":
+ this_vif = "vif%s.0" % dom_id
+ else:
+ this_vif = "vif%s.1" % dom_id
+
+ if vif == this_vif:
+ vif_ofport = execute_get_output('/usr/bin/ovs-vsctl', 'get',
+ 'Interface', vif, 'ofport')
+
+ params = dict(VIF_NAME=vif,
+ MAC=data['mac'],
+ OF_PORT=vif_ofport)
+
+ ovs = OvsFlow(bridge, params)
+
+ if command == 'offline':
+ # I haven't found a way to clear only IPv4 or IPv6 rules.
+ ovs.clear_flows(vif_ofport)
+
+ if command == 'online':
+ if net_type in ('ipv4', 'all') and 'ips' in data:
+ for ip4 in data['ips']:
+ ovs.params.update({'IPV4_ADDR': ip4['ip']})
+ apply_ovs_ipv4_flows(ovs, bridge, params)
+ if net_type in ('ipv6', 'all') and 'ip6s' in data:
+ for ip6 in data['ip6s']:
+ link_local = str(netaddr.EUI(data['mac']).eui64()\
+ .ipv6_link_local())
+ ovs.params.update({'IPV6_LINK_LOCAL_ADDR': link_local})
+ ovs.params.update({'IPV6_GLOBAL_ADDR': ip6['ip']})
+ apply_ovs_ipv6_flows(ovs, bridge, params)
+
+
+def apply_ovs_ipv4_flows(ovs, bridge, params):
+ # allow valid ARP outbound (both request / reply)
+ ovs.add("priority=3,in_port=%(OF_PORT)s,dl_src=%(MAC)s,arp,"
+ "arp_sha=%(MAC)s,nw_src=%(IPV4_ADDR)s,actions=normal")
+
+ ovs.add("priority=3,in_port=%(OF_PORT)s,dl_src=%(MAC)s,arp,"
+ "arp_sha=%(MAC)s,nw_src=0.0.0.0,actions=normal")
+
+ # allow valid IPv4 outbound
+ ovs.add("priority=3,in_port=%(OF_PORT)s,dl_src=%(MAC)s,ip,"
+ "nw_src=%(IPV4_ADDR)s,actions=normal")
+
+
+def apply_ovs_ipv6_flows(ovs, bridge, params):
+ # allow valid IPv6 ND outbound (are both global and local IPs needed?)
+ # Neighbor Solicitation
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,icmp_type=135,nd_sll=%(MAC)s,"
+ "actions=normal")
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,icmp_type=135,actions=normal")
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,icmp_type=135,nd_sll=%(MAC)s,"
+ "actions=normal")
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,icmp_type=135,actions=normal")
+
+ # Neighbor Advertisement
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,icmp_type=136,"
+ "nd_target=%(IPV6_LINK_LOCAL_ADDR)s,actions=normal")
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,icmp_type=136,actions=normal")
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,icmp_type=136,"
+ "nd_target=%(IPV6_GLOBAL_ADDR)s,actions=normal")
+ ovs.add("priority=6,in_port=%(OF_PORT)s,dl_src=%(MAC)s,icmp6,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,icmp_type=136,actions=normal")
+
+ # drop all other neighbor discovery (req b/c we permit all icmp6 below)
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=135,actions=drop")
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=136,actions=drop")
+
+ # do not allow sending specifc ICMPv6 types
+ # Router Advertisement
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=134,actions=drop")
+ # Redirect Gateway
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=137,actions=drop")
+ # Mobile Prefix Solicitation
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=146,actions=drop")
+ # Mobile Prefix Advertisement
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=147,actions=drop")
+ # Multicast Router Advertisement
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=151,actions=drop")
+ # Multicast Router Solicitation
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=152,actions=drop")
+ # Multicast Router Termination
+ ovs.add("priority=5,in_port=%(OF_PORT)s,icmp6,icmp_type=153,actions=drop")
+
+ # allow valid IPv6 outbound, by type
+ ovs.add("priority=4,in_port=%(OF_PORT)s,dl_src=%(MAC)s,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,icmp6,actions=normal")
+ ovs.add("priority=4,in_port=%(OF_PORT)s,dl_src=%(MAC)s,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,icmp6,actions=normal")
+ ovs.add("priority=4,in_port=%(OF_PORT)s,dl_src=%(MAC)s,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,tcp6,actions=normal")
+ ovs.add("priority=4,in_port=%(OF_PORT)s,dl_src=%(MAC)s,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,tcp6,actions=normal")
+ ovs.add("priority=4,in_port=%(OF_PORT)s,dl_src=%(MAC)s,"
+ "ipv6_src=%(IPV6_GLOBAL_ADDR)s,udp6,actions=normal")
+ ovs.add("priority=4,in_port=%(OF_PORT)s,dl_src=%(MAC)s,"
+ "ipv6_src=%(IPV6_LINK_LOCAL_ADDR)s,udp6,actions=normal")
+ # all else will be dropped ...
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 4:
+ print "usage: %s [online|offline] vif-domid-idx [ipv4|ipv6|all] " % \
+ os.path.basename(sys.argv[0])
+ sys.exit(1)
+ else:
+ command, vif_raw, net_type = sys.argv[1:4]
+ main(command, vif_raw, net_type)
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
index 48122e6d64..662def205f 100755
--- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
+++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# Copyright 2010 OpenStack LLC.
+# Copyright 2010-2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -29,15 +29,18 @@ import sys
import simplejson as json
+from novalib import execute, execute_get_output
+
+
def main(dom_id, command, only_this_vif=None):
- xsls = execute('/usr/bin/xenstore-ls',
- '/local/domain/%s/vm-data/networking' % dom_id, True)
+ xsls = execute_get_output('/usr/bin/xenstore-ls',
+ '/local/domain/%s/vm-data/networking' % dom_id)
macs = [line.split("=")[0].strip() for line in xsls.splitlines()]
for mac in macs:
- xsread = execute('/usr/bin/enstore-read',
- '/local/domain/%s/vm-data/networking/%s' %
- (dom_id, mac), True)
+ xsread = execute_get_output('/usr/bin/xenstore-read',
+ '/local/domain/%s/vm-data/networking/%s' %
+ (dom_id, mac))
data = json.loads(xsread)
for ip in data['ips']:
if data["label"] == "public":
@@ -52,17 +55,6 @@ def main(dom_id, command, only_this_vif=None):
apply_iptables_rules(command, params)
-def execute(*command, return_stdout=False):
- devnull = open(os.devnull, 'w')
- command = map(str, command)
- proc = subprocess.Popen(command, close_fds=True,
- stdout=subprocess.PIPE, stderr=devnull)
- devnull.close()
- if return_stdout:
- return proc.stdout.read()
- else:
- return None
-
# A note about adding rules:
# Whenever we add any rule to iptables, arptables or ebtables we first
# delete the same rule to ensure the rule only exists once.
@@ -113,8 +105,8 @@ def apply_ebtables_rules(command, params):
ebtables('-D', 'FORWARD', '-p', '0806', '-o', params['VIF'],
'--arp-ip-dst', params['IP'],
'-j', 'ACCEPT')
- ebtables('-D', 'FORWARD', '-p', '0800', '-o',
- params['VIF'], '--ip-dst', params['IP'],
+ ebtables('-D', 'FORWARD', '-p', '0800', '-o', params['VIF'],
+ '--ip-dst', params['IP'],
'-j', 'ACCEPT')
if command == 'online':
ebtables('-A', 'FORWARD', '-p', '0806',