Skip to content

Instantly share code, notes, and snippets.

@pcrockett
Created February 8, 2021 18:24
Show Gist options
  • Save pcrockett/c16b4028bc81a52f5d4069ea2e7a7f76 to your computer and use it in GitHub Desktop.
Save pcrockett/c16b4028bc81a52f5d4069ea2e7a7f76 to your computer and use it in GitHub Desktop.
Inspired by Matt's Traceroute, but more friendly for logging
#!/usr/bin/env bash
# LICENSE: GPLv3 - https://www.gnu.org/licenses/gpl-3.0.en.html
set -Eeuo pipefail
[[ "${BASH_VERSINFO[0]}" -lt 5 ]] && echo "Bash >= 5 required" && exit 1
readonly DEPENDENCIES=(ping)
readonly SCRIPT_NAME=$(basename "${0}")
function show_usage() {
printf "Usage: %s [OPTIONS...] [TARGET]\n" "${SCRIPT_NAME}" >&2
printf " -t, --ttl\t\t\tSet ping TTL\n" >&2
printf " -h, --help\t\t\tShow this help message then exit\n" >&2
}
function panic() {
>&2 echo "Fatal: ${*}"
exit 1
}
function is_installed() {
command -v "${1}" >/dev/null 2>&1
}
for dep in "${DEPENDENCIES[@]}"; do
is_installed "${dep}" || panic "Missing '${dep}'"
done
function is_set() {
# Use this like so:
#
# is_set "${VAR_NAME+x}" || show_usage_and_exit
#
# https://stackoverflow.com/a/13864829
test ! -z "${1}"
}
function parse_commandline() {
while [ "${#}" -gt "0" ]; do
local consume=1
case "${1}" in
-t|--ttl)
test "${#}" -gt 1 || panic "No TTL specified."
consume=2
ARG_TTL="${2}"
;;
-h|-\?|--help)
ARG_HELP="true"
;;
*)
if is_set "${ARG_TARGET+x}"; then
panic "Unexpected paramter: \"${1}\""
else
ARG_TARGET="${1}"
fi
;;
esac
shift ${consume}
done
}
parse_commandline "${@}"
if is_set "${ARG_HELP+x}"; then
show_usage
exit 1
fi
is_set "${ARG_TTL+x}" || panic "--ttl parameter is required"
test "${ARG_TTL}" -gt 0 || panic "--ttl must be a number greater than 0"
test "${ARG_TTL}" -le 255 || panic "--ttl must be a number less than or equal to 255"
function ping_target() {
local ping_output
ping_output=$(ping -c 1 -W 1 -t "${2}" "${1}")
if [[ "${ping_output}" =~ From\ (.+)\ icmp_seq=[0-9]+\ Time\ to\ live\ exceeded ]]; then
echo "${BASH_REMATCH[1]}"
elif [[ "${ping_output}" =~ [0-9]+\ bytes\ from\ (.+): ]]; then
echo "${BASH_REMATCH[1]} TARGET"
else
echo "FAIL"
fi
}
red="\e[1;31m"
green="\e[1;32m"
reset_color="\e[0m"
function print_results() {
local color="${reset_color}"
if [ "${1}" == OK ]; then
color="${green}"
elif [ "${1}" == FAIL ]; then
color="${red}"
fi
printf "${color}%s\t%s\t%s\t%s${reset_color}\n" "${@}"
}
while true; do
ping_time=$(date --iso-8601=seconds)
ttl=1
while [ "${ttl}" -le "${ARG_TTL}" ]; do
ip_addr="$(ping_target "${ARG_TARGET}" "${ttl}")"
if [ "${ip_addr}" == "FAIL" ]; then
print_results FAIL "${ping_time}" "ttl=${ttl}" "NA"
else
print_results OK "${ping_time}" "ttl=${ttl}" "${ip_addr}"
fi
if [[ "${ip_addr}" =~ \ TARGET$ ]]; then
# We have reached our target. We need to exit this while loop
# because we can't go further.
ttl="${ARG_TTL}"
fi
ttl=$((ttl + 1))
done
sleep 1
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment