How to Detect If Two IP Addresses Are from the Same Network Modem


In this guide, we explore how to determine whether two IP addresses, either IPv4 or IPv6, are from the same modem or network. This is useful for troubleshooting, network analysis, and ensuring devices are on the same subnet. You will find examples in JavaScript and PHP, making it easy to integrate into your own projects.

Understanding the Comparison of IP Addresses

IP addresses are unique identifiers for devices on a network. To determine if two IP addresses are from the same modem, we compare their network prefixes: the first three octets for IPv4 and the first four groups for IPv6. This technique works for most standard subnet configurations.

Live Test Tool: Detect Same IP Address

Use this live tool to test whether two IP addresses belong to the same modem or network. The tool supports both IPv4 and IPv6 addresses and validates the inputs before comparing their prefixes.

How to Use

  1. Enter two IP addresses in the provided fields.
  2. Click the "Check" button to compare the addresses.
  3. View the result, which indicates if the IPs are from the same network or modem.

Enter two IP addresses below to check if they belong to the same network or modem.

Check

PHP Code Example

This PHP code IP Address Validator is a PHP class designed to help developers efficiently validate, identify, and compare IP addresses. It supports both IPv4 and IPv6, enabling seamless integration into networking applications. This powerful tool also allows subnet comparison and CIDR validation, making it ideal for enterprise and cloud networking.

PHP
<?php
/**
 * A class to validate IP addresses, check if two IPs belong to the same network,
 * and handle CIDR notation.
 */
class IPAddressValidator {

    public function __construct() {}

    public static function isValidIPv4(string $ip): bool {
        return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
    }

    public static function isValidIPv6(string $ip): bool {
        return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
    }

    /**
     * Checks if two IPs belong to the same network based on an optional subnet mask length.
     *
     * @param string $ip1 The first IP address.
     * @param string $ip2 The second IP address.
     * @param int|null $subnetLength Optional subnet mask length for comparison.
     *                               Defaults to classful network rules if not specified.
     * @return array An associative array with success status and message.
     */

    public static function isSameModem(string $ip1, string $ip2, ?int $subnetLength = null): array {
        if (!self::isValidIPv4($ip1) && !self::isValidIPv6($ip1)) {
            return [
                'success' => false,
                'message' => 'The first IP address is invalid.'
            ];
        }

        if (!self::isValidIPv4($ip2) && !self::isValidIPv6($ip2)) {
            return [
                'success' => false,
                'message' => 'The second IP address is invalid.'
            ];
        }

        if (self::isValidIPv4($ip1) && self::isValidIPv4($ip2)) {
            return self::compareIPv4Networks($ip1, $ip2, $subnetLength);
        }

        if (self::isValidIPv6($ip1) && self::isValidIPv6($ip2)) {
            return self::compareIPv6Networks($ip1, $ip2, $subnetLength);
        }

        return [
            'success' => false,
            'message' => 'Cannot compare IPv4 and IPv6 addresses.'
        ];
    }

    /**
     * Validates and parses a CIDR notation.
     *
     * @param string $cidr The CIDR notation (e.g., 192.168.1.0/24).
     * @return array|null
     * Returns an associative array with 'ip' and 'prefix' keys if valid, null otherwise.
     */
    public static function parseCIDR(string $cidr): ?array {
        $parts = explode('/', $cidr);
        if (count($parts) !== 2) {
            return null; // Invalid format
        }

        [$ip, $prefix] = $parts;
        $prefix = (int)$prefix;

        if (self::isValidIPv4($ip) && $prefix >= 0 && $prefix <= 32) {
            return ['ip' => $ip, 'prefix' => $prefix];
        }

        if (self::isValidIPv6($ip) && $prefix >= 0 && $prefix <= 128) {
            return ['ip' => $ip, 'prefix' => $prefix];
        }

        return null; // Invalid IP or prefix
    }

    private static function compareIPv4Networks(string $ip1, string $ip2, ?int $subnetLength): array {
        if ($subnetLength === null) {
            $subnetLength = 24; // Default to /24
        }

        $mask = ~((1 << (32 - $subnetLength)) - 1);
        $longIp1 = ip2long($ip1) & $mask;
        $longIp2 = ip2long($ip2) & $mask;

        $isSameNetwork = $longIp1 === $longIp2;
        return [
            'success' => true,
            'message' => $isSameNetwork ? 'Same network' : 'Different networks'
        ];
    }

    private static function compareIPv6Networks(string $ip1, string $ip2, ?int $subnetLength): array {
        if ($subnetLength === null) {
            $subnetLength = 64; // Default to /64
        }

        $binIp1 = @inet_pton($ip1);
        $binIp2 = @inet_pton($ip2);

        if ($binIp1 === false || $binIp2 === false) {
            return [
                'success' => false,
                'message' => 'Invalid IPv6 address provided.'
            ];
        }

        $mask = str_repeat("\xff", intdiv($subnetLength, 8)) .
                chr((0xff << (8 - $subnetLength % 8)) & 0xff) .
                str_repeat("\x00", 16 - ceil($subnetLength / 8));

        $isSameNetwork = ($binIp1 & $mask) === ($binIp2 & $mask);
        return [
            'success' => true,
            'message' => $isSameNetwork ? 'Same network' : 'Different networks'
        ];
    }
}
?>

How It Works

The IPAddressValidator class provides the following static methods to handle various IP address-related operations:

1. isValidIPv4 Checks if an IP address is a valid IPv4 address.

PHP
$isIPv4 = IPAddressValidator::isValidIPv4('192.168.1.1');
// Returns true for valid IPv4

