tcp_input.c

来自「eCos操作系统源码」· C语言 代码 · 共 2,238 行 · 第 1/5 页

C
2,238
字号
	/*	 * Check that TCP offset makes sense,	 * pull out TCP options and adjust length.		XXX	 */	off = th->th_off << 2;	if (off < sizeof (struct tcphdr) || off > tlen) {		tcpstat.tcps_rcvbadoff++;		goto drop;	}	tlen -= off;	/* tlen is used instead of ti->ti_len */	if (off > sizeof (struct tcphdr)) {#ifdef INET6		if (isipv6) {			IP6_EXTHDR_CHECK(m, off0, off, );			ip6 = mtod(m, struct ip6_hdr *);			th = (struct tcphdr *)((caddr_t)ip6 + off0);		} else#endif /* INET6 */	      {		if (m->m_len < sizeof(struct ip) + off) {			if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) {				tcpstat.tcps_rcvshort++;				return;			}			ip = mtod(m, struct ip *);			ipov = (struct ipovly *)ip;			th = (struct tcphdr *)((caddr_t)ip + off0);		}	      }		optlen = off - sizeof (struct tcphdr);		optp = (u_char *)(th + 1);	}	thflags = th->th_flags;#ifdef TCP_DROP_SYNFIN	/*	 * If the drop_synfin option is enabled, drop all packets with	 * both the SYN and FIN bits set. This prevents e.g. nmap from	 * identifying the TCP/IP stack.	 *	 * This is incompatible with RFC1644 extensions (T/TCP).	 */	if (drop_synfin && (thflags & (TH_SYN|TH_FIN)) == (TH_SYN|TH_FIN))		goto drop;#endif	/*	 * Convert TCP protocol specific fields to host format.	 */	NTOHL(th->th_seq);	NTOHL(th->th_ack);	NTOHS(th->th_win);	NTOHS(th->th_urp);	/*	 * Delay droping TCP, IP headers, IPv6 ext headers, and TCP options,	 * until after ip6_savecontrol() is called and before other functions	 * which don't want those proto headers.	 * Because ip6_savecontrol() is going to parse the mbuf to	 * search for data to be passed up to user-land, it wants mbuf	 * parameters to be unchanged.	 */	drop_hdrlen = off0 + off;	/*	 * Locate pcb for segment.	 */findpcb:#ifdef IPFIREWALL_FORWARD	if (ip_fw_fwd_addr != NULL#ifdef INET6	    && isipv6 == NULL /* IPv6 support is not yet */#endif /* INET6 */	    ) {		/*		 * Diverted. Pretend to be the destination.		 * already got one like this? 		 */		inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport,			ip->ip_dst, th->th_dport, 0, m->m_pkthdr.rcvif);		if (!inp) {			/* 			 * No, then it's new. Try find the ambushing socket			 */			if (!ip_fw_fwd_addr->sin_port) {				inp = in_pcblookup_hash(&tcbinfo, ip->ip_src,				    th->th_sport, ip_fw_fwd_addr->sin_addr,				    th->th_dport, 1, m->m_pkthdr.rcvif);			} else {				inp = in_pcblookup_hash(&tcbinfo,				    ip->ip_src, th->th_sport,	    			    ip_fw_fwd_addr->sin_addr,				    ntohs(ip_fw_fwd_addr->sin_port), 1,				    m->m_pkthdr.rcvif);			}		}		ip_fw_fwd_addr = NULL;	} else#endif	/* IPFIREWALL_FORWARD */      {#ifdef INET6	if (isipv6)		inp = in6_pcblookup_hash(&tcbinfo, &ip6->ip6_src, th->th_sport,					 &ip6->ip6_dst, th->th_dport, 1,					 m->m_pkthdr.rcvif);	else#endif /* INET6 */	inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport,	    ip->ip_dst, th->th_dport, 1, m->m_pkthdr.rcvif);      }#ifdef IPSEC#ifdef INET6	if (isipv6) {		if (inp != NULL && ipsec6_in_reject_so(m, inp->inp_socket)) {			ipsec6stat.in_polvio++;			goto drop;		}	} else#endif /* INET6 */	if (inp != NULL && ipsec4_in_reject_so(m, inp->inp_socket)) {		ipsecstat.in_polvio++;		goto drop;	}#endif /*IPSEC*/	/*	 * If the state is CLOSED (i.e., TCB does not exist) then	 * all data in the incoming segment is discarded.	 * If the TCB exists but is in CLOSED state, it is embryonic,	 * but should either do a listen or a connect soon.	 */	if (inp == NULL) {		if (log_in_vain) {#ifdef INET6			char dbuf[INET6_ADDRSTRLEN], sbuf[INET6_ADDRSTRLEN];#else /* INET6 */			char dbuf[4*sizeof "123"], sbuf[4*sizeof "123"];#endif /* INET6 */#ifdef INET6			if (isipv6) {				strcpy(dbuf, ip6_sprintf(&ip6->ip6_dst));				strcpy(sbuf, ip6_sprintf(&ip6->ip6_src));			} else#endif		      {			strcpy(dbuf, inet_ntoa(ip->ip_dst));			strcpy(sbuf, inet_ntoa(ip->ip_src));		      }			switch (log_in_vain) {			case 1:				if(thflags & TH_SYN)					log(LOG_INFO,			    		"Connection attempt to TCP %s:%d from %s:%d\n",			    		dbuf, ntohs(th->th_dport),					sbuf,					ntohs(th->th_sport));				break;			case 2:				log(LOG_INFO,			    	"Connection attempt to TCP %s:%d from %s:%d flags:0x%x\n",			    	dbuf, ntohs(th->th_dport), sbuf,			    	ntohs(th->th_sport), thflags);				break;			default:				break;			}		}		if (blackhole) { 			switch (blackhole) {			case 1:				if (thflags & TH_SYN)					goto drop;				break;			case 2:				goto drop;			default:				goto drop;			}		}		rstreason = BANDLIM_RST_CLOSEDPORT;		goto dropwithreset;	}	tp = intotcpcb(inp);	if (tp == 0) {		rstreason = BANDLIM_RST_CLOSEDPORT;		goto dropwithreset;	}	if (tp->t_state == TCPS_CLOSED)		goto drop;	/* Unscale the window into a 32-bit value. */	if ((thflags & TH_SYN) == 0)		tiwin = th->th_win << tp->snd_scale;	else		tiwin = th->th_win;	so = inp->inp_socket;	if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {#ifdef TCPDEBUG		if (so->so_options & SO_DEBUG) {			ostate = tp->t_state;#ifdef INET6			if (isipv6)				bcopy((char *)ip6, (char *)tcp_saveipgen,				      sizeof(*ip6));			else#endif /* INET6 */			bcopy((char *)ip, (char *)tcp_saveipgen, sizeof(*ip));			tcp_savetcp = *th;		}#endif		if (so->so_options & SO_ACCEPTCONN) {			register struct tcpcb *tp0 = tp;			struct socket *so2;#ifdef IPSEC			struct socket *oso;#endif#ifdef INET6			struct inpcb *oinp = sotoinpcb(so);#endif /* INET6 */#ifndef IPSEC			/*			 * Current IPsec implementation makes incorrect IPsec			 * cache if this check is done here.			 * So delay this until duplicated socket is created.			 */			if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) {				/*				 * Note: dropwithreset makes sure we don't				 * send a RST in response to a RST.				 */				if (thflags & TH_ACK) {					tcpstat.tcps_badsyn++;					rstreason = BANDLIM_RST_OPENPORT;					goto dropwithreset;				}				goto drop;			}#endif#ifdef INET6			/*			 * If deprecated address is forbidden,			 * we do not accept SYN to deprecated interface			 * address to prevent any new inbound connection from			 * getting established.			 * When we do not accept SYN, we send a TCP RST,			 * with deprecated source address (instead of dropping			 * it).  We compromise it as it is much better for peer			 * to send a RST, and RST will be the final packet			 * for the exchange.			 *			 * If we do not forbid deprecated addresses, we accept			 * the SYN packet.  RFC2462 does not suggest dropping			 * SYN in this case.			 * If we decipher RFC2462 5.5.4, it says like this:			 * 1. use of deprecated addr with existing			 *    communication is okay - "SHOULD continue to be			 *    used"			 * 2. use of it with new communication:			 *   (2a) "SHOULD NOT be used if alternate address			 *        with sufficient scope is available"			 *   (2b) nothing mentioned otherwise.			 * Here we fall into (2b) case as we have no choice in			 * our source address selection - we must obey the peer.			 *			 * The wording in RFC2462 is confusing, and there are			 * multiple description text for deprecated address			 * handling - worse, they are not exactly the same.			 * I believe 5.5.4 is the best one, so we follow 5.5.4.			 */			if (isipv6 && !ip6_use_deprecated) {				struct in6_ifaddr *ia6;				if ((ia6 = ip6_getdstifaddr(m)) &&				    (ia6->ia6_flags & IN6_IFF_DEPRECATED)) {					tp = NULL;					rstreason = BANDLIM_RST_OPENPORT;					goto dropwithreset;				}			}#endif			so2 = sonewconn(so, 0);			if (so2 == 0) {				tcpstat.tcps_listendrop++;				so2 = sodropablereq(so);				if (so2) {					if (tcp_lq_overflow)						sototcpcb(so2)->t_flags |= 						    TF_LQ_OVERFLOW;					tcp_drop(sototcpcb(so2), ETIMEDOUT);					so2 = sonewconn(so, 0);				}				if (!so2)					goto drop;			}#ifdef IPSEC			oso = so;#endif			so = so2;			/*			 * This is ugly, but ....			 *			 * Mark socket as temporary until we're			 * committed to keeping it.  The code at			 * ``drop'' and ``dropwithreset'' check the			 * flag dropsocket to see if the temporary			 * socket created here should be discarded.			 * We mark the socket as discardable until			 * we're committed to it below in TCPS_LISTEN.			 */			dropsocket++;			inp = (struct inpcb *)so->so_pcb;#ifdef INET6			if (isipv6)				inp->in6p_laddr = ip6->ip6_dst;			else {				inp->inp_vflag &= ~INP_IPV6;				inp->inp_vflag |= INP_IPV4;#endif /* INET6 */			inp->inp_laddr = ip->ip_dst;#ifdef INET6			}#endif /* INET6 */			inp->inp_lport = th->th_dport;			if (in_pcbinshash(inp) != 0) {				/*				 * Undo the assignments above if we failed to				 * put the PCB on the hash lists.				 */#ifdef INET6				if (isipv6)					inp->in6p_laddr = in6addr_any;				else#endif /* INET6 */				inp->inp_laddr.s_addr = INADDR_ANY;				inp->inp_lport = 0;				goto drop;			}#ifdef IPSEC			/*			 * To avoid creating incorrectly cached IPsec			 * association, this is need to be done here.			 *			 * Subject: (KAME-snap 748)			 * From: Wayne Knowles <w.knowles@niwa.cri.nz>			 * ftp://ftp.kame.net/pub/mail-list/snap-users/748			 */			if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) {				/*				 * Note: dropwithreset makes sure we don't				 * send a RST in response to a RST.				 */				if (thflags & TH_ACK) {					tcpstat.tcps_badsyn++;					rstreason = BANDLIM_RST_OPENPORT;					goto dropwithreset;				}				goto drop;			}#endif#ifdef INET6			if (isipv6) {  				/* 				 * Inherit socket options from the listening  				 * socket. 				 * Note that in6p_inputopts are not (even 				 * should not be) copied, since it stores				 * previously received options and is used to 				 * detect if each new option is different than 				 * the previous one and hence should be passed 				 * to a user. 				 * If we copied in6p_inputopts, a user would 				 * not be able to receive options just after 				 * calling the accept system call. 				 */				inp->inp_flags |=					oinp->inp_flags & INP_CONTROLOPTS; 				if (oinp->in6p_outputopts) 					inp->in6p_outputopts = 						ip6_copypktopts(oinp->in6p_outputopts, 								M_NOWAIT);			} else#endif /* INET6 */			inp->inp_options = ip_srcroute();#ifdef IPSEC			/* copy old policy into new socket's */			if (ipsec_copy_policy(sotoinpcb(oso)->inp_sp,			                      inp->inp_sp))				printf("tcp_input: could not copy policy\n");#endif			tp = intotcpcb(inp);			tp->t_state = TCPS_LISTEN;			tp->t_flags |= tp0->t_flags & (TF_NOPUSH|TF_NOOPT);			/* Compute proper scaling value from buffer space */			while (tp->request_r_scale < TCP_MAX_WINSHIFT &&			   TCP_MAXWIN << tp->request_r_scale <			   so->so_rcv.sb_hiwat)				tp->request_r_scale++;		}	}#ifdef INET6	/* save packet options if user wanted */	if (isipv6 && (inp->in6p_flags & INP_CONTROLOPTS) != 0) {		struct ip6_recvpktopts opts6;		/*		 * Temporarily re-adjusting the mbuf before ip6_savecontrol(),		 * which is necessary for FreeBSD only due to difference from		 * other BSD stacks.		 * XXX: we'll soon make a more natural fix after getting a		 *      consensus.		 */		ip6_savecontrol(inp, ip6, m, &opts6, &inp->in6p_inputopts);		if (inp->in6p_inputopts)			ip6_update_recvpcbopt(inp->in6p_inputopts, &opts6);		if (opts6.head) {			if (sbappendcontrol(&inp->in6p_socket->so_rcv,					    NULL, opts6.head)			    == 0)				m_freem(opts6.head);		}	}#endif /* INET6 */	/*	 * Segment received on connection.	 * Reset idle time and keep-alive timer.	 */	tp->t_rcvtime = ticks;	if (TCPS_HAVEESTABLISHED(tp->t_state))		callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);	/*	 * Process options if not in LISTEN state,	 * else do it below (after getting remote address).	 */	if (tp->t_state != TCPS_LISTEN)		tcp_dooptions(tp, optp, optlen, th, &to);	/*	 * Header prediction: check for the two common cases

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?