Project

General

Profile

Feature #13017 » diag_packet_capture-v6-master.patch

Phil Wardt, 04/09/2022 11:07 AM

View differences:

src/usr/local/www/diag_packet_capture.php
135 135
	}
136 136
}
137 137

  
138
function tcpdump_get_running_process($f){
139
	$processcheck = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep '/usr/sbin/[t]cpdump.*{$f}'"));
140
	return $processcheck;
141
}
142

  
138 143
if ($_POST['downloadbtn'] == gettext("Download Capture")) {
139 144
	$nocsrf = true;
140 145
}
......
146 151

  
147 152
$fp = "/root/";
148 153
$fn = "packetcapture.cap";
154
$fe = "packetcapture.error";
149 155
$fns = "packetcapture.start";
150 156
$snaplen = 0;//default packet length
151 157
$count = 100;//default number of packets to capture
......
155 161
$protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp', 'pfsync', 'ospf',
156 162
		        '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp', '!pfsync', '!ospf');
157 163

  
158
$input_errors = array();
159

  
160 164
$interfaces = get_configured_interface_with_descr();
161 165
if (ipsec_enabled()) {
162 166
	$interfaces['enc0'] = "IPsec";
......
175 179

  
176 180
$interfaces = array_merge($interfaces, interface_ipsec_vti_list_all());
177 181

  
182
$input_errors = array();
183
$do_tcpdump = false;
178 184
if ($_POST) {
179 185
	$host = $_POST['host'];
180 186
	$selectedif = $_POST['interface'];
......
185 191
	$detail = $_POST['detail'];
186 192
	$fam = $_POST['fam'];
187 193
	$proto = $_POST['proto'];
194
	$dnsquery = isset($_POST['dnsquery']);
188 195

  
196
	if ($_POST['startbtn'] != "") {
197
		$action = gettext("Start");
198

  
199
		// check for input errors
189 200
		if (!array_key_exists($selectedif, $interfaces)) {
190 201
			$input_errors[] = gettext("Invalid interface.");
191 202
		}
......
245 256
		}
246 257
	}
247 258

  
248
	if ($count == "") {
249
		$count = 0;
259
		if ($count == "") {// default is 100
260
			$count = 100;
250 261
		} else {
251 262
			if (!is_numeric($count) || $count < 0) {
252 263
				$input_errors[] = gettext("Invalid value specified for packet count.");
253 264
			}
254 265
		}
255 266

  
267
		// process capture options only if no input errors
256 268
	if (!count($input_errors)) {
257
		$do_tcpdump = true;
258 269

  
259

  
260
		if ($_POST['promiscuous']) {
270
			if ($promiscuous) {
261 271
				//if promiscuous mode is checked
262 272
				$disablepromiscuous = "";
263 273
			} else {
......
265 275
			$disablepromiscuous = "-p";
266 276
		}
267 277

  
268
		if ($_POST['dnsquery']) {
278
			if ($dnsquery) {
269 279
				//if dns lookup is checked
270 280
				$disabledns = "";
271 281
			} else {
......
275 285

  
276 286
			if ($_POST['startbtn'] != "") {
277 287
				$action = gettext("Start");
288
				$do_tcpdump = true;
278 289

  
279
			//delete previous packet capture if it exists
290
				//Cature will be started, delete previous packet capture if it exists
291
				touch($fp.$fns);
280 292
				unlink_if_exists($fp.$fn);
293
			}
294
		}
281 295
		} elseif ($_POST['stopbtn'] != "") {
282 296
			$action = gettext("Stop");
283
			$processes_running = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'"));
297
		/* check if nmap scan is already running */
298
		$processes_running = tcpdump_get_running_process($fn);
299
		$processisrunning = ($processes_running != "");
284 300

  
285 301
		//explode processes into an array, (delimiter is new line)
286 302
		$processes_running_array = explode("\n", $processes_running);
......
291 307
			$process_id = substr($process, 0, $process_id_pos);
292 308
			exec("kill $process_id");
293 309
		}
310
	} elseif ($_POST['viewbtn'] != "" || $_POST['refreshbtn'] != "") {
311
		$action = gettext("View");
294 312
	} elseif ($_POST['downloadbtn'] != "") {
295 313
		//download file
314
		$action = gettext("Download");
296 315
		send_user_download('file', $fp.$fn);
297 316
	} elseif ($_POST['clearbtn'] != "") {
317
		$action = gettext("Delete");
298 318
		//delete previous packet capture if it exists
299 319
		unlink_if_exists($fp.$fn);
320
		unlink_if_exists($fp.$fe);
300 321
		}
301
	}
302
} else {
303
	$do_tcpdump = false;
304
}
322
}
305 323

  
306 324
$excl = gettext("Exclude");
307 325

  
......
349 367
	'Promiscuous',
