1# 2# sockd-stat.awk for the DANTE socks 4/5 daemon 3# Copyleft 2001 Stephan Eisvogel <eisvogel@hawo.stw.uni-erlangen.de> 4# This code is licensed under GPL v2 5# 6# If you use this regularly, help boost my ego ;) and do a: 7# uname -a | mail -s "I use sockd-stat!" eisvogel@hawo.stw.uni-erlangen.de 8# 9# If you make any fixes or improvements, please do send do them to me! 10# All testing was done with DANTE version 1.1.6 on RedHat(tm) Linux. 11# If there are updates you can probably grab them from: 12# http://www.hawo.stw.uni-erlangen.de/~eisvogel/sockd 13# 14# 15# HOW TO USE 16# 17# Find out how your system rotates its logs and run this script 18# post-rotating, e.g. by doing a 19# 20# zcat /var/log/sockd.1.gz | awk -f /root/bin/sockd-stat.awk | \ 21# mail -s "Weekly SOCKD usage" root 22# 23# If you have a directory called /etc/logrotate.d then take a look 24# at the files in there for some run-post-rotate examples. Note that 25# this script uses some basic GNU-tools to get things done, namely 26# "sort", "head", and "rm", if you want reverse IP lookups you need 27# the "nslookup" utility as well. 28# 29# This is my last big AWK script, I am going Perl. 30# 31# 32# RELEASE NOTES 33# 34# v1.0 @ 01.01.2001 35# NEC's socks daemon keeps crashing on Linux with 200+ users no 36# matter what I do, so we switched to DANTE for the time being. 37# Didn't like the last remote exploit too much either, maybe NEC 38# needs more DJB-type developers? 39# v1.1 @ 02.01.2001 40# + fixed issue with different syslog daemons (Michael Shuldman) 41# + added request count output for the port statistics 42# + blocked UDP requests were not counted 43# + changed regexp that matches lines with statistics 44# + rmfile() function 45# + code cleanups, renamed some variables for clarity 46# + beautified output IMO 47# + added request types to general statistics 48 49BEGIN { 50 # 51 # Configurable items 52 # (0 means all) 53 # 54 SHOW_PORTS=30; 55 SHOW_CLIENTS=0; 56 SHOW_DESTINATIONS=50; 57 if (nodns == 1) 58 LOOKUP_IPS=0; 59 else 60 LOOKUP_IPS=1; 61 62 # 63 # no need to change anything below 64 # 65 IGNORECASE=1; 66 MEG=1024*1024; 67 lastdns="(unknown)"; 68 69 lines=0; 70 passed=0; 71 denied=0; 72 73 total_to_client_bytes=0; 74 total_from_client_bytes=0; 75 total_to_target_bytes=0; 76 total_from_target_bytes=0; 77} 78 79# 80# Sort 'filename' contents numerically in 'field' 81# and display the 'count' largest lines, optionally 82# treat first field as IP address and append it 83# resolved into a hostname to the end of the line 84# 85function sorted_output (filename, field, count, rev_dns, curhost,a,b) 86{ 87 # sort and stuff all lines it into a temp file 88 if (count==0) 89 system("sort -nr +"field" "filename" >"filename".1"); 90 else system("sort -nr +"field" "filename" | head -n "count" >"filename".1"); 91 92 # read temp file back in, print it out and rev-dns if needed 93 b=filename".1"; 94 while ((getline curhost < b)>0) { 95 printf "%s",curhost; 96 if (rev_dns==1) { 97 split(curhost,a); 98 dns_lookup(a[1],filename".2"); 99 printf "%s",lastdns; 100 } 101 printf "\n"; 102 } 103 close(b); 104 rmfile(b); 105} 106 107# 108# Wade through /etc/services and grab the descriptive service name of a port 109# 110function get_service (port, myline,a,s) 111{ 112 split(port,s,/\//); 113 curr_service=s[1]; 114 while ((getline myline < "/etc/services")>0) { 115 split(myline,a); 116 if (a[2]==port) { 117 curr_service=a[1]; 118 break; 119 } 120 } 121 close ("/etc/services"); 122} 123 124# 125# Somewhere we just have to draw a line 126# 127function draw_line (n, i) 128{ 129 for (i=0; i<n; i++) printf "-"; 130 printf "\n"; 131} 132 133# 134# Simple DNS lookup using the nslookup command 135# 136function dns_lookup (ip,tmpfile, name,host,a) 137{ 138 lastdns=ip; # unsuccessful lookup -> show IP 139 system("nslookup "ip" 2>/dev/null >"tmpfile); 140 while ((getline name < tmpfile)>0) { 141 split(name,host); 142 if (index(host[1],"Name:")!=0) { 143 lastdns=host[2]; 144 break; 145 } 146 } 147 close(tmpfile) 148 rmfile(tmpfile); 149} 150 151# 152# Remove file sans output 153# 154function rmfile (filename) 155{ 156 system("rm "filename" 2>/dev/null >/dev/null"); 157} 158 159# 160# Matches all lines, count them 161# 162/.*/ { 163 lines++; 164} 165 166# 167# Count blocked requests 168# 169/ sockd\[[0-9]+\]: block\([0-9]+\): / { 170 denied++; 171 # 172 # compensate for different syslog daemons 173 # 174 field_ofs=0; 175 split($0,curr_line); 176 for (i=1; i<=NF; i++) 177 if (match(curr_line[i],/sockd\[[0-9]+\]:/)) { 178 field_ofs = i + 1; 179 break; 180 } 181 # 182 # count BLOCK item 183 # 184 m_block[curr_line[field_ofs+2]] += 1; 185 next; 186} 187 188# 189# Matches lines with statistical information 190# 191/ sockd\[[0-9]+\]: pass\([0-9]+\): .* \]: .* -> .* -> .* -> .* -> / { 192 193 passed++; 194 195 # 196 # compensate for different syslog daemons 197 # 198 field_ofs=0; 199 split($0,curr_line); 200 for (i=1; i<=NF; i++) 201 if (match(curr_line[i],/sockd\[[0-9]+\]:/)) { 202 field_ofs = i + 1; 203 break; 204 } 205 # 206 # count PASS item 207 # 208 m_pass[curr_line[field_ofs+2]] += 1; 209 210 # 211 # clean up fields 212 # 213 gsub(/,/,"",curr_line[field_ofs+9]); 214 gsub(/:/,"",curr_line[field_ofs+15]); 215 split(curr_line[field_ofs+6],a,/\./); 216 c_ip=a[1]"."a[2]"."a[3]"."a[4]; 217 if (match(curr_line[field_ofs+12],/^`world'$/)) 218 curr_line[field_ofs+12]="0.0.0.0."a[5]; 219 split(curr_line[field_ofs+12],a,/\./); 220 t_ip=a[1]"."a[2]"."a[3]"."a[4]; 221 t_port=a[5]; 222 223 # 224 # add to totals 225 # 226 total_to_client_bytes += curr_line[field_ofs+4]; 227 total_from_client_bytes += curr_line[field_ofs+9]; 228 total_to_target_bytes += curr_line[field_ofs+10]; 229 total_from_target_bytes += curr_line[field_ofs+15]; 230 231 # 232 # update client 233 # (but exclude broken socksified Half-Life requests) 234 # 235 if (match(c_ip,/0\.0\.0\.0/)==0) { 236 client[c_ip]=1; 237 to_client[c_ip] += curr_line[field_ofs+4]; 238 from_client[c_ip] += curr_line[field_ofs+9]; 239 } 240 241 # 242 # update target 243 # 244 target[t_ip]=1; 245 to_target[t_ip] += curr_line[field_ofs+10]; 246 from_target[t_ip] += curr_line[field_ofs+15]; 247 248 # 249 # update port 250 # 251 if (match(curr_line[field_ofs+2],/tcp/)) { 252 tcp_port[t_port]=1; 253 tcp_port_to[t_port] += curr_line[field_ofs+10]; 254 tcp_port_from[t_port] += curr_line[field_ofs+15]; 255 tcp_port_used[t_port] += 1; 256 } else { 257 udp_port[t_port]=1; 258 udp_port_to[t_port] += curr_line[field_ofs+10]; 259 udp_port_from[t_port] += curr_line[field_ofs+15]; 260 udp_port_used[t_port] += 1; 261 } 262} 263 264 265# 266# After all lines are done, sort and print the statistics 267# 268END { 269 # create a random filename for sorting 270 srand(); 271 CONVFMT="%d" 272 r=rand()*999999999; 273 tmpfile="/tmp/tmp.sockd-stat."r; 274 275 printf "SOCKD statistics version 1.1\n"; 276 printf "Copyleft 2001 Stephan Eisvogel <eisvogel@hawo.stw.uni-erlangen.de>\n"; 277 278 total_clients=0; 279 total_targets=0; 280 for (c in client) total_clients++; 281 for (t in target) total_targets++; 282 283 printf "\nGeneral statistics\n"; draw_line(41); 284 printf "Lines parsed : %12d\n", lines; 285 printf "Unique clients : %12d\n", total_clients; 286 printf "Unique targets : %12d\n", total_targets; 287 printf "Passed requests : %12d\n", passed; 288 289 for (m in m_pass) printf " %-22s : %12d\n", m, m_pass[m] >> tmpfile 290 close(tmpfile); 291 sorted_output(tmpfile,2,0,0); 292 rmfile(tmpfile); 293 294 printf "Blocked requests : %12d\n", denied; 295 for (m in m_block) printf " %-22s : %12d\n", m, m_block[m] >> tmpfile 296 close(tmpfile); 297 sorted_output(tmpfile,2,0,0); 298 rmfile(tmpfile); 299 draw_line(41); 300 301 printf "\nVolume totals\n"; draw_line(28); 302 printf "Clients <-- %10.1f MB\n",total_to_client_bytes/MEG; 303 printf "Clients --> %10.1f MB\n",total_from_client_bytes/MEG; 304 printf "Targets <-- %10.1f MB\n",total_to_target_bytes/MEG; 305 printf "Targets --> %10.1f MB\n",total_from_target_bytes/MEG; 306 draw_line(28); 307 308 # 309 # TCP/UDP port stats 310 # 311 printf "\nPort Service "; 312 printf "To From Total Requests\n"; 313 draw_line(77); 314 for (p in tcp_port) { 315 printf "%-6s ",p >> tmpfile; 316 get_service(p"/tcp"); 317 outline="("curr_service"/tcp)"; 318 printf "%-23s",outline >>tmpfile; 319 printf "%8.1f MB %8.1f MB %8.1f MB %8d\n", 320 tcp_port_to[p]/MEG, 321 tcp_port_from[p]/MEG, 322 (tcp_port_from[p]+tcp_port_to[p])/MEG, 323 tcp_port_used[p] >> tmpfile; 324 } 325 for (p in udp_port) { 326 printf "%-6s ",p >> tmpfile; 327 get_service(p"/udp"); 328 outline="("curr_service"/udp)"; 329 printf "%-23s",outline >>tmpfile; 330 printf "%8.1f MB %8.1f MB %8.1f MB %8d\n", 331 udp_port_to[p]/MEG, 332 udp_port_from[p]/MEG, 333 (udp_port_from[p]+udp_port_to[p])/MEG, 334 udp_port_used[p] >> tmpfile; 335 } 336 close(tmpfile); 337 sorted_output(tmpfile,6,SHOW_PORTS,0); 338 rmfile(tmpfile); 339 draw_line(77); 340 341 # 342 # Client stats 343 # 344 print "\nClient IP To From Total FQDN"; 345 draw_line(77); 346 for (c in client) { 347 printf "%-16s %8.1f MB %8.1f MB %8.1f MB \n", 348 c, 349 to_client[c]/MEG, 350 from_client[c]/MEG, 351 (from_client[c]+to_client[c])/MEG >> tmpfile; 352 } 353 close(tmpfile); 354 sorted_output(tmpfile,5,SHOW_CLIENTS,LOOKUP_IPS); 355 rmfile(tmpfile); 356 draw_line(77); 357 358 # 359 # Destination stats 360 # 361 print "\nDestination IP To From Total FQDN"; 362 draw_line(77); 363 for (t in target) { 364 printf "%-16s %8.1f MB %8.1f MB %8.1f MB \n", 365 t, 366 to_target[t]/MEG, 367 from_target[t]/MEG, 368 (from_target[t]+to_target[t])/MEG >> tmpfile; 369 } 370 close(tmpfile); 371 sorted_output(tmpfile,5,SHOW_DESTINATIONS,LOOKUP_IPS); 372 rmfile(tmpfile); 373 draw_line(77); 374 printf "\n"; 375} 376