libpcap 从 AF_LINK sockaddr_dl 获取 MAC (OSX)

发布于 2024-10-09 23:43:38 字数 2369 浏览 7 评论 0原文

我正在尝试使用 C 获取 OSX 上所有接口的 MAC 地址。获取它的常见方法 Linux 不适用于 BSD - 从我所看到的一切来看,您必须获取接口并查找类型的接口AF_LINK。我的问题是 LLADDR(sockaddr_dl) 给了我一大堆数据(包括我的 MAC),但我不知道数据是什么格式。以下代码将输出:

设备:en1 链接 sdl_alen: 101 mac: 31:f8:1e:df:d6:22:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 :b0:06:10:00:01:00:00:00:c0:02:10:00:01:00:00:00:00:00:00:00:00:00:00:00:40 :03:10:00:01:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:03:00:6c:6f :30:00:00:00:00:00:00:00:00:00:00:00:00:00:70:03:10:00:01:00:00:00:e0: 02:10:00:01:00:00:

我的 MAC 是粗体的。似乎一直都是这种格式,但如果我可以将 LLADDR(sockaddr_dl) 转换为某种东西,我会更舒服。在 net/if_dl.h 中,LLADDR 被定义为:

#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))

据我所知,这表明结果的类型为 (void *) - 没有帮助。

其他帖子如:

在弄清楚如何使用 ioctl/SIOCGIFADDR/SIOCGIFCONF 在 Mac OS X 上获取以太网接口信息时遇到问题?

似乎认为他们已经解决了,但是如果你查看代码,你可以看到由于 sdl_alen 不是 6,它将无法工作。

int main() {
    pcap_if_t *alldevs;
    pcap_if_t *d;
    pcap_addr_t *alladdrs;
    pcap_addr_t *a;

    struct sockaddr_dl* link;

    char eb[PCAP_ERRBUF_SIZE];
    char *addr_buf[40];

    if (pcap_findalldevs(&alldevs, eb) == -1) {
        printf("no devs found\n");
        return(-1);
    }

    for (d = alldevs; d != NULL; d = d->next) {
        printf("Device: %s\n", d->name);
        alladdrs = d->addresses;
        for (a = alladdrs; a != NULL; a = a->next) {
            if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
                // MAC ADDRESS
                //struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
                link = (struct sockaddr_dl*)a->addr->sa_data;

                char mac[link->sdl_alen];
                caddr_t macaddr = LLADDR(link);

                memcpy(mac, LLADDR(link), link->sdl_alen);
                printf("link sdl_alen: %i\n", link->sdl_alen);
                int i;
                printf("mac: ");
                for(i = 0; i<link->sdl_alen; i++){
                    printf("%02x:", (unsigned char)mac[i]);
                }
                printf("\n");
            }
        }
    }
}

I am trying to obtain the MAC addresses of all of my interface on OSX using C. The common ways to obtain it Linux dont work on BSD - from everything I have seen, you must obtain the interfaces and look for the ones that are of type AF_LINK. My problem is that the LLADDR(sockaddr_dl) gives me a whole bunch of data (which includes my MAC) and I dont know what format the data is in. For example; the following code will output:

Device: en1
link sdl_alen: 101 mac:
31:f8:1e:df:d6:22:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:b0:06:10:00:01:00:00:00:c0:02:10:00:01:00:00:00:00:00:00:00:00:00:00:00:40
:03:10:00:01:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:03:00:6c:6f:30:00:00:00:00:00:00:00:00:00:00:00:00:00:70:03:10:00:01:00:00:00:e0:
02:10:00:01:00:00:

My MAC is bolded. It seems that this is the format all of the time, but I would be a lot more comfortable if I could cast LLADDR(sockaddr_dl) to something. In the net/if_dl.h, LLADDR is defied as:

#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))

which, as far as I can tell, is saying that the results are of type (void *) - no help.

Other posts like:

Having a problem figuring out how to get Ethernet interface info on Mac OS X using ioctl/SIOCGIFADDR/SIOCGIFCONF?

