Devin.KR

이진수와 CIDR 계산

90분 안팎

학습 목표

IPv4와 프리픽스를 입력받아 네트워크·브로드캐스트·호스트 범위를 출력합니다.

개념

학교 단말이 자기 망을 판별하려면 마스크가 필요합니다

학교 학생망에 10.20.10.11을 배정했다고 가정합니다. 숫자만 보고 10.20.10.200이 같은 망인지 판단할 수는 없습니다. 프리픽스 /24라면 포함되지만 /25라면 다른 망입니다. 잘못된 마스크는 단말이 목적지를 직접 찾을지 게이트웨이에 넘길지 결정하는 기준을 바꿉니다. 이 레슨의 목표는 주소와 프리픽스를 함께 읽고 네트워크·브로드캐스트·일반 호스트 범위를 같은 계산으로 구하는 것입니다.

IPv4 주소는 32비트이고 점으로 나눈 네 칸은 각각 8비트입니다. 한 칸의 정수 범위는 0부터 255입니다. 10.20.10.11의 마지막 11을 이진수로 쓰면 00001011입니다. 십진수는 사람이 읽기 위한 표현이며 범위 비교는 비트 기준으로 이뤄집니다. “앞 세 칸이 같으면 같은 망”은 /24에 우연히 맞는 규칙이지 /23이나 /25에도 적용할 주소 판별법이 아닙니다.

CIDR의 /25는 왼쪽 25비트가 네트워크 부분이라는 뜻입니다. 마스크는 네트워크 비트 자리에 1, 호스트 자리에 0을 놓아 255.255.255.128로 표시합니다. 11111111.11111111.11111111.10000000의 맨 마지막 1이 네 번째 칸을 둘로 나눕니다. 호스트 비트는 일곱 개가 남아 총 128개의 주소 조합을 만듭니다. 프리픽스를 늘리면 한 망의 주소 개수는 줄어듭니다.

AND 연산으로 시작 주소를 구합니다

네트워크 주소는 단말 주소와 마스크의 비트 AND 결과입니다. AND는 같은 자리의 두 값이 모두 1일 때만 1입니다. /25에서 10.20.10.11의 마지막 칸 00001011과 마스크 10000000을 비교하면 00000000이 남습니다. 따라서 시작 주소는 10.20.10.0입니다. 같은 계산을 10.20.10.130에 적용하면 마지막 칸은 10000000, 즉 128이 되어 다음 /25에 속합니다.

브로드캐스트 주소는 네트워크 부분을 유지하고 호스트 비트를 모두 1로 채웁니다. 첫 /25는 10.20.10.0부터 10.20.10.127까지이고 다음 /25는 10.20.10.128부터 10.20.10.255까지입니다. 전통적인 다중접근 서브넷에서는 두 끝을 일반 단말에 배정하지 않으므로 첫 범위의 단말은 .1부터 .126까지입니다. 주소 .127은 그 망의 브로드캐스트이지 마지막 단말 주소가 아닙니다.

총 주소 수는 2의 (32에서 프리픽스를 뺀 값) 제곱입니다. 일반 호스트 수는 여기서 두 개를 뺍니다. /24는 256개 중 254개, /25는 128개 중 126개, /30은 네 개 중 두 개입니다. /31 점대점 링크와 /32 단일 주소는 별도 사용 규칙이 있으므로 이 공식으로 모두 설명하지 않습니다. 이번 브라우저 계산기의 계약은 일반 범위를 배우는 /0부터 /30까지로 제한하고 /31·/32는 ERROR로 거부합니다.

경계값으로 계산을 검토합니다

가장 흔한 실수는 .128을 첫 /25의 마지막 호스트로 세는 것입니다. 포함 여부와 사용 가능 여부를 구분합니다. .127은 첫 망에 포함되지만 일반 호스트로 사용할 수 없고, .128은 첫 망 밖이면서 다음 망의 시작 주소입니다. .129는 다음 망의 첫 일반 호스트입니다. 실무에서 잘못된 주소를 지적할 때 “같은 대역이 아니다”와 “대역 안의 예약 주소다”를 다르게 보고해야 수정할 값이 명확해집니다.

/23에서는 세 번째 칸의 경계도 계산해야 합니다. 10.20.11.200/23의 네트워크는 10.20.10.0/23이고 끝은 10.20.11.255입니다. 세 번째 칸이 다른 두 주소가 같은 망일 수 있다는 사례입니다. 시작 주소를 손으로 찾을 때 블록 간격 2를 적용할 수 있지만, 기록에는 계산된 CIDR과 전체 범위를 적습니다. 마지막 칸만 보는 습관은 학교 요구가 커져 /23이 필요해질 때 장애를 만듭니다.

프리픽스 0은 계산상 전체 IPv4 범위이며 주소 수는 4294967296입니다. 이를 모두 리스트로 만들면 메모리가 불필요하게 커집니다. 이 레슨의 프로그램은 network_address와 broadcast_address에서 한 칸씩 이동해 첫·끝 호스트를 구하고 num_addresses로 개수를 읽습니다. 전체 hosts 목록을 만들지 않습니다. 큰 입력에서도 계산이 짧게 끝나는 이유를 설명할 수 있어야 경계 테스트가 의미를 가집니다.

Python 객체에서 주소와 망을 구분합니다

ipaddress.ip_interface는 단말 IP와 그 단말이 속한 망을 함께 보존합니다. ip_interface("10.20.10.11/25").ip는 10.20.10.11이고 .network는 10.20.10.0/25입니다. ip_network에 호스트 비트가 있는 주소를 기본 strict 검사로 넣으면 has host bits set 오류가 날 수 있습니다. 주소 입력을 읽는 계산기에는 interface를 사용하고 설계된 망 자체를 읽는 검사기에는 network의 엄격한 검사를 사용합니다.

계산 결과를 편하게 얻으려고 아무 곳에서나 strict=False를 쓰면 설계 파일의 오타가 자동으로 정규화됩니다. 예를 들어 관리자가 망 시작으로 .128/24를 적었는데 프로그램이 조용히 .0/24로 고치면 의도와 기록이 달라집니다. 사용자 입력이 단말 주소인지 망 선언인지 먼저 정합니다. 이번 입력은 단말 주소/프리픽스 한 토큰이므로 interface가 의도를 유지하면서 망 계산을 제공합니다.

ValueError는 주소 숫자 범위나 프리픽스 형식이 유효하지 않다는 신호입니다. 10.20.10.256/24, /33, 비연속 마스크는 받아들이지 않습니다. 예외를 잡은 뒤 ERROR 한 줄로 출력하되 일부 계산 결과를 먼저 내보내지 않습니다. 형식 오류가 있는 입력에 network만 출력하고 프로그램이 멈추면 채점 결과도 인계 문서도 부분 결과를 정상 값으로 오해할 수 있습니다.

계산기 계약을 고정하고 테스트를 읽습니다

표준 입력은 IPv4/십진 프리픽스 한 토큰입니다. 앞뒤 공백은 허용하지만 줄이 추가되거나 두 토큰이 들어오면 ERROR입니다. 정상 출력 순서는 network, broadcast, first, last, hosts입니다. network에는 주소와 /프리픽스가 함께 나옵니다. IPv6·누락된 슬래시·비십진 프리픽스·/31 이상은 계약에서 제외합니다. 제한을 지키는 것은 다른 주소 체계가 존재하지 않는다고 주장하는 일이 아닙니다.

시작 코드는 모든 입력에 ERROR를 출력합니다. 형식 검사부터 구현하고 유효한 경우에만 다섯 줄을 만들면 됩니다. /24 한 사례만 통과하는 것으로 끝내지 않고 .127·.128이 갈리는 /25, 작은 /30, 세 번째 칸을 포함하는 /23과 /0을 실행합니다. /31 거부 사례도 넣어 일반 호스트 공식의 적용 경계를 코드에 드러냅니다. 결과를 눈대중으로 정답에 맞추기보다 객체와 산술의 관계를 확인합니다.

