📄 toipflowdumps.cc
字号:
// -*- c-basic-offset: 4 -*-/* * toipflowdumps.{cc,hh} -- creates separate trace files for each flow * Eddie Kohler * * Copyright (c) 2002 International Computer Science Institute * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */#include <click/config.h>#include "toipflowdumps.hh"#include <click/confparse.hh>#include <click/error.hh>#include <click/packet_anno.hh>#include <click/router.hh>#include <click/standard/scheduleinfo.hh>#include <clicknet/udp.h>#include <clicknet/icmp.h>#include <unistd.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/wait.h>#include <fcntl.h>#include "elements/analysis/ipsumdumpinfo.hh"CLICK_DECLS#ifdef i386# define PUT4(p, d) *reinterpret_cast<uint32_t *>((p)) = htonl((d))#else# define PUT4(p, d) do { (p)[0] = (d)>>24; (p)[1] = (d)>>16; (p)[2] = (d)>>8; (p)[3] = (d); } while (0)#endif#define PUT1(p, d) ((p)[0] = (d))ToIPFlowDumps::Flow::Flow(const Packet *p, const String &filename, bool absolute_time, bool absolute_seq, bool binary, bool ip_id, int tcp_opt, bool tcp_window) : _next(0), _flowid(p), _ip_p(p->ip_header()->ip_p), _aggregate(AGGREGATE_ANNO(p)), _packet_count(0), _note_count(0), _filename(filename), _outputted(false), _binary(binary), _tcp_opt(tcp_opt), _npkt(0), _nnote(0){ // use the encapsulated IP header for ICMP errors if (_ip_p == IP_PROTO_ICMP) { const click_icmp *icmph = p->icmp_header(); // should assert some things here const click_ip *embedded_iph = reinterpret_cast<const click_ip *>(icmph + 1); _flowid = IPFlowID(embedded_iph); _ip_p = embedded_iph->ip_p; } if (PAINT_ANNO(p) & 1) // reverse _flowid _flowid = _flowid.rev(); _have_first_seq[0] = _have_first_seq[1] = absolute_seq; _first_seq[0] = _first_seq[1] = 0; if (absolute_time) _first_timestamp = Timestamp(); else // make first packet have timestamp .000001 _first_timestamp = p->timestamp_anno() - Timestamp::epsilon(); if (ip_id) _ip_ids = new uint16_t[NPKT]; else _ip_ids = 0; if (tcp_window && _ip_p == IP_PROTO_TCP) _tcp_windows = new uint16_t[NPKT]; else _tcp_windows = 0; // sanity checks assert(_aggregate && (_ip_p == IP_PROTO_TCP || _ip_p == IP_PROTO_UDP));}ToIPFlowDumps::Flow::~Flow(){ delete[] _ip_ids; delete[] _tcp_windows;}intToIPFlowDumps::Flow::create_directories(const String &n, ErrorHandler *errh){ int slash = n.find_right('/'); if (slash <= 0) return 0; String component = n.substring(0, slash); if (access(component.cc(), F_OK) >= 0) return 0; else if (create_directories(component, errh) < 0) return -1; else if (mkdir(component.cc(), 0777) < 0) return errh->error("making directory %s: %s", component.cc(), strerror(errno)); else return 0;}voidToIPFlowDumps::Flow::output_binary(StringAccum &sa){ union { uint32_t u[8]; uint16_t us[16]; char c[32]; } buf; int pi = 0, ni = 0; const uint16_t *opt = reinterpret_cast<const uint16_t *>(_opt_info.data()); const uint16_t *end_opt = opt + (_opt_info.length() / 2); while (pi < _npkt || ni < _nnote) if (ni >= _nnote || _note[ni].before_pkt > pi) { int pos; buf.u[1] = ntohl(_pkt[pi].timestamp.sec());#if HAVE_NANOTIMESTAMP buf.u[2] = ntohl(_pkt[pi].timestamp.nsec());#else buf.u[2] = ntohl(_pkt[pi].timestamp.usec());#endif if (_ip_p == IP_PROTO_TCP) { buf.u[3] = ntohl(_pkt[pi].th_seq); buf.u[4] = ntohl(_pkt[pi].payload_len); buf.u[5] = ntohl(_pkt[pi].th_ack); pos = 24; } else { buf.u[3] = ntohl(_pkt[pi].payload_len); pos = 16; } if (_ip_ids) buf.us[pos>>1] = _ip_ids[pi], pos += 2; if (_tcp_windows) buf.us[pos>>1] = _tcp_windows[pi], pos += 2; if (_ip_p == IP_PROTO_TCP) buf.c[pos++] = _pkt[pi].th_flags; buf.c[pos++] = _pkt[pi].direction; buf.u[0] = ntohl(pos); sa.append(&buf.c[0], pos); // handle TCP options specially if (opt < end_opt && opt[0] == pi) { int original_pos = sa.length() - pos; IPSummaryDump::unparse_tcp_opt_binary(sa, reinterpret_cast<const uint8_t *>(opt + 2), opt[1], _tcp_opt); PUT4(sa.data() + original_pos, sa.length() - original_pos); opt += 2 + (opt[1] / 2); } pi++; } else { int len = (ni == _nnote - 1 ? _note_text.length() : _note[ni+1].pos) - _note[ni].pos; int record_len = (4 + len + 2); buf.u[0] = ntohl(record_len | 0x80000000U); buf.c[4] = '#'; sa.append(&buf.c[0], 5); sa.append(_note_text.data() + _note[ni].pos, len); sa.append("\n", 1); ni++; }}intToIPFlowDumps::Flow::output(ErrorHandler *errh){ static StringAccum sa; int fd; if (_filename == "-") fd = STDOUT_FILENO; else if (_outputted) fd = open(_filename.cc(), O_WRONLY | O_APPEND); else if (create_directories(_filename, errh) < 0) return -1; else fd = open(_filename.cc(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) return errh->error("%s: %s", _filename.cc(), strerror(errno)); // make a guess about how much data we'll need sa.clear(); sa.reserve(_npkt * (_binary ? 28 : 40) + _note_text.length() + _nnote * 8 + _opt_info.length() + 16); if (!_outputted) { sa << "!IPSummaryDump 1.2\n!flowid " << _flowid.saddr() << ' ' << ntohs(_flowid.sport()) << ' ' << _flowid.daddr() << ' ' << ntohs(_flowid.dport()) << ' ' << (_ip_p == IP_PROTO_TCP ? 'T' : 'U') << "\n!aggregate " << _aggregate << '\n';#if HAVE_NANOTIMESTAMP sa << "!data ntimestamp";#else sa << "!data timestamp";#endif if (_binary) { if (_ip_p == IP_PROTO_TCP) sa << " tcp_seq payload_len tcp_ack"; else sa << " payload_len"; if (_ip_ids) sa << " ip_id"; if (_tcp_windows) sa << " tcp_window"; if (_ip_p == IP_PROTO_TCP) sa << " tcp_flags"; sa << " direction"; if (_ip_p == IP_PROTO_TCP) { if (_tcp_opt & IPSummaryDump::DO_TCPOPT_TIMESTAMP) sa << " tcp_opt"; else sa << " tcp_ntopt"; } } else { sa << " direction"; if (_ip_ids) sa << " ip_id"; if (_ip_p == IP_PROTO_TCP) { sa << " tcp_flags tcp_seq payload_len tcp_ack"; if (_tcp_opt & IPSummaryDump::DO_TCPOPT_TIMESTAMP) sa << " tcp_opt"; else sa << " tcp_ntopt"; } else sa << " payload_len"; } sa << '\n'; if (_have_first_seq[0] && _first_seq[0] && _ip_p == IP_PROTO_TCP) sa << "!firstseq > " << _first_seq[0] << '\n'; if (_have_first_seq[1] && _first_seq[1] && _ip_p == IP_PROTO_TCP) sa << "!firstseq < " << _first_seq[1] << '\n'; if (_first_timestamp) sa << "!firsttime " << _first_timestamp << '\n'; if (_binary) sa << "!binary\n"; } if (_binary) output_binary(sa); else { int pi = 0, ni = 0; const uint16_t *opt = reinterpret_cast<const uint16_t *>(_opt_info.data()); const uint16_t *end_opt = opt + (_opt_info.length() / 2); while (pi < _npkt || ni < _nnote) if (ni >= _nnote || _note[ni].before_pkt > pi) { int direction = _pkt[pi].direction; sa << _pkt[pi].timestamp << ' ' << (direction == 0 ? '>' : '<') << ' '; if (_ip_ids) sa << _ip_ids[pi] << ' '; if (_ip_p == IP_PROTO_TCP) { int flags = _pkt[pi].th_flags; if (flags == TH_ACK) sa << 'A'; else if (flags == (TH_ACK | TH_PUSH)) sa << 'P' << 'A'; else if (flags == 0) sa << '.'; else for (int flag = 0; flag < 7; flag++) if (flags & (1 << flag)) sa << IPSummaryDump::tcp_flags_word[flag]; sa << ' ' << _pkt[pi].th_seq << ' ' << _pkt[pi].payload_len << ' ' << _pkt[pi].th_ack; if (_tcp_windows) sa << ' ' << ntohs(_tcp_windows[pi]); if (opt < end_opt && opt[0] == pi) { sa << ' '; IPSummaryDump::unparse_tcp_opt(sa, reinterpret_cast<const uint8_t *>(opt + 2), opt[1], _tcp_opt); opt += 2 + (opt[1] / 2); } sa << '\n'; } else sa << _pkt[pi].payload_len << '\n'; pi++; } else { int len = (ni == _nnote - 1 ? _note_text.length() : _note[ni+1].pos) - _note[ni].pos; sa << '#'; sa.append(_note_text.data() + _note[ni].pos, len); sa << '\n'; ni++; } } _npkt = 0; _opt_info.clear(); _note_text.clear(); _nnote = 0; // actually write data int pos = 0; while (pos < sa.length()) { int written = write(fd, sa.data() + pos, sa.length() - pos); if (written < 0 && errno != EINTR) { errh->error("%s: %s", _filename.cc(), strerror(errno)); break; } pos += written; } if (fd != STDOUT_FILENO) close(fd); _outputted = true; return 0;}inline voidToIPFlowDumps::Flow::unlink(ErrorHandler *errh){ if (_outputted && ::unlink(_filename.cc()) < 0) errh->error("%s: %s", _filename.cc(), strerror(errno));}voidToIPFlowDumps::Flow::store_opt(const click_tcp *tcph, int direction){ const uint8_t *opt = reinterpret_cast<const uint8_t *>(tcph + 1); const uint8_t *end_opt = opt + ((tcph->th_off << 2) - sizeof(click_tcp)); bool any = false; int original_len = _opt_info.length(); char *data; while (opt < end_opt) switch (*opt) { case TCPOPT_EOL: goto done; case TCPOPT_NOP: opt++; break; case TCPOPT_MAXSEG: if (opt[1] != TCPOLEN_MAXSEG || opt + opt[1] > end_opt) goto bad_opt; else goto good_opt; case TCPOPT_WSCALE: if (opt[1] != TCPOLEN_WSCALE || opt + opt[1] > end_opt) goto bad_opt; else goto good_opt; case TCPOPT_SACK_PERMITTED: if (opt[1] != TCPOLEN_SACK_PERMITTED || opt + opt[1] > end_opt) goto bad_opt; else goto good_opt; case TCPOPT_TIMESTAMP: if (opt[1] != TCPOLEN_TIMESTAMP || opt + opt[1] > end_opt) goto bad_opt; else if (_tcp_opt & IPSummaryDump::DO_TCPOPT_TIMESTAMP) goto good_opt; else goto ignore_opt; good_opt: if (!any && (data = _opt_info.extend(4))) *(reinterpret_cast<uint16_t *>(data)) = _npkt; if ((data = _opt_info.extend(opt[1]))) memcpy(data, opt, opt[1]); opt += opt[1]; any = true; break; case TCPOPT_SACK: if (opt[1] % 8 != 2 || opt + opt[1] > end_opt) goto bad_opt; if (!any && (data = _opt_info.extend(4))) *(reinterpret_cast<uint16_t *>(data)) = _npkt; if ((data = _opt_info.extend(opt[1]))) { // argh... must renumber sequence numbers in sack blocks memcpy(data, opt, 2); const uint8_t *end_sack_opt = opt + opt[1]; for (opt += 2, data += 2; opt < end_sack_opt; opt += 8, data += 8) { uint32_t buf[2]; memcpy(buf, opt, 8); if (!_have_first_seq[!direction]) { _first_seq[!direction] = ntohl(buf[0]); _have_first_seq[!direction] = true; } buf[0] = htonl(ntohl(buf[0]) - _first_seq[!direction]); buf[1] = htonl(ntohl(buf[1]) - _first_seq[!direction]); memcpy(data, buf, 8); } } else opt += opt[1]; any = true; break; default: if (opt[1] == 0 || opt + opt[1] > end_opt) goto bad_opt; else goto ignore_opt; ignore_opt: opt += opt[1]; break; } done: if (any) { if (_opt_info.length() & 1) _opt_info.append('\0'); *(reinterpret_cast<uint16_t *>(_opt_info.data() + original_len) + 1) = _opt_info.length() - original_len - 4; } return; bad_opt: _opt_info.set_length(original_len);}intToIPFlowDumps::Flow::add_pkt(const Packet *p, ErrorHandler *errh){ // ICMP errors are handled as notes, not packets if (PAINT_ANNO(p) >= 2) { assert(p->ip_header()->ip_p == IP_PROTO_ICMP); StringAccum sa; sa << p->timestamp_anno() << ' ' << (PAINT_ANNO(p) & 1 ? '>' : '<') << " ICMP_error"; // this doesn't count as a note, really; it is a packet _note_count--; if (_packet_count < 0xFFFFFFFFU) _packet_count++; return add_note(sa.take_string(), errh); } if (_npkt >= NPKT && output(errh) < 0) return -1; int direction = (PAINT_ANNO(p) & 1); const click_ip *iph = p->ip_header(); assert(iph->ip_p == _ip_p); _pkt[_npkt].timestamp = p->timestamp_anno() - _first_timestamp; _pkt[_npkt].direction = direction; if (_ip_ids) _ip_ids[_npkt] = iph->ip_id; if (_ip_p == IP_PROTO_TCP) { const click_tcp *tcph = p->tcp_header();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -