nuttx-apps/nshlib/nsh_routecmds.c
2024-08-26 01:54:46 +08:00

772 lines
17 KiB
C

/****************************************************************************
* apps/nshlib/nsh_routecmds.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "netutils/netlib.h"
#include "nsh.h"
#include "nsh_console.h"
#if defined(CONFIG_NET) && defined(CONFIG_NET_ROUTE)
/****************************************************************************
* Private Functions
****************************************************************************/
static int slash_notation(FAR char *arg)
{
FAR char *sptr;
FAR char *ptr;
/* If an address contains a /, then the netmask is imply by the following
* numeric value.
*/
sptr = strchr(arg, '/');
if (sptr != NULL)
{
/* Make sure that everything following the slash is a decimal digit. */
ptr = sptr + 1;
while (isdigit(*ptr))
{
ptr++;
}
/* There should be nothing be digits after the slash and up to the
* NULL terminator.
*/
if (*ptr == '\0')
{
*sptr++ = '\0';
return atoi(sptr);
}
}
return ERROR;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: cmd_addroute
*
* nsh> addroute <target> [<netmask>] <router>
*
****************************************************************************/
#ifndef CONFIG_NSH_DISABLE_ADDROUTE
int cmd_addroute(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
{
union
{
#ifdef CONFIG_NET_IPv4
struct sockaddr_in ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct sockaddr_in6 ipv6;
#endif
struct sockaddr_storage ipx;
} target;
union
{
#ifdef CONFIG_NET_IPv4
struct sockaddr_in ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct sockaddr_in6 ipv6;
#endif
struct sockaddr_storage ipx;
} netmask;
union
{
#ifdef CONFIG_NET_IPv4
struct sockaddr_in ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct sockaddr_in6 ipv6;
#endif
struct sockaddr_storage ipx;
} router;
union
{
#ifdef CONFIG_NET_IPv4
struct in_addr ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct in6_addr ipv6;
#endif
} inaddr;
sa_family_t family;
int rtrndx;
int shift;
int sockfd;
int ret;
FAR char *sptr;
/* First, check if we are setting the default route */
if (strcmp(argv[1], "default") == 0)
{
/* We are expecting an ip and an interface name to follow. */
if (argc < 4)
{
nsh_error(vtbl, g_fmtargrequired, argv[0]);
goto errout;
}
else if (argc > 4)
{
nsh_error(vtbl, g_fmttoomanyargs, argv[0]);
goto errout;
}
/* Convert the target IP address string into its binary form */
#ifdef CONFIG_NET_IPv4
family = PF_INET;
ret = inet_pton(AF_INET, argv[2], &inaddr.ipv4);
if (ret != 1)
#endif
{
#ifdef CONFIG_NET_IPv6
family = PF_INET6;
ret = inet_pton(AF_INET6, argv[2], &inaddr.ipv6);
if (ret != 1)
#endif
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout;
}
}
#ifdef CONFIG_NET_IPv4
if (family == PF_INET)
{
ret = netlib_set_dripv4addr(argv[3], &inaddr.ipv4);
if (ret != 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0]);
goto errout;
}
}
#endif
#ifdef CONFIG_NET_IPv6
if (family == PF_INET6)
{
ret = netlib_set_dripv6addr(argv[3], &inaddr.ipv6);
if (ret != 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0]);
goto errout;
}
}
#endif
return OK;
}
/* Check if slash notation is used with the target address */
shift = slash_notation(argv[1]);
/* There will be 2 or 3 arguments, depending on if slash notation is
* used.
*/
if (shift > 0 && argc != 3)
{
nsh_error(vtbl, g_fmttoomanyargs, argv[0]);
goto errout;
}
else if (shift < 0 && argc != 4)
{
nsh_error(vtbl, g_fmtargrequired, argv[0]);
goto errout;
}
/* We need to remove the slash notation before passing it to inet_pton */
if (shift > 0)
{
sptr = strchr(argv[1], '/');
if (sptr != NULL)
{
*sptr = '\0';
}
}
/* Convert the target IP address string into its binary form */
#ifdef CONFIG_NET_IPv4
family = PF_INET;
ret = inet_pton(AF_INET, argv[1], &inaddr.ipv4);
if (ret != 1)
#endif
{
#ifdef CONFIG_NET_IPv6
family = PF_INET6;
ret = inet_pton(AF_INET6, argv[1], &inaddr.ipv6);
if (ret != 1)
#endif
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout;
}
}
/* We need to have a socket (any socket) in order to perform the ioctl */
sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL);
if (sockfd < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "socket", NSH_ERRNO);
goto errout;
}
/* Format the target sockaddr instance */
memset(&target, 0, sizeof(target));
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
target.ipv4.sin_family = AF_INET;
target.ipv4.sin_addr = inaddr.ipv4;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
target.ipv6.sin6_family = AF_INET6;
memcpy(&target.ipv6.sin6_addr, &inaddr.ipv6,
sizeof(struct in6_addr));
}
#endif
/* Convert the netmask IP address string into its binary form */
if (shift >= 0)
{
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
/* /0 -> 0x00000000
* /8 -> 0xff000000
* /24 -> 0xffffff00
* /32 -> 0xffffffff
*/
if (shift > 32)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
inaddr.ipv4.s_addr = htonl(0xffffffff << (32 - shift));
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
int i;
/* /0 -> 0000:0000:0000:0000:0000:0000:0000:0000
* /16 -> ffff:0000:0000:0000:0000:0000:0000:0000
* /32 -> ffff:ffff:0000:0000:0000:0000:0000:0000
* ...
* /128 -> ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
*/
memset(&inaddr.ipv6, 0, sizeof(inaddr.ipv6));
for (i = 0; i < 8 && shift >= 16; i++, shift -= 16)
{
inaddr.ipv6.s6_addr16[i] = 0xffff;
}
if (shift > 16 || (shift > 0 && i >= 8))
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
/* /0 -> 0x0000
* /1 -> 0x8000
* /2 -> 0xc000
* ...
* /16 -> 0xffff
*/
if (shift > 0)
{
inaddr.ipv6.s6_addr16[i] = htons(0xffff << (16 - shift));
}
}
#endif
rtrndx = 2;
}
else
{
/* Slash notation not used.. mask should follow the target address */
ret = inet_pton(family, argv[2], &inaddr);
if (ret != 1)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
rtrndx = 3;
}
/* Format the netmask sockaddr instance */
memset(&netmask, 0, sizeof(netmask));
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
netmask.ipv4.sin_family = AF_INET;
netmask.ipv4.sin_addr = inaddr.ipv4;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
netmask.ipv6.sin6_family = AF_INET6;
memcpy(&netmask.ipv6.sin6_addr, &inaddr.ipv6,
sizeof(struct in6_addr));
}
#endif
/* Convert the router IP address string into its binary form */
ret = inet_pton(family, argv[rtrndx], &inaddr);
if (ret != 1)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
/* Format the router sockaddr instance */
memset(&router, 0, sizeof(router));
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
router.ipv4.sin_family = AF_INET;
router.ipv4.sin_addr = inaddr.ipv4;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
router.ipv6.sin6_family = AF_INET6;
memcpy(&router.ipv6.sin6_addr, &inaddr.ipv6,
sizeof(struct in6_addr));
}
#endif
/* Then add the route */
ret = addroute(sockfd, &target.ipx, &netmask.ipx,
&router.ipx, sizeof(router));
if (ret < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "addroute", NSH_ERRNO);
goto errout_with_sockfd;
}
close(sockfd);
return OK;
errout_with_sockfd:
close(sockfd);
errout:
return ERROR;
}
#endif /* !CONFIG_NSH_DISABLE_ADDROUTE */
/****************************************************************************
* Name: cmd_delroute
*
* nsh> delroute <target> [<netmask>]
*
****************************************************************************/
#ifndef CONFIG_NSH_DISABLE_DELROUTE
int cmd_delroute(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
{
union
{
#ifdef CONFIG_NET_IPv4
struct sockaddr_in ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct sockaddr_in6 ipv6;
#endif
struct sockaddr_storage ipx;
} target;
union
{
#ifdef CONFIG_NET_IPv4
struct sockaddr_in ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct sockaddr_in6 ipv6;
#endif
struct sockaddr_storage ipx;
} netmask;
union
{
#ifdef CONFIG_NET_IPv4
struct in_addr ipv4;
#endif
#ifdef CONFIG_NET_IPv6
struct in6_addr ipv6;
#endif
} inaddr;
sa_family_t family;
int shift;
int sockfd;
int ret;
/* Check if slash notation is used with the target address */
shift = slash_notation(argv[1]);
/* There will be 1 or 2 arguments, depending on if slash notation is
* used.
*/
if (shift > 0 && argc != 2)
{
nsh_error(vtbl, g_fmttoomanyargs, argv[0]);
goto errout;
}
else if (shift < 0 && argc != 3)
{
nsh_error(vtbl, g_fmtargrequired, argv[0]);
goto errout;
}
/* Convert the target IP address string into its binary form */
#ifdef CONFIG_NET_IPv4
family = PF_INET;
ret = inet_pton(AF_INET, argv[1], &inaddr.ipv4);
if (ret != 1)
#endif
{
#ifdef CONFIG_NET_IPv6
family = PF_INET6;
ret = inet_pton(AF_INET6, argv[1], &inaddr.ipv6);
if (ret != 1)
#endif
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout;
}
}
/* We need to have a socket (any socket) in order to perform the ioctl */
sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL);
if (sockfd < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "socket", NSH_ERRNO);
goto errout;
}
/* Format the target sockaddr instance */
memset(&target, 0, sizeof(target));
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
target.ipv4.sin_family = AF_INET;
target.ipv4.sin_addr = inaddr.ipv4;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
target.ipv6.sin6_family = AF_INET6;
memcpy(&target.ipv6.sin6_addr, &inaddr.ipv6,
sizeof(struct in6_addr));
}
#endif
/* Convert the netmask IP address string into its binary form */
if (shift >= 0)
{
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
/* /0 -> 0x00000000
* /8 -> 0xff000000
* /24 -> 0xffffff00
* /32 -> 0xffffffff
*/
if (shift > 32)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
inaddr.ipv4.s_addr = htonl(0xffffffff << (32 - shift));
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
int i;
/* /0 -> 0000:0000:0000:0000:0000:0000:0000:0000
* /16 -> ffff:0000:0000:0000:0000:0000:0000:0000
* /32 -> ffff:ffff:0000:0000:0000:0000:0000:0000
* ...
* /128 -> ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
*/
memset(&inaddr.ipv6, 0, sizeof(inaddr.ipv6));
for (i = 0; i < 8 && shift >= 16; i++, shift -= 16)
{
inaddr.ipv6.s6_addr16[i] = 0xffff;
}
if (shift > 16 || (shift > 0 && i >= 8))
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
/* /0 -> 0x0000
* /1 -> 0x8000
* /2 -> 0xc000
* ...
* /16 -> 0xffff
*/
if (shift > 0)
{
inaddr.ipv6.s6_addr16[i] = htons(0xffff << (16 - shift));
}
}
#endif
}
else
{
/* Slash notation not used.. mask should follow the target address */
ret = inet_pton(family, argv[2], &inaddr);
if (ret != 1)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
goto errout_with_sockfd;
}
}
/* Format the netmask sockaddr instance */
memset(&netmask, 0, sizeof(netmask));
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (family == PF_INET)
#endif
{
netmask.ipv4.sin_family = AF_INET;
netmask.ipv4.sin_addr = inaddr.ipv4;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
netmask.ipv6.sin6_family = AF_INET6;
memcpy(&netmask.ipv6.sin6_addr, &inaddr.ipv6,
sizeof(struct in6_addr));
}
#endif
/* Then delete the route */
ret = delroute(sockfd, &target.ipx, &netmask.ipx, sizeof(target));
if (ret < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "delroute", NSH_ERRNO);
goto errout_with_sockfd;
}
close(sockfd);
return OK;
errout_with_sockfd:
close(sockfd);
errout:
return ERROR;
}
#endif /* !CONFIG_NSH_DISABLE_DELROUTE */
/****************************************************************************
* Name: cmd_route
*
* nsh> route <ipv4|ipv6>
*
****************************************************************************/
#ifndef CONFIG_NSH_DISABLE_ROUTE
int cmd_route(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
{
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
bool ipv6 = false;
#endif
/* If both IPv4 and IPv6 and defined then there will be exactly one
* argument. Otherwise no arguments are required, but an addition
* argument is accepted.
*/
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
if (strcmp(argv[1], "ipv4") == 0)
{
ipv6 = false;
}
else if (strcmp(argv[1], "ipv6") == 0)
{
ipv6 = true;
}
else
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
#elif defined(CONFIG_NET_IPv4)
if (argc == 2 && strcmp(argv[1], "ipv4") != 0)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
#else
if (argc == 2 && strcmp(argv[1], "ipv6") != 0)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
#endif
/* Then just cat the procfs file containing the routing table info */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (!ipv6)
#endif
{
return nsh_catfile(vtbl, argv[0],
CONFIG_NSH_PROC_MOUNTPOINT "/net/route/ipv4");
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
return nsh_catfile(vtbl, argv[0],
CONFIG_NSH_PROC_MOUNTPOINT "/net/route/ipv6");
}
#endif
return ERROR; /* Shouldn't get here */
}
#endif
#endif /* CONFIG_NET && CONFIG_NET_ROUTE */