Subversion Repositories uuid_mac_utils

Rev

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

Rev 17 Rev 18
Line 16... Line 16...
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
18
 * limitations under the License.
19
 */
19
 */
20
 
20
 
-
 
21
// Very good resources for information about OUI, EUI, MAC, ...
-
 
22
// - https://mac-address.alldatafeeds.com/faq#how-to-recognise-mac-address-application
-
 
23
// - https://standards.ieee.org/wp-content/uploads/import/documents/tutorials/eui.pdf
-
 
24
// - https://en.m.wikipedia.org/wiki/Organizationally_unique_identifier
-
 
25
 
21
const IEEE_MAC_REGISTRY = __DIR__ . '/../web-data';
26
const IEEE_MAC_REGISTRY = __DIR__ . '/../web-data';
22
 
27
 
23
/**
28
/**
24
 * Checks if a MAC, EUI, or IPv6-LinkLocal address is valid
29
 * Checks if a MAC, EUI, ELI, or IPv6-LinkLocal address is valid
25
 * @param string $mac MAC, EUI, or IPv6-LinkLocal Address
30
 * @param string $mac MAC, EUI, or IPv6-LinkLocal Address
26
 * @return bool True if it is valid
31
 * @return bool True if it is valid
27
 */
32
 */
28
function mac_valid(string $mac): bool {
33
function mac_valid(string $mac): bool {
29
        $tmp = ipv6linklocal_to_mac48($mac);
34
        $tmp = ipv6linklocal_to_mac48($mac);
Line 38... Line 43...
38
 
43
 
39
        return ($mac === '');
44
        return ($mac === '');
40
}
45
}
41
 
46
 
42
/**
47
/**
43
 * Returns the amount of bits of a MAC or EUI
48
 * Returns the amount of bits of a MAC, EUI, or ELI
44
 * @param string $mac
49
 * @param string $mac
45
 * @return false|int
50
 * @return false|int
46
 */
51
 */
47
function eui_bits(string $mac) {
52
function eui_bits(string $mac) {
48
        if (!mac_valid($mac)) return false;
53
        if (!mac_valid($mac)) return false;
49
        $mac = mac_canonize($mac, '');
54
        $mac = mac_canonize($mac, '');
50
        return (int)(strlen($mac)*4);
55
        return (int)(strlen($mac)*4);
51
}
56
}
52
 
57
 
53
/**
58
/**
54
 * Canonizes a MAC, EUI, or IPv6-LinkLocal address
59
 * Canonizes a MAC, EUI, ELI, or IPv6-LinkLocal address
55
 * @param string $mac MAC, EUI, or IPv6-LinkLocal Address
60
 * @param string $mac MAC, EUI, ELI, or IPv6-LinkLocal Address
56
 * @param string $delimiter Desired delimiter for inserting between each octet
61
 * @param string $delimiter Desired delimiter for inserting between each octet
57
 * @return string|false The canonized address (Note: IPv6-Linklocal becomes EUI-64)
62
 * @return string|false The canonized address (Note: IPv6-Linklocal becomes EUI-64)
58
 */
63
 */
