setsockopt的工作流程(从用户空间到内核)

来源:互联网 发布:mac xampp设置htdoc 编辑:程序博客网 时间:2024/05/22 13:59

一、用户空间

获取套接口选项:

1.  int getsockopt ( int sockfd, int level, int optname, void * optval, socklen_t *opteln ) 设置套接口选项:

2.  int setsockopt ( int sockfd, int level, int optname, const void * optval, socklen_t *opteln )

 

二、内核空间

系统调用setsockopt是通过内核sys_setsockopt函数实现的

  1. asmlinkage long sys_setsockopt(int fd,int level, int optname, char __user *optval, int optlen)
  2. {
  3. ……
  4.     if ((sock = sockfd_lookup_light(fd,&err,&fput_needed))!= NULL)
  5.     {
  6. ……
  7.         if (level == SOL_SOCKET)//基本套接口
  8. 套接口类型(linux/socket.h):
  9. /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
  10. #define SOL_IP          0
  11. /* #define SOL_ICMP       1       No-no-no! Due to Linux :-) we cannot use SOL_ICMP=1 */
  12. #define SOL_TCP               6
  13. #define SOL_UDP              17
  14. ……
  15.             err=sock_setsockopt(sock,level,optname,optval,optlen);
  16.         else
  17.             err=sock->ops->setsockopt(sock, level, optname, optval, optlen);
  18.     ……
  19.     }
  20.     return err;
  21. }
假如我们定义的套接口是IPPROTO_IP=0,所以我们将调用SOL_IP套接口,err=sock->ops->setsockopt(sock, level, optname, optval, optlen),该钩子为proto_ops类型,所以它指向了我们注册的inet_stream_ops钩子,所以setsockopt指向了sock_common_setsockopt函数(net/core/sock.c
net/ipv4/af_inet.c文件中,函数inet_init注册了inetsw_array数组
  1. for(q = inetsw_array; q< &inetsw_array[INETSW_ARRAY_LEN];++q)
  2.         inet_register_protosw(q);
  1. static struct inet_protosw inetsw_array[]=
  2. {
  3.         {
  4.                 .type= SOCK_STREAM,
  5.                 .protocol= IPPROTO_TCP,
  6.                 .prot= &tcp_prot,
  7.                 .ops= &inet_stream_ops,
  8.                 .capability= -1,
  9.                 .no_check= 0,
  10.                 .flags= INET_PROTOSW_PERMANENT |
  11.              INET_PROTOSW_ICSK,
  12.         },
  13.         ……
  14. };

譬如它注册了inet_stream_ops针对tcp协议

  1. const struct proto_ops inet_stream_ops= {
  2.     .family        = PF_INET,
  3.     .owner        = THIS_MODULE,
  4.     ……
  5.     .setsockopt    = sock_common_setsockopt,
  6.     .getsockopt    = sock_common_getsockopt,
  7. ……
  8. };

现在我们看接口函数sock_common_setsockopt

  1. int sock_common_setsockopt(struct socket*sock, int level, int optname,
  2.              char __user *optval, int optlen)
  3. {
  4.     struct sock *sk= sock->sk;
  5.     return sk->sk_prot->setsockopt(sk, level, optname, optval, optlen);
  6. }

由于sk->sk_prot是指向协议的钩子,如果它指向了tcp协议钩子,setsockopt指向了tcp_setsockopt函数(net/ipv4/tcp.c

  1. struct proto tcp_prot= {
  2.     .name            ="TCP",
  3.     .owner            = THIS_MODULE,
  4.     ……
  5.     .setsockopt        = tcp_setsockopt,
  6.     .getsockopt        = tcp_getsockopt,
  7.     ……
  8. }

 

  1. int tcp_setsockopt(struct sock*sk, int level, int optname, char __user *optval,
  2.          int optlen)
  3. {
  4.     struct inet_connection_sock *icsk = inet_csk(sk);

  5.     if (level != SOL_TCP)//显然我们注册的是SOL_IP所以执行下面语句
  6.         return icsk->icsk_af_ops->setsockopt(sk, level, optname,
  7.                          optval, optlen);
  8.     return do_tcp_setsockopt(sk, level, optname, optval, optlen);//如果注册时SOL_TCP就执行
  9. }

如果注册时SOL_IP则由于isck->isck_af_opsinet_connection_sock_af_ops类型的钩子,此钩子的注册为

  1. struct inet_connection_sock_af_ops ipv4_specific= {
  2. ……
  3.     .setsockopt    = ip_setsockopt,
  4.     .getsockopt    = ip_getsockopt,
  5. ……
  6. };

注册ip_setsockoptnet/ipv4/tcp_ipv4.c)接口

  1. int ip_setsockopt(struct sock*sk, int level,
  2.         int optname, char __user*optval, int optlen)
  3. {
  4.     int err;
  5.     if (level != SOL_IP)
  6.         return -ENOPROTOOPT;
  7.     err = do_ip_setsockopt(sk, level, optname, optval, optlen);//注册是IPPROTO_IP执行
  8. #ifdef CONFIG_NETFILTER
  9.     /* we needto exclude all possible ENOPROTOOPTs except defaultcase */
  10.     if (err ==-ENOPROTOOPT && optname!= IP_HDRINCL&&
  11.         optname != IP_IPSEC_POLICY&& optname != IP_XFRM_POLICY
  12. #ifdef CONFIG_IP_MROUTE
  13.         &&(optname < MRT_BASE|| optname > (MRT_BASE + 10))
  14. #endif
  15.      ) {
  16.         lock_sock(sk);
  17.         err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);//如果编译选项中设置了CONFIG_NETFILTER,就调用nf_setsockopt函数
  18.         release_sock(sk);
  19.     }
  20. #endif
  21.     return err;
  22. }

到此我们知道了如果注册时IPPROTO_IP最终会调用do_ip_setsockopt和nf_setsockopt函数

如果注册了IPPROTO_TCP最终会调用do_tcp_setsockopt函数

0 0
原创粉丝点击