]>
Commit | Line | Data |
---|---|---|
c7699875 | 1 | /* $USAGI: $ */ |
2 | ||
3 | /* | |
4 | * Copyright (C)2004 USAGI/WIDE Project | |
ae665a52 | 5 | * |
c7699875 | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
ae665a52 | 10 | * |
c7699875 | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
ae665a52 | 15 | * |
c7699875 | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | /* | |
21 | * based on iproute.c | |
22 | */ | |
23 | /* | |
24 | * Authors: | |
25 | * Masahide NAKAMURA @USAGI | |
26 | */ | |
27 | ||
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <netdb.h> | |
32 | #include <linux/xfrm.h> | |
33 | #include "utils.h" | |
34 | #include "xfrm.h" | |
35 | #include "ip_common.h" | |
36 | ||
9bec1a43 SH |
37 | //#define NLMSG_DELETEALL_BUF_SIZE (4096-512) |
38 | #define NLMSG_DELETEALL_BUF_SIZE 8192 | |
c7699875 | 39 | |
40 | /* | |
41 | * Receiving buffer defines: | |
42 | * nlmsg | |
43 | * data = struct xfrm_usersa_info | |
44 | * rtattr | |
45 | * rtattr | |
2534613e | 46 | * ... (max count of rtattr is XFRM_MAX+1 |
c7699875 | 47 | * |
48 | * each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t | |
49 | */ | |
50 | #define NLMSG_BUF_SIZE 4096 | |
51 | #define RTA_BUF_SIZE 2048 | |
52 | #define XFRM_ALGO_KEY_BUF_SIZE 512 | |
0c7a5945 | 53 | #define CTX_BUF_SIZE 256 |
c7699875 | 54 | |
55 | static void usage(void) __attribute__((noreturn)); | |
56 | ||
57 | static void usage(void) | |
58 | { | |
cbec0219 DW |
59 | fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n"); |
60 | fprintf(stderr, " [ mark MARK [ mask MASK ] ] [ reqid REQID ] [ seq SEQ ]\n"); | |
61 | fprintf(stderr, " [ replay-window SIZE ] [ replay-seq SEQ ] [ replay-oseq SEQ ]\n"); | |
62 | fprintf(stderr, " [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n"); | |
dc8867d0 | 63 | fprintf(stderr, " [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n"); |
cbec0219 DW |
64 | fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n"); |
65 | fprintf(stderr, " [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n"); | |
66 | fprintf(stderr, "Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n"); | |
9bec1a43 | 67 | fprintf(stderr, "Usage: ip xfrm state { deleteall | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n"); |
c1fa2253 | 68 | fprintf(stderr, " [ flag FLAG-LIST ]\n"); |
cbec0219 DW |
69 | fprintf(stderr, "Usage: ip xfrm state flush [ proto XFRM-PROTO ]\n"); |
70 | fprintf(stderr, "Usage: ip xfrm state count\n"); | |
71 | fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n"); | |
72 | fprintf(stderr, "XFRM-PROTO := "); | |
29aa4dd7 | 73 | fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP)); |
74 | fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH)); | |
7ea4f5d3 MN |
75 | fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_COMP)); |
76 | fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ROUTING)); | |
cbec0219 DW |
77 | fprintf(stderr, "%s\n", strxf_xfrmproto(IPPROTO_DSTOPTS)); |
78 | fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] ALGO\n"); | |
79 | fprintf(stderr, "ALGO := { "); | |
7809c616 | 80 | fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT)); |
f3b9aa3d | 81 | fprintf(stderr, "%s", strxf_algotype(XFRMA_ALG_AUTH)); |
29665f92 | 82 | fprintf(stderr, " } ALGO-NAME ALGO-KEYMAT |\n"); |
f3b9aa3d | 83 | fprintf(stderr, " %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC)); |
29665f92 | 84 | fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-TRUNC-LEN |\n"); |
cbec0219 | 85 | fprintf(stderr, " %s", strxf_algotype(XFRMA_ALG_AEAD)); |
29665f92 | 86 | fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-ICV-LEN |\n"); |
f3b9aa3d DW |
87 | fprintf(stderr, " %s", strxf_algotype(XFRMA_ALG_COMP)); |
88 | fprintf(stderr, " ALGO-NAME\n"); | |
e8740e42 | 89 | fprintf(stderr, "MODE := transport | tunnel | beet | ro | in_trigger\n"); |
cbec0219 DW |
90 | fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); |
91 | fprintf(stderr, "FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4\n"); | |
dc8867d0 ND |
92 | fprintf(stderr, "EXTRA-FLAG-LIST := [ EXTRA-FLAG-LIST ] EXTRA-FLAG\n"); |
93 | fprintf(stderr, "EXTRA-FLAG := dont-encap-dscp\n"); | |
cbec0219 DW |
94 | fprintf(stderr, "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n"); |
95 | fprintf(stderr, "UPSPEC := proto { { "); | |
96 | fprintf(stderr, "%s | ", strxf_proto(IPPROTO_TCP)); | |
97 | fprintf(stderr, "%s | ", strxf_proto(IPPROTO_UDP)); | |
98 | fprintf(stderr, "%s | ", strxf_proto(IPPROTO_SCTP)); | |
99 | fprintf(stderr, "%s", strxf_proto(IPPROTO_DCCP)); | |
100 | fprintf(stderr, " } [ sport PORT ] [ dport PORT ] |\n"); | |
101 | fprintf(stderr, " { "); | |
102 | fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMP)); | |
103 | fprintf(stderr, "%s | ", strxf_proto(IPPROTO_ICMPV6)); | |
104 | fprintf(stderr, "%s", strxf_proto(IPPROTO_MH)); | |
105 | fprintf(stderr, " } [ type NUMBER ] [ code NUMBER ] |\n"); | |
106 | fprintf(stderr, " %s", strxf_proto(IPPROTO_GRE)); | |
107 | fprintf(stderr, " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n"); | |
108 | fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n"); | |
109 | fprintf(stderr, "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n"); | |
110 | fprintf(stderr, " { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n"); | |
111 | fprintf(stderr, "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n"); | |
c7699875 | 112 | |
c7699875 | 113 | exit(-1); |
114 | } | |
115 | ||
116 | static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type, | |
1758a81f | 117 | char *name, char *key, char *buf, int max) |
c7699875 | 118 | { |
119 | int len; | |
7809c616 | 120 | int slen = strlen(key); |
c7699875 | 121 | |
eaa34ee3 | 122 | #if 0 |
c7699875 | 123 | /* XXX: verifying both name and key is required! */ |
29665f92 | 124 | fprintf(stderr, "warning: ALGO-NAME/ALGO-KEYMAT values will be sent to the kernel promiscuously! (verifying them isn't implemented yet)\n"); |
c7699875 | 125 | #endif |
126 | ||
127 | strncpy(alg->alg_name, name, sizeof(alg->alg_name)); | |
128 | ||
7809c616 | 129 | if (slen > 2 && strncmp(key, "0x", 2) == 0) { |
54f7328a | 130 | /* split two chars "0x" from the top */ |
131 | char *p = key + 2; | |
132 | int plen = slen - 2; | |
133 | int i; | |
134 | int j; | |
135 | ||
136 | /* Converting hexadecimal numbered string into real key; | |
137 | * Convert each two chars into one char(value). If number | |
138 | * of the length is odd, add zero on the top for rounding. | |
c7699875 | 139 | */ |
7809c616 | 140 | |
54f7328a | 141 | /* calculate length of the converted values(real key) */ |
142 | len = (plen + 1) / 2; | |
143 | if (len > max) | |
29665f92 | 144 | invarg("ALGO-KEYMAT value makes buffer overflow\n", key); |
c7699875 | 145 | |
54f7328a | 146 | for (i = - (plen % 2), j = 0; j < len; i += 2, j++) { |
147 | char vbuf[3]; | |
737f15f6 | 148 | __u8 val; |
c7699875 | 149 | |
54f7328a | 150 | vbuf[0] = i >= 0 ? p[i] : '0'; |
151 | vbuf[1] = p[i + 1]; | |
152 | vbuf[2] = '\0'; | |
c7699875 | 153 | |
54f7328a | 154 | if (get_u8(&val, vbuf, 16)) |
29665f92 | 155 | invarg("ALGO-KEYMAT value is invalid", key); |
7809c616 | 156 | |
1758a81f | 157 | buf[j] = val; |
c7699875 | 158 | } |
c7699875 | 159 | } else { |
7809c616 | 160 | len = slen; |
c7699875 | 161 | if (len > 0) { |
162 | if (len > max) | |
29665f92 | 163 | invarg("ALGO-KEYMAT value makes buffer overflow\n", key); |
c7699875 | 164 | |
99500b56 | 165 | memcpy(buf, key, len); |
c7699875 | 166 | } |
167 | } | |
168 | ||
169 | alg->alg_key_len = len * 8; | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
fb7399b2 | 174 | static int xfrm_seq_parse(__u32 *seq, int *argcp, char ***argvp) |
175 | { | |
176 | int argc = *argcp; | |
177 | char **argv = *argvp; | |
178 | ||
179 | if (get_u32(seq, *argv, 0)) | |
e8740e42 | 180 | invarg("SEQ value is invalid", *argv); |
fb7399b2 | 181 | |
182 | *seq = htonl(*seq); | |
183 | ||
184 | *argcp = argc; | |
185 | *argvp = argv; | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
c7699875 | 190 | static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) |
191 | { | |
192 | int argc = *argcp; | |
193 | char **argv = *argvp; | |
9e566a46 | 194 | int len = strlen(*argv); |
195 | ||
196 | if (len > 2 && strncmp(*argv, "0x", 2) == 0) { | |
197 | __u8 val = 0; | |
c7699875 | 198 | |
9e566a46 | 199 | if (get_u8(&val, *argv, 16)) |
e8740e42 | 200 | invarg("FLAG value is invalid", *argv); |
9e566a46 | 201 | *flags = val; |
202 | } else { | |
eaa34ee3 | 203 | while (1) { |
204 | if (strcmp(*argv, "noecn") == 0) | |
205 | *flags |= XFRM_STATE_NOECN; | |
206 | else if (strcmp(*argv, "decap-dscp") == 0) | |
207 | *flags |= XFRM_STATE_DECAP_DSCP; | |
c1fa2253 MN |
208 | else if (strcmp(*argv, "nopmtudisc") == 0) |
209 | *flags |= XFRM_STATE_NOPMTUDISC; | |
7ea4f5d3 MN |
210 | else if (strcmp(*argv, "wildrecv") == 0) |
211 | *flags |= XFRM_STATE_WILDRECV; | |
15bb82c6 AB |
212 | else if (strcmp(*argv, "icmp") == 0) |
213 | *flags |= XFRM_STATE_ICMP; | |
214 | else if (strcmp(*argv, "af-unspec") == 0) | |
215 | *flags |= XFRM_STATE_AF_UNSPEC; | |
98f5519c ND |
216 | else if (strcmp(*argv, "align4") == 0) |
217 | *flags |= XFRM_STATE_ALIGN4; | |
eaa34ee3 | 218 | else { |
219 | PREV_ARG(); /* back track */ | |
220 | break; | |
221 | } | |
222 | ||
223 | if (!NEXT_ARG_OK()) | |
224 | break; | |
225 | NEXT_ARG(); | |
226 | } | |
9e566a46 | 227 | } |
c7699875 | 228 | |
c7699875 | 229 | *argcp = argc; |
230 | *argvp = argv; | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
dc8867d0 ND |
235 | static int xfrm_state_extra_flag_parse(__u32 *extra_flags, int *argcp, char ***argvp) |
236 | { | |
237 | int argc = *argcp; | |
238 | char **argv = *argvp; | |
239 | int len = strlen(*argv); | |
240 | ||
241 | if (len > 2 && strncmp(*argv, "0x", 2) == 0) { | |
242 | __u32 val = 0; | |
243 | ||
244 | if (get_u32(&val, *argv, 16)) | |
245 | invarg("\"EXTRA-FLAG\" is invalid", *argv); | |
246 | *extra_flags = val; | |
247 | } else { | |
248 | while (1) { | |
249 | if (strcmp(*argv, "dont-encap-dscp") == 0) | |
250 | *extra_flags |= XFRM_SA_XFLAG_DONT_ENCAP_DSCP; | |
251 | else { | |
252 | PREV_ARG(); /* back track */ | |
253 | break; | |
254 | } | |
255 | ||
256 | if (!NEXT_ARG_OK()) | |
257 | break; | |
258 | NEXT_ARG(); | |
259 | } | |
260 | } | |
261 | ||
262 | *argcp = argc; | |
263 | *argvp = argv; | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
c7699875 | 268 | static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) |
269 | { | |
270 | struct rtnl_handle rth; | |
271 | struct { | |
272 | struct nlmsghdr n; | |
273 | struct xfrm_usersa_info xsinfo; | |
274 | char buf[RTA_BUF_SIZE]; | |
275 | } req; | |
de95ae7c | 276 | struct xfrm_replay_state replay; |
c7699875 | 277 | char *idp = NULL; |
1758a81f | 278 | char *aeadop = NULL; |
c7699875 | 279 | char *ealgop = NULL; |
280 | char *aalgop = NULL; | |
281 | char *calgop = NULL; | |
7ea4f5d3 | 282 | char *coap = NULL; |
0c7a5945 | 283 | char *sctxp = NULL; |
dc8867d0 | 284 | __u32 extra_flags = 0; |
c90cda94 | 285 | struct xfrm_mark mark = {0, 0}; |
0c7a5945 JL |
286 | struct { |
287 | struct xfrm_user_sec_ctx sctx; | |
288 | char str[CTX_BUF_SIZE]; | |
289 | } ctx; | |
c7699875 | 290 | |
291 | memset(&req, 0, sizeof(req)); | |
de95ae7c | 292 | memset(&replay, 0, sizeof(replay)); |
0c7a5945 | 293 | memset(&ctx, 0, sizeof(ctx)); |
c7699875 | 294 | |
295 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); | |
296 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; | |
297 | req.n.nlmsg_type = cmd; | |
298 | req.xsinfo.family = preferred_family; | |
299 | ||
300 | req.xsinfo.lft.soft_byte_limit = XFRM_INF; | |
301 | req.xsinfo.lft.hard_byte_limit = XFRM_INF; | |
302 | req.xsinfo.lft.soft_packet_limit = XFRM_INF; | |
303 | req.xsinfo.lft.hard_packet_limit = XFRM_INF; | |
304 | ||
305 | while (argc > 0) { | |
7809c616 | 306 | if (strcmp(*argv, "mode") == 0) { |
c7699875 | 307 | NEXT_ARG(); |
308 | xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); | |
c90cda94 JHS |
309 | } else if (strcmp(*argv, "mark") == 0) { |
310 | xfrm_parse_mark(&mark, &argc, &argv); | |
c7699875 | 311 | } else if (strcmp(*argv, "reqid") == 0) { |
312 | NEXT_ARG(); | |
313 | xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); | |
fb7399b2 | 314 | } else if (strcmp(*argv, "seq") == 0) { |
315 | NEXT_ARG(); | |
316 | xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv); | |
eaa34ee3 | 317 | } else if (strcmp(*argv, "replay-window") == 0) { |
318 | NEXT_ARG(); | |
319 | if (get_u8(&req.xsinfo.replay_window, *argv, 0)) | |
e8740e42 | 320 | invarg("value after \"replay-window\" is invalid", *argv); |
de95ae7c HX |
321 | } else if (strcmp(*argv, "replay-seq") == 0) { |
322 | NEXT_ARG(); | |
323 | if (get_u32(&replay.seq, *argv, 0)) | |
e8740e42 | 324 | invarg("value after \"replay-seq\" is invalid", *argv); |
de95ae7c HX |
325 | } else if (strcmp(*argv, "replay-oseq") == 0) { |
326 | NEXT_ARG(); | |
327 | if (get_u32(&replay.oseq, *argv, 0)) | |
e8740e42 | 328 | invarg("value after \"replay-oseq\" is invalid", *argv); |
c7699875 | 329 | } else if (strcmp(*argv, "flag") == 0) { |
330 | NEXT_ARG(); | |
331 | xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); | |
dc8867d0 ND |
332 | } else if (strcmp(*argv, "extra-flag") == 0) { |
333 | NEXT_ARG(); | |
334 | xfrm_state_extra_flag_parse(&extra_flags, &argc, &argv); | |
c7699875 | 335 | } else if (strcmp(*argv, "sel") == 0) { |
336 | NEXT_ARG(); | |
23d5b0d5 | 337 | preferred_family = AF_UNSPEC; |
c7699875 | 338 | xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); |
0c5982fd | 339 | preferred_family = req.xsinfo.sel.family; |
c7699875 | 340 | } else if (strcmp(*argv, "limit") == 0) { |
341 | NEXT_ARG(); | |
342 | xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); | |
5cf576d9 SH |
343 | } else if (strcmp(*argv, "encap") == 0) { |
344 | struct xfrm_encap_tmpl encap; | |
345 | inet_prefix oa; | |
346 | NEXT_ARG(); | |
347 | xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); | |
348 | NEXT_ARG(); | |
349 | if (get_u16(&encap.encap_sport, *argv, 0)) | |
e8740e42 | 350 | invarg("SPORT value after \"encap\" is invalid", *argv); |
5cf576d9 SH |
351 | encap.encap_sport = htons(encap.encap_sport); |
352 | NEXT_ARG(); | |
353 | if (get_u16(&encap.encap_dport, *argv, 0)) | |
e8740e42 | 354 | invarg("DPORT value after \"encap\" is invalid", *argv); |
5cf576d9 SH |
355 | encap.encap_dport = htons(encap.encap_dport); |
356 | NEXT_ARG(); | |
357 | get_addr(&oa, *argv, AF_UNSPEC); | |
358 | memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); | |
359 | addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, | |
360 | (void *)&encap, sizeof(encap)); | |
7ea4f5d3 MN |
361 | } else if (strcmp(*argv, "coa") == 0) { |
362 | inet_prefix coa; | |
363 | xfrm_address_t xcoa; | |
364 | ||
365 | if (coap) | |
366 | duparg("coa", *argv); | |
367 | coap = *argv; | |
368 | ||
369 | NEXT_ARG(); | |
370 | ||
371 | get_prefix(&coa, *argv, preferred_family); | |
372 | if (coa.family == AF_UNSPEC) | |
e8740e42 | 373 | invarg("value after \"coa\" has an unrecognized address family", *argv); |
7ea4f5d3 | 374 | if (coa.bytelen > sizeof(xcoa)) |
e8740e42 | 375 | invarg("value after \"coa\" is too large", *argv); |
7ea4f5d3 MN |
376 | |
377 | memset(&xcoa, 0, sizeof(xcoa)); | |
378 | memcpy(&xcoa, &coa.data, coa.bytelen); | |
379 | ||
380 | addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR, | |
381 | (void *)&xcoa, sizeof(xcoa)); | |
0c7a5945 JL |
382 | } else if (strcmp(*argv, "ctx") == 0) { |
383 | char *context; | |
384 | ||
385 | if (sctxp) | |
386 | duparg("ctx", *argv); | |
387 | sctxp = *argv; | |
388 | ||
389 | NEXT_ARG(); | |
390 | context = *argv; | |
391 | ||
392 | xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx); | |
393 | addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX, | |
394 | (void *)&ctx, ctx.sctx.len); | |
c7699875 | 395 | } else { |
7809c616 | 396 | /* try to assume ALGO */ |
397 | int type = xfrm_algotype_getbyname(*argv); | |
398 | switch (type) { | |
1758a81f | 399 | case XFRMA_ALG_AEAD: |
7809c616 | 400 | case XFRMA_ALG_CRYPT: |
401 | case XFRMA_ALG_AUTH: | |
f323f2a3 | 402 | case XFRMA_ALG_AUTH_TRUNC: |
7809c616 | 403 | case XFRMA_ALG_COMP: |
404 | { | |
405 | /* ALGO */ | |
406 | struct { | |
1758a81f HX |
407 | union { |
408 | struct xfrm_algo alg; | |
409 | struct xfrm_algo_aead aead; | |
f323f2a3 | 410 | struct xfrm_algo_auth auth; |
1758a81f | 411 | } u; |
7809c616 | 412 | char buf[XFRM_ALGO_KEY_BUF_SIZE]; |
1758a81f | 413 | } alg = {}; |
7809c616 | 414 | int len; |
f323f2a3 | 415 | __u32 icvlen, trunclen; |
7809c616 | 416 | char *name; |
f3b9aa3d | 417 | char *key = ""; |
1758a81f | 418 | char *buf; |
7809c616 | 419 | |
420 | switch (type) { | |
1758a81f | 421 | case XFRMA_ALG_AEAD: |
ec839527 | 422 | if (ealgop || aalgop || aeadop) |
cbec0219 | 423 | duparg("ALGO-TYPE", *argv); |
1758a81f HX |
424 | aeadop = *argv; |
425 | break; | |
7809c616 | 426 | case XFRMA_ALG_CRYPT: |
ec839527 | 427 | if (ealgop || aeadop) |
cbec0219 | 428 | duparg("ALGO-TYPE", *argv); |
7809c616 | 429 | ealgop = *argv; |
430 | break; | |
431 | case XFRMA_ALG_AUTH: | |
f323f2a3 | 432 | case XFRMA_ALG_AUTH_TRUNC: |
ec839527 | 433 | if (aalgop || aeadop) |
cbec0219 | 434 | duparg("ALGO-TYPE", *argv); |
7809c616 | 435 | aalgop = *argv; |
436 | break; | |
437 | case XFRMA_ALG_COMP: | |
438 | if (calgop) | |
cbec0219 | 439 | duparg("ALGO-TYPE", *argv); |
7809c616 | 440 | calgop = *argv; |
441 | break; | |
442 | default: | |
443 | /* not reached */ | |
e8740e42 | 444 | invarg("ALGO-TYPE value is invalid\n", *argv); |
7809c616 | 445 | } |
446 | ||
447 | if (!NEXT_ARG_OK()) | |
cbec0219 | 448 | missarg("ALGO-NAME"); |
7809c616 | 449 | NEXT_ARG(); |
450 | name = *argv; | |
451 | ||
f3b9aa3d DW |
452 | switch (type) { |
453 | case XFRMA_ALG_AEAD: | |
454 | case XFRMA_ALG_CRYPT: | |
455 | case XFRMA_ALG_AUTH: | |
456 | case XFRMA_ALG_AUTH_TRUNC: | |
457 | if (!NEXT_ARG_OK()) | |
29665f92 | 458 | missarg("ALGO-KEYMAT"); |
f3b9aa3d DW |
459 | NEXT_ARG(); |
460 | key = *argv; | |
461 | break; | |
462 | } | |
7809c616 | 463 | |
1758a81f HX |
464 | buf = alg.u.alg.alg_key; |
465 | len = sizeof(alg.u.alg); | |
466 | ||
f323f2a3 ND |
467 | switch (type) { |
468 | case XFRMA_ALG_AEAD: | |
469 | if (!NEXT_ARG_OK()) | |
cbec0219 | 470 | missarg("ALGO-ICV-LEN"); |
f323f2a3 ND |
471 | NEXT_ARG(); |
472 | if (get_u32(&icvlen, *argv, 0)) | |
e8740e42 | 473 | invarg("ALGO-ICV-LEN value is invalid", |
f323f2a3 ND |
474 | *argv); |
475 | alg.u.aead.alg_icv_len = icvlen; | |
476 | ||
477 | buf = alg.u.aead.alg_key; | |
478 | len = sizeof(alg.u.aead); | |
479 | break; | |
480 | case XFRMA_ALG_AUTH_TRUNC: | |
481 | if (!NEXT_ARG_OK()) | |
cbec0219 | 482 | missarg("ALGO-TRUNC-LEN"); |
f323f2a3 ND |
483 | NEXT_ARG(); |
484 | if (get_u32(&trunclen, *argv, 0)) | |
e8740e42 | 485 | invarg("ALGO-TRUNC-LEN value is invalid", |
f323f2a3 ND |
486 | *argv); |
487 | alg.u.auth.alg_trunc_len = trunclen; | |
488 | ||
489 | buf = alg.u.auth.alg_key; | |
490 | len = sizeof(alg.u.auth); | |
491 | break; | |
492 | } | |
7809c616 | 493 | |
494 | xfrm_algo_parse((void *)&alg, type, name, key, | |
1758a81f HX |
495 | buf, sizeof(alg.buf)); |
496 | len += alg.u.alg.alg_key_len; | |
7809c616 | 497 | |
498 | addattr_l(&req.n, sizeof(req.buf), type, | |
499 | (void *)&alg, len); | |
500 | break; | |
501 | } | |
502 | default: | |
503 | /* try to assume ID */ | |
504 | if (idp) | |
505 | invarg("unknown", *argv); | |
506 | idp = *argv; | |
507 | ||
508 | /* ID */ | |
509 | xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, | |
510 | &req.xsinfo.family, 0, &argc, &argv); | |
511 | if (preferred_family == AF_UNSPEC) | |
512 | preferred_family = req.xsinfo.family; | |
513 | } | |
c7699875 | 514 | } |
515 | argc--; argv++; | |
516 | } | |
517 | ||
de95ae7c HX |
518 | if (replay.seq || replay.oseq) |
519 | addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL, | |
520 | (void *)&replay, sizeof(replay)); | |
521 | ||
dc8867d0 ND |
522 | if (extra_flags) |
523 | addattr32(&req.n, sizeof(req.buf), XFRMA_SA_EXTRA_FLAGS, | |
524 | extra_flags); | |
525 | ||
c7699875 | 526 | if (!idp) { |
e8740e42 | 527 | fprintf(stderr, "Not enough information: ID is required\n"); |
c7699875 | 528 | exit(1); |
529 | } | |
530 | ||
c90cda94 JHS |
531 | if (mark.m & mark.v) { |
532 | int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, | |
533 | (void *)&mark, sizeof(mark)); | |
534 | if (r < 0) { | |
535 | fprintf(stderr, "XFRMA_MARK failed\n"); | |
536 | exit(1); | |
537 | } | |
538 | } | |
539 | ||
6128fdfd DW |
540 | if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) { |
541 | switch (req.xsinfo.mode) { | |
542 | case XFRM_MODE_TRANSPORT: | |
543 | case XFRM_MODE_TUNNEL: | |
544 | break; | |
545 | case XFRM_MODE_BEET: | |
546 | if (req.xsinfo.id.proto == IPPROTO_ESP) | |
547 | break; | |
548 | default: | |
549 | fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n", | |
7ea4f5d3 MN |
550 | strxf_xfrmproto(req.xsinfo.id.proto)); |
551 | exit(1); | |
552 | } | |
6128fdfd DW |
553 | |
554 | switch (req.xsinfo.id.proto) { | |
555 | case IPPROTO_ESP: | |
556 | if (calgop) { | |
557 | fprintf(stderr, "ALGO-TYPE value \"%s\" is invalid with XFRM-PROTO value \"%s\"\n", | |
558 | strxf_algotype(XFRMA_ALG_COMP), | |
559 | strxf_xfrmproto(req.xsinfo.id.proto)); | |
560 | exit(1); | |
561 | } | |
562 | if (!ealgop && !aeadop) { | |
563 | fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n", | |
564 | strxf_algotype(XFRMA_ALG_CRYPT), | |
565 | strxf_algotype(XFRMA_ALG_AEAD), | |
566 | strxf_xfrmproto(req.xsinfo.id.proto)); | |
567 | exit(1); | |
568 | } | |
569 | break; | |
570 | case IPPROTO_AH: | |
571 | if (ealgop || aeadop || calgop) { | |
572 | fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n", | |
573 | strxf_algotype(XFRMA_ALG_CRYPT), | |
574 | strxf_algotype(XFRMA_ALG_AEAD), | |
575 | strxf_algotype(XFRMA_ALG_COMP), | |
576 | strxf_xfrmproto(req.xsinfo.id.proto)); | |
577 | exit(1); | |
578 | } | |
579 | if (!aalgop) { | |
580 | fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n", | |
581 | strxf_algotype(XFRMA_ALG_AUTH), | |
582 | strxf_algotype(XFRMA_ALG_AUTH_TRUNC), | |
583 | strxf_xfrmproto(req.xsinfo.id.proto)); | |
584 | exit(1); | |
585 | } | |
586 | break; | |
587 | case IPPROTO_COMP: | |
588 | if (ealgop || aalgop || aeadop) { | |
589 | fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n", | |
590 | strxf_algotype(XFRMA_ALG_CRYPT), | |
591 | strxf_algotype(XFRMA_ALG_AUTH), | |
592 | strxf_algotype(XFRMA_ALG_AUTH_TRUNC), | |
593 | strxf_algotype(XFRMA_ALG_AEAD), | |
594 | strxf_xfrmproto(req.xsinfo.id.proto)); | |
595 | exit(1); | |
596 | } | |
597 | if (!calgop) { | |
598 | fprintf(stderr, "ALGO-TYPE value \"%s\" is required with XFRM-PROTO value \"%s\"\n", | |
599 | strxf_algotype(XFRMA_ALG_COMP), | |
600 | strxf_xfrmproto(req.xsinfo.id.proto)); | |
601 | exit(1); | |
602 | } | |
603 | break; | |
604 | } | |
605 | } else { | |
606 | if (ealgop || aalgop || aeadop || calgop) { | |
607 | fprintf(stderr, "ALGO is invalid with XFRM-PROTO value \"%s\"\n", | |
7ea4f5d3 MN |
608 | strxf_xfrmproto(req.xsinfo.id.proto)); |
609 | exit(1); | |
610 | } | |
7ea4f5d3 MN |
611 | } |
612 | ||
6128fdfd DW |
613 | if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) { |
614 | switch (req.xsinfo.mode) { | |
615 | case XFRM_MODE_ROUTEOPTIMIZATION: | |
616 | case XFRM_MODE_IN_TRIGGER: | |
617 | break; | |
618 | case 0: | |
619 | fprintf(stderr, "\"mode\" is required with XFRM-PROTO value \"%s\"\n", | |
7ea4f5d3 MN |
620 | strxf_xfrmproto(req.xsinfo.id.proto)); |
621 | exit(1); | |
6128fdfd DW |
622 | default: |
623 | fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n", | |
7ea4f5d3 | 624 | strxf_xfrmproto(req.xsinfo.id.proto)); |
6128fdfd | 625 | exit(1); |
7ea4f5d3 | 626 | } |
7ea4f5d3 | 627 | |
6128fdfd DW |
628 | if (!coap) { |
629 | fprintf(stderr, "\"coa\" is required with XFRM-PROTO value \"%s\"\n", | |
7ea4f5d3 | 630 | strxf_xfrmproto(req.xsinfo.id.proto)); |
c7699875 | 631 | exit(1); |
632 | } | |
633 | } else { | |
6128fdfd DW |
634 | if (coap) { |
635 | fprintf(stderr, "\"coa\" is invalid with XFRM-PROTO value \"%s\"\n", | |
7ea4f5d3 | 636 | strxf_xfrmproto(req.xsinfo.id.proto)); |
6128fdfd | 637 | exit(1); |
c7699875 | 638 | } |
639 | } | |
640 | ||
641 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
642 | exit(1); | |
643 | ||
644 | if (req.xsinfo.family == AF_UNSPEC) | |
645 | req.xsinfo.family = AF_INET; | |
646 | ||
cd70f3f5 | 647 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) |
c7699875 | 648 | exit(2); |
649 | ||
650 | rtnl_close(&rth); | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
fb7399b2 | 655 | static int xfrm_state_allocspi(int argc, char **argv) |
656 | { | |
657 | struct rtnl_handle rth; | |
658 | struct { | |
659 | struct nlmsghdr n; | |
660 | struct xfrm_userspi_info xspi; | |
661 | char buf[RTA_BUF_SIZE]; | |
662 | } req; | |
663 | char *idp = NULL; | |
664 | char *minp = NULL; | |
665 | char *maxp = NULL; | |
c90cda94 | 666 | struct xfrm_mark mark = {0, 0}; |
fb7399b2 | 667 | char res_buf[NLMSG_BUF_SIZE]; |
668 | struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf; | |
669 | ||
670 | memset(res_buf, 0, sizeof(res_buf)); | |
671 | ||
672 | memset(&req, 0, sizeof(req)); | |
673 | ||
674 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)); | |
675 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
676 | req.n.nlmsg_type = XFRM_MSG_ALLOCSPI; | |
677 | req.xspi.info.family = preferred_family; | |
678 | ||
679 | #if 0 | |
680 | req.xsinfo.lft.soft_byte_limit = XFRM_INF; | |
681 | req.xsinfo.lft.hard_byte_limit = XFRM_INF; | |
682 | req.xsinfo.lft.soft_packet_limit = XFRM_INF; | |
683 | req.xsinfo.lft.hard_packet_limit = XFRM_INF; | |
684 | #endif | |
685 | ||
686 | while (argc > 0) { | |
687 | if (strcmp(*argv, "mode") == 0) { | |
688 | NEXT_ARG(); | |
689 | xfrm_mode_parse(&req.xspi.info.mode, &argc, &argv); | |
c90cda94 JHS |
690 | } else if (strcmp(*argv, "mark") == 0) { |
691 | xfrm_parse_mark(&mark, &argc, &argv); | |
fb7399b2 | 692 | } else if (strcmp(*argv, "reqid") == 0) { |
693 | NEXT_ARG(); | |
694 | xfrm_reqid_parse(&req.xspi.info.reqid, &argc, &argv); | |
695 | } else if (strcmp(*argv, "seq") == 0) { | |
696 | NEXT_ARG(); | |
697 | xfrm_seq_parse(&req.xspi.info.seq, &argc, &argv); | |
698 | } else if (strcmp(*argv, "min") == 0) { | |
699 | if (minp) | |
700 | duparg("min", *argv); | |
701 | minp = *argv; | |
702 | ||
703 | NEXT_ARG(); | |
704 | ||
705 | if (get_u32(&req.xspi.min, *argv, 0)) | |
e8740e42 | 706 | invarg("value after \"min\" is invalid", *argv); |
fb7399b2 | 707 | } else if (strcmp(*argv, "max") == 0) { |
708 | if (maxp) | |
709 | duparg("max", *argv); | |
710 | maxp = *argv; | |
711 | ||
712 | NEXT_ARG(); | |
713 | ||
714 | if (get_u32(&req.xspi.max, *argv, 0)) | |
e8740e42 | 715 | invarg("value after \"max\" is invalid", *argv); |
fb7399b2 | 716 | } else { |
717 | /* try to assume ID */ | |
718 | if (idp) | |
719 | invarg("unknown", *argv); | |
720 | idp = *argv; | |
721 | ||
722 | /* ID */ | |
723 | xfrm_id_parse(&req.xspi.info.saddr, &req.xspi.info.id, | |
724 | &req.xspi.info.family, 0, &argc, &argv); | |
725 | if (req.xspi.info.id.spi) { | |
e8740e42 | 726 | fprintf(stderr, "\"spi\" is invalid\n"); |
fb7399b2 | 727 | exit(1); |
728 | } | |
729 | if (preferred_family == AF_UNSPEC) | |
730 | preferred_family = req.xspi.info.family; | |
731 | } | |
732 | argc--; argv++; | |
733 | } | |
734 | ||
735 | if (!idp) { | |
e8740e42 | 736 | fprintf(stderr, "Not enough information: ID is required\n"); |
fb7399b2 | 737 | exit(1); |
738 | } | |
739 | ||
740 | if (minp) { | |
741 | if (!maxp) { | |
742 | fprintf(stderr, "\"max\" is missing\n"); | |
743 | exit(1); | |
744 | } | |
745 | if (req.xspi.min > req.xspi.max) { | |
e8740e42 | 746 | fprintf(stderr, "value after \"min\" is larger than value after \"max\"\n"); |
fb7399b2 | 747 | exit(1); |
748 | } | |
749 | } else { | |
750 | if (maxp) { | |
751 | fprintf(stderr, "\"min\" is missing\n"); | |
752 | exit(1); | |
753 | } | |
754 | ||
755 | /* XXX: Default value defined in PF_KEY; | |
756 | * See kernel's net/key/af_key.c(pfkey_getspi). | |
757 | */ | |
758 | req.xspi.min = 0x100; | |
759 | req.xspi.max = 0x0fffffff; | |
760 | ||
761 | /* XXX: IPCOMP spi is 16-bits; | |
762 | * See kernel's net/xfrm/xfrm_user(verify_userspi_info). | |
763 | */ | |
764 | if (req.xspi.info.id.proto == IPPROTO_COMP) | |
765 | req.xspi.max = 0xffff; | |
766 | } | |
767 | ||
c90cda94 JHS |
768 | if (mark.m & mark.v) { |
769 | int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, | |
770 | (void *)&mark, sizeof(mark)); | |
771 | if (r < 0) { | |
772 | fprintf(stderr, "XFRMA_MARK failed\n"); | |
773 | exit(1); | |
774 | } | |
775 | } | |
776 | ||
fb7399b2 | 777 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) |
778 | exit(1); | |
779 | ||
780 | if (req.xspi.info.family == AF_UNSPEC) | |
781 | req.xspi.info.family = AF_INET; | |
782 | ||
783 | ||
cd70f3f5 | 784 | if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0) |
fb7399b2 | 785 | exit(2); |
786 | ||
787 | if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { | |
788 | fprintf(stderr, "An error :-)\n"); | |
789 | exit(1); | |
790 | } | |
791 | ||
792 | rtnl_close(&rth); | |
793 | ||
794 | return 0; | |
795 | } | |
796 | ||
c7699875 | 797 | static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo) |
798 | { | |
799 | if (!filter.use) | |
800 | return 1; | |
801 | ||
802 | if (filter.id_src_mask) | |
eaa34ee3 | 803 | if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr, |
804 | filter.id_src_mask)) | |
c7699875 | 805 | return 0; |
806 | if (filter.id_dst_mask) | |
eaa34ee3 | 807 | if (xfrm_addr_match(&xsinfo->id.daddr, &filter.xsinfo.id.daddr, |
808 | filter.id_dst_mask)) | |
c7699875 | 809 | return 0; |
810 | if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask) | |
811 | return 0; | |
812 | if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask) | |
813 | return 0; | |
814 | if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask) | |
815 | return 0; | |
816 | if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask) | |
817 | return 0; | |
818 | if (filter.state_flags_mask) | |
819 | if ((xsinfo->flags & filter.xsinfo.flags) == 0) | |
820 | return 0; | |
821 | ||
822 | return 1; | |
823 | } | |
824 | ||
fb7399b2 | 825 | int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n, |
826 | void *arg) | |
c7699875 | 827 | { |
828 | FILE *fp = (FILE*)arg; | |
c7699875 | 829 | struct rtattr * tb[XFRMA_MAX+1]; |
90f93024 | 830 | struct rtattr * rta; |
c595c790 SH |
831 | struct xfrm_usersa_info *xsinfo = NULL; |
832 | struct xfrm_user_expire *xexp = NULL; | |
833 | struct xfrm_usersa_id *xsid = NULL; | |
834 | int len = n->nlmsg_len; | |
c7699875 | 835 | |
836 | if (n->nlmsg_type != XFRM_MSG_NEWSA && | |
90f93024 | 837 | n->nlmsg_type != XFRM_MSG_DELSA && |
669ae748 | 838 | n->nlmsg_type != XFRM_MSG_UPDSA && |
90f93024 | 839 | n->nlmsg_type != XFRM_MSG_EXPIRE) { |
c7699875 | 840 | fprintf(stderr, "Not a state: %08x %08x %08x\n", |
841 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
842 | return 0; | |
843 | } | |
844 | ||
669ae748 SH |
845 | if (n->nlmsg_type == XFRM_MSG_DELSA) { |
846 | /* Dont blame me for this .. Herbert made me do it */ | |
847 | xsid = NLMSG_DATA(n); | |
af1b6a41 | 848 | len -= NLMSG_SPACE(sizeof(*xsid)); |
669ae748 | 849 | } else if (n->nlmsg_type == XFRM_MSG_EXPIRE) { |
90f93024 SH |
850 | xexp = NLMSG_DATA(n); |
851 | xsinfo = &xexp->state; | |
af1b6a41 | 852 | len -= NLMSG_SPACE(sizeof(*xexp)); |
90f93024 SH |
853 | } else { |
854 | xexp = NULL; | |
855 | xsinfo = NLMSG_DATA(n); | |
af1b6a41 | 856 | len -= NLMSG_SPACE(sizeof(*xsinfo)); |
90f93024 SH |
857 | } |
858 | ||
c7699875 | 859 | if (len < 0) { |
860 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
861 | return -1; | |
862 | } | |
863 | ||
669ae748 | 864 | if (xsinfo && !xfrm_state_filter_match(xsinfo)) |
c7699875 | 865 | return 0; |
866 | ||
669ae748 | 867 | if (n->nlmsg_type == XFRM_MSG_DELSA) |
c595c790 | 868 | fprintf(fp, "Deleted "); |
669ae748 SH |
869 | else if (n->nlmsg_type == XFRM_MSG_UPDSA) |
870 | fprintf(fp, "Updated "); | |
c595c790 SH |
871 | else if (n->nlmsg_type == XFRM_MSG_EXPIRE) |
872 | fprintf(fp, "Expired "); | |
873 | ||
669ae748 SH |
874 | if (n->nlmsg_type == XFRM_MSG_DELSA) |
875 | rta = XFRMSID_RTA(xsid); | |
876 | else if (n->nlmsg_type == XFRM_MSG_EXPIRE) | |
90f93024 | 877 | rta = XFRMEXP_RTA(xexp); |
ae665a52 | 878 | else |
90f93024 SH |
879 | rta = XFRMS_RTA(xsinfo); |
880 | ||
881 | parse_rtattr(tb, XFRMA_MAX, rta, len); | |
c7699875 | 882 | |
c595c790 | 883 | if (n->nlmsg_type == XFRM_MSG_DELSA) { |
669ae748 SH |
884 | //xfrm_policy_id_print(); |
885 | ||
886 | if (!tb[XFRMA_SA]) { | |
887 | fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n"); | |
888 | return -1; | |
889 | } | |
890 | if (RTA_PAYLOAD(tb[XFRMA_SA]) < sizeof(*xsinfo)) { | |
891 | fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n"); | |
892 | return -1; | |
c595c790 | 893 | } |
bdf9e86d | 894 | xsinfo = RTA_DATA(tb[XFRMA_SA]); |
c595c790 | 895 | } |
c7699875 | 896 | |
fb7399b2 | 897 | xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL); |
c7699875 | 898 | |
90f93024 SH |
899 | if (n->nlmsg_type == XFRM_MSG_EXPIRE) { |
900 | fprintf(fp, "\t"); | |
901 | fprintf(fp, "hard %u", xexp->hard); | |
902 | fprintf(fp, "%s", _SL_); | |
903 | } | |
904 | ||
7809c616 | 905 | if (oneline) |
906 | fprintf(fp, "\n"); | |
669ae748 | 907 | fflush(fp); |
7809c616 | 908 | |
c7699875 | 909 | return 0; |
910 | } | |
911 | ||
912 | static int xfrm_state_get_or_delete(int argc, char **argv, int delete) | |
913 | { | |
914 | struct rtnl_handle rth; | |
915 | struct { | |
916 | struct nlmsghdr n; | |
917 | struct xfrm_usersa_id xsid; | |
7ea4f5d3 | 918 | char buf[RTA_BUF_SIZE]; |
c7699875 | 919 | } req; |
920 | struct xfrm_id id; | |
921 | char *idp = NULL; | |
c90cda94 | 922 | struct xfrm_mark mark = {0, 0}; |
c7699875 | 923 | |
924 | memset(&req, 0, sizeof(req)); | |
925 | ||
926 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)); | |
927 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
928 | req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA; | |
929 | req.xsid.family = preferred_family; | |
930 | ||
931 | while (argc > 0) { | |
7ea4f5d3 | 932 | xfrm_address_t saddr; |
c7699875 | 933 | |
c90cda94 JHS |
934 | if (strcmp(*argv, "mark") == 0) { |
935 | xfrm_parse_mark(&mark, &argc, &argv); | |
936 | } else { | |
937 | if (idp) | |
938 | invarg("unknown", *argv); | |
939 | idp = *argv; | |
c7699875 | 940 | |
c90cda94 JHS |
941 | /* ID */ |
942 | memset(&id, 0, sizeof(id)); | |
943 | memset(&saddr, 0, sizeof(saddr)); | |
944 | xfrm_id_parse(&saddr, &id, &req.xsid.family, 0, | |
945 | &argc, &argv); | |
c7699875 | 946 | |
c90cda94 JHS |
947 | memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr)); |
948 | req.xsid.spi = id.spi; | |
949 | req.xsid.proto = id.proto; | |
c7699875 | 950 | |
c90cda94 JHS |
951 | addattr_l(&req.n, sizeof(req.buf), XFRMA_SRCADDR, |
952 | (void *)&saddr, sizeof(saddr)); | |
953 | } | |
7ea4f5d3 | 954 | |
c7699875 | 955 | argc--; argv++; |
956 | } | |
957 | ||
c90cda94 JHS |
958 | if (mark.m & mark.v) { |
959 | int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, | |
960 | (void *)&mark, sizeof(mark)); | |
961 | if (r < 0) { | |
962 | fprintf(stderr, "XFRMA_MARK failed\n"); | |
963 | exit(1); | |
964 | } | |
965 | } | |
966 | ||
c7699875 | 967 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) |
968 | exit(1); | |
969 | ||
970 | if (req.xsid.family == AF_UNSPEC) | |
971 | req.xsid.family = AF_INET; | |
972 | ||
973 | if (delete) { | |
cd70f3f5 | 974 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) |
c7699875 | 975 | exit(2); |
976 | } else { | |
977 | char buf[NLMSG_BUF_SIZE]; | |
978 | struct nlmsghdr *res_n = (struct nlmsghdr *)buf; | |
979 | ||
980 | memset(buf, 0, sizeof(buf)); | |
981 | ||
cd70f3f5 | 982 | if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0) |
c7699875 | 983 | exit(2); |
984 | ||
985 | if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { | |
986 | fprintf(stderr, "An error :-)\n"); | |
987 | exit(1); | |
988 | } | |
989 | } | |
990 | ||
991 | rtnl_close(&rth); | |
992 | ||
993 | return 0; | |
994 | } | |
995 | ||
996 | /* | |
997 | * With an existing state of nlmsg, make new nlmsg for deleting the state | |
998 | * and store it to buffer. | |
999 | */ | |
6dc9f016 | 1000 | static int xfrm_state_keep(const struct sockaddr_nl *who, |
50772dc5 | 1001 | struct nlmsghdr *n, |
6dc9f016 | 1002 | void *arg) |
c7699875 | 1003 | { |
1004 | struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; | |
1005 | struct rtnl_handle *rth = xb->rth; | |
1006 | struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); | |
1007 | int len = n->nlmsg_len; | |
1008 | struct nlmsghdr *new_n; | |
1009 | struct xfrm_usersa_id *xsid; | |
1010 | ||
1011 | if (n->nlmsg_type != XFRM_MSG_NEWSA) { | |
1012 | fprintf(stderr, "Not a state: %08x %08x %08x\n", | |
1013 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
1014 | return 0; | |
1015 | } | |
1016 | ||
1017 | len -= NLMSG_LENGTH(sizeof(*xsinfo)); | |
1018 | if (len < 0) { | |
1019 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
1020 | return -1; | |
1021 | } | |
1022 | ||
1023 | if (!xfrm_state_filter_match(xsinfo)) | |
1024 | return 0; | |
1025 | ||
1026 | if (xb->offset > xb->size) { | |
9bec1a43 | 1027 | fprintf(stderr, "State buffer overflow\n"); |
c7699875 | 1028 | return -1; |
1029 | } | |
1030 | ||
1031 | new_n = (struct nlmsghdr *)(xb->buf + xb->offset); | |
1032 | new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid)); | |
1033 | new_n->nlmsg_flags = NLM_F_REQUEST; | |
1034 | new_n->nlmsg_type = XFRM_MSG_DELSA; | |
1035 | new_n->nlmsg_seq = ++rth->seq; | |
1036 | ||
1037 | xsid = NLMSG_DATA(new_n); | |
1038 | xsid->family = xsinfo->family; | |
1039 | memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr)); | |
1040 | xsid->spi = xsinfo->id.spi; | |
1041 | xsid->proto = xsinfo->id.proto; | |
1042 | ||
7ea4f5d3 MN |
1043 | addattr_l(new_n, xb->size, XFRMA_SRCADDR, &xsinfo->saddr, |
1044 | sizeof(xsid->daddr)); | |
1045 | ||
c7699875 | 1046 | xb->offset += new_n->nlmsg_len; |
1047 | xb->nlmsg_count ++; | |
1048 | ||
1049 | return 0; | |
1050 | } | |
1051 | ||
9bec1a43 | 1052 | static int xfrm_state_list_or_deleteall(int argc, char **argv, int deleteall) |
c7699875 | 1053 | { |
1054 | char *idp = NULL; | |
1055 | struct rtnl_handle rth; | |
1056 | ||
bd641cd6 | 1057 | if(argc > 0) |
1058 | filter.use = 1; | |
c7699875 | 1059 | filter.xsinfo.family = preferred_family; |
1060 | ||
1061 | while (argc > 0) { | |
1062 | if (strcmp(*argv, "mode") == 0) { | |
1063 | NEXT_ARG(); | |
1064 | xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv); | |
1065 | ||
1066 | filter.mode_mask = XFRM_FILTER_MASK_FULL; | |
1067 | ||
1068 | } else if (strcmp(*argv, "reqid") == 0) { | |
1069 | NEXT_ARG(); | |
1070 | xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv); | |
1071 | ||
1072 | filter.reqid_mask = XFRM_FILTER_MASK_FULL; | |
1073 | ||
1074 | } else if (strcmp(*argv, "flag") == 0) { | |
1075 | NEXT_ARG(); | |
1076 | xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv); | |
1077 | ||
1078 | filter.state_flags_mask = XFRM_FILTER_MASK_FULL; | |
1079 | ||
1080 | } else { | |
1081 | if (idp) | |
1082 | invarg("unknown", *argv); | |
1083 | idp = *argv; | |
1084 | ||
1085 | /* ID */ | |
7809c616 | 1086 | xfrm_id_parse(&filter.xsinfo.saddr, &filter.xsinfo.id, |
1087 | &filter.xsinfo.family, 1, &argc, &argv); | |
c7699875 | 1088 | if (preferred_family == AF_UNSPEC) |
1089 | preferred_family = filter.xsinfo.family; | |
1090 | } | |
1091 | argc--; argv++; | |
1092 | } | |
1093 | ||
1094 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
1095 | exit(1); | |
1096 | ||
9bec1a43 | 1097 | if (deleteall) { |
c7699875 | 1098 | struct xfrm_buffer xb; |
9bec1a43 | 1099 | char buf[NLMSG_DELETEALL_BUF_SIZE]; |
c7699875 | 1100 | int i; |
1101 | ||
1102 | xb.buf = buf; | |
1103 | xb.size = sizeof(buf); | |
1104 | xb.rth = &rth; | |
1105 | ||
1106 | for (i = 0; ; i++) { | |
1107 | xb.offset = 0; | |
1108 | xb.nlmsg_count = 0; | |
1109 | ||
1110 | if (show_stats > 1) | |
9bec1a43 | 1111 | fprintf(stderr, "Delete-all round = %d\n", i); |
c7699875 | 1112 | |
1113 | if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { | |
1114 | perror("Cannot send dump request"); | |
1115 | exit(1); | |
1116 | } | |
1117 | ||
cd70f3f5 | 1118 | if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb) < 0) { |
9bec1a43 | 1119 | fprintf(stderr, "Delete-all terminated\n"); |
c7699875 | 1120 | exit(1); |
1121 | } | |
1122 | if (xb.nlmsg_count == 0) { | |
1123 | if (show_stats > 1) | |
9bec1a43 | 1124 | fprintf(stderr, "Delete-all completed\n"); |
c7699875 | 1125 | break; |
1126 | } | |
1127 | ||
f31a37f7 | 1128 | if (rtnl_send_check(&rth, xb.buf, xb.offset) < 0) { |
9bec1a43 | 1129 | perror("Failed to send delete-all request\n"); |
c7699875 | 1130 | exit(1); |
1131 | } | |
1132 | if (show_stats > 1) | |
9bec1a43 | 1133 | fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count); |
c7699875 | 1134 | |
1135 | xb.offset = 0; | |
1136 | xb.nlmsg_count = 0; | |
1137 | } | |
1138 | ||
1139 | } else { | |
1140 | if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { | |
1141 | perror("Cannot send dump request"); | |
1142 | exit(1); | |
1143 | } | |
1144 | ||
cd70f3f5 | 1145 | if (rtnl_dump_filter(&rth, xfrm_state_print, stdout) < 0) { |
c7699875 | 1146 | fprintf(stderr, "Dump terminated\n"); |
1147 | exit(1); | |
1148 | } | |
1149 | } | |
1150 | ||
1151 | rtnl_close(&rth); | |
1152 | ||
1153 | exit(0); | |
1154 | } | |
1155 | ||
d1f28cf1 | 1156 | static int print_sadinfo(struct nlmsghdr *n, void *arg) |
0bb4a4c2 | 1157 | { |
1158 | FILE *fp = (FILE*)arg; | |
1159 | __u32 *f = NLMSG_DATA(n); | |
1160 | struct rtattr *tb[XFRMA_SAD_MAX+1]; | |
1161 | struct rtattr *rta; | |
0bb4a4c2 | 1162 | __u32 *cnt; |
1163 | ||
1164 | int len = n->nlmsg_len; | |
1165 | ||
1166 | len -= NLMSG_LENGTH(sizeof(__u32)); | |
1167 | if (len < 0) { | |
1168 | fprintf(stderr, "SADinfo: Wrong len %d\n", len); | |
1169 | return -1; | |
1170 | } | |
1171 | ||
1172 | rta = XFRMSAPD_RTA(f); | |
1173 | parse_rtattr(tb, XFRMA_SAD_MAX, rta, len); | |
1174 | ||
bdf9e86d | 1175 | if (tb[XFRMA_SAD_CNT]) { |
0bb4a4c2 | 1176 | fprintf(fp,"\t SAD"); |
bdf9e86d | 1177 | cnt = (__u32 *)RTA_DATA(tb[XFRMA_SAD_CNT]); |
0bb4a4c2 | 1178 | fprintf(fp," count %d", *cnt); |
1179 | } else { | |
1180 | fprintf(fp,"BAD SAD info returned\n"); | |
1181 | return -1; | |
1182 | } | |
1183 | ||
1184 | if (show_stats) { | |
bdf9e86d SH |
1185 | if (tb[XFRMA_SAD_HINFO]) { |
1186 | struct xfrmu_sadhinfo *si; | |
0bb4a4c2 | 1187 | |
bdf9e86d SH |
1188 | if (RTA_PAYLOAD(tb[XFRMA_SAD_HINFO]) < sizeof(*si)) { |
1189 | fprintf(fp,"BAD SAD length returned\n"); | |
1190 | return -1; | |
1191 | } | |
1192 | ||
1193 | si = RTA_DATA(tb[XFRMA_SAD_HINFO]); | |
1194 | fprintf(fp," (buckets "); | |
1195 | fprintf(fp,"count %d", si->sadhcnt); | |
1196 | fprintf(fp," Max %d", si->sadhmcnt); | |
1197 | fprintf(fp,")"); | |
1198 | } | |
0bb4a4c2 | 1199 | } |
1200 | fprintf(fp,"\n"); | |
1201 | ||
1202 | return 0; | |
1203 | } | |
1204 | ||
1205 | static int xfrm_sad_getinfo(int argc, char **argv) | |
1206 | { | |
1207 | struct rtnl_handle rth; | |
1208 | struct { | |
1209 | struct nlmsghdr n; | |
1210 | __u32 flags; | |
1211 | char ans[64]; | |
1212 | } req; | |
1213 | ||
1214 | memset(&req, 0, sizeof(req)); | |
1215 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags)); | |
1216 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
1217 | req.n.nlmsg_type = XFRM_MSG_GETSADINFO; | |
1218 | req.flags = 0XFFFFFFFF; | |
1219 | ||
1220 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
1221 | exit(1); | |
1222 | ||
cd70f3f5 | 1223 | if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) |
0bb4a4c2 | 1224 | exit(2); |
1225 | ||
1226 | print_sadinfo(&req.n, (void*)stdout); | |
1227 | ||
1228 | rtnl_close(&rth); | |
1229 | ||
1230 | return 0; | |
1231 | } | |
1232 | ||
9bec1a43 | 1233 | static int xfrm_state_flush(int argc, char **argv) |
bd641cd6 | 1234 | { |
1235 | struct rtnl_handle rth; | |
1236 | struct { | |
1237 | struct nlmsghdr n; | |
1238 | struct xfrm_usersa_flush xsf; | |
1239 | } req; | |
9bec1a43 | 1240 | char *protop = NULL; |
bd641cd6 | 1241 | |
1242 | memset(&req, 0, sizeof(req)); | |
1243 | ||
1244 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)); | |
1245 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
1246 | req.n.nlmsg_type = XFRM_MSG_FLUSHSA; | |
7ea4f5d3 | 1247 | req.xsf.proto = 0; |
bd641cd6 | 1248 | |
9bec1a43 SH |
1249 | while (argc > 0) { |
1250 | if (strcmp(*argv, "proto") == 0) { | |
1251 | int ret; | |
1252 | ||
1253 | if (protop) | |
1254 | duparg("proto", *argv); | |
1255 | protop = *argv; | |
1256 | ||
1257 | NEXT_ARG(); | |
1258 | ||
1259 | ret = xfrm_xfrmproto_getbyname(*argv); | |
1260 | if (ret < 0) | |
e8740e42 | 1261 | invarg("XFRM-PROTO value is invalid", *argv); |
9bec1a43 SH |
1262 | |
1263 | req.xsf.proto = (__u8)ret; | |
1264 | } else | |
1265 | invarg("unknown", *argv); | |
1266 | ||
1267 | argc--; argv++; | |
1268 | } | |
1269 | ||
bd641cd6 | 1270 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) |
1271 | exit(1); | |
1272 | ||
1273 | if (show_stats > 1) | |
e8740e42 | 1274 | fprintf(stderr, "Flush state with XFRM-PROTO value \"%s\"\n", |
9bec1a43 | 1275 | strxf_xfrmproto(req.xsf.proto)); |
bd641cd6 | 1276 | |
cd70f3f5 | 1277 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) |
bd641cd6 | 1278 | exit(2); |
1279 | ||
1280 | rtnl_close(&rth); | |
1281 | ||
1282 | return 0; | |
1283 | } | |
1284 | ||
c7699875 | 1285 | int do_xfrm_state(int argc, char **argv) |
1286 | { | |
1287 | if (argc < 1) | |
9bec1a43 | 1288 | return xfrm_state_list_or_deleteall(0, NULL, 0); |
c7699875 | 1289 | |
1290 | if (matches(*argv, "add") == 0) | |
1291 | return xfrm_state_modify(XFRM_MSG_NEWSA, 0, | |
1292 | argc-1, argv+1); | |
1293 | if (matches(*argv, "update") == 0) | |
1294 | return xfrm_state_modify(XFRM_MSG_UPDSA, 0, | |
1295 | argc-1, argv+1); | |
fb7399b2 | 1296 | if (matches(*argv, "allocspi") == 0) |
1297 | return xfrm_state_allocspi(argc-1, argv+1); | |
9bec1a43 | 1298 | if (matches(*argv, "delete") == 0) |
c7699875 | 1299 | return xfrm_state_get_or_delete(argc-1, argv+1, 1); |
9bec1a43 SH |
1300 | if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0) |
1301 | return xfrm_state_list_or_deleteall(argc-1, argv+1, 1); | |
c7699875 | 1302 | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 |
1303 | || matches(*argv, "lst") == 0) | |
9bec1a43 | 1304 | return xfrm_state_list_or_deleteall(argc-1, argv+1, 0); |
c7699875 | 1305 | if (matches(*argv, "get") == 0) |
1306 | return xfrm_state_get_or_delete(argc-1, argv+1, 0); | |
9bec1a43 SH |
1307 | if (matches(*argv, "flush") == 0) |
1308 | return xfrm_state_flush(argc-1, argv+1); | |
0bb4a4c2 | 1309 | if (matches(*argv, "count") == 0) { |
1310 | return xfrm_sad_getinfo(argc, argv); | |
1311 | } | |
c7699875 | 1312 | if (matches(*argv, "help") == 0) |
1313 | usage(); | |
1314 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv); | |
1315 | exit(-1); | |
1316 | } |