59
function mac_canonize(string $mac, string $delimiter="-") {
64
function mac_canonize(string $mac, string $delimiter="-") {
60
        if (!mac_valid($mac)) return false;
65
        if (!mac_valid($mac)) return false;
Line 131... Line 136...
131
 */
136
 */
132
function eui64_to_eui48(string $eui64) {
137
function eui64_to_eui48(string $eui64) {
133
        if (!mac_valid($eui64)) return false;
138
        if (!mac_valid($eui64)) return false;
134
        $eui64 = mac_canonize($eui64, '');
139
        $eui64 = mac_canonize($eui64, '');
135
        if (eui_bits($eui64) == 48) return mac_canonize($eui64);
140
        if (eui_bits($eui64) == 48) return mac_canonize($eui64);
-
 
141
        if ($eui64[1] == 'A') return false; // do not allow ELI-64
136
 
142
 
137
        if (substr($eui64, 6, 4) == 'FFFF') {
143
        if (substr($eui64, 6, 4) == 'FFFF') {
138
                // EUI-64 to MAC-48
144
                // EUI-64 to MAC-48
139
                return mac_canonize(substr($eui64, 0, 6).substr($eui64, 10, 6));
145
                return mac_canonize(substr($eui64, 0, 6).substr($eui64, 10, 6));
140
        } else if (substr($eui64, 6, 4) == 'FFFE') {
146
        } else if (substr($eui64, 6, 4) == 'FFFE') {
Line 160... Line 166...
160
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
166
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
161
        //       MAC48-to-EUI64 Encapsulation uses 0xFFFF middle part
167
        //       MAC48-to-EUI64 Encapsulation uses 0xFFFF middle part
162
        if (!mac_valid($mac48)) return false;
168
        if (!mac_valid($mac48)) return false;
163
        $mac48 = mac_canonize($mac48, '');
169
        $mac48 = mac_canonize($mac48, '');
164
        if (eui_bits($mac48) == 64) return mac_canonize($mac48);
170
        if (eui_bits($mac48) == 64) return mac_canonize($mac48);
-
 
171
        if ($mac48[1] == 'A') return false; // do not allow ELI-48
165
 
172
 
166
        $eui64 = substr($mac48, 0, 6).'FFFF'.substr($mac48, 6, 6);
173
        $eui64 = substr($mac48, 0, 6).'FFFF'.substr($mac48, 6, 6);
167
        return mac_canonize($eui64);
174
        return mac_canonize($eui64);
168
}
175
}
169
 
176
 
Line 176... Line 183...
176
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
183
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
177
        //       EUI48-to-EUI64 Encapsulation uses 0xFFFF middle part
184
        //       EUI48-to-EUI64 Encapsulation uses 0xFFFF middle part
178
        if (!mac_valid($eui48)) return false;
185
        if (!mac_valid($eui48)) return false;
179
        $eui48 = mac_canonize($eui48, '');
186
        $eui48 = mac_canonize($eui48, '');
180
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
187
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
-
 
188
        if ($eui48[1] == 'A') return false; // do not allow ELI-48
181
 
189
 
182
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
190
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
183
        return mac_canonize($eui64);
191
        return mac_canonize($eui64);
184
}
192
}
185
 
193
 
Line 192... Line 200...
192
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
200
        // Note: MAC-48 is used for network hardware; EUI-48 is used to identify other devices and software.
193
        //       EUI48-to-ModifiedEUI64 Encapsulation uses 0xFFFE middle part (SIC! This was a mistake by IETF, since it should actually be 0xFFFF!)
201
        //       EUI48-to-ModifiedEUI64 Encapsulation uses 0xFFFE middle part (SIC! This was a mistake by IETF, since it should actually be 0xFFFF!)
194
        if (!mac_valid($eui48)) return false;
202
        if (!mac_valid($eui48)) return false;
195
        $eui48 = mac_canonize($eui48, '');
203
        $eui48 = mac_canonize($eui48, '');
196
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
204
        if (eui_bits($eui48) == 64) return mac_canonize($eui48);
-
 
205
        if ($eui48[1] == 'A') return false; // do not allow ELI-48
197
 
206
 
198
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
207
        $eui64 = substr($eui48, 0, 6).'FFFE'.substr($eui48, 6, 6);
199
 
208
 
200
        $eui64[1] = dechex(hexdec($eui64[1]) | 2); // flip seventh bit
209
        $eui64[1] = dechex(hexdec($eui64[1]) | 2); // flip seventh bit
201
 
210
 
Line 246... Line 255...
246
        $mac = str_pad($mac, 16, '0', STR_PAD_LEFT);
255
        $mac = str_pad($mac, 16, '0', STR_PAD_LEFT);
247
        return strtolower('fe80::'.substr($mac,0, 4).':'.substr($mac,4, 4).':'.substr($mac,8, 4).':'.substr($mac,12, 4));
256
        return strtolower('fe80::'.substr($mac,0, 4).':'.substr($mac,4, 4).':'.substr($mac,8, 4).':'.substr($mac,12, 4));
248
}
257
}
249
 
258
 
250
/**
259
/**
251
 * Prints information about an IPv6-LinkLocal address, MAC, or EUI.
260
 * Prints information about an IPv6-LinkLocal address, MAC, EUI, or ELI.
252
 * @param string $mac IPv6-LinkLocal address, MAC, or EUI
261
 * @param string $mac IPv6-LinkLocal address, MAC, EUI, or ELI
253
 * @return void
262
 * @return void
254
 * @throws Exception
263
 * @throws Exception
255
 */
264
 */
