Linux 网络系统学习

IPv6 的初始化

作者: 小马哥 rstevens  (rstevens2008@hotmail.com)

欢迎转载,未经允许,请勿用于商业目的

 

1.   网络子系统

1.1.                    网络子系统概述

 

Linux 内核中,与网络相关的代码是一个相对独立的子系统,称为网络子系统。

网络子系统是一个层次化的结构,可分为以下几个层次:

 

1、Socket

Linux 在发展过程中,采用 BSD socket APIs 作为自己的网络相关的 API 接口。同时,Linux 的目标又要能支持各种不同的协议族,而且这些协议族都可以使用 BSD socket APIs 作为应用层的编程接口。因此,在 socket APIs 与协议族层之间抽象出一个 socket 层,用于将 user space socket API 调用,转给具体的协议族做处理。

 

2、协议族层(INET协议族、INET6协议族等)

Linux 网络子系统功能上相当完备,它不仅支持 INET 协议族(也就是通常所说的 TCP/IP stack),而且还支持其它很多种协议族,如 DECnet, ROSE, NETBEUI 等。INET6 就是一种新增加的协议族。

对于 INETINET6 协议族来说, 又进一步划分为传输层和网络层。

 

3、设备驱动层

设备驱动层则主要将协议族层与物理的网络设备隔离开。它不在本文的讨论范围之内。

 

下图是 Linux 网络系统层次结构图。


 

 


1.2.                    网络子系统的初始化

 

·        Socket 层的初始化:

 

Init()->do_basic_setup()->sock_init()

 

Sock_init():对 sock skbuff 结构进行 SLAB 内存的初始化工作

 

 

·        各种网络协议族的初始化:

 

Do_initcalls()

对于编译到内核中的功能模块(而不是以模块的形式动态加载),它的初始化函数会在这个地方被调用到。

            内核映象中专门有一个初始化段,所有编译到内核中的功能模块的初始化函数都会加入到这个段中;而 do_initcalls() 就是依次执行初始化段中的这些函数。

            INET 协议族通常是被编译进内核的;它的模块初始化函数是 net/ipv4/af_inet.c 中的 inet_init()

            INET6 是作为一个模块编译的。它的模块初始化函数是 net/ipv6/af_inet6.c 中的 inet6_init()

           


2.   协议族

 

Linux 网络子系统可以支持不同的协议族,Linux 所支持的协议族定义在 include/linux/socket.h

 

 

2.1.                    协议族数据结构

 

协议族数据结构是 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;

};

 

这个结构中,最重要的是create 函数,一个新的协议族,必须提供此函数的实现。这是因为:    

不同的网络协议族,从 user space 的使用方法来说,都是一样的,都是先调用 socket() 来创建一个 socket fd,然后通过这个 fd 发送/接收数据。

user space 通过 socket() 系统调用进入内核后,根据第一个参数协议族类型,来调用相应协议族的 create() 函数。对 INET6 来说,这个函数是 inet6_create()

因此,要实现一个新的协议族,首先需要提供一个create() 的实现。关于 create() 里面具体做了什么,后面再叙述。

Linux 系统通过这种方式,可以很方便的支持新的网络协议族,而不用修改已有的代码。这很好的符合了 “开-闭原则”,对扩展开放,对修改封闭。

 

 

2.2.                    协议族注册

 

Linux 维护一个struct net_proto_family  的数组net_families[]

如果要支持一个新的网络协议族,那么需要定义自己的struct net_proto_family,并且通过调用 sock_register 将它注册到 net_families[] 中。

 

 


3.   socket 层的主要数据结构

 

socket 层又叫 “socket access protocol layer”。它处于 BSD socket APIs 与底层具体的协议族之间。这是一个抽象层,它起着承上启下的作用。在这一层的数据结构也有着这种特点。

 

 

3.1.                    Struct socket

 

user space,通过 socket() 创建的 socket fd,在内核中对应的就是一个 struct socket

 

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;

};

 

它定义于 include/linux/net.h

 

 

3.2.                    Struct proto_ops

 

Struct socket ops 域指向一个 struct proto_ops 结构,struct proto_ops定义于 include/linux/net.h 中,它是 socket 层提供给上层的接口,这个结构中,都是 BSD socket API 的具体实现的函数指针。

 

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

};

 

一个 socket API 通过系统调用进入内核后,首先由 socket 层处理。Socket 层找到对应的 struct socket,通过它找到 struct proto_ops,然后由它所指向的函数进行进一步处理。

 

sendmsg() 这个函数为例,从 user space 通过系统调用进入 kernel 后,由 sys_sendmsg()sock_sendmsg() 依次处理,然后交给 struct proto_ops sendmsg() 处理。

 

 


4.   传输层、网络层的主要数据结构

 

Socket 层之下,是具体的协议族。

INET INET6 来说,又分为传输层和网络层。

这两层重要的数据结构有 struct sock struct proto

 

 

4.1.                    Struct sock

 

struct sock 定义于 include/net/sock.h 中,用于 INET INET6 协议族。

 

应用层的 socket fd,在 socket 层对应的是 struct socket struct socket 很简单,并不做什么具体的事情,它通过 sk 域与一个 struct sock 关联。因此,对应用层的一个 socket fd 来说,在内核中对应的是一个 struct socket 加上一个 struct sock 结构,struct socket 负责 socket 层的处理,struct sock 负责传输层、网络层的处理。

 

2.4 内核中,这个结构非常杂乱,到 2.6 内核,对它做了简化,但仍然有很多成员。我们在这里不具体了解它的作用,只要知道它所处的层次即可。

 

 

4.2.                    Struct proto

 

struct sock 通过 sk_prot 域指向 struct proto 结构。

 

struct  proto {

            
void                               ( * close)( struct  sock  * sk,

                                                            
long  timeout);

            
int                                  ( * connect)( struct  sock  * sk,

                                                        
struct  sockaddr  * uaddr,

                                                            
int  addr_len);

            
int                                  ( * disconnect)( struct  sock  * sk,  int  flags);

 

            
struct  sock  *                  ( * accept) ( struct  sock  * sk,  int  flags,  int   * err);

 

            
int                                  ( * ioctl)( struct  sock  * sk,  int  cmd,

                                                             unsigned 
long  arg);

            
int                                  ( * init)( struct  sock  * sk);

            
int                                  ( * destroy)( struct  sock  * sk);

            
void                               ( * shutdown)( struct  sock  * sk,  int  how);

            
int                                  ( * setsockopt)( struct  sock  * sk,  int  level,

                                                            
int  optname,  char  __user  * optval,

                                                            
int  optlen);

            
int                                  ( * getsockopt)( struct  sock  * sk,  int  level,

                                                            
int  optname,  char  __user  * optval,

                                                            
int  __user  * option);        

            
int                                  ( * sendmsg)( struct  kiocb  * iocb,  struct  sock  * sk,

                                                               
struct  msghdr  * msg, size_t len);

            
int                                  ( * recvmsg)( struct  kiocb  * iocb,  struct  sock  * sk,

                                                               
struct  msghdr  * msg,

                                                            size_t len, 
int  noblock,  int  flags,

                                                            
int   * addr_len);

            
int                                  ( * sendpage)( struct  sock  * sk,  struct  page  * page,

                                                            
int  offset, size_t size,  int  flags);

            
int                                  ( * bind)( struct  sock  * sk,

                                                            
struct  sockaddr  * uaddr,  int  addr_len);

 

            
int                                  ( * backlog_rcv) ( struct  sock  * sk,

                                                                        
struct  sk_buff  * skb);

 

            …

}

 

struct proto是传输层提供给 socket 层的接口。可以看到,它的成员也都是 BSD socket API 相关的函数指针。

 

