[7/7] ui/ncurses: Allow user to configure IPv6 addresses

Message ID 20180509053705.3143-8-sam@mendozajonas.com
State New
Headers show
Series
  • IPv6 Support
Related show

Commit Message

Samuel Mendoza-Jonas May 9, 2018, 5:37 a.m.
Add an option to switch between IPv4 and IPv6 address schemes. For now
only support IPv4 /or/ IPv6, not a combination.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
---
 ui/ncurses/nc-config.c  | 114 +++++++++++++++++++++++++++++++++++-----
 ui/ncurses/nc-widgets.c |  67 ++++++++++++++++++++++-
 ui/ncurses/nc-widgets.h |   4 ++
 3 files changed, 171 insertions(+), 14 deletions(-)

Patch

diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c
index 5c0f23b..e2c9887 100644
--- a/ui/ncurses/nc-config.c
+++ b/ui/ncurses/nc-config.c
@@ -33,7 +33,7 @@ 
 #include "nc-config.h"
 #include "nc-widgets.h"
 
-#define N_FIELDS	48
+#define N_FIELDS	50
 
 extern struct help_text config_help_text;
 
@@ -89,6 +89,9 @@  struct config_screen {
 		struct nc_widget_label		*network_l;
 		struct nc_widget_select		*network_f;
 
+		struct nc_widget_label		*net_addr_type_l;
+		struct nc_widget_select		*net_addr_type_f;
+
 		struct nc_widget_label		*iface_l;
 		struct nc_widget_select		*iface_f;
 		struct nc_widget_label		*ip_addr_l;
@@ -258,6 +261,11 @@  static int screen_process_form(struct config_screen *screen)
 
 	net_conf_type = widget_select_get_value(screen->widgets.network_f);
 
+	if (widget_select_get_value(screen->widgets.net_addr_type_f) == ADDR_IPV6)
+		config->network.addr_type = ADDR_IPV6;
+	else
+		config->network.addr_type = ADDR_IPV4;
+
 	/* if we don't have any network interfaces, prevent per-interface
 	 * configuration */
 	if (sysinfo->n_interfaces == 0)
@@ -527,6 +535,11 @@  static void config_screen_layout_widgets(struct config_screen *screen)
 
 	y += 1;
 
+	y += layout_pair(screen, y, screen->widgets.net_addr_type_l,
+			widget_select_base(screen->widgets.net_addr_type_f));
+
+	y += 1;
+
 	/* conditionally show iface select */
 	wl = widget_label_base(screen->widgets.iface_l);
 	wf = widget_select_base(screen->widgets.iface_f);
@@ -674,6 +687,46 @@  static void config_screen_layout_widgets(struct config_screen *screen)
 			y, screen->field_x + 28);
 }
 
