1/*
2 * $FreeBSD$
3 *
4 * Copyright (c) 1995
5 *      Gary J Palmer. All rights reserved.
6 * Copyright (c) 1996
7 *      Jordan K. Hubbard. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer,
14 *    verbatim and that no modifications are made prior to this
15 *    point in the file.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33/*
34 * All kinds of hacking also performed by jkh on this code.  Don't
35 * blame Gary for every bogosity you see here.. :-)
36 *
37 * -jkh
38 */
39
40#include "sysinstall.h"
41#include <sys/param.h>
42#include <sys/sysctl.h>
43#include <sys/types.h>
44#include <sys/socket.h>
45#include <sys/sockio.h>
46
47#include <netinet/in.h>
48#include <net/if.h>
49#include <net/if_media.h>
50
51#include <netdb.h>
52#include <paths.h>
53#include <ifaddrs.h>
54
55/* The help file for the TCP/IP setup screen */
56#define TCP_HELPFILE		"tcp"
57
58/* These are nasty, but they make the layout structure a lot easier ... */
59
60static char	hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
61		gateway[IPADDR_FIELD_LEN], nameserver[INET6_ADDRSTRLEN];
62static int	okbutton, cancelbutton;
63static char	ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
64static char	ipv6addr[INET6_ADDRSTRLEN];
65
66/* What the screen size is meant to be */
67#define TCP_DIALOG_Y		0
68#define TCP_DIALOG_X		8
69#define TCP_DIALOG_WIDTH	COLS - 16
70#define TCP_DIALOG_HEIGHT	LINES - 2
71
72static Layout layout[] = {
73#define LAYOUT_HOSTNAME		0
74    { 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
75      "Host:", "Your fully-qualified hostname, e.g. foo.example.com",
76      hostname, STRINGOBJ, NULL },
77#define LAYOUT_DOMAINNAME	1
78    { 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
79      "Domain:",
80      "The name of the domain that your machine is in, e.g. example.com",
81      domainname, STRINGOBJ, NULL },
82#define LAYOUT_GATEWAY		2
83    { 5, 2, 18, IPADDR_FIELD_LEN - 1,
84      "IPv4 Gateway:",
85      "IPv4 address of host forwarding packets to non-local destinations",
86      gateway, STRINGOBJ, NULL },
87#define LAYOUT_NAMESERVER	3
88    { 5, 35, 18, INET6_ADDRSTRLEN - 1,
89      "Name server:", "IPv4 or IPv6 address of your local DNS server",
90      nameserver, STRINGOBJ, NULL },
91#define LAYOUT_IPADDR		4
92    { 10, 10, 18, IPADDR_FIELD_LEN - 1,
93      "IPv4 Address:",
94      "The IPv4 address to be used for this interface",
95      ipaddr, STRINGOBJ, NULL },
96#define LAYOUT_NETMASK		5
97    { 10, 35, 18, IPADDR_FIELD_LEN - 1,
98      "Netmask:",
99      "The netmask for this interface, e.g. 0xffffff00 for a class C network",
100      netmask, STRINGOBJ, NULL },
101#define LAYOUT_EXTRAS		6
102    { 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
103      "Extra options to ifconfig (usually empty):",
104      "Any interface-specific options to ifconfig you would like to add",
105      extras, STRINGOBJ, NULL },
106#define LAYOUT_OKBUTTON		7
107    { 19, 15, 0, 0,
108      "OK", "Select this if you are happy with these settings",
109      &okbutton, BUTTONOBJ, NULL },
110#define LAYOUT_CANCELBUTTON	8
111    { 19, 35, 0, 0,
112      "CANCEL", "Select this if you wish to cancel this screen",
113      &cancelbutton, BUTTONOBJ, NULL },
114    LAYOUT_END,
115};
116
117#define _validByte(b) ((b) >= 0 && (b) <= 255)
118
119/* whine */
120static void
121feepout(char *msg)
122{
123    beep();
124    msgConfirm("%s", msg);
125}
126
127/* Verify IP address integrity */
128static int
129verifyIP(char *ip, unsigned long *mask, unsigned long *out)
130{
131    long a, b, c, d;
132    char *endptr, *endptr_prev;
133
134    unsigned long parsedip;
135    unsigned long max_addr = (255 << 24) | (255 << 16) | (255 << 8) | 255;
136
137    if (ip == NULL)
138	return 0;
139    a = strtol(ip, &endptr, 10);
140    if (endptr - ip == 0 || *endptr++ != '.')
141	return 0;
142    endptr_prev = endptr;
143    b = strtol(endptr, &endptr, 10);
144    if (endptr - endptr_prev == 0 || *endptr++ != '.')
145	return 0;
146    endptr_prev = endptr;
147    c = strtol(endptr, &endptr, 10);
148    if (endptr - endptr_prev == 0 || *endptr++ != '.')
149	return 0;
150    endptr_prev = endptr;
151    d = strtol(endptr, &endptr, 10);
152    if (*endptr != '\0' || endptr - endptr_prev == 0)
153	return 0;
154    if (!_validByte(a) || !_validByte(b) || !_validByte(c) || !_validByte(d))
155	return 0;
156    parsedip = (a << 24) | (b << 16) | (c << 8) | d;
157    if (out)
158	*out = parsedip;
159    /*
160     * The ip address must not be network or broadcast address.
161     */
162    if (mask && ((parsedip == (parsedip & *mask)) ||
163	(parsedip == ((parsedip & *mask) + max_addr - *mask))))
164	return 0;
165    return 1;
166}
167
168static int
169verifyIP6(char *ip)
170{
171    struct addrinfo hints, *res;
172
173    memset(&hints, 0, sizeof(hints));
174    hints.ai_family = AF_INET6;
175    hints.ai_socktype = SOCK_STREAM;
176    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
177    if (getaddrinfo(ip, NULL, &hints, &res) == 0) {
178	freeaddrinfo(res);
179	return 1;
180    }
181    return 0;
182}
183
184/* Verify IPv4 netmask as being well-formed as
185   a 0x or AAA.BBB.CCC.DDD mask */
186static int
187verifyNetmask(const char *netmask, unsigned long *out)
188{
189    unsigned long mask;
190    long tmp;
191    char *endptr;
192
193    if (netmask[0] == '0' && (netmask[1] == 'x' || netmask[1] == 'X')) {
194        /* Parse out hex mask */
195        mask = strtoul(netmask, &endptr, 0);
196        if (*endptr != '\0')
197            return 0;
198    } else {
199        /* Parse out quad decimal mask */
200        tmp = strtoul(netmask, &endptr, 10);
201        if (!_validByte(tmp) || *endptr++ != '.')
202            return 0;
203	mask = tmp;
204        tmp = strtoul(endptr, &endptr, 10);
205        if (!_validByte(tmp) || *endptr++ != '.')
206            return 0;
207	mask = (mask << 8) + tmp;
208        tmp = strtoul(endptr, &endptr, 10);
209        if (!_validByte(tmp) || *endptr++ != '.')
210            return 0;
211	mask = (mask << 8) + tmp;
212        tmp = strtoul(endptr, &endptr, 10);
213        if (!_validByte(tmp) || *endptr++ != '\0')
214            return 0;
215	mask = (mask << 8) + tmp;
216    }
217    /* Verify that we have a continous netmask */
218    if ((((-mask & mask) - 1) | mask) != 0xffffffff)
219        return 0;
220    if (out)
221        *out = mask;
222    return 1;
223}
224
225static int
226verifyGW(char *gw, unsigned long *ip, unsigned long *mask)
227{
228    unsigned long parsedgw;
229
230    if (!verifyIP(gw, mask, &parsedgw))
231	return 0;
232    /* Gateway needs to be within the set of IPs reachable through the
233       interface */
234    if (ip && mask && ((parsedgw & *mask) != (*ip & *mask)))
235	return 0;
236    return 1;
237}
238
239/* Check for the settings on the screen - the per-interface stuff is
240   moved to the main handling code now to do it on the fly - sigh */
241static int
242verifySettings(void)
243{
244    unsigned long parsedip;
245    unsigned long parsednetmask;
246
247    if (!hostname[0])
248	feepout("Must specify a host name of some sort!");
249    else if (netmask[0] && !verifyNetmask(netmask, &parsednetmask))
250	feepout("Invalid netmask value");
251    else if (nameserver[0] && !verifyIP(nameserver, NULL, NULL) &&
252		    !verifyIP6(nameserver))
253	feepout("Invalid name server IP address specified");
254    else if (ipaddr[0] && !verifyIP(ipaddr, &parsednetmask, &parsedip))
255	feepout("Invalid IPv4 address");
256    else if (gateway[0] && strcmp(gateway, "NO") &&
257	     !verifyGW(gateway, ipaddr[0] ? &parsedip : NULL,
258		     netmask[0] ? &parsednetmask : NULL))
259	feepout("Invalid gateway IPv4 address specified");
260    else
261	return 1;
262    return 0;
263}
264
265static void
266dhcpGetInfo(Device *devp)
267{
268    char leasefile[PATH_MAX];
269
270    snprintf(leasefile, sizeof(leasefile), "%sdhclient.leases.%s",
271	_PATH_VARDB, devp->name);
272    /* If it fails, do it the old-fashioned way */
273    if (dhcpParseLeases(leasefile, hostname, domainname,
274			 nameserver, ipaddr, gateway, netmask) == -1) {
275	FILE *ifp;
276	char *cp, cmd[256], data[2048];
277	int i, j;
278
279	/* Bah, now we have to kludge getting the information from ifconfig */
280	snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
281	ifp = popen(cmd, "r");
282	if (ifp) {
283	    j = fread(data, 1, sizeof(data), ifp);
284	    fclose(ifp);
285	    if (j < 0)	/* paranoia */
286		j = 0;
287	    data[j] = '\0';
288	    if (isDebug())
289		msgDebug("DHCP configured interface returns %s\n", data);
290	    /* XXX This is gross as it assumes a certain ordering to
291	       ifconfig's output! XXX */
292	    if ((cp = strstr(data, "inet ")) != NULL) {
293		i = 0;
294		cp += 5;	/* move over keyword */
295		while (*cp != ' ')
296		    ipaddr[i++] = *(cp++);
297		ipaddr[i] = '\0';
298		if (!strncmp(++cp, "netmask", 7)) {
299		    i = 0;
300		    cp += 8;
301		    while (*cp != ' ')
302			netmask[i++] = *(cp++);
303		    netmask[i] = '\0';
304		}
305	    }
306	}
307    }
308
309    /* If we didn't get a name server value, hunt for it in resolv.conf */
310    if (!nameserver[0] && file_readable("/etc/resolv.conf"))
311	configEnvironmentResolv("/etc/resolv.conf");
312    if (hostname[0])
313	variable_set2(VAR_HOSTNAME, hostname, 0);
314}
315
316static void
317rtsolGetInfo(Device *devp)
318{
319    FILE *ifp;
320    char *cp, cmd[256], data[2048];
321    int i;
322
323    snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
324    if ((ifp = popen(cmd, "r")) == NULL)
325	return;
326    while (fgets(data, sizeof(data), ifp) != NULL) {
327	if (isDebug())
328	    msgDebug("RTSOL configured interface returns %s\n", data);
329	if ((cp = strstr(data, "inet6 ")) != NULL) {
330	    cp += 6;	/* move over keyword */
331	    if (strncmp(cp, "fe80:", 5)) {
332		i = 0;
333		while (*cp != ' ')
334		    ipv6addr[i++] = *(cp++);
335		ipv6addr[i] = '\0';
336	    }
337	}
338    }
339    fclose(ifp);
340}
341
342/* This is it - how to get TCP setup values */
343int
344tcpOpenDialog(Device *devp)
345{
346    WINDOW              *ds_win, *save = NULL;
347    ComposeObj          *obj = NULL;
348    int                 n = 0, filled = 0, cancel = FALSE;
349    int			max, ret = DITEM_SUCCESS;
350    int			use_dhcp = FALSE;
351    int			use_rtsol = FALSE;
352    char                *tmp;
353    char		title[80];
354
355    save = savescr();
356    /* Initialise vars from previous device values */
357    if (devp->private) {
358	DevInfo *di = (DevInfo *)devp->private;
359
360	SAFE_STRCPY(ipaddr, di->ipaddr);
361	SAFE_STRCPY(netmask, di->netmask);
362	SAFE_STRCPY(extras, di->extras);
363	use_dhcp = di->use_dhcp;
364	use_rtsol = di->use_rtsol;
365    }
366    else { /* See if there are any defaults */
367	char *cp;
368	char *old_interactive = NULL;
369
370	/*
371	 * This is a hack so that the dialogs below are interactive in a
372	 * script if we have requested interactive behavior.
373	 */
374	if (variable_get(VAR_NONINTERACTIVE) &&
375	  variable_get(VAR_NETINTERACTIVE)) {
376	    old_interactive = strdup(VAR_NONINTERACTIVE);
377	    variable_unset(VAR_NONINTERACTIVE);
378	}
379
380
381	/*
382	 * Try a RTSOL scan if such behavior is desired.
383	 * If the variable was configured and is YES, do it.
384	 * If it was configured to anything else, treat it as NO.
385	 * Otherwise, ask the question interactively.
386	 */
387	if (!variable_get(VAR_NO_INET6) &&
388	    (!variable_cmp(VAR_TRY_RTSOL, "YES") ||
389	    (variable_get(VAR_TRY_RTSOL)==0 && !msgNoYes("Do you want to try IPv6 configuration of the interface?")))) {
390	    int i;
391	    size_t len;
392
393	    i = 0;
394	    sysctlbyname("net.inet6.ip6.forwarding", NULL, 0, &i, sizeof(i));
395	    i = 1;
396	    sysctlbyname("net.inet6.ip6.accept_rtadv", NULL, 0, &i, sizeof(i));
397	    vsystem("ifconfig %s up", devp->name);
398	    len = sizeof(i);
399	    sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
400	    sleep(i + 1);
401	    Mkdir("/var/run");
402	    msgNotify("Scanning for RA servers...");
403	    if (0 == vsystem("rtsol %s", devp->name)) {
404		len = sizeof(i);
405		sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
406		sleep(i + 1);
407		rtsolGetInfo(devp);
408		use_rtsol = TRUE;
409	    } else
410		use_rtsol = FALSE;
411	}
412
413
414	/*
415	 * First try a DHCP scan if such behavior is desired.
416	 * If the variable was configured and is YES, do it.
417	 * If it was configured to anything else, treat it as NO.
418	 * Otherwise, ask the question interactively.
419	 */
420	if (!variable_cmp(VAR_TRY_DHCP, "YES") ||
421	    (variable_get(VAR_TRY_DHCP)==0 && !msgNoYes("Do you want to try DHCP configuration of the interface?"))) {
422	    Mkdir("/var/db");
423	    Mkdir("/var/run");
424	    Mkdir("/tmp");
425	    msgNotify("Scanning for DHCP servers...");
426	    /* XXX clear any existing lease */
427	    /* XXX limit protocol to N tries */
428	    if (0 == vsystem("dhclient %s", devp->name)) {
429		dhcpGetInfo(devp);
430		use_dhcp = TRUE;
431	    }
432	    else
433		use_dhcp = FALSE;
434	}
435
436	/* Restore old VAR_NONINTERACTIVE if needed. */
437	if (old_interactive != NULL) {
438	    variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
439	    free(old_interactive);
440	}
441
442	/* Special hack so it doesn't show up oddly in the tcpip setup menu */
443	if (!strcmp(gateway, "NO"))
444	    gateway[0] = '\0';
445
446	/* Get old IP address from variable space, if available */
447	if (!ipaddr[0]) {
448	    if ((cp = variable_get(VAR_IPADDR)) != NULL)
449		SAFE_STRCPY(ipaddr, cp);
450	    else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL)
451		SAFE_STRCPY(ipaddr, cp);
452	}
453
454	/* Get old netmask from variable space, if available */
455	if (!netmask[0]) {
456	    if ((cp = variable_get(VAR_NETMASK)) != NULL)
457		SAFE_STRCPY(netmask, cp);
458	    else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL)
459		SAFE_STRCPY(netmask, cp);
460	}
461
462	/* Get old extras string from variable space, if available */
463	if (!extras[0]) {
464	    if ((cp = variable_get(VAR_EXTRAS)) != NULL)
465		SAFE_STRCPY(extras, cp);
466	    else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL)
467		SAFE_STRCPY(extras, cp);
468	}
469    }
470
471    /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */
472    if (!hostname[0]) {
473	tmp = variable_get(VAR_HOSTNAME);
474	if (tmp)
475	    SAFE_STRCPY(hostname, tmp);
476    }
477    if (!domainname[0]) {
478	tmp = variable_get(VAR_DOMAINNAME);
479	if (tmp)
480	    SAFE_STRCPY(domainname, tmp);
481    }
482    if (!gateway[0]) {
483	tmp = variable_get(VAR_GATEWAY);
484	if (tmp && strcmp(tmp, "NO"))
485	    SAFE_STRCPY(gateway, tmp);
486    }
487    if (!nameserver[0]) {
488	tmp = variable_get(VAR_NAMESERVER);
489	if (tmp)
490	    SAFE_STRCPY(nameserver, tmp);
491    }
492
493    /* If non-interactive, jump straight over the dialog crap and into config section */
494    if (variable_get(VAR_NONINTERACTIVE) &&
495	!variable_get(VAR_NETINTERACTIVE)) {
496	if (!hostname[0])
497	    msgConfirm("WARNING: hostname variable not set and is a non-optional\n"
498		       "parameter.  Please add this to your installation script\n"
499		       "or set the netInteractive variable (see sysinstall man page)");
500	else
501	    goto netconfig;
502    }
503
504    /* Now do all the screen I/O */
505    dialog_clear_norefresh();
506
507    /* Modify the help line for PLIP config */
508    if (!strncmp(devp->name, "plip", 4))
509	layout[LAYOUT_EXTRAS].help =
510         "For PLIP configuration, you must enter the peer's IP address here.";
511
512    /* We need a curses window */
513    tmp = " Network Configuration ";
514    if (ipv6addr[0])
515	tmp = string_concat(tmp, "(IPv6 ready) ");
516    if (!(ds_win = openLayoutDialog(TCP_HELPFILE, tmp,
517				    TCP_DIALOG_X, TCP_DIALOG_Y, TCP_DIALOG_WIDTH, TCP_DIALOG_HEIGHT))) {
518	beep();
519	msgConfirm("Cannot open TCP/IP dialog window!!");
520	restorescr(save);
521	return DITEM_FAILURE;
522    }
523
524    /* Draw interface configuration box */
525    draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17,
526	     dialog_attr, border_attr);
527    wattrset(ds_win, dialog_attr);
528    sprintf(title, " Configuration for Interface %s ", devp->name);
529    mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title);
530
531    /* Some more initialisation before we go into the main input loop */
532    obj = initLayoutDialog(ds_win, layout, TCP_DIALOG_X, TCP_DIALOG_Y, &max);
533
534reenter:
535    cancelbutton = okbutton = 0;
536    while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel)) {
537	/* Prevent this from being irritating if user really means NO */
538	if (filled < 3) {
539	    /* Insert a default value for the netmask, 0xffffff00 is
540	     * the most appropriate one (entire class C, or subnetted
541	     * class A/B network).
542	     */
543	    if (!netmask[0]) {
544		strcpy(netmask, "255.255.255.0");
545		RefreshStringObj(layout[LAYOUT_NETMASK].obj);
546		++filled;
547	    }
548	    if (!index(hostname, '.') && domainname[0]) {
549		strcat(hostname, ".");
550		strcat(hostname, domainname);
551		RefreshStringObj(layout[LAYOUT_HOSTNAME].obj);
552		++filled;
553	    }
554	    else if (((tmp = index(hostname, '.')) != NULL) && !domainname[0]) {
555		SAFE_STRCPY(domainname, tmp + 1);
556		RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj);
557		++filled;
558	    }
559	}
560    }
561    if (!cancel && !verifySettings())
562	goto reenter;
563
564    /* Clear this crap off the screen */
565    delwin(ds_win);
566    dialog_clear_norefresh();
567    use_helpfile(NULL);
568
569    /* We actually need to inform the rest of sysinstall about this
570       data now if the user hasn't selected cancel.  Save the stuff
571       out to the environment via the variable_set() mechanism */
572
573netconfig:
574    if (!cancel) {
575	DevInfo *di;
576	char temp[512], ifn[255];
577	int ipv4_enable = FALSE;
578
579	if (hostname[0]) {
580	    variable_set2(VAR_HOSTNAME, hostname, 1);
581	    sethostname(hostname, strlen(hostname));
582	}
583	if (domainname[0])
584	    variable_set2(VAR_DOMAINNAME, domainname, 0);
585	if (gateway[0])
586	    variable_set2(VAR_GATEWAY, gateway, use_dhcp ? 0 : 1);
587	if (nameserver[0])
588	    variable_set2(VAR_NAMESERVER, nameserver, 0);
589	if (ipaddr[0])
590	    variable_set2(VAR_IPADDR, ipaddr, 0);
591	if (ipv6addr[0])
592	    variable_set2(VAR_IPV6ADDR, ipv6addr, 0);
593
594	if (!devp->private)
595	    devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo));
596	di = devp->private;
597	SAFE_STRCPY(di->ipaddr, ipaddr);
598	SAFE_STRCPY(di->netmask, netmask);
599	SAFE_STRCPY(di->extras, extras);
600	di->use_dhcp = use_dhcp;
601	di->use_rtsol = use_rtsol;
602
603	if (use_dhcp || ipaddr[0])
604	    ipv4_enable = TRUE;
605	if (ipv4_enable) {
606	    sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
607	    if (use_dhcp) {
608		if (strlen(extras) > 0)
609		    sprintf(temp, "DHCP %s", extras);
610		else
611		    sprintf(temp, "DHCP");
612	    } else
613		sprintf(temp, "inet %s %s netmask %s",
614			ipaddr, extras, netmask);
615	    variable_set2(ifn, temp, 1);
616	}
617	if (use_rtsol)
618	    variable_set2(VAR_IPV6_ENABLE, "YES", 1);
619	if (!use_dhcp)
620	    configResolv(NULL);	/* XXX this will do it on the MFS copy XXX */
621	ret = DITEM_SUCCESS;
622    }
623    else
624	ret = DITEM_FAILURE;
625    restorescr(save);
626    return ret;
627}
628
629static Device *NetDev;
630
631static int
632netHook(dialogMenuItem *self)
633{
634    Device **devs;
635
636    devs = deviceFindDescr(self->prompt, self->title, DEVICE_TYPE_NETWORK);
637    if (devs) {
638	if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE)
639	    NetDev = devs[0];
640	else
641	    NetDev = NULL;
642    }
643    return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE;
644}
645
646static char *
647tcpDeviceScan(void)
648{
649	int s;
650	struct ifmediareq ifmr;
651	struct ifaddrs *ifap, *ifa;
652	char *network_dev;
653
654	if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)
655		return (NULL);
656
657	if (getifaddrs(&ifap) < 0)
658		return (NULL);
659
660	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
661		memset(&ifmr, 0, sizeof(ifmr));
662		strlcpy(ifmr.ifm_name, ifa->ifa_name, sizeof(ifmr.ifm_name));
663
664		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
665			continue;	/* some devices don't support this */
666
667		if ((ifmr.ifm_status & IFM_AVALID) == 0)
668			continue;	/* not active */
669
670		if (IFM_TYPE(ifmr.ifm_active) != IFM_ETHER)
671			continue;	/* not an ethernet device */
672
673		if (ifmr.ifm_status & IFM_ACTIVE) {
674			network_dev = strdup(ifa->ifa_name);
675			freeifaddrs(ifap);
676
677			if (!variable_get(VAR_NONINTERACTIVE))
678				msgConfirm("Using interface %s", network_dev);
679
680			msgDebug("tcpDeviceScan found %s", network_dev);
681			return (network_dev);
682		}
683	}
684
685	close(s);
686
687	freeifaddrs(ifap);
688
689	return (NULL);
690}
691
692/* Get a network device */
693Device *
694tcpDeviceSelect(void)
695{
696    DMenu *menu;
697    Device **devs, *rval;
698    char *dev, *network_dev;
699    int cnt;
700
701    rval = NULL;
702
703    if (variable_get(VAR_NETWORK_DEVICE)) {
704	network_dev = variable_get(VAR_NETWORK_DEVICE);
705
706	/*
707	 * netDev can be set to several types of values.
708	 * If netDev is set to ANY, scan all network devices
709	 * looking for a valid link, and go with the first
710	 * device found. netDev can also be specified as a
711	 * comma delimited list, with each network device
712	 * tried in order. netDev can also be set to a single
713	 * network device.
714	 */
715	if (!strcmp(network_dev, "ANY"))
716		network_dev = strdup(tcpDeviceScan());
717
718	while ((dev = strsep(&network_dev, ",")) != NULL) {
719	    devs = deviceFind(dev, DEVICE_TYPE_NETWORK);
720	    cnt = deviceCount(devs);
721
722	    if (cnt) {
723		if (DITEM_STATUS(tcpOpenDialog(devs[0])) == DITEM_SUCCESS)
724		    return (devs[0]);
725	    }
726	}
727
728	if (!variable_get(VAR_NONINTERACTIVE))
729		msgConfirm("No network devices available!");
730
731	return (NULL);
732    }
733
734    devs = deviceFind(NULL, DEVICE_TYPE_NETWORK);
735    cnt = deviceCount(devs);
736
737    if ((!RunningAsInit) && (variable_check("NETWORK_CONFIGURED=NO") != TRUE)) {
738	if (!msgYesNo("Running multi-user, assume that the network is already configured?"))
739	    return devs[0];
740    }
741    if (cnt == 1) {
742	if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
743	    rval = devs[0];
744    }
745    else {
746	int status;
747
748	menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL);
749	if (!menu)
750	    msgFatal("Unable to create network device menu!  Argh!");
751	status = dmenuOpenSimple(menu, FALSE);
752	free(menu);
753	if (status)
754	    rval = NetDev;
755    }
756    return rval;
757}
758
759/* Do it from a menu that doesn't care about status */
760int
761tcpMenuSelect(dialogMenuItem *self)
762{
763    Device *tmp;
764    WINDOW *save;
765
766    variable_set("NETWORK_CONFIGURED=NO",0);
767    tmp = tcpDeviceSelect();
768    variable_unset("NETWORK_CONFIGURED");
769    save = savescr();
770    if (tmp && tmp->private && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
771	if (!DEVICE_INIT(tmp))
772	    msgConfirm("Initialization of %s device failed.", tmp->name);
773    restorescr(save);
774    return DITEM_SUCCESS;
775}
776