#include "xfrm.h"
#include "ip_common.h"
-//#define NLMSG_FLUSH_BUF_SIZE (4096-512)
-#define NLMSG_FLUSH_BUF_SIZE 8192
+//#define NLMSG_DELETEALL_BUF_SIZE (4096-512)
+#define NLMSG_DELETEALL_BUF_SIZE 8192
/*
* Receiving buffer defines:
static void usage(void)
{
fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n");
- fprintf(stderr, " [ reqid REQID ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n");
+ fprintf(stderr, " [ reqid REQID ] [ seq SEQ ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n");
fprintf(stderr, " [ encap ENCAP ] [ sel SELECTOR ] [ LIMIT-LIST ]\n");
+ fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ reqid REQID ] [ seq SEQ ]\n");
+ fprintf(stderr, " [ min SPI max SPI ]\n");
fprintf(stderr, "Usage: ip xfrm state { delete | get } ID\n");
- fprintf(stderr, "Usage: ip xfrm state { flush | list } [ ID ] [ mode MODE ] [ reqid REQID ]\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 flush [ proto XFRM_PROTO ]\n");
fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n");
//fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n");
for (i = - (plen % 2), j = 0; j < len; i += 2, j++) {
char vbuf[3];
- char val;
+ __u8 val;
vbuf[0] = i >= 0 ? p[i] : '0';
vbuf[1] = p[i + 1];
return 0;
}
+static int xfrm_seq_parse(__u32 *seq, int *argcp, char ***argvp)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ if (get_u32(seq, *argv, 0))
+ invarg("\"SEQ\" is invalid", *argv);
+
+ *seq = htonl(*seq);
+
+ *argcp = argc;
+ *argvp = argv;
+
+ return 0;
+}
+
static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp)
{
int argc = *argcp;
} else if (strcmp(*argv, "reqid") == 0) {
NEXT_ARG();
xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv);
+ } else if (strcmp(*argv, "seq") == 0) {
+ NEXT_ARG();
+ xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv);
} else if (strcmp(*argv, "replay-window") == 0) {
NEXT_ARG();
if (get_u8(&req.xsinfo.replay_window, *argv, 0))
return 0;
}
+static int xfrm_state_allocspi(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct xfrm_userspi_info xspi;
+ char buf[RTA_BUF_SIZE];
+ } req;
+ char *idp = NULL;
+ char *minp = NULL;
+ char *maxp = NULL;
+ char res_buf[NLMSG_BUF_SIZE];
+ struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf;
+
+ memset(res_buf, 0, sizeof(res_buf));
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = XFRM_MSG_ALLOCSPI;
+ req.xspi.info.family = preferred_family;
+
+#if 0
+ req.xsinfo.lft.soft_byte_limit = XFRM_INF;
+ req.xsinfo.lft.hard_byte_limit = XFRM_INF;
+ req.xsinfo.lft.soft_packet_limit = XFRM_INF;
+ req.xsinfo.lft.hard_packet_limit = XFRM_INF;
+#endif
+
+ while (argc > 0) {
+ if (strcmp(*argv, "mode") == 0) {
+ NEXT_ARG();
+ xfrm_mode_parse(&req.xspi.info.mode, &argc, &argv);
+ } else if (strcmp(*argv, "reqid") == 0) {
+ NEXT_ARG();
+ xfrm_reqid_parse(&req.xspi.info.reqid, &argc, &argv);
+ } else if (strcmp(*argv, "seq") == 0) {
+ NEXT_ARG();
+ xfrm_seq_parse(&req.xspi.info.seq, &argc, &argv);
+ } else if (strcmp(*argv, "min") == 0) {
+ if (minp)
+ duparg("min", *argv);
+ minp = *argv;
+
+ NEXT_ARG();
+
+ if (get_u32(&req.xspi.min, *argv, 0))
+ invarg("\"min\" value is invalid", *argv);
+ } else if (strcmp(*argv, "max") == 0) {
+ if (maxp)
+ duparg("max", *argv);
+ maxp = *argv;
+
+ NEXT_ARG();
+
+ if (get_u32(&req.xspi.max, *argv, 0))
+ invarg("\"max\" value is invalid", *argv);
+ } else {
+ /* try to assume ID */
+ if (idp)
+ invarg("unknown", *argv);
+ idp = *argv;
+
+ /* ID */
+ xfrm_id_parse(&req.xspi.info.saddr, &req.xspi.info.id,
+ &req.xspi.info.family, 0, &argc, &argv);
+ if (req.xspi.info.id.spi) {
+ fprintf(stderr, "\"SPI\" must be zero\n");
+ exit(1);
+ }
+ if (preferred_family == AF_UNSPEC)
+ preferred_family = req.xspi.info.family;
+ }
+ argc--; argv++;
+ }
+
+ if (!idp) {
+ fprintf(stderr, "Not enough information: \"ID\" is required\n");
+ exit(1);
+ }
+
+ if (minp) {
+ if (!maxp) {
+ fprintf(stderr, "\"max\" is missing\n");
+ exit(1);
+ }
+ if (req.xspi.min > req.xspi.max) {
+ fprintf(stderr, "\"min\" valie is larger than \"max\" one\n");
+ exit(1);
+ }
+ } else {
+ if (maxp) {
+ fprintf(stderr, "\"min\" is missing\n");
+ exit(1);
+ }
+
+ /* XXX: Default value defined in PF_KEY;
+ * See kernel's net/key/af_key.c(pfkey_getspi).
+ */
+ req.xspi.min = 0x100;
+ req.xspi.max = 0x0fffffff;
+
+ /* XXX: IPCOMP spi is 16-bits;
+ * See kernel's net/xfrm/xfrm_user(verify_userspi_info).
+ */
+ if (req.xspi.info.id.proto == IPPROTO_COMP)
+ req.xspi.max = 0xffff;
+ }
+
+ if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+ exit(1);
+
+ if (req.xspi.info.family == AF_UNSPEC)
+ req.xspi.info.family = AF_INET;
+
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0)
+ exit(2);
+
+ if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
+ fprintf(stderr, "An error :-)\n");
+ exit(1);
+ }
+
+ rtnl_close(&rth);
+
+ return 0;
+}
+
static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo)
{
if (!filter.use)
return 1;
}
-static int xfrm_selector_iszero(struct xfrm_selector *s)
-{
- struct xfrm_selector s0;
-
- memset(&s0, 0, sizeof(s0));
-
- return (memcmp(&s0, s, sizeof(s0)) == 0);
-}
-
-static int xfrm_state_print(const struct sockaddr_nl *who,
- struct nlmsghdr *n,
- void *arg)
+int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+ void *arg)
{
FILE *fp = (FILE*)arg;
- struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n);
- int len = n->nlmsg_len;
struct rtattr * tb[XFRMA_MAX+1];
+ struct rtattr * rta;
+ struct xfrm_usersa_info *xsinfo = NULL;
+ struct xfrm_user_expire *xexp = NULL;
+ struct xfrm_usersa_id *xsid = NULL;
+ int len = n->nlmsg_len;
if (n->nlmsg_type != XFRM_MSG_NEWSA &&
- n->nlmsg_type != XFRM_MSG_DELSA) {
+ n->nlmsg_type != XFRM_MSG_DELSA &&
+ n->nlmsg_type != XFRM_MSG_UPDSA &&
+ n->nlmsg_type != XFRM_MSG_EXPIRE) {
fprintf(stderr, "Not a state: %08x %08x %08x\n",
n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
return 0;
}
- len -= NLMSG_LENGTH(sizeof(*xsinfo));
+ if (n->nlmsg_type == XFRM_MSG_DELSA) {
+ /* Dont blame me for this .. Herbert made me do it */
+ xsid = NLMSG_DATA(n);
+ len -= NLMSG_LENGTH(sizeof(*xsid));
+ } else if (n->nlmsg_type == XFRM_MSG_EXPIRE) {
+ xexp = NLMSG_DATA(n);
+ xsinfo = &xexp->state;
+ len -= NLMSG_LENGTH(sizeof(*xexp));
+ } else {
+ xexp = NULL;
+ xsinfo = NLMSG_DATA(n);
+ len -= NLMSG_LENGTH(sizeof(*xsinfo));
+ }
+
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
- if (!xfrm_state_filter_match(xsinfo))
+ if (xsinfo && !xfrm_state_filter_match(xsinfo))
return 0;
- parse_rtattr(tb, XFRMA_MAX, XFRMS_RTA(xsinfo), len);
-
if (n->nlmsg_type == XFRM_MSG_DELSA)
fprintf(fp, "Deleted ");
+ else if (n->nlmsg_type == XFRM_MSG_UPDSA)
+ fprintf(fp, "Updated ");
+ else if (n->nlmsg_type == XFRM_MSG_EXPIRE)
+ fprintf(fp, "Expired ");
- xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode,
- xsinfo->reqid, xsinfo->family, 1, fp, NULL);
-
- fprintf(fp, "\t");
- fprintf(fp, "replay-window %u ", xsinfo->replay_window);
- if (show_stats > 0)
- fprintf(fp, "seq 0x%08u ", xsinfo->seq);
- if (show_stats > 0 || xsinfo->flags) {
- __u8 flags = xsinfo->flags;
-
- fprintf(fp, "flag ");
- XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOECN, "noecn");
- XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_DECAP_DSCP, "decap-dscp");
- if (flags)
- fprintf(fp, "%x", flags);
- if (show_stats > 0)
- fprintf(fp, " (0x%s)", strxf_mask8(flags));
- }
- fprintf(fp, "%s", _SL_);
+ if (n->nlmsg_type == XFRM_MSG_DELSA)
+ rta = XFRMSID_RTA(xsid);
+ else if (n->nlmsg_type == XFRM_MSG_EXPIRE)
+ rta = XFRMEXP_RTA(xexp);
+ else
+ rta = XFRMS_RTA(xsinfo);
- xfrm_xfrma_print(tb, xsinfo->family, fp, "\t");
+ parse_rtattr(tb, XFRMA_MAX, rta, len);
- if (!xfrm_selector_iszero(&xsinfo->sel))
- xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, "\tsel ");
+ if (n->nlmsg_type == XFRM_MSG_DELSA) {
+ //xfrm_policy_id_print();
- if (show_stats > 0) {
- xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, "\t");
- xfrm_stats_print(&xsinfo->stats, fp, "\t");
+ if (!tb[XFRMA_SA]) {
+ fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n");
+ return -1;
+ }
+ if (RTA_PAYLOAD(tb[XFRMA_SA]) < sizeof(*xsinfo)) {
+ fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n");
+ return -1;
+ }
+ xsinfo = (struct xfrm_usersa_info *)RTA_DATA(tb[XFRMA_SA]);
+ }
+
+ xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL);
+
+ if (n->nlmsg_type == XFRM_MSG_EXPIRE) {
+ fprintf(fp, "\t");
+ fprintf(fp, "hard %u", xexp->hard);
+ fprintf(fp, "%s", _SL_);
}
if (oneline)
fprintf(fp, "\n");
+ fflush(fp);
return 0;
}
return 0;
if (xb->offset > xb->size) {
- fprintf(stderr, "Flush buffer overflow\n");
+ fprintf(stderr, "State buffer overflow\n");
return -1;
}
return 0;
}
-static int xfrm_state_list_or_flush(int argc, char **argv, int flush)
+static int xfrm_state_list_or_deleteall(int argc, char **argv, int deleteall)
{
char *idp = NULL;
struct rtnl_handle rth;
if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
exit(1);
- if (flush) {
+ if (deleteall) {
struct xfrm_buffer xb;
- char buf[NLMSG_FLUSH_BUF_SIZE];
+ char buf[NLMSG_DELETEALL_BUF_SIZE];
int i;
xb.buf = buf;
xb.nlmsg_count = 0;
if (show_stats > 1)
- fprintf(stderr, "Flush round = %d\n", i);
+ fprintf(stderr, "Delete-all round = %d\n", i);
if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) {
perror("Cannot send dump request");
}
if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb, NULL, NULL) < 0) {
- fprintf(stderr, "Flush terminated\n");
+ fprintf(stderr, "Delete-all terminated\n");
exit(1);
}
if (xb.nlmsg_count == 0) {
if (show_stats > 1)
- fprintf(stderr, "Flush completed\n");
+ fprintf(stderr, "Delete-all completed\n");
break;
}
if (rtnl_send(&rth, xb.buf, xb.offset) < 0) {
- perror("Failed to send flush request\n");
+ perror("Failed to send delete-all request\n");
exit(1);
}
if (show_stats > 1)
- fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count);
+ fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count);
xb.offset = 0;
xb.nlmsg_count = 0;
exit(0);
}
-static int xfrm_state_flush_all(void)
+static int xfrm_state_flush(int argc, char **argv)
{
struct rtnl_handle rth;
struct {
struct nlmsghdr n;
struct xfrm_usersa_flush xsf;
} req;
+ char *protop = NULL;
memset(&req, 0, sizeof(req));
req.n.nlmsg_type = XFRM_MSG_FLUSHSA;
req.xsf.proto = IPSEC_PROTO_ANY;
+ while (argc > 0) {
+ if (strcmp(*argv, "proto") == 0) {
+ int ret;
+
+ if (protop)
+ duparg("proto", *argv);
+ protop = *argv;
+
+ NEXT_ARG();
+
+ ret = xfrm_xfrmproto_getbyname(*argv);
+ if (ret < 0)
+ invarg("\"XFRM_PROTO\" is invalid", *argv);
+
+ req.xsf.proto = (__u8)ret;
+ } else
+ invarg("unknown", *argv);
+
+ argc--; argv++;
+ }
+
if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
exit(1);
if (show_stats > 1)
- fprintf(stderr, "Flush all\n");
+ fprintf(stderr, "Flush state proto=%s\n",
+ (req.xsf.proto == IPSEC_PROTO_ANY) ? "any" :
+ strxf_xfrmproto(req.xsf.proto));
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
exit(2);
int do_xfrm_state(int argc, char **argv)
{
if (argc < 1)
- return xfrm_state_list_or_flush(0, NULL, 0);
+ return xfrm_state_list_or_deleteall(0, NULL, 0);
if (matches(*argv, "add") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, 0,
if (matches(*argv, "update") == 0)
return xfrm_state_modify(XFRM_MSG_UPDSA, 0,
argc-1, argv+1);
- if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0)
+ if (matches(*argv, "allocspi") == 0)
+ return xfrm_state_allocspi(argc-1, argv+1);
+ if (matches(*argv, "delete") == 0)
return xfrm_state_get_or_delete(argc-1, argv+1, 1);
+ if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0)
+ return xfrm_state_list_or_deleteall(argc-1, argv+1, 1);
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|| matches(*argv, "lst") == 0)
- return xfrm_state_list_or_flush(argc-1, argv+1, 0);
+ return xfrm_state_list_or_deleteall(argc-1, argv+1, 0);
if (matches(*argv, "get") == 0)
return xfrm_state_get_or_delete(argc-1, argv+1, 0);
- if (matches(*argv, "flush") == 0) {
- if (argc-1 < 1)
- return xfrm_state_flush_all();
- else
- return xfrm_state_list_or_flush(argc-1, argv+1, 1);
- }
+ if (matches(*argv, "flush") == 0)
+ return xfrm_state_flush(argc-1, argv+1);
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv);