PXE (Preboot Execution Environment)

PXE(Preboot Execution Environment)는 네트워크를 통해 운영체제를 부팅하는 업계 표준 기술입니다. 이 문서에서는 PXE의 역사와 스펙 구조부터 DHCP/BOOTP 프로토콜 상세, TFTP/HTTP 파일 전송, PXE ROM과 UNDI 드라이버, NBP(Network Bootstrap Program) 체인로딩, iPXE 고급 스크립팅, UEFI PXE/HTTP Boot, PXELINUX/GRUB 네트워크 부트로더(Bootloader), iSCSI/NFS 루트 파일시스템(Filesystem), 대규모 프로비저닝(Cobbler/MAAS/Foreman/FOG), Secure PXE Boot, 리눅스 커널 네트워크 부팅 파라미터, 디스크리스(diskless) 부팅 구성, 그리고 트러블슈팅까지 네트워크 부팅의 모든 것을 심층 분석합니다.

전제 조건: 부팅 과정(Boot Process)UEFI 문서를 먼저 읽으세요. PXE는 펌웨어(Firmware) 단계에서 시작되므로, BIOS/UEFI 부팅 흐름을 이해해야 합니다. 네트워크 기초가 필요하다면 네트워킹 개요도 참고하세요.
일상 비유: PXE는 "전화 한 통으로 배달시키는 부팅"입니다. 컴퓨터(손님)가 네트워크(전화)로 "나 부팅할 파일 줘!"라고 요청하면, DHCP 서버(교환원)가 "여기로 가세요"라고 안내하고, TFTP 서버(배달원)가 부팅 파일을 전달합니다. 로컬 디스크 없이도 운영체제를 실행할 수 있습니다.

핵심 요약

  • PXE — Intel이 1999년에 제정한 네트워크 부팅 표준. 펌웨어(BIOS/UEFI)에 내장된 PXE ROM이 DHCP+TFTP로 부트 파일을 받아 실행합니다.
  • DHCP Option 66/67 — 실무에서 많이 쓰는 TFTP 서버/부트 파일 전달 수단입니다. 다만 장애 분석 시에는 siaddr, file, Option 66/67, ProxyDHCP 응답을 함께 봐야 합니다.
  • NBP — Network Bootstrap Program. PXELINUX, GRUB, iPXE 등 네트워크를 통해 전달되는 1차 부트로더입니다.
  • iPXE — 오픈소스 PXE 구현. HTTP/HTTPS, iSCSI, Wi-Fi, 스크립팅 등 레거시 PXE의 한계를 넘는 기능을 제공합니다.
  • UEFI HTTP Boot — UEFI가 정의한 현대적 네트워크 부팅 경로입니다. TFTP 대신 HTTP/HTTPS URI로 EFI 이미지를 전달합니다.

단계별 이해

  1. 펌웨어가 NIC를 깨운다
    BIOS는 PXE Option ROM과 UNDI를, UEFI는 SNP/MNP와 PXE Base Code 또는 HTTP Boot 스택을 사용해 네트워크를 초기화합니다.
  2. DHCP로 "어디서 무엇을 받을지" 묻습니다
    클라이언트는 IP 주소만 받는 것이 아니라, 부트 파일 이름, 다음 서버, 아키텍처 코드, 벤더 클래스까지 함께 협상합니다.
  3. 첫 번째 로더(Loader)인 NBP를 가져옵니다
    레거시 PXE는 주로 TFTP로 pxelinux.0 같은 NBP를 가져오고, UEFI HTTP Boot는 HTTP URI로 shimx64.efigrubx64.efi를 받습니다.
  4. NBP가 커널과 initrd를 로드합니다
    PXELINUX, GRUB, iPXE가 커널 파라미터를 조합하고, 필요하면 HTTP, iSCSI, NFS 같은 더 고급 프로토콜로 2차 리소스를 가져옵니다.
  5. 진짜 난이도는 rootfs부터 시작됩니다
    네트워크 부팅 성공은 1차 로더 전달로 끝나지 않습니다. initramfs 안의 NIC 드라이버, NFS/iSCSI 재연결, Secure Boot 신뢰 체인(Chain of Trust), 자동 설치 스크립트가 최종 성공을 좌우합니다.

실무 선택 가이드

현장에서 "PXE"라는 말을 넓게 쓰는 경우가 많지만, 실제로는 Legacy PXE, UEFI PXE, UEFI HTTP Boot, iPXE 체인로딩이 서로 다른 설계 선택지입니다. 무엇을 고를지는 펌웨어 모드, Secure Boot 요구사항, 이미지 크기, 운영 자동화 수준, 루트 파일시스템 방식에 따라 달라집니다.

검토 기준 (2026-03-07): 이 문서는 Intel PXE 2.1, RFC 2131/2132/4578/5970/7440, UEFI 2.11, IANA Processor Architecture Types 레지스트리(2026-02-02 갱신), iPXE 공식 문서를 기준으로 사실관계를 다시 점검해 보강했습니다.
환경우선 선택이유주의점
구형 서버, BIOS 전용PXELINUX 또는 undionly.kpxe가장 폭넓은 호환성. 기존 PXE ROM이 바로 이해 가능TFTP 성능이 낮고, Secure Boot 개념이 없음
BIOS지만 대형 이미지, 동적 메뉴, iSCSI 필요iPXE 체인로딩첫 단계만 TFTP, 이후는 HTTP/HTTPS/iSCSI로 전환 가능DHCP에서 iPXE 무한 루프를 반드시 차단해야 함
UEFI + 표준 네트워크 부팅만 필요UEFI PXE펌웨어 기본 기능만으로 구현 가능여전히 TFTP 중심이라 대규모 동시 부팅에는 비효율적
UEFI + 큰 EFI 이미지 + 빠른 전송UEFI HTTP BootBoot URI를 직접 전달하므로 웹 서버 인프라 활용이 쉬움펌웨어별 HTTP/HTTPS/TLS 구현 편차가 큼
UEFI + Secure Boot 필수shim + GRUB 또는 서명된 iPXE서명 검증(Signature Verification) 체인과 배포판 기본 신뢰 체계를 재사용 가능서명되지 않은 ipxe.efi는 기본적으로 거부될 수 있음
호스트별 분기, API 연동, 메타데이터 기반 부팅iPXE 스크립트MAC/UUID/Serial/Platform 기반 동적 정책 구현에 가장 유연DHCP보다 HTTP API 계층의 가용성과 인증이 더 중요해짐
진짜 디스크리스 운영PXE/iPXE + NFS root 또는 iSCSI SAN boot로컬 디스크 없이 운영체제 유지 가능부팅보다 initramfs, 드라이버 내장, rootfs 재연결이 더 어렵다
BIOS/UEFI 혼재 환경Option 93 분기 + iPXE 혼합하나의 DHCP 정책에서 아키텍처별 NBP를 정교하게 나눌 수 있음IANA 아키텍처 코드와 HTTP Boot 코드를 정확히 관리해야 함
용어 주의: 실무에서는 "PXE 서버"가 TFTP 서버, HTTP Boot 서버, iPXE 스크립트 서버, 자동 설치 서버를 통칭하는 경우가 많습니다. 그러나 표준 문서 기준으로는 Intel PXE 2.1UEFI HTTP Boot는 별개의 메커니즘이며, 장애 분석 시 이 둘을 구분해야 문제 지점을 빠르게 찾을 수 있습니다.
펌웨어와 요구사항에 따른 네트워크 부팅 선택 시작: 이 장비는 어떤 펌웨어인가? BIOS / UEFI / Secure Boot / 이미지 크기 / 운영 자동화 요구사항 Legacy BIOS NIC Option ROM + UNDI DHCP + TFTP 중심 UEFI SNP/MNP + PXE/HTTP Boot .efi 이미지 실행 BIOS에서 단순 설치만 필요 PXELINUX / EXTLINUX 계열 장점: 최고 호환성 약점: TFTP 병목, 보안 약함 고속 전송/동적 정책 필요 iPXE 체인로딩 BIOS: undionly.kpxe / UEFI: ipxe.efi HTTP/HTTPS/iSCSI/API 연동에 최적 UEFI PXE 표준 TFTP 경로 가장 단순 속도는 느림 HTTP Boot URI 직접 전달 웹 인프라 활용 펌웨어 편차 큼 모든 경로의 최종 난이도는 initramfs, rootfs 재연결, Secure Boot 서명, 자동 설치 체인에 있습니다

PXE 역사와 스펙 진화

역사적 타임라인

네트워크 부팅의 계보: PXE 이전에도 여러 네트워크 부팅 기술이 있었습니다.
연도기술설명
1981BOOTP (RFC 951)최초의 네트워크 부팅 프로토콜. UDP 67/68 포트 사용. 정적 매핑(Mapping) 기반
1985RARP (RFC 903)Reverse ARP. MAC→IP 역변환. 간단하지만 라우터 통과 불가
1993DHCP (RFC 1531)BOOTP 확장. 동적 IP 할당, 옵션 확장 가능
1997DHCP Options (RFC 2132)Option 66(TFTP server), 67(bootfile) 등 부팅 관련 옵션 표준화
1999PXE 2.1 (Intel)Intel이 제정한 업계 표준. UNDI API, PXE Base Code, ProxyDHCP 정의
2006gPXEEtherboot 후속. HTTP, iSCSI, DNS 지원 추가
2010iPXEgPXE 포크. HTTPS, Wi-Fi, 스크립팅, SAN boot 등 현대적 기능
2015UEFI HTTP BootUEFI 2.5 스펙에 HTTP(S) 부팅 공식 포함
2020+UEFI HTTPS Boot + RedfishHTTPS 기반 부팅과 BMC/Redfish 자동화가 결합된 운영 패턴 확산

PXE 2.1 스펙 구조

Intel PXE 스펙 2.1은 다음 컴포넌트로 구성됩니다:

컴포넌트역할상세
PXE Base Code핵심 프로토콜 엔진DHCP/TFTP/UDP/IP 스택. BIOS에서는 16비트 리얼모드 API
UNDIUniversal Network Driver InterfaceNIC 하드웨어 추상화. PXE API가 NIC를 제어하는 표준 인터페이스
PXE Boot Server부팅 서버 프로토콜Boot Server Discovery Protocol. 여러 부팅 서버 중 선택
ProxyDHCPDHCP 보조 서버기존 DHCP 변경 없이 PXE 옵션만 추가 전달
BISBoot Integrity Services부팅 파일 무결성(Integrity) 검증 (실무에서는 거의 미사용)
PXE 2.1 스펙 계층 구조 NBP (Network Bootstrap Program) PXELINUX / GRUB / iPXE / Windows Boot Manager PXE Base Code API PXENV_TFTP_READ, PXENV_UDP_WRITE ... Boot Server Discovery ProxyDHCP / Boot Menu / BIS DHCP/BOOTP IP 할당 + 부팅 정보 TFTP 파일 전송 (UDP 69) UDP/IP 네트워크 스택 ARP/RARP 주소 해석 UNDI (Universal Network Driver Interface) PXENV_UNDI_OPEN / PXENV_UNDI_TRANSMIT / PXENV_UNDI_ISR — NIC 하드웨어 추상화 NIC Hardware (Option ROM / UEFI Driver) Intel I210/I350, Realtek RTL8111, Broadcom BCM57xx, Mellanox ConnectX ...

PXE 부팅 전체 흐름

4단계 부팅 과정

PXE 부팅은 크게 4단계로 진행됩니다: (1) DHCP 디스커버리(2) NBP 다운로드(3) 커널/initrd 로드(4) OS 부팅

PXE 부팅 전체 시퀀스 (BIOS Legacy) PXE Client DHCP Server TFTP Server NFS/HTTP Phase 1: DHCP Discovery (DORA) DHCPDISCOVER (broadcast) Option 60: Vendor Class, Option 93: arch DHCPOFFER IP + next-server + filename DHCPREQUEST DHCPACK Option 66: tftp-server, Option 67: pxelinux.0 Phase 2: NBP (Network Bootstrap Program) 다운로드 TFTP Read Request: pxelinux.0 TFTP Data: NBP 바이너리 전송 (512B blocks) PXE가 NBP를 0x7C00(16bit) 또는 지정 주소에 로드 → 실행 Phase 3: 커널 + initrd 다운로드 TFTP: pxelinux.cfg/default (설정 파일) 설정 파일 반환 (KERNEL, APPEND, INITRD 지시어) TFTP: vmlinuz (커널 이미지) TFTP: initrd.img (초기 RAM 디스크) NBP가 커널+initrd를 메모리에 배치 → 커널 엔트리 점프 Phase 4: OS 부팅 (커널 → rootfs) 커널 시작: start_kernel() → 네트워크 드라이버 초기화 DHCP (커널 ip= 파라미터) IP 재할당 NFS mount / HTTP fetch / iSCSI login (root= 파라미터) Root 파일시스템 마운트 pivot_root / switch_root → /sbin/init 실행 네트워크 부팅 완료!

상세 단계별 분석

Step 1: 전원 투입 → PXE ROM 실행

  1. BIOS POST 완료 후 부팅 장치 순회
  2. NIC의 Option ROM(PXE ROM) 발견 → 시그니처 0x55AA 확인
  3. PXE ROM이 메모리에 로드되어 INT 19h 부팅 벡터 설정
  4. PXE Base Code 초기화: UNDI 드라이버 로드 → NIC 활성화

Step 2: DHCP 4-Way Handshake (DORA)

Client                          Server
  |                               |
  |--- DHCPDISCOVER (broadcast) --|  src: 0.0.0.0:68 → 255.255.255.255:67
  |    Option 60: PXEClient:Arch  |  Option 93: Client System Architecture
  |    Option 94: Client NDI      |  Option 97: Client Machine Identifier (UUID)
  |                               |
  |------ DHCPOFFER --------------|  서버 → 클라이언트 (unicast 또는 broadcast)
  |    yiaddr: 192.168.1.100      |  할당할 IP 주소
  |    siaddr: 192.168.1.1        |  TFTP 서버 주소 (next-server)
  |    file: "pxelinux.0"         |  부트 파일명
  |                               |
  |--- DHCPREQUEST ---------------|  선택한 서버에 IP 요청 확인
  |                               |
  |------ DHCPACK ----------------|  IP 할당 확정 + 모든 옵션 전달
  |                               |

Step 3: NBP 다운로드 및 실행

  1. PXE Base Code가 TFTP 클라이언트를 실행
  2. siaddr(next-server)에서 file(filename) 다운로드
  3. NBP를 메모리 0x7C00(리얼모드) 또는 지정 주소에 로드
  4. PXE API 테이블 포인터를 레지스터(Register)에 설정한 후 NBP로 점프

Step 4: NBP가 커널 로드

  1. NBP(PXELINUX 등)가 PXE API를 통해 설정 파일 요청
  2. 설정 파일에 지정된 커널(vmlinuz)과 initrd 다운로드
  3. 커널 커맨드 라인 설정 (예: root=/dev/nfs nfsroot=...)
  4. Protected Mode 전환 후 커널 엔트리 포인트로 점프

DHCP/BOOTP 프로토콜

DHCP 패킷(Packet) 구조

DHCP는 BOOTP(RFC 951)를 기반으로 확장된 프로토콜(RFC 2131)입니다. PXE 부팅에서는 각 요청과 응답을 구분하기 위해 트랜잭션(Transaction) ID를 함께 사용합니다. 아래는 PXE 부팅에서 핵심적인 패킷 구조입니다.

/* DHCP/BOOTP 패킷 구조 (RFC 2131) */
struct dhcp_packet {
    uint8_t  op;          /* 1=BOOTREQUEST, 2=BOOTREPLY */
    uint8_t  htype;       /* 하드웨어 타입: 1=Ethernet */
    uint8_t  hlen;        /* 하드웨어 주소 길이: 6 (MAC) */
    uint8_t  hops;        /* 릴레이 에이전트 홉 수 */
    uint32_t xid;         /* 트랜잭션 ID */
    uint16_t secs;        /* 클라이언트 시작 후 경과 시간 */
    uint16_t flags;       /* bit0=Broadcast flag */
    uint32_t ciaddr;      /* Client IP (이미 할당된 경우) */
    uint32_t yiaddr;      /* Your IP (서버가 할당하는 IP) */
    uint32_t siaddr;      /* Server IP (next-server = TFTP 서버) */
    uint32_t giaddr;      /* Gateway IP (릴레이 에이전트) */
    uint8_t  chaddr[16];  /* Client hardware address (MAC) */
    char     sname[64];   /* Server host name (옵션) */
    char     file[128];   /* Boot file name (NBP 경로) */
    uint8_t  options[];   /* DHCP 옵션 (magic cookie 0x63825363 시작) */
}; /* 최소 크기: 300 bytes, 일반적으로 576 bytes */

PXE 관련 핵심 DHCP 옵션

옵션 번호이름용도예시 값
60Vendor Class IdentifierPXE 클라이언트 식별"PXEClient:Arch:00000:UNDI:002001"
66TFTP Server NameTFTP 서버 호스트명/IP"192.168.1.1"
67Bootfile NameNBP 파일 경로"pxelinux.0" 또는 "grubx64.efi"
93Client System Architecture클라이언트 아키텍처0x0000(x86 BIOS), 0x0007(x64 UEFI), 0x000B(ARM64 UEFI)
94Client Network InterfaceUNDI 버전 정보UNDI:003:010 (v3.10)
97Client Machine IdentifierUUID/GUIDSMBIOS에서 가져온 시스템 UUID
128-135PXE Vendor Options벤더별 확장멀티캐스트 부팅, 자격 증명 등
175iPXE EncapsulatediPXE 전용 옵션iPXE 스크립트 URL 등
209PXELINUX Config설정 파일 경로"pxelinux.cfg/default"
210PXELINUX PathprefixTFTP 경로 접두사"/tftpboot/linux/"

DHCP 필드 우선순위(Priority)와 해석

실무에서 가장 많이 헷갈리는 부분은 "부트 파일 정보가 정확히 어디에 들어가느냐"입니다. RFC 2132 관점에서 Option 66/67은 sname/file 필드를 다른 용도로 오버로드했을 때의 보조 수단이지만, 실제 PXE/UEFI 펌웨어는 BOOTP 헤더와 DHCP 옵션을 함께 해석합니다.

위치일반적 의미실무 포인트
siaddrnext-server, 부트 서버 주소레거시 PXE에서 가장 먼저 확인할 필드 중 하나입니다.
file 헤더 필드NBP 이름 또는 Boot URIUEFI HTTP Boot에서는 여기 자체에 http://... URI가 들어갈 수 있습니다.
Option 66TFTP Server Namesname를 다른 목적으로 쓸 때 보조적으로 전달됩니다. 일부 서버는 관행적으로 항상 같이 보냅니다.
Option 67Bootfile Namefile 필드를 대체하거나 보완합니다. HTTP Boot에서는 URI를 담는 구현도 흔합니다.
Option 60Vendor ClassPXEClientHTTPClient를 구분해 BIOS/UEFI/HTTP Boot 정책을 나눕니다.
ProxyDHCP 응답PXE 전용 부가 정보동일한 DORA 흐름처럼 보이지만, 실제로는 DHCP 서버 응답과 합쳐서 해석됩니다.
패킷 분석 규칙: "Option 67이 없으니 부팅 파일이 없습니다"라고 단정하면 안 됩니다. siaddr, file, Option 66/67, ProxyDHCP(4011) 응답을 모두 캡처해서 비교해야 정확합니다.

아키텍처 자동 감지와 부트파일 분기

DHCP Option 93을 사용하여 클라이언트 아키텍처를 감지하고, 적절한 부트 파일을 제공합니다. 아래 값은 현장에서 자주 보게 되는 대표값이며, 최종 권위는 RFC 4578의 초기 표보다 IANA Processor Architecture Types 레지스트리에 있습니다.

Option 93 값아키텍처권장 부트파일
0x0000 (0)x86 BIOS (IA-32)pxelinux.0 또는 undionly.kpxe
0x0006 (6)x86 UEFI (32-bit)syslinux.efi32 또는 grubia32.efi
0x0007 (7)x64 UEFI (64-bit)grubx64.efi 또는 shimx64.efi
0x0009 (9)EFI Byte Code (EBC)ipxe.efi 또는 EBC 지원 EFI 애플리케이션
0x000A (10)ARM 32-bit UEFIgrubarm.efi
0x000B (11)ARM64 UEFI (AArch64)grubaa64.efi
0x000F (15)x86 UEFI HTTP Boothttp://server/boot/bootia32.efi
0x0010 (16)x64 UEFI HTTP Boothttp://server/boot/shimx64.efi
0x0013 (19)ARM64 UEFI HTTP Boothttp://server/boot/shimaa64.efi
0x0019 (25)RISC-V 32-bit UEFIgrubriscv32.efi
0x001B (27)RISC-V 64-bit UEFIgrubriscv64.efi
분기 규칙: HTTP Boot는 아키텍처 값만으로 충분하지 않습니다. Option 93=0x0010 같은 HTTP Boot 코드와 함께 Vendor Class가 HTTPClient인지도 확인해야, TFTP용 .efi와 HTTP URI를 잘못 섞는 사고를 막을 수 있습니다.

ISC DHCP 서버 PXE 설정 예제

# /etc/dhcp/dhcpd.conf - PXE 부팅을 위한 ISC DHCP 설정

# 전역 옵션
option arch-type code 93 = unsigned integer 16;

subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    option domain-name-servers 8.8.8.8;
    next-server 192.168.1.10;    # TFTP 서버 주소

    # 아키텍처별 부트파일 분기
    if option arch-type = 00:07 {
        # x64 UEFI 클라이언트
        filename "grubx64.efi";
    } elsif option arch-type = 00:0B {
        # ARM64 UEFI 클라이언트
        filename "grubaa64.efi";
    } elsif option arch-type = 00:00 {
        # Legacy BIOS 클라이언트
        filename "pxelinux.0";
    }

    # 특정 MAC 주소에 고정 IP 할당
    host server01 {
        hardware ethernet 00:11:22:33:44:55;
        fixed-address 192.168.1.50;
        filename "pxelinux.0";
    }
}

dnsmasq PXE 설정 예제

# /etc/dnsmasq.conf - dnsmasq PXE 부팅 설정 (DHCP + TFTP 통합)

# DHCP 범위
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h

# 내장 TFTP 서버 활성화
enable-tftp
tftp-root=/var/lib/tftpboot

# PXE 아키텍처별 부트파일 분기
# tag:x86PC  = BIOS (arch 0)
# tag:EFI_x64 = UEFI x64 (arch 7)
# tag:EFI_AA64 = UEFI ARM64 (arch 11)
dhcp-match=set:x86PC,option:client-arch,0
dhcp-match=set:EFI_x64,option:client-arch,7
dhcp-match=set:EFI_AA64,option:client-arch,11

dhcp-boot=tag:x86PC,pxelinux.0
dhcp-boot=tag:EFI_x64,grubx64.efi
dhcp-boot=tag:EFI_AA64,grubaa64.efi

# ProxyDHCP 모드 (기존 DHCP 서버가 있을 때)
# dhcp-range=192.168.1.0,proxy
# pxe-service=x86PC,"PXE Boot",pxelinux
# pxe-service=X86-64_EFI,"PXE Boot UEFI",grubx64.efi

# iPXE 체인로딩: 일반 PXE는 iPXE를 로드, iPXE는 스크립트를 로드
dhcp-match=set:ipxe,175  # iPXE sends option 175
dhcp-boot=tag:!ipxe,undionly.kpxe
dhcp-boot=tag:ipxe,http://192.168.1.10/boot.ipxe

# DHCP 로깅
log-dhcp

ProxyDHCP 메커니즘

ProxyDHCP란? 기존 DHCP 서버(IP 할당)를 수정하지 않고, 별도 ProxyDHCP 서버가 PXE 부팅 옵션(filename, next-server)만 추가로 전달하는 메커니즘입니다. 대기업 네트워크에서 DHCP 관리자와 PXE 관리자가 다를 때 유용합니다.
ProxyDHCP 동작 흐름 PXE Client 기존 DHCP Server (IP 할당만) ProxyDHCP Server (PXE 옵션만, port 4011) 1. DHCPDISCOVER (broadcast, option 60: PXEClient) 2. DHCPOFFER (IP 할당, PXE 옵션 없음) 3. ProxyDHCP OFFER (IP=0.0.0.0, next-server + filename 포함) 4. DHCPREQUEST → DHCPACK 5. PXE Boot Server Request (port 4011, unicast) 6. Boot Server ACK (최종 filename + next-server) → 이후 TFTP로 NBP 다운로드 진행
ProxyDHCP 핵심 포인트:
- 기존 DHCP 서버의 IP 할당을 방해하지 않음
- ProxyDHCP는 yiaddr=0.0.0.0으로 응답 (IP를 할당하지 않음)
- PXE 클라이언트는 두 응답을 모두 수신하여 병합
- ProxyDHCP는 port 67 (broadcast) 또는 port 4011 (unicast) 사용
- dnsmasq의 dhcp-range=...,proxy 옵션으로 간단히 구성 가능

DHCP 옵션 바이너리 인코딩

DHCP 옵션은 TLV(Type-Length-Value) 형식으로 인코딩됩니다. PXE 관련 옵션의 실제 바이트 구조를 이해하면 패킷 분석과 트러블슈팅에 도움됩니다.

DHCP 옵션 TLV 바이너리 인코딩 Magic Cookie (4 bytes): 0x63 0x82 0x53 0x63 ← DHCP 옵션 시작 마커 Option 53 (DHCP Message Type): Type: 53 (0x35) Len: 1 (0x01) Value: 1 DISCOVER Option 60 (Vendor Class Identifier): Type: 60 (0x3C) Len: 32 (0x20) "PXEClient:Arch:00000:UNDI:002001" Option 93 (Client System Architecture): Type: 93 (0x5D) Len: 2 (0x02) 0x00 0x07 x64 UEFI ← 서버가 이 값을 보고 grubx64.efi 제공 Option 97 (Client Machine Identifier - UUID): Type: 97 (0x61) Len: 17 (0x11) Type:0 b8 94 59 08 d6 a6 41 a9 61 1d 74 a6 ab 80 b8 3d (SMBIOS UUID) End Option: 0xFF (255) ← 옵션 목록 종료 (Length 필드 없음) Type Length Value ※ Type 0(Pad)과 255(End)는 Length 필드 없음

BOOTP vs DHCP vs PXE 패킷 비교

세 프로토콜은 동일한 패킷 구조를 공유하지만, 사용하는 필드와 옵션이 다릅니다:

필드/기능BOOTP (RFC 951)DHCP (RFC 2131)PXE (Intel 2.1)
패킷 구조300 bytes 고정576+ bytes (옵션 확장)DHCP + 벤더 확장
op 필드1(req)/2(reply)동일동일
siaddr부팅 서버 IPnext-serverTFTP 서버 (Option 66 우선)
file (128B)부트 파일명부트 파일명NBP 경로 (Option 67 우선)
IP 할당정적 매핑만동적 풀 + 정적DHCP 방식 사용
옵션 영역64 bytes (vendor)가변 (magic cookie 이후)DHCP + PXE 전용 옵션
Magic Cookie없음0x63825363동일
메시지 타입없음Option 53동일
아키텍처 감지불가불가 (표준)Option 93 (arch type)
UUID 전달불가불가 (표준)Option 97 (UUID)
ProxyDHCP불가불가port 4011 유니캐스트
재시도단순 타임아웃지수 백오프DHCP 방식 + PXE 타임아웃
/* BOOTP와 DHCP의 옵션 영역 차이 */

/* BOOTP: 64바이트 vendor-specific 영역 (RFC 951) */
struct bootp_packet {
    /* ... 공통 필드 ... */
    uint8_t  vend[64];    /* vendor-specific area */
};
/* 매우 제한적: 서브넷 마스크, 게이트웨이 정도만 전달 가능 */

/* DHCP: 가변 길이 옵션 영역 (RFC 2131) */
struct dhcp_packet {
    /* ... 공통 필드 (BOOTP와 동일) ... */
    uint8_t  options[];   /* 가변 길이, magic cookie 0x63825363로 시작 */
};
/* Magic Cookie 다음에 TLV 형식 옵션이 이어짐:
 * [Type:1B][Length:1B][Value:N bytes] ...
 * 마지막에 0xFF(End) 옵션
 *
 * PXE 확장:
 * - Option 60: "PXEClient:Arch:NNNNN:UNDI:MMMMMM"
 * - Option 93: 2바이트 아키텍처 코드 (0=BIOS, 7=x64UEFI, 11=ARM64)
 * - Option 94: UNDI 버전 (major.minor)
 * - Option 97: 1바이트 타입 + 16바이트 UUID (SMBIOS)
 * - Option 175: iPXE 캡슐화 옵션
 */

TFTP 프로토콜

TFTP 개요

TFTP(Trivial File Transfer Protocol, RFC 1350)는 PXE의 기본 파일 전송 프로토콜입니다. UDP 기반으로 매우 단순하지만, 그만큼 한계가 있습니다.

특성TFTPFTPHTTP
전송 계층UDP (포트 69)TCP (포트 20/21)TCP (포트 80/443)
인증없음사용자/비밀번호다양 (Basic, Token, ...)
블록 크기512B (기본), 최대 65464B가변가변
최대 파일 크기~32MB (512B블록), ~4GB (확장)무제한무제한
디렉토리 목록불가가능가능
보안없음TLS (FTPS)TLS (HTTPS)
PXE 지원모든 PXE ROM미지원iPXE, UEFI HTTP Boot

TFTP 패킷 타입

/* TFTP 패킷 타입 (RFC 1350) */
#define TFTP_RRQ   1   /* Read Request  - 파일 읽기 요청 */
#define TFTP_WRQ   2   /* Write Request - 파일 쓰기 요청 (PXE에서 미사용) */
#define TFTP_DATA  3   /* Data          - 데이터 블록 전송 */
#define TFTP_ACK   4   /* Acknowledgment - 데이터 수신 확인 */
#define TFTP_ERROR 5   /* Error         - 오류 메시지 */
#define TFTP_OACK  6   /* Option ACK    - 옵션 협상 응답 (RFC 2347) */

/* RRQ/WRQ 패킷 구조 */
struct tftp_rrq {
    uint16_t opcode;     /* 1 (RRQ) */
    char     filename[]; /* NUL 종료 문자열, 예: "pxelinux.0\0" */
    char     mode[];     /* "octet\0" (바이너리 모드) */
    /* 이하 옵션들 (RFC 2347): "blksize\0" "1468\0" "tsize\0" "0\0" */
};

/* DATA 패킷 구조 */
struct tftp_data {
    uint16_t opcode;     /* 3 (DATA) */
    uint16_t block;      /* 블록 번호 (1부터 시작) */
    uint8_t  data[];     /* 데이터 (최대 blksize 바이트) */
};
/* 마지막 블록: data 길이 < blksize → 전송 완료 */

TFTP 확장 옵션 (RFC 2347-2349)

옵션RFC설명기본값권장값
blksizeRFC 2348블록 크기 (8-65464)5121468 (MTU 최적화)
tsizeRFC 2349전송 크기 (바이트)미지원요청: 0 → 응답: 실제 크기
timeoutRFC 2349재전송(Retransmission) 타임아웃 (초)미정의1-5
windowsizeRFC 7440윈도우 크기 (블록 수)14-64 (성능 향상)
성능 주의: 기본 TFTP(512B 블록, 윈도우 1)는 매우 느립니다. 100MB 커널 이미지를 전송하면 약 200,000번의 ACK 왕복이 필요합니다. blksize=1468windowsize=64를 설정하면 전송 효율이 큰 폭으로 개선됩니다. 대규모 환경에서는 HTTP 기반 부팅(iPXE/UEFI HTTP Boot)을 권장합니다.
TFTP 파일 전송 흐름 (블록 크기 협상 포함) Client :69 TFTP Server :69 RRQ "pxelinux.0" octet blksize=1468 tsize=0 OACK blksize=1468 tsize=42780 ACK block#0 (OACK 확인) DATA block#1 (1468 bytes) ACK block#1 ... (반복) ... DATA block#30 (312 bytes < 1468 = 마지막!) ACK block#30 → 전송 완료 총 42,780 bytes = 29 × 1468 + 312 bytes (30 블록)

TFTP 서버 구성

# tftpd-hpa 설치 및 설정 (Debian/Ubuntu)
sudo apt install tftpd-hpa

# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create --verbose --blocksize 1468"

