layers_Ethernet.js

const { LinkLayerType } = require('../enums');
const { compile } = require('struct-compile');
const { macToString, macFromString } = require('./mac');
const { OsiModelLayers } = require('./osi');
const { ETHERTYPE } = require('./enums');
const child = require('./child');
const mixins = require('./mixins');

const { EthernetHeader } = compile(`
  //@NE
  struct EthernetHeader {
    //destination MAC
    uint8_t dst[6];

    //source MAC
    uint8_t src[6];

    //underlying protocol
    uint16_t type;
  } __attribute__(packed);
`);

const childProto = {
  [ETHERTYPE.IP]: 'IPv4',
  [ETHERTYPE.IPV6]: 'IPv6',
  [ETHERTYPE.ARP]: 'ARP',
  [ETHERTYPE.VLAN]: 'Vlan',
  [ETHERTYPE.IEEE_802_1AD]: 'Vlan',
  [ETHERTYPE.PPPOES]: 'PPPoESession',
  [ETHERTYPE.PPPOED]: 'PPPoEDiscovery',
  [ETHERTYPE.MPLS]: 'Mpls',
  [ETHERTYPE.WAKE_ON_LAN]: 'WakeOnLanLayer',
};

const lookupChild = child.lookupChild(childProto);
const lookupKey = child.lookupKey(childProto);

/**
 * Ethernet protocol layer
 * @class
 * @implements {Layer}
 * @property {string} src - Source MAC address.
 * @property {string} dst - Destination MAC address.
 * @property {number} type - Protocol type.
 */
class Ethernet extends EthernetHeader {
  static linktype = LinkLayerType.LINKTYPE_ETHERNET;
  name = 'Ethernet';

  /**
   * @param {Buffer|Object} data - Input buffer or object with protocol fields.
   * @param {Object} opts - Options for the layer.
   */
  constructor(data = {}, opts = {}) {
    super(data);
    mixins.ctor(this, data, opts);
  }

  static toAlloc = () => EthernetHeader.prototype.config.length;

  static osi = OsiModelLayers.DataLink;
  osi = OsiModelLayers.DataLink;

  /**
   * The source mac address in human-readable format.
   * @type {string}
   */
  get src() {
    return macToString(super.src);
  }

  set src(val) {
    super.src = macFromString(val);
  }

  /**
   * The destination mac address in human-readable format.
   * @type {string}
   */
  get dst() {
    return macToString(super.dst);
  }

  set dst(val) {
    super.dst = macFromString(val);
  }

  toObject() {
    return {
      ...super.toObject(),
      src: this.src,
      dst: this.dst,
    };
  }

  defaults(obj, layers) {
    if (!obj.type) {
      if (this.next) {
        this.type = lookupKey(layers, this.next);
      }
      else {
        this.type = ETHERTYPE.IP;
      }
    }
  }

  nextProto(layers) {
    return lookupChild(layers, this.type, this);
  }
};

module.exports = { Ethernet };