Subversion Repositories uuid_mac_utils

Rev

Rev 19 | Rev 21 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 19 Rev 20
Line 1... Line 1...
1
<?php
1
<?php
2
 
2
 
3
/*
3
/*
4
 * MAC (EUI-48 and EUI-64) utils for PHP
4
 * MAC (EUI-48 and EUI-64) utils for PHP
5
 * Copyright 2017 - 2023 Daniel Marschall, ViaThinkSoft
5
 * Copyright 2017 - 2023 Daniel Marschall, ViaThinkSoft
6
 * Version 2023-05-01
6
 * Version 2023-05-03
7
 *
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
10
 * You may obtain a copy of the License at
11
 *
11
 *
Line 24... Line 24...
24
// - https://en.m.wikipedia.org/wiki/Organizationally_unique_identifier
24
// - https://en.m.wikipedia.org/wiki/Organizationally_unique_identifier
25
 
25
 
26
const IEEE_MAC_REGISTRY = __DIR__ . '/../web-data';
26
const IEEE_MAC_REGISTRY = __DIR__ . '/../web-data';
27
 
27
 
28
/**
28
/**
29
 * Checks if a MAC, EUI, ELI, or IPv6-Link-Local address is valid
29
 * Checks if a MAC, EUI, ELI, AAI, SAI, or IPv6-Link-Local address is valid
30
 * @param string $mac MAC, EUI, or IPv6-Link-Local Address
30
 * @param string $mac MAC, EUI, or IPv6-Link-Local Address
31
 * @return bool True if it is valid
31
 * @return bool True if it is valid
32
 */
32
 */
33
function mac_valid(string $mac): bool {
33
function mac_valid(string $mac): bool {
34
        $tmp = ipv6linklocal_to_mac48($mac);
34
        $tmp = ipv6linklocal_to_mac48($mac);
Line 43... Line 43...
43
 
43
 
44
        return ($mac === '');
44
        return ($mac === '');
45
}
45
}
46
 
46
 
47
/**
47
/**
48
 * Returns the amount of bits of a MAC, EUI, or ELI
48
 * Returns the amount of bits of a MAC, EUI, ELI, AAI, or SAI
49
 * @param string $mac
49
 * @param string $mac
50
 * @return false|int
50
 * @return false|int
51
 */
51
 */
52
function eui_bits(string $mac) {
52
function eui_bits(string $mac) {
53
        if (!mac_valid($mac)) return false;
53
        if (!mac_valid($mac)) return false;
54
        $mac = mac_canonize($mac, '');
54
        $mac = mac_canonize($mac, '');
55
        return (int)(strlen($mac)*4);
55
        return (int)(strlen($mac)*4);
56
}
56
}
57
 
57
 
58
/**
58
/**
59
 * Canonizes a MAC, EUI, ELI, or IPv6-Link-Local address
59
 * Canonizes a MAC, EUI, ELI, AAI, SAI, or IPv6-Link-Local address
60
 * @param string $mac MAC, EUI, ELI, or IPv6-Link-Local Address
60
 * @param string $mac MAC, EUI, ELI, or IPv6-Link-Local Address
61
 * @param string $delimiter Desired delimiter for inserting between each octet
61
 * @param string $delimiter Desired delimiter for inserting between each octet
62
 * @return string|false The canonized address (Note: IPv6-Link-Local becomes EUI-64)
62
 * @return string|false The canonized address (Note: IPv6-Link-Local becomes EUI-64)
63
 */
63
 */
64
function mac_canonize(string $mac, string $delimiter="-") {
64
function mac_canonize(string $mac, string $delimiter="-") {
Line 119... Line 119...
119
                        if ($n == 0) continue;
119
                        if ($n == 0) continue;
120
                        else if ($n == 1) $out .= sprintf("%-32s %s\n", "Address of registrant:", $y);
120
                        else if ($n == 1) $out .= sprintf("%-32s %s\n", "Address of registrant:", $y);
121
                        else if ($n >= 2) $out .= sprintf("%-32s %s\n", "", $y);
121
                        else if ($n >= 2) $out .= sprintf("%-32s %s\n", "", $y);
122
                }
122
                }
123
 
123
 
124
                // TODO: also print the date of last update of the OUI files
-
 
125
 
