diff options
author | net[shemminger]!shemminger <net[shemminger]!shemminger> | 2004-07-07 17:05:56 +0000 |
---|---|---|
committer | net[shemminger]!shemminger <net[shemminger]!shemminger> | 2004-07-07 17:05:56 +0000 |
commit | c7699875bee00fbcd057fc62c30d6560b044e007 (patch) | |
tree | 1dbe3c77191d1f56f72a3f0b4106a2d9d0740f3c /ip | |
parent | 7798b5237ef2b710c87f7f052d134d2180ffbd5c (diff) |
Import patch ipxfrm-20040707_2.diff
(Logical change 1.53)
Diffstat (limited to 'ip')
-rw-r--r-- | ip/Makefile | 2 | ||||
-rw-r--r-- | ip/ip.c | 6 | ||||
-rw-r--r-- | ip/ip_common.h | 1 | ||||
-rw-r--r-- | ip/ipxfrm.c | 808 | ||||
-rw-r--r-- | ip/xfrm.h | 102 | ||||
-rw-r--r-- | ip/xfrm_policy.c | 671 | ||||
-rw-r--r-- | ip/xfrm_state.c | 636 |
7 files changed, 2224 insertions, 2 deletions
diff --git a/ip/Makefile b/ip/Makefile index 2aa00518..df4fbda1 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -1,6 +1,6 @@ IPOBJ=ip.o ipaddress.o iproute.o iprule.o \ rtm_map.o iptunnel.o ipneigh.o iplink.o ipmaddr.o \ - ipmonitor.o ipmroute.o + ipmonitor.o ipmroute.o ipxfrm.o xfrm_state.o xfrm_policy.o RTMONOBJ=rtmon.o @@ -40,7 +40,7 @@ static void usage(void) fprintf(stderr, "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" "where OBJECT := { link | addr | route | rule | neigh | tunnel |\n" -" maddr | mroute | monitor }\n" +" maddr | mroute | monitor | xfrm }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -r[esolve] |\n" " -f[amily] { inet | inet6 | ipx | dnet | link } | -o[neline] }\n"); exit(-1); @@ -136,6 +136,8 @@ int main(int argc, char **argv) return do_iptunnel(argc-1, argv+1); if (strcmp(basename, "ipmonitor") == 0) return do_ipmonitor(argc-1, argv+1); + if (strcmp(basename, "ipxfrm") == 0) + return do_xfrm(argc-1, argv+1); if (argc > 1) { if (matches(argv[1], "address") == 0) @@ -158,6 +160,8 @@ int main(int argc, char **argv) return do_iptunnel(argc-2, argv+2); if (matches(argv[1], "monitor") == 0) return do_ipmonitor(argc-2, argv+2); + if (matches(argv[1], "xfrm") == 0) + return do_xfrm(argc-2, argv+2); if (matches(argv[1], "help") == 0) usage(); fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv[1]); diff --git a/ip/ip_common.h b/ip/ip_common.h index 5ac43218..598c998a 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -18,3 +18,4 @@ extern int do_iplink(int argc, char **argv); extern int do_ipmonitor(int argc, char **argv); extern int do_multiaddr(int argc, char **argv); extern int do_multiroute(int argc, char **argv); +extern int do_xfrm(int argc, char **argv); diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c index e69de29b..786600bd 100644 --- a/ip/ipxfrm.c +++ b/ip/ipxfrm.c @@ -0,0 +1,808 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <time.h> +#include <netdb.h> +#include <net/if.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/xfrm.h> + +#include "utils.h" +#include "xfrm.h" + +struct xfrm_filter filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm XFRM_OBJECT { COMMAND | help }\n" + "where XFRM_OBJECT := { state | policy }\n"); + exit(-1); +} + +const char *strxf_flags(__u8 flags) +{ + static char str[16]; + const int sn = sizeof(flags) * 8 - 1; + __u8 b; + int i = 0; + + for (b = (1 << sn); b > 0; b >>= 1) + str[i++] = ((b & flags) ? '1' : '0'); + str[i] = '\0'; + + return str; +} + +const char *strxf_share(__u8 share) +{ + static char str[32]; + + switch (share) { + case XFRM_SHARE_ANY: + strcpy(str, "any"); + break; + case XFRM_SHARE_SESSION: + strcpy(str, "session"); + break; + case XFRM_SHARE_USER: + strcpy(str, "user"); + break; + case XFRM_SHARE_UNIQUE: + strcpy(str, "unique"); + break; + default: + sprintf(str, "unknown-share(%d)", share); + break; + } + + return str; +} + +void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, FILE *fp, + const char *prefix) +{ + char abuf[256]; + __u32 spi; + struct protoent *pp; + char pbuf[32]; + char *p; + + if (prefix) + fprintf(fp, prefix); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "%s ", rt_addr_n2a(family, sizeof(*saddr), + saddr, abuf, sizeof(abuf))); + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "%s\n", rt_addr_n2a(family, sizeof(id->daddr), + &id->daddr, abuf, sizeof(abuf))); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + + pp = getprotobynumber(id->proto); + if (pp) + p = pp->p_name; + else { + sprintf(pbuf, "%d", id->proto); + p = pbuf; + } + + switch (id->proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + case IPPROTO_COMP: + fprintf(fp, "%s ", p); + break; + default: + fprintf(fp, "unspec(%s)", p); + break; + } + + switch (id->proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + case IPPROTO_COMP: + default: + spi = ntohl(id->spi); + fprintf(fp, "spi %d(0x%08x) ", spi, spi); + break; + } + + fprintf(fp, "reqid %d ", reqid); + fprintf(fp, "%s\n", (mode ? "tunnel" : "transport")); +} + +static const char *strxf_limit(__u64 limit) +{ + static char str[32]; + if (limit == XFRM_INF) + strcpy(str, "(INF)"); + else + sprintf(str, "%llu", limit); + + return str; +} + +void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix) +{ + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "stats:\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "replay-window %d ", s->replay_window); + fprintf(fp, "replay %d ", s->replay); + fprintf(fp, "failed %d", s->integrity_failed); + fprintf(fp, "\n"); +} + +static const char *strxf_time(__u64 time) +{ + static char str[32]; + struct tm *tp; + time_t t; + + if (time == 0) { + strcpy(str, "(undefined)"); + } else { + /* XXX: treat time in the same manner of xfrm_{user,state}.c */ + t = (long)time; + tp = localtime(&t); + + sprintf(str, "%04d/%02d/%02d %02d:%02d:%02d", + tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + } + + return str; +} + +void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix) +{ + if (cfg) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "lifetime config:\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "limit: "); + fprintf(fp, "soft "); + fprintf(fp, strxf_limit(cfg->soft_byte_limit)); + fprintf(fp, "(bytes), hard "); + fprintf(fp, strxf_limit(cfg->hard_byte_limit)); + fprintf(fp, "(bytes)\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "limit: "); + fprintf(fp, "soft "); + fprintf(fp, strxf_limit(cfg->soft_packet_limit)); + fprintf(fp, "(packets), hard "); + fprintf(fp, strxf_limit(cfg->hard_packet_limit)); + fprintf(fp, "(packets)\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "expire add: "); + fprintf(fp, "soft "); + fprintf(fp, "%llu", cfg->soft_add_expires_seconds); + fprintf(fp, "(sec), hard "); + fprintf(fp, "%llu", cfg->hard_add_expires_seconds); + fprintf(fp, "(sec)\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "expire use: "); + fprintf(fp, "soft "); + fprintf(fp, "%llu", cfg->soft_use_expires_seconds); + fprintf(fp, "(sec), hard "); + fprintf(fp, "%llu", cfg->hard_use_expires_seconds); + fprintf(fp, "(sec)\n"); + } + if (cur) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "lifetime current:\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "%llu(bytes), ", cur->bytes); + fprintf(fp, "%llu(packets)\n", cur->packets); + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "add %s ", strxf_time(cur->add_time)); + fprintf(fp, "use %s", strxf_time(cur->use_time)); + fprintf(fp, "\n"); + } +} + +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix) +{ + char abuf[256]; + __u16 f; + + f = sel->family; + if (f == AF_UNSPEC) + f = family; + if (f == AF_UNSPEC) + f = preferred_family; + + if (prefix) + fprintf(fp, prefix); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "%s/%d[%u]", rt_addr_n2a(f, sizeof(sel->saddr), + &sel->saddr, + abuf, sizeof(abuf)), + sel->prefixlen_s, sel->sport); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, " %s/%d[%u]", rt_addr_n2a(f, sizeof(sel->daddr), + &sel->daddr, + abuf, sizeof(abuf)), + sel->prefixlen_d, sel->dport); + + fprintf(fp, "\n"); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + + fprintf(fp, "upspec %u ", sel->proto); + + if (sel->ifindex > 0) { + char buf[IF_NAMESIZE]; + + memset(buf, '\0', sizeof(buf)); + if_indextoname(sel->ifindex, buf); + fprintf(fp, "dev %s ", buf); + } else + fprintf(fp, "dev (none) "); + + fprintf(fp, "uid %u", sel->user); + fprintf(fp, "\n"); +} + +static void xfrm_algo_print(struct xfrm_algo *algo, FILE *fp, + const char *prefix) +{ + int len; + int i; + + if (prefix) + fprintf(fp, prefix); + + fprintf(fp, "%s", algo->alg_name); + + len = algo->alg_key_len / 8; + for (i = 0; i < len; i ++) { + if (i % 4 == 0) + fprintf(fp, " "); + fprintf(fp, "%x", algo->alg_key[i]); + } + + fprintf(fp, "\n"); +} + +static const char *strxf_mask(__u32 mask) +{ + static char str[128]; + const int sn = sizeof(mask) * 8 - 1; + __u32 b; + int finish = 0; + int broken = 0; + int i = 0; + + for (b = (1 << sn); b > 0; b >>= 1) { + if ((b & mask) == 0) { + if (!finish) + finish = 1; + } else { + if (!finish) + i ++; + else { + broken = 1; + break; + } + } + } + + if (!broken) + sprintf(str, "%u", i); + else + sprintf(str, "broken(%u)", mask); + + return str; +} + +static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int ntmpls, + __u16 family, FILE *fp, const char *prefix) +{ + char buf[32]; + const char *p = NULL; + int i; + + if (prefix) { + strcpy(buf, prefix); + strcat(buf, " "); + } else + strcpy(buf, " "); + p = buf; + + for (i = 0; i < ntmpls; i++) { + struct xfrm_user_tmpl *tmpl = &tmpls[i]; + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "tmpl-%d:\n", i+1); + xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode, + tmpl->reqid, family, fp, p); + + fprintf(fp, p); + fprintf(fp, "\t"); + fprintf(fp, "level %s ", ((tmpl->optional == 0) ? "required" : + (tmpl->optional == 1) ? "use" : + "unknown-level")); + fprintf(fp, "share %s ", strxf_share(tmpl->share)); + fprintf(fp, "algo-mask:"); + fprintf(fp, "E=%s, ", strxf_mask(tmpl->ealgos)); + fprintf(fp, "A=%s, ", strxf_mask(tmpl->aalgos)); + fprintf(fp, "C=%s", strxf_mask(tmpl->calgos)); + fprintf(fp, "\n"); + } +} + +void xfrm_xfrma_print(struct rtattr *tb[], int ntb, __u16 family, + FILE *fp, const char *prefix) +{ + int i; + + for (i = 0; i < ntb; i++) { + __u16 type = tb[i]->rta_type; + void *data = RTA_DATA(tb[i]); + + switch (type) { + case XFRMA_ALG_CRYPT: + if (prefix) + fprintf(fp, prefix); + xfrm_algo_print((struct xfrm_algo *)data, fp, "E:"); + break; + case XFRMA_ALG_AUTH: + if (prefix) + fprintf(fp, prefix); + xfrm_algo_print((struct xfrm_algo *)data, fp, "A:"); + break; + case XFRMA_ALG_COMP: + if (prefix) + fprintf(fp, prefix); + xfrm_algo_print((struct xfrm_algo *)data, fp, "C:"); + break; + case XFRMA_ENCAP: + if (prefix) + fprintf(fp, prefix); + /* XXX */ + fprintf(fp, "encap: (not implemented yet!)\n"); + break; + case XFRMA_TMPL: + { + int len = tb[i]->rta_len; + int ntmpls = len / sizeof(struct xfrm_user_tmpl); + + xfrm_tmpl_print((struct xfrm_user_tmpl *)data, + ntmpls, family, fp, prefix); + break; + } + default: + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "unknown rta_type: %u\n", type); + break; + } + } +} + +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst; + inet_prefix src; + __u8 proto = 0; + + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("\"SADDR\" address family is AF_UNSPEC", *argv); + if (family) + *family = src.family; + + memcpy(saddr, &src.data, sizeof(*saddr)); + + filter.id_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("\"DADDR\" address family is AF_UNSPEC", *argv); + if (family) + *family = dst.family; + + memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); + + filter.id_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "proto") == 0) { + struct protoent *pp; + + NEXT_ARG(); + + pp = getprotobyname(*argv); + if (pp) + proto = pp->p_proto; + else { + if (get_u8(&proto, *argv, 0)) + invarg("\"PROTO\" is invalid", *argv); + } + + switch (proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + case IPPROTO_COMP: + id->proto = proto; + break; + default: + invarg("\"PROTO\" is unsuppored proto", *argv); + } + + filter.id_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "spi") == 0) { + __u32 spi; + + NEXT_ARG(); + if (get_u32(&spi, *argv, 0)) + invarg("\"SPI\" is invalid", *argv); + + spi = htonl(spi); + id->spi = spi; + + filter.id_spi_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between \"SADDR\" and \"DADDR\"", *argv); + if (proto == 0) + missarg("PROTO"); + + if (argc == *argcp) + missarg("ID"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (matches(*argv, "transport") == 0) + *mode = 0; + else if (matches(*argv, "tunnel") == 0) + *mode = 1; + else + invarg("\"MODE\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +/* NOTE: reqid is used by host-byte order */ +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (get_u32(reqid, *argv, 0)) + invarg("\"REQID\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_selector_upspec_parse(struct xfrm_selector *sel, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + __u8 upspec; + + while (1) { + if (strcmp(*argv, "proto") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "any") == 0) + upspec = 0; + else { + struct protoent *pp; + pp = getprotobyname(*argv); + if (pp) + upspec = pp->p_proto; + else { + if (get_u8(&upspec, *argv, 0)) + invarg("\"UPSPEC\" is invalid", *argv); + } + } + sel->proto = upspec; + + filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + + if (get_u16(&sel->sport, *argv, 0)) + invarg("\"PORT\" is invalid", *argv); + sel->sport = htons(sel->sport); + if (sel->sport) + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + + if (get_u16(&sel->dport, *argv, 0)) + invarg("\"PORT\" is invalid", *argv); + sel->dport = htons(sel->dport); + if (sel->dport) + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + if (argc == *argcp) + missarg("UPSPEC"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst; + inet_prefix src; + + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("\"SADDR\" address family is AF_UNSPEC", *argv); + sel->family = src.family; + + memcpy(&sel->saddr, &src.data, sizeof(sel->saddr)); + sel->prefixlen_s = src.bitlen; + + filter.sel_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("\"DADDR\" address family is AF_UNSPEC", *argv); + sel->family = dst.family; + + memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr)); + sel->prefixlen_d = dst.bitlen; + + filter.sel_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "upspec") == 0) { + NEXT_ARG(); + + xfrm_selector_upspec_parse(sel, &argc, &argv); + + } else if (strcmp(*argv, "dev") == 0) { + int ifindex; + + NEXT_ARG(); + + if (strcmp(*argv, "none") == 0) + ifindex = 0; + else { + ifindex = if_nametoindex(*argv); + if (ifindex <= 0) + invarg("\"DEV\" is invalid", *argv); + } + sel->ifindex = ifindex; + + filter.sel_dev_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between \"SADDR\" and \"DADDR\"", *argv); + + if (argc == *argcp) + missarg("SELECTOR"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + + if (strcmp(*argv, "time-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_add_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "time-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_add_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "time-use-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_use_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-use-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "time-use-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_use_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-use-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "byte-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_byte_limit, *argv, 0); + if (ret) + invarg("\"byte-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "byte-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_byte_limit, *argv, 0); + if (ret) + invarg("\"byte-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "packet-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_packet_limit, *argv, 0); + if (ret) + invarg("\"packet-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "packet-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_packet_limit, *argv, 0); + if (ret) + invarg("\"packet-hard\" value is invalid", *argv); + } else + invarg("\"LIMIT\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int do_xfrm(int argc, char **argv) +{ + memset(&filter, 0, sizeof(filter)); + + if (argc < 1) + usage(); + + if (strcmp(*argv, "state") == 0 || + strcmp(*argv, "sa") == 0) { + return do_xfrm_state(argc-1, argv+1); + } else if (strcmp(*argv, "policy") == 0 || + strcmp(*argv, "pol") == 0) { + return do_xfrm_policy(argc-1, argv+1); + } else if (strcmp(*argv, "help") == 0) { + usage(); + fprintf(stderr, "xfrm Object \"%s\" is unknown.\n", *argv); + exit(-1); + } + usage(); +} @@ -0,0 +1,102 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#ifndef __XFRM_H__ +#define __XFRM_H__ 1 + +#include <stdio.h> +#include <sys/socket.h> +#include <linux/xfrm.h> +#include "utils.h" + +#define XFRM_MAX_DEPTH 6 + +#define XFRMS_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info)))) +#define XFRMS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_info)) + +#define XFRMP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_info)))) +#define XFRMP_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_info)) + +struct xfrm_buffer { + char *buf; + int size; + int offset; + + int nlmsg_count; + struct rtnl_handle *rth; +}; + +struct xfrm_filter { + int use; + + struct xfrm_usersa_info xsinfo; + __u32 id_src_mask; + __u32 id_dst_mask; + __u32 id_proto_mask; + __u32 id_spi_mask; + __u32 mode_mask; + __u32 reqid_mask; + __u32 state_flags_mask; + + struct xfrm_userpolicy_info xpinfo; + __u32 dir_mask; + __u32 sel_src_mask; + __u32 sel_dst_mask; + __u32 sel_dev_mask; + __u32 upspec_proto_mask; + __u32 upspec_sport_mask; + __u32 upspec_dport_mask; + __u32 index_mask; + __u32 action_mask; + __u32 priority_mask; +}; +#define XFRM_FILTER_MASK_FULL (~(__u32)0) + +extern struct xfrm_filter filter; + +int do_xfrm_state(int argc, char **argv); +int do_xfrm_policy(int argc, char **argv); + +const char *strxf_flags(__u8 flags); +const char *strxf_share(__u8 share); +void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, FILE *fp, + const char *prefix); +void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix); +void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix); +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix); +void xfrm_xfrma_print(struct rtattr *tb[], int ntb, __u16 family, + FILE *fp, const char *prefix); +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int *argcp, char ***argvp); +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp); +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp); +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp); +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp); + +#endif diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c index e69de29b..4449d10f 100644 --- a/ip/xfrm_policy.c +++ b/ip/xfrm_policy.c @@ -0,0 +1,671 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/netlink.h> +#include <linux/xfrm.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +//#define NLMSG_FLUSH_BUF_SIZE (4096-512) +#define NLMSG_FLUSH_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_userpolicy_info + * rtattr + * data = struct xfrm_user_tmpl[] + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_TMPLS_BUF_SIZE 1024 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip xfrm policy { add | update } dir DIR sel SELECTOR [ index INDEX ] \n"); + fprintf(stderr, " [ action ACTION ] [ priority PRIORITY ] [ LIMIT-LIST ] [ TMPL-LIST ]\n"); + fprintf(stderr, " [ sel SELECTOR | index INDEX ] [ TMPL-LIST ]\n"); + fprintf(stderr, "Usage: ip xfrm policy { delete | get } dir DIR [ sel SELECTOR | index INDEX ]\n"); + fprintf(stderr, "Usage: ip xfrm policy { flush | list } [ dir DIR ] [ sel SELECTOR ]\n"); + fprintf(stderr, " [ index INDEX ] [ action ACTION ] [ priority PRIORITY ]\n"); + fprintf(stderr, "DIR := [ in | out | fwd ]\n"); + + fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ upspec UPSPEC ] [ dev DEV ]\n"); + + fprintf(stderr, "UPSPEC := proto PROTO [ sport PORT ] [ dport PORT ]\n"); + + //fprintf(stderr, "DEV - device name(default=none)\n"); + + fprintf(stderr, "ACTION := [ allow | block ](default=allow)\n"); + + //fprintf(stderr, "PRIORITY - priority value(default=0)\n"); + + fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n"); + fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n"); + fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] NUMBER ]\n"); + + fprintf(stderr, "TMPL-LIST := [ TMPL-LIST ] | [ tmpl TMPL ] | [ tmpl remain ](change only)\n"); + fprintf(stderr, "TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]\n"); + fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); + + fprintf(stderr, "XFRM_PROTO := [ esp | ah | ipcomp ]\n"); + + fprintf(stderr, "MODE := [ transport | tunnel ](default=transport)\n"); + //fprintf(stderr, "REQID - number(default=0)\n"); + fprintf(stderr, "LEVEL := [ required | use ](default=required)\n"); + + exit(-1); +} + +static int xfrm_policy_dir_parse(__u8 *dir, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "in") == 0) + *dir = XFRM_POLICY_IN; + else if (strcmp(*argv, "out") == 0) + *dir = XFRM_POLICY_OUT; + else if (strcmp(*argv, "fwd") == 0) + *dir = XFRM_POLICY_FWD; + else + invarg("\"DIR\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *idp = NULL; + + while (1) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&tmpl->mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&tmpl->reqid, &argc, &argv); + } else if (strcmp(*argv, "level") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "required") == 0) + tmpl->optional = 0; + else if (strcmp(*argv, "use") == 0) + tmpl->optional = 1; + else + invarg("\"level\" value is invalid\n", *argv); + + } else { + if (idp) { + PREV_ARG(); /* back track */ + break; + } + idp = *argv; + xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family, + &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = tmpl->family; + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + if (argc == *argcp) + missarg("TMPL"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_info xpinfo; + char buf[RTA_BUF_SIZE]; + } req; + char *dirp = NULL; + char tmpls_buf[XFRM_TMPLS_BUF_SIZE]; + int tmpls_len = 0; + + memset(&req, 0, sizeof(req)); + memset(&tmpls_buf, 0, sizeof(tmpls_buf)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.xpinfo.sel.family = preferred_family; + + req.xpinfo.lft.soft_byte_limit = XFRM_INF; + req.xpinfo.lft.hard_byte_limit = XFRM_INF; + req.xpinfo.lft.soft_packet_limit = XFRM_INF; + req.xpinfo.lft.hard_packet_limit = XFRM_INF; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "sel") == 0) { + NEXT_ARG(); + xfrm_selector_parse(&req.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpinfo.sel.family; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + req.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + req.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("\"action\" value is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.priority, *argv, 0)) + invarg("\"PRIORITY\" is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xpinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "tmpl") == 0) { + struct xfrm_user_tmpl *tmpl; + + if (tmpls_len + sizeof(*tmpl) > sizeof(tmpls_buf)) { + fprintf(stderr, "Too many tmpls: buffer overflow\n"); + exit(1); + } + tmpl = (struct xfrm_user_tmpl *)((char *)tmpls_buf + tmpls_len); + + tmpl->family = preferred_family; + tmpl->aalgos = (~(__u32)0); + tmpl->ealgos = (~(__u32)0); + tmpl->calgos = (~(__u32)0); + + NEXT_ARG(); + xfrm_tmpl_parse(tmpl, &argc, &argv); + + tmpls_len += sizeof(*tmpl); + } else + invarg("unknown", *argv); + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: \"DIR\" is required.\n"); + exit(1); + } + + if (tmpls_len > 0) { + addattr_l(&req.n, sizeof(req), XFRMA_TMPL, + (void *)tmpls_buf, tmpls_len); + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpinfo.sel.family == AF_UNSPEC) + req.xpinfo.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_filter_match(struct xfrm_userpolicy_info *xpinfo) +{ + if (!filter.use) + return 1; + + if ((xpinfo->dir^filter.xpinfo.dir)&filter.dir_mask) + return 0; + + if (filter.sel_src_mask) { + if (memcmp(&xpinfo->sel.saddr, &filter.xpinfo.sel.saddr, + filter.sel_src_mask) != 0) + return 0; + if (xpinfo->sel.prefixlen_s != filter.xpinfo.sel.prefixlen_s) + return 0; + } + + if (filter.sel_dst_mask) { + if (memcmp(&xpinfo->sel.daddr, &filter.xpinfo.sel.daddr, + filter.sel_dst_mask) != 0) + return 0; + if (xpinfo->sel.prefixlen_d != filter.xpinfo.sel.prefixlen_d) + return 0; + } + + if ((xpinfo->sel.ifindex^filter.xpinfo.sel.ifindex)&filter.sel_dev_mask) + return 0; + + if ((xpinfo->sel.proto^filter.xpinfo.sel.proto)&filter.upspec_proto_mask) + return 0; + + if (filter.upspec_sport_mask) { + if ((xpinfo->sel.sport^filter.xpinfo.sel.sport)&filter.upspec_sport_mask) + return 0; + } + + if (filter.upspec_dport_mask) { + if ((xpinfo->sel.dport^filter.xpinfo.sel.dport)&filter.upspec_dport_mask) + return 0; + } + + if ((xpinfo->index^filter.xpinfo.index)&filter.index_mask) + return 0; + + if ((xpinfo->action^filter.xpinfo.action)&filter.action_mask) + return 0; + + if ((xpinfo->priority^filter.xpinfo.priority)&filter.priority_mask) + return 0; + + return 1; +} + +int xfrm_policy_print(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[XFRM_MAX_DEPTH]; + int ntb; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY && + n->nlmsg_type != XFRM_MSG_DELPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_policy_filter_match(xpinfo)) + return 0; + + memset(tb, 0, sizeof(tb)); + ntb = parse_rtattr_byindex(tb, XFRM_MAX_DEPTH, XFRMP_RTA(xpinfo), len); + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) + fprintf(fp, "Deleted "); + + xfrm_selector_print(&xpinfo->sel, preferred_family, fp, NULL); + + fprintf(fp, "\t"); + fprintf(fp, "%s ", (xpinfo->dir == XFRM_POLICY_IN ? "in " : + xpinfo->dir == XFRM_POLICY_OUT ? "out" : + xpinfo->dir == XFRM_POLICY_FWD ? "fwd" : + "unknown-dir")); + fprintf(fp, "%s ", (xpinfo->action == XFRM_POLICY_ALLOW ? "allow" : + xpinfo->action == XFRM_POLICY_BLOCK ? "block" : + "unknown-action")); + fprintf(fp, "index %u ", xpinfo->index); + fprintf(fp, "priority %u ", xpinfo->priority); + fprintf(fp, "share %s ", strxf_share(xpinfo->share)); + fprintf(fp, "flags 0x%s", strxf_flags(xpinfo->flags)); + fprintf(fp, "\n"); + + if (show_stats > 0) + xfrm_lifetime_print(&xpinfo->lft, &xpinfo->curlft, fp, "\t"); + + xfrm_xfrma_print(tb, ntb, xpinfo->sel.family, fp, "\t"); + + return 0; +} + +static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, + void *res_nlbuf) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_id xpid; + } req; + char *dirp = NULL; + char *selp = NULL; + char *indexp = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY : XFRM_MSG_GETPOLICY; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpid.dir, &argc, &argv); + + } else if (strcmp(*argv, "sel") == 0) { + if (selp) + duparg("sel", *argv); + selp = *argv; + + NEXT_ARG(); + xfrm_selector_parse(&req.xpid.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpid.sel.family; + + } else if (strcmp(*argv, "index") == 0) { + if (indexp) + duparg("index", *argv); + indexp = *argv; + + NEXT_ARG(); + if (get_u32(&req.xpid.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + } else + invarg("unknown", *argv); + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: \"DIR\" is required.\n"); + exit(1); + } + if (!selp && !indexp) { + fprintf(stderr, "Not enough information: either \"SELECTOR\" or \"INDEX\" is required.\n"); + exit(1); + } + if (selp && indexp) + duparg2("SELECTOR", "INDEX"); + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpid.sel.family == AF_UNSPEC) + req.xpid.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, res_nlbuf, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_delete(int argc, char **argv) +{ + return xfrm_policy_get_or_delete(argc, argv, 1, NULL); +} + +static int xfrm_policy_get(int argc, char **argv) +{ + char buf[NLMSG_BUF_SIZE]; + struct nlmsghdr *n = (struct nlmsghdr *)buf; + + memset(buf, 0, sizeof(buf)); + + xfrm_policy_get_or_delete(argc, argv, 0, n); + + if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + return 0; +} + +/* + * With an existing policy of nlmsg, make new nlmsg for deleting the policy + * and store it to buffer. + */ +int xfrm_policy_keep(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_userpolicy_id *xpid; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_policy_filter_match(xpinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "Flush buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xpid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELPOLICY; + new_n->nlmsg_seq = ++rth->seq; + + xpid = NLMSG_DATA(new_n); + memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel)); + xpid->dir = xpinfo->dir; + xpid->index = xpinfo->index; + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count ++; + + return 0; +} + +static int xfrm_policy_list_or_flush(int argc, char **argv, int flush) +{ + struct rtnl_handle rth; + + if (argc > 0) + filter.use = 1; + filter.xpinfo.sel.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + NEXT_ARG(); + xfrm_policy_dir_parse(&filter.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "sel") == 0) { + NEXT_ARG(); + xfrm_selector_parse(&filter.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xpinfo.sel.family; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + filter.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + filter.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("\"action\" value is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.priority, *argv, 0)) + invarg("\"PRIORITY\" is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else + invarg("unknown", *argv); + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (flush) { + struct xfrm_buffer xb; + char buf[NLMSG_FLUSH_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Flush round = %d\n", i); + + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Flush completed\n"); + break; + } + + if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send flush request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + } else { + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +int do_xfrm_policy(int argc, char **argv) +{ + if (argc < 1) + return xfrm_policy_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_policy_modify(XFRM_MSG_UPDPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0) + return xfrm_policy_delete(argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_policy_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_policy_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return xfrm_policy_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm policy help\".\n", *argv); + exit(-1); +} diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c index e69de29b..9ea6257c 100644 --- a/ip/xfrm_state.c +++ b/ip/xfrm_state.c @@ -0,0 +1,636 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/xfrm.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +//#define NLMSG_FLUSH_BUF_SIZE (4096-512) +#define NLMSG_FLUSH_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_usersa_info + * rtattr + * rtattr + * ... (max count of rtattr is XFRM_MAX_DEPTH) + * + * each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_ALGO_KEY_BUF_SIZE 512 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n"); + fprintf(stderr, " [ reqid REQID ] [ FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ]\n"); + + fprintf(stderr, "Usage: ip xfrm state { delete | get } ID\n"); + fprintf(stderr, "Usage: ip xfrm state { flush | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n"); + fprintf(stderr, " [ FLAG_LIST ]\n"); + + fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); + fprintf(stderr, "XFRM_PROTO := [ esp | ah | ipcomp ]\n"); + //fprintf(stderr, "SPI - security parameter index(default=0)\n"); + + fprintf(stderr, "MODE := [ transport | tunnel ](default=transport)\n"); + //fprintf(stderr, "REQID - number(default=0)\n"); + + fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] [ flag FLAG ]\n"); + fprintf(stderr, "FLAG := [ noecn ]\n"); + + fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] | [ algo ALGO ]\n"); + fprintf(stderr, "ALGO := ALGO_TYPE ALGO_NAME ALGO_KEY\n"); + fprintf(stderr, "ALGO_TYPE := [ E | A | C ]\n"); + //fprintf(stderr, "ALGO_NAME - algorithm name\n"); + //fprintf(stderr, "ALGO_KEY - algorithm key\n"); + + fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ upspec UPSPEC ] [ dev DEV ]\n"); + + fprintf(stderr, "UPSPEC := proto PROTO [ sport PORT ] [ dport PORT ]\n"); + + //fprintf(stderr, "DEV - device name(default=none)\n"); + fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n"); + fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n"); + fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] COUNT ]\n"); + exit(-1); +} + +static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type, + char *name, char *key, int max) +{ + int len; + +#if 1 + /* XXX: verifying both name and key is required! */ + fprintf(stderr, "warning: ALGONAME/ALGOKEY will send to kernel promiscuously!(verifying them isn't implemented yet)\n"); +#endif + + strncpy(alg->alg_name, name, sizeof(alg->alg_name)); + + if (strncmp(key, "0x", 2) == 0) { + /* + * XXX: fix me!! + */ + __u64 val = 0; + char *p = (char *)&val; + + if (get_u64(&val, key, 16)) + invarg("\"ALGOKEY\" is invalid", key); + + len = (strlen(key) - 2) / 2; + if (len > sizeof(val)) + invarg("\"ALGOKEY\" is invalid: too large", key); + + if (len > 0) { + int index = sizeof(val) - len; + if (len > max) + invarg("\"ALGOKEY\" makes buffer overflow\n", key); + + memcpy(alg->alg_key, &p[index], len); + } + + } else { + len = strlen(key); + if (len > 0) { + if (len > max) + invarg("\"ALGOKEY\" makes buffer overflow\n", key); + + strncpy(alg->alg_key, key, len); + } + } + + alg->alg_key_len = len * 8; + + return 0; +} + +static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "noecn") == 0) + *flags |= XFRM_STATE_NOECN; + else + invarg("\"FLAG\" is invalid", *argv); + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_info xsinfo; + char buf[RTA_BUF_SIZE]; + } req; + char *idp = NULL; + char *ealgop = NULL; + char *aalgop = NULL; + char *calgop = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.xsinfo.family = preferred_family; + + req.xsinfo.lft.soft_byte_limit = XFRM_INF; + req.xsinfo.lft.hard_byte_limit = XFRM_INF; + req.xsinfo.lft.soft_packet_limit = XFRM_INF; + req.xsinfo.lft.hard_packet_limit = XFRM_INF; + + while (argc > 0) { + if (strcmp(*argv, "algo") == 0) { + struct { + struct xfrm_algo alg; + char buf[XFRM_ALGO_KEY_BUF_SIZE]; + } alg; + int len; + enum xfrm_attr_type_t type; + char *name; + char *key; + + NEXT_ARG(); + + if (strcmp(*argv, "E") == 0) { + if (ealgop) + duparg("ALGOTYPE", *argv); + ealgop = *argv; + type = XFRMA_ALG_CRYPT; + } else if (strcmp(*argv, "A") == 0) { + if (aalgop) + duparg("ALGOTYPE", *argv); + aalgop = *argv; + type = XFRMA_ALG_AUTH; + + } else if (strcmp(*argv, "C") == 0) { + if (calgop) + duparg("ALGOTYPE", *argv); + calgop = *argv; + type = XFRMA_ALG_COMP; + } else + invarg("\"ALGOTYPE\" is invalid\n", *argv); + + if (!NEXT_ARG_OK()) + missarg("ALGONAME"); + NEXT_ARG(); + name = *argv; + + if (!NEXT_ARG_OK()) + missarg("ALGOKEY"); + NEXT_ARG(); + key = *argv; + + memset(&alg, 0, sizeof(alg)); + + xfrm_algo_parse((void *)&alg, type, name, key, sizeof(alg.buf)); + len = sizeof(struct xfrm_algo) + alg.alg.alg_key_len; + + addattr_l(&req.n, sizeof(req.buf), type, + (void *)&alg, len); + + } else if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); + } else if (strcmp(*argv, "sel") == 0) { + NEXT_ARG(); + xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); + + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); + } else { + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, + &req.xsinfo.family, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xsinfo.family; + } + argc--; argv++; + } + + if (!idp) { + fprintf(stderr, "Not enough information: \"ID\" is required\n"); + exit(1); + } + + if (ealgop || aalgop || calgop) { + if (req.xsinfo.id.proto != IPPROTO_ESP && + req.xsinfo.id.proto != IPPROTO_AH && + req.xsinfo.id.proto != IPPROTO_COMP) { + fprintf(stderr, "\"ALGO\" is invalid with proto=%d\n", req.xsinfo.id.proto); + exit(1); + } + } else { + if (req.xsinfo.id.proto == IPPROTO_ESP || + req.xsinfo.id.proto == IPPROTO_AH || + req.xsinfo.id.proto == IPPROTO_COMP) { + fprintf(stderr, "\"ALGO\" is required with proto=%d\n", req.xsinfo.id.proto); + exit (1); + } + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsinfo.family == AF_UNSPEC) + req.xsinfo.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo) +{ + if (!filter.use) + return 1; + + if (filter.id_src_mask) + if (memcmp(&xsinfo->saddr, &filter.xsinfo.saddr, + filter.id_src_mask) != 0) + return 0; + if (filter.id_dst_mask) + if (memcmp(&xsinfo->id.daddr, &filter.xsinfo.id.daddr, + filter.id_dst_mask) != 0) + return 0; + if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask) + return 0; + if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask) + return 0; + if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask) + return 0; + if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask) + return 0; + if (filter.state_flags_mask) + if ((xsinfo->flags & filter.xsinfo.flags) == 0) + return 0; + + return 1; +} + +int xfrm_state_print(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[XFRMA_MAX+1]; + int ntb; + + if (n->nlmsg_type != XFRM_MSG_NEWSA && + n->nlmsg_type != XFRM_MSG_DELSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + memset(tb, 0, sizeof(tb)); + ntb = parse_rtattr_byindex(tb, XFRM_MAX_DEPTH, XFRMS_RTA(xsinfo), len); + + if (n->nlmsg_type == XFRM_MSG_DELSA) + fprintf(fp, "Deleted "); + + xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode, + xsinfo->reqid, xsinfo->family, fp, NULL); + + if (show_stats > 0) { + fprintf(fp, "\t"); + fprintf(fp, "seq 0x%08u ", xsinfo->seq); + fprintf(fp, "replay-window %d ", xsinfo->replay_window); + fprintf(fp, "flags "); + if (xsinfo->flags & XFRM_STATE_NOECN) + fprintf(fp, "noecn "); + fprintf(fp, "(0x%s)", strxf_flags(xsinfo->flags)); + + fprintf(fp, "\n"); + } + + xfrm_xfrma_print(tb, ntb, xsinfo->family, fp, "\t"); + + if (show_stats > 0) { + fprintf(fp, "\tsel:\n"); + xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, "\t "); + } + + if (show_stats > 0) { + xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, "\t"); + xfrm_stats_print(&xsinfo->stats, fp, "\t"); + } + + return 0; +} + +static int xfrm_state_get_or_delete(int argc, char **argv, int delete) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_id xsid; + } req; + struct xfrm_id id; + char *idp = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA; + req.xsid.family = preferred_family; + + while (argc > 0) { + /* + * XXX: Source address is not used and ignore it to follow + * XXX: a manner of setkey e.g. in the case of deleting/getting + * XXX: message of IPsec SA. + */ + xfrm_address_t ignore_saddr; + + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + memset(&id, 0, sizeof(id)); + xfrm_id_parse(&ignore_saddr, &id, &req.xsid.family, + &argc, &argv); + + memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr)); + req.xsid.spi = id.spi; + req.xsid.proto = id.proto; + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsid.family == AF_UNSPEC) + req.xsid.family = AF_INET; + + if (delete) { + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + } else { + char buf[NLMSG_BUF_SIZE]; + struct nlmsghdr *res_n = (struct nlmsghdr *)buf; + + memset(buf, 0, sizeof(buf)); + + if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0) + exit(2); + + if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + } + + rtnl_close(&rth); + + return 0; +} + +/* + * With an existing state of nlmsg, make new nlmsg for deleting the state + * and store it to buffer. + */ +int xfrm_state_keep(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_usersa_id *xsid; + + if (n->nlmsg_type != XFRM_MSG_NEWSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "Flush buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELSA; + new_n->nlmsg_seq = ++rth->seq; + + xsid = NLMSG_DATA(new_n); + xsid->family = xsinfo->family; + memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr)); + xsid->spi = xsinfo->id.spi; + xsid->proto = xsinfo->id.proto; + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count ++; + + return 0; +} + +static int xfrm_state_list_or_flush(int argc, char **argv, int flush) +{ + char *idp = NULL; + struct rtnl_handle rth; + + filter.use = 1; + filter.xsinfo.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv); + + filter.mode_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv); + + filter.reqid_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv); + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&filter.xsinfo.saddr, + &filter.xsinfo.id, + &filter.xsinfo.family, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xsinfo.family; + } + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (flush) { + struct xfrm_buffer xb; + char buf[NLMSG_FLUSH_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Flush round = %d\n", i); + + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Flush completed\n"); + break; + } + + if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send flush request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + + } else { + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_print, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +int do_xfrm_state(int argc, char **argv) +{ + if (argc < 1) + return xfrm_state_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_state_modify(XFRM_MSG_NEWSA, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_state_modify(XFRM_MSG_UPDSA, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_state_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return xfrm_state_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv); + exit(-1); +} |