--- a/src/usr/local/www/diag_packet_capture.php +++ b/src/usr/local/www/diag_packet_capture.php @@ -135,6 +135,11 @@ } } +function tcpdump_get_running_process($f){ + $processcheck = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep '/usr/sbin/[t]cpdump.*{$f}'")); + return $processcheck; +} + if ($_POST['downloadbtn'] == gettext("Download Capture")) { $nocsrf = true; } @@ -146,6 +151,7 @@ $fp = "/root/"; $fn = "packetcapture.cap"; +$fe = "packetcapture.error"; $fns = "packetcapture.start"; $snaplen = 0;//default packet length $count = 100;//default number of packets to capture @@ -155,8 +161,6 @@ $protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp', 'pfsync', 'ospf', '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp', '!pfsync', '!ospf'); -$input_errors = array(); - $interfaces = get_configured_interface_with_descr(); if (ipsec_enabled()) { $interfaces['enc0'] = "IPsec"; @@ -175,6 +179,8 @@ $interfaces = array_merge($interfaces, interface_ipsec_vti_list_all()); +$input_errors = array(); +$do_tcpdump = false; if ($_POST) { $host = $_POST['host']; $selectedif = $_POST['interface']; @@ -185,7 +191,12 @@ $detail = $_POST['detail']; $fam = $_POST['fam']; $proto = $_POST['proto']; + $dnsquery = isset($_POST['dnsquery']); + if ($_POST['startbtn'] != "") { + $action = gettext("Start"); + + // check for input errors if (!array_key_exists($selectedif, $interfaces)) { $input_errors[] = gettext("Invalid interface."); } @@ -245,19 +256,18 @@ } } - if ($count == "") { - $count = 0; + if ($count == "") {// default is 100 + $count = 100; } else { if (!is_numeric($count) || $count < 0) { $input_errors[] = gettext("Invalid value specified for packet count."); } } + // process capture options only if no input errors if (!count($input_errors)) { - $do_tcpdump = true; - - if ($_POST['promiscuous']) { + if ($promiscuous) { //if promiscuous mode is checked $disablepromiscuous = ""; } else { @@ -265,7 +275,7 @@ $disablepromiscuous = "-p"; } - if ($_POST['dnsquery']) { + if ($dnsquery) { //if dns lookup is checked $disabledns = ""; } else { @@ -275,12 +285,18 @@ if ($_POST['startbtn'] != "") { $action = gettext("Start"); + $do_tcpdump = true; - //delete previous packet capture if it exists + //Cature will be started, delete previous packet capture if it exists + touch($fp.$fns); unlink_if_exists($fp.$fn); + } + } } elseif ($_POST['stopbtn'] != "") { $action = gettext("Stop"); - $processes_running = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'")); + /* check if nmap scan is already running */ + $processes_running = tcpdump_get_running_process($fn); + $processisrunning = ($processes_running != ""); //explode processes into an array, (delimiter is new line) $processes_running_array = explode("\n", $processes_running); @@ -291,17 +307,19 @@ $process_id = substr($process, 0, $process_id_pos); exec("kill $process_id"); } + } elseif ($_POST['viewbtn'] != "" || $_POST['refreshbtn'] != "") { + $action = gettext("View"); } elseif ($_POST['downloadbtn'] != "") { //download file + $action = gettext("Download"); send_user_download('file', $fp.$fn); } elseif ($_POST['clearbtn'] != "") { + $action = gettext("Delete"); //delete previous packet capture if it exists unlink_if_exists($fp.$fn); + unlink_if_exists($fp.$fe); } - } -} else { - $do_tcpdump = false; -} +} $excl = gettext("Exclude"); @@ -349,23 +367,23 @@ 'Promiscuous', 'Enable promiscuous mode', $promiscuous -))->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 ' . - '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 ' . - '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' . +))->setHelp('Put the interface into promiscuous mode.%1$s' . + '%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 ' . + '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 ' . + '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' . 'Promiscuous mode requires more kernel processing of packets. This puts a slightly higher demand on system resources, especially ' . '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 ' . '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', - '

', - '

', + '
', + '

', + '
', '', '', - '
', ' [1]' . ' [2]', ' [3]', - '

' -); + '

'); $section->addInput(new Form_Select( 'fam', @@ -382,45 +400,62 @@ '*Protocol', $proto, $protocollist -))->setHelp('Select the protocol to capture, or "Any". '); +))->setHelp('Select the protocol to capture, or "Any".'); $section->addInput(new Form_Input( 'host', 'Host Address', 'text', $host -))->setHelp('This value is either the Source or Destination IP address, subnet in CIDR notation, or MAC address.%1$s' . +))->setHelp('Source or Destination IP, subnet or MAC address.%1$s' . + '%2$sThis value is either the Source or Destination IP address, subnet in CIDR notation, or MAC address.%3$s' . '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". ' . - 'Separating with a pipe ("|") performs a boolean "OR".%1$s' . - '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' . - 'If this field is left blank, all packets on the specified interface will be captured.', - '
'); + 'Separating with a pipe ("|") performs a boolean "OR".%3$s' . + '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' . + 'If this field is left blank, all packets on the specified interface will be captured.%4$s', + '
', + '

', + '
', + '

'); + $section->addInput(new Form_Input( 'port', 'Port', 'text', $port -))->setHelp('The port can be either the source or destination port. The packet capture will look for this port in either field. ' . - '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".' . - 'Leave blank if not filtering by port.'); +))->setHelp('Port to match on either the source or destination.%1$s' . + '%2$sThe port can be either the source or destination port. The packet capture will look for this port in either field.%3$s' . + '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". Leave blank if not filtering by port.%4$s', + '
', + '

', + '
', + '

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

