Python的网络编程[3] -> BOOTP 协议[0] -> BOOTP 的基本理论

BOOTP协议 / BOOTP Protocol


目录

  1. 基本理论
  2. BOOTP 与 DHCP
  3. 通信流程
  4. 数据报文格式 
  5. 报文加解码实现

1. 基本理论 / Basic Theory

BOOTP(Bootstrap Protocol)是一种引导协议,基于RFC951协议,基于UDP协议,也称为自举协议,是DHCP协议的前身。BOOTP用于无盘工作站(类似网吧无盘结构)的局域网中,可以让无盘工作站从一个中心服务器上获得IP地址。通过BOOTP协议可以为局域网中的无盘工作站分配动态IP地址,这样就不需要管理员去为每个用户去设置静态IP地址。

BOOTP使用UDP报文传输,并使用保留端口号67BOOTP服务器,客户端使用此端口作为目标端口发送请求,通常是广播)和68BOOTP客户端,服务器使用此端口作为目标端口发送应答)工作。使用BOOTP协议的时候,一般包括Bootstrap Protocol Server(自举协议服务端)和Bootstrap Protocol Client(自举协议客户端)两部分。

2. BOOTP 与 DHCP / BOOTP and DHCP

BOOTP协议(BOOTstrap Protocol): 引导程序协议,是一种C/S协议,克服了RARP协议的两个缺陷,具体表现为:(1)BOOTP协议是一个C/S程序,BOOTP服务器可以位于Internet的任何地方;(2)BOOT协议除了返回IP地址外,还提供其他配置信息(如子网掩码等),这些信息可以记录在BOOTP报文的选项部分。但是BOOTP协议的缺陷也很明显:BOOTP协议是一个静态配置协议。也就是说当客户请求自己的IP地址时,BOOTP服务器就会查找一张(MAC-->IP)的映射表,这种映射关系必须是事先设定好的。也就是说:BOOTP协议中,MAC地址和IP地址之间的绑定关系是静态的,是固定存放在一张表中,除非管理员更改这张表。所以后来就提出了现在耳熟能详的DHCP协议。

DHCP协议(Dynamic Host Configuration Protocal):动态主机配置协议。同BOOTP协议一样,DHCP协议也是基于C/S方式的,DHCP是BOOTP的继承者,并且能够兼容BOOTP。DHCP不仅能够处理静态配置(此时等同于BOOTP协议),而且能够处理动态配置。也就是说,在客户在查找其自己的IP地址时,DHCP服务器中(MAC-->IP)地址可以事先存在,也可以临时分配。换句话说:当客户发送DHCP请求报文时,DHCP服务器服务器先在其数据库中查找该计算机的配置信息。若找到,则返回找到的信息。若找不到,则从服务器的IP地址池中取一个地址分配给该计算机。

通信流程 / Communication Flow

       芯片中的BOOTP启动代码启动客户端,此时客户端还没有地址,因此客户端以0.0.0.0为本机地址,向255.255.255.255:67广播一个请求报文,服务端接收到请求报文后进行检验,并将配置的offer_ip及需要下载启动的文件名插入返回报文,广播回客户端,客户端接收后根据信息启动TFTP下载启动文件。传输过程中包含了重传策略,服务器接收检验转发处理等(RFC951)。

       此处包含一个鸡和蛋的问题,即如果客户端不知道自己IP地址,服务器怎么发送IP报文到客户端。

无论何时一条引导应答被发送,发送设备执行下列操作:    

1.如果客户端知道自己的IP地址('ciaddr'字段非零),因为客户端能够回应ARPs (Address Resolution Protocol, 6.4.1.2),那么IP能够正常发送。

2.如果客户端还不知道自己的IP地址(ciaddr是零),客户端就不能回应引导应答发送程序回的ARPs。这时有两种选择:   

a.如果发送程序有必需的核心或驱动钩子程序来人工建立ARP地址缓冲条目,就可以使用'chaddr'和'yiaddr'字段填入一个条目。当然,这个条目象正常ARP建立的其它条目一样有一个生命时间,引导应答的发送程序就能够简单地发送引导应答到客户端的IP地址了。UNIX (4.2BSD)有这种功能。

b.如果发送程序缺少这些核心钩子程序,就只能简单发送引导应答到相应接口的广播地址。这只是在前面情况外的额外的广播。

通信流程大致如下,

                     (0.0.0.0)  Client    --(<broadcast>, 67)-->   Server

              TFTP <---- Ip and file    <--(<broadcast>, 68)--   (offer ip and boot file name)

Note: ARP(Address Resolution Protocol),是根据IP地址获取物理(mac)地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。地址解析协议是建立在网络中各个主机互相信任的基础上的,网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。相关协议有RARP、代理ARP。NDP用于在IPv6中代替地址解析协议。

