Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

Record your dynamic WAN IP addresses

A launchd + shell script exercise to record your DSL router's dynamic WAN IP addresses. Requires some customization on your part. Use at your own risk.

1. create the launchd item in /Library/LaunchDaemons

/usr/bin/sudo /bin/bash -c '

yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"
EX_MARK='!'

/bin/cat > "${LaunchdPlistFile}" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<${EX_MARK}DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
   <dict>
      <key>Disabled</key>
      <true/>
      <key>GroupName</key>
      <string>${yourname}</string>
      <key>Label</key>
      <string>net.${yourname}.wanip.update</string>
      <key>ProgramArguments</key>
      <array>
         <string>/Users/${yourname}/Library/wanip.sh</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
      <key>StartInterval</key>
      <integer>20</integer>
      <key>UserName</key>
      <string>${yourname}</string>
   </dict>
</plist>
EOF
'


#-------------------------


yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"

open -e "${LaunchdPlistFile}"
ls -l "${LaunchdPlistFile}"

/usr/bin/groups
/usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}"
#/usr/bin/sudo /usr/sbin/chown root:admin "${LaunchdPlistFile}"
/usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}"

# after creating ~/Library/wanip.sh below
/usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null
#/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null

/usr/bin/sudo /bin/launchctl list
/usr/bin/sudo /usr/bin/fs_usage | /usr/bin/egrep -i wanip


2. create a shell script that will be run by the launchd item at the specified intervals in seconds (here: every 20 seconds)

Version 1:
#!/bin/bash

# Version 1 with two columns (date, IP address)
# cat ~/Library/wanip.sh   (/Users/yourname/Library/wanip.sh)
# /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh
# /bin/chmod 0744 ~/Library/wanip.sh

declare last_line_closed last_line_offline last_line_unreachable newfile old_wanip time wanip

declare IF='en0'
declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt"

# try to find your router_wanip_site by surfing to the IP addresses returned by the following commands:
# route -n get default | egrep interface | awk '{print $NF}'
# ipconfig getoption en0 router
# ipconfig getoption en0 domain_name_server

declare router_wanip_site='http://xxxx.xx/xxx.htm'
#declare router_wanip_site='http://checkip.dyndns.org'   # alternative


/bin/sleep 3

/usr/sbin/ipconfig waitall

if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then

   /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null
 
   if [[ $? -ne 0 ]]; then 
      time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
      /usr/bin/logger -i "${time}     router_wanip_site is unreachable for wanip.sh"
      last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "$last_line_unreachable" ]]; then exit 0; fi
      echo "${time}          router_wanip_site is unreachable" >> "${wanip_record_file}"
      exit 0
   fi


   # match first IP address with egrep
   wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')"
   wanip="${wanip// /}"

   # alternative with sed for matching a line with a characteristic string plus IP address
   #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')"


   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"

   if [[ -n "${wanip}" ]]; then 
      old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")"
      if [[ "${wanip}" == "${old_wanip}" ]]; then exit 0; fi
      echo "${time}          ${wanip}" >> "${wanip_record_file}"
   else
      last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "${last_line_closed}" ]]; then exit 0; fi
      echo "${time}          connection closed" >> "${wanip_record_file}"
   fi

else

   last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")"
   if [[ -n "$last_line_offline" ]]; then exit 0; fi
   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   echo "${time}          offline" >> "${wanip_record_file}"

fi

if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then
   newfile="${wanip_record_file}-$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   /bin/mv "${wanip_record_file}" "${newfile}"
fi

exit 0


Version 2:
#!/bin/bash

# Version 2 has an additional $name column
# cat ~/Library/wanip.sh   (/Users/yourname/Library/wanip.sh)
# /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh
# /bin/chmod 0744 ~/Library/wanip.sh

declare format last_line_closed last_line_offline last_line_unreachable name newfile old_name old_wanip time wanip

declare IF='en0'
declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt"

# try to find your router_wanip_site by surfing to the IP addresses returned by the following commands:
# route -n get default | egrep interface | awk '{print $NF}'
# ipconfig getoption en0 router
# ipconfig getoption en0 domain_name_server

declare router_wanip_site='http://xxxx.xx/xxx.htm'
#declare router_wanip_site='http://checkip.dyndns.org'   # alternative

name="$(/usr/bin/who | /usr/bin/awk '/console/ {print $1}')"
name="${name//[[:cntrl:]]/,}"
if [[ -z "${name}" ]]; then name='[logout]'; fi

old_name="$(/usr/bin/sed -E -n -e '$,$s/^[^ ]+ +([^ ]+) +[^ ].*$/\1/p' "${wanip_record_file}")"

