@@ -2002,6 +2002,48 @@ int xfrm_init_state(struct xfrm_state *x)
EXPORT_SYMBOL(xfrm_init_state);
+static int clock_change_callback(struct notifier_block *nb,
+ unsigned long reason, void *arg)
+{
+ struct xfrm_state_walk *walk;
+ struct xfrm_state *state;
+ struct net *net;
+ long next;
+
+ rtnl_lock();
+ for_each_net(net) {
+ spin_lock_bh(&xfrm_state_lock);
+ list_for_each_entry(walk, &net->xfrm.state_all, all) {
+ state = container_of(walk, struct xfrm_state, km);
+ xfrm_state_hold(state);
+ spin_unlock_bh(&xfrm_state_lock);
+
+ spin_lock_bh(&state->lock);
+ if (state->km.dying) {
+ next = 0;
+ } else {
+ state->km.dying = 1;
+ km_state_expired(state, 0, 0);
+ next = state->lft.hard_add_expires_seconds -
+ state->lft.soft_add_expires_seconds;
+ }
+ state->km.state = XFRM_STATE_EXPIRED;
+ tasklet_hrtimer_start(&state->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL);
+ spin_unlock_bh(&state->lock);
+ xfrm_state_put(state);
+ spin_lock_bh(&xfrm_state_lock);
+ }
+ spin_unlock_bh(&xfrm_state_lock);
+ }
+ rtnl_unlock();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block clock_change_notifier = {
+ .notifier_call = clock_change_callback,
+};
+
int __net_init xfrm_state_init(struct net *net)
{
unsigned int sz;
@@ -2026,6 +2068,7 @@ int __net_init xfrm_state_init(struct net *net)
INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
init_waitqueue_head(&net->xfrm.km_waitq);
+ register_clock_change_notifier(&clock_change_notifier);
return 0;
out_byspi:
@@ -2057,6 +2100,7 @@ void xfrm_state_fini(struct net *net)
xfrm_hash_free(net->xfrm.state_bysrc, sz);
WARN_ON(!hlist_empty(net->xfrm.state_bydst));
xfrm_hash_free(net->xfrm.state_bydst, sz);
+ unregister_clock_change_notifier(&clock_change_notifier);
}
#ifdef CONFIG_AUDITSYSCALL