/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "structs/structs.h" #include "structs/type/array.h" #include "net/route_msg.h" #include "net/uroute.h" #include "util/typed_mem.h" #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) ((x) += ROUNDUP((n)->sa_len)) #ifdef RTF_CLONING #define WRITABLE_FLAGS (RTF_STATIC | RTF_LLINFO | RTF_REJECT | RTF_BLACKHOLE \ | RTF_PROTO1 | RTF_PROTO2 | RTF_CLONING \ | RTF_XRESOLVE | RTF_UP | RTF_GATEWAY) #else #define WRITABLE_FLAGS (RTF_STATIC | RTF_REJECT | RTF_BLACKHOLE \ | RTF_PROTO1 | RTF_PROTO2 \ | RTF_XRESOLVE | RTF_UP | RTF_GATEWAY) #endif struct route_flag { const char *name; int bit; }; static const struct route_flag route_flags[] = { #define FLAG(x) { #x, RTF_ ## x } FLAG(UP), FLAG(GATEWAY), FLAG(HOST), FLAG(REJECT), FLAG(DYNAMIC), FLAG(MODIFIED), FLAG(DONE), #ifdef RTF_CLONING FLAG(CLONING), #endif FLAG(XRESOLVE), #ifdef RTF_LLINFO FLAG(LLINFO), #endif FLAG(STATIC), FLAG(BLACKHOLE), FLAG(PROTO2), FLAG(PROTO1), FLAG(PRCLONING), #ifdef RTF_WASCLONED FLAG(WASCLONED), #endif FLAG(PROTO3), FLAG(PINNED), FLAG(LOCAL), FLAG(BROADCAST), FLAG(MULTICAST), #undef FLAG { NULL, 0 } }; /* Route structure */ struct uroute { int flags; struct sockaddr *dest; struct sockaddr *gateway; struct sockaddr *netmask; }; /* * Internal functions */ static int uroute_do_route(struct uroute *route, int sock, int type); static const char *uroute_sockaddr_string(const struct sockaddr *sa); /* * Create a new route. */ struct uroute * uroute_create(const struct sockaddr *dest, const struct sockaddr *gateway, const struct sockaddr *netmask) { struct uroute *route; if (dest == NULL || gateway == NULL) { errno = EINVAL; return (NULL); } if ((route = MALLOC("uroute", sizeof(*route))) == NULL) return (NULL); memset(route, 0, sizeof(*route)); if ((route->dest = MALLOC("uroute.dest", dest->sa_len)) == NULL) goto fail; memcpy(route->dest, dest, dest->sa_len); if ((route->gateway = MALLOC("uroute.gateway", gateway->sa_len)) == NULL) goto fail; memcpy(route->gateway, gateway, gateway->sa_len); if (netmask != NULL) { if ((route->netmask = MALLOC("uroute.netmask", netmask->sa_len)) == NULL) goto fail; memcpy(route->netmask, netmask, netmask->sa_len); } route->flags = RTF_STATIC; return (route); fail: /* Clean up */ uroute_destroy(&route); return (NULL); } /* * Free a route. */ void uroute_destroy(struct uroute **routep) { struct uroute *const route = *routep; if (route == NULL) return; FREE("uroute.dest", route->dest); FREE("uroute.gateway", route->gateway); FREE("uroute.netmask", route->netmask); FREE("uroute", route); *routep = NULL; } /* * Get the destination address. */ const struct sockaddr * uroute_get_dest(struct uroute *route) { return (route->dest); } /* * Get the gateway address. */ const struct sockaddr * uroute_get_gateway(struct uroute *route) { return (route->gateway); } /* * Get the netmask. * * Returns NULL for a host route. */ const struct sockaddr * uroute_get_netmask(struct uroute *route) { return (route->netmask); } /* * Get the flags. */ int uroute_get_flags(struct uroute *route) { return (route->flags); } /* * Set the flags. */ void uroute_set_flags(struct uroute *route, int flags) { route->flags = flags; } /* * Add route to the kernel routing table. */ int uroute_add(struct uroute *route) { return (uroute_do_route(route, -1, RTM_ADD)); } /* * Delete a route. */ int uroute_delete(struct uroute *route) { return (uroute_do_route(route, -1, RTM_DELETE)); } /* * Get the installed kernel route that matches the destination "dest". */ struct uroute * uroute_get(const struct sockaddr *dest) { struct uroute *route = NULL; struct route_msg *msg = NULL; struct route_msg **list = NULL; struct sockaddr_dl sdl; int errno_save; int sock = -1; int num = 0; int seq; int i; /* Build new route message */ if ((msg = route_msg_create()) == NULL) goto fail; seq = route_msg_get_seq(msg); route_msg_set_type(msg, RTM_GET); if (route_msg_set_dest(msg, dest) == -1) goto fail; memset(&sdl, 0, sizeof(sdl)); sdl.sdl_type = IFT_OTHER; sdl.sdl_len = 8; sdl.sdl_family = AF_LINK; if (route_msg_set_ifp(msg, (struct sockaddr *)&sdl) == -1) goto fail; route_msg_set_flags(msg, RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_STATIC); /* Send request */ if ((sock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) goto fail; if (route_msg_send(msg, sock) == -1) goto fail; route_msg_destroy(&msg); msg = NULL; /* Get response */ while (1) { /* Decode it */ if ((num = route_msg_recv(&list, sock, TYPED_MEM_TEMP)) == -1) goto fail; /* Check returned messages for a match */ for (i = 0; i < num; i++) { struct route_msg *const msg = list[i]; if (route_msg_get_pid(msg) == getpid() && route_msg_get_seq(msg) == seq) { if ((route = uroute_create(route_msg_get_dest( msg), route_msg_get_gateway(msg), route_msg_get_netmask(msg))) == NULL) goto fail; break; } } if (route != NULL) break; /* Free list and try again */ for (i = 0; i < num; i++) route_msg_destroy(&list[i]); FREE(TYPED_MEM_TEMP, list); list = NULL; } fail: /* Clean up and exit */ errno_save = errno; if (sock != -1) (void)close(sock); if (msg != NULL) route_msg_destroy(&msg); if (list != NULL) { for (i = 0; i < num; i++) route_msg_destroy(&list[i]); FREE(TYPED_MEM_TEMP, list); } errno = errno_save; return (route); } /* * Build and send a route message by type. */ static int uroute_do_route(struct uroute *route, int sock, int type) { struct route_msg *msg; int close_sock = 0; int r = -1; int flags; /* Create new route message */ if ((msg = route_msg_create()) == NULL) return (-1); /* Open socket if requested */ if (sock == -1) { if ((sock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) goto fail; close_sock = 1; } /* Build message */ route_msg_set_type(msg, type); flags = route->flags & WRITABLE_FLAGS; flags |= RTF_UP; if (route->gateway->sa_family != AF_LINK) flags |= RTF_GATEWAY; if (route->netmask == NULL) flags |= RTF_HOST; else flags &= ~RTF_HOST; route_msg_set_flags(msg, flags); if (route_msg_set_dest(msg, route->dest) == -1) goto fail; if (route_msg_set_gateway(msg, route->gateway) == -1) goto fail; if (route_msg_set_netmask(msg, route->netmask) == -1) goto fail; /* Send messsage */ r = route_msg_send(msg, sock); fail: /* Clean up */ route_msg_destroy(&msg); if (close_sock) (void)close(sock); /* Done */ return (r); } /* * Get the entire routing table. */ int uroute_get_all(struct uroute ***listp, const char *mtype) { static int oid[6] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_DUMP, 0 }; static const int noid = sizeof(oid) / sizeof(*oid); struct route_msg **list = NULL; struct uroute **routes = NULL; u_char *buf = NULL; size_t actual; size_t needed; int num = 0; int i; /* Get size estimate */ if (sysctl(oid, noid, NULL, &needed, NULL, 0) < 0) goto fail; /* Allocate buffer for returned value */ if ((buf = MALLOC(TYPED_MEM_TEMP, needed)) == NULL) goto fail; /* Get actual data */ actual = needed; if (sysctl(oid, noid, buf, &actual, NULL, 0) < 0) goto fail; /* Decode route messages */ if ((num = route_msg_decode(buf, actual, &list, TYPED_MEM_TEMP)) == -1) goto fail; /* Create routes array from route message array */ if ((routes = MALLOC(mtype, num * sizeof(*routes))) == NULL) goto fail; memset(routes, 0, num * sizeof(*routes)); for (i = 0; i < num; i++) { struct route_msg *const msg = list[i]; if ((routes[i] = uroute_create(route_msg_get_dest(msg), route_msg_get_gateway(msg), route_msg_get_netmask(msg))) == NULL) { while (i > 0) uroute_destroy(&routes[--i]); FREE(mtype, routes); routes = NULL; goto fail; } routes[i]->flags = route_msg_get_flags(msg); } fail: /* Clean up and exit */ if (buf != NULL) FREE(TYPED_MEM_TEMP, buf); if (list != NULL) { for (i = 0; i < num; i++) route_msg_destroy(&list[i]); FREE(TYPED_MEM_TEMP, list); } if (routes != NULL) { *listp = routes; return (num); } return (-1); } /* * Print a route. */ void uroute_print(struct uroute *route, FILE *fp) { int didflag; int i; fprintf(fp, "dest %s gateway %s", uroute_sockaddr_string(route->dest), uroute_sockaddr_string(route->gateway)); if (route->netmask != NULL) { fprintf(fp, " netmask %s", uroute_sockaddr_string(route->netmask)); } fprintf(fp, " flags=<"); for (i = didflag = 0; route_flags[i].name != NULL; i++) { if ((route->flags & route_flags[i].bit) != 0) { if (didflag) fprintf(fp, ","); didflag = 1; fprintf(fp, "%s", route_flags[i].name); } } fprintf(fp, ">"); } static const char * uroute_sockaddr_string(const struct sockaddr *sa) { static char buf[2][256]; static int n; int i; n ^= 1; switch (sa->sa_family) { case AF_INET: strlcpy(buf[n], inet_ntoa(((struct sockaddr_in *)(void *) sa)->sin_addr), sizeof(buf[n])); break; case AF_LINK: /* XXX implement me */ default: snprintf(buf[n], sizeof(buf[n]), "{ len=%d af=%d data=[", sa->sa_len, sa->sa_family); for (i = 2; i < sa->sa_len; i++) { snprintf(buf[n] + strlen(buf[n]), sizeof(buf[n]) - strlen(buf[n]), " %02X", ((u_char *)sa)[i]); } strlcat(buf[n], " ] }", sizeof(buf[n])); } return (buf[n]); } #ifdef UROUTE_TEST int main(int ac, char **av) { struct sockaddr_in dest; struct sockaddr_in gateway; struct sockaddr_in netmask; struct uroute *route = NULL; int do_netmask = 0; int fail = 0; int cmd = 0; memset(&dest, 0, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_len = sizeof(dest); memset(&gateway, 0, sizeof(gateway)); gateway.sin_family = AF_INET; gateway.sin_len = sizeof(gateway); memset(&netmask, 0, sizeof(netmask)); netmask.sin_family = AF_INET; netmask.sin_len = sizeof(netmask); av++; ac--; switch (ac) { case 4: if (inet_aton(av[3], &netmask.sin_addr)) ; else { u_long haddr; if (sscanf(av[3], "%lu", &haddr) != 1) err(1, "invalid netmask \"%s\"", av[3]); netmask.sin_addr.s_addr = htonl(haddr); } do_netmask = 1; // fall through case 3: if (!inet_aton(av[2], &gateway.sin_addr)) err(1, "invalid IP address \"%s\"", av[2]); if (!strcmp(av[0], "add")) cmd = 1; else if (!strcmp(av[0], "delete")) cmd = 2; else { fail = 1; break; } if (!inet_aton(av[1], &dest.sin_addr)) err(1, "invalid IP address \"%s\"", av[1]); if ((route = uroute_create((struct sockaddr *)&dest, (struct sockaddr *)&gateway, do_netmask ? (struct sockaddr *)&netmask : NULL)) == NULL) err(1, "uroute_create"); break; case 2: if (strcmp(av[0], "get") != 0) { fail = 1; break; } if (!inet_aton(av[1], &dest.sin_addr)) err(1, "invalid IP address \"%s\"", av[1]); break; case 1: if (strcmp(av[0], "list") != 0) { fail = 1; break; } // fall through case 0: cmd = 3; break; default: fail = 1; break; } if (fail) { fprintf(stderr, "Usage: uroute " " [dest [ gateway [netmask] ]]\n"); exit(1); } switch (cmd) { case 0: // get if ((route = uroute_get((struct sockaddr *)&dest)) == NULL) err(1, "uroute_get"); uroute_print(route, stdout); printf("\n"); uroute_destroy(&route); break; case 1: // add if (uroute_add(route) == -1) err(1, "uroute_add"); uroute_destroy(&route); break; case 2: // delete if (uroute_delete(route) == -1) err(1, "uroute_delete"); uroute_destroy(&route); break; case 3: // list { struct uroute **list; int num; int i; if ((num = uroute_get_all(&list, "main")) == -1) err(1, "uroute_get_all"); for (i = 0; i < num; i++) { uroute_print(list[i], stdout); printf("\n"); } for (i = 0; i < num; i++) uroute_destroy(&list[i]); FREE("main", list); break; } default: assert(0); } /* Done */ typed_mem_dump(stdout); return (0); } #endif