introduce "fixlimits" option of "ip xfrm state save",
if have these option we will change the limits saved to
dump file according to the real passed time/bytes/packets
since the state add-time.

For instance, we have a process in container which set
xfrm state 10sec ago with a timeout of 30sec and now we
do C/R for the whole container. As the process in question
knows nothing about C/R, after restore it might expect that
xfrm state dissappears in 30-10=20sec, so we need to set
timeout to 20. If difference is <=0 set 1 at least to do
not lose the limit completely.

Signed-off-by: Pavel Tikhomirov <ptikhomi...@virtuozzo.com>
---
 ip/xfrm_state.c    | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 man/man8/ip-xfrm.8 | 10 ++++++-
 2 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index 56d9f22..6c55b3e 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -32,6 +32,7 @@
 #include "xfrm.h"
 #include "ip_common.h"
 #include <errno.h>
+#include <time.h>
 
 /* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */
 #define NLMSG_DELETEALL_BUF_SIZE 8192
@@ -66,7 +67,8 @@ static void usage(void)
        fprintf(stderr, "Usage: ip xfrm state { delete | get } ID [ mark MARK [ 
mask MASK ] ]\n");
        fprintf(stderr, "Usage: ip xfrm state { deleteall | list } [ ID ] [ 
mode MODE ] [ reqid REQID ]\n");
        fprintf(stderr, "        [ flag FLAG-LIST ]\n");
-       fprintf(stderr, "Usage: ip xfrm state { save | restore }\n");
+       fprintf(stderr, "Usage: ip xfrm state save [ fixlimits ]\n");
+       fprintf(stderr, "Usage: ip xfrm state restore\n");
        fprintf(stderr, "Usage: ip xfrm state flush [ proto XFRM-PROTO ]\n");
        fprintf(stderr, "Usage: ip xfrm state count\n");
        fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ 
spi SPI ]\n");
@@ -1077,10 +1079,89 @@ static int xfrm_state_keep(const struct sockaddr_nl 
*who,
 
 static __u32 state_dump_magic = 0x71706987;
 
+static inline void fixup_lft_limit(__u64 *lft_limit, __u64 lft_cur)
+{
+       if (lft_cur < *lft_limit)
+               /*
+                * Limit is not yet hit, decrease it by
+                * current lifetime
+                */
+               *lft_limit -= lft_cur;
+       else
+               /*
+                * Limit is already hit or almost hit, set it
+                * to 1 to hit it imediately after restore
+                */
+               *lft_limit = 1;
+}
+
+static int fixup_lifetime(struct xfrm_lifetime_cur *curlft, struct 
xfrm_lifetime_cfg *lft)
+{
+       unsigned long now;
+       __u64 time_since_add, time_since_use;
+
+       now = time(NULL);
+       if (now < 0) {
+               fprintf(stderr, "Failed to get current time\n");
+               return -1;
+       }
+       time_since_add = now - curlft->add_time;
+       time_since_use = now - (curlft->use_time ? : curlft->add_time);
+
+       /* Fixup expire timeouts */
+       if (lft->hard_add_expires_seconds)
+               fixup_lft_limit(&lft->hard_add_expires_seconds, time_since_add);
+       if (lft->hard_use_expires_seconds)
+               fixup_lft_limit(&lft->hard_use_expires_seconds, time_since_use);
+       if (lft->soft_add_expires_seconds)
+               fixup_lft_limit(&lft->soft_add_expires_seconds, time_since_add);
+       if (lft->soft_use_expires_seconds)
+               fixup_lft_limit(&lft->soft_use_expires_seconds, time_since_use);
+
+       /* Fixup expire limits */
+       if (lft->hard_byte_limit != XFRM_INF)
+               fixup_lft_limit(&lft->hard_byte_limit, curlft->bytes);
+       if (lft->hard_packet_limit != XFRM_INF)
+               fixup_lft_limit(&lft->hard_packet_limit, curlft->packets);
+       if (lft->soft_byte_limit != XFRM_INF)
+               fixup_lft_limit(&lft->soft_byte_limit, curlft->bytes);
+       if (lft->soft_packet_limit != XFRM_INF)
+               fixup_lft_limit(&lft->soft_packet_limit, curlft->packets);
+
+       return 0;
+}
+
+static int save_state(const struct sockaddr_nl *who, struct nlmsghdr *n,
+               void *arg)
+{
+       struct xfrm_usersa_info *xsinfo;
+       int len = n->nlmsg_len;
+
+       if (n->nlmsg_type != XFRM_MSG_NEWSA) {
+               fprintf(stderr, "BUG: wrong nlmsg_type: %08x\n",
+                       n->nlmsg_type);
+               return -1;
+       }
+
+       xsinfo = NLMSG_DATA(n);
+       len -= NLMSG_SPACE(sizeof(*xsinfo));
+
+       if (len < 0) {
+               fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+               return -1;
+       }
+
+       if (fixup_lifetime(&xsinfo->curlft, &xsinfo->lft))
+               return -1;
+
+       return save_nlmsg(who, n, arg);
+}
+
 static int xfrm_state_list_deleteall_or_save(int argc, char **argv, int 
deleteall, int save)
 {
        char *idp = NULL;
        struct rtnl_handle rth;
+       int fixlimits = 0;
 
        if (argc > 0)
                filter.use = 1;
@@ -1105,6 +1186,9 @@ static int xfrm_state_list_deleteall_or_save(int argc, 
char **argv, int deleteal
 
                        filter.state_flags_mask = XFRM_FILTER_MASK_FULL;
 
+               } else if (strcmp(*argv, "fixlimits") == 0) {
+                       fixlimits = 1;
+
                } else {
                        if (idp)
                                invarg("unknown", *argv);
@@ -1179,7 +1263,7 @@ static int xfrm_state_list_deleteall_or_save(int argc, 
char **argv, int deleteal
                if (save) {
                        if (dump_write_magic(state_dump_magic))
                                return -1;
-                       rtnl_filter = save_nlmsg;
+                       rtnl_filter = fixlimits ? save_state : save_nlmsg;
                }
 
                struct xfrm_address_filter addrfilter = {
diff --git a/man/man8/ip-xfrm.8 b/man/man8/ip-xfrm.8
index f5b8290..2fd088d 100644
--- a/man/man8/ip-xfrm.8
+++ b/man/man8/ip-xfrm.8
@@ -104,7 +104,7 @@ ip-xfrm \- transform configuration
 .BR "ip xfrm state count"
 
 .ti -8
-.BR "ip xfrm state save"
+.BR "ip xfrm state save" " [ " fixlimits " ]"
 
 .ti -8
 .BR "ip xfrm state restore"
@@ -550,6 +550,14 @@ encapsulates packets with protocol
 .RI "using source port " SPORT ", destination port "  DPORT
 .RI ", and original address " OADDR "."
 
+.P
+If the
+.BI fixlimits
+option is set on save, change entry
+.I LIMITs
+saved to dump file according to the real time/bytes/packets passed
+since entry add-time.
+
 .sp
 .PP
 .TS
-- 
2.5.5

Reply via email to