Project

General

Profile

Download (12.7 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	diag_packet_capture.php
4
	Copyright (C) 2013-2015 Electric Sheep Fencing, LP
5
	All rights reserved
6

    
7
	Redistribution and use in source and binary forms, with or without
8
	modification, are permitted provided that the following conditions are met:
9

    
10
	1. Redistributions of source code must retain the above copyright notice,
11
	this list of conditions and the following disclaimer.
12

    
13
	2. Redistributions in binary form must reproduce the above copyright
14
	notice, this list of conditions and the following disclaimer in the
15
	documentation and/or other materials provided with the distribution.
16

    
17
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
	POSSIBILITY OF SUCH DAMAGE.
27
*/
28

    
29
/*
30
	pfSense_BUILDER_BINARIES:	/bin/ps /usr/bin/grep	/usr/sbin/tcpdump
31
	pfSense_MODULE: routing
32
*/
33

    
34
##|+PRIV
35
##|*IDENT=page-diagnostics-packetcapture
36
##|*NAME=Diagnostics: Packet Capture page
37
##|*DESCR=Allow access to the 'Diagnostics: Packet Capture' page.
38
##|*MATCH=diag_packet_capture.php*
39
##|-PRIV
40

    
41
$allowautocomplete = true;
42

    
43
function fixup_host_logic($value) {
44
	return str_replace(array(" ", ",", "+", "|", "!"), array("", "and ", "and ", "or ", "not "), $value);
45
}
46

    
47
function strip_host_logic($value) {
48
	return str_replace(array(" ", ",", "+", "|", "!"), array("", "", "", "", ""), $value);
49
}
50

    
51
function get_host_boolean($value, $host) {
52
	$value = str_replace(array("!", $host), array("", ""), $value);
53
	$andor = "";
54
	switch (trim($value)) {
55
		case "|":
56
			$andor = "or ";
57
			break;
58
		case ",":
59
		case "+":
60
			$andor = "and ";
61
			break;
62
	}
63

    
64
	return $andor;
65
}
66

    
67
function has_not($value) {
68
	return strpos($value, '!') !== false;
69
}
70

    
71
function fixup_not($value) {
72
	return str_replace("!", "not ", $value);
73
}
74

    
75
function strip_not($value) {
76
	return ltrim(trim($value), '!');
77
}
78

    
79
function fixup_host($value, $position) {
80
	$host = strip_host_logic($value);
81
	$not = has_not($value) ? "not " : "";
82
	$andor = ($position > 0) ? get_host_boolean($value, $host) : "";
83
	if (is_ipaddr($host))
84
		return "{$andor}host {$not}" . $host;
85
	elseif (is_subnet($host))
86
		return "{$andor}net {$not}" . $host;
87
	else
88
		return "";
89
}
90

    
91
if ($_POST['downloadbtn'] == gettext("Download Capture"))
92
	$nocsrf = true;
93

    
94
$pgtitle = array(gettext("Diagnostics"), gettext("Packet Capture"));
95
require_once("guiconfig.inc");
96
require_once("pfsense-utils.inc");
97

    
98
$fp = "/root/";
99
$fn = "packetcapture.cap";
100
$snaplen = 0;//default packet length
101
$count = 100;//default number of packets to capture
102

    
103
$fams = array('ip', 'ip6');
104
$protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp',
105
		        '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp');
106

    
107
$input_errors = array();
108

    
109
$interfaces = get_configured_interface_with_descr();
110

    
111
if (isset($config['ipsec']['enable']))
112
	$interfaces['ipsec'] = "IPsec";
113

    
114
foreach (array('server', 'client') as $mode) {
115
	if (is_array($config['openvpn']["openvpn-{$mode}"])) {
116
		foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) {
117
			if (!isset($setting['disable'])) {
118
				$interfaces['ovpn' . substr($mode, 0, 1) . $setting['vpnid']] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']);
119
			}
120
		}
121
	}
