diff options
Diffstat (limited to 'sbin/atm/atmconfig/natm.c')
-rw-r--r-- | sbin/atm/atmconfig/natm.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/sbin/atm/atmconfig/natm.c b/sbin/atm/atmconfig/natm.c new file mode 100644 index 0000000..a383f8f --- /dev/null +++ b/sbin/atm/atmconfig/natm.c @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2003 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Hartmut Brandt <harti@freebsd.org> + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_mib.h> +#include <net/if_atm.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include "atmconfig.h" +#include "private.h" +#include "diag.h" + +static void natm_add(int, char *[]); +static void natm_delete(int, char *[]); +static void natm_show(int, char *[]); + +const struct cmdtab natm_tab[] = { + { "add", NULL, natm_add }, + { "delete", NULL, natm_delete }, + { "show", NULL, natm_show }, + { NULL, NULL, NULL } +}; + +/* + * Structure to hold a route + */ +struct natm_route { + TAILQ_ENTRY(natm_route) link; + struct in_addr host; + struct diagif *aif; + u_int flags; + int llcsnap; + u_int vpi, vci; + u_int traffic; + u_int pcr, scr, mbs, icr, mcr; + u_int tbe, nrm, trm, adtf, rif, rdf, cdf; +}; +static TAILQ_HEAD(, natm_route) natm_route_list = + TAILQ_HEAD_INITIALIZER(natm_route_list); + +static void +store_route(struct rt_msghdr *rtm) +{ + u_int i; + struct natm_route *r; + char *cp; + struct sockaddr *sa; + struct sockaddr_in *sain; + struct sockaddr_dl *sdl; + struct diagif *aif; + u_int n; + + r = malloc(sizeof(*r)); + if (r == NULL) + err(1, "allocate route"); + + r->flags = rtm->rtm_flags; + cp = (char *)(rtm + 1); + for (i = 1; i != 0; i <<= 1) { + if (rtm->rtm_addrs & i) { + sa = (struct sockaddr *)cp; + cp += roundup(sa->sa_len, sizeof(long)); + switch (i) { + + case RTA_DST: + if (sa->sa_family != AF_INET) { + warnx("RTA_DST not AF_INET %u", sa->sa_family); + goto fail; + } + sain = (struct sockaddr_in *)(void *)sa; + if (sain->sin_len < 4) + r->host.s_addr = INADDR_ANY; + else + r->host = sain->sin_addr; + break; + + case RTA_GATEWAY: + if (sa->sa_family != AF_LINK) { + warnx("RTA_GATEWAY not AF_LINK"); + goto fail; + } + sdl = (struct sockaddr_dl *)(void *)sa; + TAILQ_FOREACH(aif, &diagif_list, link) + if (strlen(aif->ifname) == + sdl->sdl_nlen && + strncmp(aif->ifname, sdl->sdl_data, + sdl->sdl_nlen) == 0) + break; + if (aif == NULL) { + warnx("interface '%.*s' not found", + sdl->sdl_nlen, sdl->sdl_data); + goto fail; + } + r->aif = aif; + + /* parse ATM stuff */ + +#define GET3() (((sdl->sdl_data[n] & 0xff) << 16) | \ + ((sdl->sdl_data[n + 1] & 0xff) << 8) | \ + ((sdl->sdl_data[n + 2] & 0xff) << 0)) +#define GET2() (((sdl->sdl_data[n] & 0xff) << 8) | \ + ((sdl->sdl_data[n + 1] & 0xff) << 0)) +#define GET1() (((sdl->sdl_data[n] & 0xff) << 0)) + + n = sdl->sdl_nlen; + if (sdl->sdl_alen < 4) { + warnx("RTA_GATEWAY alen too short"); + goto fail; + } + r->llcsnap = GET1() & ATM_PH_LLCSNAP; + n++; + r->vpi = GET1(); + n++; + r->vci = GET2(); + n += 2; + if (sdl->sdl_alen == 4) { + /* old address */ + r->traffic = ATMIO_TRAFFIC_UBR; + r->pcr = 0; + break; + } + /* new address */ + r->traffic = GET1(); + n++; + switch (r->traffic) { + + case ATMIO_TRAFFIC_UBR: + if (sdl->sdl_alen >= 5 + 3) { + r->pcr = GET3(); + n += 3; + } else + r->pcr = 0; + break; + + case ATMIO_TRAFFIC_CBR: + if (sdl->sdl_alen < 5 + 3) { + warnx("CBR address too short"); + goto fail; + } + r->pcr = GET3(); + n += 3; + break; + + case ATMIO_TRAFFIC_VBR: + if (sdl->sdl_alen < 5 + 3 * 3) { + warnx("VBR address too short"); + goto fail; + } + r->pcr = GET3(); + n += 3; + r->scr = GET3(); + n += 3; + r->mbs = GET3(); + n += 3; + break; + + case ATMIO_TRAFFIC_ABR: + if (sdl->sdl_alen < 5 + 4 * 3 + 2 + + 1 * 2 + 3) { + warnx("ABR address too short"); + goto fail; + } + r->pcr = GET3(); + n += 3; + r->mcr = GET3(); + n += 3; + r->icr = GET3(); + n += 3; + r->tbe = GET3(); + n += 3; + r->nrm = GET1(); + n++; + r->trm = GET1(); + n++; + r->adtf = GET2(); + n += 2; + r->rif = GET1(); + n++; + r->rdf = GET1(); + n++; + r->cdf = GET1(); + n++; + break; + + default: + goto fail; + } + break; + } + } + } + + TAILQ_INSERT_TAIL(&natm_route_list, r, link); + + return; + fail: + free(r); +} + +/* + * Fetch the INET routes that a ours + */ +static void +natm_route_fetch(void) +{ + int name[6]; + size_t needed; + u_char *buf, *next; + struct rt_msghdr *rtm; + + name[0] = CTL_NET; + name[1] = PF_ROUTE; + name[2] = 0; + name[3] = AF_INET; + name[4] = NET_RT_DUMP; + name[5] = 0; + + if (sysctl(name, 6, NULL, &needed, NULL, 0) == -1) + err(1, "rtable estimate"); + needed *= 2; + if ((buf = malloc(needed)) == NULL) + err(1, "rtable buffer (%zu)", needed); + if (sysctl(name, 6, buf, &needed, NULL, 0) == -1) + err(1, "rtable get"); + + next = buf; + while (next < buf + needed) { + rtm = (struct rt_msghdr *)(void *)next; + next += rtm->rtm_msglen; + + if (rtm->rtm_type == RTM_GET) { + if ((rtm->rtm_flags & (RTF_UP | RTF_HOST | + RTF_STATIC)) == (RTF_UP | RTF_HOST | RTF_STATIC) && + (rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY | + RTA_IFP)) == (RTA_DST | RTA_GATEWAY | RTA_IFP)) + store_route(rtm); + } + } +} + +static u_long +parse_num(const char *arg, const char *name, u_long limit) +{ + u_long res; + char *end; + + errno = 0; + res = strtoul(arg, &end, 10); + if (*end != '\0' || end == arg || errno != 0) + errx(1, "cannot parse %s '%s'", name, arg); + if (res > limit) + errx(1, "%s out of range (0...%lu)", name, limit); + return (res); +} + +static void +do_route(u_int type, u_int flags, const struct sockaddr_in *sain, + const struct sockaddr_dl *sdl) +{ + struct { + struct rt_msghdr h; + char space[512]; + } msg; + char *ptr; + int s; + ssize_t rlen; + + /* create routing message */ + bzero(&msg, sizeof(msg)); + msg.h.rtm_msglen = sizeof(msg.h); + msg.h.rtm_version = RTM_VERSION; + msg.h.rtm_type = type; + msg.h.rtm_index = 0; + msg.h.rtm_flags = flags; + msg.h.rtm_addrs = RTA_DST | (sdl != NULL ? RTA_GATEWAY : 0); + msg.h.rtm_pid = getpid(); + + ptr = (char *)&msg + sizeof(msg.h); + memcpy(ptr, sain, sain->sin_len); + ptr += roundup(sain->sin_len, sizeof(long)); + msg.h.rtm_msglen += roundup(sain->sin_len, sizeof(long)); + + if (sdl != NULL) { + memcpy(ptr, sdl, sdl->sdl_len); + ptr += roundup(sdl->sdl_len, sizeof(long)); + msg.h.rtm_msglen += roundup(sdl->sdl_len, sizeof(long)); + } + + /* open socket */ + s = socket(PF_ROUTE, SOCK_RAW, AF_INET); + if (s == -1) + err(1, "cannot open routing socket"); + + rlen = write(s, &msg, msg.h.rtm_msglen); + if (rlen == -1) + err(1, "writing to routing socket"); + if ((size_t)rlen != msg.h.rtm_msglen) + errx(1, "short write to routing socket: %zu %u", + (size_t)rlen, msg.h.rtm_msglen); + close(s); +} + +/* + * Add a new NATM route + */ +static void +natm_add(int argc, char *argv[]) +{ + int opt; + struct hostent *hp; + struct sockaddr_in sain; + struct sockaddr_dl sdl; + struct diagif *aif; + u_long num, num1; + u_int idx; + + static int printonly; + + static const struct option opts[] = { + { "printonly", OPT_SIMPLE, &printonly }, + { NULL, 0, NULL } + }; + + while ((opt = parse_options(&argc, &argv, opts)) != -1) + switch (opt) { + } + + if (argc < 5) + errx(1, "missing arguments for 'natm add'"); + + memset(&sdl, 0, sizeof(sdl)); + sdl.sdl_len = sizeof(sdl); + sdl.sdl_family = AF_LINK; + + /* get the IP address for <dest> */ + memset(&sain, 0, sizeof(sain)); + hp = gethostbyname(argv[0]); + if (hp == NULL) + errx(1, "bad hostname %s: %s", argv[0], hstrerror(h_errno)); + if (hp->h_addrtype != AF_INET) + errx(1, "bad address type for %s", argv[0]); + sain.sin_len = sizeof(sain); + sain.sin_family = AF_INET; + memcpy(&sain.sin_addr, hp->h_addr, sizeof(sain.sin_addr)); + + /* find interface */ + diagif_fetch(); + TAILQ_FOREACH(aif, &diagif_list, link) + if (strcmp(aif->ifname, argv[1]) == 0) + break; + if (aif == NULL) + errx(1, "unknown ATM interface '%s'", argv[1]); + sdl.sdl_index = aif->index; + strcpy(sdl.sdl_data, aif->ifname); + idx = sdl.sdl_nlen = strlen(aif->ifname); + idx++; + + /* verify VPI/VCI */ + num = parse_num(argv[2], "VPI", (1U << aif->mib.vpi_bits)); + sdl.sdl_data[idx++] = num & 0xff; + num = parse_num(argv[3], "VCI", (1U << aif->mib.vci_bits)); + if (num == 0) + errx(1, "VCI may not be 0"); + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = num & 0xff; + + /* encapsulation */ + if (strcasecmp(argv[4], "llc/snap") == 0) { + sdl.sdl_data[sdl.sdl_nlen] = ATM_PH_LLCSNAP; + } else if (strcasecmp(argv[4], "aal5") == 0) { + sdl.sdl_data[sdl.sdl_nlen] = 0; + } else + errx(1, "bad encapsulation type '%s'", argv[4]); + + /* look at the traffic */ + argc -= 5; + argv += 5; + + if (argc != 0) { + if (strcasecmp(argv[0], "ubr") == 0) { + sdl.sdl_data[idx++] = ATMIO_TRAFFIC_UBR; + if (argc == 1) + /* ok */; + else if (argc == 2) { + num = parse_num(argv[1], "PCR", aif->mib.pcr); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + } else + errx(1, "too many parameters for UBR"); + + } else if (strcasecmp(argv[0], "cbr") == 0) { + sdl.sdl_data[idx++] = ATMIO_TRAFFIC_CBR; + if (argc == 1) + errx(1, "missing PCR for CBR"); + if (argc > 2) + errx(1, "too many parameters for CBR"); + num = parse_num(argv[1], "PCR", aif->mib.pcr); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + } else if (strcasecmp(argv[0], "vbr") == 0) { + sdl.sdl_data[idx++] = ATMIO_TRAFFIC_VBR; + + if (argc < 4) + errx(1, "missing arg(s) for VBR"); + if (argc > 4) + errx(1, "too many parameters for VBR"); + + num = parse_num(argv[1], "PCR", aif->mib.pcr); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + num = parse_num(argv[2], "SCR", num); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + num = parse_num(argv[3], "MBS", 0xffffffLU); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + } else if (strcasecmp(argv[0], "abr") == 0) { + sdl.sdl_data[idx++] = ATMIO_TRAFFIC_ABR; + if (argc < 11) + errx(1, "missing arg(s) for ABR"); + if (argc > 11) + errx(1, "too many parameters for ABR"); + + num = parse_num(argv[1], "PCR", aif->mib.pcr); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num1 = parse_num(argv[2], "MCR", num); + sdl.sdl_data[idx++] = (num1 >> 16) & 0xff; + sdl.sdl_data[idx++] = (num1 >> 8) & 0xff; + sdl.sdl_data[idx++] = (num1 >> 0) & 0xff; + + num = parse_num(argv[3], "ICR", num); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + if (num < num1) + errx(1, "ICR must be >= MCR"); + + num = parse_num(argv[4], "TBE", 0xffffffUL); + sdl.sdl_data[idx++] = (num >> 16) & 0xff; + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num = parse_num(argv[5], "NRM", 0x7UL); + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num = parse_num(argv[6], "TRM", 0x7UL); + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num = parse_num(argv[7], "ADTF", 0x3ffUL); + sdl.sdl_data[idx++] = (num >> 8) & 0xff; + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num = parse_num(argv[8], "RIF", 0xfUL); + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num = parse_num(argv[9], "RDF", 0xfUL); + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + num = parse_num(argv[10], "CDF", 0x7UL); + sdl.sdl_data[idx++] = (num >> 0) & 0xff; + + } else + errx(1, "bad traffic type '%s'", argv[0]); + } else + sdl.sdl_data[idx++] = ATMIO_TRAFFIC_UBR; + + sdl.sdl_alen = idx - sdl.sdl_nlen; + sdl.sdl_len += sdl.sdl_nlen + sdl.sdl_alen; + + if (printonly) { + printf("route add -iface %s -link %.*s", + inet_ntoa(sain.sin_addr), sdl.sdl_nlen, sdl.sdl_data); + for (idx = 0; idx < sdl.sdl_alen; idx++) + printf("%c%x", ".:"[idx == 0], + (u_int)sdl.sdl_data[sdl.sdl_nlen + idx] & 0xffU); + printf("\n"); + exit(0); + } + + do_route(RTM_ADD, RTF_HOST | RTF_STATIC | RTF_UP, &sain, &sdl); +} + +/* + * Delete an NATM route + */ +static void +natm_delete(int argc, char *argv[]) +{ + int opt; + struct hostent *hp; + struct sockaddr_in sain; + u_int vpi, vci; + struct diagif *aif; + struct natm_route *r; + + static int printonly; + + static const struct option opts[] = { + { "printonly", OPT_SIMPLE, &printonly }, + { NULL, 0, NULL } + }; + + while ((opt = parse_options(&argc, &argv, opts)) != -1) + switch (opt) { + } + + diagif_fetch(); + natm_route_fetch(); + + memset(&sain, 0, sizeof(sain)); + sain.sin_len = sizeof(sain); + sain.sin_family = AF_INET; + + if (argc == 1) { + /* get the IP address for <dest> */ + hp = gethostbyname(argv[0]); + if (hp == NULL) + errx(1, "bad hostname %s: %s", argv[0], + hstrerror(h_errno)); + if (hp->h_addrtype != AF_INET) + errx(1, "bad address type for %s", argv[0]); + memcpy(&sain.sin_addr, hp->h_addr, sizeof(sain.sin_addr)); + + TAILQ_FOREACH(r, &natm_route_list, link) + if (r->host.s_addr == sain.sin_addr.s_addr) + break; + if (r == NULL) + errx(1, "no NATM route to host '%s' (%s)", argv[0], + inet_ntoa(sain.sin_addr)); + + } else if (argc == 3) { + TAILQ_FOREACH(aif, &diagif_list, link) + if (strcmp(aif->ifname, argv[0]) == 0) + break; + if (aif == 0) + errx(1, "no such interface '%s'", argv[0]); + + vpi = parse_num(argv[1], "VPI", 0xff); + vci = parse_num(argv[2], "VCI", 0xffff); + + TAILQ_FOREACH(r, &natm_route_list, link) + if (r->aif == aif && r->vpi == vpi && r->vci == vci) + break; + if (r == NULL) + errx(1, "no such NATM route %s %u %u", argv[0], + vpi, vci); + sain.sin_addr = r->host; + + } else + errx(1, "bad number of arguments for 'natm delete'"); + + if (printonly) { + printf("route delete %s\n", inet_ntoa(r->host)); + exit(0); + } + + do_route(RTM_DELETE, r->flags, &sain, NULL); +} + +/* + * Show NATM routes + */ +static void +natm_show(int argc, char *argv[]) +{ + int opt; + struct natm_route *r; + struct hostent *hp; + + static const char *const traffics[] = { + [ATMIO_TRAFFIC_UBR] = "UBR", + [ATMIO_TRAFFIC_CBR] = "CBR", + [ATMIO_TRAFFIC_VBR] = "VBR", + [ATMIO_TRAFFIC_ABR] = "ABR" + }; + + static int numeric, abr; + + static const struct option opts[] = { + { "abr", OPT_SIMPLE, &abr }, + { "numeric", OPT_SIMPLE, &numeric }, + { NULL, 0, NULL } + }; + + static const char head[] = + "Destination Iface VPI VCI Encaps Trf PCR " + "SCR/MCR MBS/ICR\n"; + static const char head_abr[] = + "Destination Iface VPI VCI Encaps Trf PCR " + "SCR/MCR MBS/ICR TBE NRM TRM ADTF RIF RDF CDF\n"; + + while ((opt = parse_options(&argc, &argv, opts)) != -1) + switch (opt) { + } + + diagif_fetch(); + natm_route_fetch(); + + heading_init(); + TAILQ_FOREACH(r, &natm_route_list, link) { + heading(abr ? head_abr : head); + if (numeric) + printf("%-20s", inet_ntoa(r->host)); + else if (r->host.s_addr == INADDR_ANY) + printf("%-20s", "default"); + else { + hp = gethostbyaddr((char *)&r->host, sizeof(r->host), + AF_INET); + if (hp != NULL) + printf("%-20s", hp->h_name); + else + printf("%-20s", inet_ntoa(r->host)); + } + printf("%-12s%-4u%-6u%-9s%-4s", r->aif->ifname, r->vpi, r->vci, + r->llcsnap ? "LLC/SNAP" : "AAL5", traffics[r->traffic]); + switch (r->traffic) { + + case ATMIO_TRAFFIC_UBR: + case ATMIO_TRAFFIC_CBR: + printf("%-8u", r->pcr); + break; + + case ATMIO_TRAFFIC_VBR: + printf("%-8u%-8u%-8u", r->pcr, r->scr, r->mbs); + break; + + case ATMIO_TRAFFIC_ABR: + printf("%-8u%-8u%-8u", r->pcr, r->mcr, r->icr); + if (abr) + printf("%-8u%-4u%-4u%-5u%-4u%-4u%-4u", + r->tbe, r->nrm, r->trm, r->adtf, + r->rif, r->rdf, r->cdf); + break; + } + printf("\n"); + } +} |