ipsec_rcv.c

来自「FREESWAN VPN源代码包」· C语言 代码 · 共 2,240 行 · 第 1/5 页

C
2,240
字号
					if(i != idat[ilen - 2 - padlen + i - 1]) {					        badpad = 1;					} 					if((i % 16) == 0) {						KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,							    "\n");					}				}				if((i % 16) != 1) {					KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,							"\n");				}				if(badpad) {					KLIPS_PRINT(debug_rcv & DB_RX_IPAD,						    "klips_debug:ipsec_rcv: "						    "warning, decrypted packet from %s has bad padding\n",						    ipaddr_txt);					KLIPS_PRINT(debug_rcv & DB_RX_IPAD,						    "klips_debug:ipsec_rcv: "						    "...may be bad decryption -- not dropped\n");					tdbp->tdb_encpad_errs += 1;				}								KLIPS_PRINT(debug_rcv & DB_RX_IPAD,					    "klips_debug:ipsec_rcv: "					    "packet decrypted from %s: next_header = %d, padding = %d\n",					    ipaddr_txt,					    next_header,					    pad - 2 - authlen);			}#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH		case IPPROTO_AH:			break;#endif /* CONFIG_IPSEC_AH */		}	       		/*		 *	Discard the original ESP/AH header		 */				ipp->protocol = next_header;				switch(proto) {#ifdef CONFIG_IPSEC_ESP		case IPPROTO_ESP:			ipp->tot_len = htons(ntohs(ipp->tot_len) - (esphlen + pad));			memmove((void *)(skb->data + esphlen),				(void *)(skb->data), iphlen);			if(skb->len < esphlen) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING				       "klips_error:ipsec_rcv: "				       "tried to skb_pull esphlen=%d, %d available.  This should never happen, please report.\n",				       esphlen, (int)(skb->len));				goto rcvleave;			}			skb_pull(skb, esphlen);			KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,				    "klips_debug:ipsec_rcv: "				    "trimming to %d.\n",				    len - esphlen - pad);			if(pad + esphlen <= len) {			        skb_trim(skb, len - esphlen - pad);			} else {				spin_unlock(&tdb_lock);			        KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,				            "klips_debug:ipsec_rcv: "					    "bogus packet, size is zero or negative, dropping.\n");				goto rcvleave;			}		break;#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH		case IPPROTO_AH:			ipp->tot_len = htons(ntohs(ipp->tot_len) - ahhlen);			memmove((void *)(skb->data + ahhlen),				(void *)(skb->data), iphlen);			if(skb->len < ahhlen) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING				       "klips_error:ipsec_rcv: "				       "tried to skb_pull ahhlen=%d, %d available.  This should never happen, please report.\n",				       ahhlen,				       (int)(skb->len));				goto rcvleave;			}			skb_pull(skb, ahhlen);			break;#endif /* CONFIG_IPSEC_AH */		}		/*		 *	Adjust pointers		 */				len = skb->len;		dat = skb->data;		#ifdef NET_21/*		skb->h.ipiph=(struct iphdr *)skb->data; */		skb->nh.raw = skb->data;		skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2);				memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));#else /* NET_21 */		skb->h.iph=(struct iphdr *)skb->data;		skb->ip_hdr=(struct iphdr *)skb->data;		memset(skb->proto_priv, 0, sizeof(struct options));#endif /* NET_21 */				ipp = (struct iphdr *)dat;		ipp->check = 0;		ipp->check = ip_fast_csum((unsigned char *)dat, iphlen >> 2);				KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,			    "klips_debug:ipsec_rcv: "			    "after <%s%s%s>, SA:%s:\n",			    IPS_XFORM_NAME(tdbp),			    sa_len ? sa : " (error)");		KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp);				skb->protocol = htons(ETH_P_IP);		skb->ip_summed = 0;		tdbprev = tdbp;		tdbnext = tdbp->tdb_inext;		if(sysctl_ipsec_inbound_policy_check) {			if(tdbnext) {				if(tdbnext->tdb_onext != tdbp) {					spin_unlock(&tdb_lock);					KLIPS_PRINT(debug_rcv,						    "klips_debug:ipsec_rcv: "						    "SA:%s, backpolicy does not agree with fwdpolicy.\n",						    sa_len ? sa : " (error)");					if(stats) {						stats->rx_dropped++;					}					goto rcvleave;				}				KLIPS_PRINT(debug_rcv,					    "klips_debug:ipsec_rcv: "					    "SA:%s, backpolicy agrees with fwdpolicy.\n",					    sa_len ? sa : " (error)");				if(					ipp->protocol != IPPROTO_AH  					&& ipp->protocol != IPPROTO_ESP #ifdef CONFIG_IPSEC_IPCOMP					&& ipp->protocol != IPPROTO_COMP					&& (tdbnext->tdb_said.proto != IPPROTO_COMP					    || (tdbnext->tdb_said.proto == IPPROTO_COMP						&& tdbnext->tdb_inext))#endif /* CONFIG_IPSEC_IPCOMP */					&& ipp->protocol != IPPROTO_IPIP					&& ipp->protocol != 0xFE  // added to support heartbeats to AT&T SIG/GIG					) {					spin_unlock(&tdb_lock);					KLIPS_PRINT(debug_rcv,						    "klips_debug:ipsec_rcv: "						    "packet with incomplete policy dropped, last successful SA:%s.\n",						    sa_len ? sa : " (error)");					if(stats) {						stats->rx_dropped++;					}					goto rcvleave;				}				KLIPS_PRINT(debug_rcv,					    "klips_debug:ipsec_rcv: "					    "SA:%s, Another IPSEC header to process.\n",					    sa_len ? sa : " (error)");			} else {				KLIPS_PRINT(debug_rcv,					    "klips_debug:ipsec_rcv: "					    "No tdb_inext from this SA:%s.\n",					    sa_len ? sa : " (error)");			}		}#ifdef CONFIG_IPSEC_IPCOMP		/* update ipcomp ratio counters, even if no ipcomp packet is present */		if (tdbnext		  && tdbnext->tdb_said.proto == IPPROTO_COMP		  && ipp->protocol != IPPROTO_COMP) {			tdbnext->tdb_comp_ratio_cbytes += ntohs(ipp->tot_len);			tdbnext->tdb_comp_ratio_dbytes += ntohs(ipp->tot_len);		}#endif /* CONFIG_IPSEC_IPCOMP */		tdbp->ips_life.ipl_bytes.ipl_count += len;		tdbp->ips_life.ipl_bytes.ipl_last   = len;		if(!tdbp->ips_life.ipl_usetime.ipl_count) {			tdbp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;		}		tdbp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;		tdbp->ips_life.ipl_packets.ipl_count += 1;			/* end decapsulation loop here */	} while(   (ipp->protocol == IPPROTO_ESP )		|| (ipp->protocol == IPPROTO_AH  )#ifdef CONFIG_IPSEC_IPCOMP		|| (ipp->protocol == IPPROTO_COMP)#endif /* CONFIG_IPSEC_IPCOMP */		);	#ifdef CONFIG_IPSEC_IPCOMP	if(tdbnext && tdbnext->tdb_said.proto == IPPROTO_COMP) {		tdbprev = tdbp;		tdbp = tdbnext;		tdbnext = tdbp->tdb_inext;	}#endif /* CONFIG_IPSEC_IPCOMP */#ifdef CONFIG_IPSEC_NAT_TRAVERSAL	if ((natt_type) && (ipp->protocol != IPPROTO_IPIP)) {		/**		 * NAT-Traversal and Transport Mode:		 *   we need to correct TCP/UDP checksum		 *		 * If we've got NAT-OA, we can fix checksum without recalculation.		 */		__u32 natt_oa = tdbp->ips_natt_oa ?			((struct sockaddr_in*)(tdbp->ips_natt_oa))->sin_addr.s_addr : 0;		__u16 pkt_len = skb->tail - (unsigned char *)ipp;		__u16 data_len = pkt_len - (ipp->ihl << 2);		switch (ipp->protocol) {			case IPPROTO_TCP:				if (data_len >= sizeof(struct tcphdr)) {					struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ipp+ipp->ihl);					if (natt_oa) {						__u32 buff[2] = { ~natt_oa, ipp->saddr };						KLIPS_PRINT(debug_rcv,				    		"klips_debug:ipsec_rcv: "							"NAT-T & TRANSPORT: "							"fix TCP checksum using NAT-OA\n");						tcp->check = csum_fold(							csum_partial((unsigned char *)buff, sizeof(buff),							tcp->check^0xffff));					}					else {						KLIPS_PRINT(debug_rcv,			    			"klips_debug:ipsec_rcv: "							"NAT-T & TRANSPORT: recalc TCP checksum\n");						if (pkt_len > (ntohs(ipp->tot_len)))							data_len -= (pkt_len - ntohs(ipp->tot_len));						tcp->check = 0;						tcp->check = csum_tcpudp_magic(ipp->saddr, ipp->daddr,							data_len, IPPROTO_TCP,							csum_partial((unsigned char *)tcp, data_len, 0));					}				}				else {					KLIPS_PRINT(debug_rcv,			    		"klips_debug:ipsec_rcv: "						"NAT-T & TRANSPORT: can't fix TCP checksum\n");				}				break;			case IPPROTO_UDP:				if (data_len >= sizeof(struct udphdr)) {					struct udphdr *udp = (struct udphdr *)((__u32 *)ipp+ipp->ihl);					if (udp->check == 0) {						KLIPS_PRINT(debug_rcv,				    		"klips_debug:ipsec_rcv: "							"NAT-T & TRANSPORT: UDP checksum already 0\n");					}					else if (natt_oa) {						__u32 buff[2] = { ~natt_oa, ipp->saddr };						KLIPS_PRINT(debug_rcv,				    		"klips_debug:ipsec_rcv: "							"NAT-T & TRANSPORT: "							"fix UDP checksum using NAT-OA\n");						udp->check = csum_fold(							csum_partial((unsigned char *)buff, sizeof(buff),							udp->check^0xffff));					}					else {						KLIPS_PRINT(debug_rcv,				    		"klips_debug:ipsec_rcv: "							"NAT-T & TRANSPORT: zero UDP checksum\n");						udp->check = 0;					}				}				else {					KLIPS_PRINT(debug_rcv,			    		"klips_debug:ipsec_rcv: "						"NAT-T & TRANSPORT: can't fix UDP checksum\n");				}				break;			default:				KLIPS_PRINT(debug_rcv,			    	"klips_debug:ipsec_rcv: "					"NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n");				break;		}	}#endif	/*	 * XXX this needs to be locked from when it was first looked	 * up in the decapsulation loop.  Perhaps it is better to put	 * the IPIP decap inside the loop.	 */	if(tdbnext) {		tdbp = tdbnext;		sa_len = satoa(tdbp->tdb_said, 0, sa, SATOA_BUF);		if((ipp->protocol != IPPROTO_IPIP) &&                    ( 0xFE != ipp->protocol))          // added to support AT&T heartbeats to SIG/GIG                { 			spin_unlock(&tdb_lock);			KLIPS_PRINT(debug_rcv,				    "klips_debug:ipsec_rcv: "				    "SA:%s, Hey!  How did this get through?  Dropped.\n",				    sa_len ? sa : " (error)");			if(stats) {				stats->rx_dropped++;			}			goto rcvleave;		}		if(sysctl_ipsec_inbound_policy_check) {			if((tdbnext = tdbp->tdb_inext)) {				char sa2[SATOA_BUF];				size_t sa_len2;				sa_len2 = satoa(tdbnext->tdb_said, 0, sa2, SATOA_BUF);				spin_unlock(&tdb_lock);				KLIPS_PRINT(debug_rcv,					    "klips_debug:ipsec_rcv: "					    "unexpected SA:%s after IPIP SA:%s\n",					    sa_len2 ? sa2 : " (error)",					    sa_len ? sa : " (error)");				if(stats) {					stats->rx_dropped++;				}				goto rcvleave;			}			if(ipp->saddr != ((struct sockaddr_in*)(tdbp->tdb_addr_s))->sin_addr.s_addr) {				spin_unlock(&tdb_lock);				ipaddr.s_addr = ipp->saddr;				addrtoa(ipaddr, 0, ipaddr_txt, sizeof(ipaddr_txt));				KLIPS_PRINT(debug_rcv,					    "klips_debug:ipsec_rcv: "					    "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n",					    sa_len ? sa : " (error)",					    ipaddr_txt);				if(stats) {					stats->rx_dropped++;				}				goto rcvleave;			}		}  if(ipp->protocol == IPPROTO_IPIP)  // added to support AT&T heartbeats to SIG/GIG  {  		/*		 * XXX this needs to be locked from when it was first looked		 * up in the decapsulation loop.  Perhaps it is better to put		 * the IPIP decap inside the loop.		 */		tdbp->ips_life.ipl_bytes.ipl_count += len;		tdbp->ips_life.ipl_bytes.ipl_last   = len;		if(!tdbp->ips_life.ipl_usetime.ipl_count) {			tdbp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;		}		tdbp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;		tdbp->ips_life.ipl_packets.ipl_count += 1;				if(skb->len < iphlen) {			spin_unlock(&tdb_lock);			printk(KERN_WARNING "klips_debug:ipsec_rcv: "			       "tried to skb_pull iphlen=%d, %d available.  This should never happen, please report.\n",			       iphlen,			       (int)(skb->len));			goto rcvleave;		}		skb_pull(skb, iphlen);#ifdef NET_21		ipp = (struct iphdr *)skb->nh.raw = skb->data;		skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2);				memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));#else /* NET_21 */		ipp = skb->ip_hdr = skb->h.iph = (struct iphdr *)skb->data;		memset(skb->proto_priv, 0, sizeof(struct options));#endif /* NET_21 */		skb->protocol = htons(ETH_P_IP);		skb->ip_summed = 0;		KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,			    "klips_debug:ipsec_rcv: "			    "IPIP tunnel stripped.\n");		KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp);  }		if(sysctl_ipsec_inbound_policy_check		   /* 		      Note: "xor" (^) logically replaces "not equal"		      (!=) and "bitwise or" (|) logically replaces		      "boolean or" (||).  This is done to speed up		      execution by doing only bitwise operations and		      no branch operations		   */		   && (((ipp->saddr & tdbp->tdb_mask_s.u.v4.sin_addr.s_addr)			            ^ tdbp->tdb_flow_s.u.v4.sin_addr.s_addr)		       | ((ipp->daddr & tdbp->tdb_mask_d.u.v4.sin_addr.s_addr)			              ^ tdbp->tdb_flow_d.u.v4.sin_addr.s_addr)) )		{			struct in_addr daddr, saddr;			char saddr_txt[ADDRTOA_BUF], daddr_txt[ADDRTOA_BUF];			char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF];						subnettoa(tdbp->tdb_flow_s.u.v4.sin_addr,				tdbp->tdb_mask_s.u.v4.sin_addr,				0, sflow_txt, sizeof(sflow_txt));			subnettoa(tdbp->tdb_flow_d.u.v4.sin_addr,				tdbp->tdb_mask_d.u.v4.sin_addr,				0, dflow_txt, sizeof(dflow_txt));			spin_unlock(&tdb_lock);			saddr.s_addr = ipp->saddr;			daddr.s_addr = ipp->daddr;			addrtoa(saddr, 0, saddr_txt, sizeof(saddr_txt));			addrtoa(daddr, 0, daddr_txt, sizeof(daddr_txt));			KLIPS_PRINT(debug_rcv,				    "klips_debug:ipsec_rcv: "				    "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n",				    sa_len ? sa : " (error)",				    sflow_txt,				    dflow_txt,				    saddr_txt,				    daddr_txt);			if(stats) {				stats->rx_dropped++;			}			goto rcvleave;		}	}#ifdef INBOUND_POLICY_CHECK_eroute	/*	  Do *not* enable this without thoroughly checking spinlock issues	  first.  In particular, nesting an eroute spinlock within a tdb	  spinlock could result in a deadlock.  (Well, only on a SMP machine	  under 2.4?)

⌨️ 快捷键说明

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