2. isValidIPv6 Checks if an IP address is a valid IPv6 address.

PHP
$isIPv6 = IPAddressValidator::isValidIPv6('2001:db8::1');
// Returns true for valid IPv6

3. isSameModem Compares two IP addresses (IPv4 or IPv6) to determine if they belong to the same network or modem, with optional subnet mask support.

PHP
$result = IPAddressValidator::isSameModem('192.168.1.1', '192.168.1.20', 24);
// Returns: ['success' => true, 'message' => 'Same network']

4. parseCIDR Validates and parses CIDR notations (e.g., 192.168.1.0/24) into a base IP and subnet mask prefix.

PHP
$cidrData = IPAddressValidator::parseCIDR('192.168.1.0/24');
// Returns: ['ip' => '192.168.1.0', 'prefix' => 24]

Usage Example

PHP
<?php

require_once './ip-address-validator.php';

// Check if two IPs are in the same subnet
$result = IPAddressValidator::isSameModem('192.168.1.1', '192.168.1.2');
if ($result['success']) {
    echo $result['message'];  // "Same network"
}

$result = IPAddressValidator::isSameModem('2001:db8:1234::', '2001:db8:1234:1::');
if ($result['success']) {
    echo $result['message'];  // "Same network"
}

// Validate and parse CIDR
$cidr = "192.168.1.0/24";
$parsedCIDR = IPAddressValidator::parseCIDR($cidr);
if ($parsedCIDR !== null) {
    echo "Valid CIDR\n";
    echo "IP: " . $parsedCIDR['ip'] . "\n";
    echo "Prefix: " . $parsedCIDR['prefix'] . "\n";
} else {
    echo "Invalid CIDR";
}

// Compare IPs using parsed CIDR
$ip1 = "192.168.1.10";
$ip2 = "192.168.1.20";
$subnetLength = $parsedCIDR['prefix'];

$result = IPAddressValidator::isSameModem($ip1, $ip2, $subnetLength);
print_r($result); // Should print: Same network

// Invalid CIDR example
$invalidCIDR = "192.168.1.0/33";
$parsedInvalidCIDR = IPAddressValidator::parseCIDR($invalidCIDR);
if ($parsedInvalidCIDR === null) {
    echo "Invalid CIDR notation: $invalidCIDR\n";
}

JavaScript Code with IP Validation

This JavaScript code provides a reliable way to validate and compare IP addresses, ensuring they are either IPv4 or IPv6 before determining if they belong to the same modem or network. It uses regular expressions to validate the IP formats and compares their prefixes first three octets for IPv4 and first four groups for IPv6. The code also includes error handling to notify users if an invalid IP address is provided.

JavaScript
class IPValidator {
    constructor() {
    	// Define regex patterns for IPv4 and IPv6 validation
        this.IPV4_PATTERN = /^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.((25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.){2}(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)$/;
        this.IPV6_PATTERN = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9]))$/;
    }

    // Validate if an IP is a valid IPv4 address
    isValidIPv4(ip) {
        return this.IPV4_PATTERN.test(ip);
    }

    // Validate if an IP is a valid IPv6 address
    isValidIPv6(ip) {
        return this.IPV6_PATTERN.test(ip);
    }

    // Determine if two IP addresses are in the same modem
    isSameModem(ip1, ip2) {
        if (!this.isValidIPv4(ip1) && !this.isValidIPv6(ip1)) {
            throw new Error("The first IP address is invalid.");
        }
        if (!this.isValidIPv4(ip2) && !this.isValidIPv6(ip2)) {
            throw new Error("The second IP address is invalid.");
        }

        const isIPv4 = ip => ip.includes(".");
        const isIPv6 = ip => ip.includes(":");

        if (isIPv4(ip1) && isIPv4(ip2)) {
            // Compare first three segments for IPv4
            const prefix1 = ip1.split(".").slice(0, 3).join(".");
            const prefix2 = ip2.split(".").slice(0, 3).join(".");
            return prefix1 === prefix2;
        }

        if (isIPv6(ip1) && isIPv6(ip2)) {
            // Compare first four segments for IPv6
            const prefix1 = ip1.split(":").slice(0, 4).join(":");
            const prefix2 = ip2.split(":").slice(0, 4).join(":");
            return prefix1 === prefix2;
        }

        throw new Error("Cannot compare IPv4 and IPv6 addresses.");
    }
}

Usage

JavaScript
// Example usage
try {
	const validator = new IPValidator();
	console.log(validator.isValidIPv4("192.168.1.1")); // true
	console.log(validator.isValidIPv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334")); // true

	console.log(validator.isSameModem("192.168.1.1", "192.168.1.2")); // true
    console.log(validator.isSameModem("192.168.1.1", "192.168.2.1")); // false
    console.log(validator.isSameModem("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:1234")); // true
} catch (error) {
    console.error(error.message);
}

Features

Features of the IP Address Validator

  1. IP Validation:
    Validates both IPv4 and IPv6 addresses to ensure they conform to proper standards.

  2. Subnet Comparison:
    Determines if two IP addresses belong to the same subnet, with optional custom subnet mask lengths.

  3. CIDR Notation Support:
    Parses and validates CIDR notations (e.g., 192.168.1.0/24) to extract the base IP and subnet prefix.

  4. Cross-Type Comparison Prevention:
    Prevents invalid comparisons between IPv4 and IPv6 addresses, ensuring robust error handling.

  5. Error Feedback:
    Provides meaningful error messages for invalid IP addresses or incompatible comparisons.

  6. Extensibility:
    Designed for easy integration into existing PHP projects, with clear and reusable methods.