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