应用层的 socket API 调用进入内核空间后,首先由socket 层的 struct proto_ops 结构做处理,此后,对于 INET INET6 协议族来说,进一步由 struct proto 的相应函数做处理。

 

还是以 sendmsg() 为例,数据在 socket 层由 struct proto_ops sendmsg() 处理完毕之后,会由 struct proto sendmsg() 进行传输层的处理。


5.   Socket 层与传输层的关联

 

INET INET6 这两种协议族,可以支持多种传输层协议,包括 TCPUDPRAW,在 2.6 内核中,又增加了一种新的传输层协议:SCTP

 

从内核角度看,要实现 INET6 协议族的某种传输层协议,则必须既提供 socket 层的 struct proto_ops 的实现,也提供 struct proto 的实现。除此之外,还需要提供一种手段,把这两个结构关联起来,也就是把 socket 层和传输层关联起来。

 

Linux 提供了一个 struct inet_protosw 的结构,用于 socket 层与传输层的关联。

 

5.1.                    struct inet_protosw

 

struct  inet_protosw {

            
struct  list_head list;

 

        
/*  These two fields form the lookup key.   */

            unsigned 
short    type;        /*  This is the 2nd argument to socket(2).  */

            
int                      protocol;  /*  This is the L4 protocol number.   */

 

            
struct  proto        * prot;

            
struct  proto_ops  * ops;

 

            
int               capability;  /*  Which (if any) capability do

                                                      * we need to use this socket

                                                      * interface?

                                      
*/

            
char              no_check;    /*  checksum on rcv/xmit/none?  */

            unsigned 
char     flags;       /*  See INET_PROTOSW_* below.   */

};

 

这个结构定义于  include/net/protocol.h 中,从它的命名上可以看到它属于 INET INET6 协议族,但是没有查到资料为什么叫做 protosw

这个结构中,ops 指向 socket 层的 struct proto_opsprot 指向传输层的 struct proto

 

因此,对 INET6 这种要支持多种传输层协议的协议族,从内核的角度来说,只需要为每一种传输层协议定义相应的 struct proto_opsstruct proto,然后再定义 struct inet_protosw,并将三者关联起来即可:

INET6 所支持的 TCP 为例:

 

static   struct  proto_ops inet6_sockraw_ops  =  {

            .family 
=            PF_INET6,

            .owner 
=           THIS_MODULE,

            .release 
=          inet6_release,

            .bind 
=              inet6_bind,

            .connect 
=         inet_dgram_connect,                  /*  ok                 */

            .socketpair 
=     sock_no_socketpair,                 /*  a do nothing   */

            .accept 
=           sock_no_accept,                                   /*  a do nothing   */

            .getname 
=        inet6_getname,

            .poll 
=               datagram_poll,                           /*  ok                 */

            .ioctl 
=   inet6_ioctl,                                 /*  must change   */

            .listen 
=             sock_no_listen,                          /*  ok                 */

            .shutdown 
=      inet_shutdown,                          /*  ok                 */

            .setsockopt 
=    sock_common_setsockopt,                   /*  ok                 */

            .getsockopt 
=    sock_common_getsockopt,                   /*  ok                 */

            .sendmsg 
=        inet_sendmsg,                            /*  ok                 */

            .recvmsg 
=        sock_common_recvmsg,                       /*  ok                 */

            .mmap 
=                        sock_no_mmap,

            .sendpage 
=       sock_no_sendpage,

};

 

struct  proto tcpv6_prot  =  {

            .name                           
=   " TCPv6 " ,

            .owner                          
=  THIS_MODULE,

            .close                           
=  tcp_close,

            .connect                       
=  tcp_v6_connect,

            .disconnect                   
=  tcp_disconnect,

            .accept                         
=  inet_csk_accept,

            .ioctl                             
=  tcp_ioctl,

            .init                               
=  tcp_v6_init_sock,

            .destroy                        
=  tcp_v6_destroy_sock,

            .shutdown                     
=  tcp_shutdown,

