New flows now get the last table update time rather than the current time.
[distributedratelimiting.git] / pcap / ulogd_PCAP.c
1 /* ulogd_PCAP.c, Version $Revision: 6016 $
2  *
3  * ulogd output target for writing pcap-style files (like tcpdump)
4  *
5  * FIXME: descr.
6  * 
7  *
8  * (C) 2002 by Harald Welte <laforge@gnumonks.org>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2 
12  *  as published by the Free Software Foundation
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * $Id: ulogd_PCAP.c 6016 2005-09-23 13:37:46Z laforge $
24  *
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <pcap.h>
36 #include <ulogd/ulogd.h>
37 #include <ulogd/conffile.h>
38
39 /*
40  * This is a timeval as stored in disk in a dumpfile.
41  * It has to use the same types everywhere, independent of the actual
42  * `struct timeval'
43  */
44
45 struct pcap_timeval {
46     int32_t tv_sec;             /* seconds */
47     int32_t tv_usec;            /* microseconds */
48 };
49
50 /*
51  * How a `pcap_pkthdr' is actually stored in the dumpfile.
52  *
53  * Do not change the format of this structure, in any way (this includes
54  * changes that only affect the length of fields in this structure),
55  * and do not make the time stamp anything other than seconds and
56  * microseconds (e.g., seconds and nanoseconds).  Instead:
57  *
58  *      introduce a new structure for the new format;
59  *
60  *      send mail to "tcpdump-workers@tcpdump.org", requesting a new
61  *      magic number for your new capture file format, and, when
62  *      you get the new magic number, put it in "savefile.c";
63  *
64  *      use that magic number for save files with the changed record
65  *      header;
66  *
67  *      make the code in "savefile.c" capable of reading files with
68  *      the old record header as well as files with the new record header
69  *      (using the magic number to determine the header format).
70  *
71  * Then supply the changes to "patches@tcpdump.org", so that future
72  * versions of libpcap and programs that use it (such as tcpdump) will
73  * be able to read your new capture file format.
74  */
75
76 struct pcap_sf_pkthdr {
77     struct pcap_timeval ts;     /* time stamp */
78     uint32_t caplen;            /* length of portion present */
79     uint32_t len;               /* length this packet (off wire) */
80 };
81
82 #ifndef ULOGD_PCAP_DEFAULT
83 #define ULOGD_PCAP_DEFAULT      "/var/log/ulogd.pcap"
84 #endif
85
86 #ifndef ULOGD_PCAP_SYNC_DEFAULT
87 #define ULOGD_PCAP_SYNC_DEFAULT 0
88 #endif
89
90 #define NIPQUAD(addr) \
91         ((unsigned char *)&addr)[0], \
92         ((unsigned char *)&addr)[1], \
93         ((unsigned char *)&addr)[2], \
94         ((unsigned char *)&addr)[3]
95
96 static config_entry_t pcapf_ce = { 
97         .key = "file", 
98         .type = CONFIG_TYPE_STRING, 
99         .options = CONFIG_OPT_NONE,
100         .u = { .string = ULOGD_PCAP_DEFAULT }
101 };
102
103 static config_entry_t pcapsync_ce = { 
104         .next = &pcapf_ce, 
105         .key = "sync", 
106         .type = CONFIG_TYPE_INT,
107         .options = CONFIG_OPT_NONE,
108         .u = { .value = ULOGD_PCAP_SYNC_DEFAULT }
109 };
110
111 static FILE *of = NULL;
112
113 struct intr_id {
114         char* name;
115         unsigned int id;                
116 };
117
118 #define INTR_IDS        5
119 static struct intr_id intr_ids[INTR_IDS] = {
120         { "raw.pkt", 0 },
121         { "raw.pktlen", 0 },
122         { "ip.totlen", 0 },
123         { "oob.time.sec", 0 },
124         { "oob.time.usec", 0 },
125 };
126
127 #define GET_VALUE(x)    ulogd_keyh[intr_ids[x].id].interp->result[ulogd_keyh[intr_ids[x].id].offset].value
128 #define GET_FLAGS(x)    ulogd_keyh[intr_ids[x].id].interp->result[ulogd_keyh[intr_ids[x].id].offset].flags
129
130 static int pcap_output(ulog_iret_t *res)
131 {
132         struct pcap_sf_pkthdr pchdr;
133
134         pchdr.caplen = GET_VALUE(1).ui32;
135         pchdr.len = GET_VALUE(2).ui32;
136
137         if (GET_FLAGS(3) & ULOGD_RETF_VALID
138             && GET_FLAGS(4) & ULOGD_RETF_VALID) {
139                 pchdr.ts.tv_sec = GET_VALUE(3).ui32;
140                 pchdr.ts.tv_usec = GET_VALUE(4).ui32;
141         } else {
142                 /* use current system time */
143                 struct timeval tv;
144                 gettimeofday(&tv, NULL);
145
146                 pchdr.ts.tv_sec = tv.tv_sec;
147                 pchdr.ts.tv_usec = tv.tv_usec;
148         }
149
150         if (fwrite(&pchdr, sizeof(pchdr), 1, of) != 1) {
151                 ulogd_log(ULOGD_ERROR, "Error during write: %s\n",
152                           strerror(errno));
153                 return 1;
154         }
155         if (fwrite(GET_VALUE(0).ptr, pchdr.caplen, 1, of) != 1) {
156                 ulogd_log(ULOGD_ERROR, "Error during write: %s\n",
157                           strerror(errno));
158                 return 1;
159         }
160
161         if (pcapf_ce.u.value)
162                 fflush(of);
163
164         return 0;
165 }
166
167 /* stolen from libpcap savefile.c */
168 #define LINKTYPE_RAW            101
169 #define TCPDUMP_MAGIC   0xa1b2c3d4
170
171 static int write_pcap_header(void)
172 {
173         struct pcap_file_header pcfh;
174         int ret;
175
176         pcfh.magic = TCPDUMP_MAGIC;
177         pcfh.version_major = PCAP_VERSION_MAJOR;
178         pcfh.version_minor = PCAP_VERSION_MINOR;
179         pcfh.thiszone = timezone;
180         pcfh.sigfigs = 0;
181         pcfh.snaplen = 65535; /* we don't know the length in advance */
182         pcfh.linktype = LINKTYPE_RAW;
183
184         ret =  fwrite(&pcfh, sizeof(pcfh), 1, of);
185         fflush(of);
186
187         return ret;
188 }
189         
190 /* get all key id's for the keys we are intrested in */
191 static int get_ids(void)
192 {
193         int i;
194         struct intr_id *cur_id;
195
196         for (i = 0; i < INTR_IDS; i++) {
197                 cur_id = &intr_ids[i];
198                 cur_id->id = keyh_getid(cur_id->name);
199                 if (!cur_id->id) {
200                         ulogd_log(ULOGD_ERROR, 
201                                 "Cannot resolve keyhash id for %s\n", 
202                                 cur_id->name);
203                         return 1;
204                 }
205         }       
206         return 0;
207 }
208
209 void append_create_outfile(void) {
210         struct stat st_dummy;
211         int exist = 0;
212
213         if (stat(pcapf_ce.u.string, &st_dummy) == 0 && st_dummy.st_size > 0) {
214                 exist = 1;
215         }
216
217         if (!exist) {
218                 of = fopen(pcapf_ce.u.string, "w");
219                 if (!of) {
220                         ulogd_log(ULOGD_FATAL, "can't open pcap file: %s\n",
221                                   strerror(errno));
222                         exit(2);
223                 }
224                 if (!write_pcap_header()) {
225                         ulogd_log(ULOGD_FATAL, "can't write pcap header: %s\n",
226                                   strerror(errno));
227                         exit(2);
228                 }
229         } else {
230                 of = fopen(pcapf_ce.u.string, "a");
231                 if (!of) {
232                         ulogd_log(ULOGD_FATAL, "can't open pcap file: %s\n", 
233                                 strerror(errno));
234                         exit(2);
235                 }               
236         }
237 }
238
239 static void pcap_signal_handler(int signal)
240 {
241         switch (signal) {
242         case SIGHUP:
243                 ulogd_log(ULOGD_NOTICE, "pcap: reopening capture file\n");
244                 fclose(of);
245                 append_create_outfile();
246                 break;
247         default:
248                 break;
249         }
250 }
251
252 static int pcap_init(void)
253 {
254         /* FIXME: error handling */
255         config_parse_file("PCAP", &pcapsync_ce);
256
257 #ifdef DEBUG_PCAP
258         of = stdout;
259 #else
260         append_create_outfile();
261 #endif
262         return 0;
263 }
264
265 static void pcap_fini(void)
266 {
267         if (of)
268                 fclose(of);
269 }
270
271 static ulog_output_t pcap_op = {
272         .name = "pcap", 
273         .init = &pcap_init,
274         .fini = &pcap_fini,
275         .output = &pcap_output,
276         .signal = &pcap_signal_handler,
277 };
278
279 void _init(void)
280 {
281         if (get_ids()) {
282                 ulogd_log(ULOGD_ERROR, "can't resolve all keyhash id's\n");
283         }
284
285         register_output(&pcap_op);
286 }