udp協議基本資料包結構
udp是不可靠、無連線的協議,不可靠是指不能檢查到資料包是否安全到達對端,但應用程式可以做保證資料包到達的機制,udp是無連線的協議說明udp的開銷小、資料包傳輸效率高,如果傳輸的資料小,建立連線的開銷、保證資料包可靠傳送需要做的工作比資料本身還有多,那麼udp是一種好的選擇。udp協議頭包含有四部分:
(1)、源埠:16位表示取值範圍是1-65535。
(2)、目的埠:也是16位。
(3)、長度:長度是16位表示,指udp資料包的整體長度,udp資料包最小是8個位元組,所以它能傳送的最大負載長度是65535-8。
(4)、校驗和:udp的校驗和用16位表示,是檢驗協議頭和負載資料。
1、UDP協議頭資料結構
udp協議頭結構體是struct udphdr,結構體元素包括:源埠、目的埠、udp報文整體長度、資料包校驗和。結構體定義在include/linux/udp.h檔案中。
struct udphdr {
__be16 source; //源埠
__be16 dest; //目的埠
__be16 len; //資料包長度
__sum16 check; //校驗和
};
2、UDP控制緩衝區
在Socket BUffer的sk_buff結構體中有一個控制緩衝區,提供給tcp/udp協議頭棧中各層協議存放私有資料,udp存放私有資料的結構體是struct udp_skb_cb,定義在include/net/udp.h中
struct udp_skb_cb { union { struct inet_skb_parm h4; //ipv4 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) struct inet6_skb_parm h6; //ipv6 #endif } header; __u16 cscov; //udp校驗和 __u8 partial_cov; //udp部分校驗和 }; //訪問緩衝區 #define UDP_SKB_CB(__skb) ((struct udp_skb_cb *)((__skb)->cb))
h4、h6:分別是ipv4、ipv6的選項資訊。
cscov:整個udp資料包的校驗和。
partial_cov:部分資料的校驗和。
udp緩衝區只能通過UDP_SKB_CB巨集來訪問。
3、udp套接字結構體
udp套接字結構體是struct udp_sock是描述了udp協議的專業特性,struct udp_sock包含了struct inet_sock,struct inet_sock是所有AF_INET地址族域套接字專用資料結構。struct udp_sock在struct inet_sock的基礎是擴充套件了udp資料包需要的全部管理、控制資訊。
struct udp_sock {
/* inet_sock has to be the first member */
struct inet_sock inet;
#define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0] //struct inet_sock的資料域
#define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1]
#define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node
int pending; /* Any pending frames ? 當前是否有等待的資料包 */
unsigned int corkflag; /* Cork is required 是否要阻塞套接字*/
__u16 encap_type; /* Is this an Encapsulation socket? 是否是封裝套接字*/
/*
* Following member retains the information to create a UDP header
* when the socket is uncorked.
*/
__u16 len; /* total length of pending frames 等待發送資料包的長度*/
/*
* Fields specific to UDP-Lite.
*/
__u16 pcslen; //輕套接字等待發送的資料包長度
__u16 pcrlen; //輕套接字等待接受的資料包長度
/* indicator bits used by pcflag: */
#define UDPLITE_BIT 0x1 /* set by udplite proto init function */
#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */
#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */
__u8 pcflag; /* marks socket as UDP-Lite if > 0 輕套接字標誌 */
__u8 unused[3];
/*
* For encapsulation sockets.
*/
int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); //封裝套接字的接受函式
};
4、udp協議和套接字層的介面
udp協議和套接字層有介面結構是struct proto,定義在net/ipv4/udp.c中,主要是管理套接字和接受傳送資料包的處理函式,udp接受資料包時要確定是把資料包分配給那個套接字,以便把資料包放入套接字的接受佇列中提供使用者讀取。udp上所有開啟的套接字由udp_v4_hash函式註冊到struct sock *udp_hash[UDP_TABLE_SIZE]雜湊連結串列中,埠號就是查詢雜湊表的雜湊值。udp和套接字層的介面struct proto udp_prot定義在net/ipv4/udp.c檔案中。
struct proto udp_prot = {
.name = "UDP",
.owner = THIS_MODULE,
.close = udp_lib_close, //關閉套接字
.connect = ip4_datagram_connect, //初始化一個連線
.disconnect = udp_disconnect, //斷開套接字
.ioctl = udp_ioctl,
.destroy = udp_destroy_sock,
.setsockopt = udp_setsockopt,
.getsockopt = udp_getsockopt,
.sendmsg = udp_sendmsg, //傳送資料包到網路層介面
.recvmsg = udp_recvmsg, //接受應用層資料
.sendpage = udp_sendpage,
.backlog_rcv = __udp_queue_rcv_skb,
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.rehash = udp_v4_rehash,
.get_port = udp_v4_get_port,
.memory_allocated = &udp_memory_allocated,
.sysctl_mem = sysctl_udp_mem,
.sysctl_wmem = &sysctl_udp_wmem_min,
.sysctl_rmem = &sysctl_udp_rmem_min,
.obj_size = sizeof(struct udp_sock),
.slab_flags = SLAB_DESTROY_BY_RCU,
.h.udp_table = &udp_table,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
#endif
};
upd和套接字層的介面實現了對資料的收發、管理,在AF_INET協議族初始化的過程中完成註冊,註冊函式是int proto_register(struct proto *prot, int alloc_slab),初始化函式是inet_init在net/ipv4/af_inet.c檔案中。
static int __init inet_init(void)
{
struct sk_buff *dummy_skb;
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL;
...
//註冊tcp協議例項
rc = proto_register(&tcp_prot, 1);
if (rc)
goto out_free_reserved_ports;
//註冊udp協議
rc = proto_register(&udp_prot, 1);
if (rc)
goto out_unregister_tcp_proto;
...
}
5、udp協議和IP層之間的介面
udp協議和IP層之間的介面由struct net_protoco結構體描述,也是定義了一系列函式指標,主要的函式是接受IP層的資料包udp_rcv和處理ICMP錯誤資訊函式udp_err。
static const struct net_protocol udp_protocol = {
.handler = udp_rcv, //接受IP層資料包函式
.err_handler = udp_err, //icmp錯誤處理函式
.gso_send_check = udp4_ufo_send_check,
.gso_segment = udp4_ufo_fragment,
.no_policy = 1,
.netns_ok = 1,
};
也是在inet_init()函式中呼叫inet_add_protocol註冊的。
static int __init inet_init(void)
{
...
//註冊傳輸層的處理函式到inet_protos全域性陣列中
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
//註冊udp處理函式
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
...
}