# 디렉토리 구조
/var/lib/tftpboot/
├── pxelinux.0              # BIOS NBP (SYSLINUX 패키지)
├── lpxelinux.0             # BIOS NBP + HTTP 지원
├── ldlinux.c32             # SYSLINUX 필수 모듈
├── libutil.c32             # SYSLINUX 유틸리티
├── menu.c32                # 텍스트 메뉴 모듈
├── vesamenu.c32            # 그래픽 메뉴 모듈
├── libcom32.c32            # COM32 라이브러리
├── grubx64.efi             # UEFI x64 NBP
├── grubaa64.efi            # UEFI ARM64 NBP
├── shimx64.efi             # Secure Boot shim
├── undionly.kpxe           # iPXE chainload 이미지
├── ipxe.efi                # iPXE UEFI 이미지
├── pxelinux.cfg/           # PXELINUX 설정 디렉토리
│   ├── default             # 기본 설정 파일
│   ├── C0A80164            # IP 기반 (192.168.1.100 = C0A80164)
│   └── 01-00-11-22-33-44-55  # MAC 기반 설정
├── grub/                   # GRUB 네트워크 설정
│   └── grub.cfg
├── vmlinuz                 # Linux 커널
├── initrd.img              # initramfs
└── images/                 # OS 설치 이미지
    ├── ubuntu/
    ├── rhel/
    └── debian/

# TFTP 서비스 시작
sudo systemctl enable --now tftpd-hpa

# 방화벽 설정
sudo firewall-cmd --add-service=tftp --permanent
sudo firewall-cmd --reload

TFTP 윈도우 크기별 전송 비교

RFC 7440의 windowsize 옵션은 TFTP 성능에 극적인 영향을 줍니다. 윈도우 크기가 1(기본)이면 매 블록마다 ACK를 기다려야 하지만, 윈도우 크기를 늘리면 여러 블록을 연속 전송할 수 있습니다.

TFTP 윈도우 크기별 전송 효율 비교 windowsize=1 (기본) Client Server DATA #1 ACK #1 DATA #2 ACK #2 DATA #3 ACK #3 DATA #4 ACK #4 ... (반복) ... RTT × N회 대기 100MB ÷ 1468B = 68,119 왕복! windowsize=4 Client Server DATA #1 DATA #2 DATA #3 DATA #4 ACK #4 DATA #5 DATA #6 DATA #7 DATA #8 ACK #8 ... (반복) ... RTT × (N÷4)회 대기 100MB ÷ 1468B ÷ 4 = 17,030 왕복 (4× 빠름)
# TFTP 서버별 windowsize 설정

# tftpd-hpa: windowsize 직접 지원 안함 (blksize만 가능)
# → 대안: atftpd 또는 dnsmasq 사용

# atftpd: windowsize 및 멀티캐스트 지원
sudo apt install atftpd
# /etc/default/atftpd
OPTIONS="--daemon --port 69 --tftpd-timeout 300 \
  --retry-timeout 5 --maxthread 100 \
  --bind-address 0.0.0.0 \
  --logfile /var/log/atftpd.log \
  --windowsize 64 --blksize 1468 \
  /var/lib/tftpboot"

# dnsmasq: TFTP windowsize 지원 (v2.87+)
# /etc/dnsmasq.d/tftp.conf
enable-tftp
tftp-root=/var/lib/tftpboot
# blksize는 지원하지만 windowsize는 클라이언트 협상에 의존

# iPXE 클라이언트의 TFTP 옵션 협상
# iPXE는 자동으로 blksize=1468, windowsize를 협상
# PXE ROM은 보통 blksize=512, windowsize=1만 지원

# 성능 측정 방법
# 서버에서 전송 속도 모니터링
sudo tcpdump -i eth0 -c 100 port 69 -w /tmp/tftp.pcap
# Wireshark에서 Statistics → I/O Graphs로 처리량 확인

UNDI 드라이버와 PXE ROM

UNDI 개요

UNDI(Universal Network Driver Interface)는 PXE 스펙에서 정의한 NIC 하드웨어 추상화 계층입니다. NBP가 특정 NIC 드라이버 없이도 네트워크 I/O를 수행할 수 있게 합니다.

UNDI API 함수설명
PXENV_UNDI_STARTUPUNDI 드라이버 초기화
PXENV_UNDI_CLEANUPUNDI 드라이버 정리
PXENV_UNDI_INITIALIZENIC 하드웨어 초기화
PXENV_UNDI_OPENNIC 열기 (패킷 수신 시작)
PXENV_UNDI_CLOSENIC 닫기
PXENV_UNDI_TRANSMIT패킷 전송
PXENV_UNDI_SET_PACKET_FILTER수신 필터 설정
PXENV_UNDI_GET_INFORMATIONNIC 정보 조회 (MAC, IRQ, ...)
PXENV_UNDI_GET_STATISTICS전송/수신 통계
PXENV_UNDI_ISR인터럽트 서비스 루틴(ISR)
PXENV_UNDI_GET_NIC_TYPENIC 타입 (PCI vendor/device ID)
PXENV_UNDI_GET_IFACE_INFO인터페이스 정보 (속도, 이름)

PXE Option ROM 구조

PCI Option ROM은 장치가 PCI 버스(Bus)에 자신을 노출한 뒤 BIOS가 ROM 헤더를 읽어 초기화 루틴을 호출하는 구조입니다. 이때 각 필드가 ROM 이미지 내부 어느 오프셋(Offset)에 놓이는지 이해하면 실제 덤프와 스펙을 대조하기 쉽습니다.

/* PCI Option ROM 헤더 (BIOS) */
struct option_rom_header {
    uint16_t signature;     /* 0x55AA - ROM 시그니처 */
    uint8_t  rom_size;      /* ROM 크기 (512바이트 단위) */
    uint8_t  init_entry[4]; /* 초기화 엔트리 포인트 (JMP 명령) */
    uint8_t  reserved[17];  /* 예약 */
    uint16_t pci_data_ptr;  /* PCI Data Structure 오프셋 */
    uint16_t pnp_header_ptr;/* PnP Expansion Header 오프셋 */
};

/* PXE ROM 초기화 흐름 (16-bit Real Mode):
 *
 * 1. BIOS가 PCI 버스를 스캔하여 NIC의 Option ROM BAR 발견
 * 2. ROM을 메모리 C000h-DFFFFh 영역에 매핑
 * 3. signature 0x55AA 확인 후 init_entry 호출
 * 4. PXE ROM이 UNDI 드라이버 설치
 * 5. INT 1Ah (PCI BIOS) 연결, INT 18h/19h 부팅 벡터 후킹
 * 6. BIOS 부팅 순서에서 네트워크 부팅 선택 시:
 *    → PXE Base Code 시작 → DHCP → TFTP → NBP 실행
 */

LOM 비트와 PXE ROM 활성화 제어

LOM(LAN-on-Motherboard)은 메인보드에 직접 납땜된 온보드 NIC를 의미합니다. PCIe 슬롯에 장착하는 애드인(Add-in) NIC와 달리, LOM은 메인보드 펌웨어(BIOS/UEFI)와 긴밀하게 연동되어 PXE 부팅 가능 여부를 NIC의 비휘발성 메모리(NVM, Non-Volatile Memory)에 저장된 LOM 비트로 제어합니다. 이 비트가 비활성화되어 있으면 BIOS POST 과정에서 해당 NIC의 Option ROM을 로드하지 않으므로, NIC가 OS에서는 정상 동작하더라도 PXE 네트워크 부팅은 수행할 수 없습니다.

LOM 하드웨어 아키텍처

LOM은 메인보드 설계 단계에서 PCB(Printed Circuit Board)에 직접 실장되는 네트워크 컨트롤러입니다. 데스크톱에서는 단일 GbE 포트가 일반적이고, 서버 메인보드에서는 듀얼(Dual) 또는 쿼드(Quad) 포트 구성이 표준입니다. LOM 컨트롤러는 칩셋의 PCH(Platform Controller Hub)와 내부 DMI 경로로 연결되며, 독립적인 PCIe 레인을 사용하는 애드인 NIC와는 물리적 토폴로지(Topology)가 다릅니다. 구형 플랫폼에서 말하는 사우스브리지의 브리지(Bridge) 역할을 오늘날에는 PCH가 더 많이 맡습니다.

LOM 하드웨어 아키텍처 — 메인보드 내부 연결 토폴로지 CPU PCIe Root Complex 내장 DMI 3.0/4.0 PCH (Platform Controller Hub) USB, SATA, PCIe 레인 분배, SPI Flash 컨트롤러, LAN 연결 Intel C620/C740 시리즈 (서버), B660/Z790 (데스크톱) 내부 PHY 또는 SGMII PCIe x4/x8 레인 SPI Bus LOM NIC (온보드) Intel I219-LM / I226-LM (데스크톱) Intel I350 / X710 (서버 듀얼/쿼드) Broadcom BCM5720 (Dell 서버) SPI Flash BIOS 펌웨어 ME(관리 엔진) 영역 LOM NVM 영역 애드인 NIC (PCIe) Intel X710 / E810 Mellanox ConnectX-6 자체 EEPROM/Flash 내장 Port 0 Port 1 Port 2 SFP+ SFP+ LOM 포트 (RJ45 — 메인보드 I/O 패널) 애드인 NIC 포트 (PCIe 슬롯) LOM: PCH 내부 버스로 연결 — NVM이 SPI Flash에 공유 저장 — BIOS가 LOM 비트를 직접 제어 애드인: 독립 PCIe 레인 — 자체 EEPROM — BIOS는 Expansion ROM BAR만 확인

LOM 구현 방식의 변천

LOM의 하드웨어 구현 방식은 PC 아키텍처의 발전에 따라 크게 세 세대로 나뉩니다. 각 세대에서 NVM과 LOM 비트의 저장 위치와 제어 방식이 달라집니다.

세대시기LOM 구현 방식NVM / LOM 비트 위치대표 칩셋·NIC
1세대: 개별 NIC 칩 ~2008 메인보드에 별도 PCI/PCIe NIC 칩을 납땜. 사우스브리지(Southbridge)의 PCI 버스에 연결 NIC 칩 자체의 EEPROM (93C46/93C56 시리얼 EEPROM). LOM 비트는 EEPROM 내 Boot Config Word에 저장 Intel 82559, Realtek RTL8111B, Broadcom BCM5751
2세대: PCH 통합 MAC 2008~2020 PCH 내부에 MAC(Media Access Controller) 통합. 외부에는 PHY(Physical Layer) 칩만 별도 실장. PCH ↔ PHY 간 MII/RGMII/SGMII 인터페이스 사용 PCH의 SPI Flash 내에 LOM 설정 영역이 할당됨. BIOS 펌웨어와 동일한 SPI Flash를 공유하므로 BIOS가 직접 LOM 비트를 읽고 쓸 수 있음 Intel I217/I218/I219-LM + PCH (6~400 시리즈)
3세대: SoC 완전 통합 2020~ MAC + PHY 모두 SoC(System on Chip)에 통합. 별도 NIC 칩 없이 CPU 다이(Die) 또는 PCH 다이에 네트워크 컨트롤러가 내장됨. 2.5GbE가 표준 SoC 내장 Flash 또는 통합 NVM 컨트롤러. BIOS 설정이 곧 LOM 비트 제어 (별도 EEPROM 없음) Intel I225-V/I226-V (Alder Lake/Raptor Lake 내장), AMD Realtek RTL8125B (B650/X670)
LOM 구현 방식 변천 — 3세대 아키텍처 비교 1세대: 개별 NIC 칩 (~2008) CPU 사우스브리지 PCI NIC 칩 MAC + PHY EEPROM RJ45 2세대: PCH 통합 MAC (2008~2020) CPU DMI PCH (MAC 내장) GbE MAC 컨트롤러 통합 SGMII PHY 칩 I219-LM SPI Flash BIOS + LOM NVM RJ45 3세대: SoC 통합 (2020~) SoC / CPU + PCH CPU 코어 + PCIe + USB + 2.5GbE MAC + PHY 통합 내장 NIC (2.5G) 통합 NVM RJ45 NVM: NIC 자체 EEPROM LOM 비트: EEPROM Word BIOS: PCI 스캔 시 EEPROM 간접 참조 NVM: PCH SPI Flash (공유) LOM 비트: SPI Flash 내 GbE 영역 BIOS: SPI Flash 직접 읽기/쓰기 NVM: SoC 통합 Flash LOM 비트: 펌웨어 설정 = NVM BIOS: 설정 변경 = 즉시 반영 세대가 진행될수록 LOM NVM이 메인보드 펌웨어에 통합 → BIOS의 LOM 비트 제어가 더 직접적으로 변화

서버 LOM 멀티포트 구성과 포트별 PXE 제어

서버 메인보드에서는 하나의 LOM 컨트롤러에 여러 물리 포트가 연결됩니다. 이 경우 NVM에는 포트별로 독립적인 설정 영역이 할당되며, 각 포트의 LOM 비트를 개별적으로 제어할 수 있습니다. 대규모 데이터센터에서는 일반적으로 Port 0만 PXE 부팅용으로 활성화하고 나머지 포트는 OS 부팅 후 데이터 트래픽용으로 사용합니다.

/* Intel I350 쿼드포트 LOM — NVM 포트별 설정 구조 */

/* NVM 전체 레이아웃 (듀얼/쿼드 포트 NIC) */
struct i350_nvm_layout {
    /* 공유 영역 (Shared Area) — 모든 포트 공통 */
    uint16_t nvm_signature;         /* Word 0x00: NVM 유효성 시그니처 */
    uint16_t hw_compat;             /* Word 0x03: 하드웨어 호환성 */
    uint16_t pcie_init_config;      /* Word 0x05: PCIe 초기화 설정 */
    /* ... */

    /* 포트별 개별 영역 (Per-Port Area) */
    /* 각 포트가 독립적인 Boot Configuration Word를 보유 */
    struct {
        uint16_t boot_config;       /* PXE Enable (LOM 비트), 부팅 프로토콜 */
        uint16_t lan_config;        /* LAN Enable, 속도, 듀플렉스 설정 */
        uint16_t mac_addr[3];       /* 포트별 고유 MAC 주소 (48-bit) */
        uint16_t subsys_id;         /* PCI Subsystem ID */
    } port[4];                      /* 최대 4포트 */
};

/* 포트별 PXE 활성화 예시 (서버 관리 정책):
 *
 * Port 0: pxe_enable=1 (PXE 부팅용 — 프로비저닝 네트워크)
 * Port 1: pxe_enable=0 (관리 네트워크 — BMC/IPMI 공유)
 * Port 2: pxe_enable=0 (프로덕션 데이터 트래픽)
 * Port 3: pxe_enable=0 (프로덕션 데이터 트래픽)
 *
 * BIOS는 PXE 부팅 시도 시 pxe_enable=1인 포트만 순회
 * → Port 0에서 DHCPDISCOVER 전송 시작
 */
서버 LOM 쿼드포트 — NVM 포트별 PXE 제어 구조 NVM (SPI Flash / EEPROM) 공유 영역 (Shared Area) 시그니처, PCIe 설정, Checksum Port 0 LOM 비트=1 PXE ON, MAC: AA:BB:CC:00:00:01 Port 1 LOM 비트=0 PXE OFF, MAC: AA:BB:CC:00:00:02 Port 2 LOM 비트=0 PXE OFF, MAC: AA:BB:CC:00:00:03 Port 3 LOM 비트=0 PXE OFF, MAC: AA:BB:CC:00:00:04 NVM Checksum (Word 0x3F) eth0 (Port 0) PXE 부팅 활성 eth1 (Port 1) 관리 네트워크 eth2 (Port 2) 데이터 트래픽 eth3 (Port 3) 데이터 트래픽 BIOS PXE 부팅 시퀀스 1. NVM 스캔 → Port 0 LOM 비트=1 확인 2. Port 0 Option ROM 로드 3. DHCPDISCOVER → TFTP → NBP 4. Port 1~3 → ROM 스킵 참고: OCP NIC 3.0 서버용 표준 메자닌(Mezzanine) NIC LOM과 동일한 NVM/PXE 제어 구조

SPI Flash 내부의 LOM NVM 영역 배치

2세대 이후 LOM에서는 메인보드의 SPI Flash를 BIOS 펌웨어, ME(Management Engine) 펌웨어, GbE(LOM) NVM이 함께 공유합니다. 각 영역은 플래시 디스크립터(Flash Descriptor)에 의해 분할되며, 접근 권한(읽기/쓰기)도 영역별로 독립적으로 관리됩니다.

/* Intel SPI Flash 레이아웃 (PCH 기반 LOM) */

/* Flash Descriptor — 플래시 영역 분할 테이블 */
struct flash_descriptor {
    uint32_t flvalsig;          /* 0x0FF0A55A — 유효 디스크립터 시그니처 */
    struct {
        uint32_t base;          /* 영역 시작 주소 */
        uint32_t limit;         /* 영역 끝 주소 */
    } region[5];
    /* region[0]: Flash Descriptor 자체 (4KB)
     * region[1]: BIOS 영역 (~8-32MB)
     * region[2]: ME (Management Engine) 영역 (~2-5MB)
     * region[3]: GbE 영역 (LOM NVM) — 8KB (2 × 4KB 미러링)
     * region[4]: Platform Data 영역 (선택)
     */
    uint32_t master_access;     /* 영역별 읽기/쓰기 권한 */
};

/* GbE 영역 (region[3]) 내부 구조 — 8KB */
struct gbe_nvm_region {
    /* Bank A (4KB) — 주 설정 */
    uint16_t mac_address[3];        /* Word 0x00-0x02: MAC 주소 */
    uint16_t nvm_version;           /* Word 0x05: NVM 이미지 버전 */
    uint16_t boot_config;           /* Word 0x0F: Boot Configuration Word
                                     *   bit 1: PXE Enable (LOM 비트)
                                     *   bit 3: LAN Enable */
    uint16_t subsys_vendor_id;      /* Word 0x0B: PCI Subsystem Vendor ID */
    uint16_t subsys_device_id;      /* Word 0x0C: PCI Subsystem Device ID */
    uint16_t led_config[3];         /* Word 0x1C-0x1E: LED 동작 설정 */
    uint16_t checksum;              /* Word 0x3F: 영역 체크섬 */

    /* Bank B (4KB) — 미러 백업 */
    /* Bank A와 동일 구조, 오류 복구용 이중화 */
};

/* SPI Flash 접근 권한 예시:
 *   BIOS: GbE 영역 읽기/쓰기 가능 → BIOS Setup에서 LOM 비트 변경
 *   ME:   GbE 영역 읽기/쓰기 가능 → 원격 관리(AMT)로 LOM 제어
 *   Host (OS): 읽기 가능, 쓰기 제한 → ethtool은 읽기만, 쓰기는 도구 필요
 */

NVM Word 0x0F 비트 필드 상세

다음 다이어그램은 Intel NIC의 Boot Configuration Word(NVM Word 0x0F)의 16비트 구조를 시각적으로 보여줍니다. LOM 비트(bit 1)가 PXE 부팅의 핵심 제어 플래그입니다.

NVM Word 0x0F — Boot Configuration Word 비트 필드 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Reserved (15:11) LB Boot Proto (9:8) Speed (7:6) APM Rsv LAN Flash PXE Rsv bit 1 — PXE Enable (LOM 비트) 1 = Option ROM 로드 허용 → PXE 네트워크 부팅 가능 0 = Option ROM 로드 차단 → PXE 부팅 불가 (OS 네트워크는 정상) bit 3 — LAN Enable 0 = NIC 자체를 비활성화 (OS에서도 NIC가 보이지 않음) bit 9:8 — Boot Protocol 00 = PXE (표준), 01 = RPL (사용 중단, 16-bit NetWare 호환), 10 = iSCSI Boot bit 7:6 — Force Speed 00 = 자동 협상 (권장), 01/10/11 = 10/100/1000 Mbps 강제 — PXE 환경에서 자동 협상 실패 시 사용 bit 5 — APM Wake-Up: WoL(Wake-on-LAN) 매직 패킷 수신 시 전원 투입 허용

NVM 설정 워드(Configuration Word) 구조

Intel NIC(예: I210, I350, X550)의 NVM에는 PXE 부팅 동작을 제어하는 설정 워드가 포함되어 있습니다. 대표적으로 NVM Word 0x0F가 부팅 관련 플래그를 담고 있습니다.

/* Intel NIC NVM Word 0x0F — Boot Configuration Word (예시) */
struct nvm_boot_config_word {
    uint16_t reserved0      : 1;  /* bit 0: 예약 */
    uint16_t pxe_enable     : 1;  /* bit 1: PXE Boot Enable (LOM 비트)
                                   *   1 = Option ROM 로드 허용 (PXE 부팅 가능)
                                   *   0 = Option ROM 로드 차단 (PXE 부팅 불가) */
    uint16_t flash_enable   : 1;  /* bit 2: Flash 기반 Option ROM 사용 */
    uint16_t lan_enable     : 1;  /* bit 3: LAN 포트 활성화 */
    uint16_t reserved4      : 1;  /* bit 4: 예약 */
    uint16_t apm_wakeup     : 1;  /* bit 5: APM Wake-Up 활성화 */
    uint16_t force_speed    : 2;  /* bit 7:6: 강제 링크 속도 설정
                                   *   00 = 자동 협상, 01 = 10 Mbps
                                   *   10 = 100 Mbps, 11 = 1000 Mbps */
    uint16_t boot_protocol  : 2;  /* bit 9:8: 부팅 프로토콜 선택
                                   *   00 = PXE, 01 = RPL (사용 중단)
                                   *   10 = iSCSI Boot */
    uint16_t legacy_boot    : 1;  /* bit 10: Legacy PXE Boot 모드 */
    uint16_t reserved11     : 5;  /* bit 15:11: 예약 */
};

/* NVM Word 0x0F 값 예시:
 * 0x0002 → PXE Enable만 활성화 (최소 설정)
 * 0x000A → PXE Enable + LAN Enable
 * 0x002A → PXE Enable + LAN Enable + APM Wake-Up
 * 0x0000 → 모든 부팅 기능 비활성화 (LOM 비트 꺼짐)
 */

PCI 확장 ROM BAR(Expansion ROM BAR)과 LOM 비트 연동

PCI 설정 공간(Configuration Space)의 레지스터 0x30은 확장 ROM 기본 주소 레지스터(Expansion ROM BAR)입니다. BIOS는 POST 과정에서 NVM의 LOM 비트를 확인한 후, 이 레지스터의 활성화 비트(bit 0)를 설정하여 Option ROM의 메모리 매핑 여부를 결정합니다.

/* PCI Configuration Space — Register 0x30: Expansion ROM BAR */
struct pci_expansion_rom_bar {
    uint32_t enable         : 1;  /* bit 0: ROM 디코딩 활성화
                                   *   BIOS가 NVM LOM 비트를 확인한 후 설정
                                   *   1 = ROM 주소 공간 활성화 → 0x55AA 시그니처 검사
                                   *   0 = ROM 비활성화 → Option ROM 무시 */
    uint32_t reserved       : 10; /* bit 10:1: 예약 */
    uint32_t base_address   : 21; /* bit 31:11: ROM 기본 주소 (2KB 정렬)
                                   *   일반적으로 C0000h-DFFFFh 영역에 매핑 */
};

/* LOM 비트 → Expansion ROM BAR 연동 흐름:
 *
 * 1. BIOS POST → PCI 버스 열거(Enumeration)
 * 2. 온보드 NIC 발견 → NVM에서 Boot Configuration Word 읽기
 * 3. LOM 비트(pxe_enable) 확인:
 *    ├─ 1 (활성화): Expansion ROM BAR.enable = 1
 *    │   → ROM을 C000h-DFFFFh에 매핑
 *    │   → 시그니처 0x55AA 확인 후 init_entry 호출
 *    │   → UNDI 드라이버 설치, INT 18h/19h 후킹
 *    │
 *    └─ 0 (비활성화): Expansion ROM BAR.enable = 0
 *        → Option ROM 스킵 → PXE 부팅 불가
 *        → NIC는 OS 드라이버로만 동작
 */
LOM 비트에 의한 PXE ROM 로드 결정 흐름 전원 투입 BIOS POST PCI 버스 열거 (Enumeration) 온보드 NIC 발견 NVM 읽기 LOM 비트 확인 활성화 (1) Expansion ROM BAR 매핑 C000h-DFFFFh, enable=1 0x55AA 시그니처 확인 init_entry 호출 PXE 부팅 가능 UNDI 설치 → DHCP → TFTP → NBP 비활성화 (0) Option ROM 스킵 enable=0, ROM 무시 PXE 부팅 불가 NIC는 OS 드라이버로만 동작 BIOS Setup 오버라이드 "Onboard LAN Boot ROM" NVM 값 변경

BIOS/UEFI 설정에서 LOM PXE 부팅 제어

대부분의 서버 및 데스크톱 BIOS/UEFI는 온보드 NIC의 PXE 부팅 기능을 설정 메뉴에서 제어할 수 있습니다. 이 설정은 내부적으로 NIC NVM의 LOM 비트를 변경하거나, BIOS 자체의 부팅 정책으로 Option ROM 로드를 제어합니다.

BIOS/펌웨어 벤더설정 경로설정 항목기본값
AMI BIOSAdvanced → Onboard Devices ConfigurationOnboard LAN Boot ROMDisabled
Phoenix/AwardIntegrated Peripherals → Onboard LANBoot ROM FunctionDisabled
Dell (iDRAC)System BIOS → Network SettingsPXE Device1 (Embedded NIC)Enabled
HP (iLO)System Configuration → BIOS/Platform → Network BootEmbedded NIC PXEEnabled
SupermicroAdvanced → Network Stack ConfigurationNetwork Stack / PXE BootEnabled
UEFI 공통Boot Options → Network BootIPv4/IPv6 PXE Support벤더별 상이

서버 제조사(Dell, HP, Supermicro 등)는 기본적으로 PXE 부팅이 활성화되어 있는 반면, 데스크톱/소비자용 메인보드는 비활성화가 기본인 경우가 많습니다. BIOS 설정 변경은 대부분 재부팅 후 즉시 반영됩니다.

NVM 직접 읽기 및 수정 도구

OS 환경에서 NIC NVM의 LOM 비트를 직접 확인하거나 수정할 수 있습니다. Linux에서는 ethtool의 EEPROM 덤프(Dump) 기능을, Intel NIC에서는 전용 도구 eeupdate를 사용합니다.

# ethtool로 NVM (EEPROM) 내용 읽기
# Word 0x0F (오프셋 0x1E-0x1F)의 Boot Configuration Word 확인
$ ethtool -e eth0 offset 0x1E length 2
Offset          Values
------          ------
0x001e:         0a 00

# 해석: 0x000A = 0000 0000 0000 1010 (binary)
#   bit 1 (pxe_enable) = 1 → PXE 부팅 활성화
#   bit 3 (lan_enable)  = 1 → LAN 포트 활성화

# Intel eeupdate 도구로 NVM 수정 (관리자 권한 필요)
# PXE 부팅 활성화: Word 0x0F의 bit 1을 1로 설정
$ ./eeupdate /NIC=1 /D /MAC        # 현재 NIC 정보 확인
$ ./eeupdate /NIC=1 /RW 0x0F       # Word 0x0F 읽기
$ ./eeupdate /NIC=1 /WW 0x0F 0x000A  # Word 0x0F에 0x000A 쓰기

# Broadcom NIC의 경우 bnxtnvm 또는 B57udiag 도구 사용
# Realtek NIC의 경우 PG Tool (Programmer Tool) 사용
주의: NVM을 직접 수정하면 NIC 펌웨어가 손상되어 하드웨어가 영구적으로 동작 불능 상태가 될 수 있습니다. 반드시 수정 전에 전체 NVM 백업(ethtool -e eth0 > nvm_backup.hex)을 수행하고, 제조사 공식 도구와 문서를 참조하십시오. BIOS 설정으로 제어 가능한 경우 NVM 직접 수정보다 BIOS 설정 변경을 우선 권장합니다.

LOM(온보드 NIC) vs 애드인 NIC(Add-in NIC) 비교

구분LOM (온보드 NIC)애드인 NIC (PCIe)
물리적 위치메인보드에 납땜 (교체 불가)PCIe 슬롯에 장착 (교체 가능)
NVM 위치메인보드 SPI Flash 또는 NIC 내장 EEPROMNIC 카드 자체의 EEPROM/Flash
PXE ROM 제어BIOS 설정 + NVM LOM 비트NIC 자체 NVM 설정
BIOS 연동BIOS가 직접 LOM 비트를 읽고 제어BIOS가 PCI 스캔 시 Option ROM BAR 확인
부팅 우선순위일반적으로 애드인 NIC보다 낮은 순위BIOS 부팅 순서에서 개별 설정 가능
실무 장점추가 비용 없음, 서버 기본 제공고성능 NIC 선택 가능, 이중화 구성 용이
PXE 부팅 실패 시BIOS에서 "Onboard LAN Boot ROM" 확인NIC NVM 설정 또는 BIOS 부팅 순서 확인
실무 팁 — PXE 부팅 실패 시 LOM 비트 점검 체크리스트:
  1. BIOS 설정에서 "Onboard LAN Boot ROM" 또는 "Network Boot" 항목이 Enabled인지 확인합니다.
  2. UEFI 모드인 경우 "Network Stack"과 "IPv4/IPv6 PXE Support"가 모두 활성화되어 있는지 확인합니다.
  3. BIOS 부팅 순서(Boot Order)에 네트워크 부팅 장치가 포함되어 있는지 확인합니다.
  4. OS에서 ethtool -e eth0 offset 0x1E length 2로 NVM의 PXE Enable 비트를 직접 확인합니다.
  5. 듀얼 NIC 서버에서는 어떤 포트가 PXE 부팅용인지 확인합니다 (일반적으로 Port 0 / NIC1이 기본).
  6. 링크(Link) LED가 점등되는지 물리 계층 연결 상태를 확인합니다.

커널 드라이버의 NVM/EEPROM 접근 코드 분석

Linux 커널의 NIC 드라이버는 NVM(EEPROM/SPI Flash)에 직접 접근하여 MAC 주소, LOM 비트, 부팅 설정 등을 읽고 씁니다. 사용자 공간(User Space)의 ethtool -e 명령은 최종적으로 커널 드라이버의 struct ethtool_ops 콜백(Callback)을 호출하며, 각 드라이버는 하드웨어에 맞는 NVM 접근 프로토콜을 구현합니다. 또한 칩셋에 따라 NVM 세마포어(Semaphore)와 하드웨어 잠금(Lock) 절차가 다르므로, 동일한 ethtool 명령이라도 내부 구현은 크게 달라집니다. Intel NIC 드라이버 계열(e1000e, igb, ixgbe, ice)을 중심으로 NVM 접근의 커널 내부 구현을 분석합니다.

/* drivers/net/ethernet/intel/e1000e/ethtool.c
 * e1000e 드라이버의 EEPROM 읽기 콜백 — ethtool -e 호출 시 실행
 *
 * ethtool이 ETHTOOL_GEEPROM ioctl을 발행하면 커널의 ethtool 계층이
 * 이 함수를 호출합니다. 바이트 오프셋을 NVM 워드 단위로 변환하고,
 * 드라이버의 저수준 NVM 읽기 함수를 통해 하드웨어에 접근합니다.
 */
static int e1000_get_eeprom(struct net_device *netdev,
                            struct ethtool_eeprom *eeprom, u8 *bytes)
{
    struct e1000_adapter *adapter = netdev_priv(netdev);
    struct e1000_hw *hw = &adapter->hw;
    u16 *eeprom_buff;
    int first_word, last_word, eeprom_len;
    int ret_val = 0;
    u16 i;

    if (eeprom->len == 0)
        return -EINVAL;

    eeprom->magic = adapter->pdev->vendor |
                    (adapter->pdev->device << 16);

    /* 바이트 오프셋 → NVM 워드 인덱스 변환
     * NVM은 16-bit 워드 단위로 주소 지정됩니다.
     * 예: ethtool -e eth0 offset 0x1E → first_word = 0x0F (Word 15)
     *     이것이 바로 Boot Configuration Word (LOM 비트 포함) */
    first_word = eeprom->offset >> 1;
    last_word = (eeprom->offset + eeprom->len - 1) >> 1;
    eeprom_len = last_word - first_word + 1;

    eeprom_buff = kmalloc_array(eeprom_len, sizeof(u16), GFP_KERNEL);
    if (!eeprom_buff)
        return -ENOMEM;

