mirror of
https://github.com/captn3m0/Scripts.git
synced 2024-09-27 22:22:53 +00:00
406 lines
15 KiB
Plaintext
406 lines
15 KiB
Plaintext
|
#!/bin/sh
|
||
|
#
|
||
|
# SOURCE: https://github.com/ocochard/graphpath
|
||
|
#
|
||
|
# Graphpath generates an ASCII network diagram from the route table of a Unix/Linux
|
||
|
# https://bsdrp.net
|
||
|
#
|
||
|
# Copyright (c) 2018, Olivier Cochard-Labbé (olivier@cochard.me)
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions
|
||
|
# are met:
|
||
|
# 1. Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer in the
|
||
|
# documentation and/or other materials provided with the distribution.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
|
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
# SUCH DAMAGE.
|
||
|
|
||
|
|
||
|
########################################################
|
||
|
# ## Concept documentation ##
|
||
|
#
|
||
|
# From this host ('me'), there are mainly 2 main families diagrams:
|
||
|
#
|
||
|
# - The first model family, when source and destination are towards differents
|
||
|
# interfaces:
|
||
|
#
|
||
|
# +-----+ +-----+ +-----+ +-----+
|
||
|
# | src | | src | | src | | src |
|
||
|
# +-----+ +-----+ +-----+ +-----+
|
||
|
# | | | |
|
||
|
# +----+ +--------+ +--------+ +----+
|
||
|
# | me | | router | | router | | me |
|
||
|
# +----+ +--------+ +--------+ +----+
|
||
|
# | | | |
|
||
|
# +-----+ +----+ +----+ +--------+
|
||
|
# | dst | | me | | me | | router |
|
||
|
# +-----+ +----+ +----+ +--------+
|
||
|
# | | |
|
||
|
# +-----+ +--------+ +-----+
|
||
|
# | dst | | router | | dst |
|
||
|
# +-----+ +--------+ +-----+
|
||
|
# |
|
||
|
# +-----+
|
||
|
# | dst |
|
||
|
# +-----+
|
||
|
#
|
||
|
# - The second model family, when source and destination are towards the same
|
||
|
# interface:
|
||
|
#
|
||
|
# +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
|
||
|
# | src | | dst | | src | | dst | | src | | dst | | src | | dst | | src | | dst |
|
||
|
# +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
|
||
|
# | | | | | | | | | |
|
||
|
# | | | | | | | | -+----+---+-
|
||
|
# | | | | | | | | |
|
||
|
# -+--+----+- | | | | +--------+ +--------+ |
|
||
|
# | | | | | | router | | router | |
|
||
|
# +--------+ +--------+ | | +--------+ +--------+ +--------+ |
|
||
|
# | router | | router | | | | router | | | |
|
||
|
# +--------+ +--------+ | | +--------+ -+-----+----+- +----+
|
||
|
# | | | | | | | me |
|
||
|
# +----+ -+--+-----+- -+--+---+- +----+ +----+
|
||
|
# | me | | | | me |
|
||
|
# +----+ +----+ +----+ +----+
|
||
|
# | me | | me |
|
||
|
# +----+ +----+
|
||
|
#
|
||
|
# Looking at these diagrams, we can extract 3 type of boxes:
|
||
|
# - standard and simple unique box: draw_block()
|
||
|
#
|
||
|
# |
|
||
|
# +----------------+
|
||
|
# | src/dst/router |
|
||
|
# | IP: address |
|
||
|
# | ARP: MAC |
|
||
|
# +----------------+
|
||
|
# |
|
||
|
#
|
||
|
# - A dual box on the same level: draw_2blocks()
|
||
|
#
|
||
|
# +---------------+ +---------------+
|
||
|
# | src or router | | dst or router |
|
||
|
# +---------------+ +---------------+
|
||
|
#
|
||
|
|
||
|
# - A 'me' block: draw_me()
|
||
|
#
|
||
|
# |
|
||
|
# +---------+
|
||
|
# | if_name |
|
||
|
# | if_ip |
|
||
|
# | route |
|
||
|
# (next only if src_int != dst_int)
|
||
|
# | route |
|
||
|
# | if_ip |
|
||
|
# | if_name |
|
||
|
# +---------+
|
||
|
# |
|
||
|
########################################################
|
||
|
|
||
|
# Forcing clean script
|
||
|
set -eu
|
||
|
|
||
|
# Functions definitions
|
||
|
|
||
|
# A usefull function (from: http://code.google.com/p/sh-die/)
|
||
|
die() { echo -n "EXIT: " >&2; echo "$@" >&2; exit 1; }
|
||
|
|
||
|
usage() {
|
||
|
echo "Usage:"
|
||
|
echo "$0 [-v] source-IP destination-IP"
|
||
|
echo " -v: display version number"
|
||
|
echo "Example:"
|
||
|
echo " $0 198.18.0.10 198.19.0.10"
|
||
|
exit 0
|
||
|
}
|
||
|
|
||
|
draw_block () {
|
||
|
# Draw one block for source&destination host and router
|
||
|
local model=$1 # src or dst, used for drawing link on the upper or lower side
|
||
|
local label=$2 # text to display into the block
|
||
|
local ip=$3 # IP address for src & dst host (=direct for a router)
|
||
|
local gateway=$4 # IP address for a router
|
||
|
local gateway_arp=$5 # ARP cache if directly connected to "me" block
|
||
|
local position=$6
|
||
|
left=""
|
||
|
right=""
|
||
|
# There is a special case: If ip=direct, this mean this block is useless
|
||
|
[ "$ip" = direct ] && return 0
|
||
|
# If in second family mode, need to move or add vertical line
|
||
|
[ "${position}" = "left" ] && left=" | "
|
||
|
[ "${position}" = "right" ] && right=" |"
|
||
|
# If label is "ROUTER TOWARDS DESTINATION", need to move this block to the left
|
||
|
#echo ${label} | grep -q "ROUTER TOWARDS DESTINATION" && left=" | "
|
||
|
# If label is "ROUTER TOWARDS SOURCE", need to add an vertical line to the left"
|
||
|
#echo ${label} | grep -q "ROUTER TOWARDS SOURCE" && right=" |"
|
||
|
[ "$model" = "dst" -a -z "${left}" ] && echo "${left} |${right}"
|
||
|
echo "${left}+----------------------------+${right}"
|
||
|
printf '%s| %-26s |%s\n' "${left}" "${label}" "${right}"
|
||
|
printf '%s| IP: %-21s|%s\n' "${left}" ${ip} "${right}"
|
||
|
[ "$gateway" = "direct" ] && printf '%s| ARP: %-21s|%s\n' "${left}" "${gateway_arp}" "${right}"
|
||
|
echo "${left}+----------------------------+${right}"
|
||
|
[ "$model" = "src" -o -n "${left}" ] && echo "${left} | ${right}" || true
|
||
|
}
|
||
|
|
||
|
draw_2blocks () {
|
||
|
# Draw two blocks on the same level
|
||
|
# Second model family
|
||
|
local label1=$1
|
||
|
local label2=$2
|
||
|
echo "+----------------------------+ +----------------------------+"
|
||
|
printf '| %-26s | | %-26s |\n' "${label1}" "${label2}"
|
||
|
if echo "${label1}" | grep -q 'ROUTER'; then
|
||
|
printf '| IP: %-18s | | IP: %-18s |\n' ${src_gateway} ${dst_gateway}
|
||
|
printf '| ARP: %-18s | | ARP: %-18s |\n' ${src_gateway_arp} ${dst_gateway_arp}
|
||
|
else
|
||
|
printf '| IP: %-18s | | IP: %-18s |\n' ${src_ip} ${dst_ip}
|
||
|
fi
|
||
|
if [ "${src_gateway}" = "direct" ]; then
|
||
|
printf '| ARP: %-18s |' ${src_gateway_arp}
|
||
|
elif [ "${dst_gateway}" = "direct" ]; then
|
||
|
printf '| |'
|
||
|
fi
|
||
|
if [ "${dst_gateway}" = "direct" ]; then
|
||
|
printf ' | ARP: %-18s |\n' ${dst_gateway_arp}
|
||
|
elif [ "${src_gateway}" = "direct" ]; then
|
||
|
printf ' | |\n'
|
||
|
fi
|
||
|
echo "+----------------------------+ +----------------------------+"
|
||
|
echo " | |"
|
||
|
}
|
||
|
|
||
|
draw_me () {
|
||
|
# Draw this device block
|
||
|
echo "+----------------------------+"
|
||
|
printf '| IF: %-18s |\n' ${src_interface}
|
||
|
printf '| MAC: %-18s |\n' ${src_interface_mac}
|
||
|
printf '| IP: %-18s |\n' ${src_interface_ip}
|
||
|
printf '| net: %-18s |\n' ${src_destination}
|
||
|
[ -n "${src_mask}" ] && printf '| mask: %-18s |\n' ${src_mask}
|
||
|
echo "| |"
|
||
|
echo "| THIS ${device} |"
|
||
|
if [ "${src_interface}" != "${dst_interface}" ]; then
|
||
|
# First model type, need to draw lower part
|
||
|
echo "| |"
|
||
|
printf '| net: %-18s |\n' ${dst_destination}
|
||
|
[ -n "${dst_mask}" ] && printf '| mask: %-18s |\n' ${dst_mask}
|
||
|
printf '| IP: %-18s |\n' ${dst_interface_ip}
|
||
|
printf '| MAC: %-18s |\n' ${dst_interface_mac}
|
||
|
printf '| IF: %-18s |\n' ${dst_interface}
|
||
|
fi
|
||
|
echo "+----------------------------+"
|
||
|
}
|
||
|
|
||
|
get_bsd() {
|
||
|
# Extract routes data
|
||
|
# Check if router enabled
|
||
|
[ $(sysctl -n net.inet.ip.forwarding) -eq 0 ] && forwarding=false || forwarding=true
|
||
|
[ $(sysctl -n net.inet6.ip6.forwarding) -eq 0 ] && forwarding6=false || forwarding6=true
|
||
|
|
||
|
# Populate src_* and dst_* variables
|
||
|
for i in src dst; do
|
||
|
eval "
|
||
|
# if route didn't fill _gateway, this mean its directly connected
|
||
|
${i}_gateway='direct'
|
||
|
# some route crossing PPP tun doesn't have _mask
|
||
|
${i}_mask='255.255.255.255'
|
||
|
# call route only once
|
||
|
tmp=\$(mktemp)
|
||
|
route -n get \${${i}_ip} > \${tmp} || die \"Route towards \${${i}_ip} not found\"
|
||
|
# Output are like this one:
|
||
|
# route to: 2.2.2.2
|
||
|
#destination: 0.0.0.0
|
||
|
# mask: 0.0.0.0
|
||
|
# gateway: 192.168.1.100
|
||
|
# fib: 0
|
||
|
# interface: bxe3
|
||
|
# flags: <UP,GATEWAY,DONE,STATIC>
|
||
|
# recvpipe sendpipe ssthresh rtt,msec mtu weight expire
|
||
|
# 0 0 0 0 1500 1 0
|
||
|
while read line; do
|
||
|
data=\$(echo \$line | cut -d ':' -f 1)
|
||
|
case \$data in
|
||
|
\"route to\")
|
||
|
${i}_routeto=\$(echo \$line | cut -d ':' -f 2 | tr -d ' ')
|
||
|
;;
|
||
|
destination)
|
||
|
${i}_destination=\$(echo \$line | cut -d ':' -f 2 | tr -d ' ')
|
||
|
;;
|
||
|
mask)
|
||
|
${i}_mask=\$(echo \$line | cut -d ':' -f 2 | tr -d ' ')
|
||
|
;;
|
||
|
gateway)
|
||
|
${i}_gateway=\$(echo \$line | cut -d ':' -f 2 | tr -d ' ')
|
||
|
;;
|
||
|
fib)
|
||
|
${i}_fib=\$(echo \$line | cut -d ':' -f 2)
|
||
|
;;
|
||
|
interface)
|
||
|
${i}_interface=\$(echo \$line | cut -d ':' -f 2 | tr -d ' ')
|
||
|
${i}_interface_mac=\$(ifconfig \${${i}_interface} | grep -E 'ether|lladdr|address' | cut -d ' ' -f 2)
|
||
|
${i}_interface_ip=\$(ifconfig \${${i}_interface} | grep -w inet | cut -d ' ' -f 2)
|
||
|
;;
|
||
|
flags)
|
||
|
${i}_flags=\$(echo \$line | cut -d ':' -f 2)
|
||
|
;;
|
||
|
esac
|
||
|
done < \${tmp}
|
||
|
[ \"\${${i}_gateway}\" = \"direct\" ] && lookup=\${${i}_routeto} || lookup=\${${i}_gateway}
|
||
|
case \${OS} in
|
||
|
OpenBSD)
|
||
|
${i}_gateway_arp=\$(arp -n \${lookup} | grep \${lookup} | tr -s ' ' | cut -d ' ' -f 2)
|
||
|
;;
|
||
|
*)
|
||
|
${i}_gateway_arp=\$(arp -n \${lookup} | cut -d ' ' -f 4)
|
||
|
;;
|
||
|
esac
|
||
|
[ \"\${${i}_gateway_arp}\" = \"no\" ] && ${i}_gateway_arp='empty' || true
|
||
|
"
|
||
|
rm ${tmp} || die "can't delete ${tmp}"
|
||
|
done
|
||
|
}
|
||
|
|
||
|
get_linux () {
|
||
|
# Extract routes data from a Linux
|
||
|
# ip route has a non consistent output: STUPID Linux !
|
||
|
# Check if router enabled
|
||
|
[ $(cat /proc/sys/net/ipv4/ip_forward) -eq 0 ] && forwarding=false || forwarding=true
|
||
|
[ $(cat /proc/sys/net/ipv6/conf/all/forwarding) -eq 0 ] && forwarding6=false || forwarding6=true
|
||
|
|
||
|
# Populate src_* and dst_* variables
|
||
|
for i in src dst; do
|
||
|
eval "
|
||
|
# if route didn't fill _gateway, this mean its directly connected
|
||
|
${i}_gateway='direct'
|
||
|
# No mask on linux (using a / into the destination variable)
|
||
|
${i}_mask=''
|
||
|
# call route only once
|
||
|
tmp=\$(mktemp)
|
||
|
ip -o route get \${${i}_ip} > \${tmp}
|
||
|
# Output are like these:
|
||
|
# 2.2.2.3 via 10.253.52.126 dev eth2 src 10.253.52.115 \ cache
|
||
|
# 192.168.237.116 dev eth1.337 src 192.168.237.115 \ cache
|
||
|
# 10.253.52.124 dev eth2 src 10.253.52.115 \ cache
|
||
|
# local 127.0.0.1 dev lo src 127.0.0.1
|
||
|
read ip via gateway dev if src if_ip < \${tmp}
|
||
|
if [ \"\${via}\" = \"via\" ]; then
|
||
|
${i}_routeto=\${${i}_ip}
|
||
|
${i}_gateway=\${gateway}
|
||
|
${i}_gateway_arp=\$(ip neigh show \${${i}_gateway} | cut -d ' ' -f 5)
|
||
|
${i}_interface=\${if}
|
||
|
${i}_interface_ip=\$(echo \${if_ip} | cut -d ' ' -f1)
|
||
|
elif [ \"\${via}\" = \"dev\" ]; then
|
||
|
${i}_routeto=\${${i}_ip}
|
||
|
${i}_interface=\${gateway}
|
||
|
${i}_interface_ip=\$(echo \${if} | cut -d ' ' -f1)
|
||
|
else
|
||
|
echo "WARNING: Not supported condition!"
|
||
|
${i}_routeto=\${via}
|
||
|
${i}_interface=\${dev}
|
||
|
${i}_interface_ip=\${via}
|
||
|
fi
|
||
|
${i}_interface_mac=\$(ip link show \${${i}_interface}| grep ether | tr -s ' ' | cut -d ' ' -f 3)
|
||
|
# We still need other data (destination subnet and mask)
|
||
|
# need to use another ip commande for getting the subnet matching
|
||
|
# ip route show to match 192.168.229.58
|
||
|
# default via 10.253.52.126 dev eth2 onlink
|
||
|
# 192.168.229.56/29 via 192.168.237.100 dev eth1.337
|
||
|
${i}_destination=\$(ip -o route show to match \${${i}_ip} | grep \${if} | cut -d ' ' -f1)
|
||
|
if [ \"\${${i}_gateway}\" = \"direct\" ]; then
|
||
|
${i}_gateway_arp=\$(ip neigh show \${${i}_routeto} | cut -d ' ' -f 5)
|
||
|
[ \"\${${i}_gateway_arp}\" = \"no\" ] && ${i}_gateway_arp='empty' || true
|
||
|
fi
|
||
|
"
|
||
|
rm ${tmp} || die "can't delete ${tmp}"
|
||
|
done
|
||
|
}
|
||
|
|
||
|
### Main function ###
|
||
|
|
||
|
version="1.0"
|
||
|
|
||
|
if [ "$#" -eq 1 ]; then
|
||
|
if [ "$1" = "-v" ]; then
|
||
|
echo "Version ${version}"
|
||
|
exit 0
|
||
|
else
|
||
|
usage
|
||
|
fi
|
||
|
fi
|
||
|
[ "$#" -ne 2 ] && usage
|
||
|
|
||
|
src_ip=$1
|
||
|
dst_ip=$2
|
||
|
device="ROUTER"
|
||
|
|
||
|
# Small checks
|
||
|
[ "${src_ip}" = "${dst_ip}" ] && die "Same source and destination IP addresses"
|
||
|
|
||
|
OS=$(uname)
|
||
|
case ${OS} in
|
||
|
FreeBSD|OpenBSD|NetBSD)
|
||
|
get_bsd
|
||
|
;;
|
||
|
Linux)
|
||
|
get_linux
|
||
|
;;
|
||
|
*)
|
||
|
die "This script was not tested on ${OS}"
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
if [ $forwarding = false -o $forwarding6 = false ]; then
|
||
|
device="HOST "
|
||
|
fi
|
||
|
|
||
|
|
||
|
[ "${device}" = "HOST" ] && echo "This tool is mainly designed for drawing router or firewall routing view"
|
||
|
|
||
|
### Start ASCII drawing
|
||
|
|
||
|
if [ "${src_interface}" != "${dst_interface}" ]; then
|
||
|
# First model family
|
||
|
if [ "${src_ip}" != "${src_interface_ip}" ]; then
|
||
|
draw_block src 'SOURCE HOST' "${src_ip}" "${src_gateway}" "${src_gateway_arp}" ""
|
||
|
[ "${src_gateway}" != "direct" ] && draw_block src 'ROUTER TOWARDS SOURCE' "${src_gateway}" direct "${src_gateway_arp}" ""
|
||
|
fi
|
||
|
draw_me
|
||
|
if [ "${dst_ip}" != "${dst_interface_ip}" ]; then
|
||
|
[ "${dst_gateway}" != "direct" ] && draw_block dst 'ROUTER TOWARDS DESTINATION' "${dst_gateway}" direct "${dst_gateway_arp}" ""
|
||
|
draw_block dst 'DESTINATION HOST' "${dst_ip}" "${dst_gateway}" "${dst_gateway_arp}" ""
|
||
|
fi
|
||
|
else
|
||
|
# Second model family
|
||
|
draw_2blocks 'SOURCE HOST' 'DESTINATION HOST'
|
||
|
if [ "${src_gateway}" = "${dst_gateway}" ]; then
|
||
|
echo " --+---+-----------------------------+---"
|
||
|
echo " | "
|
||
|
draw_block src 'ROUTER' "${src_gateway}" direct "${src_gateway_arp}" ""
|
||
|
else
|
||
|
if [ "${src_gateway}" != "direct" -a "${dst_gateway}" = "direct" ]; then
|
||
|
draw_block src 'ROUTER TOWARDS SOURCE' "${src_gateway}" direct "${src_gateway_arp}" "right"
|
||
|
elif [ "${dst_gateway}" != "direct" -a "${src_gateway}" = "direct" ]; then
|
||
|
draw_block dst 'ROUTER TOWARDS DESTINATION' "${dst_gateway}" direct "${dst_gateway_arp}" "left"
|
||
|
else
|
||
|
draw_2blocks 'ROUTER TOWARDS SOURCE' 'ROUTER TOWARDS DESTINATION'
|
||
|
fi
|
||
|
echo " --+---+-----------------------------+---"
|
||
|
echo " |"
|
||
|
fi
|
||
|
draw_me
|
||
|
fi
|