Project

General

Profile

Download (14 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	diag_packet_capture.php
4
*/
5
/* ====================================================================
6
 *  Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved. 
7
 *
8
 *  Redistribution and use in source and binary forms, with or without modification, 
9
 *  are permitted provided that the following conditions are met: 
10
 *
11
 *  1. Redistributions of source code must retain the above copyright notice,
12
 *      this list of conditions and the following disclaimer.
13
 *
14
 *  2. Redistributions in binary form must reproduce the above copyright
15
 *      notice, this list of conditions and the following disclaimer in
16
 *      the documentation and/or other materials provided with the
17
 *      distribution. 
18
 *
19
 *  3. All advertising materials mentioning features or use of this software 
20
 *      must display the following acknowledgment:
21
 *      "This product includes software developed by the pfSense Project
22
 *       for use in the pfSense software distribution. (http://www.pfsense.org/). 
23
 *
24
 *  4. The names "pfSense" and "pfSense Project" must not be used to
25
 *       endorse or promote products derived from this software without
26
 *       prior written permission. For written permission, please contact
27
 *       coreteam@pfsense.org.
28
 *
29
 *  5. Products derived from this software may not be called "pfSense"
30
 *      nor may "pfSense" appear in their names without prior written
31
 *      permission of the Electric Sheep Fencing, LLC.
32
 *
33
 *  6. Redistributions of any form whatsoever must retain the following
34
 *      acknowledgment:
35
 *
36
 *  "This product includes software developed by the pfSense Project
37
 *  for use in the pfSense software distribution (http://www.pfsense.org/).
38
  *
39
 *  THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
40
 *  EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42
 *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
43
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48
 *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
51
 *
52
 *  ====================================================================
53
 *
54
 */
55

    
56
/*
57
	pfSense_BUILDER_BINARIES:	/bin/ps /usr/bin/grep	/usr/sbin/tcpdump
58
	pfSense_MODULE: routing
59
*/
60

    
61
##|+PRIV
62
##|*IDENT=page-diagnostics-packetcapture
63
##|*NAME=Diagnostics: Packet Capture page
64
##|*DESCR=Allow access to the 'Diagnostics: Packet Capture' page.
65
##|*MATCH=diag_packet_capture.php*
66
##|-PRIV
67

    
68
$allowautocomplete = true;
69

    
70
function fixup_host_logic($value) {
71
	return str_replace(array(" ", ",", "+", "|", "!"), array("", "and ", "and ", "or ", "not "), $value);
72
}
73

    
74
function strip_host_logic($value) {
75
	return str_replace(array(" ", ",", "+", "|", "!"), array("", "", "", "", ""), $value);
76
}
77

    
78
function get_host_boolean($value, $host) {
79
	$value = str_replace(array("!", $host), array("", ""), $value);
80
	$andor = "";
81
	switch (trim($value)) {
82
		case "|":
83
			$andor = "or ";
84
			break;
85
		case ",":
86
		case "+":
87
			$andor = "and ";
88
			break;
89
	}
90

    
91
	return $andor;
92
}
93

    
94
function has_not($value) {
95
	return strpos($value, '!') !== false;
96
}
97

    
98
function fixup_not($value) {
99
	return str_replace("!", "not ", $value);
100
}
101

    
102
function strip_not($value) {
103
	return ltrim(trim($value), '!');
104
}
105

    
106
function fixup_host($value, $position) {
107
	$host = strip_host_logic($value);
108
	$not = has_not($value) ? "not " : "";
109
	$andor = ($position > 0) ? get_host_boolean($value, $host) : "";
110
	if (is_ipaddr($host)) {
111
		return "{$andor}host {$not}" . $host;
112
	} elseif (is_subnet($host)) {
113
		return "{$andor}net {$not}" . $host;
114
	} else {
115
		return "";
116
	}
117
}
118

    
119
if ($_POST['downloadbtn'] == gettext("Download Capture")) {
120
	$nocsrf = true;
121
}
122

    
123
$pgtitle = array(gettext("Diagnostics"), gettext("Packet Capture"));
124
require_once("guiconfig.inc");
125
require_once("pfsense-utils.inc");
126

    
127
$fp = "/root/";
128
$fn = "packetcapture.cap";
129
$snaplen = 0;//default packet length
130
$count = 100;//default number of packets to capture
131

    
132
$fams = array('ip', 'ip6');
133
$protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp',
134
		        '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp');
