This commit was generated by cvs2svn to compensate for changes in r2587,
[iproute2.git] / ip / xfrm_state.c
index b5b6214..3eefaff 100644 (file)
@@ -34,8 +34,8 @@
 #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:
@@ -56,11 +56,14 @@ static void usage(void) __attribute__((noreturn));
 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");
@@ -137,7 +140,7 @@ static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type,
 
                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];
@@ -163,6 +166,22 @@ static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type,
        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;
@@ -232,6 +251,9 @@ static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
                } 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))
@@ -372,6 +394,136 @@ static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
        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)
@@ -400,77 +552,89 @@ static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo)
        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;
 }
@@ -576,7 +740,7 @@ static int xfrm_state_keep(const struct sockaddr_nl *who,
                return 0;
 
        if (xb->offset > xb->size) {
-               fprintf(stderr, "Flush buffer overflow\n");
+               fprintf(stderr, "State buffer overflow\n");
                return -1;
        }
 
@@ -598,7 +762,7 @@ static int xfrm_state_keep(const struct sockaddr_nl *who,
        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;
@@ -643,9 +807,9 @@ static int xfrm_state_list_or_flush(int argc, char **argv, int flush)
        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;
@@ -657,7 +821,7 @@ static int xfrm_state_list_or_flush(int argc, char **argv, int flush)
                        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");
@@ -665,21 +829,21 @@ static int xfrm_state_list_or_flush(int argc, char **argv, int flush)
                        }
 
                        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;
@@ -702,13 +866,14 @@ static int xfrm_state_list_or_flush(int argc, char **argv, int flush)
        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));
 
@@ -717,11 +882,34 @@ static int xfrm_state_flush_all(void)
        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);
@@ -734,7 +922,7 @@ static int xfrm_state_flush_all(void)
 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,
@@ -742,19 +930,19 @@ int do_xfrm_state(int argc, char **argv)
        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);