1/* $NetBSD: net.c,v 1.45 2023/12/17 18:46:42 martin Exp $ */ 2 3/* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 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 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35/* net.c -- routines to fetch files off the network. */ 36 37#include <sys/ioctl.h> 38#include <sys/param.h> 39#include <sys/resource.h> 40#include <sys/socket.h> 41#include <sys/stat.h> 42#include <sys/statvfs.h> 43#include <sys/statvfs.h> 44#include <sys/sysctl.h> 45#include <sys/wait.h> 46#include <arpa/inet.h> 47#include <net/if.h> 48#include <net/if_media.h> 49#include <netinet/in.h> 50#include <net80211/ieee80211_ioctl.h> 51#include <netinet/ip_var.h> 52#ifdef INET6 53#include <netinet6/ip6_var.h> 54#endif 55 56#include <err.h> 57#include <stdio.h> 58#include <stdlib.h> 59#include <string.h> 60#include <curses.h> 61#include <time.h> 62#include <unistd.h> 63 64#include "defs.h" 65#include "md.h" 66#include "msg_defs.h" 67#include "menu_defs.h" 68#include "txtwalk.h" 69 70int network_up = 0; 71/* Access to network information */ 72#define MAX_NETS 15 73struct net_desc { 74 char if_dev[STRSIZE]; 75 char name[STRSIZE]; // TODO 76}; 77 78static char net_dev[STRSIZE]; 79static char net_domain[STRSIZE]; 80static char net_host[STRSIZE]; 81static char net_ip[SSTRSIZE]; 82static char net_srv_ip[SSTRSIZE]; 83static char net_mask[SSTRSIZE]; 84char net_namesvr[STRSIZE]; 85static char net_defroute[STRSIZE]; 86static char net_media[STRSIZE]; 87static char net_ssid[STRSIZE]; 88static char net_passphrase[STRSIZE]; 89static char sl_flags[STRSIZE]; 90static int net_dhcpconf; 91#define DHCPCONF_IPADDR 0x01 92#define DHCPCONF_NAMESVR 0x02 93#define DHCPCONF_HOST 0x04 94#define DHCPCONF_DOMAIN 0x08 95#ifdef INET6 96static char net_ip6[STRSIZE]; 97#define IP6CONF_AUTOHOST 0x01 98#endif 99 100 101/* URL encode unsafe characters. */ 102 103static char *url_encode (char *dst, const char *src, const char *ep, 104 const char *safe_chars, 105 int encode_leading_slash); 106 107static void write_etc_hosts(FILE *f); 108 109#define DHCPCD "/sbin/dhcpcd" 110#define WPA_SUPPLICANT "/usr/sbin/wpa_supplicant" 111#include <signal.h> 112static int config_eth_medium(char *); 113static int config_dhcp(char *); 114static int config_wlan(char *); 115 116#ifdef INET6 117static int is_v6kernel (void); 118#endif 119 120/* 121 * URL encode unsafe characters. See RFC 1738. 122 * 123 * Copies src string to dst, encoding unsafe or reserved characters 124 * in %hex form as it goes, and returning a pointer to the result. 125 * The result is always a nul-terminated string even if it had to be 126 * truncated to avoid overflowing the available space. 127 * 128 * This url_encode() function does not operate on complete URLs, it 129 * operates on strings that make up parts of URLs. For example, in a 130 * URL like "ftp://username:password@host/path", the username, password, 131 * host and path should each be encoded separately before they are 132 * joined together with the punctuation characters. 133 * 134 * In most ordinary use, the path portion of a URL does not start with 135 * a slash; the slash is a separator between the host portion and the 136 * path portion, and is dealt with by software outside the url_encode() 137 * function. However, it is valid for url_encode() to be passed a 138 * string that does begin with a slash. For example, the string might 139 * represent a password, or a path part of a URL that the user really 140 * does want to begin with a slash. 141 * 142 * len is the length of the destination buffer. The result will be 143 * truncated if necessary to fit in the destination buffer. 144 * 145 * safe_chars is a string of characters that should not be encoded. If 146 * safe_chars is non-NULL, any characters in safe_chars as well as any 147 * alphanumeric characters will be copied from src to dst without 148 * encoding. Some potentially useful settings for this parameter are: 149 * 150 * NULL Everything is encoded (even alphanumerics) 151 * "" Everything except alphanumerics are encoded 152 * "/" Alphanumerics and '/' remain unencoded 153 * "$-_.+!*'()," Consistent with a strict reading of RFC 1738 154 * "$-_.+!*'(),/" As above, except '/' is not encoded 155 * "-_.+!,/" As above, except shell special characters are encoded 156 * 157 * encode_leading_slash is a flag that determines whether or not to 158 * encode a leading slash in a string. If this flag is set, and if the 159 * first character in the src string is '/', then the leading slash will 160 * be encoded (as "%2F"), even if '/' is one of the characters in the 161 * safe_chars string. Note that only the first character of the src 162 * string is affected by this flag, and that leading slashes are never 163 * deleted, but either retained unchanged or encoded. 164 * 165 * Unsafe and reserved characters are defined in RFC 1738 section 2.2. 166 * The most important parts are: 167 * 168 * The characters ";", "/", "?", ":", "@", "=" and "&" are the 169 * characters which may be reserved for special meaning within a 170 * scheme. No other characters may be reserved within a scheme. 171 * [...] 172 * 173 * Thus, only alphanumerics, the special characters "$-_.+!*'(),", 174 * and reserved characters used for their reserved purposes may be 175 * used unencoded within a URL. 176 * 177 */ 178 179#define RFC1738_SAFE "$-_.+!*'()," 180#define RFC1738_SAFE_LESS_SHELL "-_.+!," 181#define RFC1738_SAFE_LESS_SHELL_PLUS_SLASH "-_.+!,/" 182 183static char * 184url_encode(char *dst, const char *src, const char *ep, 185 const char *safe_chars, int encode_leading_slash) 186{ 187 int ch; 188 189 ep--; 190 191 for (; dst < ep; src++) { 192 ch = *src & 0xff; 193 if (ch == 0) 194 break; 195 if (safe_chars != NULL && 196 (ch != '/' || !encode_leading_slash) && 197 (isalnum(ch) || strchr(safe_chars, ch))) { 198 *dst++ = ch; 199 } else { 200 /* encode this char */ 201 if (ep - dst < 3) 202 break; 203 snprintf(dst, ep - dst, "%%%02X", ch); 204 dst += 3; 205 } 206 encode_leading_slash = 0; 207 } 208 *dst = '\0'; 209 return dst; 210} 211 212static const char *ignored_if_names[] = { 213 "gre", /* net */ 214 "ipip", /* netinet */ 215 "gif", /* netinet6 */ 216 "faith", /* netinet6 */ 217 "lo", /* net */ 218 "lo0", /* net */ 219#if 0 220 "mdecap", /* netinet -- never in IF list (?) XXX */ 221#endif 222 "ppp", /* net */ 223#if 0 224 "sl", /* net */ 225#endif 226 "strip", /* net */ 227 "tun", /* net */ 228 /* XXX others? */ 229 NULL, 230}; 231 232static bool 233have_working_ipv4(void) 234{ 235 uint64_t ipstats[IP_NSTATS]; 236 size_t size = sizeof(ipstats); 237 238 /* At least some packets delivered to upper layers? */ 239 if (sysctlbyname("net.inet.ip.stats", ipstats, &size, NULL, 0) == -1) 240 return false; 241 if (ipstats[IP_STAT_DELIVERED] < 10) /* arbitrary threshold */ 242 return false; 243 244 /* do we have a default route? */ 245 if (run_program(RUN_SILENT|RUN_ERROR_OK, 246 "/sbin/route get -inet default") != 0) 247 return false; 248 249 return true; 250} 251 252#ifdef INET6 253static bool 254have_working_ipv6(void) 255{ 256 uint64_t ipstats[IP6_NSTATS]; 257 size_t size = sizeof(ipstats); 258 259 /* At least some packets delivered to upper layers? */ 260 if (sysctlbyname("net.inet6.ip6.stats", ipstats, &size, NULL, 0) == -1) 261 return false; 262 if (ipstats[IP6_STAT_DELIVERED] < 10) /* arbitrary threshold */ 263 return false; 264 265 /* do we have a default route? */ 266 if (run_program(RUN_SILENT|RUN_ERROR_OK, 267 "/sbin/route get -inet6 default") != 0) 268 return false; 269 270 return true; 271} 272#else 273#define have_working_ipv6() false 274#endif 275 276static int 277get_ifconfig_info(struct net_desc *devs) 278{ 279 char *buf_in; 280 char *buf_tmp; 281 const char **ignore; 282 char *buf; 283 char *tmp; 284 int textsize; 285 int i; 286 287 /* Get ifconfig information */ 288 textsize = collect(T_OUTPUT, &buf_in, "/sbin/ifconfig -l 2>/dev/null"); 289 if (textsize < 0) { 290 if (logfp) 291 (void)fprintf(logfp, 292 "Aborting: Could not run ifconfig.\n"); 293 (void)fprintf(stderr, "Could not run ifconfig."); 294 exit(1); 295 } 296 297 buf = malloc (STRSIZE * sizeof(char)); 298 for (i = 0, buf_tmp = buf_in; i < MAX_NETS && strlen(buf_tmp) > 0 299 && buf_tmp < buf_in + strlen(buf_in);) { 300 tmp = stpncpy(buf, buf_tmp, strcspn(buf_tmp," \n")); 301 *tmp='\0'; 302 buf_tmp += (strcspn(buf_tmp, " \n") + 1) * sizeof(char); 303 304 /* Skip ignored interfaces */ 305 for (ignore = ignored_if_names; *ignore != NULL; ignore++) { 306 size_t len = strlen(*ignore); 307 if (strncmp(buf, *ignore, len) == 0 && 308 isdigit((unsigned char)buf[len])) 309 break; 310 } 311 if (*ignore != NULL) 312 continue; 313 314 strlcpy (devs[i].if_dev, buf, STRSIZE); 315 i++; 316 } 317 if (i < MAX_NETS) 318 devs[i].if_dev[0] = 0; /* XXX ? */ 319 320 free(buf); 321 free(buf_in); 322 return i; 323} 324 325static int 326do_ifreq(struct ifreq *ifr, unsigned long cmd, void *data) 327{ 328 int sock; 329 int rval; 330 331 sock = socket(PF_INET, SOCK_DGRAM, 0); 332 if (sock == -1) 333 return -1; 334 335 memset(ifr, 0, sizeof *ifr); 336 ifr->ifr_data = data; 337 strlcpy(ifr->ifr_name, net_dev, sizeof ifr->ifr_name); 338 rval = ioctl(sock, cmd, ifr); 339 close(sock); 340 341 return rval; 342} 343 344static int 345do_ifmreq(struct ifmediareq *ifmr, unsigned long cmd) 346{ 347 int sock; 348 int rval; 349 350 sock = socket(PF_INET, SOCK_DGRAM, 0); 351 if (sock == -1) 352 return -1; 353 354 memset(ifmr, 0, sizeof *ifmr); 355 strlcpy(ifmr->ifm_name, net_dev, sizeof ifmr->ifm_name); 356 rval = ioctl(sock, cmd, ifmr); 357 close(sock); 358 359 return rval; 360} 361 362/* Fill in defaults network values for the selected interface */ 363static void 364get_ifinterface_info(void) 365{ 366 struct ifreq ifr; 367 struct ifmediareq ifmr; 368 struct sockaddr_in *sa_in = (void*)&ifr.ifr_addr; 369 int modew; 370 const char *media_opt; 371 const char *sep; 372 373 if (do_ifreq(&ifr, SIOCGIFADDR, NULL) == 0 && 374 sa_in->sin_addr.s_addr != 0) 375 strlcpy(net_ip, inet_ntoa(sa_in->sin_addr), sizeof net_ip); 376 377 if (do_ifreq(&ifr, SIOCGIFNETMASK, NULL) == 0 && 378 sa_in->sin_addr.s_addr != 0) 379 strlcpy(net_mask, inet_ntoa(sa_in->sin_addr), sizeof net_mask); 380 381 if (do_ifmreq(&ifmr, SIOCGIFMEDIA) == 0) { 382 /* Get the name of the media word */ 383 modew = ifmr.ifm_current; 384 strlcpy(net_media, get_media_subtype_string(modew), 385 sizeof net_media); 386 /* and add any media options */ 387 sep = " mediaopt "; 388 while ((media_opt = get_media_option_string(&modew)) != NULL) { 389 strlcat(net_media, sep, sizeof net_media); 390 strlcat(net_media, media_opt, sizeof net_media); 391 sep = ","; 392 } 393 } 394} 395 396#ifndef INET6 397#define get_if6interface_info() 398#else 399static void 400get_if6interface_info(void) 401{ 402 char *textbuf, *t; 403 int textsize; 404 405 textsize = collect(T_OUTPUT, &textbuf, 406 "/sbin/ifconfig %s inet6 2>/dev/null", net_dev); 407 if (textsize >= 0) { 408 char *p; 409 410 (void)strtok(textbuf, "\n"); /* ignore first line */ 411 while ((t = strtok(NULL, "\n")) != NULL) { 412 if (strncmp(t, "\tinet6 ", 7) != 0) 413 continue; 414 t += 7; 415 if (strstr(t, "tentative") || strstr(t, "duplicated")) 416 continue; 417 if (strncmp(t, "fe80:", 5) == 0) 418 continue; 419 420 p = t; 421 while (*p && *p != ' ' && *p != '\n') 422 p++; 423 *p = '\0'; 424 strlcpy(net_ip6, t, sizeof(net_ip6)); 425 break; 426 } 427 } 428 free(textbuf); 429} 430#endif 431 432static void 433get_host_info(void) 434{ 435 char hostname[MAXHOSTNAMELEN + 1]; 436 char *dot; 437 438 /* Check host (and domain?) name */ 439 if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != 0) { 440 hostname[sizeof(hostname) - 1] = 0; 441 /* check for a . */ 442 dot = strchr(hostname, '.'); 443 if (dot == NULL) { 444 /* if not found its just a host, punt on domain */ 445 strlcpy(net_host, hostname, sizeof net_host); 446 } else { 447 /* split hostname into host/domain parts */ 448 *dot++ = 0; 449 strlcpy(net_host, hostname, sizeof net_host); 450 strlcpy(net_domain, dot, sizeof net_domain); 451 } 452 } 453} 454 455/* 456 * recombine name parts split in get_host_info and config_network 457 * (common code moved here from write_etc_hosts) 458 */ 459static char * 460recombine_host_domain(void) 461{ 462 static char recombined[MAXHOSTNAMELEN + 1]; 463 int l = strlen(net_host) - strlen(net_domain); 464 465 strlcpy(recombined, net_host, sizeof(recombined)); 466 467 if (strlen(net_domain) != 0 && (l <= 0 || 468 net_host[l - 1] != '.' || 469 strcasecmp(net_domain, net_host + l) != 0)) { 470 /* net_host isn't an FQDN. */ 471 strlcat(recombined, ".", sizeof(recombined)); 472 strlcat(recombined, net_domain, sizeof(recombined)); 473 } 474 return recombined; 475} 476 477#ifdef INET6 478static int 479is_v6kernel(void) 480{ 481 int s; 482 483 s = socket(PF_INET6, SOCK_DGRAM, 0); 484 if (s < 0) 485 return 0; 486 close(s); 487 return 1; 488} 489#endif 490 491static int 492handle_license(const char *dev) 493{ 494 static struct { 495 const char *dev; 496 const char *lic; 497 } licdev[] = { 498 { "iwi", "/libdata/firmware/if_iwi/LICENSE.ipw2200-fw" }, 499 { "ipw", "/libdata/firmware/if_ipw/LICENSE" }, 500 }; 501 502 size_t i; 503 504 for (i = 0; i < __arraycount(licdev); i++) 505 if (strncmp(dev, licdev[i].dev, 3) == 0) { 506 char buf[64]; 507 int val; 508 size_t len = sizeof(int); 509 (void)snprintf(buf, sizeof(buf), "hw.%s.accept_eula", 510 licdev[i].dev); 511 if (sysctlbyname(buf, &val, &len, NULL, 0) != -1 512 && val != 0) 513 return 1; 514 msg_fmt_display(MSG_license, "%s%s", 515 dev, licdev[i].lic); 516 if (ask_yesno(NULL)) { 517 val = 1; 518 if (sysctlbyname(buf, NULL, NULL, &val, 519 0) == -1) 520 return 0; 521 add_sysctl_conf("%s=1", buf); 522 return 1; 523 } else 524 return 0; 525 } 526 return 1; 527} 528 529/* 530 * Get the information to configure the network, configure it and 531 * make sure both the gateway and the name server are up. 532 */ 533int 534config_network(int force) 535{ 536 char *textbuf; 537 int octet0; 538 int dhcp_config; 539 int nfs_root = 0; 540 int slip = 0; 541 int pid, status; 542 char **ap, *slcmd[10], *in_buf; 543 char buffer[STRSIZE]; 544 char hostname[MAXHOSTNAMELEN + 1]; 545 struct statvfs sb; 546 struct net_desc net_devs[MAX_NETS]; 547 menu_ent *net_menu; 548 int menu_no; 549 int num_devs; 550 int selected_net; 551 int i; 552#ifdef INET6 553 int v6config = 1, rv; 554#endif 555 556 FILE *f; 557 time_t now; 558 559 if (network_up) 560 return (1); 561 562 num_devs = get_ifconfig_info(net_devs); 563 564 if (num_devs < 1) { 565 /* No network interfaces found! */ 566 hit_enter_to_continue(NULL, MSG_nonet); 567 return -1; 568 } 569 570 if (!force && (have_working_ipv4() || have_working_ipv6())) { 571 if (ask_yesno(MSG_network_ok)) { 572 network_up = 1; 573 return 1; 574 } 575 } 576 577 net_menu = calloc(num_devs, sizeof(*net_menu)); 578 if (net_menu == NULL) { 579 err_msg_win(err_outofmem); 580 return -1; 581 } 582 583 for (i = 0; i < num_devs; i++) { 584 net_menu[i].opt_name = net_devs[i].if_dev; 585 net_menu[i].opt_flags = OPT_EXIT; 586 net_menu[i].opt_action = set_menu_select; 587 } 588 589 menu_no = new_menu(MSG_netdevs, 590 net_menu, num_devs, -1, 4, 0, 0, 591 MC_SCROLL, 592 NULL, NULL, NULL, NULL, MSG_cancel); 593again: 594 selected_net = -1; 595 msg_display(MSG_asknetdev); 596 process_menu(menu_no, &selected_net); 597 msg_clear(); 598 599 if (selected_net == -1) { 600 free_menu(menu_no); 601 free(net_menu); 602 return 0; 603 } 604 605 network_up = 1; 606 dhcp_config = 0; 607 608 strlcpy(net_dev, net_devs[selected_net].if_dev, sizeof net_dev); 609 610 if (!handle_license(net_dev)) 611 goto done; 612 613 slip = net_dev[0] == 's' && net_dev[1] == 'l' && 614 isdigit((unsigned char)net_dev[2]); 615 616 /* If root is on NFS do not reconfigure the interface. */ 617 if (statvfs("/", &sb) == 0 && strcmp(sb.f_fstypename, "nfs") == 0) { 618 nfs_root = 1; 619 get_ifinterface_info(); 620 get_if6interface_info(); 621 get_host_info(); 622 } else if (!slip) { 623 /* Preload any defaults we can find */ 624 get_ifinterface_info(); 625 get_if6interface_info(); 626 get_host_info(); 627 628 /* domain and host */ 629 msg_display(MSG_netinfo); 630 631 if (!config_wlan(net_dev)) { 632 config_eth_medium(net_dev); 633 } 634 635 net_dhcpconf = 0; 636 /* try a dhcp configuration */ 637 dhcp_config = config_dhcp(net_dev); 638 if (dhcp_config) { 639 char *nline; 640 641 /* Get newly configured data off interface. */ 642 get_ifinterface_info(); 643 get_if6interface_info(); 644 get_host_info(); 645 646 net_dhcpconf |= DHCPCONF_IPADDR; 647 648 /* 649 * Extract default route from output of 650 * 'route -n show' 651 */ 652 if (collect(T_OUTPUT, &textbuf, 653 "/sbin/route -n show | " 654 "while read dest gateway flags;" 655 " do [ \"$dest\" = default ] && {" 656 " echo \"$gateway\"; break; };" 657 " done" ) > 0) 658 strlcpy(net_defroute, textbuf, 659 sizeof net_defroute); 660 free(textbuf); 661 if ((nline = strchr(net_defroute, '\n'))) 662 *nline = '\0'; 663 664 /* pull nameserver info out of /etc/resolv.conf */ 665 if (collect(T_OUTPUT, &textbuf, 666 "cat /etc/resolv.conf 2>/dev/null |" 667 " while read keyword address rest;" 668 " do [ \"$keyword\" = nameserver ] &&" 669 " { echo \"$address\"; break; };" 670 " done" ) > 0) 671 strlcpy(net_namesvr, textbuf, 672 sizeof net_namesvr); 673 free(textbuf); 674 if ((nline = strchr(net_namesvr, '\n'))) 675 *nline = '\0'; 676 if (net_namesvr[0] != '\0') 677 net_dhcpconf |= DHCPCONF_NAMESVR; 678 679 /* pull domain info out of /etc/resolv.conf */ 680 if (collect(T_OUTPUT, &textbuf, 681 "cat /etc/resolv.conf 2>/dev/null |" 682 " while read keyword domain rest;" 683 " do [ \"$keyword\" = domain ] &&" 684 " { echo \"$domain\"; break; };" 685 " done" ) > 0) 686 strlcpy(net_domain, textbuf, 687 sizeof net_domain); 688 free(textbuf); 689 if (net_domain[0] == '\0') { 690 /* pull domain info out of /etc/resolv.conf */ 691 if (collect(T_OUTPUT, &textbuf, 692 "cat /etc/resolv.conf 2>/dev/null |" 693 " while read keyword search rest;" 694 " do [ \"$keyword\" = search ] &&" 695 " { echo \"$search\"; break; };" 696 " done" ) > 0) 697 strlcpy(net_domain, textbuf, 698 sizeof net_domain); 699 free(textbuf); 700 } 701 if ((nline = strchr(net_domain, '\n'))) 702 *nline = '\0'; 703 if (net_domain[0] != '\0') 704 net_dhcpconf |= DHCPCONF_DOMAIN; 705 706 if (gethostname(net_host, sizeof(net_host)) == 0 && 707 net_host[0] != 0) 708 net_dhcpconf |= DHCPCONF_HOST; 709 } 710 } 711 712 /* 713 * Prompt for hostname and domain, even when using DHCP. The names 714 * discovered on the network may not match the desired values 715 * for the target system. 716 */ 717 strlcpy(hostname, recombine_host_domain(), MAXHOSTNAMELEN); 718 msg_prompt_add(MSG_net_host, net_host, net_host, 719 sizeof net_host); 720 msg_prompt_add(MSG_net_domain, net_domain, net_domain, 721 sizeof net_domain); 722 if (strcmp(hostname, recombine_host_domain()) != 0) { 723 net_dhcpconf &= ~(DHCPCONF_DOMAIN|DHCPCONF_HOST); 724 } 725 726 if (!dhcp_config) { 727 /* Manually configure IPv4 */ 728 if (!nfs_root) 729 msg_prompt_add(MSG_net_ip, net_ip, net_ip, 730 sizeof net_ip); 731 if (slip) 732 msg_prompt_add(MSG_net_srv_ip, net_srv_ip, net_srv_ip, 733 sizeof net_srv_ip); 734 else if (!nfs_root) { 735 /* We don't want netmasks for SLIP */ 736 octet0 = atoi(net_ip); 737 if (!net_mask[0]) { 738 if (0 <= octet0 && octet0 <= 127) 739 strlcpy(net_mask, "0xff000000", 740 sizeof(net_mask)); 741 else if (128 <= octet0 && octet0 <= 191) 742 strlcpy(net_mask, "0xffff0000", 743 sizeof(net_mask)); 744 else if (192 <= octet0 && octet0 <= 223) 745 strlcpy(net_mask, "0xffffff00", 746 sizeof(net_mask)); 747 } 748 msg_prompt_add(MSG_net_mask, net_mask, net_mask, 749 sizeof net_mask); 750 } 751 msg_prompt_add(MSG_net_defroute, net_defroute, net_defroute, 752 sizeof net_defroute); 753 } 754 755 if (!(net_dhcpconf & DHCPCONF_NAMESVR)) { 756#ifdef INET6 757 if (v6config) { 758 rv = 0; 759 process_menu(MENU_namesrv6, &rv); 760 if (!rv) 761 msg_prompt_add(MSG_net_namesrv, net_namesvr, 762 net_namesvr, sizeof net_namesvr); 763 } else 764#endif 765 msg_prompt_add(MSG_net_namesrv, net_namesvr, net_namesvr, 766 sizeof net_namesvr); 767 } 768 769 /* confirm the setting */ 770 msg_clear(); 771 if (slip) 772 msg_fmt_table_add(MSG_netok_slip, "%s%s%s%s%s%s%s%s%s", 773 net_domain, 774 net_host, 775 *net_namesvr == '\0' ? "<none>" : net_namesvr, 776 net_dev, 777 *net_media == '\0' ? "<default>" : net_media, 778 *net_ip == '\0' ? "<none>" : net_ip, 779 *net_srv_ip == '\0' ? "<none>" : net_srv_ip, 780 *net_mask == '\0' ? "<none>" : net_mask, 781 *net_defroute == '\0' ? "<none>" : net_defroute); 782 else 783 msg_fmt_table_add(MSG_netok, "%s%s%s%s%s%s%s%s", 784 net_domain, 785 net_host, 786 *net_namesvr == '\0' ? "<none>" : net_namesvr, 787 net_dev, 788 *net_media == '\0' ? "<default>" : net_media, 789 *net_ip == '\0' ? "<none>" : net_ip, 790 *net_mask == '\0' ? "<none>" : net_mask, 791 *net_defroute == '\0' ? "<none>" : net_defroute); 792#ifdef INET6 793 msg_fmt_table_add(MSG_netokv6, "%s", 794 !is_v6kernel() ? "<not supported>" : net_ip6); 795#endif 796done: 797 if (!ask_yesno(MSG_netok_ok)) 798 goto again; 799 800 free_menu(menu_no); 801 free(net_menu); 802 803 run_program(0, "/sbin/ifconfig lo0 127.0.0.1"); 804 805 /* dhcpcd will have configured it all for us */ 806 if (dhcp_config) { 807 fflush(NULL); 808 network_up = 1; 809 return network_up; 810 } 811 812 /* 813 * we may want to perform checks against inconsistent configuration, 814 * like IPv4 DNS server without IPv4 configuration. 815 */ 816 817 /* Create /etc/resolv.conf if a nameserver was given */ 818 if (net_namesvr[0] != '\0') { 819 f = fopen("/etc/resolv.conf", "w"); 820 if (f == NULL) { 821 if (logfp) 822 (void)fprintf(logfp, 823 "%s", msg_string(MSG_resolv)); 824 (void)fprintf(stderr, "%s", msg_string(MSG_resolv)); 825 exit(1); 826 } 827 scripting_fprintf(NULL, "cat <<EOF >/etc/resolv.conf\n"); 828 time(&now); 829 scripting_fprintf(f, ";\n; BIND data file\n; %s %s;\n", 830 "Created by NetBSD sysinst on", safectime(&now)); 831 if (net_domain[0] != '\0') 832 scripting_fprintf(f, "search %s\n", net_domain); 833 if (net_namesvr[0] != '\0') 834 scripting_fprintf(f, "nameserver %s\n", net_namesvr); 835 scripting_fprintf(NULL, "EOF\n"); 836 fflush(NULL); 837 fclose(f); 838 } 839 840 if (net_ip[0] != '\0') { 841 if (slip) { 842 /* XXX: needs 'ifconfig sl0 create' much earlier */ 843 /* Set SLIP interface UP */ 844 run_program(0, "/sbin/ifconfig %s inet %s %s up", 845 net_dev, net_ip, net_srv_ip); 846 strcpy(sl_flags, "-s 115200 -l /dev/tty00"); 847 msg_prompt_win(MSG_slattach, -1, 12, 70, 0, 848 sl_flags, sl_flags, sizeof sl_flags); 849 850 /* XXX: wtf isn't run_program() used here? */ 851 pid = fork(); 852 if (pid == 0) { 853 strcpy(buffer, "/sbin/slattach "); 854 strcat(buffer, sl_flags); 855 in_buf = buffer; 856 857 for (ap = slcmd; (*ap = strsep(&in_buf, " ")) != NULL;) 858 if (**ap != '\0') 859 ++ap; 860 861 execvp(slcmd[0], slcmd); 862 } else 863 wait4(pid, &status, WNOHANG, 0); 864 } else if (!nfs_root) { 865 if (net_mask[0] != '\0') { 866 run_program(0, "/sbin/ifconfig %s inet %s netmask %s", 867 net_dev, net_ip, net_mask); 868 } else { 869 run_program(0, "/sbin/ifconfig %s inet %s", 870 net_dev, net_ip); 871 } 872 } 873 } 874 875 /* Set host name */ 876 if (net_host[0] != '\0') 877 sethostname(net_host, strlen(net_host)); 878 879 /* Set a default route if one was given */ 880 if (!nfs_root && net_defroute[0] != '\0') { 881 run_program(RUN_DISPLAY | RUN_PROGRESS, 882 "/sbin/route -n flush -inet"); 883 run_program(RUN_DISPLAY | RUN_PROGRESS, 884 "/sbin/route -n add default %s", net_defroute); 885 } 886 887 /* 888 * wait for addresses to become valid 889 */ 890 if (!nfs_root) { 891 msg_display_add(MSG_wait_network); 892 network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS, 893 "/sbin/ifconfig -w 15 -W 5"); 894 } else { 895 /* Assume network is up. */ 896 network_up = 1; 897 } 898 899 fflush(NULL); 900 901 return network_up; 902} 903 904const char * 905url_proto(unsigned int xfer) 906{ 907 switch (xfer) { 908 case XFER_FTP: return "ftp"; 909 case XFER_HTTP: return "http"; 910 case XFER_HTTPS: return "https"; 911 } 912 913 return ""; 914} 915 916void 917make_url(char *urlbuffer, struct ftpinfo *f, const char *dir) 918{ 919 char ftp_user_encoded[STRSIZE]; 920 char ftp_dir_encoded[STRSIZE]; 921 char *cp; 922 const char *dir2; 923 924 /* 925 * f->pass is quite likely to contain unsafe characters 926 * that need to be encoded in the URL (for example, 927 * "@", ":" and "/" need quoting). Let's be 928 * paranoid and also encode f->user and f->dir. (For 929 * example, f->dir could easily contain '~', which is 930 * unsafe by a strict reading of RFC 1738). 931 */ 932 if (strcmp("ftp", f->user) == 0 && f->pass[0] == 0) { 933 ftp_user_encoded[0] = 0; 934 } else { 935 cp = url_encode(ftp_user_encoded, f->user, 936 ftp_user_encoded + sizeof ftp_user_encoded - 1, 937 RFC1738_SAFE_LESS_SHELL, 0); 938 *cp++ = ':'; 939 cp = url_encode(cp, f->pass, 940 ftp_user_encoded + sizeof ftp_user_encoded - 1, 941 NULL, 0); 942 *cp++ = '@'; 943 *cp = 0; 944 } 945 cp = url_encode(ftp_dir_encoded, f->dir, 946 ftp_dir_encoded + sizeof ftp_dir_encoded - 1, 947 RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 1); 948 if (cp != ftp_dir_encoded && cp[-1] != '/') 949 *cp++ = '/'; 950 951 dir2 = dir; 952 while (*dir2 == '/') 953 ++dir2; 954 955 url_encode(cp, dir2, 956 ftp_dir_encoded + sizeof ftp_dir_encoded, 957 RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 0); 958 959 snprintf(urlbuffer, STRSIZE, "%s://%s%s/%s", url_proto(f->xfer), 960 ftp_user_encoded, f->xfer_host[XFER_HOST(f->xfer)], 961 ftp_dir_encoded); 962} 963 964 965/* ftp_fetch() and pkgsrc_fetch() are essentially the same, with a different 966 * ftpinfo var and pkgsrc always using .tgz suffix, while for 967 * regular sets we only use .tgz for source sets on some architectures. */ 968static int do_ftp_fetch(const char *, bool, struct ftpinfo *); 969 970static int 971ftp_fetch(const char *set_name) 972{ 973 return do_ftp_fetch(set_name, use_tgz_for_set(set_name), &ftp); 974} 975 976static int 977pkgsrc_fetch(const char *set_name) 978{ 979 return do_ftp_fetch(set_name, true, &pkgsrc); 980} 981 982static int 983do_ftp_fetch(const char *set_name, bool force_tgz, struct ftpinfo *f) 984{ 985 const char *ftp_opt; 986 char url[STRSIZE]; 987 int rval; 988 989 /* 990 * Invoke ftp to fetch the file. 991 */ 992 if (strcmp("ftp", f->user) == 0 && f->pass[0] == 0) { 993 /* do anon ftp */ 994 ftp_opt = "-a "; 995 } else { 996 ftp_opt = ""; 997 } 998 999 make_url(url, f, set_dir_for_set(set_name)); 1000 rval = run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_XFER_DIR, 1001 "/usr/bin/ftp %s%s/%s%s", 1002 ftp_opt, url, set_name, 1003 force_tgz ? dist_tgz_postfix : dist_postfix); 1004 1005 return rval ? SET_RETRY : SET_OK; 1006} 1007 1008 1009// XXX: check MSG_netnotup_continueanyway and MSG_netnotup 1010 1011int 1012get_pkgsrc(void) 1013{ 1014 int rv = -1; 1015 1016 process_menu(MENU_pkgsrc, &rv); 1017 1018 if (rv == SET_SKIP) 1019 return SET_SKIP; 1020 1021 fetch_fn = pkgsrc_fetch; 1022 snprintf(ext_dir_pkgsrc, sizeof ext_dir_pkgsrc, "%s/%s", 1023 target_prefix(), xfer_dir + (*xfer_dir == '/')); 1024 1025 return SET_OK; 1026} 1027 1028int 1029get_via_ftp(unsigned int xfer) 1030{ 1031 arg_rv arg; 1032 1033 if (!network_up) 1034 config_network(0); 1035 1036 arg.rv = -1; 1037 arg.arg = (void*)(uintptr_t)(xfer); 1038 process_menu(MENU_ftpsource, &arg); 1039 1040 if (arg.rv == SET_RETRY) 1041 return SET_RETRY; 1042 1043 /* We'll fetch each file just before installing it */ 1044 fetch_fn = ftp_fetch; 1045 ftp.xfer = xfer; 1046 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s", target_prefix(), 1047 xfer_dir + (*xfer_dir == '/')); 1048 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s", target_prefix(), 1049 xfer_dir + (*xfer_dir == '/')); 1050 1051 return SET_OK; 1052} 1053 1054int 1055get_via_nfs(void) 1056{ 1057 struct statvfs sb; 1058 int rv; 1059 1060 /* If root is on NFS and we have sets, skip this step. */ 1061 if (statvfs(set_dir_bin, &sb) == 0 && 1062 strcmp(sb.f_fstypename, "nfs") == 0) { 1063 strlcpy(ext_dir_bin, set_dir_bin, sizeof ext_dir_bin); 1064 strlcpy(ext_dir_src, set_dir_src, sizeof ext_dir_src); 1065 return SET_OK; 1066 } 1067 1068 /* Get server and filepath */ 1069 rv = -1; 1070 process_menu(MENU_nfssource, &rv); 1071 1072 if (rv == SET_RETRY) 1073 return SET_RETRY; 1074 1075 /* Mount it */ 1076 if (run_program(0, "/sbin/mount -r -o -2,-i,-r=1024 -t nfs %s:%s /mnt2", 1077 nfs_host, nfs_dir)) 1078 return SET_RETRY; 1079 1080 mnt2_mounted = 1; 1081 1082 snprintf(ext_dir_bin, sizeof ext_dir_bin, "/mnt2/%s", set_dir_bin); 1083 snprintf(ext_dir_src, sizeof ext_dir_src, "/mnt2/%s", set_dir_src); 1084 1085 /* return location, don't clean... */ 1086 return SET_OK; 1087} 1088 1089/* 1090 * write the new contents of /etc/hosts to the specified file 1091 */ 1092static void 1093write_etc_hosts(FILE *f) 1094{ 1095 scripting_fprintf(f, "#\n"); 1096 scripting_fprintf(f, "# Added by NetBSD sysinst\n"); 1097 scripting_fprintf(f, "#\n"); 1098 1099 if (net_domain[0] != '\0') 1100 scripting_fprintf(f, "127.0.0.1 localhost.%s\n", net_domain); 1101 1102 scripting_fprintf(f, "%s\t", net_ip); 1103 if (net_domain[0] != '\0') 1104 scripting_fprintf(f, "%s ", recombine_host_domain()); 1105 scripting_fprintf(f, "%s\n", net_host); 1106} 1107 1108/* 1109 * Write the network config info the user entered via menus into the 1110 * config files in the target disk. Be careful not to lose any 1111 * information we don't immediately add back, in case the install 1112 * target is the currently-active root. 1113 */ 1114void 1115mnt_net_config(void) 1116{ 1117 char ifconfig_fn[STRSIZE]; 1118 FILE *ifconf = NULL; 1119 1120 if (!network_up) 1121 return; 1122 if (!ask_yesno(MSG_mntnetconfig)) 1123 return; 1124 1125 /* Write hostname to /etc/rc.conf */ 1126 if ((net_dhcpconf & DHCPCONF_HOST) == 0) 1127 if (del_rc_conf("hostname") == 0) 1128 add_rc_conf("hostname=%s\n", recombine_host_domain()); 1129 1130 /* Copy resolv.conf to target. If DHCP was used to create it, 1131 * it will be replaced on next boot anyway. */ 1132 if (net_namesvr[0] != '\0') 1133 dup_file_into_target("/etc/resolv.conf"); 1134 1135 /* Copy wpa_supplicant.conf to target. */ 1136 if (net_ssid[0] != '\0') 1137 dup_file_into_target("/etc/wpa_supplicant.conf"); 1138 1139 /* 1140 * bring the interface up, it will be necessary for IPv6, and 1141 * it won't make trouble with IPv4 case either 1142 */ 1143 snprintf(ifconfig_fn, sizeof ifconfig_fn, "/etc/ifconfig.%s", net_dev); 1144 ifconf = target_fopen(ifconfig_fn, "w"); 1145 if (ifconf != NULL) { 1146 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n", 1147 target_prefix(), ifconfig_fn); 1148 scripting_fprintf(ifconf, "up\n"); 1149 if (*net_media != '\0') 1150 scripting_fprintf(ifconf, "media %s\n", net_media); 1151 scripting_fprintf(NULL, "EOF\n"); 1152 } 1153 1154 if ((net_dhcpconf & DHCPCONF_IPADDR) == 0) { 1155 FILE *hosts; 1156 1157 /* Write IPaddr and netmask to /etc/ifconfig.if[0-9] */ 1158 if (ifconf != NULL) { 1159 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n", 1160 target_prefix(), ifconfig_fn); 1161 if (*net_media != '\0') 1162 scripting_fprintf(ifconf, 1163 "%s netmask %s media %s\n", 1164 net_ip, net_mask, net_media); 1165 else 1166 scripting_fprintf(ifconf, "%s netmask %s\n", 1167 net_ip, net_mask); 1168 scripting_fprintf(NULL, "EOF\n"); 1169 } 1170 1171 /* 1172 * Add IPaddr/hostname to /etc/hosts. 1173 * Be careful not to clobber any existing contents. 1174 * Relies on ordered search of /etc/hosts. XXX YP? 1175 */ 1176 hosts = target_fopen("/etc/hosts", "a"); 1177 if (hosts != 0) { 1178 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/hosts\n", 1179 target_prefix()); 1180 write_etc_hosts(hosts); 1181 (void)fclose(hosts); 1182 scripting_fprintf(NULL, "EOF\n"); 1183 } 1184 1185 if (del_rc_conf("defaultroute") == 0) 1186 add_rc_conf("defaultroute=\"%s\"\n", net_defroute); 1187 } else { 1188 /* 1189 * Start dhcpcd quietly and in master mode, but restrict 1190 * it to our interface 1191 */ 1192 add_rc_conf("dhcpcd=YES\n"); 1193 add_rc_conf("dhcpcd_flags=\"-qM %s\"\n", net_dev); 1194 } 1195 1196 if (net_ssid[0] != '\0') { 1197 add_rc_conf("wpa_supplicant=YES\n"); 1198 add_rc_conf("wpa_supplicant_flags=\"-B -s -i %s -D bsd -c /etc/wpa_supplicant.conf\"\n", net_dev); 1199 } 1200 1201 if (ifconf) 1202 fclose(ifconf); 1203 1204 fflush(NULL); 1205} 1206 1207int 1208config_wlan(char *inter) 1209{ 1210 FILE *wpa_conf = NULL; 1211 char wpa_cmd[256]; 1212 struct ifreq ifr = {0}; 1213 struct ieee80211_nwid nwid = {0}; 1214 1215 /* skip non-WLAN devices */ 1216 if (do_ifreq(&ifr, SIOCG80211NWID, &nwid) == -1) 1217 return 0; 1218 1219 if (!file_mode_match(WPA_SUPPLICANT, S_IFREG)) 1220 return 0; 1221 1222 msg_prompt_add(MSG_net_ssid, net_ssid, net_ssid, 1223 sizeof net_ssid); 1224 if (net_ssid[0] == '\0') 1225 return 0; 1226 1227 msg_prompt_noecho(MSG_net_passphrase, net_passphrase, net_passphrase, 1228 sizeof net_passphrase); 1229 1230 wpa_conf = fopen("/etc/wpa_supplicant.conf", "a"); 1231 if (wpa_conf == NULL) 1232 return 0; 1233 1234 scripting_fprintf(NULL, 1235 "cat <<EOF >>%s/etc/wpa_supplicant.conf\n", 1236 target_prefix()); 1237 scripting_fprintf(wpa_conf, "\n#\n"); 1238 scripting_fprintf(wpa_conf, "# Added by NetBSD sysinst\n"); 1239 scripting_fprintf(wpa_conf, "#\n"); 1240 scripting_fprintf(wpa_conf, "network={\n"); 1241 scripting_fprintf(wpa_conf, 1242 "\tssid=\"%s\"\n", net_ssid); 1243 if (net_passphrase[0] != '\0') { 1244 scripting_fprintf(wpa_conf, "\tpsk=\"%s\"\n", 1245 net_passphrase); 1246 } else { 1247 scripting_fprintf(wpa_conf, "\tkey_mgmt=NONE\n"); 1248 } 1249 scripting_fprintf(wpa_conf, "\tscan_ssid=1\n"); 1250 scripting_fprintf(wpa_conf, "}\n"); 1251 (void)fclose(wpa_conf); 1252 scripting_fprintf(NULL, "EOF\n"); 1253 1254 if (run_program(RUN_DISPLAY | RUN_PROGRESS, 1255 "/sbin/ifconfig %s up", inter) != 0) 1256 return 0; 1257 1258 /* 1259 * have to use system() here to avoid the server process dying 1260 */ 1261 if (snprintf(wpa_cmd, sizeof(wpa_cmd), 1262 WPA_SUPPLICANT 1263 " -B -s -i %s -D bsd -c /etc/wpa_supplicant.conf", inter) < 0) 1264 return 0; 1265 (void)do_system(wpa_cmd); 1266 1267 return 1; 1268} 1269 1270int 1271config_dhcp(char *inter) 1272{ 1273 int dhcpautoconf; 1274 1275 /* 1276 * Don't bother checking for an existing instance of dhcpcd, just 1277 * ask it to renew the lease. It will fork and daemonize if there 1278 * wasn't already an instance. 1279 */ 1280 1281 if (!file_mode_match(DHCPCD, S_IFREG)) 1282 return 0; 1283 if (ask_yesno(MSG_Perform_autoconfiguration)) { 1284 /* spawn off dhcpcd and wait for parent to exit */ 1285 dhcpautoconf = run_program(RUN_DISPLAY | RUN_PROGRESS, 1286 "%s -d -n %s", DHCPCD, inter); 1287 return dhcpautoconf ? 0 : 1; 1288 } 1289 return 0; 1290} 1291 1292 1293int 1294config_eth_medium(char *inter) 1295{ 1296 char *textbuf = NULL; 1297 1298 for (;;) { 1299 msg_prompt_add(MSG_net_media, net_media, net_media, 1300 sizeof net_media); 1301 1302 /* 1303 * ifconfig does not allow media specifiers on 1304 * IFM_MANUAL interfaces. Our UI gives no way 1305 * to set an option back 1306 * to null-string if it gets accidentally set. 1307 * Check for plausible alternatives. 1308 */ 1309 if (strcmp(net_media, "<default>") == 0 || 1310 strcmp(net_media, "default") == 0 || 1311 strcmp(net_media, "<manual>") == 0 || 1312 strcmp(net_media, "manual") == 0 || 1313 strcmp(net_media, "<none>") == 0 || 1314 strcmp(net_media, "none") == 0 || 1315 strcmp(net_media, " ") == 0) { 1316 *net_media = '\0'; 1317 } 1318 1319 if (*net_media == '\0') 1320 break; 1321 /* 1322 * We must set the media type here - to give dhcp 1323 * a chance 1324 */ 1325 if (run_program(0, "/sbin/ifconfig %s media %s", 1326 net_dev, net_media) == 0) 1327 break; 1328 /* Failed to set - output the supported values */ 1329 if (collect(T_OUTPUT, &textbuf, "/sbin/ifconfig -m %s |" 1330 "while IFS=; read line;" 1331 " do [ \"$line\" = \"${line#*media}\" ] || " 1332 "echo $line;" 1333 " done", net_dev ) > 0) 1334 msg_display(textbuf); 1335 free(textbuf); 1336 } 1337 return 0; 1338} 1339