setsockopt函数

7500阅读 0评论2013-12-14 liubangbo
分类:LINUX

内核版本:2.6.21.5
1. 还是先贴上原型吧:
   int setsockopt (int socket,  int level,  int optname,  void *optval,  socklen toptlen)
  我们都知道这个函数是用来设置socket属性的,通过这个函数可以设置接收和发送缓冲区大小,端口复用等。从这里我们可以猜到这个函数是通过设置struct sock这个结构某些字段来实现的。现在我们来看看这个函数,然后慢慢加上各个属性的
  意义,即设置了一个属性,网络会产生什么反应。

2.
  

点击(此处)折叠或打开

  1. /*
  2.  *    Set a socket option. Because we don't know the option lengths we have
  3.  *    to pass the user mode parameter for the protocols to sort out.
  4.  */

  5. asmlinkage long sys_setsockopt(int fd, int level, int optname,
  6.              char __user *optval, int optlen)
  7. {
  8.     int err, fput_needed;
  9.     struct socket *sock;

  10.     if (optlen < 0)
  11.         return -EINVAL;

  12.     sock = sockfd_lookup_light(fd, &err, &fput_needed);
  13.     if (sock != NULL) {
  14.         err = security_socket_setsockopt(sock, level, optname);
  15.         if (err)
  16.             goto out_put;

  17.         if (level == SOL_SOCKET)     //从这里我们看到根据level的不同,有2大类可以设置,一个就是对socket进行设置,另一个就是根据具体协议进行设置
  18.             err =
  19.              sock_setsockopt(sock, level, optname, optval,
  20.                      optlen);
  21.         else
  22.             err =
  23.              sock->ops->setsockopt(sock, level, optname, optval,
  24.                          optlen);
  25. out_put:
  26.         fput_light(sock->file, fput_needed);
  27.     }
  28.     return err;
  29. }
3. 先看对socket属性的设置
   

