Subject: Re: [Vpn-failover] [RFC] IPSEC failover - Netlink part

From: Ulrich Weber <[email-masked]>
Date: Thu, 04 Nov 2004 15:01:20 +0100

Hi,

I take the opportunity to post my code as well. At the moment I'm working at
the same as Krisztian. My solutions however is for openswan, which uses
netlink instead of pfkey.

Please find attached the appropriate netlink modifications to
include/linux/xfrm.h and net/xfrm/xfrm_users.c

In theses patches, a new netlink group (XFRMGRP_REPLAY) is added to notify
about seq number changes. Each notify message contains a xfrm_usersa_id
struct with the replay struct attached as rt attribute. In addition, these
replay struct is also attached at ipsec sa dumps.

Any comments are welcome :)

Cheers
~ Ulrich
--- linux.org/include/linux/xfrm.h.	2004-10-11 04:57:07.000000000 +0200
+++ linux/include/linux/xfrm.h	2004-10-18 17:00:43.000000000 +0200
@@ -140,6 +140,9 @@
 	XFRM_MSG_FLUSHPOLICY,
 #define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY
 
+	XFRM_MSG_UPDSEQ,
+#define XFRM_MSG_UPDSEQ XFRM_MSG_UPDSEQ
+
 	XFRM_MSG_MAX
 };
 
@@ -171,6 +174,7 @@
 	XFRMA_ALG_COMP,		/* struct xfrm_algo */
 	XFRMA_ENCAP,		/* struct xfrm_algo + struct xfrm_encap_tmpl */
 	XFRMA_TMPL,		/* 1 or more struct xfrm_user_tmpl */
+	XFRMA_REPLAY,		/* struct xfrm_replay_state */ 
 	__XFRMA_MAX
 
 #define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -258,5 +258,6 @@

 #define XFRMGRP_ACQUIRE                1
 #define XFRMGRP_EXPIRE         2
+#define XFRMGRP_REPLAY         3

 #endif /* _LINUX_XFRM_H */
--- linux.org/net/xfrm/xfrm_user.c	2004-10-18 23:54:32.000000000 +0200
+++ linux/net/xfrm/xfrm_user.c	2004-10-21 16:27:59.000000000 +0200
@@ -240,6 +240,12 @@
 	if ((err = attach_encap_tmpl(&x->encap, xfrma[XFRMA_ENCAP-1])))
 		goto error;
 
+        if(xfrma[XFRMA_REPLAY-1]) {
+                struct xfrm_replay_state *replay;
+                replay = RTA_DATA(xfrma[XFRMA_REPLAY - 1]);
+                x->replay = *replay;
+        }
+
 	err = -ENOENT;
 	x->type = xfrm_get_type(x->id.proto, x->props.family);
 	if (x->type == NULL)
@@ -368,6 +375,8 @@
 	if (x->encap)
 		RTA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
 
+	RTA_PUT(skb, XFRMA_REPLAY, sizeof(x->replay), &x->replay);
+
 	nlh->nlmsg_len = skb->tail - b;
 out:
 	sp->this_idx++;
@@ -852,6 +861,27 @@
 	return 0;
 }
 
+static int xfrm_update_seq(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
+{
+        struct xfrm_state *x;
+        struct xfrm_usersa_id *p = NLMSG_DATA(nlh);
+	struct xfrm_replay_state *replay;
+ 
+	x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family);
+        if (x == NULL) {
+		printk(KERN_INFO "Found no xfrm state for sa seq update\n");
+                return -ESRCH;
+		}
+
+	if(xfrma[XFRMA_REPLAY-1]) {
+		replay = RTA_DATA(xfrma[XFRMA_REPLAY - 1]);
+		x->replay = *replay;
+		}
+	else return -EINVAL;
+		
+	return 0;
+} 
+
 static const int xfrm_msg_min[(XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)] = {
 	NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)),	/* NEW SA */
 	NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)),	/* DEL SA */
@@ -867,6 +897,7 @@
 	NLMSG_LENGTH(sizeof(struct xfrm_user_polexpire)), /* POLEXPIRE */
 	NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush)),	/* FLUSH SA */
 	NLMSG_LENGTH(0),				/* FLUSH POLICY */
+	NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)),/* UPD SEQ */
 };
 
 static struct xfrm_link {
@@ -893,6 +924,7 @@
 	{},
 	{	.doit	=	xfrm_flush_sa		},
 	{	.doit	=	xfrm_flush_policy	},
+	{       .doit   =       xfrm_update_seq		},
 };
 
 static int xfrm_done(struct netlink_callback *cb)
@@ -1050,6 +1082,33 @@
 	return -1;
 }
 
+static int build_replay(struct sk_buff *skb, struct xfrm_state *x, int event)
+{
+        struct xfrm_usersa_id *id;
+        struct nlmsghdr *nlh;
+        unsigned char *b = skb->tail;
+
+        nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_UPDSEQ,
+                        sizeof(*id));
+        id = NLMSG_DATA(nlh);
+        nlh->nlmsg_flags = 0;
+
+	id->daddr = x->id.daddr;
+	id->spi = x->id.spi;
+	id->family = x->props.family;
+	id->proto = x->id.proto;
+
+	RTA_PUT(skb, XFRMA_REPLAY, sizeof(x->replay), &x->replay);
+
+        nlh->nlmsg_len = skb->tail - b;
+        return skb->len;
+
+rtattr_failure:
+nlmsg_failure:
+        skb_trim(skb, b - skb->data);
+        return -1;
+}
+
 static int xfrm_send_state_notify(struct xfrm_state *x, int hard)
 {
 	struct sk_buff *skb;
@@ -1218,12 +1277,28 @@
 	return netlink_broadcast(xfrm_nl, skb, 0, XFRMGRP_EXPIRE, GFP_ATOMIC);
 }
 
+static int xfrm_send_replay_notify(struct xfrm_state *x, int event) {
+        struct sk_buff *skb;
+
+        skb = alloc_skb(sizeof(struct xfrm_usersa_id) + sizeof(struct xfrm_replay_state) + 16, GFP_ATOMIC);
+        if (skb == NULL)
+                return -ENOMEM;
+
+        if (build_replay(skb, x, event) < 0)
+                BUG();
+
+        NETLINK_CB(skb).dst_groups = XFRMGRP_REPLAY;
+
+	return netlink_broadcast(xfrm_nl, skb, 0, XFRMGRP_REPLAY, GFP_ATOMIC);
+}
+
 static struct xfrm_mgr netlink_mgr = {
 	.id		= "netlink",
 	.notify		= xfrm_send_state_notify,
 	.acquire	= xfrm_send_acquire,
 	.compile_policy	= xfrm_compile_policy,
 	.notify_policy	= xfrm_send_policy_notify,
+	.notify_seq	= xfrm_send_replay_notify,
 };
 
 static int __init xfrm_user_init(void)