format='%-35s%-20s%-20s\n'   # for printf

#/bin/sleep 3

/usr/sbin/ipconfig waitall

if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then

   /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null
 
   if [[ $? -ne 0 ]]; then 
      time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
      /usr/bin/logger -i "${time}     router_wanip_site is unreachable for wanip.sh"
      last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "$last_line_unreachable" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
         fi
         exit 0
      fi
      printf "${format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
      exit 0
   fi


   # match first IP address with egrep
   wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')"
   wanip="${wanip// /}"

   # alternative with sed for matching a line with a characteristic string plus IP address
   #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')"


   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"

   if [[ -n "${wanip}" ]]; then 
      old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")"
      if [[ "${wanip}" == "${old_wanip}" ]]; then
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
         fi
         exit 0
      fi

      printf "${format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
   else
      last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "${last_line_closed}" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
         fi
         exit 0
      fi
      printf "${format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
   fi

else

   last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")"
   time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   if [[ -n "$last_line_offline" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"
         fi
      exit 0
   fi
   printf "${format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"

fi


if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then
   newfile="${wanip_record_file}-$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)"
   /bin/mv "${wanip_record_file}" "${newfile}"
fi

exit 0


Version 3:
/usr/bin/sudo /bin/bash -c '

yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"
EX_MARK='!'

/bin/cat > "${LaunchdPlistFile}" <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<${EX_MARK}DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Disabled</key>
        <true/>
        <key>GroupName</key>
        <string>${yourname}</string>
        <key>Label</key>
        <string>net.${yourname}.wanip.update</string>
        <key>ProgramArguments</key>
        <array>
            <string>/Users/${yourname}/Library/wanip.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>UserName</key>
        <string>${yourname}</string>
</dict>
</plist>
EOF
'


#----------------------------------------------


yourname=$(/usr/bin/logname)
LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist"

open -e "${LaunchdPlistFile}"
ls -l "${LaunchdPlistFile}"

/usr/bin/groups
/usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}"
#/usr/bin/sudo /usr/sbin/chown root:admin "${LaunchdPlistFile}"
/usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}"

# after creating ~/Library/wanip.sh below
/usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null
#/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null

/usr/bin/sudo /bin/launchctl list
/usr/bin/sudo /usr/bin/fs_usage | /usr/bin/egrep -i wanip


#-----------------------------------------------------------------------------------------------------


#!/bin/bash

# Version 3 is started only once by the launchd item and records shutdown processes
# Inspired by: "Re: how to run scripts at shutdown - how does launchd shutdown a system?", 
#              http://lists.apple.com/archives/macos-x-server/2007/Oct/msg00021.html
# cat ~/Library/wanip.sh   (/Users/yourname/Library/wanip.sh)
# /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh
# /bin/chmod 0744 ~/Library/wanip.sh

declare last_line_closed last_line_offline last_line_unreachable name 
declare newfile old_name old_wanip printf_format time time_format wanip

declare IF='en0'
declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt"

# try to find your router_wanip_site by surfing to the IP addresses returned by the following commands:
# route -n get default | egrep interface | awk '{print $NF}'
# ipconfig getoption en0 router
# ipconfig getoption en0 domain_name_server

declare router_wanip_site='http://xxxx.xx/xxx.htm'
#declare router_wanip_site='http://checkip.dyndns.org'   # alternative


WHILEVAR=1
TRAPSIGNAL=

function exit_function() {
   TRAPSIGNAL='yes'
   # $! holds the PID of last process that has been started in the background (cmd &)
   [[ $! -gt $$ ]] && kill -TERM $!
}

trap exit_function SIGHUP SIGINT SIGTERM


time_format='+%Y-%m-%d--%H.%M.%S--%Z'
printf_format='%-40s%-20s%-20s\n'


while [[ ${WHILEVAR} ]]; do

   name="$(/usr/bin/who | /usr/bin/awk '/console/ {print $1}')"
   name="${name//[[:cntrl:]]/,}"
   if [[ -z "${name}" ]]; then name='[logout]'; fi
   old_name="$(/usr/bin/sed -E -n -e '$,$s/^[^ ]+ +([^ ]+) +[^ ].*$/\1/p' "${wanip_record_file}")"
   /usr/sbin/ipconfig waitall

   /bin/sleep 20 &     # time interval
   wait $!

   if [[ ${TRAPSIGNAL} ]]; then
      time="$(/bin/date ${time_format})"
      printf "${printf_format}" "${time}" "${name}" "shutdown" >> "${wanip_record_file}"
      exit 0
   fi


