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

Annotation of /scripts/report_iptables_ulog.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 45 - (hide annotations)
Fri Sep 5 06:08:42 2008 UTC (16 years ago) by apollock
File MIME type: text/x-python
File size: 5893 byte(s)
Initial commit

1 apollock 45 #!/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     __licence__ = "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_prefix(ulogentry):
104     """
105     Return the prefix component of a ulog entry
106    
107     Args:
108     ulogentry: a list of strings representing one line of a ulog entry
109    
110     Returns:
111     A list consisting of the prefix and the number of the field after the
112     prefix
113     """
114    
115     prefix=[]
116    
117     for component in xrange(PREFIX_FIELD_START, len(ulogentry)):
118     if not ulogentry[component].startswith(FIELD_AFTER_PREFIX):
119     prefix.append(ulogentry[component])
120     else:
121     break
122    
123     # If for some reason we reached the end of the line without finding
124     # FIELD_AFTER_PREFIX, something is probably wrong
125    
126     if component == len(ulogentry):
127     return None
128     else:
129     return (" ".join(prefix), component)
130    
131    
132     def process_log(options):
133     """
134     Process the log file
135    
136     Args:
137     options object
138    
139     Returns:
140     A set of IP addresses
141    
142     Discussion:
143    
144     A line of the log file can look like:
145     Aug 31 06:46:06 caesar FORWARD too-hard-basket IN=eth0 OUT=eth1 MAC=00:08:02:52:
146     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
147     REC=0x00 TTL=59 ID=0 DF PROTO=ICMP TYPE=8 CODE=0 ID=42504 SEQ=6344
148    
149     Notably, the "FORWARD too-hard-basket" is a user definable string, which can
150     contain whitespace, so therefore there's no predetermined position that the
151     SRC= field can be found in, therefore for each line, we need to iterate over
152     each whitespace-separated "word" until we find the right one. Bit of a bummer
153     that.
154     """
155    
156     # TODO(apollock): This needs refactoring, there's too much duplication
157    
158     ips = set()
159    
160     # TODO(apollock): exception handling!
161     try:
162     log = open(options.log)
163     except IOError, e:
164     logging.critical("Could not open %s (%s)" % (options.log, e.strerror))
165     sys.exit(1)
166     while True:
167     log_entry = log.readline().split()
168     if not log_entry:
169     logging.debug("Reached the end of the file")
170     break
171     if options.hostname and log_entry[HOSTNAME_FIELD] != options.hostname:
172     logging.debug("Entry not for the host we're looking for")
173     continue
174     # We know where the prefix starts (if there is one)
175     if log_entry[PREFIX_FIELD_START].startswith(FIELD_AFTER_PREFIX):
176     # We have an entry with no prefix at all
177     logging.debug("Entry has no prefix")
178     if options.prefix:
179     continue
180     else:
181     # We need to build the prefix and check
182     (prefix, next_field) = get_prefix(log_entry)
183     if prefix and prefix == options.prefix:
184     # We have a valid line to work with
185     ips.add(log_entry[next_field + SRC_FIELD_OFFSET].split("SRC=")[1])
186     else:
187     logging.debug("(1) Entry not for the prefix we're looking for")
188     continue
189     else:
190     # We have part of the prefix and need to build it
191     (prefix, next_field) = get_prefix(log_entry)
192     if prefix and prefix == options.prefix:
193     # We have a valid line to work with
194     ips.add(log_entry[next_field + SRC_FIELD_OFFSET].split("SRC=")[1])
195     else:
196     logging.debug("(2) Entry not for the prefix we're looking for: %s"
197     % " ".join(log_entry))
198     continue
199     log.close()
200    
201     return ips
202    
203    
204     def report_ips(options, ips):
205     """
206     Submit the IPs to the blacklist service
207    
208     Args:
209     options: options object
210     ips: set of IP addresses to report
211     """
212    
213     SUBMISSION_URL="http://blacklist.steve.org.uk/cgi-bin/report.cgi?src=%s"
214     #SUBMISSION_URL="http://www.andrew.net.au/cgi-bin/report.cgi?src=%s"
215    
216     for ip in ips:
217     if not options.dryrun:
218     result = urllib.urlopen(SUBMISSION_URL % (ip))
219     logging.info("%s: %s" % (ip, "".join(result.readlines()).rstrip()))
220     result.close()
221     else:
222     print SUBMISSION_URL % (ip)
223    
224     def main():
225     options = process_options()
226    
227     setup_logging(options)
228    
229     ips = process_log(options)
230    
231     if ips:
232     report_ips(options, ips)
233    
234    
235     if __name__ == "__main__":
236     main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.22