]>
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 ip.c, 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 <sys/types.h> | |
32 | #include <sys/socket.h> | |
33 | #include <time.h> | |
34 | #include <netdb.h> | |
35 | #include <net/if.h> | |
36 | #include <linux/netlink.h> | |
37 | #include <linux/rtnetlink.h> | |
38 | #include <linux/xfrm.h> | |
39 | ||
40 | #include "utils.h" | |
41 | #include "xfrm.h" | |
42 | ||
43 | struct xfrm_filter filter; | |
44 | ||
45 | static void usage(void) __attribute__((noreturn)); | |
46 | ||
47 | static void usage(void) | |
48 | { | |
49 | fprintf(stderr, | |
50 | "Usage: ip xfrm XFRM_OBJECT { COMMAND | help }\n" | |
51 | "where XFRM_OBJECT := { state | policy }\n"); | |
52 | exit(-1); | |
53 | } | |
54 | ||
7809c616 | 55 | struct typeent { |
56 | const char *t_name; | |
57 | int t_type; | |
58 | }; | |
59 | ||
29aa4dd7 | 60 | static const struct typeent xfrmproto_types[]= { |
61 | { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, | |
62 | { "comp", IPPROTO_COMP }, { NULL, -1 } | |
63 | }; | |
64 | ||
65 | int xfrm_xfrmproto_getbyname(char *name) | |
66 | { | |
67 | int i; | |
68 | ||
69 | for (i = 0; ; i++) { | |
70 | const struct typeent *t = &xfrmproto_types[i]; | |
71 | if (!t->t_name || t->t_type == -1) | |
72 | break; | |
73 | ||
74 | if (strcmp(t->t_name, name) == 0) | |
75 | return t->t_type; | |
76 | } | |
77 | ||
78 | return -1; | |
79 | } | |
80 | ||
81 | const char *strxf_xfrmproto(__u8 proto) | |
82 | { | |
83 | int i; | |
84 | ||
85 | for (i = 0; ; i++) { | |
86 | const struct typeent *t = &xfrmproto_types[i]; | |
87 | if (!t->t_name || t->t_type == -1) | |
88 | break; | |
89 | ||
90 | if (t->t_type == proto) | |
91 | return t->t_name; | |
92 | } | |
93 | ||
94 | return NULL; | |
95 | } | |
96 | ||
7809c616 | 97 | static const struct typeent algo_types[]= { |
98 | { "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH }, | |
99 | { "comp", XFRMA_ALG_COMP }, { NULL, -1 } | |
100 | }; | |
101 | ||
102 | int xfrm_algotype_getbyname(char *name) | |
103 | { | |
104 | int i; | |
105 | ||
106 | for (i = 0; ; i++) { | |
107 | const struct typeent *t = &algo_types[i]; | |
108 | if (!t->t_name || t->t_type == -1) | |
109 | break; | |
110 | ||
111 | if (strcmp(t->t_name, name) == 0) | |
112 | return t->t_type; | |
113 | } | |
114 | ||
115 | return -1; | |
116 | } | |
117 | ||
118 | const char *strxf_algotype(int type) | |
119 | { | |
120 | int i; | |
121 | ||
122 | for (i = 0; ; i++) { | |
123 | const struct typeent *t = &algo_types[i]; | |
124 | if (!t->t_name || t->t_type == -1) | |
125 | break; | |
126 | ||
127 | if (t->t_type == type) | |
128 | return t->t_name; | |
129 | } | |
130 | ||
131 | return NULL; | |
132 | } | |
133 | ||
c7699875 | 134 | const char *strxf_flags(__u8 flags) |
135 | { | |
136 | static char str[16]; | |
137 | const int sn = sizeof(flags) * 8 - 1; | |
138 | __u8 b; | |
139 | int i = 0; | |
140 | ||
141 | for (b = (1 << sn); b > 0; b >>= 1) | |
142 | str[i++] = ((b & flags) ? '1' : '0'); | |
143 | str[i] = '\0'; | |
144 | ||
145 | return str; | |
146 | } | |
147 | ||
148 | const char *strxf_share(__u8 share) | |
149 | { | |
150 | static char str[32]; | |
151 | ||
152 | switch (share) { | |
153 | case XFRM_SHARE_ANY: | |
154 | strcpy(str, "any"); | |
155 | break; | |
156 | case XFRM_SHARE_SESSION: | |
157 | strcpy(str, "session"); | |
158 | break; | |
159 | case XFRM_SHARE_USER: | |
160 | strcpy(str, "user"); | |
161 | break; | |
162 | case XFRM_SHARE_UNIQUE: | |
163 | strcpy(str, "unique"); | |
164 | break; | |
165 | default: | |
7809c616 | 166 | sprintf(str, "%d", share); |
c7699875 | 167 | break; |
168 | } | |
169 | ||
170 | return str; | |
171 | } | |
172 | ||
ad273962 | 173 | const char *strxf_proto(__u8 proto) |
174 | { | |
175 | static char buf[32]; | |
176 | struct protoent *pp; | |
177 | const char *p; | |
178 | ||
179 | pp = getprotobynumber(proto); | |
180 | if (pp) | |
181 | p = pp->p_name; | |
182 | else { | |
183 | sprintf(buf, "%d", proto); | |
184 | p = buf; | |
185 | } | |
186 | ||
187 | return p; | |
188 | } | |
189 | ||
c7699875 | 190 | void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, |
191 | __u8 mode, __u32 reqid, __u16 family, FILE *fp, | |
192 | const char *prefix) | |
193 | { | |
194 | char abuf[256]; | |
195 | __u32 spi; | |
c7699875 | 196 | |
197 | if (prefix) | |
198 | fprintf(fp, prefix); | |
199 | ||
200 | memset(abuf, '\0', sizeof(abuf)); | |
ad273962 | 201 | fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), |
202 | saddr, abuf, sizeof(abuf))); | |
c7699875 | 203 | memset(abuf, '\0', sizeof(abuf)); |
7809c616 | 204 | fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), |
205 | &id->daddr, abuf, sizeof(abuf))); | |
206 | fprintf(fp, "%s", _SL_); | |
c7699875 | 207 | |
208 | if (prefix) | |
209 | fprintf(fp, prefix); | |
210 | fprintf(fp, "\t"); | |
211 | ||
29aa4dd7 | 212 | fprintf(fp, "proto %s ", strxf_xfrmproto(id->proto)); |
c7699875 | 213 | |
ad273962 | 214 | spi = ntohl(id->spi); |
7809c616 | 215 | fprintf(fp, "spi 0x%08x", spi); |
ad273962 | 216 | if (show_stats > 0) |
7809c616 | 217 | fprintf(fp, "(%u)", spi); |
ad273962 | 218 | fprintf(fp, " "); |
c7699875 | 219 | |
ad273962 | 220 | fprintf(fp, "reqid %u", reqid); |
221 | if (show_stats > 0) | |
222 | fprintf(fp, "(0x%08x)", reqid); | |
223 | fprintf(fp, " "); | |
c7699875 | 224 | |
7809c616 | 225 | fprintf(fp, "mode "); |
226 | switch (mode) { | |
227 | case 0: | |
228 | fprintf(fp, "transport"); | |
229 | break; | |
230 | case 1: | |
231 | fprintf(fp, "tunnel"); | |
232 | break; | |
233 | default: | |
234 | fprintf(fp, "%u", mode); | |
235 | break; | |
236 | } | |
237 | fprintf(fp, "%s", _SL_); | |
c7699875 | 238 | } |
239 | ||
240 | static const char *strxf_limit(__u64 limit) | |
241 | { | |
242 | static char str[32]; | |
243 | if (limit == XFRM_INF) | |
244 | strcpy(str, "(INF)"); | |
245 | else | |
b906243b | 246 | sprintf(str, "%llu", (unsigned long long) limit); |
c7699875 | 247 | |
248 | return str; | |
249 | } | |
250 | ||
251 | void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix) | |
252 | { | |
253 | if (prefix) | |
254 | fprintf(fp, prefix); | |
7809c616 | 255 | fprintf(fp, "stats:"); |
256 | fprintf(fp, "%s", _SL_); | |
c7699875 | 257 | |
258 | if (prefix) | |
259 | fprintf(fp, prefix); | |
260 | fprintf(fp, " "); | |
261 | fprintf(fp, "replay-window %d ", s->replay_window); | |
262 | fprintf(fp, "replay %d ", s->replay); | |
263 | fprintf(fp, "failed %d", s->integrity_failed); | |
7809c616 | 264 | fprintf(fp, "%s", _SL_); |
c7699875 | 265 | } |
266 | ||
267 | static const char *strxf_time(__u64 time) | |
268 | { | |
269 | static char str[32]; | |
c7699875 | 270 | |
7809c616 | 271 | if (time == 0) |
272 | strcpy(str, "-"); | |
273 | else { | |
274 | time_t t; | |
275 | struct tm *tp; | |
276 | ||
277 | /* XXX: treat time in the same manner of kernel's | |
278 | * net/xfrm/xfrm_{user,state}.c | |
279 | */ | |
c7699875 | 280 | t = (long)time; |
281 | tp = localtime(&t); | |
282 | ||
44d3eb25 | 283 | strftime(str, sizeof(str), "%Y-%m-%d %T", tp); |
c7699875 | 284 | } |
285 | ||
286 | return str; | |
287 | } | |
288 | ||
289 | void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, | |
290 | struct xfrm_lifetime_cur *cur, | |
291 | FILE *fp, const char *prefix) | |
292 | { | |
293 | if (cfg) { | |
294 | if (prefix) | |
295 | fprintf(fp, prefix); | |
7809c616 | 296 | fprintf(fp, "lifetime config:"); |
297 | fprintf(fp, "%s", _SL_); | |
c7699875 | 298 | |
299 | if (prefix) | |
300 | fprintf(fp, prefix); | |
301 | fprintf(fp, " "); | |
302 | fprintf(fp, "limit: "); | |
303 | fprintf(fp, "soft "); | |
304 | fprintf(fp, strxf_limit(cfg->soft_byte_limit)); | |
305 | fprintf(fp, "(bytes), hard "); | |
306 | fprintf(fp, strxf_limit(cfg->hard_byte_limit)); | |
7809c616 | 307 | fprintf(fp, "(bytes)"); |
308 | fprintf(fp, "%s", _SL_); | |
c7699875 | 309 | |
310 | if (prefix) | |
311 | fprintf(fp, prefix); | |
312 | fprintf(fp, " "); | |
313 | fprintf(fp, "limit: "); | |
314 | fprintf(fp, "soft "); | |
315 | fprintf(fp, strxf_limit(cfg->soft_packet_limit)); | |
316 | fprintf(fp, "(packets), hard "); | |
317 | fprintf(fp, strxf_limit(cfg->hard_packet_limit)); | |
7809c616 | 318 | fprintf(fp, "(packets)"); |
319 | fprintf(fp, "%s", _SL_); | |
c7699875 | 320 | |
321 | if (prefix) | |
322 | fprintf(fp, prefix); | |
323 | fprintf(fp, " "); | |
324 | fprintf(fp, "expire add: "); | |
325 | fprintf(fp, "soft "); | |
b906243b | 326 | fprintf(fp, "%llu", (unsigned long long) cfg->soft_add_expires_seconds); |
c7699875 | 327 | fprintf(fp, "(sec), hard "); |
b906243b | 328 | fprintf(fp, "%llu", (unsigned long long) cfg->hard_add_expires_seconds); |
7809c616 | 329 | fprintf(fp, "(sec)"); |
330 | fprintf(fp, "%s", _SL_); | |
c7699875 | 331 | |
332 | if (prefix) | |
333 | fprintf(fp, prefix); | |
334 | fprintf(fp, " "); | |
335 | fprintf(fp, "expire use: "); | |
336 | fprintf(fp, "soft "); | |
b906243b | 337 | fprintf(fp, "%llu", (unsigned long long) cfg->soft_use_expires_seconds); |
c7699875 | 338 | fprintf(fp, "(sec), hard "); |
b906243b | 339 | fprintf(fp, "%llu", (unsigned long long) cfg->hard_use_expires_seconds); |
7809c616 | 340 | fprintf(fp, "(sec)"); |
341 | fprintf(fp, "%s", _SL_); | |
c7699875 | 342 | } |
343 | if (cur) { | |
344 | if (prefix) | |
345 | fprintf(fp, prefix); | |
7809c616 | 346 | fprintf(fp, "lifetime current:"); |
347 | fprintf(fp, "%s", _SL_); | |
c7699875 | 348 | |
349 | if (prefix) | |
350 | fprintf(fp, prefix); | |
351 | fprintf(fp, " "); | |
b906243b | 352 | fprintf(fp, "%llu(bytes), ", (unsigned long long) cur->bytes); |
353 | fprintf(fp, "%llu(packets)", (unsigned long long) cur->packets); | |
7809c616 | 354 | fprintf(fp, "%s", _SL_); |
355 | ||
c7699875 | 356 | if (prefix) |
357 | fprintf(fp, prefix); | |
358 | fprintf(fp, " "); | |
359 | fprintf(fp, "add %s ", strxf_time(cur->add_time)); | |
360 | fprintf(fp, "use %s", strxf_time(cur->use_time)); | |
7809c616 | 361 | fprintf(fp, "%s", _SL_); |
c7699875 | 362 | } |
363 | } | |
364 | ||
365 | void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, | |
366 | FILE *fp, const char *prefix) | |
367 | { | |
368 | char abuf[256]; | |
369 | __u16 f; | |
370 | ||
371 | f = sel->family; | |
372 | if (f == AF_UNSPEC) | |
373 | f = family; | |
374 | if (f == AF_UNSPEC) | |
375 | f = preferred_family; | |
376 | ||
377 | if (prefix) | |
378 | fprintf(fp, prefix); | |
379 | ||
380 | memset(abuf, '\0', sizeof(abuf)); | |
ad273962 | 381 | fprintf(fp, "src %s/%d ", rt_addr_n2a(f, sizeof(sel->saddr), |
382 | &sel->saddr, abuf, sizeof(abuf)), | |
383 | sel->prefixlen_s); | |
c7699875 | 384 | |
385 | memset(abuf, '\0', sizeof(abuf)); | |
7809c616 | 386 | fprintf(fp, "dst %s/%d ", rt_addr_n2a(f, sizeof(sel->daddr), |
ad273962 | 387 | &sel->daddr, abuf, sizeof(abuf)), |
388 | sel->prefixlen_d); | |
c7699875 | 389 | |
7809c616 | 390 | if (sel->proto) |
391 | fprintf(fp, "proto %s ", strxf_proto(sel->proto)); | |
c70b36d2 | 392 | switch (sel->proto) { |
393 | case IPPROTO_TCP: | |
394 | case IPPROTO_UDP: | |
395 | case IPPROTO_SCTP: | |
396 | default: /* XXX */ | |
397 | if (sel->sport_mask) | |
398 | fprintf(fp, "sport %u ", ntohs(sel->sport)); | |
399 | if (sel->dport_mask) | |
400 | fprintf(fp, "dport %u ", ntohs(sel->dport)); | |
401 | break; | |
402 | case IPPROTO_ICMP: | |
403 | case IPPROTO_ICMPV6: | |
404 | /* type/code is stored at sport/dport in selector */ | |
405 | if (sel->sport_mask) | |
406 | fprintf(fp, "type %u ", ntohs(sel->sport)); | |
407 | if (sel->dport_mask) | |
408 | fprintf(fp, "code %u ", ntohs(sel->dport)); | |
409 | break; | |
410 | } | |
c7699875 | 411 | |
412 | if (sel->ifindex > 0) { | |
44d3eb25 | 413 | char buf[IFNAMSIZ]; |
c7699875 | 414 | |
415 | memset(buf, '\0', sizeof(buf)); | |
416 | if_indextoname(sel->ifindex, buf); | |
417 | fprintf(fp, "dev %s ", buf); | |
ad273962 | 418 | } |
c7699875 | 419 | |
ad273962 | 420 | if (show_stats > 0) |
421 | fprintf(fp, "uid %u", sel->user); | |
7809c616 | 422 | |
423 | fprintf(fp, "%s", _SL_); | |
c7699875 | 424 | } |
425 | ||
7809c616 | 426 | static void xfrm_algo_print(struct xfrm_algo *algo, int type, FILE *fp, |
c7699875 | 427 | const char *prefix) |
428 | { | |
429 | int len; | |
430 | int i; | |
431 | ||
432 | if (prefix) | |
433 | fprintf(fp, prefix); | |
434 | ||
7809c616 | 435 | fprintf(fp, "%s ", strxf_algotype(type)); |
436 | fprintf(fp, "%s ", algo->alg_name); | |
c7699875 | 437 | |
7809c616 | 438 | fprintf(fp, "0x"); |
c7699875 | 439 | len = algo->alg_key_len / 8; |
7809c616 | 440 | for (i = 0; i < len; i ++) |
441 | fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]); | |
c7699875 | 442 | |
7809c616 | 443 | if (show_stats > 0) |
444 | fprintf(fp, " (%d bits)", algo->alg_key_len); | |
445 | ||
446 | fprintf(fp, "%s", _SL_); | |
c7699875 | 447 | } |
448 | ||
449 | static const char *strxf_mask(__u32 mask) | |
450 | { | |
451 | static char str[128]; | |
452 | const int sn = sizeof(mask) * 8 - 1; | |
453 | __u32 b; | |
454 | int finish = 0; | |
455 | int broken = 0; | |
456 | int i = 0; | |
457 | ||
458 | for (b = (1 << sn); b > 0; b >>= 1) { | |
459 | if ((b & mask) == 0) { | |
460 | if (!finish) | |
461 | finish = 1; | |
462 | } else { | |
463 | if (!finish) | |
464 | i ++; | |
465 | else { | |
466 | broken = 1; | |
467 | break; | |
468 | } | |
469 | } | |
470 | } | |
471 | ||
472 | if (!broken) | |
473 | sprintf(str, "%u", i); | |
474 | else | |
475 | sprintf(str, "broken(%u)", mask); | |
476 | ||
477 | return str; | |
478 | } | |
479 | ||
480 | static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int ntmpls, | |
481 | __u16 family, FILE *fp, const char *prefix) | |
482 | { | |
c7699875 | 483 | int i; |
484 | ||
c7699875 | 485 | for (i = 0; i < ntmpls; i++) { |
486 | struct xfrm_user_tmpl *tmpl = &tmpls[i]; | |
487 | ||
488 | if (prefix) | |
489 | fprintf(fp, prefix); | |
ad273962 | 490 | |
491 | fprintf(fp, "tmpl"); | |
c7699875 | 492 | xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode, |
ad273962 | 493 | tmpl->reqid, family, fp, prefix); |
c7699875 | 494 | |
7809c616 | 495 | if (prefix) |
496 | fprintf(fp, prefix); | |
c7699875 | 497 | fprintf(fp, "\t"); |
ad273962 | 498 | switch (tmpl->optional) { |
499 | case 0: | |
7809c616 | 500 | if (show_stats > 0) |
501 | fprintf(fp, "level required "); | |
ad273962 | 502 | break; |
503 | case 1: | |
7809c616 | 504 | fprintf(fp, "level use "); |
ad273962 | 505 | break; |
506 | default: | |
7809c616 | 507 | fprintf(fp, "level %d ", tmpl->optional); |
ad273962 | 508 | break; |
509 | } | |
ad273962 | 510 | |
511 | if (show_stats > 0) { | |
512 | fprintf(fp, "share %s ", strxf_share(tmpl->share)); | |
513 | fprintf(fp, "algo-mask:"); | |
7809c616 | 514 | fprintf(fp, "%s=%s, ", |
515 | strxf_algotype(XFRMA_ALG_CRYPT), | |
516 | strxf_mask(tmpl->ealgos)); | |
517 | fprintf(fp, "%s=%s, ", | |
518 | strxf_algotype(XFRMA_ALG_AUTH), | |
519 | strxf_mask(tmpl->aalgos)); | |
520 | fprintf(fp, "%s=%s", | |
521 | strxf_algotype(XFRMA_ALG_COMP), | |
522 | strxf_mask(tmpl->calgos)); | |
ad273962 | 523 | } |
7809c616 | 524 | fprintf(fp, "%s", _SL_); |
c7699875 | 525 | } |
526 | } | |
527 | ||
528 | void xfrm_xfrma_print(struct rtattr *tb[], int ntb, __u16 family, | |
529 | FILE *fp, const char *prefix) | |
530 | { | |
531 | int i; | |
532 | ||
533 | for (i = 0; i < ntb; i++) { | |
534 | __u16 type = tb[i]->rta_type; | |
535 | void *data = RTA_DATA(tb[i]); | |
536 | ||
537 | switch (type) { | |
538 | case XFRMA_ALG_CRYPT: | |
c7699875 | 539 | case XFRMA_ALG_AUTH: |
c7699875 | 540 | case XFRMA_ALG_COMP: |
7809c616 | 541 | xfrm_algo_print((struct xfrm_algo *)data, type, fp, |
542 | prefix); | |
c7699875 | 543 | break; |
544 | case XFRMA_ENCAP: | |
545 | if (prefix) | |
546 | fprintf(fp, prefix); | |
547 | /* XXX */ | |
7809c616 | 548 | fprintf(fp, "encap (not implemented yet!)"); |
549 | fprintf(fp, "%s", _SL_); | |
c7699875 | 550 | break; |
551 | case XFRMA_TMPL: | |
552 | { | |
553 | int len = tb[i]->rta_len; | |
554 | int ntmpls = len / sizeof(struct xfrm_user_tmpl); | |
555 | ||
556 | xfrm_tmpl_print((struct xfrm_user_tmpl *)data, | |
557 | ntmpls, family, fp, prefix); | |
558 | break; | |
559 | } | |
560 | default: | |
561 | if (prefix) | |
562 | fprintf(fp, prefix); | |
7809c616 | 563 | fprintf(fp, "%u (unknown rta_type)", type); |
564 | fprintf(fp, "%s", _SL_); | |
c7699875 | 565 | break; |
566 | } | |
567 | } | |
568 | } | |
569 | ||
570 | int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, | |
7809c616 | 571 | int loose, int *argcp, char ***argvp) |
c7699875 | 572 | { |
573 | int argc = *argcp; | |
574 | char **argv = *argvp; | |
575 | inet_prefix dst; | |
576 | inet_prefix src; | |
c7699875 | 577 | |
578 | memset(&dst, 0, sizeof(dst)); | |
579 | memset(&src, 0, sizeof(src)); | |
580 | ||
581 | while (1) { | |
582 | if (strcmp(*argv, "src") == 0) { | |
583 | NEXT_ARG(); | |
584 | ||
585 | get_prefix(&src, *argv, preferred_family); | |
586 | if (src.family == AF_UNSPEC) | |
587 | invarg("\"SADDR\" address family is AF_UNSPEC", *argv); | |
588 | if (family) | |
589 | *family = src.family; | |
590 | ||
591 | memcpy(saddr, &src.data, sizeof(*saddr)); | |
592 | ||
593 | filter.id_src_mask = src.bitlen; | |
594 | ||
595 | } else if (strcmp(*argv, "dst") == 0) { | |
596 | NEXT_ARG(); | |
597 | ||
598 | get_prefix(&dst, *argv, preferred_family); | |
599 | if (dst.family == AF_UNSPEC) | |
600 | invarg("\"DADDR\" address family is AF_UNSPEC", *argv); | |
601 | if (family) | |
602 | *family = dst.family; | |
603 | ||
604 | memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); | |
605 | ||
606 | filter.id_dst_mask = dst.bitlen; | |
607 | ||
608 | } else if (strcmp(*argv, "proto") == 0) { | |
29aa4dd7 | 609 | int ret; |
c7699875 | 610 | |
611 | NEXT_ARG(); | |
612 | ||
29aa4dd7 | 613 | ret = xfrm_xfrmproto_getbyname(*argv); |
614 | if (ret < 0) | |
615 | invarg("\"XFRM_PROTO\" is invalid", *argv); | |
c7699875 | 616 | |
29aa4dd7 | 617 | id->proto = (__u8)ret; |
c7699875 | 618 | |
619 | filter.id_proto_mask = XFRM_FILTER_MASK_FULL; | |
620 | ||
621 | } else if (strcmp(*argv, "spi") == 0) { | |
622 | __u32 spi; | |
623 | ||
624 | NEXT_ARG(); | |
625 | if (get_u32(&spi, *argv, 0)) | |
626 | invarg("\"SPI\" is invalid", *argv); | |
627 | ||
628 | spi = htonl(spi); | |
629 | id->spi = spi; | |
630 | ||
631 | filter.id_spi_mask = XFRM_FILTER_MASK_FULL; | |
632 | ||
633 | } else { | |
634 | PREV_ARG(); /* back track */ | |
635 | break; | |
636 | } | |
637 | ||
638 | if (!NEXT_ARG_OK()) | |
639 | break; | |
640 | NEXT_ARG(); | |
641 | } | |
642 | ||
643 | if (src.family && dst.family && (src.family != dst.family)) | |
644 | invarg("the same address family is required between \"SADDR\" and \"DADDR\"", *argv); | |
c7699875 | 645 | |
29aa4dd7 | 646 | if (loose == 0 && id->proto == 0) |
647 | missarg("XFRM_PROTO"); | |
c7699875 | 648 | if (argc == *argcp) |
649 | missarg("ID"); | |
650 | ||
651 | *argcp = argc; | |
652 | *argvp = argv; | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
657 | int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp) | |
658 | { | |
659 | int argc = *argcp; | |
660 | char **argv = *argvp; | |
661 | ||
662 | if (matches(*argv, "transport") == 0) | |
663 | *mode = 0; | |
664 | else if (matches(*argv, "tunnel") == 0) | |
665 | *mode = 1; | |
666 | else | |
667 | invarg("\"MODE\" is invalid", *argv); | |
668 | ||
669 | *argcp = argc; | |
670 | *argvp = argv; | |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
675 | /* NOTE: reqid is used by host-byte order */ | |
676 | int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp) | |
677 | { | |
678 | int argc = *argcp; | |
679 | char **argv = *argvp; | |
680 | ||
681 | if (get_u32(reqid, *argv, 0)) | |
682 | invarg("\"REQID\" is invalid", *argv); | |
683 | ||
684 | *argcp = argc; | |
685 | *argvp = argv; | |
686 | ||
687 | return 0; | |
688 | } | |
689 | ||
690 | static int xfrm_selector_upspec_parse(struct xfrm_selector *sel, | |
691 | int *argcp, char ***argvp) | |
692 | { | |
693 | int argc = *argcp; | |
694 | char **argv = *argvp; | |
c70b36d2 | 695 | char *sportp = NULL; |
696 | char *dportp = NULL; | |
697 | char *typep = NULL; | |
698 | char *codep = NULL; | |
c7699875 | 699 | |
700 | while (1) { | |
701 | if (strcmp(*argv, "proto") == 0) { | |
7809c616 | 702 | __u8 upspec; |
703 | ||
c7699875 | 704 | NEXT_ARG(); |
705 | ||
706 | if (strcmp(*argv, "any") == 0) | |
707 | upspec = 0; | |
708 | else { | |
709 | struct protoent *pp; | |
710 | pp = getprotobyname(*argv); | |
711 | if (pp) | |
712 | upspec = pp->p_proto; | |
713 | else { | |
714 | if (get_u8(&upspec, *argv, 0)) | |
7809c616 | 715 | invarg("\"PROTO\" is invalid", *argv); |
c7699875 | 716 | } |
717 | } | |
718 | sel->proto = upspec; | |
719 | ||
720 | filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL; | |
721 | ||
722 | } else if (strcmp(*argv, "sport") == 0) { | |
c70b36d2 | 723 | sportp = *argv; |
724 | ||
c7699875 | 725 | NEXT_ARG(); |
726 | ||
727 | if (get_u16(&sel->sport, *argv, 0)) | |
728 | invarg("\"PORT\" is invalid", *argv); | |
729 | sel->sport = htons(sel->sport); | |
730 | if (sel->sport) | |
731 | sel->sport_mask = ~((__u16)0); | |
732 | ||
733 | filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; | |
734 | ||
735 | } else if (strcmp(*argv, "dport") == 0) { | |
c70b36d2 | 736 | dportp = *argv; |
737 | ||
c7699875 | 738 | NEXT_ARG(); |
739 | ||
740 | if (get_u16(&sel->dport, *argv, 0)) | |
741 | invarg("\"PORT\" is invalid", *argv); | |
742 | sel->dport = htons(sel->dport); | |
743 | if (sel->dport) | |
744 | sel->dport_mask = ~((__u16)0); | |
745 | ||
746 | filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; | |
747 | ||
c70b36d2 | 748 | } else if (strcmp(*argv, "type") == 0) { |
749 | typep = *argv; | |
750 | ||
751 | NEXT_ARG(); | |
752 | ||
753 | if (get_u16(&sel->sport, *argv, 0) || | |
754 | (sel->sport & ~((__u16)0xff))) | |
755 | invarg("\"type\" value is invalid", *argv); | |
756 | sel->sport = htons(sel->sport); | |
757 | sel->sport_mask = ~((__u16)0); | |
758 | ||
759 | filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; | |
760 | ||
761 | ||
762 | } else if (strcmp(*argv, "code") == 0) { | |
763 | codep = *argv; | |
764 | ||
765 | NEXT_ARG(); | |
766 | ||
767 | if (get_u16(&sel->dport, *argv, 0) || | |
768 | (sel->dport & ~((__u16)0xff))) | |
769 | invarg("\"code\" value is invalid", *argv); | |
770 | sel->dport = htons(sel->dport); | |
771 | sel->dport_mask = ~((__u16)0); | |
772 | ||
773 | filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; | |
774 | ||
c7699875 | 775 | } else { |
776 | PREV_ARG(); /* back track */ | |
777 | break; | |
778 | } | |
779 | ||
780 | if (!NEXT_ARG_OK()) | |
781 | break; | |
782 | NEXT_ARG(); | |
783 | } | |
784 | if (argc == *argcp) | |
785 | missarg("UPSPEC"); | |
c70b36d2 | 786 | if (sportp || dportp) { |
787 | switch (sel->proto) { | |
788 | case IPPROTO_TCP: | |
789 | case IPPROTO_UDP: | |
790 | case IPPROTO_SCTP: | |
791 | break; | |
792 | default: | |
793 | fprintf(stderr, "\"sport\" and \"dport\" are invalid with proto=%s\n", strxf_proto(sel->proto)); | |
794 | exit(1); | |
795 | } | |
796 | } | |
797 | if (typep || codep) { | |
798 | switch (sel->proto) { | |
799 | case IPPROTO_ICMP: | |
800 | case IPPROTO_ICMPV6: | |
801 | break; | |
802 | default: | |
803 | fprintf(stderr, "\"type\" and \"code\" are invalid with proto=%s\n", strxf_proto(sel->proto)); | |
804 | exit(1); | |
805 | } | |
806 | } | |
c7699875 | 807 | |
808 | *argcp = argc; | |
809 | *argvp = argv; | |
810 | ||
811 | return 0; | |
812 | } | |
813 | ||
814 | int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) | |
815 | { | |
816 | int argc = *argcp; | |
817 | char **argv = *argvp; | |
818 | inet_prefix dst; | |
819 | inet_prefix src; | |
7809c616 | 820 | char *upspecp = NULL; |
c7699875 | 821 | |
822 | memset(&dst, 0, sizeof(dst)); | |
823 | memset(&src, 0, sizeof(src)); | |
824 | ||
825 | while (1) { | |
826 | if (strcmp(*argv, "src") == 0) { | |
827 | NEXT_ARG(); | |
828 | ||
829 | get_prefix(&src, *argv, preferred_family); | |
830 | if (src.family == AF_UNSPEC) | |
831 | invarg("\"SADDR\" address family is AF_UNSPEC", *argv); | |
832 | sel->family = src.family; | |
833 | ||
834 | memcpy(&sel->saddr, &src.data, sizeof(sel->saddr)); | |
835 | sel->prefixlen_s = src.bitlen; | |
836 | ||
837 | filter.sel_src_mask = src.bitlen; | |
838 | ||
839 | } else if (strcmp(*argv, "dst") == 0) { | |
840 | NEXT_ARG(); | |
841 | ||
842 | get_prefix(&dst, *argv, preferred_family); | |
843 | if (dst.family == AF_UNSPEC) | |
844 | invarg("\"DADDR\" address family is AF_UNSPEC", *argv); | |
845 | sel->family = dst.family; | |
846 | ||
847 | memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr)); | |
848 | sel->prefixlen_d = dst.bitlen; | |
849 | ||
850 | filter.sel_dst_mask = dst.bitlen; | |
851 | ||
c7699875 | 852 | } else if (strcmp(*argv, "dev") == 0) { |
853 | int ifindex; | |
854 | ||
855 | NEXT_ARG(); | |
856 | ||
857 | if (strcmp(*argv, "none") == 0) | |
858 | ifindex = 0; | |
859 | else { | |
860 | ifindex = if_nametoindex(*argv); | |
861 | if (ifindex <= 0) | |
862 | invarg("\"DEV\" is invalid", *argv); | |
863 | } | |
864 | sel->ifindex = ifindex; | |
865 | ||
866 | filter.sel_dev_mask = XFRM_FILTER_MASK_FULL; | |
867 | ||
868 | } else { | |
7809c616 | 869 | if (upspecp) { |
870 | PREV_ARG(); /* back track */ | |
871 | break; | |
872 | } else { | |
873 | upspecp = *argv; | |
874 | xfrm_selector_upspec_parse(sel, &argc, &argv); | |
875 | } | |
c7699875 | 876 | } |
877 | ||
878 | if (!NEXT_ARG_OK()) | |
879 | break; | |
880 | ||
881 | NEXT_ARG(); | |
882 | } | |
883 | ||
884 | if (src.family && dst.family && (src.family != dst.family)) | |
885 | invarg("the same address family is required between \"SADDR\" and \"DADDR\"", *argv); | |
886 | ||
887 | if (argc == *argcp) | |
888 | missarg("SELECTOR"); | |
889 | ||
890 | *argcp = argc; | |
891 | *argvp = argv; | |
892 | ||
893 | return 0; | |
894 | } | |
895 | ||
896 | int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, | |
897 | int *argcp, char ***argvp) | |
898 | { | |
899 | int argc = *argcp; | |
900 | char **argv = *argvp; | |
901 | int ret; | |
902 | ||
903 | if (strcmp(*argv, "time-soft") == 0) { | |
904 | NEXT_ARG(); | |
905 | ret = get_u64(&lft->soft_add_expires_seconds, *argv, 0); | |
906 | if (ret) | |
907 | invarg("\"time-soft\" value is invalid", *argv); | |
908 | } else if (strcmp(*argv, "time-hard") == 0) { | |
909 | NEXT_ARG(); | |
910 | ret = get_u64(&lft->hard_add_expires_seconds, *argv, 0); | |
911 | if (ret) | |
912 | invarg("\"time-hard\" value is invalid", *argv); | |
913 | } else if (strcmp(*argv, "time-use-soft") == 0) { | |
914 | NEXT_ARG(); | |
915 | ret = get_u64(&lft->soft_use_expires_seconds, *argv, 0); | |
916 | if (ret) | |
917 | invarg("\"time-use-soft\" value is invalid", *argv); | |
918 | } else if (strcmp(*argv, "time-use-hard") == 0) { | |
919 | NEXT_ARG(); | |
920 | ret = get_u64(&lft->hard_use_expires_seconds, *argv, 0); | |
921 | if (ret) | |
922 | invarg("\"time-use-hard\" value is invalid", *argv); | |
923 | } else if (strcmp(*argv, "byte-soft") == 0) { | |
924 | NEXT_ARG(); | |
925 | ret = get_u64(&lft->soft_byte_limit, *argv, 0); | |
926 | if (ret) | |
927 | invarg("\"byte-soft\" value is invalid", *argv); | |
928 | } else if (strcmp(*argv, "byte-hard") == 0) { | |
929 | NEXT_ARG(); | |
930 | ret = get_u64(&lft->hard_byte_limit, *argv, 0); | |
931 | if (ret) | |
932 | invarg("\"byte-hard\" value is invalid", *argv); | |
933 | } else if (strcmp(*argv, "packet-soft") == 0) { | |
934 | NEXT_ARG(); | |
935 | ret = get_u64(&lft->soft_packet_limit, *argv, 0); | |
936 | if (ret) | |
937 | invarg("\"packet-soft\" value is invalid", *argv); | |
938 | } else if (strcmp(*argv, "packet-hard") == 0) { | |
939 | NEXT_ARG(); | |
940 | ret = get_u64(&lft->hard_packet_limit, *argv, 0); | |
941 | if (ret) | |
942 | invarg("\"packet-hard\" value is invalid", *argv); | |
943 | } else | |
944 | invarg("\"LIMIT\" is invalid", *argv); | |
945 | ||
946 | *argcp = argc; | |
947 | *argvp = argv; | |
948 | ||
949 | return 0; | |
950 | } | |
951 | ||
952 | int do_xfrm(int argc, char **argv) | |
953 | { | |
954 | memset(&filter, 0, sizeof(filter)); | |
955 | ||
956 | if (argc < 1) | |
957 | usage(); | |
958 | ||
ad273962 | 959 | if (matches(*argv, "state") == 0 || |
960 | matches(*argv, "sa") == 0) { | |
c7699875 | 961 | return do_xfrm_state(argc-1, argv+1); |
ad273962 | 962 | } else if (matches(*argv, "policy") == 0) |
c7699875 | 963 | return do_xfrm_policy(argc-1, argv+1); |
ad273962 | 964 | else if (matches(*argv, "help") == 0) { |
c7699875 | 965 | usage(); |
966 | fprintf(stderr, "xfrm Object \"%s\" is unknown.\n", *argv); | |
967 | exit(-1); | |
968 | } | |
969 | usage(); | |
970 | } |