350 368
	'Enable promiscuous mode',
351 369
	$promiscuous
352
))->setHelp('%1$sNon-promiscuous mode captures only traffic that is directly relevant to the host (sent by it, sent or broadcast to it, or routed through it) and ' .
353
	'does not show packets that are ignored at network adapter level.%2$s%3$sPromiscuous mode%4$s ("sniffing") captures all data seen by the adapter, whether ' .
354
	'or not it is valid or related to the host, but in some cases may have undesirable side effects and not all adapters support this option. Click Info for details %5$s' .
370
))->setHelp('Put the interface into promiscuous mode.%1$s' .
371
	'%2$sNon-promiscuous mode captures only traffic that is directly relevant to the host (sent by it, sent or broadcast to it, or routed through it) and ' .
372
	'does not show packets that are ignored at network adapter level.%3$s%4$sPromiscuous mode%5$s ("sniffing") captures all data seen by the adapter, whether ' .
373
	'or not it is valid or related to the host, but in some cases may have undesirable side effects and not all adapters support this option.%3$s' .
355 374
	'Promiscuous mode requires more kernel processing of packets. This puts a slightly higher demand on system resources, especially ' .
356 375
	'on very busy networks or low power processors. The change in packet processing may allow a hostile host to detect that an adapter is in promiscuous mode ' .
357 376
	'or to \'fingerprint\' the kernel (see %6$s). Some network adapters may not support or work well in promiscuous mode (see %7$s).%8$s',
358 377

  
359
	'<p style="margin-bottom:2px;padding-bottom:0px">',
360
	'</p><p style="margin:0px;padding:0px">',
378
	'<span class="infoblock" style="font-size:90%"><br />',
379
	'<p style="margin:0px;padding:0px">',
380
	'<br />',
361 381
	'<a href="https://en.wikipedia.org/wiki/Promiscuous_mode">',
362 382
	'</a>',
363
	'<span class="infoblock" style="font-size:90%"><br />',
364 383
	'&nbsp;<a target="_blank" href="https://security.stackexchange.com/questions/3630/how-to-find-out-that-a-nic-is-in-promiscuous-mode-on-a-lan">[1]</a>' .
365 384
		'&nbsp;<a href="https://nmap.org/nsedoc/scripts/sniffer-detect.html">[2]</a>',
366 385
	'&nbsp;<a target="_blank" href="https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+12.2-RELEASE+and+Ports&arch=default&format=html">[3]</a>',
367
	'</span></p>'
368
);
386
	'</p></span>');
369 387

  
370 388
$section->addInput(new Form_Select(
371 389
	'fam',
......
382 400
	'*Protocol',
383 401
	$proto,
384 402
	$protocollist
385
))->setHelp('Select the protocol to capture, or "Any". ');
403
))->setHelp('Select the protocol to capture, or "Any".');
386 404

  
387 405
$section->addInput(new Form_Input(
388 406
	'host',
389 407
	'Host Address',
390 408
	'text',
391 409
	$host
392
))->setHelp('This value is either the Source or Destination IP address, subnet in CIDR notation, or MAC address.%1$s' .
410
))->setHelp('Source or Destination IP, subnet or MAC address.%1$s' .
411
			'%2$sThis value is either the Source or Destination IP address, subnet in CIDR notation, or MAC address.%3$s' .
393 412
			'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". ' .
394
			'Separating with a pipe ("|") performs a boolean "OR".%1$s' .
395
			'MAC addresses must be entered in colon-separated format, such as xx:xx:xx:xx:xx:xx or a partial address consisting of one (xx), two (xx:xx), or four (xx:xx:xx:xx) segments.%1$s' .
396
			'If this field is left blank, all packets on the specified interface will be captured.',
397
			'<br />');
413
			'Separating with a pipe ("|") performs a boolean "OR".%3$s' .
414
			'MAC addresses must be entered in colon-separated format, such as xx:xx:xx:xx:xx:xx or a partial address consisting of one (xx), two (xx:xx), or four (xx:xx:xx:xx) segments.%3$s' .
415
			'If this field is left blank, all packets on the specified interface will be captured.%4$s',
398 416

  
417
			'<span class="infoblock" style="font-size:90%"><br />',
418
			'<p style="margin:0px;padding:0px">',
419
			'<br />',
420
			'</p></span>');
421

  
399 422
$section->addInput(new Form_Input(
400 423
	'port',
401 424
	'Port',
402 425
	'text',
403 426
	$port
404
))->setHelp('The port can be either the source or destination port. The packet capture will look for this port in either field. ' .
405
			'Matching can be negated by preceding the value with "!". Multiple ports may be specified. Comma (",") separated values perform a boolean "AND". Separating with a pipe ("|") performs a boolean "OR".' .
406
			'Leave blank if not filtering by port.');
427
))->setHelp('Port to match on either the source or destination.%1$s' .
428
			'%2$sThe port can be either the source or destination port. The packet capture will look for this port in either field.%3$s' .
429
			'Matching can be negated by preceding the value with "!". Multiple ports may be specified. Comma (",") separated values perform a boolean "AND". ' .
430
			'Separating with a pipe ("|") performs a boolean "OR". Leave blank if not filtering by port.%4$s',
407 431

  
432
			'<span class="infoblock" style="font-size:90%"><br />',
433
			'<p style="margin:0px;padding:0px">',
434
			'<br />',
435
			'</p></span>');
436

  
408 437
$section->addInput(new Form_Input(
409 438
	'snaplen',
410 439
	'Packet Length',
411 440
	'text',
412 441
	$snaplen
413
))->setHelp('The Packet length is the number of bytes of each packet that will be captured. Default value is 0, ' .
414
			'which will capture the entire frame regardless of its size.');
442
))->setHelp('Number of bytes to capture for each packet.%1$s' .
443
			'%2$sDefault value is 0, which will set capture size to 262144 bytes.%3$s' .
444
			'Note that taking larger snapshots both  increases the amount of time it takes to process packets and, effectively, ' .
445
			'decreases the amount of packet buffering. This may cause packets to be lost.%3$s' .
446
			'You should limit snaplen to the smallest number that will capture the protocol information you are interested in.%4$s',
415 447

  
448
			'<span class="infoblock" style="font-size:90%"><br />',
449
			'<p style="margin:0px;padding:0px">',
450
			'<br />',
451
			'</p></span>');
452

  
416 453
$section->addInput(new Form_Input(
417 454
	'count',
418 455
	'Count',
419 456
	'text',
420 457
	$count
421
))->setHelp('This is the number of packets the packet capture will grab. Default value is 100.%s' .
422
			'Enter 0 (zero) for no count limit.',
423
			'<br />');
458
))->setHelp('Number of packets to capture before exiting (0 for no limit.)');
424 459

  
425 460
$section->addInput(new Form_Select(
426 461
	'detail',
......
432 467
		  'full' => gettext('Full'),
433 468
		  'none' => gettext('None'),
434 469
	)
435
))->setHelp('This is the level of detail that will be displayed after hitting "Stop" when the packets have been captured.%s' .
436
			'This option does not affect the level of detail when downloading the packet capture. ',
470
))->setHelp('Level of detail when viewing captured results.%s' .
471
			'It does not affect the level of detail when downloading the packet capture.',
437 472
			'<br />');
438 473

  
439 474
$section->addInput(new Form_Checkbox(
440 475
	'dnsquery',
441 476
	'Reverse DNS Lookup',
442 477
	'Do reverse DNS lookup',
443
	$_POST['dnsquery']
444
))->setHelp('The packet capture will perform a reverse DNS lookup associated with all IP addresses.%s' .
445
			'This option can cause delays for large packet captures.',
446
			'<br />');
