int write_rte(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t length) { struct rtent *rp; int var; long val; u_long dst; char buf[8]; u_short flags; int oldty; /* * object identifier is of form: * 1.3.6.1.2.1.4.21.1.X.A.B.C.D , where A.B.C.D is IP address. * IPADDR starts at offset 10. */ if (length != 14) { snmp_log(LOG_ERR, "length error\n"); return SNMP_ERR_NOCREATION; } #ifdef solaris2 /* not implemented */ return SNMP_ERR_NOTWRITABLE; #endif var = name[9]; dst = *((u_long *) & name[10]); rp = findCacheRTE(dst); if (!rp) { rp = cacheKernelRTE(dst); } if (action == RESERVE1 && !rp) { rp = newCacheRTE(); if (!rp) { snmp_log(LOG_ERR, "newCacheRTE"); return SNMP_ERR_RESOURCEUNAVAILABLE; } rp->rt_dst = dst; rp->rt_type = rp->xx_type = 2; } else if (action == COMMIT) { } else if (action == FREE) { if (rp && rp->rt_type == 2) { /* was invalid before */ delCacheRTE(dst); } } netsnmp_assert(NULL != rp); /* should have found or created rp */ switch (var) { case IPROUTEDEST: if (action == RESERVE1) { if (var_val_type != ASN_IPADDRESS) { snmp_log(LOG_ERR, "not IP address"); return SNMP_ERR_WRONGTYPE; } memcpy(buf, var_val, (var_val_len > 8) ? 8 : var_val_len); rp->xx_dst = *((u_long *) buf); } else if (action == COMMIT) { rp->rt_dst = rp->xx_dst; } break; case IPROUTEMETRIC1: if (action == RESERVE1) { if (var_val_type != ASN_INTEGER) { snmp_log(LOG_ERR, "not int1"); return SNMP_ERR_WRONGTYPE; } val = *((long *) var_val); if (val < -1) { snmp_log(LOG_ERR, "not right1"); return SNMP_ERR_WRONGVALUE; } rp->xx_metric1 = val; } else if (action == RESERVE2) { if ((rp->xx_metric1 == 1) && (rp->xx_type != 4)) { snmp_log(LOG_ERR, "reserve2 failed\n"); return SNMP_ERR_WRONGVALUE; } } else if (action == COMMIT) { rp->rt_metric1 = rp->xx_metric1; } break; case IPROUTEIFINDEX: if (action == RESERVE1) { if (var_val_type != ASN_INTEGER) { snmp_log(LOG_ERR, "not right2"); return SNMP_ERR_WRONGTYPE; } val = *((long *) var_val); if (val <= 0) { snmp_log(LOG_ERR, "not right3"); return SNMP_ERR_WRONGVALUE; } rp->xx_ifix = val; } else if (action == COMMIT) { rp->rt_ifix = rp->xx_ifix; } break; case IPROUTENEXTHOP: if (action == RESERVE1) { if (var_val_type != ASN_IPADDRESS) { snmp_log(LOG_ERR, "not right4"); return SNMP_ERR_WRONGTYPE; } memcpy(buf, var_val, (var_val_len > 8) ? 8 : var_val_len); rp->xx_nextIR = *((u_long *) buf); } else if (action == COMMIT) { rp->rt_nextIR = rp->xx_nextIR; } break; case IPROUTETYPE: /* * flag meaning: * * IPROUTEPROTO (rt_proto): none: (cant set == 3 (netmgmt)) * * IPROUTEMETRIC1: 1 iff gateway, 0 otherwise * IPROUTETYPE: 4 iff gateway, 3 otherwise */ if (action == RESERVE1) { if (var_val_type != ASN_INTEGER) { return SNMP_ERR_WRONGTYPE; } val = *((long *) var_val); if ((val < 2) || (val > 4)) { /* only accept invalid, direct, indirect */ snmp_log(LOG_ERR, "not right6"); return SNMP_ERR_WRONGVALUE; } rp->xx_type = val; } else if (action == COMMIT) { oldty = rp->rt_type; rp->rt_type = rp->xx_type; if (rp->rt_type == 2) { /* invalid, so delete from kernel */ if (delRoute (rp->rt_dst, rp->rt_nextIR, rp->rt_ifix, rp->old_flags) < 0) { snmp_log_perror("delRoute"); } } else { /* * it must be valid now, so flush to kernel */ if (oldty != 2) { /* was the old entry valid ? */ if (delRoute (rp->old_dst, rp->old_nextIR, rp->old_ifix, rp->old_flags) < 0) { snmp_log_perror("delRoute"); } } /* * not invalid, so remove from cache */ flags = (rp->rt_type == 4 ? RTF_GATEWAY : 0); if (addRoute(rp->rt_dst, rp->rt_nextIR, rp->rt_ifix, flags) < 0) { snmp_log_perror("addRoute"); } delCacheRTE(rp->rt_type); } } break; case IPROUTEPROTO: default: DEBUGMSGTL(("snmpd", "unknown sub-id %d in write_rte\n", var)); return SNMP_ERR_NOCREATION; } return SNMP_ERR_NOERROR; }
static boolean_t addRoute(route_entry_t *entry) { static int skfd = -1; #if !defined(freebsd8) struct rtentry rt; #else struct ortentry rt; #endif if (entry) { memset((char *) &rt, 0, sizeof (rt)); rt.rt_flags |= RTF_UP; if (entry->mask == 0xffffffff) { rt.rt_flags |= RTF_HOST; } #if !defined(freebsd8) rt.rt_metric = 0; ((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = entry->mask; ((struct sockaddr_in *) &rt.rt_genmask)->sin_family = AF_INET; if (entry->iface[0] != '\0') { rt.rt_dev = entry->iface; } #else #endif ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = entry->dst; ((struct sockaddr_in *) &rt.rt_dst)->sin_family = AF_INET; ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = entry->gateway; ((struct sockaddr_in *) &rt.rt_gateway)->sin_family = AF_INET; rt.rt_flags |= RTF_GATEWAY; #if !defined(freebsd8) if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { #else if ((skfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { #endif DEBUGP(DERROR, "addRoute", "socket: %s", strerror(errno)); return FALSE; } #if !defined(freebsd8) if (ioctl(skfd, SIOCADDRT, &rt) < 0) { DEBUGP(DERROR, "addRoute", "ioctl: %s", strerror(errno)); close(skfd); return FALSE; } #else struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; memset(&r, 0, sizeof (r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_ADD; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; struct sockaddr_in dst = {.sin_addr.s_addr = entry->dst, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in gw = {.sin_addr.s_addr = entry->gateway, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in mask = {.sin_addr.s_addr = entry->mask, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; memcpy(&r.addrs[RTAX_DST], &dst, dst.sin_len); memcpy(&r.addrs[RTAX_GATEWAY], &gw, gw.sin_len); memcpy(&r.addrs[RTAX_NETMASK], &mask, mask.sin_len); r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; r.rtm.rtm_flags = RTF_STATIC | RTF_GATEWAY; r.rtm.rtm_msglen = sizeof (r); if (entry->mask != 0xffffffff) { r.rtm.rtm_addrs |= RTA_NETMASK; } else { r.rtm.rtm_flags |= (RTF_HOST); } if (write(skfd, &r, r.rtm.rtm_msglen) != r.rtm.rtm_msglen) { DEBUGP(DERROR, "addRoute", "write: %s", strerror(errno)); close(skfd); return FALSE; } #endif close(skfd); return TRUE; } return FALSE; } static boolean_t delRoute(route_entry_t * entry) { static int skfd = -1; #if !defined(freebsd8) struct rtentry rt; #else struct ortentry rt; #endif if (entry) { memset((char *) &rt, 0, sizeof (rt)); rt.rt_flags |= RTF_UP; if (entry->mask == 0xffffffff) { rt.rt_flags |= RTF_HOST; } #if !defined(freebsd8) rt.rt_metric = 0; ((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = entry->mask; ((struct sockaddr_in *) &rt.rt_genmask)->sin_family = AF_INET; if (entry->iface[0] != '\0') { rt.rt_dev = entry->iface; } #else #endif ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = entry->dst; ((struct sockaddr_in *) &rt.rt_dst)->sin_family = AF_INET; ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = entry->gateway; ((struct sockaddr_in *) &rt.rt_gateway)->sin_family = AF_INET; rt.rt_flags |= RTF_GATEWAY; #if !defined(freebsd8) if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { #else if ((skfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { #endif DEBUGP(DERROR, "delRoute", "socket: %s", strerror(errno)); return FALSE; } #if !defined(freebsd8) if (ioctl(skfd, SIOCDELRT, &rt) < 0) { DEBUGP(DERROR, "delRoute", "ioctl: %s", strerror(errno)); close(skfd); return FALSE; } #else struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; memset(&r, 0, sizeof (r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_DELETE; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; struct sockaddr_in dst = {.sin_addr.s_addr = entry->dst, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in gw = {.sin_addr.s_addr = entry->gateway, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in mask = {.sin_addr.s_addr = entry->mask, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; memcpy(&r.addrs[RTAX_DST], &dst, dst.sin_len); memcpy(&r.addrs[RTAX_GATEWAY], &gw, gw.sin_len); memcpy(&r.addrs[RTAX_NETMASK], &mask, mask.sin_len); r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; r.rtm.rtm_flags = RTF_DONE; r.rtm.rtm_msglen = sizeof (r); if (entry->mask != 0xffffffff) { r.rtm.rtm_addrs |= RTA_NETMASK; } else { r.rtm.rtm_flags |= (RTF_HOST); } if (write(skfd, &r, r.rtm.rtm_msglen) != r.rtm.rtm_msglen) { DEBUGP(DERROR, "delRoute", "write: %s", strerror(errno)); close(skfd); return FALSE; } #endif close(skfd); return TRUE; } return FALSE; } static list_t *routeLookup(route_entry_t *key) { list_t *result = NULL; int skfd = -1; if (key) { #ifdef freebsd8 struct ortentry rt; memset((char *) &rt, 0, sizeof (rt)); rt.rt_flags |= RTF_UP; if (key->mask == 0xffffffff) { rt.rt_flags |= RTF_HOST; } rt.rt_flags |= RTF_GATEWAY; if ((skfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { DEBUGP(DERROR, "routeLookup", "socket: %s", strerror(errno)); return result; } struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; memset(&r, 0, sizeof (r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_GET; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; struct sockaddr_in dst = {.sin_addr.s_addr = key->dst, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in mask = {.sin_addr.s_addr = key->mask, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; memcpy(&r.addrs[RTAX_DST], &dst, dst.sin_len); memcpy(&r.addrs[RTAX_NETMASK], &mask, mask.sin_len); r.rtm.rtm_addrs = RTA_DST; r.rtm.rtm_flags = RTF_DONE; r.rtm.rtm_msglen = sizeof (r); if (key->mask != 0xffffffff) { r.rtm.rtm_addrs |= RTA_NETMASK; } else { r.rtm.rtm_flags |= (RTF_HOST); } if (write(skfd, &r, r.rtm.rtm_msglen) != r.rtm.rtm_msglen) { DEBUGP(DERROR, "routeLookup", "write: %s", strerror(errno)); close(skfd); return result; } result = I(List)->new(); while (1) { if (read(skfd, (struct rt_msghdr *) &r, sizeof (r)) < 0) { DEBUGP(DERROR, "routeLookup", "read: %s", strerror(errno)); close(skfd); break; } route_entry_t *e = calloc(1, sizeof (route_entry_t)); e->gateway = ((struct sockaddr_in*) &r.addrs[RTAX_GATEWAY])->sin_addr.s_addr; I(List)->insert(result, I(ListItem)->new(e)); if (r.rtm.rtm_flags & RTF_DONE) { break; } } #endif #ifdef linux struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; struct nhop { in_addr_t gw; char dev[IFNAMSIZ]; }; list_t *nhops = NULL; struct nlmsghdr *h; struct rtmsg *rtmp; struct rtattr *rtatp; int rtattrlen; int rval = -1; char buf[4096]; char dev[IFNAMSIZ]; in_addr_t src = 0; in_addr_t dst = 0; in_addr_t mask = 0; in_addr_t gw = 0; if ((skfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { DEBUGP(DERROR, "routeLookup", "socket: %s", strerror(errno)); return FALSE; } memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; req.n.nlmsg_type = RTM_GETROUTE; req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); if (send(skfd, &req, req.n.nlmsg_len, 0) < 0) { DEBUGP(DERROR, "routeLookup", "send: %s", strerror(errno)); close(skfd); return result; } if ((rval = recv(skfd, buf, sizeof (buf), 0)) < 0) { DEBUGP(DERROR, "routeLookup", "recv: %s", strerror(errno)); close(skfd); return result; } for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, rval); h = NLMSG_NEXT(h, rval)) { rtmp = (struct rtmsg *) NLMSG_DATA(h); rtatp = (struct rtattr *) RTM_RTA(rtmp); rtattrlen = RTM_PAYLOAD(h); src = 0; dst = 0; mask = 0; gw = 0; if (result == NULL) { result = I(List)->new(); } for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { switch (rtatp->rta_type) { case RTA_OIF: { int oif_index = *(int *) RTA_DATA(rtatp); if_indextoname(oif_index, dev); break; } case RTA_PREFSRC: src = *((in_addr_t *) RTA_DATA(rtatp)); break; case RTA_DST: dst = *((in_addr_t *) RTA_DATA(rtatp)); mask = rtmp->rtm_dst_len != 0 ? htonl(~0 << (32 - rtmp->rtm_dst_len)) : 0; break; case RTA_GATEWAY: gw = *((in_addr_t *) RTA_DATA(rtatp)); break; case RTA_MULTIPATH: { nhops = I(List)->new(); struct rtnexthop *nh = NULL; struct rtattr *nhrtattr = NULL; int nh_len = RTA_PAYLOAD(rtatp); for (nh = RTA_DATA(rtatp); RTNH_OK(nh, nh_len); nh = RTNH_NEXT(nh)) { struct nhop *hop = calloc(1, sizeof (struct nhop)); int attr_len = nh->rtnh_len - sizeof (*nh); if (nh_len < sizeof (*nh)) break; if (nh->rtnh_len > nh_len) break; if (nh->rtnh_len > sizeof (*nh)) { if_indextoname(nh->rtnh_ifindex, hop->dev); for (nhrtattr = RTNH_DATA(nh); RTA_OK(nhrtattr, attr_len); nhrtattr = RTA_NEXT(nhrtattr, attr_len)) { switch (nhrtattr->rta_type) { case RTA_GATEWAY: hop->gw = *((in_addr_t *) RTA_DATA(nhrtattr)); break; } } I(List)->insert(nhops, I(ListItem)->new(hop)); } nh_len -= NLMSG_ALIGN(nh->rtnh_len); } break; } } } if (nhops == NULL) { if (key && ((key->dst != dst) || (key->iface[0] != '\0' && strcmp(key->iface, dev) != 0))) { continue; } route_entry_t *r = calloc(1, sizeof (route_entry_t)); r->gateway = gw; r->mask = mask; r->dst = dst; r->src = src; strcpy(r->iface, dev); I(List)->insert(result, I(ListItem)->new(r)); } else { struct nhop *h = NULL; list_item_t *item = NULL; while (item = I(List)->pop(nhops)) { h = item->data; if (key && ((key->dst != dst) || (key->iface[0] != '\0' && strcmp(key->iface, h->dev) != 0))) { I(ListItem)->destroy(&item); free(h); continue; } route_entry_t *r = calloc(1, sizeof (route_entry_t)); r->gateway = h->gw; r->mask = mask; r->dst = dst; r->src = src; strcpy(r->iface, h->dev); I(List)->insert(result, I(ListItem)->new(r)); I(ListItem)->destroy(&item); free(h); } I(List)->destroy(&nhops); } } #endif close(skfd); return result; } return result; } static list_t * cacheLookup(route_entry_t * dest) { list_t *result = NULL; #if defined(linux) FILE *f = fopen("/proc/net/rt_cache", "r"); if (f) { char buf[512]; result = I(List)->new(); fgets(buf, sizeof (buf), f); // skip header while (!feof(f)) { if (fgets(buf, sizeof (buf), f)) { list_t *fields = I(String)->tokenize(buf, "\t"); int i = 0; list_item_t *item; route_entry_t *entry = calloc(1, sizeof (route_entry_t)); while ((item = I(List)->pop(fields))) { switch (i) { case 0: strcpy(entry->iface, (char*) item->data); break; case 1: sscanf((char*) item->data, "%X", &entry->dst); break; case 2: sscanf((char*) item->data, "%X", &entry->gateway); break; case 7: sscanf((char*) item->data, "%X", &entry->src); break; } free(item->data); I(ListItem)->destroy(&item); i++; } I(List)->destroy(&fields); if (dest) { if (dest->dst && dest->dst != entry->dst) { free(entry); continue; } } I(List)->insert(result, I(ListItem)->new(entry)); } } fclose(f); } #endif return result; } static boolean_t addHostRoute(in_addr_t dst, in_addr_t gw, char *iface) { route_entry_t route = {.dst = dst, .src = 0, .gateway = gw, .mask = 0xffffffff}; boolean_t result = FALSE; if (iface) strcpy(route.iface, iface); else route.iface[0] = '\0'; result = addRoute(&route); return result; } static boolean_t delHostRoute(in_addr_t dst, in_addr_t gw, char *iface) { route_entry_t route = {.dst = dst, .src = 0, .gateway = gw, .mask = 0xffffffff}; boolean_t result = FALSE; if (iface) strcpy(route.iface, iface); else route.iface[0] = '\0'; result = delRoute(&route); return result; } static in_addr_t getIfIP(char *iface) { in_addr_t result = 0; if (iface) { struct ifreq req; int sock = socket(AF_INET, SOCK_DGRAM, 0); memset(&req, 0, sizeof (struct ifreq)); strcpy(req.ifr_name, iface); if (ioctl(sock, SIOCGIFADDR, &req) >= 0) { result = ((struct sockaddr_in *) &req.ifr_addr)->sin_addr.s_addr; } else { DEBUGP(DERROR, "getIfIP", "ioctl: %s", strerror(errno)); } close(sock); } return result; } static in_addr_t getIfGW(char *iface) { route_entry_t route = {.dst = 0, .src = 0, .gateway = 0, .mask = 0}; list_t *routes = NULL; in_addr_t result = 0; if (iface) strcpy(route.iface, iface); else route.iface[0] = '\0'; routes = routeLookup(&route); if (routes) { if (I(List)->count(routes) > 0) { list_item_t *e = I(List)->pop(routes); route_entry_t *entry = (route_entry_t *) e->data; result = entry->gateway; free(entry); I(ListItem)->destroy(&e); } I(List)->clear(routes, TRUE); I(List)->destroy(&routes); } return result; } IMPLEMENT_INTERFACE(Route) = { .addRoute = addRoute, .delRoute = delRoute, .cacheLookup = cacheLookup, .addHostRoute = addHostRoute, .delHostRoute = delHostRoute, .getIfGW = getIfGW, .getIfIP = getIfIP };