]>
Commit | Line | Data |
---|---|---|
c7699875 | 1 | /* $USAGI: $ */ |
2 | ||
3 | /* | |
4 | * Copyright (C)2004 USAGI/WIDE Project | |
5 | * | |
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. | |
10 | * | |
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. | |
15 | * | |
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 | |
53 | ||
54 | static void usage(void) __attribute__((noreturn)); | |
55 | ||
56 | static void usage(void) | |
57 | { | |
58 | fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n"); | |
fb7399b2 | 59 | fprintf(stderr, " [ reqid REQID ] [ seq SEQ ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n"); |
5cf576d9 | 60 | fprintf(stderr, " [ encap ENCAP ] [ sel SELECTOR ] [ LIMIT-LIST ]\n"); |
fb7399b2 | 61 | fprintf(stderr, "Usage: ip xfrm state allocspi ID [ mode MODE ] [ reqid REQID ] [ seq SEQ ]\n"); |
62 | fprintf(stderr, " [ min SPI max SPI ]\n"); | |
c7699875 | 63 | fprintf(stderr, "Usage: ip xfrm state { delete | get } ID\n"); |
9bec1a43 | 64 | fprintf(stderr, "Usage: ip xfrm state { deleteall | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n"); |
eaa34ee3 | 65 | fprintf(stderr, " [ flag FLAG_LIST ]\n"); |
9bec1a43 | 66 | fprintf(stderr, "Usage: ip xfrm state flush [ proto XFRM_PROTO ]\n"); |
c7699875 | 67 | |
68 | fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); | |
29aa4dd7 | 69 | //fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n"); |
9e566a46 | 70 | fprintf(stderr, "XFRM_PROTO := [ "); |
29aa4dd7 | 71 | fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP)); |
72 | fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH)); | |
73 | fprintf(stderr, "%s ", strxf_xfrmproto(IPPROTO_COMP)); | |
7809c616 | 74 | fprintf(stderr, "]\n"); |
9e566a46 | 75 | |
c7699875 | 76 | //fprintf(stderr, "SPI - security parameter index(default=0)\n"); |
77 | ||
34e099e2 | 78 | fprintf(stderr, "MODE := [ transport | tunnel | beet ](default=transport)\n"); |
c7699875 | 79 | //fprintf(stderr, "REQID - number(default=0)\n"); |
80 | ||
eaa34ee3 | 81 | fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); |
82 | fprintf(stderr, "FLAG := [ noecn | decap-dscp ]\n"); | |
5cf576d9 SH |
83 | |
84 | fprintf(stderr, "ENCAP := ENCAP-TYPE SPORT DPORT OADDR\n"); | |
85 | fprintf(stderr, "ENCAP-TYPE := espinudp | espinudp-nonike\n"); | |
c7699875 | 86 | |
7809c616 | 87 | fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] | [ ALGO ]\n"); |
c7699875 | 88 | fprintf(stderr, "ALGO := ALGO_TYPE ALGO_NAME ALGO_KEY\n"); |
7809c616 | 89 | fprintf(stderr, "ALGO_TYPE := [ "); |
90 | fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT)); | |
91 | fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AUTH)); | |
92 | fprintf(stderr, "%s ", strxf_algotype(XFRMA_ALG_COMP)); | |
93 | fprintf(stderr, "]\n"); | |
94 | ||
c7699875 | 95 | //fprintf(stderr, "ALGO_NAME - algorithm name\n"); |
96 | //fprintf(stderr, "ALGO_KEY - algorithm key\n"); | |
97 | ||
eaa34ee3 | 98 | fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ UPSPEC ] [ dev DEV ]\n"); |
c7699875 | 99 | |
c70b36d2 | 100 | fprintf(stderr, "UPSPEC := proto PROTO [ [ sport PORT ] [ dport PORT ] |\n"); |
101 | fprintf(stderr, " [ type NUMBER ] [ code NUMBER ] ]\n"); | |
102 | ||
c7699875 | 103 | |
104 | //fprintf(stderr, "DEV - device name(default=none)\n"); | |
105 | fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n"); | |
106 | fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n"); | |
107 | fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] COUNT ]\n"); | |
108 | exit(-1); | |
109 | } | |
110 | ||
111 | static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type, | |
112 | char *name, char *key, int max) | |
113 | { | |
114 | int len; | |
7809c616 | 115 | int slen = strlen(key); |
c7699875 | 116 | |
eaa34ee3 | 117 | #if 0 |
c7699875 | 118 | /* XXX: verifying both name and key is required! */ |
119 | fprintf(stderr, "warning: ALGONAME/ALGOKEY will send to kernel promiscuously!(verifying them isn't implemented yet)\n"); | |
120 | #endif | |
121 | ||
122 | strncpy(alg->alg_name, name, sizeof(alg->alg_name)); | |
123 | ||
7809c616 | 124 | if (slen > 2 && strncmp(key, "0x", 2) == 0) { |
54f7328a | 125 | /* split two chars "0x" from the top */ |
126 | char *p = key + 2; | |
127 | int plen = slen - 2; | |
128 | int i; | |
129 | int j; | |
130 | ||
131 | /* Converting hexadecimal numbered string into real key; | |
132 | * Convert each two chars into one char(value). If number | |
133 | * of the length is odd, add zero on the top for rounding. | |
c7699875 | 134 | */ |
7809c616 | 135 | |
54f7328a | 136 | /* calculate length of the converted values(real key) */ |
137 | len = (plen + 1) / 2; | |
138 | if (len > max) | |
139 | invarg("\"ALGOKEY\" makes buffer overflow\n", key); | |
c7699875 | 140 | |
54f7328a | 141 | for (i = - (plen % 2), j = 0; j < len; i += 2, j++) { |
142 | char vbuf[3]; | |
737f15f6 | 143 | __u8 val; |
c7699875 | 144 | |
54f7328a | 145 | vbuf[0] = i >= 0 ? p[i] : '0'; |
146 | vbuf[1] = p[i + 1]; | |
147 | vbuf[2] = '\0'; | |
c7699875 | 148 | |
54f7328a | 149 | if (get_u8(&val, vbuf, 16)) |
150 | invarg("\"ALGOKEY\" is invalid", key); | |
7809c616 | 151 | |
54f7328a | 152 | alg->alg_key[j] = val; |
c7699875 | 153 | } |
c7699875 | 154 | } else { |
7809c616 | 155 | len = slen; |
c7699875 | 156 | if (len > 0) { |
157 | if (len > max) | |
158 | invarg("\"ALGOKEY\" makes buffer overflow\n", key); | |
159 | ||
160 | strncpy(alg->alg_key, key, len); | |
161 | } | |
162 | } | |
163 | ||
164 | alg->alg_key_len = len * 8; | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
fb7399b2 | 169 | static int xfrm_seq_parse(__u32 *seq, int *argcp, char ***argvp) |
170 | { | |
171 | int argc = *argcp; | |
172 | char **argv = *argvp; | |
173 | ||
174 | if (get_u32(seq, *argv, 0)) | |
175 | invarg("\"SEQ\" is invalid", *argv); | |
176 | ||
177 | *seq = htonl(*seq); | |
178 | ||
179 | *argcp = argc; | |
180 | *argvp = argv; | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
c7699875 | 185 | static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) |
186 | { | |
187 | int argc = *argcp; | |
188 | char **argv = *argvp; | |
9e566a46 | 189 | int len = strlen(*argv); |
190 | ||
191 | if (len > 2 && strncmp(*argv, "0x", 2) == 0) { | |
192 | __u8 val = 0; | |
c7699875 | 193 | |
9e566a46 | 194 | if (get_u8(&val, *argv, 16)) |
195 | invarg("\"FLAG\" is invalid", *argv); | |
196 | *flags = val; | |
197 | } else { | |
eaa34ee3 | 198 | while (1) { |
199 | if (strcmp(*argv, "noecn") == 0) | |
200 | *flags |= XFRM_STATE_NOECN; | |
201 | else if (strcmp(*argv, "decap-dscp") == 0) | |
202 | *flags |= XFRM_STATE_DECAP_DSCP; | |
203 | else { | |
204 | PREV_ARG(); /* back track */ | |
205 | break; | |
206 | } | |
207 | ||
208 | if (!NEXT_ARG_OK()) | |
209 | break; | |
210 | NEXT_ARG(); | |
211 | } | |
9e566a46 | 212 | } |
c7699875 | 213 | |
214 | filter.state_flags_mask = XFRM_FILTER_MASK_FULL; | |
215 | ||
216 | *argcp = argc; | |
217 | *argvp = argv; | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) | |
223 | { | |
224 | struct rtnl_handle rth; | |
225 | struct { | |
226 | struct nlmsghdr n; | |
227 | struct xfrm_usersa_info xsinfo; | |
228 | char buf[RTA_BUF_SIZE]; | |
229 | } req; | |
230 | char *idp = NULL; | |
231 | char *ealgop = NULL; | |
232 | char *aalgop = NULL; | |
233 | char *calgop = NULL; | |
234 | ||
235 | memset(&req, 0, sizeof(req)); | |
236 | ||
237 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); | |
238 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; | |
239 | req.n.nlmsg_type = cmd; | |
240 | req.xsinfo.family = preferred_family; | |
241 | ||
242 | req.xsinfo.lft.soft_byte_limit = XFRM_INF; | |
243 | req.xsinfo.lft.hard_byte_limit = XFRM_INF; | |
244 | req.xsinfo.lft.soft_packet_limit = XFRM_INF; | |
245 | req.xsinfo.lft.hard_packet_limit = XFRM_INF; | |
246 | ||
247 | while (argc > 0) { | |
7809c616 | 248 | if (strcmp(*argv, "mode") == 0) { |
c7699875 | 249 | NEXT_ARG(); |
250 | xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); | |
251 | } else if (strcmp(*argv, "reqid") == 0) { | |
252 | NEXT_ARG(); | |
253 | xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); | |
fb7399b2 | 254 | } else if (strcmp(*argv, "seq") == 0) { |
255 | NEXT_ARG(); | |
256 | xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv); | |
eaa34ee3 | 257 | } else if (strcmp(*argv, "replay-window") == 0) { |
258 | NEXT_ARG(); | |
259 | if (get_u8(&req.xsinfo.replay_window, *argv, 0)) | |
260 | invarg("\"replay-window\" value is invalid", *argv); | |
c7699875 | 261 | } else if (strcmp(*argv, "flag") == 0) { |
262 | NEXT_ARG(); | |
263 | xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); | |
264 | } else if (strcmp(*argv, "sel") == 0) { | |
265 | NEXT_ARG(); | |
266 | xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); | |
c7699875 | 267 | } else if (strcmp(*argv, "limit") == 0) { |
268 | NEXT_ARG(); | |
269 | xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); | |
5cf576d9 SH |
270 | } else if (strcmp(*argv, "encap") == 0) { |
271 | struct xfrm_encap_tmpl encap; | |
272 | inet_prefix oa; | |
273 | NEXT_ARG(); | |
274 | xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); | |
275 | NEXT_ARG(); | |
276 | if (get_u16(&encap.encap_sport, *argv, 0)) | |
277 | invarg("\"encap\" sport value is invalid", *argv); | |
278 | encap.encap_sport = htons(encap.encap_sport); | |
279 | NEXT_ARG(); | |
280 | if (get_u16(&encap.encap_dport, *argv, 0)) | |
281 | invarg("\"encap\" dport value is invalid", *argv); | |
282 | encap.encap_dport = htons(encap.encap_dport); | |
283 | NEXT_ARG(); | |
284 | get_addr(&oa, *argv, AF_UNSPEC); | |
285 | memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); | |
286 | addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, | |
287 | (void *)&encap, sizeof(encap)); | |
c7699875 | 288 | } else { |
7809c616 | 289 | /* try to assume ALGO */ |
290 | int type = xfrm_algotype_getbyname(*argv); | |
291 | switch (type) { | |
292 | case XFRMA_ALG_CRYPT: | |
293 | case XFRMA_ALG_AUTH: | |
294 | case XFRMA_ALG_COMP: | |
295 | { | |
296 | /* ALGO */ | |
297 | struct { | |
298 | struct xfrm_algo alg; | |
299 | char buf[XFRM_ALGO_KEY_BUF_SIZE]; | |
300 | } alg; | |
301 | int len; | |
302 | char *name; | |
303 | char *key; | |
304 | ||
305 | switch (type) { | |
306 | case XFRMA_ALG_CRYPT: | |
307 | if (ealgop) | |
308 | duparg("ALGOTYPE", *argv); | |
309 | ealgop = *argv; | |
310 | break; | |
311 | case XFRMA_ALG_AUTH: | |
312 | if (aalgop) | |
313 | duparg("ALGOTYPE", *argv); | |
314 | aalgop = *argv; | |
315 | break; | |
316 | case XFRMA_ALG_COMP: | |
317 | if (calgop) | |
318 | duparg("ALGOTYPE", *argv); | |
319 | calgop = *argv; | |
320 | break; | |
321 | default: | |
322 | /* not reached */ | |
323 | invarg("\"ALGOTYPE\" is invalid\n", *argv); | |
324 | } | |
325 | ||
326 | if (!NEXT_ARG_OK()) | |
327 | missarg("ALGONAME"); | |
328 | NEXT_ARG(); | |
329 | name = *argv; | |
330 | ||
331 | if (!NEXT_ARG_OK()) | |
332 | missarg("ALGOKEY"); | |
333 | NEXT_ARG(); | |
334 | key = *argv; | |
335 | ||
336 | memset(&alg, 0, sizeof(alg)); | |
337 | ||
338 | xfrm_algo_parse((void *)&alg, type, name, key, | |
339 | sizeof(alg.buf)); | |
340 | len = sizeof(struct xfrm_algo) + alg.alg.alg_key_len; | |
341 | ||
342 | addattr_l(&req.n, sizeof(req.buf), type, | |
343 | (void *)&alg, len); | |
344 | break; | |
345 | } | |
346 | default: | |
347 | /* try to assume ID */ | |
348 | if (idp) | |
349 | invarg("unknown", *argv); | |
350 | idp = *argv; | |
351 | ||
352 | /* ID */ | |
353 | xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, | |
354 | &req.xsinfo.family, 0, &argc, &argv); | |
355 | if (preferred_family == AF_UNSPEC) | |
356 | preferred_family = req.xsinfo.family; | |
357 | } | |
c7699875 | 358 | } |
359 | argc--; argv++; | |
360 | } | |
361 | ||
362 | if (!idp) { | |
363 | fprintf(stderr, "Not enough information: \"ID\" is required\n"); | |
364 | exit(1); | |
365 | } | |
366 | ||
367 | if (ealgop || aalgop || calgop) { | |
368 | if (req.xsinfo.id.proto != IPPROTO_ESP && | |
369 | req.xsinfo.id.proto != IPPROTO_AH && | |
370 | req.xsinfo.id.proto != IPPROTO_COMP) { | |
29aa4dd7 | 371 | fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); |
c7699875 | 372 | exit(1); |
373 | } | |
374 | } else { | |
375 | if (req.xsinfo.id.proto == IPPROTO_ESP || | |
376 | req.xsinfo.id.proto == IPPROTO_AH || | |
377 | req.xsinfo.id.proto == IPPROTO_COMP) { | |
29aa4dd7 | 378 | fprintf(stderr, "\"ALGO\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); |
c7699875 | 379 | exit (1); |
380 | } | |
381 | } | |
382 | ||
383 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
384 | exit(1); | |
385 | ||
386 | if (req.xsinfo.family == AF_UNSPEC) | |
387 | req.xsinfo.family = AF_INET; | |
388 | ||
389 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | |
390 | exit(2); | |
391 | ||
392 | rtnl_close(&rth); | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
fb7399b2 | 397 | static int xfrm_state_allocspi(int argc, char **argv) |
398 | { | |
399 | struct rtnl_handle rth; | |
400 | struct { | |
401 | struct nlmsghdr n; | |
402 | struct xfrm_userspi_info xspi; | |
403 | char buf[RTA_BUF_SIZE]; | |
404 | } req; | |
405 | char *idp = NULL; | |
406 | char *minp = NULL; | |
407 | char *maxp = NULL; | |
408 | char res_buf[NLMSG_BUF_SIZE]; | |
409 | struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf; | |
410 | ||
411 | memset(res_buf, 0, sizeof(res_buf)); | |
412 | ||
413 | memset(&req, 0, sizeof(req)); | |
414 | ||
415 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)); | |
416 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
417 | req.n.nlmsg_type = XFRM_MSG_ALLOCSPI; | |
418 | req.xspi.info.family = preferred_family; | |
419 | ||
420 | #if 0 | |
421 | req.xsinfo.lft.soft_byte_limit = XFRM_INF; | |
422 | req.xsinfo.lft.hard_byte_limit = XFRM_INF; | |
423 | req.xsinfo.lft.soft_packet_limit = XFRM_INF; | |
424 | req.xsinfo.lft.hard_packet_limit = XFRM_INF; | |
425 | #endif | |
426 | ||
427 | while (argc > 0) { | |
428 | if (strcmp(*argv, "mode") == 0) { | |
429 | NEXT_ARG(); | |
430 | xfrm_mode_parse(&req.xspi.info.mode, &argc, &argv); | |
431 | } else if (strcmp(*argv, "reqid") == 0) { | |
432 | NEXT_ARG(); | |
433 | xfrm_reqid_parse(&req.xspi.info.reqid, &argc, &argv); | |
434 | } else if (strcmp(*argv, "seq") == 0) { | |
435 | NEXT_ARG(); | |
436 | xfrm_seq_parse(&req.xspi.info.seq, &argc, &argv); | |
437 | } else if (strcmp(*argv, "min") == 0) { | |
438 | if (minp) | |
439 | duparg("min", *argv); | |
440 | minp = *argv; | |
441 | ||
442 | NEXT_ARG(); | |
443 | ||
444 | if (get_u32(&req.xspi.min, *argv, 0)) | |
445 | invarg("\"min\" value is invalid", *argv); | |
446 | } else if (strcmp(*argv, "max") == 0) { | |
447 | if (maxp) | |
448 | duparg("max", *argv); | |
449 | maxp = *argv; | |
450 | ||
451 | NEXT_ARG(); | |
452 | ||
453 | if (get_u32(&req.xspi.max, *argv, 0)) | |
454 | invarg("\"max\" value is invalid", *argv); | |
455 | } else { | |
456 | /* try to assume ID */ | |
457 | if (idp) | |
458 | invarg("unknown", *argv); | |
459 | idp = *argv; | |
460 | ||
461 | /* ID */ | |
462 | xfrm_id_parse(&req.xspi.info.saddr, &req.xspi.info.id, | |
463 | &req.xspi.info.family, 0, &argc, &argv); | |
464 | if (req.xspi.info.id.spi) { | |
465 | fprintf(stderr, "\"SPI\" must be zero\n"); | |
466 | exit(1); | |
467 | } | |
468 | if (preferred_family == AF_UNSPEC) | |
469 | preferred_family = req.xspi.info.family; | |
470 | } | |
471 | argc--; argv++; | |
472 | } | |
473 | ||
474 | if (!idp) { | |
475 | fprintf(stderr, "Not enough information: \"ID\" is required\n"); | |
476 | exit(1); | |
477 | } | |
478 | ||
479 | if (minp) { | |
480 | if (!maxp) { | |
481 | fprintf(stderr, "\"max\" is missing\n"); | |
482 | exit(1); | |
483 | } | |
484 | if (req.xspi.min > req.xspi.max) { | |
485 | fprintf(stderr, "\"min\" valie is larger than \"max\" one\n"); | |
486 | exit(1); | |
487 | } | |
488 | } else { | |
489 | if (maxp) { | |
490 | fprintf(stderr, "\"min\" is missing\n"); | |
491 | exit(1); | |
492 | } | |
493 | ||
494 | /* XXX: Default value defined in PF_KEY; | |
495 | * See kernel's net/key/af_key.c(pfkey_getspi). | |
496 | */ | |
497 | req.xspi.min = 0x100; | |
498 | req.xspi.max = 0x0fffffff; | |
499 | ||
500 | /* XXX: IPCOMP spi is 16-bits; | |
501 | * See kernel's net/xfrm/xfrm_user(verify_userspi_info). | |
502 | */ | |
503 | if (req.xspi.info.id.proto == IPPROTO_COMP) | |
504 | req.xspi.max = 0xffff; | |
505 | } | |
506 | ||
507 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
508 | exit(1); | |
509 | ||
510 | if (req.xspi.info.family == AF_UNSPEC) | |
511 | req.xspi.info.family = AF_INET; | |
512 | ||
513 | ||
514 | if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0) | |
515 | exit(2); | |
516 | ||
517 | if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { | |
518 | fprintf(stderr, "An error :-)\n"); | |
519 | exit(1); | |
520 | } | |
521 | ||
522 | rtnl_close(&rth); | |
523 | ||
524 | return 0; | |
525 | } | |
526 | ||
c7699875 | 527 | static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo) |
528 | { | |
529 | if (!filter.use) | |
530 | return 1; | |
531 | ||
532 | if (filter.id_src_mask) | |
eaa34ee3 | 533 | if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr, |
534 | filter.id_src_mask)) | |
c7699875 | 535 | return 0; |
536 | if (filter.id_dst_mask) | |
eaa34ee3 | 537 | if (xfrm_addr_match(&xsinfo->id.daddr, &filter.xsinfo.id.daddr, |
538 | filter.id_dst_mask)) | |
c7699875 | 539 | return 0; |
540 | if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask) | |
541 | return 0; | |
542 | if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask) | |
543 | return 0; | |
544 | if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask) | |
545 | return 0; | |
546 | if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask) | |
547 | return 0; | |
548 | if (filter.state_flags_mask) | |
549 | if ((xsinfo->flags & filter.xsinfo.flags) == 0) | |
550 | return 0; | |
551 | ||
552 | return 1; | |
553 | } | |
554 | ||
fb7399b2 | 555 | int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n, |
556 | void *arg) | |
c7699875 | 557 | { |
558 | FILE *fp = (FILE*)arg; | |
c7699875 | 559 | struct rtattr * tb[XFRMA_MAX+1]; |
90f93024 | 560 | struct rtattr * rta; |
c595c790 SH |
561 | struct xfrm_usersa_info *xsinfo = NULL; |
562 | struct xfrm_user_expire *xexp = NULL; | |
563 | struct xfrm_usersa_id *xsid = NULL; | |
564 | int len = n->nlmsg_len; | |
c7699875 | 565 | |
566 | if (n->nlmsg_type != XFRM_MSG_NEWSA && | |
90f93024 | 567 | n->nlmsg_type != XFRM_MSG_DELSA && |
669ae748 | 568 | n->nlmsg_type != XFRM_MSG_UPDSA && |
90f93024 | 569 | n->nlmsg_type != XFRM_MSG_EXPIRE) { |
c7699875 | 570 | fprintf(stderr, "Not a state: %08x %08x %08x\n", |
571 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
572 | return 0; | |
573 | } | |
574 | ||
669ae748 SH |
575 | if (n->nlmsg_type == XFRM_MSG_DELSA) { |
576 | /* Dont blame me for this .. Herbert made me do it */ | |
577 | xsid = NLMSG_DATA(n); | |
af1b6a41 | 578 | len -= NLMSG_SPACE(sizeof(*xsid)); |
669ae748 | 579 | } else if (n->nlmsg_type == XFRM_MSG_EXPIRE) { |
90f93024 SH |
580 | xexp = NLMSG_DATA(n); |
581 | xsinfo = &xexp->state; | |
af1b6a41 | 582 | len -= NLMSG_SPACE(sizeof(*xexp)); |
90f93024 SH |
583 | } else { |
584 | xexp = NULL; | |
585 | xsinfo = NLMSG_DATA(n); | |
af1b6a41 | 586 | len -= NLMSG_SPACE(sizeof(*xsinfo)); |
90f93024 SH |
587 | } |
588 | ||
c7699875 | 589 | if (len < 0) { |
590 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
591 | return -1; | |
592 | } | |
593 | ||
669ae748 | 594 | if (xsinfo && !xfrm_state_filter_match(xsinfo)) |
c7699875 | 595 | return 0; |
596 | ||
669ae748 | 597 | if (n->nlmsg_type == XFRM_MSG_DELSA) |
c595c790 | 598 | fprintf(fp, "Deleted "); |
669ae748 SH |
599 | else if (n->nlmsg_type == XFRM_MSG_UPDSA) |
600 | fprintf(fp, "Updated "); | |
c595c790 SH |
601 | else if (n->nlmsg_type == XFRM_MSG_EXPIRE) |
602 | fprintf(fp, "Expired "); | |
603 | ||
669ae748 SH |
604 | if (n->nlmsg_type == XFRM_MSG_DELSA) |
605 | rta = XFRMSID_RTA(xsid); | |
606 | else if (n->nlmsg_type == XFRM_MSG_EXPIRE) | |
90f93024 | 607 | rta = XFRMEXP_RTA(xexp); |
c595c790 | 608 | else |
90f93024 SH |
609 | rta = XFRMS_RTA(xsinfo); |
610 | ||
611 | parse_rtattr(tb, XFRMA_MAX, rta, len); | |
c7699875 | 612 | |
c595c790 | 613 | if (n->nlmsg_type == XFRM_MSG_DELSA) { |
669ae748 SH |
614 | //xfrm_policy_id_print(); |
615 | ||
616 | if (!tb[XFRMA_SA]) { | |
617 | fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n"); | |
618 | return -1; | |
619 | } | |
620 | if (RTA_PAYLOAD(tb[XFRMA_SA]) < sizeof(*xsinfo)) { | |
621 | fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n"); | |
622 | return -1; | |
c595c790 | 623 | } |
669ae748 | 624 | xsinfo = (struct xfrm_usersa_info *)RTA_DATA(tb[XFRMA_SA]); |
c595c790 | 625 | } |
c7699875 | 626 | |
fb7399b2 | 627 | xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL); |
c7699875 | 628 | |
90f93024 SH |
629 | if (n->nlmsg_type == XFRM_MSG_EXPIRE) { |
630 | fprintf(fp, "\t"); | |
631 | fprintf(fp, "hard %u", xexp->hard); | |
632 | fprintf(fp, "%s", _SL_); | |
633 | } | |
634 | ||
7809c616 | 635 | if (oneline) |
636 | fprintf(fp, "\n"); | |
669ae748 | 637 | fflush(fp); |
7809c616 | 638 | |
c7699875 | 639 | return 0; |
640 | } | |
641 | ||
642 | static int xfrm_state_get_or_delete(int argc, char **argv, int delete) | |
643 | { | |
644 | struct rtnl_handle rth; | |
645 | struct { | |
646 | struct nlmsghdr n; | |
647 | struct xfrm_usersa_id xsid; | |
648 | } req; | |
649 | struct xfrm_id id; | |
650 | char *idp = NULL; | |
651 | ||
652 | memset(&req, 0, sizeof(req)); | |
653 | ||
654 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)); | |
655 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
656 | req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA; | |
657 | req.xsid.family = preferred_family; | |
658 | ||
659 | while (argc > 0) { | |
660 | /* | |
661 | * XXX: Source address is not used and ignore it to follow | |
662 | * XXX: a manner of setkey e.g. in the case of deleting/getting | |
663 | * XXX: message of IPsec SA. | |
664 | */ | |
665 | xfrm_address_t ignore_saddr; | |
666 | ||
667 | if (idp) | |
668 | invarg("unknown", *argv); | |
669 | idp = *argv; | |
670 | ||
671 | /* ID */ | |
672 | memset(&id, 0, sizeof(id)); | |
7809c616 | 673 | xfrm_id_parse(&ignore_saddr, &id, &req.xsid.family, 0, |
c7699875 | 674 | &argc, &argv); |
675 | ||
676 | memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr)); | |
677 | req.xsid.spi = id.spi; | |
678 | req.xsid.proto = id.proto; | |
679 | ||
680 | argc--; argv++; | |
681 | } | |
682 | ||
683 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
684 | exit(1); | |
685 | ||
686 | if (req.xsid.family == AF_UNSPEC) | |
687 | req.xsid.family = AF_INET; | |
688 | ||
689 | if (delete) { | |
690 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | |
691 | exit(2); | |
692 | } else { | |
693 | char buf[NLMSG_BUF_SIZE]; | |
694 | struct nlmsghdr *res_n = (struct nlmsghdr *)buf; | |
695 | ||
696 | memset(buf, 0, sizeof(buf)); | |
697 | ||
698 | if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0) | |
699 | exit(2); | |
700 | ||
701 | if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { | |
702 | fprintf(stderr, "An error :-)\n"); | |
703 | exit(1); | |
704 | } | |
705 | } | |
706 | ||
707 | rtnl_close(&rth); | |
708 | ||
709 | return 0; | |
710 | } | |
711 | ||
712 | /* | |
713 | * With an existing state of nlmsg, make new nlmsg for deleting the state | |
714 | * and store it to buffer. | |
715 | */ | |
6dc9f016 | 716 | static int xfrm_state_keep(const struct sockaddr_nl *who, |
50772dc5 | 717 | struct nlmsghdr *n, |
6dc9f016 | 718 | void *arg) |
c7699875 | 719 | { |
720 | struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; | |
721 | struct rtnl_handle *rth = xb->rth; | |
722 | struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); | |
723 | int len = n->nlmsg_len; | |
724 | struct nlmsghdr *new_n; | |
725 | struct xfrm_usersa_id *xsid; | |
726 | ||
727 | if (n->nlmsg_type != XFRM_MSG_NEWSA) { | |
728 | fprintf(stderr, "Not a state: %08x %08x %08x\n", | |
729 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
730 | return 0; | |
731 | } | |
732 | ||
733 | len -= NLMSG_LENGTH(sizeof(*xsinfo)); | |
734 | if (len < 0) { | |
735 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
736 | return -1; | |
737 | } | |
738 | ||
739 | if (!xfrm_state_filter_match(xsinfo)) | |
740 | return 0; | |
741 | ||
742 | if (xb->offset > xb->size) { | |
9bec1a43 | 743 | fprintf(stderr, "State buffer overflow\n"); |
c7699875 | 744 | return -1; |
745 | } | |
746 | ||
747 | new_n = (struct nlmsghdr *)(xb->buf + xb->offset); | |
748 | new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid)); | |
749 | new_n->nlmsg_flags = NLM_F_REQUEST; | |
750 | new_n->nlmsg_type = XFRM_MSG_DELSA; | |
751 | new_n->nlmsg_seq = ++rth->seq; | |
752 | ||
753 | xsid = NLMSG_DATA(new_n); | |
754 | xsid->family = xsinfo->family; | |
755 | memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr)); | |
756 | xsid->spi = xsinfo->id.spi; | |
757 | xsid->proto = xsinfo->id.proto; | |
758 | ||
759 | xb->offset += new_n->nlmsg_len; | |
760 | xb->nlmsg_count ++; | |
761 | ||
762 | return 0; | |
763 | } | |
764 | ||
9bec1a43 | 765 | static int xfrm_state_list_or_deleteall(int argc, char **argv, int deleteall) |
c7699875 | 766 | { |
767 | char *idp = NULL; | |
768 | struct rtnl_handle rth; | |
769 | ||
bd641cd6 | 770 | if(argc > 0) |
771 | filter.use = 1; | |
c7699875 | 772 | filter.xsinfo.family = preferred_family; |
773 | ||
774 | while (argc > 0) { | |
775 | if (strcmp(*argv, "mode") == 0) { | |
776 | NEXT_ARG(); | |
777 | xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv); | |
778 | ||
779 | filter.mode_mask = XFRM_FILTER_MASK_FULL; | |
780 | ||
781 | } else if (strcmp(*argv, "reqid") == 0) { | |
782 | NEXT_ARG(); | |
783 | xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv); | |
784 | ||
785 | filter.reqid_mask = XFRM_FILTER_MASK_FULL; | |
786 | ||
787 | } else if (strcmp(*argv, "flag") == 0) { | |
788 | NEXT_ARG(); | |
789 | xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv); | |
790 | ||
791 | filter.state_flags_mask = XFRM_FILTER_MASK_FULL; | |
792 | ||
793 | } else { | |
794 | if (idp) | |
795 | invarg("unknown", *argv); | |
796 | idp = *argv; | |
797 | ||
798 | /* ID */ | |
7809c616 | 799 | xfrm_id_parse(&filter.xsinfo.saddr, &filter.xsinfo.id, |
800 | &filter.xsinfo.family, 1, &argc, &argv); | |
c7699875 | 801 | if (preferred_family == AF_UNSPEC) |
802 | preferred_family = filter.xsinfo.family; | |
803 | } | |
804 | argc--; argv++; | |
805 | } | |
806 | ||
807 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) | |
808 | exit(1); | |
809 | ||
9bec1a43 | 810 | if (deleteall) { |
c7699875 | 811 | struct xfrm_buffer xb; |
9bec1a43 | 812 | char buf[NLMSG_DELETEALL_BUF_SIZE]; |
c7699875 | 813 | int i; |
814 | ||
815 | xb.buf = buf; | |
816 | xb.size = sizeof(buf); | |
817 | xb.rth = &rth; | |
818 | ||
819 | for (i = 0; ; i++) { | |
820 | xb.offset = 0; | |
821 | xb.nlmsg_count = 0; | |
822 | ||
823 | if (show_stats > 1) | |
9bec1a43 | 824 | fprintf(stderr, "Delete-all round = %d\n", i); |
c7699875 | 825 | |
826 | if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { | |
827 | perror("Cannot send dump request"); | |
828 | exit(1); | |
829 | } | |
830 | ||
831 | if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb, NULL, NULL) < 0) { | |
9bec1a43 | 832 | fprintf(stderr, "Delete-all terminated\n"); |
c7699875 | 833 | exit(1); |
834 | } | |
835 | if (xb.nlmsg_count == 0) { | |
836 | if (show_stats > 1) | |
9bec1a43 | 837 | fprintf(stderr, "Delete-all completed\n"); |
c7699875 | 838 | break; |
839 | } | |
840 | ||
841 | if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { | |
9bec1a43 | 842 | perror("Failed to send delete-all request\n"); |
c7699875 | 843 | exit(1); |
844 | } | |
845 | if (show_stats > 1) | |
9bec1a43 | 846 | fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count); |
c7699875 | 847 | |
848 | xb.offset = 0; | |
849 | xb.nlmsg_count = 0; | |
850 | } | |
851 | ||
852 | } else { | |
853 | if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { | |
854 | perror("Cannot send dump request"); | |
855 | exit(1); | |
856 | } | |
857 | ||
858 | if (rtnl_dump_filter(&rth, xfrm_state_print, stdout, NULL, NULL) < 0) { | |
859 | fprintf(stderr, "Dump terminated\n"); | |
860 | exit(1); | |
861 | } | |
862 | } | |
863 | ||
864 | rtnl_close(&rth); | |
865 | ||
866 | exit(0); | |
867 | } | |
868 | ||
9bec1a43 | 869 | static int xfrm_state_flush(int argc, char **argv) |
bd641cd6 | 870 | { |
871 | struct rtnl_handle rth; | |
872 | struct { | |
873 | struct nlmsghdr n; | |
874 | struct xfrm_usersa_flush xsf; | |
875 | } req; | |
9bec1a43 | 876 | char *protop = NULL; |
bd641cd6 | 877 | |
878 | memset(&req, 0, sizeof(req)); | |
879 | ||
880 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)); | |
881 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
882 | req.n.nlmsg_type = XFRM_MSG_FLUSHSA; | |
883 | req.xsf.proto = IPSEC_PROTO_ANY; | |
884 | ||
9bec1a43 SH |
885 | while (argc > 0) { |
886 | if (strcmp(*argv, "proto") == 0) { | |
887 | int ret; | |
888 | ||
889 | if (protop) | |
890 | duparg("proto", *argv); | |
891 | protop = *argv; | |
892 | ||
893 | NEXT_ARG(); | |
894 | ||
895 | ret = xfrm_xfrmproto_getbyname(*argv); | |
896 | if (ret < 0) | |
897 | invarg("\"XFRM_PROTO\" is invalid", *argv); | |
898 | ||
899 | req.xsf.proto = (__u8)ret; | |
900 | } else | |
901 | invarg("unknown", *argv); | |
902 | ||
903 | argc--; argv++; | |
904 | } | |
905 | ||
bd641cd6 | 906 | if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) |
907 | exit(1); | |
908 | ||
909 | if (show_stats > 1) | |
9bec1a43 SH |
910 | fprintf(stderr, "Flush state proto=%s\n", |
911 | (req.xsf.proto == IPSEC_PROTO_ANY) ? "any" : | |
912 | strxf_xfrmproto(req.xsf.proto)); | |
bd641cd6 | 913 | |
914 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | |
915 | exit(2); | |
916 | ||
917 | rtnl_close(&rth); | |
918 | ||
919 | return 0; | |
920 | } | |
921 | ||
c7699875 | 922 | int do_xfrm_state(int argc, char **argv) |
923 | { | |
924 | if (argc < 1) | |
9bec1a43 | 925 | return xfrm_state_list_or_deleteall(0, NULL, 0); |
c7699875 | 926 | |
927 | if (matches(*argv, "add") == 0) | |
928 | return xfrm_state_modify(XFRM_MSG_NEWSA, 0, | |
929 | argc-1, argv+1); | |
930 | if (matches(*argv, "update") == 0) | |
931 | return xfrm_state_modify(XFRM_MSG_UPDSA, 0, | |
932 | argc-1, argv+1); | |
fb7399b2 | 933 | if (matches(*argv, "allocspi") == 0) |
934 | return xfrm_state_allocspi(argc-1, argv+1); | |
9bec1a43 | 935 | if (matches(*argv, "delete") == 0) |
c7699875 | 936 | return xfrm_state_get_or_delete(argc-1, argv+1, 1); |
9bec1a43 SH |
937 | if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0) |
938 | return xfrm_state_list_or_deleteall(argc-1, argv+1, 1); | |
c7699875 | 939 | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 |
940 | || matches(*argv, "lst") == 0) | |
9bec1a43 | 941 | return xfrm_state_list_or_deleteall(argc-1, argv+1, 0); |
c7699875 | 942 | if (matches(*argv, "get") == 0) |
943 | return xfrm_state_get_or_delete(argc-1, argv+1, 0); | |
9bec1a43 SH |
944 | if (matches(*argv, "flush") == 0) |
945 | return xfrm_state_flush(argc-1, argv+1); | |
c7699875 | 946 | if (matches(*argv, "help") == 0) |
947 | usage(); | |
948 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv); | |
949 | exit(-1); | |
950 | } |