用戶數據報協議,即UDP,是一個面向數據報的簡單運輸層協議:進程的每次輸出操作只產生一個UDP數據報,從而發送
一個IP數據報。
進程通過創建一個Internet域內的SOCK_DGRAM類型的插口,來訪問UDP。該類型插口默認地稱爲無連接的。每次進程發送
數據時,必須指定目的IP地址和端口號。每次從插口上接收數據報時,進程可以從數據報中收到源IP地址和端口號。
UDP插口也可以被連接到一個特殊的IP地址和端口,這樣,所有寫到該插口的數據報都被髮往該目的地,而且只有來自該IP
地址和端口號的數據才被傳給該進程。
1.UDP的protosw結構
下圖顯示了UDP的協議交換入口
2.UDP的首部
UDP首部定義成一個udphdr結構,下圖是UDP首部的數據結構和圖。
在代碼中,通常把udp首部作爲一個緊跟着UDP首部的IP首部來引用。這就是udp_input如何處理收到的ip數據報,以及
udp_output如何構造外出的ip數據報,這種聯合的ip/udp首部是一個udpiphdr結構,如下圖所示:
20個字節的ip首部定義成一個ipovly結構,如下圖所示。不幸的是,這個結構並不是一個真正的ip首部,大小雖然相同,但是
字段不同。
3.udp_init函數
domaininit函數在系統初始化時調用udp的初始化函數(udp_init),這個函數所做的唯一的工作是把頭部PCB的向前和向
後指針指向它自己。這是一個雙向鏈表。
udb PCB的其他部分都被初始化爲0,儘管在這個頭部PCB中唯一使用的字段是inp_lport,它是要分配的下一個UDP臨時
端口號。
4.udp_output函數
當應用程序調用一下五個寫函數中的任意一個時,發生UDP輸出。這五個函數是:send,sendto或sendmsg、write或
writev。如果插口已經連接上,則可任意調用調用這五個函數,儘管用sendto或sendmsg不能指定目的地址。如果插口
還沒有連接上,則只能調用sendto和sendmsg,並且必須指定一個目的地址。下圖總結了這五個函數,它們在終止時,
都調用udp_output,該函數再調用ip_output。
五個函數終止調用sosend,並把一個指向msghdr結構的指針作爲參數傳給該函數。要輸出的數據被分裝在一個mbuf鏈
上,sosend把一個可選的目標地址和可選的控制信息放在mbuf中,併發布PRU_SEND請求。
udp_output函數的處理流程如下:
1.丟掉可選控制信息。udp輸出不適用任何控制信息。
2.臨時連接一個未連接上的接口。如果調用方爲UDP數據報指定了目的地址,則插口是由in_pcbconnect臨時連接到該地址
的,並在該函數的最後被斷連。
3.在前面加上ip/udp首部。M_PREPEND在數據的前面爲IP和UDP首部分配空間。
4.1.UDP檢驗和計算和僞首部
在討論udp_output的後一部分之前,我們描述一下udp如何填充ip/udp首部的某些字段,如何計算udp檢驗和,如何傳遞
ip/udp首部及數據給ip輸出的,這些工作很巧妙地使用了ipovly結構。
下圖顯示了udp_output在由m指向的mbuf鏈的第一個存儲器上構造的28字節ip/udp首部。沒有陰影的字段是udp_output
填充的,有陰影的字段是由ip_output填充的。
在計算UDP檢驗和使用以下三個事實:1.在僞首部中(如下圖)的第三個32bit字看起來與ip首部中的第三個32bit字類似:
2個8bit值和一個16bit值。2.僞首部中的三個32bit值的順序是無關的。事實上,internet檢驗和的計算不依賴於所使用的
16bit值的順序。3.在檢驗和計算中另外再加上一個全0的32bit字沒有任何影響。
udp_output利用這三個事實,填充udpiphdr結構的字段,如下圖所示。
在20字節的ip首部中,最後三個32bit字被用作檢驗和計算的爲首部,ip首部的前兩個32bit字也用作檢驗和計算中,但他們
被初始化爲0,不影響最後的檢驗和。
下圖總結了我們描述的操作:
1.上圖中最上面的圖是爲首部的協議定義。
2.中間的圖是源代碼中使用的udpiphdr結構,被用於計算udp檢驗和。
3.下面的圖是出現在線路上的ip/udp首部。上面有箭頭的7個字段是udp_output在檢驗和之前填充的,上面有星號的3個字段
是udp_output在檢驗和計算之後填充的,其他6個有陰影的字段是ip_output填充的。
下面是udp_output函數的後半部分。
1.爲檢驗和計算準備僞首部。
2.計算檢驗和
3.填充UDP長度、TTL和TOS。
4.發送數據報。調用ip_output發送數據報。
5.斷連臨時連接的插口。
5.udp_input函數
5.1.對收到的udp數據報的一般確認
1.丟棄IP選項。
2.驗證UDP長度。與UDP數據報相關的兩個長度是:IP首部的長度字段(ip_len)和UDP首部的長度字段(uh_ulen)。
比較這兩個長度,可能有三種可能性:
1)ip_len等於uh_ulen。這是通常情況。
2)ip_len大於uh_ulen。ip首部太大,如下圖所示。代碼相信兩個長度中小的那個,並從數據報的最後移走多餘的數據字節,
從mbuf鏈的最後截斷數據。
3)ip_len小於uh_ulen。當udp首部的長度給定時,ip數據報比可能的小,如下圖所示。這說明數據報有錯誤,必須丟棄,
沒有其他的選擇。
3.保存ip首部的備份,驗證udp的檢驗和。
5.2.分用單播數據報
假定數據報的目的地址是一個單播地址。
1.檢查“向後一個”高速緩存。udp維護一個指針,該指針指向最後在其上接收數據報的internet pcb,在查看pcb之前,可能必須
搜索udp表上的pcb,把最近一次接收pcb的外部和本地地址以及端口號和收到的數據報進行比較。這稱爲“向後一個”高速緩存。
它是根據這樣一個假設,即收到的數據報極有可能要發往上一個數據報發往的同一端口。
2.搜索所有的UDP的PCB。
3.生成ICMP端口不可達差錯。如果沒有找到匹配的PCB,UDP通常產生一個ICMP端口不可達差錯。
4.返回源站IP地址和源站端口。
5.處理IP_RECVDSTADDR插口選項。該選項把收到的UDP數據報中的目的IP地址作爲控制信息返回。
6.把數據加到插口的接收隊列中。
5.3.分用多播和廣播數據報
這些數據報被提交給匹配的所有插口,而不僅僅是一個插口。
6.udp_saveopt函數
如果進程指定了IP_RECVDSTADDR插口選項,則udp_input調用udp_saveopt,從收到的數據報中接收目的IP地址。
7.udp_ctlinput函數
當icmp_input收到一個ICMP差錯(目的主機不可達、參數問題、重定向、源站抑制和超時)時,調用相應協議的pr_ctlinput
函數。對於UDP,調用udp_cltinput。我們來看下對收到的ICMP所做的處理:
1.icmp_input把icmp類型和碼轉換成PRC_xxxx差錯碼。
2.把PRC_xxxx差錯碼傳給協議的控制輸入函數。
3.internet pcb協議(TCP和UDP)把PRC_xxx差錯碼映射到一個unix的errno值,這個值被返回給進程。
8.udp_usrreq函數
許多操作都要調用協議的用戶請求函數,在某個UDP插口上調用五個寫函數中的任意一個,都以請求PRU_SEND調用UDP
的用戶請求函數結束。下面討論該函數中各個請求。
1.PRU_ATTACH請求,來自socket系統調用,分配一個新的pcb,把它加到udp pcb表的前面,把插口結構和pcb連接在一起。
2.close系統調用發佈PRU_DETACH請求。從udp表中移走pcb,並釋放該pcb。
3.PRU_BIND請求完成綁定操作。
4.如果有PRU_LISTEN請求,則是無效的,只有面向連接的協議才使用它。
5.在一個udp應用程序中,客戶或服務器,可以調用connect,它發送CONNECT請求修改插口發送或接收的外部ip地址和端口號。
6.socketpair系統調用發佈PRU_CONNECT2請求。
8.對於UDP插口,有兩種情況會產生PRU_DISCONNECT請求:
a.當關閉了一個連接上的UDP插口時,在PRU_DETACH之前調用PRU_DISCONNECT。
10.調用無法寫函數,發佈PRU_SEND請求,最終調用udp_output發送該數據報。
12.PRU_SOCKADDR和PRU_PEERADDR請求分配來自系統調用getsockname和getpeername。
9.udp_sysctl函數
udp的sysctl函數只支持一個選項,udp檢驗和標誌位。系統管理員可以禁止用sysctl程序使能或靜止udp檢驗和。