一、用户空间
获取套接口选项:
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函数实现的
- asmlinkage long sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen)
- {
- ……
- if ((sock = sockfd_lookup_light(fd, &err, &fput_needed)) != NULL)
- {
- ……
- if (level == SOL_SOCKET)//基本套接口
- 套接口类型(linux/socket.h):
- /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
- #define SOL_IP 0
- /* #define SOL_ICMP 1 No-no-no! Due to Linux :-) we cannot use SOL_ICMP=1 */
- #define SOL_TCP 6
- #define SOL_UDP 17
- ……
- err=sock_setsockopt(sock,level,optname,optval,optlen);
- else
- err=sock->ops->setsockopt(sock, level, optname, optval, optlen);
- ……
- }
- return err;
- }
假如我们定义的套接口是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数组
- for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
- inet_register_protosw(q);
- 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 |
- INET_PROTOSW_ICSK,
- },
- ……
- };
譬如它注册了inet_stream_ops针对tcp协议
- const struct proto_ops inet_stream_ops = {
- .family = PF_INET,
- .owner = THIS_MODULE,
- ……
- .setsockopt = sock_common_setsockopt,
- .getsockopt = sock_common_getsockopt,
- ……
- };
现在我们看接口函数sock_common_setsockopt
- int sock_common_setsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int optlen)
- {
- struct sock *sk = sock->sk;
- return sk->sk_prot->setsockopt(sk, level, optname, optval, optlen);
- }
由于sk->sk_prot是指向协议的钩子,如果它指向了tcp协议钩子,setsockopt指向了tcp_setsockopt函数(net/ipv4/tcp.c)
- struct proto tcp_prot = {
- .name = "TCP",
- .owner = THIS_MODULE,
- ……
- .setsockopt = tcp_setsockopt,
- .getsockopt = tcp_getsockopt,
- ……
- }
- int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
- int optlen)
- {
- struct inet_connection_sock *icsk = inet_csk(sk);
- if (level != SOL_TCP)//显然我们注册的是SOL_IP所以执行下面语句
- return icsk->icsk_af_ops->setsockopt(sk, level, optname,
- optval, optlen);
- return do_tcp_setsockopt(sk, level, optname, optval, optlen);//如果注册时SOL_TCP就执行
- }
如果注册时SOL_IP则由于isck->isck_af_ops是inet_connection_sock_af_ops类型的钩子,此钩子的注册为
- struct inet_connection_sock_af_ops ipv4_specific = {
- ……
- .setsockopt = ip_setsockopt,
- .getsockopt = ip_getsockopt,
- ……
- };
注册ip_setsockopt(net/ipv4/tcp_ipv4.c)接口
- int ip_setsockopt(struct sock *sk, int level,
- int optname, char __user *optval, int optlen)
- {
- int err;
- if (level != SOL_IP)
- return -ENOPROTOOPT;
- err = do_ip_setsockopt(sk, level, optname, optval, optlen);//注册是IPPROTO_IP执行
- #ifdef CONFIG_NETFILTER
- /* we need to exclude all possible ENOPROTOOPTs except default case */
- if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
- optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY
- #ifdef CONFIG_IP_MROUTE
- && (optname < MRT_BASE || optname > (MRT_BASE + 10))
- #endif
- ) {
- lock_sock(sk);
- err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);//如果编译选项中设置了CONFIG_NETFILTER,就调用nf_setsockopt函数
- release_sock(sk);
- }
- #endif
- return err;
- }
到此我们知道了如果注册时IPPROTO_IP最终会调用do_ip_setsockopt和nf_setsockopt函数
如果注册了IPPROTO_TCP最终会调用do_tcp_setsockopt函数
阅读(4545) | 评论(0) | 转发(2) |