/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C)2004 USAGI/WIDE Project * * based on ip.c, iproute.c * * Authors: * Masahide NAKAMURA @USAGI */ #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "xfrm.h" #include "ip_common.h" #define STRBUF_SIZE (128) 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 | monitor\n"); exit(-1); } /* This is based on utils.c(inet_addr_match) */ int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits) { __u32 *a1 = (__u32 *)x1; __u32 *a2 = (__u32 *)x2; int words = bits >> 0x05; bits &= 0x1f; if (words) if (memcmp(a1, a2, words << 2)) return -1; if (bits) { __u32 w1, w2; __u32 mask; w1 = a1[words]; w2 = a2[words]; mask = htonl((0xffffffff) << (0x20 - bits)); if ((w1 ^ w2) & mask) return 1; } return 0; } int xfrm_xfrmproto_is_ipsec(__u8 proto) { return (proto == IPPROTO_ESP || proto == IPPROTO_AH || proto == IPPROTO_COMP); } int xfrm_xfrmproto_is_ro(__u8 proto) { return (proto == IPPROTO_ROUTING || proto == IPPROTO_DSTOPTS); } struct typeent { const char *t_name; int t_type; }; static const struct typeent xfrmproto_types[] = { { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP }, { "route2", IPPROTO_ROUTING }, { "hao", IPPROTO_DSTOPTS }, { "ipsec-any", IPSEC_PROTO_ANY }, { NULL, -1 } }; int xfrm_xfrmproto_getbyname(char *name) { int i; for (i = 0; ; i++) { const struct typeent *t = &xfrmproto_types[i]; if (!t->t_name || t->t_type == -1) break; if (strcmp(t->t_name, name) == 0) return t->t_type; } return -1; } const char *strxf_xfrmproto(__u8 proto) { static char str[16]; int i; for (i = 0; ; i++) { const struct typeent *t = &xfrmproto_types[i]; if (!t->t_name || t->t_type == -1) break; if (t->t_type == proto) return t->t_name; } sprintf(str, "%u", proto); return str; } static const struct typeent algo_types[] = { { "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH }, { "comp", XFRMA_ALG_COMP }, { "aead", XFRMA_ALG_AEAD }, { "auth-trunc", XFRMA_ALG_AUTH_TRUNC }, { NULL, -1 } }; int xfrm_algotype_getbyname(char *name) { int i; for (i = 0; ; i++) { const struct typeent *t = &algo_types[i]; if (!t->t_name || t->t_type == -1) break; if (strcmp(t->t_name, name) == 0) return t->t_type; } return -1; } const char *strxf_algotype(int type) { static char str[32]; int i; for (i = 0; ; i++) { const struct typeent *t = &algo_types[i]; if (!t->t_name || t->t_type == -1) break; if (t->t_type == type) return t->t_name; } sprintf(str, "%d", type); return str; } static const char *strxf_mask8(__u8 mask) { static char str[16]; const int sn = sizeof(mask) * 8 - 1; __u8 b; int i = 0; for (b = (1 << sn); b > 0; b >>= 1) str[i++] = ((b & mask) ? '1' : '0'); str[i] = '\0'; return str; } const char *strxf_mask32(__u32 mask) { static char str[16]; sprintf(str, "%.8x", mask); return str; } static 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, "%u", share); break; } return str; } const char *strxf_proto(__u8 proto) { static char buf[32]; struct protoent *pp; const char *p; pp = getprotobynumber(proto); if (pp) p = pp->p_name; else { sprintf(buf, "%u", proto); p = buf; } return p; } const char *strxf_ptype(__u8 ptype) { static char str[16]; switch (ptype) { case XFRM_POLICY_TYPE_MAIN: strcpy(str, "main"); break; case XFRM_POLICY_TYPE_SUB: strcpy(str, "sub"); break; default: sprintf(str, "%u", ptype); break; } return str; } static void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, __u8 mode, __u32 reqid, __u16 family, int force_spi, FILE *fp, const char *prefix, const char *title) { if (title) fputs(title, fp); fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), saddr)); fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), &id->daddr)); fprintf(fp, "%s", _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, "\t"); fprintf(fp, "proto %s ", strxf_xfrmproto(id->proto)); if (show_stats > 0 || force_spi || id->spi) { __u32 spi = ntohl(id->spi); fprintf(fp, "spi 0x%08x", spi); if (show_stats > 0) fprintf(fp, "(%u)", spi); fprintf(fp, " "); } fprintf(fp, "reqid %u", reqid); if (show_stats > 0) fprintf(fp, "(0x%08x)", reqid); fprintf(fp, " "); fprintf(fp, "mode "); switch (mode) { case XFRM_MODE_TRANSPORT: fprintf(fp, "transport"); break; case XFRM_MODE_TUNNEL: fprintf(fp, "tunnel"); break; case XFRM_MODE_ROUTEOPTIMIZATION: fprintf(fp, "ro"); break; case XFRM_MODE_IN_TRIGGER: fprintf(fp, "in_trigger"); break; case XFRM_MODE_BEET: fprintf(fp, "beet"); break; default: fprintf(fp, "%u", mode); break; } fprintf(fp, "%s", _SL_); } static const char *strxf_limit(__u64 limit) { static char str[32]; if (limit == XFRM_INF) strcpy(str, "(INF)"); else sprintf(str, "%llu", (unsigned long long) limit); return str; } static void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix) { if (prefix) fputs(prefix, fp); fprintf(fp, "stats:%s", _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " replay-window %u replay %u failed %u%s", s->replay_window, s->replay, s->integrity_failed, _SL_); } static const char *strxf_time(__u64 time) { static char str[32]; if (time == 0) strcpy(str, "-"); else { time_t t; struct tm *tp; /* XXX: treat time in the same manner of kernel's * net/xfrm/xfrm_{user,state}.c */ t = (long)time; tp = localtime(&t); strftime(str, sizeof(str), "%Y-%m-%d %T", tp); } return str; } static void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, struct xfrm_lifetime_cur *cur, FILE *fp, const char *prefix) { if (cfg) { if (prefix) fputs(prefix, fp); fprintf(fp, "lifetime config:%s", _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " limit: soft %s(bytes),", strxf_limit(cfg->soft_byte_limit)); fprintf(fp, " hard %s(bytes)%s", strxf_limit(cfg->hard_byte_limit), _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " limit: soft %s(packets),", strxf_limit(cfg->soft_packet_limit)); fprintf(fp, " hard %s(packets)%s", strxf_limit(cfg->hard_packet_limit), _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " expire add: soft %llu(sec), hard %llu(sec)%s", (unsigned long long) cfg->soft_add_expires_seconds, (unsigned long long) cfg->hard_add_expires_seconds, _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " expire use: soft %llu(sec), hard %llu(sec)%s", (unsigned long long) cfg->soft_use_expires_seconds, (unsigned long long) cfg->hard_use_expires_seconds, _SL_); } if (cur) { if (prefix) fputs(prefix, fp); fprintf(fp, "lifetime current:%s", _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " %llu(bytes), %llu(packets)%s", (unsigned long long) cur->bytes, (unsigned long long) cur->packets, _SL_); if (prefix) fputs(prefix, fp); fprintf(fp, " add %s ", strxf_time(cur->add_time)); fprintf(fp, "use %s%s", strxf_time(cur->use_time), _SL_); } } void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, FILE *fp, const char *prefix) { __u16 f; f = sel->family; if (f == AF_UNSPEC) f = family; if (f == AF_UNSPEC) f = preferred_family; if (prefix) fputs(prefix, fp); fprintf(fp, "src %s/%u ", rt_addr_n2a(f, sizeof(sel->saddr), &sel->saddr), sel->prefixlen_s); fprintf(fp, "dst %s/%u ", rt_addr_n2a(f, sizeof(sel->daddr), &sel->daddr), sel->prefixlen_d); if (sel->proto) fprintf(fp, "proto %s ", strxf_proto(sel->proto)); switch (sel->proto) { case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_SCTP: case IPPROTO_DCCP: default: /* XXX */ if (sel->sport_mask) fprintf(fp, "sport %u ", ntohs(sel->sport)); if (sel->dport_mask) fprintf(fp, "dport %u ", ntohs(sel->dport)); break; case IPPROTO_ICMP: case IPPROTO_ICMPV6: /* type/code is stored at sport/dport in selector */ if (sel->sport_mask) fprintf(fp, "type %u ", ntohs(sel->sport)); if (sel->dport_mask) fprintf(fp, "code %u ", ntohs(sel->dport)); break; case IPPROTO_GRE: if (sel->sport_mask || sel->dport_mask) fprintf(fp, "key %u ", (((__u32)ntohs(sel->sport)) << 16) + ntohs(sel->dport)); break; case IPPROTO_MH: if (sel->sport_mask) fprintf(fp, "type %u ", ntohs(sel->sport)); if (sel->dport_mask) { if (show_stats > 0) fprintf(fp, "(dport) 0x%.4x ", sel->dport); } break; } if (sel->ifindex > 0) fprintf(fp, "dev %s ", ll_index_to_name(sel->ifindex)); if (show_stats > 0) fprintf(fp, "uid %u", sel->user); fprintf(fp, "%s", _SL_); } static void __xfrm_algo_print(struct xfrm_algo *algo, int type, int len, FILE *fp, const char *prefix, int newline, bool nokeys) { int keylen; int i; if (prefix) fputs(prefix, fp); fprintf(fp, "%s ", strxf_algotype(type)); if (len < sizeof(*algo)) { fprintf(fp, "(ERROR truncated)"); goto fin; } len -= sizeof(*algo); fprintf(fp, "%s ", algo->alg_name); keylen = algo->alg_key_len / 8; if (len < keylen) { fprintf(fp, "(ERROR truncated)"); goto fin; } if (nokeys) fprintf(fp, "<