Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1501984
  • 博文数量: 228
  • 博客积分: 1698
  • 博客等级: 上尉
  • 技术积分: 3241
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-24 21:49
个人简介

Linux

文章分类

全部博文(228)

文章存档

2017年(1)

2016年(43)

2015年(102)

2014年(44)

2013年(5)

2012年(30)

2011年(3)

分类: LINUX

2015-02-06 21:04:04

很久没更新了,最近涉及Linux协议栈的工作,再梳理一下

点击(此处)折叠或打开

  1. SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
  2.         int, addrlen)
  3. {
  4.     struct socket *sock;
  5.     struct sockaddr_storage address;
  6.     int err, fput_needed;
  7.     
  8.     /× lookup socket ×/
  9.     /× file = current->files->fdt->fd[fd] ×/
  10.     /* sock = file->private_data (set in sock_map_fd while socket was created) */
  11.     sock = sockfd_lookup_light(fd, &err, &fput_needed);
  12.     if (!sock)
  13.         goto out;
  14.     
  15.     /* copy user addr to kernel */
  16.     err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address);
  17.     if (err < 0)
  18.         goto out_put;

  19.     err security_socket_connect(sock, (struct sockaddr *)&address, addrlen);
  20.     if (err)
  21.         goto out_put;
  22.     
  23.     /* 对应inet_stream_connect */ 
  24.     err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
  25.                  sock->file->f_flags);
  26. out_put:
  27.     fput_light(sock->file, fput_needed);
  28. out:
  29.     return err;
  30. }

点击(此处)折叠或打开

  1. /*
  2.  *    Connect to a remote host. There is regrettably still a little
  3.  *    TCP 'magic' in here.
  4.  */
  5. int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
  6.             int addr_len, int flags)
  7. {
  8.     struct sock *sk = sock->sk;
  9.     int err;
  10.     long timeo;

  11.     lock_sock(sk);

  12.     if (uaddr->sa_family == AF_UNSPEC) {
  13.         err = sk->sk_prot->disconnect(sk, flags);
  14.         sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
  15.         goto out;
  16.     }

  17.     switch (sock->state) {
  18.     default:
  19.         err = -EINVAL;
  20.         goto out;
  21.     case SS_CONNECTED:
  22.         err = -EISCONN;
  23.         goto out;
  24.     case SS_CONNECTING:
  25.         err = -EALREADY;
  26.         /* Fall out of switch with err, set for this state */
  27.         break;
  28.     /* sock->state在inet_create时被设置为SS_UNCONNECTED */
  29.     case SS_UNCONNECTED:
  30.         err = -EISCONN;
  31.         /* 在inet_create中调用的sock_init_data函数中sk->sk_state被初始化为TCP_CLOSE */
  32.         /* 在inet_create中sk->sk_prot->init中同样会被初始化为TCP_CLOSE,init对应了tcp_v4_init_sock函数,初始化的动作重复了 */        
  33.         if (sk->sk_state != TCP_CLOSE)
  34.             goto out;

  35.         /* 对应tcp_prot.connect,即tcp_v4_connect */
  36.         err = sk->sk_prot->connect(sk, uaddr, addr_len);
  37.         if (err < 0)
  38.             goto out;
  39.         
  40.         /× 更新当前状态为SS_CONNECTING ×/
  41.         sock->state = SS_CONNECTING;

  42.         /* Just entered SS_CONNECTING state; the only
  43.          * difference is that return value in non-blocking
  44.          * case is EINPROGRESS, rather than EALREADY.
  45.          */
  46.         err = -EINPROGRESS;
  47.         break;
  48.     }
  49.    
  50.     timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);

  51.     if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
  52.         /* Error code is set above */
  53.         if (!timeo || !inet_wait_for_connect(sk, timeo))
  54.             goto out;

  55.         err = sock_intr_errno(timeo);
  56.         if (signal_pending(current))
  57.             goto out;
  58.     }

  59.     /* Connection was closed by RST, timeout, ICMP error
  60.      * or another process disconnected us.
  61.      */
  62.     if (sk->sk_state == TCP_CLOSE)
  63.         goto sock_error;

  64.     /* sk->sk_err may be not zero now, if RECVERR was ordered by user
  65.      * and error was received after socket entered established state.
  66.      * Hence, it is handled normally after connect() return successfully.
  67.      */

  68.     sock->state = SS_CONNECTED;
  69.     err = 0;
  70. out:
  71.     release_sock(sk);
  72.     return err;

  73. sock_error:
  74.     err = sock_error(sk) ? : -ECONNABORTED;
  75.     sock->state = SS_UNCONNECTED;
  76.     if (sk->sk_prot->disconnect(sk, flags))
  77.         sock->state = SS_DISCONNECTING;
  78.     goto out;
  79. }
继续分析tcp_v4_connect函数