122
}
123

    
124
if ($_POST) {
125
	$host = $_POST['host'];
126
	$selectedif = $_POST['interface'];
127
	$count = $_POST['count'];
128
	$snaplen = $_POST['snaplen'];
129
	$port = $_POST['port'];
130
	$detail = $_POST['detail'];
131
	$fam = $_POST['fam'];
132
	$proto = $_POST['proto'];
133

    
134
	if (!array_key_exists($selectedif, $interfaces)) {
135
		$input_errors[] = gettext("Invalid interface.");
136
	}
137

    
138
	if ($fam !== "" && $fam !== "ip" && $fam !== "ip6") {
139
		$input_errors[] = gettext("Invalid address family.");
140
	}
141

    
142
	if ($proto !== "" && !in_array(strip_not($proto), $protos)) {
143
		$input_errors[] = gettext("Invalid protocol.");
144
	}
145

    
146
	if ($host != "") {
147
		$host_string = str_replace(array(" ", "|", ","), array("", "#|", "#+"), $host);
148

    
149
		if (strpos($host_string, '#') === false) {
150
			$hosts = array($host);
151
		} else {
152
			$hosts = explode('#', $host_string);
153
		}
154

    
155
		foreach ($hosts as $h) {
156
			if (!is_subnet(strip_host_logic($h)) && !is_ipaddr(strip_host_logic($h))) {
157
				$input_errors[] = sprintf(gettext("A valid IP address or CIDR block must be specified. [%s]"), $h);
158
			}
159
		}
160
	}
161

    
162
	if ($port != "") {
163
		if (!is_port(strip_not($port))) {
164
			$input_errors[] = gettext("Invalid value specified for port.");
165
		}
166
	}
167

    
168
	if ($snaplen == "") {
169
		$snaplen = 0;
170
	} else {
171
		if (!is_numeric($snaplen) || $snaplen < 0) {
172
			$input_errors[] = gettext("Invalid value specified for packet length.");
173
		}
174
	}
175

    
176
	if ($count == "") {
177
		$count = 0;
178
	} else {
179
		if (!is_numeric($count) || $count < 0) {
180
			$input_errors[] = gettext("Invalid value specified for packet count.");
181
		}
182
	}
183

    
184
	if (!count($input_errors)) {
185
		$do_tcpdump = true;
186

    
187
		conf_mount_rw();
188

    
189
		if ($_POST['promiscuous']) {
190
			//if promiscuous mode is checked
191
			$disablepromiscuous = "";
192
		} else {
193
			//if promiscuous mode is unchecked
194
			$disablepromiscuous = "-p";
195
		}
196

    
197
		if ($_POST['dnsquery']) {
198
			//if dns lookup is checked
199
			$disabledns = "";
200
		} else {
201
			//if dns lookup is unchecked
202
			$disabledns = "-n";
203
		}
204

    
205
		if ($_POST['startbtn'] != "" ) {
206
			$action = gettext("Start");
207

    
208
			//delete previous packet capture if it exists
209
			if (file_exists($fp.$fn))
210
				unlink ($fp.$fn);
211

    
212
		} elseif ($_POST['stopbtn']!= "") {
213
			$action = gettext("Stop");
214
			$processes_running = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'"));
215

    
216
			//explode processes into an array, (delimiter is new line)
217
			$processes_running_array = explode("\n", $processes_running);
218

    
219
			//kill each of the packetcapture processes
220
			foreach ($processes_running_array as $process) {
221
				$process_id_pos = strpos($process, ' ');
222
				$process_id = substr($process, 0, $process_id_pos);
223
				exec("kill $process_id");
224
			}
225

    
226
		} elseif ($_POST['downloadbtn']!= "") {
227
			//download file
228
			$fs = filesize($fp.$fn);
229
			header("Content-Type: application/octet-stream");
230
			header("Content-Disposition: attachment; filename=$fn");
231
			header("Content-Length: $fs");
232
			readfile($fp.$fn);
233
			exit;
234
		}
235
	}
236
} else {
237
	$do_tcpdump = false;
238
}
239

    
240
$protocollist = array(
241
	'' => 'Any',
242
	'icmp' => 'ICMP',
243
	'!icmp' => 'Exclude ICMP',
244
	'icmp6' => 'ICMPv6',
245
	'!icmp6' => 'Exclude ICMPv6',
246
	'tcp' => 'TCP',
247
	'!tcp' => 'Exclude TCP',
248
	'udp' => 'UDP',
249
	'!udp' => 'Exclude UDP',
250
	'arp' => 'ARP',
251
	'!arp' => 'Exclude ARP',
252
	'carp' => 'CARP (VRRP)',
253
	'!carp' => 'Exclude CARP (VRRP)',
254
	'esp' => 'ESP'
255
);
256

    
257
include("head.inc");
258

    
259
if ($input_errors)
260
	print_input_errors($input_errors);