478
	$dnsquery
479
))->setHelp('Perform a reverse DNS lookup for all captured IP addresses.%s' .
480
			'%2$sThe packet capture will perform a reverse DNS lookup associated with all IP addresses. It will also convert port numbers, protocols, etc. to names.%3$s' .
481
			'This option can cause delays for large packet captures especially if used to preview results while a scan is running.%4$s',
447 482

  
483
			'<span class="infoblock" style="font-size:90%"><br />',
484
			'<p style="margin:0px;padding:0px">',
485
			'<br />',
486
			'</p></span>');
487

  
448 488
$form->add($section);
449 489

  
450 490
/* check to see if packet capture tcpdump is already running */
451
$processcheck = (trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'")));
491
$processes_running = tcpdump_get_running_process($fn);
492
$processisrunning = ($processes_running != "");
452 493

  
453
$processisrunning = ($processcheck != "");
454

  
455
if (($action == gettext("Stop") or $action == "") and $processisrunning != true) {
494
if ($processisrunning or $do_tcpdump) {
456 495
	$form->addGlobal(new Form_Button(
457
		'startbtn',
458
		'Start',
459
		null,
460
		'fa-play-circle'
461
	))->addClass('btn-success');
462
} else {
463
	$form->addGlobal(new Form_Button(
464 496
		'stopbtn',
465 497
		'Stop',
466 498
		null,
467 499
		'fa-stop-circle'
468 500
	))->addClass('btn-warning');
469
	if ($action == gettext("Start")) {
470
		touch("/root/packetcapture.start");
471
	}
501

  
502
	$form->addGlobal(new Form_Button(
503
		'refreshbtn',
504
		'Refresh Results',
505
		null,
506
		'fa-retweet'
507
	))->addClass('btn-primary');
508

  
472 509
	if (file_exists($fp.$fns)) {
473 510
		$section->addInput(new Form_StaticText(
474
			'Last capture start',
511
			'Capture started on',
475 512
			date("F jS, Y g:i:s a.", filemtime($fp.$fns))
476 513
		));
477 514
	}
478
}
515
} else {
516
	$form->addGlobal(new Form_Button(
517
		'startbtn',
518
		'Start',
519
		null,
520
		'fa-play-circle'
521
	))->addClass('btn-success');
479 522

  
480
if (file_exists($fp.$fn) and $processisrunning != true) {
523
	if (file_exists($fp.$fn) or file_exists($fp.$fe)) {
481 524
		$form->addGlobal(new Form_Button(
482 525
			'viewbtn',
483 526
			'View Capture',
......
498 541
			null,
499 542
			'fa-trash'
500 543
		))->addClass('btn-danger');
544
	}
545
}
501 546

  
502
	if (file_exists($fp.$fns)) {
547
if (file_exists($fp.$fn)) {
503 548
		$section->addInput(new Form_StaticText(
504
			'Last capture start',
505
			date("F jS, Y g:i:s a.", filemtime($fp.$fns))
506
		));
507
	}
508
	$section->addInput(new Form_StaticText(
509
		'Last capture stop',
549
		'Last Capture Results',
510 550
		date("F jS, Y g:i:s a.", filemtime($fp.$fn))
511 551
	));
512 552
}
513 553

  
514 554
print($form);
515 555

  
516
if ($do_tcpdump) :
556
if ($do_tcpdump) {
517 557
	$matches = array();
518 558

  
519 559
	if (in_array($fam, $fams)) {
......
553 593

  
554 594
	$selectedif = convert_friendly_interface_to_real_interface_name($selectedif);
555 595

  
556
	if ($action == gettext("Start")) {
557 596
		$matchstr = implode(" and ", $matches);
558
		$cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disablepromiscuous} {$searchcount} -s {$snaplen} -w {$fp}{$fn} " . escapeshellarg($matchstr);
597
	if (strlen($matchstr) !== 0) $matchstr = escapeshellarg($matchstr);
598

  
599
	$cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disabledns} {$disablepromiscuous} {$searchcount} -s {$snaplen} -U -w {$fp}{$fn} {$matchstr} >/dev/null 2>{$fp}{$fe} &";
559 600
		print_info_box(gettext('Packet capture is running'), 'info');
560 601
		?>
561 602
		<div class="infoblock">
562 603
		<? print_info_box(gettext('Command line') . ': ' . htmlspecialchars($cmd), 'info', false); ?>
563 604
		</div>
564 605
		<?php
565
		mwexec_bg ($cmd);
606
	exec($cmd);
607
} elseif ($action == gettext("View") || $action == gettext("Stop")) {
608
		if (file_exists($fp.$fe) && filesize($fp.$fe) > 0) {
609
?>
610

  
611
<div class="panel panel-default">
612
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Capture Summary')?></h2></div>
613
	<div class="panel-body">
614
		<div class="form-group">
615
<?php
616

  
617
			print('<textarea class="form-control" rows="10" style="font-size: 13px; font-family: consolas,monaco,roboto mono,liberation mono,courier;">');
618
			if (filesize($fp.$fe) > $max_display_size) {
619
				print(gettext("Packet Capture summary file is too large to display in the GUI.") .
620
					"\n" .
621
					gettext("Download the file, or view it in the console or ssh shell.") .
622
					"\n" .
623
					gettext("Summary file: {$fp}{$fe}"));
566 624
			} else {
625
				print(file_get_contents($fp.$fe));
626
			}
627
			print('</textarea>');
628

  
567 629
?>
630
		</div>
631
	</div>
632
</div>
633
<?php
634
		}
635
?>
568 636

  
569 637
<div class="panel panel-default">
570 638
	<div class="panel-heading"><h2 class="panel-title"><?=gettext('Packets Captured')?></h2></div>
571 639
	<div class="panel-body">
572 640
		<div class="form-group">
573 641
<?php
574
		if ($_POST['proto'] == "carp") {
642
		if ($proto == "carp") {
575 643
			$iscarp = "-T carp";
576 644
		} else {
577 645
			$iscarp = "";
578 646
		}
579
		$detail_args = "";
647

  
648
		$options_args = "";
580 649
		switch ($detail) {
581 650
			case "full":
582
				$detail_args = "-vv -e";
651
				$options_args = "-vv -e";
583 652
				break;
584 653
			case "high":
585
				$detail_args = "-vv";
654
				$options_args = "-vv";
586 655
				break;
587 656
			case "medium":
588
				$detail_args = "-v";
657
				$options_args = "-v";
589 658
				break;
590 659
			case "normal":
591 660
			default:
592
				$detail_args = "-q";
661
				$options_args = "-q";
593 662
				break;
594 663
		}
595 664

  
665
        if ($dnsquery !== true) {
666
            $options_args .= " -n";
667
        }
668

  
596 669
		print('<textarea class="form-control" rows="20" style="font-size: 13px; font-family: consolas,monaco,roboto mono,liberation mono,courier;">');
597 670
		if (file_exists($fp.$fn) && (filesize($fp.$fn) > $max_display_size)) {
598 671
			print(gettext("Packet capture file is too large to display in the GUI.") .
599 672
			    "\n" .
600
			    gettext("Download the file, or view it in the console or ssh shell."));
601
		} elseif (!file_exists($fp.$fn)) {
602
			print(gettext("No capture file to display."));
603
		} elseif ($detail == 'none') {
673
				gettext("Download the file, or view it in the console or ssh shell.") .
674
				"\n" .
675
				gettext("Capture file: {$fp}{$fn}"));
676
		} elseif (!file_exists($fp.$fn) || (filesize($fp.$fn) === 0)) {
677
			print(gettext("No capture results to display."));
678
		} elseif ($detail === 'none') {
604 679
			print(gettext("Select a detail level to view the contents of the packet capture."));
605 680
		} else {
606
			system("/usr/sbin/tcpdump {$disabledns} {$detail_args} {$iscarp} -r {$fp}{$fn}");
681
			system("/usr/sbin/tcpdump {$options_args} {$iscarp} -r {$fp}{$fn}");
607 682
		}
608 683
		print('</textarea>');
609 684

  
......
612 687
	</div>
613 688
</div>
614 689
<?php
615
	}
616
endif;
690
}
617 691

  
618 692
include("foot.inc");
(7-7/7)