static switchlink_handle_t process_oif_list(struct nlattr *attr, switchlink_handle_t vrf_h) { switchlink_db_status_t status; switchlink_db_oifl_info_t oifl_info; memset(&oifl_info, 0, sizeof(switchlink_db_oifl_info_t)); struct rtnexthop *rnh = (struct rtnexthop *)nla_data(attr); int attrlen = nla_len(attr); while (RTNH_OK(rnh, attrlen)) { switchlink_db_interface_info_t ifinfo; memset(&ifinfo, 0, sizeof(switchlink_db_interface_info_t)); status = switchlink_db_interface_get_info(rnh->rtnh_ifindex, &ifinfo); if (status == SWITCHLINK_DB_STATUS_SUCCESS) { oifl_info.intfs[oifl_info.num_intfs++] = ifinfo.intf_h; } rnh = RTNH_NEXT(rnh); } if (!oifl_info.num_intfs) { return 0; } status = switchlink_db_oifl_get_info(&oifl_info); if (status == SWITCHLINK_DB_STATUS_ITEM_NOT_FOUND) { switchlink_db_oifl_add(&oifl_info); } return oifl_info.oifl_h; }
/* * debug attribute RTA_MULTIPATH */ void debug_rta_multipath(int lev, struct rtmsg *rtm, struct rtattr *rta, const char *name) { struct rtnexthop *rtnh; int rtnh_len = RTA_PAYLOAD(rta); struct rtattr *rtnha[__RTA_MAX]; char ifname[IFNAMSIZ] = ""; char flags_list[MAX_STR_SIZE] = ""; if(debug_rta_len_chk(lev, rta, name, sizeof(*rtnh))) return; rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(rta->rta_len)); for(rtnh = RTA_DATA(rta); RTNH_OK(rtnh, rtnh_len); rtnh = RTNH_NEXT(rtnh), rtnh_len -= RTNH_ALIGN(rtnh->rtnh_len)) { conv_rtnh_flags(rtnh->rtnh_flags, flags_list, sizeof(flags_list)); if_indextoname_from_lists(rtnh->rtnh_ifindex, ifname); rec_dbg(lev, " [ rtnexthop(%d) ]", sizeof(*rtnh)); rec_dbg(lev, " rtnh_len(%d): %hu", sizeof(rtnh->rtnh_len), rtnh->rtnh_len); rec_dbg(lev, " rtnh_flags(%d): %d(%s)", sizeof(rtnh->rtnh_flags), rtnh->rtnh_flags, flags_list); rec_dbg(lev, " rtnh_hops(%d): %d", sizeof(rtnh->rtnh_hops), rtnh->rtnh_hops); rec_dbg(lev, " rtnh_ifindex(%d): %d(%s)", sizeof(rtnh->rtnh_ifindex), rtnh->rtnh_ifindex, ifname); parse_rtattr(rtnha, RTA_MAX, RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh)); if(rtnha[RTA_GATEWAY]) debug_rta_af(lev+3, rtnha[RTA_GATEWAY], "RTA_GATEWAY", rtm->rtm_family); } }
/* * parse_multipath_attr */ int parse_multipath_attr (netlink_msg_ctx_t *ctx, struct rtattr *mpath_rtattr) { size_t len, attr_len; struct rtnexthop *rtnh; struct rtattr *rtattrs[RTA_MAX + 1]; struct rtattr *rtattr, *gateway; int if_index; const char *err_msg; rtnh = RTA_DATA(mpath_rtattr); len = RTA_PAYLOAD(mpath_rtattr); for (; len > 0; len -= NLMSG_ALIGN(rtnh->rtnh_len), rtnh = RTNH_NEXT(rtnh)) { if (!RTNH_OK(rtnh, len)) { netlink_msg_ctx_set_err(ctx, "Malformed nh"); return 0; } if (rtnh->rtnh_len <= sizeof(*rtnh)) { netlink_msg_ctx_set_err(ctx, "NH len too small"); return 0; } /* * Parse attributes included in the nexthop. */ err_msg = NULL; if (!parse_rtattrs_(RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh), rtattrs, NUM_OF(rtattrs), &err_msg)) { netlink_msg_ctx_set_err(ctx, err_msg); return 0; } gateway = rtattrs[RTA_GATEWAY]; netlink_msg_ctx_add_nh(ctx, rtnh->rtnh_ifindex, gateway); } return 1; }
//db_route_t bool nl_to_route_info(int rt_msg_type, struct nlmsghdr *hdr, cps_api_object_t obj) { struct rtmsg *rtmsg = (struct rtmsg *)NLMSG_DATA(hdr); char addr_str[INET6_ADDRSTRLEN]; if(hdr->nlmsg_len < NLMSG_LENGTH(sizeof(*rtmsg))) return false; if(rt_msg_type == RTM_NEWROUTE) { cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_MSG_TYPE,ROUTE_ADD); } else if(rt_msg_type == RTM_DELROUTE) { cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_MSG_TYPE,ROUTE_DEL); } else { return false; } cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_PROTOCOL,rtmsg->rtm_protocol); cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_FAMILY,rtmsg->rtm_family); int attr_len = nlmsg_attrlen(hdr,sizeof(*rtmsg)); struct nlattr *head = nlmsg_attrdata(hdr, sizeof(struct rtmsg)); struct nlattr *attrs[__IFLA_MAX]; memset(attrs,0,sizeof(attrs)); if (nla_parse(attrs,__IFLA_MAX,head,attr_len)!=0) { EV_LOG(ERR,ROUTE,0,"NL-ROUTE-PARSE","Failed to parse attributes"); return false; } cps_api_key_init(cps_api_object_key(obj),cps_api_qualifier_TARGET, cps_api_obj_cat_ROUTE,cps_api_route_obj_ROUTE,0 ); if(attrs[RTA_DST]!=NULL) { rta_add_ip((struct nlattr*)attrs[RTA_DST],rtmsg->rtm_family, obj,cps_api_if_ROUTE_A_PREFIX); } if((rtmsg->rtm_flags & RTM_F_CLONED) && (rtmsg->rtm_family == AF_INET6)) { // Skip cloned route updates char addr_str[INET6_ADDRSTRLEN]; EV_LOG(INFO, NETLINK,3,"ROUTE-EVENT","Cache entry %s", (attrs[RTA_DST]!=NULL)?(inet_ntop(rtmsg->rtm_family, ((struct in6_addr *) nla_data((struct nlattr*)attrs[RTA_DST])), addr_str, INET6_ADDRSTRLEN)):""); return false; } cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_PREFIX_LEN,rtmsg->rtm_dst_len); size_t hop_count = 0; cps_api_attr_id_t ids[3]; const int ids_len = sizeof(ids)/sizeof(*ids); ids[0] = cps_api_if_ROUTE_A_NH; ids[1] = hop_count; if (attrs[RTA_GATEWAY]!=NULL) { ids[2] = cps_api_if_ROUTE_A_NEXT_HOP_ADDR; rta_add_e_ip((struct nlattr*)attrs[RTA_GATEWAY], rtmsg->rtm_family,obj, ids,ids_len); rta_add_ip((struct nlattr*)attrs[RTA_GATEWAY],rtmsg->rtm_family,obj, cps_api_if_ROUTE_A_NEXT_HOP_ADDR); } if (attrs[RTA_OIF]!=NULL) { ids[2] = cps_api_if_ROUTE_A_NH_IFINDEX; unsigned int *x = (unsigned int *) nla_data(attrs[RTA_OIF]); cps_api_object_e_add(obj,ids,ids_len,cps_api_object_ATTR_T_U32, nla_data(attrs[RTA_OIF]),sizeof(uint32_t)); cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_NH_IFINDEX,*x); } nas_os_log_route_info(rt_msg_type, rtmsg, attrs); if (attrs[RTA_MULTIPATH]) { //array of next hops struct rtnexthop * rtnh = (struct rtnexthop * )nla_data(attrs[RTA_MULTIPATH]); int remaining = nla_len(attrs[RTA_MULTIPATH]); while (RTNH_OK(rtnh, remaining)) { ids[1] = hop_count; ids[2] = cps_api_if_ROUTE_A_NH_IFINDEX; uint32_t _int = rtnh->rtnh_ifindex; cps_api_object_e_add(obj,ids,ids_len,cps_api_object_ATTR_T_U32, &rtnh->rtnh_ifindex,sizeof(uint32_t)); _int = rtnh->rtnh_flags; ids[2] = cps_api_if_ROUTE_A_NEXT_HOP_FLAGS; cps_api_object_e_add(obj,ids,ids_len,cps_api_object_ATTR_T_U32, &_int,sizeof(uint32_t)); ids[2] = cps_api_if_ROUTE_A_NEXT_HOP_WEIGHT; _int = rtnh->rtnh_hops; cps_api_object_e_add(obj,ids,ids_len,cps_api_object_ATTR_T_U32, &_int,sizeof(uint32_t)); struct nlattr *nhattr[__RTA_MAX]; memset(nhattr,0,sizeof(nhattr)); nhrt_parse(nhattr,__IFLA_MAX,rtnh); if (nhattr[RTA_GATEWAY]) { ids[2] = cps_api_if_ROUTE_A_NEXT_HOP_ADDR; rta_add_e_ip((struct nlattr*)nhattr[RTA_GATEWAY], rtmsg->rtm_family,obj, ids,ids_len); EV_LOG(INFO, NETLINK,3,"ROUTE-EVENT","MultiPath nh-cnt:%d gateway:%s ifIndex:%d nh-flags:0x%x weight:%d", hop_count, ((rtmsg->rtm_family == AF_INET) ? (inet_ntop(rtmsg->rtm_family, ((struct in_addr *) nla_data((struct nlattr*)nhattr[RTA_GATEWAY])), addr_str, INET_ADDRSTRLEN)) : (inet_ntop(rtmsg->rtm_family, ((struct in6_addr *) nla_data((struct nlattr*)nhattr[RTA_GATEWAY])), addr_str, INET6_ADDRSTRLEN))), rtnh->rtnh_ifindex, rtnh->rtnh_flags, rtnh->rtnh_hops); } else { EV_LOG(INFO, NETLINK,3,"ROUTE-EVENT","MultiPath nh-cnt:%d ifIndex:%d nh-flags:0x%x weight:%d", hop_count, rtnh->rtnh_ifindex, rtnh->rtnh_flags, rtnh->rtnh_hops); } rtnh = rtnh_next(rtnh,&remaining); ++hop_count; } } else { ++hop_count; } cps_api_object_attr_add_u32(obj,cps_api_if_ROUTE_A_HOP_COUNT,hop_count); return true; }
static switchlink_handle_t process_ecmp(uint8_t family, struct nlattr *attr, switchlink_handle_t vrf_h) { switchlink_db_status_t status; if ((family != AF_INET) && (family != AF_INET6)) { return 0; } switchlink_db_ecmp_info_t ecmp_info; memset(&ecmp_info, 0, sizeof(switchlink_db_ecmp_info_t)); struct rtnexthop *rnh = (struct rtnexthop *)nla_data(attr); int attrlen = nla_len(attr); while (RTNH_OK(rnh, attrlen)) { struct rtattr *rta = RTNH_DATA(rnh); if (rta->rta_type == RTA_GATEWAY) { switchlink_ip_addr_t gateway; memset(&gateway, 0, sizeof(switchlink_ip_addr_t)); gateway.family = family; if (family == AF_INET) { gateway.ip.v4addr.s_addr = ntohl(*((uint32_t *)RTA_DATA(rta))); gateway.prefix_len = 32; } else { gateway.prefix_len = 128; } switchlink_db_interface_info_t ifinfo; memset(&ifinfo, 0, sizeof(switchlink_db_interface_info_t)); status = switchlink_db_interface_get_info(rnh->rtnh_ifindex, &ifinfo); if (status == SWITCHLINK_DB_STATUS_SUCCESS) { switchlink_db_neigh_info_t neigh_info; memset(&neigh_info, 0, sizeof(switchlink_db_neigh_info_t)); memcpy(&(neigh_info.ip_addr), &gateway, sizeof(switchlink_ip_addr_t)); neigh_info.intf_h = ifinfo.intf_h; neigh_info.vrf_h = vrf_h; status = switchlink_db_neighbor_get_info(&neigh_info); if (status == SWITCHLINK_DB_STATUS_SUCCESS) { ecmp_info.nhops[ecmp_info.num_nhops] = neigh_info.nhop_h; } else { ecmp_info.nhops[ecmp_info.num_nhops] = g_cpu_rx_nhop_h; } ecmp_info.num_nhops++; assert(ecmp_info.num_nhops < SWITCHLINK_ECMP_NUM_MEMBERS_MAX); } } rnh = RTNH_NEXT(rnh); } if (!ecmp_info.num_nhops) { return 0; } status = switchlink_db_ecmp_get_info(&ecmp_info); if (status == SWITCHLINK_DB_STATUS_ITEM_NOT_FOUND) { switchlink_ecmp_create(&ecmp_info); switchlink_db_ecmp_add(&ecmp_info); } return ecmp_info.ecmp_h; }
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 };
/* * parse route message */ int parse_rtmsg(struct nlmsghdr *nlh) { struct rtmsg *rtm; int rtm_len; struct rtattr *rta[__RTA_MAX]; char ipv[MAX_STR_SIZE] = ""; char msg[MAX_MSG_SIZE] = ""; char *mp = msg; int log_opts = get_log_opts(); int res; /* debug nlmsghdr */ if(log_opts & L_DEBUG) debug_nlmsg(0, nlh); /* get rtmsg */ rtm_len = NLMSG_PAYLOAD(nlh, 0); if(rtm_len < sizeof(*rtm)) { rec_log("error: %s: rtmsg: length too short", __func__); return(1); } rtm = (struct rtmsg *)NLMSG_DATA(nlh); /* parse route attributes */ parse_rtattr(rta, RTA_MAX, RTM_RTA(rtm), RTM_PAYLOAD(nlh)); /* debug rtmsg */ if(log_opts & L_DEBUG) debug_rtmsg(0, rtm, rta, rtm_len); /* check address family */ char dst[INET6_ADDRSTRLEN] = ""; if(rtm->rtm_family == AF_INET) { strcpy(ipv, "ipv4"); strcpy(dst, "0.0.0.0"); } else if(rtm->rtm_family == AF_INET6) { strcpy(ipv, "ipv6"); strcpy(dst, "::"); } else { rec_log("error: %s: unknown address family: %d", __func__, rtm->rtm_family); return(1); } /* convert from table id to table name */ char table[MAX_STR_SIZE] = ""; snprintf(table, sizeof(table), "%s", conv_rt_table(rtm->rtm_table, 0)); if(!strncmp(table, "unknown", sizeof(table))) snprintf(table, sizeof(table), "%d", rtm->rtm_table); /* check route table id(other than RT_TABLE_LOCAL) */ if(rtm->rtm_table == RT_TABLE_LOCAL) return(1); /* check route protocol(other than RTPROT_UNSPEC) */ if(rtm->rtm_protocol == RTPROT_UNSPEC) return(1); /* check route flags(other then RTM_F_CLONED) */ if(rtm->rtm_flags & RTM_F_CLONED) return(1); /* get destination prefix */ if(rta[RTA_DST]) { res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_DST], dst, sizeof(dst)); if(res) { rec_log("error: %s: RTA_DST: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } } /* no RTA_DST attribute if destination is a default gateway */ mp = add_log(msg, mp, "destination=%s/%d ", dst, rtm->rtm_dst_len); /* get source prefix */ if(rta[RTA_SRC]) { char src[INET6_ADDRSTRLEN] = ""; res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_SRC], src, sizeof(src)); if(res == 1) { rec_log("error: %s: RTA_SRC: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } mp = add_log(msg, mp, "source=%s/%d ", src, rtm->rtm_src_len); } /* get preferred source address */ if(rta[RTA_PREFSRC]) { char prefsrc[INET6_ADDRSTRLEN] = ""; res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_PREFSRC], prefsrc, sizeof(prefsrc)); if(res) { rec_log("error: %s: RTA_PREFSRC: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } mp = add_log(msg, mp, "preferred-source=%s ", prefsrc); } /* get tos */ if(rtm->rtm_tos) mp = add_log(msg, mp, "tos=0x%.2x ", rtm->rtm_tos); /* get ingress interface */ if(rta[RTA_IIF]) { unsigned iifindex; char iifname[IFNAMSIZ] = ""; if(RTA_PAYLOAD(rta[RTA_IIF]) < sizeof(iifindex)) { rec_log("error: %s: RTA_IIF: payload too short", __func__); return(1); } iifindex = *((unsigned *)RTA_DATA(rta[RTA_IIF])); if_indextoname_from_lists(iifindex, iifname); mp = add_log(msg, mp, "in=%s ", iifname); } /* get gateway address */ if(rta[RTA_GATEWAY]) { char nexthop[INET6_ADDRSTRLEN] = ""; res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_GATEWAY], nexthop, sizeof(nexthop)); if(res) { rec_log("error: %s: RTA_GATEWAY: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } mp = add_log(msg, mp, "nexthop=%s ", nexthop); } /* get egress interface */ if(rta[RTA_OIF]) { unsigned oifindex; char oifname[IFNAMSIZ] = ""; if(RTA_PAYLOAD(rta[RTA_OIF]) < sizeof(oifindex)) { rec_log("error: %s: RTA_OIF: payload too short", __func__); return(1); } oifindex = *((unsigned *)RTA_DATA(rta[RTA_OIF])); if_indextoname_from_lists(oifindex, oifname); mp = add_log(msg, mp, "interface=%s ", oifname); } /* get priority(but metric) */ char metric[MAX_STR_SIZE] = ""; if(rta[RTA_PRIORITY]) { if(RTA_PAYLOAD(rta[RTA_PRIORITY]) < sizeof(int)) { rec_log("error: %s: RTA_PRIORITY: payload too short", __func__); return(1); } snprintf(metric, sizeof(metric), "metric=%d ", *((int *)RTA_DATA(rta[RTA_PRIORITY]))); } /* convert route message type */ char type[MAX_STR_SIZE] = ""; snprintf(type, sizeof(type), "%s", conv_rtn_type(rtm->rtm_type, 0)); /* convert route message protocol */ char proto[MAX_STR_SIZE] = ""; snprintf(proto, sizeof(proto), "%s", conv_rtprot(rtm->rtm_protocol, 0)); /* get table id & name */ if(rta[RTA_TABLE]) { int table_id = *(int *)RTA_DATA(rta[RTA_TABLE]); if(RTA_PAYLOAD(rta[RTA_TABLE]) < sizeof(int)) { rec_log("error: %s: RTA_TABLE: payload too short", __func__); return(1); } snprintf(table, sizeof(table), "%s", conv_rt_table(table_id, 0)); if(!strncmp(table, "unknown", sizeof(table))) snprintf(table, sizeof(table), "%d", table_id); } /* get multipath */ if(rta[RTA_MULTIPATH]) { struct rtnexthop *rtnh; int rtnh_len = RTA_PAYLOAD(rta[RTA_MULTIPATH]); struct rtattr *rtna[__RTA_MAX]; char rtnh_ifname[IFNAMSIZ] = ""; char rtnh_nexthop[INET6_ADDRSTRLEN] = ""; if(RTA_PAYLOAD(rta[RTA_MULTIPATH]) < sizeof(*rtnh)) { rec_log("error: %s: RTA_MULTIPATH: payload too short", __func__); return(1); } rtnh = RTA_DATA(rta[RTA_MULTIPATH]); for(; RTNH_OK(rtnh, rtnh_len); rtnh = RTNH_NEXT(rtnh), rtnh_len -= RTNH_ALIGN(rtnh->rtnh_len)) { parse_rtattr(rtna, RTA_MAX, RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh)); if(rtna[RTA_GATEWAY]) { res = inet_ntop_ifa(rtm->rtm_family, rtna[RTA_GATEWAY], rtnh_nexthop, sizeof(rtnh_nexthop)); if(res) { rec_log("error: %s: RTA_GATEWAY: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } } /* get interface name & logging routing table message */ if_indextoname_from_lists(rtnh->rtnh_ifindex, rtnh_ifname); if(nlh->nlmsg_type == RTM_NEWROUTE) rec_log("%s route added: %snexthop=%s interface=%s " "%sweight=%d type=%s protocol=%s table=%s", ipv, msg, rtnh_nexthop, rtnh_ifname, metric, rtnh->rtnh_hops+1, type, proto, table); else if(nlh->nlmsg_type == RTM_DELROUTE) rec_log("%s route deleted: %snexthop=%s interface=%s " "%sweight=%d type=%s protocol=%s table=%s", ipv, msg, rtnh_nexthop, rtnh_ifname, metric, rtnh->rtnh_hops+1, type, proto, table); } return(0); } /* logging routing message */ if(nlh->nlmsg_type == RTM_NEWROUTE) rec_log("%s route added: %s%stype=%s protocol=%s table=%s", ipv, msg, metric, type, proto, table); else if(nlh->nlmsg_type == RTM_DELROUTE) rec_log("%s route deleted: %s%stype=%s proto=%s table=%s", ipv, msg, metric, type, proto, table); return(0); }