点击(此处)折叠或打开

  1. /*
  2.  *    This is meant for all protocols to use and covers goings on
  3.  *    at the socket level. Everything here is generic.
  4.  */

  5. int sock_setsockopt(struct socket *sock, int level, int optname,
  6.          char __user *optval, int optlen)
  7. {
  8.     struct sock *sk=sock->sk;
  9.     struct sk_filter *filter;
  10.     int val;
  11.     int valbool;
  12.     struct linger ling;
  13.     int ret = 0;

  14.     /*
  15.      *    Options without arguments
  16.      */

  17. #ifdef SO_DONTLINGER        /* Compatibility item... */
  18.     if (optname == SO_DONTLINGER) {
  19.         lock_sock(sk);
  20.         sock_reset_flag(sk, SOCK_LINGER);
  21.         release_sock(sk);
  22.         return 0;
  23.     }
  24. #endif

  25.     if(optlen<sizeof(int))
  26.         return(-EINVAL);

  27.     if (get_user(val, (int __user *)optval))  //从用户空间向内核空间传递参数,get_user 和 put_user这两个函数只能传递一些简单类型的变量,像char,int, long等类型,对一些大数据块的传递
  28.                                               //要用copy_from_user和copy_to_user
  29.         return -EFAULT;

  30.     valbool = val?1:0;  //从这里我们看到参数被分为1和0

  31.     lock_sock(sk);

  32.     switch(optname)
  33.     {
  34.         case SO_DEBUG:
  35.             if(val && !capable(CAP_NET_ADMIN))
  36.             {
  37.                 ret = -EACCES;
  38.             }
  39.             else if (valbool)
  40.                 sock_set_flag(sk, SOCK_DBG);
  41.             else
  42.                 sock_reset_flag(sk, SOCK_DBG);
  43.             break;
  44.         case SO_REUSEADDR:       //这里就是对端口复用的设置,还记得我们在bind socket的时候,只有设置了这一个属性不同的socket才能bind到同一个端口上
  45.             sk->sk_reuse = valbool;
  46.             break;
  47.         case SO_TYPE:
  48.         case SO_ERROR:
  49.             ret = -ENOPROTOOPT;
  50.             break;
  51.         case SO_DONTROUTE:
  52.             if (valbool)
  53.                 sock_set_flag(sk, SOCK_LOCALROUTE);
  54.             else
  55.                 sock_reset_flag(sk, SOCK_LOCALROUTE);
  56.             break;
  57.         case SO_BROADCAST:
  58.             sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
  59.             break;
  60.         case SO_SNDBUF:
  61.             /* Don't error on this BSD doesn't and if you think
  62.              about it this is right. Otherwise apps have to
  63.              play 'guess the biggest size' games. RCVBUF/SNDBUF
  64.              are treated in BSD as hints */

  65.             if (val > sysctl_wmem_max)
  66.                 val = sysctl_wmem_max;
  67. set_sndbuf:
  68.             sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
  69.             if ((val * 2) < SOCK_MIN_SNDBUF)
  70.                 sk->sk_sndbuf = SOCK_MIN_SNDBUF;
  71.             else
  72.                 sk->sk_sndbuf = val * 2;

  73.             /*
  74.              *    Wake up sending tasks if we
  75.              *    upped the value.
  76.              */
  77.             sk->sk_write_space(sk);
  78.             break;

  79.         case SO_SNDBUFFORCE:
  80.             if (!capable(CAP_NET_ADMIN)) {
  81.                 ret = -EPERM;
  82.                 break;
  83.             }
  84.             goto set_sndbuf;

  85.         case SO_RCVBUF:       //这里对接收缓冲区大小进行的设置
  86.             /* Don't error on this BSD doesn't and if you think
  87.              about it this is right. Otherwise apps have to
  88.              play 'guess the biggest size' games. RCVBUF/SNDBUF
  89.              are treated in BSD as hints */

  90.             if (val > sysctl_rmem_max)   //看到了没,这里不允许用户随意设置呢,有最大值,得用sysctl修改,或在/proc目录下调整这个值
  91.                 val = sysctl_rmem_max;
  92. set_rcvbuf:
  93.             sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
  94.             /*
  95.              * We double it on the way in to account for
  96.              * "struct sk_buff" etc. overhead. Applications
  97.              * assume that the SO_RCVBUF setting they make will
  98.              * allow that much actual data to be received on that
  99.              * socket.
  100.              *
  101.              * Applications are unaware that "struct sk_buff" and
  102.              * other overheads allocate from the receive buffer
  103.              * during socket buffer allocation.
  104.              *
  105.              * And after considering the possible alternatives,
  106.              * returning the value we actually used in getsockopt
  107.              * is the most desirable behavior.
  108.              */
  109.             if ((val * 2) < SOCK_MIN_RCVBUF)     //这里是最小值256
  110.                 sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
  111.             else
  112.                 sk->sk_rcvbuf = val * 2;     //设置的值要加倍
  113.             break;

  114.         case SO_RCVBUFFORCE:              //看到没这个是force,允许野蛮...
  115.             if (!capable(CAP_NET_ADMIN)) {
  116.                 ret = -EPERM;
  117.                 break;
  118.             }
  119.             goto set_rcvbuf;

  120.         case SO_KEEPALIVE:
  121. #ifdef CONFIG_INET
  122.             if (sk->sk_protocol == IPPROTO_TCP)
  123.                 tcp_set_keepalive(sk, valbool);
  124. #endif
  125.             sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
  126.             break;

  127.         case SO_OOBINLINE:
  128.             sock_valbool_flag(sk, SOCK_URGINLINE, valbool);
  129.             break;

  130.         case SO_NO_CHECK:
  131.             sk->sk_no_check = valbool;
  132.             break;

  133.         case SO_PRIORITY:
  134.             if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
  135.                 sk->sk_priority = val;
  136.             else
  137.                 ret = -EPERM;
  138.             break;

  139.         case SO_LINGER:
  140.             if(optlen<sizeof(ling)) {
  141.                 ret = -EINVAL;    /* 1003.1g */
  142.                 break;
  143.             }
  144.             if (copy_from_user(&ling,optval,sizeof(ling))) {
  145.                 ret = -EFAULT;
  146.                 break;
  147.             }
  148.             if (!ling.l_onoff)
  149.                 sock_reset_flag(sk, SOCK_LINGER);
  150.             else {
  151. #if (BITS_PER_LONG == 32)
  152.                 if ((unsigned int)ling.l_linger >= MAX_SCHEDULE_TIMEOUT/HZ)
  153.                     sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
  154.                 else
  155. #endif
  156.                     sk->sk_lingertime = (unsigned int)ling.l_linger * HZ;
  157.                 sock_set_flag(sk, SOCK_LINGER);
  158.             }
  159.             break;

  160.         case SO_BSDCOMPAT:
  161.             sock_warn_obsolete_bsdism("setsockopt");
  162.             break;

  163.         case SO_PASSCRED:
  164.             if (valbool)
  165.                 set_bit(SOCK_PASSCRED, &sock->flags);
  166.             else
  167.                 clear_bit(SOCK_PASSCRED, &sock->flags);
  168.             break;

  169.         case SO_TIMESTAMP:
  170.             if (valbool) {
  171.                 sock_set_flag(sk, SOCK_RCVTSTAMP);
  172.                 sock_enable_timestamp(sk);
  173.             } else
  174.                 sock_reset_flag(sk, SOCK_RCVTSTAMP);
  175.             break;

  176.         case SO_RCVLOWAT:
  177.             if (val < 0)
  178.                 val = INT_MAX;
  179.             sk->sk_rcvlowat = val ? : 1;
  180.             break;

  181.         case SO_RCVTIMEO:    //还记得这么吗,这个就是阻塞在recvfrom函数的时间设置,等待skb的时间
  182.             ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen);
  183.             break;

  184.         case SO_SNDTIMEO:
  185.             ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
  186.             break;

  187. #ifdef CONFIG_NETDEVICES
  188.         case SO_BINDTODEVICE:
  189.         {
  190.             char devname[IFNAMSIZ];

  191.             /* Sorry... */
  192.             if (!capable(CAP_NET_RAW)) {
  193.                 ret = -EPERM;
  194.                 break;
  195.             }

  196.             /* Bind this socket to a particular device like "eth0",
  197.              * as specified in the passed interface name. If the
  198.              * name is "" or the option length is zero the socket
  199.              * is not bound.
  200.              */

  201.             if (!valbool) {
  202.                 sk->sk_bound_dev_if = 0;
  203.             } else {
  204.                 if (optlen > IFNAMSIZ - 1)
  205.                     optlen = IFNAMSIZ - 1;
  206.                 memset(devname, 0, sizeof(devname));
  207.                 if (copy_from_user(devname, optval, optlen)) {
  208.                     ret = -EFAULT;
  209.                     break;
  210.                 }

  211.                 /* Remove any cached route for this socket. */
  212.                 sk_dst_reset(sk);

  213.                 if (devname[0] == '\0') {
  214.                     sk->sk_bound_dev_if = 0;
  215.                 } else {
  216.                     struct net_device *dev = dev_get_by_name(devname);
  217.                     if (!dev) {
  218.                         ret = -ENODEV;
  219.                         break;
  220.                     }
  221.                     sk->sk_bound_dev_if = dev->ifindex;
  222.                     dev_put(dev);
  223.                 }
  224.             }
  225.             break;
  226.         }
  227. #endif


  228.         case SO_ATTACH_FILTER:
  229.             ret = -EINVAL;
  230.             if (optlen == sizeof(struct sock_fprog)) {
  231.                 struct sock_fprog fprog;

  232.                 ret = -EFAULT;
  233.                 if (copy_from_user(&fprog, optval, sizeof(fprog)))
  234.                     break;

  235.                 ret = sk_attach_filter(&fprog, sk);
  236.             }
  237.             break;

  238.         case SO_DETACH_FILTER:
  239.             rcu_read_lock_bh();
  240.             filter = rcu_dereference(sk->sk_filter);
  241.             if (filter) {
  242.                 rcu_assign_pointer(sk->sk_filter, NULL);
  243.                 sk_filter_release(sk, filter);
  244.                 rcu_read_unlock_bh();
  245.                 break;
  246.             }
  247.             rcu_read_unlock_bh();
  248.             ret = -ENONET;
  249.             break;

  250.         case SO_PASSSEC:
  251.             if (valbool)
  252.                 set_bit(SOCK_PASSSEC, &sock->flags);
  253.             else
  254.                 clear_bit(SOCK_PASSSEC, &sock->flags);
  255.             break;

  256.         /* We implement the SO_SNDLOWAT etc to
  257.          not be settable (1003.1g 5.3) */
  258.         default:
  259.             ret = -ENOPROTOOPT;
  260.             break;
  261.     }
  262.     release_sock(sk);
  263.     return ret;
  264. }
