ipsec_rcv.c
来自「FREESWAN VPN源代码包」· C语言 代码 · 共 2,240 行 · 第 1/5 页
C
2,240 行
/* * receive code * Copyright (C) 1996, 1997 John Ioannidis. * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */char ipsec_rcv_c_version[] = "RCSID $Id: ipsec_rcv.c,v 1.15 2003/07/04 19:07:34 ken Exp $";#include <linux/config.h>#include <linux/version.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/kernel.h> /* printk() */#define IPSEC_KLIPS1_COMPAT 1#include "ipsec_param.h"#ifdef MALLOC_SLAB# include <linux/slab.h> /* kmalloc() */#else /* MALLOC_SLAB */# include <linux/malloc.h> /* kmalloc() */#endif /* MALLOC_SLAB */#include <linux/errno.h> /* error codes */#include <linux/types.h> /* size_t */#include <linux/interrupt.h> /* mark_bh */#include <linux/netdevice.h> /* struct device, and other headers */#include <linux/etherdevice.h> /* eth_type_trans */#include <linux/ip.h> /* struct iphdr */#include <linux/skbuff.h>#include <freeswan.h>#ifdef SPINLOCK# ifdef SPINLOCK_23# include <linux/spinlock.h> /* *lock* */# else /* SPINLOCK_23 */# include <asm/spinlock.h> /* *lock* */# endif /* SPINLOCK_23 */#endif /* SPINLOCK */#ifdef NET_21# include <asm/uaccess.h># include <linux/in6.h># define proto_priv cb#endif /* NET21 */#include <asm/checksum.h>#include <net/ip.h>#include "radij.h"#include "ipsec_encap.h"#include "ipsec_sa.h"#include "ipsec_radij.h"#include "ipsec_netlink.h"#include "ipsec_xform.h"#include "ipsec_tunnel.h"#include "ipsec_rcv.h"#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)# include "ipsec_ah.h"#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */#ifdef CONFIG_IPSEC_ESP# include "ipsec_esp.h"#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_IPCOMP# include "ipcomp.h"#endif /* CONFIG_IPSEC_COMP */#include <pfkeyv2.h>#include <pfkey.h>#include "ipsec_proto.h"#include "ipsec_alg.h"#ifdef CONFIG_IPSEC_NAT_TRAVERSAL#include <linux/udp.h>#endif#ifdef CONFIG_IPSEC_DEBUGint debug_ah = 0;int debug_esp = 0;int debug_rcv = 0;#endif /* CONFIG_IPSEC_DEBUG */int sysctl_ipsec_inbound_policy_check = 1;#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)__u32 zeroes[AH_AMAX];#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) *//* * Check-replay-window routine, adapted from the original * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt * * This is a routine that implements a 64 packet window. This is intend- * ed on being an implementation sample. */DEBUG_NO_STATIC intipsec_checkreplaywindow(struct ipsec_sa*tdbp, __u32 seq){ __u32 diff; if (tdbp->tdb_replaywin == 0) /* replay shut off */ return 1; if (seq == 0) return 0; /* first == 0 or wrapped */ /* new larger sequence number */ if (seq > tdbp->tdb_replaywin_lastseq) { return 1; /* larger is good */ } diff = tdbp->tdb_replaywin_lastseq - seq; /* too old or wrapped */ /* if wrapped, kill off SA? */ if (diff >= tdbp->tdb_replaywin) { return 0; } /* this packet already seen */ if (tdbp->tdb_replaywin_bitmap & (1 << diff)) return 0; return 1; /* out of order but good */}DEBUG_NO_STATIC intipsec_updatereplaywindow(struct ipsec_sa*tdbp, __u32 seq){ __u32 diff; if (tdbp->tdb_replaywin == 0) /* replay shut off */ return 1; if (seq == 0) return 0; /* first == 0 or wrapped */ /* new larger sequence number */ if (seq > tdbp->tdb_replaywin_lastseq) { diff = seq - tdbp->tdb_replaywin_lastseq; /* In win, set bit for this pkt */ if (diff < tdbp->tdb_replaywin) tdbp->tdb_replaywin_bitmap = (tdbp->tdb_replaywin_bitmap << diff) | 1; else /* This packet has way larger seq num */ tdbp->tdb_replaywin_bitmap = 1; if(seq - tdbp->tdb_replaywin_lastseq - 1 > tdbp->tdb_replaywin_maxdiff) { tdbp->tdb_replaywin_maxdiff = seq - tdbp->tdb_replaywin_lastseq - 1; } tdbp->tdb_replaywin_lastseq = seq; return 1; /* larger is good */ } diff = tdbp->tdb_replaywin_lastseq - seq; /* too old or wrapped */ /* if wrapped, kill off SA? */ if (diff >= tdbp->tdb_replaywin) {/* if(seq < 0.25*max && tdbp->tdb_replaywin_lastseq > 0.75*max) { deltdbchain(tdbp); }*/ return 0; } /* this packet already seen */ if (tdbp->tdb_replaywin_bitmap & (1 << diff)) return 0; tdbp->tdb_replaywin_bitmap |= (1 << diff); /* mark as seen */ return 1; /* out of order but good */}int#ifdef PROTO_HANDLER_SINGLE_PARMipsec_rcv(struct sk_buff *skb)#else /* PROTO_HANDLER_SINGLE_PARM */#ifdef NET_21ipsec_rcv(struct sk_buff *skb, unsigned short xlen)#else /* NET_21 */ipsec_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, __u32 daddr_unused, unsigned short xlen, __u32 saddr, int redo, struct inet_protocol *protocol)#endif /* NET_21 */#endif /* PROTO_HANDLER_SINGLE_PARM */{#ifdef NET_21#ifdef CONFIG_IPSEC_DEBUG struct device *dev = skb->dev;#endif /* CONFIG_IPSEC_DEBUG */#endif /* NET_21 */ unsigned char protoc; struct iphdr *ipp; int authlen = 0;#ifdef CONFIG_IPSEC_ESP struct esp *espp = NULL; int esphlen = 0;#ifdef CONFIG_IPSEC_ENC_3DES __u32 iv[ESP_IV_MAXSZ_INT];#endif /* !CONFIG_IPSEC_ENC_3DES */#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH struct ah *ahp = NULL; int ahhlen = 0;#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) struct iphdr ipo;#endif#endif /* CONFIG_IPSEC_AH */ unsigned char *authenticator = NULL; union { MD5_CTX md5; SHA1_CTX sha1; } tctx; __u8 hash[AH_AMAX];#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */#ifdef CONFIG_IPSEC_IPCOMP struct ipcomphdr*compp = NULL;#endif /* CONFIG_IPSEC_IPCOMP */ int hard_header_len; int iphlen; unsigned char *dat; struct ipsec_sa *tdbp = NULL; struct sa_id said; struct net_device_stats *stats = NULL; /* This device's statistics */ struct device *ipsecdev = NULL, *prvdev; struct ipsecpriv *prv; char name[9]; char sa[SATOA_BUF]; size_t sa_len; char ipaddr_txt[ADDRTOA_BUF]; int i; struct in_addr ipaddr; __u8 next_header = 0; __u8 proto; #ifdef CONFIG_IPSEC_ESP int pad = 0, padlen;#endif /* CONFIG_IPSEC_ESP */ int ilen; /* content to be decrypted/authenticated */ int len; /* packet length */ int replay = 0; /* replay value in AH or ESP packet */ __u8 *idat; /* pointer to content to be decrypted/authenticated */ struct ipsec_sa* tdbprev = NULL; /* previous SA from outside of packet */ struct ipsec_sa* tdbnext = NULL; /* next SA towards inside of packet */#ifdef INBOUND_POLICY_CHECK_eroute struct sockaddr_encap matcher; /* eroute search key */ struct eroute *er; struct ipsec_sa* policy_tdb = NULL; struct sa_id policy_said; struct sockaddr_encap policy_eaddr; struct sockaddr_encap policy_emask;#endif /* INBOUND_POLICY_CHECK_eroute */#ifdef CONFIG_IPSEC_ALG struct ipsec_alg_enc *ixt_e=NULL; struct ipsec_alg_auth *ixt_a=NULL;#endif /* CONFIG_IPSEC_ALG */#ifdef CONFIG_IPSEC_NAT_TRAVERSAL __u16 natt_len = 0, natt_sport = 0, natt_dport = 0; __u8 natt_type = 0;#endif /* Don't unlink in the middle of a turnaround */ MOD_INC_USE_COUNT; if (skb == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "NULL skb passed in.\n"); goto rcvleave; } if (skb->data == NULL) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "NULL skb->data passed in, packet is bogus, dropping.\n"); goto rcvleave; }#ifdef CONFIG_IPSEC_NAT_TRAVERSAL if (skb->sk && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_UDP) { /** * Packet comes from udp_queue_rcv_skb so it is already defrag, * checksum verified, ... (ie safe to use) * * If the packet is not for us, return -1 and udp_queue_rcv_skb * will continue to handle it (do not kfree skb !!). */ struct udp_opt *tp = &(skb->sk->tp_pinfo.af_udp); struct iphdr *ip = (struct iphdr *)skb->nh.iph; struct udphdr *udp = (struct udphdr *)((__u32 *)ip+ip->ihl); __u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr); __u32 *udpdata32 = (__u32 *)udpdata; natt_sport = ntohs(udp->source); natt_dport = ntohs(udp->dest); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "suspected ESPinUDP packet (NAT-Traversal) [%d].\n", tp->esp_in_udp); KLIPS_IP_PRINT(debug_rcv, ip); if (udpdata < skb->tail) { unsigned int len = skb->tail - udpdata; if ((len==1) && (udpdata[0]==0xff)) { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " /* not IPv6 compliant message */ "NAT-keepalive from %d.%d.%d.%d.\n", NIPQUAD(ip->saddr)); goto rcvleave; } else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE) && (len > (2*sizeof(__u32) + sizeof(struct esp))) && (udpdata32[0]==0) && (udpdata32[1]==0) ) { /* ESP Packet with Non-IKE header */ KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "ESPinUDP pkt with Non-IKE - spi=0x%x\n", udpdata32[2]); natt_type = ESPINUDP_WITH_NON_IKE; natt_len = sizeof(struct udphdr)+(2*sizeof(__u32)); } else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP) && (len > sizeof(struct esp)) && (udpdata32[0]!=0) ) { /* ESP Packet without Non-ESP header */ natt_type = ESPINUDP_WITH_NON_ESP; natt_len = sizeof(struct udphdr); KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "ESPinUDP pkt without Non-ESP - spi=0x%x\n", udpdata32[0]); } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "IKE packet - not handled here\n"); MOD_DEC_USE_COUNT; return -1; } } else { MOD_DEC_USE_COUNT; return -1; } }#endif #ifdef IPH_is_SKB_PULLED /* In Linux 2.4.4, the IP header has been skb_pull()ed before the packet is passed to us. So we'll skb_push() to get back to it. */ if (skb->data == skb->h.raw) { skb_push(skb, skb->h.raw - skb->nh.raw); }#endif /* IPH_is_SKB_PULLED */ ipp = (struct iphdr *)skb->data; iphlen = ipp->ihl << 2; /* dev->hard_header_len is unreliable and should not be used */ hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0; if((hard_header_len < 0) || (hard_header_len > skb_headroom(skb))) hard_header_len = 0;#ifdef NET_21 /* if skb was cloned (most likely due to a packet sniffer such as tcpdump being momentarily attached to the interface), make a copy of our own to modify */ if(skb_cloned(skb)) { /* include any mac header while copying.. */ if(skb_headroom(skb) < hard_header_len) { printk(KERN_WARNING "klips_error:ipsec_rcv: " "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n", hard_header_len, skb_headroom(skb)); goto rcvleave; } skb_push(skb, hard_header_len); if#ifdef SKB_COW_NEW (skb_cow(skb, skb_headroom(skb)) != 0)#else /* SKB_COW_NEW */ ((skb = skb_cow(skb, skb_headroom(skb))) == NULL)#endif /* SKB_COW_NEW */ { goto rcvleave; } if(skb->len < hard_header_len) { printk(KERN_WARNING "klips_error:ipsec_rcv: " "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n", hard_header_len, skb->len); goto rcvleave; } skb_pull(skb, hard_header_len); } #endif /* NET_21 */ #if IP_FRAGMENT_LINEARIZE /* In Linux 2.4.4, we may have to reassemble fragments. They are not assembled automatically to save TCP from having to copy twice. */ if (skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_ATOMIC) != 0) { goto rcvleave; } } ipp = (struct iphdr *)skb->nh.iph; iphlen = ipp->ihl << 2;#endif #ifdef CONFIG_IPSEC_NAT_TRAVERSAL if (natt_len) { /** * Now, we are sure packet is ESPinUDP. Remove natt_len bytes from * packet and modify protocol to ESP. */ if (((unsigned char *)skb->data > (unsigned char *)skb->nh.iph) && ((unsigned char *)skb->nh.iph > (unsigned char *)skb->head)) { unsigned int _len = (unsigned char *)skb->data - (unsigned char *)skb->nh.iph; KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: adjusting skb: skb_push(%u)\n", _len); skb_push(skb, _len); } KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "removing %d bytes from ESPinUDP packet\n", natt_len); ipp = (struct iphdr *)skb->data; iphlen = ipp->ihl << 2; ipp->tot_len = htons(ntohs(ipp->tot_len) - natt_len); if (skb->len < iphlen + natt_len) { printk(KERN_WARNING "klips_error:ipsec_rcv: " "ESPinUDP packet is too small (%d < %d+%d). " "This should never happen, please report.\n", (int)(skb->len), iphlen, natt_len);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?