JS에서 서브넷 마스크와 관련된 계산에 빠지다

개요


JS에서 IP를 계산할 기회가 거의 없을 수 있습니다(node.js 제외).
중요한 이론을 정리하기 위해 JS로 시행을 시도했을 때의 기록이다.

※ 주의


여기에 기재된 코드는 설명에 사용되는'최소한의 기능'만 구현됐고, 입력값을 검사하는 기구 등은 존재하지 않는다.예외 값을 입력하면 오류가 발생합니다.

서브넷 마스크 관련 계산


대전제로 서브넷을 정리하는 계산.

IPv4 주소 문자열을 Number 형식으로 변환


비트 연산을 이용하여 IPv4 주소의 문자열, 예를 들어 192.168.0.1 형식의 문자열을 계산하기 위해 Number형으로 변환합니다.
// IPv4 to binary string
const ip2bin = (ip) => ip.split(".").map(e => Number(e).toString(2).padStart(8, '0')).join('')
// IPv4 to Number
const ip2long = (ip) => parseInt(ip2bin(ip), 2)
// Number to IPv4
const long2ip = (num) => {
    let bin = Number(num).toString(2).padStart(32, '0')
    return [
        bin.slice(0, 8),
        bin.slice(8, 16),
        bin.slice(16, 24),
        bin.slice(24, 32),
    ].map(e => parseInt(e, 2)).join('.')
}

console.log(ip2bin("192.0.34.166")) // 11000000000000000010001010100110
console.log(ip2long("192.0.34.166")) // 3221234342
console.log(long2ip(ip2long("192.0.34.166"))) // 192.0.34.166
주제 밖의 말, ip 2 long/long2ip는 PHP에 동명 함수가 존재한다.
ip2long - php.net
이런 거 참고했어.

CIDR과 서브넷의 상호 변환


CIDR은 바이너리로 표시할 때 처음부터 시작하는 1개의 수를 표시하고 최대값은 32까지의 정수값이다.
CIDR과 서브넷 마스크는 다음과 같은 관계가 있습니다.
26  # CIDR11111111.11111111.11111111.11000000 # 26個1を並べ2進数表記に変換255.255.255.192 # 1バイト(=8bit)ずつに区切ってそれぞれを10進数に変換する
코드는 다음과 같다.
// CIDR to Number
const cidr2long = (cidr) => parseInt(String("").padStart(cidr, '1').padEnd(32, '0'), 2)
// CIDR to SubnetMask
const cidr2subnetmask = (num) => long2ip(cidr2long(num))
// SubnetMask to CIDR
const subnetmask2cidr = (ip) => ip2bin(ip).split('1').length - 1

console.log(cidr2subnetmask(26)) // 255.255.255.192
console.log(subnetmask2cidr("255.255.255.192")) // 26

네트워크 주소 및 브로드캐스트 주소


네트워크 주소(시작 주소)는 IP 주소와 서브넷 마스크의 AND(논리적 볼륨)로 계산됩니다.
또한 브로드캐스트 주소(종료 주소)는 IP 주소와 반전된 서브넷 마스크의 OR(논리 및)로 계산됩니다.
인상은 이런느낌↓
ip & subnetmask // ネットワークアドレス
ip | ~subnetmask // ブロードキャストアドレス
그래서 순수하게 JS의 비트 논리적 적과 비트 논리적 합으로 계산하려고 했는데 지금은 푹 빠졌다.
console.log(64) // 64
console.log(64 | 0) // 64
console.log(ip2long("192.168.0.1")) // 3232235521
console.log(3232235521) // 3232235521
console.log(3232235521 | 0) // -1062731775
여기 코드 다섯 번째 줄처럼 계산에서 의도하지 않게 마이너스로 변했다.
이것은 2의 부호화 (기호가 있는 32비트 정수) 를 나타낸다.
기호가 있는 32자리 정수는 -2의 32차방에서 2의 32차방-1을 나타낼 수 있다.
이번에는 32비트가 표시할 수 있는 수치를 모두 기호가 없는 정수로 표현하고자 하기 때문에 변환 방법을 찾을 때'기호가 없는 오른쪽 이산자'를 기호가 없는 표현으로 변환할 수 있다.
console.log((3232235521 | 0) >>> 0) // 3232235521
이 계산을 이용하여 네트워크 주소와 방송 주소를 다음과 같이 계산한다.
// ネットワークアドレス
const getNetworkAddr = (ip, subnetmask) => (ip & subnetmask) >>> 0
// ブロードキャストアドレス
const getBroadcastAddr = (ip, subnetmask) => (ip | ~subnetmask) >>> 0

카테고리