4. 我们看看通过这个函数对udp属性的设置
    一路下去会进入这个函数udp_setsockopt
   

点击(此处)折叠或打开

  1. int udp_setsockopt(struct sock *sk, int level, int optname,
  2.          char __user *optval, int optlen)
  3. {
  4.     if (level == SOL_UDP || level == SOL_UDPLITE)
  5.         return udp_lib_setsockopt(sk, level, optname, optval, optlen,
  6.                      udp_push_pending_frames);
  7.     return ip_setsockopt(sk, level, optname, optval, optlen);
  8. }



  9. /*
  10.  *    Socket option code for UDP
  11.  */
  12. int udp_lib_setsockopt(struct sock *sk, int level, int optname,
  13.          char __user *optval, int optlen,
  14.          int (*push_pending_frames)(struct sock *))
  15. {
  16.     struct udp_sock *up = udp_sk(sk);
  17.     int val;
  18.     int err = 0;

  19.     if(optlen<sizeof(int))
  20.         return -EINVAL;

  21.     if (get_user(val, (int __user *)optval))
  22.         return -EFAULT;

  23.     switch(optname) {
  24.     case UDP_CORK:
  25.         if (val != 0) {
  26.             up->corkflag = 1;  //这个属性是设置是否允许小块数据合并成一个大快数据发送,把木塞堵住。默认不设置,所以当我们用sendto(udp)发送小数据的时候,在对端立即
  27.                                //会收到,但当我们设置这个属性的时候,sendto小数据块的时候,在对端不会立即收到,不信你试试
  28.         } else {
  29.             up->corkflag = 0;
  30.             lock_sock(sk);
  31.             (*push_pending_frames)(sk);
  32.             release_sock(sk);
  33.         }
  34.         break;

  35.     case UDP_ENCAP:
  36.         switch (val) {
  37.         case 0:
  38.         case UDP_ENCAP_ESPINUDP:
  39.         case UDP_ENCAP_ESPINUDP_NON_IKE:
  40.             up->encap_type = val;
  41.             break;
  42.         default:
  43.             err = -ENOPROTOOPT;
  44.             break;
  45.         }
  46.         break;

  47.     /*
  48.      *     UDP-Lite






上一篇:recvfrom函数
下一篇:代码级优化(代码质量)