Esempio n. 1
0
static int xfrm_acquire_print(const struct sockaddr_nl *who,
			      struct nlmsghdr *n, void *arg)
{
	FILE *fp = (FILE*)arg;
	struct xfrm_user_acquire *xacq = NLMSG_DATA(n);
	int len = n->nlmsg_len;
	struct rtattr * tb[XFRMA_MAX+1];
	__u16 family;

	len -= NLMSG_LENGTH(sizeof(*xacq));
	if (len < 0) {
		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
		return -1;
	}

	parse_rtattr(tb, XFRMA_MAX, XFRMACQ_RTA(xacq), len);

	family = xacq->sel.family;
	if (family == AF_UNSPEC)
		family = xacq->policy.sel.family;
	if (family == AF_UNSPEC)
		family = preferred_family;

	fprintf(fp, "acquire ");

	fprintf(fp, "proto %s ", strxf_xfrmproto(xacq->id.proto));
	if (show_stats > 0 || xacq->id.spi) {
		__u32 spi = ntohl(xacq->id.spi);
		fprintf(fp, "spi 0x%08x", spi);
		if (show_stats > 0)
			fprintf(fp, "(%u)", spi);
		fprintf(fp, " ");
	}
	fprintf(fp, "%s", _SL_);

	xfrm_selector_print(&xacq->sel, family, fp, "  sel ");

	xfrm_policy_info_print(&xacq->policy, tb, fp, "    ", "  policy ");

	if (show_stats > 0)
		fprintf(fp, "  seq 0x%08u ", xacq->seq);
	if (show_stats > 0) {
		fprintf(fp, "%s-mask %s ",
			strxf_algotype(XFRMA_ALG_CRYPT),
			strxf_mask32(xacq->ealgos));
		fprintf(fp, "%s-mask %s ",
			strxf_algotype(XFRMA_ALG_AUTH),
			strxf_mask32(xacq->aalgos));
		fprintf(fp, "%s-mask %s",
			strxf_algotype(XFRMA_ALG_COMP),
			strxf_mask32(xacq->calgos));
	}
	fprintf(fp, "%s", _SL_);

	if (oneline)
		fprintf(fp, "\n");
	fflush(fp);

