linux 下getsockopt()和setsockopt()分析

来源:互联网 发布:js 禁用radio 编辑:程序博客网 时间:2024/05/13 06:07
1:在linux socket编译成中,我们经常使用如下类似的命令:


       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
如:
struct ip_mreq host_madd_ifadd; //struct init instance
host_madd_ifadd.imr_multiaddr.s_addr=inet_addr(MCAST_ADDR);
host_madd_ifadd.imr_interface.s_addr=inet_addr(LocalIPAddr);   
//sever host join into multicast.将本机加入组播组,足有加入组才能接受到消息。
setsockopt(soketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,(void *)&host_madd_ifadd, sizeof( host_madd_ifadd ));


在linux中每一个协议如:TCP,UDP,RAW类型的协议,其表示的数据结构为:现以UDP为例

struct proto udp_prot = {
.name   = "UDP",
.owner   = THIS_MODULE,
.close   = udp_lib_close,
.connect   = ip4_datagram_connect,
.disconnect   = udp_disconnect,
.ioctl   = udp_ioctl,
.destroy   = udp_destroy_sock,
.setsockopt  = udp_setsockopt,
.getsockopt   = udp_getsockopt,

.sendmsg   = udp_sendmsg,
.recvmsg   = udp_recvmsg,
.sendpage   = udp_sendpage,
.backlog_rcv   = __udp_queue_rcv_skb,
.hash   = udp_lib_hash,
.unhash   = udp_lib_unhash,
.get_port   = udp_v4_get_port,
.memory_allocated  = &udp_memory_allocated,
.sysctl_mem   = sysctl_udp_mem,
.sysctl_wmem   = &sysctl_udp_wmem_min,
.sysctl_rmem   = &sysctl_udp_rmem_min,
.obj_size   = sizeof(struct udp_sock),
.slab_flags   = SLAB_DESTROY_BY_RCU,
.h.udp_table   = &udp_table,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
#endif
};在上面的数据结构中有:setsockopt,getsockopt函数指针,上面的例子中:
其中IPPROTO_IP的定义为:  IPPROTO_IP = 0, /* Dummy protocol for TCP*/
#define SOL_IP 0
#define SOL_TCP 6
#define SOL_UDP 17

IPPROTO_IP = 0, /* Dummy protocol for TCP*/
IPPROTO_TCP = 6, /* Transmission Control Protocol*/
IPPROTO_UDP = 17, /* User Datagram Protocol*

上面的值需要保持一致。 
setsockopt(soketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,(void *)&host_madd_ifadd, sizeof( host_madd_ifadd ));
将调用此 .setsockopt  = udp_setsockopt,函数定义。最终将会调用:udp_setsockopt()函数。

int udp_setsockopt(struct sock *sk, int level, int optname,char __user *optval, int optlen)
{
if (level == SOL_UDP  ||  level == SOL_UDPLITE)
return udp_lib_setsockopt(sk, level, optname, optval, optlen,udp_push_pending_frames);
return ip_setsockopt(sk, level, optname, optval, optlen);
}
上面的setsockopt()中 level=IPPROTO_IP,optname=IP_ADD_MEMBERSHIP,
其上面的setsockopt会触发内核中的如下函数
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);
#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 &&
!ip_mroute_opt(optname)) 

{

lock_sock(sk);
err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
release_sock(sk);
}
#endif
return err;
}
上面中的 level=IPPROTO_IP,optname=IP_ADD_MEMBERSHIP时将执行do_ip_setsockopt()函数中的。

static int do_ip_setsockopt(struct sock *sk, int level,int optname, char __user *optval, int optlen)
{
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
{
struct ip_mreqn mreq;
err = -EPROTO;
if (inet_sk(sk)->is_icsk)
break;
if (optlen < sizeof(struct ip_mreq))
goto e_inval;
err = -EFAULT;
if (optlen >= sizeof(struct ip_mreqn)) {
if (copy_from_user(&mreq, optval, sizeof(mreq)))
break;
} else {
memset(&mreq, 0, sizeof(mreq));
if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
break;
}
if (optname == IP_ADD_MEMBERSHIP)
err = ip_mc_join_group(sk, &mreq);
else
err = ip_mc_leave_group(sk, &mreq);
break;
}//实现了用户态数据和内核数据的交互。
}


需要注意的是:ip_setsockopt()函数中nf_setsockopt()函数是用来处理netfilter中注册的sockopt命令字。

2:
在linux内核netfilter中使用
int nf_register_sockopt(struct nf_sockopt_ops *reg);和nf_unregister_sockopt(struct nf_sockopt_ops *reg);
动态的注册和注销sockopt命令字。其中orgi_ipt_sockopts的定义如下:
/*those initial value will be overwrited by orignal iptables sockopts*/
static struct nf_sockopt_ops orgi_ipt_sockopts =
{
    /*pls check linux/in.h*/
#define IPT_TEMP_BASE_CTL       60
#define IPT_TEMP_SET_MAX        (IPT_TEMP_BASE_CTL+1)
#define IPT_TEMP_GET_MAX        (IPT_TEMP_BASE_CTL+2)
    .pf = PF_INET,
    .set_optmin = IPT_TEMP_BASE_CTL,
    .set_optmax = IPT_TEMP_SET_MAX,
    .set = nat_ipt_set_ctl,
    .get_optmin = IPT_TEMP_BASE_CTL,
    .get_optmax = IPT_TEMP_GET_MAX,
    .get = nat_ipt_get_ctl
};
上面IPT_TEMP_BASE_CTL定义的是自己的sockopt命令字,其不能于内核中的取值相同。
使用nf_register_sockopt(&orgi_ipt_sockopts);将定义的nf_sockopt_ops注册进内核:
使用nf_unregister_sockopt(&orgi_ipt_sockopts);将定义的nf_sockopt_ops注销:

注册的orgi_ipt_sockopts的执行将会使用如下的函数:
int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,int len)
{
return nf_sockopt(sk, pf, val, opt, &len, 0);
}
int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,int *len)
{
return nf_sockopt(sk, pf, val, opt, len, 1);
}
static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,char __user *opt, int *len, int get)
{
struct nf_sockopt_ops *ops;
int ret;
ops = nf_sockopt_find(sk, pf, val, get);
if (IS_ERR(ops))
return PTR_ERR(ops);
if (get)
ret = ops->get(sk, val, opt, len);
else
ret = ops->set(sk, val, opt, *len);


module_put(ops->owner);
return ret;
}
在上面的函数中使用参数pf,val,和get,执行nf_sockopt_find(sk, pf, val, get);找出nf_sockopt_ops。
对于上面自己定义的orgi_ipt_sockopts中的pf,val,和get的值分别为:pf=PF_INET,val=IPT_TEMP_BASE_CTL.get=0.
根据这三个参数将获得自定义的orgi_ipt_sockopts。之后根据get来执行set还是get函数,最终会执行自己定义的:
nat_ipt_set_ctl()和nat_ipt_get_ctl()函数。