135

    
136
$input_errors = array();
137

    
138
$interfaces = get_configured_interface_with_descr();
139
if (isset($config['ipsec']['enable'])) {
140
	$interfaces['ipsec'] = "IPsec";
141
}
142
foreach (array('server', 'client') as $mode) {
143
	if (is_array($config['openvpn']["openvpn-{$mode}"])) {
144
		foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) {
145
			if (!isset($setting['disable'])) {
146
				$interfaces['ovpn' . substr($mode, 0, 1) . $setting['vpnid']] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']);
147
			}
148
		}
149
	}
150
}
151

    
152
if ($_POST) {
153
	$host = $_POST['host'];
154
	$selectedif = $_POST['interface'];
155
	$count = $_POST['count'];
156
	$snaplen = $_POST['snaplen'];
157
	$port = $_POST['port'];
158
	$detail = $_POST['detail'];
159
	$fam = $_POST['fam'];
160
	$proto = $_POST['proto'];
161

    
162
	if (!array_key_exists($selectedif, $interfaces)) {
163
		$input_errors[] = gettext("Invalid interface.");
164
	}
165

    
166
	if ($fam !== "" && $fam !== "ip" && $fam !== "ip6") {
167
		$input_errors[] = gettext("Invalid address family.");
168
	}
169

    
170
	if ($proto !== "" && !in_array(strip_not($proto), $protos)) {
171
		$input_errors[] = gettext("Invalid protocol.");
172
	}
173

    
174
	if ($host != "") {
175
		$host_string = str_replace(array(" ", "|", ","), array("", "#|", "#+"), $host);
176

    
177
		if (strpos($host_string, '#') === false) {
178
			$hosts = array($host);
179
		} else {
180
			$hosts = explode('#', $host_string);
181
		}
182

    
183
		foreach ($hosts as $h) {
184
			if (!is_subnet(strip_host_logic($h)) && !is_ipaddr(strip_host_logic($h))) {
185
				$input_errors[] = sprintf(gettext("A valid IP address or CIDR block must be specified. [%s]"), $h);
186
			}
187
		}
188
	}
189

    
190
	if ($port != "") {
191
		if (!is_port(strip_not($port))) {
192
			$input_errors[] = gettext("Invalid value specified for port.");
193
		}
194
	}
195

    
196
	if ($snaplen == "") {
197
		$snaplen = 0;
198
	} else {
199
		if (!is_numeric($snaplen) || $snaplen < 0) {
200
			$input_errors[] = gettext("Invalid value specified for packet length.");
201
		}
202
	}
203

    
204
	if ($count == "") {
205
		$count = 0;
206
	} else {
207
		if (!is_numeric($count) || $count < 0) {
208
			$input_errors[] = gettext("Invalid value specified for packet count.");
209
		}
210
	}
211

    
212
	if (!count($input_errors)) {
213
		$do_tcpdump = true;
214

    
215
		conf_mount_rw();
216

    
217
		if ($_POST['promiscuous']) {
218
			//if promiscuous mode is checked
219
			$disablepromiscuous = "";
220
		} else {
221
			//if promiscuous mode is unchecked
222
			$disablepromiscuous = "-p";
223
		}
224

    
225
		if ($_POST['dnsquery']) {
226
			//if dns lookup is checked
227
			$disabledns = "";
228
		} else {
229
			//if dns lookup is unchecked
230
			$disabledns = "-n";
231
		}
232

    
233
		if ($_POST['startbtn'] != "") {
234
			$action = gettext("Start");
235

    
236
			//delete previous packet capture if it exists
237
			if (file_exists($fp.$fn)) {
238
				unlink ($fp.$fn);
239
			}
240

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

    
245
			//explode processes into an array, (delimiter is new line)
246
			$processes_running_array = explode("\n", $processes_running);
247

    
248
			//kill each of the packetcapture processes
249
			foreach ($processes_running_array as $process) {
250
				$process_id_pos = strpos($process, ' ');
251
				$process_id = substr($process, 0, $process_id_pos);
252
				exec("kill $process_id");
253
			}
254

    
255
		} elseif ($_POST['downloadbtn'] != "") {
256
			//download file
257
			$fs = filesize($fp.$fn);
258
			header("Content-Type: application/octet-stream");
259
			header("Content-Disposition: attachment; filename=$fn");
260
			header("Content-Length: $fs");
261
			readfile($fp.$fn);
262
			exit;
263
		}
264
	}
265
} else {
266
	$do_tcpdump = false;
267
}
268

    
269
$protocollist = array(
270
	'' => 'Any',
271
	'icmp' => 'ICMP',
272
	'!icmp' => 'Exclude ICMP',
273
	'icmp6' => 'ICMPv6',
274
	'!icmp6' => 'Exclude ICMPv6',
275
	'tcp' => 'TCP',
276
	'!tcp' => 'Exclude TCP',
277
	'udp' => 'UDP',
278
	'!udp' => 'Exclude UDP',
279
	'arp' => 'ARP',
280
	'!arp' => 'Exclude ARP',
281
	'carp' => 'CARP (VRRP)',
282
	'!carp' => 'Exclude CARP (VRRP)',
283
	'esp' => 'ESP'
284
);
285

    
286
include("head.inc");
287

    
288
if ($input_errors)
289
	print_input_errors($input_errors);
