Adds hooks for applying ovs flows when vifs are created and destroyed for XenServer instances.
This commit is contained in:
commit
8185703771
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
#DEVICE_BRIDGES="eth0:xenbr0 eth1:xenbr1"
|
|
@ -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"
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue