65.9K
CodeProject 正在变化。 阅读更多。
Home

IPv6 子网划分 - 第 2 部分/2

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (43投票s)

2013年9月30日

BSD

6分钟阅读

viewsIcon

107568

downloadIcon

5766

IPv6 子网计算器/工具详解 (最新版本: 5.0)

C# 截图

Java 截图

 

 

引言

在本文的第二部分,我将尝试解释应用程序中使用的函数,以及如何查找相邻子网的技术。

(请点击链接查看本文第一部分: IPv6 子网划分 第 1 部分/2[^] )

关于本应用程序

首先,检查输入的值,以确定用户输入的是否为有效的 IPv6 地址。为此,函数 IsAddressCorrect() 接收输入的 string,执行必要的检查,并根据检查结果返回 'true''false' 布尔值。

public boolean IsAddressCorrect(String sin) {
    int nDC = 0;
    int nC = 0;

    sin = sin.trim();
    String s = sin;
    char[] chars = s.toCharArray();

    /* 0. Error: Empty */
    if (s.isEmpty()) {
        errmsg = "> Empty address";
        return false;
    }


    /* 1. Error: UNDEFINED '::' */
    if (s.equals("::")) {
        errmsg = "> Undefined";
        return false;
    }
    /* 2. Error: Triple or more colons entered */
    if ((s.length() <= 1) || (s.contains(":::"))) {
        errmsg = "> Not valid";
        return false;
    }
    /* 3. Error: Not valid hex */
    if (!Pattern.matches("^[0-9A-Fa-f:]+$", s)) {
        errmsg = "> Not valid hex-digit";
        return false;
    }
    /* 4. Error: Cannot start or end with ':' */
    if (chars[0] == ':' && chars[1] != ':') {
        errmsg = "> Not valid: ':'";
        return false;
    }
    if (chars[s.length() - 1] == ':' && chars[s.length() - 2] != ':') {
        errmsg = "> Not valid: ':'";
        return false;
    }
    /* 5. Error: More than 2 Bytes */
    String[] sa = s.split(":", -1);
    for (int j = 0; j < sa.length; j++) {
        if (sa[j].length() > 4) {
            errmsg = "> More than 2 bytes";
            return false;
        }
    }
    /* 6. Error: Number of DoubleColons and Colons */
    s = sin;
    nDC = s.split("::", -1).length - 1;
    s = s.replace("::", "**");
    nC = s.split(":", -1).length - 1;

    /* Case I. DoubleColon can only appear once - RFC4291 */
    if (nDC > 1) {
        errmsg = "> DoubleColon can only appear once";
        return false;
    }
    /* Case II. No DoubleColon means there must be 7 colons */
    if (nDC == 0 && nC != 7) {
        errmsg = "> Not valid, there must be 7 colons";
        return false;
    }
    /* Case III. If DoubleColon at start/end, max. colons must be 6 or less */
    s = sin;
    int sL = s.length();
    if ((chars[0] == ':' && chars[1] == ':')
            || (chars[sL - 1] == ':' && chars[sL - 2] == ':')) {
        if (nDC == 1 && nC > 6) {
            errmsg = "> Excessive colons";
            return false;
        }
    } /* Case IV. If DoubleColon in middle, max. colons must be 5 or less
       */ 
    else if (nDC == 1 && nC > 5) {
        errmsg = "> Excessive colons";
        return false;
    }


    /* End of Check */
    errmsg = "> ok";
    return true;
} 

如果输入的值是有效的 IPv6 地址,那么我们需要将其形式化为 16 字节的十六进制值,以便对其进行计算。函数 FormalizeAddress() 接收一个有效的 IPv6 地址作为 string,在地址被压缩的情况下填充必要的零,然后返回一个 16 字节的正式 IPv6 地址,数据类型为 BigInteger

public BigInteger FormalizeAddr(String sin) {
 
    String[] Resv6 = new String[]{"0000", "0000", "0000",
                                  "0000", "0000", "0000",
                                  "0000", "0000"};
 
    BigInteger Finalv6 = BigInteger.ZERO;
    sin = sin.trim();
    String s = sin;
    String[] sa = s.split(":", -1);
    int nC = 0;
    s = s.replace("::", "**");
    nC = s.split(":", -1).length - 1;

    /* Start of Building Result v6 address */
   for (int k = 0; k < sa.length; k++) {
       if (sa[k].length() == 0) {
           continue;
       } else {
            sa[k] = String.format("%4s", sa[k]).replace(' ', '0');
       }
   }

   if ((sa[sa.length - 1].length() == 0) 
       && (sa[sa.length - 2].length() == 0)) {
       int t = nC + 1;
       for (int i = 0; i < t; i++) {
          Resv6[i] = sa[i];
       } 
   } else if (sa[0].length() == 0 && sa[1].length() == 0) { 
       int t = nC + 1; 
       for (int i = 0; i < t; i++) {
          Resv6[7 - i] = sa[sa.length - 1 - i];
       }
   } else {
       int idx = Arrays.asList(sa).indexOf("");
       for (int i = 0; i < idx; i++) {
           Resv6[i] = sa[i];
       }
       for (int i = 0; i < sa.length - idx - 1; i++) {
       Resv6[7 - i] = sa[sa.length - 1 - i];
    } 
  } 
  /* End of Building Result IPv6 address */ 
  s = ""; 
  for (int i = 0; i < 8; i++) { 
     s += Resv6[i]; 
  } 
  
  Finalv6 = new BigInteger(s, 16);
  return Finalv6; 
}; 

以上这两个函数也可以组合到一个通用类库中来验证和形式化 IPv6 地址,但我将它们写在单独的类中,以使应用程序的功能简单易懂

正如我在《IPv6 子网划分 第 1 部分/2》一文中解释的那样,使用 /前缀长度值可以轻松直观地找到给定地址的子网开始和结束地址。下面的 StartEndAddresses() 函数接收一个 IPv6 地址,并在 SEaddress 对象中返回子网的开始和结束地址。

public SEaddress StartEndAddresses(SEaddress input) { 
   BigInteger mask = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);
   mask = mask.shiftRight(input.slash);
   input.End = mask.or(input.Resultv6);
   mask = mask.not();
   input.Start = mask.and(input.Resultv6);

   return input;
}

其中 SEaddress 是一个存储对象

public class SEaddress {
    public BigInteger Resultv6 = BigInteger.ZERO;
    public BigInteger Start = BigInteger.ZERO;
    public BigInteger End = BigInteger.ZERO;
    public int slash = 0;
    public int subnetslash = 0;
    public BigInteger subnetidx = BigInteger.ZERO;
}

另一个问题是如何找到相邻的子网地址,或者紧跟在我们子网后面的子网?换句话说,我们如何在相邻的子网地址空间中移动或遍历

实现这一目标的一个非常简单的方法是获取结束地址,然后加一,这将得到相邻子网地址的开始地址。使用这个开始地址,找到结束地址,然后加一,这将得到第二个相邻的子网地址,依此类推。正如你所注意到的,通过这个算法,我们现在可以在子网地址空间中移动或遍历了。

获得相邻子网地址的迭代过程可以是:

  • 步骤 1: 获取 IPv6 地址
  • 步骤 2: 查找子网开始地址
  • 步骤 3: 查找子网结束地址
  • 步骤 4: 将子网结束地址加一。得到下一个子网的开始地址。
  • 步骤 5: 从步骤 4 中找到的地址开始,转到步骤 3。

对于子网划分,我们通过第二个滑块或轨道条借用位。尽管我们可以轻松找到我们的子网,但现在另一个问题是我们的子网索引号是多少,以及如何找到它的索引号?也就是说,它是第 4 个子网,还是第 5 个,还是第 6 个,或者哪个?

答案就在地址本身,我们可以从地址中提取这个索引号。

实际上,使用第二个滑块,即借用位,我们也正在创建子网索引号。
两个滑块之间的间隔就是我们的子网索引号。例如,如果我们把第一个滑块拖到 /48,第二个滑块拖到 /52,这意味着我们借用了 4 位,创建了 24=16 个不同的子网,索引值从 0 到 15。

那么,我们如何在应用程序中处理查找索引值呢?我们可以通过测试位并将它们设置到我们的索引变量中来复制此间隔内的位。要测试位,我们可以执行按位运算 (number &(1<< i )),其中 i 是要移位的整数。如果结果大于零,则该位置的位为 1,否则为零。

代码可能是这样的(这小部分也包含在 StartEndAddresses() 函数中)

