* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses>.
*/
/*
* based on iproute.c
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 ]\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, "ALGO := { ");
fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT));
fprintf(stderr, "%s", strxf_algotype(XFRMA_ALG_AUTH));
- fprintf(stderr, " } ALGO-NAME ALGO-KEY |\n");
+ fprintf(stderr, " } ALGO-NAME ALGO-KEYMAT |\n");
fprintf(stderr, " %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC));
- fprintf(stderr, " ALGO-NAME ALGO-KEY ALGO-TRUNC-LEN |\n");
+ fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-TRUNC-LEN |\n");
fprintf(stderr, " %s", strxf_algotype(XFRMA_ALG_AEAD));
- fprintf(stderr, " ALGO-NAME ALGO-KEY ALGO-ICV-LEN |\n");
+ 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 | ro | in_trigger | beet\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\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));
#if 0
/* XXX: verifying both name and key is required! */
- fprintf(stderr, "warning: ALGO-NAME/ALGO-KEY will send to kernel promiscuously! (verifying them isn't implemented yet)\n");
+ fprintf(stderr, "warning: ALGO-NAME/ALGO-KEYMAT values will be sent to the kernel promiscuously! (verifying them isn't implemented yet)\n");
#endif
strncpy(alg->alg_name, name, sizeof(alg->alg_name));
/* calculate length of the converted values(real key) */
len = (plen + 1) / 2;
if (len > max)
- invarg("\"ALGO-KEY\" makes buffer overflow\n", key);
+ invarg("ALGO-KEYMAT value makes buffer overflow\n", key);
for (i = - (plen % 2), j = 0; j < len; i += 2, j++) {
char vbuf[3];
vbuf[2] = '\0';
if (get_u8(&val, vbuf, 16))
- invarg("\"ALGO-KEY\" is invalid", key);
+ invarg("ALGO-KEYMAT value is invalid", key);
buf[j] = val;
}
len = slen;
if (len > 0) {
if (len > max)
- invarg("\"ALGO-KEY\" makes buffer overflow\n", key);
+ invarg("ALGO-KEYMAT value makes buffer overflow\n", key);
- strncpy(buf, key, len);
+ memcpy(buf, key, len);
}
}
char **argv = *argvp;
if (get_u32(seq, *argv, 0))
- invarg("\"SEQ\" is invalid", *argv);
+ invarg("SEQ value is invalid", *argv);
*seq = htonl(*seq);
__u8 val = 0;
if (get_u8(&val, *argv, 16))
- invarg("\"FLAG\" is invalid", *argv);
+ invarg("FLAG value is invalid", *argv);
*flags = val;
} else {
while (1) {
*flags |= XFRM_STATE_AF_UNSPEC;
else if (strcmp(*argv, "align4") == 0)
*flags |= XFRM_STATE_ALIGN4;
+ else if (strcmp(*argv, "esn") == 0)
+ *flags |= XFRM_STATE_ESN;
+ else {
+ PREV_ARG(); /* back track */
+ break;
+ }
+
+ if (!NEXT_ARG_OK())
+ break;
+ NEXT_ARG();
+ }
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+
+ return 0;
+}
+
+static int xfrm_state_extra_flag_parse(__u32 *extra_flags, int *argcp, char ***argvp)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+ int len = strlen(*argv);
+
+ if (len > 2 && strncmp(*argv, "0x", 2) == 0) {
+ __u32 val = 0;
+
+ if (get_u32(&val, *argv, 16))
+ invarg("\"EXTRA-FLAG\" is invalid", *argv);
+ *extra_flags = val;
+ } else {
+ while (1) {
+ if (strcmp(*argv, "dont-encap-dscp") == 0)
+ *extra_flags |= XFRM_SA_XFLAG_DONT_ENCAP_DSCP;
else {
PREV_ARG(); /* back track */
break;
{
struct rtnl_handle rth;
struct {
- struct nlmsghdr n;
+ struct nlmsghdr n;
struct xfrm_usersa_info xsinfo;
- char buf[RTA_BUF_SIZE];
+ char buf[RTA_BUF_SIZE];
} req;
struct xfrm_replay_state replay;
+ struct xfrm_replay_state_esn replay_esn;
+ __u32 replay_window = 0;
+ __u32 seq = 0, oseq = 0, seq_hi = 0, oseq_hi = 0;
char *idp = NULL;
char *aeadop = NULL;
char *ealgop = 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;
memset(&req, 0, sizeof(req));
memset(&replay, 0, sizeof(replay));
+ memset(&replay_esn, 0, sizeof(replay_esn));
memset(&ctx, 0, sizeof(ctx));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
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("\"replay-window\" value is invalid", *argv);
+ if (get_u32(&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("\"replay-seq\" value is invalid", *argv);
+ if (get_u32(&seq, *argv, 0))
+ invarg("value after \"replay-seq\" is invalid", *argv);
+ } else if (strcmp(*argv, "replay-seq-hi") == 0) {
+ NEXT_ARG();
+ if (get_u32(&seq_hi, *argv, 0))
+ invarg("value after \"replay-seq-hi\" is invalid", *argv);
} else if (strcmp(*argv, "replay-oseq") == 0) {
NEXT_ARG();
- if (get_u32(&replay.oseq, *argv, 0))
- invarg("\"replay-oseq\" value is invalid", *argv);
+ if (get_u32(&oseq, *argv, 0))
+ invarg("value after \"replay-oseq\" is invalid", *argv);
+ } else if (strcmp(*argv, "replay-oseq-hi") == 0) {
+ NEXT_ARG();
+ if (get_u32(&oseq_hi, *argv, 0))
+ invarg("value after \"replay-oseq-hi\" 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_encap_type_parse(&encap.encap_type, &argc, &argv);
NEXT_ARG();
if (get_u16(&encap.encap_sport, *argv, 0))
- invarg("\"encap\" sport value is invalid", *argv);
+ 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("\"encap\" dport value is invalid", *argv);
+ invarg("DPORT value after \"encap\" is invalid", *argv);
encap.encap_dport = htons(encap.encap_dport);
NEXT_ARG();
get_addr(&oa, *argv, AF_UNSPEC);
get_prefix(&coa, *argv, preferred_family);
if (coa.family == AF_UNSPEC)
- invarg("\"coa\" address family is AF_UNSPEC", *argv);
+ invarg("value after \"coa\" has an unrecognized address family", *argv);
if (coa.bytelen > sizeof(xcoa))
- invarg("\"coa\" address length is too large", *argv);
+ invarg("value after \"coa\" is too large", *argv);
memset(&xcoa, 0, sizeof(xcoa));
memcpy(&xcoa, &coa.data, coa.bytelen);
break;
default:
/* not reached */
- invarg("\"ALGO-TYPE\" is invalid\n", *argv);
+ invarg("ALGO-TYPE value is invalid\n", *argv);
}
if (!NEXT_ARG_OK())
case XFRMA_ALG_AUTH:
case XFRMA_ALG_AUTH_TRUNC:
if (!NEXT_ARG_OK())
- missarg("ALGO-KEY");
+ missarg("ALGO-KEYMAT");
NEXT_ARG();
key = *argv;
break;
missarg("ALGO-ICV-LEN");
NEXT_ARG();
if (get_u32(&icvlen, *argv, 0))
- invarg("\"aead\" ICV length is invalid",
+ invarg("ALGO-ICV-LEN value is invalid",
*argv);
alg.u.aead.alg_icv_len = icvlen;
missarg("ALGO-TRUNC-LEN");
NEXT_ARG();
if (get_u32(&trunclen, *argv, 0))
- invarg("\"auth\" trunc length is invalid",
+ invarg("ALGO-TRUNC-LEN value is invalid",
*argv);
alg.u.auth.alg_trunc_len = trunclen;
argc--; argv++;
}
- if (replay.seq || replay.oseq)
- addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL,
- (void *)&replay, sizeof(replay));
+ if (req.xsinfo.flags & XFRM_STATE_ESN &&
+ replay_window == 0) {
+ fprintf(stderr, "Error: esn flag set without replay-window.\n");
+ exit(-1);
+ }
+
+ if (replay_window > XFRMA_REPLAY_ESN_MAX) {
+ fprintf(stderr,
+ "Error: replay-window (%u) > XFRMA_REPLAY_ESN_MAX (%u).\n",
+ replay_window, XFRMA_REPLAY_ESN_MAX);
+ exit(-1);
+ }
+
+ if (req.xsinfo.flags & XFRM_STATE_ESN ||
+ replay_window > (sizeof(replay.bitmap) * 8)) {
+ replay_esn.seq = seq;
+ replay_esn.oseq = oseq;
+ replay_esn.seq_hi = seq_hi;
+ replay_esn.oseq_hi = oseq_hi;
+ replay_esn.replay_window = replay_window;
+ replay_esn.bmp_len = (replay_window + sizeof(__u32) * 8 - 1) /
+ (sizeof(__u32) * 8);
+ addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_ESN_VAL,
+ &replay_esn, sizeof(replay_esn));
+ } else {
+ if (seq || oseq) {
+ replay.seq = seq;
+ replay.oseq = oseq;
+ addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL,
+ &replay, sizeof(replay));
+ }
+ req.xsinfo.replay_window = replay_window;
+ }
+
+ 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");
+ fprintf(stderr, "Not enough information: ID is required\n");
exit(1);
}
- if (mark.m & mark.v) {
+ if (mark.m) {
int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
(void *)&mark, sizeof(mark));
if (r < 0) {
if (req.xsinfo.family == AF_UNSPEC)
req.xsinfo.family = AF_INET;
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+ if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
exit(2);
rtnl_close(&rth);
{
struct rtnl_handle rth;
struct {
- struct nlmsghdr n;
+ struct nlmsghdr n;
struct xfrm_userspi_info xspi;
- char buf[RTA_BUF_SIZE];
+ char buf[RTA_BUF_SIZE];
} req;
char *idp = NULL;
char *minp = NULL;
NEXT_ARG();
if (get_u32(&req.xspi.min, *argv, 0))
- invarg("\"min\" value is invalid", *argv);
+ invarg("value after \"min\" is invalid", *argv);
} else if (strcmp(*argv, "max") == 0) {
if (maxp)
duparg("max", *argv);
NEXT_ARG();
if (get_u32(&req.xspi.max, *argv, 0))
- invarg("\"max\" value is invalid", *argv);
+ invarg("value after \"max\" is invalid", *argv);
} else {
/* try to assume ID */
if (idp)
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");
+ fprintf(stderr, "\"spi\" is invalid\n");
exit(1);
}
if (preferred_family == AF_UNSPEC)
}
if (!idp) {
- fprintf(stderr, "Not enough information: \"ID\" is required\n");
+ fprintf(stderr, "Not enough information: ID is required\n");
exit(1);
}
exit(1);
}
if (req.xspi.min > req.xspi.max) {
- fprintf(stderr, "\"min\" value is larger than \"max\" value\n");
+ fprintf(stderr, "value after \"min\" is larger than value after \"max\"\n");
exit(1);
}
} else {
req.xspi.info.family = AF_INET;
- if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
+ if (rtnl_talk(&rth, &req.n, res_n, sizeof(res_buf)) < 0)
exit(2);
if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
{
struct rtnl_handle rth;
struct {
- struct nlmsghdr n;
+ struct nlmsghdr n;
struct xfrm_usersa_id xsid;
- char buf[RTA_BUF_SIZE];
+ char buf[RTA_BUF_SIZE];
} req;
struct xfrm_id id;
char *idp = NULL;
req.xsid.family = AF_INET;
if (delete) {
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+ if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
exit(2);
} else {
char buf[NLMSG_BUF_SIZE];
memset(buf, 0, sizeof(buf));
- if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
+ if (rtnl_talk(&rth, &req.n, res_n, sizeof(req)) < 0)
exit(2);
if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
xb.rth = &rth;
for (i = 0; ; i++) {
+ struct {
+ struct nlmsghdr n;
+ char buf[NLMSG_BUF_SIZE];
+ } req = {
+ .n.nlmsg_len = NLMSG_HDRLEN,
+ .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .n.nlmsg_type = XFRM_MSG_GETSA,
+ .n.nlmsg_seq = rth.dump = ++rth.seq,
+ };
+
xb.offset = 0;
xb.nlmsg_count = 0;
if (show_stats > 1)
fprintf(stderr, "Delete-all round = %d\n", i);
- if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) {
+ if (rtnl_send(&rth, (void *)&req, req.n.nlmsg_len) < 0) {
perror("Cannot send dump request");
exit(1);
}
}
} else {
- if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) {
+ struct xfrm_address_filter addrfilter = {
+ .saddr = filter.xsinfo.saddr,
+ .daddr = filter.xsinfo.id.daddr,
+ .family = filter.xsinfo.family,
+ .splen = filter.id_src_mask,
+ .dplen = filter.id_dst_mask,
+ };
+ struct {
+ struct nlmsghdr n;
+ char buf[NLMSG_BUF_SIZE];
+ } req = {
+ .n.nlmsg_len = NLMSG_HDRLEN,
+ .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .n.nlmsg_type = XFRM_MSG_GETSA,
+ .n.nlmsg_seq = rth.dump = ++rth.seq,
+ };
+
+ if (filter.xsinfo.id.proto)
+ addattr8(&req.n, sizeof(req), XFRMA_PROTO,
+ filter.xsinfo.id.proto);
+ addattr_l(&req.n, sizeof(req), XFRMA_ADDRESS_FILTER,
+ &addrfilter, sizeof(addrfilter));
+
+ if (rtnl_send(&rth, (void *)&req, req.n.nlmsg_len) < 0) {
perror("Cannot send dump request");
exit(1);
}
fprintf(fp,"BAD SAD length returned\n");
return -1;
}
-
+
si = RTA_DATA(tb[XFRMA_SAD_HINFO]);
fprintf(fp," (buckets ");
fprintf(fp,"count %d", si->sadhcnt);
if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
exit(1);
- if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
+ if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
exit(2);
print_sadinfo(&req.n, (void*)stdout);
ret = xfrm_xfrmproto_getbyname(*argv);
if (ret < 0)
- invarg("\"XFRM-PROTO\" is invalid", *argv);
+ invarg("XFRM-PROTO value is invalid", *argv);
req.xsf.proto = (__u8)ret;
} else
exit(1);
if (show_stats > 1)
- fprintf(stderr, "Flush state proto=%s\n",
+ fprintf(stderr, "Flush state with XFRM-PROTO value \"%s\"\n",
strxf_xfrmproto(req.xsf.proto));
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+ if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
exit(2);
rtnl_close(&rth);