diff options
Diffstat (limited to 'system/xen/openvswitch')
-rw-r--r-- | system/xen/openvswitch/README.openvswitch-extended | 23 | ||||
-rw-r--r-- | system/xen/openvswitch/openvswitch-clean.sh | 127 | ||||
-rw-r--r-- | system/xen/openvswitch/openvswitch.conf | 15 | ||||
-rw-r--r-- | system/xen/openvswitch/vif-openvswitch-extended | 197 |
4 files changed, 362 insertions, 0 deletions
diff --git a/system/xen/openvswitch/README.openvswitch-extended b/system/xen/openvswitch/README.openvswitch-extended new file mode 100644 index 0000000000..399a7d37ad --- /dev/null +++ b/system/xen/openvswitch/README.openvswitch-extended @@ -0,0 +1,23 @@ +vif-openvswitch-extended: This script extends vif-openvswitch features. + +The original vif-openvswitch script, which can also be found in this package, +was used as a template for this "extended" script. The main purpose here is to +add HTB rate limiting and IP/ARP address spoof prevention between domU guests. + +Until openvswitch.conf is present and configured in XEN_CONFIG_DIR path, this +script will behave just like the original one. This package uses /etc/xen config +path by default. + +To enable this script for all guests by default, set xl.conf variable like this: + + vif.default.script="vif-openvswitch-extended" + +Additionally, included is an openvswitch-clean.sh helper script; it can be used +to clean up and (re)apply configuration options found in openvswitch.conf file. +Due to the fact that this script is based on the original script, which you +might already be using, it is easy to switch over. You can start by configuring +openvswitch.conf, xl.conf and finally run helper script to set everything up. + +These scripts were written for my own use, and its possible they have some bugs +or unforeseen deficiencies on your system. In either case, feel free to write +me an email about it. diff --git a/system/xen/openvswitch/openvswitch-clean.sh b/system/xen/openvswitch/openvswitch-clean.sh new file mode 100644 index 0000000000..2d02d0ea44 --- /dev/null +++ b/system/xen/openvswitch/openvswitch-clean.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# This script applies configuration found in /etc/xen/openvswitch.conf to +# all running domains and removes orphan entries from openvswitch database. +# Written by Mario Preksavec <mario@slackware.hr> + +if [ -f /etc/xen/openvswitch.conf ]; then + declare -A rate ipv4 ipv6 + . /etc/xen/openvswitch.conf + for domid in $(xenstore-list /local/domain); do + # Skip dom0 + if [ $domid -eq 0 ]; then continue; fi + + # Take settings from config file + name=$(xenstore-read /local/domain/$domid/name) + if [ ! -z ${rate[$name]} ]; then + rate=${rate[$name]} + elif [ ! -z ${rate[::default]} ]; then + rate=${rate[::default]} + else + rate=0 + fi + + if [ ! -z ${ipv4[$name]} ]; then + ipv4=${ipv4[$name]} + elif [ ! -z ${ipv4[::default]} ]; then + ipv4=${ipv4[::default]} + fi + + if [ ! -z ${ipv6[$name]} ]; then + ipv6=${ipv6[$name]} + elif [ ! -z ${ipv6[::default]} ]; then + ipv6=${ipv6[::default]} + fi + + # Domain can have more then one vif + for vif in $(xenstore-list /local/domain/$domid/device/vif); do + dev=vif$domid.$vif + # Handle qemu device names + if [ -e /sys/class/net/${dev}-emu ]; then dev=${dev}-emu; fi + + bridge=$(xenstore-read /local/domain/0/backend/vif/$domid/$vif/bridge) + port=$(ovs-vsctl get interface $dev ofport) + + # Remove flows and qos + ovs-ofctl del-flows $bridge in_port=$port + ovs-vsctl --timeout=30 -- --if-exists clear port $dev qos + + if [ $rate -gt 0 ]; then + echo "Domain $name -- added ${rate}MB/s rate restriction to dev $dev" + policing_rate=$((rate * 1000)) + policing_burst=$((rate * 100)) + min_rate=$((rate * 1000000)) + max_rate=$((rate * 1000000)) + qos_id="@qos_$dev" + que_id="@que_$dev" + ovs-vsctl -- set interface $dev \ + ingress_policing_rate=$policing_rate \ + ingress_policing_burst=$policing_burst \ + -- set port $dev qos=$qos_id \ + -- --id=$qos_id create qos type=linux-htb \ + other-config:max-rate=$max_rate queues=0=$que_id \ + -- --id=$que_id create queue other-config:min-rate=$min_rate \ + other-config:max-rate=$max_rate >/dev/null 2>&1 + fi + + if [ ! -z "$ipv4" ] || [ ! -z "$ipv6" ]; then + mac=$(xenstore-read /local/domain/$domid/device/vif/$vif/mac) + + if [ ! -z "$ipv4" ]; then + echo "Domain $name -- added IPv4 $ipv4 restriction to dev $dev" + ovs-ofctl add-flow $bridge "in_port=$port priority=39000 \ + dl_type=0x0800 nw_src=$ipv4 dl_src=$mac idle_timeout=0 \ + action=normal" >/dev/null 2>&1 + fi + + if [ ! -z "$ipv6" ]; then + echo "Domain $name -- added IPv6 $ipv6 restriction to dev $dev" + ovs-ofctl add-flow $bridge "in_port=$port priority=39000 \ + dl_type=0x86dd ipv6_src=$ipv6 dl_src=$mac idle_timeout=0 \ + action=normal" >/dev/null 2>&1 + fi + + echo "Domain $name -- added ARP $mac restriction to dev $dev" + ovs-ofctl add-flow $bridge "in_port=$port priority=38500 \ + dl_type=0x0806 dl_src=$mac idle_timeout=0 action=normal" \ + >/dev/null 2>&1 + ovs-ofctl add-flow $bridge "in_port=$port priority=38000 \ + idle_timeout=0 action=drop" >/dev/null 2>&1 + + fi + done + done + + # Behold, the garbage collector! + for bridge in $(ovs-vsctl list-br); do + # Remove unused ports -- unexistent devices + for dev in $(ovs-vsctl list-ports $bridge); do + if [ ! -e /sys/class/net/$dev ]; then + ovs-vsctl -- del-port $bridge $dev + fi + done + # Remove unused flows -- unexistent ports + for port in $(ovs-ofctl dump-flows $bridge \ + | awk 'match($0, /in_port=([0-9]+)/, a) {print a[1]}' \ + | sort -n | uniq); do + dev=$(ovs-vsctl --bare -- --columns=name find interface ofport=$port) + if [ -z "$dev" ]; then + ovs-ofctl del-flows $bridge in_port=$port + fi + done + done + + # Remove unused qos + for qos in $(ovs-vsctl list qos | awk '/^_uuid/ {print $NF}'); do + if [ $(ovs-vsctl list port | grep -cF $qos) -eq 0 ]; then + ovs-vsctl -- destroy qos $qos + fi + done + + # Remove unused queues + for queue in $(ovs-vsctl list queue | awk '/^_uuid/ {print $NF}'); do + if [ $(ovs-vsctl list qos | grep -cF $queue) -eq 0 ]; then + ovs-vsctl -- destroy queue $queue + fi + done +fi diff --git a/system/xen/openvswitch/openvswitch.conf b/system/xen/openvswitch/openvswitch.conf new file mode 100644 index 0000000000..4d6a43e06f --- /dev/null +++ b/system/xen/openvswitch/openvswitch.conf @@ -0,0 +1,15 @@ +# This is a very simple configuration file written in bash and sourced by +# vif-openvswitch-extended script. Setting either IPv4 or IPv6 vars enables full +# ARP restriction on the interface. Things get even more restrictive when one or +# more defaults are set and applied to all guests. Rate limits are specified in +# megabytes per second and ip address can have a netmask. For individual domain +# configuration, use dom_name config as a starting point and replace it with the +# actual domain name. + +#rate[::default]="10" +#ipv4[::default]="169.254.0.0/16" +#ipv6[::default]="fe80::/64" + +#rate[dom_name]="100" +#ipv4[dom_name]="10.0.0.2" +#ipv6[dom_name]="fd00::2" diff --git a/system/xen/openvswitch/vif-openvswitch-extended b/system/xen/openvswitch/vif-openvswitch-extended new file mode 100644 index 0000000000..41a70ca906 --- /dev/null +++ b/system/xen/openvswitch/vif-openvswitch-extended @@ -0,0 +1,197 @@ +#!/bin/bash +#============================================================================ +# ${XEN_SCRIPT_DIR}/vif-openvswitch-extended +# +# Script for configuring a vif in openvswitch mode, extended to support +# HTB rate limiting and IP/ARP spoof prevention. +# Some inspiration drawn from: +# http://openvswitch.org/support/config-cookbooks/qos-rate-limiting/ +# http://openvswitch.org/pipermail/discuss/2011-May/005178.html +# Original script modified by Mario Preksavec <mario@slackware.hr> +# +# Rate limiting and antispoof config file: +# XEN_CONFIG_DIR/openvswitch.conf +# +# Usage: +# vif-openvswitch-extended (add|remove|online|offline) +# +# Environment vars: +# vif vif interface name (required). +# XENBUS_PATH path to this device's details in the XenStore (required). +# +# Read from the store: +# bridge openvswitch to add the vif to (required). +# ip list of IP networks for the vif, space-separated (optional). +# +# up: +# Enslaves the vif interface to the bridge and adds iptables rules +# for its ip addresses (if any). +# +# down: +# Removes the vif interface from the bridge and removes the iptables +# rules for its ip addresses (if any). +#============================================================================ + +dir=$(dirname "$0") +. "$dir/vif-common.sh" + +check_tools() +{ + if ! command -v ovs-vsctl > /dev/null 2>&1; then + fatal "Unable to find ovs-vsctl tool" + fi + if ! command -v ip > /dev/null 2>&1; then + fatal "Unable to find ip tool" + fi +} +openvswitch_external_id() { + local dev=$1 + local key=$2 + local value=$3 + + echo "-- set interface $dev external-ids:\"$key\"=\"$value\"" +} + +openvswitch_external_id_all() { + local dev=$1 + local frontend_id=$(xenstore_read "$XENBUS_PATH/frontend-id") + local vm_path=$(xenstore_read "/local/domain/${frontend_id}/vm") + local name=$(xenstore_read "${vm_path}/name") + openvswitch_external_id $dev "xen-vm-name" "$name" + local uuid=$(xenstore_read "${vm_path}/uuid") + openvswitch_external_id $dev "xen-vm-uuid" "$uuid" + local mac=$(xenstore_read "$XENBUS_PATH/mac") + openvswitch_external_id $dev "attached-mac" "$mac" +} + +add_to_openvswitch () { + local dev=$1 + local bridge="$(xenstore_read_default "$XENBUS_PATH/bridge" "$bridge")" + local tag trunk + + if [[ $bridge =~ ^([^.:]+)(\.([[:digit:]]+))?(:([[:digit:]]+(:[[:digit:]]+)*))?$ ]]; then + bridge="${BASH_REMATCH[1]}" + tag="${BASH_REMATCH[3]}" + trunk="${BASH_REMATCH[5]//:/,}" + else + fatal "No valid bridge was specified" + fi + + if [ $trunk ]; then + local trunk_arg="trunk=$trunk" + fi + + if [ $tag ]; then + local tag_arg="tag=$tag" + fi + + local vif_details="$(openvswitch_external_id_all $dev)" + + do_or_die ovs-vsctl --timeout=30 \ + -- --if-exists del-port $dev \ + -- add-port "$bridge" $dev $tag_arg $trunk_arg $vif_details + do_or_die ip link set $dev up + + if [ -f ${XEN_CONFIG_DIR}/openvswitch.conf ]; then + declare -A rate ipv4 ipv6 + . ${XEN_CONFIG_DIR}/openvswitch.conf + local frontend_id=$(xenstore_read "$XENBUS_PATH/frontend-id") + local name=$(xenstore_read "/local/domain/${frontend_id}/name") + + if [ ! -z ${rate[$name]} ]; then + local rate=${rate[$name]} + elif [ ! -z ${rate[::default]} ]; then + local rate=${rate[::default]} + else + local rate=0 + fi + + if [ $rate -gt 0 ]; then + local policing_rate=$((rate * 1000)) + local policing_burst=$((rate * 100)) + local min_rate=$((rate * 1000000)) + local max_rate=$((rate * 1000000)) + local qos_id="@qos_$dev" + local que_id="@que_$dev" + do_or_die ovs-vsctl -- set interface $dev \ + ingress_policing_rate=$policing_rate \ + ingress_policing_burst=$policing_burst \ + -- set port $dev qos=$qos_id \ + -- --id=$qos_id create qos type=linux-htb \ + other-config:max-rate=$max_rate queues=0=$que_id \ + -- --id=$que_id create queue other-config:min-rate=$min_rate \ + other-config:max-rate=$max_rate > /dev/null + fi + + if [ ! -z ${ipv4[$name]} ]; then + local ipv4=${ipv4[$name]} + elif [ ! -z ${ipv4[::default]} ]; then + local ipv4=${ipv4[::default]} + fi + + if [ ! -z ${ipv6[$name]} ]; then + local ipv6=${ipv6[$name]} + elif [ ! -z ${ipv6[::default]} ]; then + local ipv6=${ipv6[::default]} + fi + + if [ ! -z "$ipv4" ] || [ ! -z "$ipv6" ]; then + local mac=$(xenstore_read "$XENBUS_PATH/mac") + local port=$(ovs-vsctl get interface $dev ofport) + + if [ ! -z "$ipv4" ]; then + do_or_die ovs-ofctl add-flow $bridge "in_port=$port priority=39000 \ + dl_type=0x0800 nw_src=$ipv4 dl_src=$mac idle_timeout=0 \ + action=normal" > /dev/null + fi + + if [ ! -z "$ipv6" ]; then + do_or_die ovs-ofctl add-flow $bridge "in_port=$port priority=39000 \ + dl_type=0x86dd ipv6_src=$ipv6 dl_src=$mac idle_timeout=0 \ + action=normal" > /dev/null + fi + + do_or_die ovs-ofctl add-flow $bridge "in_port=$port priority=38500 \ + dl_type=0x0806 dl_src=$mac idle_timeout=0 action=normal" > /dev/null + + do_or_die ovs-ofctl add-flow $bridge "in_port=$port priority=38000 \ + idle_timeout=0 action=drop" > /dev/null + fi + fi +} + +case "$command" in + add|online) + check_tools + setup_virtual_bridge_port $dev + add_to_openvswitch $dev + ;; + + remove|offline) + if [ -f ${XEN_CONFIG_DIR}/openvswitch.conf ]; then + bridge=$(xenstore_read_default "$XENBUS_PATH/bridge" "$bridge") + queues=$(ovs-vsctl -- --if-exists get qos $dev queues \ + | sed 's/[0-9]\+=//g;s/[{,}]//g') + # Remove flows + do_without_error ovs-ofctl del-flows $bridge in_port=$(ovs-vsctl \ + -- --if-exists get interface $dev ofport) + # Remove queues & qos + do_without_error ovs-vsctl --timeout=30 \ + -- --if-exists destroy queue $queues \ + -- --if-exists destroy qos $dev \ + -- --if-exists clear port $dev qos + fi + do_without_error ovs-vsctl --timeout=30 \ + -- --if-exists del-port $dev + do_without_error ip link set $dev down + ;; +esac + +if [ "$type_if" = vif ]; then + handle_iptable +fi + +log debug "Successful vif-openvswitch $command for $dev." +if [ "$type_if" = vif -a "$command" = "online" ]; then + success +fi |