#! /bin/sh # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. # # 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. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 for dir in "$sbindir" "$bindir" /sbin /bin /usr/sbin /usr/bin; do case :$PATH: in *:$dir:*) ;; *) PATH=$PATH:$dir ;; esac done ## ----- ## ## start ## ## ----- ## # Keep track of removed vports so we can reload them if needed removed_vports="" insert_mods () { # Try loading openvswitch again. action "Inserting openvswitch module" modprobe openvswitch for vport in $removed_vports; do # Don't treat failures to load vports as fatal error action "Inserting $vport module" modprobe $vport || true done } insert_mod_if_required () { # If this kernel has no module support, expect we're done. if test ! -e /proc/modules then log_success_msg "Kernel has no loadable module support. Skipping modprobe" return 0 fi # If openvswitch is already loaded then we're done. test -e /sys/module/openvswitch -o -e /sys/module/openvswitch_mod && \ return 0 # Load openvswitch. If that's successful then we're done. insert_mods && return 0 # If the bridge module is loaded, then that might be blocking # openvswitch. Try to unload it, if there are no bridges. test -e /sys/module/bridge || return 1 bridges=`echo /sys/class/net/*/bridge | sed 's,/sys/class/net/,,g;s,/bridge,,g'` if test "$bridges" != "*"; then log_warning_msg "not removing bridge module because bridges exist ($bridges)" return 1 fi action "removing bridge module" rmmod bridge || return 1 # Try loading openvswitch again. insert_mods } ovs_vsctl () { ovs-vsctl --no-wait "$@" } set_system_ids () { set ovs_vsctl set Open_vSwitch . OVS_VERSION=`ovs-vswitchd --version | sed 's/.*) //;1q'` set "$@" ovs-version="$OVS_VERSION" case $SYSTEM_ID in random) id_file=$etcdir/system-id.conf uuid_file=$etcdir/install_uuid.conf if test -e "$id_file"; then SYSTEM_ID=`cat "$id_file"` elif test -e "$uuid_file"; then # Migrate from old file name. . "$uuid_file" SYSTEM_ID=$INSTALLATION_UUID echo "$SYSTEM_ID" > "$id_file" elif SYSTEM_ID=`uuidgen`; then echo "$SYSTEM_ID" > "$id_file" else log_failure_msg "missing uuidgen, could not generate system ID" fi ;; '') log_failure_msg "system ID not configured, please use --system-id" ;; *) ;; esac set "$@" external-ids:system-id="\"$SYSTEM_ID\"" if test X"$SYSTEM_TYPE" != X; then set "$@" system-type="\"$SYSTEM_TYPE\"" else log_failure_msg "no default system type, please use --system-type" fi if test X"$SYSTEM_VERSION" != X; then set "$@" system-version="\"$SYSTEM_VERSION\"" else log_failure_msg "no default system version, please use --system-version" fi action "Configuring Open vSwitch system IDs" "$@" $extra_ids } check_force_cores () { if test X"$FORCE_COREFILES" = Xyes; then ulimit -c 67108864 fi } del_transient_ports () { for port in `ovs-vsctl --bare -- --columns=name find port other_config:transient=true`; do ovs_vsctl -- del-port "$port" done } start_ovsdb () { check_force_cores if daemon_is_running ovsdb-server; then log_success_msg "ovsdb-server is already running" else # Create initial database or upgrade database schema. upgrade_db $DB_FILE $DB_SCHEMA || return 1 # Start ovsdb-server. set ovsdb-server "$DB_FILE" for db in $EXTRA_DBS; do case $db in /*) ;; *) db=$dbdir/$db ;; esac if test ! -f "$db"; then log_warning_msg "$db (from \$EXTRA_DBS) does not exist." elif ovsdb-tool db-version "$db" >/dev/null; then set "$@" "$db" else log_warning_msg "$db (from \$EXTRA_DBS) cannot be read as a database (see error message above)" fi done set "$@" -vconsole:emer -vsyslog:err -vfile:info set "$@" --remote=punix:"$DB_SOCK" set "$@" --private-key=db:Open_vSwitch,SSL,private_key set "$@" --certificate=db:Open_vSwitch,SSL,certificate set "$@" --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert start_daemon "$OVSDB_SERVER_PRIORITY" "$OVSDB_SERVER_WRAPPER" "$@" \ || return 1 # Initialize database settings. ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \ || return 1 set_system_ids || return 1 if test X"$DELETE_BRIDGES" = Xyes; then for bridge in `ovs_vsctl list-br`; do ovs_vsctl del-br $bridge done fi if test X"$DELETE_TRANSIENT_PORTS" = Xyes; then del_transient_ports fi fi } add_managers () { # Now that ovs-vswitchd has started and completed its initial # configuration, tell ovsdb-server to conenct to the remote managers. We # used to do this at ovsdb-server startup time, but waiting for # ovs-vswitchd to finish configuring means that remote managers see less # churn in the database at startup or restart. (For example, managers # won't briefly see empty datapath-id or ofport columns for records that # exist at startup.) action "Enabling remote OVSDB managers" \ ovs-appctl -t ovsdb-server ovsdb-server/add-remote \ db:Open_vSwitch,Open_vSwitch,manager_options } start_forwarding () { check_force_cores insert_mod_if_required || return 1 if daemon_is_running ovs-vswitchd; then log_success_msg "ovs-vswitchd is already running" else # Increase the limit on the number of open file descriptors. # On Linux, ovs-vswitchd needs about three file descriptors # per bridge and "n-handler-threads" file descriptors per bridge # port, so this allows a very large number of bridges and ports. MAXFD=65535 if [ $(ulimit -n) -lt $MAXFD ]; then ulimit -n $MAXFD fi # Start ovs-vswitchd. set ovs-vswitchd unix:"$DB_SOCK" set "$@" -vconsole:emer -vsyslog:err -vfile:info if test X"$MLOCKALL" != Xno; then set "$@" --mlockall fi start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@" fi } ## ---- ## ## stop ## ## ---- ## stop_ovsdb () { stop_daemon ovsdb-server } stop_forwarding () { stop_daemon ovs-vswitchd } ## ----------------- ## ## force-reload-kmod ## ## ----------------- ## internal_interfaces () { # Outputs a list of internal interfaces: # # - There is an internal interface for every bridge, whether it # has an Interface record or not and whether the Interface # record's 'type' is properly set or not. # # - There is an internal interface for each Interface record whose # 'type' is 'internal'. # # But ignore interfaces that don't really exist. for d in `(ovs_vsctl --bare \ -- --columns=name find Interface type=internal \ -- list-br) | sort -u` do if test -e "/sys/class/net/$d"; then printf "%s " "$d" fi done } ovs_save () { bridges=`ovs_vsctl -- --real list-br` if [ -n "${bridges}" ] && \ "$datadir/scripts/ovs-save" "$1" ${bridges} > "$2"; then chmod +x "$2" return 0 fi [ -z "${bridges}" ] && return 0 } save_ofports_if_required () { # Save OpenFlow port numbers if we are upgrading from a pre-1.10 branch. # # (Versions 1.10 and later save OpenFlow port numbers without assistance, # so we don't have to do anything for them. case `ovs-appctl version | sed 1q` in "ovs-vswitchd (Open vSwitch) 1."[0-9].*) action "Saving ofport values" ovs_save save-ofports \ "${script_ofports}" ;; esac } save_flows_if_required () { if test X"$DELETE_BRIDGES" != Xyes; then action "Saving flows" ovs_save save-flows "${script_flows}" fi } save_interfaces () { "$datadir/scripts/ovs-save" save-interfaces ${ifaces} \ > "${script_interfaces}" } restore_ofports () { [ -x "${script_ofports}" ] && \ action "Restoring ofport values" "${script_ofports}" } flow_restore_wait () { ovs_vsctl set open_vswitch . other_config:flow-restore-wait="true" } flow_restore_complete () { ovs_vsctl --if-exists remove open_vswitch . other_config \ flow-restore-wait="true" } restore_flows () { [ -x "${script_flows}" ] && \ action "Restoring saved flows" "${script_flows}" } restore_interfaces () { [ ! -x "${script_interfaces}" ] && return 0 action "Restoring interface configuration" "${script_interfaces}" rc=$? if test $rc = 0; then level=debug else level=err fi log="logger -p daemon.$level -t ovs-save" $log "interface restore script exited with status $rc:" $log -f "$script_interfaces" } init_restore_scripts () { script_interfaces=`mktemp` script_flows=`mktemp` script_ofports=`mktemp` trap 'rm -f "${script_interfaces}" "${script_flows}" "${script_ofports}"' 0 } force_reload_kmod () { ifaces=`internal_interfaces` action "Detected internal interfaces: $ifaces" true init_restore_scripts save_flows_if_required save_ofports_if_required # Restart the database first, since a large database may take a # while to load, and we want to minimize forwarding disruption. stop_ovsdb start_ovsdb # Restore of ofports should happen before vswitchd is restarted. restore_ofports stop_forwarding if action "Saving interface configuration" save_interfaces; then : else log_warning_msg "Failed to save configuration, not replacing kernel module" start_forwarding add_managers exit 1 fi chmod +x "$script_interfaces" for dp in `ovs-dpctl dump-dps`; do action "Removing datapath: $dp" ovs-dpctl del-dp "$dp" done for vport in `awk '/^vport_/ { print $1 }' /proc/modules`; do action "Removing $vport module" rmmod $vport if ! grep -q $vport /proc/modules; then removed_vports="$removed_vports $vport" fi done # try both old and new names in case this is post upgrade if test -e /sys/module/openvswitch_mod; then action "Removing openvswitch module" rmmod openvswitch_mod elif test -e /sys/module/openvswitch; then action "Removing openvswitch module" rmmod openvswitch fi # Start vswitchd by asking it to wait till flow restore is finished. flow_restore_wait start_forwarding # Restore saved flows and inform vswitchd that we are done. restore_flows flow_restore_complete add_managers restore_interfaces "$datadir/scripts/ovs-check-dead-ifs" } ## ------- ## ## restart ## ## ------- ## save_interfaces_if_required () { # Save interfaces if we are upgrading from a pre-1.10 branch. case `ovs-appctl version | sed 1q` in "ovs-vswitchd (Open vSwitch) 1."[0-9].*) ifaces=`internal_interfaces` action "Detected internal interfaces: $ifaces" true if action "Saving interface configuration" save_interfaces; then chmod +x "$script_interfaces" fi ;; esac } restart () { if daemon_is_running ovsdb-server && daemon_is_running ovs-vswitchd; then init_restore_scripts save_interfaces_if_required save_flows_if_required save_ofports_if_required fi # Restart the database first, since a large database may take a # while to load, and we want to minimize forwarding disruption. stop_ovsdb start_ovsdb # Restore of ofports, if required, should happen before vswitchd is # restarted. restore_ofports stop_forwarding # Start vswitchd by asking it to wait till flow restore is finished. flow_restore_wait start_forwarding # Restore saved flows and inform vswitchd that we are done. restore_flows flow_restore_complete add_managers # Restore the interfaces if required. Return true even if restore fails. restore_interfaces || true } ## --------------- ## ## enable-protocol ## ## --------------- ## enable_protocol () { # Translate the protocol name to a number, because "iptables -n -L" prints # some protocols by name (despite the -n) and therefore we need to look for # both forms. # # (iptables -S output is more uniform but old iptables doesn't have it.) protonum=`grep "^$PROTOCOL[ ]" /etc/protocols | awk '{print $2}'` if expr X"$protonum" : X'[0-9]\{1,\}$' > /dev/null; then :; else log_failure_msg "unknown protocol $PROTOCOL" return 1 fi name=$PROTOCOL match="(\$2 == \"$PROTOCOL\" || \$2 == $protonum)" insert="iptables -I INPUT -p $PROTOCOL" if test X"$DPORT" != X; then name="$name to port $DPORT" match="$match && /dpt:$DPORT/" insert="$insert --dport $DPORT" fi if test X"$SPORT" != X; then name="$name from port $SPORT" match="$match && /spt:$SPORT/" insert="$insert --sport $SPORT" fi insert="$insert -j ACCEPT" if (iptables -n -L INPUT) >/dev/null 2>&1; then if iptables -n -L INPUT | awk "$match { n++ } END { exit n == 0 }" then # There's already a rule for this protocol. Don't override it. log_success_msg "iptables already has a rule for $name, not explicitly enabling" else action "Enabling $name with iptables" $insert fi elif (iptables --version) >/dev/null 2>&1; then action "cannot list iptables rules, not adding a rule for $name" else action "iptables binary not installed, not adding a rule for $name" fi } ## ---- ## ## main ## ## ---- ## set_defaults () { SYSTEM_ID= DELETE_BRIDGES=no DELETE_TRANSIENT_PORTS=no DAEMON_CWD=/ FORCE_COREFILES=yes MLOCKALL=yes OVSDB_SERVER_PRIORITY=-10 OVS_VSWITCHD_PRIORITY=-10 OVSDB_SERVER_WRAPPER= OVS_VSWITCHD_WRAPPER= DB_FILE=$dbdir/conf.db DB_SOCK=$rundir/db.sock DB_SCHEMA=$datadir/vswitch.ovsschema EXTRA_DBS= PROTOCOL=gre DPORT= SPORT= type_file=$etcdir/system-type.conf version_file=$etcdir/system-version.conf if test -e "$type_file" ; then SYSTEM_TYPE=`cat $type_file` SYSTEM_VERSION=`cat $version_file` elif (lsb_release --id) >/dev/null 2>&1; then SYSTEM_TYPE=`lsb_release --id -s` system_release=`lsb_release --release -s` system_codename=`lsb_release --codename -s` SYSTEM_VERSION="${system_release}-${system_codename}" else SYSTEM_TYPE=unknown SYSTEM_VERSION=unknown fi } usage () { set_defaults cat <&2 "$0: unknown option \"$arg\" (use --help for help)" return fi eval $var=\$value } daemons () { echo ovsdb-server ovs-vswitchd } set_defaults extra_ids= command= for arg do case $arg in -h | --help) usage ;; -V | --version) echo "$0 (Open vSwitch) $VERSION" exit 0 ;; --external-id=*) value=`expr X"$arg" : 'X[^=]*=\(.*\)'` case $value in *=*) extra_ids="$extra_ids external-ids:$value" ;; *) echo >&2 "$0: --external-id argument not in the form \"key=value\"" exit 1 ;; esac ;; --[a-z]*=*) option=`expr X"$arg" : 'X--\([^=]*\)'` value=`expr X"$arg" : 'X[^=]*=\(.*\)'` type=string set_option ;; --no-[a-z]*) option=`expr X"$arg" : 'X--no-\(.*\)'` value=no type=bool set_option ;; --[a-z]*) option=`expr X"$arg" : 'X--\(.*\)'` value=yes type=bool set_option ;; -*) echo >&2 "$0: unknown option \"$arg\" (use --help for help)" exit 1 ;; *) if test X"$command" = X; then command=$arg else echo >&2 "$0: exactly one non-option argument required (use --help for help)" exit 1 fi ;; esac done case $command in start) start_ovsdb || exit 1 start_forwarding add_managers ;; stop) stop_forwarding stop_ovsdb ;; restart) restart ;; status) rc=0 for daemon in `daemons`; do daemon_status $daemon || rc=$? done exit $rc ;; version) for daemon in `daemons`; do $daemon --version done ;; force-reload-kmod) force_reload_kmod ;; load-kmod) insert_mod_if_required ;; enable-protocol) enable_protocol ;; help) usage ;; '') echo >&2 "$0: missing command name (use --help for help)" exit 1 ;; *) echo >&2 "$0: unknown command \"$command\" (use --help for help)" exit 1 ;; esac