/[svn.andrew.net.au]/scripts/report_iptables_ulog.py
ViewVC logotype

Contents of /scripts/report_iptables_ulog.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 47 - (show annotations)
Sat Sep 6 06:19:41 2008 UTC (16 years ago) by apollock
File MIME type: text/x-python
File size: 5787 byte(s)
Refactored process_log, made it more robust
Rewrote prefix determination code

1 #!/usr/bin/python
2
3 import optparse
4 import logging
5 import urllib
6 import sys
7
8 #
9 # See
10 # http://blog.steve.org.uk/wash_your_face_and_try_again__if_you_survive_.html
11 # http://blog.steve.org.uk/i_don_t_have_no_other_pants_.html
12 # for more background
13 #
14 # Copyright (c) 2008 Andrew Pollock <me@andrew.net.au>
15 #
16 # Permission to use, copy, distribute, modify, and otherwise knock yourself out
17 # granted under the terms of the GNU General Public Licence v2
18 #
19
20 __author__ = "Andrew Pollock <me@andrew.net.au>"
21 __license__ = "GPLv2"
22
23 HOSTNAME_FIELD=3
24 PREFIX_FIELD_START=4
25 FIELD_AFTER_PREFIX="IN="
26 SRC_FIELD_OFFSET=3
27
28
29 def setup_logging(options):
30 """
31 Initialise logging
32
33 Args:
34 options: options object
35 """
36
37 logging.getLogger("").setLevel(1)
38
39 console = logging.StreamHandler()
40 console_formatter = logging.Formatter("%(levelname)-8s %(message)s")
41 console.setFormatter(console_formatter)
42 if options.verbose:
43 console.setLevel(logging.DEBUG)
44 else:
45 console.setLevel(logging.INFO)
46
47 logging.getLogger("").addHandler(console)
48
49
50 def process_options():
51 """
52 Do all the command line option parsing
53
54 Returns:
55 an options object
56 """
57
58 USAGE="usage: %prog [options]"
59
60 parser = optparse.OptionParser(usage=USAGE)
61 parser.add_option(
62 "-v", "--verbose",
63 default=False,
64 action="store_true",
65 dest="verbose",
66 help="increase verbosity",
67 )
68 parser.add_option(
69 "-p", "--prefix",
70 default="",
71 action="store",
72 dest="prefix",
73 help="prefix to restrict matches to",
74 )
75 parser.add_option(
76 "--host",
77 default="",
78 action="store",
79 dest="hostname",
80 help="host to restrict matches to",
81 )
82 parser.add_option(
83 "-l",
84 "--logfile",
85 default="/var/log/ulog/syslogemu.log",
86 action="store",
87 dest="log",
88 help="log file to process [default: %default]",
89 )
90 parser.add_option(
91 "-n",
92 "--dry-run",
93 default=False,
94 action="store_true",
95 dest="dryrun",
96 help="do nothing, just show what would happen",
97 )
98 (options, unused_args) = parser.parse_args()
99
100 return options
101
102
103 def get_field_after_prefix(ulogentry):
104 """
105 Return the index of the field after the prefix
106
107 Args:
108 ulogentry: a list of strings representing one line of a ulog entry
109
110 Returns:
111 the index of the first field after the prefix
112 """
113
114 for component in xrange(PREFIX_FIELD_START, len(ulogentry)):
115 if not ulogentry[component].startswith(FIELD_AFTER_PREFIX):
116 continue
117 else:
118 break
119
120 # If for some reason we reached the end of the line without finding
121 # FIELD_AFTER_PREFIX, something is probably wrong
122
123 if component == len(ulogentry):
124 return 0
125 else:
126 return component
127
128
129 def process_log(options):
130 """
131 Process the log file
132
133 Args:
134 options object
135
136 Returns:
137 A set of IP addresses
138
139 Discussion:
140
141 A line of the log file can look like:
142 Aug 31 06:46:06 caesar FORWARD too-hard-basket IN=eth0 OUT=eth1 MAC=00:08:02:52:
143 49:d7:00:02:3b:01:f0:87:08:00 SRC=64.142.100.44 DST=172.16.0.7 LEN=128 TOS=00 P
144 REC=0x00 TTL=59 ID=0 DF PROTO=ICMP TYPE=8 CODE=0 ID=42504 SEQ=6344
145
146 Notably, the "FORWARD too-hard-basket" is a user definable string, which can
147 contain whitespace, so therefore there's no predetermined position that the
148 SRC= field can be found in, therefore for each line, we need to iterate over
149 each whitespace-separated "word" until we find the right one. Bit of a bummer
150 that.
151 """
152
153 ips = set()
154
155 try:
156 log = open(options.log)
157 except IOError, e:
158 logging.critical("Could not open %s (%s)" % (options.log, e.strerror))
159 sys.exit(1)
160 while True:
161 log_entry = log.readline().split()
162 if not log_entry:
163 logging.debug("Reached the end of the file")
164 break
165 if len(log_entry) < PREFIX_FIELD_START + SRC_FIELD_OFFSET:
166 # We've got a unexpected log entry
167 # This should avoid any IndexError exceptions
168 logging.critical("Ignoring unexpected log entry: %s" % (" ".join(log_entry)))
169 continue
170 if options.hostname and log_entry[HOSTNAME_FIELD] != options.hostname:
171 logging.debug("Entry not for the host we're looking for")
172 continue
173 # We know where the prefix starts (if there is one)
174 if options.prefix and log_entry[PREFIX_FIELD_START].startswith(FIELD_AFTER_PREFIX):
175 # We have an entry with no prefix at all, but we're looking for entries
176 # with a prefix so therefore this entry is automatically not what we're
177 # looking for
178 logging.debug("Entry has no prefix")
179 continue
180 else:
181 # We have a prefixed entry, or we're looking for one
182 next_field = get_field_after_prefix(log_entry)
183 if next_field and \
184 " ".join(log_entry[PREFIX_FIELD_START:next_field]) == options.prefix:
185 # We have a valid line to work with
186 ips.add(log_entry[next_field + SRC_FIELD_OFFSET].split("SRC=")[1])
187 else:
188 logging.debug("(2) Entry not for the prefix we're looking for: %s"
189 % " ".join(log_entry))
190 continue
191 log.close()
192
193 return ips
194
195
196 def report_ips(options, ips):
197 """
198 Submit the IPs to the blacklist service
199
200 Args:
201 options: options object
202 ips: set of IP addresses to report
203 """
204
205 # TODO(apollock): exception handling
206
207 SUBMISSION_URL="http://blacklist.steve.org.uk/cgi-bin/report.cgi?src=%s"
208 #SUBMISSION_URL="http://www.andrew.net.au/cgi-bin/report.cgi?src=%s"
209
210 for ip in ips:
211 if not options.dryrun:
212 result = urllib.urlopen(SUBMISSION_URL % (ip))
213 logging.info("%16s: %s" % (ip, "".join(result.readlines()).rstrip()))
214 result.close()
215 else:
216 print SUBMISSION_URL % (ip)
217
218
219 def main():
220 options = process_options()
221
222 setup_logging(options)
223
224 ips = process_log(options)
225
226 if ips:
227 report_ips(options, ips)
228
229
230 if __name__ == "__main__":
231 main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.22