#!/bin/bash # I, Ian Kelling, follow the GNU license recommendations at # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small # programs. If a small program grows beyond 300 lines, I plan to switch # its license to GPL. # Copyright 2024 Ian Kelling # 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. # usage $0 [-c] [off] # off: Turn off static ip. # -c config only, don't tell networkmanager to change anything # -f force interface reup if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4 set -eE -o pipefail trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR m() { printf "%s\n" "$*"; "$@"; } ## begin arg parsing ## force=false off=false while [[ $1 ]]; do case $1 in -f) force=true ;; off) off=true ;; *) echo "$0: error unexpected argument: $1" >&2 exit 1 ;; esac shift done ## end arg parsing ## shopt -s nullglob wiredx=1 # device that has an eth0, but we aren't using it because it is # broken. We could just hardcode a mac comparison with `cat # /sys/class/net/eth0/address` but this is cooler. if [[ -e /sys/class/net/eth0 ]]; then bus_info=$(ethtool -i eth0 | awk '$1 == "bus-info:" { print $2 }') if [[ $bus_info != usb* ]]; then wiredx=2 fi fi eth_dev=eth$(( wiredx - 1 )) nm_con=$(nmcli device show $eth_dev | \ awk '$1 == "GENERAL.CONNECTION:" {out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}' ||:) if [[ ! $nm_con ]]; then nm_con="Wired connection $wiredx" fi con_exists=false if nmcli con | grep -q "^$nm_con " &>/dev/null; then con_exists=true fi declare -a args if $off; then if ! $con_exists; then echo "warning: no existing connection: $nm_con found in output of nmcli con" exit 0 fi tmpstr=$(nmcli con show "$nm_con" 2>/dev/null | sort -r | awk '$1 == "ipv4.method:" || $1 == "ipv4.addresses:" || $1 == "ipv4.gateway:" || $1 == "ipv4.dns:" || $1 == "GENERAL.STATE:" {print $2}' ) { read -r ipv4_method read -r ipv4_gateway read -r ipv4_dns read -r ipv4_addresses read -r state }<<<"$tmpstr" reup=false if [[ $state == activated ]]; then reup=true fi if [[ $ipv4_method != auto ]]; then args+=(ipv4.method auto) fi if [[ $ipv4_addresses != -- ]]; then args+=(-ipv4.addresses "$ipv4_addresses") fi if [[ $ipv4_dns != -- ]]; then args+=(-ipv4.dns "$ipv4_dns") fi if [[ $ipv4_gateway != -- ]]; then # undocumented in t11 man nmcli. guessed randomly args+=(ipv4.gateway 0.0.0.0) fi if (( ${#args[@]} >= 1 )); then m nmcli con mod "$nm_con" "${args[@]}" if $reup; then m nmcli con up "$nm_con" fi else echo "$0: found expected state, nothing to do." fi exit 0 fi if [[ $(dig +short @10.2.0.1 -x 10.2.0.2 2>&1 ||:) == kd.b8.nz. ]] \ && ip n show 10.2.0.1 | grep . &>/dev/null; then # we are at_home=true while read -r ip_suf host mac; do if [[ ! $ip_suf || $ip_suf == \#* ]]; then continue fi if [[ $mac != usb ]]; then continue fi if [[ $host == ${HOSTNAME}c ]]; then ip=10.2.0.$ip_suf/16 gateway=10.2.0.1 dns=8.8.8.4,8.8.8.8 break fi done

/dev/null; then apt-get install dig fi myip=$(dig +short @192.168.0.25 $HOSTNAME.office.fsf.org) if [[ ! $ip ]]; then echo "$0: error: didnt detect home network and failed to get office ip" exit 1 fi dns=192.168.0.10,192.168.0.25 gateway=192.168.0.1 ip=$myip/24 fi if ! $force && $con_exists; then current=$(nmcli con show "$nm_con" 2>/dev/null | sort -r | awk '$1 == "ipv4.method:" || $1 == "ipv4.addresses:" || $1 == "ipv4.gateway:" || $1 == "ipv4.dns:" {print $2}') expected="manual $gateway $dns $ip" if [[ $current == "$expected" ]]; then echo "$0: found expected state, nothing to do." exit 0 fi fi m nmcli con mod 'Wired connection 1' ipv4.method manual ipv4.addresses $ip ipv4.gateway $gateway ipv4.dns $dns state=$(nmcli con show "$nm_con" 2>/dev/null | awk '$1 == "GENERAL.STATE:" {print $2}') if [[ $state == activated ]]; then m nmcli con up "$nm_con" fi # example of down cli #nmcli con mod 'Wired connection 1' ipv4.method auto -ipv4.addresses 10.2.0.9/16 ipv4.gateway 0.0.0.0 -ipv4.dns "8.8.8.4,8.8.8.8" # FYI: the result of running, for example # nmcli con mod "Wired connection 1" \ # ipv4.method manual \ # ipv4.addresses "10.2.0.23/24" \ # ipv4.gateway "10.2.0.1" \ # ipv4.dns "8.8.8.4,8.8.8.8" # creates a fille named "/etc/NetworkManager/system-connections/Wired connection 1.nmconnection", # below. # # The nmcli man page says you should just edit files in that dir and # then run nmcli con reload to reread them all to load your changes, but # I've found that to be unreliable, the systemd journal would say # something like "reload happened" then nothing would change in the # connect that the file clearly modifies, so I switched over to using # the command line and just ignoring those files. # # I see no reason to keep the same file name, or a bunch of # setting that seem irrelevant, and empty sections don't seem to do # anything according to the man page. # # [connection] # id=Wired connection 1 # uuid=b0fb7694-dfe6-31a1-81fa-7c17b61515a7 # type=ethernet # interface-name=eth1 # timestamp=1715728264 # [ethernet] # [ipv4] # address1=10.2.0.23/16,10.2.0.1 # dns=8.8.8.4;8.8.8.8; # method=manual # [ipv6] # addr-gen-mode=stable-privacy # method=auto # [proxy]