diff options
-rw-r--r-- | include/linux/sysctl.h | 2 | ||||
-rw-r--r-- | include/linux/xfrm.h | 30 | ||||
-rw-r--r-- | include/net/xfrm.h | 44 | ||||
-rw-r--r-- | net/core/sysctl_net_core.c | 23 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 76 | ||||
-rw-r--r-- | net/xfrm/xfrm_user.c | 4 |
6 files changed, 176 insertions, 3 deletions
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 6e8880ea49e7..b686548f32e0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -261,6 +261,8 @@ enum NET_CORE_DEV_WEIGHT=17, NET_CORE_SOMAXCONN=18, NET_CORE_BUDGET=19, + NET_CORE_AEVENT_ETIME=20, + NET_CORE_AEVENT_RSEQTH=21, }; /* /proc/sys/net/ethernet */ diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 82fbb758e28f..b54a12940ef6 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -156,6 +156,10 @@ enum { XFRM_MSG_FLUSHPOLICY, #define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY + XFRM_MSG_NEWAE, +#define XFRM_MSG_NEWAE XFRM_MSG_NEWAE + XFRM_MSG_GETAE, +#define XFRM_MSG_GETAE XFRM_MSG_GETAE __XFRM_MSG_MAX }; #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1) @@ -194,6 +198,21 @@ struct xfrm_encap_tmpl { xfrm_address_t encap_oa; }; +/* AEVENT flags */ +enum xfrm_ae_ftype_t { + XFRM_AE_UNSPEC, + XFRM_AE_RTHR=1, /* replay threshold*/ + XFRM_AE_RVAL=2, /* replay value */ + XFRM_AE_LVAL=4, /* lifetime value */ + XFRM_AE_ETHR=8, /* expiry timer threshold */ + XFRM_AE_CR=16, /* Event cause is replay update */ + XFRM_AE_CE=32, /* Event cause is timer expiry */ + XFRM_AE_CU=64, /* Event cause is policy update */ + __XFRM_AE_MAX + +#define XFRM_AE_MAX (__XFRM_AE_MAX - 1) +}; + /* Netlink message attributes. */ enum xfrm_attr_type_t { XFRMA_UNSPEC, @@ -205,6 +224,10 @@ enum xfrm_attr_type_t { XFRMA_SA, XFRMA_POLICY, XFRMA_SEC_CTX, /* struct xfrm_sec_ctx */ + XFRMA_LTIME_VAL, + XFRMA_REPLAY_VAL, + XFRMA_REPLAY_THRESH, + XFRMA_ETIMER_THRESH, __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) @@ -235,6 +258,11 @@ struct xfrm_usersa_id { __u8 proto; }; +struct xfrm_aevent_id { + __u32 flags; + struct xfrm_usersa_id sa_id; +}; + struct xfrm_userspi_info { struct xfrm_usersa_info info; __u32 min; @@ -306,6 +334,8 @@ enum xfrm_nlgroups { #define XFRMNLGRP_SA XFRMNLGRP_SA XFRMNLGRP_POLICY, #define XFRMNLGRP_POLICY XFRMNLGRP_POLICY + XFRMNLGRP_AEVENTS, +#define XFRMNLGRP_AEVENTS XFRMNLGRP_AEVENTS __XFRMNLGRP_MAX }; #define XFRMNLGRP_MAX (__XFRMNLGRP_MAX - 1) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 8d362c49b8a9..bc005e62e434 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -20,6 +20,10 @@ #define XFRM_ALIGN8(len) (((len) + 7) & ~7) +extern struct sock *xfrm_nl; +extern u32 sysctl_xfrm_aevent_etime; +extern u32 sysctl_xfrm_aevent_rseqth; + extern struct semaphore xfrm_cfg_sem; /* Organization of SPD aka "XFRM rules" @@ -135,6 +139,16 @@ struct xfrm_state /* State for replay detection */ struct xfrm_replay_state replay; + /* Replay detection state at the time we sent the last notification */ + struct xfrm_replay_state preplay; + + /* Replay detection notification settings */ + u32 replay_maxage; + u32 replay_maxdiff; + + /* Replay detection notification timer */ + struct timer_list rtimer; + /* Statistics */ struct xfrm_stats stats; @@ -169,6 +183,7 @@ struct km_event u32 hard; u32 proto; u32 byid; + u32 aevent; } data; u32 seq; @@ -305,7 +320,21 @@ struct xfrm_policy struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH]; }; -#define XFRM_KM_TIMEOUT 30 +#define XFRM_KM_TIMEOUT 30 +/* which seqno */ +#define XFRM_REPLAY_SEQ 1 +#define XFRM_REPLAY_OSEQ 2 +#define XFRM_REPLAY_SEQ_MASK 3 +/* what happened */ +#define XFRM_REPLAY_UPDATE XFRM_AE_CR +#define XFRM_REPLAY_TIMEOUT XFRM_AE_CE + +/* default aevent timeout in units of 100ms */ +#define XFRM_AE_ETIME 10 +/* Async Event timer multiplier */ +#define XFRM_AE_ETH_M 10 +/* default seq threshold size */ +#define XFRM_AE_SEQT_SIZE 2 struct xfrm_mgr { @@ -865,6 +894,7 @@ extern int 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 void xfrm_replay_notify(struct xfrm_state *x, int event); extern int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb); extern int xfrm_state_mtu(struct xfrm_state *x, int mtu); extern int xfrm_init_state(struct xfrm_state *x); @@ -965,4 +995,16 @@ static inline int xfrm_policy_id2dir(u32 index) return index & 7; } +static inline int xfrm_aevent_is_on(void) +{ + return netlink_has_listeners(xfrm_nl,XFRMNLGRP_AEVENTS); +} + +static inline void xfrm_aevent_doreplay(struct xfrm_state *x) +{ + if (xfrm_aevent_is_on()) + xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); +} + + #endif /* _NET_XFRM_H */ diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 2f278c8e4743..710453656721 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -26,6 +26,11 @@ extern int sysctl_core_destroy_delay; extern char sysctl_divert_version[]; #endif /* CONFIG_NET_DIVERT */ +#ifdef CONFIG_XFRM +extern u32 sysctl_xfrm_aevent_etime; +extern u32 sysctl_xfrm_aevent_rseqth; +#endif + ctl_table core_table[] = { #ifdef CONFIG_NET { @@ -111,6 +116,24 @@ ctl_table core_table[] = { .proc_handler = &proc_dostring }, #endif /* CONFIG_NET_DIVERT */ +#ifdef CONFIG_XFRM + { + .ctl_name = NET_CORE_AEVENT_ETIME, + .procname = "xfrm_aevent_etime", + .data = &sysctl_xfrm_aevent_etime, + .maxlen = sizeof(u32), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = NET_CORE_AEVENT_RSEQTH, + .procname = "xfrm_aevent_rseqth", + .data = &sysctl_xfrm_aevent_rseqth, + .maxlen = sizeof(u32), + .mode = 0644, + .proc_handler = &proc_dointvec + }, +#endif /* CONFIG_XFRM */ #endif /* CONFIG_NET */ { .ctl_name = NET_CORE_SOMAXCONN, diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index c656cbaf35e8..8eaee499cad5 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -20,6 +20,8 @@ #include <linux/module.h> #include <asm/uaccess.h> +u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME; +u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE; /* Each xfrm_state may be linked to two tables: 1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl) @@ -62,6 +64,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x) { if (del_timer(&x->timer)) BUG(); + if (del_timer(&x->rtimer)) + BUG(); kfree(x->aalg); kfree(x->ealg); kfree(x->calg); @@ -190,11 +194,16 @@ struct xfrm_state *xfrm_state_alloc(void) init_timer(&x->timer); x->timer.function = xfrm_timer_handler; x->timer.data = (unsigned long)x; + init_timer(&x->rtimer); + x->rtimer.function = xfrm_replay_timer_handler; + x->rtimer.data = (unsigned long)x; x->curlft.add_time = (unsigned long)xtime.tv_sec; x->lft.soft_byte_limit = XFRM_INF; x->lft.soft_packet_limit = XFRM_INF; x->lft.hard_byte_limit = XFRM_INF; x->lft.hard_packet_limit = XFRM_INF; + x->replay_maxage = 0; + x->replay_maxdiff = 0; spin_lock_init(&x->lock); } return x; @@ -228,6 +237,8 @@ static int __xfrm_state_delete(struct xfrm_state *x) spin_unlock(&xfrm_state_lock); if (del_timer(&x->timer)) __xfrm_state_put(x); + if (del_timer(&x->rtimer)) + __xfrm_state_put(x); /* The number two in this test is the reference * mentioned in the comment below plus the reference @@ -426,6 +437,10 @@ static void __xfrm_state_insert(struct xfrm_state *x) if (!mod_timer(&x->timer, jiffies + HZ)) xfrm_state_hold(x); + if (x->replay_maxage && + !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) + xfrm_state_hold(x); + wake_up(&km_waitq); } @@ -762,6 +777,62 @@ out: } EXPORT_SYMBOL(xfrm_state_walk); + +void xfrm_replay_notify(struct xfrm_state *x, int event) +{ + struct km_event c; + /* we send notify messages in case + * 1. we updated on of the sequence numbers, and the seqno difference + * is at least x->replay_maxdiff, in this case we also update the + * timeout of our timer function + * 2. if x->replay_maxage has elapsed since last update, + * and there were changes + * + * The state structure must be locked! + */ + + switch (event) { + case XFRM_REPLAY_UPDATE: + if (x->replay_maxdiff && + (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && + (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) + return; + + break; + + case XFRM_REPLAY_TIMEOUT: + if ((x->replay.seq == x->preplay.seq) && + (x->replay.bitmap == x->preplay.bitmap) && + (x->replay.oseq == x->preplay.oseq)) + return; + + break; + } + + memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); + c.event = XFRM_MSG_NEWAE; + c.data.aevent = event; + km_state_notify(x, &c); + +resched: + if (x->replay_maxage && + !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) + xfrm_state_hold(x); + +} + +static void xfrm_replay_timer_handler(unsigned long data) +{ + struct xfrm_state *x = (struct xfrm_state*)data; + + spin_lock(&x->lock); + + if (xfrm_aevent_is_on() && x->km.state == XFRM_STATE_VALID) + xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT); + + spin_unlock(&x->lock); +} + int xfrm_replay_check(struct xfrm_state *x, u32 seq) { u32 diff; @@ -805,6 +876,9 @@ void xfrm_replay_advance(struct xfrm_state *x, u32 seq) diff = x->replay.seq - seq; x->replay.bitmap |= (1U << diff); } + + if (xfrm_aevent_is_on()) + xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); } EXPORT_SYMBOL(xfrm_replay_advance); @@ -835,7 +909,7 @@ void km_state_notify(struct xfrm_state *x, struct km_event *c) EXPORT_SYMBOL(km_policy_notify); EXPORT_SYMBOL(km_state_notify); -static void km_state_expired(struct xfrm_state *x, int hard) +void km_state_expired(struct xfrm_state *x, int hard) { struct km_event c; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 7de17559249a..6f643e58e044 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -28,7 +28,7 @@ #include <net/netlink.h> #include <asm/uaccess.h> -static struct sock *xfrm_nl; +struct sock *xfrm_nl; static int verify_one_alg(struct rtattr **xfrma, enum xfrm_attr_type_t type) { @@ -1618,3 +1618,5 @@ module_init(xfrm_user_init); module_exit(xfrm_user_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); +EXPORT_SYMBOL(xfrm_nl); + |