    /* 실제 NVM 하드웨어 읽기 수행
     * e1000_read_nvm()는 hw->nvm.ops.read를 호출하며,
     * 이는 칩 종류에 따라 EERD 레지스터 또는 SPI 프로토콜을 사용합니다. */
    ret_val = e1000_read_nvm(hw, first_word, eeprom_len, eeprom_buff);
    if (ret_val) {
        /* NVM 읽기 실패 시 -EIO 반환
         * 원인: SPI Flash 손상, PCH 잠금, 하드웨어 오류 */
        kfree(eeprom_buff);
        return -EIO;
    }

    /* 16-bit 워드 배열 → 바이트 배열로 변환하여 사용자 공간에 전달
     * ethtool은 이 바이트 배열을 16진수 덤프로 출력합니다. */
    for (i = 0; i < eeprom_len; i++)
        le16_to_cpus(&eeprom_buff[i]);

    memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
    kfree(eeprom_buff);

    return ret_val;
}
/* drivers/net/ethernet/intel/e1000e/nvm.c
 * EERD(EEPROM Read) 레지스터를 통한 NVM 워드 읽기
 *
 * Intel I217/I218/I219-LM 등 PCH 통합 MAC에서 사용하는 방식입니다.
 * CPU가 EERD 레지스터에 읽을 주소를 쓰면, PCH 하드웨어가 SPI Flash의
 * GbE 영역에서 데이터를 읽어 EERD에 결과를 채웁니다.
 */
s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset,
                        u16 words, u16 *data)
{
    struct e1000_nvm_info *nvm = &hw->nvm;
    u32 i, eerd = 0;
    s32 ret_val = 0;

    /* NVM 하드웨어 세마포어 획득
     * 동시에 BIOS, ME(Management Engine), OS 드라이버가
     * 같은 SPI Flash에 접근할 수 있으므로 하드웨어 수준 잠금이 필요합니다.
     * SWSM(Software Semaphore) 레지스터의 SMBI 비트를 사용합니다. */
    ret_val = nvm->ops.acquire(hw);
    if (ret_val)
        return ret_val;

    for (i = 0; i < words; i++) {
        /* EERD 레지스터에 읽기 요청 설정:
         *   bit 0: START (읽기 시작)
         *   bit 15:2: 주소 (NVM 워드 오프셋)
         *
         * 예: offset=0x0F (Boot Config Word)
         *   → eerd = (0x0F << E1000_NVM_RW_ADDR_SHIFT) | E1000_NVM_RW_REG_START
         *   → eerd = 0x0000003C | 0x00000001 = 0x0000003D */
        eerd = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) +
               E1000_NVM_RW_REG_START;

        E1000_WRITE_REG(hw, E1000_EERD, eerd);

        /* DONE 비트 폴링 — PCH가 SPI Flash에서 데이터를 읽을 때까지 대기
         * 일반적으로 수 마이크로초 이내에 완료됩니다.
         * 타임아웃 발생 시 SPI Flash 하드웨어 오류 가능성이 있습니다. */
        ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ);
        if (ret_val)
            break;

        /* EERD 레지스터의 상위 16비트에서 데이터 추출
         * bit 31:16: 읽은 NVM 데이터 (16-bit 워드) */
        data[i] = (E1000_READ_REG(hw, E1000_EERD) >>
                   E1000_NVM_RW_REG_DATA) & 0xFFFF;
    }

    /* NVM 세마포어 해제 — 다른 에이전트(BIOS, ME)가 접근 가능해짐 */
    nvm->ops.release(hw);

    return ret_val;
}
/* drivers/net/ethernet/intel/igb/igb_ethtool.c
 * I350/I210 멀티포트 LOM — 포트별 NVM 영역 오프셋 계산
 *
 * 서버용 쿼드포트 LOM(I350)에서는 하나의 SPI Flash에
 * 4개 포트의 NVM 영역이 연속 배치됩니다.
 * 각 포트는 독립적인 Boot Configuration Word를 보유합니다.
 */
static int igb_get_eeprom(struct net_device *netdev,
                          struct ethtool_eeprom *eeprom, u8 *bytes)
{
    struct igb_adapter *adapter = netdev_priv(netdev);
    struct e1000_hw *hw = &adapter->hw;
    u16 *eeprom_buff;
    int first_word, last_word, eeprom_len;
    int ret_val = 0;
    u16 i;

    if (eeprom->len == 0)
        return -EINVAL;

    eeprom->magic = adapter->pdev->vendor |
                    (adapter->pdev->device << 16);

    first_word = eeprom->offset >> 1;
    last_word = (eeprom->offset + eeprom->len - 1) >> 1;
    eeprom_len = last_word - first_word + 1;

    eeprom_buff = kmalloc_array(eeprom_len, sizeof(u16), GFP_KERNEL);
    if (!eeprom_buff)
        return -ENOMEM;

    /* igb 드라이버의 NVM 읽기
     * I350 쿼드포트에서 hw->bus.func가 PCI 함수 번호(0~3)를 나타내며,
     * igb_read_nvm() 내부에서 포트별 NVM 오프셋을 자동 계산합니다.
     *
     * I350 NVM 레이아웃 (포트별 오프셋):
     *   Port 0: 오프셋 0x000 ~ 0x07F (공유 + 포트 0 전용)
     *   Port 1: 오프셋 0x080 ~ 0x0FF
     *   Port 2: 오프셋 0x100 ~ 0x17F
     *   Port 3: 오프셋 0x180 ~ 0x1FF
     *
     * 따라서 Port 2의 Boot Config Word(0x0F)는
     * 실제 NVM 오프셋 0x10F에 위치합니다. */
    if (hw->nvm.type == e1000_nvm_eeprom_spi)
        ret_val = igb_read_nvm(hw, first_word, eeprom_len, eeprom_buff);
    else {
        for (i = 0; i < eeprom_len; i++) {
            ret_val = igb_read_nvm(hw, first_word + i, 1,
                                   &eeprom_buff[i]);
            if (ret_val)
                break;
        }
    }

    if (ret_val) {
        kfree(eeprom_buff);
        return -EIO;
    }

    for (i = 0; i < eeprom_len; i++)
        le16_to_cpus(&eeprom_buff[i]);

    memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
    kfree(eeprom_buff);
    return ret_val;
}
/* drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
 * 10GbE NIC(X520/X540/X550)의 SPI Flash 비트뱅(Bit-Bang) 프로토콜
 *
 * 10GbE NIC는 EERD 레지스터 대신 SPI 프로토콜을 직접 구현하여
 * Flash 메모리에 접근합니다. GPIO 핀을 통해 SPI 클럭(SCK),
 * 데이터 입력(SI), 데이터 출력(SO), 칩 셀렉트(CS)를 제어합니다.
 */
s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
                                        u16 *data)
{
    s32 status;
    u16 word_in;
    u8 read_opcode = IXGBE_EEPROM_READ_OPCODE_SPI;

    hw->eeprom.ops.init_params(hw);

    if (offset >= hw->eeprom.word_size) {
        status = IXGBE_ERR_EEPROM;
        goto out;
    }

    /* SPI Flash 접근 세마포어 획득 */
    status = hw->eeprom.ops.acquire(hw);
    if (status)
        goto out;

    /* SPI 주소 크기에 따라 명령어 결정
     * 256-word 이상의 Flash는 확장 주소(A8) 비트가 필요합니다.
     *   표준 READ (03h): 24-bit 주소
     *   주소 비트 8을 opcode에 OR 처리 */
    if (hw->eeprom.word_size > 256)
        if (offset >= 256)
            read_opcode |= IXGBE_EEPROM_A8_OPCODE_SPI;

    /* SPI 비트뱅 시퀀스:
     * 1. CS(Chip Select) LOW로 SPI Flash 선택
     * 2. READ 명령어(03h) 전송 (8 클럭)
     * 3. 주소 전송 (16-bit, MSB first)
     * 4. 데이터 수신 (16-bit, MSB first)
     * 5. CS HIGH로 전송 완료 */
    ixgbe_standby_eeprom(hw);

    /* SPI READ 명령 + 주소 전송 */
    ixgbe_shift_out_eeprom_bits(hw, read_opcode,
                                 IXGBE_EEPROM_OPCODE_BITS);
    ixgbe_shift_out_eeprom_bits(hw, (u16)(offset * 2),
                                 hw->eeprom.address_bits);

    /* 16-bit 데이터 수신 — SPI MISO 라인에서 비트 단위로 읽기 */
    word_in = ixgbe_shift_in_eeprom_bits(hw, 16);
    *data = word_in;

    ixgbe_standby_eeprom(hw);
    hw->eeprom.ops.release(hw);

out:
    return status;
}
커널 NVM 접근 계층 — ethtool에서 하드웨어까지 사용자 공간 (Userspace) ethtool -e eth0 offset 0x1E length 2 ioctl(SIOCETHTOOL) 커널 ethtool 계층 dev_ethtool() → ethtool_get_eeprom() ops->get_eeprom() NIC 드라이버 ethtool_ops e1000_get_eeprom() / igb_get_eeprom() e1000_read_nvm() NVM 하드웨어 추상화 계층 hw->nvm.ops.read() — 칩별 프로토콜 선택 EERD 레지스터 방식 e1000e (I217/I218/I219) 주소 쓰기 → DONE 폴링 → 데이터 읽기 SPI 비트뱅 방식 ixgbe (X520/X540/X550) CS→CMD→ADDR→DATA (GPIO) Admin Queue 커맨드 ice (E810), i40e (X710/XL710) AQ 0x0701 NVM Read → 펌웨어 처리 NVM 하드웨어 (SPI Flash / EEPROM) — LOM 비트(Word 0x0F bit 1) 저장
드라이버대상 NICNVM 접근 방식세마포어특징
e1000eI217/I218/I219-LMEERD 레지스터SWSM(Software Semaphore)PCH 통합 MAC, SPI Flash 공유
igbI350/I210/I211EERD + Flash 직접SW_FW_SYNC멀티포트 NVM 오프셋 자동 계산
ixgbeX520/X540/X550SPI 비트뱅(Bit-Bang)SWSMGPIO 핀으로 SPI 프로토콜 직접 구현
i40eX710/XL710/XXV710Admin Queue 커맨드NVM 잠금(AQ)펌웨어가 NVM 접근 중재, 직접 접근 불가
iceE810/E830Admin Queue 커맨드NVM 잠금(AQ)Shadow RAM 활용, 트랜잭션 기반 쓰기
실무 팁 — LOM NIC의 커널 드라이버 확인 방법:
# 현재 NIC가 사용하는 커널 드라이버 확인
$ ethtool -i eth0
driver: e1000e
version: 6.1.0-k
firmware-version: 0.5-4
bus-info: 0000:00:1f.6    # PCH 내장 NIC (BDF 00:1F.6 = 전형적인 LOM)

# PCI 장치별 바인딩된 드라이버 확인
$ lspci -k -s 00:1f.6
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (14) I219-LM
    Subsystem: Dell Device 0b1a
    Kernel driver in use: e1000e
    Kernel modules: e1000e

# NVM 워드 크기 확인 (드라이버 로그)
$ dmesg | grep -i "nvm\|eeprom\|flash"
[    2.145] e1000e 0000:00:1f.6: NVM checksum is valid
[    2.146] e1000e 0000:00:1f.6: NVM size = 64KB

ethtool NVM 접근의 커널 내부 호출 흐름

사용자 공간에서 ethtool -e(읽기) 또는 ethtool -E(쓰기)를 실행하면, 커널 내부에서는 ioctl(SIOCETHTOOL)dev_ethtool()ethtool_get_eeprom()/ethtool_set_eeprom()의 호출 체인을 거칩니다. 특히 쓰기 경로에서는 magic number 검증이 수행되어 실수로 잘못된 NIC의 NVM을 변경하는 것을 방지합니다.

/* include/uapi/linux/ethtool.h
 * ethtool EEPROM 접근에 사용되는 구조체
 *
 * 사용자 공간의 ethtool 유틸리티가 이 구조체를 채워
 * ioctl 시스템 호출로 커널에 전달합니다.
 */
struct ethtool_eeprom {
    __u32 cmd;      /* ETHTOOL_GEEPROM (0x0000000b) = 읽기
                     * ETHTOOL_SEEPROM (0x0000000c) = 쓰기 */
    __u32 magic;    /* 읽기 시: 드라이버가 vendor_id | (device_id << 16)으로 채움
                     * 쓰기 시: 사용자가 동일한 값을 전달해야 함 (검증용)
                     *
                     * 예: Intel I219-LM
                     *   vendor_id = 0x8086 (Intel)
                     *   device_id = 0x15BB (I219-LM v9)
                     *   → magic = 0x15BB8086
                     *
                     * 잘못된 magic → -EINVAL 반환 (NVM 보호) */
    __u32 offset;   /* 읽기/쓰기 시작 바이트 오프셋
                     * 예: Word 0x0F = 바이트 오프셋 0x1E */
    __u32 len;      /* 읽기/쓰기 바이트 수 */
};

/* ethtool -e eth0 offset 0x1E length 2 실행 시:
 *   cmd    = ETHTOOL_GEEPROM (0x0b)
 *   offset = 0x1E (30)
 *   len    = 2
 *
 * ethtool -E eth0 magic 0x15BB8086 offset 0x1E value 0x0A 실행 시:
 *   cmd    = ETHTOOL_SEEPROM (0x0c)
 *   magic  = 0x15BB8086 (사용자 지정)
 *   offset = 0x1E
 *   len    = 1
 */
/* net/ethtool/ioctl.c (커널 6.x)
 * ethtool ioctl 메인 디스패처 — EEPROM 읽기 경로
 *
 * 사용자 공간의 ioctl(SIOCETHTOOL)이 소켓 계층을 거쳐
 * dev_ethtool()에 도달하면, 명령어별로 핸들러를 분기합니다.
 */
int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
{
    /* ... 전처리 생략 ... */
    u32 ethcmd;

    if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
        return -EFAULT;

    switch (ethcmd) {
    /* ... 다른 ethtool 명령들 ... */

    case ETHTOOL_GEEPROM:
        /* NVM 읽기 — 관리자 권한(CAP_NET_ADMIN) 불필요 */
        rc = ethtool_get_eeprom(dev, useraddr);
        break;

    case ETHTOOL_SEEPROM:
        /* NVM 쓰기 — CAP_NET_ADMIN 필수 (root 또는 적절한 capability)
         * 일반 사용자는 NVM을 읽을 수는 있지만 쓸 수 없습니다. */
        rc = ethtool_set_eeprom(dev, useraddr);
        break;
    }
    return rc;
}

/* ethtool_get_eeprom() — NVM 읽기 핸들러 */
static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
{
    struct ethtool_eeprom eeprom;
    const struct ethtool_ops *ops = dev->ethtool_ops;
    void __user *userbuf = useraddr + sizeof(eeprom);
    u8 *data;
    int ret;

    if (!ops->get_eeprom || !ops->get_eeprom_len)
        return -EOPNOTSUPP;

    if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
        return -EFAULT;

    /* EEPROM 크기 검증 */
    if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
        return -EINVAL;

    data = kzalloc(eeprom.len, GFP_USER);
    if (!data)
        return -ENOMEM;

    /* 드라이버의 get_eeprom 콜백 호출
     * 여기서 e1000_get_eeprom(), igb_get_eeprom() 등이 실행됩니다.
     * 드라이버는 magic 필드에 vendor_id | (device_id << 16)을 채웁니다. */
    ret = ops->get_eeprom(dev, &eeprom, data);
    if (ret)
        goto out;

    /* 결과를 사용자 공간으로 복사 */
    if (copy_to_user(useraddr, &eeprom, sizeof(eeprom))) {
        ret = -EFAULT;
        goto out;
    }
    if (copy_to_user(userbuf, data, eeprom.len))
        ret = -EFAULT;

out:
    kfree(data);
    return ret;
}

/* ethtool_set_eeprom() — NVM 쓰기 핸들러 */
static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
{
    struct ethtool_eeprom eeprom;
    const struct ethtool_ops *ops = dev->ethtool_ops;
    void __user *userbuf = useraddr + sizeof(eeprom);
    u8 *data;
    int ret;

    if (!ops->set_eeprom || !ops->get_eeprom_len)
        return -EOPNOTSUPP;

    if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
        return -EFAULT;

    if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
        return -EINVAL;

    data = memdup_user(userbuf, eeprom.len);
    if (IS_ERR(data))
        return PTR_ERR(data);

    /* 드라이버의 set_eeprom 콜백 호출
     * 드라이버 내부에서 magic number를 검증합니다:
     *   if (eeprom->magic != (vendor_id | (device_id << 16)))
     *       return -EINVAL;
     *
     * 이 검증 덕분에 잘못된 NIC를 대상으로 NVM을 쓰는 것을 방지합니다.
     * 예를 들어 I219-LM의 magic(0x15BB8086)으로 I350(0x15218086)의
     * NVM을 쓰려고 하면 -EINVAL로 거부됩니다. */
    ret = ops->set_eeprom(dev, &eeprom, data);

    kfree(data);
    return ret;
}
ethtool EEPROM 읽기/쓰기 — 커널 호출 시퀀스 ethtool (User) Socket Layer ethtool Core NIC Driver NVM 읽기 ioctl(fd, SIOCETHTOOL, &ifr) dev_ethtool(net, ifr) ops->get_eeprom(dev, &ee, buf) EERD/SPI Read NVM 데이터 바이트 배열 + magic copy_to_user() 0x001e: 0a 00 쓰기 ioctl(SIOCETHTOOL) + magic ethtool_set_eeprom() ops->set_eeprom() + magic 검증 magic 불일치 → -EINVAL 반환 magic 일치 EERD/SPI Write
# ethtool NVM 읽기/쓰기 전체 세션 예제

# 1. NIC 정보 확인 — magic number 계산에 필요
$ ethtool -i eth0
driver: e1000e
bus-info: 0000:00:1f.6

$ lspci -n -s 00:1f.6
00:1f.6 0200: 8086:15bb (rev 10)
# vendor=0x8086, device=0x15bb → magic = 0x15BB8086

# 2. NVM 읽기 — Word 0x0F (Boot Configuration Word)
$ sudo ethtool -e eth0 offset 0x1E length 2
Offset          Values
------          ------
0x001e:         0a 00

# 해석: Little-Endian → 0x000A = 0000 0000 0000 1010
#   bit 1 = 1 → PXE Enable (LOM 비트 ON)
#   bit 3 = 1 → LAN Enable

# 3. NVM 전체 백업 (수정 전 필수!)
$ sudo ethtool -e eth0 > /tmp/nvm_backup_eth0.hex
$ wc -l /tmp/nvm_backup_eth0.hex
257 /tmp/nvm_backup_eth0.hex

# 4. NVM 쓰기 — PXE 비활성화 (bit 1을 0으로)
# Word 0x0F = 0x0008 (LAN Enable만, PXE Disable)
# 바이트 오프셋 0x1E에 0x08, 0x1F에 0x00 쓰기
$ sudo ethtool -E eth0 magic 0x15BB8086 offset 0x1E value 0x08
$ sudo ethtool -E eth0 magic 0x15BB8086 offset 0x1F value 0x00

# 5. 쓰기 결과 확인
$ sudo ethtool -e eth0 offset 0x1E length 2
Offset          Values
------          ------
0x001e:         08 00
# 0x0008 = PXE Disable, LAN Enable → 다음 재부팅부터 PXE 부팅 불가

# 6. 잘못된 magic으로 쓰기 시도 — 거부됨
$ sudo ethtool -E eth0 magic 0xDEADBEEF offset 0x1E value 0x0A
Cannot set EEPROM data: Invalid argument
# magic 불일치 → 커널 드라이버가 -EINVAL 반환
주의 — NVM 쓰기 시 magic number 보호 메커니즘:

ethtool -E로 NVM을 쓸 때는 반드시 올바른 magic number를 지정해야 합니다. magic number는 vendor_id | (device_id << 16)으로 계산하며, lspci -n으로 확인할 수 있습니다. 이 보호 메커니즘은 잘못된 NIC의 NVM을 실수로 변경하는 것을 방지하지만, NVM 데이터 자체의 유효성은 검증하지 않습니다. 잘못된 값을 쓰면 NIC가 영구적으로 동작하지 않을 수 있으므로, 수정 전 반드시 전체 NVM을 백업하고 제조사 문서에서 정확한 비트 필드를 확인하십시오.

칩셋별 프리부트 모델 비교

같은 "PXE 부팅"이라도 실제 구현은 NIC 칩셋마다 크게 다릅니다. 어떤 제품은 NIC 자체 flash에 Option ROM을 넣고, 어떤 제품은 시스템 BIOS flash가 대신 보관하며, 어떤 제품은 아예 NIC 펌웨어가 FlexBoot 같은 프리부트 이미지를 직접 관리합니다. 따라서 OS에서 링크가 살아 있습니다는 사실만으로 프리부트 경로까지 정상이라고 판단하면 안 됩니다.

칩셋 계열대표 예시프리부트 코드 위치영구 설정 위치PXE 실패 시 먼저 볼 지점
Intel 독립형 컨트롤러I210, I211, I350애드인 NIC는 로컬 flash, 임베디드/LOM 설계는 system BIOS flash 또는 보드 NVMNIC NVM 또는 iNVM올바른 NVM 이미지, Option ROM 저장 위치, 전원 재인가 필요 여부
Intel PCH/SoC LOMI219-LM, I225-V, I226-LM대개 BIOS/UEFI flash 내부 네트워크 스택 또는 펌웨어 드라이버플랫폼 SPI 영역, BIOS NVRAM, Setup 정책Network Stack, IPv4 PXE Support, CSM/UEFI 모드 분기
Broadcom 서버 LOMBCM5720, BCM57414, BCM57416서버 펌웨어가 읽는 MBA/FlexBoot 이미지NVRAM 디렉터리 엔트리, BIOS/BMC 정책포트별 MBA 설정, 프로토콜(PXE/iSCSI/FCoE), BMC Boot Override 충돌
Realtek 데스크톱 NICRTL8111, RTL8125BIOS Option ROM 또는 UEFI 네트워크 드라이버EEPROM 또는 eFuse, BIOS 설정eFuse 설계 여부, BIOS Network Boot, Option ROM 활성화 정책
Mellanox/NVIDIA 데이터센터 NICConnectX-5, ConnectX-6, ConnectX-7NIC 펌웨어가 관리하는 FlexBoot/UEFI 이미지펌웨어 구성 플래시BOOT_OPTION_ROM_EN, PXE/iSCSI/FCoE 모드, 포트별 부트 정책
Marvell/Aquantia 고속 NICAQC107, AQC113OEM이 번들한 Option ROM 또는 UEFI 드라이버보드별 펌웨어 패키지, OEM 도구OS 링크는 정상인데 pre-boot 코드가 미탑재인지, OEM BIOS 업데이트가 필요한지
핵심 구분: Intel I210I219 계열과 같은 방식으로 다루면 안 됩니다. I210은 독립 PCIe 컨트롤러라서 NIC 로컬 flash, system BIOS flash, iNVM 중 어디에 프리부트 코드와 설정이 들어 있는지 보드 설계마다 달라질 수 있습니다. 반면 I219류는 대체로 플랫폼 펌웨어와 더 강하게 결합된 LOM 모델입니다.

Intel I210/I211 특이사항

I210은 PXE 문서에서 자주 언급되지만, 실제 bring-up과 장애 분석 관점에서는 별도로 떼어 설명하는 편이 정확합니다. 이유는 독립형 GbE 컨트롤러이면서도 pre-boot 저장 위치, NVM 이미지 종류, flashless/iNVM 운용 방식이 보드 설계에 따라 크게 달라지기 때문입니다.

항목I210I211실무 의미
프리부트 지원 범위PXE, iSCSI Boot, UEFI 경로를 폭넓게 지원합니다.PXE 중심이며 system BIOS flash와 기본 설정 경로를 전제로 보는 편이 안전합니다.OS 드라이버가 정상 동작해도 pre-boot 기능 범위는 별도 검증이 필요합니다.
Option ROM 저장 위치애드인 NIC는 로컬 flash, LOM/임베디드 설계는 system BIOS flash에 둘 수 있습니다.실무에서는 system BIOS flash 경로를 먼저 의심하는 편이 맞습니다.카드형 I210과 온보드 설계를 같은 절차로 점검하면 원인을 놓치기 쉽습니다.
NVM 이미지 분기I210-AT/IT는 copper PHY 계열, I210-IS/AS는 SGMII/SerDes 계열로 보는 편이 안전합니다.구성 자체는 단순하지만 기본 설정 제약이 상대적으로 큽니다.잘못된 이미지를 쓰면 OS 링크는 살아도 PXE DHCP가 실패하거나 부팅 메뉴가 사라질 수 있습니다.
flashless/iNVM 설계가능하지만 수정 자유도가 낮고 보드 bring-up 절차가 민감합니다.제약이 더 크며 기본 설정 의존도가 높습니다."NIC는 보이는데 PXE만 안 됨" 증상의 대표 원인 후보입니다.
설정 반영 시점NVM 또는 프리부트 이미지를 바꾼 뒤 완전 전원 재인가가 필요할 수 있습니다.BIOS flash 업데이트 절차와 플랫폼 정책 영향이 큽니다.단순 reboot만 반복하면 pre-boot 상태가 바뀌지 않는 사례가 있습니다.
I210 보드 bring-up 팁: 애드인 I210 카드라면 로컬 flash에 PXE/iSCSI/UEFI ROM이 실제로 들어 있는지를 먼저 보십시오. 임베디드 보드의 I210-IS/AS 설계라면 SGMII/KX 이미지 선택외부 PHY 초기화 경로를 먼저 확인하는 편이 빠릅니다. 프리부트 ROM을 NIC 로컬 flash에 넣는 설계는 보통 충분한 flash 용량을 전제로 잡아야 하며, 현장에서는 이 부분이 누락되어 "OS는 되지만 PXE는 없음" 상태가 자주 발생합니다.
# Intel I210/I211 계열 PXE 점검 순서

# 1. 실제 칩 variant와 드라이버 확인
$ lspci -nn | grep -i ethernet
$ ethtool -i eth0

# 2. Option ROM 가시성 확인
$ lspci -vv -s 03:00.0 | grep -i rom

# 3. Intel igb 계열이면 Boot Configuration Word 확인
$ sudo ethtool -e eth0 offset 0x1E length 2

# 4. 보드 문서와 NVM image 종류 대조
#    - I210-AT/IT: copper PHY 계열 image
#    - I210-IS/AS: SGMII/SerDes 계열 image
#    image가 어긋나면 PXE 링크 또는 DHCP가 비정상일 수 있습니다.

# 5. NVM/ROM 갱신 후에는 reboot만 하지 말고
#    AC 전원까지 완전히 끊는 power cycle이 필요한지 확인
$ sudo poweroff

주요 NIC 칩셋의 프리부트 제어 상세

앞서 설명한 NVM 구조와 접근 방식은 주로 Intel NIC를 중심으로 다루었습니다. 그러나 실무 환경에서는 Broadcom(Dell/HPE 서버), Realtek(데스크톱/소비자용 메인보드), Mellanox/NVIDIA(고성능 데이터센터)의 LOM도 널리 사용됩니다. 각 벤더의 NVM 구조와 PXE 부팅 제어 방식은 크게 다르며, 사용하는 커널 드라이버와 관리 도구도 다릅니다.

/* drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
 * Broadcom NetXtreme-C/E (bnxt) — HWRM 펌웨어 커맨드를 통한 NVM 접근
 *
 * Broadcom NIC는 Intel과 달리 OS 드라이버가 NVM 하드웨어에 직접 접근하지 않습니다.
 * 대신 NIC 내장 펌웨어(ChiMP — Chip Management Processor)에
 * HWRM(Hardware Resource Manager) 커맨드를 전송하여 NVM 읽기/쓰기를 요청합니다.
 */
static int bnxt_get_nvram(struct net_device *dev,
                          struct ethtool_eeprom *eeprom, u8 *buf)
{
    struct bnxt *bp = netdev_priv(dev);
    int rc;

    eeprom->magic = bp->pdev->vendor | (bp->pdev->device << 16);

    /* HWRM_NVM_READ 커맨드를 펌웨어에 전송
     * bp->hwrm_cmd_ring을 통해 ChiMP 프로세서에 명령을 전달하고,
     * 펌웨어가 Flash에서 데이터를 읽어 DMA 버퍼에 반환합니다.
     *
     * HWRM 커맨드 흐름:
     *   드라이버 → HWRM Ring → ChiMP CPU → Flash Controller → Flash
     *   Flash → ChiMP CPU → DMA → Host Memory (buf)
     *
     * 이 간접 접근 방식의 장점:
     *   - Flash 잠금/동기화를 펌웨어가 관리 (드라이버 복잡도 감소)
     *   - Flash 종류(SPI, QSPI, NOR)에 무관하게 동일한 인터페이스
     *   - 펌웨어 업데이트 중에도 안전한 동시 접근 보장 */
    rc = bnxt_hwrm_nvm_read(bp, eeprom->offset, eeprom->len, buf);

    return rc;
}

/* Broadcom NVRAM 디렉터리 구조
 * Intel의 플랫(flat) 워드 기반 NVM과 달리, Broadcom은 디렉터리 기반입니다.
 * 각 NVM 항목은 타입(type)과 인스턴스(instance)로 식별됩니다. */
struct bnxt_nvm_directory_entry {
    __le16 type;            /* 항목 타입 */
    __le16 ordinal;         /* 같은 타입 내 순서 */
    __le16 ext;             /* 확장 타입 */
    __le16 attr;            /* 속성 (읽기 전용, 숨김 등) */
    __le32 data_offset;     /* Flash 내 데이터 오프셋 */
    __le32 data_length;     /* 데이터 길이 (바이트) */
    __le32 checksum;        /* CRC32 체크섬 */
};

/* PXE 관련 NVRAM 디렉터리 타입:
 *   0x09: MBA Config (Multi Boot Agent) — PXE Enable/Disable, 부팅 프로토콜
 *   0x0A: MBA Alt Config — 대체 MBA 설정 (포트별)
 *   0x22: Boot Config — 부팅 순서, 재시도 횟수
 *   0x70: VPD (Vital Product Data) — MAC 주소, 시리얼 번호
 */
/* drivers/net/ethernet/realtek/r8169_main.c
 * Realtek RTL8111/RTL8125 — 레지스터 I/O를 통한 EEPROM 접근
 *
 * Realtek NIC는 Intel의 EERD나 Broadcom의 HWRM과 달리,
 * 레지스터에 직접 비트 조작으로 EEPROM을 읽고 씁니다.
 * 93C46/93C56 시리얼 EEPROM 또는 eFuse를 사용합니다.
 */

/* EEPROM 레지스터 비트 정의 */
#define RTL_EEPROM_CMD     0x50    /* EEPROM 명령 레지스터 */
#define RTL_93C46_CMD_READ 0x06    /* 93C46 읽기 명령 (3-bit) */

/* EEPROM 읽기 시퀀스 (비트뱅 프로토콜):
 * 1. Cfg9346 레지스터에 0xC0 쓰기 → EEPROM 접근 모드 진입
 * 2. 시작 비트(1) + 읽기 명령(10) + 주소(6-bit) 전송
 * 3. 16-bit 데이터 수신 (MSB first)
 * 4. Cfg9346 레지스터에 0x00 쓰기 → 접근 모드 종료 */
static u16 rtl_eeprom_read(struct rtl8169_private *tp, int addr)
{
    u16 data = 0;
    int i;

    /* EEPROM 접근 모드 활성화 */
    RTL_W8(tp, Cfg9346, Cfg9346_Unlock);

    /* 시작 비트 + READ 명령 전송 */
    rtl_eeprom_cmd(tp, RTL_93C46_CMD_READ | (addr & 0x3F));

    /* 16-bit 데이터 비트 단위 수신 */
    for (i = 15; i >= 0; i--) {
        RTL_W8(tp, RTL_EEPROM_CMD, EE_CLK);  /* 클럭 HIGH */
        udelay(5);
        if (RTL_R8(tp, RTL_EEPROM_CMD) & EE_DO)
            data |= (1 << i);  /* 데이터 비트 읽기 */
        RTL_W8(tp, RTL_EEPROM_CMD, 0);       /* 클럭 LOW */
        udelay(5);
    }

    /* EEPROM 접근 모드 비활성화 */
    RTL_W8(tp, Cfg9346, Cfg9346_Lock);

    return data;
}

/* Realtek NIC의 PXE 관련 EEPROM 구조:
 *   Word 0x00~0x02: MAC 주소 (48-bit)
 *   Word 0x07: Autoload 설정 — PXE/WoL 관련 비트 포함
 *     bit 4: PXE Boot Enable (Realtek의 LOM 비트에 해당)
 *     bit 5: WoL Enable
 *   Word 0x08: LED 설정
 *   Word 0x09: 서브시스템 벤더 ID
 *
 * 참고: RTL8125 (2.5GbE)는 eFuse 기반으로 EEPROM 구조가 다릅니다.
 *   eFuse는 일회성 프로그래밍(OTP) 메모리이므로 쓰기 후 변경 불가합니다. */
