Sponge
CS144's user-space TCP library
udp_tcpdump.cc
Go to the documentation of this file.
1 #include "parser.hh"
2 #include "tcp_header.hh"
3 #include "tcp_segment.hh"
4 #include "util.hh"
5 
6 #include <arpa/inet.h>
7 #include <cstdint>
8 #include <cstdlib>
9 #include <cstring>
10 #include <iomanip>
11 #include <iostream>
12 #include <pcap/pcap.h>
13 #include <sstream>
14 #include <string>
15 #include <sys/socket.h>
16 #include <unistd.h>
17 #include <vector>
18 
19 using namespace std;
20 
21 static void show_usage(const char *arg0, const char *errmsg) {
22  cout << "Usage: " << arg0 << " [-i <intf>] [-F <file>] [-h|--help] <expression>\n\n"
23  << " -i <intf> only capture packets from <intf> (default: all)\n\n"
24 
25  << " -F <file> reads in a filter expression from <file>\n"
26  << " <expression> is ignored if -F is supplied.\n\n"
27 
28  << " -h, --help show this message\n\n"
29  << " <expression> a filter expression in pcap-filter(7) syntax\n";
30 
31  if (errmsg != nullptr) {
32  cout << '\n' << errmsg;
33  }
34  cout << endl;
35 }
36 
37 static void check_arg(char *arg0, int argc, int curr, const char *errmsg) {
38  if (curr + 1 >= argc) {
39  show_usage(arg0, errmsg);
40  exit(1);
41  }
42 }
43 
44 static int parse_arguments(int argc, char **argv, char **dev_ptr) {
45  int curr = 1;
46  while (curr < argc) {
47  if (strncmp("-i", argv[curr], 3) == 0) {
48  check_arg(argv[0], argc, curr, "ERROR: -i requires an argument");
49  *dev_ptr = argv[curr + 1];
50  curr += 2;
51 
52  } else if ((strncmp("-h", argv[curr], 3) == 0) || (strncmp("--help", argv[curr], 7) == 0)) {
53  show_usage(argv[0], nullptr);
54  exit(0);
55 
56  } else {
57  break;
58  }
59  }
60 
61  return curr;
62 }
63 
64 static string inet4_addr(const uint8_t *data) {
65  char addrbuf[128];
66  auto *addr = reinterpret_cast<const in_addr *>(data);
67  if (inet_ntop(AF_INET, addr, static_cast<char *>(addrbuf), 128) == nullptr) {
68  return "unknown";
69  }
70  return string(static_cast<char *>(addrbuf));
71 }
72 
73 static string inet6_addr(const uint8_t *data) {
74  char addrbuf[128];
75  auto *addr = reinterpret_cast<const in6_addr *>(data);
76  if (inet_ntop(AF_INET6, addr, static_cast<char *>(addrbuf), 128) == nullptr) {
77  return "unknown";
78  }
79  return string(static_cast<char *>(addrbuf));
80 }
81 
82 static int process_ipv4_ipv6(int len, const uint8_t *data, string &src_addr, string &dst_addr) {
83  // this is either an IPv4 or IPv6 packet, we hope
84  if (len < 1) {
85  return -1;
86  }
87  int data_offset = 0;
88  const uint8_t pt = data[0] & 0xf0;
89  if (pt == 0x40) {
90  // check packet length and proto
91  data_offset = (data[0] & 0x0f) * 4;
92  if (len < data_offset) {
93  return -1;
94  }
95  if (data[9] != 0x11) {
96  cerr << "Not UDP; ";
97  return -1;
98  }
99  src_addr = inet4_addr(data + 12);
100  dst_addr = inet4_addr(data + 16);
101  } else if (pt == 0x60) {
102  // check packet length
103  if (len < 42) {
104  return -1;
105  }
106  data_offset = 40;
107  uint8_t nxt = data[6];
108  while (nxt != 0x11) {
109  if (nxt != 0 && nxt != 43 && nxt != 60) {
110  cerr << "Not UDP or fragmented; ";
111  return -1;
112  }
113  nxt = data[data_offset];
114  data_offset += 8 * (1 + data[data_offset + 1]);
115  if (len < data_offset + 2) {
116  return -1;
117  }
118  }
119  src_addr = inet6_addr(data + 8);
120  dst_addr = inet6_addr(data + 24);
121  } else {
122  return -1;
123  }
124 
125  return data_offset + 8; // skip UDP header
126 }
127 
128 int main(int argc, char **argv) {
129  char *dev = nullptr;
130  const int exp_start = parse_arguments(argc, argv, &dev);
131 
132  // create pcap handle
133  if (dev != nullptr) {
134  cout << "Capturing on interface " << dev;
135  } else {
136  cout << "Capturing on all interfaces";
137  }
138  pcap_t *p_hdl = nullptr;
139  const int dl_type = [&] {
140  char errbuf[PCAP_ERRBUF_SIZE] = {
141  0,
142  };
143  p_hdl = pcap_open_live(dev, 65535, 0, 100, static_cast<char *>(errbuf));
144  if (p_hdl == nullptr) {
145  cout << "\nError initiating capture: " << static_cast<char *>(errbuf) << endl;
146  exit(1);
147  }
148  int dlt = pcap_datalink(p_hdl);
149  // need to handle: DLT_RAW, DLT_NULL, DLT_EN10MB, DLT_LINUX_SLL
150  if (dlt != DLT_RAW && dlt != DLT_NULL && dlt != DLT_EN10MB && dlt != DLT_LINUX_SLL
151 #ifdef DLT_LINUX_SLL2
152  && dlt != DLT_LINUX_SLL2
153 #endif
154  ) {
155  cout << "\nError: unsupported datalink type " << pcap_datalink_val_to_description(dlt) << endl;
156  exit(1);
157  }
158  cout << " (type: " << pcap_datalink_val_to_description(dlt) << ")\n";
159  return dlt;
160  }();
161 
162  // compile and set filter
163  {
164  struct bpf_program p_flt {};
165  stringstream f_stream;
166  for (int i = exp_start; i < argc; ++i) {
167  f_stream << argv[i] << ' ';
168  }
169  string filter_expression = f_stream.str();
170  cout << "Using filter expression: " << filter_expression << "\n";
171  if (pcap_compile(p_hdl, &p_flt, filter_expression.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) {
172  cout << "Error compiling filter expression: " << pcap_geterr(p_hdl) << endl;
173  return EXIT_FAILURE;
174  }
175  if (pcap_setfilter(p_hdl, &p_flt) != 0) {
176  cout << "Error configuring packet filter: " << pcap_geterr(p_hdl) << endl;
177  return EXIT_FAILURE;
178  }
179  pcap_freecode(&p_flt);
180  }
181 
182  int next_ret = 0;
183  struct pcap_pkthdr *pkt_hdr = nullptr;
184  const uint8_t *pkt_data = nullptr;
185  cout << setfill('0');
186  while ((next_ret = pcap_next_ex(p_hdl, &pkt_hdr, &pkt_data)) >= 0) {
187  if (next_ret == 0) {
188  // timeout; just listen again
189  continue;
190  }
191 
192  size_t hdr_off = 0;
193  int start_off = 0;
194  // figure out where in the datagram to look based on link type
195  if (dl_type == DLT_NULL) {
196  hdr_off = 4;
197  if (pkt_hdr->caplen < hdr_off) {
198  cerr << "[INFO] Skipping malformed packet.\n";
199  continue;
200  }
201  const uint8_t pt = pkt_data[3];
202  if (pt != 2 && pt != 24 && pt != 28 && pt != 30) {
203  cerr << "[INFO] Skipping non-IP packet.\n";
204  continue;
205  }
206  } else if (dl_type == DLT_EN10MB) {
207  hdr_off = 14;
208  if (pkt_hdr->caplen < hdr_off) {
209  cerr << "[INFO] Skipping malformed packet.\n";
210  continue;
211  }
212  const uint16_t pt = (pkt_data[12] << 8) | pkt_data[13];
213  if (pt != 0x0800 && pt != 0x86dd) {
214  cerr << "[INFO] Skipping non-IP packet.\n";
215  continue;
216  }
217  } else if (dl_type == DLT_LINUX_SLL) {
218  hdr_off = 16;
219  if (pkt_hdr->caplen < hdr_off) {
220  cerr << "[INFO] Skipping malformed packet.\n";
221  continue;
222  }
223  const uint16_t pt = (pkt_data[14] << 8) | pkt_data[15];
224  if (pt != 0x0800 && pt != 0x86dd) {
225  cerr << "[INFO] Skipping non-IP packet.\n";
226  continue;
227  }
228 #ifdef DLT_LINUX_SLL2
229  } else if (dl_type == DLT_LINUX_SLL2) {
230  if (pkt_hdr->caplen < 20) {
231  cerr << "[INFO] Skipping malformed packet.\n";
232  continue;
233  }
234  const uint16_t pt = (pkt_data[0] << 8) | pkt_data[1];
235  hdr_off = 20;
236  if (pt != 0x0800 && pt != 0x86dd) {
237  cerr << "[INFO] Skipping non-IP packet.\n";
238  continue;
239  }
240 #endif
241  } else if (dl_type != DLT_RAW) {
242  cerr << "Mysterious datalink type. Giving up.";
243  return EXIT_FAILURE;
244  }
245 
246  // now actually parse the packet
247  string src{}, dst{};
248  if ((start_off = process_ipv4_ipv6(pkt_hdr->caplen - hdr_off, pkt_data + hdr_off, src, dst)) < 0) {
249  cerr << "Error parsing IPv4/IPv6 packet. Skipping.\n";
250  continue;
251  }
252 
253  // hdr_off + start_off is now the start of the UDP payload
254  const size_t payload_off = hdr_off + start_off;
255  const size_t payload_len = pkt_hdr->caplen - payload_off;
256 
257  string_view payload{reinterpret_cast<const char *>(pkt_data) + payload_off, payload_len};
258 
259  // try to parse UDP payload as TCP packet
260  auto seg = TCPSegment{};
261  if (const auto res = seg.parse(string(payload), 0); res > ParseResult::BadChecksum) {
262  cout << "(did not recognize TCP header) src: " << src << " dst: " << dst << '\n';
263  } else {
264  const TCPHeader &tcp_hdr = seg.header();
265  uint32_t seqlen = seg.length_in_sequence_space();
266 
267  cout << src << ':' << tcp_hdr.sport << " > " << dst << ':' << tcp_hdr.dport << "\n Flags ["
268 
269  << (tcp_hdr.urg ? "U" : "") << (tcp_hdr.psh ? "P" : "") << (tcp_hdr.rst ? "R" : "")
270  << (tcp_hdr.syn ? "S" : "") << (tcp_hdr.fin ? "F" : "") << (tcp_hdr.ack ? "." : "")
271 
272  << "] cksum 0x" << hex << setw(4) << tcp_hdr.cksum << dec
273  << (res == ParseResult::NoError ? " (correct)" : " (incorrect!)")
274 
275  << " seq " << tcp_hdr.seqno;
276 
277  if (seqlen > 0) {
278  cout << ':' << (tcp_hdr.seqno + seqlen);
279  }
280 
281  cout << " ack " << tcp_hdr.ackno << " win " << tcp_hdr.win << " length " << payload_len << endl;
282  }
283  hexdump(payload.data(), payload.size(), 8);
284  }
285 
286  pcap_close(p_hdl);
287  if (next_ret == -1) {
288  cout << "Error listening for packet: " << pcap_geterr(p_hdl) << endl;
289  return EXIT_FAILURE;
290  }
291 
292  return EXIT_SUCCESS;
293 }
ParseResult::NoError
@ NoError
Success.
util.hh
std::string
tcp_segment.hh
len
constexpr size_t len
Definition: tcp_benchmark.cc:12
TCPHeader::win
uint16_t win
window size
Definition: tcp_header.hh:48
TCPHeader::rst
bool rst
rst flag
Definition: tcp_header.hh:45
std::stringstream
inet4_addr
static string inet4_addr(const uint8_t *data)
Definition: udp_tcpdump.cc:64
if
if(out0 !=0x32||out1 !=val1||out2 !=val2||out3 !=val3||out4 !=val4)
Definition: parser_example.cc:30
std::setfill
T setfill(T... args)
tcp_header.hh
std::hex
T hex(T... args)
ParseResult::BadChecksum
@ BadChecksum
Bad checksum.
TCPHeader::dport
uint16_t dport
destination port
Definition: tcp_header.hh:38
std::cout
TCPHeader::syn
bool syn
syn flag
Definition: tcp_header.hh:46
TCPHeader::ack
bool ack
ack flag
Definition: tcp_header.hh:43
TCPHeader::fin
bool fin
fin flag
Definition: tcp_header.hh:47
parse_arguments
static int parse_arguments(int argc, char **argv, char **dev_ptr)
Definition: udp_tcpdump.cc:44
TCPHeader::cksum
uint16_t cksum
checksum
Definition: tcp_header.hh:49
std::uint8_t
TCPHeader::psh
bool psh
push flag
Definition: tcp_header.hh:44
TCPHeader::sport
uint16_t sport
source port
Definition: tcp_header.hh:37
main
int main(int argc, char **argv)
Definition: udp_tcpdump.cc:128
std::strncmp
T strncmp(T... args)
std::endl
T endl(T... args)
std
show_usage
static void show_usage(const char *arg0, const char *errmsg)
Definition: udp_tcpdump.cc:21
TCPHeader::seqno
WrappingInt32 seqno
sequence number
Definition: tcp_header.hh:39
process_ipv4_ipv6
static int process_ipv4_ipv6(int len, const uint8_t *data, string &src_addr, string &dst_addr)
Definition: udp_tcpdump.cc:82
TCPHeader::urg
bool urg
urgent flag
Definition: tcp_header.hh:42
check_arg
static void check_arg(char *arg0, int argc, int curr, const char *errmsg)
Definition: udp_tcpdump.cc:37
std::stringstream::str
T str(T... args)
TCPHeader::ackno
WrappingInt32 ackno
ack number
Definition: tcp_header.hh:40
std::setw
T setw(T... args)
inet6_addr
static string inet6_addr(const uint8_t *data)
Definition: udp_tcpdump.cc:73
TCPHeader
TCP segment header
Definition: tcp_header.hh:9
hexdump
void hexdump(const uint8_t *data, const size_t len, const size_t indent)
Definition: util.cc:113
TCPSegment
TCP segment
Definition: tcp_segment.hh:10
std::exit
T exit(T... args)
parser.hh