256
function decode_mac(string $mac) {
265
function decode_mac(string $mac) {
257
        // Amazing website about MAC addresses: https://mac-address.alldatafeeds.com/faq#how-to-recognise-mac-address-application
-
 
258
 
-
 
259
        echo sprintf("%-32s %s\n", "Input:", $mac);
266
        echo sprintf("%-32s %s\n", "Input:", $mac);
260
 
267
 
261
        // Format MAC for machine readability
268
        // Format MAC for machine readability
262
        $mac = mac_canonize($mac, '');
269
        $mac = mac_canonize($mac, '');
263
 
270
 
264
        $type = '';
271
        $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 {
265
        $tmp = ipv6linklocal_to_mac48($mac);
278
                $tmp = ipv6linklocal_to_mac48($mac);
266
        if ($tmp !== false) {
279
                if ($tmp !== false) {
267
                $mac = $tmp;
280
                        $mac = $tmp;
268
                $type = 'IPv6-Link-Local';
281
                        $type = 'IPv6-Link-Local';
269
        }
282
                }
Line 285... Line 298...
285
                        }
298
                                }
286
                } else {
299
                        } else {
287
                        assert(false); /** @phpstan-ignore-line */
300
                                assert(false); /** @phpstan-ignore-line */
288
                }
301
                        }
289
        }
302
                }
-
 
303
        }
290
        echo sprintf("%-32s %s\n", "Type:", $type);
304
        echo sprintf("%-32s %s\n", "Type:", $type);
291
 
305
 
292
        echo "\n";
306
        echo "\n";
293
 
307
 
294
        // Show various representations
308
        // Show various representations
-
 
309
        if ($mac[1] == 'A') {
-
 
310
                // Note: There does not seem to exist an algorithm for converting ELI-48 <=> ELI-64
-
 
311
                echo sprintf("%-32s %s\n", "ELI-".eui_bits($mac).":", mac_canonize($mac));
-
 
312
        } else {
295
        $eui48 = eui64_to_eui48($mac);
313
                $eui48 = eui64_to_eui48($mac);
296
        echo sprintf("%-32s %s\n", "EUI-48:", (eui_bits($eui48) != 48) ? 'Not available' : $eui48);
314
                echo sprintf("%-32s %s\n", "EUI-48:", (eui_bits($eui48) != 48) ? 'Not available' : $eui48);
297
        if (eui_bits($mac) == 48) {
315
                if (eui_bits($mac) == 48) {
298
                $eui64 = mac48_to_eui64($mac);
316
                        $eui64 = mac48_to_eui64($mac);
299
                echo sprintf("%-32s %s\n", "EUI-64:", ((eui_bits($eui64) != 64) ? 'Not available' : $eui64).' (MAC-48 to EUI-64 Encapsulation)');
317
                        echo sprintf("%-32s %s\n", "EUI-64:", ((eui_bits($eui64) != 64) ? 'Not available' : $eui64).' (MAC-48 to EUI-64 Encapsulation)');
Line 305... Line 323...
305
                echo sprintf("%-32s %s\n", "IPv6 link local address:", $ipv6);
323
                        echo sprintf("%-32s %s\n", "IPv6 link local address:", $ipv6);
306
        } else {
324
                } else {
307
                $eui64 = mac_canonize($mac);
325
                        $eui64 = mac_canonize($mac);
308
                echo sprintf("%-32s %s\n", "EUI-64:", $eui64);
326
                        echo sprintf("%-32s %s\n", "EUI-64:", $eui64);
309
        }
327
                }
-
 
328
        }
310
 
329
 
311
        // Vergabestelle
330
        // Vergabestelle
312
        $ul = hexdec($mac[1]) & 2; // Bit #LSB+1 of Byte 1
331
        $ul = hexdec($mac[1]) & 2; // Bit #LSB+1 of Byte 1
313
        $ul_ = ($ul == 0) ? '[0] Universally Administered Address (UAA)' : '[1] Locally Administered Address (LAA)';
332
        $ul_ = ($ul == 0) ? '[0] Universally Administered Address (UAA)' : '[1] Locally Administered Address (LAA)';
314
        echo sprintf("%-32s %s\n", "Administration type (U/L flag):", $ul_);
333
        echo sprintf("%-32s %s\n", "Administration type (U/L flag):", $ul_);
315
 
334
 
316
        // Empfaengergruppe
335
        // Empfaengergruppe
317
        $ig = hexdec($mac[1]) & 1; // Bit #LSB+0 of Byte 1
336
        $ig = hexdec($mac[1]) & 1; // Bit #LSB+0 of Byte 1
318
        $ig_ = ($ig == 0) ? '[0] Individual (Unicast)' : '[1] Group (Multicast)';
337
        $ig_ = ($ig == 0) ? '[0] Unicast (Individual)' : '[1] Multicast (Group)';
319
        echo sprintf("%-32s %s\n", "Transmission type (I/G flag):", $ig_);
338
        echo sprintf("%-32s %s\n", "Transmission type (I/G flag):", $ig_);
320
 
339
 
321
        // Query IEEE registries
340
        // Query IEEE registries
322
        // TODO: gilt OUI nur bei Individual UAA? For LAA, should we convert to UAA and then query the registry?
-
 
