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