Node.js Obtenga la dirección Mac de la tarjeta de red física real

En las aplicaciones basadas en Electron, existe un requisito comercial para obtener la dirección Mac de la tarjeta de red física para la identificación única del cliente.

Cuando recibió la demanda por primera vez, podría pensar que esto no es fácil, simplemente llame a la networkInterfacesAPI proporcionada por el módulo os de Node.js. Así que empieza de inmediato:

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

Se escribió en dos minutos. Pruebe que el valor de retorno sea coherente con la información ipconfig/ifconfigimpresa y envíe el código con confianza.

Los compañeros de prueba lo comprobaron ese día y dijeron que no había problema, pero al día siguiente llamaron a la puerta: los valores obtenidos por la misma computadora hoy y ayer son diferentes. Después de varias investigaciones y análisis, finalmente se descubrió que la hermana de prueba estaba en casa debido al control de la epidemia y estaba usando VPN para acceder de forma remota a la red de la oficina para trabajar.

Resultó que la tarjeta de red virtual se usó cuando se abrió la VPN y luego te diste cuenta de que las cosas no eran tan simples. De hecho, las tarjetas de red virtual se pueden usar en escenarios como VPN y máquinas virtuales.

1. Filtrar según el valor de campo del valor de retorno de las interfaces de red

networkInterfacesSe puede obtener la información básica de todas las tarjetas de red , y la información efectiva se puede obtener filtrando según los valores de campos como internal, , etc.:mac

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

Sin embargo, en escenarios donde existen tarjetas de red virtual, como VPN y máquinas virtuales, esta información no se puede usar para distinguir de manera efectiva.

2. Filtrar según las características de la tarjeta de red virtual Mac

Si se pueden obtener las características de la tarjeta de red virtual, la identificación y el filtrado se pueden realizar en función de los puntos característicos relevantes.

Con base en seis años de acumulación práctica en un proyecto interno e implementaciones similares en vscode, hemos obtenido una lista de características de direcciones Mac predeterminadas de tarjetas de red virtuales comunes.La referencia es la siguiente:

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

Por lo tanto, se puede implementar un método para juzgar si se trata de una tarjeta de red virtual 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));
}
复制代码

En consecuencia, el getMacmétodo mejorar de la siguiente manera:

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. Filtrar según las características de la palabra clave de descripción

En el sistema Windows, ipconfig /allpuede wmic nic getobtener los detalles de todas las tarjetas de red ejecutando o comando, que incluye información de descripción.

Basándonos en el análisis de la experiencia práctica, hemos resumido una lista de características de las palabras clave comunes de descripción de tarjetas de red virtual. La referencia es la siguiente:

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 中的相关实现逻辑较为相似,但又增加了基于描述信息过滤的规则逻辑。

Supongo que te gusta

Origin juejin.im/post/7150324677078941703
Recomendado
Clasificación