sa.c
来自「xen 3.2.2 源码」· C语言 代码 · 共 757 行 · 第 1/2 页
C
757 行
/* * Copyright (C) 2004 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/kernel.h>#include <tunnel.h>#include <vnet.h>#include <sa.h>#include <sa_algorithm.h>#include "hash_table.h"#include "allocate.h"#define MODULE_NAME "IPSEC"#define DEBUG 1#undef DEBUG#include "debug.h"/** @file IPSEC Security Association (SA). *//** Maximum number of protocols.*/#define INET_PROTOCOL_MAX 256/** Table of SA types indexed by protocol. */static SAType *sa_type[INET_PROTOCOL_MAX] = {};/** Hash a protocol number. * * @param protocol protocol number * @return hashcode */static inline unsigned char InetProtocol_hash(int protocol){ return (protocol) & (INET_PROTOCOL_MAX - 1);}/** Register an SA type. * It is an error if an SA type is already registered for the protocol. * * @param type SA type * @return 0 on success, error code otherwise */int SAType_add(SAType *type){ int err = -EINVAL; int hash; if(!type) goto exit; hash = InetProtocol_hash(type->protocol); if(sa_type[hash]) goto exit; err = 0; sa_type[hash] = type; exit: return err;}/** Deregister an SA type. * It is an error if no SA type is registered for the protocol. * * @param type SA type * @return 0 on success, error code otherwise */int SAType_del(SAType *type){ int err = -EINVAL; int hash; if(!type) goto exit; hash = InetProtocol_hash(type->protocol); if(!sa_type[hash]) goto exit; err = 0; sa_type[hash] = NULL; exit: return err;}int SAType_get(int protocol, SAType **type){ int err = -ENOENT; int hash; hash = InetProtocol_hash(protocol); *type = sa_type[hash]; if(!*type) goto exit; err = 0; exit: return err;}/* Defeat compiler warnings about unused functions. */static int sa_key_check(SAKey *key, enum sa_alg_type type) __attribute__((unused));static u32 random_spi(void) __attribute__((unused));static u32 generate_key(u32 key, u32 offset, u32 spi) __attribute__((unused));/** Check a key has an acceptable length for an algorithm. * * @param key key * @param type algorithm * @return 0 on success, error code otherwise */static int sa_key_check(SAKey *key, enum sa_alg_type type){ return 0;}static unsigned long sa_spi_counter = 0;/** Mangle some input to generate output. * This is used to derive spis and keying material from secrets, * so it probably ought to be cryptographically strong. * Probably ought to use a good hash (sha1) or cipher (aes). * * @param input input bytes * @param n number of bytes * @return mangled value */static u32 mangle(void *input, int n){ return hash_hvoid(0, input, n);}/** Generate a random spi. * Uses a hashed counter. * * @return spi */static u32 random_spi(void){ u32 spi; do{ spi = sa_spi_counter++; spi = mangle(&spi, sizeof(spi)); } while(!spi); return spi;} /** Generate a spi for a given protocol and address, using a secret key. * The offset is used when it is necessary to generate more than one spi * for the same protocol and address. * * @param key key * @param offset offset * @param protocol protocol * @param addr IP address * @return spi */static u32 generate_spi(u32 key, u32 offset, u32 protocol, u32 addr){ u32 input[] = { key, offset, protocol, addr }; return mangle(input, sizeof(input));}/** Generate keying material for a given spi, based on a * secret. * * @param key secret * @param offset offset * @param spi spi * @return keying material */static u32 generate_key(u32 key, u32 offset, u32 spi){ u32 input[] = { key, offset, spi }; return mangle(input, sizeof(input));} /** Allocate a spi. * Want to use random ones. * So check for ones not in use. * * When using static keying, both ends need to agree on key. * How does that work? Also, will suddenly get traffic using a spi, * and will have to create SA then. Or need to create in advance. * But can't do that because don't know peers. * When get message on a spi that doesn't exist - do what? * Use a spi related to the destination addr and a secret. * Then receiver can check if spi is ok and create SA on demand. * Use hash of key, protocol, addr to generate. Then have to check * for in-use because of potential collisions. Receiver can do the * same hash and check spi is in usable range. Then derive keys from * the spi (using another secret). * * @param key spi generation key * @param protocol protocol * @param addr IP address * @param spip return parameter for spi * @return 0 on success, error code otherwise */int sa_spi_alloc(u32 key, u32 protocol, u32 addr, u32 *spip){ int err = 0; int i = 0, n = 100; u32 spi; for(i = 0; i < n; i++, spi++){ spi = generate_spi(key, i, protocol, addr); if(!spi) continue; if(!sa_table_lookup_spi(spi, protocol, addr)){ *spip = spi; goto exit; } } err = -ENOMEM; exit: return err;}/** Table of SAs. Indexed by unique id and spi/protocol/addr triple. */static HashTable *sa_table = NULL;static u32 sa_id = 1;/** Hash an SA id. * * @param id SA id * @return hashcode */static inline Hashcode sa_table_hash_id(u32 id){ return hash_hvoid(0, &id, sizeof(id));}/** Hash SA spi/protocol/addr. * * @param spi spi * @param protocol protocol * @param addr IP address * @return hashcode */static inline Hashcode sa_table_hash_spi(u32 spi, u32 protocol, u32 addr){ u32 a[] = { spi, protocol, addr }; return hash_hvoid(0, a, sizeof(a));}/** Test if an SA entry has a given value. * * @param arg contains SA pointer * @param table hashtable * @param entry entry containing SA * @return 1 if it does, 0 otherwise */static int sa_table_state_fn(TableArg arg, HashTable *table, HTEntry *entry){ return entry->value == arg.ptr;}/** Test if an SA entry has a given id. * * @param arg contains SA id * @param table hashtable * @param entry entry containing SA * @return 1 if it does, 0 otherwise */static int sa_table_id_fn(TableArg arg, HashTable *table, HTEntry *entry){ SAState *state = entry->value; u32 id = arg.ul; return state->ident.id == id;}/** Test if an SA entry has a given spi/protocol/addr. * * @param arg contains SAIdent pointer * @param table hashtable * @param entry entry containing SA * @return 1 if it does, 0 otherwise */static int sa_table_spi_fn(TableArg arg, HashTable *table, HTEntry *entry){ SAState *state = entry->value; SAIdent *ident = arg.ptr; return state->ident.spi == ident->spi && state->ident.protocol == ident->protocol && state->ident.addr == ident->addr;}/** Free an SA entry. Decrements the SA refcount and frees the entry. * * @param table containing table * @param entry to free */static void sa_table_free_fn(HashTable *table, HTEntry *entry){ if(!entry) return; if(entry->value){ SAState *state = entry->value; SAState_decref(state); } deallocate(entry);}/** Initialize the SA table. * * @return 0 on success, error code otherwise */int sa_table_init(void){ int err = 0; sa_table = HashTable_new(0); if(!sa_table){ err = -ENOMEM; goto exit; } sa_table->entry_free_fn = sa_table_free_fn; exit: return err;}void sa_table_exit(void){ HashTable_free(sa_table);}/** Remove an SA from the table. * * @param state SA */int sa_table_delete(SAState *state){ int count = 0; Hashcode h1, h2; TableArg arg = { .ptr = state }; // Remove by id. h1 = sa_table_hash_id(state->ident.id); count += HashTable_remove_entry(sa_table, h1, sa_table_state_fn, arg); // Remove by spi/protocol/addr if spi nonzero. if(!state->ident.spi) goto exit; h2 = sa_table_hash_spi(state->ident.spi, state->ident.protocol, state->ident.addr); if(h1 == h2) goto exit; count += HashTable_remove_entry(sa_table, h2, sa_table_state_fn, arg); exit: return count;}/** Add an SA to the table. * The SA is indexed by id and spi/protocol/addr (if the spi is non-zero). * * @param state SA * @return 0 on success, error code otherwise */int sa_table_add(SAState *state){ int err = 0; Hashcode h1, h2; int entries = 0; dprintf(">\n"); // Index by id. h1 = sa_table_hash_id(state->ident.id); if(!HashTable_add_entry(sa_table, h1, HKEY(state->ident.id), state)){ err = -ENOMEM; goto exit; } entries++; SAState_incref(state); // Index by spi/protocol/addr if spi non-zero. if(state->ident.spi){ h2 = sa_table_hash_spi(state->ident.spi, state->ident.protocol, state->ident.addr); if(h1 != h2){ if(!HashTable_add_entry(sa_table, h2, HKEY(state->ident.id), state)){ err = -ENOMEM; goto exit; } entries++; SAState_incref(state); } } exit: if(err && entries){ sa_table_delete(state); } dprintf("< err=%d\n", err); return err;}/** Find an SA by spi/protocol/addr. * Increments the SA refcount on success. * * @param spi spi * @param protocol protocol * @param addr IP address * @return SA or NULL */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?