# Broadcom NIC: bnxtnvm 도구로 PXE 설정 읽기/변경
# Dell PowerEdge 서버에서 Broadcom BCM5720 LOM 관리 예시

# NVM 디렉터리 목록 조회
$ sudo bnxtnvm -dev=eth0 -dir
Directory entries:
  Type 0x09 (MBA Config)     Offset: 0x10000  Length: 128
  Type 0x0A (MBA Alt Config) Offset: 0x10100  Length: 64
  Type 0x22 (Boot Config)    Offset: 0x10200  Length: 256
  Type 0x70 (VPD)            Offset: 0x20000  Length: 512

# MBA(Multi Boot Agent) PXE 설정 확인
$ sudo bnxtnvm -dev=eth0 -cfg -mba
MBA Configuration:
  PXE Boot:     Enabled
  Protocol:     PXE (UNDI)
  Speed:        Auto-Negotiate
  VLAN:         None
  Boot Retry:   3

# PXE 부팅 비활성화
$ sudo bnxtnvm -dev=eth0 -cfg -mba -pxe=disable
MBA PXE Boot: Disabled (effective after reboot)

# Broadcom B57udiag 도구 (EFI Shell 또는 DOS 환경)
Shell> B57udiag -cmd SetMBAConfig -pxe disable -nic 1
# Realtek NIC: PG Tool (Programmer Tool)로 EEPROM 읽기/변경
# 주의: Realtek PG Tool은 DOS/UEFI Shell 전용이며, Linux에서는 ethtool 사용

# Linux에서 ethtool로 Realtek NVM 읽기
$ ethtool -i eth0
driver: r8169
bus-info: 0000:03:00.0

$ sudo ethtool -e eth0 offset 0x0E length 2
Offset          Values
------          ------
0x000e:         30 00
# Word 0x07 = 0x0030
# bit 4 (PXE Enable) = 1 → PXE 부팅 활성화
# bit 5 (WoL Enable) = 1

# Realtek PG Tool (DOS/UEFI Shell):
# PGE.exe: RTL8111용, PGEE.exe: RTL8125(2.5GbE)용
> PGE /r 7         # Word 7 읽기
> PGE /w 7 0010    # Word 7에 0x0010 쓰기 (WoL만 활성화, PXE 비활성화)
# Mellanox/NVIDIA ConnectX: mlxconfig 도구로 PXE 설정 관리
# ConnectX-5/6/7 NIC의 Option ROM 및 부팅 설정

# 현재 부팅 설정 조회
$ sudo mlxconfig -d /dev/mst/mt4125_pciconf0 query | grep -i boot
         BOOT_OPTION_ROM_EN_P1           True(1)
         BOOT_OPTION_ROM_EN_P2           True(1)
         LEGACY_BOOT_PROTOCOL_P1         PXE(1)
         LEGACY_BOOT_PROTOCOL_P2         PXE(1)
         UEFI_HII_EN                     True(1)
         BOOT_VLAN_EN_P1                 False(0)

# Port 1의 PXE Option ROM 비활성화
$ sudo mlxconfig -d /dev/mst/mt4125_pciconf0 set BOOT_OPTION_ROM_EN_P1=False

# 부팅 프로토콜 변경 (PXE → iSCSI)
$ sudo mlxconfig -d /dev/mst/mt4125_pciconf0 set LEGACY_BOOT_PROTOCOL_P1=2
# 0=None, 1=PXE, 2=iSCSI, 3=Both, 4=FCoE

# 설정 적용 확인 (재부팅 필요)
$ sudo mlxconfig -d /dev/mst/mt4125_pciconf0 query | grep BOOT_OPTION
         BOOT_OPTION_ROM_EN_P1           False(0)
         BOOT_OPTION_ROM_EN_P2           True(1)
벤더별 NVM 접근 아키텍처 비교 Intel (e1000e/igb) ethtool_ops.get_eeprom EERD 레지스터 또는 SPI Bit-Bang SPI Flash / EEPROM Broadcom (bnxt) ethtool_ops.get_eeprom HWRM 펌웨어 커맨드 ChiMP 프로세서 중재 NVRAM (디렉터리 기반) Realtek (r8169) ethtool_ops.get_eeprom 레지스터 I/O 비트뱅 93C46/93C56 프로토콜 EEPROM / eFuse Mellanox/NVIDIA (mlx5) mlxconfig (사용자 도구) 펌웨어 메일박스 커맨드 FW Access Register Flash (FW 관리 영역) 드라이버 직접 접근 하드웨어 레지스터로 NVM 직접 읽기/쓰기 펌웨어 간접 접근 NIC 내장 CPU가 NVM 접근 중재 레지스터 비트뱅 시리얼 EEPROM 직접 클럭/데이터 조작 펌웨어 메일박스 ConnectX FW가 모든 NVM 접근 관리 PXE 부팅 제어 필드 Word 0x0F bit 1 (Boot Config Word) MBA Config Type 0x09 (NVRAM 디렉터리) Word 0x07 bit 4 (Autoload Config) BOOT_OPTION_ROM_EN (FW Configuration) eeupdate / ethtool bnxtnvm / B57udiag PG Tool / ethtool mlxconfig / mstconfig
벤더대표 칩/컨트롤러NVM 타입커널 드라이버PXE 제어 필드관리 도구 (Linux)관리 도구 (EFI/DOS)
IntelI210, I219-LM, I350, X710SPI Flash / EEPROM / iNVMe1000e, igb, i40e, iceNVM Word 0x0F bit 1ethtool, eeupdateeeupdate (EFI)
BroadcomBCM5720, BCM57416NVRAM (디렉터리 기반)tg3, bnxt_enMBA Config (Type 0x09)bnxtnvm, ethtoolB57udiag, UEFI HII
RealtekRTL8111, RTL812593C46 EEPROM / eFuser8169Word 0x07 bit 4ethtoolPGE / PGEE (PG Tool)
Mellanox/NVIDIAConnectX-5/6/7Flash (FW 관리)mlx5_coreBOOT_OPTION_ROM_ENmlxconfig, mstconfigFlexBoot (UEFI)
Marvell/AquantiaAQC113, PresteraFlash (FW 관리)atlantic, presteraFW Boot Configatl_fwupdMarvell FW Tools
실무 팁 — eFuse NIC의 PXE 설정 변경 불가 문제:

Realtek RTL8125(2.5GbE) 등 eFuse 기반 NIC에서는 EEPROM과 달리 eFuse가 OTP(One-Time Programmable) 메모리이므로, 한 번 프로그래밍된 PXE 설정을 소프트웨어로 변경할 수 없습니다. 이 경우 BIOS 설정에서 "Network Boot" 옵션으로 PXE를 제어해야 합니다. BIOS는 NIC의 eFuse 값과 무관하게 Option ROM 로드 여부를 결정할 수 있으므로, eFuse에 PXE가 활성화되어 있더라도 BIOS에서 비활성화하면 PXE 부팅이 차단됩니다.

원격 관리(BMC/IPMI/Redfish)를 통한 LOM 제어

서버 환경에서는 물리적으로 BIOS Setup에 접근하지 않고도 BMC(Baseboard Management Controller)를 통해 원격으로 LOM의 PXE 부팅 설정을 변경할 수 있습니다. 대규모 데이터센터에서 수천 대의 서버를 관리할 때 IPMI, 벤더별 BMC 도구(racadm, ilorest), 또는 Redfish API를 사용하여 PXE 부팅을 자동으로 설정하는 것이 표준 운영 방식입니다.

# IPMI를 통한 PXE 부팅 제어
# ipmitool은 IPMI(Intelligent Platform Management Interface) 표준 도구입니다.

# 다음 1회 부팅만 PXE로 설정 (일시적 — 재부팅 후 원래 순서로 복귀)
$ ipmitool -I lanplus -H 192.168.1.100 -U admin -P password \
    chassis bootdev pxe
Set Boot Device to pxe

# 영구적으로 PXE 부팅 우선순위 설정 (옵션 persistent)
$ ipmitool -I lanplus -H 192.168.1.100 -U admin -P password \
    chassis bootdev pxe options=persistent
Set Boot Device to pxe

# 현재 부팅 장치 설정 확인
$ ipmitool -I lanplus -H 192.168.1.100 -U admin -P password \
    chassis bootparam get 5
Boot parameter version: 1
Boot parameter 5 is valid/unlocked
Boot parameter data: 8004000000
 Boot Flags :
   - Boot Flag Valid
   - Options apply to only next boot
   - BIOS PC Compatible (different from legacy) boot
   - Boot Device Selector : Force PXE
   - Console Redirection control : System Default

# IPMI raw 커맨드로 Boot Options Parameter 5 직접 설정
# 바이트 구조: [set selector] [block data...]
# 0x80 = Boot Flag Valid, 0x04 = Force PXE
$ ipmitool -I lanplus -H 192.168.1.100 -U admin -P password \
    raw 0x00 0x08 0x05 0x80 0x04 0x00 0x00 0x00

# 부팅 장치 초기화 (BIOS 기본 순서로 복귀)
$ ipmitool -I lanplus -H 192.168.1.100 -U admin -P password \
    chassis bootdev none
# Dell iDRAC: racadm을 통한 LOM PXE 설정
# Dell PowerEdge 서버에서 온보드 NIC의 PXE를 포트별로 제어

# 현재 NIC PXE 설정 조회
$ racadm get NIC.NICConfig.1.LegacyBootProto
LegacyBootProto=PXE

$ racadm get NIC.NICConfig.2.LegacyBootProto
LegacyBootProto=NONE

# NIC Port 1의 PXE 부팅 활성화
$ racadm set NIC.NICConfig.1.LegacyBootProto PXE

# NIC Port 2의 PXE 부팅 비활성화
$ racadm set NIC.NICConfig.2.LegacyBootProto NONE
# 옵션: PXE, iSCSI, FCoE, NONE

# BIOS 부팅 순서에 NIC 추가 (UEFI 모드)
$ racadm set BIOS.BiosBootSettings.UefiBootSeq \
    "NIC.PxeDevice.1-1, HardDisk.List.1-1"

# 설정 적용을 위한 재부팅 예약
$ racadm jobqueue create BIOS.Setup.1-1 -r pwrcycle -s TIME_NOW

# 원격으로 현재 활성 NIC 부팅 옵션 전체 조회
$ racadm get NIC.NICConfig -t | grep -E "Boot|PXE"
# HPE iLO: ilorest를 통한 LOM PXE 설정
# HPE ProLiant 서버에서 온보드 NIC PXE 제어

# iLO에 로그인
$ ilorest login 192.168.1.101 -u admin -p password

# 현재 네트워크 부팅 설정 조회
$ ilorest get --selector Bios. | grep -i pxe
PXEBootPolicy=Auto
NetworkBootRetry=Enabled
NetworkBootRetryCount=3

# 네트워크 부팅 활성화
$ ilorest set PXEBootPolicy=Auto --selector Bios. --commit

# 1회 PXE 부팅 설정
$ ilorest rawpatch /tmp/pxe_once.json
# pxe_once.json 내용:
# {"Boot": {"BootSourceOverrideTarget": "Pxe",
#           "BootSourceOverrideEnabled": "Once"}}

$ ilorest logout
# Redfish API를 통한 LOM PXE 제어 (벤더 무관 표준)
# DMTF Redfish는 IPMI를 대체하는 현대적 서버 관리 API입니다.

# 현재 부팅 설정 조회
$ curl -sk -u admin:password \
    https://192.168.1.100/redfish/v1/Systems/1 \
    | python3 -m json.tool | grep -A10 '"Boot"'
{
    "Boot": {
        "BootSourceOverrideEnabled": "Disabled",
        "BootSourceOverrideTarget": "None",
        "BootSourceOverrideTarget@Redfish.AllowableValues": [
            "None", "Pxe", "Cd", "Usb", "Hdd", "BiosSetup",
            "UefiTarget", "UefiHttp"
        ],
        "UefiTargetBootSourceOverride": ""
    }
}

# 다음 1회 부팅만 PXE로 설정
$ curl -sk -u admin:password -X PATCH \
    -H "Content-Type: application/json" \
    https://192.168.1.100/redfish/v1/Systems/1 \
    -d '{"Boot": {
           "BootSourceOverrideEnabled": "Once",
           "BootSourceOverrideTarget": "Pxe"
         }}'

# 영구적 PXE 부팅 설정
$ curl -sk -u admin:password -X PATCH \
    -H "Content-Type: application/json" \
    https://192.168.1.100/redfish/v1/Systems/1 \
    -d '{"Boot": {
           "BootSourceOverrideEnabled": "Continuous",
           "BootSourceOverrideTarget": "Pxe"
         }}'

# UEFI HTTP Boot 설정 (PXE 대신 HTTP 부팅)
$ curl -sk -u admin:password -X PATCH \
    -H "Content-Type: application/json" \
    https://192.168.1.100/redfish/v1/Systems/1 \
    -d '{"Boot": {
           "BootSourceOverrideEnabled": "Once",
           "BootSourceOverrideTarget": "UefiHttp",
           "HttpBootUri": "http://boot.example.com/efi/grubx64.efi"
         }}'

# NIC 어댑터 정보 및 부팅 설정 조회
$ curl -sk -u admin:password \
    https://192.168.1.100/redfish/v1/Systems/1/NetworkAdapters \
    | python3 -m json.tool
# Python Redfish 라이브러리를 사용한 대규모 PXE 자동 설정
# 데이터센터에서 수백 대 서버를 일괄 PXE 부팅 모드로 전환하는 스크립트

import redfish
import csv
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed

def set_pxe_boot(host, username, password, persistent=False):
    """단일 서버의 PXE 부팅 설정"""
    try:
        client = redfish.redfish_client(
            base_url=f"https://{host}",
            username=username,
            password=password,
            default_prefix="/redfish/v1"
        )
        client.login()

        # 부팅 설정 변경
        body = {
            "Boot": {
                "BootSourceOverrideEnabled": "Continuous" if persistent else "Once",
                "BootSourceOverrideTarget": "Pxe"
            }
        }

        response = client.patch("/redfish/v1/Systems/1", body=body)

        if response.status in [200, 204]:
            print(f"[OK] {host}: PXE boot {'persistent' if persistent else 'once'}")
            return True
        else:
            print(f"[FAIL] {host}: HTTP {response.status}")
            return False

    except Exception as e:
        print(f"[ERROR] {host}: {e}")
        return False
    finally:
        try:
            client.logout()
        except:
            pass

# CSV 파일에서 서버 목록 읽기
# 형식: hostname,bmc_ip,username,password
servers = []
with open("servers.csv") as f:
    for row in csv.DictReader(f):
        servers.append(row)

# 최대 20개 서버를 동시에 PXE 설정 (병렬 처리)
results = {"success": 0, "fail": 0}
with ThreadPoolExecutor(max_workers=20) as executor:
    futures = {
        executor.submit(
            set_pxe_boot,
            srv["bmc_ip"], srv["username"], srv["password"],
            persistent=True
        ): srv["hostname"]
        for srv in servers
    }

    for future in as_completed(futures):
        hostname = futures[future]
        if future.result():
            results["success"] += 1
        else:
            results["fail"] += 1

print(f"\nComplete: {results['success']} OK, {results['fail']} FAIL")
LOM 제어 3가지 경로 — In-Band / BIOS / BMC Out-of-Band 경로 1: In-Band (OS 환경) ethtool / eeupdate / mlxconfig NIC 드라이버 (커널) PCIe 버스 → NIC 레지스터 경로 2: BIOS/UEFI Setup 사용자 BIOS 메뉴 조작 BIOS 펌웨어 (AMI/Phoenix) SPI Flash 직접 접근 경로 3: BMC Out-of-Band IPMI / Redfish API / racadm BMC (iDRAC / iLO / IPMI) I2C/SPI → SPI Flash / NVRAM NVM LOM 비트 (Boot Config Word) Word 0x0F bit 1 = PXE Enable OS 실행 중 가능 재부팅 후 반영 POST 단계에서 설정 물리적 콘솔/KVM 필요 전원 꺼진 상태에서도 가능 원격 대규모 자동화 적합
제어 방식도구접근 조건영속성적합 환경
In-Band (OS)ethtool, eeupdate, mlxconfigOS 실행 중, root 권한영구 (NVM 직접 변경)단일 서버 유지보수
BIOS SetupBIOS 메뉴, UEFI ShellPOST 단계 (콘솔/KVM 필요)영구 (NVM 또는 BIOS NVRAM)초기 설치, 물리적 접근 가능 시
IPMIipmitoolBMC 네트워크 연결일시적(1회) 또는 영구소규모 서버 팜
벤더 BMCracadm, ilorestBMC 네트워크 연결영구 (BIOS/NVM 변경)벤더 동종 환경
Redfish APIcurl, Python redfishBMC 네트워크 연결일시적 또는 영구대규모 멀티벤더 데이터센터
실무 팁 — 대규모 환경에서의 PXE 부팅 제어 전략:
  • 10대 미만: BIOS Setup 또는 IPMI chassis bootdev pxe로 충분합니다.
  • 10~100대: 벤더 BMC 도구(racadm, ilorest) + 셸 스크립트로 일괄 설정합니다.
  • 100대 이상: Redfish API + Python 스크립트로 병렬 자동화합니다. IPMI보다 Redfish를 권장하는 이유는 JSON 기반 표준 API로 벤더 간 호환성이 우수하고, HTTPS를 통한 보안 통신을 지원하기 때문입니다.
  • 주의: BMC를 통한 부팅 설정 변경은 IPMI/Redfish의 "Boot Override" 기능으로, 이는 NVM의 LOM 비트를 직접 변경하지 않고 BIOS에 부팅 장치를 지시하는 방식입니다. 따라서 BMC 설정과 NVM 설정이 충돌할 경우 일반적으로 BMC의 Boot Override가 우선합니다.

UEFI HTTP Boot와 LOM 비트의 관계

전통적인 PXE 부팅은 BIOS Legacy 환경에서 Option ROM(UNDI 드라이버)을 통해 DHCP + TFTP 프로토콜로 부트 파일을 내려받습니다. 그러나 현대의 UEFI 환경에서는 PXE가 UEFI Network Stack을 통해 동작하거나, 아예 PXE를 대체하는 HTTP Boot가 도입되었습니다. 부팅 모드에 따라 LOM 비트의 역할과 영향이 크게 달라지므로, 각 모드별 차이를 이해하는 것이 중요합니다.

항목Legacy PXE (BIOS)UEFI PXEUEFI HTTP Boot
부팅 환경16-bit Real Mode64-bit UEFI64-bit UEFI
네트워크 드라이버Option ROM (UNDI)UEFI NIC 드라이버 (SNP)UEFI NIC 드라이버 (SNP)
프로토콜 스택PXE BC → UNDI → DHCP → TFTPSNP → MNP → IP4 → UDP → MTFTPSNP → MNP → IP4/IP6 → TCP → HTTP/HTTPS
서버 주소 취득DHCP Option 66 (next-server)DHCP Option 66 또는 DHCPv6DHCP Option 60 (HTTPClient) + URL
부트 파일 전송TFTP (UDP, 블록 단위)TFTP 또는 MTFTPHTTP/HTTPS (TCP, 스트리밍)
최대 파일 크기~32MB (TFTP 제약)~32MB (TFTP 제약)무제한 (HTTP)
보안없음Secure Boot 연동 가능HTTPS + Secure Boot
LOM 비트 영향직접 제어 (Option ROM 로드 결정)간접 영향 (UEFI 드라이버 별도)간접 영향 (Network Stack 설정)
Legacy PXE vs UEFI PXE vs UEFI HTTP Boot — 부팅 흐름 비교 Legacy PXE (BIOS) UEFI PXE UEFI HTTP Boot BIOS POST LOM 비트 확인 → Option ROM 로드 UNDI 드라이버 초기화 PXE Base Code → DHCP TFTP → pxelinux.0 다운로드 NBP 실행 → 커널 로드 UEFI DXE Phase Network Stack 드라이버 로드 SNP → MNP → IP4 프로토콜 스택 PXE BC Protocol → DHCP TFTP → grubx64.efi 다운로드 UEFI App 실행 → 커널 로드 UEFI DXE Phase Network Stack + HTTP 드라이버 로드 SNP → MNP → IP4 → TCP → HTTP DHCP Option 60 (HTTPClient) HTTP(S) → EFI 바이너리 다운로드 UEFI App 실행 → 커널 로드 LOM 비트(NVM Word 0x0F bit 1)의 영향 직접 제어 LOM=0 → Option ROM 미로드 → PXE 불가 간접 영향 UEFI 드라이버 별도, Network Stack 설정 우선 간접 영향 Option ROM 불필요, HTTP Stack 설정 우선 Legacy BIOS → LOM 비트가 PXE 부팅의 핵심 제어 | UEFI → Network Stack 활성화 여부가 더 중요 (LOM 비트는 Legacy 호환용) 순수 UEFI 환경에서 PXE가 안 될 때는 LOM 비트보다 BIOS의 "Network Stack" / "IPv4 PXE Support" 설정을 먼저 확인하십시오

부팅 모드에 따라 LOM 비트의 역할이 달라지므로, PXE 부팅 설정 시 현재 시스템의 부팅 모드를 먼저 확인해야 합니다.

부팅 모드LOM 비트(NVM) 영향PXE 제어 위치확인/설정 방법
Legacy BIOS 직접 제어 — LOM 비트=0이면 Option ROM이 로드되지 않아 PXE 부팅 불가 NVM Word 0x0F bit 1 + BIOS "Onboard LAN Boot ROM" ethtool -e, BIOS Setup, eeupdate
UEFI + CSM Legacy 호환 시 영향 — CSM이 Option ROM을 Legacy 방식으로 로드할 때 LOM 비트 확인 NVM LOM 비트 + BIOS CSM 설정 + Network Boot 옵션 BIOS "CSM Configuration" → "Network" → Legacy/UEFI 선택
순수 UEFI (CSM 없음) 간접 영향 — UEFI NIC 드라이버는 펌웨어에 내장되어 LOM 비트와 무관하게 로드 가능 BIOS "Network Stack" + "IPv4/IPv6 PXE Support" UEFI 변수 efibootmgr -v, BIOS "Network Boot" 메뉴
UEFI HTTP Boot 거의 무관 — HTTP Boot는 UEFI Network Stack 기반이며 Option ROM을 사용하지 않음 BIOS "HTTP Boot" 옵션 + DHCP 서버 설정 (Option 60) BIOS "HTTP Boot Configuration", Redfish API
# UEFI 부팅 항목에서 네트워크 부팅 확인
$ efibootmgr -v
BootCurrent: 0001
Timeout: 5 seconds
BootOrder: 0001,0002,0003,0004
Boot0001* ubuntu        HD(1,...)/File(\EFI\ubuntu\shimx64.efi)
Boot0002* UEFI PXE: IPv4 Intel(R) I219-LM
                        PciRoot(0x0)/Pci(0x1F,0x6)/MAC(001122334455,1)/
                        IPv4(0.0.0.0)
