c8bbea76e49c03b0006a8e350079704475601cca
[anna.git] / example / diameter / pcapDecoder / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // http://redmine.teslayout.com/projects/anna-suite
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     *  Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36 // Standard
37 #include <pcap.h>
38 #include <stdlib.h>
39 #include <netinet/ip.h>
40 #include <arpa/inet.h>
41 #include <iostream>
42 #include <fstream>
43
44 // STL
45 #include <string>
46 #include <map>
47
48 #include <anna/core/DataBlock.hpp>
49 #include <anna/core/util/Tokenizer.hpp>
50 #include <anna/core/functions.hpp>
51 #include <anna/core/tracing/Logger.hpp>
52 #include <anna/core/tracing/TraceWriter.hpp>
53 #include <anna/core/RuntimeException.hpp>
54
55
56 using namespace anna;
57
58 // Payload and frame metadata /////////////////////////////////////////////////////////////////////////////
59 class Payload {
60
61   std::string _sourceIP;
62   std::string _destinationIP;
63   time_t _timestamp;
64   int _timestampU; // usecs
65   std::string _data;
66   size_t _diameterLength;
67
68 public:
69   Payload() {
70     reset();
71   }
72
73   void setDiameterLength(size_t dl) {
74     if(_diameterLength == -1) {
75       _diameterLength = dl;
76       LOGDEBUG(
77         Logger::debug(anna::functions::asString("Diameter message length: %d bytes", dl), ANNA_FILE_LOCATION));
78     }
79   }
80
81   void setSourceIP(const std::string &srcIP) throw() {
82     _sourceIP = srcIP;
83   }
84   void setDestinationIP(const std::string &dstIP) throw() {
85     _destinationIP = dstIP;
86   }
87   void setTimestamp(time_t ts) throw() {
88     _timestamp = ts;
89   }
90   void setTimestampU(int tsu) throw() {
91     _timestampU = tsu;
92   }
93   // Returns true if completed:
94   bool appendData(const char *data, size_t size) throw(RuntimeException) {
95     LOGDEBUG(
96       Logger::debug(anna::functions::asString("Appending %d bytes", size), ANNA_FILE_LOCATION));
97     _data.append(data, size);
98
99     if(_data.size() > _diameterLength)
100       throw RuntimeException(
101         "Data overflow (unexpected offset exceed diameter message length)",
102         ANNA_FILE_LOCATION);
103
104     if(_data.size() < _diameterLength)
105       return false;
106
107     LOGDEBUG(anna::Logger::debug("Completed!", ANNA_FILE_LOCATION));
108     return true;
109   }
110
111   void reset() throw() {
112     _sourceIP = "";
113     _destinationIP = "";
114     _timestamp = 0;
115     _timestampU = 0;
116     _data = "";
117     _diameterLength = -1; // not calculated yet
118   }
119
120   const std::string &getSourceIP() const throw() {
121     return _sourceIP;
122   }
123   const std::string &getDestinationIP() const throw() {
124     return _destinationIP;
125   }
126   time_t getTimestamp() const throw() {
127     return _timestamp;
128   }
129   int getTimestampU() const throw() {
130     return _timestampU;
131   }
132   const std::string &getData() const throw() {
133     return _data;
134   }
135   std::string getDataAsHex() const throw() {
136     return anna::functions::asHexString(
137              anna::DataBlock(_data.c_str(), _data.size()));
138   }
139 };
140
141 // Data maps //////////////////////////////////////////////////////////////////////////////////////////////
142 typedef std::map < int /* frame */, Payload > payloads_t;
143 typedef std::map < int /* frame */, Payload >::const_iterator payloads_it;
144 payloads_t G_payloads;
145
146 // Sniffing structures ////////////////////////////////////////////////////////////////////////////////////
147
148 /* ethernet headers are always exactly 14 bytes */
149 #define SIZE_ETHERNET 14
150 /* Ethernet addresses are 6 bytes */
151 #define ETHER_ADDR_LEN  6
152
153 /* Ethernet header */
154 struct sniff_ethernet {
155   u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
156   u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
157   u_short ether_type; /* IP? ARP? RARP? etc */
158 };
159
160 /* IP header */
161 struct sniff_ip {
162   u_char ip_vhl; /* version << 4 | header length >> 2 */
163   u_char ip_tos; /* type of service */
164   u_short ip_len; /* total length */
165   u_short ip_id; /* identification */
166   u_short ip_off; /* fragment offset field */
167 #define IP_RF 0x8000    /* reserved fragment flag */
168 #define IP_DF 0x4000    /* dont fragment flag */
169 #define IP_MF 0x2000    /* more fragments flag */
170 #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
171   u_char ip_ttl; /* time to live */
172   u_char ip_p; /* protocol */
173   u_short ip_sum; /* checksum */
174   struct in_addr ip_src, ip_dst; /* source and dest address */
175 };
176 #define IP_OFF(ip)              (((ip)->ip_off) & IP_OFFMASK)
177 #define IP_DF_VAL(ip)           ((((ip)->ip_off) & IP_DF) >> 14)
178 #define IP_MF_VAL(ip)           ((((ip)->ip_off) & IP_MF) >> 13)
179 #define IP_HL(ip)   (((ip)->ip_vhl) & 0x0f)
180 #define IP_V(ip)    (((ip)->ip_vhl) >> 4)
181
182 /* TCP header */
183 typedef u_int tcp_seq;
184
185 struct sniff_tcp {
186   u_short th_sport; /* source port */
187   u_short th_dport; /* destination port */
188   tcp_seq th_seq; /* sequence number */
189   tcp_seq th_ack; /* acknowledgement number */
190   u_char th_offx2; /* data offset, rsvd */
191 #define TH_OFF(th)  (((th)->th_offx2 & 0xf0) >> 4)
192   u_char th_flags;
193 #define TH_FIN 0x01
194 #define TH_SYN 0x02
195 #define TH_RST 0x04
196 #define TH_PUSH 0x08
197 #define TH_ACK 0x10
198 #define TH_URG 0x20
199 #define TH_ECE 0x40
200 #define TH_CWR 0x80
201 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
202   u_short th_win; /* window */
203   u_short th_sum; /* checksum */
204   u_short th_urp; /* urgent pointer */
205 };
206
207 // Payload extraction /////////////////////////////////////////////////////////////////////////////////////
208 u_char *getPayload(const u_char* packet, int packetSize, int &payloadSize,
209                    std::string &srcIp, std::string &dstIp, int &fragmentId, bool &dfFlag,
210                    bool &mfFlag, int &fragmentOffset) {
211   const struct sniff_ethernet *ethernet; /* The ethernet header */
212   const struct sniff_ip *ip; /* The IP header */
213   const struct sniff_tcp *tcp; /* The TCP header */
214   u_int size_ip;
215   u_int size_tcp;
216   ethernet = (struct sniff_ethernet*)(packet);
217   ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
218   size_ip = IP_HL(ip) * 4; // 4 bytes per 32 bits word
219
220   if(size_ip < 20) {
221     LOGDEBUG(
222       Logger::debug(anna::functions::asString("Invalid IP header length: %d bytes", size_ip), ANNA_FILE_LOCATION));
223     return NULL;
224   }
225
226   static char str[INET_ADDRSTRLEN];
227   inet_ntop(AF_INET, &(ip->ip_src), str, INET_ADDRSTRLEN);
228   srcIp = str;
229   inet_ntop(AF_INET, &(ip->ip_dst), str, INET_ADDRSTRLEN);
230   dstIp = str;
231   LOGDEBUG(
232     Logger::debug(anna::functions::asString("ip_id: %d | ip_off: %d", ip->ip_id, ip->ip_off), ANNA_FILE_LOCATION));
233   fragmentId = ip->ip_id;
234   dfFlag = IP_DF_VAL(ip);
235   mfFlag = IP_MF_VAL(ip);
236   fragmentOffset = IP_OFF(ip);
237   tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
238   size_tcp = TH_OFF(tcp) * 4;
239
240   if(size_tcp < 20) {
241     LOGDEBUG(
242       Logger::debug(anna::functions::asString("Invalid TCP header length: %d bytes", size_tcp), ANNA_FILE_LOCATION));
243     return NULL;
244   }
245
246   int payloadOffset = SIZE_ETHERNET + size_ip + size_tcp;
247   LOGDEBUG(
248     Logger::debug(anna::functions::asString("PayloadOffset=%d", payloadOffset), ANNA_FILE_LOCATION));
249   payloadSize = packetSize - payloadOffset;
250   return ((u_char *)(packet + payloadOffset));
251 }
252
253 // Sniffing callback //////////////////////////////////////////////////////////////////////////////////////
254 void my_callback(u_char *useless, const struct pcap_pkthdr* pkthdr,
255                  const u_char* packet) {
256   static int count = 1;
257   static Payload auxPayload;
258   int packetSize = pkthdr->len;
259   int payloadSize;
260   std::string srcIp, dstIp;
261   int fragmentId, fragmentOffset;
262   bool dfFlag, mfFlag;
263   const u_char* payload = getPayload(packet, packetSize, payloadSize, srcIp,
264                                      dstIp, fragmentId, dfFlag, mfFlag, fragmentOffset);
265
266   if(payload && (payloadSize > 0)) {
267     LOGDEBUG(
268       std::string msg; msg += anna::functions::asString("\nFrame %d:", count); msg += anna::functions::asHexString(anna::DataBlock((const char *)packet, pkthdr->len)); time_t time = pkthdr->ts.tv_sec; msg += "\n"; msg += anna::functions::asString("\ntimestamp %d.%d", pkthdr->ts.tv_sec, pkthdr->ts.tv_usec); msg += anna::functions::asString("\ndate %s", ctime(&time)); msg += anna::functions::asString("\ncaplen %d", pkthdr->caplen); msg += anna::functions::asString("\npacketSize %d", packetSize); msg += anna::functions::asString("\npayloadSize %d", payloadSize); msg += "\nPayload:"; msg += anna::functions::asHexString(anna::DataBlock((const char *)payload, payloadSize)); msg += "\n"; msg += anna::functions::asString("\nsourceIP %s", srcIp.c_str()); msg += anna::functions::asString("\ndestinationIP %s", dstIp.c_str()); msg += "\n"; msg += anna::functions::asString("\nfragmentId %d:", fragmentId); msg += anna::functions::asString("\nDF %s:", (dfFlag ? "1" : "0")); msg += anna::functions::asString("\nMF %s:", (mfFlag ? "1" : "0")); msg += anna::functions::asString("\nfragmentOffset %d:", fragmentOffset);
269       Logger::debug(msg, ANNA_FILE_LOCATION););
270     auxPayload.setDiameterLength(
271       (payload[1] << 16) + (payload[2] << 8) + payload[3]);
272     auxPayload.setSourceIP(srcIp);
273     auxPayload.setDestinationIP(dstIp);
274     auxPayload.setTimestamp(pkthdr->ts.tv_sec);
275     auxPayload.setTimestampU(pkthdr->ts.tv_usec);
276     bool completed = auxPayload.appendData((const char *) payload, payloadSize);
277
278     if(completed) {
279       G_payloads[count] = auxPayload;
280       auxPayload.reset();
281     }
282   }
283
284   count++;
285 }
286
287 bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) throw() {
288   // Get hex string
289   static char buffer[8192];
290   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
291
292   if(infile.is_open()) {
293     infile >> buffer;
294     std::string hexString(buffer, strlen(buffer));
295     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
296     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
297     LOGDEBUG(
298       std::string msg = "Hex string (remove colons if exists): ";
299       msg += hexString;
300       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
301     );
302     anna::functions::fromHexString(hexString, db);
303     // Close file
304     infile.close();
305     return true;
306   }
307
308   return false;
309 }
310
311
312 void _exit(const std::string &message, int resultCode = 1) {
313   if(resultCode)
314     std::cerr << message << std::endl << std::endl;
315   else
316     std::cout << message << std::endl << std::endl;
317
318   exit(resultCode);
319 }
320
321
322 //-------------------------------------------------------------------
323 int main(int argc, char **argv) {
324   std::string exec = argv[0];
325   std::string execBN = exec.substr(exec.find_last_of("/") + 1);
326   std::string filetrace = execBN + ".trace";
327   std::cout << std::endl;
328
329   //check command line arguments
330   if(argc < 2) {
331     std::string msg = "Usage: "; msg += exec;
332     msg += " <pcap file> [--write-hex: to write hex files] [--debug: activates debug level traces (warning by default)]\n\n";
333     _exit(msg);
334   }
335
336   // Command-line parameters:
337   std::string inputFile = argv[1];
338   bool isHex = (inputFile.substr(inputFile.find_last_of(".") + 1) == "hex");
339   std::string outputFile = inputFile; // extension will be added later
340   std::string optionals;
341   int indx = 2;
342   while(indx < argc) { optionals += " "; optionals += argv[indx]; indx++; }
343
344   bool debug = (optionals.find("--debug") != std::string::npos);
345   bool writeHex = (optionals.find("--write-hex") != std::string::npos);
346   Logger::setLevel(debug ? Logger::Debug:Logger::Warning);
347   Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
348
349   anna::DataBlock db_aux(true);
350
351   // SNIFFING //////////////////////////////////////////////////////////////////////////////////////////////7
352   //temporary packet buffers
353   struct pcap_pkthdr header; // The header that pcap gives us
354   const u_char *packet;      // The actual packet
355   //------------------
356   //open the pcap file
357   pcap_t *handle;
358   char errbuf[PCAP_ERRBUF_SIZE];        //not sure what to do with this, oh well
359   handle = pcap_open_offline(inputFile.c_str(), errbuf); //call pcap library function
360
361   if(handle == NULL) _exit(errbuf, 2);
362
363   // TODO: add filtering. At the moment, pcap must be previously filtered for diameter protocol ('tcp port 3868' or any other filter allowed)
364   /*
365   // Filtering:
366   std::string filter = ?????;
367   struct bpf_program _fp;
368   struct bpf_program *fp = &_fp;
369   bpf_u_int32 netmask = 4294967295; // FFFFFFFF
370
371   if (pcap_compile(handle, fp, (char*)(filter.c_str()), 1, netmask) == -1) {
372         std::cerr << "Couldn't compile the filter " << filter << std::endl;
373     return(2); 
374   }
375
376   if (pcap_setfilter(handle, fp) == -1) {
377     std::cerr << "Couldn't set the filter " << filter << std::endl; 
378     return(2); 
379   }
380   */
381
382   //begin processing the packets in this particular file
383   int packets = -1;
384
385   try {
386     while(packets != 0)
387       packets = pcap_dispatch(handle, -1, (pcap_handler) my_callback, NULL);
388   } catch(RuntimeException &ex) {
389     _exit(ex.asString());
390   }
391
392   pcap_close(handle);  //close the pcap file
393
394   // Print payloads //////////////////////////////////////////////////////////////////////////////////////////////
395   // Open output file:
396   outputFile += ".report";
397   std::ofstream out(outputFile.c_str(), std::ifstream::out);
398
399   for(payloads_it it = G_payloads.begin(); it != G_payloads.end(); it++) {
400     LOGDEBUG(
401       Logger::debug(anna::functions::asString("Dumping frame %d", it->first), ANNA_FILE_LOCATION));
402     time_t ts = (it->second).getTimestamp();
403     int tsu = (it->second).getTimestampU();
404     std::string ts_str = ctime(&ts);
405     ts_str.erase(ts_str.find("\n"));
406     out << "Frame:           " << anna::functions::asString(it->first) << std::endl;
407     out << "Date:            " << ts_str << std::endl;
408     out << "Timestamp:       " << anna::functions::asString((int)ts) << "."
409         << anna::functions::asString((int)tsu) << std::endl;
410     out << "Origin IP:       " << (it->second).getSourceIP() << std::endl;
411     out << "Destination IP:  " << (it->second).getDestinationIP() << std::endl;
412     out << "Destination IP:  " << (it->second).getDestinationIP() << std::endl;
413     out << "Hex String:      " << (it->second).getDataAsHex() << std::endl;
414
415     // Create hex file:
416     if (writeHex) {
417       std::string hexFile =  anna::functions::asString(it->first) + ".hex";
418       std::ofstream hex(hexFile.c_str(), std::ifstream::out);
419       hex << (it->second).getDataAsHex();
420       hex.close();
421     }
422
423     out << std::endl;
424   }
425
426   // Close output file:
427   out.close();
428   std::string msg = "Open '"; msg += filetrace; msg += "' in order to see process traces.\n";
429   msg += "Open '"; msg += outputFile; msg += "' to see decoding results.\n";
430   if (writeHex) msg += "Open '<frame number>.hex' to see specific frame data.";
431   _exit(msg, 0);
432 }
433