제출물에는 작은 /25 한 쌍의 수기 계산을 덧붙입니다. 네트워크 시작을 AND로 얻고 호스트 비트를 모두 1로 채운 끝을 적은 다음 Python 결과와 대조합니다. 주소 계산 성공은 실제 통신 성공의 증거가 아니므로 ping 성공 같은 문구를 붙이지 않습니다. 이진수와 값 표현의 더 넓은 개념은 서재 장에서 이어 읽고 다음 레슨에서는 계산된 범위가 여러 역할 사이에서 겹치는지 검사합니다.

사실 확인 참고: Python ipaddress 문서. 실습 설명과 예제는 학교 프로젝트에 맞춰 직접 작성했습니다.

따라하기

마스크의 마지막 비트

계산을 직접 실행한 뒤 범위와 경계를 비교합니다.

address=11
mask=128
print(format(address,'08b'))
print(format(mask,'08b'))
print('AND',format(address & mask,'08b'))

실행 결과

00001011
10000000
AND 00000000

/25 두 경계 비교

계산을 직접 실행한 뒤 범위와 경계를 비교합니다.

import ipaddress as ip
for text in ['10.20.10.127/25','10.20.10.128/25']:
    a=ip.ip_interface(text)
    print(a.ip,a.network,a.network.broadcast_address)

실행 결과

10.20.10.127 10.20.10.0/25 10.20.10.127
10.20.10.128 10.20.10.128/25 10.20.10.255

범위와 개수를 상수 공간으로 구하기

계산을 직접 실행한 뒤 범위와 경계를 비교합니다.

import ipaddress as ip
n=ip.ip_interface('10.20.11.200/23').network
print('network',n)
print('broadcast',n.broadcast_address)
print('first',n.network_address+1)
print('last',n.broadcast_address-1)
print('hosts',n.num_addresses-2)

실행 결과

network 10.20.10.0/23
broadcast 10.20.11.255
first 10.20.10.1
last 10.20.11.254
hosts 510

엄격한 망 선언의 오류 구별

계산을 직접 실행한 뒤 범위와 경계를 비교합니다.

import ipaddress as ip
try:
    ip.ip_network('10.20.10.11/24')
except ValueError:
    print('ERROR: host bits set in network declaration')
print('interface network',ip.ip_interface('10.20.10.11/24').network)

실행 결과

ERROR: host bits set in network declaration
interface network 10.20.10.0/24

확인 문제

실습

IPv4/십진 프리픽스 한 토큰을 읽습니다. /0~ /30만 지원합니다. 정상은 network CIDR, broadcast 주소, first 주소, last 주소, hosts 정수를 순서대로 다섯 줄 출력합니다. 각 줄은 키와 값 사이 공백 한 개입니다. 잘못된 형식·IPv6·/31·/32·복수 토큰은 ERROR 한 줄입니다. 앞뒤 공백은 허용합니다. hosts를 열거하지 않고 범위 산술로 계산합니다.

모범 답안
import ipaddress as ip
import re
import sys

def main():
    text=sys.stdin.read().strip()
    if not re.fullmatch(r'[0-9]+(?:\.[0-9]+){3}/(?:0|[1-9][0-9]?)',text):
        print('ERROR');return
    try:
        a=ip.IPv4Interface(text)
        n=a.network
        if n.prefixlen>30: raise ValueError('unsupported prefix')
    except ValueError:
        print('ERROR');return
    print('network',n)
    print('broadcast',n.broadcast_address)
    print('first',n.network_address+1)
    print('last',n.broadcast_address-1)
    print('hosts',n.num_addresses-2)
main()

더 읽기

면접 질문

  • 같은 서브넷과 다른 서브넷으로 통신하는 흐름을 설명합니다.