vector get_routes_by_table(int table,int family){ vector routes; vector_init(&routes); struct{ struct nlmsghdr n; struct rtmsg r; char buf[1024]; }route_req; memset(&route_req, 0, sizeof(route_req)); struct rtnl_handle rth; if (rtnl_open(&rth, 0) < 0){ printf("cannot open rtnetlink\n"); return routes; } route_req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); route_req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; route_req.n.nlmsg_type = RTM_GETROUTE; route_req.r.rtm_family = family; route_req.r.rtm_table = table; route_req.r.rtm_dst_len = 0; route_req.r.rtm_src_len = 0; int nbytes=0,reply_len=0;//ret, count=0; ssize_t counter = 5000; char reply_ptr[5000]; char* buf = reply_ptr; struct nlmsghdr *nlp; struct rtmsg *rtp; struct rtattr *rtap; int rtl; unsigned long bufsize ; nlp = malloc(sizeof(struct nlmsghdr)); memset(nlp, 0, sizeof(struct nlmsghdr)); int rbytes __attribute__((unused)) = rtnl_dump_request(&rth,RTM_GETROUTE,&route_req,sizeof(route_req)); for(;;){ if( counter < sizeof(struct nlmsghdr)){ printf("Routing table is bigger than %lu\n", sizeof(reply_ptr)); vector_free(&routes); return routes; } nbytes = recv(rth.fd, &reply_ptr[reply_len], counter, 0); if(nbytes < 0 ){ printf("Error in recv\n"); break; } if(nbytes == 0) printf("EOF in netlink\n"); nlp = (struct nlmsghdr*)(&reply_ptr[reply_len]); if (nlp->nlmsg_type == NLMSG_DONE){ // All data has been received. // Truncate the reply to exclude this message, // i.e. do not increase reply_len. break; } if (nlp->nlmsg_type == NLMSG_ERROR){ printf("Error in msg\n"); vector_free(&routes); return routes; } reply_len += nbytes; counter -= nbytes; } bufsize = reply_len; nlp = (struct nlmsghdr*)buf; for (;NLMSG_OK(nlp, bufsize); nlp = NLMSG_NEXT(nlp, bufsize)){ /* Get the route data */ rtp = (struct rtmsg *) NLMSG_DATA(nlp); /* We only need route from the specific protocol*/ if (rtp->rtm_table != table) continue; /* Get attributes of route_entry */ rtap = (struct rtattr *) RTM_RTA(rtp); /* Get the route atttibutes len */ rtl = RTM_PAYLOAD(nlp); /* Loop through all attributes */ struct netlink_route *route = malloc(sizeof(struct netlink_route)); route->proto = rtp->rtm_protocol; route->prefix = rtp->rtm_dst_len; for ( ; RTA_OK(rtap, rtl);rtap = RTA_NEXT(rtap, rtl)){ if(rtap->rta_type == RTA_DST){ struct sockaddr_storage dest; if(family == AF_INET){ ((struct sockaddr_in*)&dest)->sin_addr.s_addr = (unsigned long) RTA_DATA(rtap); dest.ss_family = AF_INET; }else{ memcpy(&((struct sockaddr_in6*)&dest)->sin6_addr.s6_addr,RTA_DATA(rtap),16); dest.ss_family = AF_INET6; } route->dest = dest; } if(rtap->rta_type == RTA_GATEWAY){ struct sockaddr_storage dest; if(family == AF_INET){ ((struct sockaddr_in*)&dest)->sin_addr.s_addr = (unsigned long) RTA_DATA(rtap); dest.ss_family = AF_INET; }else{ memcpy(&((struct sockaddr_in6*)&dest)->sin6_addr.s6_addr,RTA_DATA(rtap),16); dest.ss_family = AF_INET6; } route->gateway = dest; } if(rtap->rta_type == RTA_PRIORITY){ route->metric = *(unsigned long *) RTA_DATA(rtap); } } vector_add(&routes,route); } rtnl_close(&rth); return routes; }
/* * See if left->addr or left->next is %defaultroute and change it to IP. * * Returns: * -1: failure * 0: done * 1: please call again: more to do */ static int resolve_defaultroute_one(struct starter_end *host, struct starter_end *peer) { /* * "left=" == host->addrtype and host->addr * "leftnexthop=" == host->nexttype and host->nexthop */ /* What kind of result are we seeking? */ bool seeking_src = (host->addrtype == KH_DEFAULTROUTE); bool seeking_gateway = (host->nexttype == KH_DEFAULTROUTE); char msgbuf[RTNL_BUFSIZE]; bool has_dst = FALSE; int query_again = 0; if (!seeking_src && !seeking_gateway) return 0; /* this end already figured out */ /* Fill netlink request */ netlink_query_init(msgbuf, host->addr_family); if (host->nexttype == KH_IPADDR) { /* * My nexthop (gateway) is specified. * We need to figure out our source IP to get there. */ netlink_query_add(msgbuf, RTA_DST, &host->nexthop); has_dst = TRUE; } else if (peer->addrtype == KH_IPADDR) { /* * Peer IP is specified. * We may need to figure out source IP * and gateway IP to get there. */ netlink_query_add(msgbuf, RTA_DST, &peer->addr); has_dst = TRUE; if (seeking_src && seeking_gateway && host->addr_family == AF_INET) { /* * If we have only peer IP and no gateway/src we must * do two queries: * 1) find out gateway for dst * 2) find out src for that gateway * Doing both in one query returns src for dst. * * (IPv6 returns link-local for gateway so we can and * do seek both in one query.) */ seeking_src = FALSE; query_again = 1; } } if (has_dst && host->addrtype == KH_IPADDR) { /* SRC works only with DST */ netlink_query_add(msgbuf, RTA_SRC, &host->addr); } /* * If we have for example host=%defaultroute + peer=%any * (no destination) the netlink reply will be full routing table. * We must do two queries: * 1) find out default gateway * 2) find out src for that default gateway */ if (!has_dst && seeking_src && seeking_gateway) { seeking_src = FALSE; query_again = 1; } if (seeking_gateway) { struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf; nlmsg->nlmsg_flags |= NLM_F_DUMP; } if (verbose) printf("\nseeking_src = %d, seeking_gateway = %d, has_dst = %d\n", seeking_src, seeking_gateway, has_dst); /* Send netlink get_route request */ ssize_t len = netlink_query(msgbuf); if (len < 0) return -1; /* Parse reply */ struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf; /* * The cast to unsigned short is to dodge an error in * netlink.h:NLMSG_OK() which triggers a GCC warning in recent * versions of GCC (2014 August): * error: comparison between signed and unsigned integer expressions * Note: as long as RTNL_BUFSIZE <= USHRT_MAX, this is safe. */ for (; NLMSG_OK(nlmsg, (unsigned short)len); nlmsg = NLMSG_NEXT(nlmsg, len)) { char r_interface[IF_NAMESIZE+1]; char r_source[ADDRTOT_BUF]; char r_gateway[ADDRTOT_BUF]; char r_destination[ADDRTOT_BUF]; if (nlmsg->nlmsg_type == NLMSG_DONE) break; if (nlmsg->nlmsg_type == NLMSG_ERROR) { printf("netlink error\n"); return -1; } /* ignore all but IPv4 and IPv6 */ struct rtmsg *rtmsg = (struct rtmsg *) NLMSG_DATA(nlmsg); if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6) continue; /* Parse one route entry */ zero(&r_interface); r_source[0] = r_gateway[0] = r_destination[0] = '\0'; struct rtattr *rtattr = (struct rtattr *) RTM_RTA(rtmsg); int rtlen = RTM_PAYLOAD(nlmsg); while (RTA_OK(rtattr, rtlen)) { switch (rtattr->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(rtattr), r_interface); break; case RTA_PREFSRC: inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr), r_source, sizeof(r_source)); break; case RTA_GATEWAY: inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr), r_gateway, sizeof(r_gateway)); break; case RTA_DST: inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr), r_destination, sizeof(r_destination)); break; } rtattr = RTA_NEXT(rtattr, rtlen); } /* * Ignore if not main table. * Ignore ipsecX or mastX interfaces. */ bool ignore = rtmsg->rtm_table != RT_TABLE_MAIN || startswith(r_interface, "ipsec") || startswith(r_interface, "mast"); if (verbose) { printf("dst %s via %s dev %s src %s table %d%s\n", r_destination, r_gateway, r_interface, r_source, rtmsg->rtm_table, ignore ? "" : " (ignored)"); } if (ignore) continue; if (seeking_src && r_source[0] != '\0') { err_t err = tnatoaddr(r_source, 0, rtmsg->rtm_family, &host->addr); if (err == NULL) { host->addrtype = KH_IPADDR; seeking_src = FALSE; if (verbose) printf("set addr: %s\n", r_source); } else if (verbose) { printf("unknown source results from kernel (%s): %s\n", r_source, err); } } if (seeking_gateway && r_destination[0] == '\0' && (has_dst || r_source[0] == '\0')) { if (r_gateway[0] == '\0' && r_interface[0] != '\0') { /* * Point-to-Point default gw without "via IP" * Attempt to find r_gateway as the IP address * on the interface. */ resolve_ppp_peer(r_interface, host->addr_family, r_gateway); } if (r_gateway[0] != '\0') { err_t err = tnatoaddr(r_gateway, 0, rtmsg->rtm_family, &host->nexthop); if (err != NULL) { printf("unknown gateway results from kernel: %s\n", err); } else { /* Note: Use first even if multiple */ host->nexttype = KH_IPADDR; seeking_gateway = FALSE; if (verbose) printf("set nexthop: %s\n", r_gateway); } } } } return query_again; }
/* * Parse a RTM_NEWROUTE or RTM_DELROUTE message. */ bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) { uint8_t type = nh->nlmsg_type; const char *msgname = rtMessageName(type); // Sanity check. if (type != RTM_NEWROUTE && type != RTM_DELROUTE) { SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname); return false; } struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA(nh); if (!checkRtNetlinkLength(nh, sizeof(*rtm))) return false; if (// Ignore static routes we've set up ourselves. (rtm->rtm_protocol != RTPROT_KERNEL && rtm->rtm_protocol != RTPROT_RA) || // We're only interested in global unicast routes. (rtm->rtm_scope != RT_SCOPE_UNIVERSE) || (rtm->rtm_type != RTN_UNICAST) || // We don't support source routing. (rtm->rtm_src_len != 0) || // Cloned routes aren't real routes. (rtm->rtm_flags & RTM_F_CLONED)) { return false; } int family = rtm->rtm_family; int prefixLength = rtm->rtm_dst_len; // Currently we only support: destination, (one) next hop, ifindex. char dst[INET6_ADDRSTRLEN] = ""; char gw[INET6_ADDRSTRLEN] = ""; char dev[IFNAMSIZ] = ""; size_t len = RTM_PAYLOAD(nh); struct rtattr *rta; for (rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { switch (rta->rta_type) { case RTA_DST: if (maybeLogDuplicateAttribute(*dst, "RTA_DST", msgname)) continue; if (!inet_ntop(family, RTA_DATA(rta), dst, sizeof(dst))) return false; continue; case RTA_GATEWAY: if (maybeLogDuplicateAttribute(*gw, "RTA_GATEWAY", msgname)) continue; if (!inet_ntop(family, RTA_DATA(rta), gw, sizeof(gw))) return false; continue; case RTA_OIF: if (maybeLogDuplicateAttribute(*dev, "RTA_OIF", msgname)) continue; if (!if_indextoname(* (int *) RTA_DATA(rta), dev)) return false; default: continue; } } // If there's no RTA_DST attribute, then: // - If the prefix length is zero, it's the default route. // - If the prefix length is nonzero, there's something we don't understand. // Ignore the event. if (!*dst && !prefixLength) { if (family == AF_INET) { strncpy(dst, "0.0.0.0", sizeof(dst)); } else if (family == AF_INET6) { strncpy(dst, "::", sizeof(dst)); } } // A useful route must have a destination and at least either a gateway or // an interface. if (!*dst || (!*gw && !*dev)) return false; // Fill in netlink event information. mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated : NlActionRouteRemoved; mSubsystem = strdup("net"); asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength); asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : ""); asprintf(&mParams[2], "INTERFACE=%s", (*dev) ? dev : ""); return true; }
int getSenderInterface(unsigned int targetIP, char* device, char* mac) { struct nlmsghdr* nlMsg; char msgBuf[BUFSIZE]; int sock; ssize_t len = 0; uint32_t msgSeq = 0; if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { syslog(LOG_ERR, "unable to create socket: '%s'", strerror(errno)); return -1; } memset(msgBuf, 0, BUFSIZE); nlMsg = (struct nlmsghdr *)msgBuf; nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlMsg->nlmsg_type = RTM_GETROUTE; nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nlMsg->nlmsg_seq = msgSeq++; nlMsg->nlmsg_pid = getpid(); if (send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0) { syslog(LOG_ERR, "unable to write to socket: '%s'", strerror(errno)); close(sock); return -1; } if ((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) { syslog(LOG_ERR, "unable to read from socket: '%s'", strerror(errno)); close(sock); return -1; } for (; nlmsg_ok(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) { struct rtmsg* rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg); if (rtMsg->rtm_family == AF_INET || rtMsg->rtm_table == RT_TABLE_MAIN) { struct rtattr* rtAttr = (struct rtattr *)RTM_RTA(rtMsg); int rtLen = RTM_PAYLOAD(nlMsg); char ifName[IF_NAMESIZE] = {0}; unsigned int dstAddr = 0, dstMask = 1; for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) { switch (rtAttr->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(rtAttr), ifName); break; case RTA_DST: dstAddr = *(u_int *)RTA_DATA(rtAttr); dstMask = rtLen; break; } } if (dstMask <= 32) { dstMask = htonl(ntohl(inet_addr("255.255.255.255")) << (32 - dstMask)); if ((dstAddr & dstMask) == (targetIP & dstMask)) { if (getInterfaceMac(ifName, mac) == 0) { close(sock); snprintf(device, IFNAMSIZ, "%s", ifName); syslog(LOG_INFO, "sending from device '%s' with MAC address '%s'", device, printMACStr(mac)); return 0; } } } } } close(sock); return 1; }
static void netlink_route(struct ufpd_thread *thread, struct nlmsghdr *nlh) { struct rtmsg *route_entry; struct rtattr *route_attr; struct fib *fib; int route_attr_len, family; uint8_t prefix[16] = {}; uint8_t nexthop[16] = {}; unsigned int prefix_len; int ifindex, port_index, i; enum fib_type type; route_entry = (struct rtmsg *)NLMSG_DATA(nlh); family = route_entry->rtm_family; prefix_len = route_entry->rtm_dst_len; ifindex = -1; port_index = -1; type = FIB_TYPE_LINK; route_attr = (struct rtattr *)RTM_RTA(route_entry); route_attr_len = RTM_PAYLOAD(nlh); while(RTA_OK(route_attr, route_attr_len)){ switch(route_attr->rta_type){ case RTA_DST: memcpy(prefix, RTA_DATA(route_attr), RTA_PAYLOAD(route_attr)); break; case RTA_GATEWAY: memcpy(nexthop, RTA_DATA(route_attr), RTA_PAYLOAD(route_attr)); type = FIB_TYPE_FORWARD; break; case RTA_OIF: ifindex = *(int *)RTA_DATA(route_attr); break; default: break; } route_attr = RTA_NEXT(route_attr, route_attr_len); } if(route_entry->rtm_table == RT_TABLE_LOCAL) type = FIB_TYPE_LOCAL; for(i = 0; i < thread->num_ports; i++){ if(ufp_tun_index(thread->plane, i) == ifindex){ port_index = i; break; } } switch(family){ case AF_INET: fib = thread->fib_inet; break; case AF_INET6: fib = thread->fib_inet6; break; default: goto out; break; } switch(nlh->nlmsg_type){ case RTM_NEWROUTE: fib_route_update(fib, family, type, prefix, prefix_len, nexthop, port_index, ifindex, thread->mpool); break; case RTM_DELROUTE: fib_route_delete(fib, family, prefix, prefix_len, ifindex); break; default: break; } out: return; }
int ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size) { struct nlmsghdr *nlp; struct rtmsg *rtp; int rtl; struct rtattr *rtap; int result; int s; NL_DEBUG_PRINT ("ENTER ReadEvents()"); result = EVT_NONE; s = GPOINTER_TO_INT (sock); /* This socket is not found by IO layer, so we do everything here */ if (count == 0) { while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR); if (count <= 0) { NL_DEBUG_PRINT ("EXIT ReadEvents()"); return result; } } for (nlp = (struct nlmsghdr *) buffer; NLMSG_OK (nlp, count); nlp = NLMSG_NEXT (nlp, count)) { int family; int addr_length; int msg_type; int table; int protocol; int scope; int rtm_type; gboolean have_dst; gboolean have_src; gboolean have_pref_src; gboolean have_gw; #ifdef AF_INET6 char dst [16]; char src [16]; char pref_src [16]; char gw [16]; #else char dst [4]; char src [4]; char pref_src [4]; char gw [4]; #endif msg_type = nlp->nlmsg_type; NL_DEBUG_PRINT ("TYPE: %d %s", msg_type, FIND_RT_TYPE_NAME (msg_type)); if (msg_type != RTM_NEWROUTE && msg_type != RTM_DELROUTE) continue; rtp = (struct rtmsg *) NLMSG_DATA (nlp); family = rtp->rtm_family; #ifdef AF_INET6 addr_length = (family == AF_INET) ? 4 : 16; #else addr_length = 4; #endif table = rtp->rtm_table; protocol = rtp->rtm_protocol; scope = rtp->rtm_scope; rtm_type = rtp->rtm_type; NL_DEBUG_PRINT ("\tRTMSG table: %d %s", table, FIND_RT_TABLE_NAME (table)); if (table != RT_TABLE_MAIN && table != RT_TABLE_LOCAL) continue; NL_DEBUG_PRINT ("\tRTMSG protocol: %d %s", protocol, FIND_RTM_PROTO_NAME (protocol)); NL_DEBUG_PRINT ("\tRTMSG scope: %d %s", scope, FIND_RTM_SCOPE_NAME (scope)); NL_DEBUG_PRINT ("\tRTMSG type: %d %s", rtm_type, FIND_RTM_TYPE_NAME (rtm_type)); rtap = (struct rtattr *) RTM_RTA (rtp); rtl = RTM_PAYLOAD (nlp); // loop & get every attribute // // // NEW_ROUTE // table = RT_TABLE_LOCAL, Scope = HOST + pref.src == src + type=LOCAL -> new if addr // RT_TABLE_MAIN, Scope = Universe, unicast, gateway exists -> NEW default route // DEL_ROUTE // table = RT_TABLE_LOCAL, Scope = HOST, perfsrc = dst + type=LOCAL -> if addr deleted // RT_TABLE_MAIN - DELROUTE + unicast -> event (gw down?) have_dst = have_src = have_pref_src = have_gw = FALSE; for(; RTA_OK (rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) { char *data; #ifdef NL_DEBUG #ifdef AF_INET6 char ip [INET6_ADDRSTRLEN]; int ip_length = INET6_ADDRSTRLEN; #else char ip [INET_ADDRSTRLEN]; int ip_length = INET_ADDRSTRLEN; #endif #endif NL_DEBUG_PRINT ("\tAttribute: %d %d (%s)", rtap->rta_len, rtap->rta_type, FIND_RTM_ATTRS_NAME (rtap->rta_type)); data = RTA_DATA (rtap); switch (rtap->rta_type) { case RTA_DST: have_dst = TRUE; memcpy (dst, data, addr_length); #ifdef NL_DEBUG *ip = 0; inet_ntop (family, RTA_DATA (rtap), ip, ip_length); NL_DEBUG_PRINT ("\t\tDst: %s", ip); #endif break; case RTA_PREFSRC: have_pref_src = TRUE; memcpy (pref_src, data, addr_length); #ifdef NL_DEBUG *ip = 0; inet_ntop (family, RTA_DATA (rtap), ip, ip_length); NL_DEBUG_PRINT ("\t\tPref. Src.: %s", ip); #endif break; case RTA_SRC: have_src = TRUE; memcpy (src, data, addr_length); #ifdef NL_DEBUG *ip = 0; inet_ntop (family, RTA_DATA (rtap), ip, ip_length); NL_DEBUG_PRINT ("\tSrc: %s", ip); #endif break; case RTA_GATEWAY: have_gw = TRUE; memcpy (gw, data, addr_length); #ifdef NL_DEBUG *ip = 0; inet_ntop (family, RTA_DATA (rtap), ip, ip_length); NL_DEBUG_PRINT ("\t\tGateway: %s", ip); #endif break; default: break; } } if (msg_type == RTM_NEWROUTE) { if (table == RT_TABLE_MAIN) { NL_DEBUG_PRINT ("NEWROUTE: Availability changed"); result |= EVT_AVAILABILITY; } else if (table == RT_TABLE_LOCAL) { NL_DEBUG_PRINT ("NEWROUTE: new IP"); if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0) result |= EVT_ADDRESS; } } else if (msg_type == RTM_DELROUTE) { if (table == RT_TABLE_MAIN) { if (rtm_type == RTN_UNICAST && (have_dst || have_pref_src)) { result |= EVT_AVAILABILITY; NL_DEBUG_PRINT ("DELROUTE: Availability changed"); } } else if (table == RT_TABLE_LOCAL) { if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0) { result |= EVT_ADDRESS; NL_DEBUG_PRINT ("DELROUTE: deleted IP"); } } } while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR); if (count <= 0) { NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result); return result; } nlp = (struct nlmsghdr *) buffer; } NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result); return result; }
static int if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm) { size_t len; struct rtmsg *rtm; struct rtattr *rta; unsigned int ifindex; struct sockaddr *sa; len = nlm->nlmsg_len - sizeof(*nlm); if (len < sizeof(*rtm)) { errno = EBADMSG; return -1; } rtm = (struct rtmsg *)NLMSG_DATA(nlm); if (rtm->rtm_table != RT_TABLE_MAIN) return -1; memset(rt, 0, sizeof(*rt)); if (rtm->rtm_type == RTN_UNREACHABLE) rt->rt_flags |= RTF_REJECT; rta = (struct rtattr *)RTM_RTA(rtm); len = RTM_PAYLOAD(nlm); while (RTA_OK(rta, len)) { sa = NULL; switch (rta->rta_type) { case RTA_DST: sa = &rt->rt_dest; break; case RTA_GATEWAY: sa = &rt->rt_gateway; break; case RTA_PREFSRC: sa = &rt->rt_ifa; break; case RTA_OIF: ifindex = *(unsigned int *)RTA_DATA(rta); rt->rt_ifp = if_findindex(ctx->ifaces, ifindex); break; case RTA_PRIORITY: rt->rt_metric = *(unsigned int *)RTA_DATA(rta); break; case RTA_METRICS: { struct rtattr *r2; size_t l2; l2 = rta->rta_len; r2 = (struct rtattr *)RTA_DATA(rta); while (RTA_OK(r2, l2)) { switch (r2->rta_type) { case RTAX_MTU: rt->rt_mtu = *(unsigned int *)RTA_DATA(r2); break; } r2 = RTA_NEXT(r2, l2); } break; } } if (sa != NULL) { socklen_t salen; sa->sa_family = rtm->rtm_family; salen = sa_addrlen(sa); memcpy((char *)sa + sa_addroffset(sa), RTA_DATA(rta), MIN(salen, RTA_PAYLOAD(rta))); } rta = RTA_NEXT(rta, len); } /* If no RTA_DST set the unspecified address for the family. */ if (rt->rt_dest.sa_family == AF_UNSPEC) rt->rt_dest.sa_family = rtm->rtm_family; rt->rt_netmask.sa_family = rtm->rtm_family; sa_fromprefix(&rt->rt_netmask, rtm->rtm_dst_len); if (sa_is_allones(&rt->rt_netmask)) rt->rt_flags |= RTF_HOST; #if 0 if (rt->rtp_ifp == NULL && rt->src.s_addr != INADDR_ANY) { struct ipv4_addr *ap; /* For some reason the default route comes back with the * loopback interface in RTA_OIF? Lets find it by * preferred source address */ if ((ap = ipv4_findaddr(ctx, &rt->src))) rt->iface = ap->iface; } #endif if (rt->rt_ifp == NULL) { errno = ESRCH; return -1; } return 0; }
static void rtnl_print_route(struct nlmsghdr *hdr) { struct rtmsg *rtm = NLMSG_DATA(hdr); uint32_t attrs_len = RTM_PAYLOAD(hdr); struct rtattr *attr = RTM_RTA(rtm); struct rta_cacheinfo *ci; int hz = get_user_hz(); char addr_str[256]; char flags[256]; tprintf(" [ Route Family %d (%s%s%s)", rtm->rtm_family, colorize_start(bold), addr_family2str(rtm->rtm_family), colorize_end()); tprintf(", Dst Len %d", rtm->rtm_dst_len); tprintf(", Src Len %d", rtm->rtm_src_len); tprintf(", ToS %d", rtm->rtm_tos); tprintf(", Table %d (%s%s%s)", rtm->rtm_table, colorize_start(bold), route_table2str(rtm->rtm_table), colorize_end()); tprintf(", Proto %d (%s%s%s)", rtm->rtm_protocol, colorize_start(bold), route_proto2str(rtm->rtm_protocol), colorize_end()); tprintf(", Scope %d (%s%s%s)", rtm->rtm_scope, colorize_start(bold), scope2str(rtm->rtm_scope), colorize_end()); tprintf(", Type %d (%s%s%s)", rtm->rtm_type, colorize_start(bold), route_type2str(rtm->rtm_type), colorize_end()); tprintf(", Flags 0x%x (%s%s%s) ]\n", rtm->rtm_flags, colorize_start(bold), flags2str(route_flags, rtm->rtm_flags, flags, sizeof(flags)), colorize_end()); for (; RTA_OK(attr, attrs_len); attr = RTA_NEXT(attr, attrs_len)) { switch (attr->rta_type) { case RTA_DST: attr_fmt(attr, "Dst %s", addr2str(rtm->rtm_family, RTA_DATA(attr), addr_str, sizeof(addr_str))); break; case RTA_SRC: attr_fmt(attr, "Src %s", addr2str(rtm->rtm_family, RTA_DATA(attr), addr_str, sizeof(addr_str))); break; case RTA_IIF: attr_fmt(attr, "Iif %d", RTA_INT(attr)); break; case RTA_OIF: attr_fmt(attr, "Oif %d", RTA_INT(attr)); break; case RTA_GATEWAY: attr_fmt(attr, "Gateway %s", addr2str(rtm->rtm_family, RTA_DATA(attr), addr_str, sizeof(addr_str))); break; case RTA_PRIORITY: attr_fmt(attr, "Priority %u", RTA_UINT32(attr)); break; case RTA_PREFSRC: attr_fmt(attr, "Pref Src %s", addr2str(rtm->rtm_family, RTA_DATA(attr), addr_str, sizeof(addr_str))); break; case RTA_MARK: attr_fmt(attr, "Mark 0x%x", RTA_UINT(attr)); break; case RTA_FLOW: attr_fmt(attr, "Flow 0x%x", RTA_UINT(attr)); break; case RTA_TABLE: attr_fmt(attr, "Table %d (%s%s%s)", RTA_UINT32(attr), colorize_start(bold), route_table2str(RTA_UINT32(attr)), colorize_end()); break; case RTA_CACHEINFO: ci = RTA_DATA(attr); tprintf("\tA: Cache ("); tprintf("expires(%ds)", ci->rta_expires / hz); tprintf(", error(%d)", ci->rta_error); tprintf(", users(%d)", ci->rta_clntref); tprintf(", used(%d)", ci->rta_used); tprintf(", last use(%ds)", ci->rta_lastuse / hz); tprintf(", id(%d)", ci->rta_id); tprintf(", ts(%d)", ci->rta_ts); tprintf(", ts age(%ds))", ci->rta_tsage); tprintf(", Len %lu\n", RTA_LEN(attr)); break; } } }
/* Parse one route */ static int rt_parse(const struct nlmsghdr *nlhdr, struct net_rt *rt) { struct rtmsg *rtmsg; struct rtattr *rtattr; int len; rtmsg = (struct rtmsg *)NLMSG_DATA(nlhdr); /* If the route does not belong to main routing table then return. */ if (RT_TABLE_MAIN != rtmsg->rtm_table) return EINVAL; sa_init(&rt->dst, rtmsg->rtm_family); rt->dstlen = rtmsg->rtm_dst_len; /* get the rtattr field */ rtattr = (struct rtattr *)RTM_RTA(rtmsg); len = RTM_PAYLOAD(nlhdr); for (;RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { switch (rtattr->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(rtattr), rt->ifname); break; case RTA_GATEWAY: switch (rtmsg->rtm_family) { case AF_INET: sa_init(&rt->gw, AF_INET); rt->gw.u.in.sin_addr.s_addr = *(uint32_t *)RTA_DATA(rtattr); break; #ifdef HAVE_INET6 case AF_INET6: sa_set_in6(&rt->gw, RTA_DATA(rtattr), 0); break; #endif default: DEBUG_WARNING("RTA_DST: unknown family %d\n", rtmsg->rtm_family); break; } break; #if 0 case RTA_PREFSRC: rt->srcaddr = *(uint32_t *)RTA_DATA(rtattr); break; #endif case RTA_DST: switch (rtmsg->rtm_family) { case AF_INET: sa_init(&rt->dst, AF_INET); rt->dst.u.in.sin_addr.s_addr = *(uint32_t *)RTA_DATA(rtattr); break; #ifdef HAVE_INET6 case AF_INET6: sa_set_in6(&rt->dst, RTA_DATA(rtattr), 0); break; #endif default: DEBUG_WARNING("RTA_DST: unknown family %d\n", rtmsg->rtm_family); break; } break; } } return 0; }
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 };
__static void pflnet_getifnfordst_rtnetlink(const struct sockaddr *sa, char ifn[IFNAMSIZ]) { struct { struct nlmsghdr nmh; struct rtmsg rtm; #define RT_SPACE 8192 unsigned char buf[RT_SPACE]; } rq; const struct sockaddr_in *sin; struct nlmsghdr *nmh; struct rtattr *rta; struct rtmsg *rtm; ssize_t rc, rca; int s, ifidx; size_t nb; sin = (void *)sa; s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (s == -1) psc_fatal("socket"); memset(&rq, 0, sizeof(rq)); rq.nmh.nlmsg_len = NLMSG_SPACE(sizeof(rq.rtm)) + RTA_LENGTH(sizeof(sin->sin_addr)); rq.nmh.nlmsg_flags = NLM_F_REQUEST; rq.nmh.nlmsg_type = RTM_GETROUTE; rq.rtm.rtm_family = sa->sa_family; rq.rtm.rtm_protocol = RTPROT_UNSPEC; rq.rtm.rtm_table = RT_TABLE_MAIN; /* # bits filled in target addr */ rq.rtm.rtm_dst_len = sizeof(sin->sin_addr) * NBBY; rq.rtm.rtm_scope = RT_SCOPE_LINK; rta = (void *)((char *)&rq + NLMSG_SPACE(sizeof(rq.rtm))); rta->rta_type = RTA_DST; rta->rta_len = RTA_LENGTH(sizeof(sin->sin_addr)); memcpy(RTA_DATA(rta), &sin->sin_addr, sizeof(sin->sin_addr)); errno = 0; rc = write(s, &rq, rq.nmh.nlmsg_len); if (rc != (ssize_t)rq.nmh.nlmsg_len) psc_fatal("routing socket length mismatch"); rc = read(s, &rq, sizeof(rq)); if (rc == -1) psc_fatal("routing socket read"); close(s); switch (rq.nmh.nlmsg_type) { case NLMSG_ERROR: { struct nlmsgerr *nlerr; nlerr = NLMSG_DATA(&rq.nmh); psc_fatalx("netlink: %s", strerror(nlerr->error)); } case NLMSG_DONE: psc_fatalx("netlink: unexpected EOF"); } nmh = &rq.nmh; nb = rc; for (; NLMSG_OK(nmh, nb); nmh = NLMSG_NEXT(nmh, nb)) { rtm = NLMSG_DATA(nmh); if (rtm->rtm_table != RT_TABLE_MAIN) continue; rta = RTM_RTA(rtm); rca = RTM_PAYLOAD(nmh); for (; RTA_OK(rta, rca); rta = RTA_NEXT(rta, rca)) { switch (rta->rta_type) { case RTA_OIF: memcpy(&ifidx, RTA_DATA(rta), sizeof(ifidx)); pflnet_getifname(ifidx, ifn); return; } } } psc_fatalx("no route for addr"); }
__static int pflnet_rtexists_rtnetlink(const struct sockaddr *sa) { struct { struct nlmsghdr nmh; struct rtmsg rtm; #define RT_SPACE 8192 unsigned char buf[RT_SPACE]; struct rtattr rta; struct nlmsgerr nlerr; } rq; struct sockaddr_in *sin = (void *)sa; in_addr_t cmpaddr, zero = 0; struct nlmsghdr *nmh; struct rtattr *rta; struct rtmsg *rtm; ssize_t rc, rca; int rv = 0, s; size_t nb; s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (s == -1) psc_fatal("socket"); memset(&rq, 0, sizeof(rq)); rq.nmh.nlmsg_len = NLMSG_SPACE(sizeof(rq.rtm)); rq.nmh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; rq.nmh.nlmsg_type = RTM_GETROUTE; rq.rtm.rtm_family = sa->sa_family; rq.rtm.rtm_protocol = RTPROT_UNSPEC; rq.rtm.rtm_table = RT_TABLE_MAIN; rq.rtm.rtm_scope = RT_SCOPE_LINK; rta = (void *)((char *)&rq.rta + NLMSG_SPACE(sizeof(rq.rtm))); rta->rta_type = RTA_DST; rta->rta_len = RTA_LENGTH(sizeof(sin->sin_addr)); memcpy(RTA_DATA(rta), &sin->sin_addr, sizeof(sin->sin_addr)); errno = 0; rc = write(s, &rq, rq.nmh.nlmsg_len); if (rc != (ssize_t)rq.nmh.nlmsg_len) psc_fatal("routing socket length mismatch"); for (;;) { rc = read(s, &rq, sizeof(rq)); if (rc == -1) psc_fatal("routing socket read"); switch (rq.nmh.nlmsg_type) { case NLMSG_ERROR: { struct nlmsgerr *nlerr; nlerr = NLMSG_DATA(&rq.nlerr); psc_fatalx("netlink: %s", strerror(nlerr->error)); } case NLMSG_DONE: goto out; } nmh = &rq.nmh; nb = rc; for (; NLMSG_OK(nmh, nb); nmh = NLMSG_NEXT(nmh, nb)) { rtm = NLMSG_DATA(nmh); if (rtm->rtm_table != RT_TABLE_MAIN) continue; rta = RTM_RTA(rtm); rca = RTM_PAYLOAD(nmh); for (; RTA_OK(rta, rca); rta = RTA_NEXT(rta, rca)) { switch (rta->rta_type) { case RTA_GATEWAY: cmpaddr = sin->sin_addr.s_addr; if (zero == cmpaddr) { rv = 1; goto out; } break; case RTA_DST: cmpaddr = sin->sin_addr.s_addr; pfl_bitstr_copy(&cmpaddr, rtm->rtm_dst_len, &zero, 0, sizeof(zero) * NBBY - rtm->rtm_dst_len); if (cmpaddr == *(in_addr_t *) RTA_DATA(rta)) { rv = 1; goto out; } break; } } } } out: close(s); return (rv); }
/* * 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); }
int get_hw_addr(struct in_addr *gw_ip, char *iface, unsigned char *hw_mac) { char buf[8192]; struct ndmsg req; struct nlmsghdr *nlhdr; if (!gw_ip || !hw_mac) { return -1; } // Send RTM_GETNEIGH request req.ndm_family = AF_INET; req.ndm_ifindex = if_nametoindex(iface); req.ndm_state = NUD_REACHABLE; req.ndm_type = NDA_LLADDR; int sock = send_nl_req(RTM_GETNEIGH, 1, &req, sizeof(req)); // Read responses unsigned nl_len = read_nl_sock(sock, buf, sizeof(buf)); if (nl_len <= 0) { return -1; } // Parse responses nlhdr = (struct nlmsghdr *)buf; while (NLMSG_OK(nlhdr, nl_len)) { struct rtattr *rt_attr; struct rtmsg *rt_msg; int rt_len; unsigned char mac[6]; struct in_addr dst_ip; int correct_ip = 0; rt_msg = (struct rtmsg *) NLMSG_DATA(nlhdr); if ((rt_msg->rtm_family != AF_INET)) { return -1; } rt_attr = (struct rtattr *) RTM_RTA(rt_msg); rt_len = RTM_PAYLOAD(nlhdr); while (RTA_OK(rt_attr, rt_len)) { switch (rt_attr->rta_type) { case NDA_LLADDR: assert(RTA_PAYLOAD(rt_attr) == IFHWADDRLEN); memcpy(mac, RTA_DATA(rt_attr), IFHWADDRLEN); break; case NDA_DST: assert(RTA_PAYLOAD(rt_attr) == sizeof(dst_ip)); memcpy(&dst_ip, RTA_DATA(rt_attr), sizeof(dst_ip)); if (memcmp(&dst_ip, gw_ip, sizeof(dst_ip)) == 0) { correct_ip = 1; } break; } rt_attr = RTA_NEXT(rt_attr, rt_len); } if (correct_ip) { memcpy(hw_mac, mac, IFHWADDRLEN); return 0; } nlhdr = NLMSG_NEXT(nlhdr, nl_len); } return -1; }
static struct ether_addr *resolve_mac_from_cache(int ai_family, const void *l3addr) { uint8_t l3addr_tmp[16]; static struct ether_addr mac_tmp; struct ether_addr *mac_result = NULL; void *buf = NULL; size_t buflen; struct nlmsghdr *nh; ssize_t len; size_t l3_len, mlen; int socknl; int parsed; int finished = 0; switch (ai_family) { case AF_INET: l3_len = 4; break; case AF_INET6: l3_len = 16; break; default: l3_len = 0; } buflen = 8192; buf = malloc(buflen); if (!buf) goto err; socknl = resolve_mac_from_cache_open(ai_family); if (socknl < 0) goto err; while (!finished) { len = resolve_mac_from_cache_dump(socknl, &buf, &buflen); if (len < 0) goto err_sock; mlen = len; for (nh = buf; NLMSG_OK(nh, mlen); nh = NLMSG_NEXT(nh, mlen)) { if (nh->nlmsg_type == NLMSG_DONE) { finished = 1; break; } parsed = resolve_mac_from_cache_parse(NLMSG_DATA(nh), RTM_PAYLOAD(nh), &mac_tmp, l3addr_tmp, l3_len); if (parsed) { if (memcmp(&l3addr_tmp, l3addr, l3_len) == 0) { mac_result = &mac_tmp; finished = 1; break; } } } } err_sock: close(socknl); err: free(buf); return mac_result; }
/** * Scan netlink message in the buffer for IPv6 default route changes. */ static int rtmon_check_defaults(const void *buf, size_t len) { struct nlmsghdr *nh; int dfltdiff = 0; for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { struct rtmsg *rtm; struct rtattr *rta; int attrlen; int delta = 0; const void *gwbuf; size_t gwlen; int oif; DPRINTF2(("nlmsg type %d flags 0x%x\n", nh->nlmsg_seq, nh->nlmsg_type, nh->nlmsg_flags)); if (nh->nlmsg_type == NLMSG_DONE) { break; } if (nh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh); DPRINTF2(("> error %d\n", ne->error)); LWIP_UNUSED_ARG(ne); break; } if (nh->nlmsg_type < RTM_BASE || RTM_MAX <= nh->nlmsg_type) { /* shouldn't happen */ DPRINTF2(("> not an RTM message!\n")); continue; } rtm = (struct rtmsg *)NLMSG_DATA(nh); attrlen = RTM_PAYLOAD(nh); if (nh->nlmsg_type == RTM_NEWROUTE) { delta = +1; } else if (nh->nlmsg_type == RTM_DELROUTE) { delta = -1; } else { /* shouldn't happen */ continue; } /* * Is this an IPv6 default route in the main table? (Local * table always has ::/0 reject route, hence the last check). */ if (rtm->rtm_family == AF_INET6 /* should always be true */ && rtm->rtm_dst_len == 0 && rtm->rtm_table == RT_TABLE_MAIN) { dfltdiff += delta; } else { /* some other route change */ continue; } gwbuf = NULL; gwlen = 0; oif = -1; for (rta = RTM_RTA(rtm); RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) { if (rta->rta_type == RTA_GATEWAY) { gwbuf = RTA_DATA(rta); gwlen = RTA_PAYLOAD(rta); } else if (rta->rta_type == RTA_OIF) { /* assert RTA_PAYLOAD(rta) == 4 */ memcpy(&oif, RTA_DATA(rta), sizeof(oif)); } } /* XXX: TODO: note that { oif, gw } was added/removed */ LWIP_UNUSED_ARG(gwbuf); LWIP_UNUSED_ARG(gwlen); LWIP_UNUSED_ARG(oif); } return dfltdiff; }
static int netlink_receive(struct netlink_fd *fd, struct nlmsghdr *reply) { struct sockaddr_nl nladdr; struct iovec iov; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; int got_reply = FALSE, len; char buf[16*1024]; iov.iov_base = buf; while (!got_reply) { int status; struct nlmsghdr *h; iov.iov_len = sizeof(buf); status = recvmsg(fd->fd, &msg, MSG_DONTWAIT); if (status < 0) { if (errno == EINTR) continue; if (errno == EAGAIN) return reply == NULL; fprintf(stderr, "Netlink overrun\n"); continue; } if (status == 0) { fprintf(stderr, "Netlink returned EOF\n"); return FALSE; } h = (struct nlmsghdr *) buf; while (NLMSG_OK(h, status)) { if (reply != NULL && h->nlmsg_seq == reply->nlmsg_seq) { len = h->nlmsg_len; if (len > reply->nlmsg_len) { fprintf(stderr, "Netlink message " "truncated\n"); len = reply->nlmsg_len; } memcpy(reply, h, len); got_reply = TRUE; } else if (h->nlmsg_type != NLMSG_DONE) { fprintf(stderr, "Unknown NLmsg: 0x%08x, len %d\n", h->nlmsg_type, h->nlmsg_len); } h = NLMSG_NEXT(h, status); } } return TRUE; } static int netlink_send(struct netlink_fd *fd, struct nlmsghdr *req) { struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = (void*) req, .iov_len = req->nlmsg_len }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; int status; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req->nlmsg_seq = ++fd->seq; status = sendmsg(fd->fd, &msg, 0); if (status < 0) { fprintf(stderr, "Cannot talk to rtnetlink\n"); return FALSE; } return TRUE; } static int netlink_talk(struct nlmsghdr *req, size_t replysize, struct nlmsghdr *reply) { struct netlink_fd fd; int ret = FALSE; if (!netlink_open(&fd)) return FALSE; if (reply == NULL) req->nlmsg_flags |= NLM_F_ACK; if (!netlink_send(&fd, req)) goto out; if (reply != NULL) { reply->nlmsg_len = replysize; ret = netlink_receive(&fd, reply); } else { ret = TRUE; } out: netlink_close(&fd); return ret; } int netlink_route_get(struct sockaddr *dst, u_int16_t *mtu, char *ifname) { struct { struct nlmsghdr n; union { struct rtmsg r; struct ifinfomsg i; }; char buf[1024]; } req; struct rtmsg *r = NLMSG_DATA(&req.n); struct rtattr *rta[RTA_MAX+1]; struct rtattr *rtax[RTAX_MAX+1]; struct rtattr *ifla[IFLA_MAX+1]; int index; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_GETROUTE; req.r.rtm_family = dst->sa_family; netlink_add_rtaddr_l(&req.n, sizeof(req), RTA_DST, dst); req.r.rtm_dst_len = 32; if (!netlink_talk(&req.n, sizeof(req), &req.n)) return FALSE; netlink_parse_rtattr(rta, RTA_MAX, RTM_RTA(r), RTM_PAYLOAD(&req.n)); if (mtu != NULL) { if (rta[RTA_METRICS] == NULL) return FALSE; netlink_parse_rtattr(rtax, RTAX_MAX, RTA_DATA(rta[RTA_METRICS]), RTA_PAYLOAD(rta[RTA_METRICS])); if (rtax[RTAX_MTU] == NULL) return FALSE; *mtu = *(int*) RTA_DATA(rtax[RTAX_MTU]); } if (ifname != NULL) { if (rta[RTA_OIF] == NULL) return FALSE; index = *(int*) RTA_DATA(rta[RTA_OIF]); memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_GETLINK; req.i.ifi_index = index; if (!netlink_talk(&req.n, sizeof(req), &req.n)) return FALSE; netlink_parse_rtattr(ifla, IFLA_MAX, IFLA_RTA(r), IFLA_PAYLOAD(&req.n)); if (ifla[IFLA_IFNAME] == NULL) return FALSE; memcpy(ifname, RTA_DATA(ifla[IFLA_IFNAME]), RTA_PAYLOAD(ifla[IFLA_IFNAME])); } return TRUE; }
void process_nl_new_route (struct nlmsghdr *nlh) { struct rtmsg *rtm = NULL; struct rtattr *rt_attr = NULL; int rt_length = 0; lispd_iface_elt *iface = NULL; int iface_index = 0; char iface_name[IF_NAMESIZE]; lisp_addr_t gateway = {.afi=AF_UNSPEC}; lisp_addr_t dst = {.afi=AF_UNSPEC};; rtm = (struct rtmsg *) NLMSG_DATA (nlh); if ((rtm->rtm_family != AF_INET) && (rtm->rtm_family != AF_INET6)) { lispd_log_msg(LISP_LOG_DEBUG_2,"process_nl_new_route: Unknown adddress family"); return; } if (rtm->rtm_table != RT_TABLE_MAIN) { /* Not interested in routes/gateways affecting tables other the main routing table */ return; } rt_attr = (struct rtattr *)RTM_RTA(rtm); rt_length = RTM_PAYLOAD(nlh); for (; RTA_OK(rt_attr, rt_length); rt_attr = RTA_NEXT(rt_attr, rt_length)) { switch (rt_attr->rta_type) { case RTA_OIF: iface_index = *(int *)RTA_DATA(rt_attr); iface = get_interface_from_index(iface_index); if_indextoname(iface_index, iface_name); if (iface == NULL) { lispd_log_msg(LISP_LOG_DEBUG_2, "process_nl_new_route: the netlink message is not for any interface associated with RLOCs (%s)", iface_name); return; } break; case RTA_GATEWAY: gateway.afi = rtm->rtm_family; switch (gateway.afi) { case AF_INET: memcpy(&(gateway.address),(struct in_addr *)RTA_DATA(rt_attr), sizeof(struct in_addr)); break; case AF_INET6: memcpy(&(gateway.address),(struct in6_addr *)RTA_DATA(rt_attr), sizeof(struct in6_addr)); break; default: break; } break; case RTA_DST: // We check if the new route message contains a destintaion. If it is, then the gateway address is not a default route. Discard it dst.afi = rtm->rtm_family; switch (dst.afi) { case AF_INET: memcpy(&(dst.address),(struct in_addr *)RTA_DATA(rt_attr), sizeof(struct in_addr)); break; case AF_INET6: memcpy(&(dst.address),(struct in6_addr *)RTA_DATA(rt_attr), sizeof(struct in6_addr)); break; default: break; } break; default: break; } } if (gateway.afi != AF_UNSPEC && iface_index != 0 && dst.afi == AF_UNSPEC) { /* Check default afi*/ if (default_rloc_afi != AF_UNSPEC && default_rloc_afi != gateway.afi) { lispd_log_msg(LISP_LOG_DEBUG_1, "process_nl_new_route: Default RLOC afi defined (-a #): Skipped %s gateway in iface %s", (gateway.afi == AF_INET) ? "IPv4" : "IPv6",iface->iface_name); return; } /* Check if the addres is a global address*/ if (is_link_local_addr(gateway) == TRUE) { lispd_log_msg(LISP_LOG_DEBUG_2,"process_nl_new_route: the extractet address from the netlink " "messages is a local link address: %s discarded", get_char_from_lisp_addr_t(gateway)); return; } /* Process the new gateway */ lispd_log_msg(LISP_LOG_DEBUG_1, "process_nl_new_route: Process new gateway associated to the interface %s: %s", iface_name, get_char_from_lisp_addr_t(gateway)); process_new_gateway(gateway,iface); } }
static void netlink_neigh(struct ufpd_thread *thread, struct nlmsghdr *nlh) { struct ndmsg *neigh_entry; struct rtattr *route_attr; struct neigh_table *neigh; int route_attr_len; int ifindex; int family; uint8_t dst_addr[16] = {}; uint8_t dst_mac[ETH_ALEN] = {}; int i, port_index = -1; neigh_entry = (struct ndmsg *)NLMSG_DATA(nlh); family = neigh_entry->ndm_family; ifindex = neigh_entry->ndm_ifindex; port_index = -1; for(i = 0; i < thread->num_ports; i++){ if(ufp_tun_index(thread->plane, i) == ifindex){ port_index = i; break; } } if(port_index < 0) goto out; route_attr = (struct rtattr *)RTM_RTA(neigh_entry); route_attr_len = RTM_PAYLOAD(nlh); while(RTA_OK(route_attr, route_attr_len)){ switch(route_attr->rta_type){ case NDA_DST: memcpy(dst_addr, RTA_DATA(route_attr), RTA_PAYLOAD(route_attr)); break; case NDA_LLADDR: memcpy(dst_mac, RTA_DATA(route_attr), RTA_PAYLOAD(route_attr)); break; default: break; } route_attr = RTA_NEXT(route_attr, route_attr_len); } switch(family){ case AF_INET: neigh = thread->neigh_inet[port_index]; break; case AF_INET6: neigh = thread->neigh_inet6[port_index]; break; default: goto out; break; } switch(nlh->nlmsg_type){ case RTM_NEWNEIGH: neigh_add(neigh, family, dst_addr, dst_mac, thread->mpool); break; case RTM_DELNEIGH: neigh_delete(neigh, family, dst_addr); break; default: break; } out: return; }