4 数据报文格式 / Data Message Format

字段   字节数     位号       描述

------   ---------   ------         ------

Op          1       1            Packet op code / Message type.包操作码/消息类型,

1 = BOOTREQUEST(引导请求), 2 = BOOTREPLY(引导应答)

Htype     1       2            Hardware address type, 硬件地址类型

'1' = 10mb ethernet 10M以太网

Hlen       1       3            Hardware address length,硬件地址长度

(eg '6' for 10mb ethernet). 例如'6'是10M以太网

Hops      1       4            client sets to zero, 客户端设置成0

optionally used by gateways,在跨越网关引导时网关可选择使用

in cross-gateway booting.

Xid         4       5-8         Transaction ID, a random number, used to match this boot request

with the responses it generates. 

事务ID,一个随机数,用来匹配引用请求和应答

Secs       2      9-10       Seconds elapsed since client started trying to boot,filled in by client

由客户端填写,客户端引导开始后的过去的秒数

--(Flags) 2      11-12      unused未使用

Ciaddr    4      13-16     Client IP address,filled in by client in bootrequest if known.

客户端IP地址,如果客户端知道就在引导请求中填入

Yiaddr    4       17-20     'Your' (client) IP address,'你的'(客户端)IP地址

filled by server if client doesn't know its own address (ciaddr was 0)

如果客户端不知道它的地址(ciaddr是0),服务器填入

Siaddr     4       21-24    Server IP address,服务器IP地址

returned in bootreply by server,由服务器在引导应答返回

Giaddr    4       25-28     Gateway IP address,used in optional cross-gateway booting

网关IP地址,在跨越网关引导中可以选择使用

Chaddr   16     29-44     Client hardware address,客户端硬件地址

filled in by client.由客户端填写,前6位mac_id,后10位填充0

Sname    64    45-108    Optional server host name, null terminated string.

可选的服务器主机名,空结束的字符串

File        128   109-236  Boot file name, null terminated string;

引导文件名,空结束的字符串

'generic' name or null in bootrequest,

fully qualified directory-path name in bootreply

在引导请求中使用'通用'名称或空

是引导应答中使用确切的目录路径名称

Vend       64    237-300  Optional vendor-specific area,可选的卖主指定的区域,

e.g. could be hardware type/serial on request,

例如,可以是请求硬件类型/序列,

or 'capability' / remote file system handleon reply. 

或应答的性能/远端文件系统句柄。

This info may be set aside for use by a third phase bootstrap or kernel.

这些信息留给第三方分析引导或核心(程序)使用。

5 报文加码实现

分别对服务端和客户端的加码利用 Python 进行实现,解码部分将在客户端和服务器的实现代码中完成,

