diff --git a/components.d.ts b/components.d.ts index f2c3146f..ea319331 100644 --- a/components.d.ts +++ b/components.d.ts @@ -117,6 +117,7 @@ declare module '@vue/runtime-core' { ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default'] LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default'] LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default'] + MacAddressConverter: typeof import('./src/tools/mac-address-converter/mac-address-converter.vue')['default'] MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default'] MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default'] MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default'] diff --git a/src/tools/index.ts b/src/tools/index.ts index aa861c93..ebf49f55 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,9 +1,8 @@ import { tool as base64FileConverter } from './base64-file-converter'; import { tool as base64StringConverter } from './base64-string-converter'; import { tool as basicAuthGenerator } from './basic-auth-generator'; - +import { tool as macAddressConverter } from './mac-address-converter'; import { tool as asciiTextDrawer } from './ascii-text-drawer'; - import { tool as textToUnicode } from './text-to-unicode'; import { tool as safelinkDecoder } from './safelink-decoder'; import { tool as pdfSignatureChecker } from './pdf-signature-checker'; @@ -152,7 +151,15 @@ export const toolsByCategory: ToolCategory[] = [ }, { name: 'Network', - components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator], + components: [ + ipv4SubnetCalculator, + ipv4AddressConverter, + ipv4RangeExpander, + macAddressLookup, + macAddressGenerator, + macAddressConverter, + ipv6UlaGenerator, + ], }, { name: 'Math', diff --git a/src/tools/mac-address-converter/index.ts b/src/tools/mac-address-converter/index.ts new file mode 100644 index 00000000..fa971345 --- /dev/null +++ b/src/tools/mac-address-converter/index.ts @@ -0,0 +1,20 @@ +import { Devices } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'MAC Address Converter', + path: '/mac-address-converter', + description: 'Change the format of a MAC address and chose between different formats (EUI-48, EUI-64, IPv6)', + keywords: [ + 'converter', + 'mac', + 'address', + 'format', + 'link-local', + 'ipv6', + 'eui-48', + 'eui-64', + ], + component: () => import('./mac-address-converter.vue'), + icon: Devices, +}); diff --git a/src/tools/mac-address-converter/mac-address-converter.service.test.ts b/src/tools/mac-address-converter/mac-address-converter.service.test.ts new file mode 100644 index 00000000..f449d6c6 --- /dev/null +++ b/src/tools/mac-address-converter/mac-address-converter.service.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest'; +import { + convertMacCISCO, convertMacCanonical, + convertMacCanonicalIEEE, convertMacCanonicalIETF, + convertMacToEUI64CISCO, convertMacToEUI64CanonicalIEEE, + convertMacToEUI64CanonicalIETF, convertMacToLinkLocalIPv6, + convertMacToNumber, +} from './mac-address-converter.service'; + +describe('mac-address-converter', () => { + it('Convert MAC Address to given format', async () => { + expect(convertMacCanonical('')).to.equal(''); + + const macValue = '00:0a:95:9d:68:16'; + + expect(convertMacCanonicalIETF(macValue)).to.equal('00:0a:95:9d:68:16'); + expect(convertMacCanonical(macValue)).to.equal('00.0a.95.9d.68.16'); + expect(convertMacCanonicalIEEE(macValue)).to.equal('00-0A-95-9D-68-16'); + expect(convertMacCISCO(macValue)).to.equal('000a.959d.6816'); + + expect(convertMacToEUI64CanonicalIETF(macValue, true)).to.equal('02:0a:95:ff:fe:9d:68:16'); // NOSONAR + expect(convertMacToEUI64CanonicalIETF(macValue, false)).to.equal('00:0a:95:ff:fe:9d:68:16'); // NOSONAR + expect(convertMacToEUI64CanonicalIEEE(macValue, true)).to.equal('02-0A-95-FF-FE-9D-68-16'); + expect(convertMacToEUI64CanonicalIEEE(macValue, false)).to.equal('00-0A-95-FF-FE-9D-68-16'); + expect(convertMacToEUI64CISCO(macValue, true)).to.equal('020a.95ff.fe9d.6816'); + expect(convertMacToEUI64CISCO(macValue, false)).to.equal('000a.95ff.fe9d.6816'); + expect(convertMacToNumber(macValue)).to.equal(45459793942); + expect(convertMacToLinkLocalIPv6(macValue)).to.equal(':fe80::20a:95ff:fe9d:6816'); + }); +}); diff --git a/src/tools/mac-address-converter/mac-address-converter.service.ts b/src/tools/mac-address-converter/mac-address-converter.service.ts new file mode 100644 index 00000000..3783126d --- /dev/null +++ b/src/tools/mac-address-converter/mac-address-converter.service.ts @@ -0,0 +1,59 @@ +function convertMac(mac: string, group: number = 2, char: string = ':'): string { + mac = mac.replace(/[\W_]+/g, ''); + return mac.match(new RegExp(`.{1,${group}}`, 'g'))?.join(char) || ''; +} + +function convertMacToEUI64(mac: string, ipv6: boolean) { + const macIETF = convertMac(mac); + // Split the MAC address into an array of hex values + const macArray = macIETF.split(':').map(hex => Number.parseInt(hex, 16)); + + // For IPv6, invert the 7th bit of the first byte + if (ipv6) { + macArray[0] ^= 0x02; + } + + // Insert FFFE in the middle + const eui64Array = [ + macArray[0], macArray[1], macArray[2], + 0xFF, 0xFE, + macArray[3], macArray[4], macArray[5], + ]; + + // Convert the array to a colon-separated string + const eui64 = eui64Array.map(byte => byte.toString(16).padStart(2, '0')).join(':'); + + // Group into IPv6 EUI-64 format (XXXX:XXFF:FEXX:XXXX) + return eui64.replace(/:/g, '').match(/.{1,4}/g)!.join(':'); +} + +export function convertMacToEUI64CanonicalIETF(mac: string, ipv6: boolean) { + return convertMac(convertMacToEUI64(mac, ipv6).toLocaleLowerCase()); +} +export function convertMacToEUI64CanonicalIEEE(mac: string, ipv6: boolean) { + return convertMac(convertMacToEUI64(mac, ipv6).toLocaleUpperCase(), 2, '-'); +} +export function convertMacToEUI64CISCO(mac: string, ipv6: boolean) { + return convertMac(convertMacToEUI64(mac, ipv6).toLocaleLowerCase(), 4, '.'); +} +export function convertMacToLinkLocalIPv6(mac: string) { + return `:fe80::${convertMac(convertMacToEUI64(mac, true).toLocaleLowerCase(), 4, ':').replace(/^0/g, '')}`; +} + +export function convertMacToNumber(mac: string) { + mac = mac.replace(/[\W_]+/g, ''); + return Number.parseInt(mac, 16); +} + +export function convertMacCanonicalIETF(mac: string): string { + return convertMac(mac.toLocaleLowerCase()); +}; +export function convertMacCanonical(mac: string): string { + return convertMac(mac, 2, '.'); +}; +export function convertMacCanonicalIEEE(mac: string): string { + return convertMac(mac.toLocaleUpperCase(), 2, '-'); +}; +export function convertMacCISCO(mac: string): string { + return convertMac(mac.toLocaleLowerCase(), 4, '.'); +}; diff --git a/src/tools/mac-address-converter/mac-address-converter.vue b/src/tools/mac-address-converter/mac-address-converter.vue new file mode 100644 index 00000000..4e49c9d5 --- /dev/null +++ b/src/tools/mac-address-converter/mac-address-converter.vue @@ -0,0 +1,101 @@ + + +