seem to think they have it figured out, but if you look through the code, you can see it will not work due to sdl_alen not being 6.

int main() {
    pcap_if_t *alldevs;
    pcap_if_t *d;
    pcap_addr_t *alladdrs;
    pcap_addr_t *a;

    struct sockaddr_dl* link;

    char eb[PCAP_ERRBUF_SIZE];
    char *addr_buf[40];

    if (pcap_findalldevs(&alldevs, eb) == -1) {
        printf("no devs found\n");
        return(-1);
    }

    for (d = alldevs; d != NULL; d = d->next) {
        printf("Device: %s\n", d->name);
        alladdrs = d->addresses;
        for (a = alladdrs; a != NULL; a = a->next) {
            if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
                // MAC ADDRESS
                //struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
                link = (struct sockaddr_dl*)a->addr->sa_data;

                char mac[link->sdl_alen];
                caddr_t macaddr = LLADDR(link);

                memcpy(mac, LLADDR(link), link->sdl_alen);
                printf("link sdl_alen: %i\n", link->sdl_alen);
                int i;
                printf("mac: ");
                for(i = 0; i<link->sdl_alen; i++){
                    printf("%02x:", (unsigned char)mac[i]);
                }
                printf("\n");
            }
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北陌 2024-10-16 23:43:38

问题是您将 sockaddr->sa_data 转换为 sockaddr_dl,而不是将 sockaddr 本身转换为 sockaddr_dl。请记住,sockaddr_dl 是 OS X/BSD 的东西,因此 #ifdef 这部分是为了可移植性。

不做:

link = (struct sockaddr_dl*)a->addr->sa_data;

做:

link = (struct sockaddr_dl*)a->addr;

然后您将获得正确的 sdl_alen 并且无需任何修改即可正常工作。如果您想真正轻松地获取可能是 AF_INET、AF_INET6 或 AF_LINK 的地址名称,请使用 getnameinfo():

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if_dl.h>

int get_sock_len(struct sockaddr *sa)
{
    switch (sa->sa_family) {
    case AF_INET:
        return sizeof(struct sockaddr_in);
    case AF_INET6:
        return sizeof(struct sockaddr_in6);
    case AF_LINK:
        return sizeof(struct sockaddr_dl);
    default:
        return -1;
    }
}

int get_numeric_address(struct sockaddr *sa, char *outbuf, size_t buflen) {
    socklen_t len;
    if ((len = get_sock_len(sa)) < 0) {
        return -1;
    }
    if (getnameinfo(sa, len, outbuf, buflen, NULL, 0, NI_NUMERICHOST)) {
        return -1;
    }
    return 0;
}

...
char buf[NI_MAXHOST];
if (!get_numeric_address(sa, buf, sizeof(buf))) { /* For some struct sockaddr *sa */
    printf("address: %s\n", buf);
} else {
    printf("doh!\n");
} 

The problem is that you are casting the sockaddr->sa_data to sockaddr_dl instead of casting the sockaddr itself to sockaddr_dl. Keep in mind that sockaddr_dl is an OS X/BSD thing, so #ifdef that part for portability.

Don't do:

link = (struct sockaddr_dl*)a->addr->sa_data;

Do:

link = (struct sockaddr_dl*)a->addr;

Then you will get the correct sdl_alen and things will work with out any hacks. And if you want to really easily get the name of addresses that may be either AF_INET, AF_INET6, or AF_LINK use getnameinfo():

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if_dl.h>

int get_sock_len(struct sockaddr *sa)
{
    switch (sa->sa_family) {
    case AF_INET:
        return sizeof(struct sockaddr_in);
    case AF_INET6:
        return sizeof(struct sockaddr_in6);
    case AF_LINK:
        return sizeof(struct sockaddr_dl);
    default:
        return -1;
    }
}

int get_numeric_address(struct sockaddr *sa, char *outbuf, size_t buflen) {
    socklen_t len;
    if ((len = get_sock_len(sa)) < 0) {
        return -1;
    }
    if (getnameinfo(sa, len, outbuf, buflen, NULL, 0, NI_NUMERICHOST)) {
        return -1;
    }
    return 0;
}

...
char buf[NI_MAXHOST];
if (!get_numeric_address(sa, buf, sizeof(buf))) { /* For some struct sockaddr *sa */
    printf("address: %s\n", buf);
} else {
    printf("doh!\n");
} 
半葬歌 2024-10-16 23:43:38

这是我最终所做的:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <errno.h>
#include <net/if_dl.h>
#include <pcap.h>

#include "mac.h"

int main() {
    printf("en1: %s\n", lookupDeviceMac("vnic0"));
}

unsigned char *lookupDeviceMac(char *dev){
    pcap_if_t *alldevs;
    pcap_if_t *d;
    pcap_addr_t *alladdrs;
    pcap_addr_t *a;

    struct sockaddr_dl* link;

    char eb[PCAP_ERRBUF_SIZE];

    char *ret = malloc(6);

    if (pcap_findalldevs(&alldevs, eb) == -1) {
        printf("%s\n", eb);
        return(ret);
    }

    for (d = alldevs; d != NULL; d = d->next) {
        if(strcmp(d->name, dev) == 0){
            printf("Device: %s\n", d->name);
            alladdrs = d->addresses;
            for (a = alladdrs; a != NULL; a = a->next) {
                if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
                    // MAC ADDRESS
                    //struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
                    link = (struct sockaddr_dl*)a->addr->sa_data;

                    char mac[link->sdl_alen];
                    caddr_t macaddr = LLADDR(link);
                    memcpy(mac, LLADDR(link), link->sdl_alen);

                    if(link->sdl_alen == 6){
                        // Seen in some sample code
                        sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[0],
                                                                     (unsigned char)mac[1],
                                                                     (unsigned char)mac[2],
                                                                     (unsigned char)mac[3],
                                                                     (unsigned char)mac[4],
                                                                     (unsigned char)mac[5]);
                    } else if(link->sdl_alen > 6) {
                        // This is what happens in OSX 10.6.5
                        sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[1],
                                                                     (unsigned char)mac[2],
                                                                     (unsigned char)mac[3],
                                                                     (unsigned char)mac[4],
                                                                     (unsigned char)mac[5],
                                                                     (unsigned char)mac[6]);
                    }
                    return(ret);
                }
            }
        }
    }
}

