| # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| description "Configure network bridge and disable wlan0 for moblab" |
| author "chromium-os-dev@chromium.org" |
| |
| start on (started network-services and |
| stopped moblab-external-storage-init RESULT=ok and |
| stopped moblab-external-settings-init RESULT=ok) |
| |
| normal exit 0 |
| |
| script |
| mkdir -p /var/log/bootup/ |
| exec >>/var/log/bootup/${UPSTART_JOB}.log 2>&1 |
| set -x |
| set -e |
| logger -t "$UPSTART_JOB" "Starting." |
| |
| SERVER_ADDRESS=192.168.231.1 |
| SERVER_NETMASK=255.255.255.0 |
| DHCPD_IFACE='lxcbr0' |
| SHILL_START_LOCK_PATH="/var/lock/shill-start.lock" |
| INTERFACE_WAN='WAN' |
| INTERFACE_LAN='LAN' |
| |
| # If this file exists, it must contain a single line with the mac address of |
| # the network interface to be used for private network shared by moblab with |
| # its DUTs. |
| PRIVATE_NETWORK_INTERFACE_MAC_ADDR='/mnt/moblab/private-network-macaddr.conf' |
| |
| # $1: Name of the network interface to check for (e.g.: eth0) |
| # returns: 1 on success, 0 otherwise. |
| node_labeled_for_private_network() { |
| local node="$1" |
| |
| local expected_addr=$(head -1 "${PRIVATE_NETWORK_INTERFACE_MAC_ADDR}" | |
| tr -d '[:space:]') |
| local got_addr=$(ip link show "${node}" | tail -1 | awk '{print $2}') |
| if [ "${expected_addr}" = "${got_addr}" ]; then |
| return 1 |
| fi |
| return 0 |
| } |
| |
| ensure_br_netfilter_loaded() { |
| if [ -e /proc/sys/net/bridge ]; then |
| return 1 |
| fi |
| |
| # Kernels older that 3.18 do not have br_netfilter as a module. |
| modprobe -n br_netfilter || return 1 |
| |
| logger -t "${UPSTART_JOB}" "Loading kernel module br_netfilter." |
| modprobe br_netfilter |
| local i=0 |
| while [ $i -le 10 ]; do |
| if [ -e /proc/sys/net/bridge ]; then |
| return 1 |
| fi |
| sleep 0.5 |
| i=$(( i + 1 )) |
| done |
| return 0 |
| } |
| |
| # Normal case, one interface connected to a network that should have |
| # access to google, that interface should have an IP address. |
| # The other interface has the DUT's connected, no DHCP server has been |
| # started yet so no IP address on that interface. |
| find_network_interfaces_by_ip_addr() { |
| logger -t "${UPSTART_JOB}" "Finding interfaces by ip address" |
| local node |
| local server_net_prefix=$( echo "${SERVER_ADDRESS}" | |
| awk -F '.' '{print $1 $2 $3}' ) |
| for node in "$@"; do |
| local inet_addr=$( ip addr show $node | |
| grep "inet\b" | |
| awk '{print $2}' | |
| cut -d/ -f1 ) |
| if [ -z "${inet_addr}" ]; then |
| if [ -z ${INT_IFACE} ]; then |
| INT_IFACE=$node |
| fi |
| else |
| # Exclude the dhcp server subnet from being the external interface |
| local net_prefix=$( echo "${inet_addr}" | |
| awk -F '.' '{print $1 $2 $3}' ) |
| if [ "${net_prefix}" != "${server_net_prefix}" ]; then |
| if [ -z ${EXT_IFACE} ]; then |
| EXT_IFACE=$node |
| else |
| logger -t "${UPSTART_JOB}" "Multiple interfaces meet the criteria \ |
| for External interface, please check wiring connections, only one\ |
| network cable to internet required." |
| fi |
| fi |
| fi |
| done |
| } |
| |
| # This is the legacy method - only works when a USB dongle is in use, |
| # and there is only a single built in ethernet |
| find_network_interfaces_by_device_type() { |
| logger -t "${UPSTART_JOB}" "Finding interfaces by device type" |
| for node in "$@"; do |
| case $(readlink -f /sys/class/net/$node) in |
| */usb*) |
| USB_IFACE=$node |
| ;; |
| */pci*) |
| EXT_IFACE=$node |
| ;; |
| esac |
| done |
| } |
| |
| # Just guess what if is which so the bootup will happen. |
| find_network_interfaces_by_guess() { |
| logger -t "${UPSTART_JOB}" "Guessing interfaces" |
| INT_IFACE="$1" |
| EXT_IFACE="$2" |
| } |
| |
| find_network_interfaces() { |
| # Find which network interfaces is connected to the WAN. |
| EXT_IFACE="" |
| INT_IFACE="" |
| local count=0 |
| find_network_interfaces_by_ip_addr $@ |
| until [ ! -z "${EXT_IFACE}" ] && [ ! -z ${INT_IFACE} ] || |
| [ ${count} -gt 5 ]; do |
| count=$(($count+1)) |
| sleep 10 |
| find_network_interfaces_by_ip_addr $@ |
| done |
| logger -t "${UPSTART_JOB}" "External ${EXT_IFACE} Internal ${INT_IFACE}" |
| # TODO(haddowk) Remove this check when we are sure IP address is working |
| # correctly in the field. |
| if [ -z "${EXT_IFACE}" ] || [ -z ${INT_IFACE} ]; then |
| find_network_interfaces_by_device_type $@ |
| fi |
| logger -t "${UPSTART_JOB}" "External ${EXT_IFACE} Internal ${INT_IFACE}" |
| if [ -z "${EXT_IFACE}" ] || [ -z ${INT_IFACE} ]; then |
| find_network_interfaces_by_guess $@ |
| fi |
| # Ethtool exits with an error code if the device does not exist if it |
| # is not present in this hardware ( happens when a guado has no usb |
| # plugged in ) |
| if [ ! -e /sys/class/net/${INT_IFACE} ]; then |
| return 0 |
| fi |
| # Line below is simpley a check that ethtool exists, -e will not exit on |
| # piped statements. |
| ethtool -i ${INT_IFACE} |
| # Workaround for issue crbug.com/804977 |
| # The realtek driver in the 3.14 kernel has a bug, dropping TX packets |
| # when the device is in promiscuous mode, which is required for a network |
| # bridge. |
| # The bridge is required so lxc containers virtual ethernet interface |
| # can share the network link on the physical interface of the host. |
| if [ "$(uname -r)" = "3.14.0" ]; then |
| if ethtool -i ${INT_IFACE} | grep --quiet "driver: r8152"; then |
| logger -t "${UPSTART_JOB}" "Realtek USB device detected, disable tso." |
| ethtool -K ${INT_IFACE} tso off |
| fi |
| fi |
| } |
| |
| wait_for_interface() { |
| # Check the interface existence for 5 mins after boot. |
| # Taking one parameter as the interface name - either 'built-in' or 'USB' |
| local reps=0 |
| while [ ${reps} -lt 300 ]; do |
| find_network_interfaces eth0 eth1 |
| local intf |
| case "$1" in |
| ${INTERFACE_WAN}) intf=${EXT_IFACE} |
| ;; |
| ${INTERFACE_LAN}) intf=${INT_IFACE} |
| ;; |
| *) logger -t "${UPSTART_JOB}" "Illegal network interface $1" |
| break |
| ;; |
| esac |
| if [ -n "${intf}" ]; then |
| logger -t "${UPSTART_JOB}" "Found $1 Ethernet interface ${intf}." |
| break |
| fi |
| if [ $((reps % 30)) -eq 0 ]; then |
| logger -t "${UPSTART_JOB}" "Waiting for $1 Ethernet connection(${reps} secs)" |
| fi |
| : $((reps += 1)) |
| sleep 1 |
| done |
| } |
| |
| set_up_forwarding() { |
| # Add delay after restart shill |
| sleep 5 |
| |
| # Configure NAT service. This needs to be done each time that shill |
| # is restarted as the restart will re-run the iptables setup job |
| # and thus wipe the below settings. |
| logger -t "${UPSTART_JOB}" "Configuring NAT Service." |
| iptables -t nat -A POSTROUTING -o ${EXT_IFACE} -j MASQUERADE |
| iptables -A FORWARD -i ${EXT_IFACE} -o ${DHCPD_IFACE} -m state \ |
| --state RELATED,ESTABLISHED -j ACCEPT |
| iptables -A FORWARD -i ${DHCPD_IFACE} -o ${EXT_IFACE} -j ACCEPT |
| |
| logger -t "${UPSTART_JOB}" "Enabling IP forwarding" |
| echo 1 > /proc/sys/net/ipv4/ip_forward |
| echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables |
| echo 0 > /proc/sys/net/bridge/bridge-nf-call-ip6tables |
| } |
| |
| lock_shill() { |
| if [ -e "${SHILL_START_LOCK_PATH}" ]; then |
| lock_holder=$(readlink "${SHILL_START_LOCK_PATH}") |
| logger -t "${UPSTART_JOB}" "Warning: lock is held by $lock_holder; will retry" |
| elif [ -L "${SHILL_START_LOCK_PATH}" ]; then |
| logger -t "${UPSTART_JOB}" "Cleaning dangling shill lock held by dead process" |
| rm -f "${SHILL_START_LOCK_PATH}" |
| fi |
| |
| local timeout=30 |
| while [ "$timeout" -gt 0 ]; do |
| ln -s /proc/$$ "${SHILL_START_LOCK_PATH}" && return 0 |
| # Failure: owner might still have the lock, so retry for a while |
| sleep 1 |
| timeout=$((timeout - 1)) |
| done |
| logger -t "${UPSTART_JOB}" "Error: could not grab shill restart lock after 30 seconds" |
| exit 1 |
| } |
| |
| unlock_shill() { |
| rm -f "${SHILL_START_LOCK_PATH}" |
| } |
| |
| # Step 1 - make sure the built-in external interface exists. |
| wait_for_interface ${INTERFACE_WAN} |
| if [ -z "${EXT_IFACE}" ]; then |
| logger -t "${UPSTART_JOB}" "No Internet connection on eth0 or eth1." |
| exit 0 |
| fi |
| |
| # Step 2 - stop shill before creating the network bridge. |
| logger -t "${UPSTART_JOB}" "stopping shill" |
| lock_shill |
| stop shill |
| |
| # Step3 - create and setup the network bridge. |
| # Step 3.1 - bring up the network bridge and set forward delay to 0. |
| logger -t "${UPSTART_JOB}" "Bringing up network bridge ${DHCPD_IFACE}" |
| # In the case of crash / restart the the bridge might already exist. |
| brctl delbr ${DHCPD_IFACE} || : |
| brctl addbr ${DHCPD_IFACE} |
| brctl setfd ${DHCPD_IFACE} 0 |
| |
| # Step 3.2 - configure server IP address with ${SERVER_ADDRESS}. |
| logger -t "${UPSTART_JOB}" "setting server IP address to ${SERVER_ADDRESS}" |
| ifconfig ${DHCPD_IFACE} ${SERVER_ADDRESS} netmask ${SERVER_NETMASK} up |
| |
| # Step 3.3 - start the dhcpd server on MobLab. |
| logger -t "${UPSTART_JOB}" "starting moblab-dhcpd-init" |
| start moblab-dhcpd-init |
| |
| # Step 4 - restart shill with the network bridge in blacklist |
| # Disable shill against the bridge interface and wlan0. |
| # Shill will automatically ignore the virtual ethernet (veth*) interfaces. |
| BLACKLISTED_DEVICES=${DHCPD_IFACE},wlan0,arcbr0 |
| logger -t "${UPSTART_JOB}" "starting shill with ${BLACKLISTED_DEVICES} blacklisted" |
| start shill BLACKLISTED_DEVICES=${BLACKLISTED_DEVICES} |
| unlock_shill |
| |
| # Step 5 - set up routing between built-in external network interface and the bridge. |
| # This module is not loaded by default in the newer kernels. |
| if ! ensure_br_netfilter_loaded; then |
| logger -t "${UPSTART_JOB}" "Verified kernel module br_netfilter" |
| else |
| logger -t "${UPSTART_JOB}" "Error: Could not load module br_netfilter" |
| exit 1 |
| fi |
| |
| set_up_forwarding |
| |
| # Step 6 - wait for USB dongle to be ready. |
| wait_for_interface ${INTERFACE_LAN} |
| if [ -z "${INT_IFACE}" ]; then |
| logger -t "${UPSTART_JOB}" "Failed to find LAN network interface for \ |
| moblab's DUTs." |
| exit 0 |
| fi |
| |
| # Step 7 - restarts shill with USB dongle interface in blacklist. |
| # Disable shill against USB Ethernet dongle(either eth0 or eth1) and wlan0. |
| logger -t "${UPSTART_JOB}" "stopping shill" |
| lock_shill |
| stop shill |
| logger -t "${UPSTART_JOB}" \ |
| "starting shill with ${BLACKLISTED_DEVICES} and ${INT_IFACE} blacklisted" |
| start shill BLACKLISTED_DEVICES=${BLACKLISTED_DEVICES},${INT_IFACE} |
| unlock_shill |
| |
| # Step 8 - finish setting USB dongle interface into the bridge. |
| if [ -e /sys/class/net/${INT_IFACE} ]; then |
| brctl addif ${DHCPD_IFACE} ${INT_IFACE} |
| fi |
| |
| |
| if [ -e /sys/class/net/${INT_IFACE} ]; then |
| logger -t "${UPSTART_JOB}" "Bringing up ${INT_IFACE}" |
| ifconfig ${INT_IFACE} up |
| fi |
| |
| # Set up the routing after shill restarts. |
| set_up_forwarding |
| logger -t "$UPSTART_JOB" "Ending." |
| end script |