[IPSEC]: Sync series - core changes
Jamal Hadi Salim [Tue, 21 Mar 2006 03:15:11 +0000 (19:15 -0800)]
This patch provides the core functionality needed for sync events
for ipsec. Derived work of Krisztian KOVACS <hidden@balabit.hu>

Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/linux/sysctl.h
include/linux/xfrm.h
include/net/xfrm.h
net/core/sysctl_net_core.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c

index 6e8880e..b686548 100644 (file)
@@ -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 */
index 82fbb75..b54a129 100644 (file)
@@ -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)
index 8d362c4..bc005e6 100644 (file)
 
 #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 */
index 2f278c8..7104536 100644 (file)
@@ -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,
index c656cba..8eaee49 100644 (file)
@@ -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;
 
index 7de1755..6f643e5 100644 (file)
@@ -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);
+