Here is what I ended up doing:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <errno.h>
#include <net/if_dl.h>
#include <pcap.h>

#include "mac.h"

int main() {
    printf("en1: %s\n", lookupDeviceMac("vnic0"));
}

unsigned char *lookupDeviceMac(char *dev){
    pcap_if_t *alldevs;
    pcap_if_t *d;
    pcap_addr_t *alladdrs;
    pcap_addr_t *a;

    struct sockaddr_dl* link;

    char eb[PCAP_ERRBUF_SIZE];

    char *ret = malloc(6);

    if (pcap_findalldevs(&alldevs, eb) == -1) {
        printf("%s\n", eb);
        return(ret);
    }

    for (d = alldevs; d != NULL; d = d->next) {
        if(strcmp(d->name, dev) == 0){
            printf("Device: %s\n", d->name);
            alladdrs = d->addresses;
            for (a = alladdrs; a != NULL; a = a->next) {
                if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
                    // MAC ADDRESS
                    //struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
                    link = (struct sockaddr_dl*)a->addr->sa_data;

                    char mac[link->sdl_alen];
                    caddr_t macaddr = LLADDR(link);
                    memcpy(mac, LLADDR(link), link->sdl_alen);

                    if(link->sdl_alen == 6){
                        // Seen in some sample code
                        sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[0],
                                                                     (unsigned char)mac[1],
                                                                     (unsigned char)mac[2],
                                                                     (unsigned char)mac[3],
                                                                     (unsigned char)mac[4],
                                                                     (unsigned char)mac[5]);
                    } else if(link->sdl_alen > 6) {
                        // This is what happens in OSX 10.6.5
                        sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[1],
                                                                     (unsigned char)mac[2],
                                                                     (unsigned char)mac[3],
                                                                     (unsigned char)mac[4],
                                                                     (unsigned char)mac[5],
                                                                     (unsigned char)mac[6]);
                    }
                    return(ret);
                }
            }
        }
    }
}
风吹过旳痕迹 2024-10-16 23:43:38

