需要使用 PHP 在 Ubuntu 上查找网络接口的 IP 地址


Need to find IP address of network interface on Ubuntu with PHP

当计算机在网络上时,我需要帮助查找计算机的IP地址。 我正在构建一个将放置在不同位置的 kiosk 类型系统,我需要能够使用 Web 浏览器在本地网络上查找该计算机的 IP 地址。

如果我使用$_SERVER['SERVER_ADDR']我会通过该计算机上的本地浏览器 (127.0.0.1) 获得要连接的 IP 地址。

我无法调用并获取公共 IP,因为这些设备可能位于路由器后面,并且我不想要路由器的公共 IP 地址。

我需要在服务器上找到该框的 IP 地址(例如:192.168.0.xxx)

我确实知道,当我从终端进行"ip addr show"时,我得到

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: em1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 61:a6:4d:63:a2:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.211/24 brd 192.168.0.255 scope global em1
    inet6 fe80::62a4:4cff:fe64:a399/64 scope link 
       valid_lft forever preferred_lft forever

当我尝试时:

$command="ip addr show";
$localIP = exec ($command);

$localIP给出了"永远preferred_lft永远valid_lft",但没有其他信息。 如果我能把整个事情都放到$localIP那么我就可以过滤掉 inet IP 地址,但它不会给我一切。

有没有更简单的方法可以做到这一点,或者在尝试执行"ip addr show"命令时我缺少什么? 另外,我在用户 apache 下运行,无法以此应用程序的 root 身份运行它。

正如exec()所记录的,只有 exec'd 命令的最后一行输出从函数返回。要捕获所有输出,您必须对函数使用可选的第二个参数:

$last_line = exec('ip addr show', $full_output);
                                  ^^^^^^^^^^^^

$full_output将是来自 exec'd 程序的输出行数组。

服务器(应该)知道如何将自己的主机名解析为正确的 IP 地址,即使它没有连接到互联网。

因此,您需要做的就是获取主机名,然后将主机名解析为 IP 地址。像这样的东西应该适合你:

$ip = gethostbyname(gethostname());

无论主机名是在DNS,/etc/hosts文件还是ActiveDirectory中注册的,只要它可以以某种方式解析,这都会得到解析的IP地址。

您可能还需要规划其他选项。因此,请检查上述方法是否有效,如果没有,则需要回退到其他选项:

  • $_SERVER['HTTP_HOST'] 包含键入到浏览器的地址栏,以便访问该页面。如果您访问通过键入(例如)的页面http://192.168.0.1/index.php浏览器,$_SERVER['HTTP_HOST']将被192.168.0.1
  • 如果您使用 DNS 名称访问该页面, gethostbyname($_SERVER['HTTP_HOST']) ;将该 DNS 名称转换为IP地址。
  • $_SERVER['SERVER_NAME']包含已在 中配置的名称Web 服务器配置作为其服务器名称。如果你打算使用这个,你不妨用PHP硬编码它。
  • $_SERVER['SERVER_ADDR']将包含服务器的主 IP 地址。这可能是也可能不是 IP 地址用于访问页面,它可能是也可能不是 IP绑定到它的 Web 服务器的地址。这在很大程度上取决于操作系统和服务器配置。如果服务器具有单个 IP 地址,则为可能是一个安全的选择,尽管在某些情况下这可能会包含127.0.0.1 .

您可以尝试如下操作:

<?php
$ip = gethostbyname(gethostname());
// if we didn't get an IP address
if ($ip === gethostname()){
    // lets see if we can get it from the
    // configured web server name
    $ip = gethostbyname($_SERVER['SERVER_NAME']);
    // if we still don't have an IP address
    if ($ip === $_SERVER['SERVER_NAME']){
        // Then we default to whatever the OS
        // is telling us is it's primary IP address
        $ip = $_SERVER['SERVER_ADDR'];
    }
}

如果你打算在linux中做命令行的事情(危险的,潜在的攻击媒介),你可以做这样的事情:

//then all non 127.0.0.1 ips should be in the $IPS variable
echo exec('/sbin/ifconfig | grep "inet addr:" | grep -v "127.0.0.1" | cut -d: -f2 | awk ''{ print $1}''', $IPS);
$ip = '';
$foundIps = count($IPS);
switch($foundIps){
    case 0:
       print "Crud, we don't have any IP addresses here!'n";
       $ip = '127.0.0.1';
       break;
    case 1:
        print "Perfect, we found a single usable IP address, this must be what we want!'n";
        $ip = $IPS[0];
        break;
    default:
        print "Oh snap, this machine has multiple IP addresses assigned, what you wanna do about that?'n"
        // I'm crazy, I'll just take the second one!
        $ip = $IPS[1];
}

这是一个简单的函数,使用系统/etc/network/interfaces将返回 eth0 和 eth1 的公共和私有 IPv4 地址设置

