1 /* Copyright (C) 2007-2020 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Danny Browning <danny.browning@protectwise.com>
22  *
23  * File based pcap packet acquisition support
24  */
25 
26 #include "source-pcap-file-helper.h"
27 #include "util-checksum.h"
28 #include "util-profiling.h"
29 #include "source-pcap-file.h"
30 
31 extern int max_pending_packets;
32 extern PcapFileGlobalVars pcap_g;
33 
34 static void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt);
35 
CleanupPcapFileFileVars(PcapFileFileVars * pfv)36 void CleanupPcapFileFileVars(PcapFileFileVars *pfv)
37 {
38     if (pfv != NULL) {
39         if (pfv->pcap_handle != NULL) {
40             pcap_close(pfv->pcap_handle);
41             pfv->pcap_handle = NULL;
42         }
43         if (pfv->filename != NULL) {
44             if (pfv->shared != NULL && pfv->shared->should_delete) {
45                 SCLogDebug("Deleting pcap file %s", pfv->filename);
46                 if (unlink(pfv->filename) != 0) {
47                     SCLogWarning(SC_ERR_PCAP_FILE_DELETE_FAILED,
48                                  "Failed to delete %s", pfv->filename);
49                 }
50             }
51             SCFree(pfv->filename);
52             pfv->filename = NULL;
53         }
54         pfv->shared = NULL;
55         SCFree(pfv);
56     }
57 }
58 
PcapFileCallbackLoop(char * user,struct pcap_pkthdr * h,u_char * pkt)59 void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
60 {
61     SCEnter();
62 
63     PcapFileFileVars *ptv = (PcapFileFileVars *)user;
64     Packet *p = PacketGetFromQueueOrAlloc();
65 
66     if (unlikely(p == NULL)) {
67         SCReturn;
68     }
69     PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
70 
71     PKT_SET_SRC(p, PKT_SRC_WIRE);
72     p->ts.tv_sec = h->ts.tv_sec;
73     p->ts.tv_usec = h->ts.tv_usec;
74     SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
75     p->datalink = ptv->datalink;
76     p->pcap_cnt = ++pcap_g.cnt;
77 
78     p->pcap_v.tenant_id = ptv->shared->tenant_id;
79     ptv->shared->pkts++;
80     ptv->shared->bytes += h->caplen;
81 
82     if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
83         TmqhOutputPacketpool(ptv->shared->tv, p);
84         PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
85         SCReturn;
86     }
87 
88     /* We only check for checksum disable */
89     if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
90         p->flags |= PKT_IGNORE_CHECKSUM;
91     } else if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_AUTO) {
92         if (ChecksumAutoModeCheck(ptv->shared->pkts, p->pcap_cnt,
93                                   SC_ATOMIC_GET(pcap_g.invalid_checksums))) {
94             pcap_g.checksum_mode = CHECKSUM_VALIDATION_DISABLE;
95             p->flags |= PKT_IGNORE_CHECKSUM;
96         }
97     }
98 
99     PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
100 
101     if (TmThreadsSlotProcessPkt(ptv->shared->tv, ptv->shared->slot, p) != TM_ECODE_OK) {
102         pcap_breakloop(ptv->pcap_handle);
103         ptv->shared->cb_result = TM_ECODE_FAILED;
104     }
105 
106     SCReturn;
107 }
108 
109 char pcap_filename[PATH_MAX] = "unknown";
110 
PcapFileGetFilename(void)111 const char *PcapFileGetFilename(void)
112 {
113     return pcap_filename;
114 }
115 
116 /**
117  *  \brief Main PCAP file reading Loop function
118  */
PcapFileDispatch(PcapFileFileVars * ptv)119 TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
120 {
121     SCEnter();
122 
123     /* initialize all the thread's initial timestamp */
124     if (likely(ptv->first_pkt_hdr != NULL)) {
125         TmThreadsInitThreadsTimestamp(&ptv->first_pkt_ts);
126         PcapFileCallbackLoop((char *)ptv, ptv->first_pkt_hdr,
127                 (u_char *)ptv->first_pkt_data);
128         ptv->first_pkt_hdr = NULL;
129         ptv->first_pkt_data = NULL;
130     }
131 
132     int packet_q_len = 64;
133     TmEcode loop_result = TM_ECODE_OK;
134     strlcpy(pcap_filename, ptv->filename, sizeof(pcap_filename));
135 
136     while (loop_result == TM_ECODE_OK) {
137         if (suricata_ctl_flags & SURICATA_STOP) {
138             SCReturnInt(TM_ECODE_OK);
139         }
140 
141         /* make sure we have at least one packet in the packet pool, to prevent
142          * us from alloc'ing packets at line rate */
143         PacketPoolWait();
144 
145         /* Right now we just support reading packets one at a time. */
146         int r = pcap_dispatch(ptv->pcap_handle, packet_q_len,
147                           (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
148         if (unlikely(r == -1)) {
149             SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s for %s",
150                        r, pcap_geterr(ptv->pcap_handle), ptv->filename);
151             if (ptv->shared->cb_result == TM_ECODE_FAILED) {
152                 SCReturnInt(TM_ECODE_FAILED);
153             }
154             loop_result = TM_ECODE_DONE;
155         } else if (unlikely(r == 0)) {
156             SCLogInfo("pcap file %s end of file reached (pcap err code %" PRId32 ")",
157                       ptv->filename, r);
158             ptv->shared->files++;
159             loop_result = TM_ECODE_DONE;
160         } else if (ptv->shared->cb_result == TM_ECODE_FAILED) {
161             SCLogError(SC_ERR_PCAP_DISPATCH,
162                        "Pcap callback PcapFileCallbackLoop failed for %s", ptv->filename);
163             loop_result = TM_ECODE_FAILED;
164         }
165         StatsSyncCountersIfSignalled(ptv->shared->tv);
166     }
167 
168     SCReturnInt(loop_result);
169 }
170 
171 /** \internal
172  *  \brief get the timestamp of the first packet and rewind
173  *  \param pfv pcap file variables for storing the timestamp
174  *  \retval bool true on success, false on error
175  */
PeekFirstPacketTimestamp(PcapFileFileVars * pfv)176 static bool PeekFirstPacketTimestamp(PcapFileFileVars *pfv)
177 {
178     int r = pcap_next_ex(pfv->pcap_handle, &pfv->first_pkt_hdr, &pfv->first_pkt_data);
179     if (r <= 0 || pfv->first_pkt_hdr == NULL) {
180         SCLogError(SC_ERR_PCAP_OPEN_OFFLINE,
181                 "failed to get first packet timestamp. pcap_next_ex(): %d", r);
182         return false;
183     }
184     /* timestamp in pfv->first_pkt_hdr may not be 'struct timeval' so
185      * do a manual copy of the members. */
186     pfv->first_pkt_ts.tv_sec = pfv->first_pkt_hdr->ts.tv_sec;
187     pfv->first_pkt_ts.tv_usec = pfv->first_pkt_hdr->ts.tv_usec;
188     return true;
189 }
190 
InitPcapFile(PcapFileFileVars * pfv)191 TmEcode InitPcapFile(PcapFileFileVars *pfv)
192 {
193     char errbuf[PCAP_ERRBUF_SIZE] = "";
194 
195     if(unlikely(pfv->filename == NULL)) {
196         SCLogError(SC_ERR_INVALID_ARGUMENT, "Filename was null");
197         SCReturnInt(TM_ECODE_FAILED);
198     }
199 
200     pfv->pcap_handle = pcap_open_offline(pfv->filename, errbuf);
201     if (pfv->pcap_handle == NULL) {
202         SCLogError(SC_ERR_FOPEN, "%s", errbuf);
203         SCReturnInt(TM_ECODE_FAILED);
204     }
205 
206     if (pfv->shared != NULL && pfv->shared->bpf_string != NULL) {
207         SCLogInfo("using bpf-filter \"%s\"", pfv->shared->bpf_string);
208 
209         if (pcap_compile(pfv->pcap_handle, &pfv->filter, pfv->shared->bpf_string, 1, 0) < 0) {
210             SCLogError(SC_ERR_BPF, "bpf compilation error %s for %s",
211                        pcap_geterr(pfv->pcap_handle), pfv->filename);
212             SCReturnInt(TM_ECODE_FAILED);
213         }
214 
215         if (pcap_setfilter(pfv->pcap_handle, &pfv->filter) < 0) {
216             SCLogError(SC_ERR_BPF,"could not set bpf filter %s for %s",
217                        pcap_geterr(pfv->pcap_handle), pfv->filename);
218             pcap_freecode(&pfv->filter);
219             SCReturnInt(TM_ECODE_FAILED);
220         }
221         pcap_freecode(&pfv->filter);
222     }
223 
224     pfv->datalink = pcap_datalink(pfv->pcap_handle);
225     SCLogDebug("datalink %" PRId32 "", pfv->datalink);
226 
227     if (!PeekFirstPacketTimestamp(pfv))
228         SCReturnInt(TM_ECODE_FAILED);
229 
230     DecoderFunc UnusedFnPtr;
231     TmEcode validated = ValidateLinkType(pfv->datalink, &UnusedFnPtr);
232     SCReturnInt(validated);
233 }
234 
ValidateLinkType(int datalink,DecoderFunc * DecoderFn)235 TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn)
236 {
237     switch (datalink) {
238         case LINKTYPE_LINUX_SLL:
239             *DecoderFn = DecodeSll;
240             break;
241         case LINKTYPE_ETHERNET:
242             *DecoderFn = DecodeEthernet;
243             break;
244         case LINKTYPE_PPP:
245             *DecoderFn = DecodePPP;
246             break;
247         case LINKTYPE_IPV4:
248         case LINKTYPE_RAW:
249         case LINKTYPE_RAW2:
250         case LINKTYPE_GRE_OVER_IP:
251             *DecoderFn = DecodeRaw;
252             break;
253         case LINKTYPE_NULL:
254             *DecoderFn = DecodeNull;
255             break;
256         case LINKTYPE_CISCO_HDLC:
257             *DecoderFn = DecodeCHDLC;
258             break;
259 
260         default:
261             SCLogError(SC_ERR_UNIMPLEMENTED,
262                     "datalink type %"PRId32" not (yet) supported in module PcapFile.",
263                     datalink);
264             SCReturnInt(TM_ECODE_FAILED);
265     }
266 
267     SCReturnInt(TM_ECODE_OK);
268 }
269