验证 IP 地址(IPv4 和 IPv6)

验证 IP 地址(IPv4 和 IPv6)

很简单,我想验证 Bash 脚本中的字符串是否代表有效的 IP 地址。我想像许多其他人一样,我也陷入了尝试使用正则表达式来做到这一点的陷阱;虽然这对于 IPv4 来说已经足够好了,但 IPv6 更复杂(因为它支持零压缩),而且它只是一个复杂且不是特别可读的解决方案。

然而,Unix 和 Linux 清楚地理解 IP 地址,所以我想知道是否有更好的方法可以在 bash 中验证 IP 地址?最好是可以用来执行此操作的相当通用的工具。

我想避免使用其他语言(例如 Python)来执行此操作,因为我实际上希望替换当前使用 PHPfilter_var函数的解决方案。它有效,但与正则表达式一样,它引入了第二种语言,我希望避免使用一种语言。

最后,如果有一个解决方案还可以处理 IP 地址范围(例如0.0.0.0-255.255.255.2550.0.0.0/16等),那将是理想的选择,但我可以自己处理这些。

答案1

使用 ip 命令怎么样?

ip route get <ip-address>

如果无效,则返回值为 1。

(2022/9/17) 为了回答@MaXi32的评论,我在没有互联网连接的情况下在我的Linux笔记本电脑上快速检查了这一点。当网络关闭时,ip 命令返回 1 表示无效 IP,返回 2 表示有效 IP。

对于有效的 IP:

$ ip route get 192.168.0.255 ; echo $?
RTNETLINK answers: Network is unreachable
2
$ ip route get fd00::192.168.0.255 ; echo $?
RTNETLINK answers: Network is unreachable
2

对于无效 IP:

$ ip route get 192.168.0.256 ; echo $?
Error: any valid prefix is expected rather than "192.168.0.256".
1
$ ip route get fd00::192.168.0.256 ; echo $?
Error: any valid prefix is expected rather than "fd00::192.168.0.256".
1

ip命令版本:

$ ip -V
ip utility, iproute2-5.9.0, libbpf 0.3.0

(2022/9/17#2) 可以使用inet_pton编写一个小程序。我认为这样更好。以下代码来自示例inet_pton 联机帮助页尝试将 IP 从文本转换为二进制,如果 IP 无效,则会引发错误:

   #include <arpa/inet.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <string.h>

   int
   main(int argc, char *argv[])
   {
       unsigned char buf[sizeof(struct in6_addr)];
       int domain, s;
       char str[INET6_ADDRSTRLEN];

       if (argc != 3) {
           fprintf(stderr, "Usage: %s {i4|i6|<num>} string\n", argv[0]);
           exit(EXIT_FAILURE);
       }

       domain = (strcmp(argv[1], "i4") == 0) ? AF_INET :
                (strcmp(argv[1], "i6") == 0) ? AF_INET6 : atoi(argv[1]);

       s = inet_pton(domain, argv[2], buf);
       if (s <= 0) {
           if (s == 0)
               fprintf(stderr, "Not in presentation format");
           else
               perror("inet_pton");
           exit(EXIT_FAILURE);
       }

       if (inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL) {
           perror("inet_ntop");
           exit(EXIT_FAILURE);
       }

       printf("%s\n", str);

       exit(EXIT_SUCCESS);
   }

答案2

bash可能缺乏对相关inet_pton(3)系统调用的直接访问,因此您可能需要调用一些可以执行的操作,例如sipcalc(应该位于各种 UNIX 的端口或包树中)。更高级的语言可能也可以得到调用inet_pton(3),或等效的,但细节将是特定于语言的,例如我使用过NetAddr::IPPerl 中与子网相关的代码。

答案3

根据 @ktaka 的回答,我不建议使用它ip route get来验证 IP 地址,除非您的脚本严重依赖于网络连接。原因是,如果您的系统没有良好的互联网连接或完全断开与互联网的连接,那么当您使用 时ip route get,您的脚本将始终返回错误代码(0 除外),即使 IP 定义正确。

解决方案:

我查找了 @Rolf Rander 给出的手动链接,我很困惑为什么ipcalcDebian 11 上的二进制文件没有该-c选项,而且我以前的ipcalc二进制文件也不支持ipv6.因此,我在 Google 上搜索了使用此关键字从手册页中获取的作者用户名dcantrell ipcalc,并在此处获得了官方链接:

https://gitlab.com/ipcalc/ipcalc

所以上面是官方ipcalc同时支持IPv4和IPv6的IP地址。要安装它,他们建议使用带介子的现代构建(但如果您不想使用介子,他们也提供旧的 make 构建)

所以,这就是我的设置ipcalc方式Debian 11

删除现有的ipcalc(现有版本不好)

apt-get -y remove ipcalc
rm -rf "/usr/bin/ipcalc"

现在从官方仓库安装 ipcalc:

apt -y install meson
git clone https://gitlab.com/ipcalc/ipcalc.git
cd ipcalc
meson setup build --buildtype=release
ninja -C build
cp build/ipcalc /usr/bin
apt -y remove meson

ipcalc --version

1.0.1

现在您可以使用它ipcalc来检查 IPv4 和 IPv6:

#!/bin/bash
IP="1.1.1.1"
if ipcalc -s -c $IP; then
  echo "valid IPv4 or IPv6"
else
  echo "Invalid IPv4 or Ipv6"
fi

-s忽略错误信息,-c是检查而不输出

如果您想检查特定的 IPv4,请使用-4或使用-6检查 IPv6。例子:

#!/bin/bash

# IP is a valid IPv6
IP="::1"

# But we want to check only IPv4

if ipcalc -s -4 -c $IP; then
  echo "valid IPv4"
else
  echo "Invalid IPv4"
fi

输出:

Invalid IPv4

我很久以前就一直在使用sipcalcand ,它们可以同时进行 IPv4 和 IPv6 检查,但我确实更喜欢它们,因为大多数输出​​都很简单,并且它具有用于其他目的的 json 输出。ipv6calcipcalc

编辑

(2022 年 10 月 23 日):

根据 @ktaka 的最新评论,我的主要观点是,在ip route get没有互联网连接的情况下使用时,如下所示:

$ ip route get 192.168.0.255 ; echo $?
RTNETLINK answers: Network is unreachable
2

如果您的系统位于防火墙后面或者连接状况不佳,它不会返回有效的 IP。 IP是有效的,应该返回成功码0,而不是告诉网络不可达等不必要的信息。

答案4

这可以解决 ipv4 的问题

# check IPv4 syntax
if [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
    echo 'Valid IP'
else
    echo "Invalid IP: $ip" >&2
    exit 1
fi

我正在将 ipv6 实现到我从中提取的脚本中,一旦完成,我将更新这篇文章。

相关内容