#!/bin/bash # # hsci - Tool to manage HiperSockets Converged Interfaces (HSCI) # # Copyright IBM Corp. 2020 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # hsdev="" ndev="" hsci="" hscibr="" hscibp="" hsci_mac="" hsif_pnetid="" netif_pnetid="" hsci_pnetid="" ############################################################################## # Concept: # -------- # | hsci | # -------- # / \ # -------- -------- # | ndev | | hsdev | # -------- -------- # # Detail (bridge w/ bridgeports): # -------- # | hsci | # -------- # | # ---------------------- # | |hsci-bp| | # | hsci-br --------- | # | | # -------- -------- | # | ndev | | hsdev | | # ---------------------- function usage { cat <<-EOD Usage: hsci COMMAND [OPTION] This tool is designed to control and show HSCI (HiperSockets Converged Interfaces) settings. A HiperSockets interface and an external network interface are converged into an HSCI interface. COMMANDS add HIPERSOCKETS_DEV NET_DEV Adds an HSCI interface del HSCI_NAME Deletes an HSCI interface show Lists the configured HSCI interfaces OPTIONS: -v, --version Prints the version number of the hsci tool and exits -h, --help Displays the help information for the command EOD } function prereqs_check { if ! [ -x "$(command -v ip)" ]; then echo "Error: No iproute2 installed on this system" >&2 return 1 fi } function get_pnetid { local netdev=$1 local pnetid="" #### ROCE: if [ -e /sys/class/net/$netdev/device/util_string ]; then pnetids="$(cat /sys/class/net/$netdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" else #### OSA /HiperSockets: if [ -e /sys/class/net/$netdev/device/chpid ]; then chpid="$(cat /sys/class/net/$netdev/device/chpid | tr [:upper:] [:lower:])" pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" fi fi if [ "$pnetids" != "" ]; then port_if="$(cat /sys/class/net/$netdev/dev_port)" (( idx=16*$port_if+1 )) (( end=$idx+15 )) pnetid="$(echo "$pnetids" | cut -c $idx-$end | tr -d ' ')" fi echo $pnetid } function check_pnetids { if [ "$hsdev" != "" ]; then hsif_pnetid="$(get_pnetid $hsdev)" else hsif_pnetid="" fi if [ "$ndev" != "" ]; then netif_pnetid="$(get_pnetid $ndev)" else netif_pnetid="" fi #Check PNETIDs if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" != "$hsif_pnetid" ]; then echo "Error: $hsdev and $ndev have different PNETIDs! They are $hsif_pnetid and $netif_pnetid respectively" >&2 return 1 else if [ "$hsif_pnetid" != "" ]; then hsci_pnetid=$hsif_pnetid else hsci_pnetid=$netif_pnetid fi return 0 fi } function verify_precon { echo "Verifying net dev $ndev and HiperSockets dev $hsdev" if [ ! -e /sys/class/net/$hsdev ]; then echo "Error: $hsdev does not exist" >&2 return 1 fi if [ "$(cat /sys/class/net/$hsdev/device/card_type 2>/dev/null)" != "HiperSockets" ]; then echo "Error: $hsdev is not a HiperSockets device" >&2 return 1 fi if [ "$(cat /sys/class/net/$hsdev/device/layer2 2>/dev/null)" != "1" ]; then echo "Error: $hsdev is not in layer 2 mode" >&2 return 1 fi if [ ! -e /sys/class/net/$hsdev/device/vnicc/bridge_invisible ]; then echo "Error: Missing vnic-characteristics support" >&2 return 1 fi if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible 2>/dev/null)" == "n/a" ]; then echo "Error: $hsdev does not support vnicc" >&2 return 1 fi if [ $(ip link show $hsdev | grep UP | wc -l) -eq 0 ]; then echo "Error: $hsdev is not in state UP" >&2 return 1 fi if [ $(bridge -d link show dev $hsdev self | grep learning_sync | wc -l) -eq 0 ]; then echo "Error: $hsdev does not support attribute learning_sync" >&2 return 1 fi if [ $(ip link show $hsdev | grep master | wc -l) -ne 0 ]; then if [ $(ip link show $hsdev | grep "master $hsci" | wc -l) -eq 0 ]; then echo "Error: $hsdev is already a subordinate to another master" >&2 return 1 fi fi #Pre-verify net_dev if [ ! -e /sys/class/net/$ndev ]; then echo "Error: $ndev does not exist" >&2 return 1 fi if [ $(ip link show $ndev | grep UP | wc -l) -eq 0 ]; then echo "Error: $ndev is not in state UP" >&2 return 1 fi if [ $(ip link show $ndev | grep master | wc -l) -ne 0 ]; then if [ $(ip link show $ndev | grep "master $hsci" | wc -l) -eq 0 ]; then echo "Error: $ndev is already a subordinate to another master" >&2 return 1 fi fi #Check PNETIDs check_pnetids if [ $? -ne 0 ]; then return 1 fi return 0 } function clean_up { ip link del $hsci >/dev/null 2>&1 bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible >/dev/null 2>&1 bridge fdb del $hsci_mac dev $hsdev self local >/dev/null 2>&1 bridge fdb del $hsci_mac dev $ndev self local >/dev/null 2>&1 ip link del $hscibr >/dev/null 2>&1 } ############################################################################## ## add a new HSCI interface ############################################################################## function add_hsci { if [ $# != 2 ]; then echo "hsci: Invalid parameters" >&2 echo "Use 'hsci --help' for more information" >&2 return 1 fi hsdev=$1 ndev=$2 hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)" hscibr=hsci$hsci_postfix-br hscibp=hsci$hsci_postfix-bp hsci=hsci$hsci_postfix #### Verify preconditions verify_precon if [ $? -ne 0 ]; then return 1 fi echo "Adding $hsci with a HiperSockets dev $hsdev and an external dev $ndev" #### Create bridge (idempotent) if [ ! -e /sys/class/net/$hscibr ]; then # ageing_time of $hscibr defaults to 30000 (300 secs) ip link add name $hscibr type bridge stp_state 0 >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not create a bridge" >&2 return 1 fi else ip link set dev $hscibr type bridge stp_state 0 >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: $hscibr is not a bridge" >&2 return 1 fi fi #### Prepare hsdev # Set VNICC of hsdev to invisible #(mandatory for co-existence with HS-OSA bridges!) echo 1 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible #### Create bridge ports ip link set dev $ndev master $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not set master for $ndev" >&2 clean_up return 1 fi ip link set dev $hsdev master $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not set master for $hsdev" >&2 clean_up return 1 fi # Do not learn from ndev, but do learn from hsci-bp: # - First define hsdev and ndev as _isolated_ bridgeports # - Then turn on learning_sync on self on hsdev # - Then define hsci-bp as non-isolated veth bridgeport # no forwarding between ndev and hsdev -> isolated on # ndev is default for outgoing unknown targets -> flood on # no need to learn external LAN targets into fdb -> learning off bridge link set dev $ndev isolated on learning off flood on mcast_flood on >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set bridge attributes on $ndev" >&2 clean_up return 1 fi # no forwarding between ndev and hsdev -> isolated on # fdb will be populated by dev-to-bridge-notification, no need to learn # -> learning off # only send to hsdev, if listed in fdb -> flood off # don't send MC/BC on hsdev -> mcast_flood off bridge link set dev $hsdev isolated on learning off flood off mcast_flood off >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set bridge attributes on $hsdev" >&2 clean_up return 1 fi # NOTE: Although not required, BCs will be sent out on hsdev. # NOTE: We need to receive BCs on hsdev, as z/OS HSCI does ARP requests on HS. ip link set dev $hscibr up >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set $hscibr up" >&2 clean_up return 1 fi #### Turn on device for bridge notification #### Toggle is required to learn full list of HS targets, #### not only future changes. bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to turn on device for bridge notification" >&2 clean_up return 1 fi # define veth pair for hsci (idempotent) if [ ! -e /sys/class/net/$hsci ]; then ip link add dev $hsci type veth peer name $hscibp >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not create veth pair $hsci - $hscibp " >&2 clean_up return 1 fi else if [ $hsci@$hscibp: != "$(ip -o -d link show dev $hsci | awk '/veth/ {print $2}' )" ]; then echo "Error: $hsci@$hscibp is not a veth" >&2 clean_up return 1 fi fi ip link set dev $hscibp master $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to add $hscibp to $hscibr" >&2 clean_up return 1 fi bridge link set dev $hscibp isolated off learning on flood on mcast_flood on >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set bridge parameters for $hscibp" >&2 clean_up return 1 fi #### Set a static forwarding rule for hsci MAC, so hsci can be used as a #### single-MAC network interface without being subject to #### ageing and re-learning #### Wait for systemd to change the MAC of hsci, if it wants to: sleep 1 hsci_mac="$(cat /sys/class/net/$hsci/address)" #### (idempotent) if [ $(bridge fdb show dev $hscibp | grep "$hsci_mac master $hscibr static" | wc -l) -eq 0 ]; then bridge fdb add $hsci_mac dev $hscibp master static if [ $? -ne 0 ]; then echo "Error: Failed to set $hsci_mac to $hscibr fdb" >&2 clean_up return 1 fi fi # Bridge-to-device learning will set this MAC on hsdev and ndev. # Old kernel code doesn't do hsci bridge-to-device learning. # In this case: Set hsci_mac as local MAC of hsdev and ndev, # so at least the single-MAC scenario works. if [ $(bridge fdb show dev $hsdev | grep "$hsci_mac self permanent" | wc -l) -eq 0 ]; then echo "Warning: $hsci will support only its current static MAC address. Please upgrade your kernel to the latest level." >&2 bridge fdb add $hsci_mac dev $hsdev self local if [ $? -ne 0 ]; then echo "Error: Failed to add $hsci_mac to $hsdev" >&2 clean_up return 1 fi fi if [ $(bridge fdb show dev $ndev | grep "$hsci_mac self permanent" | wc -l) -eq 0 ]; then bridge fdb add $hsci_mac dev $ndev self local if [ $? -ne 0 ]; then echo "Error: Failed to add $hsci_mac to $ndev" >&2 clean_up return 1 fi fi #### Set veth pair to UP ip link set dev $hscibp up >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set $hscibp up" >&2 clean_up return 1 fi ip link set dev $hsci up >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set $hsci up" >&2 clean_up return 1 fi echo "Added HSCI interface $hsci" return 0 } ############################################################################## ## Delete HSCI ############################################################################## function del_hsci { if [ $# != 1 ]; then echo "hsci: invalid parameters" >&2 echo "Use 'hsci --help' for more information" >&2 return 1 fi hsci=$1 if [ $(ip link show dev $hsci | wc -l) -eq 0 ]; then echo "Error: $hsci does not exit" >&2 return 1 fi hsci_mac="$(cat /sys/class/net/$hsci/address)" #### Find hscibp and hscibr hscibp="$(ip -o link show dev $hsci | awk '{print $2}')" if [[ $hscibp != *@* ]]; then # $hsci has no HSCI veth peer echo "Warning: $hsci may have been created by an older version of hsci" >&2 mvp=1 hscibp="" hscibr=$hsci else mvp=0 hscibp=${hscibp##*@} hscibp=${hscibp%:} echo "$hsci is paired with $hscibp" >&2 hscibr="$(ip link show dev $hscibp | awk '{for(x=1;x&2 fi if [ "$ndev" == "" ]; then echo "Error: $hsci has no active external port" >&2 fi echo "Deleting HSCI interface $hsci with HiperSockets interface $hsdev and external interface $ndev" #### Delete veth before resetting learning_sync and deleting bridge, #### so fdb entries are cleaned up (synced)! ip link del $hsci >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to delete $hsci" >&2 fi # Bridge-to-device learning will remove learned MACs from hsdev and ndev. # Old kernel code doesn't do hsci bridge-to-device learning. # In this case: Remove the hsci_mac that was added during 'add' from # hsdev and ndev. if [ "$hsdev" != "" ] && [ $mvp -eq 0 ]; then if [ $(bridge fdb show dev $hsdev | grep "$hsci_mac self permanent" | wc -l) -ne 0 ]; then echo "Warning: It seems your kernel does not support all hsci features, please upgrade." >&2 bridge fdb del $hsci_mac dev $hsdev self local if [ $? -ne 0 ]; then echo "Error: Failed to delete $hsci_mac from $hsdev" >&2 fi fi fi # In the mvp case hsci_mac == hs_mac and was only set on ndev. if [ "$ndev" != "" ]; then if [ $(bridge fdb show dev $ndev | grep "$hsci_mac self permanent" | wc -l) -ne 0 ]; then bridge fdb del $hsci_mac dev $ndev self local if [ $? -ne 0 ]; then echo "Error: Failed to delete $hsci_mac from $ndev" >&2 fi fi fi #### Reset learning_sync if [ "$hsdev" != "" ]; then bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to turn off learning_sync on $hsdev" >&2 fi fi #### Delete bridge if [ "$hscibr" != "" ]; then ip link del $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to delete $hscibr" >&2 fi fi if [ "$hsdev" != "" ]; then echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible fi echo "Deleted $hsci" return 0 } ############################################################################## ## Show HSCI ############################################################################## function print_row { if [ $mvp -eq 0 ]; then printf '%-8s %-16s %-15s %-15s\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev" else printf '%-8s %-16s %-15s %-15s (v1)\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev" fi } function list_active { hsci="" hsdev=$1 ndev="" hscibp="" mvp=1 hsci_pnetid="$(get_pnetid $hsdev)" hscibr="$(ip link show dev $hsdev | awk '{for(x=1;x&2 print_row return 1 fi hsci=${hsci##*@} hsci=${hsci%:} print_row return 0 } function print_header { if [ $header -eq 0 ]; then echo "HSCI PNET_ID HiperSockets External " echo "------------------------------------------------------------" header=1 fi } function list_one { local hsnetdev=$1 if [ $(bridge -d link show dev $hsnetdev 2>/dev/null | grep "learning_sync on" | wc -l) -ne 0 ]; then print_header list_active $hsnetdev fi return 0 } function show_hsci { if [ $# != 0 ]; then echo "hsci: invalid parameters" >&2 echo "Use 'hsci --help' for more information" >&2 return 1 fi header=0 for hs_net_dev in $(ls -1 /sys/class/net/); do list_one $hs_net_dev done return 0 } #============================================================================== function print_version() { echo "hsci utility: version %S390_TOOLS_VERSION%" echo "Copyright IBM Corp. 2020" } ############################################################################## ##### Main ############################################################################## prereqs_check args="$(getopt -u -o hv -l help,version -- $*)" [ $? -ne 0 ] && exit 2 set -- $args while true; do case $1 in -v | --version) print_version exit 0 ;; -h | --help) usage exit 0 ;; --) ;; add) shift add_hsci "$@" exit $? ;; del) shift del_hsci "$@" exit $? ;; show) shift show_hsci "$@" exit $? ;; *) echo "hsci: Please specify a valid command or option" >&2 echo "Use 'hsci --help' for more information" >&2 exit 1 esac shift done