PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/iptableslog/iptableslog.pas

https://bitbucket.org/reiniero/smalltools
Pascal | 493 lines | 339 code | 31 blank | 123 comment | 10 complexity | 752cd90653c76e519bc20c757eac932f MD5 | raw file
  1. {*
  2. This source code is provided under the MIT license:
  3. Copyright (C) 2011-2013 by Reinier Olislagers
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. *}
  20. unit iptableslog;
  21. // Import IPTables firewall output from an rsyslog file and dump it as csv
  22. // Todo: check for compatiblity with syslog, maybe syslog-ng
  23. {$mode objfpc}{$H+}
  24. interface
  25. uses
  26. Classes, SysUtils, RegExpr;
  27. type
  28. { TIPTablesLog }
  29. //todo: convert to e.g. bufdataset so we can do more with it
  30. //include check for assigned so we don't get nasty errors
  31. TIPTablesLogRecord = record
  32. {See e.g.
  33. https://secure.wikimedia.org/wikipedia/en/wiki/Transmission_Control_Protocol
  34. }
  35. TimeStamp: TDateTime; //Syslog event timestamp
  36. PrecisionTimestamp: string;
  37. //There apparently is some kind of precision timestamp in rsyslog (but not in BSD syslog)
  38. Host: string; //Host where syslog event originated
  39. Chain: string; //Name of the chain generating the event
  40. InterfaceIn: string; //Incoming interface (IN= field)
  41. InterfaceOut: string; //Outgoing interface (OUT=)
  42. Source: string; //Source IP address (SRC=)
  43. MAC: string; //Source? MAC address (MAC=)
  44. SourcePort: integer; //Source port (SPT=)
  45. Destination: string; //Destination (DEST=)
  46. DestinationPort: integer; //Destination port (DPT=)
  47. PacketLength: integer; //Length of packet (LEN=)
  48. TOS: string; //TOS flags (TOS=), a hex value
  49. PREC: string; //(PREC=) what does this mean?; a hex value
  50. ID: integer; //ID of packet??
  51. TTL: integer; //Time-to-live count (TTL=)
  52. Protocol: string; //Protocol (e.g. TCP, UDP) (PROTO=)
  53. Window: integer; //Receive window size (WINDOW=)
  54. RES: string; //Reserved field (RES=)
  55. {
  56. //IP flags not found in my output
  57. NS: boolean; //ECN-nonce concealment protection flag
  58. CWR: boolean; //Congestion Window Reduced flag
  59. ECE: boolean; //ECE flag
  60. }
  61. URG: boolean; //Urgent flag (URGP=) (for OOB transmission, not widely used)
  62. ACK: boolean; //Acknowledgement field (ACK) (all data packets after handshake need this set)
  63. PSH: boolean; //Push
  64. RST: boolean; //Reset (RST) (break connection)
  65. SYN: boolean; //SYN flag (handshake: SYN=>SYN+ACK=>ACK)
  66. FIN: boolean;
  67. //FIN: disconnect half of the connection (full disconnect handshake e.g. FIN=>FIN+ACK=>ACK)
  68. DF: boolean; //Don't fragment
  69. end;
  70. TIPTablesLog = class(TObject)
  71. private
  72. //Log lines
  73. FLines: TStringList;
  74. FLinesProcessed: integer;
  75. //Input file
  76. FLogFile: string;
  77. //File where parsed output will be written
  78. FOutputFile: string;
  79. // Converts various fields 'Jan',' ','1','08:34:55' into date/time
  80. function GetTimeStamp(const MonthName, Param2, Param3, Param4: string): TDateTime;
  81. function ValueAfter(const DataList: TStringList; const SearchFor: string;
  82. FirstField: integer): string;
  83. function Quote(const ABoolean: boolean): string;
  84. function Quote(const PlainString: string): string;
  85. function Quote(const ATimeStamp: TDateTime): string;
  86. public
  87. //Source file with messages
  88. property InputFile: string read FLogFile write FLogFile;
  89. property LinesProcessed: integer read FLinesProcessed;
  90. //Destination file
  91. property OutputFile: string read FOutputFile write FOutputFile;
  92. //Process input and write output
  93. procedure Parse;
  94. //Show output fields
  95. function ShowFields(): string;
  96. constructor Create;
  97. destructor Destroy; override;
  98. end;
  99. const
  100. Delim = ',';
  101. implementation
  102. uses dateutils;
  103. { TIPTablesLog }
  104. function TIPTablesLog.Quote(const ABoolean: boolean): string;
  105. begin
  106. if ABoolean then
  107. Result := '1'
  108. else
  109. Result := '0';
  110. end;
  111. function TIPTablesLog.Quote(const PlainString: string): string;
  112. // FPC has bugs in delimited text export so rolling my own... sigh
  113. const
  114. QuoteChar = '"';
  115. begin
  116. Result := StringReplace(PlainString, QuoteChar, QuoteChar + QuoteChar,
  117. [rfReplaceAll, rfIgnoreCase]);
  118. Result := QuoteChar + PlainString + QuoteChar;
  119. end;
  120. function TIPTablesLog.Quote(const ATimeStamp: TDateTime): string;
  121. const
  122. QuoteChar = '"';
  123. begin
  124. Result := QuoteChar + FormatDateTime('yyyy-mm-dd hh:nn:ss', ATimeStamp) + QuoteChar;
  125. end;
  126. function TIPTablesLog.GetTimeStamp(
  127. const MonthName, Param2, Param3, Param4: string): TDateTime;
  128. // Parse month name, day, time as present in split out strings.
  129. // Can deal with extra space between month and day but no other formatting changes.
  130. var
  131. DayInt: integer;
  132. HourInt: integer;
  133. MinuteInt: integer;
  134. LogYear: word;
  135. MonthInt: integer;
  136. MonthSelect: string;
  137. ParamShiftRight: boolean;
  138. SecondInt: integer;
  139. ThisMonth: word;
  140. ToDay: word;
  141. begin
  142. MonthSelect := AnsiUpperCase(Copy(MonthName, 1, 3));
  143. case MonthSelect of
  144. 'JAN': MonthInt := 1;
  145. 'FEB': MonthInt := 2;
  146. 'MAR': MonthInt := 3;
  147. 'APR': MonthInt := 4;
  148. 'MAY': MonthInt := 5;
  149. 'JUN': MonthInt := 6;
  150. 'JUL': MonthInt := 7;
  151. 'AUG': MonthInt := 8;
  152. 'SEP': MonthInt := 9;
  153. 'OCT': MonthInt := 10;
  154. 'NOV': MonthInt := 11;
  155. 'DEC': MonthInt := 12;
  156. else
  157. MonthInt := -1;
  158. end;
  159. // We could have Mar 1 or Jan 22 (note extra space). If so, we need to shift our parameters
  160. if Trim(Param2) = '' then
  161. ParamShiftRight := True
  162. else
  163. ParamShiftRight := False;
  164. if ParamShiftRight then
  165. begin
  166. DayInt := StrToIntDef(Param3, -1);
  167. //12345678
  168. //23:59:59
  169. HourInt := StrToIntDef(Copy(Param4, 1, 2), -1);
  170. MinuteInt := StrToIntDef(Copy(Param4, 4, 2), -1);
  171. SecondInt := StrToIntDef(Copy(Param4, 7, 2), -1);
  172. end
  173. else
  174. begin
  175. DayInt := StrToIntDef(Param2, -1);
  176. ;
  177. HourInt := StrToIntDef(Copy(Param3, 1, 2), -1);
  178. MinuteInt := StrToIntDef(Copy(Param3, 4, 2), -1);
  179. SecondInt := StrToIntDef(Copy(Param3, 7, 2), -1);
  180. end;
  181. // Assume within last 12 months... so set up year
  182. DecodeDate(Date, LogYear, ThisMonth, ToDay);
  183. if MonthInt > ThisMonth then
  184. LogYear := LogYear - 1;
  185. try
  186. Result := EncodeDateTime(LogYear, MonthInt, DayInt, HourInt, MinuteInt, SecondInt, 0);
  187. except
  188. Result := MinDateTime; //default date that is so far out it can't be plausible
  189. end;
  190. end;
  191. function TIPTablesLog.ValueAfter(const DataList: TStringList;
  192. const SearchFor: string; FirstField: integer): string;
  193. // Searches a stringlist and gets the value after SearchFor
  194. // Handy for key=value lists
  195. // note: SearchFor may appear anywhere in the field.
  196. // todo: check if it can simply be replaced by stringlist .Names and .Values?
  197. var
  198. Counter: integer;
  199. KeyPos: integer;
  200. begin
  201. Result := EmptyStr;
  202. for Counter := FirstField to DataList.Count - 1 do
  203. begin
  204. KeyPos := AnsiPos(SearchFor, DataList[Counter]);
  205. if KeyPos > 0 then
  206. begin
  207. Result := Copy(DataList[Counter], KeyPos + Length(SearchFor),
  208. Length(DataList[Counter]));
  209. break; //Get out of fields loop
  210. end;
  211. end;
  212. end;
  213. procedure TIPTablesLog.Parse;
  214. var
  215. FirstIPTablesDataField: integer; //First interesting field in output (usually IN=ethx)
  216. LineNum: integer;
  217. IPTables: TRegExpr;
  218. OutputTextFile: TextFile;
  219. TheRecord: TIPTablesLogRecord; //Single log record
  220. RecordList: TStringList;
  221. procedure DetermineFields;
  222. // Determines position of fields
  223. const
  224. LookFor = 'OUT=';
  225. var
  226. Counter: integer;
  227. begin
  228. //determine fields based on OUT= (IN= is problematic as the FW chain can run up to there without spaces)
  229. FirstIPTablesDataField := -1;
  230. for Counter := 0 to RecordList.Count - 1 do
  231. begin
  232. if Pos(LookFor, RecordList[Counter]) = 1 then
  233. begin
  234. FirstIPTablesDataField := Counter - 1; //Looking for the field before this field
  235. exit;
  236. end;
  237. end;
  238. end;
  239. begin
  240. //If performance is bad, perhaps go back to old school readln for line by line reading
  241. FLines.LoadFromFile(FLogFile);
  242. IPTables := TRegexpr.Create;
  243. RecordList := TStringList.Create;
  244. AssignFile(OutputTextFile, OutputFile);
  245. try
  246. try
  247. Rewrite(OutputTextFile);
  248. {
  249. Mar 1 10:49:51 MyServer kernel: [ 547.563456] [UFW ALLOW] IN=eth0 OUT= MAC=33:11:2f:90:c3:7b:00:21:27:f7:5d:41:08:00 SRC=215.14.80.193 DST=192.168.5.110 LEN=60 TOS=0x00 PREC=0x00 TTL=42 ID=16050 DF PROTO=TCP SPT=38985 DPT=22 WINDOW=5840 RES=0x00 SYN URGP=0
  250. Jan 21 13:25:01 router kernel: [517703.569726] [DMZ-OUTBOUND-default-D]IN=eth2 OUT=eth1 SRC=12.13.14.15 DST=172.145.153.201 LEN=40 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=80 DPT=13298 WINDOW=0 RES=0x00 RST URGP=0
  251. }
  252. // Should match (only) on IPTables chains. Capture group with firewall chain name
  253. // Capture groups: () in the regex
  254. // Capture group 1 matches host name
  255. // Capture group 2 matches rsyslog kernel timestamp(?) that may exist, e.g.:
  256. // kernel: [ 77880.665120]
  257. // Capture group 3 matches [chainname], e.g.:
  258. // [DMZ-OUTBOUND-default-D]
  259. // Between OUT and SRC, allow for optional presence of MAC field
  260. // like MAC=00:11:22:33:d3:7a:00:41:24:55:66:77:18:00
  261. // Capture group 4,5 match MAC address including MAC
  262. // Capture group 6 match MAC digits only
  263. // The regex used may be a bit loose, but I'm more concerned about leaving out valid lines than
  264. // trying to include invalid ones - the NAME=VALUE handling below will deal with it.
  265. // OUT may have an empty value
  266. // We use lazy matching
  267. // .*?
  268. // before PROTO to limit our matches
  269. // It matches ip address etc by just looking at anything except space (\S). This should make it IPv6 ready.
  270. // Note: this regex implementation does not seem to support [ ] in all situations (or I don't understand it ;)
  271. // worked around it with ()
  272. IPTables.Expression := RegExprString(
  273. '(\w+)\skernel: (\[\s*\d+\.\d+]) (\[.+])\s*IN=\w+ OUT=\w*((\s+MAC=([a-fA-F\d\-:]*)\s)|\s+)SRC=(\S)* DST=(\S)* LEN=\d+ TOS=\w+ PREC=\w+ TTL=\d+ ID=\w+');
  274. RecordList.StrictDelimiter := True;
  275. //RecordList.QuoteChar:=''; //NULL or something?
  276. RecordList.Delimiter := ' ';
  277. //Header row
  278. writeln(OutputTextFile,
  279. Quote('TimeStamp') + Delim + Quote('PrecisionTimestamp') + Delim +
  280. Quote('Host') + Delim + Quote('Chain') + Delim + Quote('InterfaceIn') +
  281. Delim + Quote('MAC') + Delim + Quote('InterfaceOut') + Delim +
  282. Quote('Source') + Delim + Quote('SourcePort') + Delim + Quote('Destination') + Delim +
  283. Quote('DestinationPort') + Delim + Quote('Protocol') + Delim +
  284. Quote('ID') + Delim + Quote('PacketLength') + Delim + Quote('TOS') + Delim +
  285. Quote('PREC') + Delim + Quote('TTL') + Delim + Quote('SYN') + Delim +
  286. Quote('ACK') + Delim + Quote('FIN') + Delim + Quote('RST') + Delim +
  287. Quote('PSH') + Delim + Quote('URG') + Delim + Quote('DF') + Delim +
  288. Quote('Window') + Delim + Quote('Reserved')
  289. );
  290. LineNum := 0; //Let's follow stringlist conventions and start at 0
  291. while (LineNum < FLines.Count) do
  292. begin
  293. // Check if we have a match for the regex: a valid firewall log entry
  294. if IPTables.Exec(RegExprString(FLines[LineNum])) then
  295. begin
  296. {$IFDEF DEBUG}
  297. {
  298. // Will only work with console, of course..
  299. writeln('cap group 1:'+IPTables.Match[1]);
  300. writeln('cap group 2:'+IPTables.Match[2]);
  301. writeln('cap group 3:'+IPTables.Match[3]);
  302. writeln('cap group 4:'+IPTables.Match[4]);
  303. writeln('cap group 5:'+IPTables.Match[5]);
  304. writeln('cap group 6:'+IPTables.Match[6]);
  305. }
  306. {$ENDIF DEBUG}
  307. RecordList.DelimitedText := FLines[LineNum];
  308. //Fixed fields
  309. TheRecord.TimeStamp := GetTimeStamp(RecordList[0], RecordList[1],
  310. RecordList[2], RecordList[3]);
  311. //We're getting rid of enclosing [ and ]
  312. TheRecord.PrecisionTimestamp :=
  313. Trim(Copy(string(IPTables.Match[2]), 2, Length(string(IPTables.Match[2])) - 2));
  314. //We're getting rid of enclosing [ and ]
  315. TheRecord.Chain := Trim(
  316. Copy(string(IPTables.Match[3]), 2, Length(string(IPTables.Match[3])) - 2));
  317. TheRecord.Host := string(IPTables.Match[1]);
  318. TheRecord.MAC := string(IPTables.Match[6]);
  319. // Variable fields. We could (and did) use fixed field positions.
  320. // While quicker, it's more tedious to maintain and breaks whenever
  321. // the logging format changes.
  322. // We have to check for fields each time, as the number of spaces may change
  323. DetermineFields;
  324. TheRecord.InterfaceIn := ValueAfter(RecordList, 'IN=', FirstIPTablesDataField);
  325. TheRecord.InterfaceOut := ValueAfter(RecordList, 'OUT=', FirstIPTablesDataField);
  326. TheRecord.Source := ValueAfter(RecordList, 'SRC=', FirstIPTablesDataField);
  327. TheRecord.Destination := ValueAfter(RecordList, 'DST=', FirstIPTablesDataField);
  328. TheRecord.ID := StrToIntDef(ValueAfter(RecordList, 'ID=',
  329. FirstIPTablesDataField), -1);
  330. TheRecord.PacketLength :=
  331. StrToIntDef(ValueAfter(RecordList, 'LEN=', FirstIPTablesDataField), -1);
  332. TheRecord.TOS := ValueAfter(RecordList, 'TOS=', FirstIPTablesDataField);
  333. TheRecord.PREC := ValueAfter(RecordList, 'PREC=', FirstIPTablesDataField);
  334. TheRecord.TTL := StrToIntDef(ValueAfter(RecordList, 'TTL=',
  335. FirstIPTablesDataField), -1);
  336. TheRecord.DF := (RecordList.IndexOf('DF') > 0);
  337. TheRecord.Protocol := ValueAfter(RecordList, 'PROTO=', FirstIPTablesDataField);
  338. TheRecord.SourcePort :=
  339. StrToIntDef(ValueAfter(RecordList, 'SPT=', FirstIPTablesDataField), -1);
  340. TheRecord.DestinationPort :=
  341. StrToIntDef(ValueAfter(RecordList, 'DPT=', FirstIPTablesDataField), -1);
  342. TheRecord.Window := StrToIntDef(ValueAfter(RecordList, 'WINDOW=',
  343. FirstIPTablesDataField), -1);
  344. TheRecord.RES := ValueAfter(RecordList, 'RES=', FirstIPTablesDataField);
  345. TheRecord.RST := (RecordList.IndexOf('RST') > 0);
  346. TheRecord.URG := (ValueAfter(RecordList, 'URGP=', FirstIPTablesDataField) = '1');
  347. //TheRecord.NS:= false; //IP flag not found in my output
  348. //TheRecord.CWR:=false; //IP flag not found in my output
  349. //TheRecord.ECE:=false; //IP flag not found in my output
  350. TheRecord.ACK := (RecordList.IndexOf('ACK') > 0);
  351. TheRecord.PSH := (RecordList.IndexOf('PSH') > 0);
  352. TheRecord.SYN := (RecordList.IndexOf('SYN') > 0);
  353. TheRecord.FIN := (RecordList.IndexOf('FIN') > 0);
  354. //write out
  355. writeln(OutputTextFile,
  356. Quote(TheRecord.TimeStamp) + Delim +
  357. Quote(TheRecord.PrecisionTimestamp) + Delim +
  358. Quote(TheRecord.Host) + Delim + Quote(TheRecord.Chain) + Delim +
  359. Quote(TheRecord.InterfaceIn) + Delim + Quote(TheRecord.MAC) + Delim +
  360. Quote(TheRecord.InterfaceOut) + Delim + Quote(TheRecord.Source) + Delim +
  361. IntToStr(TheRecord.SourcePort) + Delim +
  362. Quote(TheRecord.Destination) + Delim +
  363. IntToStr(TheRecord.DestinationPort) + Delim +
  364. Quote(TheRecord.Protocol) + Delim + IntToStr(TheRecord.ID) + Delim +
  365. IntToStr(TheRecord.PacketLength) + Delim + Quote(TheRecord.TOS) + Delim +
  366. Quote(TheRecord.PREC) + Delim + IntToStr(TheRecord.TTL) + Delim +
  367. {
  368. Quote(TheRecord.NS)+Delim+
  369. Quote(TheRecord.CWR)+Delim+
  370. Quote(TheRecord.ECE)+Delim+
  371. }
  372. Quote(TheRecord.SYN) + Delim + Quote(TheRecord.ACK) + Delim +
  373. Quote(TheRecord.FIN) + Delim + Quote(TheRecord.RST) + Delim +
  374. Quote(TheRecord.PSH) + Delim + Quote(TheRecord.URG) + Delim +
  375. Quote(TheRecord.DF) + Delim + IntToStr(TheRecord.Window) + Delim +
  376. Quote(TheRecord.RES));
  377. end
  378. else
  379. begin
  380. //optionally output something
  381. {$IFDEF DEBUG}
  382. //run with e.g. -dDEBUG (e.g. in project options/custom/other)
  383. writeln('No match for line: (enclosed by *)');
  384. writeln('*' + FLines[LineNum] + '*');
  385. {$ENDIF}
  386. end;
  387. LineNum := LineNum + 1;
  388. end;
  389. except
  390. on E: Exception do
  391. begin
  392. writeln('Error occurred : ' + E.ClassName + '/' + E.Message);
  393. writeln('Specified input file : ' + InputFile);
  394. writeln('Specified output file: ' + OutputFile);
  395. halt(13);
  396. end;
  397. end;
  398. finally
  399. FLinesProcessed := LineNum; //Should work as linenum has been increased in last loop
  400. IPTables.Free;
  401. RecordList.Free;
  402. Close(OutputTextFile);
  403. end;
  404. end;
  405. function TIPTablesLog.ShowFields(): string;
  406. begin
  407. //todo: use an array with field names etc, or better yet:
  408. //rebuild record into some kind of class/list that has metadata (field name)
  409. //this will avoid problems with changing field names/positions
  410. Result :=
  411. '## Field name Format Description:' + LineEnding +
  412. '== ================== ================ ========================================' + LineEnding
  413. +
  414. '01 TimeStamp Date/Time Syslog event timestamp' +
  415. LineEnding + '02 PrecisionTimestamp Floating point Precision timestamp? (not in BSD syslog)'
  416. +
  417. LineEnding + '03 Host Text Hostname where syslog event originated'
  418. +
  419. LineEnding + '04 Chain Text Name of the iptables chain' +
  420. LineEnding + '05 InterfaceIn Text Incoming interface (IN= field)' +
  421. LineEnding + '06 MAC Text Concatenated MAC addresses of' +
  422. LineEnding + ' firewall and source (MAC=)' +
  423. LineEnding + '07 InterfaceOut Text Outgoing interface (OUT=)' +
  424. LineEnding + '08 Source Text Source IP address (SRC=)' +
  425. LineEnding + '09 SourcePort Integer Source port (SPT=)' +
  426. LineEnding + '10 Destination Text Destination (DEST=)' +
  427. LineEnding + '11 DestinationPort Integer Destination port (DPT=)' +
  428. LineEnding + '12 Protocol Text Protocol (e.g. TCP, UDP) (PROTO=)' +
  429. LineEnding + '13 ID Integer ID of packet?? (ID=)' +
  430. LineEnding + '14 PacketLength Integer Length of packet (LEN=)' +
  431. LineEnding + '15 TOS Text (hex) TOS (Type of Service) flags (TOS=)' +
  432. LineEnding + '16 PREC Text (hex) Unknown (PREC=)' +
  433. LineEnding + '17 TTL Integer Time-to-live count (TTL=)' +
  434. LineEnding + '18 SYN Boolean SYN flag (SYN)' +
  435. LineEnding + '19 ACK Boolean Acknowledgement flag (ACK)' +
  436. LineEnding + '20 FIN Boolean FIN flag: no more data from sender (FIN)'
  437. +
  438. LineEnding + '21 RST Boolean Reset flag (RST)' +
  439. LineEnding + '22 PSH Boolean Push flag (PSH)' +
  440. LineEnding + '23 URG Boolean Urgent flag (URGP=)' +
  441. LineEnding + '24 DF Boolean Don''t fragment flag (DF)' +
  442. LineEnding + '25 Window Integer Receive window size (WINDOW=)' +
  443. LineEnding + '26 Reserved Text (hex) Reserved field (RES=)' +
  444. LineEnding + LineEnding;
  445. end;
  446. constructor TIPTablesLog.Create;
  447. begin
  448. inherited Create;
  449. FLines := TStringList.Create;
  450. FLogFile := '/var/log/messages';
  451. FOutputFile := 'iptableslog.csv';
  452. FLinesProcessed := 0;
  453. end;
  454. destructor TIPTablesLog.Destroy;
  455. begin
  456. FLines.Free;
  457. inherited Destroy;
  458. end;
  459. end.