如何获取不在给定IP地址范围内的所有IP地址

时间:2018-04-27 08:21:45

标签: algorithm network-programming ip salesforce apex

我需要能够输出不在给定IP地址范围列表中的所有IP地址范围。

我可以使用某种算法来完成这种任务,我可以将其转换为工作代码吗?

  • 基本上我会使用Salesforce Apex代码,因此如果可能的话,任何类似JAVA的语言都可以。

3 个答案:

答案 0 :(得分:1)

我认为简单解决方案的关键是要记住IP地址可以被视为多个类型的长,因此可以对它们进行排序。

我假设排除的范围以“漂亮”的方式给出,意味着没有重叠,没有与全局范围的部分重叠等等。您当然可以在以后添加此类输入检查。

在此示例中,我将所有网络范围(全局,包含,排除)作为NetworkRange类的实例。

以下是NetworkRange的实施。请注意方法splitByExcludedRangeincludes

public class NetworkRange {

    private long startAddress;
    private long endAddress;

    public NetworkRange(String start, String end) {
        startAddress = addressRepresentationToAddress(start);
        endAddress = addressRepresentationToAddress(end);
    }

    public NetworkRange(long start, long end) {
        startAddress = start;
        endAddress   = end;
    }

    public String getStartAddress() {
        return addressToAddressRepresentation(startAddress);
    }

    public String getEndAddress() {
        return addressToAddressRepresentation(endAddress);
    }

    static String addressToAddressRepresentation(long address) {
        String result = String.valueOf(address % 256);

        for (int i = 1; i < 4; i++) {
            address = address / 256;
            result = String.valueOf(address % 256) + "." + result;
        }

        return result;
    }

    static long addressRepresentationToAddress(String addressRep) {
        long result = 0L;
        String[] tokens = addressRep.split("\\.");

        for (int i = 0; i < 4; i++) {

            result += Math.pow(256, i) * Long.parseLong(tokens[3-i]);
        }

        return result;
    }

    public List<NetworkRange> splitByExcludedRange(NetworkRange excludedRange) {
        if (this.startAddress == excludedRange.startAddress && this.endAddress == excludedRange.endAddress)
            return Arrays.asList();

        if (this.startAddress == excludedRange.startAddress)
            return Arrays.asList(new NetworkRange(excludedRange.endAddress+1, this.endAddress));

        if (this.endAddress == excludedRange.endAddress)
            return Arrays.asList(new NetworkRange(this.startAddress, excludedRange.startAddress-1));

        return Arrays.asList(new NetworkRange(this.startAddress, excludedRange.startAddress-1),
                             new NetworkRange(excludedRange.endAddress+1, this.endAddress));
    }

    public boolean includes(NetworkRange excludedRange) {
        return this.startAddress <= excludedRange.startAddress && this.endAddress >= excludedRange.endAddress;
    }

    public String toString() {
        return "[" + getStartAddress() + "-" + getEndAddress() + "]";
    }
}

现在是计算左边包含的网络范围的类。它接受构造函数中的全局范围。

public class RangeProducer {

    private NetworkRange global;

    public RangeProducer(NetworkRange global) {
     this.global = global;
    }

    public List<NetworkRange> computeEffectiveRanges(List<NetworkRange> excludedRanges) {
        List<NetworkRange> effectiveRanges = new ArrayList<>();
        effectiveRanges.add(global);
        List<NetworkRange> effectiveRangesSplitted = new ArrayList<>();

        for (NetworkRange excludedRange : excludedRanges) {
            for (NetworkRange effectiveRange : effectiveRanges) {
                if (effectiveRange.includes(excludedRange)) {
                    effectiveRangesSplitted.addAll(effectiveRange.splitByExcludedRange(excludedRange));
                } else {
                    effectiveRangesSplitted.add(effectiveRange);
                }
            }

            effectiveRanges = effectiveRangesSplitted;
            effectiveRangesSplitted = new ArrayList<>();
        }
        return effectiveRanges;
    }
}

您可以运行以下示例:

public static void main(String[] args) {
        NetworkRange global = new NetworkRange("10.0.0.0", "10.255.255.255");

        NetworkRange ex1 = new NetworkRange("10.0.0.0", "10.0.1.255");
        NetworkRange ex2 = new NetworkRange("10.1.0.0", "10.1.1.255");
        NetworkRange ex3 = new NetworkRange("10.6.1.0", "10.6.2.255");
        List<NetworkRange> excluded = Arrays.asList(ex1, ex2, ex3);

        RangeProducer producer = new RangeProducer(global);

        for (NetworkRange effective : producer.computeEffectiveRanges(excluded)) {
            System.out.println(effective);
        }
    }

输出应为:

[10.0.2.0-10.0.255.255]
[10.1.2.0-10.6.0.255]
[10.6.3.0-10.255.255.255]

答案 1 :(得分:0)

首先,我假设你的意思是你得到一个或多个不相交的CIDR范围作为输入,并且需要产生所有CIDR范围的列表,不包括任何给定的输入。为方便起见,我们进一步假设输入不包括整个IP地址空间:即0.0.0.0/0。 (这可以通过一个特殊情况来适应,但不是很有意义。)

我之前编写的代码与此类似,虽然我无权共享代码,但我可以描述这种方法。它本质上是一种二进制搜索算法,您可以反复将整个地址空间二等分,直到您将您感兴趣的一个范围隔离开来。