290

    
291
require('classes/Form.class.php');
292

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

    
295
$section = new Form_Section('General Logging Options');
296

    
297
$section->addInput(new Form_Select(
298
	'interface',
299
	'Interface',
300
	$selectedif,
301
	$interfaces
302
))->setHelp('Select the interface on which to capture traffic. ');
303

    
304
$section->addInput(new Form_Checkbox(
305
	'promiscuous',
306
	'Promiscuous',
307
	'Packet capture will be performed using promiscuous mode',
308
	$pconfig['promiscuous']
309
))->setHelp('Note: Some network adapters do not support or work well in promiscuous mode.'. '<br />' .
310
			'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">' .
311
			'Packet capture' . '</a>');
312

    
313
$section->addInput(new Form_Select(
314
	'fam',
315
	'Address Family',
316
	$fam,
317
	array('' => 'Any',
318
		  'ip' => 'IPv4 Only',
319
		  'ip6' => 'IPv6 Only'
320
	)
321
))->setHelp('Select the type of traffic to be captured');
322

    
323
$section->addInput(new Form_Select(
324
	'proto',
325
	'Protocol',
326
	$proto,
327
	$protocollist
328
))->setHelp('Select the protocol to capture, or "Any". ');
329

    
330
$section->addInput(new Form_Input(
331
	'host',
332
	'Host Address',
333
	'text',
334
	$host
335
))->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 />' .
336
			'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". ' .
337
			'Separating with a pipe ("|") performs a boolean "OR".' . '<br />' .
338
			'If you leave this field blank, all packets on the specified interface will be captured.');
339

    
340
$section->addInput(new Form_Input(
341
	'port',
342
	'Port',
343
	'text',
344
	$port
345
))->setHelp('The port can be either the source or destination port. The packet capture will look for this port in either field. ' .
346
			'Leave blank if you do not want to filter by port.');
347

    
348
$section->addInput(new Form_Input(
349
	'snaplen',
350
	'Packet Length',
351
	'text',
352
	$snaplen
353
))->setHelp('The Packet length is the number of bytes of each packet that will be captured. Default value is 0, ' .
354
			'which will capture the entire frame regardless of its size.');
355

    
356
$section->addInput(new Form_Input(
357
	'count',
358
	'Count',
359
	'text',
360
	$count
361
))->setHelp('This is the number of packets the packet capture will grab. Default value is 100.' . '<br />' .
362
			'Enter 0 (zero) for no count limit.');
363

    
364
$section->addInput(new Form_Select(
365
	'detail',
366
	'Level of detail',
367
	$detail,
368
	array('' => 'Any',
369
		  'normal' => 'Normal',
370
		  'medium' => 'Medium',
371
		  'high' => 'High',
372
		  'full' => 'Full',
373
	)
374
))->setHelp('This is the level of detail that will be displayed after hitting "Stop" when the packets have been captured.' . '<br />' .
375
			'This option does not affect the level of detail when downloading the packet capture. ');