            .setsockopt                   
=  tcp_setsockopt,

            .getsockopt                  
=  tcp_getsockopt,

            .sendmsg                      
=  tcp_sendmsg,

            .recvmsg                       
=  tcp_recvmsg,

            .backlog_rcv                
=  tcp_v6_do_rcv,

            .hash                            
=  tcp_v6_hash,

            .unhash                         
=  tcp_unhash,

            .get_port                      
=  tcp_v6_get_port,

            .enter_memory_pressure           
=  tcp_enter_memory_pressure,

            .sockets_allocated        
=   & tcp_sockets_allocated,

            .memory_allocated       
=   & tcp_memory_allocated,

            .memory_pressure        
=   & tcp_memory_pressure,

            .orphan_count              
=   & tcp_orphan_count,

            .sysctl_mem                  
=  sysctl_tcp_mem,

            .sysctl_wmem               
=  sysctl_tcp_wmem,

            .sysctl_rmem                
=  sysctl_tcp_rmem,

            .max_header                 
=  MAX_TCP_HEADER,

            .obj_size                       
=   sizeof ( struct  tcp6_sock),

            .twsk_obj_size             
=   sizeof ( struct  tcp6_timewait_sock),

            .rsk_prot                      
=   & tcp6_request_sock_ops,

};

 

 

static   struct  inet_protosw tcpv6_protosw  =  {

            .type                 
=           SOCK_STREAM,

            .protocol          
=           IPPROTO_TCP,

            .prot                 
=            & tcpv6_prot,

            .ops                  
=            & inet6_stream_ops,

            .capability         
=            - 1 ,

            .no_check        
=            0 ,

            .flags                
=           INET_PROTOSW_PERMANENT,

};

 

 

5.2.                    Socket 层和传输层关联表

 

Linux INET6 协议族定义一个 struct inet_protosw 的链表数组inetsw6[]

要支持某种传输层协议,首先实现相应的 struct proto_opsstruct proto,然后实现struct inet_protosw,将两者关联,最后,通过 inet6_register_protosw() ,将此 struct inet_protosw 注册到 inet6_sw[] 中。

 

注册的时候,根据 struct inet_protosw type ,将它放到 inet6_sw[type] 所在的链表中,相同的 type, 不同的 protocol,会在同一个链表上。

 

 

 


6.   建立数据结构之间的关系

 

user space 角度看,要使用 INET6 协议族的某种传输层协议,首先需要通过 socket() 调用创建一个相应的 socket fd,然后再通过这个 socket fd,接收和发送数据。

 

socket() 的原型是:

int socket(int domain, int type, int protocol);

 

domain 指定了协议族.

 

type 表明在网络中通信所遵循的模式。主要的值有:SOCK_STREAMSOCK_DGRAMSOCK_RAW等。

SOCK_STREAM 是面向流的通信方式,而 SOCK_DGRAM 是面向报文的通信方式。不同的通信方式,在接收数据和发送数据时,具有不同的处理方式。

 

Protocol 则指明具体的传输层协议。不同的传输层协议,可能具有相同的 type,例如 TCP SCTP 都是 SOCK_STREAM 类型。

 

socket(PF_INET6, SOCK_STREAM, 0) 为例,在进入内核空间后,

 

根据 domain,找到inet6_family_ops

创建 struct socket

调用inet6_family_opsde  create(),也就是inet6_create()

inet6_create() 根据 type protocol inet6_sw[] 中找到对应的 struct inet_protosw,也就是tcpv6_protosw

创建 struct sock,将 struct socket struct sock 关联起来

struct socket tcpv6_protosw ops ,也就是inet6_stream_ops 关联起来

struct sock tcpv6_protosw prot,也就是tcpv6_prot 关联起来。

 

这样,socket 层和传输层的数据结构之间的关系建立起来了,此后,应用层通过 socket fd 接收或者发送数据的时候,就会先经过 socket inet6_stream_ops 处理,然后经过传输层的 tcpv6_prot 处理。

 

 