323
        if (count(glob(IEEE_MAC_REGISTRY.DIRECTORY_SEPARATOR.'*.txt')) > 0) {
341
        if (count(glob(IEEE_MAC_REGISTRY.DIRECTORY_SEPARATOR.'*.txt')) > 0) {
-
 
342
                if ($mac[1] == 'A') {
-
 
343
                        // Query the CID registry
-
 
344
                        if (
-
 
345
                                ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'cid.txt', 'CID', $mac))
-
 
346
                        ) {
-
 
347
                                echo $x;
-
 
348
                        }
-
 
349
                } else {
-
 
350
                        // Query the OUI registries
-
 
351
                        // TODO: Should we try to convert Unicast<=>Multicast if one of them can't be found?
324
                if (
352
                        if (
325
                        # The IEEE Registration Authority distinguishes between IABs and OUI-36 values. Both are 36-bit values which may be used to generate EUI-48 values, but IABs may not be used to generate EUI-64 values.[6]
353
                                # The IEEE Registration Authority distinguishes between IABs and OUI-36 values. Both are 36-bit values which may be used to generate EUI-48 values, but IABs may not be used to generate EUI-64 values.[6]
326
                        # Note: The Individual Address Block (IAB) is an inactive registry activity, which has been replaced by the MA-S registry product as of January 1, 2014.
354
                                # Note: The Individual Address Block (IAB) is an inactive registry activity, which has been replaced by the MA-S registry product as of January 1, 2014.
327
                        ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'iab.txt', 'IAB', $mac)) ||
355
                                ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'iab.txt', 'IAB', $mac)) ||
328
                        ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'oui36.txt', 'OUI-36 (MA-S)', $mac)) ||
356
                                ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'oui36.txt', 'OUI-36 (MA-S)', $mac)) ||
Line 330... Line 358...
330
                        ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'oui.txt', 'OUI-24 (MA-L)', $mac))
358
                                ($x = _lookup_ieee_registry(IEEE_MAC_REGISTRY . DIRECTORY_SEPARATOR . 'oui.txt', 'OUI-24 (MA-L)', $mac))
331
                ) {
359
                        ) {
332
                        echo $x;
360
                                echo $x;
333
                }
361
                        }
334
        }
362
                }
-
 
363
        }
335
 
364
 
336
        $vm = '';
365
        $vm = '';
337
        // === FAQ "Detection rules which don't have their dedicated page yet" ===
366
        // === FAQ "Detection rules which don't have their dedicated page yet" ===
338
        // https://wiki.xenproject.org/wiki/Xen_Networking
367
        // https://wiki.xenproject.org/wiki/Xen_Networking
339
        // https://mcpmag.com/articles/2007/11/27/hey-vm-whats-your-hypervisor.aspx
368
        // https://mcpmag.com/articles/2007/11/27/hey-vm-whats-your-hypervisor.aspx
Line 769... Line 798...
769
        $hightest = eui64_to_eui48($high);
798
        $hightest = eui64_to_eui48($high);
770
        if ($hightest === false) return false;
799
        if ($hightest === false) return false;
771
 
800
 
772
        if ((eui_bits($mactest) != eui_bits($lowtest)) || (eui_bits($lowtest) != eui_bits($hightest))) {
801
        if ((eui_bits($mactest) != eui_bits($lowtest)) || (eui_bits($lowtest) != eui_bits($hightest))) {
773
                $mactest = eui48_to_eui64($mac);
802
                $mactest = eui48_to_eui64($mac);
-
 
803
                if ($mactest === false) return false; // e.g. trying ELI-48 to ELI-64
774
                $lowtest = eui48_to_eui64($low);
804
                $lowtest = eui48_to_eui64($low);
-
 
805
                if ($lowtest === false) return false; // e.g. trying ELI-48 to ELI-64
775
                $hightest = eui48_to_eui64($high);
806
                $hightest = eui48_to_eui64($high);
-
 
807
                if ($hightest === false) return false; // e.g. trying ELI-48 to ELI-64
776
        }
808
        }
777
 
809
 
778
        $mactest = strtoupper(preg_replace('@[^0-9A-F]@', '', $mactest));
810
        $mactest = strtoupper(preg_replace('@[^0-9A-F]@', '', $mactest));
779
        $lowtest = strtoupper(preg_replace('@[^0-9A-F]@', '', $lowtest));
811
        $lowtest = strtoupper(preg_replace('@[^0-9A-F]@', '', $lowtest));
780
        $hightest = strtoupper(preg_replace('@[^0-9A-F]@', '', $hightest));
812
        $hightest = strtoupper(preg_replace('@[^0-9A-F]@', '', $hightest));