376

    
377
$section->addInput(new Form_Checkbox(
378
	'dnsquery',
379
	'Reverse DNS Lookup',
380
	null,
381
	$_POST['dnsquery']
382
))->setHelp('This check box will cause the packet capture to perform a reverse DNS lookup associated with all IP addresses.' . '<br />' .
383
			'This option can cause delays for large packet captures.');
384

    
385
$form->add($section);
386

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

    
390
$processisrunning = ($processcheck != "");
391

    
392
if (($action == gettext("Stop") or $action == "") and $processisrunning != true) {
393
	$form->addGlobal(new Form_Button(
394
		'startbtn',
395
		'Start'
396
	))->removeClass('btn-primary')->addClass('btn-success');
397
}
398
else {
399
	$form->addGlobal(new Form_Button(
400
		'stopbtn',
401
		'Stop'
402
	))->removeClass('btn-primary')->addClass('btn-warning');
403
}
404

    
405
if (file_exists($fp.$fn) and $processisrunning != true) {
406
	$form->addGlobal(new Form_Button(
407
		'viewbtn',
408
		'View Capture'
409
	))->removeClass('btn-primary');
410

    
411
	$form->addGlobal(new Form_Button(
412
		'downloadbtn',
413
		'Download Capture'
414
	))->removeClass('btn-primary');
415

    
416
	$section->addInput(new Form_StaticText(
417
		'Last capture',
418
		date("F jS, Y g:i:s a.", filemtime($fp.$fn))
419
	));
420
}
421

    
422
print($form);
423

    
424
if ($do_tcpdump) :
425
	$matches = array();
426

    
427
	if (in_array($fam, $fams))
428
		$matches[] = $fam;
429

    
430
	if (in_array($proto, $protos))
431
		$matches[] = fixup_not($proto);
432

    
433
	if ($port != "")
434
		$matches[] = "port ".fixup_not($port);
435

    
436
	if ($host != "") {
437
		$hostmatch = "";
438
		$hostcount = 0;
439

    
440
		foreach ($hosts as $h) {
441
			$h = fixup_host($h, $hostcount++);
442

    
443
			if (!empty($h))
444
				$hostmatch .= " " . $h;
445
		}
446

    
447
		if (!empty($hostmatch))
448
			$matches[] = "({$hostmatch})";
449
	}
450

    
451
	if ($count != "0" ) {
452
		$searchcount = "-c " . $count;
453
	} else {
454
		$searchcount = "";
455
	}
456

    
457
	$selectedif = convert_friendly_interface_to_real_interface_name($selectedif);
458

    
459
	if ($action == gettext("Start")) {
460
		$matchstr = implode($matches, " and ");
461

    
462
		print_info_box(gettext('Packet Capture is running'), 'info');
463

    
464
		$cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disablepromiscuous} {$searchcount} -s {$snaplen} -w {$fp}{$fn} " . escapeshellarg($matchstr);
465
		// Debug
466
		//echo $cmd;
467
		mwexec_bg ($cmd);
468
	} else {
469
?>
470

    
471
<div class="panel panel-default">
472
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Packets Captured')?></h2></div>
473
	<div class="panel-body">
474

    
475
<?php
476
		$detail_args = "";
477
		switch ($detail) {
478
			case "full":
479
				$detail_args = "-vv -e";
480
				break;
481
			case "high":
482
				$detail_args = "-vv";
483
				break;
484
			case "medium":
485
				$detail_args = "-v";
486
				break;
487
			case "normal":
488
			default:
489
				$detail_args = "-q";
490
				break;
491
		}
492

    
493
		print('<pre>');
494
		system("/usr/sbin/tcpdump {$disabledns} {$detail_args} -r {$fp}{$fn}");
495
		print('</pre>');
496

    
497
		conf_mount_ro();
498
?>
499
	</div>
500
</div>
501
<?php
502
	}
503
endif;
504

    
505
include("foot.inc");
(29-29/238)