function getIps()
{
    $ifs_raw=file('/etc/network/interfaces');
    $tocheck=array('eth0','eth1');
    foreach($tocheck as $if)
    {
        foreach($ifs_raw as $idx=>$line)
        {
            if(strpos($line,"iface {$if} inet static")!==false)
            {
                $address=$ifs_raw[$idx+1];
                $address=trim($address);$address=ltrim($address,"'t");$address=rtrim($address,"'t");
                list($address_text,$addrip)=explode(" ",$address);
                if($address_text=='address')
                {
                    if(filter_var($addrip, FILTER_VALIDATE_IP) !== false)
                    {
                        //Valid IP
                        if(false===filter_var($addrip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE |  FILTER_FLAG_NO_RES_RANGE))
                        {
                            $ips['private']=$addrip;
                        }
                        else
                        {
                            $ips['public']=$addrip;
                        }
                    }
                }
            }       
        }
    }
    return $ips;
}

如果你只是想要特定的接口,你可以使用这个

$ip = `ifconfig en1 inet | grep inet | awk '{print $2}'`

输出只是此接口的 IP

你可以使用我写的这个库。 它可以读/写/etc/网络/接口

https://github.com/carp3/networkinterfaces

<?php
//include composer autoloader
include 'vendor/autoload.php';
// 'import' NetworkInterfaces class
use NetworkInterfaces'Adaptor;
use NetworkInterfaces'NetworkInterfaces;
// create new handle from /etc/networking/interfaces
$handle = new NetworkInterfaces('/etc/networking/interfaces');
// parse file
$handle->parse();
// change eth0 ip address
$handle->Adaptors['eth0']->address = '192.168.0.30';
// Write changes to /etc/networking/interfaces
$handle->write();

在UNIX操作系统上查找IP地址的简单命令

ip route | grep src | awk -F 'src' '{print $NF; exit}' | awk '{print $1}'

在RHEL/CentOS/OEL/Ubuntu/Fedora上测试

$_SERVER['REMOTE_ADDR'];应该可以解决问题。

如果这不起作用,您可以随时shell_exec($command);乱。但请记住,您需要设置 php 文件权限才能执行。

debian/ubuntu 中的 Web 界面配置在/etc/network/interfaces 文件中。如果 php cgi 进程没有被 chroot 并看到这个文件,您可以解析它并获取 IP 地址,则可以免费读取您。它很容易阅读。像这样的事情应该可以工作...

$myFile = "/etc/network/interfaces";
$fh = fopen($myFile, 'r');
$theData = "";
$resoult = "";
while(! feof($file)){
 $theData = fgets($fh);
 if($theData == "iface em1 inet static"){
  while(! feof($file)){
   $theData = fgets($fh);
   if(split(" ",$theData)[0] == "address"){
    $resoult = split(" ",$theData)[1];
    brake;
   }
  }
  brake;
 }
}
fclose($fh);
echo $resoult;

好的,我找到了一个解决方案....

Bubba -> 您的解决方案返回了 127.0.0.1 地址。 盒子的域是 demo01.com 的,看起来它正在拉动本地 127 地址。 我尝试使用该计算机上的Web浏览器以及使用实际192.168.x地址的另一台计算机上的浏览器调用php文件,两次它都给了我127.0.0.1地址。

宝琳娜 -> IP 地址由主机路由器动态设置。 由于这是在几个地方进行的,我无法将 IP 硬编码到盒子中。 我不知道硬编码是否真的与此有关,但我确实得到了文件内容,但它们没有 IP 地址: # 此文件描述了系统上可用的网络接口 # 以及如何激活它们。有关更多信息,请参阅接口 (5)。

# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
#auto em1
#iface em1 inet dhcp

Marc B -> 你的解决方案对我有用。 我不知道我需要将第二个变量添加到调用中才能让它显示所有内容。 我可以从那里解析出 IP 地址。 这只是为了显示正在设置信息亭的人,如果由于某种原因有多个地址,我可以拉出每个地址并显示列表,然后他们可以玩命中和未命中,无论如何他们可能只需要担心 2 个 IP(lan 和 wan)

我没有足够的果汁来增加你的反应,如果可以的话,我会的。 但我很感激,谢谢你!

Joren -> 这不符合我的需求,remote_address返回调用脚本的计算机的地址。 如果我从远程计算机调用它,我将获取该计算机的 IP,如果我从浏览器中调用它,我要么必须已经知道盒子的地址才能将其放入地址栏中(我没有,这就是我需要脚本的原因)或从 127.0.0.1 或本地主机调用它,其中我得到 127.0.0.1 作为响应在任何一种情况下。

编辑/etc/sudoers 以允许 www-data 执行任何命令

INI PHP 代码:

    $output = shell_exec("sudo ifconfig");
    echo "<pre>$output</pre>";

如果有多个网络适配器,则按矩阵顺序执行数据通信。

请参阅下面的Raspberry pi或Ubuntu linux代码。

$ifs = exec("netstat -rne | grep -v ' Iface' | grep 'UG ' | awk '{print $5, $8}'");
$interfaces = explode(PHP_EOL, $ifs);
$networks = array();
foreach($interfaces as $eth) {
    list($metric, $ethname) = explode(" ", $eth);
    $ipaddr = exec("ifconfig ${ethname} | grep 'inet ' | cut -d: -f2 | awk '{print $2}'");
    $networks[$metric] = array($ethname=>$ipaddr);
}
ksort($networks);
print_r($networks);

Array
(
    [30] => Array
        (
            [eth0] => 192.168.0.31
        )
)