	return 0;
}
Esempio n. 2
0
static void usage(void)
{
	fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ XFRM_OPT ] [ mode MODE ]\n");
	fprintf(stderr, "        [ reqid REQID ] [ seq SEQ ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n");
	fprintf(stderr, "        [ encap ENCAP ] [ sel SELECTOR ] [ replay-seq SEQ ]\n");
	fprintf(stderr, "        [ replay-oseq SEQ ] [ 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 { 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, "Usage: ip xfrm state count \n");

	fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n");
	//fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n");
	fprintf(stderr, "XFRM_PROTO := [ ");
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP));
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH));
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_COMP));
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING));
	fprintf(stderr, "%s ", strxf_xfrmproto(IPPROTO_DSTOPTS));
	fprintf(stderr, "]\n");

	//fprintf(stderr, "SPI - security parameter index(default=0)\n");

 	fprintf(stderr, "MODE := [ transport | tunnel | ro | beet ](default=transport)\n");
 	//fprintf(stderr, "REQID - number(default=0)\n");

	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
	fprintf(stderr, "FLAG := [ noecn | decap-dscp | nopmtudisc | wildrecv ]\n");

        fprintf(stderr, "ENCAP := ENCAP-TYPE SPORT DPORT OADDR\n");
        fprintf(stderr, "ENCAP-TYPE := espinudp | espinudp-nonike\n");

	fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] | [ ALGO ]\n");
	fprintf(stderr, "ALGO := ALGO_TYPE ALGO_NAME ALGO_KEY "
			"[ ALGO_ICV_LEN ]\n");
	fprintf(stderr, "ALGO_TYPE := [ ");
	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AEAD));
	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT));
	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AUTH));
	fprintf(stderr, "%s ", strxf_algotype(XFRMA_ALG_COMP));
	fprintf(stderr, "]\n");

	//fprintf(stderr, "ALGO_NAME - algorithm name\n");
	//fprintf(stderr, "ALGO_KEY - algorithm key\n");

	fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ UPSPEC ] [ dev DEV ]\n");

	fprintf(stderr, "UPSPEC := proto PROTO [ [ sport PORT ] [ dport PORT ] |\n");
	fprintf(stderr, "                        [ type NUMBER ] [ code NUMBER ] ]\n");


	//fprintf(stderr, "DEV - device name(default=none)\n");
	fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n");
	fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n");
	fprintf(stderr, "         [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] COUNT ]\n");
	exit(-1);
}
Esempio n. 3
0
static void usage(void)
{
	fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n");
	fprintf(stderr, "        [ mark MARK [ mask MASK ] ] [ reqid REQID ] [ seq SEQ ]\n");
	fprintf(stderr, "        [ replay-window SIZE ] [ replay-seq SEQ ] [ replay-oseq SEQ ]\n");
	fprintf(stderr, "        [ replay-seq-hi SEQ ] [ replay-oseq-hi SEQ ]\n");
	fprintf(stderr, "        [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n");
	fprintf(stderr, "        [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n");
	fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n");
	fprintf(stderr, "        [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n");
	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 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");
	fprintf(stderr, "XFRM-PROTO := ");
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP));
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH));
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_COMP));
	fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING));
	fprintf(stderr, "%s\n", strxf_xfrmproto(IPPROTO_DSTOPTS));
	fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] ALGO\n");
	fprintf(stderr, "ALGO := { ");
	fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT));
	fprintf(stderr, "%s", strxf_algotype(XFRMA_ALG_AUTH));
	fprintf(stderr, " } ALGO-NAME ALGO-KEYMAT |\n");
	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC));
	fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-TRUNC-LEN |\n");
	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AEAD));
	fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-ICV-LEN |\n");
	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_COMP));
	fprintf(stderr, " ALGO-NAME\n");
	fprintf(stderr, "MODE := transport | tunnel | beet | ro | in_trigger\n");
	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
	fprintf(stderr, "FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4 | esn\n");
	fprintf(stderr, "EXTRA-FLAG-LIST := [ EXTRA-FLAG-LIST ] EXTRA-FLAG\n");
	fprintf(stderr, "EXTRA-FLAG := dont-encap-dscp\n");
	fprintf(stderr, "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n");
	fprintf(stderr, "UPSPEC := proto { { ");
	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_TCP));
	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_UDP));
	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_SCTP));
	fprintf(stderr, "%s", strxf_proto(IPPROTO_DCCP));
	fprintf(stderr, " } [ sport PORT ] [ dport PORT ] |\n");
	fprintf(stderr, "                  { ");
	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMP));
	fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMPV6));
	fprintf(stderr, "%s", strxf_proto(IPPROTO_MH));
	fprintf(stderr, " } [ type NUMBER ] [ code NUMBER ] |\n");
	fprintf(stderr, "                  %s", strxf_proto(IPPROTO_GRE));
	fprintf(stderr, " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n");
	fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n");
	fprintf(stderr, "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n");
	fprintf(stderr, "         { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n");
        fprintf(stderr, "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n");

	exit(-1);
}
Esempio n. 4
0
static void xfrm_algo_print(struct xfrm_algo *algo, int type, int len,
			    FILE *fp, const char *prefix)
{
	int keylen;
	int i;

	if (prefix)
		fprintf(fp, prefix);

	fprintf(fp, "%s ", strxf_algotype(type));

	if (len < sizeof(*algo)) {
		fprintf(fp, "(ERROR truncated)");
		goto fin;
	}
	len -= sizeof(*algo);

	fprintf(fp, "%s ", algo->alg_name);

	keylen = algo->alg_key_len / 8;
	if (len < keylen) {
		fprintf(fp, "(ERROR truncated)");
		goto fin;
	}

	fprintf(fp, "0x");
	for (i = 0; i < keylen; i ++)
		fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]);

	if (show_stats > 0)
		fprintf(fp, " (%d bits)", algo->alg_key_len);

 fin:
	fprintf(fp, "%s", _SL_);
}
static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int len,
			    __u16 family, FILE *fp, const char *prefix)
{
	int ntmpls = len / sizeof(struct xfrm_user_tmpl);
	int i;

	if (ntmpls <= 0) {
		if (prefix)
			fputs(prefix, fp);
		fprintf(fp, "(ERROR \"tmpl\" truncated)");
		fprintf(fp, "%s", _SL_);
		return;
	}

	for (i = 0; i < ntmpls; i++) {
		struct xfrm_user_tmpl *tmpl = &tmpls[i];

		if (prefix)
			fputs(prefix, fp);

		xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode,
				   tmpl->reqid, tmpl->family, 0, fp, prefix, "tmpl ");

		if (show_stats > 0 || tmpl->optional) {
			if (prefix)
				fputs(prefix, fp);
			fprintf(fp, "\t");
			switch (tmpl->optional) {
			case 0:
				if (show_stats > 0)
					fprintf(fp, "level required ");
				break;
			case 1:
				fprintf(fp, "level use ");
				break;
			default:
				fprintf(fp, "level %u ", tmpl->optional);
				break;
			}

			if (show_stats > 0)
				fprintf(fp, "share %s ", strxf_share(tmpl->share));

			fprintf(fp, "%s", _SL_);
		}

		if (show_stats > 0) {
			if (prefix)
				fputs(prefix, fp);
			fprintf(fp, "\t");
			fprintf(fp, "%s-mask %s ",
				strxf_algotype(XFRMA_ALG_CRYPT),
				strxf_mask32(tmpl->ealgos));
			fprintf(fp, "%s-mask %s ",
				strxf_algotype(XFRMA_ALG_AUTH),
				strxf_mask32(tmpl->aalgos));
			fprintf(fp, "%s-mask %s",
				strxf_algotype(XFRMA_ALG_COMP),
				strxf_mask32(tmpl->calgos));

			fprintf(fp, "%s", _SL_);
		}
	}
}
Esempio n. 6
0
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
{
    struct rtnl_handle rth;
    struct {
        struct nlmsghdr	n;
        struct xfrm_usersa_info xsinfo;
        char  			buf[RTA_BUF_SIZE];
    } req;
    struct xfrm_replay_state replay;
    char *idp = NULL;
    char *aeadop = NULL;
    char *ealgop = NULL;
    char *aalgop = NULL;
    char *calgop = NULL;
    char *coap = NULL;
    char *sctxp = NULL;
    __u32 extra_flags = 0;
    struct xfrm_mark mark = {0, 0};
    struct {
        struct xfrm_user_sec_ctx sctx;
        char    str[CTX_BUF_SIZE];
    } ctx;

    memset(&req, 0, sizeof(req));
    memset(&replay, 0, sizeof(replay));
    memset(&ctx, 0, sizeof(ctx));

    req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
    req.n.nlmsg_flags = NLM_F_REQUEST|flags;
    req.n.nlmsg_type = cmd;
    req.xsinfo.family = preferred_family;

    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;

    while (argc > 0) {
        if (strcmp(*argv, "mode") == 0) {
            NEXT_ARG();
            xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv);
        } else if (strcmp(*argv, "mark") == 0) {
            xfrm_parse_mark(&mark, &argc, &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))
                invarg("value after \"replay-window\" is invalid", *argv);
        } else if (strcmp(*argv, "replay-seq") == 0) {
            NEXT_ARG();
            if (get_u32(&replay.seq, *argv, 0))
                invarg("value after \"replay-seq\" is invalid", *argv);
        } else if (strcmp(*argv, "replay-oseq") == 0) {
            NEXT_ARG();
            if (get_u32(&replay.oseq, *argv, 0))
                invarg("value after \"replay-oseq\" is invalid", *argv);
        } else if (strcmp(*argv, "flag") == 0) {
            NEXT_ARG();
            xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv);
        } else if (strcmp(*argv, "extra-flag") == 0) {
            NEXT_ARG();
            xfrm_state_extra_flag_parse(&extra_flags, &argc, &argv);
        } else if (strcmp(*argv, "sel") == 0) {
            NEXT_ARG();
            preferred_family = AF_UNSPEC;
            xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv);
            preferred_family = req.xsinfo.sel.family;
        } else if (strcmp(*argv, "limit") == 0) {
            NEXT_ARG();
            xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv);
        } else if (strcmp(*argv, "encap") == 0) {
            struct xfrm_encap_tmpl encap;
            inet_prefix oa;
            NEXT_ARG();
            xfrm_encap_type_parse(&encap.encap_type, &argc, &argv);
            NEXT_ARG();
            if (get_u16(&encap.encap_sport, *argv, 0))
                invarg("SPORT value after \"encap\" is invalid", *argv);
            encap.encap_sport = htons(encap.encap_sport);
            NEXT_ARG();
            if (get_u16(&encap.encap_dport, *argv, 0))
                invarg("DPORT value after \"encap\" is invalid", *argv);
            encap.encap_dport = htons(encap.encap_dport);
            NEXT_ARG();
            get_addr(&oa, *argv, AF_UNSPEC);
            memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa));
            addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP,
                      (void *)&encap, sizeof(encap));
        } else if (strcmp(*argv, "coa") == 0) {
            inet_prefix coa;
            xfrm_address_t xcoa;

            if (coap)
                duparg("coa", *argv);
            coap = *argv;

            NEXT_ARG();

            get_prefix(&coa, *argv, preferred_family);
            if (coa.family == AF_UNSPEC)
                invarg("value after \"coa\" has an unrecognized address family", *argv);
            if (coa.bytelen > sizeof(xcoa))
                invarg("value after \"coa\" is too large", *argv);

            memset(&xcoa, 0, sizeof(xcoa));
            memcpy(&xcoa, &coa.data, coa.bytelen);

            addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR,
                      (void *)&xcoa, sizeof(xcoa));
        } else if (strcmp(*argv, "ctx") == 0) {
            char *context;

            if (sctxp)
                duparg("ctx", *argv);
            sctxp = *argv;

            NEXT_ARG();
            context = *argv;

            xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
            addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX,
                      (void *)&ctx, ctx.sctx.len);
        } else {
            /* try to assume ALGO */
            int type = xfrm_algotype_getbyname(*argv);
            switch (type) {
            case XFRMA_ALG_AEAD:
            case XFRMA_ALG_CRYPT:
            case XFRMA_ALG_AUTH:
            case XFRMA_ALG_AUTH_TRUNC:
            case XFRMA_ALG_COMP:
            {
                /* ALGO */
                struct {
                    union {
                        struct xfrm_algo alg;
                        struct xfrm_algo_aead aead;
                        struct xfrm_algo_auth auth;
                    } u;
                    char buf[XFRM_ALGO_KEY_BUF_SIZE];
                } alg = {};
                int len;
                __u32 icvlen, trunclen;
                char *name;
                char *key = "";
                char *buf;

                switch (type) {
                case XFRMA_ALG_AEAD:
                    if (ealgop || aalgop || aeadop)
                        duparg("ALGO-TYPE", *argv);
                    aeadop = *argv;
                    break;
                case XFRMA_ALG_CRYPT:
                    if (ealgop || aeadop)
                        duparg("ALGO-TYPE", *argv);
                    ealgop = *argv;
                    break;
                case XFRMA_ALG_AUTH:
                case XFRMA_ALG_AUTH_TRUNC:
                    if (aalgop || aeadop)
                        duparg("ALGO-TYPE", *argv);
                    aalgop = *argv;
                    break;
                case XFRMA_ALG_COMP:
                    if (calgop)
                        duparg("ALGO-TYPE", *argv);
                    calgop = *argv;
                    break;
                default:
                    /* not reached */
                    invarg("ALGO-TYPE value is invalid\n", *argv);
                }

                if (!NEXT_ARG_OK())
                    missarg("ALGO-NAME");
                NEXT_ARG();
                name = *argv;

                switch (type) {
                case XFRMA_ALG_AEAD:
                case XFRMA_ALG_CRYPT:
                case XFRMA_ALG_AUTH:
                case XFRMA_ALG_AUTH_TRUNC:
                    if (!NEXT_ARG_OK())
                        missarg("ALGO-KEYMAT");
                    NEXT_ARG();
                    key = *argv;
                    break;
                }

                buf = alg.u.alg.alg_key;
                len = sizeof(alg.u.alg);

                switch (type) {
                case XFRMA_ALG_AEAD:
                    if (!NEXT_ARG_OK())
                        missarg("ALGO-ICV-LEN");
                    NEXT_ARG();
                    if (get_u32(&icvlen, *argv, 0))
                        invarg("ALGO-ICV-LEN value is invalid",
                               *argv);
                    alg.u.aead.alg_icv_len = icvlen;

                    buf = alg.u.aead.alg_key;
                    len = sizeof(alg.u.aead);
                    break;
                case XFRMA_ALG_AUTH_TRUNC:
                    if (!NEXT_ARG_OK())
                        missarg("ALGO-TRUNC-LEN");
                    NEXT_ARG();
                    if (get_u32(&trunclen, *argv, 0))
                        invarg("ALGO-TRUNC-LEN value is invalid",
                               *argv);
                    alg.u.auth.alg_trunc_len = trunclen;

                    buf = alg.u.auth.alg_key;
                    len = sizeof(alg.u.auth);
                    break;
                }

                xfrm_algo_parse((void *)&alg, type, name, key,
                                buf, sizeof(alg.buf));
                len += alg.u.alg.alg_key_len;

                addattr_l(&req.n, sizeof(req.buf), type,
                          (void *)&alg, len);
                break;
            }
            default:
                /* try to assume ID */
                if (idp)
                    invarg("unknown", *argv);
                idp = *argv;

                /* ID */
                xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id,
                              &req.xsinfo.family, 0, &argc, &argv);
                if (preferred_family == AF_UNSPEC)
                    preferred_family = req.xsinfo.family;
            }
        }
        argc--;
        argv++;
    }

    if (replay.seq || replay.oseq)
        addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL,
                  (void *)&replay, sizeof(replay));

    if (extra_flags)
        addattr32(&req.n, sizeof(req.buf), XFRMA_SA_EXTRA_FLAGS,
                  extra_flags);

    if (!idp) {
        fprintf(stderr, "Not enough information: ID is required\n");
        exit(1);
    }

    if (mark.m) {
        int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
                          (void *)&mark, sizeof(mark));
        if (r < 0) {
            fprintf(stderr, "XFRMA_MARK failed\n");
            exit(1);
        }
    }

    if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
        switch (req.xsinfo.mode) {
        case XFRM_MODE_TRANSPORT:
        case XFRM_MODE_TUNNEL:
            break;
        case XFRM_MODE_BEET:
            if (req.xsinfo.id.proto == IPPROTO_ESP)
                break;
        default:
            fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n",
                    strxf_xfrmproto(req.xsinfo.id.proto));
            exit(1);
        }

        switch (req.xsinfo.id.proto) {
        case IPPROTO_ESP:
            if (calgop) {
                fprintf(stderr, "ALGO-TYPE value \"%s\" is invalid with XFRM-PROTO value \"%s\"\n",
                        strxf_algotype(XFRMA_ALG_COMP),
                        strxf_xfrmproto(req.xsinfo.id.proto));
                exit(1);
            }
            if (!ealgop && !aeadop) {
                fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n",
                        strxf_algotype(XFRMA_ALG_CRYPT),
                        strxf_algotype(XFRMA_ALG_AEAD),
                        strxf_xfrmproto(req.xsinfo.id.proto));
                exit(1);
            }
            break;
        case IPPROTO_AH:
            if (ealgop || aeadop || calgop) {
                fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n",
                        strxf_algotype(XFRMA_ALG_CRYPT),
                        strxf_algotype(XFRMA_ALG_AEAD),
                        strxf_algotype(XFRMA_ALG_COMP),
                        strxf_xfrmproto(req.xsinfo.id.proto));
                exit(1);
            }
            if (!aalgop) {
                fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n",
                        strxf_algotype(XFRMA_ALG_AUTH),
                        strxf_algotype(XFRMA_ALG_AUTH_TRUNC),
                        strxf_xfrmproto(req.xsinfo.id.proto));
                exit(1);
            }
            break;
        case IPPROTO_COMP:
            if (ealgop || aalgop || aeadop) {
                fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n",
                        strxf_algotype(XFRMA_ALG_CRYPT),
                        strxf_algotype(XFRMA_ALG_AUTH),
                        strxf_algotype(XFRMA_ALG_AUTH_TRUNC),
                        strxf_algotype(XFRMA_ALG_AEAD),
                        strxf_xfrmproto(req.xsinfo.id.proto));
                exit(1);
            }
            if (!calgop) {
                fprintf(stderr, "ALGO-TYPE value \"%s\" is required with XFRM-PROTO value \"%s\"\n",
                        strxf_algotype(XFRMA_ALG_COMP),
                        strxf_xfrmproto(req.xsinfo.id.proto));
                exit(1);
            }
            break;
        }
    } else {
        if (ealgop || aalgop || aeadop || calgop) {
            fprintf(stderr, "ALGO is invalid with XFRM-PROTO value \"%s\"\n",
                    strxf_xfrmproto(req.xsinfo.id.proto));
            exit(1);
        }
    }

    if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
        switch (req.xsinfo.mode) {
        case XFRM_MODE_ROUTEOPTIMIZATION:
        case XFRM_MODE_IN_TRIGGER:
            break;
        case 0:
            fprintf(stderr, "\"mode\" is required with XFRM-PROTO value \"%s\"\n",
                    strxf_xfrmproto(req.xsinfo.id.proto));
            exit(1);
        default:
            fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n",
                    strxf_xfrmproto(req.xsinfo.id.proto));
            exit(1);
        }

        if (!coap) {
            fprintf(stderr, "\"coa\" is required with XFRM-PROTO value \"%s\"\n",
                    strxf_xfrmproto(req.xsinfo.id.proto));
            exit(1);
        }
    } else {
        if (coap) {
            fprintf(stderr, "\"coa\" is invalid with XFRM-PROTO value \"%s\"\n",
                    strxf_xfrmproto(req.xsinfo.id.proto));
            exit(1);
        }
    }

    if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
        exit(1);

    if (req.xsinfo.family == AF_UNSPEC)
        req.xsinfo.family = AF_INET;

    if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
        exit(2);

    rtnl_close(&rth);

    return 0;
}