Boot0003* UEFI HTTP: IPv4 Intel(R) I219-LM
                        PciRoot(0x0)/Pci(0x1F,0x6)/MAC(001122334455,1)/
                        Uri(http://boot.example.com/efi/grubx64.efi)
Boot0004* UEFI PXE: IPv6 Intel(R) I219-LM
                        PciRoot(0x0)/Pci(0x1F,0x6)/MAC(001122334455,1)/
                        IPv6(...)

# UEFI PXE 부팅 항목이 없는 경우 (Network Stack 비활성화)
# → BIOS Setup에서 "Network Stack" 활성화 필요
# → LOM 비트와 무관한 UEFI 펌웨어 설정

# UEFI Shell에서 네트워크 인터페이스 확인
Shell> ifconfig -l
  iface  : eth0
  name   : Intel(R) I219-LM
  mac    : 00:11:22:33:44:55
  ipv4   : 0.0.0.0 (not configured)
  status : media present

# UEFI Shell에서 HTTP Boot URL 확인
Shell> httpboot
HTTP Boot URI: http://boot.example.com/efi/grubx64.efi
참고 — UEFI HTTP Boot의 장점:
  • 확장성: HTTP 서버(nginx, Apache)는 TFTP 서버보다 동시 연결 처리 능력이 훨씬 뛰어납니다. 수천 대 동시 부팅이 가능합니다.
  • 보안: HTTPS를 사용하면 부트 이미지 전송이 TLS로 암호화되어 중간자 공격(Man-in-the-Middle)을 방지합니다.
  • 파일 크기: TFTP의 블록 크기 제약(기본 512바이트, 최대 ~32MB)이 없어 대용량 이미지를 직접 전송할 수 있습니다.
  • 인프라: 기존 웹 서버와 CDN 인프라를 활용할 수 있어 별도의 TFTP 서버 구축이 불필요합니다.
  • 한계: UEFI 펌웨어가 HTTP Boot를 지원해야 하며, 2016년 이후 출시된 서버/워크스테이션 대부분이 지원합니다.

Secure Boot 환경에서의 LOM Option ROM 로드

UEFI Secure Boot가 활성화된 환경에서는 모든 실행 가능한 바이너리(UEFI 드라이버, Option ROM, 부트로더)가 펌웨어의 서명 데이터베이스(db)에 등록된 키로 서명되어 있어야 합니다. Secure Boot는 인증서와 해시(Hash)를 단계적으로 검증하는 신뢰 체인으로 동작하므로, 기존 Option ROM에서 취약점(Vulnerability)이 발견되면 dbx에 해당 서명이나 해시가 추가될 수 있습니다. 따라서 LOM 비트가 활성화되어 있고 BIOS 설정에서 PXE가 켜져 있더라도, NIC의 Option ROM이 유효한 서명을 포함하지 않으면 Secure Boot 펌웨어가 로드를 거부하여 PXE 부팅이 실패합니다.

서명 데이터베이스약어역할Option ROM과의 관계
Platform KeyPK서명 계층의 최상위 루트 키. OEM(메인보드 제조사)이 설정PK로 KEK를 서명, 간접적으로 Option ROM 신뢰 체인 구성
Key Exchange KeyKEKdb/dbx를 수정할 수 있는 키. OS 벤더(Microsoft 등)가 소유KEK 소유자가 db에 NIC 벤더 키를 추가/제거 가능
Signature Databasedb신뢰할 수 있는 서명 목록 (인증서, 해시)Option ROM의 서명이 db에 있으면 로드 허용
Forbidden Signaturesdbx차단할 서명/해시 목록 (블랙리스트)취약점이 발견된 Option ROM 해시를 등록하여 차단
/* drivers/firmware/efi/libstub/secureboot.c
 * 커널의 EFI Stub에서 Secure Boot 상태를 감지하는 코드
 *
 * 커널이 UEFI를 통해 부팅될 때, EFI Stub은 Secure Boot 상태를
 * 확인하여 커널의 보안 정책을 조정합니다.
 * 이 정보는 부팅 파라미터로 커널에 전달됩니다.
 */
enum efi_secureboot_mode efi_get_secureboot(void)
{
    efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
    u32 attr;
    unsigned long size;
    efi_status_t status;
    u8 secboot, setupmode, moksbstate;

    /* EFI 변수 "SecureBoot" 읽기
     * 0 = Secure Boot 비활성화
     * 1 = Secure Boot 활성화 */
    size = sizeof(secboot);
    status = get_efi_var(L"SecureBoot", &efi_variable_guid,
                         NULL, &size, &secboot);
    if (status == EFI_NOT_FOUND)
        return efi_secureboot_mode_disabled;
    if (status != EFI_SUCCESS)
        return efi_secureboot_mode_unknown;
    if (secboot == 0)
        return efi_secureboot_mode_disabled;

    /* "SetupMode" 확인
     * SetupMode=1이면 Secure Boot가 켜져 있어도
     * 서명 검증을 하지 않습니다 (초기 설정 모드). */
    size = sizeof(setupmode);
    status = get_efi_var(L"SetupMode", &efi_variable_guid,
                         NULL, &size, &setupmode);
    if (status != EFI_SUCCESS)
        return efi_secureboot_mode_unknown;
    if (setupmode == 1)
        return efi_secureboot_mode_disabled;

    /* MokSBStateRT 확인 (Shim 부트로더의 MOK 상태)
     * MOK(Machine Owner Key)로 Secure Boot 검증을 확장할 수 있습니다.
     * 커스텀 iPXE ROM을 사용할 때 MOK에 키를 등록하면
     * 표준 db에 없는 Option ROM도 로드 가능합니다. */
    size = sizeof(moksbstate);
    status = get_efi_var(L"MokSBStateRT", &efi_shim_guid,
                         NULL, &size, &moksbstate);
    /* moksbstate=1이면 MOK 기반 Secure Boot 비활성화 */

    return efi_secureboot_mode_enabled;
}

/* Secure Boot와 Option ROM의 관계:
 *
 * UEFI 펌웨어의 Option ROM 로드 순서:
 * 1. PCI 열거 → NIC 발견 → LOM 비트 확인
 * 2. LOM 비트=1 → Option ROM BAR 매핑
 * 3. Option ROM 헤더의 PE/COFF 서명 추출
 * 4. Secure Boot 활성화 시:
 *    ├─ 서명이 db에 존재 → ROM 로드 허용
 *    ├─ 서명이 dbx에 존재 → ROM 로드 거부 (블랙리스트)
 *    └─ 서명 미등록 → ROM 로드 거부
 * 5. ROM 실행 → UNDI/PXE 초기화
 *
 * 주요 NIC 벤더의 Option ROM 서명 상태:
 *   Intel: Microsoft UEFI CA로 서명 → 대부분의 시스템에서 동작
 *   Broadcom: Microsoft UEFI CA로 서명 → 대부분의 시스템에서 동작
 *   Mellanox: 자체 서명 + Microsoft CA → 대부분의 시스템에서 동작
 *   커스텀 iPXE: 미서명 → Secure Boot 환경에서 거부됨
 */
Secure Boot 환경에서 Option ROM 로드 결정 흐름 UEFI DXE: PCI 열거 → NIC 발견 LOM 비트 확인 0 Option ROM 스킵 (PXE 불가) 1 Option ROM BAR 매핑 → PE/COFF 추출 Secure Boot 활성화? 비활성화 ROM 즉시 로드 활성화 서명 검증 db에 존재 PXE 부팅 성공 미등록/dbx ROM 로드 거부 (PXE 실패)
# Secure Boot 환경에서 커스텀 iPXE ROM을 사용하기 위한 서명 절차

# 1. Secure Boot 상태 확인
$ mokutil --sb-state
SecureBoot enabled

# 2. 현재 등록된 서명 키 확인
$ mokutil --list-enrolled | head -20
[key 1]
SHA1 Fingerprint: ab:cd:ef:...
Issuer: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, ...
Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, ...
# → Microsoft UEFI CA가 기본 등록되어 있음

# 3. 자체 서명 키 생성 (MOK용)
$ openssl req -new -x509 -newkey rsa:2048 -keyout MOK.key \
    -out MOK.crt -nodes -days 3650 \
    -subj "/CN=Custom PXE ROM Key"

# 4. iPXE ROM에 서명
$ sbsign --key MOK.key --cert MOK.crt \
    --output ipxe-signed.efi ipxe.efi

# 5. 서명 검증
$ sbverify --cert MOK.crt ipxe-signed.efi
Signature verification OK

# 6. MOK에 키 등록 (다음 재부팅 시 확인 화면 표시)
$ sudo mokutil --import MOK.crt
# 비밀번호 설정 후 재부팅 → MOK Manager에서 키 등록 확인

# 7. 재부팅 후 MOK 등록 확인
$ mokutil --list-enrolled | grep "Custom PXE ROM Key"
Subject: CN=Custom PXE ROM Key
증상Secure BootLOM 비트가능한 원인해결 방법
PXE 부팅 메뉴 표시됨, 정상 동작ON1(정상) 벤더 ROM이 Microsoft CA로 서명됨조치 불필요
PXE 부팅 메뉴 자체가 나타나지 않음ON1Option ROM 서명이 db에 없거나 dbx에 등록됨Secure Boot 일시 비활성화 또는 ROM 키를 db/MOK에 등록
PXE 부팅 메뉴 나타나지 않음OFF0LOM 비트가 비활성화됨BIOS Setup에서 LOM/PXE 활성화 또는 NVM 수정
커스텀 iPXE ROM이 로드되지 않음ON1iPXE가 미서명 또는 자체 서명(db 미등록)sbsign으로 서명 후 MOK에 키 등록
펌웨어 업데이트 후 PXE 실패ON1dbx 업데이트로 기존 ROM 해시가 블랙리스트에 추가NIC 펌웨어 업데이트로 새 서명 적용
주의 — Secure Boot와 PXE 부팅 실패의 무증상 문제:

Secure Boot가 Option ROM 로드를 거부할 때 대부분의 UEFI 펌웨어는 에러 메시지 없이 조용히 다음 부팅 장치로 넘어갑니다. 따라서 "PXE가 갑자기 안 됨"이라는 증상이 나타나면 LOM 비트뿐만 아니라 Secure Boot 상태도 반드시 확인해야 합니다. 특히 dbx(Forbidden Signatures Database) 업데이트 후 기존에 동작하던 NIC의 Option ROM이 블랙리스트에 추가되어 PXE가 실패하는 사례가 있습니다. mokutil --sb-statemokutil --list-enrolled로 현재 상태를 확인하십시오.

LOM PXE 부팅 트러블슈팅 심화

PXE 부팅은 하드웨어(NIC, 케이블), 펌웨어(BIOS, Option ROM), 네트워크(DHCP, TFTP), 소프트웨어(NBP, 커널)의 4개 계층이 모두 정상 동작해야 성공합니다. 한 계층이라도 문제가 있으면 부팅이 실패하므로, 체계적인 단계별 진단이 필수입니다. 다음은 각 실패 단계별 상세 디버깅 절차입니다.

PXE 부팅 실패 — 단계별 진단 흐름도 PXE 부팅 시도 BIOS에 NIC 표시? 아니오 시나리오 1: NIC 미인식 LOM 비트, PCH, 하드웨어 점검 Link UP? 아니오 시나리오 2: Media Test Failure 케이블, SFP, 스위치 포트, speed 점검 DHCP 응답? 아니오 시나리오 3: DHCP 실패 DHCP 서버, relay, VLAN, 802.1X 점검 부트파일 전송? 아니오 시나리오 4/5: TFTP/NBP 실패 TFTP 서버, 파일 경로, 아키텍처 점검 커널 부팅? 아니오 시나리오 6: root 마운트 실패 ip= 파라미터, NFS/iSCSI 설정 점검 부팅 성공 1단계 2단계 3단계 4단계 5단계

시나리오 1: NIC가 BIOS 부팅 메뉴에 나타나지 않는 경우

BIOS 부팅 장치 목록에 네트워크 부팅 옵션이 표시되지 않는 것은 가장 근본적인 PXE 실패입니다. 원인은 LOM 비트 비활성화, BIOS 설정 오류, Secure Boot 거부, 또는 하드웨어 결함일 수 있습니다.

# OS에서 NIC 하드웨어 존재 여부 확인
$ lspci | grep -i ethernet
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (14) I219-LM
# → NIC가 PCI 버스에 보이면 하드웨어는 정상

# NIC가 lspci에 보이지 않는 경우:
# 1. BIOS에서 "Onboard LAN"이 Disabled → Enable로 변경
# 2. PCH의 LAN 컨트롤러가 BIOS에서 비활성화됨
# 3. 하드웨어 결함 (드물지만 가능)

# NVM의 LOM 비트 및 LAN Enable 확인
$ sudo ethtool -e eth0 offset 0x1E length 2
0x001e:         08 00
# 0x0008 = bit 3(LAN Enable)=1, bit 1(PXE Enable)=0
# → LAN은 활성화되어 있으나 PXE가 비활성화됨
# → NVM Word 0x0F의 bit 1을 1로 변경 필요

# Expansion ROM BAR 상태 확인
$ sudo lspci -vvv -s 00:1f.6 | grep -i "expansion\|rom"
    Expansion ROM at feb00000 [disabled] [size=512K]
# [disabled] → LOM 비트가 0이거나 BIOS가 ROM을 비활성화함

# Secure Boot 환경에서 ROM 로드 거부 확인
$ dmesg | grep -i "secure\|verified\|signature"
[    1.234] Secure boot enabled
# Secure Boot ON + 미서명 ROM → 무증상 거부 가능

시나리오 2: "Media test failure" 또는 "No link" 오류

PXE ROM은 로드되었으나 네트워크 링크가 확립되지 않아 DHCP 패킷을 전송할 수 없는 상태입니다.

# 물리 계층 링크 상태 확인
$ ethtool eth0 | grep -i "link\|speed\|duplex"
    Speed: 1000Mb/s
    Duplex: Full
    Link detected: yes
# Link detected: no → 케이블 또는 스위치 포트 확인

# NVM의 Force Speed 설정 확인 (자동 협상 실패 원인)
$ sudo ethtool -e eth0 offset 0x1E length 2
# Word 0x0F bit 7:6 → Force Speed
# 00 = Auto-negotiate (권장)
# 01/10/11 = 강제 속도 → 스위치 설정과 불일치 시 링크 실패

# 스위치 포트와의 속도/듀플렉스 불일치 확인
$ ethtool eth0
Settings for eth0:
    Supported link modes:   10baseT/Half 10baseT/Full
                            100baseT/Half 100baseT/Full
                            1000baseT/Full
    Advertised link modes:  1000baseT/Full  # ← 자동 협상이 아닌 강제 설정
# → 스위치가 100Mbps만 지원하면 링크 불가

# SFP/SFP+ 트랜시버 상태 (10GbE LOM)
$ ethtool -m eth0
    Identifier     : 0x03 (SFP)
    Vendor name    : Intel Corp
    Laser output power : 0.5 mW (-3.0 dBm)
    Module temperature : 35 C
# 온도 이상 또는 출력 0이면 SFP 교체 필요

시나리오 3: DHCP Discover 전송 실패 ("No offer received")

링크는 정상이나 DHCP 서버로부터 응답을 받지 못하는 경우입니다.

# DHCP 패킷 캡처 (다른 서버에서 모니터링)
$ sudo tcpdump -i eth0 -n port 67 or port 68 -vv
# DHCPDISCOVER가 보이지만 DHCPOFFER가 없음:
#   → DHCP 서버 미동작 또는 해당 서브넷에 대한 scope 미설정
#   → DHCP relay(ip helper-address) 미설정 (다른 VLAN인 경우)

# VLAN 태깅 문제 확인
# PXE 클라이언트는 기본적으로 VLAN 태그 없이 패킷을 전송합니다.
# 스위치 포트가 trunk/tagged 모드이면 DHCP 패킷이 드롭될 수 있습니다.
# → 스위치 포트를 access/untagged 모드로 설정하거나
#    NVM에서 VLAN ID를 설정합니다.

# 802.1X 인증 확인
# NAC(Network Access Control)이 활성화된 환경에서는
# 802.1X 인증 전까지 모든 트래픽이 차단됩니다.
# PXE 클라이언트는 802.1X를 지원하지 않으므로:
# → 스위치에서 해당 포트의 802.1X를 비활성화하거나
# → MAC 인증 바이패스(MAB) 설정

# DHCP 서버 로그 확인 (서버 측)
$ journalctl -u isc-dhcp-server -f
# 또는
$ tail -f /var/log/dhcpd.log

시나리오 4: TFTP 전송 도중 타임아웃

# TFTP 서버 접근 가능 여부 확인
$ tftp 192.168.1.10 -c get pxelinux.0
Received 42635 bytes in 0.1 seconds

# TFTP 포트(69/UDP) 방화벽 확인
$ sudo iptables -L -n | grep 69
# TFTP는 UDP 69번 포트 + 임의 고번호 데이터 포트를 사용합니다.
# 방화벽에서 UDP 69뿐만 아니라 관련 conntrack 모듈도 필요합니다:
$ sudo modprobe nf_conntrack_tftp

# MTU 문제 확인 (점보 프레임 환경)
# PXE 클라이언트는 일반적으로 MTU 1500을 사용합니다.
# 중간 네트워크 장비의 MTU가 더 작으면 TFTP 패킷이 드롭됩니다.
$ ping -s 1472 -M do 192.168.1.10
# 응답 없음 → MTU 문제 확인

# TFTP 서버의 대용량 파일 전송 문제
# tftpd-hpa의 블록 크기 기본값은 512바이트입니다.
# 대용량 커널 이미지 전송 시 --blocksize 1468 옵션 사용:
# /etc/default/tftpd-hpa:
# TFTP_OPTIONS="--secure --blocksize 1468"

시나리오 5: NBP(pxelinux.0/grubx64.efi) 로드 실패

# BIOS/UEFI 아키텍처 불일치 확인
# Legacy BIOS에는 pxelinux.0(16-bit), UEFI에는 grubx64.efi/shimx64.efi 필요
# DHCP 서버 설정 확인:
# dhcpd.conf 예시:
#   if option arch = 00:07 {   # UEFI x64
#       filename "grubx64.efi";
#   } elsif option arch = 00:00 {  # Legacy BIOS
#       filename "pxelinux.0";
#   }

# DHCP 응답의 filename 확인
$ sudo tcpdump -i eth0 -n port 67 -vv 2>&1 | grep "file"
#   file "grubx64.efi"

# TFTP 서버에 파일이 존재하는지 확인
$ ls -la /srv/tftp/grubx64.efi
-rw-r--r-- 1 root root 1048576 ... grubx64.efi

# Secure Boot 환경에서 shimx64.efi를 먼저 로드해야 하는 경우
# shim → grubx64.efi → 커널 체인으로 서명 검증
# filename을 "shimx64.efi"로 변경

시나리오 6: 커널 부팅 후 NFS/iSCSI root 마운트 실패

# 커널 ip= 파라미터 확인 (커널 명령줄)
# 올바른 형식:
#   ip=dhcp                    # DHCP 자동 설정
#   ip=10.0.0.2::10.0.0.1:255.255.255.0::eth0:off
#                               # client:server:gw:mask:hostname:device:autoconf

# 커널 dmesg에서 네트워크 부팅 관련 로그 확인
$ dmesg | grep -E "IP-Config|NFS|nfsroot|DHCP|eth0"
[    3.456] IP-Config: Guessing netmask 255.255.255.0
[    3.457] IP-Config: Complete:
[    3.457]   device=eth0, addr=10.0.0.2, mask=255.255.255.0
[    3.458]   gateway=10.0.0.1, server=10.0.0.1
[    3.459] IP-Config: eth0 complete (from 10.0.0.1)
[    4.123] VFS: Cannot open root device "nfs" or unknown-block(0,255)
# → NFS 서버 접근 불가 또는 export 설정 오류

# NFS 서버 설정 확인
$ showmount -e 10.0.0.1
Export list for 10.0.0.1:
/srv/nfsroot 10.0.0.0/24
# → 클라이언트 IP가 허용 범위에 포함되는지 확인

# 커널 빌드 옵션 확인 (NFS와 NIC 드라이버가 built-in이어야 함)
$ zcat /proc/config.gz | grep -E "CONFIG_NFS_FS|CONFIG_ROOT_NFS|CONFIG_E1000E"
CONFIG_NFS_FS=y          # NFS가 =m(모듈)이면 root 마운트 불가!
CONFIG_ROOT_NFS=y
CONFIG_E1000E=y          # NIC 드라이버도 =y(built-in) 필수!
진단 단계확인 명령어정상 출력실패 시 조치
NIC PCI 인식lspci | grep -i ethernetNIC 장치 표시BIOS "Onboard LAN" 활성화, 하드웨어 점검
LOM 비트 확인ethtool -e eth0 offset 0x1E length 2bit 1 = 1NVM 수정 또는 BIOS PXE 설정 활성화
Option ROM BARlspci -vvv -s BDF | grep ROM[enabled]LOM 비트 또는 Secure Boot 확인
링크 상태ethtool eth0 | grep LinkLink detected: yes케이블, SFP, 스위치 포트, speed 점검
DHCP 패킷tcpdump -i eth0 port 67 or 68DISCOVER → OFFERDHCP 서버, relay, VLAN, 802.1X 점검
TFTP 전송tftp server -c get filename파일 수신 성공방화벽, 파일 경로, MTU 점검
NBP 아키텍처file /srv/tftp/grubx64.efiPE32+ executable (EFI)BIOS/UEFI 모드에 맞는 NBP 사용
커널 NFS rootdmesg | grep IP-ConfigIP-Config: Completeip= 파라미터, NFS export, 드라이버 확인
데이터센터 PXE 트러블슈팅 — 5단계 빠른 분류법:
  1. LED 확인 — NIC 포트의 Link LED가 켜져 있는지 물리적으로 확인합니다. LED가 꺼져 있으면 물리 계층(케이블, SFP, 스위치 포트) 문제입니다.
  2. BIOS 부팅 메뉴 — 부팅 장치 목록에 "PXE" 또는 "Network Boot" 항목이 있는지 확인합니다. 없으면 LOM 비트 또는 Secure Boot 문제입니다.
  3. DHCP 스니핑 — 같은 VLAN의 다른 서버에서 tcpdump port 67으로 PXE 클라이언트의 DHCPDISCOVER가 보이는지 확인합니다. 보이지 않으면 네트워크 격리(Isolation) 문제입니다.
  4. TFTP 수동 테스트 — DHCP에서 지정된 TFTP 서버와 파일 경로로 수동 다운로드를 시도합니다.
  5. 시리얼 콘솔 — 커널 부팅 단계의 오류는 시리얼 콘솔(console=ttyS0,115200)로 로그를 캡처합니다.

칩셋별 대표 장애 패턴

같은 "PXE 실패"라도 칩셋마다 자주 반복되는 실패 양상은 다릅니다. 아래 표는 앞서 본 일반 절차를 실제 NIC 계열에 대응시키기 위한 빠른 참조표입니다.

칩셋/계열현장에서 자주 보는 증상먼저 의심할 원인우선 확인할 지점
Intel I210 / I211OS에서는 NIC가 정상인데 PXE 메뉴가 없거나 DHCP가 전혀 나가지 않습니다.잘못된 NVM image, 로컬 flash 미탑재, system BIOS flash 경로 누락, power cycle 미실시lspci -vv | grep -i rom, ethtool -e, 보드 설계 문서, 완전 전원 재인가 여부
Intel I219 / I225 / I226UEFI 부팅 메뉴에는 NIC가 보이는데 PXE 항목이 사라지거나 HTTP Boot만 보입니다.BIOS의 Network Stack 또는 IPv4 PXE Support 비활성화, CSM/UEFI 모드 조합 문제BIOS Setup의 Network Boot 메뉴, efibootmgr -v, BIOS 업데이트 이력
Broadcom BCM57xx / BCM574xx포트별로 어떤 포트는 PXE가 되고 어떤 포트는 안 됩니다. BMC에서 PXE를 걸었는데 기대한 포트가 아닙니다.MBA/FlexBoot가 포트별로 다르게 설정됨, BMC Boot Override와 NIC NVRAM 정책 충돌bnxtnvm -cfg -mba, iDRAC/iLO 설정, 포트 번호와 실제 케이블 연결 대응표
Realtek RTL8111 / RTL8125Windows나 Linux에서는 링크가 잘 잡히는데 BIOS PXE는 보드마다 동작 편차가 큽니다.eFuse 기반 설계, OEM BIOS가 Option ROM/UEFI driver를 번들하지 않음, 보드 정책상 Network Boot 비활성BIOS의 Network Boot, OEM BIOS 릴리스 노트, ethtool -i, 필요 시 PG Tool
Mellanox/NVIDIA ConnectXFlexBoot 메뉴는 뜨지만 PXE 대신 iSCSI/FCoE로 가거나, 원하는 포트가 아닌 다른 포트가 부팅을 시도합니다.BOOT_OPTION_ROM_EN_Px, LEGACY_BOOT_PROTOCOL_Px, VLAN/port policy 불일치mlxconfig query, 포트별 부트 프로토콜, 스위치 access/trunk 설정
Marvell/Aquantia고속 NIC는 OS에서 문제없는데 pre-boot는 전혀 보이지 않습니다.OEM이 PXE용 Option ROM을 번들하지 않았거나 BIOS 패키지에 통합하지 않음OEM 펌웨어 패키지, UEFI 드라이버 탑재 여부, BIOS 업데이트 후 변화
운영 팁: Intel과 Broadcom은 NVM 또는 NVRAM 자체 설정이 문제인 경우가 많고, Intel PCH LOM과 Realtek은 플랫폼 BIOS 정책이 더 자주 원인입니다. Mellanox/NVIDIA는 NIC 펌웨어 설정이 우선입니다. 즉, "어디에 설정이 저장되는 칩셋인가"를 먼저 구분하면 진단 속도가 크게 빨라집니다.

PXE API 호출 메커니즘

; NBP에서 PXE API 호출 예제 (16-bit Real Mode Assembly)
; PXE API는 !PXE 또는 PXENV+ 구조체를 통해 호출

; !PXE 구조체 찾기 (ES:BX에 전달됨)
; BX = !PXE 구조체의 오프셋

; PXENV_TFTP_READ_FILE 호출 예제
mov bx, PXENV_TFTP_READ_FILE   ; 함수 번호: 0x0023
mov di, tftp_read_params        ; 파라미터 구조체 오프셋
push ds
push di
push bx
call far [pxe_entry_point]      ; !PXE EntryPointSP
add sp, 6
; AX = 반환값 (0 = 성공)

; 파라미터 구조체
tftp_read_params:
    .status:     dw 0            ; 반환 상태
    .filename:   times 128 db 0  ; 파일명
    .buffersize: dd 0            ; 버퍼 크기
    .bufferaddr: dd 0            ; 버퍼 세그먼트:오프셋
    .serverip:   dd 0            ; 서버 IP
    .gatewayip:  dd 0            ; 게이트웨이 IP
    .mcastip:    dd 0            ; 멀티캐스트 IP
    .tftpport:   dw 69           ; TFTP 포트
    .tftpblksize: dw 512         ; 블록 크기
UNDI 드라이버 스택과 NBP 상호작용 NBP (PXELINUX / iPXE) PXE Base Code API TFTP_READ / UDP_WRITE / ... PXE 부가 API GET_CACHED_INFO / DISCOVERY UNDI Driver (Universal Network Driver Interface) OPEN / CLOSE / TRANSMIT / ISR / GET_NIC_TYPE H/W UNDI (Option ROM) NIC 벤더 제공 펌웨어 드라이버 S/W UNDI 소프트웨어 전용 드라이버 (iPXE) NIC Hardware (Intel I210, Realtek RTL8169, Broadcom BCM57xx, ...)

PXE 리얼 모드 메모리 맵(Memory Map)

BIOS PXE 부팅 시 16-bit 리얼 모드의 메모리 레이아웃은 NBP 개발과 트러블슈팅에 핵심적인 정보입니다. PXE ROM, UNDI 드라이버, NBP 각각이 특정 메모리 영역을 사용합니다.

PXE 리얼 모드 (16-bit) 메모리 맵 ... 확장 메모리 (1MB 이상) ... 0x100000 BIOS ROM (F0000-FFFFF) 0xF0000 PXE Option ROM 영역 (C0000-DFFFF) PXE ROM + UNDI 드라이버 시그니처: 0x55AA, PCI Data Structure 0xC0000 VGA 프레임 버퍼 (A0000-BFFFF) 0xA0000 UNDI 코드/데이터 세그먼트 PXE Base Code 런타임 (가변 위치) ~0x90000 !PXE / PXENV+ 구조체 PXE API 진입점, UNDI 데이터 세그먼트 포인터 NBP 로드 영역 pxelinux.0 / undionly.kpxe 등 최대 ~512KB (리얼 모드 제한) ~0x7C00 스택, 부트 섹터 영역 0x0600 IVT + BDA (0000-04FF) 0x0000 NBP 엔트리 포인트: ES:BX → !PXE 구조체 포인터 SS:SP → 유효한 스택 설정 CS:IP → 0000:7C00 (실행 시작) !PXE 구조체 (40 bytes): Signature: "!PXE" EntryPointSP: far call 진입점 UNDIDataSeg/UNDICodeSeg UEFI PXE의 차이점: UEFI 모드에서는 리얼 모드 메모리 맵이 적용되지 않습니다. EFI 부트 서비스가 메모리를 관리하고, NBP는 PE32+ EFI Application으로 로드됩니다. EFI_PXE_BASE_CODE_PROTOCOL과 EFI_SIMPLE_NETWORK_PROTOCOL을 통해 네트워크 접근합니다.
/* !PXE 구조체 (PXE 2.1 Spec) - NBP가 PXE API에 접근하는 핵심 */
struct pxe_t {
    uint8_t  Signature[4];       /* "!PXE" */
    uint8_t  StructLength;       /* 구조체 크기 */
    uint8_t  StructCksum;        /* 체크섬 (전체 합=0) */
    uint8_t  StructRev;          /* 구조체 리비전 */
    uint8_t  reserved1;
    SEGOFF16 UNDIROMID;          /* UNDI ROM ID 구조체 포인터 */
    SEGOFF16 BaseROMID;          /* BC ROM ID 구조체 포인터 */
    SEGOFF16 EntryPointSP;       /* API 진입점 (스택 파라미터) */
    SEGOFF16 EntryPointESP;      /* API 진입점 (확장 스택) */
    SEGOFF16 StatusCallout;      /* 콜아웃 함수 포인터 */
    uint8_t  reserved2;
    uint8_t  SegDescCnt;         /* 세그먼트 디스크립터 수 */
    SEGSEL   FirstSelector;      /* UNDI 코드/데이터 세그먼트 */
    /* ... 세그먼트 디스크립터들 ... */
};

/* PXENV+ 구조체 (하위 호환용, PXE 2.0 이전) */
struct pxenv_plus {
    uint8_t  Signature[6];       /* "PXENV+" */
    uint16_t Version;            /* PXE 버전 (예: 0x0201) */
    uint8_t  Length;
    uint8_t  Checksum;
    SEGOFF16 RMEntry;            /* 리얼 모드 API 진입점 */
    uint32_t PMOffset;           /* 프로텍티드 모드 오프셋 */
    uint16_t PMSelector;         /* 프로텍티드 모드 셀렉터 */
    uint16_t StackSeg;           /* 스택 세그먼트 */
    uint16_t StackSize;          /* 스택 크기 */
    /* ... */
};

/* NBP에서 !PXE 구조체 탐색 순서:
 * 1. ES:BX 레지스터 확인 (BIOS가 전달)
 * 2. "!PXE" 시그니처 검증
 * 3. 실패 시 "PXENV+" 탐색 (0x9FC00~0xA0000)
 * 4. 체크섬 검증 (전체 바이트 합 = 0)
 */

PXELINUX 부트로더

PXELINUX 개요

PXELINUX는 SYSLINUX 프로젝트의 일부로, PXE 환경에서 가장 널리 사용되는 NBP입니다.

변종파일특징
pxelinux.0~40KB기본 BIOS PXE 부트로더. TFTP만 지원
lpxelinux.0~90KBHTTP, FTP, TFTP 지원. DNS 해석 가능
syslinux.efi~200KBUEFI 모드 PXE 부트로더

설정 파일 탐색 순서

PXELINUX는 다음 순서로 설정 파일을 탐색합니다 (첫 번째 발견 파일 사용):

클라이언트 정보:
  UUID:  b8945908-d6a6-41a9-611d-74a6ab80b83d
  MAC:   01-88-99-aa-bb-cc-dd
  IP:    192.168.1.100 (hex: C0A80164)

PXELINUX 설정 파일 탐색 순서:
  1. pxelinux.cfg/b8945908-d6a6-41a9-611d-74a6ab80b83d   ← UUID
  2. pxelinux.cfg/01-88-99-aa-bb-cc-dd                    ← MAC (01- 접두사)
  3. pxelinux.cfg/C0A80164                                 ← 전체 IP (hex)
  4. pxelinux.cfg/C0A8016                                  ← IP 앞 7자리
  5. pxelinux.cfg/C0A801                                   ← IP 앞 6자리
  6. pxelinux.cfg/C0A80                                    ← IP 앞 5자리
  7. pxelinux.cfg/C0A8                                     ← 서브넷 (Class B)
  8. pxelinux.cfg/C0A                                      ← 앞 3자리
  9. pxelinux.cfg/C0                                       ← 앞 2자리
 10. pxelinux.cfg/C                                        ← 앞 1자리
 11. pxelinux.cfg/default                                  ← 기본 설정

→ 이 메커니즘으로 호스트별/서브넷별/전체 기본 설정을 계층적으로 관리할 수 있습니다.

PXELINUX 설정 파일 예제

# /var/lib/tftpboot/pxelinux.cfg/default

# 전역 설정
DEFAULT vesamenu.c32        # 그래픽 메뉴 사용
PROMPT 0                    # 자동 메뉴 진입
TIMEOUT 300                 # 30초 타임아웃 (1/10초 단위)
ONTIMEOUT local             # 타임아웃 시 로컬 부팅

# 메뉴 테마
MENU TITLE PXE Boot Menu
MENU BACKGROUND pxelinux.cfg/splash.png
MENU COLOR border  30;44  #40ffffff #a0000000 std
MENU COLOR title   1;36;44 #9033ccff #a0000000 std
MENU COLOR sel     7;37;40 #e0ffffff #20ffffff all

# 로컬 디스크 부팅
LABEL local
    MENU LABEL Boot from ^Local Disk
    MENU DEFAULT
    LOCALBOOT 0

# Ubuntu 24.04 LTS 설치
LABEL ubuntu2404
    MENU LABEL ^Ubuntu 24.04 LTS Install
    KERNEL images/ubuntu/vmlinuz
    INITRD images/ubuntu/initrd
    APPEND root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://192.168.1.10/ubuntu-24.04-live-server-amd64.iso autoinstall ds=nocloud-net;s=http://192.168.1.10/autoinstall/

# RHEL 10 계열 설치 (Kickstart)
LABEL rhel10
    MENU LABEL ^RHEL 10 Kickstart Install
    KERNEL images/rhel/vmlinuz
    INITRD images/rhel/initrd.img
    APPEND inst.repo=http://192.168.1.10/rhel10/ inst.ks=http://192.168.1.10/ks/rhel10.cfg ip=dhcp

# Debian 13 설치 (Preseed)
LABEL debian13
    MENU LABEL ^Debian 13 Trixie Netinstall
    KERNEL images/debian/linux
    INITRD images/debian/initrd.gz
    APPEND auto=true priority=critical url=http://192.168.1.10/preseed/debian13.cfg interface=auto vga=788

# 디스크리스 리눅스 (NFS Root)
LABEL diskless
    MENU LABEL ^Diskless Linux (NFS Root)
    KERNEL vmlinuz-diskless
    INITRD initrd-diskless.img
    APPEND root=/dev/nfs nfsroot=192.168.1.10:/exports/diskless,vers=4,tcp ip=dhcp rw

# Memtest86+
LABEL memtest
    MENU LABEL ^Memtest86+
    KERNEL memtest86+

# iPXE 체인로딩
LABEL ipxe
    MENU LABEL ^iPXE Advanced Boot
    KERNEL ipxe.lkrn
    APPEND dhcp && chain http://192.168.1.10/boot.ipxe

PXELINUX COM32 모듈

모듈용도
menu.c32텍스트 기반 부팅 메뉴
vesamenu.c32VESA 그래픽 부팅 메뉴 (배경 이미지 지원)
chain.c32디스크/파티션 체인로딩
memdiskISO/플로피/하드 디스크 이미지를 RAM에서 부팅
pxechn.c32다른 PXE 서버로 체인로딩
reboot.c32시스템 재부팅
poweroff.c32시스템 종료 (APM/ACPI)
hdt.c32하드웨어 정보 표시
ifcpu64.c32CPU 아키텍처(32/64비트) 감지 분기
linux.c32Linux 커널 직접 로드

GRUB 네트워크 부팅

GRUB PXE 개요

GRUB2는 UEFI 환경에서 가장 널리 사용되는 네트워크 부트로더입니다. PXELINUX보다 기능이 풍부하고 UEFI Secure Boot를 지원합니다.

GRUB 네트워크 설정

# /var/lib/tftpboot/grub/grub.cfg - GRUB 네트워크 부팅 설정

set default=0
set timeout=30

# 변수 설정
set pxe_server="192.168.1.10"
set nfs_server="192.168.1.10"

# 메뉴 테마
loadfont unicode
set gfxmode=auto
insmod all_video
insmod gfxterm
terminal_output gfxterm

menuentry "Boot from Local Disk" {
    exit
}

menuentry "Ubuntu 24.04 LTS Install" {
    linux (tftp)/$pxe_server/images/ubuntu/vmlinuz \
        ip=dhcp \
        url=http://$pxe_server/ubuntu-24.04-live-server-amd64.iso \
        autoinstall ds=nocloud-net\;s=http://$pxe_server/autoinstall/
    initrd (tftp)/$pxe_server/images/ubuntu/initrd
}

menuentry "RHEL 10 Kickstart Install" {
    linuxefi (tftp)/images/rhel/vmlinuz \
        inst.repo=http://$pxe_server/rhel10/ \
        inst.ks=http://$pxe_server/ks/rhel10.cfg \
        ip=dhcp
    initrdefi (tftp)/images/rhel/initrd.img
}

menuentry "Diskless Linux (NFS Root)" {
    linux (tftp)/vmlinuz-diskless \
        root=/dev/nfs \
        nfsroot=$nfs_server:/exports/diskless,vers=4,tcp \
        ip=dhcp rw
    initrd (tftp)/initrd-diskless.img
}

menuentry "iPXE Chainload" {
    chainloader (tftp)/ipxe.efi
}

GRUB EFI 네트워크 이미지 빌드

# GRUB UEFI 네트워크 부트 이미지 생성
# 필요한 모듈을 포함한 standalone EFI 이미지

# x86_64 UEFI
grub-mknetdir --net-directory=/var/lib/tftpboot --subdir=/grub

# 또는 수동으로 standalone 이미지 생성
grub-mkimage -O x86_64-efi \
    -o grubx64.efi \
    -p '(tftp)/grub' \
    -d /usr/lib/grub/x86_64-efi \
    efinet tftp http net normal linux linuxefi \
    all_video boot configfile echo part_gpt part_msdos \
    search search_fs_uuid search_label reboot halt

# ARM64 UEFI
grub-mkimage -O arm64-efi \
    -o grubaa64.efi \
    -p '(tftp)/grub' \
    -d /usr/lib/grub/arm64-efi \
    efinet tftp http net normal linux \
    all_video boot configfile echo

# Secure Boot을 사용할 경우 shim 체인로딩
# shimx64.efi → grubx64.efi 순서로 로드
cp /usr/lib/shim/shimx64.efi.signed /var/lib/tftpboot/shimx64.efi
cp /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed /var/lib/tftpboot/grubx64.efi

iPXE 고급 부팅

iPXE 개요

iPXE는 gPXE의 후속 프로젝트로, 레거시 PXE의 한계를 극복한 오픈소스 네트워크 부트 펌웨어입니다.

기능레거시 PXEiPXE
프로토콜TFTP만HTTP, HTTPS, iSCSI, AoE, FCoE, TFTP, FTP, NFS
DNS미지원지원
스크립팅없음내장 스크립트 언어
Wi-Fi미지원WPA2 지원 (실험적)
VLAN일부 NIC만완전 지원
SAN Boot미지원iSCSI, AoE, FCoE, Infiniband SRP
암호화(Encryption)없음HTTPS/TLS 지원 (공식 문서 기준 TLSv1.0-1.2)
인증없음HTTP Basic/Digest, 클라이언트 인증서
Embedded Script없음ROM에 스크립트 내장 가능
무선 네트워크미지원802.11 지원 (일부 칩셋)

iPXE 체인로딩 전략

iPXE 체인로딩: PXE ROM → iPXE → HTTP Boot NIC PXE ROM DHCP → TFTP (기본 PXE 기능만) undionly.kpxe iPXE 코어 로드 UNDI 드라이버 재사용 iPXE Script HTTP/HTTPS로 다운로드 메뉴, 조건 분기, 루프 Linux Kernel vmlinuz + initrd HTTP로 고속 전송 무한 루프 방지 (핵심!): 1. PXE ROM이 DHCP하면 → 서버가 undionly.kpxe 전달 (iPXE가 아닌 클라이언트) 2. iPXE가 DHCP하면 → user-class="iPXE" 또는 Option 175로 식별 → boot.ipxe 전달 dnsmasq: dhcp-match=set:ipxe,175 / dhcp-boot=tag:!ipxe,undionly.kpxe / dhcp-boot=tag:ipxe,http://server/boot.ipxe

체인로딩 바이너리 선택

펌웨어권장 이미지의미언제 바꾸는가
Legacy BIOSundionly.kpxe펌웨어가 제공한 UNDI 드라이버를 재사용기본 PXE ROM 호환성을 최대화하고 싶을 때
Legacy BIOSipxe.pxeiPXE가 자체 NIC 드라이버 사용펌웨어 UNDI 구현이 불안정하거나 기능이 부족할 때
UEFIipxe.efi일반적인 UEFI 체인로딩용 EFI 애플리케이션가장 보편적인 UEFI iPXE 진입점(Entry Point)
UEFIsnponly.efiUEFI SNP 드라이버를 재사용펌웨어 NIC 드라이버는 안정적이지만 iPXE 내장 드라이버 충돌을 피하고 싶을 때
실무 팁: iPXE 공식 DHCP 예제는 BIOS 환경에서 undionly.kpxe, UEFI 환경에서 ipxe.efi를 기본 체인로딩 대상으로 제시합니다. UEFI에서 특정 NIC 드라이버 문제가 있으면 snponly.efi가 더 잘 맞는 경우가 있습니다.

iPXE 스크립트 예제

#!ipxe
# boot.ipxe - iPXE 메인 부팅 스크립트

# 변수 설정
set boot-server http://192.168.1.10
set menu-timeout 30000
set submenu-timeout ${menu-timeout}

# 네트워크 초기화
dhcp || goto netboot_failed
echo IP: ${ip} / Gateway: ${gateway} / DNS: ${dns}

# 아키텍처 감지
iseq ${platform} efi && goto efi_boot ||
goto bios_boot

:efi_boot
set boot-prefix ${boot-server}/efi
goto start

:bios_boot
set boot-prefix ${boot-server}/bios
goto start

:start
menu iPXE Boot Menu - ${ip}
item --gap --             -- OS Installation --
item ubuntu               Ubuntu 24.04 LTS
item rhel                 RHEL 10 계열
item debian               Debian 13 Trixie
item --gap --             -- Utilities --
item diskless             Diskless Linux (NFS)
item iscsi                iSCSI SAN Boot
item memtest              Memtest86+
item --gap --             -- Other --
item shell                iPXE Shell
item reboot               Reboot
item exit                 Exit to BIOS/UEFI
choose --default ubuntu --timeout ${menu-timeout} selected
goto ${selected}

:ubuntu
kernel ${boot-server}/images/ubuntu/vmlinuz
initrd ${boot-server}/images/ubuntu/initrd
imgargs vmlinuz ip=dhcp url=${boot-server}/iso/ubuntu-24.04.iso autoinstall ds=nocloud-net;s=${boot-server}/autoinstall/
boot || goto failed

:rhel
kernel ${boot-server}/images/rhel/vmlinuz
initrd ${boot-server}/images/rhel/initrd.img
imgargs vmlinuz inst.repo=${boot-server}/rhel10/ inst.ks=${boot-server}/ks/rhel10.cfg ip=dhcp
boot || goto failed

:debian
kernel ${boot-server}/images/debian/linux
initrd ${boot-server}/images/debian/initrd.gz
imgargs linux auto=true priority=critical url=${boot-server}/preseed/debian13.cfg interface=auto
boot || goto failed

:diskless
kernel ${boot-server}/vmlinuz-diskless
initrd ${boot-server}/initrd-diskless.img
imgargs vmlinuz-diskless root=/dev/nfs nfsroot=192.168.1.10:/exports/diskless,vers=4 ip=dhcp rw
boot || goto failed

:iscsi
# iSCSI SAN 부팅
set iscsi-server 192.168.1.20
set iscsi-target iqn.2024-01.com.example:storage.lun0
sanhook iscsi:${iscsi-server}::::${iscsi-target} || goto failed
# iSCSI LUN에서 부팅
sanboot iscsi:${iscsi-server}::::${iscsi-target} || goto failed

:memtest
chain ${boot-server}/memtest86+.bin || goto failed

:shell
echo iPXE Shell. Type 'exit' to return to menu.
shell
goto start

:reboot
reboot

:exit
exit

:failed
echo Boot failed. Retrying in 5 seconds...
sleep 5
goto start

:netboot_failed
echo Network initialization failed!
echo Falling back to local boot...
sleep 3
exit 1

iPXE 고급 스크립팅

#!ipxe
# advanced-boot.ipxe - 고급 기능 데모

# === 환경 변수 활용 ===
echo Platform: ${platform}      # pcbios 또는 efi
echo BuildArch: ${buildarch}    # i386 또는 x86_64 또는 arm64
echo MAC: ${mac}                # xx:xx:xx:xx:xx:xx
echo Serial: ${serial}          # 시리얼 번호
echo UUID: ${uuid}              # SMBIOS UUID
echo Manufacturer: ${manufacturer}
echo Product: ${product}
echo Chip: ${chip}              # NIC 칩셋

# === 조건 분기 ===
# MAC 주소 기반 분기
iseq ${mac} 00:11:22:33:44:55 && goto special_host ||

# 서브넷 기반 분기
isset ${netX/gateway} && goto has_gateway || goto no_gateway

# 제조사 기반 분기
iseq ${manufacturer} "Dell Inc." && set vendor dell ||
iseq ${manufacturer} "HP" && set vendor hp ||
iseq ${manufacturer} "Lenovo" && set vendor lenovo ||
set vendor generic

# === HTTPS + 인증 ===
set cert-server https://pxe.example.com
# 클라이언트 인증서 (빌드 시 내장)
# imgfetch ${cert-server}/cert/${uuid}.pem

# === 동적 메뉴 (서버에서 생성) ===
# 서버가 MAC/UUID/arch에 따라 동적 스크립트 생성
chain ${cert-server}/api/boot?mac=${mac:hexhyp}&arch=${buildarch}&uuid=${uuid}&platform=${platform} ||
echo Dynamic menu failed, falling back...

# === VLAN 태깅 ===
# vcreate --tag 100 net0
# dhcp net0-100

# === 루프와 재시도 ===
set retry-count 0
:retry_loop
inc retry-count
iseq ${retry-count} 5 && goto give_up ||
echo Attempt ${retry-count} of 5...
dhcp && goto dhcp_ok ||
sleep 2
goto retry_loop

:dhcp_ok
echo DHCP succeeded on attempt ${retry-count}

:give_up
echo All retry attempts exhausted.

iPXE 빌드와 커스터마이징

# iPXE 소스 빌드

# 의존성 설치
sudo apt install build-essential liblzma-dev isolinux \
     mtools mkisofs syslinux perl

# 소스 클론
git clone https://github.com/ipxe/ipxe.git
cd ipxe/src

# === 기본 빌드 ===
# BIOS용 PXE chainload 이미지 (UNDI 재사용)
make bin/undionly.kpxe

# BIOS용 standalone 이미지 (내장 드라이버 사용)
make bin/ipxe.pxe

# UEFI용 이미지
make bin-x86_64-efi/ipxe.efi

# ARM64 UEFI용
make bin-arm64-efi/ipxe.efi

# === 내장 스크립트와 함께 빌드 ===
cat > embed.ipxe << 'SCRIPT'
#!ipxe
dhcp
chain http://192.168.1.10/boot.ipxe
SCRIPT
make bin/undionly.kpxe EMBED=embed.ipxe

# === 빌드 옵션 커스터마이징 ===
# config/general.h 에서 기능 활성화/비활성화
# DOWNLOAD_PROTO_HTTPS  - HTTPS 지원
# DOWNLOAD_PROTO_NFS    - NFS 지원
# IMAGE_SCRIPT          - 스크립트 지원
# NET_PROTO_IPV6        - IPv6 지원
# CONSOLE_CMD           - 콘솔 명령
# VLAN_CMD              - VLAN 명령
# NSLOOKUP_CMD          - DNS 조회 명령
# PING_CMD              - 핑 명령

# HTTPS 활성화 빌드
cat > custom_config.h << 'EOF'
#define DOWNLOAD_PROTO_HTTPS
#define IMAGE_TRUST_CMD
#define CERT_CMD
#define CONSOLE_CMD
#define NSLOOKUP_CMD
#define PING_CMD
#define VLAN_CMD
EOF
make bin/undionly.kpxe EMBED=embed.ipxe CONFIG=custom_config

# === USB/ISO 이미지 생성 ===
make bin/ipxe.usb      # USB 부팅 이미지
make bin/ipxe.iso      # CD/DVD ISO 이미지
make bin/ipxe.lkrn     # Linux 커널 형식 (GRUB/PXELINUX에서 로드 가능)

Etherboot → gPXE → iPXE 계보

iPXE의 역사를 이해하면 다양한 레거시 환경에서 만나는 네트워크 부팅 소프트웨어의 관계를 파악할 수 있습니다.

프로젝트시기주요 특징상태
Etherboot1995-2006최초의 오픈소스 네트워크 부팅. NIC별 드라이버 바이너리. Tagged image format종료 (gPXE로 전환)
gPXE2006-2010Etherboot 후속. HTTP, iSCSI, DNS 지원 추가. UNDI 호환 모드. GPL v2종료 (iPXE로 포크)
iPXE2010-현재gPXE 포크. HTTPS, Wi-Fi, 스크립팅, 확장된 SAN boot, UEFI 지원. 공식 암호화 문서 기준 TLSv1.0-1.2 지원. GPL v2활발한 개발 중
왜 gPXE에서 iPXE로 포크되었나? gPXE 프로젝트의 핵심 개발자 Michael Brown이 상표권 분쟁과 프로젝트 거버넌스 문제로 2010년에 iPXE를 포크했습니다. iPXE는 gPXE의 모든 기능을 포함하면서 HTTPS, 스크립팅 확장, UEFI 지원, Wi-Fi 등을 추가했습니다. 다만 HTTPS 사용 시 서버가 TLS 1.3 전용으로 강제되면 일부 iPXE 클라이언트는 접속하지 못할 수 있으므로, 서버 정책에서 TLS 1.2를 함께 허용하는지 확인하는 편이 안전합니다. QEMU/KVM은 2011년부터 내장 PXE ROM으로 iPXE를 사용합니다 (이전에는 gPXE → Etherboot).
iPXE 내부 아키텍처 스크립트 엔진 (iPXE Script) bzImage Loader EFI Image Loader Multiboot Loader PXE NBP Loader PEM/DER Loader HTTP/HTTPS TFTP iSCSI AoE FCoE FTP/NFS SLP TCP (HTTPS/TLS) UDP DHCP/DNS IPv4 / IPv6 / ARP / NDP Net Device Abstraction (VLAN, Bonding, Wi-Fi WPA) SAN Device Abstraction (Block I/O) UNDI (PXE 호환) Intel e1000 Realtek r8169 Virtio-net EFI SNP USB NIC Wi-Fi BIOS (16/32-bit Real→Protected Mode) UEFI (Native 64-bit EFI Application)

Netboot.xyz — iPXE 기반 인터넷 부팅

Netboot.xyz는 iPXE 기반의 인터넷 부팅 서비스입니다. 단일 iPXE 이미지만 TFTP에 배치하면, 인터넷에서 수십 개의 OS 설치 이미지를 동적으로 선택하여 부팅할 수 있습니다.
# Netboot.xyz 설정 (가장 간단한 PXE 구성법)

# 1. Netboot.xyz 이미지 다운로드
wget https://boot.netboot.xyz/ipxe/netboot.xyz.kpxe     # BIOS용
wget https://boot.netboot.xyz/ipxe/netboot.xyz.efi       # UEFI용

# TFTP에 배치
cp netboot.xyz.kpxe /var/lib/tftpboot/
cp netboot.xyz.efi /var/lib/tftpboot/

# 2. dnsmasq 설정
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi64,option:client-arch,7
dhcp-boot=tag:bios,netboot.xyz.kpxe
dhcp-boot=tag:efi64,netboot.xyz.efi

# 이것만으로 Ubuntu, Debian, CentOS, Fedora, Arch, FreeBSD,
# Windows PE, Memtest86+, SystemRescue, DBAN 등
# 수십 가지 OS를 네트워크에서 직접 설치 가능!

# 3. 자체 호스팅 Netboot.xyz (Docker)
docker run -d --name netbootxyz \
  -p 3000:3000 \         # 웹 관리 인터페이스
  -p 69:69/udp \          # TFTP
  -p 8080:80 \            # HTTP 부팅 파일
  -v /data/netboot:/config \
  ghcr.io/netbootxyz/netbootxyz

UEFI PXE & HTTP Boot

UEFI PXE vs Legacy PXE

항목Legacy BIOS PXEUEFI PXEUEFI HTTP Boot
실행 환경16-bit Real ModeNative (32/64-bit)Native (32/64-bit)
드라이버UNDI (Option ROM)EFI SNP/MNP 드라이버EFI HTTP 프로토콜
부트 파일.0 (COM 바이너리).efi (PE32+ 바이너리).efi (PE32+ 바이너리)
전송 프로토콜TFTPTFTPHTTP/HTTPS
파일 크기 제한~32MB (blksize 미확장)없음없음
Secure Boot불가지원지원 (HTTPS 시 TLS)
IPv6미지원지원지원
속도느림 (TFTP)느림 (TFTP)빠름 (HTTP/TCP)
UEFI PXE 부팅 흐름 (EFI_PXE_BASE_CODE_PROTOCOL) SEC/PEI/DXE UEFI 펌웨어 초기화 BDS Boot Device Selection Network Boot 선택 SNP/MNP 초기화 Simple Network Protocol NIC UEFI 드라이버 로드 EFI_PXE_BASE_CODE PXE Base Code Protocol 시작 DHCP/TFTP 실행 UEFI 네트워크 프로토콜 스택 EFI_PXE_BASE_CODE_PROTOCOL / EFI_HTTP_PROTOCOL / EFI_MTFTP4_PROTOCOL EFI_TCP4_PROTOCOL / EFI_UDP4_PROTOCOL / EFI_DHCP4_PROTOCOL / EFI_DNS4_PROTOCOL EFI_IP4_PROTOCOL / EFI_IP6_PROTOCOL / EFI_ARP_PROTOCOL / EFI_IP4_CONFIG2_PROTOCOL EFI_MANAGED_NETWORK_PROTOCOL (MNP) / EFI_VLAN_CONFIG_PROTOCOL EFI_SIMPLE_NETWORK_PROTOCOL (SNP) — NIC 하드웨어 추상화 NIC UEFI Driver (Intel UndiDxe, Realtek, Broadcom, Mellanox ...)

UEFI HTTP Boot 설정

HTTP Boot의 장점: Boot URI를 직접 전달하므로 TFTP보다 큰 이미지를 다루기 쉽고, 기존 웹 서버 인프라(nginx, Apache, CDN, 캐시(Cache) 프록시)를 그대로 활용할 수 있습니다. UEFI 2.5부터 HTTP Boot가 공식 규격에 포함되었고, 최신 UEFI 2.11 문서도 DHCPv4/DHCPv6 기반 HTTP Boot 절차를 계속 규정합니다.
# UEFI HTTP Boot를 위한 DHCP 설정 (ISC DHCPd)
# DHCP Option 60에 "HTTPClient"가 포함된 요청 처리

class "httpclients" {
    match if substring (option vendor-class-identifier, 0, 10) = "HTTPClient";

    # HTTP Boot URI를 BOOTP file 필드로 전달
    if option client-arch = 00:10 {
        # x64 UEFI HTTP Boot
        filename "http://192.168.1.10/boot/shimx64.efi";
    } elsif option client-arch = 00:13 {
        # ARM64 UEFI HTTP Boot
        filename "http://192.168.1.10/boot/shimaa64.efi";
    }
}

# dnsmasq 설정
# HTTP Boot 클라이언트에 HTTP URL 제공
dhcp-match=set:HTTPClient,option:vendor-class,HTTPClient
dhcp-boot=tag:HTTPClient,"http://192.168.1.10/boot/shimx64.efi"

# === nginx HTTP Boot 서버 ===
# /etc/nginx/sites-available/pxe
server {
    listen 80;
    server_name pxe.example.com;

    root /var/www/pxeboot;
    autoindex on;

    # EFI 바이너리 MIME 타입
    types {
        application/efi efi;
    }

    location /boot/ {
        alias /var/lib/tftpboot/;
    }

    location /images/ {
        alias /var/www/pxeboot/images/;
    }

    location /iso/ {
        alias /var/www/pxeboot/iso/;
    }

    # iPXE 동적 스크립트 (PHP/Python 백엔드)
    location /api/boot {
        proxy_pass http://127.0.0.1:8080;
    }
}

# === HTTPS HTTP Boot (TLS) ===
server {
    listen 443 ssl;
    server_name pxe.example.com;

    ssl_certificate /etc/ssl/certs/pxe.crt;
    ssl_certificate_key /etc/ssl/private/pxe.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    root /var/www/pxeboot;
    # ... (동일한 location 설정)
}

HTTP Boot의 DHCP 규칙

UEFI 2.11 기준으로 HTTP Boot 클라이언트는 PXEClient가 아니라 HTTPClient라는 Vendor Class를 사용합니다. DHCPv4에서는 Boot URI를 file 필드나 Option 67로 받을 수 있고, DHCPv6에서는 RFC 5970의 OPTION_BOOTFILE_URL(59)을 사용합니다.

항목DHCPv4DHCPv6
클라이언트 식별Option 60 = HTTPClient:Arch:....Option 16 Vendor Class = HTTPClient:Arch:....
부트 대상 전달file 필드 또는 Option 67에 URI 저장Option 59 BOOTFILE_URL
아키텍처 전달Option 93Option 61 CLIENT_ARCH_TYPE
NIC 인터페이스 정보Option 94Option 62 NII
추가 파라미터벤더 옵션 또는 NBP 내부 처리Option 60 BOOTFILE_PARAM
운영 포인트: HTTP Boot는 "DHCP가 끝나면 자동으로 안전해진다"는 뜻이 아닙니다. DHCP 단계 자체는 여전히 위변조 가능하므로, 무결성은 Secure Boot 서명 체인과 HTTPS 서버 인증, 그리고 관리 네트워크 분리에 의해 보강해야 합니다. 또한 펌웨어마다 지원 TLS 버전, SNI 처리, 인증서 저장소가 다르므로 파일이 아닌 펌웨어 호환성이 실제 장애 원인이 되는 경우가 많습니다.

UEFI PXE Vendor Class 식별

DHCP Option 60 (Vendor Class)의미부트 파일 형식
PXEClient:Arch:00000:UNDI:...BIOS PXE16-bit .0/.com
PXEClient:Arch:00006:UNDI:...UEFI x86 PXEPE32 .efi
PXEClient:Arch:00007:UNDI:...UEFI x64 PXEPE32+ .efi
PXEClient:Arch:00011:UNDI:...UEFI ARM64 PXEPE32+ .efi
HTTPClient:Arch:00015:UNDI:...UEFI x86 HTTP BootPE32 .efi (HTTP/HTTPS URI)
HTTPClient:Arch:00016:UNDI:...UEFI x64 HTTP BootPE32+ .efi (HTTP URL)
HTTPClient:Arch:00017:UNDI:...UEFI EBC HTTP BootEBC EFI 애플리케이션
HTTPClient:Arch:00019:UNDI:...UEFI ARM64 HTTP BootPE32+ .efi (HTTP URL)

리눅스 커널 네트워크 부팅 파라미터

ip= 파라미터

커널이 부팅 시 네트워크를 설정하는 핵심 파라미터입니다. initramfs 안의 네트워크 설정과는 별개로, 커널 자체에 내장된 네트워크 초기화 코드(net/ipv4/ipconfig.c)가 처리합니다.

ip= 파라미터 형식:
  ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>

예시:
  ip=dhcp                                         # 모든 NIC에서 DHCP
  ip=192.168.1.100::192.168.1.1:255.255.255.0:myhost:eth0:off
  ip=::::myhost:eth0:dhcp                          # eth0에서 DHCP, 호스트명 지정
  ip=192.168.1.100::192.168.1.1:255.255.255.0::eth0:off:8.8.8.8:8.8.4.4

autoconf 값:
  dhcp   - DHCP만 사용
  bootp  - BOOTP만 사용
  rarp   - RARP만 사용
  both   - BOOTP와 RARP 모두 시도
  on     - DHCP, BOOTP, RARP 모두 시도
  off    - 정적 설정 (나머지 필드에서 IP 가져옴)
  any    - 사용 가능한 모든 프로토콜 시도

nfsroot= 파라미터

nfsroot= 파라미터 형식:
  nfsroot=[server-ip:]root-dir[,nfs-options]

예시:
  nfsroot=192.168.1.10:/exports/diskless,vers=4,tcp
  nfsroot=/exports/diskless,nfsvers=3,rsize=8192,wsize=8192
  nfsroot=192.168.1.10:/exports/diskless,nolock,tcp

주요 NFS 마운트 옵션:
  vers=4 / nfsvers=3  - NFS 버전 지정
  tcp / udp           - 전송 프로토콜
  rsize=N / wsize=N   - 읽기/쓰기 버퍼 크기
  nolock              - NLM 락 비활성화 (diskless에서 권장)
  ro / rw             - 읽기 전용/읽기 쓰기

네트워크 부팅 관련 전체 커널 파라미터

파라미터설명예시
ip=커널 수준 네트워크 설정ip=dhcp
nfsroot=NFS 루트 파일시스템nfsroot=10.0.0.1:/nfs/root
root=루트 장치/프로토콜root=/dev/nfs, root=iscsi:...
netboot=nfsNFS 네트워크 부팅 명시netboot=nfs
BOOTIF=PXE가 설정한 부팅 인터페이스 MACBOOTIF=01-00-11-22-33-44-55
rd.neednet=1dracut: initrd에서 네트워크 활성화rd.neednet=1
rd.iscsi.initiator=dracut: iSCSI initiator 이름rd.iscsi.initiator=iqn.2024...
rd.iscsi.target.name=dracut: iSCSI target 이름rd.iscsi.target.name=iqn.2024...
rd.iscsi.target.ip=dracut: iSCSI target IPrd.iscsi.target.ip=10.0.0.1
iscsi_target_name=initramfs-tools: iSCSI targetiscsi_target_name=iqn.2024...
rd.live.imagedracut: Live 이미지 부팅rd.live.image
fetch=casper: HTTP/NFS에서 squashfs 가져오기fetch=http://server/fs.squashfs

커널 소스: net/ipv4/ipconfig.c

/* net/ipv4/ipconfig.c - 커널 네트워크 자동 설정 핵심 코드 */

/* ip= 파라미터 파싱 */
static int __init ic_proto_name(char *name)
{
    if (!strcmp(name, "on") || !strcmp(name, "any"))
        return IC_BOOTP | IC_RARP;
    if (!strcmp(name, "off") || !strcmp(name, "none"))
        return 0;
    if (!strcmp(name, "dhcp"))
        return IC_BOOTP | IC_USE_DHCP;
    if (!strcmp(name, "bootp"))
        return IC_BOOTP;
    if (!strcmp(name, "rarp"))
        return IC_RARP;
    if (!strcmp(name, "both"))
        return IC_BOOTP | IC_RARP;
    return IC_BOOTP | IC_RARP;
}

/* DHCP/BOOTP 패킷 수신 처리 */
static void __init ic_bootp_recv(struct sk_buff *skb,
                                  struct net_device *dev)
{
    struct bootp_pkt *b = (struct bootp_pkt *)skb->data;

    /* 트랜잭션 ID 확인 */
    if (b->xid != ic_dev_xid)
        goto drop;

    /* IP 주소 할당 */
    ic_myaddr = b->your_ip;   /* yiaddr → 클라이언트 IP */
    ic_servaddr = b->server_ip; /* siaddr → TFTP 서버 */
    ic_gateway = b->relay_ip;   /* giaddr → 게이트웨이 */

    /* DHCP 옵션 파싱 */
    ic_do_bootp_ext(b->exten, skb->len - sizeof(*b));
    /* ... rootpath, hostname, DNS 등 추출 ... */
}

/* NFS 루트 마운트 (fs/nfs/nfsroot.c) */
static int __init nfs_root_setup(char *line)
{
    /* nfsroot=server:/path,options 파싱 */
    ROOT_DEV = Root_NFS;  /* root=/dev/nfs 효과 */
    /* ... */
}

NFS Root 디스크리스 부팅

디스크리스 부팅 개요

디스크리스(diskless) 부팅은 로컬 디스크 없이 네트워크 파일시스템을 루트로 사용하는 부팅 방식입니다. 씬 클라이언트, 클러스터 노드, 임베디드 시스템에서 널리 사용됩니다.

디스크리스(Diskless) 부팅 아키텍처 디스크리스 클라이언트 [ Client A: 192.168.1.101 ] [ Client B: 192.168.1.102 ] [ Client C: 192.168.1.103 ] 로컬 디스크 없음, PXE 부팅 root=/dev/nfs ip=dhcp L2 Switch 1Gbps / 10Gbps DHCP + TFTP IP 할당 + 커널/initrd NFS Server / (rootfs) 제공 NFS Exports /exports/ base/ (공유 RO) overlay/ (호스트별) clientA/ clientB/ clientC/ 디스크리스 rootfs 전략 1. 전체 복사: 클라이언트별 완전한 rootfs 복사본 (간단하지만 디스크 낭비) 2. 공유 RO + 개별 RW: base(RO) + OverlayFS(RW) 조합 (권장) 3. tmpfs overlay: base(NFS RO) + tmpfs(RW) → 재부팅 시 변경 초기화 (씬 클라이언트) 4. iSCSI LUN: 클라이언트별 블록 디바이스 제공 (로컬 디스크와 동일한 성능) 5. NBD/dm-cache: 네트워크 블록 디바이스 + 로컬 SSD 캐시 (하이브리드)

NFS 서버 설정

# NFS 서버 설정 (Ubuntu/Debian)
sudo apt install nfs-kernel-server

# 디스크리스 루트 파일시스템 준비
sudo mkdir -p /exports/diskless

# debootstrap으로 최소 rootfs 생성 (Debian stable: trixie)
sudo debootstrap --arch=amd64 trixie /exports/diskless \
    http://deb.debian.org/debian

# 또는 기존 시스템 복사
# sudo rsync -axHAX --exclude='/proc/*' --exclude='/sys/*' \
#     --exclude='/dev/*' --exclude='/tmp/*' / /exports/diskless/

# NFS 내보내기 설정
# /etc/exports
/exports/diskless   192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
/exports/base       192.168.1.0/24(ro,sync,no_subtree_check)
/exports/overlay    192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

# NFS 서비스 재시작
sudo exportfs -arv
sudo systemctl restart nfs-kernel-server

# 방화벽 (NFS v4: TCP 2049만 필요)
sudo firewall-cmd --add-service=nfs --permanent
sudo firewall-cmd --reload

디스크리스 클라이언트 rootfs 설정

# /exports/diskless/etc/fstab - 디스크리스 클라이언트용
# NFS root는 커널이 자동 마운트하므로 여기서는 추가 마운트만
proc            /proc   proc    defaults    0   0
sysfs           /sys    sysfs   defaults    0   0
tmpfs           /tmp    tmpfs   defaults    0   0
tmpfs           /run    tmpfs   defaults    0   0
tmpfs           /var/log tmpfs  defaults    0   0

# /exports/diskless/etc/network/interfaces
auto lo
iface lo inet loopback

# DHCP (커널 ip=dhcp로 이미 설정되지만, 사용자 공간에서도 필요)
auto eth0
iface eth0 inet dhcp

# 커널 및 initramfs 생성 (NFS root 지원 포함)
# chroot /exports/diskless
# apt install linux-image-amd64 initramfs-tools

# /exports/diskless/etc/initramfs-tools/initramfs.conf
MODULES=netboot
BOOT=nfs

# initramfs 재생성
# update-initramfs -u

# TFTP에 커널/initrd 복사
sudo cp /exports/diskless/boot/vmlinuz-* /var/lib/tftpboot/vmlinuz-diskless
sudo cp /exports/diskless/boot/initrd.img-* /var/lib/tftpboot/initrd-diskless.img

OverlayFS 디스크리스 구성

# initramfs 안에서 OverlayFS 설정하는 스크립트
# /exports/diskless/etc/initramfs-tools/scripts/init-bottom/overlay

#!/bin/sh
# NFS를 lower로, tmpfs를 upper로 사용하는 OverlayFS
# 재부팅 시 모든 변경 사항 초기화됨

PREREQ=""
prereqs() { echo "$PREREQ"; }
case $1 in prereqs) prereqs; exit 0;; esac

