00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "config.h"
00011
00012 #include <sys/param.h>
00013 #include <sys/types.h>
00014 #include <sys/socket.h>
00015 #ifdef HAVE_SYS_SYSCTL_H
00016 #include <sys/sysctl.h>
00017 #endif
00018 #ifdef HAVE_STREAMS_MIB2
00019 #include <sys/stream.h>
00020 #include <sys/tihdr.h>
00021 #include <sys/tiuser.h>
00022 #include <inet/common.h>
00023 #include <inet/mib2.h>
00024 #include <inet/ip.h>
00025 #undef IP_ADDR_LEN
00026 #include <stropts.h>
00027 #elif defined(HAVE_STREAMS_ROUTE)
00028 #include <sys/stream.h>
00029 #include <sys/stropts.h>
00030 #endif
00031
00032 #define route_t oroute_t
00033 #include <net/route.h>
00034 #undef route_t
00035 #include <netinet/in.h>
00036
00037 #include <errno.h>
00038 #include <fcntl.h>
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <unistd.h>
00043
00044 #include "dnet.h"
00045
00046 #define ROUNDUP(a) \
00047 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
00048
00049 #ifdef HAVE_SOCKADDR_SA_LEN
00050 #define NEXTSA(s) \
00051 ((struct sockaddr *)((u_char *)(s) + ROUNDUP((s)->sa_len)))
00052 #else
00053 #define NEXTSA(s) \
00054 ((struct sockaddr *)((u_char *)(s) + ROUNDUP(sizeof(*(s)))))
00055 #endif
00056
00057 struct route_handle {
00058 int fd;
00059 int seq;
00060 #ifdef HAVE_STREAMS_MIB2
00061 int ip_fd;
00062 #endif
00063 };
00064
00065 #ifdef DEBUG
00066 static void
00067 route_msg_print(struct rt_msghdr *rtm)
00068 {
00069 printf("v: %d type: 0x%x flags: 0x%x addrs: 0x%x pid: %d seq: %d\n",
00070 rtm->rtm_version, rtm->rtm_type, rtm->rtm_flags,
00071 rtm->rtm_addrs, rtm->rtm_pid, rtm->rtm_seq);
00072 }
00073 #endif
00074
00075 static int
00076 route_msg(route_t *r, int type, struct addr *dst, struct addr *gw)
00077 {
00078 struct addr net;
00079 struct rt_msghdr *rtm;
00080 struct sockaddr *sa;
00081 u_char buf[BUFSIZ];
00082 pid_t pid;
00083 int len;
00084
00085 memset(buf, 0, sizeof(buf));
00086
00087 rtm = (struct rt_msghdr *)buf;
00088 rtm->rtm_version = RTM_VERSION;
00089 if ((rtm->rtm_type = type) != RTM_DELETE)
00090 rtm->rtm_flags = RTF_UP;
00091 rtm->rtm_addrs = RTA_DST;
00092 rtm->rtm_seq = ++r->seq;
00093
00094
00095 sa = (struct sockaddr *)(rtm + 1);
00096 if (addr_net(dst, &net) < 0 || addr_ntos(&net, sa) < 0)
00097 return (-1);
00098 sa = NEXTSA(sa);
00099
00100
00101 if (gw != NULL && type != RTM_GET) {
00102 rtm->rtm_flags |= RTF_GATEWAY;
00103 rtm->rtm_addrs |= RTA_GATEWAY;
00104 if (addr_ntos(gw, sa) < 0)
00105 return (-1);
00106 sa = NEXTSA(sa);
00107 }
00108
00109 if (dst->addr_ip == IP_ADDR_ANY || dst->addr_bits < IP_ADDR_BITS) {
00110 rtm->rtm_addrs |= RTA_NETMASK;
00111 if (addr_btos(dst->addr_bits, sa) < 0)
00112 return (-1);
00113 sa = NEXTSA(sa);
00114 } else
00115 rtm->rtm_flags |= RTF_HOST;
00116
00117 rtm->rtm_msglen = (u_char *)sa - buf;
00118 #ifdef DEBUG
00119 route_msg_print(rtm);
00120 #endif
00121 #ifdef HAVE_STREAMS_ROUTE
00122 if (ioctl(r->fd, RTSTR_SEND, rtm) < 0)
00123 return (-1);
00124 #else
00125 if (write(r->fd, buf, rtm->rtm_msglen) < 0)
00126 return (-1);
00127
00128 pid = getpid();
00129
00130 while (type == RTM_GET && (len = read(r->fd, buf, sizeof(buf))) > 0) {
00131 if (len < (int)sizeof(*rtm)) {
00132 return (-1);
00133 }
00134 if (rtm->rtm_type == type && rtm->rtm_pid == pid &&
00135 rtm->rtm_seq == r->seq) {
00136 if (rtm->rtm_errno) {
00137 errno = rtm->rtm_errno;
00138 return (-1);
00139 }
00140 break;
00141 }
00142 }
00143 #endif
00144 if (type == RTM_GET && (rtm->rtm_addrs & (RTA_DST|RTA_GATEWAY)) ==
00145 (RTA_DST|RTA_GATEWAY)) {
00146 sa = (struct sockaddr *)(rtm + 1);
00147 sa = NEXTSA(sa);
00148
00149 if (addr_ston(sa, gw) < 0 || gw->addr_type != ADDR_TYPE_IP) {
00150 errno = ESRCH;
00151 return (-1);
00152 }
00153 }
00154 return (0);
00155 }
00156
00157 route_t *
00158 route_open(void)
00159 {
00160 route_t *r;
00161
00162 if ((r = calloc(1, sizeof(*r))) != NULL) {
00163 r->fd = -1;
00164 #ifdef HAVE_STREAMS_MIB2
00165 if ((r->ip_fd = open(IP_DEV_NAME, O_RDWR)) < 0)
00166 return (route_close(r));
00167 #endif
00168 #ifdef HAVE_STREAMS_ROUTE
00169 if ((r->fd = open("/dev/route", O_RDWR, 0)) < 0)
00170 #else
00171 if ((r->fd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0)
00172 #endif
00173 return (route_close(r));
00174 }
00175 return (r);
00176 }
00177
00178 int
00179 route_add(route_t *r, const struct route_entry *entry)
00180 {
00181 struct route_entry rtent;
00182
00183 memcpy(&rtent, entry, sizeof(rtent));
00184
00185 if (route_msg(r, RTM_ADD, &rtent.route_dst, &rtent.route_gw) < 0)
00186 return (-1);
00187
00188 return (0);
00189 }
00190
00191 int
00192 route_delete(route_t *r, const struct route_entry *entry)
00193 {
00194 struct route_entry rtent;
00195
00196 memcpy(&rtent, entry, sizeof(rtent));
00197
00198 if (route_get(r, &rtent) < 0)
00199 return (-1);
00200
00201 if (route_msg(r, RTM_DELETE, &rtent.route_dst, &rtent.route_gw) < 0)
00202 return (-1);
00203
00204 return (0);
00205 }
00206
00207 int
00208 route_get(route_t *r, struct route_entry *entry)
00209 {
00210 if (route_msg(r, RTM_GET, &entry->route_dst, &entry->route_gw) < 0)
00211 return (-1);
00212
00213 return (0);
00214 }
00215
00216 #if defined(HAVE_SYS_SYSCTL_H) || defined(HAVE_STREAMS_ROUTE)
00217 int
00218 route_loop(route_t *r, route_handler callback, void *arg)
00219 {
00220 struct rt_msghdr *rtm;
00221 struct route_entry entry;
00222 struct sockaddr *sa;
00223 char *buf, *lim, *next;
00224 int ret;
00225 #ifdef HAVE_SYS_SYSCTL_H
00226 int mib[6] = { CTL_NET, PF_ROUTE, 0, 0 , NET_RT_DUMP, 0 };
00227 size_t len;
00228
00229 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
00230 return (-1);
00231
00232 if (len == 0)
00233 return (0);
00234
00235 if ((buf = malloc(len)) == NULL)
00236 return (-1);
00237
00238 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
00239 free(buf);
00240 return (-1);
00241 }
00242 lim = buf + len;
00243 next = buf;
00244 #else
00245 struct rt_giarg giarg, *gp;
00246
00247 memset(&giarg, 0, sizeof(giarg));
00248 giarg.gi_op = KINFO_RT_DUMP;
00249
00250 if (ioctl(r->fd, RTSTR_GETROUTE, &giarg) < 0)
00251 return (-1);
00252
00253 if ((buf = malloc(giarg.gi_size)) == NULL)
00254 return (-1);
00255
00256 gp = (struct rt_giarg *)buf;
00257 gp->gi_size = giarg.gi_size;
00258 gp->gi_op = KINFO_RT_DUMP;
00259 gp->gi_where = buf;
00260 gp->gi_arg = RTF_UP | RTF_GATEWAY;
00261
00262 if (ioctl(r->fd, RTSTR_GETROUTE, buf) < 0) {
00263 free(buf);
00264 return (-1);
00265 }
00266 lim = buf + gp->gi_size;
00267 next = buf + sizeof(giarg);
00268 #endif
00269 for (ret = 0; next < lim; next += rtm->rtm_msglen) {
00270 rtm = (struct rt_msghdr *)next;
00271 sa = (struct sockaddr *)(rtm + 1);
00272
00273 if (addr_ston(sa, &entry.route_dst) < 0 ||
00274 (rtm->rtm_addrs & RTA_GATEWAY) == 0)
00275 continue;
00276
00277 sa = NEXTSA(sa);
00278
00279 if (addr_ston(sa, &entry.route_gw) < 0)
00280 continue;
00281
00282 if (entry.route_dst.addr_type != entry.route_gw.addr_type ||
00283 (entry.route_dst.addr_type != ADDR_TYPE_IP &&
00284 entry.route_dst.addr_type != ADDR_TYPE_IP6))
00285 continue;
00286
00287 if (rtm->rtm_addrs & RTA_NETMASK) {
00288 sa = NEXTSA(sa);
00289 if (addr_stob(sa, &entry.route_dst.addr_bits) < 0)
00290 continue;
00291 }
00292 if ((ret = callback(&entry, arg)) != 0)
00293 break;
00294 }
00295 free(buf);
00296
00297 return (ret);
00298 }
00299 #elif defined(HAVE_STREAMS_MIB2)
00300
00301 #ifdef IRE_DEFAULT
00302
00303 #define IRE_ROUTE IRE_CACHE
00304 #define IRE_ROUTE_REDIRECT IRE_HOST_REDIRECT
00305 #endif
00306
00307 int
00308 route_loop(route_t *r, route_handler callback, void *arg)
00309 {
00310 struct route_entry entry;
00311 struct sockaddr_in sin;
00312 struct strbuf msg;
00313 struct T_optmgmt_req *tor;
00314 struct T_optmgmt_ack *toa;
00315 struct T_error_ack *tea;
00316 struct opthdr *opt;
00317 mib2_ipRouteEntry_t *rt, *rtend;
00318 u_char buf[8192];
00319 int flags, rc, rtable, ret;
00320
00321 tor = (struct T_optmgmt_req *)buf;
00322 toa = (struct T_optmgmt_ack *)buf;
00323 tea = (struct T_error_ack *)buf;
00324
00325 tor->PRIM_type = T_OPTMGMT_REQ;
00326 tor->OPT_offset = sizeof(*tor);
00327 tor->OPT_length = sizeof(*opt);
00328 tor->MGMT_flags = T_CURRENT;
00329
00330 opt = (struct opthdr *)(tor + 1);
00331 opt->level = MIB2_IP;
00332 opt->name = opt->len = 0;
00333
00334 msg.maxlen = sizeof(buf);
00335 msg.len = sizeof(*tor) + sizeof(*opt);
00336 msg.buf = buf;
00337
00338 if (putmsg(r->ip_fd, &msg, NULL, 0) < 0)
00339 return (-1);
00340
00341 opt = (struct opthdr *)(toa + 1);
00342 msg.maxlen = sizeof(buf);
00343
00344 for (;;) {
00345 flags = 0;
00346 if ((rc = getmsg(r->ip_fd, &msg, NULL, &flags)) < 0)
00347 return (-1);
00348
00349
00350 if (rc == 0 &&
00351 msg.len >= sizeof(*toa) &&
00352 toa->PRIM_type == T_OPTMGMT_ACK &&
00353 toa->MGMT_flags == T_SUCCESS && opt->len == 0)
00354 break;
00355
00356 if (msg.len >= sizeof(*tea) && tea->PRIM_type == T_ERROR_ACK)
00357 return (-1);
00358
00359 if (rc != MOREDATA || msg.len < (int)sizeof(*toa) ||
00360 toa->PRIM_type != T_OPTMGMT_ACK ||
00361 toa->MGMT_flags != T_SUCCESS)
00362 return (-1);
00363
00364 rtable = (opt->level == MIB2_IP && opt->name == MIB2_IP_21);
00365
00366 msg.maxlen = sizeof(buf) - (sizeof(buf) % sizeof(*rt));
00367 msg.len = 0;
00368 flags = 0;
00369
00370 do {
00371 rc = getmsg(r->ip_fd, NULL, &msg, &flags);
00372
00373 if (rc != 0 && rc != MOREDATA)
00374 return (-1);
00375
00376 if (!rtable)
00377 continue;
00378
00379 rt = (mib2_ipRouteEntry_t *)msg.buf;
00380 rtend = (mib2_ipRouteEntry_t *)(msg.buf + msg.len);
00381
00382 sin.sin_family = AF_INET;
00383
00384 for ( ; rt < rtend; rt++) {
00385 if ((rt->ipRouteInfo.re_ire_type &
00386 (IRE_BROADCAST|IRE_ROUTE_REDIRECT|
00387 IRE_LOCAL|IRE_ROUTE)) != 0 ||
00388 rt->ipRouteNextHop == IP_ADDR_ANY)
00389 continue;
00390
00391 sin.sin_addr.s_addr = rt->ipRouteNextHop;
00392 addr_ston((struct sockaddr *)&sin,
00393 &entry.route_gw);
00394
00395 sin.sin_addr.s_addr = rt->ipRouteDest;
00396 addr_ston((struct sockaddr *)&sin,
00397 &entry.route_dst);
00398
00399 sin.sin_addr.s_addr = rt->ipRouteMask;
00400 addr_stob((struct sockaddr *)&sin,
00401 &entry.route_dst.addr_bits);
00402
00403 if ((ret = callback(&entry, arg)) != 0)
00404 return (ret);
00405 }
00406 } while (rc == MOREDATA);
00407 }
00408 return (0);
00409 }
00410 #elif defined(HAVE_NET_RADIX_H)
00411
00412 #include <nlist.h>
00413
00414 static int
00415 _kread(int fd, void *addr, void *buf, int len)
00416 {
00417 if (lseek(fd, (off_t)addr, SEEK_SET) == (off_t)-1L)
00418 return (-1);
00419 return (read(fd, buf, len) == len ? 0 : -1);
00420 }
00421
00422 static int
00423 _radix_walk(int fd, struct radix_node *rn, route_handler callback, void *arg)
00424 {
00425 struct radix_node rnode;
00426 struct rtentry rt;
00427 struct sockaddr_in sin;
00428 struct route_entry entry;
00429 int ret = 0;
00430 again:
00431 _kread(fd, rn, &rnode, sizeof(rnode));
00432 if (rnode.rn_b < 0) {
00433 if (!(rnode.rn_flags & RNF_ROOT)) {
00434 _kread(fd, rn, &rt, sizeof(rt));
00435 _kread(fd, rt_key(&rt), &sin, sizeof(sin));
00436 addr_ston((struct sockaddr *)&sin, &entry.route_dst);
00437 if (!(rt.rt_flags & RTF_HOST)) {
00438 _kread(fd, rt_mask(&rt), &sin, sizeof(sin));
00439 addr_stob((struct sockaddr *)&sin,
00440 &entry.route_dst.addr_bits);
00441 }
00442 _kread(fd, rt.rt_gateway, &sin, sizeof(sin));
00443 addr_ston((struct sockaddr *)&sin, &entry.route_gw);
00444 if ((ret = callback(&entry, arg)) != 0)
00445 return (ret);
00446 }
00447 if ((rn = rnode.rn_dupedkey))
00448 goto again;
00449 } else {
00450 rn = rnode.rn_r;
00451 if ((ret = _radix_walk(fd, rnode.rn_l, callback, arg)) != 0)
00452 return (ret);
00453 if ((ret = _radix_walk(fd, rn, callback, arg)) != 0)
00454 return (ret);
00455 }
00456 return (ret);
00457 }
00458
00459 int
00460 route_loop(route_t *r, route_handler callback, void *arg)
00461 {
00462 struct radix_node_head *rnh, head;
00463 struct nlist nl[2];
00464 int fd, ret = 0;
00465
00466 memset(nl, 0, sizeof(nl));
00467 nl[0].n_name = "radix_node_head";
00468
00469 if (knlist(nl) < 0 || nl[0].n_type == 0 ||
00470 (fd = open("/dev/kmem", O_RDONLY, 0)) < 0)
00471 return (-1);
00472
00473 for (_kread(fd, (void *)nl[0].n_value, &rnh, sizeof(rnh));
00474 rnh != NULL; rnh = head.rnh_next) {
00475 _kread(fd, rnh, &head, sizeof(head));
00476
00477 if (head.rnh_af == AF_INET) {
00478 if ((ret = _radix_walk(fd, head.rnh_treetop,
00479 callback, arg)) != 0)
00480 break;
00481 }
00482 }
00483 close(fd);
00484 return (ret);
00485 }
00486 #else
00487 int
00488 route_loop(route_t *r, route_handler callback, void *arg)
00489 {
00490 errno = ENOSYS;
00491 return (-1);
00492 }
00493 #endif
00494
00495 route_t *
00496 route_close(route_t *r)
00497 {
00498 if (r != NULL) {
00499 #ifdef HAVE_STREAMS_MIB2
00500 if (r->ip_fd >= 0)
00501 close(r->ip_fd);
00502 #endif
00503 if (r->fd >= 0)
00504 close(r->fd);
00505 free(r);
00506 }
00507 return (NULL);
00508 }