From 4183353cc8dae3af89cd293e022e8edd567b7d4f Mon Sep 17 00:00:00 2001 From: C Freeman Date: Mon, 21 Jun 2021 14:56:57 -0400 Subject: [PATCH] Script for setting up linux namespaces (#7256) * Script for setting up linux namespaces This will let developers run a controller and device on the same machines. * Apply suggestions from code review Co-authored-by: Tennessee Carmel-Veilleux * reformatting. * Add example and header. * Restyled by shellharden * Restyled by shfmt Co-authored-by: Tennessee Carmel-Veilleux Co-authored-by: Restyled.io --- scripts/tools/linux_ip_namespace_setup.sh | 233 ++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 scripts/tools/linux_ip_namespace_setup.sh diff --git a/scripts/tools/linux_ip_namespace_setup.sh b/scripts/tools/linux_ip_namespace_setup.sh new file mode 100755 index 00000000000000..1520efe1bc3368 --- /dev/null +++ b/scripts/tools/linux_ip_namespace_setup.sh @@ -0,0 +1,233 @@ +#!/usr/bin/env bash +# +# +# Copyright (c) 2021 Project CHIP Authors +# +# 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. +# +# +# Description: +# This is a utility script that can be used by developers to do +# simulate a device and controller on the same linux machine. This +# script is not intended to be used in a testing framework as-is +# because it requires root access to set up network namespaces. +# +# To use this script, compile the required device example, and +# run inside the namespace using +# sudo /linux_ip_namespace_setup.sh -r +# +# The controller can then be started in a new terminal and will +# be able to communicate with the application as if it were on +# a separate network. +# + +NAMESPACE="MatterTester" +HOST_SIDE_IF_NAME="heth0" +NAMESPACE_SIDE_IF_NAME="neth0" +BRIDGE_NAME="nbridge" +BRIDGE_ADDR="192.168.4.50" +NAMESPACE_ADDR="192.168.4.45" +HOST_IPV6_ADDR=fc00::1 +BRIDGE_IPV6_ADDR=fc00::b +NAMESPACE_IPV6_ADDR=fc00::a + +function run_setup() { + # Create namespace. + ip netns add "$NAMESPACE" + + # Create two virtual interfaces and link them - one on host side, one on namespace side. + ip link add "$HOST_SIDE_IF_NAME" type veth peer name "$NAMESPACE_SIDE_IF_NAME" + + # Give the host a known IPv6 addr and set the host side up + ip -6 addr add "$HOST_IPV6_ADDR"/64 dev "$HOST_SIDE_IF_NAME" + ip link set "$HOST_SIDE_IF_NAME" up + + # Associate namespace IF with the namespace + ip link set "$NAMESPACE_SIDE_IF_NAME" netns "$NAMESPACE" + + # Give the namespace IF an address (something nothing else is using) and set it up + echo "Adding address for namespace IF" + ip netns exec "$NAMESPACE" ip -6 addr add "$NAMESPACE_IPV6_ADDR"/64 dev "$NAMESPACE_SIDE_IF_NAME" + ip netns exec "$NAMESPACE" ip link set dev "$NAMESPACE_SIDE_IF_NAME" up + + # Add a route to the namespace to go through the bridge + echo "Setting routes for namespace" + ip netns exec "$NAMESPACE" ip -6 route add default dev "$NAMESPACE_SIDE_IF_NAME" + + echo "Setup complete." +} + +function run_add_ipv4() { + # Give the namespace an IPv4 address + ip netns exec "$NAMESPACE" ip addr add "$NAMESPACE_ADDR"/24 dev "$NAMESPACE_SIDE_IF_NAME" + + # Add a bridge, give it an address (something nothing else is using) + echo "Setting up bridge" + ip link add name "$BRIDGE_NAME" type bridge + ip -6 addr add "$BRIDGE_IPV6_ADDR"/64 dev "$BRIDGE_NAME" + ip addr add "$BRIDGE_ADDR"/24 brd + dev "$BRIDGE_NAME" + + # For ipv6 and ipv4 to work together, need the bridge to ignore the ipv6 packets (DROP here means don't bridge) + ebtables-legacy -t broute -A BROUTING -p ipv6 -j DROP -i "$HOST_SIDE_IF_NAME" + ip link set "$BRIDGE_NAME" up + + # Connect the host side to the bridge, so now we have bridge <-> host_side_if <-> namespace_if + echo "Connecting host virtual IF to bridge" + ip link set "$HOST_SIDE_IF_NAME" master "$BRIDGE_NAME" + + #ip netns exec ${NAMESPACE} ip route add default via ${BRIDGE_ADDR} dev ${NAMESPACE_SIDE_IF_NAME} +} + +function run_cmd() { + # Start the app in the namespace + echo "Running $1 in namespace." + ip netns exec "$NAMESPACE" "$1" +} + +function run_cleanup() { + # Deleting the namespace will remove the namespace and peer'd interfaces to + have_ebtables_legacy=$1 + ip netns delete "$NAMESPACE" + if ifconfig | grep "$BRIDGE_NAME"; then + if [ "$have_ebtables_legacy" = true ]; then + # Just try to drop the additional rule - it references our interface + # so if it's there, we added it. + ebtables-legacy -t broute -D BROUTING -p ipv6 -j DROP -i "$HOST_SIDE_IF_NAME" >/dev/null + fi + ip link delete dev "$BRIDGE_NAME" type bridge + fi +} + +function help() { + echo "Usage: $file_name [ options ... ]" + echo "" + + echo "This script is used to set up linux namespaces for Matter device testing" + echo "between a controller and device on the same linux machine." + echo "" + echo "To use this script, run the device code in a namespace using the -r command" + echo "and a controller in a seperate terminal to simulate two devices communicating" + echo "across a network." + echo "Example:" + echo "--------" + echo "Terminal 1:" + echo "sudo /$file_name -r /" + echo "" + echo "Terminal 2:" + echo "/chip-device-ctrl" + echo "" + echo "This script requires sudo for setup and requires access to ebtables-legacy" + echo "to set up dual ipv4/ipv6 namespaces. Defaults to ipv6 only." + echo "" + + echo "Options: + -h, --help Display this information. + -s, --setup Setup an IP namespace. Will run cleanup if namespace exists. + -4, --ipv4 Add ipv4 support. + -r, --run filename Run file in the namespace. Will setup namespace if required. + -c, --cleanup Delete namespace and routes +" +} + +declare setup=false +declare filename="" +declare run=false +declare cleanup=false +declare ipv4=false + +file_name=${0##*/} + +while (($#)); do + case $1 in + --help | -h) + help + exit 1 + ;; + --setup | -s) + setup=true + ;; + --run | -r) + run=true + filename=$2 + shift + ;; + --cleanup | -c) + cleanup=true + ;; + --ipv4 | -4) + ipv4=true + ;; + -*) + help + echo "Unknown Option \"$1\"" + exit 1 + ;; + esac + shift +done + +if [[ $EUID -ne 0 ]]; then + echo "You must run this script with superuser privileges." + exit 1 +fi + +if ifconfig | grep "$HOST_SIDE_IF_NAME"; then + issetup=true +else + issetup=false +fi + +if [ "$setup" = false ] && [ "$run" = false ] && [ "$cleanup" = false ]; then + echo "Must specify one or more of -s, -r, -c." + exit 1 +fi + +if command -v ebtables-legacy >/dev/null; then + have_ebtables_legacy=true +else + have_ebtables_legacy=false +fi + +if [ "$ipv4" = true ] && [ "$have_ebtables_legacy" = false ]; then + echo "To set up namespaces with ipv4/ipv6 connectivity, ebtables-legacy" + echo "is required. For example, to install on machines using APT:" + echo "sudo apt-get install ebtables" + exit 1 +fi + +if [ "$run" = true ]; then + if [ "$issetup" = false ]; then + setup=true + fi +fi + +if [ "$setup" = true ]; then + if [ "$issetup" = true ]; then + cleanup=true + fi +fi + +if [ "$cleanup" = true ]; then + run_cleanup "$have_ebtables_legacy" +fi + +if [ "$setup" = true ]; then + run_setup + if [ "$ipv4" = true ]; then + run_add_ipv4 + fi +fi + +if [ "$run" = true ]; then + run_cmd "$filename" +fi