input.subnetidx = BigInteger.ZERO;
   int delta = input.subnetslash - input.slash - 1;
   for (int i = (127 - input.slash); i > (127 - input.subnetslash); i--) {
       if (input.Start.testBit(i) == true) {
                input.subnetidx = input.subnetidx.setBit(delta);
       } 
       else {
                input.subnetidx = input.subnetidx.clearBit(delta);
       }
       delta--;
   }
return input;

到目前为止,我们已经知道如何找到子网的开始/结束地址和子网索引。剩下的是在一个简单的 for 循环中输出这些值,其中包含一个 'upto' 限制变量,例如:

for (count = 0; count < upto; count++) {
     subnets = Subnetting(subnets);
       //Display the Results
       //...
     subnets.Start = subnets.End.add(BigInteger.ONE);
}

历史

  • 2013 年 9 月 29 日: v1.0
  • 2013 年 11 月 16 日: C# v1.1
    • 添加了新的 v1.8 C# 应用程序
  • 2013 年 12 月 12 日: C# v1.2
    • 进行了一些打字校正,并添加了 v1.10 C# 应用程序
  • 2014 年 1 月 26 日: C# v1.3
    • 添加了新的 v1.11 C# 应用程序
  • 2017 年 10 月 15 日: C# v3.6
    • 添加/更新了 C# 应用程序
  • 2017 年 11 月 13 日: Java v2.0
    • 添加了基于 JavaFX 的新 v2.0 Java 应用程序
  • 2017 年 12 月 7 日: C# v3.7, JavaFX v3.0
    • 添加了 MySQL 数据库连接支持
      • 使用 MySQL 数据库服务器存储/更新/管理 IPv6 前缀
      • 使用 MySQL-AB JDBC 连接器驱动程序测试,版本 5.1.23
      • 驱动程序名称: mysql-connector-java-5.1.23-bin.jar
    • 添加了已分配/可用前缀的统计视图
      • 注意: 已分配的前缀必须存在于数据库中。
    • 添加了 4 字节 AS 号明文/点分转换工具
      • 将自治系统号从 asplain 转换为 asdot,反之亦然。
  • 2018 年 3 月 22 日: C# v3.8, JavaFX v3.2
    • 添加了一个菜单项来管理所有打开的窗口/窗体
    • 添加了一个复选框来显示或保存前缀结束地址
  • 2018 年 12 月 7 日: C# v3.9
    • 添加了 IPv6 地址类型信息
  • 2019 年 10 月 23 日: JavaFX v3.3
    • JavaFX MySQL 数据库连接器升级版本: 8.0.18 (推荐)
    • DB 前缀可以从 Main/subnetRange/prefixSubLevels 窗体查询
    • 使用 XML 文件保存最后的设置,例如 DB 信息。
    • 添加了一个文本字段显示子网掩码
  • 2019 年 10 月 23 日: C# v4.0
    • .NET Framework 目标已升级到 4.7.2
    • MySQL 数据库连接器测试版本: 8.0.18 (推荐)
    • 使用 XML 文件保存最后的设置,例如 DB 信息、语言。
    • DB 前缀可以从 Main/subnetRange/prefixSubLevels 窗体查询
    • 添加了一个列表按钮来显示已安装的数据库驱动程序名称
    • 添加了一个文本字段显示子网掩码
  • 2020 年 1 月 6 日: C# v4.1
    • .NET Framework 目标已升级到 4.8
    • 添加了 IPv4 模式。IPv6 和 IPv4 设置都保存在 XML 文件中。
  • 2020 年 2 月 23 日: C# v4.4
    • 修复了 List Reverse DNS 中的一个小错误。
    • 在 Compress Tool 下添加了 SNMA。
  • 2020 年 4 月 16 日: C# v4.5
    • 添加了服务名称和端口号工具 (数据可从 IANA 更新)。
    • 添加了虚拟键盘。
  • 2022 年 5 月 24 日: C# v5.0
    • C# 数据库结构已更改。
    • MySQL 数据库连接器测试版本: 8.0.28 (推荐)
    • 父前缀可见。
  • 2022 年 5 月 24 日: Java v4.0
    • Java 代码已完全转换为原生 Java (将不使用 JavaFX)
    • MySQL 数据库连接器测试版本: 8.0.28 (推荐)
    • 父前缀可见。
© . All rights reserved.