. /scripts/functions

# tmpfs 생성 (writable layer)
mkdir -p /overlay
mount -t tmpfs tmpfs /overlay -o size=2G
mkdir -p /overlay/upper /overlay/work

# OverlayFS 마운트
# ${rootmnt}는 NFS로 마운트된 read-only rootfs
mkdir -p /overlay/merged
mount -t overlay overlay /overlay/merged \
    -o lowerdir=${rootmnt},upperdir=/overlay/upper,workdir=/overlay/work

# rootfs 교체
mkdir -p /overlay/merged/nfs-root
mount --move ${rootmnt} /overlay/merged/nfs-root
mount --move /overlay/merged ${rootmnt}

exit 0

iSCSI SAN 부팅

iSCSI 부팅 개요

iSCSI(Internet Small Computer Systems Interface)는 SCSI 명령을 TCP/IP로 전달하는 프로토콜입니다. PXE/iPXE로 초기 부팅 후 iSCSI LUN을 루트 디바이스로 사용하면, 로컬 디스크와 거의 동일한 블록 레벨 I/O 성능을 얻을 수 있습니다.

iSCSI SAN 부팅 흐름 1. PXE DHCP IP 할당 NBP: iPXE 로드 2. iPXE iSCSI sanhook / sanboot iSCSI target 연결 3. 커널 + initrd iSCSI LUN에서 부팅 또는 TFTP에서 로드 4. initrd iSCSI 재연결 open-iscsi: iscsiadm login root=/dev/sda1 마운트 iSCSI Target Server Target: iqn.2024-01.com.example:storage LUN 0: /dev/zvol/pool/client-A (50GB, ext4) LUN 1: /dev/zvol/pool/client-B (50GB, ext4) LUN 2: /dev/zvol/pool/shared-ro (100GB, squashfs) Auth: CHAP (mutual), Portal: 10.0.0.1:3260

iSCSI Target 서버 설정

# targetcli를 사용한 iSCSI target 설정 (Linux LIO)
sudo apt install targetcli-fb

# LVM 또는 파일 기반 LUN 생성
sudo lvcreate -L 50G -n client-A vg0
# 또는: dd if=/dev/zero of=/var/lib/iscsi/client-A.img bs=1M count=51200

sudo targetcli

# targetcli 내부 명령:
/> backstores/block create client-A /dev/vg0/client-A
/> iscsi/ create iqn.2024-01.com.example:storage
/> iscsi/iqn.2024-01.com.example:storage/tpg1/luns/ create /backstores/block/client-A
/> iscsi/iqn.2024-01.com.example:storage/tpg1/acls/ create iqn.2024-01.com.example:client-A

# CHAP 인증 설정
/> iscsi/iqn.2024-01.com.example:storage/tpg1/acls/iqn.2024-01.com.example:client-A/ set auth userid=client-A
/> iscsi/iqn.2024-01.com.example:storage/tpg1/acls/iqn.2024-01.com.example:client-A/ set auth password=secret123

# 포털 설정 (기본 0.0.0.0:3260)
/> iscsi/iqn.2024-01.com.example:storage/tpg1/portals/ create 10.0.0.1

/> saveconfig
/> exit

iPXE iSCSI 부팅 스크립트

#!ipxe
# iscsi-boot.ipxe

dhcp
set iscsi-server 10.0.0.1
set iscsi-port 3260
set iscsi-target iqn.2024-01.com.example:storage
set iscsi-initiator iqn.2024-01.com.example:${mac:hexhyp}

# iSCSI 연결 및 SAN 부팅
set root-path iscsi:${iscsi-server}::${iscsi-port}:1:${iscsi-target}
sanhook ${root-path} || goto failed
sanboot ${root-path} || goto failed

:failed
echo iSCSI boot failed!
shell

대규모 프로비저닝 시스템

프로비저닝 도구 비교

도구개발사특징규모자동 설치
Cobbler커뮤니티DHCP/DNS/TFTP 통합 관리, 프로파일 시스템중-대Kickstart, Preseed
MAASCanonical"Metal as a Service", 베어메탈 클라우드cloud-init, Curtin
ForemanTheforeman라이프사이클 관리, Puppet/Ansible 통합Kickstart, Preseed
FOG Project커뮤니티이미지 기반 배포, 디스크 클로닝소-중이미지 복원
TinkerbellEquinix클라우드 네이티브, 워크플로 기반Workflow Actions
IronicOpenStackOpenStack 베어메탈 프로비저닝IPA (Ironic Python Agent)
RazorPuppet정책 기반 프로비저닝Kickstart, Preseed
MatchboxCoreOS/Red HatCoreOS/Flatcar 전용, iPXE 통합중-대Ignition
Netboot.xyz커뮤니티인터넷 기반 멀티 OS 부팅 메뉴다양

Kickstart 자동 설치 예제 (RHEL 10 계열)

# /var/www/pxeboot/ks/rhel10.cfg - Kickstart 파일
# RHEL 10 계열 무인 설치

# 설치 소스
url --url="http://192.168.1.10/rhel10/"
repo --name="AppStream" --baseurl="http://192.168.1.10/rhel10/AppStream/"

# 시스템 설정
text
lang en_US.UTF-8
keyboard us
timezone Asia/Seoul --utc
rootpw --iscrypted $6$rounds=656000$salt$hash...
selinux --enforcing
firewall --enabled --ssh
services --enabled=sshd

# 네트워크
network --bootproto=dhcp --device=link --activate --hostname=server01.example.com

# 디스크 파티셔닝
zerombr
clearpart --all --initlabel
autopart --type=lvm --fstype=xfs

# BIOS/UEFI 공용 예시: 위치를 MBR로 고정하지 않음
bootloader --append="crashkernel=auto"

# 패키지
%packages
@^minimal-environment
vim-enhanced
tmux
open-iscsi
nfs-utils
%end

# 설치 후 스크립트
%post --log=/root/ks-post.log
# SSH 키 배포
mkdir -p /root/.ssh
curl -o /root/.ssh/authorized_keys http://192.168.1.10/keys/admin.pub
chmod 600 /root/.ssh/authorized_keys

# 설치 상태 등록
primary_ip=$(hostname -I | awk '{print $1}')
curl -X POST http://192.168.1.10/api/register \
    -d "hostname=$(hostname)&ip=${primary_ip}"
%end

# 설치 완료 후 재부팅
reboot
Kickstart 실무 메모: RHEL 10 공식 문서는 PXE와 UEFI HTTP Boot 모두에서 inst.ks=를 같은 방식으로 커널 인자에 추가하는 구성을 제시합니다. 혼합 BIOS/UEFI 환경 문서라면 BIOS 전용 표현인 --location=mbr를 기본 예제로 두기보다, 범용적인 bootloader --append=... 형태를 먼저 보여 주는 편이 안전합니다. Kickstart 파일은 배포 전에 ksvalidator -v RHEL10로 문법을 먼저 점검하는 편이 좋습니다.

cloud-init / Autoinstall 예제 (Ubuntu)

# /var/www/pxeboot/autoinstall/user-data
# Ubuntu 24.04 LTS Autoinstall 설정

#cloud-config
autoinstall:
  version: 1

  locale: en_US.UTF-8
  keyboard:
    layout: us

  network:
    version: 2
    ethernets:
      id0:
        match:
          driver: e1000*
        dhcp4: true

  storage:
    layout:
      name: lvm

  identity:
    hostname: ubuntu-server
    username: admin
    password: "$6$rounds=656000$..."

  ssh:
    install-server: true
    authorized-keys:
      - ssh-ed25519 AAAA... admin@example.com

  packages:
    - vim
    - tmux
    - nfs-common
    - open-iscsi

  late-commands:
    # 첫 부팅 시 로컬 디스크에서 부팅하도록 설정
    - curtin in-target --target=/target -- efibootmgr -o 0001
    # 설치 후 알림
    - "curl -X POST http://192.168.1.10/api/installed?host=$(hostname)"
Ubuntu Autoinstall 실무 메모: NoCloud 네트워크 방식은 PXE 커널 인자에 autoinstall ds=nocloud-net;s=http://...를 넣고, HTTP 루트에 user-datameta-data를 함께 제공하는 패턴이 가장 일반적입니다. Ubuntu 24.04부터는 cloud-config를 사용할 때 최상위 autoinstall: 구조와 스키마 검증이 더 엄격해졌으므로, 예전의 느슨한 YAML을 그대로 재사용하면 설치 초반에 중단될 수 있습니다.

배포판별 자동설치 실무 메모

배포판현재 문서 기준권장 전달 방식실무에서 자주 틀리는 지점
RHEL 10 계열Kickstart + Anacondainst.repo= + inst.ks=, PXE 또는 UEFI HTTP BootBIOS 전용 --location=mbr 고정, 배포 전 ksvalidator 미실행
Ubuntu 24.04 LTSSubiquity + cloud-init Autoinstallautoinstall ds=nocloud-net;s=... + HTTP user-data/meta-data#cloud-config 헤더 누락, 최상위 autoinstall: 키 누락, 오래된 스키마 재사용
Debian 13 stable (trixie)Debian Installer + Preseedauto=true priority=critical url=...UEFI 환경에서 GPT/EFI 관련 partman 옵션 누락, interface=auto 미지정

배포판별 자동설치 실패 지점 빠른 점검표

PXE 단계는 통과했는데 무인 설치가 기대대로 이어지지 않는 경우, 배포판마다 먼저 봐야 할 로그와 커널 인자가 다릅니다. 아래 표는 현장에서 어디부터 줄여 들어갈지를 빠르게 고르기 위한 요약입니다.

배포판자주 보이는 증상가장 먼저 볼 로그/파일바로 실행할 명령우선 의심할 지점
RHEL 10 계열설치기가 올라오지만 저장소 인식, 디스크 분할, %pre/%post에서 멈춥니다./tmp/pre-anaconda-logs/, /tmp/anaconda.log, /tmp/storage.log, /tmp/program.loginst.debug rd.debug 추가 후 Ctrl+Alt+F2, less /tmp/anaconda.log, less /tmp/storage.loginst.repo= 또는 inst.ks= 오타, NIC 드라이버 미포함, 스토리지 장치명 가정, Kickstart 문법 미검증
Ubuntu 24.04 LTS자동 설치로 가지 않고 대화형 설치기로 떨어지거나, 초반 스키마 오류로 중단됩니다./var/log/installer/, /var/log/installer/subiquity-server-debug.log, /var/log/installer/autoinstall-user-data, /var/log/cloud-init.logcloud-init schema --config-file user-data --annotate, grep -n '^autoinstall:' user-data, grep -n '^#cloud-config' user-data#cloud-config 헤더 누락, 최상위 autoinstall: 키 누락, meta-data 미제공, ds=nocloud-net;s= URL 경로 오류
Debian 13 stable (trixie)Preseed가 무시되어 질문 화면이 다시 나오거나, 네트워크 선택/EFI 분할에서 멈춥니다.설치 중 /var/log/syslog, /var/log/partman, 설치 후 /var/log/installer/cat /proc/cmdline, tail -f /var/log/syslog, 커널 인자에 interface=auto netcfg/dhcp_timeout=60 추가url= 도달 실패, Preseed 파일 문법 오류, EFI 파티션 항목 누락, 잘못된 NIC를 첫 인터페이스로 선택
자동설치 디버깅 순서: RHEL 계열은 먼저 inst.debug로 Anaconda 로그를 남기고, Ubuntu 계열은 PXE 전에 cloud-init schema로 YAML을 검증하고, Debian 계열은 설치기 셸에서 /proc/cmdline/var/log/syslog를 바로 확인하는 편이 가장 빠릅니다.
대규모 PXE 프로비저닝 워크플로 1. 서버 투입 랙 설치, 케이블 연결 PXE 부팅 설정 2. 자동 발견 DHCP → MAC 등록 IPMI/BMC 감지 3. 프로파일 할당 OS + 역할 선택 (웹 / DB / 컴퓨트) 4. PXE 설치 커널 부팅 → 무인설치 Kickstart/Preseed 5. 구성 관리 Ansible/Puppet 서비스 배포 대규모 배포 최적화 포인트 1. TFTP → HTTP 전환: 대규모 동시 부팅 시 TFTP는 병목. iPXE + HTTP/nginx 조합으로 1000+ 노드 동시 부팅 2. 멀티캐스트 TFTP: 동일 이미지를 여러 노드에 동시 전송 (UDPcast) 3. BitTorrent 배포: 대규모 이미지 배포에 P2P 활용 (Murder, Drax) 4. 로컬 캐싱: 서브넷별 캐싱 프록시(Squid, nginx cache)로 WAN 트래픽 감소 5. IPMI/BMC 연동: ipmitool/Redfish로 원격 전원 제어 + PXE 부팅 순서 자동 설정 6. DHCP 릴레이: 여러 서브넷 지원을 위한 ip helper-address / dhcp-relay 구성

Secure PXE Boot

PXE 보안 위협과 대응

위협설명대응
Rogue DHCP악성 DHCP 서버가 잘못된 부팅 서버로 유도DHCP Snooping, 802.1X, VLAN 분리
TFTP 탈취NBP/커널 파일을 악성으로 교체HTTPS 전송 (iPXE/UEFI HTTP Boot)
MITM 공격네트워크 중간에서 부팅 트래픽 변조TLS/HTTPS, IPSec, 802.1X
악성 NBP서명되지 않은 부트로더 실행UEFI Secure Boot + Shim
부팅 이미지 변조커널/initrd 무결성 훼손IMA/EVM, dm-verity, 서명 검증
인증 없는 접근아무 기기나 PXE 부팅 가능MAC 필터링, 802.1X NAC, CHAP

Secure PXE Boot Chain

UEFI Secure PXE Boot Chain UEFI 펌웨어 PK → KEK → db Microsoft CA 포함 검증 shimx64.efi MS 서명됨 MOK DB 관리 검증 grubx64.efi Distro CA 서명 또는 MOK 서명 검증 vmlinuz 커널 서명 검증 Lockdown LSM 검증 커널 모듈 모듈 서명 검증 CONFIG_MODULE_SIG 전송 보안 계층 UEFI HTTP Boot + HTTPS: 서버 인증과 신뢰 저장소는 펌웨어 구현에 의존 iPXE + HTTPS: 빌드 시 CA 인증서 내장, 서버 인증서 검증

Secure PXE Boot 구성 절차

# 1. MOK (Machine Owner Key) 생성
openssl req -new -x509 -newkey rsa:2048 -keyout MOK.key -out MOK.crt \
    -days 3650 -nodes -subj "/CN=PXE Boot Signing Key"

# 2. GRUB EFI 서명
sbsign --key MOK.key --cert MOK.crt --output grubx64.efi.signed grubx64.efi

# 3. 커널 서명
sbsign --key MOK.key --cert MOK.crt --output vmlinuz.signed vmlinuz

# 4. Shim에 MOK 등록 (각 클라이언트에서 1회)
# shimx64.efi가 처음 부팅 시 MokManager로 진입
# → MOK.cer(DER 형식) 등록
openssl x509 -in MOK.crt -outform der -out MOK.cer

# 5. TFTP에 배치
cp shimx64.efi.signed /var/lib/tftpboot/shimx64.efi
cp grubx64.efi.signed /var/lib/tftpboot/grubx64.efi
cp vmlinuz.signed /var/lib/tftpboot/vmlinuz

# 6. iPXE HTTPS 빌드 (CA 인증서 내장)
cd ipxe/src
make bin-x86_64-efi/ipxe.efi EMBED=boot.ipxe \
    TRUST=/etc/ssl/certs/ca-certificates.crt \
    CERT=client.crt PRIVKEY=client.key

# 7. 네트워크 보안
# 802.1X RADIUS 인증 (PXE 전 네트워크 접근 제어)
# DHCP Snooping: 신뢰할 수 있는 포트만 DHCP 응답 허용
# Dynamic ARP Inspection: ARP 스푸핑 방지

IPv6 PXE 부팅

IPv6 PXE 개요

IPv6에서는 BOOTP의 siaddr/file 모델보다 URL 기반 전달이 더 중요해집니다. UEFI는 IPv6 환경에서 netboot6(PXE over DHCPv6 + MTFTP)와 HTTP Boot를 모두 정의하며, DHCPv6 옵션 집합(RFC 5970)을 통해 부트 URI와 아키텍처 정보를 전달합니다.

항목IPv4 PXEIPv6 PXE
주소 할당DHCPv4DHCPv6, RA, 필요 시 SLAAC 병행
부트 위치 전달siaddr, file, Option 66/67Option 59 BOOTFILE_URL
아키텍처 정보Option 93Option 61 CLIENT_ARCH_TYPE
NIC 인터페이스 식별Option 94Option 62 NII
부트파일 예시filename "pxelinux.0"tftp://[2001:db8::1]/grubx64.efi 또는 https://boot.example.com/shimx64.efi
다음 서버 지정next-server 또는 siaddrURI 안에 서버와 경로를 모두 포함
멀티캐스트제한적네이티브 지원
BIOS 지원지원미지원 (UEFI만)

DHCPv6 부트 옵션 세트

RFC 5970 옵션이름역할예시
59BOOTFILE_URL부트 이미지의 전체 URI 전달http://[2001:db8::10]/boot/shimx64.efi
60BOOTFILE_PARAM부트 이미지에 전달할 부가 파라미터root=/dev/nfs, ip=dhcp
61CLIENT_ARCH_TYPE클라이언트 아키텍처 전달x64 UEFI, ARM64 UEFI, HTTP Boot 등
62NIINetwork Interface IdentifierUNDI/SNP 인터페이스 버전, 타입
설계 포인트: IPv6 환경에서는 "TFTP 서버 주소"와 "파일 이름"을 따로 조합하는 사고보다, 완전한 URI를 전달합니다고 생각하는 편이 이해와 디버깅(Debugging)에 훨씬 유리합니다. 이 점 때문에 UEFI HTTP Boot는 IPv6 환경에서 특히 자연스럽게 맞아떨어집니다.
# ISC DHCPv6 PXE 설정
# /etc/dhcp/dhcpd6.conf

subnet6 2001:db8::/64 {
    range6 2001:db8::100 2001:db8::200;

    # PXE 부트파일 URL (DHCPv6 Option 59)
    option dhcp6.bootfile-url "tftp://[2001:db8::1]/grubx64.efi";

    # HTTP Boot
    # option dhcp6.bootfile-url "http://[2001:db8::1]/boot/shimx64.efi";
}

# dnsmasq IPv6 PXE
dhcp-range=::100,::200,constructor:eth0,ra-names,slaac,64,12h
dhcp-option=option6:bootfile-url,"tftp://[2001:db8::1]/grubx64.efi"

멀티캐스트 부팅과 성능 최적화

멀티캐스트 TFTP

동일한 이미지를 수백 대의 서버에 동시에 전송해야 할 때, 유니캐스트 TFTP는 심각한 병목(Bottleneck)이 됩니다. 멀티캐스트를 사용하면 네트워크 대역폭(Bandwidth)을 절약하면서 대규모 동시 배포가 가능합니다.

방식도구장점단점
유니캐스트 TFTPtftpd-hpa간단, 호환성 최고노드 수 × 대역폭
멀티캐스트 TFTPatftpd (mtftp)1:N 동시 전송PXE ROM 지원 필요
UDPcastudp-sender/receiver대규모 이미지 배포커스텀 리시버 필요
HTTPnginx/ApacheTCP 신뢰성, 빠른 속도iPXE 또는 UEFI HTTP Boot 필요
BitTorrentMurder, opentrackerP2P, 대규모에 최적초기 시드 필요, 복잡성

전송 프로토콜별 성능 비교

테스트 환경: 100MB vmlinuz + 50MB initrd, 1Gbps 네트워크

┌──────────────────────┬──────────────┬─────────────────┬──────────────────┐
│ 프로토콜             │ 단일 클라이언트│ 10대 동시       │ 100대 동시       │
├──────────────────────┼──────────────┼─────────────────┼──────────────────┤
│ TFTP (512B blk)      │ ~120초       │ ~1200초 (직렬)  │ 사실상 불가      │
│ TFTP (1468B blk)     │ ~25초        │ ~250초          │ ~2500초          │
│ TFTP (1468B, win=64) │ ~8초         │ ~80초           │ ~800초           │
│ HTTP (nginx)         │ ~2초         │ ~3초            │ ~15초            │
│ HTTPS (nginx)        │ ~3초         │ ~5초            │ ~20초            │
│ 멀티캐스트 TFTP      │ ~25초        │ ~30초           │ ~40초            │
│ UDPcast              │ ~3초         │ ~5초            │ ~10초            │
└──────────────────────┴──────────────┴─────────────────┴──────────────────┘
* 실제 성능은 네트워크 환경, 서버 성능에 따라 크게 다를 수 있음

nginx PXE 서버 최적화

# /etc/nginx/conf.d/pxe-optimized.conf
server {
    listen 80;
    server_name pxe.example.com;
    root /var/www/pxeboot;

    # 동시 접속 최적화
    keepalive_timeout 30;
    keepalive_requests 100;

    # sendfile + tcp_nopush로 커널 수준 최적화
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # 대용량 파일 전송 최적화
    directio 4m;          # 4MB 이상 파일은 DirectIO
    aio threads;          # 비동기 I/O
    output_buffers 2 1m;  # 출력 버퍼

    # 바이트 범위 요청 (iPXE가 사용)
    max_ranges 1;

    # 캐싱 헤더
    location ~* \.(efi|img|vmlinuz|iso)$ {
        expires 1h;
        add_header Cache-Control "public, no-transform";
    }

    # 접속 제한 (DoS 방지)
    limit_conn_zone $binary_remote_addr zone=pxe:10m;
    limit_conn pxe 10;
}

UDPcast 멀티캐스트 배포

UDPcast는 대규모 동시 이미지 배포에 최적화된 멀티캐스트/브로드캐스트 도구입니다. PXE + iPXE와 결합하여 수백 대의 서버에 동시에 디스크 이미지를 전송할 수 있습니다.

# UDPcast 설치 및 사용

# 서버 설치 (Debian/Ubuntu)
sudo apt install udpcast

# === 1. 디스크 이미지 멀티캐스트 전송 ===

# 송신측 (서버): 디스크 이미지 전송
udp-sender --file /images/ubuntu-golden.img \
    --mcast-addr 239.1.1.1 \
    --portbase 9000 \
    --min-receivers 10 \      # 최소 10대 수신 대기 후 시작
    --max-wait 60 \            # 최대 60초 대기
    --blocksize 1456 \         # 블록 크기 (MTU 최적화)
    --fec 8x8/64 \             # Forward Error Correction
    --autostart 30 \           # 30초 후 자동 시작
    --log /var/log/udpcast.log

# 수신측 (클라이언트): initrd 안에서 실행
udp-receiver --mcast-addr 239.1.1.1 \
    --portbase 9000 \
    --file /dev/sda \          # 직접 디스크에 기록
    --nosync                    # 완료 시 sync 생략 (빠른 재부팅)

# === 2. 파이프를 통한 실시간 압축 전송 ===

# 송신측
lz4 -c /dev/sda | udp-sender --pipe "cat" \
    --mcast-addr 239.1.1.1 --portbase 9000 --min-receivers 5

# 수신측
udp-receiver --pipe "lz4 -d > /dev/sda" \
    --mcast-addr 239.1.1.1 --portbase 9000

# === 3. iPXE 스크립트에서 UDPcast 연동 ===
# initrd에 udp-receiver를 포함시키고, 커널 파라미터로 멀티캐스트 주소 전달
# imgargs vmlinuz ip=dhcp udpcast_addr=239.1.1.1 udpcast_port=9000

atftpd 멀티캐스트 TFTP

# atftpd: 멀티캐스트 TFTP 서버 (RFC 2090)
sudo apt install atftpd

# /etc/default/atftpd 설정
USE_INETD=false
OPTIONS="--daemon --port 69 \
  --tftpd-timeout 300 \
  --retry-timeout 5 \
  --maxthread 200 \
  --bind-address 0.0.0.0 \
  --verbose 5 \
  --logfile /var/log/atftpd.log \
  --mtftp 239.1.1.1,1758 \
  --mtftp-port 1758 \
  /var/lib/tftpboot"

# 멀티캐스트 TFTP 동작 원리:
# 1. 첫 번째 클라이언트가 RRQ 전송
# 2. 서버가 멀티캐스트 그룹으로 DATA 전송 시작
# 3. 이후 클라이언트들은 진행 중인 멀티캐스트 스트림에 합류
# 4. 놓친 블록은 유니캐스트로 재요청
# 주의: PXE ROM이 MTFTP를 지원해야 함 (많은 ROM이 미지원)

sudo systemctl restart atftpd

DHCP 릴레이와 다중 서브넷

DHCP 릴레이 개요

PXE 클라이언트의 DHCPDISCOVER는 브로드캐스트이므로, 라우터를 넘어갈 수 없습니다. 다중 서브넷 환경에서는 DHCP 릴레이 에이전트(ip helper-address)가 필요합니다.

DHCP 릴레이를 통한 다중 서브넷 PXE 부팅 서브넷 A (192.168.1.0/24) PXE Client A PXE Client B 서브넷 B (192.168.2.0/24) PXE Client C PXE Client D L3 Router DHCP Relay Agent ip helper-address DHCP + TFTP Server 10.0.0.1 (관리 서브넷) 릴레이 동작: 1. DISCOVER를 수신 2. giaddr에 수신 인터페이스 IP 설정 3. 유니캐스트로 DHCP 서버에 전달 4. 서버가 giaddr 서브넷 풀에서 IP 할당
# Cisco/Juniper 라우터 DHCP Relay 설정

# Cisco IOS
interface GigabitEthernet0/1
 description Subnet-A
 ip address 192.168.1.1 255.255.255.0
 ip helper-address 10.0.0.1

interface GigabitEthernet0/2
 description Subnet-B
 ip address 192.168.2.1 255.255.255.0
 ip helper-address 10.0.0.1

# Linux DHCP Relay Agent
sudo apt install isc-dhcp-relay

# /etc/default/isc-dhcp-relay
SERVERS="10.0.0.1"
INTERFACES="eth1 eth2"
OPTIONS="-a"  # Agent Information (Option 82) 추가

sudo systemctl enable --now isc-dhcp-relay

# ISC DHCP 서버: 서브넷별 설정
# giaddr로 클라이언트 서브넷 식별
subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    next-server 10.0.0.1;
    filename "pxelinux.0";
}

subnet 192.168.2.0 netmask 255.255.255.0 {
    range 192.168.2.100 192.168.2.200;
    option routers 192.168.2.1;
    next-server 10.0.0.1;
    filename "pxelinux.0";
}

DHCP Option 82 (Relay Agent Information)

DHCP Option 82(RFC 3046)는 릴레이 에이전트가 DHCP 패킷에 추가하는 정보로, 클라이언트가 연결된 물리적 위치(스위치 포트, VLAN)를 DHCP 서버에 알려줍니다. 대규모 PXE 환경에서 자동 프로비저닝에 핵심적입니다.

DHCP Option 82 구조 (RFC 3046):
┌──────────────────────────────────────────────────────┐
│ Option 82 (Relay Agent Information)                  │
│  Type: 82 (0x52), Length: variable                   │
│                                                       │
│  Sub-option 1: Circuit ID (물리적 포트 식별)          │
│    - 스위치명 + 포트 번호                             │
│    - 예: "Gi0/1" (GigabitEthernet 0/1)              │
│    - 예: "Ethernet1/0/24" (스택 스위치)              │
│                                                       │
│  Sub-option 2: Remote ID (원격 장비 식별)            │
│    - 릴레이 에이전트(스위치)의 MAC 또는 식별자        │
│    - 예: "00:1A:2B:3C:4D:5E" (스위치 MAC)           │
│                                                       │
│  Sub-option 5: Link Selection (선택적)               │
│    - DHCP 서버가 사용할 서브넷 지정                   │
│  Sub-option 6: Subscriber ID (선택적)                │
│    - 가입자 식별 (ISP 환경)                          │
└──────────────────────────────────────────────────────┘

활용 시나리오:
- 스위치 포트 기반 자동 프로비저닝: "Gi0/1에 연결된 서버 → Web 프로파일"
- 랙/위치 기반 IP 할당: 포트 정보로 물리적 위치 파악
- 보안 감사: 어떤 포트에서 DHCP 요청이 왔는지 추적
# ISC DHCP에서 Option 82 활용 예제

# Option 82 서브옵션 정의
option agent.circuit-id code 1 = text;
option agent.remote-id code 2 = text;

# Circuit ID 기반 PXE 프로파일 자동 할당
class "rack1-servers" {
    # 랙1의 스위치 포트 Gi0/1~Gi0/24에서 온 요청
    match if substring(option agent.circuit-id, 0, 4) = "Gi0/";
}

# 클래스별 다른 부트 파일/OS 프로파일 할당
pool {
    allow members of "rack1-servers";
    range 192.168.1.100 192.168.1.124;
    next-server 10.0.0.1;
    filename "profiles/rack1/pxelinux.0";
}

# Cisco 스위치에서 Option 82 설정
# ip dhcp snooping
# ip dhcp snooping vlan 100
# ip dhcp snooping information option    ← Option 82 삽입 활성화
# interface GigabitEthernet0/1
#  ip dhcp snooping trust                ← DHCP 서버 쪽 포트

트러블슈팅

일반적인 문제와 해결

증상원인해결
PXE-E51: No DHCP or proxyDHCP offers were receivedDHCP 서버 미응답DHCP 서비스 확인, 방화벽(Firewall) UDP 67/68 열기, VLAN/포트 확인
PXE-E53: No boot filename receivedDHCP에 filename 미설정filename 또는 Option 67 설정 확인
PXE-E32: TFTP open timeoutTFTP 서버 접근 불가TFTP 서비스 확인, 방화벽 UDP 69 열기, next-server IP 확인
PXE-E36: Error received from TFTP server파일 없음 또는 권한 오류파일 경로/권한 확인: ls -la /var/lib/tftpboot/
PXE-E3B: TFTP Error - File not found부트파일 경로 오류정확한 파일명 확인, 대소문자 주의
PXE-E61: Media test failure, check cable네트워크 물리적 문제케이블, 스위치 포트, 링크 상태 확인
NBP 로드 후 검은 화면아키텍처 불일치BIOS→.0 파일, UEFI→.efi 파일 확인
GRUB: error: no suitable video mode found그래픽 모드 문제terminal_output console 사용
"Booting from ROM" 후 멈춤PXE ROM 비활성화BIOS에서 NIC PXE/Network Boot 활성화
UEFI HTTP Boot 실패HTTP 서버 문제URL 접근 가능 확인, Content-Type 헤더 확인

실패 메시지별 즉시 대응표

현장에서는 에러 코드 하나만 보고도 점검 순서를 줄여야 합니다. 아래 표는 콘솔에 보이는 문자열을 기준으로 즉시 어디를 봐야 하는지를 압축한 대응표입니다.

실패 메시지문제가 난 계층즉시 의미첫 확인 명령/행동
PXE-E51: No DHCP or proxyDHCP offers were received네트워크 제어면클라이언트가 DHCPDISCOVER는 보냈지만 OFFER를 못 받았거나, relay/proxyDHCP 경로가 끊겼습니다.tcpdump -i eth0 port 67 or 68, DHCP 서버 로그, VLAN/access 포트 확인
PXE-E53: No boot filename receivedDHCP 정책IP는 받았지만 filename, Option 67, Boot URI 중 적절한 값이 오지 않았습니다.DHCP 응답의 file 필드와 Option 67, 아키텍처별 분기 규칙 확인
PXE-E32: TFTP open timeoutTFTP 경로next-server에는 도달하지 못하거나 UDP 69 이후 데이터 포트가 막혔습니다.tftp server -c get pxelinux.0, 방화벽, nf_conntrack_tftp, MTU 확인
PXE-E36 또는 PXE-E3BTFTP 파일/권한파일명, 경로, 권한, 심볼릭 링크, 대소문자 중 하나가 틀렸습니다.ls -la /var/lib/tftpboot, 서버 로그, 아키텍처별 파일명 확인
PXE-E61: Media test failure, check cable물리 계층링크가 올라오지 않았거나 speed/duplex 협상이 어긋났습니다.케이블, SFP, 스위치 포트, ethtool eth0, 강제 속도 설정 확인
Booting from ROM... 후 멈춤Option ROM / NBP 전환ROM은 로드됐지만 NBP 실행 또는 다음 단계 핸드오프에서 멈췄습니다.BIOS/UEFI 모드와 NBP 파일 형식 일치 여부, Secure Boot, ROM 크기/서명 확인
NBP is too big to fit in free base memoryLegacy BIOS 메모리16비트 BIOS 가용 메모리 한계 때문에 NBP 또는 초기 모듈 조합이 너무 큽니다.PXELINUX 모듈 축소, iPXE 체인로딩 전환, 불필요한 COM32 모듈 제거
Could not boot image: Exec format error아키텍처/포맷BIOS에 .efi, UEFI에 BIOS NBP, 또는 CPU 아키텍처가 다른 이미지를 넘겼습니다.file grubx64.efi, Option 93/HTTPClient 분기, efibootmgr -v 확인
start_image() returned Invalid ParameterUEFI 로더 체인UEFI가 EFI application은 받았지만 이미지 형식, 의존 파일, 서명, 메모리 조건이 맞지 않습니다.shimx64.efigrubx64.efi 조합, Secure Boot 상태, 서버 파일 무결성 확인
VFS: Cannot open root device "nfs"커널 이후 rootfsPXE 자체는 성공했지만 커널 내 네트워크 재초기화 또는 NFS export 단계에서 실패했습니다.dmesg | grep IP-Config, NFS export, built-in NIC 드라이버, ip= 파라미터 확인

디버깅 도구와 명령어

# === DHCP 트래픽 캡처 ===
# tcpdump로 DHCP 패킷 모니터링
sudo tcpdump -i eth0 -vvv port 67 or port 68 or port 4011

# Wireshark 필터
# bootp || dhcp || tftp

# === TFTP 테스트 ===
# TFTP 클라이언트로 직접 파일 요청
tftp 192.168.1.10
tftp> get pxelinux.0
tftp> quit

# curl로 TFTP 테스트
curl tftp://192.168.1.10/pxelinux.0 -o /tmp/pxelinux.0

# === DHCP 서버 로그 ===
# ISC DHCP
sudo journalctl -u isc-dhcp-server -f

# dnsmasq
sudo journalctl -u dnsmasq -f
# 또는 dnsmasq --log-dhcp 옵션

# === TFTP 서버 로그 ===
sudo journalctl -u tftpd-hpa -f

# === iPXE 디버깅 ===
# iPXE 쉘에서 직접 디버깅
iPXE> dhcp net0
iPXE> show net0/ip
iPXE> show net0/netmask
iPXE> show net0/gateway
iPXE> show net0/dns
iPXE> nslookup pxe.example.com
iPXE> ping 192.168.1.10
iPXE> imgfetch tftp://192.168.1.10/pxelinux.0
iPXE> chain http://192.168.1.10/boot.ipxe

# === NFS 디버깅 ===
# NFS 마운트 테스트
sudo mount -t nfs 192.168.1.10:/exports/diskless /mnt -o vers=4,tcp
ls /mnt/

# NFS 서버 내보내기 확인
showmount -e 192.168.1.10

# rpcinfo (NFS v3)
rpcinfo -p 192.168.1.10

# === iSCSI 디버깅 ===
# iSCSI target 탐색
iscsiadm -m discovery -t sendtargets -p 10.0.0.1:3260

# iSCSI 로그인
iscsiadm -m node -T iqn.2024-01.com.example:storage -p 10.0.0.1 --login

# 세션 확인
iscsiadm -m session -P 3

# === 커널 네트워크 부팅 디버깅 ===
# 커널 커맨드 라인에 추가:
# ip=dhcp debug loglevel=7

# 시리얼 콘솔로 부팅 로그 확인:
# console=ttyS0,115200n8

캡처 포인트 선택 가이드

PXE 장애 분석에서 가장 흔한 실수는 잘못된 위치에서 패킷을 잡는 것입니다. DHCPDISCOVER가 안 보인다고 바로 서버 문제로 가면 안 되고, 먼저 어느 지점에서 패킷이 사라지는지 확인해야 합니다.