下图描述了 socket 层与传输层之间数据结构关系的建立过程,以及发送数据时,是如何通过这些数据结构进行交互的。





7.   网络层协议类型

 

注册协议族,是从面向 user space 的角度来考虑的。当注册一个新的协议族后,user space 就可以创建此协议族的 socket,并通过此 socket 来接收和发送数据了。

而从面向设备驱动层的角度来考虑,通常需要为新的协议族注册一个网络层协议类型(或者叫“包类型”)。我们知道当网络设备接收到数据包之后,经过 L2 处理之后,需要根据其网络层协议类型,进行进一步处理。对于 INET 协议族来说,就是 IP 协议,而对于 INET6 协议族来说,就是 IPv6 协议。

因此,要实现一个新的协议族,还必须提供对网络层协议包进行处理的方法。

这个结构是 struct packet_type

           

struct  packet_type {

            __be16                                    type;     
/*  This is really htons(ether_type).  */

            
struct  net_device            * dev;     /*  NULL is wildcarded here           */

            
int                                  ( * func) ( struct  sk_buff  * ,

                                                             
struct  net_device  * ,

                                                             
struct  packet_type  * ,

                                                             
struct  net_device  * );

            
void                                * af_packet_priv;

            
struct  list_head  list;

};

 

这个结构中中的 func  ,就是在网络层对接收到的数据包进行处理的方法。

 

Linux 系统中维护一个 struct packet_type 的数组ptype_base[],一个新的 struct packet_type 变量通过调用 dev_add_packet() ,注册到这个数组中。

在接收数据包的过程中,根据网络层协议类型,到 ptype_base[] 数组中寻找对应的 struct packet_type 变量,然后由它的 func() 做进一步处理。

 


8.   传输层协议类型

 

同样,网络层处理完毕之后,需要交给传输层处理。因此,一个新的传输层协议需要向网络层注册处理函数。

 

INET6 协议来说,这个结构是struct inet6_protocol

 

struct  inet6_protocol

{

            
int          ( * handler)( struct  sk_buff  ** skb, unsigned  int   * nhoffp);

 

            
void       ( * err_handler)( struct  sk_buff  * skb,

                                           
struct  inet6_skb_parm  * opt,

                                           
int  type,  int  code,  int  offset,

                                           __u32 info);

            unsigned 
int        flags;     /*  INET6_PROTO_xxx  */

};

 

这个结构中的 handler ,就是传输层提供给网络层的处理函数。

 

 

Linux 系统中维护一个struct inet6_protocol 的数组inet6_protos[],一个新的struct inet6_protocol 变量通过调用inet6_add_protocol() ,注册到这个数组中。在接收数据包的过程中,根据传输层协议类型,到 inet6_protos[] 数组中寻找对应的 struct inet6_protocol 变量,然后由它的 handler() 做进一步处理。

 


9.   数据包从设备驱动层向上传递处理的过程




10.                     INET6 的初始化

 

经过前面的分析,现在可以理解 INET6 协议族在初始化的时候要做哪些事情:

 

1、注册 INET6 协议族,提供协议族的创建函数。

2、为所支持的传输层协议分别提供 struct proto_opsstruct protostruct inet_protosw 结构,并注册到关联表中。

3、向设备驱动层注册 IPv6 数据包的处理函数

4、向网络层注册 TCPUDPRAW 等传输层的处理函数。

5、其它初始化工作

 

 

10.1.               注册 INET6 协议族

 

对于 INET6 的实现来说,第一步是要注册 INET6 协议族。

 

static   struct  net_proto_family inet6_family_ops  =  {

            .family 
=  PF_INET6,

            .create 
=  inet6_create,

            .owner  
=  THIS_MODULE,

};

 

sock_register(
& inet6_family_ops);


inet6_create() 的实现:TBW

 

10.2.               TCPUDPRAW 等传输层协议提供关联表

 

1、初始化关联表

for (r  =   & inetsw6[ 0 ]; r  <   & inetsw6[SOCK_MAX];  ++ r)

                        INIT_LIST_HEAD(r);

 

2RAW 的关联

 

static   struct  proto_ops inet6_sockraw_ops  =  {

            .family 
=            PF_INET6,

            .owner 
=           THIS_MODULE,

            .release 
=          inet6_release,

            .bind 
=              inet6_bind,

            .connect 
=         inet_dgram_connect,                  /*  ok                 */

            .socketpair 
=     sock_no_socketpair,                 /*  a do nothing   */

            .accept 
=           sock_no_accept,                                   /*  a do nothing   */

            .getname 
=        inet6_getname,

            .poll 
=               datagram_poll,                           /*  ok                 */

            .ioctl 
=   inet6_ioctl,                                 /*  must change   */

            .listen 
=             sock_no_listen,                          /*  ok                 */

            .shutdown 
=      inet_shutdown,                          /*  ok                 */

            .setsockopt 
=    sock_common_setsockopt,                   /*  ok                 */

            .getsockopt 
=    sock_common_getsockopt,                   /*  ok                 */

            .sendmsg 
=        inet_sendmsg,                            /*  ok                 */

            .recvmsg 
=        sock_common_recvmsg,                       /*  ok                 */

            .mmap 
=                        sock_no_mmap,

            .sendpage 
=       sock_no_sendpage,

};

 

struct  proto rawv6_prot  =  {

            .name 
=                          " RAWv6 " ,

            .owner 
=           THIS_MODULE,

            .close 
=             rawv6_close,

            .connect 
=         ip6_datagram_connect,

            .disconnect 
=     udp_disconnect,

            .ioctl 
=   rawv6_ioctl,

            .init 
=                 rawv6_init_sk,

            .destroy 
=          inet6_destroy_sock,

            .setsockopt 
=    rawv6_setsockopt,

            .getsockopt 
=    rawv6_getsockopt,

            .sendmsg 
=        rawv6_sendmsg,

            .recvmsg 
=        rawv6_recvmsg,

            .bind 
=              rawv6_bind,

            .backlog_rcv 
=  rawv6_rcv_skb,

            .hash 
=              raw_v6_hash,

            .unhash 
=           raw_v6_unhash,

            .obj_size 
=         sizeof ( struct  raw6_sock),

};

 

static   struct  inet_protosw rawv6_protosw  =  {

            .type                 
=  SOCK_RAW,

            .protocol          
=  IPPROTO_IP,           /*  wild card  */

            .prot                 
=   & rawv6_prot,

            .ops                  
=   & inet6_sockraw_ops,

            .capability         
=  CAP_NET_RAW,

            .no_check        
=  UDP_CSUM_DEFAULT,

            .flags                
=  INET_PROTOSW_REUSE,

};

 

inet6_register_protosw(
& rawv6_protosw);

 

3UDP 的关联

struct  proto_ops inet6_dgram_ops  =  {

            .family 
=            PF_INET6,

            .owner 
=           THIS_MODULE,

            .release 
=          inet6_release,

            .bind 
=              inet6_bind,

            .connect 
=         inet_dgram_connect,                  /*  ok                 */

            .socketpair 
=     sock_no_socketpair,                 /*  a do nothing   */

            .accept 
=           sock_no_accept,                                   /*  a do nothing   */

            .getname 
=        inet6_getname,

            .poll 
=               udp_poll,                                   /*  ok                 */

            .ioctl 
=   inet6_ioctl,                                 /*  must change   */

            .listen 
=             sock_no_listen,                          /*  ok                 */

            .shutdown 
=      inet_shutdown,                          /*  ok                 */

            .setsockopt 
=    sock_common_setsockopt,                   /*  ok                 */

            .getsockopt 
=    sock_common_getsockopt,                   /*  ok                 */

            .sendmsg 
=        inet_sendmsg,                            /*  ok                 */