261

    
262
require('classes/Form.class.php');
263

    
264
$form = new Form(false); // No button yet. We add those later depending on the required action
265

    
266
$section = new Form_Section('General Logging Options');
267

    
268
$section->addInput(new Form_Select(
269
	'interface',
270
	'Interface',
271
	$selectedif,
272
	$interfaces
273
))->setHelp('Select the interface on which to capture traffic. ');
274

    
275
$section->addInput(new Form_Checkbox(
276
	'promiscuous',
277
	'Promiscuous',
278
	'Packet capture will be performed using promiscuous mode',
279
	$pconfig['promiscuous']
280
))->setHelp('Note: Some network adapters do not support or work well in promiscuous mode.'. '<br />' .
281
			'More: ' . '<a target="_blank" href="http://www.freebsd.org/cgi/man.cgi?query=tcpdump&amp;apropos=0&amp;sektion=0&amp;manpath=FreeBSD+8.3-stable&amp;arch=default&amp;format=html">' .
282
			'Packet capture' . '</a>');
283

    
284
$section->addInput(new Form_Select(
285
	'fam',
286
	'Address Family',
287
	$fam,
288
	array('' => 'Any',
289
		  'ip' => 'IPv4 Only',
290
		  'ip6' => 'IPv6 Only'
291
	)
292
))->setHelp('Select the type of traffic to be captured');
293

    
294
$section->addInput(new Form_Select(
295
	'proto',
296
	'Protocol',
297
	$proto,
298
	$protocollist
299
))->setHelp('Select the protocol to capture, or "Any". ');
300

    
301
$section->addInput(new Form_Input(
302
	'host',
303
	'Host Address',
304
	'text',
305
	$host
306
))->setHelp('This value is either the Source or Destination IP address or subnet in CIDR notation. The packet capture will look for this address in either field.' . '<br />' .
307
			'Matching can be negated by preceding the value with "!". Multiple IP addresses or CIDR subnets may be specified. Comma (",") separated values perform a boolean "AND". ' .
308
			'Separating with a pipe ("|") performs a boolean "OR".' . '<br />' .
309
			'If you leave this field blank, all packets on the specified interface will be captured.');
310

    
311
$section->addInput(new Form_Input(
312
	'port',
313
	'Port',
314
	'text',
315
	$port
316
))->setHelp('The port can be either the source or destination port. The packet capture will look for this port in either field. ' .
317
			'Leave blank if you do not want to filter by port.');
318

    
319
$section->addInput(new Form_Input(
320
	'snaplen',
321
	'Packet Length',
322
	'text',
323
	$snaplen
324
))->setHelp('The Packet length is the number of bytes of each packet that will be captured. Default value is 0, ' .
325
			'which will capture the entire frame regardless of its size.');
326

    
327
$section->addInput(new Form_Input(
328
	'count',
329
	'Count',
330
	'text',
331
	$count
332
))->setHelp('This is the number of packets the packet capture will grab. Default value is 100.' . '<br />' .
333
			'Enter 0 (zero) for no count limit.');
334

    
335
$section->addInput(new Form_Select(
336
	'detail',
337
	'Level of detail',
338
	$detail,
339
	array('' => 'Any',
340
		  'normal' => 'Normal',
341
		  'medium' => 'Medium',
342
		  'high' => 'High',
343
		  'full' => 'Full',
344
	)
345
))->setHelp('This is the level of detail that will be displayed after hitting "Stop" when the packets have been captured.' . '<br />' .
346
			'This option does not affect the level of detail when downloading the packet capture. ');
