# == Synopsis # # Takes a single line of an IPTables log and parses it into a huge hash full of information # # This is heavily based off of the POE::Filter::Log::IPTables Perl module # # The hash looks something like this: # (returned hash) # line: contains the full line # in_int: inbound interface # out_int: outbound interface # mac: mac address # ip: # src: source address # dst: dst address # length, ttl, id: self-explanatory # tos: type of service # prec: prec? it's a field... # fragment_flags: array of flags from the packet # type: can either be tcp, udb, or icmp # tcp: # dpt, spt: source, dest port # window: tcp window # res: reserve # urgp: urgent # flags: array of tcp flags (syn,fin,etc) # udp: # dpt,spt: source, dest port # length: length # icmp: # type: icmp type # code: icmp code # error_header: packet header if type is 3, 4, or 11 # unknown: # proto: catchall incase something weird happens # # If syslog is passed, treat line like syslog line and strip timestamp and kernel header off # # If you have having problems with your chain, please take a look at where it parses the chain # somewhere around line 63. Your logs may differ than mine in this area. # == Usage # # iploghash = IPTables.parse(IPTables_log_line_as_string, syslog => 1) # # == Author # # Joe Topjian, joe@terrarum.net module IPTables def IPTables.parse(line, syslog = nil) iplog = Hash.new # save the whole line line = line.chomp iplog['line'] = line.dup # if the syslog option is set, get rid of the syslog timestamp if (syslog) line.sub!(/^\w\w\w\s+\d+ \d\d:\d\d:\d\d \w+ /, '') end # remove prefix and get chain if (line =~ /kernel: (.*) ?IN=/) iplog['chain'] = $1.split[1] end line.sub!(/kernel: (.*) ?IN=/, 'IN=') # inbound interface if (line =~ /^IN=(\w+)? /) iplog['in_int'] = $1; if ($1) line.sub!(/IN=#{$1} /,'') else line.sub!(/IN= /,'') end end # outbound interface if (line =~ / ?OUT=([A-Za-z0-9]+?)? /) iplog['out_int'] = $1 if ($1) line.sub!(/ ?OUT=#{$1} /,'') else line.sub!(/ ?OUT= /,'') end end # input MAC address if (line =~ / ?MAC=([0-9A-F:]+)? /i) iplog['mac'] = $1 if ($1) line.sub!(/ ?MAC=#{$1} /,'') else line.sub!(/ ?MAC= /,'') end end (line, iplog['ip']) = IPTables.parseIP(line) if (line =~ / ?PROTO=ICMP /) line.gsub!(/^PROTO=ICMP /,'') iplog['ip']['type'] = 'icmp' (iplog['ip']['icmp'], leftover) = IPTables.parseICMP(line) elsif (line =~ / ?PROTO=TCP /) line.gsub!(/^PROTO=TCP /,'') iplog['ip']['type'] = 'tcp' (iplog['ip']['tcp'], leftover) = IPTables.parseTCP(line) elsif (line =~ / ?PROTO=UDP /) line.gsub!(/^PROTO=UDP /,'') iplog['ip']['type'] = 'udp' (iplog['ip']['udp'], leftover) = IPTables.parseUDP(line) else iplog['ip']['type'] = 'unknown' (iplog['ip']['unknown'], leftover) = IPTables.parseUnknown(line) end iplog['leftover'] = leftover return iplog end def IPTables.parseIP(line) ipinfo = Hash.new # source and dest addresses "SRC DST".split.each do |x| if (line =~ / ?#{x}=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) /) ipinfo[x.downcase] = $1 line.sub!(/ ?#{x}=#{$1} /,'') end end "LEN TTL ID".split.each do |x| if (line =~ / ?#{x}=(\d+) /) ipinfo[x.downcase] = $1 line.sub!(/ ?#{x}=#{$1}/,'') end end # type of service and prec "TOS PREC".split.each do |x| if (line =~ / ?#{x}=(0x[0-9A-Z]{2}) /i) ipinfo[x.downcase] = $1 line.sub!(/ ?#{x}=#{$1} /,'') end end # IP Flags ipflags = Array.new "CE DF MF".split.each do |flag| if (line =~ /^#{flag} /) ipflags.push(flag) line.sub!(/^#{flag} /,'') end end ipinfo['fragment_flags'] = ipflags return line, ipinfo end def IPTables.parseTCP(line) tcpinfo = Hash.new # tcp stuff "SPT DPT WINDOW".split.each do |x| if (line =~ /#{x}=(\d+)/) tcpinfo[x.downcase] = $1 line.sub!(/#{x}=#{$1}/,'') end end # reserved if (line =~ /RES=(0x[0-9A-Z]{2}) /i) tcpinfo['res'] = $1 line.sub!(/RES=#{$1} /,'') end # flags tcpflags = Array.new "CWR ECE URG ACK PSH RST SYN FIN".split.each do |flag| if (line =~ /#{flag} /) tcpflags.push(flag) line.sub!(/#{flag} /,'') end end tcpinfo['flags'] = tcpflags # urgent if (line =~ /URGP=(\d+) ?/) tcpinfo['urgp'] = $1 line.sub!(/URGP=#{$1} ?/, '') end if (line and line !~ /^\s+$/) return tcpinfo, line else return tcpinfo, nil end end def IPTables.parseUDP(line) udpinfo = Hash.new "SPT DPT LEN".split.each do |x| if (line =~ /#{x}=(\d+)/) udpinfo[x.downcase] = $1 line.sub!(/#{x}=#{$1}/,'') end end if (line and line !~ /^\s+$/) return udpinfo, line else return udpinfo, nil end end def IPTables.parseICMP(line) icmpinfo = Hash.new packet = nil "TYPE CODE ID SEQ".split.each do |x| if (line =~ /#{x}=(\d+) /) icmpinfo[x.downcase] = $1 line.sub!(/#{x}=#{$1} /,'') end end "3 4 11".split.each do |type| if (type == icmpinfo['type']) if (line =~ /\[(.+)\]/) packet = $1 icmpinfo['error_header'] = IPTables.parse(packet) end end end if (packet) # similar to quotemeta? quoted = packet.gsub(/\W/) { '\\'+$& } line.sub!(/\[#{quoted}\]/,'') end if (line and line !~ /^\s+$/) return icmpinfo, line else return icmpinfo, nil end end def IPTables.parseUnknown(line) unknown = Hash.new if (line =~ /PROTO=(\w+) /) unknown['proto'] = $1 line.sub!(/PROTO=#{$1} ?/,'') end if (line and line !~ /^\s+$/) return unknown, line else return unknown, nil end end end