Objective-C:如何获取路由器地址?

8

我尝试用这种方式获取路由器地址。

- (NSString *) routerIp {

  NSString *address = @"error";
  struct ifaddrs *interfaces = NULL;
  struct ifaddrs *temp_addr = NULL;
  int success = 0;

  // retrieve the current interfaces - returns 0 on success
  success = getifaddrs(&interfaces);
  if (success == 0)
  {
    // Loop through linked list of interfaces
    temp_addr = interfaces;
    while(temp_addr != NULL)
    {
      if(temp_addr->ifa_addr->sa_family == AF_INET)
      {
        // Check if interface is en0 which is the wifi connection on the iPhone
        if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"])
        {
          // Get NSString from C String //ifa_addr
          address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr)];
        }
      }

      temp_addr = temp_addr->ifa_next;
    }
  }

  // Free memory
  freeifaddrs(interfaces);

  return address;
}

路由器的地址通常看起来像xxx.xxx.255.255,但它应该像xxx.xxx.0.1或类似这样的形式...
有什么办法可以获得有效的地址吗?
感谢您的帮助!

1
如果ifa->ifa_dstaddr是广播地址,则解释了“255”的含义。 - Bill
1
看看这个链接,可能对你有帮助 https://dev59.com/EljUa4cB1Zd3GeqPUcVk - Rohan Pawar
3个回答

11

我一直在寻找一种方法来获取默认网关的IP地址。这里我只专注于这个问题 - 我不需要获取默认路由器的MAC地址,所以这只是部分回答了提问者的问题。

我不喜欢解析netstat输出的想法。而且,你不能根据你机器上一个接口的IP地址来获取默认路由。与你的IP地址在同一范围内的任何IP地址都可以是默认网关 - 唯一的规则是你和默认网关的IP地址需要属于同一子网。

我还想确保即使我的本地接口中有多个IP地址分配给它(例如我同时连接到Wifi和有线以太网,并且两个接口都启用,具有其中的IP地址),也能获取默认网关的IP地址。对于我的应用程序来说,无论通过哪个接口设置默认路由都没有关系(可以是en0、en1、ppp0或其他任何接口)。

我认为获取这些信息的最佳(也是最符合Apple风格的)方式是使用系统配置框架 - 本贴帖子中的链接指向了这个方向,但并没有详细说明如何使用它,而我在苹果文档中也没有找到非常有用的信息(至少考虑到我的技能水平)。

请注意,我对Objective-C并不是很有经验 - 我正在编写一个我自己需要的应用程序(而且我找不到),并且对于这个应用程序,我只需要默认网关的IP地址。

所以 - 这就是我在我的应用程序中正在做的事情,它似乎工作得很好(而且比我迄今为止找到的大多数其他解决方案都更短、更简单):

- (NSString *)defaultRouter {

    SCDynamicStoreRef ds = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("myapp"), NULL, NULL);
    CFDictionaryRef dr = SCDynamicStoreCopyValue(ds, CFSTR("State:/Network/Global/IPv4"));
    CFStringRef router = CFDictionaryGetValue(dr, CFSTR("Router"));
    NSString *routerString = [NSString stringWithString:(__bridge NSString *)router];
    CFRelease(dr);
    CFRelease(ds);

    return routerString;
}

请注意,在我的应用程序中,上述内容是更大方法的一部分(实际上我在那里没有一个defaultRouter:方法)。另外,为了简洁起见,我省略了一些检查(比如[routerString length]等)。

希望有人能发现这个有用。如果上面的代码中有任何错误,请随时进行修复 - 我还是新手!

PS. 当我检查'scutil'的输出时,我得到了要查找的想法,它使用了系统配置框架本身:

MacBook:~$ scutil 
> show State:/Network/Global/IPv4
<dictionary> {
  PrimaryInterface : en1
  PrimaryService : <service_id>
  Router : <ipv4_address>
}
> show State:/Network/Global/IPv6
<dictionary> {
  PrimaryInterface : en1
  PrimaryService : <service_id>
  Router : <ipv6_address>
}

11
SCDynamicStoreCreateSCDynamicStoreCopyValue 在 iOS 上不可用。这只适用于 Mac。 - thegrinner
@thegrinner 我没有尝试过这些函数,但是这些函数在iOS下是可用的。你可以通过下一个命令来确保:nm -m /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration | grep SCDynamicStoreCreate - Maxim Kholyavkin
不,这些在iOS 8.1上不是公开可用的。在SCDynamicStore.h中: SCDynamicStoreCreate ... __OSX_AVAILABLE_STARTING(__MAC_10_1,__IPHONE_NA); 尝试引用它们会产生编译器错误。 - The Mad Gamer

4

这是我编写的一些代码,用于查找机器的路由。 我从netstat代码中复制了系统调用。请注意,他们强烈建议不要这样做,因为这些系统调用不是受支持的API,可能随时更改。他们建议我只解析netstat的输出。

CustomRoute.c:

#import "CustomRoute.h"
@implementation CustomRoute

+ (NSMutableArray*) getRoutes
{
    NSMutableArray* routeArray = [NSMutableArray array];
    CustomRoute* route = nil;

    size_t needed;
    int mib[6];
    char *buf, *next, *lim;
    register struct rt_msghdr2 *rtm;

    mib[0] = CTL_NET;
    mib[1] = PF_ROUTE;
    mib[2] = 0;
    mib[3] = 0;
    mib[4] = NET_RT_DUMP2;
    mib[5] = 0;

    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
        err(1, "sysctl: net.route.0.0.dump estimate");
    }

    if ((buf = malloc(needed)) == 0) {
        err(2, "malloc(%lu)", (unsigned long)needed);
    }
    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
        err(1, "sysctl: net.route.0.0.dump");
    }

    lim  = buf + needed;

    for (next = buf; next < lim; next += rtm->rtm_msglen) {
        rtm = (struct rt_msghdr2 *)next;
        route = [self getRoute:rtm];
        if(route != nil)
        {
            [routeArray addObject:route];
        }
    }
    free(buf);
    printf("Total routes: %u\n",    [routeArray count]);
    return routeArray;
}


