• 技术文章 >web前端 >js教程

    如何利用Node获取物理网卡mac地址

    青灯夜游青灯夜游2022-10-13 19:46:20转载287
    本篇文章介绍一下利用Node获取真实物理网卡的 MAC 地址的方法,其中主要讨论了基于实践经验对虚拟网卡的识别处理方式,希望对大家有所帮助!

    大前端成长进阶课程:进入学习

    在基于 Electron 的应用中,有一个业务需求是获取物理网卡的 Mac 地址以用于客户机唯一性识别。

    刚接到需求时你可能会想,这还不简单,调用 Node.js 的 os 模块提供的 networkInterfaces API 就行了。【相关教程推荐:nodejs视频教程

    于是马上开干:

    import { networkInterfaces } from 'os';
    
     function isZeroMac(mac) {
      return /^(0{1,2}[:-]){5}0{1,2}$/.test(mac);
    }
    
    function getMac(family = 'IPv4') {
        const nif = networkInterfaces();
        for (const list of Object.values(nif)) {
            const item = list.find(d => !d.internal && !isZeroMac(d.mac) && (!d.family ||d.family === family));
            if (item) return item.mac;
       }
    
       return '';
    }

    两分钟就写完了,测试一下返回值也与 ipconfig/ifconfig 打印的信息一致,满怀信心的提交代码完工。

    测试同学当天验证了一下表示没什么问题,然而第二天却找上门了:同一台电脑今昨两天取到的值不一样。经过各种排查分析,最后才发现原来这位测试妹妹因疫情管控居家了,用着 VPN 远程接入办公网络干活呢。

    原来开 VPN 的时候使用了虚拟网卡,此时你才发现事情并没有那么简单。实际上,在存在 VPN、虚拟机等场景下,都可能会使用到虚拟网卡。

    1. 根据 networkInterfaces 返回值的字段值过滤

    networkInterfaces 可以获取到所有网卡的基本信息,可根据 internalmac 等字段的值做一次过滤,得到有效的信息:

    const isValid = (item) => item.internal === false && !isZeroMac(item.mac);

    但是对于 VPN、虚拟机等存在虚拟网卡的场景下,仅根据该信息无法进行有效区分。

    2. 根据虚拟网卡 Mac 特征过滤

    如果能够得到虚拟网卡的特征,则可基于相关特征点进行识别与过滤。

    基于某内部项目长达六年的实践积累以及参考 vscode 中类似的实现,我们得到了一个常见虚拟网卡默认 Mac 地址特征的列表,参考如下:

    // see https://standards-oui.ieee.org/oui/oui.txt
    const virtualMacPrefix = new Set([
      '00:05:69', // vmware1
      '00:0c:29', // vmware2
      '00:50:56', // vmware3
      '00:1c:14', // vmware
      '00:1c:42', // parallels1
      '02:00:4c', // Microsoft Loopback Adapter (微软回环网卡)
      '00:03:ff', // microsoft virtual pc
      '00:0f:4b', // virtual iron 4
      '00:16:3e', // red hat xen , oracle vm , xen source, novell xen
      '08:00:27', // virtualbox
    ]);

    于是可以据此实现一个是否为虚拟网卡的判断方法 isVirtualMac

    export function isMac(mac: string) {
      return /^([\da-f]{1,2}[:-]){5}([\da-f]{1,2})$/i.test(mac);
    }
    
    export function formatMac(mac: string) {
      return String(mac).trim().toLowerCase().replace(/-/g, ':');
    }
    
    export function isVirtualMac(mac: string) {
      return isMac(mac) && virtualMacPrefix.has(formatMac(mac).slice(0, 8));
    }

    据此可对 getMac 方法改进如下:

    function getMac(family = 'IPv4') {
        const nif = networkInterfaces();
        for (const list of Object.values(nif)) {
            const item = list.find(d => !d.internal && !isZeroMac(d.mac) && (!d.family ||d.family === family) && !isVirtualMac(d.mac));
            if (item) return item.mac;
       }
    
       return '';
    }

    3. 根据描述关键字特征过滤

    在 Windows 系统下,可以通过执行 ipconfig /allwmic nic get 命令得到所有网卡的详情,其中包含了描述信息。

    基于实践经验分析,我们总结了一个常见虚拟网卡描述关键字的特征列表,参考如下:

    const virtualDescList = ['virtual', ' vpn ', ' ssl ', 'tap-windows', 'hyper-v', 'km-test', 'microsoft loopback'];

    若经过前述规则过滤之后仍然存在多个网卡信息,则可继续获取网卡详情,并基于 virtualDescList 列表以尝试进一步的过滤处理:

    // 执行 wmic nic get 命令获取所有网卡详情
    function getNetworkIFacesInfoByWmic() {
      // 略
    }
    
    if (hasMutiMac(list)) {
      const info = await getNetworkIFacesInfoByWmic();
    
      list = list.filter(item => {
        if (!info.config[item.mac]) return true;
        const desc = String(info.config[item.mac].desc).toLowerCase();
        return !virtualDescList.some(d => desc.includes(d));
      });
    }

    getNetworkIFacesInfoByWmic 方法的具体实现可以参见这里:

    https://github.com/lzwme/get-physical-address/blob/main/src/getIFacesByExec.ts#L121

    4. 按优先级规则排序

    过滤方式会将视为无效的项排除,但是可能会因规则的误差而导致最后得到的列表为空。为了避免这种可能现象的出现,可以将过滤排除改为优先级排序方式,最后取列表第一项视为最优选项。

    排序方法实现示例:

    /**
     * sort by: !internal > !zeroMac(mac) > visual > family=IPv4 
     */
    function ifacesSort(list: NetworkInterfaceInfo[]) {
      return list.sort((a, b) => {
        if (a.internal !== b.internal) return a.internal ? 1 : -1;
        if (isZeroMac(a.mac) !== isZeroMac(b.mac)) return isZeroMac(a.mac) ? 1 : -1;
    
        const isVirtualA = isVirtualMac(a.mac);
        const isVirtualB = isVirtualMac(b.mac);
        if (isVirtualA !== isVirtualB) return isVirtualA ? 1 : -1;
    
        if (a.family !== b.family) return a.family === 'IPv6' ? 1 : -1;
      });
    }

    于是最终的逻辑大致如下:

    1.png

    5. 总结与参考

    实际上社区里已经有 addressgetmacmacaddress 等较为流行的相关库,但它们都不涉及虚拟网卡的识别。 本文主要介绍了基于实践经验对虚拟网卡的识别处理方式,与 vscode 中的相关实现逻辑较为相似,但又增加了基于描述信息过滤的规则逻辑。

    更多node相关知识,请访问:nodejs 教程

    以上就是如何利用Node获取物理网卡mac地址的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    快捷开发Web应用及小程序:点击使用

    支持亿级表,高并发,自动生成可视化后台。

    专题推荐:node Node.js nodejs​
    上一篇:Webpack是什么?详解它是如何工作的? 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• 浅析Node处理CPU密集型任务的方法• node服务CPU过高怎么办?聊聊排查思路• Node.js的内置模块 event,利用它怎么实现发布订阅模式• 聊聊怎么使用Node的内置模块zlib进行gzip压缩• 一文通过实践解析nodejs中间件
    1/1

    PHP中文网