if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then

   /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null
 
   if [[ $? -ne 0 ]]; then 
      time="$(/bin/date ${time_format})"
      /usr/bin/logger -i "${time}     router_wanip_site is unreachable for wanip.sh"
      last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "$last_line_unreachable" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
         fi
         continue
      fi
      printf "${printf_format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}"
      continue
   fi


   # match first IP address with egrep
   wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')"
   wanip="${wanip// /}"

   # alternative with sed for matching a line with a characteristic string plus IP address
   #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \
              #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')"


   time="$(/bin/date ${time_format})"

   if [[ -n "${wanip}" ]]; then 
      old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")"
      if [[ "${wanip}" == "${old_wanip}" ]]; then
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
         fi
         continue
      fi
      printf "${printf_format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}"
   else
      last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")"
      if [[ -n "${last_line_closed}" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
         fi
         continue
      fi
      printf "${printf_format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}"
   fi

else

   last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")"
   time="$(/bin/date ${time_format})"
   if [[ -n "$last_line_offline" ]]; then 
         if [[ "${name}" != "${old_name}" ]]; then
            printf "${printf_format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"
         fi
      continue
   fi
   printf "${printf_format}" "${time}" "${name}" "offline" >> "${wanip_record_file}"

fi


if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then
   newfile="${wanip_record_file}-$(/bin/date ${time_format})"
   /bin/mv "${wanip_record_file}" "${newfile}"
fi

done

exit 0

Analyze internet traffic volume with dynamic ipfw rules

# cf. Example ipfw ruleset, http://codesnippets.joyent.com/posts/show/1267
# cf. also http://codesnippets.joyent.com/posts/show/1729

man ipfw 2>/dev/null | less -p "If the ruleset"
man ipfw 2>/dev/null | less -p "These dynamic rules"
man ipfw 2>/dev/null | less -p "All rules"

man ipfw 2>/dev/null | less -p "STATEFUL FIREWALL"     # press [n]
man ipfw 2>/dev/null | less -p "SYSCTL VARIABLES"
man ipfw 2>/dev/null | less -p "EXAMPLES"
man ipfw 2>/dev/null | less -p "DYNAMIC RULES"

/usr/bin/sudo /sbin/ipfw -d -e -t list
/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/sed -E -n -e '1,/^## Dynamic rules/p'
/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/sed -E -n -e '/^## Dynamic rules/,$p'
/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/awk '/^## Dynamic rules/,/^$/ {print $0}'
/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/awk '/<->/ {print $0}'
/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/awk '{ if ( $0 ~ /<->/ ) {print $0}}'

/usr/bin/sudo /sbin/ipfw -d -e -t list | grep RULENUM
/usr/bin/sudo /sbin/ipfw -d -e -t list | grep IPADDR

/usr/sbin/sysctl -a | egrep 'tcp'
/usr/sbin/sysctl -a | egrep 'net.inet'
/usr/sbin/sysctl -a | egrep '\.fw'
/usr/sbin/sysctl -a | egrep 'ip.fw'
/usr/sbin/sysctl -a | egrep 'li[fv]e'
/usr/sbin/sysctl -a | egrep 'ip.fw.+life'

/usr/sbin/sysctl -n net.inet.tcp.always_keepalive
/usr/sbin/sysctl -n net.inet.ip.fw.dyn_keepalive
/usr/sbin/sysctl -n net.inet.ip.fw.dyn_buckets
/usr/sbin/sysctl -n net.inet.ip.fw.dyn_count
/usr/sbin/sysctl -n net.inet.ip.fw.dyn_max


# list all dynamic ipfw rules

function ipfwtraffic() {

   declare args argsregex bytes megabytes

   if [[ $# -eq 0 ]]; then

      /usr/bin/sudo /sbin/ipfw -d -e -t list | \
         /usr/bin/awk '/<->/ {printf "%-10s %-10s %-20s %-10s %-20s %-10s %-10s\n", $3, $6, $7, $8, $10, $11, $1}' | \
         /usr/bin/sort -bu | while IFS=" " read  bytes proto ipnum1 port1 ipnum2 port2 rulenum; do

      # byte
      #bytes=$(printf "%s\n" "${bytes}" | /usr/bin/awk '{ total = total + $1 } END { print total }')
      #printf "\e[1mbytes\e[m: %-17s %-10s %-40s \e[1mrule\e[m: %-15s \e[1mports\e[m: %-15s\n" \
           #"${bytes}" "${proto}" "${ipnum1}  ::  ${ipnum2}" "${rulenum}" "${port1}  ${port2}"

      # mega byte
      megabytes=$(printf "%s\n" "${bytes}" | /usr/bin/awk '{ total = (total + $1) / (1024*1024.0) } END { print total }')
      printf "\e[1mmbytes\e[m: %-20.6f %-10s %-40s \e[1mrule\e[m: %-15s \e[1mports\e[m: %-15s\n" \
           "${megabytes}" "${proto}" "${ipnum1}  ::  ${ipnum2}" "${rulenum}" "${port1}  ${port2}"

      done | /usr/bin/sort -rn -k 2,2

   else

      args="${@}"
      if [[ "${args}" != "${args//[^. [:digit:]]/}" ]]; then 
         printf "%s\n" 'Found at least one invalid rule number or IP address!'
         return 1
      fi

      if [[ "${args//[ [:digit:]]/}" == '' ]]; then 
         argsregex="^0*(${args// /|})"    #  ipfw rule numbers 
      else
         argsregex="(${args// /|})"    #  IP addresses
      fi
    
      #echo $argsregex

      /usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/egrep "${argsregex}" | \
         /usr/bin/awk '/<->/ {printf "%-10s %-10s %-20s %-10s %-20s %-10s %-10s\n", $3, $6, $7, $8, $10, $11, $1}' | \
         /usr/bin/sort -bu | while IFS=" " read  bytes proto ipnum1 port1 ipnum2 port2 rulenum; do

      # byte
      #bytes=$(printf "%s\n" "${bytes}" | /usr/bin/awk '{ total = total + $1 } END { print total }')
      #printf "\e[1mbytes\e[m: %-17s %-10s %-40s \e[1mrule\e[m: %-15s \e[1mports\e[m: %-15s\n" \
           #"${bytes}" "${proto}" "${ipnum1}  ::  ${ipnum2}" "${rulenum}" "${port1}  ${port2}"

      # mega byte
      megabytes=$(printf "%s\n" "${bytes}" | /usr/bin/awk '{ total = (total + $1) / (1024*1024.0) } END { print total }')
      printf "\e[1mmbytes\e[m: %-20.6f %-10s %-40s \e[1mrule\e[m: %-15s \e[1mports\e[m: %-15s\n" \
           "${megabytes}" "${proto}" "${ipnum1}  ::  ${ipnum2}" "${rulenum}" "${port1}  ${port2}"

      done | /usr/bin/sort -rn -k 2,2

   fi

   return 0
}



# usage:
# ipfwtraffic
# ipfwtraffic [rulenum1] [rulenum2] [rulenum3] ...
# ipfwtraffic [ipaddr1] [ipaddr2] [ipaddr3] ...


ipfwtraffic
ipfwtraffic  9600 10600 11000
ipfwtraffic xx.xxx.xx.xxx xx.xxx.xx.xx

ipfwtraffic | grep 'xx.xxx.xx.xx'



#------------------------------------------------------------------------------



# summarize pairs of IP addresses

function ipfwdynstats() {

   declare args argsregex dynrules ipaddr_pairs

   OIFS=${IFS}
   IFS=$'\n'

   if [[ $# -eq 0 ]]; then

      ipaddr_pairs=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/awk '/<->/ {print $7, $10}' | /usr/bin/sort -bu))

      dynrules=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/awk '/<->/ {print $7, $8, $10, $11, $6, $2, $3, $1}' | /usr/bin/sort -bu))

   else

      args="${@}"

      if [[ "${args}" != "${args//[^. [:digit:]]/}" ]]; then 
         printf "%s\n" 'Found at least one invalid rule number or IP address!'
         return 1
      fi

      if [[ "${args//[ [:digit:]]/}" == '' ]]; then 
         argsregex="^0*(${args// /|})"    #  ipfw rule numbers 
      else
         argsregex="(${args// /|})"    #  IP addresses
      fi
    

      ipaddr_pairs=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/egrep "${argsregex}" | \
           /usr/bin/awk '/<->/ {print $7, $10}' | /usr/bin/sort -bu))

      dynrules=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/egrep "${argsregex}" | \
           /usr/bin/awk '/<->/ {print $7, $8, $10, $11, $6, $2, $3, $1}' | /usr/bin/sort -bu))


   fi


