root@51: #!/bin/bash
root@51: #============================================================================
root@51: # Default Xen network start/stop script.
root@51: # Xend calls a network script when it starts.
root@51: # The script name to use is defined in /etc/xen/xend-config.sxp
root@51: # in the network-script field.
root@51: #
root@51: # This script creates a bridge (default xenbr${vifnum}), adds a device
root@51: # (default eth${vifnum}) to it, copies the IP addresses from the device
root@51: # to the bridge and adjusts the routes accordingly.
root@51: #
root@51: # If all goes well, this should ensure that networking stays up.
root@51: # However, some configurations are upset by this, especially
root@51: # NFS roots. If the bridged setup does not meet your needs,
root@51: # configure a different script, for example using routing instead.
root@51: #
root@51: # Usage:
root@51: #
root@51: # network-bridge (start|stop|status) {VAR=VAL}*
root@51: #
root@51: # Vars:
root@51: #
root@51: # vifnum     Virtual device number to use (default 0). Numbers >=8
root@51: #            require the netback driver to have nloopbacks set to a
root@51: #            higher value than its default of 8.
root@51: # bridge     The bridge to use (default xenbr${vifnum}).
root@51: # netdev     The interface to add to the bridge (default eth${vifnum}).
root@51: # antispoof  Whether to use iptables to prevent spoofing (default no).
root@51: #
root@51: # Internal Vars:
root@51: # pdev="p${netdev}"
root@51: # vdev="veth${vifnum}"
root@51: # vif0="vif0.${vifnum}"
root@51: #
root@51: # start:
root@51: # Creates the bridge
root@51: # Copies the IP and MAC addresses from netdev to vdev
root@51: # Renames netdev to be pdev 
root@51: # Renames vdev to be netdev 
root@51: # Enslaves pdev, vdev to bridge
root@51: #
root@51: # stop:
root@51: # Removes netdev from the bridge
root@51: # Transfers addresses, routes from netdev to pdev
root@51: # Renames netdev to vdev
root@51: # Renames pdev to netdev 
root@51: # Deletes bridge
root@51: #
root@51: # status:
root@51: # Print addresses, interfaces, routes
root@51: #
root@51: #============================================================================
root@51: 
root@51: 
root@51: dir=$(dirname "$0")
root@51: . "$dir/xen-script-common.sh"
root@51: . "$dir/xen-network-common.sh"
root@51: 
root@51: findCommand "$@"
root@51: evalVariables "$@"
root@51: 
root@51: modprobe netloop > /dev/null 2>&1 || true
root@51: 
root@51: vifnum=${vifnum:-$(ip route list | awk '/^default / { print $NF }' | sed 's/^[^0-9]*//')}
root@51: vifnum=${vifnum:-0}
root@51: bridge=${bridge:-xenbr${vifnum}}
root@51: netdev=${netdev:-eth${vifnum}}
root@51: antispoof=${antispoof:-no}
root@51: 
root@51: pdev="p${netdev}"
root@51: vdev="veth${vifnum}"
root@51: vif0="vif0.${vifnum}"
root@51: 
root@51: get_ip_info() {
root@51:     addr_pfx=`ip addr show dev $1 | egrep '^ *inet' | sed -e 's/ *inet //' -e 's/ .*//'`
root@51:     gateway=`ip route show dev $1 | fgrep default | sed 's/default via //'`
root@51: }
root@51:     
root@51: do_ifup() {
root@51:     if ! ifup $1 ; then
root@51:         if [ ${addr_pfx} ] ; then
root@51:             # use the info from get_ip_info()
root@51:             ip addr flush $1
root@51:             ip addr add ${addr_pfx} dev $1
root@51:             ip link set dev $1 up
root@51:             [ ${gateway} ] && ip route add default via ${gateway}
root@51:         fi
root@51:     fi
root@51: }
root@51: 
root@51: # Usage: transfer_addrs src dst
root@51: # Copy all IP addresses (including aliases) from device $src to device $dst.
root@51: transfer_addrs () {
root@51:     local src=$1
root@51:     local dst=$2
root@51:     # Don't bother if $dst already has IP addresses.
root@51:     if ip addr show dev ${dst} | egrep -q '^ *inet ' ; then
root@51:         return
root@51:     fi
root@51:     # Address lines start with 'inet' and have the device in them.
root@51:     # Replace 'inet' with 'ip addr add' and change the device name $src
root@51:     # to 'dev $src'.
root@51:     ip addr show dev ${src} | egrep '^ *inet ' | sed -e "
root@51: s/inet/ip addr add/
root@51: s@\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+/[0-9]\+\)@\1@
root@51: s/${src}/dev ${dst}/
root@51: " | sh -e
root@51:     # Remove automatic routes on destination device
root@51:     ip route list | sed -ne "
root@51: /dev ${dst}\( \|$\)/ {
root@51:   s/^/ip route del /
root@51:   p
root@51: }" | sh -e
root@51: }
root@51: 
root@51: # Usage: transfer_routes src dst
root@51: # Get all IP routes to device $src, delete them, and
root@51: # add the same routes to device $dst.
root@51: # The original routes have to be deleted, otherwise adding them
root@51: # for $dst fails (duplicate routes).
root@51: transfer_routes () {
root@51:     local src=$1
root@51:     local dst=$2
root@51:     # List all routes and grep the ones with $src in.
root@51:     # Stick 'ip route del' on the front to delete.
root@51:     # Change $src to $dst and use 'ip route add' to add.
root@51:     ip route list | sed -ne "
root@51: /dev ${src}\( \|$\)/ {
root@51:   h
root@51:   s/^/ip route del /
root@51:   P
root@51:   g
root@51:   s/${src}/${dst}/
root@51:   s/^/ip route add /
root@51:   P
root@51:   d
root@51: }" | sh -e
root@51: }
root@51: 
root@51: 
root@51: ##
root@51: # link_exists interface
root@51: #
root@51: # Returns 0 if the interface named exists (whether up or down), 1 otherwise.
root@51: #
root@51: link_exists()
root@51: {
root@51:     if ip link show "$1" >/dev/null 2>/dev/null
root@51:     then
root@51:         return 0
root@51:     else
root@51:         return 1
root@51:     fi
root@51: }
root@51: 
root@51: # Set the default forwarding policy for $dev to drop.
root@51: # Allow forwarding to the bridge.
root@51: antispoofing () {
root@51:     iptables -P FORWARD DROP
root@51:     iptables -F FORWARD
root@51:     iptables -A FORWARD -m physdev --physdev-in ${pdev} -j ACCEPT
root@51:     iptables -A FORWARD -m physdev --physdev-in ${vif0} -j ACCEPT
root@51: }
root@51: 
root@51: # Usage: show_status dev bridge
root@51: # Print ifconfig and routes.
root@51: show_status () {
root@51:     local dev=$1
root@51:     local bridge=$2
root@51:     
root@51:     echo '============================================================'
root@51:     ip addr show ${dev}
root@51:     ip addr show ${bridge}
root@51:     echo ' '
root@51:     brctl show ${bridge}
root@51:     echo ' '
root@51:     ip route list
root@51:     echo ' '
root@51:     route -n
root@51:     echo '============================================================'
root@51: }
root@51: 
root@51: op_start () {
root@51:     if [ "${bridge}" = "null" ] ; then
root@51: 	return
root@51:     fi
root@51: 
root@51:     if ! link_exists "$vdev"; then
root@51:         if link_exists "$pdev"; then
root@51:             # The device is already up.
root@51:             return
root@51:         else
root@51:             echo "
root@51: Link $vdev is missing.
root@51: This may be because you have reached the limit of the number of interfaces
root@51: that the loopback driver supports.  If the loopback driver is a module, you
root@51: may raise this limit by passing it as a parameter (nloopbacks=<N>); if the
root@51: driver is compiled statically into the kernel, then you may set the parameter
root@51: using loopback.nloopbacks=<N> on the domain 0 kernel command line.
root@51: " >&2
root@51:             exit 1
root@51:         fi
root@51:     fi
root@51: 
root@51:     create_bridge ${bridge}
root@51: 
root@51:     if link_exists "$vdev"; then
root@51: 	mac=`ip link show ${netdev} | grep 'link\/ether' | sed -e 's/.*ether \(..:..:..:..:..:..\).*/\1/'`
root@51: 	preiftransfer ${netdev}
root@51: 	transfer_addrs ${netdev} ${vdev}
root@51: 	if ! ifdown ${netdev}; then
root@51: 	    # If ifdown fails, remember the IP details.
root@51: 	    get_ip_info ${netdev}
root@51: 	    ip link set ${netdev} down
root@51: 	    ip addr flush ${netdev}
root@51: 	fi
root@51: 	ip link set ${netdev} name ${pdev}
root@51: 	ip link set ${vdev} name ${netdev}
root@51: 
root@51: 	setup_bridge_port ${pdev}
root@51: 	setup_bridge_port ${vif0}
root@51: 	ip link set ${netdev} addr ${mac} arp on
root@51: 
root@51: 	ip link set ${bridge} up
root@51: 	add_to_bridge  ${bridge} ${vif0}
root@51: 	add_to_bridge2 ${bridge} ${pdev}
root@51: 	do_ifup ${netdev}
root@51:     else
root@51: 	# old style without ${vdev}
root@51: 	transfer_addrs  ${netdev} ${bridge}
root@51: 	transfer_routes ${netdev} ${bridge}
root@51:     fi
root@51: 
root@51:     if [ ${antispoof} = 'yes' ] ; then
root@51: 	antispoofing
root@51:     fi
root@51: }
root@51: 
root@51: op_stop () {
root@51:     if [ "${bridge}" = "null" ]; then
root@51: 	return
root@51:     fi
root@51:     if ! link_exists "$bridge"; then
root@51: 	return
root@51:     fi
root@51: 
root@51:     if link_exists "$pdev"; then
root@51: 	ip link set dev ${vif0} down
root@51: 	mac=`ip link show ${netdev} | grep 'link\/ether' | sed -e 's/.*ether \(..:..:..:..:..:..\).*/\1/'`
root@51: 	transfer_addrs ${netdev} ${pdev}
root@51: 	if ! ifdown ${netdev}; then
root@51: 	    get_ip_info ${netdev}
root@51: 	fi
root@51: 	ip link set ${netdev} down arp off
root@51: 	ip link set ${netdev} addr fe:ff:ff:ff:ff:ff
root@51: 	ip link set ${pdev} down
root@51: 	ip addr flush ${netdev}
root@51: 	ip link set ${pdev} addr ${mac} arp on
root@51: 
root@51: 	brctl delif ${bridge} ${pdev}
root@51: 	brctl delif ${bridge} ${vif0}
root@51: 	ip link set ${bridge} down
root@51: 
root@51: 	ip link set ${netdev} name ${vdev}
root@51: 	ip link set ${pdev} name ${netdev}
root@51: 	do_ifup ${netdev}
root@51:     else
root@51: 	transfer_routes ${bridge} ${netdev}
root@51: 	ip link set ${bridge} down
root@51:     fi
root@51:     brctl delbr ${bridge}
root@51: }
root@51: 
root@51: # adds $dev to $bridge but waits for $dev to be in running state first
root@51: add_to_bridge2() {
root@51:     local bridge=$1
root@51:     local dev=$2
root@51:     local maxtries=10
root@51: 
root@51:     echo -n "Waiting for ${dev} to negotiate link."
root@51:     ip link set ${dev} up
root@51:     for i in `seq ${maxtries}` ; do
root@51: 	if ifconfig ${dev} | grep -q RUNNING ; then
root@51: 	    break
root@51: 	else
root@51: 	    echo -n '.'
root@51: 	    sleep 1
root@51: 	fi
root@51:     done
root@51: 
root@51:     if [ ${i} -eq ${maxtries} ] ; then echo '(link isnt in running state)' ; fi
root@51: 
root@51:     add_to_bridge ${bridge} ${dev}
root@51: }
root@51: 
root@51: case "$command" in
root@51:     start)
root@51: 	op_start
root@51: 	;;
root@51:     
root@51:     stop)
root@51: 	op_stop
root@51: 	;;
root@51: 
root@51:     status)
root@51: 	show_status ${netdev} ${bridge}
root@51: 	;;
root@51: 
root@51:     *)
root@51: 	echo "Unknown command: $command" >&2
root@51: 	echo 'Valid commands are: start, stop, status' >&2
root@51: 	exit 1
root@51: esac