mirror of
https://github.com/eblot/pybootd.git
synced 2024-08-17 18:00:33 +03:00
Initial support for architecture-based bootfile selection
This commit is contained in:
parent
867639db90
commit
352f2c16f5
@ -9,7 +9,6 @@ pool_start = 192.168.25.100
|
||||
pool_count = 5
|
||||
domain = localdomain
|
||||
server_name = debug
|
||||
boot_file = pxelinux.0
|
||||
lease_time = 86400
|
||||
access = mac
|
||||
allow_simple_dhcp = enable
|
||||
@ -18,6 +17,17 @@ set_gateway = true
|
||||
; use "nc -l -u 127.0.0.1 -p 12345" to debug
|
||||
; notify = 192.168.26.201:12345;192.168.26.200:12345
|
||||
|
||||
[bootfile]
|
||||
; BIOS boot file
|
||||
default = pxelinux.0
|
||||
; EFI Intel x86_84
|
||||
00007 = shimx64.efi
|
||||
|
||||
[buggy_clients]
|
||||
; list of stupid clients (mostly Intel's) that ignores valid subnet broadcast
|
||||
; and require global DHCP offer broadcast
|
||||
1C-69-7A-00-6F-4C = yes
|
||||
|
||||
[mac]
|
||||
; see doc: byte separator should be defined with dash, not column
|
||||
00-AA-55-12-34-56 = enable
|
||||
|
@ -133,7 +133,7 @@ DHCP_OPTIONS = {0: 'Byte padding',
|
||||
57: 'Message length',
|
||||
58: 'Renewal time',
|
||||
59: 'Rebinding time',
|
||||
60: 'Class ID',
|
||||
60: 'Vendor class identifier',
|
||||
61: 'GUID',
|
||||
64: 'Network Information Service+ domain',
|
||||
65: 'Network Information Service+ servers',
|
||||
@ -200,7 +200,8 @@ class BootpServer:
|
||||
(ST_IDLE, ST_PXE, ST_DHCP) = range(3) # Current state
|
||||
|
||||
BOOTP_SECTION = 'bootpd'
|
||||
BUGGY_CLIENTS_SECTION = 'buggy_clients'
|
||||
BOOT_FILE_SECTION = 'bootfile'
|
||||
BUGGY_CLIENT_SECTION = 'buggy_clients'
|
||||
|
||||
def __init__(self, logger, config):
|
||||
self.sock = []
|
||||
@ -252,10 +253,19 @@ class BootpServer:
|
||||
self.acl[entry.upper().replace('-', ':')] = \
|
||||
to_bool(self.config.get(access, entry))
|
||||
self.buggy_clients = set()
|
||||
if self.config.options(self.BUGGY_CLIENTS_SECTION):
|
||||
for entry in self.config.options(self.BUGGY_CLIENTS_SECTION):
|
||||
if to_bool(self.config.get(self.BUGGY_CLIENTS_SECTION, entry)):
|
||||
if self.config.options(self.BUGGY_CLIENT_SECTION):
|
||||
for entry in self.config.options(self.BUGGY_CLIENT_SECTION):
|
||||
if to_bool(self.config.get(self.BUGGY_CLIENT_SECTION, entry)):
|
||||
self.buggy_clients.add(entry.upper().replace('-', ':'))
|
||||
self.boot_files = dict()
|
||||
if not self.config.options(self.BOOT_FILE_SECTION):
|
||||
raise BootpError("Mising '%s' section" % self.BOOT_FILE_SECTION)
|
||||
for entry in self.config.options(self.BOOT_FILE_SECTION):
|
||||
self.boot_files[entry] = self.config.get(self.BOOT_FILE_SECTION,
|
||||
entry)
|
||||
if 'default' not in self.boot_files:
|
||||
raise BootpError("'%s' section should contain at least the default"
|
||||
"boot file")
|
||||
# pre-fill ippool if specified
|
||||
if self.config.has_section('static_dhcp'):
|
||||
for mac_str, ip_str in config.items('static_dhcp'):
|
||||
@ -316,7 +326,7 @@ class BootpServer:
|
||||
iface = self.find_interface(self.config.get(self.BOOTP_SECTION,
|
||||
'pool_start'))
|
||||
if not iface:
|
||||
raise RuntimeError('Unable to retrieve binding interface')
|
||||
raise BootpError('Unable to retrieve binding interface')
|
||||
if platform == 'linux':
|
||||
from socket import SO_BINDTODEVICE
|
||||
sock.setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, iface)
|
||||
@ -324,8 +334,8 @@ class BootpServer:
|
||||
IP_BOUND_IF = 25
|
||||
sock.setsockopt(IPPROTO_IP, IP_BOUND_IF, if_nametoindex(iface))
|
||||
else:
|
||||
raise RuntimeError('Bind to interface not supported on %s' %
|
||||
platform)
|
||||
raise BootpError('Bind to interface not supported on %s' %
|
||||
platform)
|
||||
self.sock.append(sock)
|
||||
self.log.info('Listening to %s:%s' % (host, port))
|
||||
sock.bind((host, int(port)))
|
||||
@ -576,10 +586,31 @@ class BootpServer:
|
||||
for opt in options[55]:
|
||||
try:
|
||||
parameter = DHCP_OPTIONS[opt]
|
||||
self.log.info('Client request: %s', parameter)
|
||||
self.log.debug('Client request: %s', parameter)
|
||||
except KeyError:
|
||||
self.log.warning('Unknown requested option: %d', opt)
|
||||
|
||||
boot_file = self.boot_files['default']
|
||||
if 60 in options:
|
||||
clientclass = options[60]
|
||||
classids = clientclass.split(b':')
|
||||
if len(classids) >= 3 and \
|
||||
classids[0].lower() == b'pxeclient' and \
|
||||
classids[1].lower() == b'arch':
|
||||
try:
|
||||
architecture = classids[2].decode()
|
||||
except UnicodeDecodeError:
|
||||
self.log.error('Unable to decode architecture')
|
||||
return
|
||||
try:
|
||||
boot_file = self.boot_files[architecture]
|
||||
self.log.info("Selecting bootfile '%s' for architecture "
|
||||
"%s", boot_file, architecture)
|
||||
except KeyError:
|
||||
self.log.error('No boot file defined for architecture %s',
|
||||
architecture)
|
||||
return
|
||||
|
||||
# construct reply
|
||||
buf[BOOTP_HOPS] = 0
|
||||
buf[BOOTP_OP] = BOOTREPLY
|
||||
@ -629,8 +660,7 @@ class BootpServer:
|
||||
self.config.get(self.BOOTP_SECTION,
|
||||
'domain', 'localdomain')]).encode()
|
||||
# file
|
||||
buf[BOOTP_FILE] = self.config.get(self.BOOTP_SECTION,
|
||||
'boot_file', '\x00').encode()
|
||||
buf[BOOTP_FILE] = boot_file.encode()
|
||||
|
||||
if not dhcp_msg_type:
|
||||
self.log.warn('No DHCP message type found, discarding request')
|
||||
|
Loading…
Reference in New Issue
Block a user