From a282e231979b1721ffd95ed5d33676895646f547 Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 10 Mar 2001 06:12:25 +0000 Subject: [PATCH] More robust DHCP parsing; document "gethostip" program. --- Makefile | 11 ++- NEWS | 3 + pxelinux.asm | 254 +++++++++++++++++++++++++++++++++++++++++------------------ pxelinux.doc | 4 +- 4 files changed, 190 insertions(+), 82 deletions(-) diff --git a/Makefile b/Makefile index cc2ee74..ebab1f8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ ## ----------------------------------------------------------------------- ## $Id$ ## -## Copyright 1998 H. Peter Anvin - All Rights Reserved +## Copyright 1998-2001 H. Peter Anvin - All Rights Reserved ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -35,12 +35,15 @@ VERSION = $(shell cat version) SOURCES = ldlinux.asm syslinux.asm syslinux.c copybs.asm \ pxelinux.asm pxe.inc mbr.asm BTARGET = bootsect.bin ldlinux.sys ldlinux.bin ldlinux.lst pxelinux.0 mbr.bin -ITARGET = syslinux.com syslinux copybs.com +ITARGET = syslinux.com syslinux copybs.com gethostip DOCS = COPYING NEWS README TODO *.doc OTHER = Makefile bin2c.pl now.pl genstupid.pl keytab-lilo.pl version \ sys2ansi.pl OBSOLETE = pxelinux.bin +# Things to install in /usr/bin +INSTALL_BIN = syslinux gethostip + all: $(BTARGET) $(ITARGET) ls -l $(BTARGET) $(ITARGET) @@ -100,8 +103,10 @@ stupid.inc: stupid.c stupid.o: stupid.c +gethostip: gethostip.o + install: all - install -c syslinux $(BINDIR) + install -c $(INSTALL_BIN) $(BINDIR) tidy: rm -f syslinux.lst copybs.lst *.o *_bin.c stupid.* pxelinux.lst diff --git a/NEWS b/NEWS index a1659fd..dad1007 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,9 @@ Changes in 1.53: setup still needs to be written. * PXELINUX: Support new "localboot" option for "label" sections. + * PXELINUX: More robust parsing of DHCP/boot server packets. + * PXELINUX: Include a small utility program "gethostip" to + compute hexadecimal IP addresses. Changes in 1.52: * PXELINUX: Fix bugs introduced by new A20 code. (SYSLINUX diff --git a/pxelinux.asm b/pxelinux.asm index 72dbd60..936d051 100644 --- a/pxelinux.asm +++ b/pxelinux.asm @@ -314,14 +314,14 @@ VKernelBuf: resb vk_size ; "Current" vkernel alignb 4 AppendBuf resb max_cmd_len+1 ; append= KbdMap resb 256 ; Keyboard map -PathPrefix resb 128 ; 128 bytes (comes from BOOTP size) +BootFile resb 256 ; Boot file from DHCP packet +PathPrefix resb 256 ; Path prefix derived from the above FKeyName resb 10*FILENAME_MAX ; File names for F-key help NumBuf resb 15 ; Buffer to load number NumBufEnd resb 1 ; Last byte in NumBuf DotQuadBuf resb 16 ; Buffer for dotted-quad IP address IPOption resb 80 ; ip= option buffer alignb 32 -BootFile resb 128 ; Boot file name from DHCP query KernelName resb FILENAME_MAX ; Mangled name for kernel KernelCName resb FILENAME_MAX ; Unmangled kernel name InitRDCName resb FILENAME_MAX ; Unmangled initrd name @@ -335,8 +335,6 @@ HighMemSize resd 1 ; End of memory pointer (bytes) KernelSize resd 1 ; Size of kernel (bytes) Stack resd 1 ; Pointer to reset stack PXEEntry resd 1 ; !PXE API entry point -MyIP resd 1 ; My IP address -ServerIP resd 1 ; IP address of boot server SavedSSSP resw 1 ; Our SS:SP while running a COMBOOT image FBytes equ $ ; Used by open/getc FBytes1 resw 1 @@ -353,7 +351,6 @@ NextCharJump resw 1 ; Routine to interpret next print char SetupSecs resw 1 ; Number of setup sectors A20Test resw 1 ; Counter for testing status of A20 CmdLineLen resw 1 ; Length of command line including null -ServerPort resw 1 ; TFTP server port ConfigFile resw 1 ; Socket for config file PktTimeout resw 1 ; Timeout for current packet KernelExtPtr resw 1 ; During search, final null pointer @@ -373,6 +370,7 @@ KbdFlags resb 1 ; Check for keyboard escapes LoadFlags resb 1 ; Loadflags from kernel A20Tries resb 1 ; Times until giving up on A20 FuncFlag resb 1 ; == 1 if pressed +OverLoad resb 1 ; Set if DHCP packet uses "overloading" alignb tftp_port_t_size Sockets resb MAX_SOCKETS*tftp_port_t_size @@ -532,14 +530,15 @@ have_entrypoint: query_bootp: mov ax,ds mov es,ax - mov di,pxe_bootp_query_pkt + mov di,pxe_bootp_query_pkt_2 mov bx,PXENV_GET_CACHED_INFO call far [PXENVEntry] + push word [pxe_bootp_query_pkt_2.status] jc .pxe_err1 cmp ax,byte 0 je .pxe_ok -.pxe_err1: +.pxe_err1: mov di,pxe_bootp_size_query_pkt mov bx,PXENV_GET_CACHED_INFO @@ -556,49 +555,44 @@ query_bootp: call writehex4 mov al, ' ' call writechr - mov ax,[pxe_bootp_query_pkt.status] + pop ax ; Status call writehex4 call crlf + jmp kaboom ; We're dead .pxe_ok: - mov eax,[trackbuf+bootp.yip] ; "Your" IP address - mov [MyIP],eax - call genipopt + pop cx ; Forget status + mov cx,[pxe_bootp_query_pkt_2.buffersize] + call parse_dhcp ; Parse DHCP packet ; ; Now, get the boot file and other info. This lives in the CACHED_REPLY ; packet (query info 3). ; - mov [pxe_bootp_query_pkt.packettype], byte 3 mov [pxe_bootp_size_query_pkt.packettype], byte 3 - mov di,pxe_bootp_query_pkt + mov di,pxe_bootp_query_pkt_3 mov bx,PXENV_GET_CACHED_INFO call far [PXENVEntry] + push word [pxe_bootp_query_pkt_3.status] jc .pxe_err1 cmp ax,byte 0 jne .pxe_err1 - mov si,trackbuf+bootp.bootfile - mov di,BootFile - mov cx,128 >> 2 - rep movsd ; Copy bootfile name + ; Packet loaded OK... + pop cx ; Forget status + mov cx,[pxe_bootp_query_pkt_3.buffersize] + call parse_dhcp ; Parse DHCP packet +; +; Generate ip= option +; + call genipopt ; -; If packet 2 didn't contain a valid IP address, guess that it's in this -; packet instead +; Print IP address ; mov eax,[MyIP] - cmp eax, byte 0 ; 0.0.0.0 bad - je .badip - cmp al,224 ; 224..255.x.x.x - jb .goodip -.badip: - mov eax,[trackbuf+bootp.yip] ; Hope this is better... - mov [MyIP],eax - call genipopt -.goodip: mov di,DotQuadBuf push di call gendotquad ; This takes network byte order input @@ -607,9 +601,6 @@ query_bootp: ror eax,16 ; (BSWAP doesn't work on 386) xchg ah,al -; -; Print IP address -; mov si,myipaddr_msg call writestr call writehex8 @@ -622,12 +613,6 @@ query_bootp: mov si,IPOption ; *** call writestr ; *** call crlf ; *** -; -; Save away the server IP and port number -; - mov eax,[trackbuf+bootp.sip] - mov [ServerIP],eax - mov [ServerPort], word TFTP_PORT ; ; Initialize UDP stack @@ -3943,6 +3928,138 @@ gendotquad: ret ; +; parse_dhcp +; +; Parse a DHCP packet. This includes dealing with "overloaded" +; option fields (see RFC 2132, section 9.3) +; +; This should fill in the following global variables, if the +; information is present: +; +; MyIP - client IP address +; ServerIP - boot server IP address +; Netmask - network mask +; Gateway - default gateway router IP +; BootFile - boot file name +; +; This assumes the DHCP packet is in "trackbuf" and the length +; of the packet in in CX on entry. +; + +parse_dhcp: + mov byte [OverLoad],0 ; Assume no overload + mov eax, [trackbuf+bootp.yip] + and eax, eax + jz .noyip + cmp al,224 ; Class D or higher -> bad + jae .noyip + mov [MyIP], eax +.noyip: + mov eax, [trackbuf+bootp.sip] + and eax, eax + jz .nosip + cmp al,224 ; Class D or higher -> bad + jae .nosip + mov [ServerIP], eax +.nosip: + sub cx, bootp.options + jbe .nooptions + mov si, trackbuf+bootp.option_magic + lodsd + cmp eax, BOOTP_OPTION_MAGIC + jne .nooptions + call parse_dhcp_options +.nooptions: + mov si, trackbuf+bootp.bootfile + test byte [OverLoad],1 + jz .nofileoverload + mov cx,128 + call parse_dhcp_options + jmp short .parsed_file +.nofileoverload: + cmp byte [si], 0 + jz .parsed_file ; No bootfile name + mov di,BootFile + mov cx,32 + rep movsd + xor al,al + stosb ; Null-terminate +.parsed_file: + mov si, trackbuf+bootp.sname + test byte [OverLoad],2 + jz .nosnameoverload + mov cx,64 + call parse_dhcp_options +.nosnameoverload: + ret + +; +; Parse a sequence of DHCP options, pointed to by DS:SI; the field +; size is CX -- some DHCP servers leave option fields unterminated +; in violation of the spec. +; +parse_dhcp_options: +.loop: + and cx,cx + jz .done + + lodsb + dec cx + jz .done ; Last byte; must be PAD, END or malformed + cmp al, 0 ; PAD option + je .loop + cmp al,255 ; END option + je .done + + ; Anything else will have a length field + mov dl,al ; DL <- option number + xor ax,ax + lodsb ; AX <- option length + dec cx + sub cx,ax ; Decrement bytes left counter + jb .done ; Malformed option: length > field size + + cmp dl,1 ; SUBNET MASK option + jne .not_subnet + mov edx,[si] + mov [Netmask],edx + jmp short .opt_done + +.not_subnet: + cmp dl,3 ; ROUTER option + jne .not_router + mov edx,[si] + mov [Gateway],edx + jmp short .opt_done + +.not_router: + cmp dl,52 ; OPTION OVERLOAD option + jne .not_overload + mov dl,[si] + mov [OverLoad],dl + jmp short .opt_done + +.not_overload: + mov dl,67 ; BOOTFILE NAME option + jne .not_bootfile + push cx + mov di,BootFile + mov cx,ax + rep movsb + mov byte [di],0 ; Null-terminate + pop cx + jmp short .opt_done_noskip + +.not_bootfile: + ; Unknown option. Skip to the next one. +.opt_done: + add si,ax +.opt_done_noskip: + jmp short .loop +.done: + ret + +; ; genipopt ; ; Generate an ip=::: @@ -3955,54 +4072,20 @@ genipopt: mov eax,'ip=' stosd dec di - mov eax,[trackbuf+bootp.yip] + mov eax,[MyIP] call gendotquad mov al,':' stosb - mov eax,[trackbuf+bootp.sip] + mov eax,[ServerIP] call gendotquad mov al,':' stosb - - xor ebx,ebx ; Unknown netmask - xor edx,edx ; Unknown router - - mov si,trackbuf+bootp.option_magic - lodsd - cmp eax,BOOTP_OPTION_MAGIC - jne .parse_done ; Unknown option format -.parseopt: - lodsb - cmp al,0 ; PAD option - je .parseopt - cmp al,255 ; END option - je .parse_done - cmp al,1 ; SUBNET MASK option - jne .not_subnet - inc si ; Skip length (always 4) - lodsd ; Read subnet mask - xchg ebx,eax ; Netmask: save in EBX - jmp short .parseopt -.not_subnet: - cmp al,3 ; ROUTER option - jne .not_router - inc si ; Skip length (always 4) - lodsd ; Read gateway address - xchg edx,eax ; Router: save in EDX - jmp short .parseopt -.not_router: - ; Unknown option. Read the length and skip ahead. - xor ah,ah - lodsb - add si,ax - jmp short .parseopt -.parse_done: - mov eax,edx ; Router + mov eax,[Netmask] call gendotquad mov al,':' stosb - mov eax,ebx ; Netmask - call gendotquad ; Zero-terminates output! + mov eax,[Gateway] + call gendotquad ; Zero-terminates its output sub di,IPOption mov [IPOptionLen],di popad @@ -4140,13 +4223,20 @@ PXENVEntry dw pxe_thunk,0 ; ; PXE query packets partially filled in ; -pxe_bootp_query_pkt: +pxe_bootp_query_pkt_2: .status: dw 0 ; Status .packettype: dw 2 ; DHCPACK packet .buffersize: dw trackbufsize ; Packet size .buffer: dw trackbuf, 0 ; seg:off of buffer .bufferlimit: dw trackbufsize ; Unused +pxe_bootp_query_pkt_3: +.status: dw 0 ; Status +.packettype: dw 3 ; Boot server packet +.buffersize: dw trackbufsize ; Packet size +.buffer: dw trackbuf, 0 ; seg:off of buffer +.bufferlimit: dw trackbufsize ; Unused + pxe_bootp_size_query_pkt: .status: dw 0 ; Status .packettype: dw 2 ; DHCPACK packet @@ -4215,10 +4305,18 @@ tftp_opt_err dw TFTP_ERROR ; ERROR packet db 'tsize option required', 0 ; Error message tftp_opt_err_len equ ($-tftp_opt_err) - alignb 2 + alignb 4, db 0 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet ; +; IP information (initialized to "unknown" values) +MyIP dd 0 ; My IP address +ServerIP dd 0 ; IP address of boot server +Netmask dd 0 ; Netmask of this subnet +Gateway dd 0 ; Default router +ServerPort dw TFTP_PORT ; TFTP server port + +; ; Variables that are uninitialized in SYSLINUX but initialized here ; ClustSize dw TFTP_BLOCKSIZE ; Bytes/cluster diff --git a/pxelinux.doc b/pxelinux.doc index 3ba2b60..dfa3c92 100644 --- a/pxelinux.doc +++ b/pxelinux.doc @@ -41,7 +41,9 @@ depends on the IP address of the booting machine. PXELINUX will search for its config file on the boot server in the following way: First, it will search for the config file using its own IP address - in upper case hexadecimal, e.g. 192.0.2.91 -> C000025B. + in upper case hexadecimal, e.g. 192.0.2.91 -> C000025B + (you can use the included progam "gethostip" to compute the + hexadecimal IP address for any host.) If that file is not found, it will remove one hex digit and try again. Ultimately, it will try looking for a file named "default" -- 2.7.4