1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
| /* * This routine handles a connection request. * It should make sure we haven't already responded. * Because of the way BSD works, we have to send a syn/ack now. * This also means it will be harder to close a socket which is * listening. */ /* 参数中daddr,saddr的理解应从远端角度出发。所以daddr表示本地地址;saddr表示远端地址 seq是函数调用tcp_init_seq()的返回值,表示本地初始化序列号; dev表示接收该数据包的接口设备 */ //tcp_rcv接收一个syn连接请求数据包后,将调用tcp_con_request函数进行具体处理 //其内部逻辑很简单:创建一个新的套接字用于通信,其本身继续监听客户端的请求 //创建一个新的套接字得设置其各个字段,这里是复制监听套接字的已有信息,然后根据需要修改部分 //然后创建数据包,设置其TCP首部,创建MAC和IP首部,然后回送给客户端, //并把该数据包插入到监听套接字sk的recive_queue队列中,该数据包已经关联了新套接字, //在accept函数中,最后返回的通信套接字则是从这个队列中获得(参见tcp_accept函数) static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, unsigned long daddr, unsigned long saddr, struct options *opt, struct device *dev, unsigned long seq) { struct sk_buff *buff; struct tcphdr *t1; unsigned char *ptr; struct sock *newsk; struct tcphdr *th; struct device *ndev=NULL; int tmp; struct rtable *rt; th = skb->h.th;//获取tcp首部 /* If the socket is dead, don't accept the connection. */ //判断套接字合法性 if (!sk->dead) { sk->data_ready(sk,0);//通知睡眠进程,有数据到达 } else //无效套接字,已经释放了的 { if(sk->debug) printk("Reset on %p: Connect on dead socket.\n",sk); tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl); tcp_statistics.TcpAttemptFails++; kfree_skb(skb, FREE_READ); return; } /* * Make sure we can accept more. This will prevent a * flurry of syns from eating up all our memory. */ //缓存的未应答数据包个数 >= 最大可缓存个数;表示满了,已经不能接收了 if (sk->ack_backlog >= sk->max_ack_backlog) { tcp_statistics.TcpAttemptFails++; kfree_skb(skb, FREE_READ); return; } /* * We need to build a new sock struct. * It is sort of bad to have a socket without an inode attached * to it, but the wake_up's will just wake up the listening socket, * and if the listening socket is destroyed before this is taken * off of the queue, this will take care of it. */ //创建一个新的套接字 newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC); if (newsk == NULL) { /* just ignore the syn. It will get retransmitted. */ tcp_statistics.TcpAttemptFails++; kfree_skb(skb, FREE_READ); return; } //复制一个套接字结构,即新的套接字中主要信息来源于监听套接字中的已有信息 memcpy(newsk, sk, sizeof(*newsk)); //下面两个就是把待发送和已接收队列初始化成数据包链表形式 skb_queue_head_init(&newsk->write_queue); skb_queue_head_init(&newsk->receive_queue); //下面是重发队列 newsk->send_head = NULL; newsk->send_tail = NULL; skb_queue_head_init(&newsk->back_log);//数据包暂存队列(中转站) //新套接字的字段设置 newsk->rtt = 0; /*TCP_CONNECT_TIME<<3*/ newsk->rto = TCP_TIMEOUT_INIT; newsk->mdev = 0; newsk->max_window = 0; newsk->cong_window = 1; newsk->cong_count = 0; newsk->ssthresh = 0; newsk->backoff = 0; newsk->blog = 0; newsk->intr = 0; newsk->proc = 0; newsk->done = 0; newsk->partial = NULL; newsk->pair = NULL; newsk->wmem_alloc = 0; newsk->rmem_alloc = 0; newsk->localroute = sk->localroute; newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF; newsk->err = 0; newsk->shutdown = 0; newsk->ack_backlog = 0; newsk->acked_seq = skb->h.th->seq+1; newsk->copied_seq = skb->h.th->seq+1; newsk->fin_seq = skb->h.th->seq; newsk->state = TCP_SYN_RECV; newsk->timeout = 0; newsk->ip_xmit_timeout = 0; newsk->write_seq = seq; newsk->window_seq = newsk->write_seq; newsk->rcv_ack_seq = newsk->write_seq; newsk->urg_data = 0; newsk->retransmits = 0; newsk->linger=0; newsk->destroy = 0; init_timer(&newsk->timer); newsk->timer.data = (unsigned long)newsk; newsk->timer.function = &net_timer; init_timer(&newsk->retransmit_timer); newsk->retransmit_timer.data = (unsigned long)newsk; newsk->retransmit_timer.function=&retransmit_timer; newsk->dummy_th.source = skb->h.th->dest; newsk->dummy_th.dest = skb->h.th->source; /* * Swap these two, they are from our point of view. */ newsk->daddr = saddr; newsk->saddr = daddr; put_sock(newsk->num,newsk);//插入 array_sock 哈希表中 newsk->dummy_th.res1 = 0; newsk->dummy_th.doff = 6; newsk->dummy_th.fin = 0; newsk->dummy_th.syn = 0; newsk->dummy_th.rst = 0; newsk->dummy_th.psh = 0; newsk->dummy_th.ack = 0; newsk->dummy_th.urg = 0; newsk->dummy_th.res2 = 0; newsk->acked_seq = skb->h.th->seq + 1;//序列号设置 newsk->copied_seq = skb->h.th->seq + 1; newsk->socket = NULL; /* * Grab the ttl and tos values and use them */ newsk->ip_ttl=sk->ip_ttl; newsk->ip_tos=skb->ip_hdr->tos; /* * Use 512 or whatever user asked for */ /* * Note use of sk->user_mss, since user has no direct access to newsk */ //ip路由表查找表项 rt=ip_rt_route(saddr, NULL,NULL); if(rt!=NULL && (rt->rt_flags&RTF_WINDOW)) newsk->window_clamp = rt->rt_window; else newsk->window_clamp = 0; if (sk->user_mss) newsk->mtu = sk->user_mss; else if(rt!=NULL && (rt->rt_flags&RTF_MSS)) newsk->mtu = rt->rt_mss - HEADER_SIZE; else { #ifdef CONFIG_INET_SNARL /* Sub Nets Are Local */ if ((saddr ^ daddr) & default_mask(saddr)) #else if ((saddr ^ daddr) & dev->pa_mask) #endif newsk->mtu = 576 - HEADER_SIZE; else newsk->mtu = MAX_WINDOW; } /* * But not bigger than device MTU */ newsk->mtu = min(newsk->mtu, dev->mtu - HEADER_SIZE); /* * This will min with what arrived in the packet */ tcp_options(newsk,skb->h.th); //服务器端创建新的套接字后,接下来就是创建一个数据包(syn+ack)回送过去 buff = newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC); if (buff == NULL) { sk->err = ENOMEM; newsk->dead = 1; newsk->state = TCP_CLOSE; /* And this will destroy it */ release_sock(newsk); kfree_skb(skb, FREE_READ); tcp_statistics.TcpAttemptFails++; return; } //字段设置 buff->len = sizeof(struct tcphdr)+4; buff->sk = newsk;//与新套接字关联 buff->localroute = newsk->localroute; t1 =(struct tcphdr *) buff->data; /* * Put in the IP header and routing stuff. */ //MAC 头+ ip 头创建 tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev, IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl); /* * Something went wrong. */ if (tmp < 0) { sk->err = tmp; buff->free = 1; kfree_skb(buff,FREE_WRITE); newsk->dead = 1; newsk->state = TCP_CLOSE; release_sock(newsk); skb->sk = sk; kfree_skb(skb, FREE_READ); tcp_statistics.TcpAttemptFails++; return; } buff->len += tmp; t1 =(struct tcphdr *)((char *)t1 +tmp); //tcp首部字段设置 memcpy(t1, skb->h.th, sizeof(*t1)); buff->h.seq = newsk->write_seq; /* * Swap the send and the receive. */ t1->dest = skb->h.th->source; t1->source = newsk->dummy_th.source; t1->seq = ntohl(newsk->write_seq++); t1->ack = 1;//确认控制位,表示这是一个确认数据包 newsk->window = tcp_select_window(newsk); newsk->sent_seq = newsk->write_seq; t1->window = ntohs(newsk->window); t1->res1 = 0; t1->res2 = 0; t1->rst = 0; t1->urg = 0; t1->psh = 0; t1->syn = 1;//同步控制位置位,和ack位一起作用 t1->ack_seq = ntohl(skb->h.th->seq+1);//确认序列号=发过来的数据包的序列号+1 t1->doff = sizeof(*t1)/4+1; ptr =(unsigned char *)(t1+1); ptr[0] = 2; ptr[1] = 4; ptr[2] = ((newsk->mtu) >> 8) & 0xff; ptr[3] =(newsk->mtu) & 0xff; //下面是tcp校验和检查 tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk); //调用_queue_xmit函数发送(前面介绍过) newsk->prot->queue_xmit(newsk, ndev, buff, 0); reset_xmit_timer(newsk, TIME_WRITE , TCP_TIMEOUT_INIT); skb->sk = newsk;//这里数据包捆绑的就是新的套接字了 /* * Charge the sock_buff to newsk. */ //原监听套接字接收队列中存放的字节数减去该数据包大小 //新创建的通信套接字则加上该数据包大小 sk->rmem_alloc -= skb->mem_len; newsk->rmem_alloc += skb->mem_len; //把这个数据包插入到reveive_queue中,这个数据包的宿主是新套接字 //在accept函数中,通信套接字则是从这个队列中获取 skb_queue_tail(&sk->receive_queue,skb); sk->ack_backlog++;//缓存的数据包个数+1 release_sock(newsk); tcp_statistics.TcpOutSegs++; }
|