我试图查看 pcap_findalldevs 报告的所有设备,最后在这里寻找有关在 MAC 操作系统上解释 AF_LINK 地址的信息。

我习惯于看到 struct sockaddr 代表接口系列并立即转换为适当的类型,而不是编写代码来访问 *sa_data*。

对于我想要的,使用 link_ntoa 将地址转换为人类可读的形式就足够了。

#import <Foundation/Foundation.h>
#include <pcap.h>
#include <netinet/in.h>
#include <net/if_dl.h>
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    pcap_if_t* allDevs = NULL;
    char errbuff[PCAP_ERRBUF_SIZE];
    if (pcap_findalldevs(&allDevs, errbuff) <0) {
        NSLog(@"Failed with error '%s'", errbuff);
    }
    else {
        for (pcap_if_t* device = allDevs; device != NULL; device = device->next) {
            for (pcap_addr_t* address = device->addresses; address != NULL; address = address->next) {
                struct sockaddr* sa_addr = address->addr;
                if (sa_addr->sa_family == AF_LINK) {
                    struct sockaddr_dl* link_addr = (struct sockaddr_dl*) sa_addr;
                    char* linkAddress = link_ntoa(link_addr);
                    NSLog(@"ntoa %s", linkAddress);
                }
            }
        }
    }
    pcap_freealldevs(allDevs);

    [pool drain];
    return 0;
}

在我的机器上运行时,我得到以下带有 AF_LINK 条目的设备。

2011-08-14 02:22:43.024 HomePlugToolHelper[12473:903] ntoa en0:0.16.cb.xx.x.xx
2011-08-14 02:22:43.027 HomePlugToolHelper[12473:903] ntoa fw0:0.16.cb.xx.xx.xx.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa en1:0.16.cb.x.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa lo0

I was trying to see all devices reported by pcap_findalldevs and ended up here looking for information on interpreting AF_LINK addresses on MAC OS.

I'm used to seeing the struct sockaddr standing in for a interface family and being immediately cast to the appropriate type and not writing code to access *sa_data*.

For what I wanted it was sufficient to use link_ntoa to convert the address to a human readable form.

#import <Foundation/Foundation.h>
#include <pcap.h>
#include <netinet/in.h>
#include <net/if_dl.h>
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    pcap_if_t* allDevs = NULL;
    char errbuff[PCAP_ERRBUF_SIZE];
    if (pcap_findalldevs(&allDevs, errbuff) <0) {
        NSLog(@"Failed with error '%s'", errbuff);
    }
    else {
        for (pcap_if_t* device = allDevs; device != NULL; device = device->next) {
            for (pcap_addr_t* address = device->addresses; address != NULL; address = address->next) {
                struct sockaddr* sa_addr = address->addr;
                if (sa_addr->sa_family == AF_LINK) {
                    struct sockaddr_dl* link_addr = (struct sockaddr_dl*) sa_addr;
                    char* linkAddress = link_ntoa(link_addr);
                    NSLog(@"ntoa %s", linkAddress);
                }
            }
        }
    }
    pcap_freealldevs(allDevs);

    [pool drain];
    return 0;
}

Running on my machine I get the following devices with AF_LINK entries.

2011-08-14 02:22:43.024 HomePlugToolHelper[12473:903] ntoa en0:0.16.cb.xx.x.xx
2011-08-14 02:22:43.027 HomePlugToolHelper[12473:903] ntoa fw0:0.16.cb.xx.xx.xx.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa en1:0.16.cb.x.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa lo0

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文