/* * 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 "util/typed_mem.h" #include "net/route_msg.h" #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) ((x) += ROUNDUP((n)->sa_len)) struct route_msg { int type; /* message type */ int index; /* index for associated ifp */ int flags; /* flags, incl. kern & msg, e.g. DONE */ pid_t pid; /* for sender to identify action */ int seq; /* for sender to identify action */ int error; /* why failed */ struct sockaddr *dest; /* destination sockaddr */ struct sockaddr *gateway; /* gateway sockaddr */ struct sockaddr *netmask; /* netmask sockaddr */ struct sockaddr *genmask; /* genmask sockaddr */ struct sockaddr *ifp; /* interface name sockaddr */ struct sockaddr *ifa; /* interface addr sockaddr */ struct sockaddr *author; /* author of redirect sockaddr */ struct sockaddr *brd; /* for NEWADDR, broadcast or p2p dest */ }; /* * Internal variables */ /* List of sockaddr's in a route message from the kernel */ struct msg_sock { const char *name; u_int offset; }; static const struct msg_sock msg_socks[] = { { "dest", offsetof(struct route_msg, dest) }, { "gateway", offsetof(struct route_msg, gateway) }, { "netmask", offsetof(struct route_msg, netmask) }, { "genmask", offsetof(struct route_msg, genmask) }, { "ifp", offsetof(struct route_msg, ifp) }, { "ifa", offsetof(struct route_msg, ifa) }, { "author", offsetof(struct route_msg, author) }, { "brd", offsetof(struct route_msg, brd) }, { NULL, 0 } }; static int prev_seq; /* last used sequence number */ /* * Internal functions */ static int route_msg_set_sa(struct route_msg *msg, const char *name, const struct sockaddr *sa); /* * Create a new route message. */ struct route_msg * route_msg_create(void) { struct route_msg *msg; if ((msg = MALLOC("route_msg", sizeof(*msg))) == NULL) return (NULL); memset(msg, 0, sizeof(*msg)); msg->seq = ++prev_seq; /* XXX not atomic */ msg->pid = getpid(); return (msg); } /* * Destroy a routing message. */ void route_msg_destroy(struct route_msg **msgp) { struct route_msg *const msg = *msgp; if (msg == NULL) return; FREE("route_msg.dest", msg->dest); FREE("route_msg.gateway", msg->gateway); FREE("route_msg.netmask", msg->netmask); FREE("route_msg.genmask", msg->genmask); FREE("route_msg.ifp", msg->ifp); FREE("route_msg.ifa", msg->ifa); FREE("route_msg.author", msg->author); FREE("route_msg.brd", msg->brd); FREE("route_msg", msg); *msgp = NULL; } /* * Get message type. */ int route_msg_get_type(struct route_msg *msg) { return (msg->type); } /* * Set message type. */ void route_msg_set_type(struct route_msg *msg, int type) { msg->type = type; } /* * Get message index. */ int route_msg_get_index(struct route_msg *msg) { return (msg->index); } /* * Set message index. */ void route_msg_set_index(struct route_msg *msg, int index) { msg->index = index; } /* * Get message flags. */ int route_msg_get_flags(struct route_msg *msg) { return (msg->flags); } /* * Set message flags. */ void route_msg_set_flags(struct route_msg *msg, int flags) { msg->flags = flags; } /* * Get message error code. */ int route_msg_get_error(struct route_msg *msg) { return (msg->error); } /* * Get message process ID. */ pid_t route_msg_get_pid(struct route_msg *msg) { return (msg->pid); } /* * Get message sequence number. */ int route_msg_get_seq(struct route_msg *msg) { return (msg->seq); } /* * Get message destination. */ const struct sockaddr * route_msg_get_dest(struct route_msg *msg) { return (msg->dest); } /* * Set message destination. */ int route_msg_set_dest(struct route_msg *msg, const struct sockaddr *dest) { return (route_msg_set_sa(msg, "dest", dest)); } /* * Get message gateway. */ const struct sockaddr * route_msg_get_gateway(struct route_msg *msg) { return (msg->gateway); } /* * Set message gateway. */ int route_msg_set_gateway(struct route_msg *msg, const struct sockaddr *gateway) { return (route_msg_set_sa(msg, "gateway", gateway)); } /* * Get message netmask. */ const struct sockaddr * route_msg_get_netmask(struct route_msg *msg) { return (msg->netmask); } /* * Set message netmask. */ int route_msg_set_netmask(struct route_msg *msg, const struct sockaddr *netmask) { return (route_msg_set_sa(msg, "netmask", netmask)); } /* * Get message cloning netmask. */ const struct sockaddr * route_msg_get_genmask(struct route_msg *msg) { return (msg->genmask); } /* * Set message cloning netmask. */ int route_msg_set_genmask(struct route_msg *msg, const struct sockaddr *genmask) { return (route_msg_set_sa(msg, "genmask", genmask)); } /* * Get message interface name. */ const struct sockaddr * route_msg_get_ifp(struct route_msg *msg) { return (msg->ifp); } /* * Set message interface name. */ int route_msg_set_ifp(struct route_msg *msg, const struct sockaddr *ifp) { return (route_msg_set_sa(msg, "ifp", ifp)); } /* * Get message interface address. */ const struct sockaddr * route_msg_get_ifa(struct route_msg *msg) { return (msg->ifa); } /* * Set message interface address. */ int route_msg_set_ifa(struct route_msg *msg, const struct sockaddr *ifa) { return (route_msg_set_sa(msg, "ifa", ifa)); } /* * Get message author of the redirect message. */ const struct sockaddr * route_msg_get_author(struct route_msg *msg) { return (msg->author); } /* * Set message author of the redirect message. */ int route_msg_set_author(struct route_msg *msg, const struct sockaddr *author) { return (route_msg_set_sa(msg, "author", author)); } /* * Get message broadcast or point-to-point destination address. */ const struct sockaddr * route_msg_get_brd(struct route_msg *msg) { return (msg->brd); } /* * Set message broadcast or point-to-point destination address. */ int route_msg_set_brd(struct route_msg *msg, const struct sockaddr *brd) { return (route_msg_set_sa(msg, "brd", brd)); } /* * Set one of the struct sockaddr fields. */ static int route_msg_set_sa(struct route_msg *msg, const char *name, const struct sockaddr *sa) { struct sockaddr *copy; struct sockaddr **sp; char buf[32]; int i; for (i = 0; msg_socks[i].name != NULL && strcmp(name, msg_socks[i].name) != 0; i++); assert(msg_socks[i].name != NULL); sp = (struct sockaddr **)(void *)((u_char *)msg + msg_socks[i].offset); snprintf(buf, sizeof(buf), "route_msg.%s", name); if (sa != NULL) { if ((copy = MALLOC(buf, sa->sa_len)) == NULL) return (-1); memcpy(copy, sa, sa->sa_len); } else copy = NULL; FREE(buf, *sp); *sp = copy; return (0); } /* * Decode one or more routing messages from raw socket data. * * Returns the number of decoded messages, or -1 for error. * The array of struct route_msg pointers is returned in *listp, * in an array allocated with memory type 'mtype'. */ int route_msg_decode(const u_char *data, size_t dlen, struct route_msg ***listp, const char *mtype) { struct route_msg **list; struct rt_msghdr rtm; int posn; int num; int i; /* Count number of messages */ for (num = posn = 0; posn < dlen; num++, posn += rtm.rtm_msglen) { if (dlen - posn < sizeof(rtm)) { errno = EINVAL; return (-1); } memcpy(&rtm, data + posn, sizeof(rtm.rtm_msglen)); if (rtm.rtm_msglen < sizeof(rtm)) { errno = EINVAL; return (-1); } } /* Allocate list */ if ((list = MALLOC(mtype, num * sizeof(*list))) == NULL) return (-1); memset(list, 0, num * sizeof(*list)); /* Decode each message */ for (i = posn = 0; i < num; i++, posn += rtm.rtm_msglen) { int posn2; int j; /* Create new route_msg object */ if ((list[i] = route_msg_create()) == NULL) goto fail; /* Copy fields from route message header */ memcpy(&rtm, data + posn, sizeof(rtm)); list[i]->type = rtm.rtm_type; list[i]->index = rtm.rtm_index; list[i]->flags = rtm.rtm_flags; list[i]->pid = rtm.rtm_pid; list[i]->seq = rtm.rtm_seq; list[i]->error = rtm.rtm_errno; /* Create struct sockaddr's from each appended sockaddr */ for (posn2 = posn + sizeof(rtm), j = 0; msg_socks[j].name != NULL; j++) { const struct sockaddr *const sa = (struct sockaddr *)(data + posn2); if ((rtm.rtm_addrs & (1 << j)) == 0) continue; if (sa->sa_len != 0) { if (route_msg_set_sa(list[i], msg_socks[j].name, sa) == -1) goto fail; } ADVANCE(posn2, sa); } } /* Done */ *listp = list; return (num); fail: /* Cleanup after failure */ for (i = 0; i < num; i++) route_msg_destroy(&list[i]); FREE(mtype, list); return (-1); } /* * Encode a route message into raw binary form. * * Returns the total encoded length, or -1 for error. */ int route_msg_encode(const struct route_msg *msg, u_char *data, size_t dlen) { struct rt_msghdr rtm; int totalLen; int i; /* Prepare header */ if ((totalLen = sizeof(rtm)) > dlen) { errno = EMSGSIZE; return (-1); } memset(&rtm, 0, sizeof(rtm)); rtm.rtm_version = RTM_VERSION; rtm.rtm_type = msg->type; rtm.rtm_index = msg->index; rtm.rtm_flags = msg->flags; rtm.rtm_pid = msg->pid; rtm.rtm_seq = msg->seq; rtm.rtm_errno = msg->error; /* Append struct sockaddr's */ for (i = 0; msg_socks[i].name != NULL; i++) { struct sockaddr *const sa = *((struct sockaddr **)(void *) ((u_char *)msg + msg_socks[i].offset)); int len; if (sa == NULL) continue; rtm.rtm_addrs |= (1 << i); len = ROUNDUP(sa->sa_len); if (totalLen + len > dlen) { errno = EMSGSIZE; return (-1); } memcpy(data + totalLen, sa, sa->sa_len); memset(data + totalLen + sa->sa_len, 0, len - sa->sa_len); totalLen += len; } /* Copy in completed header */ rtm.rtm_msglen = totalLen; memcpy(data, &rtm, sizeof(rtm)); /* Done */ return (totalLen); } /* * Send a route message to a socket. */ int route_msg_send(struct route_msg *msg, int sock) { u_char buf[512]; int count; int elen; int r; if ((elen = route_msg_encode(msg, buf, sizeof(buf))) == -1) return (-1); for (count = 0; count < 32; count++) { if ((r = send(sock, buf, elen, 0)) >= 0) return (0); switch (errno) { case ENETUNREACH: case ESRCH: break; default: return (-1); } } return (-1); } /* * Receive route messages from a socket. */ int route_msg_recv(struct route_msg ***listp, int sock, const char *mtype) { u_char buf[512]; int r; /* Read next raw message array */ if ((r = recv(sock, buf, sizeof(buf), 0)) < 0) return (-1); /* Decode it */ return (route_msg_decode(buf, r, listp, mtype)); } /* * Print route message as a string. */ void route_msg_print(struct route_msg *msg, FILE *fp) { const char *typestr = NULL; fprintf(fp, "type="); switch (msg->type) { case RTM_ADD: typestr = "ADD"; break; case RTM_DELETE: typestr = "DELETE"; break; case RTM_CHANGE: typestr = "CHANGE"; break; case RTM_GET: typestr = "GET"; break; case RTM_LOSING: typestr = "LOSING"; break; case RTM_REDIRECT: typestr = "REDIRECT"; break; case RTM_MISS: typestr = "MISS"; break; case RTM_LOCK: typestr = "LOCK"; break; case RTM_OLDADD: typestr = "OLDADD"; break; case RTM_OLDDEL: typestr = "OLDDEL"; break; case RTM_RESOLVE: typestr = "RESOLVE"; break; case RTM_NEWADDR: typestr = "NEWADDR"; break; case RTM_DELADDR: typestr = "DELADDR"; break; case RTM_IFINFO: typestr = "IFINFO"; break; case RTM_NEWMADDR: typestr = "NEWMADDR"; break; case RTM_DELMADDR: typestr = "DELMADDR"; break; default: fprintf(fp, "%d", msg->type); break; } if (typestr != NULL) fprintf(fp, "%s", typestr); fprintf(fp, " index=%d flags=0x%x pid=%lu seq=%d errno=%d", msg->index, msg->flags, (u_long)msg->pid, msg->seq, msg->error); if (msg->dest != NULL) fprintf(fp, " dest=%p", msg->dest); if (msg->gateway != NULL) fprintf(fp, " gateway=%p", msg->gateway); if (msg->netmask != NULL) fprintf(fp, " netmask=%p", msg->netmask); if (msg->genmask != NULL) fprintf(fp, " genmask=%p", msg->genmask); if (msg->ifp != NULL) fprintf(fp, " ifp=%p", msg->ifp); if (msg->ifa != NULL) fprintf(fp, " ifa=%p", msg->ifa); if (msg->author != NULL) fprintf(fp, " author=%p", msg->author); if (msg->brd != NULL) fprintf(fp, " brd=%p", msg->brd); fflush(fp); }