            .recvmsg 
=        sock_common_recvmsg,                       /*  ok                 */

            .mmap 
=                        sock_no_mmap,

            .sendpage 
=       sock_no_sendpage,

};

 

struct  proto udpv6_prot  =  {

            .name 
=                          " UDPv6 " ,

            .owner 
=           THIS_MODULE,

            .close 
=             udpv6_close,

            .connect 
=         ip6_datagram_connect,

            .disconnect 
=     udp_disconnect,

            .ioctl 
=   udp_ioctl,

            .destroy 
=          udpv6_destroy_sock,

            .setsockopt 
=    udpv6_setsockopt,

            .getsockopt 
=    udpv6_getsockopt,

            .sendmsg 
=        udpv6_sendmsg,

            .recvmsg 
=        udpv6_recvmsg,

            .backlog_rcv 
=  udpv6_queue_rcv_skb,

            .hash 
=              udp_v6_hash,

            .unhash 
=           udp_v6_unhash,

            .get_port 
=        udp_v6_get_port,

            .obj_size 
=         sizeof ( struct  udp6_sock),

};

 

static   struct  inet_protosw udpv6_protosw  =  {

            .type 
=       SOCK_DGRAM,

            .protocol 
=   IPPROTO_UDP,

            .prot 
=        & udpv6_prot,

            .ops 
=         & inet6_dgram_ops,

            .capability 
=- 1 ,

            .no_check 
=   UDP_CSUM_DEFAULT,

            .flags 
=      INET_PROTOSW_PERMANENT,

};

 

inet6_register_protosw(
& udpv6_protosw);

 

4TCP 的关联

 

前面已经看过 TCP 相关的结构。

inet6_register_protosw(&tcpv6_protosw);

 

 

 

10.3.               注册 IPv6 包的接收函数

 

static   struct  packet_type ipv6_packet_type  =  {

            .type 
=  __constant_htons(ETH_P_IPV6),

            .func 
=  ipv6_rcv,

};

 

ipv6_packet_init()  ==>  dev_add_pack(&ipv6_packet_type);

 

ipv6_rcv 的具体实现不在本文范围之内。

 

 

10.4.               注册传输层协议

 

 

static   struct  inet6_protocol udpv6_protocol  =  {

            .handler            
=           udpv6_rcv,

            .err_handler      
=           udpv6_err,

            .flags                
=           INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,

};

 

static   struct  inet6_protocol tcpv6_protocol  =  {

            .handler            
=           tcp_v6_rcv,

            .err_handler      
=           tcp_v6_err,

            .flags                
=           INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,

};

 

 

inet6_add_protocol(
& udpv6_protocol, IPPROTO_UDP);

inet6_add_protocol(
& tcpv6_protocol, IPPROTO_TCP);

 

RAW 不需要注册。

 

Udpv6_rctcp_v6_rcv 的具体实现不在本文范围之内。

 

 

10.5.               其它

 

此外,还要做其它初始化工作,包括 ICMPv6IGMPv6Neighbor discoveryroute 等等的初始化。本文不具体分析。

 


11.                     附录:

 

 

Structure

Register functions

description

 

net_proto_family

sock_register

注册协议族

 

packet_type

dev_add_pack

向设备驱动层注册网络层协议处理函数

 

inet6_protocol

inet6_add_protocol

向网络层注册传输层协议处理函数

 

proto_ops

BSD APIs socket 层的接口

 

 

 

Proto

Socket 层与传输层的接口

 

 

 

inet_protosw

struct proto_ops struct proto 对应起来

inet6_register_protosw

注册到系统的 struct inet_protosw数组 inetsw6

此数组用于创建 socket 之用。

 

Proto

Socket 层与传输层的接口

proto_register

将传输层协议处理函数注册到系统中的 struct proto 的链表 proto_list

这个目的是为了在 proc 系统中显示各种协议的信息

 

 

 

 

 
Logo

更多推荐