diff --git a/src/www/diag_packet_capture.php b/src/www/diag_packet_capture.php index 0aef67c04..4488a066b 100644 --- a/src/www/diag_packet_capture.php +++ b/src/www/diag_packet_capture.php @@ -1,494 +1,490 @@ 0) ? get_host_boolean($value, $host) : ""; - if (is_ipaddr($host)) - return "{$andor}host {$not}" . $host; - elseif (is_subnet($host)) - return "{$andor}net {$not}" . $host; - else - return ""; -} - -if ($_POST['downloadbtn'] == gettext("Download Capture")) - $nocsrf = true; - require_once("guiconfig.inc"); require_once("pfsense-utils.inc"); require_once("interfaces.inc"); -$fp = "/root/"; -$fn = "packetcapture.cap"; -$snaplen = 0;//default packet length -$count = 100;//default number of packets to capture +/** + * kill tcp dump process + */ +function stop_capture() +{ + $processes_running = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep packetcapture.cap | /usr/bin/egrep -v '(pflog|grep)'")); + foreach (explode("\n", $processes_running) as $process) { + exec("kill ". explode(' ',$process)[0]); + } +} -$fams = array('ip', 'ip6'); -$protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp', - '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp'); +/** + * start capture operation + * @param array $option, options to pass to tpcdump (interface, promiscuous, snaplen, fam, host, proto, port) + */ +function start_capture($options) +{ + $cmd_opts = array(); + $filter_opts = array(); + $intf = get_real_interface($options['interface']); + $cmd_opts[] = '-i ' . $intf; -$input_errors = array(); + if (empty($options['promiscuous'])) { + // disable promiscuous mode + $cmd_opts[] = '-p'; + } + if (!empty($options['snaplen']) && is_numeric($options['snaplen'])) { + // setup Packet Length + $cmd_opts[] = '-s '. $options['snaplen']; + } + + if (!empty($options['count']) && is_numeric($options['count'])) { + // setup count + $cmd_opts[] = '-c '. $options['count']; + } + + if (!empty($options['fam']) && in_array($options['fam'], array('ip', 'ip6'))) { + // filter address family + $filter_opts[] = $options['fam']; + } + + if (!empty($options['proto'])) { + // filter protocol + $filter_opts[] = $options['proto']; + } + + if (!empty($options['host'])) { + // filter host argument + $filter = ''; + $prev_token = ''; + foreach (explode(' ', $options['host']) as $token) { + if (in_array(trim($token), array('and', 'or'))) { + $filter .= $token; + } elseif (is_ipaddr($token)) { + $filter .= "host " . $prev_token . " " . $token; + } elseif (is_subnet($token)) { + $filter .= "net " . $prev_token . " " . $token; + } + if (trim($token) == 'not') { + $prev_token = 'not'; + } else { + $prev_token = ''; + } + $filter .= " "; + } + + $filter_opts[] = "( ". $filter . " )"; + } + + if (!empty($options['port'])) { + // filter port + $filter_opts[] = "port " . str_replace("!", "not ", $options['port']); + } + + if (!empty($intf)) { + $cmd = '/usr/sbin/tcpdump '; + $cmd .= implode(' ', $cmd_opts); + $cmd .= ' -w /root/packetcapture.cap '; + $cmd .= " ".escapeshellarg(implode(' and ', $filter_opts)); + //delete previous packet capture if it exists + if (file_exists('/root/packetcapture.cap')) { + unlink ('/root/packetcapture.cap'); + } + mwexec_bg($cmd); + } +} + +/** + * check if packetcapture is running + * @return bool + */ +function capture_running() +{ + $processcheck = (trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep packetcapture.cap | /usr/bin/egrep -v '(pflog|grep)'"))); + if (!empty($processcheck)) { + return true; + } else { + return false; + } + +} + +// define selectable interfaces $interfaces = get_configured_interface_with_descr(); - if (isset($config['ipsec']['enable'])) { - $interfaces['ipsec'] = 'IPsec'; + $interfaces['ipsec'] = 'IPsec'; } foreach (array('server', 'client') as $mode) { - if (isset($config['openvpn']["openvpn-{$mode}"])) { - foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) { - if (!isset($setting['disable'])) { - $interfaces['ovpn' . substr($mode, 0, 1) . $setting['vpnid']] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']); - } - } - } + if (isset($config['openvpn']["openvpn-{$mode}"])) { + foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) { + if (!isset($setting['disable'])) { + $interfaces['ovpn' . substr($mode, 0, 1) . $setting['vpnid']] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']); + } + } + } } -if ($_POST) { - $host = $_POST['host']; - $selectedif = $_POST['interface']; - $count = $_POST['count']; - $snaplen = $_POST['snaplen']; - $port = $_POST['port']; - $detail = $_POST['detail']; - $fam = $_POST['fam']; - $proto = $_POST['proto']; - if (!array_key_exists($selectedif, $interfaces)) { - $input_errors[] = gettext("Invalid interface."); - } - if ($fam !== "" && $fam !== "ip" && $fam !== "ip6") { - $input_errors[] = gettext("Invalid address family."); - } - if ($proto !== "" && !in_array(strip_not($proto), $protos)) { - $input_errors[] = gettext("Invalid protocol."); - } +if ($_SERVER['REQUEST_METHOD'] === 'GET') { + if (isset($_GET['download'])) { + // download capture file + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename=packetcapture.cap"); + header("Content-Length: ".filesize("/root/packetcapture.cap")); + readfile("/root/packetcapture.cap"); + exit; + } elseif (!empty($_GET['view'])) { + // download capture contents + if (!empty($_GET['dnsquery'])) { + //if dns lookup is checked + $disabledns = ""; + } else { + //if dns lookup is unchecked + $disabledns = "-n"; + } + $detail_args = ""; + switch (!empty($_GET['detail']) ? $_GET['detail'] : null) { + case "full": + $detail_args = "-vv -e"; + break; + case "high": + $detail_args = "-vv"; + break; + case "medium": + $detail_args = "-v"; + break; + case "normal": + default: + $detail_args = "-q"; + break; + } + $result = array(); + $dump_output = array(); + exec("/usr/sbin/tcpdump {$disabledns} {$detail_args} -r /root/packetcapture.cap", $dump_output); + // reformat raw output to 1 packet per array item + foreach ($dump_output as $line) { + if ($line[0] == ' ' && count($result) > 0) { + $result[count($result)-1] .= "\n" . $line; + } else { + $result[] = $line; + } + } + echo json_encode($result); + exit; + } else { + // set form defaults + $pconfig = array(); + $pconfig['interface'] = "WAN"; + $pconfig['promiscuous'] = null; + $pconfig['fam'] = null; + $pconfig['proto'] = null; + $pconfig['host'] = null; + $pconfig['port'] = null; + $pconfig['snaplen'] = null; + $pconfig['count'] = 100; + } +} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { + $input_errors = array(); + $pconfig = $_POST; - if ($host != "") { - $host_string = str_replace(array(" ", "|", ","), array("", "#|", "#+"), $host); - if (strpos($host_string, '#') === false) { - $hosts = array($host); - } else { - $hosts = explode('#', $host_string); - } - foreach ($hosts as $h) { - if (!is_subnet(strip_host_logic($h)) && !is_ipaddr(strip_host_logic($h))) { - $input_errors[] = sprintf(gettext("A valid IP address or CIDR block must be specified. [%s]"), $h); - } - } - } - if ($port != "") { - if (!is_port(strip_not($port))) { - $input_errors[] = gettext("Invalid value specified for port."); - } - } - if ($snaplen == "") { - $snaplen = 0; - } else { - if (!is_numeric($snaplen) || $snaplen < 0) { - $input_errors[] = gettext("Invalid value specified for packet length."); - } - } - if ($count == "") { - $count = 0; - } else { - if (!is_numeric($count) || $count < 0) { - $input_errors[] = gettext("Invalid value specified for packet count."); - } - } + if (!empty($_POST['start'])) { + if (!array_key_exists($pconfig['interface'], $interfaces)) { + $input_errors[] = gettext("Invalid interface."); + } + if ($pconfig['fam'] !== "" && $pconfig['fam'] !== "ip" && $pconfig['fam'] !== "ip6") { + $input_errors[] = gettext("Invalid address family."); + } + $protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp', + '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp'); + if ($pconfig['proto'] !== "" && !in_array(ltrim(trim($pconfig['proto']), '!'), $protos)) { + $input_errors[] = gettext("Invalid protocol."); + } - if (!count($input_errors)) { - $do_tcpdump = true; - - if ($_POST['promiscuous']) { - //if promiscuous mode is checked - $disablepromiscuous = ""; - } else { - //if promiscuous mode is unchecked - $disablepromiscuous = "-p"; - } - - if ($_POST['dnsquery']) { - //if dns lookup is checked - $disabledns = ""; - } else { - //if dns lookup is unchecked - $disabledns = "-n"; - } - - if ($_POST['startbtn'] != "" ) { - $action = gettext("Start"); - - //delete previous packet capture if it exists - if (file_exists($fp.$fn)) - unlink ($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)'")); - - //explode processes into an array, (delimiter is new line) - $processes_running_array = explode("\n", $processes_running); - - //kill each of the packetcapture processes - foreach ($processes_running_array as $process) { - $process_id_pos = strpos($process, ' '); - $process_id = substr($process, 0, $process_id_pos); - exec("kill $process_id"); - } - - } elseif ($_POST['downloadbtn']!= "") { - //download file - $fs = filesize($fp.$fn); - header("Content-Type: application/octet-stream"); - header("Content-Disposition: attachment; filename=$fn"); - header("Content-Length: $fs"); - readfile($fp.$fn); - exit; - } - } -} else { - $do_tcpdump = false; + if (!empty($pconfig['host'])) { + foreach (explode(' ', $pconfig['host']) as $token) { + if (!in_array(trim($token), array('and', 'or','not')) && !is_ipaddr($token) && !is_subnet($token) ) { + $input_errors[] = sprintf(gettext("A valid IP address or CIDR block must be specified. [%s]"), $token); + } + } + } + if (!empty($pconfig['port']) && !is_port(ltrim(trim($pconfig['port']), 'not'))) { + $input_errors[] = gettext("Invalid value specified for port."); + } + if (!empty($pconfig['snaplen']) && (!is_numeric($pconfig['snaplen']) || $snaplen < 0)) { + $input_errors[] = gettext("Invalid value specified for packet length."); + } + if (!empty($pconfig['count']) && (!is_numeric($pconfig['count']) || $count < 0)) { + $input_errors[] = gettext("Invalid value specified for packet count."); + } + if (count($input_errors) == 0) { + start_capture($pconfig); + } + } elseif (!empty($pconfig['stop'])) { + stop_capture(); + } elseif (!empty($pconfig['remove'])) { + if (file_exists('/root/packetcapture.cap')) { + unlink ('/root/packetcapture.cap'); + } + header("Location: diag_packet_capture.php"); + exit; + } } include("head.inc"); ?> -
+