Skip to content

Commit e897525

Browse files
committed
Merge branch 'feat/mac-address-converter' into chore/all-my-stuffs
# Conflicts: # src/tools/index.ts
2 parents 48a51cb + c45a055 commit e897525

File tree

5 files changed

+214
-0
lines changed

5 files changed

+214
-0
lines changed

src/tools/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
3333
import { tool as asciiTextDrawer } from './ascii-text-drawer';
3434
import { tool as daysCalculator } from './days-calculator';
3535
import { tool as dateDurationCalculator } from './date-duration-calculator';
36+
import { tool as macAddressConverter } from './mac-address-converter';
37+
import { tool as asciiTextDrawer } from './ascii-text-drawer';
3638
import { tool as textToUnicode } from './text-to-unicode';
3739
import { tool as certificateKeyParser } from './certificate-key-parser';
3840
import { tool as crcCalculator } from './crc-calculator';
@@ -280,6 +282,8 @@ export const toolsByCategory: ToolCategory[] = [
280282
outlookParser,
281283
integersToIp,
282284
ipGeoLocation,
285+
macAddressConverter,
286+
ipv6UlaGenerator,
283287
],
284288
},
285289
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Devices } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'MAC Address Converter',
6+
path: '/mac-address-converter',
7+
description: 'Change the format of a MAC address and chose between different formats (EUI-48, EUI-64, IPv6)',
8+
keywords: [
9+
'converter',
10+
'mac',
11+
'address',
12+
'format',
13+
'link-local',
14+
'ipv6',
15+
'eui-48',
16+
'eui-64',
17+
],
18+
component: () => import('./mac-address-converter.vue'),
19+
icon: Devices,
20+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, it } from 'vitest';
2+
import {
3+
convertMacCISCO, convertMacCanonical,
4+
convertMacCanonicalIEEE, convertMacCanonicalIETF,
5+
convertMacToEUI64CISCO, convertMacToEUI64CanonicalIEEE,
6+
convertMacToEUI64CanonicalIETF, convertMacToLinkLocalIPv6,
7+
convertMacToNumber,
8+
} from './mac-address-converter.service';
9+
10+
describe('mac-address-converter', () => {
11+
it('Convert MAC Address to given format', async () => {
12+
expect(convertMacCanonical('')).to.equal('');
13+
14+
const macValue = '00:0a:95:9d:68:16';
15+
16+
expect(convertMacCanonicalIETF(macValue)).to.equal('00:0a:95:9d:68:16');
17+
expect(convertMacCanonical(macValue)).to.equal('00.0a.95.9d.68.16');
18+
expect(convertMacCanonicalIEEE(macValue)).to.equal('00-0A-95-9D-68-16');
19+
expect(convertMacCISCO(macValue)).to.equal('000a.959d.6816');
20+
21+
expect(convertMacToEUI64CanonicalIETF(macValue, true)).to.equal('02:0a:95:ff:fe:9d:68:16'); // NOSONAR
22+
expect(convertMacToEUI64CanonicalIETF(macValue, false)).to.equal('00:0a:95:ff:fe:9d:68:16'); // NOSONAR
23+
expect(convertMacToEUI64CanonicalIEEE(macValue, true)).to.equal('02-0A-95-FF-FE-9D-68-16');
24+
expect(convertMacToEUI64CanonicalIEEE(macValue, false)).to.equal('00-0A-95-FF-FE-9D-68-16');
25+
expect(convertMacToEUI64CISCO(macValue, true)).to.equal('020a.95ff.fe9d.6816');
26+
expect(convertMacToEUI64CISCO(macValue, false)).to.equal('000a.95ff.fe9d.6816');
27+
expect(convertMacToNumber(macValue)).to.equal(45459793942);
28+
expect(convertMacToLinkLocalIPv6(macValue)).to.equal(':fe80::20a:95ff:fe9d:6816');
29+
});
30+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
function convertMac(mac: string, group: number = 2, char: string = ':'): string {
2+
mac = mac.replace(/[\W_]+/g, '');
3+
return mac.match(new RegExp(`.{1,${group}}`, 'g'))?.join(char) || '';
4+
}
5+
6+
function convertMacToEUI64(mac: string, ipv6: boolean) {
7+
const macIETF = convertMac(mac);
8+
// Split the MAC address into an array of hex values
9+
const macArray = macIETF.split(':').map(hex => Number.parseInt(hex, 16));
10+
11+
// For IPv6, invert the 7th bit of the first byte
12+
if (ipv6) {
13+
macArray[0] ^= 0x02;
14+
}
15+
16+
// Insert FFFE in the middle
17+
const eui64Array = [
18+
macArray[0], macArray[1], macArray[2],
19+
0xFF, 0xFE,
20+
macArray[3], macArray[4], macArray[5],
21+
];
22+
23+
// Convert the array to a colon-separated string
24+
const eui64 = eui64Array.map(byte => byte.toString(16).padStart(2, '0')).join(':');
25+
26+
// Group into IPv6 EUI-64 format (XXXX:XXFF:FEXX:XXXX)
27+
return eui64.replace(/:/g, '').match(/.{1,4}/g)!.join(':');
28+
}
29+
30+
export function convertMacToEUI64CanonicalIETF(mac: string, ipv6: boolean) {
31+
return convertMac(convertMacToEUI64(mac, ipv6).toLocaleLowerCase());
32+
}
33+
export function convertMacToEUI64CanonicalIEEE(mac: string, ipv6: boolean) {
34+
return convertMac(convertMacToEUI64(mac, ipv6).toLocaleUpperCase(), 2, '-');
35+
}
36+
export function convertMacToEUI64CISCO(mac: string, ipv6: boolean) {
37+
return convertMac(convertMacToEUI64(mac, ipv6).toLocaleLowerCase(), 4, '.');
38+
}
39+
export function convertMacToLinkLocalIPv6(mac: string) {
40+
return `:fe80::${convertMac(convertMacToEUI64(mac, true).toLocaleLowerCase(), 4, ':').replace(/^0/g, '')}`;
41+
}
42+
43+
export function convertMacToNumber(mac: string) {
44+
mac = mac.replace(/[\W_]+/g, '');
45+
return Number.parseInt(mac, 16);
46+
}
47+
48+
export function convertMacCanonicalIETF(mac: string): string {
49+
return convertMac(mac.toLocaleLowerCase());
50+
};
51+
export function convertMacCanonical(mac: string): string {
52+
return convertMac(mac, 2, '.');
53+
};
54+
export function convertMacCanonicalIEEE(mac: string): string {
55+
return convertMac(mac.toLocaleUpperCase(), 2, '-');
56+
};
57+
export function convertMacCISCO(mac: string): string {
58+
return convertMac(mac.toLocaleLowerCase(), 4, '.');
59+
};
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<script setup lang="ts">
2+
import InputCopyable from '../../components/InputCopyable.vue';
3+
import {
4+
convertMacCISCO, convertMacCanonical,
5+
convertMacCanonicalIEEE, convertMacCanonicalIETF,
6+
convertMacToEUI64CISCO, convertMacToEUI64CanonicalIEEE,
7+
convertMacToEUI64CanonicalIETF, convertMacToLinkLocalIPv6,
8+
convertMacToNumber,
9+
} from './mac-address-converter.service';
10+
11+
const input = ref('AA:BB:CC:DD:EE:FF');
12+
13+
const formats = computed(() => [
14+
{
15+
label: 'Canonical IETF Format:',
16+
value: convertMacCanonicalIETF(input.value),
17+
},
18+
{
19+
label: 'Canonical Format:',
20+
value: convertMacCanonical(input.value),
21+
},
22+
{
23+
label: 'Canonical IEEE Format:',
24+
value: convertMacCanonicalIEEE(input.value),
25+
},
26+
{
27+
label: 'Cisco:',
28+
value: convertMacCISCO(input.value),
29+
},
30+
{
31+
label: 'Hex:',
32+
value: convertMacToNumber(input.value).toString(16),
33+
},
34+
{
35+
label: 'Decimal:',
36+
value: convertMacToNumber(input.value).toString(10),
37+
},
38+
{
39+
label: 'EUI-64 Canonical IETF Format:',
40+
value: convertMacToEUI64CanonicalIETF(input.value, false),
41+
},
42+
{
43+
label: 'EUI-64 Canonical IEEE Format:',
44+
value: convertMacToEUI64CanonicalIEEE(input.value, false),
45+
},
46+
{
47+
label: 'EUI-64 Cisco:',
48+
value: convertMacToEUI64CISCO(input.value, false),
49+
},
50+
{
51+
label: 'EUI-64 IPv6 Canonical IETF Format:',
52+
value: convertMacToEUI64CanonicalIETF(input.value, true),
53+
},
54+
{
55+
label: 'EUI-64 IPv6 Canonical IEEE Format:',
56+
value: convertMacToEUI64CanonicalIEEE(input.value, true),
57+
},
58+
{
59+
label: 'EUI-64 IPv6 Cisco:',
60+
value: convertMacToEUI64CISCO(input.value, true),
61+
},
62+
{
63+
label: 'Link-Local IPv6:',
64+
value: convertMacToLinkLocalIPv6(input.value),
65+
},
66+
]);
67+
68+
const inputLabelAlignmentConfig = {
69+
labelPosition: 'left',
70+
labelWidth: '120px',
71+
labelAlign: 'right',
72+
};
73+
</script>
74+
75+
<template>
76+
<c-card>
77+
<c-input-text
78+
v-model:value="input"
79+
label="MAC address:"
80+
size="large"
81+
placeholder="Type a MAC address"
82+
clearable
83+
autocomplete="off"
84+
autocorrect="off"
85+
autocapitalize="off"
86+
spellcheck="false"
87+
mb-5
88+
/>
89+
90+
<div my-16px divider />
91+
92+
<InputCopyable
93+
v-for="format in formats"
94+
:key="format.label"
95+
:value="format.value"
96+
:label="format.label"
97+
v-bind="inputLabelAlignmentConfig"
98+
mb-1
99+
/>
100+
</c-card>
101+
</template>

0 commit comments

Comments
 (0)