我正在尝试确定最有效的方法来确定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);
}
答案 0 :(得分:1)
此问题需要两个步骤:(1)从字符串解析地址和范围的开始/结束; (2)比较数值。这两个部分都可用的API是基于C的,因此并不是特别用户友好。
将它们放在一起:
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)