-
 
126
                return $out;
124
                return $out;
127
        }
125
        }
128
 
126
 
129
        return false;
127
        return false;
130
}
128
}
Line 136... Line 134...
136
 */
134
 */
137
function eui64_to_eui48(string $eui64) {
135
function eui64_to_eui48(string $eui64) {
138
        if (!mac_valid($eui64)) return false;
136
        if (!mac_valid($eui64)) return false;
139
        $eui64 = mac_canonize($eui64, '');
137
        $eui64 = mac_canonize($eui64, '');
140
        if (eui_bits($eui64) == 48) return mac_canonize($eui64);
138
        if (eui_bits($eui64) == 48) return mac_canonize($eui64);
141
        if ($eui64[1] == 'A') return false; // do not allow ELI-64
139
        if (($eui64[1] != '0') && ($eui64[1] != '4') && ($eui64[1] != '8') && ($eui64[1] != 'C')) return false; // only allow EUI
142
 
140
 
143
        if (substr($eui64, 6, 4) == 'FFFF') {
141
        if (substr($eui64, 6, 4) == 'FFFF') {
144
                // EUI-64 to MAC-48
142
                // EUI-64 to MAC-48
145
                return mac_canonize(substr($eui64, 0, 6).substr($eui64, 10, 6));
143
                return mac_canonize(substr($eui64, 0, 6).substr($eui64, 10, 6));
146
        } else if (substr($eui64, 6, 4) == 'FFFE') {
144
        } else if (substr($eui64, 6, 4) == 'FFFE') {
Line 166... Line 164...
166
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
164
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
167
        //       MAC48-to-EUI64 Encapsulation uses 0xFFFF middle part
165
        //       MAC48-to-EUI64 Encapsulation uses 0xFFFF middle part
168
        if (!mac_valid($mac48)) return false;
166
        if (!mac_valid($mac48)) return false;
169
        $mac48 = mac_canonize($mac48, '');
167
        $mac48 = mac_canonize($mac48, '');
170
        if (eui_bits($mac48) == 64) return mac_canonize($mac48);
168
        if (eui_bits($mac48) == 64) return mac_canonize($mac48);
171
        if ($mac48[1] == 'A') return false; // do not allow ELI-48
169
        if (($mac48[1] != '0') && ($mac48[1] != '4') && ($mac48[1] != '8') && ($mac48[1] != 'C')) return false; // only allow EUI
172
 
170
 
173
        $eui64 = substr($mac48, 0, 6).'FFFF'.substr($mac48, 6, 6);
171
        $eui64 = substr($mac48, 0, 6).'FFFF'.substr($mac48, 6, 6);
174
        return mac_canonize($eui64);
172
        return mac_canonize($eui64);
175
}
173
}
176
 
174
 
Line 183... Line 181...
183
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
181
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
184
        //       EUI48-to-EUI64 Encapsulation uses 0xFFFF middle part
182
        //       EUI48-to-EUI64 Encapsulation uses 0xFFFF middle part
185
        if (!mac_valid($eui48)) return false;
183
        if (!mac_valid($eui48)) return false;
186
        $eui48 = mac_canonize($eui48, '');
184
        $eui48 = mac_canonize($eui48, '');
187
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
185
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
188
        if ($eui48[1] == 'A') return false; // do not allow ELI-48
186
        if (($eui48[1] != '0') && ($eui48[1] != '4') && ($eui48[1] != '8') && ($eui48[1] != 'C')) return false; // only allow EUI
189
 
187
 
190
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
188
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
191
        return mac_canonize($eui64);
189
        return mac_canonize($eui64);
192
}
190
}
193
 
191
 
Line 200... Line 198...
200
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
198
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
201
        //       EUI48-to-ModifiedEUI64 Encapsulation uses 0xFFFE middle part (SIC! This was a mistake by IETF, since it should actually be 0xFFFF!)
199
        //       EUI48-to-ModifiedEUI64 Encapsulation uses 0xFFFE middle part (SIC! This was a mistake by IETF, since it should actually be 0xFFFF!)
202
        if (!mac_valid($eui48)) return false;
200
        if (!mac_valid($eui48)) return false;
203
        $eui48 = mac_canonize($eui48, '');
201
        $eui48 = mac_canonize($eui48, '');
