| /**************************************************************************** |
| * apps/fsutils/ipcfg/ipcfg_text.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * 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 <stdio.h> |
| #include <stdbool.h> |
| #include <unistd.h> |
| #include <ctype.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <arpa/inet.h> |
| |
| #include "fsutils/ipcfg.h" |
| #include "ipcfg.h" |
| |
| #ifndef CONFIG_IPCFG_BINARY |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv4) |
| static const char *g_ipv4proto_name[] = |
| { |
| "none", /* IPv4PROTO_NONE */ |
| "static", /* IPv4PROTO_STATIC */ |
| "dhcp", /* IPv4PROTO_DHCP */ |
| "fallback" /* IPv4PROTO_FALLBACK */ |
| }; |
| #endif |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv6) |
| static const char *g_ipv6proto_name[] = |
| { |
| "none", /* IPv6PROTO_NONE */ |
| "static", /* IPv6PROTO_STATIC */ |
| "autoconf", /* IPv6PROTO_AUTOCONF */ |
| "fallback" /* IPv6PROTO_FALLBACK */ |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ipcfg_trim |
| * |
| * Description: |
| * Skip over any whitespace. |
| * |
| * Input Parameters: |
| * line - Pointer to line buffer |
| * index - Current index into the line buffer |
| * |
| * Returned Value: |
| * New value of index. |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_IPCFG_BINARY |
| static int ipcfg_trim(FAR char *line, int index) |
| { |
| int ret; |
| while (line[index] != '\0' && isspace(line[index])) |
| { |
| index++; |
| } |
| |
| ret = index; |
| while (line[index] != '\0') |
| { |
| if (!isprint(line[index])) |
| { |
| line[index] = '\0'; |
| break; |
| } |
| |
| index++; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_put_ipv4addr |
| * |
| * Description: |
| * Write a <variable>=<address> value pair to the stream. |
| * |
| * Input Parameters: |
| * stream - The output stream |
| * variable - The variable namespace |
| * address - The IP address to write |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv4) |
| static int ipcfg_put_ipv4addr(FAR FILE *stream, FAR const char *variable, |
| in_addr_t address) |
| { |
| if (address != 0) |
| { |
| struct in_addr saddr = |
| { |
| address |
| }; |
| |
| char converted[INET_ADDRSTRLEN]; |
| |
| /* Convert the address to ASCII text */ |
| |
| if (inet_ntop(AF_INET, &saddr, converted, INET_ADDRSTRLEN) == NULL) |
| { |
| int ret = -errno; |
| ferr("ERROR: inet_ntop() failed: %d\n", ret); |
| return ret; |
| } |
| |
| fprintf(stream, "%s=%s\n", variable, converted); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_check_ipv6addr |
| * |
| * Description: |
| * Check for a valid IPv6 address, i.e., not all zeroes. |
| * |
| * Input Parameters: |
| * address - A pointer to the address to check. |
| * |
| * Returned value: |
| * Zero (OK) is returned if the address is non-zero. -ENXIO is returned if |
| * the address is zero. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_NET_IPv6 |
| static int ipcfg_check_ipv6addr(FAR const struct in6_addr *address) |
| { |
| int i; |
| |
| for (i = 0; i < 4; i++) |
| { |
| if (address->s6_addr32[i] != 0) |
| { |
| return OK; |
| } |
| } |
| |
| return -ENXIO; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_put_ipv6addr |
| * |
| * Description: |
| * Write a <variable>=<address> value pair to the stream. |
| * |
| * Input Parameters: |
| * stream - The output stream |
| * variable - The variable namespace |
| * address - The IP address to write |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv6) |
| static int ipcfg_put_ipv6addr(FAR FILE *stream, FAR const char *variable, |
| FAR const struct in6_addr *address) |
| { |
| /* If the address is all zero, then omit it */ |
| |
| if (ipcfg_check_ipv6addr(address) == OK) |
| { |
| char converted[INET6_ADDRSTRLEN]; |
| |
| /* Convert the address to ASCII text */ |
| |
| if (inet_ntop(AF_INET6, address, converted, INET6_ADDRSTRLEN) == NULL) |
| { |
| int ret = -errno; |
| ferr("ERROR: inet_ntop() failed: %d\n", ret); |
| return ret; |
| } |
| |
| fprintf(stream, "%s=%s\n", variable, converted); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_write_ipv4 |
| * |
| * Description: |
| * Write the IPv4 configuration to a human-readable, text IP Configuration |
| * file. |
| * |
| * Input Parameters: |
| * stream - Stream of the open file to write to |
| * ipv4cfg - The IPv4 configration to write |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv4) |
| static int ipcfg_write_ipv4(FAR FILE *stream, |
| FAR const struct ipv4cfg_s *ipv4cfg) |
| { |
| /* Format and write the file */ |
| |
| if ((unsigned)ipv4cfg->proto > MAX_IPv4PROTO) |
| { |
| ferr("ERROR: Unrecognized IPv4PROTO value: %d\n", ipv4cfg->proto); |
| return -EINVAL; |
| } |
| |
| fprintf(stream, "IPv4PROTO=%s\n", g_ipv4proto_name[ipv4cfg->proto]); |
| |
| ipcfg_put_ipv4addr(stream, "IPv4IPADDR", ipv4cfg->ipaddr); |
| ipcfg_put_ipv4addr(stream, "IPv4NETMASK", ipv4cfg->netmask); |
| ipcfg_put_ipv4addr(stream, "IPv4ROUTER", ipv4cfg->router); |
| ipcfg_put_ipv4addr(stream, "IPv4DNS", ipv4cfg->dnsaddr); |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_write_ipv6 |
| * |
| * Description: |
| * Write the IPv6 configuration to a human-readable, text IP Configuration |
| * file. |
| * |
| * Input Parameters: |
| * stream - Stream of the open file to write to |
| * ipv6cfg - The IPv6 configration to write |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv6) |
| static int ipcfg_write_ipv6(FAR FILE *stream, |
| FAR const struct ipv6cfg_s *ipv6cfg) |
| { |
| /* Format and write the file */ |
| |
| if ((unsigned)ipv6cfg->proto > MAX_IPv6PROTO) |
| { |
| ferr("ERROR: Unrecognized IPv6PROTO value: %d\n", ipv6cfg->proto); |
| return -EINVAL; |
| } |
| |
| fprintf(stream, "IPv6PROTO=%s\n", g_ipv6proto_name[ipv6cfg->proto]); |
| |
| ipcfg_put_ipv6addr(stream, "IPv6IPADDR", &ipv6cfg->ipaddr); |
| ipcfg_put_ipv6addr(stream, "IPv6NETMASK", &ipv6cfg->netmask); |
| ipcfg_put_ipv6addr(stream, "IPv6ROUTER", &ipv6cfg->router); |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ipcfg_read_text_ipv4 |
| * |
| * Description: |
| * Read IPv4 configuration from a human-readable, text IP Configuration |
| * file. |
| * |
| * Input Parameters: |
| * path - The full path to the IP configuration file |
| * netdev - Network device name string |
| * ipv4cfg - Location to read IPv4 configration to |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_NET_IPv4 |
| int ipcfg_read_text_ipv4(FAR const char *path, FAR const char *netdev, |
| FAR struct ipv4cfg_s *ipv4cfg) |
| { |
| char line[MAX_LINESIZE]; |
| FAR FILE *stream; |
| int index; |
| int ret = -ENOENT; |
| |
| DEBUGASSERT(path != NULL && netdev != NULL && ipv4cfg != NULL); |
| |
| /* Open the file for reading */ |
| |
| stream = fopen(path, "r"); |
| if (stream == NULL) |
| { |
| ret = -errno; |
| if (ret != -ENOENT) |
| { |
| ferr("ERROR: Failed to open %s: %d\n", path, ret); |
| } |
| |
| return ret; |
| } |
| |
| /* Process each line in the file */ |
| |
| memset(ipv4cfg, 0, sizeof(FAR struct ipv4cfg_s)); |
| |
| while (fgets(line, MAX_LINESIZE, stream) != NULL) |
| { |
| /* Skip any leading whitespace */ |
| |
| index = ipcfg_trim(line, 0); |
| |
| /* Check for a blank line or a comment */ |
| |
| if (line[index] != '\0' && line[index] != '#') |
| { |
| FAR char *variable = &line[index]; |
| FAR char *value; |
| |
| /* Expect <variable>=<value> pair */ |
| |
| value = strchr(variable, '='); |
| if (value == NULL) |
| { |
| ferr("ERROR: Skipping malformed line in file: %s\n", line); |
| continue; |
| } |
| |
| /* NUL-terminate the variable string */ |
| |
| *value++ = '\0'; |
| |
| /* Process the variable assignment */ |
| |
| if (strcmp(variable, "DEVICE") == 0) |
| { |
| /* Just assure that it matches the filename */ |
| |
| if (strcmp(value, netdev) != 0) |
| { |
| ferr("ERROR: Bad device in file: %s=%s\n", |
| variable, value); |
| } |
| } |
| else if (strcmp(variable, "IPv4PROTO") == 0) |
| { |
| if (strcmp(value, "none") == 0) |
| { |
| ipv4cfg->proto = IPv4PROTO_NONE; |
| } |
| else if (strcmp(value, "static") == 0) |
| { |
| ipv4cfg->proto = IPv4PROTO_STATIC; |
| } |
| else if (strcmp(value, "dhcp") == 0) |
| { |
| ipv4cfg->proto = IPv4PROTO_DHCP; |
| } |
| else if (strcmp(value, "fallback") == 0) |
| { |
| ipv4cfg->proto = IPv4PROTO_FALLBACK; |
| } |
| else |
| { |
| ferr("ERROR: Unrecognized IPv4PROTO: %s=%s\n", |
| variable, value); |
| } |
| |
| /* Assume IPv4 settings are present if the IPv4PROTO |
| * setting is encountered. |
| */ |
| |
| ret = OK; |
| } |
| else if (strcmp(variable, "IPv4IPADDR") == 0) |
| { |
| ipv4cfg->ipaddr = inet_addr(value); |
| } |
| else if (strcmp(variable, "IPv4NETMASK") == 0) |
| { |
| ipv4cfg->netmask = inet_addr(value); |
| } |
| else if (strcmp(variable, "IPv4ROUTER") == 0) |
| { |
| ipv4cfg->router = inet_addr(value); |
| } |
| else if (strcmp(variable, "IPv4DNS") == 0) |
| { |
| ipv4cfg->dnsaddr = inet_addr(value); |
| } |
| |
| /* Anything other than some IPv6 settings would be an error. |
| * This is a sloppy check because it does not detect invalid |
| * names variables that begin with "IPv6". |
| */ |
| |
| else if (strncmp(variable, "IPv6", 4) != 0) |
| { |
| ferr("ERROR: Unrecognized variable: %s=%s\n", variable, value); |
| } |
| } |
| } |
| |
| /* Close the file and return */ |
| |
| fclose(stream); |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_read_text_ipv6 |
| * |
| * Description: |
| * Read IPv6 configuration from a human-readable, text IP Configuration |
| * file. |
| * |
| * Input Parameters: |
| * path - The full path to the IP configuration file |
| * netdev - Network device name string |
| * ipv6cfg - Location to read IPv6 configration to |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_NET_IPv6 |
| int ipcfg_read_text_ipv6(FAR const char *path, FAR const char *netdev, |
| FAR struct ipv6cfg_s *ipv6cfg) |
| { |
| char line[MAX_LINESIZE]; |
| FAR FILE *stream; |
| bool found = false; |
| int index; |
| int ret; |
| |
| DEBUGASSERT(path != NULL && netdev != NULL && ipv6cfg != NULL); |
| |
| /* Open the file for reading */ |
| |
| stream = fopen(path, "r"); |
| if (stream == NULL) |
| { |
| ret = -errno; |
| if (ret != -ENOENT) |
| { |
| ferr("ERROR: Failed to open %s: %d\n", path, ret); |
| } |
| |
| return ret; |
| } |
| |
| /* Process each line in the file */ |
| |
| memset(ipv6cfg, 0, sizeof(FAR struct ipv6cfg_s)); |
| |
| while (fgets(line, MAX_LINESIZE, stream) != NULL) |
| { |
| /* Skip any leading whitespace */ |
| |
| index = ipcfg_trim(line, 0); |
| |
| /* Check for a blank line or a comment */ |
| |
| if (line[index] != '\0' && line[index] != '#') |
| { |
| FAR char *variable = &line[index]; |
| FAR char *value; |
| |
| /* Expect <variable>=<value> pair */ |
| |
| value = strchr(variable, '='); |
| if (value == NULL) |
| { |
| ferr("ERROR: Skipping malformed line in file: %s\n", line); |
| continue; |
| } |
| |
| /* NUL-terminate the variable string */ |
| |
| *value++ = '\0'; |
| |
| /* Process the variable assignment */ |
| |
| if (strcmp(variable, "DEVICE") == 0) |
| { |
| /* Just assure that it matches the filename */ |
| |
| if (strcmp(value, netdev) != 0) |
| { |
| ferr("ERROR: Bad device in file: %s=%s\n", |
| variable, value); |
| } |
| } |
| else if (strcmp(variable, "IPv6PROTO") == 0) |
| { |
| if (strcmp(value, "none") == 0) |
| { |
| ipv6cfg->proto = IPv6PROTO_NONE; |
| } |
| else if (strcmp(value, "static") == 0) |
| { |
| ipv6cfg->proto = IPv6PROTO_STATIC; |
| } |
| else if (strcmp(value, "autoconf") == 0) |
| { |
| ipv6cfg->proto = IPv6PROTO_AUTOCONF; |
| } |
| else if (strcmp(value, "fallback") == 0) |
| { |
| ipv6cfg->proto = IPv6PROTO_FALLBACK; |
| } |
| else |
| { |
| ferr("ERROR: Unrecognized IPv6PROTO: %s=%s\n", |
| variable, value); |
| } |
| |
| /* Assume IPv4 settings are present if the IPv6BOOTPROTO |
| * setting is encountered. |
| */ |
| |
| found = true; |
| } |
| else if (strcmp(variable, "IPv6IPADDR") == 0) |
| { |
| ret = inet_pton(AF_INET6, value, &ipv6cfg->ipaddr); |
| if (ret < 0) |
| { |
| ret = -errno; |
| ferr("ERROR: inet_pton() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| else if (strcmp(variable, "IPv6NETMASK") == 0) |
| { |
| ret = inet_pton(AF_INET6, value, &ipv6cfg->netmask); |
| if (ret < 0) |
| { |
| ret = -errno; |
| ferr("ERROR: inet_pton() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| else if (strcmp(variable, "IPv6ROUTER") == 0) |
| { |
| ret = inet_pton(AF_INET6, value, &ipv6cfg->router); |
| if (ret < 0) |
| { |
| ret = -errno; |
| ferr("ERROR: inet_pton() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| |
| /* Anything other than some IPv4 settings would be an error. |
| * This is a sloppy check because it does not detect invalid |
| * names variables that begin with "IPv4". |
| */ |
| |
| else if (strncmp(variable, "IPv4", 4) != 0) |
| { |
| ferr("ERROR: Unrecognized variable: %s=%s\n", variable, value); |
| } |
| } |
| } |
| |
| /* Close the file and return */ |
| |
| fclose(stream); |
| return found ? OK : -ENOENT; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_write_text_ipv4 |
| * |
| * Description: |
| * Write the IPv4 configuration to a human-readable, text IP Configuration |
| * file. |
| * |
| * Input Parameters: |
| * path - The full path to the IP configuration file |
| * netdev - Network device name string |
| * ipv4cfg - The IPv4 configration to write |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv4) |
| int ipcfg_write_text_ipv4(FAR const char *path, FAR const char *netdev, |
| FAR const struct ipv4cfg_s *ipv4cfg) |
| { |
| #ifdef CONFIG_NET_IPv6 |
| struct ipv6cfg_s ipv6cfg; |
| bool ipv6 = false; |
| #endif |
| FAR FILE *stream; |
| int ret; |
| |
| DEBUGASSERT(path != NULL && netdev != NULL && ipv4cfg != NULL); |
| |
| #ifdef CONFIG_NET_IPv6 |
| /* Read any IPv6 data in the file */ |
| |
| ret = ipcfg_read_text_ipv6(path, netdev, &ipv6cfg); |
| if (ret < 0) |
| { |
| /* -ENOENT is not an error. It simply means that there is no IPv6 |
| * configuration in the file. |
| */ |
| |
| if (ret != -ENOENT) |
| { |
| return ret; |
| } |
| } |
| else |
| { |
| ipv6 = true; |
| } |
| #endif |
| |
| /* Open the file for writing (truncates) */ |
| |
| stream = fopen(path, "w"); |
| if (stream == NULL) |
| { |
| ret = -errno; |
| ferr("ERROR: Failed to open %s: %d\n", path, ret); |
| return ret; |
| } |
| |
| /* Save the device name */ |
| |
| fprintf(stream, "DEVICE=%s\n", netdev); |
| |
| /* Write the IPv4 configuration */ |
| |
| ret = ipcfg_write_ipv4(stream, ipv4cfg); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| #ifdef CONFIG_NET_IPv6 |
| /* Followed by any IPv6 data in the file */ |
| |
| if (ipv6) |
| { |
| ret = ipcfg_write_ipv6(stream, &ipv6cfg); |
| } |
| #endif |
| |
| fclose(stream); |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: ipcfg_write_text_ipv6 |
| * |
| * Description: |
| * Write the IPv6 configuration to a binary IP Configuration file. |
| * |
| * Input Parameters: |
| * path - The full path to the IP configuration file |
| * netdev - Network device name string |
| * ipv6cfg - The IPv6 configration to write |
| * |
| * Returned Value: |
| * Zero is returned on success; a negated errno value is returned on any |
| * failure. |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_IPCFG_WRITABLE) && defined(CONFIG_NET_IPv6) |
| int ipcfg_write_text_ipv6(FAR const char *path, FAR const char *netdev, |
| FAR const struct ipv6cfg_s *ipv6cfg) |
| { |
| #ifdef CONFIG_NET_IPv4 |
| struct ipv4cfg_s ipv4cfg; |
| bool ipv4 = false; |
| #endif |
| FAR FILE *stream; |
| int ret; |
| |
| DEBUGASSERT(path != NULL && netdev != NULL && ipv6cfg != NULL); |
| |
| #ifdef CONFIG_NET_IPv4 |
| /* Read any IPv4 data in the file */ |
| |
| ret = ipcfg_read_text_ipv4(path, netdev, &ipv4cfg); |
| if (ret < 0) |
| { |
| /* -ENOENT is not an error. It simply means that there is no IPv4 |
| * configuration in the file. |
| */ |
| |
| if (ret != -ENOENT) |
| { |
| return ret; |
| } |
| } |
| else |
| { |
| ipv4 = true; |
| } |
| #endif |
| |
| /* Open the file for writing (truncates) */ |
| |
| stream = fopen(path, "w"); |
| if (stream == NULL) |
| { |
| ret = -errno; |
| ferr("ERROR: Failed to open %s: %d\n", path, ret); |
| return ret; |
| } |
| |
| /* Save the device name */ |
| |
| fprintf(stream, "DEVICE=%s\n", netdev); |
| |
| #ifdef CONFIG_NET_IPv4 |
| if (ipv4) |
| { |
| /* Write the IPv4 configuration. This is really unnecessary in most |
| * cases since the IPv4 data should already be in place. |
| */ |
| |
| ret = ipcfg_write_ipv4(stream, &ipv4cfg); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| } |
| #endif |
| |
| /* Write the IPv6 configuration */ |
| |
| ret = ipcfg_write_ipv6(stream, ipv6cfg); |
| fclose(stream); |
| return ret; |
| } |
| #endif |
| |
| #endif /* !CONFIG_IPCFG_BINARY */ |