1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
11 #include <netinet/ip.h>
12 #include <arpa/inet.h>
20 #include <anna/core/DataBlock.hpp>
21 #include <anna/core/functions.hpp>
22 #include <anna/core/tracing/Logger.hpp>
23 #include <anna/core/tracing/TraceWriter.hpp>
24 #include <anna/core/RuntimeException.hpp>
29 // Payload and frame metadata /////////////////////////////////////////////////////////////////////////////
32 std::string _sourceIP;
33 std::string _destinationIP;
35 int _timestampU; // usecs
37 size_t _diameterLength;
44 void setDiameterLength(size_t dl) {
45 if(_diameterLength == -1) {
48 Logger::debug(anna::functions::asString("Diameter message length: %d bytes", dl), ANNA_FILE_LOCATION));
52 void setSourceIP(const std::string &srcIP) throw() {
55 void setDestinationIP(const std::string &dstIP) throw() {
56 _destinationIP = dstIP;
58 void setTimestamp(time_t ts) throw() {
61 void setTimestampU(int tsu) throw() {
64 // Returns true if completed:
65 bool appendData(const char *data, size_t size) throw(RuntimeException) {
67 Logger::debug(anna::functions::asString("Appending %d bytes", size), ANNA_FILE_LOCATION));
68 _data.append(data, size);
70 if(_data.size() > _diameterLength)
71 throw RuntimeException(
72 "Data overflow (unexpected offset exceed diameter message length)",
75 if(_data.size() < _diameterLength)
78 LOGDEBUG(anna::Logger::debug("Completed!", ANNA_FILE_LOCATION));
82 void reset() throw() {
88 _diameterLength = -1; // not calculated yet
91 const std::string &getSourceIP() const throw() {
94 const std::string &getDestinationIP() const throw() {
95 return _destinationIP;
97 time_t getTimestamp() const throw() {
100 int getTimestampU() const throw() {
103 const std::string &getData() const throw() {
106 std::string getDataAsHex() const throw() {
107 return anna::functions::asHexString(
108 anna::DataBlock(_data.c_str(), _data.size()));
112 // Data maps //////////////////////////////////////////////////////////////////////////////////////////////
113 typedef std::map < int /* frame */, Payload > payloads_t;
114 typedef std::map < int /* frame */, Payload >::const_iterator payloads_it;
115 payloads_t G_payloads;
117 // Sniffing structures ////////////////////////////////////////////////////////////////////////////////////
119 /* ethernet headers are always exactly 14 bytes */
120 #define SIZE_ETHERNET 14
121 /* Ethernet addresses are 6 bytes */
122 #define ETHER_ADDR_LEN 6
124 /* Ethernet header */
125 struct sniff_ethernet {
126 u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
127 u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
128 u_short ether_type; /* IP? ARP? RARP? etc */
133 u_char ip_vhl; /* version << 4 | header length >> 2 */
134 u_char ip_tos; /* type of service */
135 u_short ip_len; /* total length */
136 u_short ip_id; /* identification */
137 u_short ip_off; /* fragment offset field */
138 #define IP_RF 0x8000 /* reserved fragment flag */
139 #define IP_DF 0x4000 /* dont fragment flag */
140 #define IP_MF 0x2000 /* more fragments flag */
141 #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
142 u_char ip_ttl; /* time to live */
143 u_char ip_p; /* protocol */
144 u_short ip_sum; /* checksum */
145 struct in_addr ip_src, ip_dst; /* source and dest address */
147 #define IP_OFF(ip) (((ip)->ip_off) & IP_OFFMASK)
148 #define IP_DF_VAL(ip) ((((ip)->ip_off) & IP_DF) >> 14)
149 #define IP_MF_VAL(ip) ((((ip)->ip_off) & IP_MF) >> 13)
150 #define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
151 #define IP_V(ip) (((ip)->ip_vhl) >> 4)
154 typedef u_int tcp_seq;
157 u_short th_sport; /* source port */
158 u_short th_dport; /* destination port */
159 tcp_seq th_seq; /* sequence number */
160 tcp_seq th_ack; /* acknowledgement number */
161 u_char th_offx2; /* data offset, rsvd */
162 #define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
172 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
173 u_short th_win; /* window */
174 u_short th_sum; /* checksum */
175 u_short th_urp; /* urgent pointer */
178 // Payload extraction /////////////////////////////////////////////////////////////////////////////////////
179 u_char *getPayload(const u_char* packet, int packetSize, int &payloadSize,
180 std::string &srcIp, std::string &dstIp, int &fragmentId, bool &dfFlag,
181 bool &mfFlag, int &fragmentOffset) {
182 const struct sniff_ethernet *ethernet; /* The ethernet header */
183 const struct sniff_ip *ip; /* The IP header */
184 const struct sniff_tcp *tcp; /* The TCP header */
187 ethernet = (struct sniff_ethernet*)(packet);
188 ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
189 size_ip = IP_HL(ip) * 4; // 4 bytes per 32 bits word
193 Logger::debug(anna::functions::asString("Invalid IP header length: %d bytes", size_ip), ANNA_FILE_LOCATION));
197 static char str[INET_ADDRSTRLEN];
198 inet_ntop(AF_INET, &(ip->ip_src), str, INET_ADDRSTRLEN);
200 inet_ntop(AF_INET, &(ip->ip_dst), str, INET_ADDRSTRLEN);
203 Logger::debug(anna::functions::asString("ip_id: %d | ip_off: %d", ip->ip_id, ip->ip_off), ANNA_FILE_LOCATION));
204 fragmentId = ip->ip_id;
205 dfFlag = IP_DF_VAL(ip);
206 mfFlag = IP_MF_VAL(ip);
207 fragmentOffset = IP_OFF(ip);
208 tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
209 size_tcp = TH_OFF(tcp) * 4;
213 Logger::debug(anna::functions::asString("Invalid TCP header length: %d bytes", size_tcp), ANNA_FILE_LOCATION));
217 int payloadOffset = SIZE_ETHERNET + size_ip + size_tcp;
219 Logger::debug(anna::functions::asString("PayloadOffset=%d", payloadOffset), ANNA_FILE_LOCATION));
220 payloadSize = packetSize - payloadOffset;
221 return ((u_char *)(packet + payloadOffset));
224 // Sniffing callback //////////////////////////////////////////////////////////////////////////////////////
225 void my_callback(u_char *useless, const struct pcap_pkthdr* pkthdr,
226 const u_char* packet) {
227 static int count = 1;
228 static Payload auxPayload;
229 int packetSize = pkthdr->len;
231 std::string srcIp, dstIp;
232 int fragmentId, fragmentOffset;
234 const u_char* payload = getPayload(packet, packetSize, payloadSize, srcIp,
235 dstIp, fragmentId, dfFlag, mfFlag, fragmentOffset);
237 if(payload && (payloadSize > 0)) {
239 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);
240 Logger::debug(msg, ANNA_FILE_LOCATION););
241 auxPayload.setDiameterLength(
242 (payload[1] << 16) + (payload[2] << 8) + payload[3]);
243 auxPayload.setSourceIP(srcIp);
244 auxPayload.setDestinationIP(dstIp);
245 auxPayload.setTimestamp(pkthdr->ts.tv_sec);
246 auxPayload.setTimestampU(pkthdr->ts.tv_usec);
247 bool completed = auxPayload.appendData((const char *) payload, payloadSize);
250 G_payloads[count] = auxPayload;
258 bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) throw() {
260 static char buffer[8192];
261 std::ifstream infile(pathfile.c_str(), std::ifstream::in);
263 if(infile.is_open()) {
265 std::string hexString(buffer, strlen(buffer));
266 // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
267 hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
269 std::string msg = "Hex string (remove colons if exists): ";
271 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
273 anna::functions::fromHexString(hexString, db);
283 void _exit(const std::string &message, int resultCode = 1) {
285 std::cerr << message << std::endl << std::endl;
287 std::cout << message << std::endl << std::endl;
293 //-------------------------------------------------------------------
294 int main(int argc, char **argv) {
295 std::string exec = argv[0];
296 std::string execBN = exec.substr(exec.find_last_of("/") + 1);
297 std::string filetrace = execBN + ".trace";
298 std::cout << std::endl;
300 //check command line arguments
302 std::string msg = "Usage: "; msg += exec;
303 msg += " <pcap file> [--write-hex: to write hex files] [--debug: activates debug level traces (warning by default)]\n\n";
307 // Command-line parameters:
308 std::string inputFile = argv[1];
309 bool isHex = (inputFile.substr(inputFile.find_last_of(".") + 1) == "hex");
310 std::string outputFile = inputFile; // extension will be added later
311 std::string optionals;
313 while(indx < argc) { optionals += " "; optionals += argv[indx]; indx++; }
315 bool debug = (optionals.find("--debug") != std::string::npos);
316 bool writeHex = (optionals.find("--write-hex") != std::string::npos);
317 Logger::setLevel(debug ? Logger::Debug:Logger::Warning);
318 Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
320 anna::DataBlock db_aux(true);
322 // SNIFFING //////////////////////////////////////////////////////////////////////////////////////////////7
323 //temporary packet buffers
324 struct pcap_pkthdr header; // The header that pcap gives us
325 const u_char *packet; // The actual packet
329 char errbuf[PCAP_ERRBUF_SIZE]; //not sure what to do with this, oh well
330 handle = pcap_open_offline(inputFile.c_str(), errbuf); //call pcap library function
332 if(handle == NULL) _exit(errbuf, 2);
334 // TODO: add filtering. At the moment, pcap must be previously filtered for diameter protocol ('tcp port 3868' or any other filter allowed)
337 std::string filter = ?????;
338 struct bpf_program _fp;
339 struct bpf_program *fp = &_fp;
340 bpf_u_int32 netmask = 4294967295; // FFFFFFFF
342 if (pcap_compile(handle, fp, (char*)(filter.c_str()), 1, netmask) == -1) {
343 std::cerr << "Couldn't compile the filter " << filter << std::endl;
347 if (pcap_setfilter(handle, fp) == -1) {
348 std::cerr << "Couldn't set the filter " << filter << std::endl;
353 //begin processing the packets in this particular file
358 packets = pcap_dispatch(handle, -1, (pcap_handler) my_callback, NULL);
359 } catch(RuntimeException &ex) {
360 _exit(ex.asString());
363 pcap_close(handle); //close the pcap file
365 // Print payloads //////////////////////////////////////////////////////////////////////////////////////////////
367 outputFile += ".report";
368 std::ofstream out(outputFile.c_str(), std::ifstream::out);
370 for(payloads_it it = G_payloads.begin(); it != G_payloads.end(); it++) {
372 Logger::debug(anna::functions::asString("Dumping frame %d", it->first), ANNA_FILE_LOCATION));
373 time_t ts = (it->second).getTimestamp();
374 int tsu = (it->second).getTimestampU();
375 std::string ts_str = ctime(&ts);
376 ts_str.erase(ts_str.find("\n"));
377 out << "Frame: " << anna::functions::asString(it->first) << std::endl;
378 out << "Date: " << ts_str << std::endl;
379 out << "Timestamp: " << anna::functions::asString((int)ts) << "."
380 << anna::functions::asString((int)tsu) << std::endl;
381 out << "Origin IP: " << (it->second).getSourceIP() << std::endl;
382 out << "Destination IP: " << (it->second).getDestinationIP() << std::endl;
383 out << "Destination IP: " << (it->second).getDestinationIP() << std::endl;
384 out << "Hex String: " << (it->second).getDataAsHex() << std::endl;
388 std::string hexFile = anna::functions::asString(it->first) + ".hex";
389 std::ofstream hex(hexFile.c_str(), std::ifstream::out);
390 hex << (it->second).getDataAsHex();
397 // Close output file:
399 std::string msg = "Open '"; msg += filetrace; msg += "' in order to see process traces.\n";
400 msg += "Open '"; msg += outputFile; msg += "' to see decoding results.\n";
401 if (writeHex) msg += "Open '<frame number>.hex' to see specific frame data.";