+static void config_screen_net_addr_change(void *arg, int value)
+{
+	struct config_screen *screen = arg;
+
+	widgetset_unpost(screen->widgetset);
+
+	if (value == ADDR_IPV4) {
+		widget_textbox_set_validator_ipv4(screen->widgets.ip_addr_f);
+		widget_textbox_set_validator_integer(screen->widgets.ip_mask_f,
+				1, 31);
+		widget_update_label(screen->widgets.ip_addr_mask_help_l,
+				_("(eg. 192.168.0.10 / 24)"));
+
+		widget_textbox_set_validator_ipv4(screen->widgets.gateway_f);
+		widget_update_label(screen->widgets.gateway_help_l,
+				_("(eg. 192.168.0.1)"));
+
+		widget_textbox_set_validator_ipv4_multi(screen->widgets.dns_f);
+		widget_update_label(screen->widgets.dns_help_l,
+				_("(eg. 192.168.0.2)"));
+	} else {
+		widget_textbox_set_validator_ipv6(screen->widgets.ip_addr_f);
+		widget_textbox_set_validator_integer(screen->widgets.ip_mask_f,
+				1, 127);
+		widget_update_label(screen->widgets.ip_addr_mask_help_l,
+				_("(eg. fd69:d75f::27 / 64)"));
+
+		widget_textbox_set_validator_ipv6(screen->widgets.gateway_f);
+		widget_update_label(screen->widgets.gateway_help_l,
+				_("(eg. fd69:d75f::1)"));
+
+		widget_textbox_set_validator_ipv6_multi(screen->widgets.dns_f);
+		widget_update_label(screen->widgets.dns_help_l,
+				_("(eg. fd69:d75f::2)"));
+	}
+
+	config_screen_layout_widgets(screen);
+	widgetset_post(screen->widgetset);
+}
+
 static void config_screen_network_change(void *arg, int value)
 {
 	struct config_screen *screen = arg;
@@ -979,6 +1032,21 @@  static void config_screen_setup_widgets(struct config_screen *screen,
 	widget_select_on_change(screen->widgets.network_f,
 			config_screen_network_change, screen);
 
+	screen->widgets.net_addr_type_l = widget_new_label(set, 0, 0,
+			_("IP Scheme:"));
+	screen->widgets.net_addr_type_f = widget_new_select(set, 0, 0,
+						COLS - screen->field_x - 1);
+
+	widget_select_add_option(screen->widgets.net_addr_type_f,
+					ADDR_IPV4, _("IPv4"),
+					config->network.addr_type == ADDR_IPV4);
+	widget_select_add_option(screen->widgets.net_addr_type_f,
+					ADDR_IPV6, _("IPv6"),
+					config->network.addr_type == ADDR_IPV6);
+
+	widget_select_on_change(screen->widgets.net_addr_type_f,
+			config_screen_net_addr_change, screen);
+
 	screen->widgets.iface_l = widget_new_label(set, 0, 0, _("Device:"));
 	screen->widgets.iface_f = widget_new_select(set, 0, 0, 50);
 
@@ -1024,21 +1092,36 @@  static void config_screen_setup_widgets(struct config_screen *screen,
 	screen->widgets.ip_addr_f = widget_new_textbox(set, 0, 0, 16, ip);
 	screen->widgets.ip_mask_l = widget_new_label(set, 0, 0, "/");
 	screen->widgets.ip_mask_f = widget_new_textbox(set, 0, 0, 3, mask);
-	screen->widgets.ip_addr_mask_help_l =
-		widget_new_label(set, 0, 0, _("(eg. 192.168.0.10 / 24)"));
 
 	widget_textbox_set_fixed_size(screen->widgets.ip_addr_f);
 	widget_textbox_set_fixed_size(screen->widgets.ip_mask_f);
-	widget_textbox_set_validator_ipv4(screen->widgets.ip_addr_f);
-	widget_textbox_set_validator_integer(screen->widgets.ip_mask_f, 1, 31);
+	if (config->network.addr_type == ADDR_IPV4) {
+		screen->widgets.ip_addr_mask_help_l =
+			widget_new_label(set, 0, 0, _("(eg. 192.168.0.10 / 24)"));
+		widget_textbox_set_validator_ipv4(screen->widgets.ip_addr_f);
+		widget_textbox_set_validator_integer(screen->widgets.ip_mask_f,
+				1, 31);
+	} else {
+		screen->widgets.ip_addr_mask_help_l =
+			widget_new_label(set, 0, 0, _("(eg. fd69:d75f::27 / 64)"));
+		widget_textbox_set_validator_ipv6(screen->widgets.ip_addr_f);
+		widget_textbox_set_validator_integer(screen->widgets.ip_mask_f,
+				1, 127);
+	}
 
 	screen->widgets.gateway_l = widget_new_label(set, 0, 0, _("Gateway:"));
 	screen->widgets.gateway_f = widget_new_textbox(set, 0, 0, 16, gw);
-	screen->widgets.gateway_help_l =
-		widget_new_label(set, 0, 0, _("(eg. 192.168.0.1)"));
-
 	widget_textbox_set_fixed_size(screen->widgets.gateway_f);
-	widget_textbox_set_validator_ipv4(screen->widgets.gateway_f);
+	if (config->network.addr_type == ADDR_IPV4) {
+		widget_textbox_set_validator_ipv4(screen->widgets.gateway_f);
+		screen->widgets.gateway_help_l =
+			widget_new_label(set, 0, 0, _("(eg. 192.168.0.1)"));
+	} else {
+		widget_textbox_set_validator_ipv6(screen->widgets.gateway_f);
+		screen->widgets.gateway_help_l =
+			widget_new_label(set, 0, 0, _("(eg. fd69:d75f::1)"));
+	}
+
 
 	screen->widgets.url_l = widget_new_label(set, 0, 0, _("URL:"));
 	screen->widgets.url_f = widget_new_textbox(set, 0, 0, 32, url);
@@ -1056,10 +1139,15 @@  static void config_screen_setup_widgets(struct config_screen *screen,
 	screen->widgets.dns_l = widget_new_label(set, 0, 0,
 					_("DNS Server(s):"));
 	screen->widgets.dns_f = widget_new_textbox(set, 0, 0, 32, str);
-	screen->widgets.dns_help_l =
-		widget_new_label(set, 0, 0, _("(eg. 192.168.0.2)"));
-
-	widget_textbox_set_validator_ipv4_multi(screen->widgets.dns_f);
+	if (config->network.addr_type == ADDR_IPV4) {
+		screen->widgets.dns_help_l =
+			widget_new_label(set, 0, 0, _("(eg. 192.168.0.2)"));
+		widget_textbox_set_validator_ipv4_multi(screen->widgets.dns_f);
+	} else {
+		screen->widgets.dns_help_l =
+			widget_new_label(set, 0, 0, _("(eg. fd69:d75f::2)"));
+		widget_textbox_set_validator_ipv6_multi(screen->widgets.dns_f);
+	}
 
 	screen->widgets.dns_dhcp_help_l = widget_new_label(set, 0, 0,
 			_("(if not provided by DHCP server)"));
diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c
index 93c882b..d17d79d 100644
--- a/ui/ncurses/nc-widgets.c
+++ b/ui/ncurses/nc-widgets.c
@@ -44,8 +44,9 @@ 
 #  error "Curses form.h not found."
 #endif
 
-#include <string.h>
+#include <arpa/inet.h>
 #include <ctype.h>
+#include <string.h>
 
 #include <talloc/talloc.h>
 #include <types/types.h>
@@ -84,6 +85,8 @@  struct nc_widgetset {
 
 	/* custom validators */
 	FIELDTYPE *ipv4_multi_type;
+	FIELDTYPE *ipv6_multi_type;
+	FIELDTYPE *ipv6_type;
 	FIELDTYPE *url_type;
 };
 
@@ -196,6 +199,10 @@  static int label_destructor(void *ptr)
 	return 0;
 }
 
+void widget_update_label(struct nc_widget_label *label, char *str)
+{
+	set_field_buffer(label->widget.field, 0, str);
+}
 
 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
 		int y, int x, char *str)
@@ -415,11 +422,59 @@  void widget_textbox_set_validator_url(struct nc_widget_textbox *textbox)
 	set_field_type(textbox->widget.field, textbox->set->url_type);
 }
 
+static bool check_ipv6_field(FIELD *field,
+		const void *arg __attribute__((unused)))
+{
+	uint8_t addr[sizeof(struct in6_addr)];
+	int rc;
+
+	rc = inet_pton(AF_INET6, strip_string(field_buffer(field, 0)), addr);
+
+	return rc == 1;
+}
+
+void widget_textbox_set_validator_ipv6(struct nc_widget_textbox *textbox)
+{
+	if (!textbox->set->ipv6_type) {
+		textbox->set->ipv6_type = new_fieldtype(check_ipv6_field, NULL);
+	}
+	set_field_type(textbox->widget.field, textbox->set->ipv6_type);
+}
+
 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
 {
 	set_field_type(textbox->widget.field, TYPE_IPV4);
 }
 
+static bool check_ipv6_multi_char(int c,
+		const void *arg __attribute__((unused)))
+{
+	return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
+		c == ':' || c == ' ';
+}
+
+static bool check_ipv6_multi_field(FIELD *field,
+		const void *arg __attribute__((unused)))
+{
+	char *tok, *saveptr, *str;
+	uint8_t addr[INET6_ADDRSTRLEN];
+	int rc;
+
+	str = field_buffer(field, 0);
+	tok = strtok_r(str, " ", &saveptr);
+	if (!tok || *tok == '\0')
+		return false;
+
+	while (tok) {
+		rc = inet_pton(AF_INET6, tok, addr);
+		if (rc != 1)
+			return false;
+		tok = strtok_r(NULL, " ", &saveptr);
+	}
+
+	return true;
+}
+
 static bool check_ipv4_multi_char(int c,
 		const void *arg __attribute__((unused)))
 {
@@ -455,6 +510,16 @@  static bool check_ipv4_multi_field(FIELD *field,
 	return true;
 }
 
+void widget_textbox_set_validator_ipv6_multi(struct nc_widget_textbox *textbox)
+{
+	if (!textbox->set->ipv6_multi_type) {
+		textbox->set->ipv6_multi_type = new_fieldtype(
+				check_ipv6_multi_field,
+				check_ipv6_multi_char);
+	}
+	set_field_type(textbox->widget.field, textbox->set->ipv6_multi_type);
+}
+
 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
 {
 	if (!textbox->set->ipv4_multi_type) {
diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h
index aa9263f..217215a 100644
--- a/ui/ncurses/nc-widgets.h
+++ b/ui/ncurses/nc-widgets.h
@@ -37,11 +37,15 @@  struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
 		int y, int x, int size, const char *str,
 		void (*click)(void *), void *arg);
 
+void widget_update_label(struct nc_widget_label *label, char *str);
+
 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox);
 void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox,
 		long min, long max);
 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox);
 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox);
+void widget_textbox_set_validator_ipv6(struct nc_widget_textbox *textbox);
+void widget_textbox_set_validator_ipv6_multi(struct nc_widget_textbox *textbox);
 void widget_textbox_set_validator_url(struct nc_widget_textbox *textbox);
 
 void widget_subset_add_option(struct nc_widget_subset *subset, const char *text);