Node.js 実際の物理ネットワーク カードの Mac アドレスを取得する

Electron ベースのアプリケーションでは、一意のクライアント識別のために物理ネットワーク カードの Mac アドレスを取得するというビジネス要件があります。

networkInterfaces最初に要求を受けたときは、Node.js の os モジュールが提供するAPIを呼び出すだけで、これは簡単ではないと思うかもしれません。だからすぐに始めてください:

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 '';
}
复制代码

It was written in two minutes. 戻り値がipconfig/ifconfig印刷され自信を持ってコードを提出してください。

テストクラスメートはその日それを検証し、問題はないと言いましたが、翌日彼らはドアに来ました: 今日と昨日の同じコンピュータで得られた値は異なります. さまざまな調査と分析の結果、最終的に、テストシスターは防疫のために自宅にいて、VPNを使用してオフィスネットワークにリモートアクセスして仕事をしていたことが判明しました。

VPNを開いたときに仮想ネットワークカードが使用されていたことが判明し、物事がそれほど単純ではないことに気付きました. 実際、仮想ネットワーク カードは、VPN や仮想マシンなどのシナリオで使用できます。

1. networkInterfaces の戻り値のフィールド値に従ってフィルタリングする

networkInterfacesすべてのネットワーク カードの基本情報を取得できinternal、 、などmacのフィールドの値に従ってフィルタリングすることにより、有効な情報を取得できます。

const isValid = (item) => item.internal === false && !isZeroMac(item.mac);
复制代码

ただし、VPN や仮想マシンなどの仮想ネットワーク カードが存在するシナリオでは、この情報を使用して効果的に区別することはできません。

2. 仮想ネットワーク カード Mac の特性に応じてフィルタリングする

仮想ネットワークカードの特性を取得できれば、関連する特性点に基づいて識別とフィルタリングを実行できます。

社内プロジェクトでの 6 年間の実践的な蓄積と 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 方法的具体实现可以参见这里:

github.com/lzwme/get-p…

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;
  });
}
复制代码

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

  • 获取全部网卡信息
  • 基于 iface 特征排序取得全部列表:en0 - mac, eth3 - linux, ethernet - windows 优先级更高
  • 基于 internal字段、虚拟网卡特征(mac)、family字段等进行排序
  • 对排序的结果进行基础过滤:internal=trueisZeroMac
  • 若过滤后列表多于1个,则基于虚拟网卡特征继续过滤
  • 若过滤结果仍多余1个,则基于描述特征继续过滤
  • 取最终结果的第一项作为最优选择

5. 总结与参考

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

おすすめ

転載: juejin.im/post/7150324677078941703