内核版本:2.6.21.5
1. 还是先贴上原型吧:
int setsockopt (int socket, int level, int optname, void *optval, socklen toptlen)
我们都知道这个函数是用来设置socket属性的,通过这个函数可以设置接收和发送缓冲区大小,端口复用等。从这里我们可以猜到这个函数是通过设置struct sock这个结构某些字段来实现的。现在我们来看看这个函数,然后慢慢加上各个属性的
意义,即设置了一个属性,网络会产生什么反应。
2.
-
/*
-
* Set a socket option. Because we don't know the option lengths we have
-
* to pass the user mode parameter for the protocols to sort out.
-
*/
-
-
asmlinkage long sys_setsockopt(int fd, int level, int optname,
-
char __user *optval, int optlen)
-
{
-
int err, fput_needed;
-
struct socket *sock;
-
-
if (optlen < 0)
-
return -EINVAL;
-
-
sock = sockfd_lookup_light(fd, &err, &fput_needed);
-
if (sock != NULL) {
-
err = security_socket_setsockopt(sock, level, optname);
-
if (err)
-
goto out_put;
-
-
if (level == SOL_SOCKET) //从这里我们看到根据level的不同,有2大类可以设置,一个就是对socket进行设置,另一个就是根据具体协议进行设置
-
err =
-
sock_setsockopt(sock, level, optname, optval,
-
optlen);
-
else
-
err =
-
sock->ops->setsockopt(sock, level, optname, optval,
-
optlen);
-
out_put:
-
fput_light(sock->file, fput_needed);
-
}
-
return err;
-
}
3. 先看对socket属性的设置
-
/*
-
* This is meant for all protocols to use and covers goings on
-
* at the socket level. Everything here is generic.
-
*/
-
-
int sock_setsockopt(struct socket *sock, int level, int optname,
-
char __user *optval, int optlen)
-
{
-
struct sock *sk=sock->sk;
-
struct sk_filter *filter;
-
int val;
-
int valbool;
-
struct linger ling;
-
int ret = 0;
-
-
/*
-
* Options without arguments
-
*/
-
-
#ifdef SO_DONTLINGER /* Compatibility item... */
-
if (optname == SO_DONTLINGER) {
-
lock_sock(sk);
-
sock_reset_flag(sk, SOCK_LINGER);
-
release_sock(sk);
-
return 0;
-
}
-
#endif
-
-
if(optlen<sizeof(int))
-
return(-EINVAL);
-
-
if (get_user(val, (int __user *)optval)) //从用户空间向内核空间传递参数,get_user 和 put_user这两个函数只能传递一些简单类型的变量,像char,int, long等类型,对一些大数据块的传递
-
//要用copy_from_user和copy_to_user
-
return -EFAULT;
-
-
valbool = val?1:0; //从这里我们看到参数被分为1和0
-
-
lock_sock(sk);
-
-
switch(optname)
-
{
-
case SO_DEBUG:
-
if(val && !capable(CAP_NET_ADMIN))
-
{
-
ret = -EACCES;
-
}
-
else if (valbool)
-
sock_set_flag(sk, SOCK_DBG);
-
else
-
sock_reset_flag(sk, SOCK_DBG);
-
break;
-
case SO_REUSEADDR: //这里就是对端口复用的设置,还记得我们在bind socket的时候,只有设置了这一个属性不同的socket才能bind到同一个端口上
-
sk->sk_reuse = valbool;
-
break;
-
case SO_TYPE:
-
case SO_ERROR:
-
ret = -ENOPROTOOPT;
-
break;
-
case SO_DONTROUTE:
-
if (valbool)
-
sock_set_flag(sk, SOCK_LOCALROUTE);
-
else
-
sock_reset_flag(sk, SOCK_LOCALROUTE);
-
break;
-
case SO_BROADCAST:
-
sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
-
break;
-
case SO_SNDBUF:
-
/* Don't error on this BSD doesn't and if you think
-
about it this is right. Otherwise apps have to
-
play 'guess the biggest size' games. RCVBUF/SNDBUF
-
are treated in BSD as hints */
-
-
if (val > sysctl_wmem_max)
-
val = sysctl_wmem_max;
-
set_sndbuf:
-
sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
-
if ((val * 2) < SOCK_MIN_SNDBUF)
-
sk->sk_sndbuf = SOCK_MIN_SNDBUF;
-
else
-
sk->sk_sndbuf = val * 2;
-
-
/*
-
* Wake up sending tasks if we
-
* upped the value.
-
*/
-
sk->sk_write_space(sk);
-
break;
-
-
case SO_SNDBUFFORCE:
-
if (!capable(CAP_NET_ADMIN)) {
-
ret = -EPERM;
-
break;
-
}
-
goto set_sndbuf;
-
-
case SO_RCVBUF: //这里对接收缓冲区大小进行的设置
-
/* Don't error on this BSD doesn't and if you think
-
about it this is right. Otherwise apps have to
-
play 'guess the biggest size' games. RCVBUF/SNDBUF
-
are treated in BSD as hints */
-
-
if (val > sysctl_rmem_max) //看到了没,这里不允许用户随意设置呢,有最大值,得用sysctl修改,或在/proc目录下调整这个值
-
val = sysctl_rmem_max;
-
set_rcvbuf:
-
sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
-
/*
-
* We double it on the way in to account for
-
* "struct sk_buff" etc. overhead. Applications
-
* assume that the SO_RCVBUF setting they make will
-
* allow that much actual data to be received on that
-
* socket.
-
*
-
* Applications are unaware that "struct sk_buff" and
-
* other overheads allocate from the receive buffer
-
* during socket buffer allocation.
-
*
-
* And after considering the possible alternatives,
-
* returning the value we actually used in getsockopt
-
* is the most desirable behavior.
-
*/
-
if ((val * 2) < SOCK_MIN_RCVBUF) //这里是最小值256
-
sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
-
else
-
sk->sk_rcvbuf = val * 2; //设置的值要加倍
-
break;
-
-
case SO_RCVBUFFORCE: //看到没这个是force,允许野蛮...
-
if (!capable(CAP_NET_ADMIN)) {
-
ret = -EPERM;
-
break;
-
}
-
goto set_rcvbuf;
-
-
case SO_KEEPALIVE:
-
#ifdef CONFIG_INET
-
if (sk->sk_protocol == IPPROTO_TCP)
-
tcp_set_keepalive(sk, valbool);
-
#endif
-
sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
-
break;
-
-
case SO_OOBINLINE:
-
sock_valbool_flag(sk, SOCK_URGINLINE, valbool);
-
break;
-
-
case SO_NO_CHECK:
-
sk->sk_no_check = valbool;
-
break;
-
-
case SO_PRIORITY:
-
if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
-
sk->sk_priority = val;
-
else
-
ret = -EPERM;
-
break;
-
-
case SO_LINGER:
-
if(optlen<sizeof(ling)) {
-
ret = -EINVAL; /* 1003.1g */
-
break;
-
}
-
if (copy_from_user(&ling,optval,sizeof(ling))) {
-
ret = -EFAULT;
-
break;
-
}
-
if (!ling.l_onoff)
-
sock_reset_flag(sk, SOCK_LINGER);
-
else {
-
#if (BITS_PER_LONG == 32)
-
if ((unsigned int)ling.l_linger >= MAX_SCHEDULE_TIMEOUT/HZ)
-
sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
-
else
-
#endif
-
sk->sk_lingertime = (unsigned int)ling.l_linger * HZ;
-
sock_set_flag(sk, SOCK_LINGER);
-
}
-
break;
-
-
case SO_BSDCOMPAT:
-
sock_warn_obsolete_bsdism("setsockopt");
-
break;
-
-
case SO_PASSCRED:
-
if (valbool)
-
set_bit(SOCK_PASSCRED, &sock->flags);
-
else
-
clear_bit(SOCK_PASSCRED, &sock->flags);
-
break;
-
-
case SO_TIMESTAMP:
-
if (valbool) {
-
sock_set_flag(sk, SOCK_RCVTSTAMP);
-
sock_enable_timestamp(sk);
-
} else
-
sock_reset_flag(sk, SOCK_RCVTSTAMP);
-
break;
-
-
case SO_RCVLOWAT:
-
if (val < 0)
-
val = INT_MAX;
-
sk->sk_rcvlowat = val ? : 1;
-
break;
-
-
case SO_RCVTIMEO: //还记得这么吗,这个就是阻塞在recvfrom函数的时间设置,等待skb的时间
-
ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen);
-
break;
-
-
case SO_SNDTIMEO:
-
ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
-
break;
-
-
#ifdef CONFIG_NETDEVICES
-
case SO_BINDTODEVICE:
-
{
-
char devname[IFNAMSIZ];
-
-
/* Sorry... */
-
if (!capable(CAP_NET_RAW)) {
-
ret = -EPERM;
-
break;
-
}
-
-
/* Bind this socket to a particular device like "eth0",
-
* as specified in the passed interface name. If the
-
* name is "" or the option length is zero the socket
-
* is not bound.
-
*/
-
-
if (!valbool) {
-
sk->sk_bound_dev_if = 0;
-
} else {
-
if (optlen > IFNAMSIZ - 1)
-
optlen = IFNAMSIZ - 1;
-
memset(devname, 0, sizeof(devname));
-
if (copy_from_user(devname, optval, optlen)) {
-
ret = -EFAULT;
-
break;
-
}
-
-
/* Remove any cached route for this socket. */
-
sk_dst_reset(sk);
-
-
if (devname[0] == '\0') {
-
sk->sk_bound_dev_if = 0;
-
} else {
-
struct net_device *dev = dev_get_by_name(devname);
-
if (!dev) {
-
ret = -ENODEV;
-
break;
-
}
-
sk->sk_bound_dev_if = dev->ifindex;
-
dev_put(dev);
-
}
-
}
-
break;
-
}
-
#endif
-
-
-
case SO_ATTACH_FILTER:
-
ret = -EINVAL;
-
if (optlen == sizeof(struct sock_fprog)) {
-
struct sock_fprog fprog;
-
-
ret = -EFAULT;
-
if (copy_from_user(&fprog, optval, sizeof(fprog)))
-
break;
-
-
ret = sk_attach_filter(&fprog, sk);
-
}
-
break;
-
-
case SO_DETACH_FILTER:
-
rcu_read_lock_bh();
-
filter = rcu_dereference(sk->sk_filter);
-
if (filter) {
-
rcu_assign_pointer(sk->sk_filter, NULL);
-
sk_filter_release(sk, filter);
-
rcu_read_unlock_bh();
-
break;
-
}
-
rcu_read_unlock_bh();
-
ret = -ENONET;
-
break;
-
-
case SO_PASSSEC:
-
if (valbool)
-
set_bit(SOCK_PASSSEC, &sock->flags);
-
else
-
clear_bit(SOCK_PASSSEC, &sock->flags);
-
break;
-
-
/* We implement the SO_SNDLOWAT etc to
-
not be settable (1003.1g 5.3) */
-
default:
-
ret = -ENOPROTOOPT;
-
break;
-
}
-
release_sock(sk);
-
return ret;
-
}
4. 我们看看通过这个函数对udp属性的设置
一路下去会进入这个函数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);
-
}
-
-
-
-
/*
-
* Socket option code for UDP
-
*/
-
int udp_lib_setsockopt(struct sock *sk, int level, int optname,
-
char __user *optval, int optlen,
-
int (*push_pending_frames)(struct sock *))
-
{
-
struct udp_sock *up = udp_sk(sk);
-
int val;
-
int err = 0;
-
-
if(optlen<sizeof(int))
-
return -EINVAL;
-
-
if (get_user(val, (int __user *)optval))
-
return -EFAULT;
-
-
switch(optname) {
-
case UDP_CORK:
-
if (val != 0) {
-
up->corkflag = 1; //这个属性是设置是否允许小块数据合并成一个大快数据发送,把木塞堵住。默认不设置,所以当我们用sendto(udp)发送小数据的时候,在对端立即
-
//会收到,但当我们设置这个属性的时候,sendto小数据块的时候,在对端不会立即收到,不信你试试
-
} else {
-
up->corkflag = 0;
-
lock_sock(sk);
-
(*push_pending_frames)(sk);
-
release_sock(sk);
-
}
-
break;
-
-
case UDP_ENCAP:
-
switch (val) {
-
case 0:
-
case UDP_ENCAP_ESPINUDP:
-
case UDP_ENCAP_ESPINUDP_NON_IKE:
-
up->encap_type = val;
-
break;
-
default:
-
err = -ENOPROTOOPT;
-
break;
-
}
-
break;
-
-
/*
-
* UDP-Lite
阅读(7543) | 评论(0) | 转发(0) |