1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
40 #include <netinet/ip.h>
41 #include <arpa/inet.h>
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>
64 using namespace anna::diameter;
67 // Payload and frame metadata /////////////////////////////////////////////////////////////////////////////
70 std::string _sourceIP;
71 std::string _destinationIP;
73 int _timestampU; // usecs
75 size_t _diameterLength;
78 Payload() { reset(); }
80 void setDiameterLength(size_t dl) {
81 if (_diameterLength == -1) {
83 LOGDEBUG (Logger::debug(anna::functions::asString("Diameter message length: %d bytes", dl), ANNA_FILE_LOCATION));
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));
101 void reset() throw() {
107 _diameterLength = -1; // not calculated yet
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())); }
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;
130 // Sniffing structures ////////////////////////////////////////////////////////////////////////////////////
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
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 */
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 */
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)
167 typedef u_int tcp_seq;
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)
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 */
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) {
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 */
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
205 LOGDEBUG (Logger::debug(anna::functions::asString("Invalid IP header length: %d bytes", size_ip), ANNA_FILE_LOCATION));
209 static char str[INET_ADDRSTRLEN];
210 inet_ntop(AF_INET, &(ip->ip_src), str, INET_ADDRSTRLEN);
212 inet_ntop(AF_INET, &(ip->ip_dst), str, INET_ADDRSTRLEN);
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);
222 tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
223 size_tcp = TH_OFF(tcp)*4;
225 LOGDEBUG (Logger::debug(anna::functions::asString("Invalid TCP header length: %d bytes", size_tcp), ANNA_FILE_LOCATION));
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));
235 // Sniffing callback //////////////////////////////////////////////////////////////////////////////////////
236 void my_callback(u_char *useless,const struct pcap_pkthdr* pkthdr,const u_char* packet)
238 static int count = 1;
240 static Payload auxPayload;
242 int packetSize = pkthdr->len;
244 std::string srcIp, dstIp;
245 int fragmentId, fragmentOffset;
247 const u_char* payload = getPayload(packet, packetSize, payloadSize, srcIp, dstIp, fragmentId, dfFlag, mfFlag, fragmentOffset);
248 if (payload && (payloadSize > 0)) {
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;
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);
261 msg += anna::functions::asHexString(anna::DataBlock((const char *)payload, payloadSize));
263 msg += anna::functions::asString("\nsourceIP %s", srcIp.c_str());
264 msg += anna::functions::asString("\ndestinationIP %s", dstIp.c_str());
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);
271 Logger::debug(msg, ANNA_FILE_LOCATION);
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);
281 G_payloads[count] = auxPayload;
291 //-------------------------------------------------------------------
292 int main(int argc, char **argv)
294 std::string exec = argv[0];
296 std::cout << std::endl;
298 //check command line arguments
300 std::cout << "Usage: " << exec << " <list of comma-separated xml dictionaries> <pcap file> [--ignore-flags: non-strict validation]" << std::endl << std::endl;
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;
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();
320 anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* stack id */);
321 // Analyze comma-separated list:
323 lst.apply(dictionaries, ",");
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;
332 for (tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
333 pathFile = anna::Tokenizer::data(tok_iter);
338 codecEngine->setDictionary(d);
339 //LOGDEBUG(anna::Logger::debug(codecEngine->asString(), ANNA_FILE_LOCATION));
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());
347 std::cout << "Written '" << all_in_one << "' (provide it next time to be more comfortable)." << std::endl;
350 } catch (anna::RuntimeException &ex) {
351 std::cerr << ex.asString() << std::endl << std::endl;
355 codecEngine->ignoreFlagsOnValidation(ignoreFlags);
357 //if (cl.exists("trace"))
358 // anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
362 // SNIFFING //////////////////////////////////////////////////////////////////////////////////////////////7
364 //temporary packet buffers
365 struct pcap_pkthdr header; // The header that pcap gives us
366 const u_char *packet; // The actual packet
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
374 if (handle == NULL) {
375 std::cerr << errbuf << std::endl << std::endl;
380 //begin processing the packets in this particular file
384 packets = pcap_dispatch(handle, -1, (pcap_handler)my_callback, NULL);
386 catch (RuntimeException &ex) {
387 std::cerr << ex.asString() << std::endl << std::endl;
390 pcap_close(handle); //close the pcap file
392 // Print payloads //////////////////////////////////////////////////////////////////////////////////////////////
395 std::string output = pcapFile; output += ".report";
396 std::ofstream out(output, std::ifstream::out);
398 anna::DataBlock db_aux(true);
400 //out.write(str.c_str(), str.size());
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"));
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;
418 // decode hex string:
419 anna::functions::fromHexString((it->second).getDataAsHex(), db_aux);
421 G_codecMsg.decode(db_aux);
423 catch (RuntimeException &ex) {
424 std::cerr << ex.asString() << std::endl << std::endl;
427 out << G_codecMsg.asXMLString();
431 // Close output file:
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;