将IP地址空间视为二叉树:根目录是完整的IPv4地址空间0.0.0.0/0。它的子节点代表地址空间的一半:0.0.0.0/1128.0.0.0/1.反过来,这些可以细分为创建子0.0.0.0/2 / 64.0.0.0/2和{{1}分别是/ 128.0.0.0/2。一直向下继续,最后得到192.0.0.0/2,个叶子,每个叶子代表一个2**32(即一个地址)。

现在,将此树视为从输入列表中排除的地址空间的一部分。因此,您的任务是遍历此树,从树中的输入列表中查找每个范围,并删除输入中树的所有部分,留下地址空间的其余部分。

幸运的是,您实际上并不需要创建所有/32个叶子。如果没有为其创建子项,则可以假定CIDR 2**32处的每个节点都包含CIDR N及以上的所有节点(您需要一个标记才能记住它已被细分) - 不再是一片叶子 - 见下面的原因)。

因此,首先,整个地址空间存在于树中,但都可以由单个叶节点表示。调用树N+1并使用单个节点excluded,

对其进行初始化

现在,考虑第一个输入范围 - 我们会调用此0.0.0.0/0.(我将trial用作初始14.27.34.0/24值,以便提供示范的具体价值)。任务是从trial中移除trial,留下剩余的地址空间。

excluded节点指针设置为current根节点开始。

开始:

excluded CIDR与trial进行比较。如果它完全相同,那么你就完成了(但是如果你的输入范围是不相交的并且你已经从输入中排除了current,那么这种情况永远不会发生)。

否则,如果0.0.0.0/0是叶节点(尚未细分,意味着它代表此CIDR级别及以下的整个地址空间),请设置其细分标志,并为其创建两个子节点:指向其地址空间前半部分的current指针,以及指向后半部分的left指针。适当地标记每个(对于根节点的子节点,将是right0.0.0.0/1)。

确定128.0.0.0/1 CIDR是否属于trial的左侧或右侧。对于我们的初始current值,它位于左侧。现在,如果那边的指针已经trial,那么你又完成了(尽管如果你的输入范围是不相交的,那么#34;不会发生&#34;)。

如果NULL CIDR 完全等同于该方节点中的CIDR,那么只需释放节点(以及它可能拥有的任何子节点,如果你只有不相交的输入),设置指向那一侧trial的指针,你就完成了。您只是通过从树上切下那片叶子来排除整个范围。

如果试用值与该侧节点中的CIDR不完全相同,请将NULL设置为该侧并重新开始(即跳转到上面的开始标签)。 / p>

因此,初始输入范围为current,您将首先将14.27.34.0/24拆分为0.0.0.0/00.0.0.0/1。然后,您将向左下拉并将128.0.0.0/1拆分为0.0.0.0/10.0.0.0/2然后您将再次向左下拉以创建64.0.0.0/2.0.0.0.0/3依此类推,直到23次拆分后,您才会将32.0.0.0/3.拆分为14.27.34.0/2314.27.34.0/24然后您将删除左侧14.27.35.0/24.子节点并将其指针设置为14.27.34.0/24,留下另一个。

这将为您留下一个包含24个叶子节点的稀疏树(在您删除目标节点之后)。其余叶节点标有*:

NULL

对于每个剩余的输入范围,您将再次遍历树,在必要时将叶节点平分,通常会产生更多叶子,但总是会切掉地址空间的某些部分。

最后,您只需按照方便的顺序遍历生成的树,收集剩余叶子的CIDR。请注意,在此阶段,必须排除先前已细分的那些。例如,在上面的树中,如果你下一次处理输入范围 (ROOT) 0.0.0.0/0 / \ 0.0.0.0/1 128.0.0.0/1* / \ 0.0.0.0/2 64.0.0.0/2* / \ 0.0.0.0/3 32.0.0.0.0/3* / \ 0.0.0.0/4 16.0.0.0/4* / \ *0.0.0.0/5 8.0.0.0/5 / \ *8.0.0.0/6 12.0.0.0/6 / \ *12.0.0.0/7 14.0.0.0/7 / \ 14.0.0.0/8 15.0.0.0/8* / \ ... / \ *14.27.32.0/23 14.27.34.0/23 / \ (null) 14.27.35.0/24* (14.27.34.0/24) ,你会留下没有子节点的14.27.35.0/24,但是它的两半都是单独剪切的,它不应该包含在输出。 (由于一些额外的复杂性,你当然可以折叠它上面的节点以适应这种情况,但是在每个节点中保留一个标志会更容易。)

答案 2 :(得分:0)

首先,您描述的内容可以简化为:

  • 您的格式为x.x.x.x - y.y.y.y
  • 您希望输出尚未采取的时间间隔&#34;在这个范围内。
  • 您希望能够有效地添加或删除间隔

我建议使用interval tree,其中每个节点都存储一个间隔,您可以有效地插入和删除节点;并查询给定点(= IP地址)的重叠。

如果您可以保证不会有重叠,您可以使用简单的TreeSet<String>,但必须保证(正确排序)所有字符串都使用xxx.xxx.xxx.xxx-yyy.yyy.yyy.yyy零填充格式

一旦您的时间间隔在树中,您就可以生成所需的输出,假设没有间隔重叠,通过执行树的深度优先预先遍历遍历,并将每个被访问节点的开始和结束存储在一个列表。鉴于此列表,

  • 在开始时预先挂起0.0.0.0
  • 最后追加255.255.255.255
  • 删除所有重复的ips(在列表中将强制彼此相邻)
  • 成对拍摄(数字将始终均匀),并且您有自由IP的间隔,完美排序。

请注意,0.0.0.0255.255.255.255实际上并不是有效的可路由IP。如果您确实需要输出真实感知的IP,则应阅读相关的RFC。