', + '
', + '

'); + $section->addInput(new Form_Input( 'count', 'Count', 'text', $count -))->setHelp('This is the number of packets the packet capture will grab. Default value is 100.%s' . - 'Enter 0 (zero) for no count limit.', - '
'); +))->setHelp('Number of packets to capture before exiting (0 for no limit.)'); $section->addInput(new Form_Select( 'detail', @@ -432,52 +467,60 @@ 'full' => gettext('Full'), 'none' => gettext('None'), ) -))->setHelp('This is the level of detail that will be displayed after hitting "Stop" when the packets have been captured.%s' . - 'This option does not affect the level of detail when downloading the packet capture. ', +))->setHelp('Level of detail when viewing captured results.%s' . + 'It does not affect the level of detail when downloading the packet capture.', '
'); $section->addInput(new Form_Checkbox( 'dnsquery', 'Reverse DNS Lookup', 'Do reverse DNS lookup', - $_POST['dnsquery'] -))->setHelp('The packet capture will perform a reverse DNS lookup associated with all IP addresses.%s' . - 'This option can cause delays for large packet captures.', - '
'); + $dnsquery +))->setHelp('Perform a reverse DNS lookup for all captured IP addresses.%s' . + '%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' . + 'This option can cause delays for large packet captures especially if used to preview results while a scan is running.%4$s', + '
', + '

', + '
', + '

'); + $form->add($section); /* check to see if packet capture tcpdump is already running */ -$processcheck = (trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'"))); +$processes_running = tcpdump_get_running_process($fn); +$processisrunning = ($processes_running != ""); -$processisrunning = ($processcheck != ""); - -if (($action == gettext("Stop") or $action == "") and $processisrunning != true) { +if ($processisrunning or $do_tcpdump) { $form->addGlobal(new Form_Button( - 'startbtn', - 'Start', - null, - 'fa-play-circle' - ))->addClass('btn-success'); -} else { - $form->addGlobal(new Form_Button( 'stopbtn', 'Stop', null, 'fa-stop-circle' ))->addClass('btn-warning'); - if ($action == gettext("Start")) { - touch("/root/packetcapture.start"); - } + + $form->addGlobal(new Form_Button( + 'refreshbtn', + 'Refresh Results', + null, + 'fa-retweet' + ))->addClass('btn-primary'); + if (file_exists($fp.$fns)) { $section->addInput(new Form_StaticText( - 'Last capture start', + 'Capture started on', date("F jS, Y g:i:s a.", filemtime($fp.$fns)) )); } -} +} else { + $form->addGlobal(new Form_Button( + 'startbtn', + 'Start', + null, + 'fa-play-circle' + ))->addClass('btn-success'); -if (file_exists($fp.$fn) and $processisrunning != true) { + if (file_exists($fp.$fn) or file_exists($fp.$fe)) { $form->addGlobal(new Form_Button( 'viewbtn', 'View Capture', @@ -498,22 +541,19 @@ null, 'fa-trash' ))->addClass('btn-danger'); + } +} - if (file_exists($fp.$fns)) { +if (file_exists($fp.$fn)) { $section->addInput(new Form_StaticText( - 'Last capture start', - date("F jS, Y g:i:s a.", filemtime($fp.$fns)) - )); - } - $section->addInput(new Form_StaticText( - 'Last capture stop', + 'Last Capture Results', date("F jS, Y g:i:s a.", filemtime($fp.$fn)) )); } print($form); -if ($do_tcpdump) : +if ($do_tcpdump) { $matches = array(); if (in_array($fam, $fams)) { @@ -553,57 +593,92 @@ $selectedif = convert_friendly_interface_to_real_interface_name($selectedif); - if ($action == gettext("Start")) { $matchstr = implode(" and ", $matches); - $cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disablepromiscuous} {$searchcount} -s {$snaplen} -w {$fp}{$fn} " . escapeshellarg($matchstr); + if (strlen($matchstr) !== 0) $matchstr = escapeshellarg($matchstr); + + $cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disabledns} {$disablepromiscuous} {$searchcount} -s {$snaplen} -U -w {$fp}{$fn} {$matchstr} >/dev/null 2>{$fp}{$fe} &"; print_info_box(gettext('Packet capture is running'), 'info'); ?>
0) { +?> + +
+

+
+
+'); + if (filesize($fp.$fe) > $max_display_size) { + print(gettext("Packet Capture summary file is too large to display in the GUI.") . + "\n" . + gettext("Download the file, or view it in the console or ssh shell.") . + "\n" . + gettext("Summary file: {$fp}{$fe}")); } else { + print(file_get_contents($fp.$fe)); + } + print(''); + ?> +
+
+
+

'); if (file_exists($fp.$fn) && (filesize($fp.$fn) > $max_display_size)) { print(gettext("Packet capture file is too large to display in the GUI.") . "\n" . - gettext("Download the file, or view it in the console or ssh shell.")); - } elseif (!file_exists($fp.$fn)) { - print(gettext("No capture file to display.")); - } elseif ($detail == 'none') { + gettext("Download the file, or view it in the console or ssh shell.") . + "\n" . + gettext("Capture file: {$fp}{$fn}")); + } elseif (!file_exists($fp.$fn) || (filesize($fp.$fn) === 0)) { + print(gettext("No capture results to display.")); + } elseif ($detail === 'none') { print(gettext("Select a detail level to view the contents of the packet capture.")); } else { - system("/usr/sbin/tcpdump {$disabledns} {$detail_args} {$iscarp} -r {$fp}{$fn}"); + system("/usr/sbin/tcpdump {$options_args} {$iscarp} -r {$fp}{$fn}"); } print(''); @@ -612,7 +687,6 @@