ipsec_tunnel.c

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

C
2,178
字号
#endif /* 0 */#endif /* NET_21 */		}		/* 		   If the sender is doing PMTU discovery, and the		   packet doesn't fit within prv->mtu, notify him		   (unless it was an ICMP packet, or it was not the		   zero-offset packet) and send it anyways.		   Note: buggy firewall configuration may prevent the		   ICMP packet from getting back.		*/		if(sysctl_ipsec_icmp		   && prv->mtu < ntohs(iph->tot_len)		   && (iph->frag_off & __constant_htons(IP_DF)) ) {			int notify = iph->protocol != IPPROTO_ICMP				&& (iph->frag_off & __constant_htons(IP_OFFSET)) == 0;			#ifdef IPSEC_obey_DF			spin_unlock(&tdb_lock);			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "fragmentation needed and DF set; %sdropping packet\n",				    notify ? "sending ICMP and " : "");			if (notify)				ICMP_SEND(skb,					  ICMP_DEST_UNREACH,					  ICMP_FRAG_NEEDED,					  prv->mtu,					  physdev);			stats->tx_errors++;			goto cleanup;#else /* IPSEC_obey_DF */			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "fragmentation needed and DF set; %spassing packet\n",				    notify ? "sending ICMP and " : "");			if (notify)				ICMP_SEND(skb,					  ICMP_DEST_UNREACH,					  ICMP_FRAG_NEEDED,					  prv->mtu,					  physdev);#endif /* IPSEC_obey_DF */		}		#ifdef MSS_HACK		/*		 * If this is a transport mode TCP packet with		 * SYN set, determine an effective MSS based on 		 * AH/ESP overheads determined above.		 */		if (iph->protocol == IPPROTO_TCP 		    && outgoing_said.proto != IPPROTO_IPIP) {			struct tcphdr *tcph = skb->h.th;			if (tcph->syn && !tcph->ack) {				if(!ipsec_adjust_mss(skb, tcph, prv->mtu)) {					spin_unlock(&tdb_lock);					printk(KERN_WARNING					       "klips_warning:ipsec_tunnel_start_xmit: "					       "ipsec_adjust_mss() failed\n");					stats->tx_errors++;					goto cleanup;				}			}		}#endif /* MSS_HACK */#ifdef CONFIG_IPSEC_NAT_TRAVERSAL	if ((natt_type) && (outgoing_said.proto != 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.		 * If we don't we can zero udp checksum.		 */		__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 *)iph;		__u16 data_len = pkt_len - (iph->ihl << 2);		switch (iph->protocol) {			case IPPROTO_TCP:				if (data_len >= sizeof(struct tcphdr)) {					struct tcphdr *tcp = (struct tcphdr *)((__u32 *)iph+iph->ihl);					if (natt_oa) {						__u32 buff[2] = { ~iph->daddr, natt_oa };						KLIPS_PRINT(debug_tunnel,							"klips_debug:ipsec_tunnel_start_xmit: "							"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_tunnel,							"klips_debug:ipsec_tunnel_start_xmit: "							"NAT-T & TRANSPORT: do not recalc TCP checksum\n");					}				}				else {					KLIPS_PRINT(debug_tunnel,						"klips_debug:ipsec_tunnel_start_xmit: "						"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 *)iph+iph->ihl);					if (udp->check == 0) {						KLIPS_PRINT(debug_tunnel,							"klips_debug:ipsec_tunnel_start_xmit: "							"NAT-T & TRANSPORT: UDP checksum already 0\n");					}					else if (natt_oa) {						__u32 buff[2] = { ~iph->daddr, natt_oa };						KLIPS_PRINT(debug_tunnel,							"klips_debug:ipsec_tunnel_start_xmit: "							"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_tunnel,							"klips_debug:ipsec_tunnel_start_xmit: "							"NAT-T & TRANSPORT: zero UDP checksum\n");						udp->check = 0;					}				}				else {					KLIPS_PRINT(debug_tunnel,						"klips_debug:ipsec_tunnel_start_xmit: "						"NAT-T & TRANSPORT: can't fix UDP checksum\n");				}				break;			default:				KLIPS_PRINT(debug_tunnel,					"klips_debug:ipsec_tunnel_start_xmit: "					"NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n");				break;		}	}#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */		if(!hard_header_stripped) {			if((saved_header = kmalloc(hard_header_len, GFP_ATOMIC)) == NULL) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING "klips_debug:ipsec_tunnel_start_xmit: "				       "Failed, tried to allocate %d bytes for temp hard_header.\n", 				       hard_header_len);				stats->tx_errors++;				goto cleanup;			}			for (i = 0; i < hard_header_len; i++) {				saved_header[i] = skb->data[i];			}			if(skb->len < hard_header_len) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: "				       "tried to skb_pull hhlen=%d, %d available.  This should never happen, please report.\n",				       hard_header_len, (int)(skb->len));				stats->tx_errors++;				goto cleanup;			}			skb_pull(skb, hard_header_len);			hard_header_stripped = 1;			/*			iph = (struct iphdr *) (skb->data); */			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "head,tailroom: %d,%d after hard_header stripped.\n",				    skb_headroom(skb), skb_tailroom(skb));			KLIPS_IP_PRINT(debug_tunnel & DB_TN_CROUT, iph);		} else {			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "hard header already stripped.\n");		}				ll_headroom = (hard_header_len + 15) & ~15;		if ((skb_headroom(skb) >= max_headroom + 2 * ll_headroom) && 		    (skb_tailroom(skb) >= max_tailroom)#ifndef NET_21			&& skb->free#endif /* !NET_21 */			) {			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "data fits in existing skb\n");		} else {			struct sk_buff* tskb = skb;			if(!oskb) {				oskb = skb;			}			tskb = skb_copy_expand(skb,			/* The reason for 2 * link layer length here still baffles me...RGB */					       max_headroom + 2 * ll_headroom,					       max_tailroom,					       GFP_ATOMIC);#ifdef NET_21			if(tskb && skb->sk) {				skb_set_owner_w(tskb, skb->sk);			}#endif /* NET_21 */			if(!(skb == oskb) ) {				dev_kfree_skb(skb, FREE_WRITE);			}			skb = tskb;			if (!skb) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING				       "klips_debug:ipsec_tunnel_start_xmit: "				       "Failed, tried to allocate %d head and %d tailroom\n", 				       max_headroom, max_tailroom);				stats->tx_errors++;				goto cleanup;			}			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "head,tailroom: %d,%d after allocation\n",				    skb_headroom(skb), skb_tailroom(skb));		}				/*		 * Apply grouped transforms to packet		 */		while (tdbp) {#ifdef CONFIG_IPSEC_ESP			struct esp *espp;#ifdef CONFIG_IPSEC_ENC_3DES			__u32 iv[ESP_IV_MAXSZ_INT];#endif /* !CONFIG_IPSEC_ENC_3DES */			unsigned char *idat, *pad;			int authlen = 0, padlen = 0, i;#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH			struct iphdr ipo;			struct ah *ahp;#endif /* CONFIG_IPSEC_AH */#if defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1)			union {#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5				MD5_CTX md5;#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1				SHA1_CTX sha1;#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */			} tctx;			__u8 hash[AH_AMAX];#endif /* defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) */			int headroom = 0, tailroom = 0, ilen = 0, len = 0;			unsigned char *dat;						iphlen = iph->ihl << 2;			pyldsz = ntohs(iph->tot_len) - iphlen;			sa_len = satoa(tdbp->tdb_said, 0, sa, SATOA_BUF);			KLIPS_PRINT(debug_tunnel & DB_TN_OXFS,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "calling output for <%s%s%s>, SA:%s\n", 				    IPS_XFORM_NAME(tdbp),				    sa_len ? sa : " (error)");						switch(tdbp->tdb_said.proto) {#ifdef CONFIG_IPSEC_AH			case IPPROTO_AH:				headroom += sizeof(struct ah);				break;#endif /* CONFIG_IPSEC_AH */#ifdef CONFIG_IPSEC_ESP			case IPPROTO_ESP:#ifdef CONFIG_IPSEC_ALG				if ((ixt_e=IPSEC_ALG_SA_ESP_ENC(tdbp))) {					blocksize = ixt_e->ixt_blocksize;					headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;				} else#endif /* CONFIG_IPSEC_ALG */				switch(tdbp->tdb_encalg) {#ifdef CONFIG_IPSEC_ENC_3DES				case ESP_3DES:					headroom += sizeof(struct esp);					break;#endif /* CONFIG_IPSEC_ENC_3DES */				default:					spin_unlock(&tdb_lock);					stats->tx_errors++;					goto cleanup;				}#ifdef CONFIG_IPSEC_ALG				if ((ixt_a=IPSEC_ALG_SA_ESP_AUTH(tdbp))) {					authlen = AHHMAC_HASHLEN;				} else#endif /* CONFIG_IPSEC_ALG */				switch(tdbp->tdb_authalg) {#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5				case AH_MD5:					authlen = AHHMAC_HASHLEN;					break;#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1				case AH_SHA:					authlen = AHHMAC_HASHLEN;					break;#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */				case AH_NONE:					break;				default:					spin_unlock(&tdb_lock);					stats->tx_errors++;					goto cleanup;				}						tailroom +=					blocksize != 1 ?					((blocksize - ((pyldsz + 2) % blocksize)) % blocksize) + 2 :					((4 - ((pyldsz + 2) % 4)) % 4) + 2;				tailroom += authlen;				break;#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_IPIP			case IPPROTO_IPIP:				headroom += sizeof(struct iphdr);				break;#endif /* !CONFIG_IPSEC_IPIP */#ifdef CONFIG_IPSEC_IPCOMP			case IPPROTO_COMP:				break;#endif /* CONFIG_IPSEC_IPCOMP */			default:				spin_unlock(&tdb_lock);				stats->tx_errors++;				goto cleanup;			}						KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "pushing %d bytes, putting %d, proto %d.\n", 				    headroom, tailroom, tdbp->tdb_said.proto);			if(skb_headroom(skb) < headroom) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING				       "klips_error:ipsec_tunnel_start_xmit: "				       "tried to skb_push headroom=%d, %d available.  This should never happen, please report.\n",				       headroom, skb_headroom(skb));				stats->tx_errors++;				goto cleanup;			}			dat = skb_push(skb, headroom);			ilen = skb->len - tailroom;			if(skb_tailroom(skb) < tailroom) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING				       "klips_error:ipsec_tunnel_start_xmit: "				       "tried to skb_put %d, %d available.  This should never happen, please report.\n",				       tailroom, skb_tailroom(skb));				stats->tx_errors++;				goto cleanup;			}			skb_put(skb, tailroom);			KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,				    "klips_debug:ipsec_tunnel_start_xmit: "				    "head,tailroom: %d,%d before xform.\n",				    skb_headroom(skb), skb_tailroom(skb));			len = skb->len;			if(len > 0xfff0) {				spin_unlock(&tdb_lock);				printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: "				       "tot_len (%d) > 65520.  This should never happen, please report.\n",				       len);				stats->tx_errors++;				goto cleanup;			}			memmove((void *)dat, (void *)(dat + headroom), iphlen);			iph = (struct iphdr *)dat;			iph->tot_len = htons(skb->len);						switch(tdbp->tdb_said.proto) {#ifdef CONFIG_IPSEC_ESP			case IPPROTO_ESP:				espp = (struct esp *)(dat + iphlen);				espp->esp_spi = tdbp->tdb_said.spi;				espp->esp_rpl = htonl(++(tdbp->tdb_replaywin_lastseq));				#ifdef CONFIG_IPSEC_ALG				if (!ixt_e)#endif /* CONFIG_IPSEC_ALG */				switch(tdbp->tdb_encalg) {#if defined(CONFIG_IPSEC_ENC_3DES)#ifdef CONFIG_IPSEC_ENC_3DES				case ESP_3DES:#endif /* CONFIG_IPSEC_ENC_3DES */					iv[0] = *((__u32*)&(espp->esp_iv)    ) =						((__u32*)(tdbp->tdb_iv))[0];					iv[1] = *((__u32*)&(espp->esp_iv) + 1) =						((__u32*)(tdbp->tdb_iv))[1];					break;#endif /* defined(CONFIG_IPSEC_ENC_3DES) */				default:					spin_unlock(&tdb_lock);					stats->tx_errors++;					goto cleanup;				}								idat = dat + iphlen + headroom;				ilen = len - (iphlen + headroom + authlen);								/* Self-describing padding */				pad = &dat[len - tailroom];				padlen = tailroom - 2 - authlen;				for (i = 0; i < padlen; i++) {					pad[i] = i + 1; 				}				dat[len - authlen - 2] = padlen;								dat[len - authlen - 1] = iph->protocol;				iph->protocol = IPPROTO_ESP;				#ifdef CONFIG_IPSEC_ALG				/* Do all operations here:				 * copy IV->ESP, encrypt, update ips IV				 */				if (ixt_e) {					int ret;					memcpy(espp->esp_iv, 						tdbp->ips_iv, 						ixt_e->ixt_ivlen/8);					ret=ipsec_alg_esp_encrypt(tdbp, 						idat, ilen, espp->esp_iv,						IPSEC_ALG_ENCRYPT);					memcpy(tdbp->ips_iv,						idat + ilen - ixt_e->ixt_ivlen/8,

⌨️ 快捷键说明

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