esp.c

来自「xen 3.2.2 源码」· 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 + -
显示快捷键?