All in one dump
[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
37 // Standard
38 #include <pcap.h>
39 #include <stdlib.h>
40 #include <netinet/ip.h>
41 #include <arpa/inet.h>
42 #include <iostream>
43 #include <fstream>
44
45 // STL
46 #include <string>
47 #include <map>
48
49 #include <anna/core/DataBlock.hpp>
50 #include <anna/core/util/Tokenizer.hpp>
51 #include <anna/core/functions.hpp>
52 #include <anna/core/tracing/Logger.hpp>
53 #include <anna/core/tracing/TraceWriter.hpp>
54 #include <anna/core/RuntimeException.hpp>
55 #include <anna/xml/xml.hpp>
56 #include <anna/diameter/stack/Engine.hpp>
57 #include <anna/diameter/codec/Engine.hpp>
58 #include <anna/diameter/codec/Message.hpp>
59
60
61
62
63 using namespace anna;
64 using namespace anna::diameter;
65
66
67 // Payload and frame metadata /////////////////////////////////////////////////////////////////////////////
68 class Payload {
69
70   std::string _sourceIP;
71   std::string _destinationIP;
72   time_t _timestamp;
73   int _timestampU; // usecs
74   std::string _data;
75   size_t _diameterLength;
76
77 public:
78   Payload() { reset(); }
79
80   void setDiameterLength(size_t dl) {
81         if (_diameterLength == -1) {
82           _diameterLength=dl;
83           LOGDEBUG (Logger::debug(anna::functions::asString("Diameter message length: %d bytes", dl), ANNA_FILE_LOCATION));
84         }
85   }
86
87   void setSourceIP(const std::string &srcIP) throw() { _sourceIP = srcIP; }
88   void setDestinationIP(const std::string &dstIP) throw() { _destinationIP = dstIP; }
89   void setTimestamp(time_t ts) throw() { _timestamp = ts; }
90   void setTimestampU(int tsu) throw() { _timestampU = tsu; }
91   // Returns true if completed:
92   bool appendData(const char *data, size_t size) throw(RuntimeException) {
93     LOGDEBUG (Logger::debug(anna::functions::asString("Appending %d bytes", size), ANNA_FILE_LOCATION));
94     _data.append(data, size);
95     if (_data.size() > _diameterLength) throw RuntimeException("Data overflow (unexpected offset exceed diameter message length)", ANNA_FILE_LOCATION);
96     if (_data.size() < _diameterLength) return false;
97     LOGDEBUG (anna::Logger::debug("Completed!", ANNA_FILE_LOCATION));
98     return true;
99   }
100
101   void reset() throw() {
102           _sourceIP = "";
103           _destinationIP = "";
104           _timestamp = 0;
105           _timestampU = 0;
106           _data = "";
107           _diameterLength = -1; // not calculated yet
108   }
109
110   const std::string &getSourceIP() const throw() { return _sourceIP; }
111   const std::string &getDestinationIP() const throw() { return _destinationIP; }
112   time_t getTimestamp() const throw() { return _timestamp; }
113   int getTimestampU() const throw() { return _timestampU; }
114   const std::string &getData() const throw() { return _data; }
115   std::string getDataAsHex() const throw() { return anna::functions::asHexString(anna::DataBlock(_data.c_str(), _data.size())); }
116 };
117
118
119
120 // Data maps //////////////////////////////////////////////////////////////////////////////////////////////
121 typedef std::map <int /* frame */, Payload> payloads_t;
122 typedef std::map <int /* frame */, Payload>::const_iterator payloads_it;
123 payloads_t G_payloads;
124 anna::diameter::codec::Message G_codecMsg;
125
126
127
128
129
130 // Sniffing structures ////////////////////////////////////////////////////////////////////////////////////
131
132 /* ethernet headers are always exactly 14 bytes */
133 #define SIZE_ETHERNET 14
134 /* Ethernet addresses are 6 bytes */
135 #define ETHER_ADDR_LEN  6
136
137 /* Ethernet header */
138 struct sniff_ethernet {
139   u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
140   u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
141   u_short ether_type; /* IP? ARP? RARP? etc */
142 };
143
144 /* IP header */
145 struct sniff_ip {
146   u_char ip_vhl;                /* version << 4 | header length >> 2 */
147   u_char ip_tos;                /* type of service */
148   u_short ip_len;               /* total length */
149   u_short ip_id;                /* identification */
150   u_short ip_off;               /* fragment offset field */
151 #define IP_RF 0x8000            /* reserved fragment flag */
152 #define IP_DF 0x4000            /* dont fragment flag */
153 #define IP_MF 0x2000            /* more fragments flag */
154 #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
155   u_char ip_ttl;                /* time to live */
156   u_char ip_p;          /* protocol */
157   u_short ip_sum;               /* checksum */
158   struct in_addr ip_src,ip_dst; /* source and dest address */
159 };
160 #define IP_OFF(ip)              (((ip)->ip_off) & IP_OFFMASK)
161 #define IP_DF_VAL(ip)           ((((ip)->ip_off) & IP_DF) >> 14)
162 #define IP_MF_VAL(ip)           ((((ip)->ip_off) & IP_MF) >> 13)
163 #define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
164 #define IP_V(ip)                (((ip)->ip_vhl) >> 4)
165
166 /* TCP header */
167 typedef u_int tcp_seq;
168
169 struct sniff_tcp {
170   u_short th_sport;     /* source port */
171   u_short th_dport;     /* destination port */
172   tcp_seq th_seq;               /* sequence number */
173   tcp_seq th_ack;               /* acknowledgement number */
174   u_char th_offx2;      /* data offset, rsvd */
175 #define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
176   u_char th_flags;
177 #define TH_FIN 0x01
178 #define TH_SYN 0x02
179 #define TH_RST 0x04
180 #define TH_PUSH 0x08
181 #define TH_ACK 0x10
182 #define TH_URG 0x20
183 #define TH_ECE 0x40
184 #define TH_CWR 0x80
185 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
186   u_short th_win;               /* window */
187   u_short th_sum;               /* checksum */
188   u_short th_urp;               /* urgent pointer */
189 };
190
191 // Payload extraction /////////////////////////////////////////////////////////////////////////////////////
192 u_char *getPayload(const u_char* packet, int packetSize, int &payloadSize, std::string &srcIp, std::string &dstIp, int &fragmentId, bool &dfFlag, bool &mfFlag, int &fragmentOffset) {
193
194   const struct sniff_ethernet *ethernet; /* The ethernet header */
195   const struct sniff_ip *ip; /* The IP header */
196   const struct sniff_tcp *tcp; /* The TCP header */
197
198   u_int size_ip;
199   u_int size_tcp;
200
201   ethernet = (struct sniff_ethernet*)(packet);
202   ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
203   size_ip = IP_HL(ip)*4; // 4 bytes per 32 bits word
204   if (size_ip < 20) {
205     LOGDEBUG (Logger::debug(anna::functions::asString("Invalid IP header length: %d bytes", size_ip), ANNA_FILE_LOCATION));
206     return NULL;
207   }
208
209   static char str[INET_ADDRSTRLEN];
210   inet_ntop(AF_INET, &(ip->ip_src), str, INET_ADDRSTRLEN);
211   srcIp = str;
212   inet_ntop(AF_INET, &(ip->ip_dst), str, INET_ADDRSTRLEN);
213   dstIp = str;
214
215   LOGDEBUG (Logger::debug(anna::functions::asString("ip_id: %d | ip_off: %d", ip->ip_id, ip->ip_off), ANNA_FILE_LOCATION));
216   fragmentId = ip->ip_id;
217   dfFlag = IP_DF_VAL(ip);
218   mfFlag = IP_MF_VAL(ip);
219   fragmentOffset = IP_OFF(ip);
220
221
222   tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
223   size_tcp = TH_OFF(tcp)*4;
224   if (size_tcp < 20) {
225     LOGDEBUG (Logger::debug(anna::functions::asString("Invalid TCP header length: %d bytes", size_tcp), ANNA_FILE_LOCATION));
226     return NULL;
227   }
228
229   int payloadOffset = SIZE_ETHERNET + size_ip + size_tcp;
230   LOGDEBUG (Logger::debug(anna::functions::asString("PayloadOffset=%d", payloadOffset), ANNA_FILE_LOCATION));
231   payloadSize = packetSize - payloadOffset;
232   return ((u_char *)(packet + payloadOffset));
233 }
234
235 // Sniffing callback //////////////////////////////////////////////////////////////////////////////////////
236 void my_callback(u_char *useless,const struct pcap_pkthdr* pkthdr,const u_char* packet)
237 {
238    static int count = 1;
239
240    static Payload auxPayload;
241
242    int packetSize = pkthdr->len;
243    int payloadSize;
244    std::string srcIp, dstIp;
245    int fragmentId, fragmentOffset;
246    bool dfFlag, mfFlag;
247    const u_char* payload = getPayload(packet, packetSize, payloadSize, srcIp, dstIp, fragmentId, dfFlag, mfFlag, fragmentOffset);
248    if (payload && (payloadSize > 0)) {
249      LOGDEBUG (
250        std::string msg;
251        msg += anna::functions::asString("\nFrame %d:", count);
252        msg += anna::functions::asHexString(anna::DataBlock((const char *)packet, pkthdr->len));
253        time_t time = pkthdr->ts.tv_sec;
254        msg += "\n";
255        msg += anna::functions::asString("\ntimestamp %d.%d", pkthdr->ts.tv_sec, pkthdr->ts.tv_usec);
256        msg += anna::functions::asString("\ndate %s", ctime(&time));
257        msg += anna::functions::asString("\ncaplen %d", pkthdr->caplen);
258        msg += anna::functions::asString("\npacketSize %d", packetSize);
259        msg += anna::functions::asString("\npayloadSize %d", payloadSize);
260        msg += "\nPayload:";
261        msg += anna::functions::asHexString(anna::DataBlock((const char *)payload, payloadSize));
262        msg += "\n";
263        msg += anna::functions::asString("\nsourceIP %s", srcIp.c_str());
264        msg += anna::functions::asString("\ndestinationIP %s", dstIp.c_str());
265        msg += "\n";
266        msg += anna::functions::asString("\nfragmentId %d:", fragmentId);
267        msg += anna::functions::asString("\nDF %s:", (dfFlag ? "1":"0"));
268        msg += anna::functions::asString("\nMF %s:", (mfFlag ? "1":"0"));
269        msg += anna::functions::asString("\nfragmentOffset %d:", fragmentOffset);
270
271        Logger::debug(msg, ANNA_FILE_LOCATION);
272      );
273
274      auxPayload.setDiameterLength((payload[1] << 16) + (payload[2] << 8) + payload[3]);
275      auxPayload.setSourceIP(srcIp);
276      auxPayload.setDestinationIP(dstIp);
277      auxPayload.setTimestamp(pkthdr->ts.tv_sec);
278      auxPayload.setTimestampU(pkthdr->ts.tv_usec);
279      bool completed = auxPayload.appendData((const char *)payload, payloadSize);
280      if (completed) {
281        G_payloads[count] = auxPayload;
282        auxPayload.reset();
283      }
284   }
285
286   count++;
287 }
288
289
290
291 //------------------------------------------------------------------- 
292 int main(int argc, char **argv) 
293
294    std::string exec = argv[0];
295
296    std::cout << std::endl;
297
298    //check command line arguments 
299    if (argc < 3) {
300      std::cout << "Usage: " << exec << " <list of comma-separated xml dictionaries> <pcap file> [--ignore-flags: non-strict validation]" << std::endl << std::endl;
301      return 1;
302    } 
303
304    // Command-line parameters:
305    std::string dictionaries = argv[1];
306    std::string pcapFile = argv[2];
307    std::string optional = argv[3];
308    bool ignoreFlags = ((argc == 4) && (optional == "--ignore-flags"));
309    std::cout << "Dictionary(ies) provided: " << dictionaries << std::endl;
310    std::cout << "Pcap file provided:       " << pcapFile << std::endl;
311    std::cout << "Validation kindness:      " << (ignoreFlags ? "non strict":"strict") << std::endl;
312
313    // Logger and engines:
314    Logger::setLevel(Logger::Debug);
315    Logger::initialize("pcapDecoder", new TraceWriter("file.trace", 2048000));
316    anna::diameter::codec::Engine *codecEngine = new anna::diameter::codec::Engine();
317    anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
318
319    try {
320       anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* stack id */);
321       // Analyze comma-separated list:
322       anna::Tokenizer lst;
323       lst.apply(dictionaries, ",");
324
325       if (lst.size() >= 1) { // always true (at least one, because -dictionary is mandatory)
326          anna::Tokenizer::const_iterator tok_min(lst.begin());
327          anna::Tokenizer::const_iterator tok_max(lst.end());
328          anna::Tokenizer::const_iterator tok_iter;
329          std::string pathFile;
330          d->allowUpdates();
331
332          for (tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
333             pathFile = anna::Tokenizer::data(tok_iter);
334             d->load(pathFile);
335          }
336       }
337
338       codecEngine->setDictionary(d);
339       //LOGDEBUG(anna::Logger::debug(codecEngine->asString(), ANNA_FILE_LOCATION));
340
341       if (lst.size() > 1) {
342         std::string all_in_one = "./dictionary-all-in-one.xml";
343         std::ofstream out(all_in_one, std::ifstream::out);
344         std::string buffer = d->asXMLString();
345         out.write(buffer.c_str(), buffer.size());
346         out.close();
347         std::cout << "Written '" << all_in_one << "' (provide it next time to be more comfortable)." << std::endl;
348       }
349
350    } catch (anna::RuntimeException &ex) {
351      std::cerr << ex.asString() << std::endl << std::endl;
352      return 1;
353    }
354
355    codecEngine->ignoreFlagsOnValidation(ignoreFlags);
356    // Tracing:
357    //if (cl.exists("trace"))
358    //   anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
359
360
361
362    // SNIFFING //////////////////////////////////////////////////////////////////////////////////////////////7
363
364    //temporary packet buffers 
365    struct pcap_pkthdr header; // The header that pcap gives us 
366    const u_char *packet;      // The actual packet 
367
368    //------------------ 
369    //open the pcap file 
370    pcap_t *handle; 
371    char errbuf[PCAP_ERRBUF_SIZE];                    //not sure what to do with this, oh well
372    handle = pcap_open_offline(pcapFile.c_str(), errbuf);   //call pcap library function
373
374    if (handle == NULL) { 
375      std::cerr << errbuf << std::endl << std::endl;
376      return 2;
377    } 
378
379
380    //begin processing the packets in this particular file
381    int packets = -1;
382    try {
383      while (packets != 0)
384           packets = pcap_dispatch(handle, -1, (pcap_handler)my_callback, NULL);
385    }
386    catch (RuntimeException &ex) {
387      std::cerr << ex.asString() << std::endl << std::endl;
388      return 1;
389    }
390    pcap_close(handle);  //close the pcap file 
391
392    // Print payloads //////////////////////////////////////////////////////////////////////////////////////////////
393
394    // Open output file:
395    std::string output = pcapFile; output += ".report";
396    std::ofstream out(output, std::ifstream::out);
397    std::string xmlStr;
398    anna::DataBlock db_aux(true);
399
400    //out.write(str.c_str(), str.size());
401
402
403    for (payloads_it it = G_payloads.begin(); it != G_payloads.end(); it++) {
404         LOGDEBUG (Logger::debug(anna::functions::asString("Dumping frame %d", it->first), ANNA_FILE_LOCATION));
405         time_t ts = (it->second).getTimestamp();
406         int tsu = (it->second).getTimestampU();
407         std::string ts_str = ctime(&ts);
408         ts_str.erase (ts_str.find ("\n"));
409
410         out << std::endl;
411         out << "===================================================================================================" << std::endl;
412         out << "Date:           " << ts_str << std::endl;
413         out << "Timestamp:      " << std::to_string(ts) << "." << std::to_string(tsu) << std::endl;
414         out << "Origin IP:      " << (it->second).getSourceIP() << std::endl;
415         out << "Destination IP: " << (it->second).getDestinationIP() << std::endl;
416         out << std::endl;
417
418         // decode hex string:
419         anna::functions::fromHexString((it->second).getDataAsHex(), db_aux);
420         try {
421           G_codecMsg.decode(db_aux);
422         }
423         catch (RuntimeException &ex) {
424           std::cerr << ex.asString() << std::endl << std::endl;
425           return 1;
426         }
427         out << G_codecMsg.asXMLString();
428    }
429
430
431    // Close output file:
432    out.close();
433
434    std::cout << "Open 'file.trace' in order to see process traces." << std::endl;
435    std::cout << "Open '" << output << "' to see conversion results." << std::endl;
436    std::cout << std::endl;
437    return 0;
438 }
439