for ((i=0; i < "${#ipaddr_pairs[@]}"; i++)); do 

   # byte
   #bytesum=$(printf "%s\n" "${dynrules[@]}" | \
        #/usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $7 }' | \
        #/usr/bin/awk '{ total = total + $1 } END { print total }')

   # mega byte
   bytesum=$(printf "%s\n" "${dynrules[@]}" | \
        /usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $7 }' | \
        /usr/bin/awk 'BEGIN { total=0 }; { total = (total + $1) / (1024*1024.0) } END { print total }')


   proto=$(printf "%s\n" "${dynrules[@]}" | \
        /usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $5 }' | \
        /usr/bin/sort -bu)


   rule=$(printf "%s\n" "${dynrules[@]}" | \
        /usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $NF }' | \
        /usr/bin/sort -bu)


   ports=$(printf "%s\n" "${dynrules[@]}" | \
        /usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $2,$4 }' | \
        /usr/bin/sort -bu)


   # byte
   #printf "\e[1mbytes\e[m: %-20s %-10s %-40s \e[1mrules\e[m: %-25s \e[1mports\e[m: %-30s\n" \
        #"${bytesum}" "${proto//[[:cntrl:]]/, }" "${ipaddr_pairs[${i}]% *}  ::  ${ipaddr_pairs[${i}]#* }" \
        #"${rule//[[:cntrl:]]/, }" "${ports//[[:cntrl:]]/, }"


   # mega byte
   printf "\e[1mmbytes\e[m: %-20.6f %-10s %-40s \e[1mrules\e[m: %-25s \e[1mports\e[m: %-30s\n" \
        "${bytesum}" "${proto//[[:cntrl:]]/, }" "${ipaddr_pairs[${i}]% *}  ::  ${ipaddr_pairs[${i}]#* }" \
        "${rule//[[:cntrl:]]/, }" "${ports//[[:cntrl:]]/, }"

