1. 程式人生 > >udp協議基本資料包結構

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");
...

}