变量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;
};
type是指套接字的类型,也就是系统调用socket的第二个参数,inet域支持的套接字类型有SOCK_STREAM(流套接字),它是一个有序 的,可靠的,基于连接的双向字节流;SOCK_DGRAM(数据报套接字),该类型的套接字是不可靠的,无连接的,有可能乱序的;SOCK_RAW(原始 套接字),所谓原始,是因为该类型的套接字不提供传输层的服务,协议栈只为该类型的套接字自动添加网络层首部,常用于网络层的附属协议(icmp, igmp等)。
protocol是一个传输层协议号,传输层的协议包括IPPROTO_TCP,IPPROTO_UDP;或者对于SOCK_RAW来讲,它是一个通配协 议号IPPROTO_IP,用于通配网络层的附属协议icmp,igmp等。对于传输层协议来讲,IPPROTO_TCP对应的套接字类型总是 SOCK_STREAM,IPPRTO_UDP对应的套接字类型总是STREAM_DGRAM,所以在socket系统调用时,可以不必指定协议号,而直 接使用通配符IPPROTO_IP。
prot是一个传输层协议绑定的操作集,比如对于IPPROTO_TCP,它就是tcp_prot,对于IPPROTO_UDP,它就是 udp_prot。而对于类型为SOCK_RAW的套接字,它没有相应的传输层协议,而是用于通配所有的网络层附属协议,所以,prot就是所有网络层附 属协议共用的一个操作集raw_prot。
ops是套接字类型绑定的操作集,对应于SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,操作集分别为inet_stream_ops,inet_dgram_ops,inet_sockraw_ops。
capability是操作这类别套接字所需要的权限,除了原始套接字需要CAP_NET_RAW权限之外,其它两类套接字不需要特殊权限(-1)。
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? */
inetsw是一个链表数组,每一项都是一个struct inet_protosw结构体的链表,总共有SOCK_MAX项,在inet_init函数对INET域进行初始化的时候,调用函数 inet_register_protosw把数组inetsw_array中定义的套接字类型全部注册到inetsw数组中,相同套接字类型,不同协议 类型的在数组的同一项,以套接字类型为索引,在系统实际使用的时候,只使用inetsw,而不使用inetsw_array,目前inet域不存在相同套 接字类型的多个协议(原始套接字使用通配符,所以也不存在这个问题)。
使用系统调用socket创建一个RAW类型的套接字,并且网络层附属协议为icmp的时候,首先会在函数__sock_create中创建一个传输层的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 = SOCK_RAW;
};
这里需要补充一下关于协议域的问题,Linux内核支持多个协议域,除了当前正关心的INET域之外,还有UNIXF域,IPX协议等等,一个域包含一个 协议族。完成一个相对独立的完整的网络通讯能力。每个域都有一个域号,比如INET域的域号为AF_INET(2),所有的已注册域号都包含在一个全局数 组net_families中,net_families是类型为struct net_proto_family的数组,该结构体的定义如下:
struct net_proto_family{
int family;
int (*create)(struct socket *sock, int protocol);
short authentication;
short encryption;
short encrypt_net;
struct module *owner;
};
INET域就在net_families数组的第三项(下标为2),其结构体定义如下:
static struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
通过函数sock_register注册到数组中,因为socket系统调用的第一个参数指定了协议域号,所以__sock_create可以通过 net_families[2]->create调用到INET域的创建函数,完成进一步的socket创建工作。
inet_create函数通过套接字类型socket->type找到inetsw[SOCK_RAW],并从该链表(其实只有一项)的头部开始 匹配网络层附属协议号,因为SOCK_RAW类型的套接字采用通配符,所以匹配成功。同时,还需要检查权限,inet_create函数进一步完善了 struct socket的内容:
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 socket是传输层的套接字,它只看到套接字类型这一层次,所以其操作函数是inet_sockraw_ops。inet_create还为其成员sk创建了一个网络层的套接字,其内容如下(只列出本文内容相关的部分):
struct sock{
.sk_family = AF_INET;
.sk_prot = &raw_prot;
.sk_prot_creator = &raw_prot;
.sk_reuse = 1;
.sk_protocol = IPPROTO_ICMP;
... ...
}
这样,在一个套接字上作一个操作,比如说send,会先调用struct socket->ops->sendmsg(),该函数会调用struct socket->sk->sk_prot->sendmsg完成实际的发送工作。
综上所述,现在要加入对原始套接(SOCK_RAW)的支持,首先要在数组inetsw_array中提供一个原始套接字类型的struct inet_protosw,在INET域初始化的时候会被加入到inetsw数组中,同时提供一个原始套接字上的所有网络层附属协议的通用操作集 raw_prot和原始套接字类型的操作集inet_sockraw_ops,并实现它们的全部函数即可。中间涉及到具体的协议,还会有一些协议相关的支 持问题要处理。
2007年10月22日星期一
订阅:
博文评论 (Atom)
没有评论:
发表评论