------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/c12964386a10902396ddd814.html
UDP是一个简单的面向数据报的传输层协议,我们先站在UDP客户端的角度来看看如何发送一个UDP数据报,以及协议栈为发送一个UDP数据报做了哪些事情。
UDP数据报可以在未连接的socket上发送(使用sendto系统调用,指定目的地址),也可以在已连接的socket上发送(使用send系统调用,不用指定目的地址),下面我们分两种情况讨论。
下面是一个在未连接的socket上发送UDP数据的用户态程序示例(注:该程序的格式和风格相当不好,只是为临时测试使用。),该程序目前还只管发送,不处理接收,关于接收,我们后面再作分析:
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include "my_inet.h"
- #include <stdio.h>
- #include <errno.h>
-
- #include <arpa/inet.h>
- #include <unistd.h>
-
- int main()
- {
- int i;
- struct sockaddr_in dest;
- dest.sin_family = MY_PF_INET;
- dest.sin_port = htons(16000);
- dest.sin_addr.s_addr = 0x013010AC; //目的地址是172.16.48.1(网络字节序)
-
- //创建UDP数据报服务的socket。
- int fd = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
- if( fd < 0 ){
- perror("socket: ");
- return -1;
- }
- int bwrite = sendto( fd, "abcdefg", 7, 0, (struct sockaddr *)&dest, sizeof(dest) );
- if( bwrite == -1 ){
- perror("send: ");
- close(fd);
- return -1;
- }
- printf("sendto: %d/n", bwrite);
- close( fd );
- return 0;
- }
- struct udphdr {
- __u16 source; //发送端端口号。
- __u16 dest; //目的端端口号。
- __u16 len; //UDP长度。
- __u16 check; //UDP检验和。
- };
--------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/c7166d8128d418d8bd3e1ee3.html
很多介绍网络编程的书籍中会这样介绍connect系统调用:将本机的一个指定的套接字连接到一个指定地址的服务器套接字上去。下面是connect系统调用的定义:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include "my_inet.h"
- #include <stdio.h>
- #include <errno.h>
-
- #include <arpa/inet.h>
- #include <unistd.h>
-
- int main()
- {
- int i;
- //代表服务器地址的结构。
- struct sockaddr_in dest;
- dest.sin_family = MY_PF_INET;
- dest.sin_port = htons(16000);
- dest.sin_addr.s_addr = 0x013010AC;//172.16.48.1的网络字节序。
-
- int fd = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
- if( fd < 0 ){
- perror("socket: ");
- return -1;
- }
- //连接操作。
- if( connect( fd, (struct sockaddr*)&dest, sizeof(dest) ) < 0 )
- perror("connect: ");
- //不必通过sendto每次传入对端地址信息了。
- int bwrite = send( fd, "abcdefg", 7, 0 );
- if( bwrite == -1 ){
- perror("send: ");
- return -1;
- }
-
- printf("sendto: %d/n", bwrite);
- close( fd );
- return 0;
- }
- struct flowi fl = { .oif = 0, //输出设备接口未定。
- .nl_u = { .ip4_u = { .daddr = 172.16.48.1 //目的地址。
- .saddr = 0.0.0.0 //源地址未定。
- .tos = 0 } }, //一般服务
- .proto = MY_IPPROTO_UDP, //UDP协议
- .uli_u = { .ports =
- { .sport = 32768, //自动选择的第一个源端口
- .dport = 16000 } } }; //目的端口
-------------------------------------------------------------------------------------------------------------------------------
现在我们换一个角度,站在服务器的一方,看看如何被动地接收一个UDP数据,并作出相应的处理。我们把前面文章提及的UDP示例客户端放到172.16.48.1上,在主机172.16.48.2的eth0网络设备接口上再配一个从属IP地址172.16.48.13。让客户端向13发送UDP数据报。
函数myudp_rcv处理接收到的UDP数据报,该函数首先从数据报头中取出源和目的地址、端口。向哈希表myudp_hash查询,看是否存在相应的sokcet等待处理该数据报。如果没有,则发生错误,即对端主机向本机的某个特定端口发送了一个UDP数据,但本机并没有该端口标识的进程需要处理该数据报,所以,这是一个目的端口不可达错误。调用myicmp_send发送一个目的端口不可达出错报文。
目的端口不可达ICMP报文需要将整个源出错UDP报文作为数据负载(payload)放进报文中,所以,首先要对源UDP报文进行基本的正确性检查。还需要生成一份详细的ICMP参数信息,用于生成ICMP报文,ICMP参数信息用结构体struct icmp_bxm表示,下面是该结构体的定义:
- struct icmp_bxm {
- struct sk_buff *skb; //指向所在的sk_buff
- int offset; //出错的源UDP报文在ICMP报文中的偏移量。
- int data_len;
-
- struct {
- struct icmphdr icmph; //icmp头,需要填充完整。
- __u32 times[3];
- } data;
- int head_len;
- struct ip_options replyopts; //ip选项。
- unsigned char optbuf[40];
- };
发送一个数据报,相应的,肯定需要一个套接字,为发送这个ICMP服文,协议栈为每个CPU创建了一个套接字,命名为myicmp_socket。------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/a0b1962b0f7331fae7cd4079.html
说起套接字的通讯,我们一般会想到C/S结构(客户端/服务端结构),从应用的角度来讲,确实如此,比如IM(即时通讯)服务,一台IM服务器同时与几万的IM客户端进行着UDP数据报的通讯。但我们从套接字的编程实现,以及TCP/IP协议栈的实现原理这一层上来看,并不存在着服务端与客户端的明显区别。
网络上的两台主机(为简化起见,我们假设它们处于同一子网内,并能互相连通),为了建立一个UDP的通讯,一端A必须事先知道另一端B的端口(B有一个进程可以接受UDP数据报)。即B必须先建立一个套接字,并自己为其选择一个固定的端口号,而不是让系统自动选择,并把这个端口号告知A(通过人,或者是熟知端口)。然后A就可以通过connect系统调用,在其socket上设置好相应参数,以后每个发出的数据包的UDP首部中总标明目的地址B和其相应的端口号;或者每发一个数据报,通过sendto传入B的地址和端口号,以确保每个发出的数据报在B端被正确的进程接收。
而A端可以自己选择一个固定的端口号,也可以由系统自动选择,这并不重要。因为B端收到来自A端的第一个数据报后,可以从UDP首部中判断A端的端口号,并在回应包中进行正确设置。在这样的情况下,我们一般就认为B是服务端,A是客户端。它们的唯一区别在于服务端的端口号必须是事先被客户端知道的。而客户端的端口号的选择则相对比较随便。
我们前面文章提到过协议栈中的函数myudp_v4_get_port可以为一个UDP套接口选择一个可以使用的端口号,也可以由我们自己选择一个端口号,然后由该函数判断是否可用。我们通过bind系统调用选择一个固定的端口号:
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);----------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/c12964385ae9802397ddd883.html
UDP是一个简单的面向数据报的运输层协议,我们先站在UDP客户端的角度来看看如何发送一个UDP数据报,以及协议栈为发送一个UDP数据报做了哪些事情。
UDP数据报可以在未连接的socket上发送(使用sendto系统调用,指定目的地址),也可以在已连接的socket上发送(使用send系统调用,不用指定目的地址),下面我们分两种情况讨论。
下面是一个在未连接的socket上发送UDP数据的用户态程序示例(注:该程序的格式和风格相当不好,只是为临时测试使用。),该程序目前还只管发送,不处理接收,关于接收,我们后面再作分析:
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include "my_inet.h"
- #include <stdio.h>
- #include <errno.h>
-
- #include <arpa/inet.h>
- #include <unistd.h>
- int main()
- {
- int i;
- struct sockaddr_in dest;
- dest.sin_family = MY_PF_INET;
- dest.sin_port = htons(16000);
- dest.sin_addr.s_addr = 0x013010AC; //目的地址是172.16.48.1(网络字节序)
-
- //创建UDP数据报服务的socket。
- int fd = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
- if( fd < 0 ){
- perror("socket: ");
- return -1;
- }
-
- int bwrite = sendto( fd, "abcdefg", 7, 0, (struct sockaddr *)&dest, sizeof(dest) );
- if( bwrite == -1 ){
- perror("send: ");
- close(fd);
- return -1;
- }
- printf("sendto: %d/n", bwrite);
- close( fd );
- return 0;
- }
----------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/a0cd024f888bf337aec3ab42.html
当建立一个UDP的socket用于网络通讯时,我们需要先为这个socket绑定一个本地端口号。因为端口在一台主机上是用于标识进程的,如果没有端口号,当收到来自对端主机的报文时,就不知道应该由哪一个进程来接收这个报文。但有时,我们建立UDP的sokcet以后,并不调用bind进行端口绑定,也能正常工作。这是因为协议栈对于没有进行端口绑定的socket进行了自动绑定。
在SOCK_DGRAM类型的套接字的操作函数集的sendmsg成员函数中,每次调用对应的第4层协议的sendmsg成员函数时,都会进行端口号的检查,如果没有绑定就调用协议的成员函数get_port进行自动绑定。代表INET域网络层套接字的结构体struct inet_sock有两个端口号相关的成员__u16 num和__u16 sport。它们都代表套接字的本地端口号。num是主机字节序,sport是网络字节序。当套接字类型为SOCK_RAW时,它们代表的是协议号(icmp,igmp等)。套接字层的sendmsg检查端口号绑定时,就是查看num是否为零。
udp协议提供udp_v4_get_port函数用于自动获取本地端口号。端口号有一个固定的数值范围,自动获取必须在这个范围内进行。数组int sysctl_local_port_range[2]指定了本地端口号的范围。其默认值为1024到4999。对于高可用性系统,它的值应该是32768到61000(在TCP协议进行初始化时,会进行这项设置)。可通过修改文件/proc/sys/net/ipv4/ip_local_port_range的内容来修改这个范围。
udp_hash是一个list数组,总共有128项,所有在协议栈中建立的udp socket全部以本地端口号为关键字被放入这个哈希数组中,全局变量udp_port_rover记录了最近一次被分配的端口号。寻找一个新的可用的端口,总是从udp_port_rover开始找,检查udp_hash[udp_port_rover & (UDP_HTABLE_SIZE - 1)]的list是否为空,如果为空,则取udp_port_rover为新的端口,如果不为空,则记录下这个list的size,同时保存下该端口号,然后遍历整个数组,找到size最小的一个list,取对应的端口号为我们所要获得的端口。然后,检查这个新获得的端口号是否已经被使用(同样,通过检查udp_hash实现)。如果已在使用中,则把端口号加上UDP_HTABLE_SIZE(128)再检查。直至获得未使用的端口号。
----------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/ba9c32fad482fe1fa8d311d7.html
下面是connect系统调用的函数原型:
- #include <sys/types.h>
- #include <sys/socket.h>
- int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen );
--------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/dfae34fa638927889e51468a.html
在Socket编程中,发送数据报文可供使用的API函数有send,sendto和sendmsg,下面是关于前两个系统调用的原型:
- #include <sys/socket.h>
- ssize_t send( int socket, const void *buffer, size_t length, int flags );
- #include <sys/socket.h>
- ssize_t sendto(int socket, const void *message, size_t length,
- int flags, const struct sockaddr *dest_addr,
- socklen_t dest_len);
- asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flags,
- struct sockaddr __user *addr, int addr_len)
- struct msghdr {
- void * msg_name;
- int msg_namelen;
- struct iovec * msg_iov;
- __kernel_size_t msg_iovlen;
- void * msg_control;
- __kernel_size_t msg_controllen;
- unsigned msg_flags;
- };
msg_name和msg_namelen就是数据报文要发向的对端的地址信息(即sendto系统调用中的addr和addr_len)。当使用send时,它们的值为NULL和0。msg_iov的定义如下:- struct iovec
- {
- void __user *iov_base;
- __kernel_size_t iov_len;
- };
- #include <sys/socket.h>
- ssize_t sendmsg(int socket, const struct msghdr *message, int flags);
-----------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/312ee8506e76c45c1138c261.html
在前一篇中,我们提到在对端主机上没有创建指定的UDP套接字时,我们向其发送一个UDP包,会得到一个目的端口不可达的ICMP出错报文。但内核在处理完该报文后,给应用程序仅仅返回一个ECONNREFUSED错误号,所以应用程序能知道的全部信息就是连接被拒绝,至于为什么被拒绝,没有办法知道。我们可以通过套接字选项的设置,让内核返回更为详细的出错信息,以利于调试程序,发现问题。下面是通过套接字选项传递扩展出错信息的一个示例程序。关于内核原理的分析,在下一篇给出。
- #include <sys/socket.h>
- #include <linux/types.h>
- #include <linux/errqueue.h>
- #include <sys/ioctl.h>
- #include "my_inet.h"
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
-
- #include <arpa/inet.h>
- #include <unistd.h>
-
- int ip_control_msg( struct cmsghdr *msg )
- {
- int ret = 0;
- switch( msg->cmsg_type ){
- case IP_RECVERR:
- {
- struct sock_extended_err *exterr;
- exterr = (struct sock_extended_err *)(CMSG_DATA(msg));
- printf("ee_errno: %u/n", exterr->ee_errno );
- printf("ee_origin: %u/n", exterr->ee_origin );
- printf("ee_type: %u/n", exterr->ee_type );
- printf("ee_code: %u/n", exterr->ee_code );
- printf("ee_pad: %u/n", exterr->ee_pad );
- printf("ee_info: %u/n", exterr->ee_info );
- printf("ee_data: %u/n", exterr->ee_data );
- }
- ret = -1;
- break;
- default:
- break;
- }
- return ret;
- }
-
- int control_msg( struct msghdr *msg )
- {
- int ret = 0;
- struct cmsghdr *control_msg = CMSG_FIRSTHDR( msg );
- while( control_msg != NULL ){
- switch( control_msg->cmsg_level ){
- case SOL_IP:
- ret = ip_control_msg( control_msg );
- break;
- default:
- break;
- }
- control_msg = CMSG_NXTHDR( msg, control_msg );
- }
- return ret;
- }
-
- int main()
- {
- int i;
- struct sockaddr_in dest;
- dest.sin_family = MY_PF_INET;
- dest.sin_port = htons(16000);
- dest.sin_addr.s_addr = 0x013010AC;
-
- int fd = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
- if( fd < 0 ){
- perror("socket: ");
- return -1;
- }
- if( connect( fd, (struct sockaddr*)&dest, sizeof(dest) ) < 0 ){
- perror("connect: ");
- return -1;
- }
-
- int val = 1;
- if( setsockopt( fd, SOL_IP, IP_RECVERR, &val, sizeof(val) ) == -1 ){
- perror("setsockopt: ");
- return -1;
- }
-
- int bwrite = send( fd, "abcdefg", 7, 0 );
- if( bwrite == -1 ){
- perror("send: ");
- return -1;
- }
- char buf[1024];
- char control_buf[1024];
- struct msghdr msg;
- struct iovec iov = { buf, 1024 };
- memset( &msg, 0, sizeof(msg) );
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = &control_buf;
- msg.msg_controllen = 1024;
-
- int bread = recvmsg( fd, &msg, MSG_ERRQUEUE );
- if( bread == -1 ){
- perror("recv: ");
- return -1;
- }
- if( control_msg( &msg ) >= 0 )
- printf("successed!/n");
- else
- printf("failed!/n");
-
- close( fd );
- return 0;
- }
----------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/58eafaf2d2b49113b17ec52a.html
接着前一篇,我们来看这个应用程序背后,内核真正做了一些什么事情。
代表MY_INET域套接字的结构体struct inet_sock有一个成员recverr,它占1bit长度,可能的取值是1或0,当为0时表示socket上出错时,只通过系统调用向应用程序返回错误号,不提供进一步的详细信息。当取值为1时,则表示socket上出错时,则向struct inet_sock的成员sk_error_queue(一个sk_buff的队列)存入一个特殊的struct sk_buff,在sk_buff的成员cb中放入详细的错误信息,应用程序通过特定的系统调用可以取得详细的出错信息。
recverr的值可以通过套接字选项操作进行设置,它是一个IP层的选项,对应的选项名是IP_RECVERR。下面的代码就是将它的值设为1(打开选项):
- int val = 1;
- if( setsockopt( fd, SOL_IP, IP_RECVERR, &val, sizeof(val) ) == -1 )
- ;//deal with error
- struct sock_exterr_skb
- {
- union {
- struct inet_skb_parm h4;
- #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- struct inet6_skb_parm h6;
- #endif
- } header;
- struct sock_extended_err ee;
- u16 addr_offset;
- u16 port;
- };
- struct sock_extended_err
- {
- __u32 ee_errno; //错误号。
- __u8 ee_origin; //产生错误的源,我们的环境下,产生错误的源为一个ICMP包。
- __u8 ee_type; //ICMP类型。
- __u8 ee_code; //ICMP代码。
- __u8 ee_pad;
- __u32 ee_info; //用于EMSGSIZE时找到的MTU。
- __u32 ee_data;
- };
ssize_t recvmsg(int s, struct msghdr *msg, int flags);------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/41442f738862bf1c8601b03b.html
用户使用socket系统调用编写应用程序时,通过一个数字来表示一个socket,所有的操作都在该数字上进行,这个数字称为套接字描述符。在系统调用的实现函数里,这个数字就会被映射成一个表示socket的结构体,该结构体保存了该socket的所有属性和数据。在内核的协议中实现中,关于表示socket的结构体,是一个比较复杂的东西,下面一一介绍。
struct socket。
这是一个基本的BSD socket,我们调用socket系统调用创建的各种不同类型的socket,开始创建的都是它,到后面,各种不同类型的socket在它的基础上进行各种扩展。struct socket是在虚拟文件系统上被创建出来的,可以把它看成一个文件,是可以被安全地扩展的。下面是其完整定义:
- struct socket {
- socket_state state;
- unsigned long flags;
- const struct proto_ops *ops;
- struct fasync_struct *fasync_list;
- struct file *file;
- struct sock *sk;
- wait_queue_head_t wait;
- short type;
- };
- typedef enum {
- SS_FREE = 0, //该socket还未分配
- SS_UNCONNECTED, //未连向任何socket
- SS_CONNECTING, //正在连接过程中
- SS_CONNECTED, //已连向一个socket
- SS_DISCONNECTING //正在断开连接的过程中
- }socket_state;
- struct proto_ops {
- int family;
- struct module *owner;
- int (*release)(struct socket *sock);
- int (*bind)(struct socket *sock, struct sockaddr *myaddr, int sockaddr_len);
- int (*connect)(struct socket *sock, struct sockaddr *vaddr, int sockaddr_len, int flags);
- int (*socketpair)(struct socket *sock1, struct socket *sock2);
- int (*accept)(struct socket *sock,struct socket *newsock, int flags);
- int (*getname)(struct socket *sock, struct sockaddr *addr,int *sockaddr_len, int peer);
- unsigned int (*poll)(struct file *file, struct socket *sock,
- struct poll_table_struct *wait);
- int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg);
- int (*listen)(struct socket *sock, int len);
- int (*shutdown)(struct socket *sock, int flags);
- int (*setsockopt)(struct socket *sock, int level,
- int optname, char __user *optval, int optlen);
- int (*getsockopt)(struct socket *sock, int level,
- int optname, char __user *optval, int __user *optlen);
- int (*sendmsg)(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len);
- int (*recvmsg)(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len, int flags);
- int (*mmap)(struct file *file, struct socket *sock,struct vm_area_struct * vma);
- ssize_t (*sendpage)(struct socket *sock, struct page *page,
- int offset, size_t size, int flags);
- };
- enum sock_type {
- SOCK_DGRAM = 1,
- SOCK_STREAM = 2,
- SOCK_RAW = 3,
- SOCK_RDM = 4,
- SOCK_SEQPACKET = 5,
- SOCK_DCCP = 6,
- SOCK_PACKET = 10,
- };
- TCP_ESTABLISHED = 1,
- TCP_SYN_SENT,
- TCP_SYN_RECV,
- TCP_FIN_WAIT1,
- TCP_FIN_WAIT2,
- TCP_TIME_WAIT,
- TCP_CLOSE,
- TCP_CLOSE_WAIT,
- TCP_LAST_ACK,
- TCP_LISTEN,
- TCP_CLOSING,
-
- TCP_MAX_STATES
- };
--------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/5c8510dfb2348613622798e5.html
接着上一篇,继续介绍struct sock。
sk_rmem_alloc, sk_wmem_alloc和sk_omem_alloc分别表示接收缓冲队列,发送缓冲队列及其它缓冲队列中已经分配的字节数,用于跟踪缓冲区的使用情况。
struct sock有一个struct sock_common成员,因为struct inet_timewait_sock也要用到它,所以把它单独归到一个结构体中,其定义如下:
- struct sock_common {
- unsigned short skc_family;
- volatile unsigned char skc_state;
- unsigned char skc_reuse;
- int skc_bound_dev_if;
- struct hlist_node skc_node;
- struct hlist_node skc_bind_node;
- atomic_t skc_refcnt;
- unsigned int skc_hash;
- struct proto *skc_prot;
- };
- struct inet_sock {
- struct sock sk;
- #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- struct ipv6_pinfo *pinet6;
- #endif
- __u32 daddr; //IPv4的目的地址。
- __u32 rcv_saddr; //IPv4的本地接收地址。
- __u16 dport; //目的端口。
- __u16 num; //本地端口(主机字节序)。
- __u32 saddr; //发送地址。
- __s16 uc_ttl; //单播的ttl。
- __u16 cmsg_flags;
- struct ip_options *opt;
- __u16 sport; //源端口。
- __u16 id; //单调递增的一个值,用于赋给iphdr的id域。
- __u8 tos; //服务类型。
- __u8 mc_ttl; //组播的ttl
- __u8 pmtudisc;
- __u8 recverr:1,
- is_icsk:1,
- freebind:1,
- hdrincl:1, //是否自己构建ip首部(用于raw协议)
- mc_loop:1; //组播是否发向回路。
- int mc_index; //组播使用的本地设备接口的索引。
- __u32 mc_addr; //组播源地址。
- struct ip_mc_socklist *mc_list; //组播组列表。
- struct {
- unsigned int flags;
- unsigned int fragsize;
- struct ip_options *opt;
- struct rtable *rt;
- int length;
- u32 addr;
- struct flowi fl;
- } cork;
- };
- struct raw_sock {
- struct inet_sock inet;
- struct icmp_filter filter;
- };
- struct udp_sock {
- struct inet_sock inet;
- int pending;
- unsigned int corkflag;
- __u16 encap_type;
- __u16 len;
- };
--------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/3dd3dbb4b6d43f738bd4b287.html
一个socket代表了通信链路的一端,存储或指向与链路有关的所有信息。Linux提供了创建socket的一个系统调用,通过该系统调用,能够得到一个用来访问套接字的描述符:
- #include <sys/types.h>
- #include <sys/socket.h>
- int socket( int domain, int type, int protocol );
asmlinkage long sys_socket( int family, int type, int protocol );- struct socket {
- socket_state state;
- unsigned long flags;
- struct proto_ops *ops;
- struct fasync_struct *fasync_list;
- struct file *file;
- struct sock *sk;
- wait_queue_head_t wait;
- short type;
- };
- typedef enum {
- SS_FREE = 0, /* 未分配 */
- SS_UNCONNECTED, /* 未连接 */
- SS_CONNECTING, /* 正在连接当中 */
- SS_CONNECTED, /* 已经连向一个套接字 */
- SS_DISCONNECTING /* 正在断开连接 */
- } socket_state;
- #define SOCK_ASYNC_NOSPACE 0
- #define SOCK_ASYNC_WAITDATA 1
- #define SOCK_NOSPACE 2
- #define SOCK_PASSCRED 3
- enum sock_type {
- SOCK_STREAM = 1, /*可靠字节流服务套接字,TCP*/
- SOCK_DGRAM = 2, /*传输层数据报服务, UDP*/
- SOCK_RAW = 3, /*网络层数据报服务, ICMP, IGMP, 原始IP*/
- SOCK_RDM = 4, /*可靠的数据报服务*/
- SOCK_SEQPACKET = 5, /*可靠的双向记录流服务*/
- SOCK_PACKET = 10, /*已废弃*/
- };
- struct net_proto_family {
- int family;
- int (*create)(struct socket *sock, int protocol);
- short authentication;
- short encryption;
- short encrypt_net;
- struct module *owner;
- };
-----------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/017863d9e1e9462c11df9b87.html
结构体sock是套接口在网络层的表示,在代码include/net/sock.h 174行定义,下面是其内容:
- struct sock {
- struct sock_common __sk_common;
- #define sk_family __sk_common.skc_family
- #define sk_state __sk_common.skc_state
- #define sk_reuse __sk_common.skc_reuse
- #define sk_bound_dev_if __sk_common.skc_bound_dev_if
- #define sk_node __sk_common.skc_node
- #define sk_bind_node __sk_common.skc_bind_node
- #define sk_refcnt __sk_common.skc_refcnt
- unsigned char sk_shutdown : 2,
- sk_no_check : 2,
- sk_userlocks : 4;
- unsigned char sk_protocol;
- unsigned short sk_type;
- int sk_rcvbuf;
- socket_lock_t sk_lock;
- wait_queue_head_t *sk_sleep;
- struct dst_entry *sk_dst_cache;
- struct xfrm_policy *sk_policy[2];
- rwlock_t sk_dst_lock;
- atomic_t sk_rmem_alloc;
- atomic_t sk_wmem_alloc;
- atomic_t sk_omem_alloc;
- struct sk_buff_head sk_receive_queue;
- struct sk_buff_head sk_write_queue;
- int sk_wmem_queued;
- int sk_forward_alloc;
- unsigned int sk_allocation;
- int sk_sndbuf;
- int sk_route_caps;
- int sk_hashent;
- unsigned long sk_flags;
- unsigned long sk_lingertime;
-
- struct {
- struct sk_buff *head;
- struct sk_buff *tail;
- } sk_backlog;
- struct sk_buff_head sk_error_queue;
- struct proto *sk_prot;
- struct proto *sk_prot_creator;
- rwlock_t sk_callback_lock;
- int sk_err,
- sk_err_soft;
- unsigned short sk_ack_backlog;
- unsigned short sk_max_ack_backlog;
- __u32 sk_priority;
- struct ucred sk_peercred;
- int sk_rcvlowat;
- long sk_rcvtimeo;
- long sk_sndtimeo;
- struct sk_filter *sk_filter;
- void *sk_protinfo;
- struct timer_list sk_timer;
- struct timeval sk_stamp;
- struct socket *sk_socket;
- void *sk_user_data;
- struct page *sk_sndmsg_page;
- struct sk_buff *sk_send_head;
- __u32 sk_sndmsg_off;
- int sk_write_pending;
- void *sk_security;
- void (*sk_state_change)(struct sock *sk);
- void (*sk_data_ready)(struct sock *sk, int bytes);
- void (*sk_write_space)(struct sock *sk);
- void (*sk_error_report)(struct sock *sk);
- int (*sk_backlog_rcv)(struct sock *sk,
- struct sk_buff *skb);
- void (*sk_destruct)(struct sock *sk);
- };
- struct sock_common {
- unsigned short skc_family; /*地址族*/
- volatile unsigned char skc_state; /*连接状态*/
- unsigned char skc_reuse; /*SO_REUSEADDR设置*/
- int skc_bound_dev_if;
- struct hlist_node skc_node;
- struct hlist_node skc_bind_node; /*哈希表相关*/
- atomic_t skc_refcnt; /*引用计数*/
- };
--------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/dc8361591b17c52a2834f087.html
socket( AF_INET, SOCK_STREAM, IPPROTO_TCP )- static struct inet_protosw inetsw_array[] =
- {
- {
- .type = SOCK_STREAM,
- .protocol = IPPROTO_TCP,
- .prot = &tcp_prot,
- .ops = &inet_stream_ops,
- .capability = -1,
- .no_check = 0,
- .flags = INET_PROTOSW_PERMANENT,
- }
- }
- struct sock {
- struct sock_common __sk_common;
- #define sk_family __sk_common.skc_family
- #define sk_state __sk_common.skc_state
- #define sk_reuse __sk_common.skc_reuse
- #define sk_bound_dev_if __sk_common.skc_bound_dev_if
- #define sk_node __sk_common.skc_node
- #define sk_bind_node __sk_common.skc_bind_node
- #define sk_refcnt __sk_common.skc_refcnt
- unsigned char sk_shutdown : 2,
- sk_no_check : 2,
- sk_userlocks : 4;
- unsigned char sk_protocol;
- unsigned short sk_type;
- int sk_rcvbuf;
- socket_lock_t sk_lock;
- wait_queue_head_t *sk_sleep;
- struct dst_entry *sk_dst_cache;
- struct xfrm_policy *sk_policy[2];
- rwlock_t sk_dst_lock;
- atomic_t sk_rmem_alloc;
- atomic_t sk_wmem_alloc;
- atomic_t sk_omem_alloc;
- struct sk_buff_head sk_receive_queue;
- struct sk_buff_head sk_write_queue;
- int sk_wmem_queued;
- int sk_forward_alloc;
- unsigned int sk_allocation;
- int sk_sndbuf;
- int sk_route_caps;
- int sk_hashent;
- unsigned long sk_flags;
- unsigned long sk_lingertime;
-
- struct {
- struct sk_buff *head;
- struct sk_buff *tail;
- } sk_backlog;
- struct sk_buff_head sk_error_queue;
- struct proto *sk_prot;
- struct proto *sk_prot_creator;
- rwlock_t sk_callback_lock;
- int sk_err,
- sk_err_soft;
- unsigned short sk_ack_backlog;
- unsigned short sk_max_ack_backlog;
- __u32 sk_priority;
- struct ucred sk_peercred;
- int sk_rcvlowat;
- long sk_rcvtimeo;
- long sk_sndtimeo;
- struct sk_filter *sk_filter;
- void *sk_protinfo;
- struct timer_list sk_timer;
- struct timeval sk_stamp;
- struct socket *sk_socket;
- void *sk_user_data;
- struct page *sk_sndmsg_page;
- struct sk_buff *sk_send_head;
- __u32 sk_sndmsg_off;
- int sk_write_pending;
- void *sk_security;
- void (*sk_state_change)(struct sock *sk);
- void (*sk_data_ready)(struct sock *sk, int bytes);
- void (*sk_write_space)(struct sock *sk);
- void (*sk_error_report)(struct sock *sk);
- int (*sk_backlog_rcv)(struct sock *sk,
- struct sk_buff *skb);
- void (*sk_destruct)(struct sock *sk);
- };
-
- __sk_common是套接口在网络层的最小表示。下面是其定义:
- struct sock_common {
- unsigned short skc_family; /*地址族*/
- volatile unsigned char skc_state; /*连接状态*/
- unsigned char skc_reuse; /*SO_REUSEADDR设置*/
- int skc_bound_dev_if;
- struct hlist_node skc_node;
- struct hlist_node skc_bind_node; /*哈希表相关*/
- atomic_t skc_refcnt; /*引用计数*/
- };
------------------------------------------------------------------------------------------------------------------------------
static int myinet_create(struct socket *sock, int protocol);- #include <sys/types.h>
- #include <sys/socket.h>
- int socket(int domain, int type, int protocol);
- int fd0 = socket( MY_PF_INET, SOCK_RAW, MY_IPPROTO_ICMP );
- int fd1 = socket( MY_PF_INET, SOCK_RAW, MY_IPPROTO_IGMP );
- int fd2 = socket( MY_PF_INET, SOCK_RAW, IPPROTO_IP );
- int fd3 = socket( MY_PF_INET, SOCK_DGRAM, MY_IPPROTO_UDP );
- int fd4 = socket( MY_PF_INET, SOCK_DGRAM, IPPROTO_IP );
- int fd5 = socket( MY_PF_INET, SOCK_STREAM, MY_IPPROTO_TCP );
- int fd6 = socket( MY_PF_INET, SOCK_STREAM, IPPROTO_IP );
- printf("fd: %d, %d, %d, %d, %d, %d, %d/n", fd0, fd1, fd2, fd3, fd4, fd5, fd6);
------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/dfae34fa36f76a899e514687.html
我们先来简单看一下系统调用close(int fd)的流程。该系统调用会调用到内核中的函数:
asmlinkage long sys_close(unsigned int fd) file->f_op->release(inode, file);- int myinet_release(struct socket *sock)
- {
- struct sock *sk = sock->sk;
-
- if (sk) {
- long timeout = 0;
- myip_mc_drop_socket(sk);
-
- if (sock_flag(sk, SOCK_LINGER) &&
- !(current->flags & PF_EXITING))
- timeout = sk->sk_lingertime;
- sock->sk = NULL;
- sk->sk_prot->close(sk, timeout);
- }
- return 0;
- }
---------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/e06db645a8939326cffca380.html
套接字选项这个话题在socket编程里,可能已经属于中高级话题了,之所以在一开始就把这个话题提上来讲,是因为我们的一个近阶段目标是能够把 MY_PF_INET域的RAW协议走通,并在上面跑起一个ping程序,所以,按照ping程序的要求,接下来,我们必须实现套接字选项系统调用 setsockopt在MY_PF_INET中RAW协议中的相关实现。
下面是该系统调用函数的原型:
- #include <sys/socket.h>
- int setsockopt( int socket, int level, int option_name,
- const void *option_value, size_t option_len);
- struct linger {
- int l_onoff; /* 延时状态(打开/关闭) */
- int l_linger; /* 延时多长时间 */
- };
如果linger.l_onoff值为0(关闭),则清sock->sk->sk_flag中的SOCK_LINGER位;否则,置该位,并赋sk->sk_lingertime值为linger.l_linger。 如果不在套接字级别上设置选项,即setsockopt系统调用的参数level不设为SOL_SOCKET,那么sys_setsockopt的实现会直接调用sock->ops->setsockopt。对MY_PF_INET域的RAW协议来讲,sock->ops = myinet_sockraw_ops,而myinet_sockraw_ops.setsockopt = sock_common_setsockopt。
而sock_common_setsockopt直接调用sock->sk->sk_prot->setsockopt。对于RAW协议来讲,即myraw_setsockopt。
下面关注myraw_setsockopt的实现。对于RAW协议来讲,level还可以有两种取值:SOL_IP和SOL_RAW。 myraw_setsockopt首先检查level是否为SOL_IP,如果是,调用myip_setsockopt函数,该函数实现IP级别上的选项,否则,为SOL_RAW级别上的选项,SOL_RAW级别上只有一个选项,即ICMP_FILTER,在MY_IPPROTO_ICMP协议下有效。它激活绑定到MY_IPPROTO_ICMP协议的一个用于myraw
socket特殊的过滤器。该值对每种ICMP消息都有一个位(掩码),可以把那种ICMP消息过滤掉,缺省时是不过滤ICMP消息。
对于ICMP_FILTER选项,myraw_setsockopt调用myraw_seticmpfilter函数,它把option_value赋给 sock->sk->filter,option_value是一个结构体:
- struct icmp_filter {
- __u32 data;
- };
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <errno.h>
-
- #include <linux/in.h>
- #include <linux/icmp.h>
- int main()
- {
- struct icmp_filter filter;
- socklen_t size = sizeof( struct icmp_filter );
- int fd = socket( PF_INET, SOCK_RAW, IPPROTO_ICMP );
- if( fd < 0 )
- perror("error: ");
-
- getsockopt( fd, SOL_RAW, ICMP_FILTER, &filter, &size );
- printf("the filter: %x/n", filter.data );
-
- filter.data = 1;
- int err = setsockopt( fd, SOL_RAW, ICMP_FILTER, &filter, sizeof(struct icmp_filter) );
- if( err < 0 )
- perror("error: ");
-
- memset( &filter, 0, sizeof( struct icmp_filter ) );
- getsockopt( fd, SOL_RAW, ICMP_FILTER, &filter, &size );
- printf("new filter: %x/n", filter.data);
-
- close(fd);
- return 0;
- }
- struct in_pktinfo
- {
- unsigned int ipi_ifindex; /* 接口索引 */
- struct in_addr ipi_spec_dst; /* 路由目的地址 */
- struct in_addr ipi_addr; /* 头标识目的地址 */
- };
ipi_ifindex指的是接收包的接口的唯一索引。ipi_spec_dst指的是路由表记录中的目的地址,而ipi_addr 指的是包头中的目的地址。如果给 sendmsg传递了IP_PKTINFO,那么外发的包会通过在ipi_ifindex中指定的接口发送出去,同时把ipi_spec_dst设置为目的地址。-------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/df83293475a0123b5bb5f580.html
套接字写有多个实现接口,我们只以其中一个接口write为线索,对套接字写(网络数据发送)的流程进行分析。系统调用write会调用内核函数sys_write,sys_write调用vfs_write完成实际的写操作。
vfs_write会先调用file->f_op->write(file从套接字描述符获得)。如果file->f_op-> write不存在,则调用do_sync_write。该函数会调用sock_aio_write,sock_aio_write又会调用 __sock_sendmsg,然后到myinet_sendmsg,最后才到sk->sk_prot->sendmsg,对于RAW协议来讲,即myraw_sendmsg。
sock_aio_write的函数原型如下:
- static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *ubuf,
- size_t size, loff_t pos)
ubuf是用户待发送数据,size是数据长度,pos是文件位置(永远为零)。在这个函数里,会把用户待发送数据封装成一个struct msghdr结构:- struct msghdr {
- void * msg_name; /* Socket name */
- int msg_namelen; /* Length of name */
- struct iovec * msg_iov; /* Data blocks */
- __kernel_size_t msg_iovlen; /* Number of blocks */
- void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
- __kernel_size_t msg_controllen; /* Length of cmsg list */
- unsigned msg_flags;
- };
- struct msghdr thehdr{
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov.iov_base = "abcdef",
- .msg_iov.iov_len = 6,
- .msg_iovlen = 1,
- .msg_control = NULL,
- .msg_controllen = 0,
- .msg_flags = 0
- };
- static int myraw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
- size_t len)
-----------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/daeaac4b0b2f62f082025c81.html
一般,用户在shell中使用ifconfig命令对网络接口进行参数配置,及接口的打开,关闭等操作。ifconfig实现网络接口配置的原理在于代表网络接口的结构体struct net_device的成员ip_ptr。前文已经讲过,ip_ptr实际指向的是一个结构体struct in_device,in_device有一个成员struct in_ifaddr *ifa_list,它指向一个链表,链表的每一项代表一个IP地址。对这个链表操作即可实现对网络接口的配置。
网络接口的操作命令按功能可以分为两组,第一组为查询命令:SIOCGIFADDR,SIOCGIFBRDADDR,SIOCGIFDSTADDR, SIOCGIFNETMASK。分别用于查询网络接口的IP地址,广播地址,目的地址,子网掩码。第二组为设置命令:SIOCSIFADDR, SIOCSIFFLAGS,SIOCSIFBRDADDR,SIOCSIFNETMASK,SIOCSIFDSTADDR。分别用于设置网络接口的IP地址,标志位,广播地址,子网掩码,目的地址。这些命令所要查询和设置的信息全部在结构体struct
in_ifaddr中。
用户空间的应用程序通过系统调用ioctl使用这些命令,ioctl的函数原型如下:
- #include <sys/ioctl.h>
- int ioctl(int d, int request, ...);
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <linux/if.h>
- #include "my_inet.h"
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
-
- int main()
- {
- struct ifreq req;
- strncpy( req.ifr_name, "eth0", IFNAMSIZ );
- struct sockaddr_in *sin;
-
- int fd = socket( MY_PF_INET, SOCK_RAW, MY_IPPROTO_ICMP );
- if( fd < 0 ){
- perror("error: ");
- return -1;
- }
-
- sin = (struct sockaddr_in *)&req.ifr_addr;
- if( ioctl( fd, SIOCGIFADDR, &req) == 0 )
- printf("%s/n", inet_ntoa(sin->sin_addr.s_addr) );
- else
- perror("ioctl error: ");
-
- sin = (struct sockaddr_in *)&req.ifr_broadaddr;
- if( ioctl( fd, SIOCGIFBRDADDR, &req) == 0 )
- printf("%s/n", inet_ntoa(sin->sin_addr.s_addr) );
- else
- perror("ioctl error: ");
-
- sin = (struct sockaddr_in *)&req.ifr_dstaddr;
- if( ioctl( fd, SIOCGIFDSTADDR, &req) == 0 )
- printf("%s/n", inet_ntoa(sin->sin_addr.s_addr) );
- else
- perror("ioctl error: ");
-
- sin = (struct sockaddr_in *)&req.ifr_netmask;
- if( ioctl( fd, SIOCGIFNETMASK, &req) == 0 )
- printf("%s/n", inet_ntoa(sin->sin_addr.s_addr) );
- else
- perror("ioctl error: ");
-
- close( fd );
- return 0;
- }
-
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <linux/if.h>
- #include "my_inet.h"
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
-
- int main()
- {
- struct ifreq req;
- strncpy( req.ifr_name, "eth0:0", IFNAMSIZ );
- struct sockaddr_in *sin;
-
- int fd = socket( MY_PF_INET, SOCK_RAW, MY_IPPROTO_ICMP );
- if( fd < 0 ){
- perror("error: ");
- return -1;
- }
-
- sin = (struct sockaddr_in *)&req.ifr_addr;
- sin->sin_family = MY_PF_INET;
- if( inet_aton("172.16.48.10", &sin->sin_addr) == 0 ){
- perror("inet_aton error: ");
- return -1;
- }
- if( ioctl( fd, SIOCSIFADDR, &req ) == 0 ){
- printf("success!/n");
- }else{
- perror("ioctl failed: ");
- }
-
- req.ifr_flags = IFF_UP;
- if( ioctl( fd, SIOCSIFFLAGS, &req ) == 0 ){
- printf("success!/n");
- }else{
- perror("ioctl failed: ");
- }
-
- sin->sin_family = MY_PF_INET;
- if( inet_aton("172.16.48.255", &sin->sin_addr) == 0 ){
- perror("inet_aton error: ");
- return -1;
- }
- if( ioctl( fd, SIOCSIFBRDADDR, &req ) == 0 ){
- printf("success!/n");
- }else{
- perror("ioctl failed: ");
- }
-
- sin->sin_family = MY_PF_INET;
- if( inet_aton("255.255.255.0", &sin->sin_addr) == 0 ){
- perror("inet_aton error: ");
- return -1;
- }
- if( ioctl( fd, SIOCSIFNETMASK, &req ) == 0 ){
- printf("success!/n");
- }else{
- perror("ioctl failed: ");
- }
-
- close( fd );
- return 0;
- }
--------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/a0cd024fe351ae36aec3ab2f.html
变量inetsw_array是inet域的一个全局数组,其类型是struct inet_protosw,该结构体的定义如下:
- struct inet_protosw {
- struct list_head list;
- unsigned short type;
- int protocol;
- struct proto *prot;
- const struct proto_ops *ops;
- int capability;
- char no_check;
- unsigned char flags;
- };
- #define INET_PROTOSW_REUSE 0x01 /* Are ports automatically reusable? */
- #define INET_PROTOSW_PERMANENT 0x02 /* Permanent protocols are unremovable. */
- #define INET_PROTOSW_ICSK 0x04 /* Is this an inet_connection_sock? */
- struct socket {
- socket_state state;
- unsigned long flags;
- const struct proto_ops *ops;
- struct fasync_struct *fasync_list;
- struct file *file;
- struct sock *sk;
- wait_queue_head_t wait;
- short type = SOCK_RAW;
- };
- struct net_proto_family{
- int family;
- int (*create)(struct socket *sock, int protocol);
- short authentication;
- short encryption;
- short encrypt_net;
- struct module *owner;
- };
- static struct net_proto_family inet_family_ops = {
- .family = PF_INET,
- .create = inet_create,
- .owner = THIS_MODULE,
- };
- struct socket {
- socket_state state = SS_UNCONNECTED;
- unsigned long flags = INET_PROTOSW_REUSE;
- const struct proto_ops *ops = &inet_sockraw_ops;
- struct fasync_struct *fasync_list;
- struct file *file;
- struct sock *sk;
- wait_queue_head_t wait;
- short type = SOCK_RAW;
- };
- struct sock{
- .sk_family = AF_INET;
- .sk_prot = &raw_prot;
- .sk_prot_creator = &raw_prot;
- .sk_reuse = 1;
- .sk_protocol = IPPROTO_ICMP;
- ... ...
- }
------------------------------------------------------------------------------------------------------------------------------
转自 http://hi.baidu.com/linux%5Fkernel/blog/item/b108cc11bca1b110b9127bb0.html
首先以socket和send两个系统调用为例,来回顾一下协议栈是如何工作的,在这过程中可以找到如何在协议栈中增加对UDP协议的支持。socket系统调用的原型是
int socket(int domain, int type, int protocol); domain是协议域,对于ipv4协议来说,其值是PF_INET(ipv4因特网协议),对于我们自己实现的ipv4协议模块,我们为其新增MY_PF_INET。所有的协议域在 include/linux/socket.h被定义,如下:- #define AF_UNSPEC 0
- #define AF_UNIX 1 // Unix域的socket
- #define AF_LOCAL 1 // AF_UNIX的POSIX命名
- #define AF_INET 2 // 因特网IP协议
- #define AF_AX25 3 // Amateur Radio AX.25
- #define AF_IPX 4 // Novell IPX
- #define AF_APPLETALK 5 // AppleTalk DDP
- #define AF_NETROM 6 // Amateur Radio NET/ROM
- #define AF_BRIDGE 7 // Multiprotocol bridge
- #define AF_ATMPVC 8 // ATM PVCs
- #define AF_X25 9 // Reserved for X.25 project
- #define AF_INET6 10 // IP version 6
- #define AF_ROSE 11 // Amateur Radio X.25 PLP
- #define AF_DECnet 12 // Reserved for DECnet project
- #define AF_NETBEUI 13 // Reserved for 802.2LLC project
- #define AF_SECURITY 14 // Security callback pseudo AF
- #define AF_KEY 15 // PF_KEY key management API
- #define AF_NETLINK 16
- #define AF_ROUTE AF_NETLINK // Alias to emulate 4.4BSD
- #define AF_PACKET 17 // Packet family
- #define AF_ASH 18 // Ash
- #define AF_ECONET 19 // Acorn Econet
- #define AF_ATMSVC 20 // ATM SVCs
- #define AF_SNA 22 // Linux SNA Project (nutters!)
- #define AF_IRDA 23 // IRDA sockets
- #define AF_PPPOX 24 // PPPoX sockets
- #define AF_WANPIPE 25 // Wanpipe API Sockets
- #define AF_LLC 26 // Linux LLC
- #define AF_BLUETOOTH 31 // Bluetooth sockets
- #define AF_MAX 32 // For now..
- struct net_proto_family {
- int family;
- int (*create)(struct socket *sock, int protocol);
- short authentication;
- short encryption;
- short encrypt_net;
- struct module *owner;
- };
- static struct net_proto_family myinet_family_ops = {
- .family = MY_PF_INET,
- .create = myinet_create,
- .owner = THIS_MODULE,
- };
- struct socket {
- socket_state state;
- unsigned long flags;
- struct proto_ops *ops;
- struct fasync_struct *fasync_list;
- struct file *file;
- struct sock *sk;
- wait_queue_head_t wait;
- short type;
- };
- struct inet_protosw {
- struct list_head list;
- unsigned short type; //套接字类型,即socket系统调用的第二个参数。
- int protocol; //第4层(传输层)协议号
- struct proto *prot; //第4层协议的操作函数集
- struct proto_ops *ops; //该类型的套接字的操作函数集
- int capability;
- char no_check;
- unsigned char flags;
- };
ssize_t send(int s, const void *buf, size_t len, int flags);- struct msghdr {
- void * msg_name; /* Socket 的名字 */
- int msg_namelen; /* 名字的长度 */
- struct iovec * msg_iov; /* 数据块 */
- __kernel_size_t msg_iovlen; /* 数据块的数量 */
- void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
- __kernel_size_t msg_controllen; /* Length of cmsg list */
- unsigned msg_flags;
- };
- int inet_sendmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t size)
- {
- struct sock *sk = sock->sk;
- if (!inet_sk(sk)->num && inet_autobind(sk))
- return -EAGAIN;
- return sk->sk_prot->sendmsg(iocb, sk, msg, size);
- }
原文:https://www.cnblogs.com/Kimbing-Ng/p/12493432.html