캡처 위치이 위치에서 알 수 있는 것추천 도구언제 우선 선택할지
클라이언트와 같은 access 포트의 미러/span클라이언트가 실제로 DHCPDISCOVER, ARP, TFTP RRQ를 내보내는지 알 수 있습니다.tcpdump, Wireshark아예 DHCP가 안 보이거나 Media test failure 직전 단계가 의심될 때
DHCP 서버 인터페이스DISCOVER가 서버까지 왔는지, OFFER가 나갔는지 즉시 구분할 수 있습니다.tcpdump, journalctlPXE-E51, relay, scope 오동작이 의심될 때
TFTP/HTTP 서버 인터페이스클라이언트가 실제로 파일 요청을 하는지, 요청 파일명이 무엇인지 알 수 있습니다.tcpdump, nginx/access log, TFTP 로그PXE-E32, PXE-E3B, 잘못된 filename이 의심될 때
스위치 uplink 또는 relay 구간VLAN 태그, relay 삽입, ACL, DHCP snooping/Option 82 영향을 볼 수 있습니다.스위치 포트 미러, 네트워크 탭클라이언트와 서버 양쪽에서 각각 정상인데 중간에서만 사라질 때
BMC KVM 또는 시리얼 콘솔펌웨어 메뉴, Secure Boot 무증상 실패, 커널 이후 rootfs 실패 로그를 볼 수 있습니다.iDRAC/iLO KVM, SOL, console=ttyS0패킷은 정상인데 화면상 부팅이 멈출 때
iPXE shell / UEFI ShellDNS, URL, 파일 접근, 인터페이스 상태를 클라이언트 측에서 직접 검증할 수 있습니다.dhcp, ping, imgfetch, ifconfig -l서버는 정상인데 특정 펌웨어나 특정 NIC만 실패할 때
빠른 판단 기준: 클라이언트 포트 미러에서 DHCPDISCOVER조차 안 보이면 물리 계층, NIC NVM, BIOS 정책을 먼저 보십시오. DISCOVER는 보이는데 서버에 안 오면 네트워크 인프라 문제이고, 서버까지 오는데 응답이 안 나가면 DHCP 정책 문제입니다. 응답까지 오고 TFTP/HTTP 요청이 끊기면 파일 경로, 방화벽, 아키텍처 분기 문제입니다.

PXE 패킷 분석 예제

# tcpdump 출력 예제: 정상적인 PXE 부팅 시퀀스

# 1. DHCPDISCOVER
12:00:00.001 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request
  Client-Ethernet-Address 00:11:22:33:44:55
  Vendor-Class: PXEClient:Arch:00000:UNDI:002001
  Parameter-Request: Subnet-Mask(1), Router(3), DNS(6), Domain(15),
    TFTP-Server(66), Boot-File(67), Vendor-Class(60), Client-Arch(93)

# 2. DHCPOFFER
12:00:00.005 IP 192.168.1.1.67 > 192.168.1.100.68: BOOTP/DHCP, Reply
  Your-IP: 192.168.1.100
  Server-IP: 192.168.1.10      ← TFTP 서버
  Boot-File: pxelinux.0        ← NBP 파일명

# 3. DHCPREQUEST + DHCPACK (생략)

# 4. TFTP RRQ
12:00:00.050 IP 192.168.1.100.2070 > 192.168.1.10.69: TFTP, RRQ "pxelinux.0"
  octet blksize 1468 tsize 0

# 5. TFTP OACK + DATA
12:00:00.051 IP 192.168.1.10.41234 > 192.168.1.100.2070: TFTP, OACK
  blksize=1468 tsize=42780
12:00:00.052 IP 192.168.1.100.2070 > 192.168.1.10.41234: TFTP, ACK block 0
12:00:00.053 IP 192.168.1.10.41234 > 192.168.1.100.2070: TFTP, DATA block 1
  ... (29 더 많은 DATA 블록) ...

# 6. PXELINUX가 설정 파일 요청
12:00:01.100 IP 192.168.1.100.2071 > 192.168.1.10.69: TFTP, RRQ
  "pxelinux.cfg/01-00-11-22-33-44-55"      ← MAC 기반 설정 시도
12:00:01.101 IP 192.168.1.10.69 > 192.168.1.100.2071: TFTP, ERROR
  code=1 File not found                     ← 없으면 다음 시도

12:00:01.102 IP 192.168.1.100.2072 > 192.168.1.10.69: TFTP, RRQ
  "pxelinux.cfg/C0A80164"                   ← IP 기반 시도
12:00:01.103 IP 192.168.1.10.69 > 192.168.1.100.2072: TFTP, ERROR
  code=1 File not found

12:00:01.104 IP 192.168.1.100.2073 > 192.168.1.10.69: TFTP, RRQ
  "pxelinux.cfg/default"                    ← 기본 설정
12:00:01.105 IP 192.168.1.10.41235 > 192.168.1.100.2073: TFTP, DATA block 1
  ... (설정 파일 전송) ...

# 7. 커널 + initrd 다운로드
12:00:02.000 IP 192.168.1.100.2074 > 192.168.1.10.69: TFTP, RRQ "vmlinuz"
  ... (대용량 전송) ...
12:00:10.000 IP 192.168.1.100.2075 > 192.168.1.10.69: TFTP, RRQ "initrd.img"
  ... (대용량 전송) ...

실전 구성 예제

최소 PXE 서버 구성 (5분 설정)

#!/bin/bash
# minimal-pxe-setup.sh - dnsmasq 기반 최소 PXE 서버 설정

set -e

PXE_IFACE="eth0"
PXE_IP="192.168.1.10"
PXE_RANGE="192.168.1.100,192.168.1.200"
TFTP_ROOT="/var/lib/tftpboot"

# 1. 패키지 설치
sudo apt update
sudo apt install -y dnsmasq pxelinux syslinux-common

# 2. TFTP 디렉토리 구성
sudo mkdir -p ${TFTP_ROOT}/pxelinux.cfg

# SYSLINUX 파일 복사
sudo cp /usr/lib/PXELINUX/pxelinux.0 ${TFTP_ROOT}/
sudo cp /usr/lib/syslinux/modules/bios/{ldlinux.c32,menu.c32,libutil.c32,vesamenu.c32,libcom32.c32} ${TFTP_ROOT}/

# 3. dnsmasq 설정
sudo tee /etc/dnsmasq.d/pxe.conf << EOF
# PXE 서버 설정
interface=${PXE_IFACE}
bind-interfaces

# DHCP
dhcp-range=${PXE_RANGE},255.255.255.0,12h
dhcp-option=option:router,${PXE_IP}
dhcp-option=option:dns-server,8.8.8.8

# TFTP
enable-tftp
tftp-root=${TFTP_ROOT}

# PXE
dhcp-boot=pxelinux.0

# 로깅
log-dhcp
log-facility=/var/log/dnsmasq-pxe.log
EOF

# 4. 기본 메뉴 설정
sudo tee ${TFTP_ROOT}/pxelinux.cfg/default << 'EOF'
DEFAULT menu.c32
PROMPT 0
TIMEOUT 300
MENU TITLE PXE Boot Menu

LABEL local
    MENU LABEL Boot from Local Disk
    MENU DEFAULT
    LOCALBOOT 0
EOF

# 5. 서비스 시작
sudo systemctl disable --now systemd-resolved 2>/dev/null || true
sudo systemctl enable --now dnsmasq

echo "PXE 서버 준비 완료!"
echo "커널/initrd를 ${TFTP_ROOT}/에 복사하고 pxelinux.cfg/default를 편집하세요."

전체 스택 구성 (DHCP + TFTP + HTTP + NFS)

#!/bin/bash
# full-pxe-stack.sh - 프로덕션급 PXE 서버 전체 스택

# === 1. dnsmasq (DHCP + TFTP + ProxyDHCP) ===
sudo tee /etc/dnsmasq.d/pxe-full.conf << 'EOF'
# DHCP
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h
dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,8.8.8.8,8.8.4.4

# TFTP
enable-tftp
tftp-root=/var/lib/tftpboot

# 아키텍처별 자동 분기
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi64,option:client-arch,7
dhcp-match=set:efiaa64,option:client-arch,11
dhcp-match=set:ipxe,175

# iPXE 체인로딩
dhcp-boot=tag:bios,tag:!ipxe,undionly.kpxe
dhcp-boot=tag:efi64,tag:!ipxe,ipxe.efi
dhcp-boot=tag:efiaa64,tag:!ipxe,ipxe-arm64.efi
dhcp-boot=tag:ipxe,http://192.168.1.10/boot.ipxe

log-dhcp
EOF

# === 2. nginx (HTTP 부팅 서버) ===
sudo tee /etc/nginx/sites-available/pxe << 'EOF'
server {
    listen 80;
    server_name pxe.example.com 192.168.1.10;
    root /var/www/pxeboot;
    autoindex on;

    sendfile on;
    tcp_nopush on;

    location /boot/ {
        alias /var/lib/tftpboot/;
    }
}
EOF
sudo ln -sf /etc/nginx/sites-available/pxe /etc/nginx/sites-enabled/
sudo systemctl reload nginx

# === 3. NFS 서버 ===
echo "/exports/diskless 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)" \
    | sudo tee -a /etc/exports
sudo exportfs -arv

# === 4. iPXE 메인 스크립트 ===
sudo tee /var/www/pxeboot/boot.ipxe << 'IPXE'
#!ipxe
set boot-url http://192.168.1.10
menu Network Boot Menu
item ubuntu    Ubuntu 24.04 LTS Install
item diskless  Diskless Linux
item local     Local Boot
choose --default local --timeout 30000 target && goto ${target} || goto local

:ubuntu
kernel ${boot-url}/images/ubuntu/vmlinuz ip=dhcp autoinstall ds=nocloud-net;s=${boot-url}/autoinstall/
initrd ${boot-url}/images/ubuntu/initrd
boot

:diskless
kernel ${boot-url}/vmlinuz-diskless root=/dev/nfs nfsroot=192.168.1.10:/exports/diskless,vers=4 ip=dhcp rw
initrd ${boot-url}/initrd-diskless.img
boot

:local
exit
IPXE

QEMU/KVM PXE 테스트 환경

QEMU/KVM을 사용하면 물리 서버 없이도 PXE 부팅을 테스트할 수 있습니다. QEMU에는 iPXE ROM이 내장되어 있어 별도 설치 없이 네트워크 부팅이 가능합니다.

# === 1. 기본 QEMU PXE 부팅 (가장 간단) ===
# QEMU의 내장 iPXE ROM으로 PXE 부팅
# -boot n: 네트워크 부팅 우선
qemu-system-x86_64 \
    -m 2048 \
    -boot n \
    -nic user,tftp=/var/lib/tftpboot,bootfile=pxelinux.0 \
    -nographic  # 시리얼 콘솔 모드

# === 2. 브리지 네트워크 + 실제 PXE 서버 ===
# 실제 네트워크의 DHCP/PXE 서버에 연결
sudo qemu-system-x86_64 \
    -m 4096 \
    -cpu host -enable-kvm \
    -boot order=n \
    -device virtio-net-pci,netdev=br0,mac=52:54:00:12:34:56 \
    -netdev bridge,id=br0,br=virbr0 \
    -drive file=test-disk.qcow2,format=qcow2,if=virtio \
    -vnc :1

# === 3. UEFI PXE 부팅 테스트 ===
# OVMF (Open Virtual Machine Firmware) 사용
sudo apt install ovmf
qemu-system-x86_64 \
    -m 4096 \
    -cpu host -enable-kvm \
    -bios /usr/share/OVMF/OVMF_CODE.fd \
    -boot order=n \
    -device virtio-net-pci,netdev=net0 \
    -netdev bridge,id=net0,br=virbr0 \
    -drive file=test-uefi.qcow2,format=qcow2,if=virtio

# === 4. iPXE ROM 커스텀 빌드로 교체 ===
# 커스텀 iPXE ROM을 QEMU에서 사용
cd ipxe/src
make bin/8086100e.rom EMBED=embed.ipxe  # Intel e1000 ROM
qemu-system-x86_64 \
    -m 2048 \
    -boot n \
    -device e1000,netdev=net0,romfile=bin/8086100e.rom \
    -netdev bridge,id=net0,br=virbr0

# === 5. 내부 DHCP 없이 테스트 (user 모드) ===
# QEMU user-mode 네트워킹으로 간단 테스트
qemu-system-x86_64 \
    -m 2048 \
    -boot n \
    -nic user,model=virtio-net-pci,\
tftp=/var/lib/tftpboot,\
bootfile=pxelinux.0,\
dhcpstart=10.0.2.100

# === 6. libvirt/virsh로 PXE VM 생성 ===
virt-install \
    --name pxe-test \
    --ram 4096 \
    --vcpus 2 \
    --disk size=20,format=qcow2 \
    --network bridge=virbr0,model=virtio \
    --pxe \
    --boot network,hd \
    --os-variant ubuntu24.04 \
    --graphics vnc

# === 7. 디버깅: QEMU 모니터에서 네트워크 확인 ===
# Ctrl+Alt+2 로 QEMU 모니터 진입
# (qemu) info network         # 네트워크 장치 목록
# (qemu) info pci             # PCI 장치 (NIC 확인)
# (qemu) hostfwd_add tcp::5555-:22  # SSH 포트 포워딩
QEMU 내장 iPXE 버전: QEMU에 내장된 iPXE ROM은 패키지 빌드 시점의 버전입니다. 최신 기능이 필요하면 romfile= 옵션으로 직접 빌드한 iPXE ROM을 사용하세요. virtio-net-pci 드라이버가 성능이 가장 좋고, iPXE도 virtio 드라이버를 내장하고 있습니다.

QEMU와 실장비의 차이

QEMU는 PXE 설정 자체를 검증하는 데는 매우 유용하지만, 실제 NIC 칩셋의 pre-boot 문제를 완전히 재현하지는 못합니다. 따라서 QEMU에서 성공했다고 해서 실장비의 PXE가 보장되지는 않습니다.

검증 항목QEMU/KVM실장비의미
DHCP/TFTP/HTTP 설정잘 재현됩니다.그대로 검증됩니다.부트 파일명, 경로, iPXE 스크립트 분기 검증에 가장 적합합니다.
PHY 링크 협상거의 재현되지 않습니다.케이블, 스위치, SFP, 자동 협상 문제가 모두 드러납니다.Media test failure, 강제 속도 불일치 같은 문제는 실장비에서만 보이는 경우가 많습니다.
NIC NVM / Option ROM내장 iPXE 또는 지정한 romfile만 사용합니다.칩셋별 NVM, BIOS flash, FlexBoot, iNVM이 실제로 관여합니다.I210, Broadcom MBA, ConnectX FlexBoot 문제는 QEMU만으로 판단하면 안 됩니다.
UEFI Secure BootOVMF로 기본 경로는 볼 수 있습니다.OEM db/dbx, 서명된 NIC ROM, 벤더 HII까지 영향받습니다.OVMF에서 되는데 서버에서만 실패하면 OEM 펌웨어 정책을 먼저 의심해야 합니다.
VLAN / 802.1X / DHCP Relay단순 NAT나 bridge 구성에서는 제한적으로만 재현됩니다.실제 access/trunk, NAC, relay 정책이 모두 작동합니다.네트워크 인프라 문제는 가상 환경보다 실장비 캡처가 더 중요합니다.
# 추천 검증 순서

# 1. QEMU user-mode
#    → 부트파일명, iPXE 스크립트, 커널 인자 기본 검증

# 2. QEMU + bridge + OVMF
#    → 실제 DHCP/HTTP/TFTP 서버와 UEFI 경로 검증

# 3. 실장비 + 시리얼 콘솔
#    → NIC NVM, 링크 협상, 스위치 정책, Secure Boot, BMC 영향 검증

# 4. 칩셋별 도구
#    → Intel: ethtool / eeupdate
#    → Broadcom: bnxtnvm / B57udiag
#    → Mellanox/NVIDIA: mlxconfig
#    → Realtek: BIOS 설정 + 필요 시 PG Tool

Preseed 자동 설치 예제 (Debian 13)

# /var/www/pxeboot/preseed/debian13.cfg
# Debian 13 Trixie 무인 설치 (Preseed)

# 로케일 및 키보드
d-i debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select us

# 네트워크
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string debian-server
d-i netcfg/get_domain string example.com

# 미러 (로컬 미러 사용 시)
d-i mirror/country string manual
d-i mirror/http/hostname string 192.168.1.10
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string

# 시간대
d-i time/zone string Asia/Seoul
d-i clock-setup/utc boolean true
d-i clock-setup/ntp boolean true

# 파티셔닝 (UEFI + GPT, 전체 디스크 LVM)
d-i partman-efi/non_efi_system boolean true
d-i partman-partitioning/choose_label select gpt
d-i partman-partitioning/default_label string gpt
d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

# 루트 비밀번호 (해시)
d-i passwd/root-password-crypted password $6$rounds=656000$...

# 사용자 계정
d-i passwd/user-fullname string Admin User
d-i passwd/username string admin
d-i passwd/user-password-crypted password $6$rounds=656000$...

# 패키지
d-i pkgsel/include string openssh-server vim tmux nfs-common open-iscsi
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false

# 부트로더 (GRUB)
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default

# 설치 완료 후
d-i finish-install/reboot_in_progress note
d-i preseed/late_command string \
    mkdir -p /target/root/.ssh; \
    wget -O /target/root/.ssh/authorized_keys http://192.168.1.10/keys/admin.pub; \
    chmod 600 /target/root/.ssh/authorized_keys; \
    in-target systemctl enable ssh
Debian Preseed 실무 메모: Debian stable은 2026-03-07 기준 13 (trixie)이며, 네트워크 프리시드는 여전히 preseed/url 또는 그 축약형 url=을 사용합니다. UEFI 서버를 기본 대상으로 둘 때는 partman-efi/non_efi_system과 GPT 라벨 선택을 같이 넣어 두는 편이 BIOS/UEFI 혼합 환경에서 덜 헷갈립니다.

Raspberry Pi 네트워크 부팅

# Raspberry Pi 4는 UEFI가 아닌 자체 네트워크 부팅 메커니즘 사용
# BCM2711 SoC의 부트 ROM이 직접 DHCP + TFTP 수행

# 1. Raspberry Pi의 시리얼 번호 확인
# Pi에서: cat /proc/cpuinfo | grep Serial
# 예: Serial : 00000000abcd1234 → 마지막 8자리: abcd1234

# 2. TFTP 디렉토리 구조 (시리얼 번호 기반)
/var/lib/tftpboot/
├── abcd1234/           # Pi 시리얼 번호
│   ├── start4.elf      # GPU 펌웨어
│   ├── fixup4.dat      # GPU 메모리 수정
│   ├── bcm2711-rpi-4-b.dtb
│   ├── config.txt      # 부팅 설정
│   ├── cmdline.txt     # 커널 커맨드 라인
│   └── kernel8.img     # ARM64 커널
└── overlays/           # DT 오버레이

# 3. config.txt (네트워크 루트)
cat > /var/lib/tftpboot/abcd1234/config.txt << 'EOF'
arm_64bit=1
kernel=kernel8.img
initramfs initrd.img followkernel
EOF

# 4. cmdline.txt
echo "console=serial0,115200 console=tty1 root=/dev/nfs \
nfsroot=192.168.1.10:/exports/pi-root,vers=4,tcp ip=dhcp rw \
rootwait" > /var/lib/tftpboot/abcd1234/cmdline.txt

# 5. dnsmasq 설정 (Pi 전용)
# Pi는 Vendor Class를 보내지 않으므로, MAC 또는 태그로 분기
dhcp-host=dc:a6:32:xx:xx:xx,set:rpi
dhcp-boot=tag:rpi,abcd1234/start4.elf
pxe-service=0,"Raspberry Pi Boot"

Windows WDS/MDT 참고

Windows 배포 서비스 (WDS)

Windows Deployment Services(WDS)는 Microsoft의 PXE 기반 OS 배포 솔루션입니다. 리눅스 PXE 환경과의 호환성을 이해하는 것이 중요합니다.

항목Linux PXEWindows WDS
DHCPISC DHCP / dnsmasqWindows DHCP Server
TFTPtftpd-hpa / dnsmasqWDS TFTP 내장
BIOS NBPpxelinux.0wdsnbp.com → pxeboot.com
UEFI NBPgrubx64.efiwdsmgfw.efi → bootmgfw.efi
이미지 형식vmlinuz + initrdWIM (Windows Imaging)
자동 설치Kickstart/Preseedunattend.xml
멀티캐스트atftpd/UDPcastWDS 멀티캐스트 내장

Linux + Windows 듀얼 PXE 환경

# iPXE 스크립트로 Linux/Windows 선택
#!ipxe

menu Dual Boot PXE Menu
item linux    Linux Installation
item windows  Windows Installation (WDS)
choose --default linux --timeout 20000 target && goto ${target}

:linux
chain http://192.168.1.10/linux-boot.ipxe

:windows
# WDS 서버로 체인로딩
# iPXE가 WDS의 wdsnbp.com을 로드하여 Windows 설치 시작
set next-server 192.168.1.20  # WDS 서버
chain tftp://${next-server}/boot/x64/wdsnbp.com

리눅스 커널 소스 분석

네트워크 부팅 관련 커널 소스 파일

파일역할
net/ipv4/ipconfig.c커널 IP 자동 설정 (DHCP/BOOTP/RARP). ip= 파라미터 처리
fs/nfs/nfsroot.cNFS 루트 마운트(Mount). nfsroot= 파라미터 처리
init/do_mounts.c루트 파일시스템 마운트 (root=/dev/nfs 감지)
drivers/firmware/efi/libstub/EFI Stub: UEFI에서 직접 커널 부팅
drivers/net/ethernet/이더넷 드라이버 (부팅 시 네트워크 초기화)
include/linux/nfs_fs.hNFS 클라이언트 헤더
drivers/scsi/iscsi_tcp.ciSCSI TCP 트랜스포트
drivers/infiniband/ulp/iser/iSCSI over RDMA (iSER)

네트워크 부팅 관련 커널 설정

# make menuconfig에서 네트워크 부팅에 필요한 옵션

# Networking → Networking options
CONFIG_IP_PNP=y              # IP: kernel level autoconfiguration
CONFIG_IP_PNP_DHCP=y         # IP: DHCP support
CONFIG_IP_PNP_BOOTP=y        # IP: BOOTP support
CONFIG_IP_PNP_RARP=y         # IP: RARP support

# File systems → Network File Systems
CONFIG_NFS_FS=y              # NFS client support (built-in, NOT module!)
CONFIG_NFS_V3=y              # NFS client support for NFS version 3
CONFIG_NFS_V4=y              # NFS client support for NFS version 4
CONFIG_ROOT_NFS=y            # Root file system on NFS

# Block devices → Network block devices
CONFIG_BLK_DEV_NBD=m         # Network Block Device
CONFIG_ISCSI_TCP=m           # iSCSI Initiator over TCP/IP

# 네트워크 드라이버 (부팅에 사용하는 NIC는 built-in 필수!)
CONFIG_E1000E=y              # Intel(R) PRO/1000 PCI-Express
CONFIG_IGB=y                 # Intel(R) 82575/82576 PCI-Express
CONFIG_IXGBE=y               # Intel(R) 10GbE PCI Express
CONFIG_MLX5_CORE=y           # Mellanox ConnectX
CONFIG_R8169=y               # Realtek RTL8169
CONFIG_BNX2X=y               # Broadcom NetXtreme II 10Gb

# initramfs 사용 시 NFS/iSCSI를 모듈로 컴파일해도 됨
# 단, initramfs에 해당 모듈을 포함해야 함

커널 IP 자동 설정 흐름

/* net/ipv4/ipconfig.c - 커널 네트워크 자동 설정 전체 흐름 */

/*
 * 호출 순서:
 * start_kernel()
 *   → kernel_init()
 *     → kernel_init_freeable()
 *       → do_basic_setup()
 *       → prepare_namespace()
 *         → ip_auto_config()      ← ip= 파라미터 처리
 *         → mount_root()           ← root= 파라미터 처리
 *           → nfs_root_setup()     ← root=/dev/nfs 시
 */

static int __init ip_auto_config(void)
{
    /* 1. 네트워크 인터페이스 탐색 */
    ic_open_devs();  /* 모든 이더넷 디바이스 열기 */

    /* 2. DHCP/BOOTP/RARP 중 활성화된 프로토콜로 IP 설정 */
    if (ic_enable) {
        /* DHCP 패킷 송신 */
        ic_bootp_init();

        /* 응답 대기 (최대 ~60초) */
        ic_dynamic();

        /* IP, 게이트웨이, DNS, NFS root path 등 설정 */
        ic_setup_if();     /* 인터페이스 IP 설정 */
        ic_setup_routes(); /* 라우팅 테이블 설정 */
    }

    /* 3. 결과 출력 */
    pr_info("IP-Config: Complete:\n");
    pr_info("     device=%s, hwaddr=%pM, ipaddr=%pI4, "
            "mask=%pI4, gw=%pI4\n",
            ic_dev->name, ic_dev->dev_addr,
            &ic_myaddr, &ic_netmask, &ic_gateway);
    pr_info("     host=%s, domain=%s, nis-domain=%s\n",
            utsname()->nodename, ic_domain, utsname()->domainname);
    pr_info("     bootserver=%pI4, rootserver=%pI4, rootpath=%s\n",
            &ic_servaddr, &root_server_addr, root_server_path);

    return 0;
}

클라우드 & 베어메탈 PXE 패턴

클라우드 환경의 PXE 부팅

퍼블릭 클라우드의 베어메탈 서비스와 프라이빗 클라우드 인프라에서 PXE는 서버 프로비저닝의 핵심 메커니즘입니다.

플랫폼PXE 방식자동화특징
Equinix MetaliPXE + Custom OSAPI + iPXE 스크립트사용자 iPXE 스크립트 URL 지정 가능. 커스텀 OS 부팅 지원
AWS OutpostsPXE + EC2 이미지SSM + CloudFormation온프레미스 AWS 인프라에서 PXE 부팅
Oracle BMiPXEOCI API베어메탈 인스턴스에 iPXE 스크립트 제공
HetznerDHCP + iPXERobot APIrescue 시스템 + installimage 스크립트
OpenStack IronicPXE/iPXE + IPANova APIIronic Python Agent로 디스크 기록
MAAS (Canonical)PXE + cloud-initREST API + CLI자동 발견 → commissioning → 배포
Tinkerbell (Equinix)iPXE + WorkflowgRPC API클라우드 네이티브, Kubernetes 기반

클라우드 베어메탈 iPXE 커스텀 부팅 예제

# Equinix Metal 스타일: 사용자 제공 iPXE 스크립트로 커스텀 OS 부팅
#!ipxe

# 서버의 메타데이터 조회 (클라우드 API에서 제공)
# Equinix Metal은 metadata.packet.net 제공
# chain https://metadata.packet.net/ipxe?uuid=${uuid}&mac=${mac}

# 또는 직접 스크립트 작성:
set base-url https://my-pxe-server.example.com

# 호스트 메타데이터 기반 분기
chain ${base-url}/api/v1/boot?uuid=${uuid}&serial=${serial}&mac=${mac:hexhyp} || goto fallback

:fallback
# Flatcar Container Linux 부팅 (CoreOS 후속)
set coreos-url https://stable.release.flatcar-linux.net/amd64-usr/current
kernel ${coreos-url}/flatcar_production_pxe.vmlinuz \
    flatcar.first_boot=1 \
    flatcar.config.url=${base-url}/ignition/${uuid}.json \
    ip=dhcp
initrd ${coreos-url}/flatcar_production_pxe_image.cpio.gz
boot
// Ignition 설정 예제 (Flatcar/CoreOS 자동 프로비저닝)
// /var/www/pxeboot/ignition/default.json
{
  "ignition": { "version": "3.4.0" },
  "storage": {
    "filesystems": [{
      "device": "/dev/sda1",
      "format": "ext4",
      "path": "/",
      "wipeFilesystem": true
    }]
  },
  "systemd": {
    "units": [{
      "name": "docker.service",
      "enabled": true
    }]
  },
  "passwd": {
    "users": [{
      "name": "core",
      "sshAuthorizedKeys": ["ssh-ed25519 AAAA... admin@example.com"],
      "groups": ["docker", "sudo"]
    }]
  }
}

OpenStack Ironic PXE 배포 흐름

OpenStack Ironic 베어메탈 프로비저닝 (PXE) 1. Enroll IPMI/Redfish 정보 등록 MAC, BMC IP ironic node-create 2. Inspect IPA PXE 부팅 H/W 정보 수집 CPU/RAM/Disk 3. Clean IPA PXE 부팅 디스크 초기화 BIOS 설정 4. Deploy IPA PXE 부팅 이미지 기록 configdrive 적용 5. Active 로컬 디스크 부팅 서비스 가동 Nova 관리 하에 IPA (Ironic Python Agent) — PXE로 부팅되는 경량 에이전트 - 커널 + initramfs 이미지 (약 200-400MB). 자체 Python 런타임 + ironic_python_agent 패키지 포함 - REST API 서버로 동작: Ironic Conductor가 HTTP로 명령 전달 - 기능: 하드웨어 인벤토리, 디스크 초기화(shred/hdparm), 이미지 기록(dd/qemu-img), RAID 설정, BIOS 설정 - 배포 이미지: QCOW2/RAW → IPA가 다운로드 → /dev/sda에 기록 → 부트로더 설치 - PXE 설정: Ironic이 자동으로 DHCP/TFTP를 관리 (iPXE 또는 PXELINUX/GRUB 사용)
# OpenStack Ironic 베어메탈 노드 등록 및 배포

# 1. 노드 등록
openstack baremetal node create \
    --driver ipmi \
    --driver-info ipmi_address=10.0.0.100 \
    --driver-info ipmi_username=admin \
    --driver-info ipmi_password=secret \
    --driver-info deploy_kernel=file:///var/lib/ironic/ipa-kernel \
    --driver-info deploy_ramdisk=file:///var/lib/ironic/ipa-ramdisk \
    --property cpus=32 --property memory_mb=65536 \
    --property local_gb=500

# 2. 포트(NIC) 등록
openstack baremetal port create \
    --node <node-uuid> \
    00:11:22:33:44:55  # PXE 부팅용 MAC 주소

# 3. 이미지 기반 배포
openstack baremetal node deploy <node-uuid> \
    --instance-info image_source=<glance-image-uuid> \
    --instance-info root_gb=50

# Ironic이 자동으로:
# 1) IPMI로 서버 전원 ON
# 2) PXE 부팅 → IPA 로드
# 3) IPA가 이미지 다운로드 → 디스크 기록
# 4) PXE에서 로컬 부팅으로 전환
# 5) 서버 재부팅 → OS 가동

흔한 실수

실수 1: 아키텍처 불일치
BIOS 모드 클라이언트에 EFI 바이너리(.efi)를, UEFI 클라이언트에 BIOS 바이너리(.0)를 제공하면 부팅 실패합니다. DHCP Option 93을 확인하여 올바른 바이너리를 제공하세요.
실수 2: TFTP 경로 문제
TFTP는 tftp-root를 기준으로 경로를 해석합니다. 절대 경로(/var/lib/tftpboot/pxelinux.0)가 아니라 상대 경로(pxelinux.0)를 사용해야 합니다.
실수 3: 방화벽 미개방
PXE에 필요한 포트: UDP 67/68(DHCP), UDP 69(TFTP), UDP 4011(ProxyDHCP), TCP 2049(NFS v4), TCP 3260(iSCSI), TCP 80/443(HTTP/HTTPS). 특히 TFTP는 데이터 전송 시 랜덤 고번호 포트를 사용하므로 conntrack 모듈이 필요합니다: modprobe nf_conntrack_tftp
실수 4: NFS root에서 모듈 누락
NFS 루트 부팅 시 NIC 드라이버와 NFS 클라이언트가 커널에 built-in(=y)이거나, initramfs에 모듈이 포함되어야 합니다. 모듈(=m)만 있고 initramfs에 없으면 네트워크를 활성화할 수 없어 루트를 마운트할 수 없습니다.
실수 5: iPXE 무한 루프
iPXE 체인로딩 설정에서 iPXE와 일반 PXE를 구분하지 않으면 무한 루프가 발생합니다. ISC dhcpd 예제는 보통 user-class = "iPXE"를, dnsmasq 예제는 Option 175를 자주 사용합니다. 어느 방식을 쓰든 "이미 iPXE에 들어온 클라이언트"를 한 번만 식별해 HTTP 스크립트로 넘겨야 합니다.
실수 6: SYSLINUX 모듈 누락
PXELINUX를 사용할 때 ldlinux.c32, libutil.c32, libcom32.c32 등 필수 COM32 모듈을 TFTP 루트에 복사하지 않으면 메뉴가 표시되지 않습니다. SYSLINUX 버전과 모듈 버전이 일치해야 합니다.

표준 및 공식 문서

PXE는 구현 편차가 큰 분야라서 블로그 글보다 표준 문서와 공식 구현 문서를 같이 보는 편이 안전합니다. 아래 문서들은 이 페이지(Page)를 보강하면서 직접 대조한 기준 자료입니다.

이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.