📄 ipsec_rcv.c
字号:
/* * 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.87 2001/06/13 20:58:40 rgb Exp $";#include <linux/config.h>#include <linux/version.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/kernel.h> /* printk() */#include <linux/malloc.h> /* kmalloc() */#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_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>#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;#ifdef CONFIG_IPSEC_ESPextern void des_ede3_cbc_encrypt(caddr_t, caddr_t, int, caddr_t, caddr_t, caddr_t, caddr_t, int);#endif /* !CONFIG_IPSEC_ESP */#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 tdb*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 tdb*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 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, unsigned short xlen, __u32 saddr, int redo, struct inet_protocol *protocol)#endif /* NET_21 */{#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; __u32 iv[2];#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH struct ah *ahp = NULL; int ahhlen = 0; struct iphdr ipo;#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 tdb *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, len, replay = 0; __u8 *idat; struct tdb* tdbprev = NULL; struct tdb* tdbnext = NULL;#ifdef INBOUND_POLICY_CHECK_eroute struct sockaddr_encap matcher; /* eroute search key */ struct eroute *er; struct tdb* policy_tdb = NULL; struct sa_id policy_said; struct sockaddr_encap policy_eaddr; struct sockaddr_encap policy_emask;#endif /* INBOUND_POLICY_CHECK_eroute */ /* 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 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 */ KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "<<< Info -- "); KLIPS_PRINTMORE(debug_rcv && skb->dev, "skb->dev=%s ", skb->dev->name ? skb->dev->name : "NULL"); KLIPS_PRINTMORE(debug_rcv && dev, "dev=%s ", dev->name ? dev->name : "NULL"); KLIPS_PRINTMORE(debug_rcv, "\n"); KLIPS_PRINT(debug_rcv && !(skb->dev && dev && (skb->dev == dev)), "klips_debug:ipsec_rcv: " "Informational -- **if this happens, find out why** skb->dev:%s is not equal to dev:%s\n", skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL", dev ? (dev->name ? dev->name : "NULL") : "NULL"); protoc = ipp->protocol;#ifndef NET_21 if((!protocol) || (protocol->protocol != protoc)) { KLIPS_PRINT(debug_rcv & DB_RX_TDB, "klips_debug:ipsec_rcv: " "protocol arg is NULL or unequal to the packet contents, this is odd, using value in packet.\n"); }#endif /* !NET_21 */ if( (protoc != IPPROTO_AH) &&#ifdef CONFIG_IPSEC_IPCOMP (protoc != IPPROTO_COMP) &&#endif /* CONFIG_IPSEC_IPCOMP */ (protoc != IPPROTO_ESP) ) { KLIPS_PRINT(debug_rcv & DB_RX_TDB, "klips_debug:ipsec_rcv: Why the hell is someone " "passing me a non-ipsec packet? -- dropped.\n"); goto rcvleave; } if(skb->dev) { for(i = 0; i < IPSEC_NUM_IF; i++) { sprintf(name, "ipsec%d", i); if(!strcmp(name, skb->dev->name)) { prv = (struct ipsecpriv *)(skb->dev->priv); if(prv) { stats = (struct net_device_stats *) &(prv->mystats); } ipsecdev = skb->dev; KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "Info -- pkt already proc'ed a group of ipsec headers, processing next group of ipsec headers.\n"); break; } if((ipsecdev = ipsec_dev_get(name)) == NULL) { KLIPS_PRINT(debug_rcv, "klips_error:ipsec_rcv: " "device %s does not exist\n", name); } prv = ipsecdev ? (struct ipsecpriv *)(ipsecdev->priv) : NULL; prvdev = prv ? (struct device *)(prv->dev) : NULL; #if 0 KLIPS_PRINT(debug_rcv && prvdev, "klips_debug:ipsec_rcv: " "physical device for device %s is %s\n", name, prvdev->name);#endif if(prvdev && skb->dev && !strcmp(prvdev->name, skb->dev->name)) { stats = prv ? ((struct net_device_stats *) &(prv->mystats)) : NULL; skb->dev = ipsecdev; KLIPS_PRINT(debug_rcv && prvdev, "klips_debug:ipsec_rcv: " "assigning packet ownership to virtual device %s from physical device %s.\n", name, prvdev->name); if(stats) { stats->rx_packets++; } break; } } } else { KLIPS_PRINT(debug_rcv, "klips_debug:ipsec_rcv: " "device supplied with skb is NULL\n"); } if(!stats) { ipsecdev = NULL; } KLIPS_PRINT((debug_rcv && !stats), "klips_error:ipsec_rcv: " "packet received from physical I/F (%s) not connected to ipsec I/F. Cannot record stats. May not have SA for decoding. Is IPSEC traffic expected on this I/F? Check routing.\n", skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL"); KLIPS_IP_PRINT(debug_rcv, ipp); /* begin decapsulating loop here */ do { authlen = 0;#ifdef CONFIG_IPSEC_ESP espp = NULL; esphlen = 0;#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH ahp = NULL; ahhlen = 0;#endif /* CONFIG_IPSEC_AH */#ifdef CONFIG_IPSEC_IPCOMP compp = NULL;#endif /* CONFIG_IPSEC_IPCOMP */ len = skb->len; dat = skb->data; ipp = (struct iphdr *)skb->data; proto = ipp->protocol; ipaddr.s_addr = ipp->saddr; addrtoa(ipaddr, 0, ipaddr_txt, sizeof(ipaddr_txt)); iphlen = ipp->ihl << 2; ipp->check = 0; /* we know the sum is good */ #ifdef CONFIG_IPSEC_ESP /* XXX this will need to be 8 for IPv6 */ if ((proto == IPPROTO_ESP) && ((len - iphlen) % 4)) { printk("klips_error:ipsec_rcv: " "got packet with content length = %d from %s -- should be on 4 octet boundary, packet dropped\n", len - iphlen, ipaddr_txt); if(stats) { stats->rx_errors++; } goto rcvleave; }#endif /* !CONFIG_IPSEC_ESP */ /* * Find tunnel control block and (indirectly) call the * appropriate tranform routine. The resulting sk_buf * is a valid IP packet ready to go through input processing. */ said.dst.s_addr = ipp->daddr; switch(proto) {#ifdef CONFIG_IPSEC_ESP case IPPROTO_ESP: espp = (struct esp *)(skb->data + iphlen); said.spi = espp->esp_spi; break;#endif /* !CONFIG_IPSEC_ESP */#ifdef CONFIG_IPSEC_AH case IPPROTO_AH: ahp = (struct ah *)(skb->data + iphlen); said.spi = ahp->ah_spi; break;#endif /* CONFIG_IPSEC_AH */#ifdef CONFIG_IPSEC_IPCOMP case IPPROTO_COMP: compp = (struct ipcomphdr *)(skb->data + iphlen); said.spi = htonl((__u32)ntohs(compp->ipcomp_cpi)); break;#endif /* CONFIG_IPSEC_IPCOMP */ default: if(stats) { stats->rx_errors++; } goto rcvleave; } said.proto = proto; sa_len = satoa(said, 0, sa, SATOA_BUF); #ifdef CONFIG_IPSEC_AH if(proto == IPPROTO_AH) { ahhlen = (ahp->ah_hl << 2) + ((caddr_t)&(ahp->ah_rpl) - (caddr_t)ahp); next_header = ahp->ah_nh; if (ahhlen != sizeof(struct ah)) { KLIPS_PRINT(debug_rcv & DB_RX_INAU, "klips_debug:ipsec_rcv: " "bad authenticator length %d, expected %d from %s\n", ahhlen - ((caddr_t)(ahp->ah_data) - (caddr_t)ahp), AHHMAC_HASHLEN, ipaddr_txt); if(stats) { stats->rx_errors++; } goto rcvleave; } }#endif /* CONFIG_IPSEC_AH */ /* The spinlock is to prevent any other process from accessing or deleting the TDB hash table or any of the TDBs while we are using and updating them. This is not optimal, but was relatively straightforward at the time. A better way to do it has been planned for more than a year, to lock the hash table and put reference counts on each TDB instead. This is not likely to happen in KLIPS1 unless a volunteer contributes it, but will be designed into KLIPS2. */ if(tdbprev == NULL) { spin_lock(&tdb_lock); } #ifdef CONFIG_IPSEC_IPCOMP if (proto == IPPROTO_COMP) { unsigned int flags = 0; if (tdbp == NULL) { spin_unlock(&tdb_lock); KLIPS_PRINT(debug_rcv,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -