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 + -
显示快捷键?