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'\x00\x00' # secs
 23         packet += b'\x80\x00' # flags (broadcast)
 24         packet += b'\x00\x00\x00\x00' # 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'\x00\x00\x00\x00' # gateway ip
 28         packet += client_mac_id # Client mac id #TODO: Change it
 29         packet += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # 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'\x00\x00'  # secs              9-10    2
 89         # TODO: Add resend time count
 90         packet += b'\x80\x00'  # flags(broadcast)  11-12   2
 91         packet += b'\x00\x00\x00\x00'  # client ip 13-16   4
 92         packet += b'\x00\x00\x00\x00'  # your client ip 17-20   4
 93         packet += b'\x00\x00\x00\x00'  # server ip 21-24   4
 94         packet += b'\x00\x00\x00\x00'  # gateway ip 25-28  4
 95         packet += client_mac_id    # mac id 29-34  6
 96         packet += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM