Files
2026-07-30 11:19:44 +05:30

480 lines
12 KiB
C

/* 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 <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#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;
}