使用Objective-C确定IPv6地址是否在范围内

时间:2020-02-01 21:58:17

标签: objective-c ipv6

我正在尝试确定最有效的方法来确定IPv6地址是否在地址范围内(包括起始地址和结束地址)。鉴于起始地址和结束地址之间可能存在大量地址,我当然不想遍历该范围以寻找匹配项。

我已经收到一些建议,即尝试进行这种范围匹配不是一种好的方法,或者对于IPv6而言不是实际/有用的,我应该研究前缀匹配。在走那条路线之前,我想看看有哪些有效的解决方案可以确定IPv6地址是否在给定范围内。

我不是网络专家,所以请原谅我对此事的无知。

我发现this Stack Overflow post的答案是不可接受的,这表明最好的方法是将IPv6地址的所有十六进制都转换为二进制,然后进行比较。这篇文章有两个问题:1.我不确定如何将PHP脚本改编成Objective-C,并且2.此答案有些反对,但不是公认的答案,因此我不确定这是否值得追求

我还发现this other Stack Overflow post似乎可以很好地将十六进制转换为二进制,但是同样,我不确定如何在生成的二进制值之间进行比较以确定范围。请注意,我使用的是问题的示例代码,而不是答案。修改此代码以达到我的目的,可以使用示例IPv6地址fe80::34cb:9850:4868:9d2c给我类似以下的输出:

fe80 = 1111111010000000
0000 = 0
0000 = 0
0000 = 0
34cb = 11010011001011
9850 = 1001100001010000
4868 = 100100001101000
9d2c = 1001110100101100

我试图达到的最终结果是能够在Mac的IP地址在定义范围内时做出反应。我已经找到了针对IPv4地址的解决方案,但是我想向我的免费macOS应用安非他明添加IPv6支持。在此先感谢您的协助。这就是我用于IPv4范围检查的内容...不确定是否可以将其改用于IPv6:

- (bool) ipAddress: (NSString *)ipAddress isBetweenIpAddress: (NSString *)rangeStart andIpAddress: (NSString *)rangeEnd
{
    uint32_t ip = [self convertIpAddress: ipAddress];
    uint32_t start = [self convertIpAddress: rangeStart];
    uint32_t end = [self convertIpAddress: rangeEnd];
    return ip >= start && ip <= end;
}

- (uint32_t) convertIpAddress: (NSString *) ipAddress
{
    struct sockaddr_in sin;
    inet_aton([ipAddress UTF8String], &sin.sin_addr);
    return ntohl(sin.sin_addr.s_addr);
}

1 个答案:

答案 0 :(得分:1)

此问题需要两个步骤:(1)从字符串解析地址和范围的开始/结束; (2)比较数值。这两个部分都可用的API是基于C的,因此并不是特别用户友好。

  • inet_pton可用于解析字符串并将其放入in6_addr结构中。
  • in6_addr包含一个.s6_addr,它是由16个字节组成的大端顺序的数组。
  • ...可以与memcmp的另一个地址进行比较。

将它们放在一起:

NSString *rangeStart = @"2001:db8::";
NSString *rangeEnd = @"2001:db8::ffff:ffff:ffff:ffff";

NSString *address = @"2001:db8::abc";

// Parse strings into in6_addrs
struct in6_addr rangeStartAddr;
struct in6_addr rangeEndAddr;
struct in6_addr addr;
if (inet_pton(AF_INET6, rangeStart.UTF8String, &rangeStartAddr) != 1) {
    abort();
}
if (inet_pton(AF_INET6, rangeEnd.UTF8String, &rangeEndAddr) != 1) {
    abort();
}
if (inet_pton(AF_INET6, address.UTF8String, &addr) != 1) {
    abort();
}

// Use memcmp to compare binary values
if (memcmp(rangeStartAddr.s6_addr, addr.s6_addr, 16) <= 0
    && memcmp(addr.s6_addr, rangeEndAddr.s6_addr, 16) <= 0) {
    NSLog(@"In range");
} else {
    NSLog(@"Not in range");
}

将每个地址转换为单个__uint128_t号也是可能的,但很麻烦。但是,如果您只需要比较,memcmp就足够了。


在Swift中,可以使用UnsafeRawBufferPointer.lexicographicallyPrecedes代替memcmp来完成同样的操作:

import Darwin

extension in6_addr {
    init?(_ str: String) {
        self.init()
        if inet_pton(AF_INET6, str, &self) != 1 {
            return nil
        }
    }
}

extension in6_addr: Comparable {
    public static func ==(lhs: in6_addr, rhs: in6_addr) -> Bool {
        return withUnsafeBytes(of: lhs) { lhsBytes in
            withUnsafeBytes(of: rhs) { rhsBytes in
                lhsBytes.prefix(16).elementsEqual(rhsBytes.prefix(16))
            }
        }
    }
    public static func <(lhs: in6_addr, rhs: in6_addr) -> Bool {
        return withUnsafeBytes(of: lhs) { lhsBytes in
            withUnsafeBytes(of: rhs) { rhsBytes in
                lhsBytes.prefix(16).lexicographicallyPrecedes(rhsBytes.prefix(16))
            }
        }
    }
}

var rangeStartAddr = in6_addr("2001:db8::")!
var rangeEndAddr = in6_addr("2001:db8::ffff:ffff:ffff:ffff")!
var addr = in6_addr("2001:db8::abc")!

(rangeStartAddr...rangeEndAddr).contains(addr)
相关问题