#!/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