点击(此处)折叠或打开

  1. /* This will initiate an outgoing connection. */
  2. int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
  3. {
  4.     struct inet_sock *inet = inet_sk(sk);
  5.     struct tcp_sock *tp = tcp_sk(sk);
  6.     struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
  7.     struct rtable *rt;
  8.     __be32 daddr, nexthop;
  9.     int tmp;
  10.     int err;
  11.     struct ip_options *inet_opt;
  12.     
  13.     /* 检查一下地址长度和协议族类型 */
  14.     if (addr_len < sizeof(struct sockaddr_in))
  15.         return -EINVAL;

  16.     if (usin->sin_family != AF_INET)
  17.         return -EAFNOSUPPORT;

  18.     /× 将nexthop和daddr暂时初始化为目标地址 ×/
  19.     nexthop = daddr = usin->sin_addr.s_addr;
  20.     inet_opt = rcu_dereference(inet->opt);
  21.     /× 如果指定了源路由选项,则按照指定的下一跳进行处理 ×/
  22.     if (inet_opt && inet_opt->srr) {
  23.         if (!daddr)
  24.             return -EINVAL;
  25.         nexthop = inet_opt->faddr;
  26.     }
  27.     /× 本地发包需要确认源IP地址和源端口,如果没有提供源IP地址会通过路由查找过程中的inet_select_addr来选择源地址 ×/
  28.     /× 路由查找成功后会生成路由缓存项,可以加速后续报文的发送,下面函数返回路由缓存项,包含源IP地址 ×/
  29.     tmp = ip_route_connect(&rt, nexthop, inet->saddr,
  30.              RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
  31.              IPPROTO_TCP,
  32.              inet->sport, usin->sin_port, sk, 1);
  33.     if (tmp < 0) {
  34.         if (tmp == -ENETUNREACH)
  35.             IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
  36.         return tmp;
  37.     }
  38.     /× 正常TCP连接查找到的都是单播路由 ×/
  39.     if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
  40.         ip_rt_put(rt);
  41.         return -ENETUNREACH;
  42.     }
  43.     /* 使用查完路由后的目的地址更新临时daddr,有可能与用户传入的目的地址不同 */
  44.     if (!inet_opt || !inet_opt->srr)
  45.         daddr = rt->rt_dst;
  46.     /* 如果没有指定源地址,则使用路由选择的源地址 */
  47.     if (!inet->saddr)
  48.         inet->saddr = rt->rt_src;
  49.     inet->rcv_saddr = inet->saddr;
  50.     
  51.     if (tp->rx_opt.ts_recent_stamp && inet->daddr != daddr) {
  52.         /* Reset inherited state */
  53.         tp->rx_opt.ts_recent     = 0;
  54.         tp->rx_opt.ts_recent_stamp = 0;
  55.         tp->write_seq         = 0;
  56.     }

  57.     if (tcp_death_row.sysctl_tw_recycle &&
  58.      !tp->rx_opt.ts_recent_stamp && rt->rt_dst == daddr) {
  59.         struct inet_peer *peer = rt_get_peer(rt);
  60.         /*
  61.          * VJ's idea. We save last timestamp seen from
  62.          * the destination in peer table, when entering state
  63.          * TIME-WAIT * and initialize rx_opt.ts_recent from it,
  64.          * when trying new connection.
  65.          */
  66.         if (peer != NULL &&
  67.          peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) {
  68.             tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
  69.             tp->rx_opt.ts_recent = peer->tcp_ts;
  70.         }
  71.     }
  72.     /* 记录目的端口和目的IP */
  73.     inet->dport = usin->sin_port;
  74.     inet->daddr = daddr;
  75.     /× 设置IP选项的长度 ×/
  76.     inet_csk(sk)->icsk_ext_hdr_len = 0;
  77.     if (inet_opt)
  78.         inet_csk(sk)->icsk_ext_hdr_len = inet_opt->optlen;
  79.     /× 允许的最小MSS ×/
  80.     tp->rx_opt.mss_clamp = 536;

  81.     /* Socket identity is still unknown (sport may be zero).
  82.      * However we set state to SYN-SENT and not releasing socket
  83.      * lock select source port, enter ourselves into the hash tables and
  84.      * complete initialization after this.
  85.      */
  86.     /× 马上要发送SYN了,所以可以更新sk->sk_state状态值了 ×/
  87.     tcp_set_state(sk, TCP_SYN_SENT);
  88.     /× 进行端口选择,并与sk绑定,下面会重点分析一下 ×/
  89.     err = inet_hash_connect(&tcp_death_row, sk);
  90.     if (err)
  91.         goto failure;
  92.     /×  使用sport更新路由缓存表 ×/
  93.     err = ip_route_newports(&rt, IPPROTO_TCP,
  94.                 inet->sport, inet->dport, sk);
  95.     if (err)
  96.         goto failure;

  97.     /* OK, now commit destination to socket. */
  98.     /× 更新sock的路由缓存项,设置gso标签 ×/
  99.     sk->sk_gso_type = SKB_GSO_TCPV4;
  100.     sk_setup_caps(sk, &rt->u.dst);
  101.     
  102.     /× 计算发送第一个报文的初始化序列号 ×/
  103.     if (!tp->write_seq)
  104.         tp->write_seq = secure_tcp_sequence_number(inet->saddr,
  105.                              inet->daddr,
  106.                              inet->sport,
  107.                              usin->sin_port);
  108.     /× 计算IP报文ID ×/
  109.     inet->id = tp->write_seq ^ jiffies;
  110.     /× 发送SYN报文 ×/ 
  111.     err = tcp_connect(sk);
  112.     rt = NULL;
  113.     if (err)
  114.         goto failure;

  115.     return 0;

  116. failure:
  117.     /*
  118.      * This unhashes the socket and releases the local port,
  119.      * if necessary.
  120.      */
  121.     tcp_set_state(sk, TCP_CLOSE);
  122.     ip_rt_put(rt);
  123.     sk->sk_route_caps = 0;
  124.     inet->dport = 0;
  125.     return err;
  126. }

未完待续




阅读(4020) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~