| #!/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 <path>/linux_ip_namespace_setup.sh -r <path_to_app> |
| # |
| # 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 separate terminal to simulate two devices communicating" |
| echo "across a network." |
| echo "Example:" |
| echo "--------" |
| echo "Terminal 1:" |
| echo "sudo <path>/$file_name -r <path>/<application_name>" |
| echo "" |
| echo "Terminal 2:" |
| echo "<path>/chip-repl" |
| 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 |