done | /usr/bin/sort -rn -k 2,2

   export IFS=${OIFS}
   return 0

}


# usage:
# ipfwdynstats
# ipfwdynstats [rulenum1] [rulenum2] [rulenum3] ...
# ipfwdynstats [ipaddr1] [ipaddr2] [ipaddr3] ...


ipfwdynstats
ipfwdynstats  5200 12700
ipfwdynstats xx.xxx.xx.xxx xxx.xxx.xx.xxx



#------------------------------------------------------------------------------



# list port-specific internet traffic

function porttraffic() {

   declare args argsregex dynrules ipaddr_pairs

   OIFS=${IFS}
   IFS=$'\n'

   if [[ $# -eq 0 ]]; then
   
      printf "%s\n" 'No port number given!'
      return 1
   
   elif [[ $# -eq 1 ]]; then

      if [[ "${1//[[:digit:]]/}" != '' ]]; then 
         printf "%s\n" 'Invalid port number!'
         return 1
      fi

      ipaddr_pairs=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | \
         /usr/bin/awk '/<->/ && ( $8 == "'"${1}"'" || $11 == "'"${1}"'" ) {print $7, $10 }' | /usr/bin/sort -bu))

      dynrules=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | \
         /usr/bin/awk '/<->/ && ( $8 == "'"${1}"'" || $11 == "'"${1}"'" ) {print $7, $8, $10, $11, $6, $2, $3, $1 }' | \
         /usr/bin/sort -bu))

   else

      args="${@:2}"   # all arguments starting with the second

      if [[ "${args}" != "${args//[^. [:digit:]]/}" ]]; then 
         printf "%s\n" 'Found at least one invalid rule number or IP address!'
         return 1
      fi

      if [[ "${args//[ [:digit:]]/}" == '' ]]; then 
         argsregex="^0*(${args// /|})"    #  ipfw rule numbers 
      else
         argsregex="(${args// /|})"    #  IP addresses
      fi
    
      #echo $argsregex

      ipaddr_pairs=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/egrep "${argsregex}" | \
         /usr/bin/awk '/<->/ && ( $8 == "'"${1}"'" || $11 == "'"${1}"'" )  {print $7, $10 }' | /usr/bin/sort -bu))

      dynrules=($(/usr/bin/sudo /sbin/ipfw -d -e -t list | /usr/bin/egrep "${argsregex}" | \
         /usr/bin/awk '/<->/ && ( $8 == "'"${1}"'" || $11 == "'"${1}"'" )  {print $7, $8, $10, $11, $6, $2, $3, $1 }' | \
         /usr/bin/sort -bu))

   fi


for ((i=0; i < "${#ipaddr_pairs[@]}"; i++)); do 

   # byte
   #bytesum=$(printf "%s\n" "${dynrules[@]}" | \
        #/usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $7 }' | \
        #/usr/bin/awk '{ total = total + $1 } END { print total }')

   # mega byte
   bytesum=$(printf "%s\n" "${dynrules[@]}" | \
        /usr/bin/awk '$1 == "'"${ipaddr_pairs[${i}]% *}"'" && $3 == "'"${ipaddr_pairs[${i}]#* }"'" { print $7 }' | \
        /usr/bin/awk 'BEGIN { total=0 }; { total = (total + $1) / (1024*1024.0) } END { print total }')


   proto=$(printf "