1#
2# Copyright (c) 2005 XenSource Ltd.
3#
4# This library is free software; you can redistribute it and/or
5# modify it under the terms of version 2.1 of the GNU Lesser General Public
6# License as published by the Free Software Foundation.
7#
8# This library is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11# Lesser General Public License for more details.
12#
13# You should have received a copy of the GNU Lesser General Public
14# License along with this library; If not, see <http://www.gnu.org/licenses/>.
15#
16
17
18dir=$(dirname "$0")
19. "$dir/xen-hotplug-common.sh"
20. "$dir/xen-network-common.sh"
21
22findCommand "$@"
23
24if [ "$command" != "online" ]  &&
25   [ "$command" != "offline" ] &&
26   [ "$command" != "add" ]     &&
27   [ "$command" != "remove" ]
28then
29  log err "Invalid command: $command"
30  exit 1
31fi
32
33
34# Parameters may be read from the environment, the command line arguments, and
35# the store, with overriding in that order.  The environment is given by the
36# driver, the command line is given by the Xend global configuration, and
37# store details are given by the per-domain or per-device configuration.
38
39evalVariables "$@"
40
41# Older versions of Xen do not pass in the type as an argument,
42# so the default value is vif.
43: ${type_if:=vif}
44
45case "$type_if" in
46    vif)
47        dev=$vif
48        ;;
49    tap)
50        dev=$INTERFACE
51        ;;
52    *)
53        log err "unknown interface type $type_if"
54        exit 1
55        ;;
56esac
57
58case "$command" in
59    online | offline)
60        test "$type_if" != vif && exit 0
61        ;;
62    add | remove)
63        test "$type_if" != tap && exit 0
64        ;;
65esac
66
67rename_vif() {
68    local dev=$1
69    local vifname=$2
70
71    # if a custom vifname was chosen and a link with that desired name
72    # already exists, then stop, before messing up whatever is using
73    # that interface (e.g. another running domU) because it's likely a
74    # configuration error
75    if ip link show "$vifname" >&/dev/null
76    then
77        fatal "Cannot rename interface $dev. An interface with name $vifname already exists."
78    fi
79    do_or_die ip link set "$dev" name "$vifname"
80}
81
82if [ "$type_if" = vif ]; then
83    # Check presence of compulsory args.
84    XENBUS_PATH="${XENBUS_PATH:?}"
85    dev="${dev:?}"
86
87    vifname=$(xenstore_read_default "$XENBUS_PATH/vifname" "")
88    if [ "$vifname" ]
89    then
90        if [ "$command" == "online" ]
91        then
92            rename_vif "$dev" "$vifname"
93        fi
94        dev="$vifname"
95    fi
96elif [ "$type_if" = tap ]; then
97    # Check presence of compulsory args.
98    : ${INTERFACE:?}
99
100    # Get xenbus_path from device name.
101    # The name is built like that: "vif${domid}.${devid}-emu".
102    dev_=${dev#vif}
103    dev_=${dev_%-emu}
104    domid=${dev_%.*}
105    devid=${dev_#*.}
106
107    XENBUS_PATH="/local/domain/0/backend/vif/$domid/$devid"
108    vifname=$(xenstore_read_default "$XENBUS_PATH/vifname" "")
109    if [ "$vifname" ]
110    then
111        vifname="${vifname}-emu"
112        if [ "$command" == "add" ]
113        then
114            rename_vif "$dev" "$vifname"
115        fi
116        dev="$vifname"
117    fi
118fi
119
120ip=${ip:-}
121ip=$(xenstore_read_default "$XENBUS_PATH/ip" "$ip")
122
123IPTABLES_WAIT_RUNE="-w"
124IPTABLES_WAIT_RUNE_CHECKED=false
125
126# When iptables introduced locking, in the event of lock contention,
127# they made "fail" rather than "wait for the lock" the default
128# behavior.  In order to select "wait for the lock" behavior, you have
129# to add the '-w' parameter.  Unfortunately, both the locking and the
130# option were only introduced in 2013, and older versions of iptables
131# will fail if the '-w' parameter is included (since they don't
132# recognize it).  So check to see if it's supported the first time we
133# use it.
134iptables_w()
135{
136    if ! $IPTABLES_WAIT_RUNE_CHECKED ; then
137	iptables $IPTABLES_WAIT_RUNE -L -n >& /dev/null
138	if [[ $? == 0 ]] ; then
139	    # If we succeed, then -w is supported; don't check again
140	    IPTABLES_WAIT_RUNE_CHECKED=true
141	elif [[ $? == 2 ]] ; then
142	    iptables -L -n >& /dev/null
143	    if [[ $? != 2 ]] ; then
144		# If we fail with PARAMETER_PROBLEM (2) with -w and
145		# don't fail with PARAMETER_PROBLEM without it, then
146		# it's the -w option
147		IPTABLES_WAIT_RUNE_CHECKED=true
148		IPTABLES_WAIT_RUNE=""
149	    fi
150	fi
151    fi
152    iptables $IPTABLES_WAIT_RUNE "$@"
153}
154
155frob_iptable()
156{
157  if [ "$command" == "online" -o "$command" == "add" ]
158  then
159    local c="-I"
160  else
161    local c="-D"
162  fi
163
164  iptables_w "$c" FORWARD -m physdev --physdev-is-bridged --physdev-in "$dev" \
165    "$@" -j ACCEPT 2>/dev/null &&
166  iptables_w "$c" FORWARD -m physdev --physdev-is-bridged --physdev-out "$dev" \
167    -j ACCEPT 2>/dev/null
168
169  if [ \( "$command" == "online" -o "$command" == "add" \) -a $? -ne 0 ]
170  then
171    log err "iptables setup failed. This may affect guest networking."
172  fi
173}
174
175
176##
177# Add or remove the appropriate entries in the iptables.  With antispoofing
178# turned on, we have to explicitly allow packets to the interface, regardless
179# of the ip setting.  If ip is set, then we additionally restrict the packets
180# to those coming from the specified networks, though we allow DHCP requests
181# as well.
182#
183handle_iptable()
184{
185  # Check for a working iptables installation.  Checking for the iptables
186  # binary is not sufficient, because the user may not have the appropriate
187  # modules installed.  If iptables is not working, then there's no need to do
188  # anything with it, so we can just return.
189  if ! iptables_w -L -n >&/dev/null
190  then
191    return
192  fi
193
194  claim_lock "iptables"
195
196  if [ "$ip" != "" ]
197  then
198      local addr
199      for addr in $ip
200      do
201        frob_iptable -s "$addr"
202      done
203
204      # Always allow the domain to talk to a DHCP server.
205      frob_iptable -p udp --sport 68 --dport 67
206  else
207      # No IP addresses have been specified, so allow anything.
208      frob_iptable
209  fi
210
211  release_lock "iptables"
212}
213
214
215##
216# ip_of interface
217#
218# Print the IP address currently in use at the given interface, or nothing if
219# the interface is not up.
220#
221ip_of()
222{
223  ip -4 -o addr show primary dev "$1" | awk '$3 == "inet" {split($4,i,"/"); print i[1]; exit}'
224}
225
226
227##
228# dom0_ip
229#
230# Print the IP address of the interface in dom0 through which we are routing.
231# This is the IP address on the interface specified as "netdev" as a parameter
232# to these scripts, or eth0 by default.  This function will call fatal if no
233# such interface could be found.
234#
235dom0_ip()
236{
237  local nd=${netdev:-eth0}
238  local result=$(ip_of "$nd")
239  if [ -z "$result" ]
240  then
241      fatal \
242"$netdev is not up.  Bring it up or specify another interface with " \
243"netdev=<if> as a parameter to $0."
244  fi
245  echo "$result"
246}
247