其中使用到了 binascii 模块wmi 模块struct 模块

  1 import binascii
  2 import socket
  3 import struct
  4 import wmi
  5 import random
  6 w = wmi.WMI()
  7 
  8 class ServerCodeC:
  9 
 10     @staticmethod
 11     def offer(transaction_id, client_ip_offer, server_ip, client_mac_id, file_path):
 12         SERVER_NAME = 'bootpserver'
 13         VENDOR = ''
 14         client_mac_id = binascii.unhexlify(client_mac_id.replace(':', ''))
 15         transaction_id = binascii.unhexlify(transaction_id)
 16         packet = b''
 17         packet += b'x02' # op
 18         packet += b'x01' # htype
 19         packet += b'x06' # hlen
 20         packet += b'x00' # hops
 21         packet += transaction_id
 22         packet += b'x00x00' # secs
 23         packet += b'x80x00' # flags (broadcast)
 24         packet += b'x00x00x00x00' # current client ip
 25         packet += socket.inet_aton(client_ip_offer) # next current client ip offer
 26         packet += socket.inet_aton(server_ip) # server ip
 27         packet += b'x00x00x00x00' # gateway ip
 28         packet += client_mac_id # Client mac id #TODO: Change it
 29         packet += b'x00x00x00x00x00x00x00x00x00x00' # client mac id padding
 30         packet += SERVER_NAME.encode('utf-8')
 31         packet += b"x00"*(64-len(SERVER_NAME))
 32         packet += file_path.encode('utf-8')
 33         packet += b"x00"*(128-len(file_path))
 34         packet += VENDOR.encode('utf-8')
 35         packet += b"x00"*(64-len(VENDOR))
 36         return packet
 37 
 38     @staticmethod
 39     def collect(msg):
 40         msgBody = {}
 41         m = list(struct.unpack('%dc' % len(msg), msg))
 42         msgBody['op'] = m[0]
 43         msgBody['htype'] = m[1]
 44         msgBody['hlen'] = m[2]
 45         msgBody['hops'] = m[3]
 46         msgBody['xid'] = ''.join(['%02x' % ord(x) for x in m[4:8]]) # transaction_id
 47         msgBody['secs'] = m[8:10]
 48         msgBody['flags'] = m[10:12]
 49         msgBody['ciaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[12:16]), msg[12:16]))).rstrip('.')
 50         msgBody['yiaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[16:20]), msg[16:20]))).rstrip('.')
 51         msgBody['siaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[20:24]), msg[20:24]))).rstrip('.')
 52         msgBody['giaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[24:28]), msg[24:28]))).rstrip('.')
 53         msgBody['chaddr'] = ''.join(['%02x:' % ord(x) for x in m[28:34]])[:-1]  # Client_mac_id (Delete last one ':')
 54         msgBody['sname'] = msg[44:108].decode('utf-8').strip('x00')
 55         msgBody['file'] = msg[108:236].decode('utf-8').strip('x00')
 56         msgBody['vend'] = msg[236:300]
 57         return msgBody
 58 
 59 class ClientCodeC():
 60     transaction_id = None
 61     client_mac_id = None
 62 
 63     @classmethod
 64     def get_xid_macid(cls):
 65         xid = ''
 66         for i in range(8):
 67             xid += hex(random.randint(0, 15))[-1]
 68         cls.transaction_id = xid
 69 
 70         mac_id = []
 71         for network in w.Win32_NetworkAdapterConfiguration(IPEnabled=1):
 72             mac_id.append(network.MACAddress)
 73         cls.client_mac_id = mac_id[0].lower()
 74 
 75     @staticmethod
 76     def request():
 77         SERVER_NAME = ''
 78         VENDER = ''
 79         transaction_id = binascii.unhexlify(ClientCodeC.transaction_id)
 80         client_mac_id = binascii.unhexlify(ClientCodeC.client_mac_id.replace(':', ''))
 81 
 82         packet = b''
 83         packet += b'x01'  # request op    1   1
 84         packet += b'x01'  # htype         2   1
 85         packet += b'x06'  # hlen          3   1
 86         packet += b'x00'  # hops          4   1
 87         packet += transaction_id # transaction_id  5-8     4
 88         packet += b'x00x00'  # secs              9-10    2
 89         # TODO: Add resend time count
 90         packet += b'x80x00'  # flags(broadcast)  11-12   2
 91         packet += b'x00x00x00x00'  # client ip 13-16   4
 92         packet += b'x00x00x00x00'  # your client ip 17-20   4
 93         packet += b'x00x00x00x00'  # server ip 21-24   4
 94         packet += b'x00x00x00x00'  # gateway ip 25-28  4
 95         packet += client_mac_id    # mac id 29-34  6
 96         packet += b'x00x00x00x00x00x00x00x00x00x00' # mac id placeholder 35-44   10
 97         packet += b'x00' * 64     # server name   45-108  64
 98         packet += b'x00' * 128    # file name 109-236 128
 99         packet += VENDER.encode('utf-8')   # vender info 237-300
100         packet += b"x00"*(64-len(VENDER))
101         return packet
102 
103     @staticmethod
104     def collect(msg):
105         msgBody = {}
106         m = list(struct.unpack('%dc' % len(msg), msg))
107         msgBody['op'] = m[0]
108         msgBody['htype'] = m[1]
109         msgBody['hlen'] = m[2]
110         msgBody['hops'] = m[3]
111         msgBody['xid'] = ''.join(['%02x' % ord(x) for x in m[4:8]]) # transaction_id
112         msgBody['secs'] = m[8:10]
113         msgBody['flags'] = m[10:12]
114         msgBody['ciaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[12:16]), msg[12:16]))).rstrip('.')
115         msgBody['yiaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[16:20]), msg[16:20]))).rstrip('.')
116         msgBody['siaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[20:24]), msg[20:24]))).rstrip('.')
117         msgBody['giaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[24:28]), msg[24:28]))).rstrip('.')
118         msgBody['chaddr'] = ''.join(['%02x:' % ord(x) for x in m[28:34]])[:-1]  # Client_mac_id (Delete last one ':')
119         msgBody['sname'] = msg[44:108].decode('utf-8').strip('x00')
120         msgBody['file'] = msg[108:236].decode('utf-8').strip('x00')
121         msgBody['vend'] = msg[236:300]
122         return msgBody


相关阅读


1. binascii 模块

2. wmi 模块

3. struct 模块

4. DHCP

参考链接


http://www.360doc.com/content/07/0822/15/39230_688439.shtml

http://blog.csdn.net/jxh_123/article/details/26449715

http://baike.baidu.com/item/ARP/609343

原文地址:https://www.cnblogs.com/stacklike/p/8149681.html