IP 주소는 사용 네트워크의 규모에 따라 A~C와 특수용'D'와 실험용'E'로 나뉜다.

  • A0.0.0.0~127.55.255.255

  • B클래스 128.0.0.0~191.255.255.255

  • 클래스 C 192.0~223.255.255.255

  • 클래스 D224.0.0.0~239.255.255

  • 클래스 E240.0.0.0~255.255.255
  • 나는 이것을 아래 코드에 떨어뜨렸다.
    const getClass = (ip) => {
        if (ip2long("0.0.0.0") <= ip && ip <= ip2long("127.255.255.255")) {
            return 'A'
        }
        if (ip2long("128.0.0.0") <= ip && ip <= ip2long("191.255.255.255")) {
            return 'B'
        }
        if (ip2long("192.0.0.0") <= ip && ip <= ip2long("223.255.255.255")) {
            return 'C'
        }
        if (ip2long("224.0.0.0") <= ip && ip <= ip2long("239.255.255.255")) {
            return 'D'
        }
        if (ip2long("240.0.0.0") <= ip && ip <= ip2long("255.255.255.255")) {
            return 'E'
        }
        return false;
    }
    console.log(getClass(ip2long("192.168.0.1"))) // C
    

    IP 주소가 지정된 범위 내에 있는지 판단


    IP 주소의 네트워크 주소가 동일한지 비교하여 확인합니다.
    const inRange = (remoteIp, acceptIp, cidr) => {
        cidr = Number(cidr)
        const remoteIpNetwork = remoteIp >>> (32 - cidr)
        const acceptIpNetwork = acceptIp >>> (32 - cidr)
        return remoteIpNetwork === acceptIpNetwork
    }
    
    console.log(inRange(ip2long("192.168.0.1"), ip2long("192.168.0.254"), 24)) 
    // true
    

    계산 방법 재정리


    서브넷 마스크와 관련된 일련의 계산을 재정리하고 계산합니다.코드가 모두 게재되다.
    const ip2bin = (ip) => ip.split(".").map(e => Number(e).toString(2).padStart(8, '0')).join('')
    
    const ip2long = (ip) => parseInt(ip2bin(ip), 2)
    
    const long2ip = (num) => {
        let bin = Number(num).toString(2).padStart(32, '0')
        return [
            bin.slice(0, 8),
            bin.slice(8, 16),
            bin.slice(16, 24),
            bin.slice(24, 32),
        ].map(e => parseInt(e, 2)).join('.')
    }
    
    const cidr2long = (cidr) => parseInt(String("").padStart(cidr, '1').padEnd(32, '0'), 2)
    
    const cidr2subnetmask = (num) => long2ip(cidr2long(Number(num)))
    
    const subnetmask2cidr = (ip) => ip2bin(ip).split('1').length - 1
    
    const getNetworkAddr = (ip, subnetmask) => (ip & subnetmask) >>> 0
    
    const getBroadcastAddr = (ip, subnetmask) => (ip | ~subnetmask) >>> 0
    
    const inRange = (remoteIp, acceptIp, cidr) => remoteIp >>> (32 - Number(cidr)) === acceptIp >>> (32 - Number(cidr))
    
    const getClass = (ip) => {
        if (ip2long("0.0.0.0") <= ip && ip <= ip2long("127.255.255.255")) {
            return 'A'
        }
        if (ip2long("128.0.0.0") <= ip && ip <= ip2long("191.255.255.255")) {
            return 'B'
        }
        if (ip2long("192.0.0.0") <= ip && ip <= ip2long("223.255.255.255")) {
            return 'C'
        }
        if (ip2long("224.0.0.0") <= ip && ip <= ip2long("239.255.255.255")) {
            return 'D'
        }
        if (ip2long("240.0.0.0") <= ip && ip <= ip2long("255.255.255.255")) {
            return 'E'
        }
        return false;
    }
    
    const ipLong = ip2long("192.168.0.1")
    const cidr = cidr2long(24)
    console.log(`
    IPアドレス: ${long2ip(ipLong)}
    サブネットマスク: /${subnetmask2cidr("255.255.255.0")} (${cidr2subnetmask(24)})
    ネットワークアドレス: ${long2ip(getNetworkAddr(ipLong, cidr))}
    使用可能IP: ${long2ip(getNetworkAddr(ipLong, cidr) + 1)}${long2ip(getBroadcastAddr(ipLong, cidr) - 1)}
    ブロードキャストアドレス: ${long2ip(getBroadcastAddr(ipLong, cidr))}
    アドレス数: ${getBroadcastAddr(ipLong, cidr) - getNetworkAddr(ipLong, cidr) + 1}
    ホストアドレス数: ${getBroadcastAddr(ipLong, cidr) - getNetworkAddr(ipLong, cidr) - 1}
    IPアドレスクラス: ${getClass(ipLong)}
    `)
    console.log(`192.168.0.1 は 192.168.0.254/24 に含まれ${ inRange(ip2long("192.168.0.1"), ip2long("192.168.0.254"), 24) ? 'ます' : 'ません' }`)
    console.log(`192.168.1.0 は 192.168.0.254/24 に含まれ${ inRange(ip2long("192.168.1.0"), ip2long("192.168.0.254"), 24) ? 'ます' : 'ません' }`)
    
    그리고 출력 결과는 여기에 있습니다.서브넷 계산 사이트의 결과와 일치하다.
    IPアドレス: 192.168.0.1
    サブネットマスク: /24 (255.255.255.0)
    ネットワークアドレス: 192.168.0.0
    使用可能IP: 192.168.0.1 〜 192.168.0.254
    ブロードキャストアドレス: 192.168.0.255
    アドレス数: 256
    ホストアドレス数: 254
    IPアドレスクラス: C
    192.168.0.1 は 192.168.0.254/24 に含まれます
    192.168.1.0 は 192.168.0.254/24 に含まれません
    

    기타


    이 보도는 아래의 보도를 하나로 요약한 것이다.
    JS에서 서브넷 마스크 계산 - 404 motivation not found
    JS에서 IP 주소가 서브넷 마스크에 지정된 범위 내에 있는지 여부를 결정합니다.
    JS에서 32비트 기호의 정수를 비트 연산할 때 빠져드는 - 404 motivation not found

    좋은 웹페이지 즐겨찾기