204
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
202
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
205
        if ($eui48[1] == 'A') return false; // do not allow ELI-48
203
        if (($eui48[1] != '0') && ($eui48[1] != '4') && ($eui48[1] != '8') && ($eui48[1] != 'C')) return false; // only allow EUI
206
 
204
 
207
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
205
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
208
 
206
 
209
        $eui64[1] = dechex(hexdec($eui64[1]) | 2); // flip seventh bit
207
        $eui64[1] = dechex(hexdec($eui64[1]) | 2); // flip seventh bit
210
 
208
 
Line 255... Line 253...
255
        $mac = str_pad($mac, 16, '0', STR_PAD_LEFT);
253
        $mac = str_pad($mac, 16, '0', STR_PAD_LEFT);
256
        return strtolower('fe80::'.substr($mac,0, 4).':'.substr($mac,4, 4).':'.substr($mac,8, 4).':'.substr($mac,12, 4));
254
        return strtolower('fe80::'.substr($mac,0, 4).':'.substr($mac,4, 4).':'.substr($mac,8, 4).':'.substr($mac,12, 4));
257
}
255
}
258
 
256
 
259
/**
257
/**
260
 * Prints information about an IPv6-Link-Local address, MAC, EUI, or ELI.
-
 
261
 * @param string $mac IPv6-Link-Local address, MAC, EUI, or ELI
258
 * @param string $mac
262
 * @return void
259
 * @return string
263
 * @throws Exception
260
 * @throws Exception
264
 */
261
 */