347

    
348
$section->addInput(new Form_Checkbox(
349
	'dnsquery',
350
	'Reverse DNS Lookup',
351
	null,
352
	$_POST['dnsquery']
353
))->setHelp('This check box will cause the packet capture to perform a reverse DNS lookup associated with all IP addresses.' . '<br />' .
354
			'This option can cause delays for large packet captures.');
355

    
356
$form->add($section);
357

    
358
/* check to see if packet capture tcpdump is already running */
359
$processcheck = (trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'")));
360

    
361
$processisrunning = ($processcheck != "");
362

    
363
if (($action == gettext("Stop") or $action == "") and $processisrunning != true) {
364
	$form->addGlobal(new Form_Button(
365
		'startbtn',
366
		'Start'
367
	))->removeClass('btn-primary')->addClass('btn-success');
368
}
369
else {
370
	$form->addGlobal(new Form_Button(
371
		'stopbtn',
372
		'Stop'
373
	))->removeClass('btn-primary')->addClass('btn-warning');
374
}
375

    
376
if (file_exists($fp.$fn) and $processisrunning != true) {
377
	$form->addGlobal(new Form_Button(
378
		'viewbtn',
379
		'View Capture'
380
	))->removeClass('btn-primary');
381

    
382
	$form->addGlobal(new Form_Button(
383
		'downloadbtn',
384
		'Download Capture'
385
	))->removeClass('btn-primary');
386

    
387
	$section->addInput(new Form_StaticText(
388
		'Last capture',
389
		date("F jS, Y g:i:s a.", filemtime($fp.$fn))
390
	));
391
}
392

    
393
print($form);
394

    
395
if ($do_tcpdump) :
396
	$matches = array();
397

    
398
	if (in_array($fam, $fams))
399
		$matches[] = $fam;
400

    
401
	if (in_array($proto, $protos))
402
		$matches[] = fixup_not($proto);
403

    
404
	if ($port != "")
405
		$matches[] = "port ".fixup_not($port);
406

    
407
	if ($host != "") {
408
		$hostmatch = "";
409
		$hostcount = 0;
410

    
411
		foreach ($hosts as $h) {
412
			$h = fixup_host($h, $hostcount++);
413

    
414
			if (!empty($h))
415
				$hostmatch .= " " . $h;
416
		}
417

    
418
		if (!empty($hostmatch))
419
			$matches[] = "({$hostmatch})";
420
	}
421

    
422
	if ($count != "0" ) {
423
		$searchcount = "-c " . $count;
424
	} else {
425
		$searchcount = "";
426
	}
427

    
428
	$selectedif = convert_friendly_interface_to_real_interface_name($selectedif);
429

    
430
	if ($action == gettext("Start")) {
431
		$matchstr = implode($matches, " and ");
432

    
433
		print_info_box(gettext('Packet Capture is running'), 'info');
434

    
435
		$cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disablepromiscuous} {$searchcount} -s {$snaplen} -w {$fp}{$fn} " . escapeshellarg($matchstr);
436
		// Debug
437
		//echo $cmd;
438
		mwexec_bg ($cmd);
439
	} else {
440
?>
441

    
442
<div class="panel panel-default">
443
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Packets Captured')?></h2></div>
444
	<div class="panel-body">
445

    
446
<?php
447
		$detail_args = "";
448
		switch ($detail) {
449
			case "full":
450
				$detail_args = "-vv -e";
451
				break;
452
			case "high":
453
				$detail_args = "-vv";
454
				break;
455
			case "medium":
456
				$detail_args = "-v";
457
				break;
458
			case "normal":
459
			default:
460
				$detail_args = "-q";
461
				break;
462
		}
463

    
464
		print('<pre>');
465
		system("/usr/sbin/tcpdump {$disabledns} {$detail_args} -r {$fp}{$fn}");
466
		print('</pre>');
467

    
468
		conf_mount_ro();
469
?>
470
	</div>
471
</div>
472
<?php
473
	}
474
endif;
475

    
476
include("foot.inc");
(28-28/237)