/* wrnad_route.c - Wind River network acceleration daemon (NAD) */ /* * Copyright (c) 2010 Wind River Systems, Inc. * * The right to copy, distribute or otherwise make use of this software * may be licensed only pursuant to the terms of an applicable Wind River * license agreement. */ /* includes */ #include #include #include #include #include #include "wrnad.h" /* defines */ /* typedefs */ /* imports */ /* globals */ /* locals */ /******************************************************************************* * * wrnad_rtnl_route_flagsstr - find rtnetlink route flags in string format * * This routine returns the rtnetlink route flags as a string * * RETURNS: string representation of rtnetlink route flags. * * ERRNO: N/A */ static const char * wrnad_rtnl_route_flagsstr(int flags) { static char flagsstr[256]; int i; flagsstr[0] = '<'; i = 1; if (flags == 0) i += sprintf(&flagsstr[i], "%s", "0,"); if (flags & RTM_F_NOTIFY) i += sprintf(&flagsstr[i], "%s", "NOTIFY,"); if (flags & RTM_F_CLONED) i += sprintf(&flagsstr[i], "%s", "CLONED,"); if (flags & RTM_F_EQUALIZE) i += sprintf(&flagsstr[i], "%s", "EQUALIZE,"); if (flags & RTM_F_PREFIX) i += sprintf(&flagsstr[i], "%s", "PREFIX,"); flagsstr[i-1] = '>'; /* remove last comma */ return flagsstr; } /******************************************************************************* * * wrnad_rtnl_route_tablestr - find rtnetlink route table in string format * * This routine returns the rtnetlink route table as a string * * RETURNS: string representation of rtnetlink route table. * * ERRNO: N/A */ static const char * wrnad_rtnl_route_tablestr(int table) { static char tablestr[32]; switch (table) { case RT_TABLE_UNSPEC: return "UNSPEC"; #if 0 case RT_TABLE_COMPAT: return "COMPAT"; #endif case RT_TABLE_DEFAULT: return "DEFAULT"; case RT_TABLE_MAIN: return "MAIN"; case RT_TABLE_LOCAL: return "LOCAL"; default: sprintf(tablestr, "%d", table); return tablestr; } } /******************************************************************************* * * wrnad_rtnl_route_protocolstr - find rtnetlink route protocol in string format * * This routine returns the rtnetlink route protocol as a string * * RETURNS: string representation of rtnetlink route protocol. * * ERRNO: N/A */ static const char * wrnad_rtnl_route_protocolstr(int protocol) { static char protocolstr[32]; switch (protocol) { case RTPROT_UNSPEC: return "UNSPEC"; case RTPROT_REDIRECT: return "REDIRECT"; case RTPROT_KERNEL: return "KERNEL"; case RTPROT_BOOT: return "BOOT"; case RTPROT_STATIC: return "STATIC"; default: sprintf(protocolstr, "%d", protocol); return protocolstr; } } /******************************************************************************* * * wrnad_rtnl_route_scopestr - find rtnetlink route scope in string format * * This routine returns the rtnetlink route scope as a string * * RETURNS: string representation of rtnetlink route scope. * * ERRNO: N/A */ static const char * wrnad_rtnl_route_scopestr(int scope) { static char scopestr[32]; switch (scope) { case RT_SCOPE_UNIVERSE: return "UNIVERSE"; case RT_SCOPE_SITE: return "SITE"; case RT_SCOPE_LINK: return "LINK"; case RT_SCOPE_HOST: return "HOST"; case RT_SCOPE_NOWHERE: return "NOWHERE"; default: sprintf(scopestr, "%d", scope); return scopestr; } } /******************************************************************************* * * wrnad_rtnl_route_typestr - find rtnetlink route type in string format * * This routine returns the rtnetlink route type as a string * * RETURNS: string representation of rtnetlink route type. * * ERRNO: N/A */ static const char * wrnad_rtnl_route_typestr(int type) { static char typestr[32]; switch (type) { case RTN_UNSPEC: return "UNSPEC"; case RTN_UNICAST: return "UNICAST"; case RTN_LOCAL: return "LOCAL"; case RTN_BROADCAST: return "BROADCAST"; case RTN_ANYCAST: return "ANYCAST"; case RTN_MULTICAST: return "MULTICAST"; case RTN_BLACKHOLE: return "BLACKHOLE"; case RTN_UNREACHABLE: return "UNREACHABLE"; case RTN_PROHIBIT: return "PROHIBIT"; case RTN_THROW: return "THROW"; case RTN_NAT: return "NAT"; case RTN_XRESOLVE: return "XRESOLVE"; default: sprintf(typestr, "%d", type); return typestr; } } /******************************************************************************* * * wrnad_rtnl_route_attrstr - find rtnetlink route attribute in string format * * This routine returns the rtnetlink route attribute as a string * * RETURNS: string representation of rtnetlink route attribute. * * ERRNO: N/A */ static const char * wrnad_rtnl_route_attrstr(int attr) { static char attrstr[32]; switch (attr) { case RTA_UNSPEC: return "RTA_UNSPEC"; case RTA_DST: return "RTA_DST"; case RTA_SRC: return "RTA_SRC"; case RTA_IIF: return "RTA_IIF"; case RTA_OIF: return "RTA_OIF"; case RTA_GATEWAY: return "RTA_GATEWAY"; case RTA_PRIORITY: return "RTA_PRIORITY"; case RTA_PREFSRC: return "RTA_PREFSRC"; case RTA_METRICS: return "RTA_METRICS"; case RTA_MULTIPATH: return "RTA_MULTIPATH"; case RTA_PROTOINFO: return "RTA_PROTOINFO"; case RTA_FLOW: return "RTA_FLOW"; case RTA_CACHEINFO: return "RTA_CACHEINFO"; case RTA_SESSION: return "RTA_SESSION"; #ifdef RTA_MP_ALGO case RTA_MP_ALGO: return "RTA_MP_ALGO"; #endif #ifdef RTA_TABLE case RTA_TABLE: return "RTA_TABLE"; #endif default: sprintf(attrstr, "%d", attr); return attrstr; } } /******************************************************************************* * * wrnad_rtnl_route_scan - scan rtnetlink socket for route messages * * This routine scans route messages received on a rtnetlink socket * * RETURNS: 0 if exited normally, else -1. * * ERRNO: N/A */ static int wrnad_rtnl_route_scan(struct rtmsg *rtm, int len, int *ifindex, uint32_t **ppgwaddr, uint32_t **ppdstaddr) { struct rtattr *rta; char buf[64]; rta = (struct rtattr *)((char *)rtm + NLMSG_ALIGN(sizeof(struct rtmsg))); while (RTA_OK(rta, len)) { wrnad_log(LOG_DEBUG, "Rtnetlink ROUTE rtattr: rta_len=%d, rta_type=%s.", rta->rta_len, wrnad_rtnl_route_attrstr(rta->rta_type)); #ifdef RTA_TABLE if (rta->rta_type == RTA_TABLE) { } else #endif if (rta->rta_type == RTA_DST) { if (inet_ntop(rtm->rtm_family, RTA_DATA(rta), buf, sizeof(buf)) != NULL) { wrnad_log(LOG_DEBUG, "Rtnetlink ROUTE attribute RTA_DST: %s", buf); } *ppdstaddr = RTA_DATA(rta); } else if (rta->rta_type == RTA_GATEWAY) { if (inet_ntop(rtm->rtm_family, RTA_DATA(rta), buf, sizeof(buf)) != NULL) { wrnad_log(LOG_DEBUG, "Rtnetlink ROUTE attribute RTA_GATEWAY: %s", buf); } *ppgwaddr = RTA_DATA(rta); } else if (rta->rta_type == RTA_OIF) { wrnad_log(LOG_DEBUG, "Rtnetlink ROUTE attribute RTA_OIF: %d", *(int *)RTA_DATA(rta)); /* Save interface index */ *ifindex = *(int *)RTA_DATA(rta); } else if (rta->rta_type == RTA_PREFSRC) { if (inet_ntop(rtm->rtm_family, RTA_DATA(rta), buf, sizeof(buf)) != NULL) { wrnad_log(LOG_DEBUG, "Rtnetlink ROUTE attribute RTA_PREFSRC: %s", buf); } } rta = RTA_NEXT(rta, len); } if (len) { wrnad_log(LOG_WARNING, "Rtnetlink ROUTE attribute malformed! len=%d rta_len=%d", len, rta->rta_len); return -1; } wrnad_log(LOG_DEBUG, "Rtnetlink ROUTE message scanned ok."); return 0; } /******************************************************************************* * * wrnad_rtnl_route_handler - rtnetlink handler for route messages * * This routine handles rtnetlink route messages. * * RETURNS: -1 = failed * 0 = skip * 1 = action * * ERRNO: N/A */ int wrnad_rtnl_route_handler(struct nlmsghdr *nlh) { struct rtmsg *rtm; int len, ret; int ifindex = 0; Wrnad_proxy_interface *proxy; uint32_t *pgwaddr = NULL; uint32_t *pdstaddr = NULL; rtm = NLMSG_DATA(nlh); len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); if (len < 0) { wrnad_log(LOG_WARNING, "Rtnetlink message type %s has wrong nlmsg length %d!", wrnad_rtnl_typestr(nlh->nlmsg_type), nlh->nlmsg_len); return -1; } wrnad_log(LOG_WARNING, "Rtnetlink ROUTE operation header: family=%s, dst_len=%d, src_len=%d, " "tos=%d, table=%s, protocol=%s, scope=%s, type=%s, flags=%s", wrnad_rtnl_familystr(rtm->rtm_family), rtm->rtm_dst_len, rtm->rtm_src_len, rtm->rtm_tos, wrnad_rtnl_route_tablestr(rtm->rtm_table), wrnad_rtnl_route_protocolstr(rtm->rtm_protocol), wrnad_rtnl_route_scopestr(rtm->rtm_scope), wrnad_rtnl_route_typestr(rtm->rtm_type), wrnad_rtnl_route_flagsstr(rtm->rtm_flags)); ret = wrnad_rtnl_route_scan(rtm, len, &ifindex, &pgwaddr, &pdstaddr); if (ret == 0) { proxy = wrnad_rtnl_check_proxy_interface(ifindex); if (proxy == NULL) { wrnad_log(LOG_DEBUG, "Rtnetlink message type %s skipped on interface %d", wrnad_rtnl_typestr(nlh->nlmsg_type), ifindex); return 0; } if (rtm->rtm_table != RT_TABLE_MAIN) { wrnad_log(LOG_DEBUG, "Rtnetlink message type %s skipped on table %d", wrnad_rtnl_typestr(nlh->nlmsg_type), rtm->rtm_table); return 0; } else if (rtm->rtm_type == RTN_LOCAL) { wrnad_log(LOG_WARNING, "Rtnetlink message type %s skipped on table %d due to RTN_LOCAL", wrnad_rtnl_typestr(nlh->nlmsg_type), rtm->rtm_table); return 0; } #ifdef WRNAD_IDENTIFY_ROUTERS if (pdstaddr != NULL && pgwaddr != NULL) { if (rtm->rtm_family == AF_INET) { if (rtm->rtm_dst_len == 32 && pdstaddr[0] == pgwaddr[0]) { #ifdef WRNAD_SELF_GATEWAY_SKIP return 0; #endif } else hash_insert_ip4addr(&proxy->v4_routers, pgwaddr); } if (rtm->rtm_family == AF_INET6) { if (rtm->rtm_dst_len == 128 && pdstaddr[0] == pgwaddr[0] && pdstaddr[1] == pgwaddr[1] && pdstaddr[2] == pgwaddr[2] && pdstaddr[3] == pgwaddr[3]) { #ifdef WRNAD_SELF_GATEWAY_SKIP /* * A host route in which the destination and gateway * addresses are the same is not a route that tells * us anything we need to know on the NAP 1.1 * NAE side (which only has a forwarding cache). */ return 0; #endif } else hash_insert_ip6addr(&proxy->v6_routers, pgwaddr); } } #endif /* Accept message */ wrnad_log(LOG_INFO, "Rtnetlink message type %s accepted on interface %s", wrnad_rtnl_typestr(nlh->nlmsg_type), proxy->ifname); /* AF_INET6 has a different value in VxWorks */ rtm->rtm_family = wrnad_af_xlat(rtm->rtm_family); /* Change into request */ nlh->nlmsg_flags = NLM_F_REQUEST; if (nlh->nlmsg_type == RTM_NEWROUTE) nlh->nlmsg_flags |= NLM_F_CREATE; return 1; } return ret; }