esp.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 904 行 · 第 1/2 页
C
904 行
/* * Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com> * * 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. * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free software Foundation, Inc., * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA * */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/kernel.h>#include <asm/uaccess.h>#include <linux/init.h>#include <linux/version.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/net.h>#include <linux/in.h>#include <linux/inet.h>#include <net/ip.h>#include <net/protocol.h>#include <net/route.h>#include <linux/if_ether.h>#include <linux/icmp.h>#include <asm/scatterlist.h>#include <linux/crypto.h>#include <linux/pfkeyv2.h>#include <linux/random.h>#include <esp.h>#include <sa.h>#include <sa_algorithm.h>#include <tunnel.h>#include <vnet.h>#include <skb_util.h>#include <skb_context.h>static const int DEBUG_ICV = 0;#define MODULE_NAME "IPSEC"#define DEBUG 1#undef DEBUG#include "debug.h"#ifndef CONFIG_CRYPTO_HMAC#warning No esp transform - CONFIG_CRYPTO_HMAC not definedint __init esp_module_init(void){ return 0;}void __exit esp_module_exit(void){}#else/* Outgoing packet: [ eth | ip | data ] * After etherip: [ eth2 | ip2 | ethip | eth | ip | data ] * After esp : [ eth2 | ip2 | esp | {ethip | eth | ip | data} | pad | icv ] * ^ + * The curly braces { ... } denote encryption. * The esp header includes the fixed esp headers and the iv (variable size). * The point marked ^ does not move. To the left is in the header, to the right * is in the frag. Remember that all outgoing skbs (from domains) have 1 frag. * Data after + is added by esp, using an extra frag. * * Incoming as above. * After decrypt: [ eth2 | ip2 | esp | ethip | eth | ip | data | pad | icv ] * Trim tail: [ eth2 | ip2 | esp | ethip | eth | ip | data ] * Drop hdr: [ eth2 | ip2 | ethip | eth | ip | data ] * ^ * The point marked ^ does not move. Incoming skbs are linear (no frags). * The tail is trimmed by adjusting skb->tail and len. * The esp hdr is dropped by using memmove to move the headers and * adjusting the skb pointers. * * todo: Now this code is in linux we can't assume 1 frag for outbound skbs, * or (maybe) that memmove is safe on inbound. *//** Round n up to a multiple of block. * If block is less than 2 does nothing. * Otherwise assume block is a power of 2. * * @param n to round up * @param block size to round to a multiple of * @return rounded value */static inline int roundupto(int n, int block){ if(block <= 1) return n; block--; return (n + block) & ~block;}/** Check if n is a multiple of block. * If block is less than 2 returns 1. * Otherwise assumes block is a power of 2. * * @param n to check * @param block block size * @return 1 if a multiple, 0 otherwise */static inline int multipleof(int n, int block){ if(block <= 1) return 1; block--; return !(n & block);}/** Convert from bits to bytes. * * @param n number of bits * @return number of bytes */static inline int bits_to_bytes(int n){ return n / 8;}/** Insert esp padding at the end of an skb. * Inserts padding bytes, number of padding bytes, protocol number. * * @param skb skb * @param offset offset from skb end to where padding should end * @param extra_n total amount of padding * @param protocol protocol number (from original ip hdr) * @return 0 on success, error code otherwise */static int esp_sa_pad(struct sk_buff *skb, int offset, int extra_n, unsigned char protocol){ int err; char *data; int pad_n = extra_n - ESP_PAD_N; int i; char buf[extra_n]; data = buf; for(i = 1; i <= pad_n; i++){ *data++ = i; } *data++ = pad_n; *data++ = protocol; err = skb_put_bits(skb, skb->len - offset - extra_n, buf, extra_n); return err;}/** Encrypt skb. Skips esp header and iv. * Assumes skb->data points at esp header. * * @param esp esp state * @parm esph esp header * @param skb packet * @param head_n size of esp header and iv * @param iv_n size of iv * @param text_n size of ciphertext * @return 0 on success, error code otherwise */static int esp_sa_encrypt(ESPState *esp, ESPHdr *esph, struct sk_buff *skb, int head_n, int iv_n, int text_n){ int err = 0; int sg_n = skb_shinfo(skb)->nr_frags + 1; struct scatterlist sg[sg_n]; err = skb_scatterlist(skb, sg, &sg_n, head_n, text_n); if(err) goto exit; if(iv_n){ crypto_cipher_set_iv(esp->cipher.tfm, esp->cipher.iv, iv_n); } crypto_cipher_encrypt(esp->cipher.tfm, sg, sg, text_n); if(iv_n){ memcpy(esph->data, esp->cipher.iv, iv_n); crypto_cipher_get_iv(esp->cipher.tfm, esp->cipher.iv, iv_n); } exit: return err;}/** Decrypt skb. Skips esp header and iv. * Assumes skb->data points at esp header. * * @param esp esp state * @parm esph esp header * @param skb packet * @param head_n size of esp header and iv * @param iv_n size of iv * @param text_n size of ciphertext * @return 0 on success, error code otherwise */static int esp_sa_decrypt(ESPState *esp, ESPHdr *esph, struct sk_buff *skb, int head_n, int iv_n, int text_n){ int err = 0; int sg_n = skb_shinfo(skb)->nr_frags + 1; struct scatterlist sg[sg_n]; err = skb_scatterlist(skb, sg, &sg_n, head_n, text_n); if(err) goto exit; if(iv_n){ crypto_cipher_set_iv(esp->cipher.tfm, esph->data, iv_n); } crypto_cipher_decrypt(esp->cipher.tfm, sg, sg, text_n); exit: return err;}/** Compute icv. Includes esp header, iv and ciphertext. * Assumes skb->data points at esp header. * * @param esp esp state * @param skb packet * @param digest_n number of bytes to digest * @param icv_n size of icv * @return 0 on success, error code otherwise */static int esp_sa_digest(ESPState *esp, struct sk_buff *skb, int digest_n, int icv_n){ int err = 0; u8 icv[icv_n]; if(DEBUG_ICV){ dprintf("> skb digest_n=%d icv_n=%d\n", digest_n, icv_n); skb_print_bits("esp", skb, 0, digest_n); } memset(icv, 0, icv_n); esp->digest.icv(esp, skb, 0, digest_n, icv); skb_put_bits(skb, digest_n, icv, icv_n); return err;}/** Check the icv and trim it from the skb tail. * * @param sa sa state * @param esp esp state * @param esph esp header * @param skb packet * @return 0 on success, error code otherwise */static int esp_check_icv(SAState *sa, ESPState *esp, ESPHdr *esph, struct sk_buff *skb){ int err = 0; int icv_n = esp->digest.icv_n; int digest_n = skb->len - icv_n; u8 icv_skb[icv_n]; u8 icv_new[icv_n]; dprintf(">\n"); if(DEBUG_ICV){ dprintf("> skb len=%d digest_n=%d icv_n=%d\n", skb->len, digest_n, icv_n); skb_print_bits("esp", skb, 0, skb->len); } if(skb_copy_bits(skb, digest_n, icv_skb, icv_n)){ wprintf("> Error getting icv from skb\n"); goto exit; } esp->digest.icv(esp, skb, 0, digest_n, icv_new); if(DEBUG_ICV){ dprintf("> len=%d icv_n=%d", digest_n, icv_n); printk("\nskb="); buf_print(icv_skb, icv_n); printk("new="); buf_print(icv_new, icv_n); } if(unlikely(memcmp(icv_new, icv_skb, icv_n))){ wprintf("> ICV check failed!\n"); err = -EINVAL; sa->counts.integrity_failures++; goto exit; } skb_trim_tail(skb, icv_n); exit: dprintf("< err=%d\n", err); return err;}/** Send a packet via an ESP SA. * * @param sa SA state * @param skb packet to send * @param tunnel underlying tunnel * @return 0 on success, negative error code otherwise */static int esp_sa_send(SAState *sa, struct sk_buff *skb, Tunnel *tunnel){ int err = 0; int ip_n; // Size of ip header. int plaintext_n; // Size of plaintext. int ciphertext_n; // Size of ciphertext (including padding). int extra_n; // Extra bytes needed for ciphertext. int icv_n = 0; // Size of integrity check value (icv). int iv_n = 0; // Size of initialization vector (iv). int head_n; // Size of esp header and iv. int tail_n; // Size of esp trailer: padding and icv. ESPState *esp; ESPHdr *esph; dprintf(">\n"); esp = sa->data; ip_n = (skb->nh.iph->ihl << 2); // Assuming skb->data points at ethernet header, exclude ethernet // header and IP header. plaintext_n = skb->len - ETH_HLEN - ip_n; // Add size of padding fields. ciphertext_n = roundupto(plaintext_n + ESP_PAD_N, esp->cipher.block_n); if(esp->cipher.pad_n > 0){ ciphertext_n = roundupto(ciphertext_n, esp->cipher.pad_n); } extra_n = ciphertext_n - plaintext_n; iv_n = esp->cipher.iv_n; icv_n = esp->digest.icv_n; dprintf("> len=%d plaintext=%d ciphertext=%d extra=%d\n", skb->len, plaintext_n, ciphertext_n, extra_n); dprintf("> iv=%d icv=%d\n", iv_n, icv_n); skb_print_bits("iv", skb, 0, skb->len); // Add headroom for esp header and iv, tailroom for the ciphertext // and icv. head_n = ESP_HDR_N + iv_n; tail_n = extra_n + icv_n; err = skb_make_room(&skb, skb, head_n, tail_n); if(err) goto exit; dprintf("> skb=%p\n", skb); // Move the headers up to make space for the esp header. We can // use memmove() since all this data fits in the skb head. // todo: Can't assume this anymore? dprintf("> header push...\n"); __skb_push(skb, head_n); if(0 && skb->mac.raw){ dprintf("> skb->mac=%p\n", skb->mac.raw); dprintf("> ETH header pull...\n"); memmove(skb->data, skb->mac.raw, ETH_HLEN); skb->mac.raw = skb->data; skb_pull_vn(skb, ETH_HLEN); } dprintf("> IP header pull...\n"); memmove(skb->data, skb->nh.raw, ip_n); skb->nh.raw = skb->data; skb_pull_vn(skb, ip_n); esph = (void*)skb->data; // Add spi and sequence number. esph->spi = sa->ident.spi; esph->seq = htonl(++sa->replay.send_seq); // Insert the padding bytes: extra bytes less the pad fields // themselves. dprintf("> esp_sa_pad ...\n"); esp_sa_pad(skb, icv_n, extra_n, skb->nh.iph->protocol); if(sa->security & SA_CONF){ dprintf("> esp_sa_encrypt...\n"); err = esp_sa_encrypt(esp, esph, skb, head_n, iv_n, ciphertext_n); if(err) goto exit; } if(icv_n){ dprintf("> esp_sa_digest...\n"); err = esp_sa_digest(esp, skb, head_n + ciphertext_n, icv_n); if(err) goto exit; } dprintf("> IP header push...\n"); __skb_push(skb, ip_n); if(0 && skb->mac.raw){ dprintf("> ETH header push...\n"); __skb_push(skb, ETH_HLEN); } // Fix ip header. Adjust length field, set protocol, zero // checksum. { // Total packet length (bytes). int tot_len = ntohs(skb->nh.iph->tot_len); tot_len += head_n; tot_len += tail_n; skb->nh.iph->protocol = IPPROTO_ESP; skb->nh.iph->tot_len = htons(tot_len); skb->nh.iph->check = 0; } err = Tunnel_send(tunnel, skb); exit: dprintf("< err=%d\n", err); return err;}/** Release an skb context. * Drops the refcount on the SA. * * @param context to free */static void esp_context_free_fn(SkbContext *context){ SAState *sa; if(!context) return; sa = context->data; if(!sa) return; context->data = NULL; SAState_decref(sa);} /** Receive a packet via an ESP SA. * Does ESP receive processing (check icv, decrypt), strips * ESP header and re-receives. * * If return 1 the packet has been freed. * If return <= 0 the caller must free. * * @param sa SA * @param skb packet * @return >= 0 on success, negative protocol otherwise */static int esp_sa_recv(SAState *sa, struct sk_buff *skb){ int err = -EINVAL; int mine = 0; int vnet = 0; //todo: fixme - need to record skb vnet somewhere ESPState *esp; ESPHdr *esph; ESPPadding *pad; int block_n; // Cipher blocksize. int icv_n; // Size of integrity check value (icv). int iv_n; // Size of initialization vector (iv). int text_n; // Size of text (ciphertext or plaintext). int head_n; // Size of esp header and iv. dprintf("> skb=%p\n", skb); // Assumes skb->data points at esp hdr. esph = (void*)skb->data; esp = sa->data; block_n = crypto_tfm_alg_blocksize(esp->cipher.tfm); icv_n = esp->digest.icv_n; iv_n = esp->cipher.iv_n; head_n = ESP_HDR_N + iv_n; text_n = skb->len - head_n - icv_n; if(text_n < ESP_PAD_N || !multipleof(text_n, block_n)){ wprintf("> Invalid size: text_n=%d tfm:block_n=%d esp:block_n=%d\n", text_n, block_n, esp->cipher.block_n); goto exit; } if(icv_n){ err = esp_check_icv(sa, esp, esph, skb); if(err) goto exit; } mine = 1; if(sa->security & SA_CONF){ err = esp_sa_decrypt(esp, esph, skb, head_n, iv_n, text_n); if(err) goto exit; } // Strip esp header by moving the other headers down.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?