265
function decode_mac(string $mac) {
262
function mac_type(string $mac): string {
266
        echo sprintf("%-32s %s\n", "Input:", $mac);
-
 
267
 
-
 
268
        // Format MAC for machine readability
263
        // Format MAC for machine readability
269
        $mac = mac_canonize($mac, '');
264
        $mac = mac_canonize($mac, '');
270
 
265
 
-
 
266
        /**
-
 
267
         *
-
 
268
         *      ZYXM
-
 
269
         * 0    0000    EUI (OUI)
-
 
270
         * 1    0001
-
 
271
         * 2    0010    AAI
-
 
272
         * 3    0011
-
 
273
         * 4    0100    EUI (OUI)
-
 
274
         * 5    0101
-
 
275
         * 6    0110    Reserved
-
 
276
         * 7    0111
-
 
277
         * 8    1000    EUI (OUI)
-
 
278
         * 9    1001
-
 
279
         * A    1010    ELI (CID)
-
 
280
         * B    1011
-
 
281
         * C    1100    EUI (OUI)
-
 
282
         * D    1101
-
 
283
         * E    1110    SAI
-
 
284
         * F    1111
-
 
285
         *
-
 
286
         */
-
 
287
 
271
        $type = '';
288
        $type = '';
272
        if ($mac[1] == 'A') {
-
 
273
                // An ELI is based on a CID-24
-
 
274
                // A CID has ZYXM bits set to 1010 (0b1010 = 0xA)
-
 
275
                // Since X=1 (U/L=1), the CID cannot be used to form a universal UAA MAC (only a local LAA MAC)
-
 
276
                $type = 'ELI-'.eui_bits($mac);;
-
 
277
        } else {
-
 
278
                $tmp = ipv6linklocal_to_mac48($mac);
289
        $tmp = ipv6linklocal_to_mac48($mac);
279
                if ($tmp !== false) {
290
        if ($tmp !== false) {
280
                        $mac = $tmp;
291
                $mac = $tmp;
281
                        $type = 'IPv6-Link-Local';
292
                $type = 'IPv6-Link-Local';
282
                }
293
        }
283
                if (!mac_valid($mac)) throw new Exception("Invalid MAC address");
294
        if (!mac_valid($mac)) throw new Exception("Invalid MAC address");
284
                if ($tmp === false) {
295
        if ($tmp === false) {
-
 
296
                if ($mac[1] == '2') {
-
 
297
                        /*
-
 
298
                         * AAI: Administratively Assigned Identifier
-
 
299
                         * Administrators who wish to assign local MAC addresses in an
-
 
300
                         * arbitrary fashion (for example, randomly) and yet maintain
-
 
301
                         * compatibility with other assignment protocols operating under the
-
 
302
                         * SLAP on the same LAN may assign a local MAC address as AAI.
-
 
303
                         */
-
 
304
                        $type = 'AAI-' . eui_bits($mac).' (Administratively Assigned Identifier)';
-
 
305
                } else if ($mac[1] == '6') {
-
 
306
                        /*
-
 
307
                         * Reserved
-
 
308
                         * may be administratively used and assigned in accordance with the
-
 
309
                         * considerations specified for AAI usage, without effect on SLAP
-
 
310
                         * assignments. However, administrators should be cognizant of
-
 
311
                         * possible future specifications… that would render administrative
-
 
312
                         * assignment incompatible with the SLAP.
-
 
313
                         */
-
 
314
                        $type = 'Reserved-' . eui_bits($mac);
-
 
315
                } else if ($mac[1] == 'A') {
-
 
316
                        /*
-
 
317
                         * ELI: Extended Local Identifier
-
 
318
                         * An ELI is based on a 24 bit CID
-
 
319
                         * A CID has ZYXM bits set to 1010 (0b1010 = 0xA)
-
 
320
                         * Since X=1 (U/L=1), the CID cannot be used to form a universal UAA MAC (only a local LAA MAC)
-
 
321
                         */
-
 
322
                        $type = 'ELI-' . eui_bits($mac).' (Extended Local Identifier)';
-
 
323
                } else if ($mac[1] == 'E') {
-
 
324
                        /*
-
 
325
                         * SAI: Standard Assigned Identifier
-
 
326
                         * Specification of the use of the SAI quadrant for SLAP address
-
 
327
                         * assignments is reserved for the standard forthcoming from IEEE
-
 
328
                         * P802.1CQ.
-
 
329
                         * An SAI is assigned by a protocol specified in an IEEE 802 standard.
-
 
330
                         * Multiple protocols for assigning SAI may be specified within various
-
 
331
                         * IEEE 802 standards. Coexistence of such protocols may be supported
-
 
332
                         * by restricting each to assignments within a subspace of SAI space.
-
 
333
                         * In some cases, an SAI assignment protocol may assign the SAI to convey
-
 
334
                         * specific information. Such information may be interpreted by receivers
-
 
335
                         * and bridges that recognize the specific SAI assignment protocol, as
-
 
336
                         * identified by the subspace of the SAI. The functionality of receivers
-
 
337
                         * and bridges that do not recognize the protocol is not affected.
-
 
338
                         */
-
 
339
                        $type = 'SAI-' . eui_bits($mac).' (Standard Assigned Identifier)';
-
 
340
                } else if ((hexdec($mac[1])&1) == 1) {
-
 
341
                        $type = 'Multicast MAC-'.eui_bits($mac);
-
 
342
                } else if (($mac[1] == '0') || ($mac[1] == '4') || ($mac[1] == '8') || ($mac[1] == 'C')) {
-
 
343
                        /*
-
 
344
                         * Extended Unique Identifier
-
 
345
                         * Based on an OUI-24, OUI-28, or OUI-36
-
 
346
                         */
285
                        if (eui_bits($mac) == 48) {
347
                        if (eui_bits($mac) == 48) {
-
 
348
                                // The name "MAC-48" has been deprecated by IEEE
286
                                $type = 'MAC-48 (network hardware) or EUI-48 (other devices and software)';
349
                                //$type = 'MAC-48 (network hardware) or EUI-48 (other devices and software)';
-
 
350
                                $type = 'EUI-48 (Extended Unique Identifier)';
287
                        } else if (eui_bits($mac) == 64) {
351
                        } else if (eui_bits($mac) == 64) {
288
                                if (substr($mac,6,4) == 'FFFE') {
352
                                if (substr($mac, 6, 4) == 'FFFE') {
289
                                        if ((hexdec($mac[1])&2) == 2) {
353
                                        if ((hexdec($mac[1]) & 2) == 2) {
290
                                                $type = 'EUI-64 (MAC/EUI-48 to Modified EUI-64 Encapsulation)';
354
                                                $type = 'EUI-64 (Extended Unique Identifier, MAC/EUI-48 to Modified EUI-64 Encapsulation)';
291
                                        } else {
355
                                        } else {
292
                                                $type = 'EUI-64 (EUI-48 to EUI-64 Encapsulation)';
356
                                                $type = 'EUI-64 (Extended Unique Identifier, EUI-48 to EUI-64 Encapsulation)';
293
                                        }
357
                                        }
294
                                } else if (substr($mac,6,4) == 'FFFF') {
358
                                } else if (substr($mac, 6, 4) == 'FFFF') {
295
                                        $type = 'EUI-64 (MAC-48 to EUI-64 Encapsulation)';
359
                                        $type = 'EUI-64 (Extended Unique Identifier, MAC-48 to EUI-64 Encapsulation)';
296
                                } else {
360
                                } else {
297
                                        $type = 'EUI-64 (Regular)';
361
                                        $type = 'EUI-64 (Extended Unique Identifier)';
298
                                }
362
                                }
299
                        } else {
363
                        } else {
300
                                assert(false); /** @phpstan-ignore-line */
364
                                assert(false); /** @phpstan-ignore-line */
301
                        }
365
                        }
302
                }
366
                }
303
        }
367
        }
-
 
368
        return $type;
-
 
369
}
-
 
370
 
-
 
371
/**
-
 
372
 * Prints information about an IPv6-Link-Local address, MAC, EUI, ELI, AAI, or SAI.
-
 
373
 * @param string $mac IPv6-Link-Local address, MAC, EUI, ELI, AAI, or SAI
-
 
374
 * @return void
-
 
375
 * @throws Exception
-
 
376
 */
-
 
377
function decode_mac(string $mac) {
-
 
378
        echo sprintf("%-32s %s\n", "Input:", $mac);
-
 
379
 
-
 
380
        // Format MAC for machine readability
-
 
381
        $mac = mac_canonize($mac, '');
-
 
382
 
-
 
383
        $type = mac_type($mac);
304
        echo sprintf("%-32s %s\n", "Type:", $type);
384
        echo sprintf("%-32s %s\n", "Type:", $type);
305
 
385
 
306
        echo "\n";
386
        echo "\n";
307
 
387
 
308
        // Show various representations
388
        // Show various representations
309
        if ($mac[1] == 'A') {
389
        if ($mac[1] == 'A') {
310
                // Note: There does not seem to exist an algorithm for converting ELI-48 <=> ELI-64
390
                // Note: There does not seem to exist an algorithm for encapsulating/converting ELI-48 <=> ELI-64
311
                echo sprintf("%-32s %s\n", "ELI-".eui_bits($mac).":", mac_canonize($mac));
391
                echo sprintf("%-32s %s\n", "ELI-".eui_bits($mac).":", mac_canonize($mac));
312
                $mac48 = eui64_to_eui48($mac);
392
                $mac48 = eui64_to_eui48($mac);
313
                echo sprintf("%-32s %s\n", "MAC-48 (Local):", (eui_bits($mac48) != 48) ? 'Not available' : $mac48);
393
                echo sprintf("%-32s %s\n", "MAC-48 (Local):", (eui_bits($mac48) != 48) ? 'Not available' : $mac48);
314
        } else {
394
        } else if (($mac[1] == '0') || ($mac[1] == '4') || ($mac[1] == '8') || ($mac[1] == 'C')) {
315
                $eui48 = eui64_to_eui48($mac);
395
                $eui48 = eui64_to_eui48($mac);
316
                echo sprintf("%-32s %s\n", "EUI-48 or MAC-48:", (eui_bits($eui48) != 48) ? 'Not available' : $eui48);
396
                echo sprintf("%-32s %s\n", "EUI-48 or MAC-48:", (eui_bits($eui48) != 48) ? 'Not available' : $eui48);
317
                if (eui_bits($mac) == 48) {
397
                if (eui_bits($mac) == 48) {
318
                        $eui64 = mac48_to_eui64($mac);
398
                        $eui64 = mac48_to_eui64($mac);
319
                        echo sprintf("%-32s %s\n", "EUI-64:", ((eui_bits($eui64) != 64) ? 'Not available' : $eui64).' (MAC-48 to EUI-64 Encapsulation)');
399
                        echo sprintf("%-32s %s\n", "EUI-64:", ((eui_bits($eui64) != 64) ? 'Not available' : $eui64).' (MAC-48 to EUI-64 Encapsulation)');