+ (CustomRoute*) getRoute:(struct rt_msghdr2 *)rtm
{
    //sockaddrs are after the message header
    struct sockaddr* dst_sa = (struct sockaddr *)(rtm + 1);

    CustomRoute* route = nil;

    if(rtm->rtm_addrs & RTA_DST)
    {
        switch(dst_sa->sa_family)
        {
            case AF_INET:
                if(dst_sa->sa_family == AF_INET && !((rtm->rtm_flags & RTF_WASCLONED) && (rtm->rtm_parentflags & RTF_PRCLONING)))
                {
                    route = [[CustomRoute alloc] initWithRtm:rtm];
                }
            break;

        }
    }

    return route;
}

-(void) setAddr:(struct sockaddr*)sa index:(int)rtax_index
{
    if(rtax_index >= 0 && rtax_index < RTAX_MAX)
    {
        memcpy(&(m_addrs[rtax_index]), sa, sizeof(struct sockaddr));
    }

}

-(NSString*) getDestination
{
    return [self getAddrStringByIndex:RTAX_DST];
}

-(NSString*) getNetmask
{
    return [self getAddrStringByIndex:RTAX_NETMASK];
}

-(NSString*) getGateway
{
    return [self getAddrStringByIndex:RTAX_GATEWAY];
}

-(NSString*) getDetails
{
    NSMutableString* result = [[NSMutableString alloc] init];
    [result appendFormat: [NSString stringWithFormat: @"message type: 0x%06x\n", m_rtm.rtm_type]];
    [result appendFormat: [NSString stringWithFormat: @"flags: 0x%06x\n", m_rtm.rtm_flags]];
    [result appendFormat: [NSString stringWithFormat: @"addrs: 0x%06x\n", m_rtm.rtm_addrs]];


    return result;
}


-initWithRtm: (struct rt_msghdr2*) rtm
{
    int i;
    struct sockaddr* sa = (struct sockaddr*)(rtm + 1);


    //copy over the route message
    memcpy(&(m_rtm), rtm, sizeof(struct rt_msghdr2));
    for(i = 0; i < RTAX_MAX; i++)
    {
        [self setAddr:&(sa[i]) index:i];
    }
    return self;
}

- init
{
    memset(m_addrs, 0, sizeof(m_addrs));
    return self;
}

@end


@implementation CustomRoute (Private)

-(NSString*) getAddrStringByIndex: (int)rtax_index
{
    NSString * routeString = nil;
    struct sockaddr* sa = &(m_addrs[rtax_index]);
    int flagVal = 1 << rtax_index;

    if(!(m_rtm.rtm_addrs & flagVal))
    {
        return @"none";
    }


    if(rtax_index >= 0 && rtax_index < RTAX_MAX)
    {
        switch(sa->sa_family)
        {
            case AF_INET:
            {
                struct sockaddr_in* si = (struct sockaddr_in *)sa;
                if(si->sin_addr.s_addr == INADDR_ANY)
                    routeString = @"default";
                else
                    routeString = [NSString stringWithCString:(char *)inet_ntoa(si->sin_addr) encoding:NSASCIIStringEncoding];
            }
            break;

            case AF_LINK:
                {
                    struct sockaddr_dl* sdl = (struct sockaddr_dl*)sa;
                    if(sdl->sdl_nlen + sdl->sdl_alen + sdl->sdl_slen == 0)
                    {
                        routeString = [NSString stringWithFormat: @"link #%d", sdl->sdl_index];
                    }
                    else
                        routeString = [NSString stringWithCString:link_ntoa(sdl) encoding:NSASCIIStringEncoding];
                }
            break;

            default:
                {
                    char a[3 * sa->sa_len];
                    char *cp;
                    char *sep = "";
                    int i;

                    if(sa->sa_len == 0)
                    {
                        routeString = @"empty";
                    }
                    else
                    {
                        a[0] = (char)NULL;
                        for(i = 0, cp = a; i < sa->sa_len; i++)
                        {
                            cp += sprintf(cp, "%s%02x", sep, (unsigned char)sa->sa_data[i]);
                            sep = ":";
                        }
                        routeString = [NSString stringWithCString:a encoding:NSASCIIStringEncoding];
                    }
                }
        }
    }
    return routeString;
}

@end

CustomRoute.h:

#import <Cocoa/Cocoa.h>
#import <net/route.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <net/if_dl.h>
#import <sys/sysctl.h>

@interface CustomRoute : NSObject {
    struct sockaddr     m_addrs[RTAX_MAX];
    struct rt_msghdr2   m_rtm;
    int                 m_len;      /* length of the sockaddr array */
}

+ (NSMutableArray*) getRoutes;
+ (CustomRoute*) getRoute:(struct rt_msghdr2 *)rtm;

- (void) setAddr:(struct sockaddr*)sa index:(int)rtax_index;

- (NSString*) getDestination;
- (NSString*) getNetmask;
- (NSString*) getGateway;
- initWithRtm: (struct rt_msghdr2*) rtm;


@end

在iOS上使用Mac OS的库和框架并不正常。 - Alena

1

实际上是广播地址,而不是子网掩码。 - Tom Dalling
如果是这种情况,那么这就是一个巨大的广播域。两个255位的八进制数足以向将近2^16 = 65536个主机进行广播。 - pokstad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接