Commit 4a618fbe authored by Alexey Kuznetsov's avatar Alexey Kuznetsov Committed by David S. Miller

[NET]: IPSEC updates.

- Add ESP tranformer.
- Add AF_KEY socket layer.
- Rework xfrm structures for user interfaces
- Add CONFIG_IP_{AH,ESP}.
parent 464ff460
/*
* Definitions for the SECurity layer
*
* Author:
* Robert Muchsel <muchsel@acm.org>
*
* 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.
*/
#ifndef _LINUX_IPSEC_H
#define _LINUX_IPSEC_H
#include <linux/config.h>
#include <linux/socket.h>
#include <net/sock.h>
#include <linux/skbuff.h>
/* The definitions, required to talk to KAME racoon IKE. */
/* Values for the set/getsockopt calls */
#include <linux/pfkeyv2.h>
/* These defines are compatible with NRL IPv6, however their semantics
is different */
#define IPSEC_PORT_ANY 0
#define IPSEC_ULPROTO_ANY 255
#define IPSEC_PROTO_ANY 255
#define IPSEC_LEVEL_NONE -1 /* send plaintext, accept any */
#define IPSEC_LEVEL_DEFAULT 0 /* encrypt/authenticate if possible */
/* the default MUST be 0, because a */
/* socket is initialized with 0's */
#define IPSEC_LEVEL_USE 1 /* use outbound, don't require inbound */
#define IPSEC_LEVEL_REQUIRE 2 /* require both directions */
#define IPSEC_LEVEL_UNIQUE 2 /* for compatibility only */
enum {
IPSEC_MODE_ANY = 0, /* We do not support this for SA */
IPSEC_MODE_TRANSPORT = 1,
IPSEC_MODE_TUNNEL = 2
};
#ifdef __KERNEL__
enum {
IPSEC_DIR_ANY = 0,
IPSEC_DIR_INBOUND = 1,
IPSEC_DIR_OUTBOUND = 2,
IPSEC_DIR_FWD = 3, /* It is our own */
IPSEC_DIR_MAX = 4,
IPSEC_DIR_INVALID = 5
};
/* skb bit flags set on packet input processing */
enum {
IPSEC_POLICY_DISCARD = 0,
IPSEC_POLICY_NONE = 1,
IPSEC_POLICY_IPSEC = 2,
IPSEC_POLICY_ENTRUST = 3,
IPSEC_POLICY_BYPASS = 4
};
#define RCV_SEC 0x0f /* options on receive */
#define RCV_AUTH 0x01 /* was authenticated */
#define RCV_CRYPT 0x02 /* was encrypted */
#define RCV_TUNNEL 0x04 /* was tunneled */
#define SND_SEC 0xf0 /* options on send, these are */
#define SND_AUTH 0x10 /* currently unused */
#define SND_CRYPT 0x20
#define SND_TUNNEL 0x40
enum {
IPSEC_LEVEL_DEFAULT = 0,
IPSEC_LEVEL_USE = 1,
IPSEC_LEVEL_REQUIRE = 2,
IPSEC_LEVEL_UNIQUE = 3
};
/*
* FIXME: ignores network encryption for now..
*/
#ifdef CONFIG_NET_SECURITY
static __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
{
return ((sk->authentication < IPSEC_LEVEL_REQUIRE) ||
(skb->security & RCV_AUTH)) &&
((sk->encryption < IPSEC_LEVEL_REQUIRE) ||
(skb->security & RCV_CRYPT));
}
#define IPSEC_MANUAL_REQID_MAX 0x3fff
#else
#define IPSEC_REPLAYWSIZE 32
#ifdef __KERNEL__
struct sock;
struct sk_buff;
static __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
{
return 1;
}
#endif /* CONFIG */
#endif
#endif /* __KERNEL__ */
#endif /* _LINUX_IPSEC_H */
......@@ -187,7 +187,7 @@ struct sadb_x_policy {
struct sadb_x_ipsecrequest {
uint16_t sadb_x_ipsecrequest_len;
uint16_t sadb_x_ipsecrequest_exttype;
uint16_t sadb_x_ipsecrequest_proto;
uint8_t sadb_x_ipsecrequest_mode;
uint8_t sadb_x_ipsecrequest_level;
uint16_t sadb_x_ipsecrequest_reqid;
......@@ -249,8 +249,8 @@ struct sadb_x_ipsecrequest {
/* Encryption algorithms */
#define SADB_EALG_NONE 0
#define SADB_EALG_DESCBC 2
#define SADB_EALG_3DESCBC 3
#define SADB_EALG_DESCBC 1
#define SADB_EALG_3DESCBC 2
#define SADB_EALG_NULL 11
#define SADB_EALG_MAX 11
......
......@@ -237,7 +237,7 @@ extern void dst_init(void);
struct flowi;
extern int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
struct sock *sk, int flags);
extern void xfrm_init(void);
#endif
......
......@@ -3,6 +3,7 @@
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/crypto.h>
#include <net/dst.h>
#include <net/route.h>
......@@ -113,6 +114,38 @@ struct xfrm_selector
void *owner;
};
struct xfrm_lifetime_cfg
{
u64 soft_byte_limit;
u64 hard_byte_limit;
u64 soft_packet_limit;
u64 hard_packet_limit;
u64 soft_add_expires_seconds;
u64 hard_add_expires_seconds;
u64 soft_use_expires_seconds;
u64 hard_use_expires_seconds;
};
struct xfrm_lifetime_cur
{
u64 bytes;
u64 packets;
u64 add_time;
u64 use_time;
};
struct xfrm_replay_state
{
u32 oseq;
u32 seq;
u32 bitmap;
};
struct xfrm_algo {
char alg_name[CRYPTO_MAX_ALG_NAME];
int alg_key_len; /* in bits */
char alg_key[0];
};
/* Full description of state of transformer. */
struct xfrm_state
......@@ -130,40 +163,39 @@ struct xfrm_state
struct {
int state;
u32 seq;
u32 warn_bytes;
u64 warn_bytes;
} km;
/* Parameters of this state. */
struct {
u8 mode;
u8 algo;
u8 replay_window;
u8 aalgo, ealgo, calgo;
u16 reqid;
xfrm_address_t saddr;
int header_len;
int trailer_len;
u32 hard_byte_limit;
u32 soft_byte_limit;
u32 replay_window;
/* More... */
} props;
struct xfrm_lifetime_cfg lft;
/* Data for transformer */
struct xfrm_algo *aalg;
struct xfrm_algo *ealg;
struct xfrm_algo *calg;
/* State for replay detection */
struct {
u32 oseq;
u32 seq;
u32 bitmap;
} replay;
struct xfrm_replay_state replay;
/* Statistics */
struct {
unsigned long lastuse;
unsigned long expires;
u32 bytes;
u32 replay_window;
u32 replay;
u32 integrity_failed;
/* More... */
} stats;
struct xfrm_lifetime_cur curlft;
/* Reference to data common to all the instances of this
* transformer. */
struct xfrm_type *type;
......@@ -182,14 +214,12 @@ enum {
XFRM_STATE_DEAD
};
#define XFRM_DST_HSIZE 1024
struct xfrm_type
{
char *description;
atomic_t refcnt;
struct module *owner;
__u8 proto;
__u8 algo;
int (*init_state)(struct xfrm_state *x, void *args);
void (*destructor)(struct xfrm_state *);
......@@ -199,6 +229,11 @@ struct xfrm_type
u32 (*get_max_size)(struct xfrm_state *, int size);
};
extern int xfrm_register_type(struct xfrm_type *type);
extern int xfrm_unregister_type(struct xfrm_type *type);
extern struct xfrm_type *xfrm_get_type(u8 proto);
extern void xfrm_put_type(struct xfrm_type *type);
struct xfrm_tmpl
{
/* id in template is interpreted as:
......@@ -212,6 +247,8 @@ struct xfrm_tmpl
/* Source address of tunnel. Ignored, if it is not a tunnel. */
xfrm_address_t saddr;
__u16 reqid;
/* Mode: transport/tunnel */
__u8 mode;
......@@ -219,7 +256,9 @@ struct xfrm_tmpl
__u8 share;
/* Bit mask of algos allowed for acquisition */
__u32 algos;
__u32 aalgos;
__u32 ealgos;
__u32 calgos;
/* If template statically resolved, hold ref here */
struct xfrm_state *resolved;
......@@ -238,8 +277,8 @@ enum
enum
{
XFRM_POLICY_IN = 0,
XFRM_POLICY_FWD = 1,
XFRM_POLICY_OUT = 2,
XFRM_POLICY_OUT = 1,
XFRM_POLICY_FWD = 2,
XFRM_POLICY_MAX = 3
};
......@@ -254,8 +293,8 @@ struct xfrm_policy
u32 priority;
u32 index;
struct xfrm_selector selector;
unsigned long expires;
unsigned long lastuse;
struct xfrm_lifetime_cfg lft;
struct xfrm_lifetime_cur curlft;
struct dst_entry *bundles;
__u8 action;
#define XFRM_POLICY_ALLOW 0
......@@ -267,6 +306,18 @@ struct xfrm_policy
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
};
struct xfrm_mgr
{
struct list_head list;
char *id;
int (*notify)(struct xfrm_state *x, int event);
int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir);
};
extern int xfrm_register_km(struct xfrm_mgr *km);
extern int xfrm_unregister_km(struct xfrm_mgr *km);
extern struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX];
static inline void xfrm_pol_hold(struct xfrm_policy *policy)
......@@ -366,21 +417,36 @@ static inline int xfrm_route_forward(struct sk_buff *skb)
extern void xfrm_state_init(void);
extern void xfrm_input_init(void);
extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *);
extern struct xfrm_state *xfrm_state_alloc(void);
extern struct xfrm_state *xfrm_state_find(u32 daddr, struct flowi *fl, struct xfrm_tmpl *tmpl);
extern struct xfrm_state *xfrm_state_find(u32 daddr, struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol);
extern int xfrm_state_check_expire(struct xfrm_state *x);
extern void xfrm_state_insert(struct xfrm_state *x);
extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb);
extern struct xfrm_state * xfrm_state_lookup(u32 daddr, u32 spi, u8 proto);
extern struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl);
extern struct xfrm_state *xfrm_state_lookup(u32 daddr, u32 spi, u8 proto);
extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
extern void xfrm_state_delete(struct xfrm_state *x);
extern void xfrm_state_flush(u8 proto);
extern int xfrm_replay_check(struct xfrm_state *x, u32 seq);
extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb);
struct xfrm_policy *xfrm_policy_alloc(void);
extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *);
struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl);
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
struct xfrm_policy *xfrm_policy_delete(int dir, struct xfrm_selector *sel);
struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete);
void xfrm_policy_flush(void);
int xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
struct flowi *fl, struct dst_entry **dst_p);
void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr);
extern void xfrm_policy_flush(void);
extern void xfrm_policy_kill(struct xfrm_policy *);
extern wait_queue_head_t *km_waitq;
extern void km_notify(struct xfrm_state *x, int event);
extern int km_query(struct xfrm_state *x);
extern int ah4_init(void);
extern void km_warn_expired(struct xfrm_state *x);
extern void km_expired(struct xfrm_state *x);
extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *pol);
......@@ -139,6 +139,15 @@ config UNIX
Say Y unless you know what you are doing.
config NET_KEY
tristate "PF_KEY sockets"
---help---
PF_KEYv2 socket family, compatible to KAME ones.
They are required if you are going to use IPsec tools ported
from KAME.
Say Y unless you know what you are doing.
config INET
bool "TCP/IP networking"
---help---
......
......@@ -16,6 +16,7 @@ obj-$(CONFIG_INET) += ipv4/
obj-$(CONFIG_UNIX) += unix/
obj-$(CONFIG_IPV6) += ipv6/
obj-$(CONFIG_PACKET) += packet/
obj-$(CONFIG_NET_KEY) += key/
obj-$(CONFIG_NET_SCHED) += sched/
obj-$(CONFIG_BRIDGE) += bridge/
obj-$(CONFIG_IPX) += ipx/
......
......@@ -348,5 +348,19 @@ config SYN_COOKIES
If unsure, say N.
config INET_AH
bool "IP: AH transformation"
---help---
Support for IPsec AH.
If unsure, say Y.
config INET_ESP
bool "IP: ESP transformation"
---help---
Support for IPsec ESP.
If unsure, say Y.
source "net/ipv4/netfilter/Kconfig"
......@@ -16,9 +16,11 @@ obj-$(CONFIG_IP_MROUTE) += ipmr.o
obj-$(CONFIG_NET_IPIP) += ipip.o
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_INET_AH) += ah.o
obj-$(CONFIG_INET_ESP) += esp.o
obj-$(CONFIG_IP_PNP) += ipconfig.o
obj-$(CONFIG_NETFILTER) += netfilter/
obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o ah.o
obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o
include $(TOPDIR)/Rules.make
#include <linux/config.h>
#include <linux/module.h>
#include <net/ip.h>
#include <net/xfrm.h>
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
#include <net/icmp.h>
#include <asm/scatterlist.h>
struct ah_data
{
......@@ -9,7 +13,7 @@ struct ah_data
int key_len;
int digest_len;
void (*digest)(struct xfrm_state*,
void (*digest)(struct ah_data*,
struct sk_buff *skb,
u8 *digest);
......@@ -67,12 +71,19 @@ void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
int len = skb->len;
int start = skb->len - skb->data_len;
int i, copy = start - offset;
struct scatterlist sg;
/* Checksum header. */
if (copy > 0) {
if (copy > len)
copy = len;
tfm->__crt_alg->cra_digest.dia_update(tfm->crt_ctx, skb->data+offset, copy);
sg.page = virt_to_page(skb->data + offset);
sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
sg.length = copy;
crypto_hmac_update(tfm, &sg, 1);
if ((len -= copy) == 0)
return;
offset += copy;
......@@ -85,14 +96,17 @@ void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
end = start + skb_shinfo(skb)->frags[i].size;
if ((copy = end - offset) > 0) {
u8 *vaddr;
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
if (copy > len)
copy = len;
vaddr = kmap_skb_frag(frag);
tfm->__crt_alg->cra_digest.dia_update(tfm->crt_ctx, vaddr+frag->page_offset+offset-start, copy);
kunmap_skb_frag(vaddr);
sg.page = frag->page;
sg.offset = frag->page_offset + offset-start;
sg.length = copy;
crypto_hmac_update(tfm, &sg, 1);
if (!(len -= copy))
return;
offset += copy;
......@@ -124,6 +138,19 @@ void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
BUG();
}
static void
ah_hmac_digest(struct ah_data *ahp, struct sk_buff *skb, u8 *auth_data)
{
struct crypto_tfm *tfm = ahp->tfm;
char digest[crypto_tfm_alg_digestsize(tfm)];
memset(auth_data, 0, ahp->digest_len);
crypto_hmac_init(tfm, ahp->key, &ahp->key_len);
skb_ah_walk(skb, tfm);
crypto_hmac_final(tfm, ahp->key, &ahp->key_len, digest);
memcpy(auth_data, digest, ahp->digest_len);
}
int ah_output(struct sk_buff *skb)
{
int err;
......@@ -186,7 +213,7 @@ int ah_output(struct sk_buff *skb)
ah->reserved = 0;
ah->spi = x->id.spi;
ah->seq_no = htonl(++x->replay.oseq);
ahp->digest(x, skb, ah->auth_data);
ahp->digest(ahp, skb, ah->auth_data);
top_iph->tos = iph->tos;
top_iph->ttl = iph->ttl;
if (x->props.mode) {
......@@ -202,7 +229,7 @@ int ah_output(struct sk_buff *skb)
skb->nh.raw = skb->data;
x->stats.bytes += skb->len;
x->curlft.bytes += skb->len;
spin_unlock_bh(&x->lock);
if ((skb->dst = dst_pop(dst)) == NULL)
goto error;
......@@ -258,7 +285,7 @@ int ah_input(struct xfrm_state *x, struct sk_buff *skb)
u8 auth_data[ahp->digest_len];
memcpy(auth_data, ah->auth_data, ahp->digest_len);
skb_push(skb, skb->data - skb->nh.raw);
ahp->digest(x, skb, ah->auth_data);
ahp->digest(ahp, skb, ah->auth_data);
if (memcmp(ah->auth_data, auth_data, ahp->digest_len)) {
x->stats.integrity_failed++;
goto out;
......@@ -295,16 +322,75 @@ void ah4_err(struct sk_buff *skb, u32 info)
xfrm_state_put(x);
}
int ah_init_state(struct xfrm_state *x, void *args)
{
struct ah_data *ahp = NULL;
if (x->aalg == NULL || x->aalg->alg_key_len == 0 ||
x->aalg->alg_key_len > 512)
goto error;
ahp = kmalloc(sizeof(*ahp), GFP_KERNEL);
if (ahp == NULL)
return -ENOMEM;
ahp->key = x->aalg->alg_key;
ahp->key_len = (x->aalg->alg_key_len+7)/8;
ahp->tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
ahp->digest = ah_hmac_digest;
ahp->digest_len = 12;
x->props.header_len = (12 + ahp->digest_len + 7)&~7;
if (x->props.mode)
x->props.header_len += 20;
x->data = ahp;
return 0;
error:
if (ahp) {
if (ahp->tfm)
crypto_free_tfm(ahp->tfm);
kfree(ahp);
}
return -EINVAL;
}
void ah_destroy(struct xfrm_state *x)
{
struct ah_data *ahp = x->data;
if (ahp->tfm) {
crypto_free_tfm(ahp->tfm);
ahp->tfm = NULL;
}
}
static struct xfrm_type ah_type =
{
.description = "AH4",
.proto = IPPROTO_AH,
.init_state = ah_init_state,
.destructor = ah_destroy,
.input = ah_input,
.output = ah_output
};
static struct inet_protocol ah4_protocol = {
.handler = xfrm4_rcv,
.err_handler = ah4_err,
};
int __init ah4_init(void)
{
SET_MODULE_OWNER(&ah_type);
if (xfrm_register_type(&ah_type) < 0) {
printk(KERN_INFO "ip ah init: can't add xfrm type\n");
return -EAGAIN;
}
if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
printk(KERN_INFO "ip ah init: can't add protocol\n");
xfrm_unregister_type(&ah_type);
return -EAGAIN;
}
return 0;
......@@ -314,4 +400,10 @@ static void __exit ah4_fini(void)
{
if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
printk(KERN_INFO "ip ah close: can't remove protocol\n");
if (xfrm_unregister_type(&ah_type) < 0)
printk(KERN_INFO "ip ah close: can't remove xfrm type\n");
}
module_init(ah4_init);
module_exit(ah4_fini);
MODULE_LICENSE("GPL");
This diff is collapsed.
......@@ -155,6 +155,53 @@ void __init flow_cache_init(void)
memset(flow_table, 0, PAGE_SIZE<<order);
}
static struct xfrm_type *xfrm_type_map[256];
static rwlock_t xfrm_type_lock = RW_LOCK_UNLOCKED;
int xfrm_register_type(struct xfrm_type *type)
{
int err = 0;
write_lock(&xfrm_type_lock);
if (xfrm_type_map[type->proto] == NULL)
xfrm_type_map[type->proto] = type;
else
err = -EEXIST;
write_unlock(&xfrm_type_lock);
return err;
}
int xfrm_unregister_type(struct xfrm_type *type)
{
int err = 0;
write_lock(&xfrm_type_lock);
if (xfrm_type_map[type->proto] != type)
err = -ENOENT;
else
xfrm_type_map[type->proto] = NULL;
write_unlock(&xfrm_type_lock);
return err;
}
struct xfrm_type *xfrm_get_type(u8 proto)
{
struct xfrm_type *type;
read_lock(&xfrm_type_lock);
type = xfrm_type_map[proto];
if (type && !try_inc_mod_count(type->owner))
type = NULL;
write_unlock(&xfrm_type_lock);
return type;
}
void xfrm_put_type(struct xfrm_type *type)
{
if (type->owner)
__MOD_DEC_USE_COUNT(type->owner);
}
/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2
* SPD calls.
......@@ -203,6 +250,10 @@ void xfrm_policy_kill(struct xfrm_policy *policy)
struct dst_entry *dst;
int i;
write_lock_bh(&policy->lock);
if (policy->dead)
goto out;
policy->dead = 1;
for (i=0; i<policy->xfrm_nr; i++) {
......@@ -216,6 +267,128 @@ void xfrm_policy_kill(struct xfrm_policy *policy)
policy->bundles = dst->next;
dst_free(dst);
}
out:
write_unlock_bh(&policy->lock);
}
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
{
struct xfrm_policy *pol, **p;
write_lock_bh(&xfrm_policy_lock);
for (p = &xfrm_policy_list[dir]; (pol=*p)!=NULL; p = &pol->next) {
if (memcmp(&policy->selector, &pol->selector, sizeof(pol->selector)) == 0) {
if (excl) {
write_unlock_bh(&xfrm_policy_lock);
return -EEXIST;
}
break;
}
}
atomic_inc(&policy->refcnt);
policy->next = pol ? pol->next : NULL;
*p = policy;
xfrm_policy_genid++;
write_unlock_bh(&xfrm_policy_lock);
if (pol) {
xfrm_policy_kill(pol);
xfrm_pol_put(pol);
}
return 0;
}
struct xfrm_policy *xfrm_policy_delete(int dir, struct xfrm_selector *sel)
{
struct xfrm_policy *pol, **p;
write_lock_bh(&xfrm_policy_lock);
for (p = &xfrm_policy_list[dir]; (pol=*p)!=NULL; p = &pol->next) {
if (memcmp(sel, &pol->selector, sizeof(*sel)) == 0) {
*p = pol->next;
break;
}
}
if (pol)
xfrm_policy_genid++;
write_unlock_bh(&xfrm_policy_lock);
return pol;
}
struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete)
{
struct xfrm_policy *pol, **p;
write_lock_bh(&xfrm_policy_lock);
for (p = &xfrm_policy_list[dir]; (pol=*p)!=NULL; p = &pol->next) {
if (pol->index == id) {
if (delete)
*p = pol->next;
break;
}
}
if (pol) {
if (delete)
xfrm_policy_genid++;
else
atomic_inc(&pol->refcnt);
}
write_unlock_bh(&xfrm_policy_lock);
return pol;
}
void xfrm_policy_flush()
{
struct xfrm_policy *xp;
int dir;
write_lock_bh(&xfrm_policy_lock);
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
while ((xp = xfrm_policy_list[dir]) != NULL) {
xfrm_policy_list[dir] = xp->next;
write_unlock_bh(&xfrm_policy_lock);
xfrm_policy_kill(xp);
xfrm_pol_put(xp);
write_lock_bh(&xfrm_policy_lock);
}
}
xfrm_policy_genid++;
write_unlock_bh(&xfrm_policy_lock);
}
int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*),
void *data)
{
struct xfrm_policy *xp;
int dir;
int count = 0;
int error = 0;
read_lock(&xfrm_policy_lock);
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
for (xp = xfrm_policy_list[dir]; xp; xp = xp->next)
count++;
}
if (count == 0) {
error = -ENOENT;
goto out;
}
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
for (xp = xfrm_policy_list[dir]; xp; xp = xp->next) {
error = func(xp, dir, --count, data);
if (error)
goto out;
}
}
out:
read_unlock(&xfrm_policy_lock);
return error;
}
......@@ -224,14 +397,13 @@ void xfrm_policy_kill(struct xfrm_policy *policy)
struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl)
{
struct xfrm_policy *pol;
unsigned long now = xtime.tv_sec;
// Not now :-) u64 now = (unsigned long)xtime.tv_sec;
read_lock(&xfrm_policy_lock);
for (pol = xfrm_policy_list[dir]; pol; pol = pol->next) {
struct xfrm_selector *sel = &pol->selector;
if (xfrm4_selector_match(sel, fl) && now < pol->expires) {
pol->lastuse = now;
if (xfrm4_selector_match(sel, fl) /* XXX && XXX now < pol->validtime */) {
atomic_inc(&pol->refcnt);
break;
}
......@@ -261,7 +433,7 @@ xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
xfrm[i] = tmpl->resolved;
atomic_inc(&tmpl->resolved->refcnt);
} else {
xfrm[i] = xfrm_state_find(daddr, fl, tmpl);
xfrm[i] = xfrm_state_find(daddr, fl, tmpl, policy);
if (xfrm[i] == NULL) {
error = -ENOMEM;
goto fail;
......@@ -269,11 +441,11 @@ xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
if (xfrm[i]->km.state == XFRM_STATE_VALID)
continue;
i++;
if (xfrm[i]->km.state == XFRM_STATE_ERROR)
error = -EINVAL;
else
error = -EAGAIN;
i++;
goto fail;
}
}
......@@ -539,7 +711,7 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
return x->id.proto == tmpl->id.proto &&
(x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
x->props.mode == tmpl->mode &&
(tmpl->algos & (1<<x->props.algo)) &&
(tmpl->aalgos & (1<<x->props.aalgo)) &&
(!x->props.mode || !tmpl->saddr.xfrm4_addr ||
tmpl->saddr.xfrm4_addr == x->props.saddr.xfrm4_addr);
}
......@@ -798,5 +970,4 @@ void __init xfrm_init(void)
xfrm_state_init();
xfrm_input_init();
ah4_init();
}
......@@ -13,7 +13,9 @@
by policy. (output)
*/
spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED;
#define XFRM_DST_HSIZE 1024
/* Hash table to find appropriate SA towards given target (endpoint
* of tunnel or destination of transport mode) allowed by selector.
......@@ -21,8 +23,8 @@ spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED;
* Main use is finding SA after policy selected tunnel or transport mode.
* Also, it can be used by ah/esp icmp error handler to find offending SA.
*/
struct list_head xfrm_state_bydst[XFRM_DST_HSIZE];
struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
static struct list_head xfrm_state_bydst[XFRM_DST_HSIZE];
static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
wait_queue_head_t *km_waitq;
......@@ -45,13 +47,68 @@ struct xfrm_state *xfrm_state_alloc(void)
void __xfrm_state_destroy(struct xfrm_state *x)
{
BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
if (x->aalg)
kfree(x->aalg);
if (x->ealg)
kfree(x->ealg);
if (x->calg)
kfree(x->calg);
if (x->type)
x->type->destructor(x);
xfrm_put_type(x->type);
kfree(x);
}
void xfrm_state_delete(struct xfrm_state *x)
{
int kill = 0;
spin_lock_bh(&x->lock);
if (x->km.state != XFRM_STATE_DEAD) {
x->km.state = XFRM_STATE_DEAD;
kill = 1;
spin_lock(&xfrm_state_lock);
list_del(&x->bydst);
atomic_dec(&x->refcnt);
if (x->id.spi) {
list_del(&x->byspi);
atomic_dec(&x->refcnt);
}
spin_unlock(&xfrm_state_lock);
}
spin_unlock_bh(&x->lock);
if (kill && x->type)
x->type->destructor(x);
wake_up(km_waitq);
}
void xfrm_state_flush(u8 proto)
{
int i;
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
for (i = 0; i < XFRM_DST_HSIZE; i++) {
restart:
list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
if (!proto || x->id.proto == proto) {
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
xfrm_state_delete(x);
xfrm_state_put(x);
spin_lock_bh(&xfrm_state_lock);
goto restart;
}
}
}
spin_unlock_bh(&xfrm_state_lock);
wake_up(km_waitq);
}
struct xfrm_state *
xfrm_state_find(u32 daddr, struct flowi *fl, struct xfrm_tmpl *tmpl)
xfrm_state_find(u32 daddr, struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol)
{
unsigned h = ntohl(daddr);
struct xfrm_state *x;
......@@ -86,7 +143,8 @@ xfrm_state_find(u32 daddr, struct flowi *fl, struct xfrm_tmpl *tmpl)
return x;
} else if (x->km.state == XFRM_STATE_ACQ) {
acquire_in_progress = 1;
} else if (x->km.state == XFRM_STATE_ERROR) {
} else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) {
if (xfrm4_selector_match(&x->sel, fl))
error = 1;
}
......@@ -112,10 +170,23 @@ xfrm_state_find(u32 daddr, struct flowi *fl, struct xfrm_tmpl *tmpl)
x->sel.proto = fl->proto;
x->sel.ifindex = fl->oif;
x->id = tmpl->id;
if (km_query(x) == 0) {
if (x->id.daddr.xfrm4_addr == 0)
x->id.daddr = x->sel.daddr;
x->props.saddr = tmpl->saddr;
if (x->props.saddr.xfrm4_addr == 0)
x->props.saddr = x->sel.saddr;
x->props.mode = tmpl->mode;
if (km_query(x, tmpl, pol) == 0) {
x->km.state = XFRM_STATE_ACQ;
list_add_tail(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt);
if (x->id.spi) {
h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
}
} else {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
......@@ -142,6 +213,7 @@ void xfrm_state_insert(struct xfrm_state *x)
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
wake_up(km_waitq);
}
int xfrm_state_check_expire(struct xfrm_state *x)
......@@ -149,16 +221,16 @@ int xfrm_state_check_expire(struct xfrm_state *x)
if (x->km.state != XFRM_STATE_VALID)
return -EINVAL;
if (x->props.hard_byte_limit &&
x->stats.bytes >= x->props.hard_byte_limit) {
km_notify(x, SADB_EXT_LIFETIME_HARD);
if (x->lft.hard_byte_limit &&
x->curlft.bytes >= x->lft.hard_byte_limit) {
km_expired(x);
return -EINVAL;
}
if (x->km.warn_bytes &&
x->stats.bytes >= x->km.warn_bytes) {
x->curlft.bytes >= x->km.warn_bytes) {
x->km.warn_bytes = 0;
km_notify(x, SADB_EXT_LIFETIME_SOFT);
km_warn_expired(x);
}
return 0;
}
......@@ -189,7 +261,6 @@ xfrm_state_lookup(u32 daddr, u32 spi, u8 proto)
daddr == x->id.daddr.xfrm4_addr &&
proto == x->id.proto) {
atomic_inc(&x->refcnt);
x->stats.lastuse = xtime.tv_sec;
spin_unlock_bh(&xfrm_state_lock);
return x;
}
......@@ -198,6 +269,150 @@ xfrm_state_lookup(u32 daddr, u32 spi, u8 proto)
return NULL;
}
struct xfrm_state *
xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr)
{
struct xfrm_state *x, *x0;
unsigned h = ntohl(daddr);
h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
x0 = NULL;
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
if (daddr == x->id.daddr.xfrm4_addr &&
mode == x->props.mode &&
proto == x->id.proto &&
saddr == x->props.saddr.xfrm4_addr &&
(!reqid || reqid == x->props.reqid)) {
if (!x0)
x0 = x;
if (x->km.state != XFRM_STATE_ACQ)
continue;
x0 = x;
break;
}
}
if (x0) {
atomic_inc(&x0->refcnt);
} else if ((x0 = xfrm_state_alloc()) != NULL) {
x0->sel.daddr.xfrm4_addr = daddr;
x0->sel.daddr.xfrm4_mask = ~0;
x0->sel.saddr.xfrm4_addr = saddr;
x0->sel.saddr.xfrm4_mask = ~0;
x0->sel.prefixlen_d = 32;
x0->sel.prefixlen_s = 32;
x0->props.saddr.xfrm4_addr = saddr;
x0->km.state = XFRM_STATE_ACQ;
x0->id.daddr.xfrm4_addr = daddr;
x0->id.proto = proto;
x0->props.mode = mode;
x0->props.reqid = reqid;
atomic_inc(&x0->refcnt);
list_add_tail(&x0->bydst, xfrm_state_bydst+h);
wake_up(km_waitq);
}
spin_unlock_bh(&xfrm_state_lock);
return x0;
}
/* Silly enough, but I'm lazy to build resolution list */
struct xfrm_state * xfrm_find_acq_byseq(u32 seq)
{
int i;
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
for (i = 0; i < XFRM_DST_HSIZE; i++) {
list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
if (x->km.seq == seq) {
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
return x;
}
}
}
spin_unlock_bh(&xfrm_state_lock);
return NULL;
}
void
xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
{
u32 h;
struct xfrm_state *x0;
if (x->id.spi)
return;
if (minspi == maxspi) {
x0 = xfrm_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto);
if (x0) {
xfrm_state_put(x0);
return;
}
x->id.spi = minspi;
} else {
u32 spi = 0;
minspi = ntohl(minspi);
maxspi = ntohl(maxspi);
for (h=0; h<maxspi-minspi+1; h++) {
spi = minspi + net_random()%(maxspi-minspi+1);
x0 = xfrm_state_lookup(x->id.daddr.xfrm4_addr, htonl(spi), x->id.proto);
if (x0 == NULL)
break;
xfrm_state_put(x0);
}
x->id.spi = htonl(spi);
}
if (x->id.spi) {
spin_lock_bh(&xfrm_state_lock);
h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
wake_up(km_waitq);
}
}
int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
void *data)
{
int i;
struct xfrm_state *x;
int count = 0;
int err = 0;
spin_lock_bh(&xfrm_state_lock);
for (i = 0; i < XFRM_DST_HSIZE; i++) {
list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
if (proto == 255 || x->id.proto == proto)
count++;
}
}
if (count == 0) {
err = -ENOENT;
goto out;
}
for (i = 0; i < XFRM_DST_HSIZE; i++) {
list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
if (proto != 255 && x->id.proto != proto)
continue;
err = func(x, --count, data);
if (err)
goto out;
}
}
out:
spin_unlock_bh(&xfrm_state_lock);
return err;
}
int xfrm_replay_check(struct xfrm_state *x, u32 seq)
{
u32 diff;
......@@ -253,13 +468,60 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl)
return 0;
}
void km_notify(struct xfrm_state *x, int event)
static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list);
static rwlock_t xfrm_km_lock = RW_LOCK_UNLOCKED;
void km_warn_expired(struct xfrm_state *x)
{
struct xfrm_mgr *km;
read_lock(&xfrm_km_lock);
list_for_each_entry(km, &xfrm_km_list, list)
km->notify(x, 0);
read_unlock(&xfrm_km_lock);
}
void km_expired(struct xfrm_state *x)
{
struct xfrm_mgr *km;
x->km.state = XFRM_STATE_EXPIRED;
read_lock(&xfrm_km_lock);
list_for_each_entry(km, &xfrm_km_list, list)
km->notify(x, 1);
read_unlock(&xfrm_km_lock);
}
int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
{
int err = -EINVAL;
struct xfrm_mgr *km;
read_lock(&xfrm_km_lock);
list_for_each_entry(km, &xfrm_km_list, list) {
err = km->acquire(x, t, pol, XFRM_POLICY_OUT);
if (!err)
break;
}
read_unlock(&xfrm_km_lock);
return err;
}
int xfrm_register_km(struct xfrm_mgr *km)
{
write_lock_bh(&xfrm_km_lock);
list_add_tail(&km->list, &xfrm_km_list);
write_unlock_bh(&xfrm_km_lock);
return 0;
}
int km_query(struct xfrm_state *x)
int xfrm_unregister_km(struct xfrm_mgr *km)
{
return -EINVAL;
write_lock_bh(&xfrm_km_lock);
list_del(&km->list);
write_unlock_bh(&xfrm_km_lock);
return 0;
}
void __init xfrm_state_init(void)
......
#
# Makefile for the key AF.
#
obj-$(CONFIG_NET_KEY) += af_key.o
include $(TOPDIR)/Rules.make
This diff is collapsed.
......@@ -53,6 +53,7 @@ extern __u32 sysctl_rmem_max;
#include <linux/inet.h>
#include <linux/mroute.h>
#include <linux/igmp.h>
#include <net/xfrm.h>
extern struct net_proto_family inet_family_ops;
......@@ -282,6 +283,43 @@ extern int (*dlci_ioctl_hook)(unsigned int, void *);
EXPORT_SYMBOL(dlci_ioctl_hook);
#endif
EXPORT_SYMBOL(xfrm_policy_alloc);
EXPORT_SYMBOL(__xfrm_policy_destroy);
EXPORT_SYMBOL(xfrm_policy_lookup);
EXPORT_SYMBOL(xfrm_bundle_create);
EXPORT_SYMBOL(xfrm_lookup);
EXPORT_SYMBOL(__xfrm_policy_check);
EXPORT_SYMBOL(__xfrm_route_forward);
EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy);
EXPORT_SYMBOL(xfrm_state_find);
EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space);
EXPORT_SYMBOL(xfrm_state_lookup);
EXPORT_SYMBOL(xfrm_replay_check);
EXPORT_SYMBOL(xfrm_replay_advance);
EXPORT_SYMBOL(xfrm_check_selectors);
EXPORT_SYMBOL(xfrm4_rcv);
EXPORT_SYMBOL(xfrm_register_type);
EXPORT_SYMBOL(xfrm_unregister_type);
EXPORT_SYMBOL(xfrm_get_type);
EXPORT_SYMBOL(inet_peer_idlock);
EXPORT_SYMBOL(xfrm_register_km);
EXPORT_SYMBOL(xfrm_unregister_km);
EXPORT_SYMBOL(xfrm_state_delete);
EXPORT_SYMBOL(xfrm_state_walk);
EXPORT_SYMBOL(xfrm_find_acq_byseq);
EXPORT_SYMBOL(xfrm_find_acq);
EXPORT_SYMBOL(xfrm_alloc_spi);
EXPORT_SYMBOL(xfrm_state_flush);
EXPORT_SYMBOL(xfrm_policy_kill);
EXPORT_SYMBOL(xfrm_policy_delete);
EXPORT_SYMBOL(xfrm_policy_insert);
EXPORT_SYMBOL(xfrm_policy_walk);
EXPORT_SYMBOL(xfrm_policy_flush);
EXPORT_SYMBOL(xfrm_policy_byid);
#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
/* inet functions common to v4 and v6 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment