1 | /* |
2 | * Copyright (c) 2001-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | /* |
30 | * History: |
31 | * 14 December, 2001 Dieter Siegmund (dieter@apple.com) |
32 | * - created |
33 | */ |
34 | #include <sys/param.h> |
35 | #include <sys/systm.h> |
36 | #include <sys/kernel.h> |
37 | #include <sys/conf.h> |
38 | #include <sys/ioctl.h> |
39 | #include <sys/proc_internal.h> |
40 | #include <sys/mount_internal.h> |
41 | #include <sys/mbuf.h> |
42 | #include <sys/filedesc.h> |
43 | #include <sys/vnode_internal.h> |
44 | #include <sys/malloc.h> |
45 | #include <sys/socket.h> |
46 | #include <sys/socketvar.h> |
47 | #include <sys/reboot.h> |
48 | #include <sys/kauth.h> |
49 | #include <net/if.h> |
50 | #include <net/if_dl.h> |
51 | #include <net/if_types.h> |
52 | #include <net/route.h> |
53 | #include <netinet/in.h> |
54 | #include <netinet/if_ether.h> |
55 | #include <netinet/dhcp_options.h> |
56 | |
57 | #include <kern/kern_types.h> |
58 | #include <kern/kalloc.h> |
59 | #include <sys/netboot.h> |
60 | #include <sys/imageboot.h> |
61 | #include <pexpert/pexpert.h> |
62 | |
63 | extern int (*mountroot)(void); |
64 | |
65 | extern unsigned char rootdevice[]; |
66 | |
67 | static int S_netboot = 0; |
68 | static struct netboot_info * S_netboot_info_p; |
69 | |
70 | void * |
71 | IOBSDRegistryEntryForDeviceTree(const char * path); |
72 | |
73 | void |
74 | IOBSDRegistryEntryRelease(void * entry); |
75 | |
76 | const void * |
77 | IOBSDRegistryEntryGetData(void * entry, const char * property_name, |
78 | int * packet_length); |
79 | |
80 | #define BOOTP_RESPONSE "bootp-response" |
81 | #define BSDP_RESPONSE "bsdp-response" |
82 | #define DHCP_RESPONSE "dhcp-response" |
83 | |
84 | #define IP_FORMAT "%d.%d.%d.%d" |
85 | #define IP_CH(ip) ((u_char *)ip) |
86 | #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] |
87 | |
88 | #define kNetBootRootPathPrefixNFS "nfs:" |
89 | #define kNetBootRootPathPrefixHTTP "http:" |
90 | |
91 | typedef enum { |
92 | kNetBootImageTypeUnknown = 0, |
93 | kNetBootImageTypeNFS = 1, // Deprecated |
94 | kNetBootImageTypeHTTP = 2, |
95 | } NetBootImageType; |
96 | |
97 | struct netboot_info { |
98 | struct in_addr client_ip; |
99 | struct in_addr server_ip; |
100 | char * server_name; |
101 | size_t server_name_length; |
102 | char * mount_point; |
103 | size_t mount_point_length; |
104 | char * image_path; |
105 | size_t image_path_length; |
106 | NetBootImageType image_type; |
107 | char * second_image_path; |
108 | size_t second_image_path_length; |
109 | }; |
110 | |
111 | /* |
112 | * Function: parse_booter_path |
113 | * Purpose: |
114 | * Parse a string of the form: |
115 | * "<IP>:<host>:<mount>[:<image_path>]" |
116 | * into the given ip address, host, mount point, and optionally, image_path. |
117 | * |
118 | * Note: |
119 | * The passed in string is modified i.e. ':' is replaced by '\0'. |
120 | * Example: |
121 | * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera" |
122 | */ |
123 | static __inline__ boolean_t |
124 | parse_booter_path(char * path, struct in_addr * iaddr_p, char const * * host, |
125 | char * * mount_dir, char * * image_path) |
126 | { |
127 | char * start; |
128 | char * colon; |
129 | |
130 | /* IP address */ |
131 | start = path; |
132 | colon = strchr(s: start, c: ':'); |
133 | if (colon == NULL) { |
134 | return FALSE; |
135 | } |
136 | *colon = '\0'; |
137 | if (inet_aton(start, iaddr_p) != 1) { |
138 | return FALSE; |
139 | } |
140 | |
141 | /* host */ |
142 | start = colon + 1; |
143 | colon = strchr(s: start, c: ':'); |
144 | if (colon == NULL) { |
145 | return FALSE; |
146 | } |
147 | *colon = '\0'; |
148 | *host = start; |
149 | |
150 | /* mount */ |
151 | start = colon + 1; |
152 | colon = strchr(s: start, c: ':'); |
153 | *mount_dir = start; |
154 | if (colon == NULL) { |
155 | *image_path = NULL; |
156 | } else { |
157 | /* image path */ |
158 | *colon = '\0'; |
159 | start = colon + 1; |
160 | *image_path = start; |
161 | } |
162 | return TRUE; |
163 | } |
164 | |
165 | /* |
166 | * Function: find_colon |
167 | * Purpose: |
168 | * Find the next unescaped instance of the colon character. |
169 | * If a colon is escaped (preceded by a backslash '\' character), |
170 | * shift the string over by one character to overwrite the backslash. |
171 | */ |
172 | static __inline__ char * |
173 | find_colon(char * str) |
174 | { |
175 | char * start = str; |
176 | char * colon; |
177 | |
178 | while ((colon = strchr(s: start, c: ':')) != NULL) { |
179 | char * dst; |
180 | char * src; |
181 | |
182 | if (colon == start) { |
183 | break; |
184 | } |
185 | if (colon[-1] != '\\') { |
186 | break; |
187 | } |
188 | for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) { |
189 | *dst = *src; |
190 | } |
191 | start = colon; |
192 | } |
193 | return colon; |
194 | } |
195 | |
196 | /* |
197 | * Function: parse_netboot_path |
198 | * Purpose: |
199 | * Parse a string of the form: |
200 | * "nfs:<IP>:<mount>[:<image_path>]" |
201 | * into the given ip address, host, mount point, and optionally, image_path. |
202 | * Notes: |
203 | * - the passed in string is modified i.e. ':' is replaced by '\0' |
204 | * - literal colons must be escaped with a backslash |
205 | * |
206 | * Examples: |
207 | * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg |
208 | * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg |
209 | */ |
210 | static __inline__ boolean_t |
211 | parse_netboot_path(char * path, struct in_addr * iaddr_p, char const * * host, |
212 | char * * mount_dir, char * * image_path) |
213 | { |
214 | static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */ |
215 | char * start; |
216 | char * colon; |
217 | |
218 | if (strncmp(s1: path, kNetBootRootPathPrefixNFS, |
219 | n: strlen(kNetBootRootPathPrefixNFS)) != 0) { |
220 | return FALSE; |
221 | } |
222 | |
223 | /* IP address */ |
224 | start = path + strlen(kNetBootRootPathPrefixNFS); |
225 | colon = strchr(s: start, c: ':'); |
226 | if (colon == NULL) { |
227 | return FALSE; |
228 | } |
229 | *colon = '\0'; |
230 | if (inet_aton(start, iaddr_p) != 1) { |
231 | return FALSE; |
232 | } |
233 | |
234 | /* mount point */ |
235 | start = colon + 1; |
236 | colon = find_colon(str: start); |
237 | *mount_dir = start; |
238 | if (colon == NULL) { |
239 | *image_path = NULL; |
240 | } else { |
241 | /* image path */ |
242 | *colon = '\0'; |
243 | start = colon + 1; |
244 | (void)find_colon(str: start); |
245 | *image_path = start; |
246 | } |
247 | *host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp)); |
248 | return TRUE; |
249 | } |
250 | |
251 | static boolean_t |
252 | parse_image_path(char * path, struct in_addr * iaddr_p, char const * * host, |
253 | char * * mount_dir, char * * image_path) |
254 | { |
255 | if (path[0] >= '0' && path[0] <= '9') { |
256 | return parse_booter_path(path, iaddr_p, host, mount_dir, |
257 | image_path); |
258 | } |
259 | return parse_netboot_path(path, iaddr_p, host, mount_dir, |
260 | image_path); |
261 | } |
262 | |
263 | static boolean_t |
264 | get_root_path(char * root_path) |
265 | { |
266 | void * entry; |
267 | boolean_t found = FALSE; |
268 | const void * pkt; |
269 | int pkt_len; |
270 | |
271 | entry = IOBSDRegistryEntryForDeviceTree(path: "/chosen" ); |
272 | if (entry == NULL) { |
273 | return FALSE; |
274 | } |
275 | pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, packet_length: &pkt_len); |
276 | if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { |
277 | printf("netboot: retrieving root path from BSDP response\n" ); |
278 | } else { |
279 | pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, |
280 | packet_length: &pkt_len); |
281 | if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { |
282 | printf("netboot: retrieving root path from BOOTP response\n" ); |
283 | } |
284 | } |
285 | if (pkt != NULL) { |
286 | int len; |
287 | dhcpol_t options; |
288 | const char * path; |
289 | const struct dhcp * reply; |
290 | |
291 | reply = (const struct dhcp *)pkt; |
292 | (void)dhcpol_parse_packet(options: &options, pkt: reply, len: pkt_len); |
293 | |
294 | path = (const char *)dhcpol_find(list: &options, |
295 | tag: dhcptag_root_path_e, len_p: &len, NULL); |
296 | if (path) { |
297 | memcpy(dst: root_path, src: path, n: len); |
298 | root_path[len] = '\0'; |
299 | found = TRUE; |
300 | } |
301 | } |
302 | IOBSDRegistryEntryRelease(entry); |
303 | return found; |
304 | } |
305 | |
306 | static void |
307 | save_path(char * * str_p, size_t * length_p, char * path) |
308 | { |
309 | *length_p = strlen(s: path) + 1; |
310 | *str_p = kalloc_data(*length_p, Z_WAITOK); |
311 | strlcpy(dst: *str_p, src: path, n: *length_p); |
312 | return; |
313 | } |
314 | |
315 | static struct netboot_info * |
316 | netboot_info_init(struct in_addr iaddr) |
317 | { |
318 | boolean_t have_root_path = FALSE; |
319 | struct netboot_info * info = NULL; |
320 | char * root_path = NULL; |
321 | |
322 | info = (struct netboot_info *)kalloc_type(struct netboot_info, Z_WAITOK | Z_ZERO); |
323 | info->client_ip = iaddr; |
324 | info->image_type = kNetBootImageTypeUnknown; |
325 | |
326 | /* check for a booter-specified path then a NetBoot path */ |
327 | root_path = zalloc(view: ZV_NAMEI); |
328 | |
329 | if (PE_parse_boot_argn(arg_string: "rp0" , arg_ptr: root_path, MAXPATHLEN) == TRUE |
330 | || PE_parse_boot_argn(arg_string: "rp" , arg_ptr: root_path, MAXPATHLEN) == TRUE |
331 | || PE_parse_boot_argn(arg_string: "rootpath" , arg_ptr: root_path, MAXPATHLEN) == TRUE) { |
332 | if (imageboot_format_is_valid(root_path)) { |
333 | printf("netboot_info_init: rp0='%s' isn't a network path," |
334 | " ignoring\n" , root_path); |
335 | } else { |
336 | have_root_path = TRUE; |
337 | } |
338 | } |
339 | if (have_root_path == FALSE) { |
340 | have_root_path = get_root_path(root_path); |
341 | } |
342 | if (have_root_path) { |
343 | const char * server_name = NULL; |
344 | char * mount_point = NULL; |
345 | char * image_path = NULL; |
346 | struct in_addr server_ip; |
347 | |
348 | if (parse_image_path(path: root_path, iaddr_p: &server_ip, host: &server_name, |
349 | mount_dir: &mount_point, image_path: &image_path)) { |
350 | /* kNetBootImageTypeNFS is deprecated */ |
351 | printf("netboot: NFS boot is deprecated\n" ); |
352 | } else if (strncmp(s1: root_path, kNetBootRootPathPrefixHTTP, |
353 | n: strlen(kNetBootRootPathPrefixHTTP)) == 0) { |
354 | info->image_type = kNetBootImageTypeHTTP; |
355 | save_path(str_p: &info->image_path, length_p: &info->image_path_length, |
356 | path: root_path); |
357 | printf("netboot: HTTP URL %s\n" , info->image_path); |
358 | } else { |
359 | printf("netboot: root path uses unrecognized format\n" ); |
360 | } |
361 | |
362 | /* check for image-within-image */ |
363 | if (info->image_path != NULL) { |
364 | if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, arg_ptr: root_path, MAXPATHLEN) |
365 | || PE_parse_boot_argn(arg_string: "rp1" , arg_ptr: root_path, MAXPATHLEN)) { |
366 | /* rp1/root-dmg is the second-level image */ |
367 | save_path(str_p: &info->second_image_path, length_p: &info->second_image_path_length, |
368 | path: root_path); |
369 | } |
370 | } |
371 | if (info->second_image_path != NULL) { |
372 | printf("netboot: nested image %s\n" , info->second_image_path); |
373 | } |
374 | } |
375 | zfree(ZV_NAMEI, root_path); |
376 | return info; |
377 | } |
378 | |
379 | static void |
380 | netboot_info_free(struct netboot_info * * info_p) |
381 | { |
382 | struct netboot_info * info = *info_p; |
383 | |
384 | if (info) { |
385 | kfree_data(info->mount_point, info->mount_point_length); |
386 | kfree_data(info->server_name, info->server_name_length); |
387 | kfree_data(info->image_path, info->image_path_length); |
388 | kfree_data(info->second_image_path, |
389 | info->second_image_path_length); |
390 | kfree_type(struct netboot_info, info); |
391 | } |
392 | *info_p = NULL; |
393 | } |
394 | |
395 | boolean_t |
396 | netboot_iaddr(struct in_addr * iaddr_p) |
397 | { |
398 | if (S_netboot_info_p == NULL) { |
399 | return FALSE; |
400 | } |
401 | |
402 | *iaddr_p = S_netboot_info_p->client_ip; |
403 | return TRUE; |
404 | } |
405 | |
406 | boolean_t |
407 | netboot_rootpath(struct in_addr * server_ip, |
408 | char * name, size_t name_len, |
409 | char * path, size_t path_len) |
410 | { |
411 | if (S_netboot_info_p == NULL) { |
412 | return FALSE; |
413 | } |
414 | |
415 | name[0] = '\0'; |
416 | path[0] = '\0'; |
417 | |
418 | if (S_netboot_info_p->mount_point_length == 0) { |
419 | return FALSE; |
420 | } |
421 | if (path_len < S_netboot_info_p->mount_point_length) { |
422 | printf("netboot: path too small %zu < %zu\n" , |
423 | path_len, S_netboot_info_p->mount_point_length); |
424 | return FALSE; |
425 | } |
426 | strlcpy(dst: path, src: S_netboot_info_p->mount_point, n: path_len); |
427 | strlcpy(dst: name, src: S_netboot_info_p->server_name, n: name_len); |
428 | *server_ip = S_netboot_info_p->server_ip; |
429 | return TRUE; |
430 | } |
431 | |
432 | |
433 | static boolean_t |
434 | get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p, |
435 | struct in_addr * router_p) |
436 | { |
437 | void * entry; |
438 | const void * pkt; |
439 | int pkt_len; |
440 | |
441 | |
442 | entry = IOBSDRegistryEntryForDeviceTree(path: "/chosen" ); |
443 | if (entry == NULL) { |
444 | return FALSE; |
445 | } |
446 | pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, packet_length: &pkt_len); |
447 | if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { |
448 | printf("netboot: retrieving IP information from DHCP response\n" ); |
449 | } else { |
450 | pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, packet_length: &pkt_len); |
451 | if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { |
452 | printf("netboot: retrieving IP information from BOOTP response\n" ); |
453 | } |
454 | } |
455 | if (pkt != NULL) { |
456 | const struct in_addr * ip; |
457 | int len; |
458 | dhcpol_t options; |
459 | const struct dhcp * reply; |
460 | |
461 | reply = (const struct dhcp *)pkt; |
462 | (void)dhcpol_parse_packet(options: &options, pkt: reply, len: pkt_len); |
463 | *iaddr_p = reply->dp_yiaddr; |
464 | ip = (const struct in_addr *) |
465 | dhcpol_find(list: &options, |
466 | tag: dhcptag_subnet_mask_e, len_p: &len, NULL); |
467 | if (ip) { |
468 | *netmask_p = *ip; |
469 | } |
470 | ip = (const struct in_addr *) |
471 | dhcpol_find(list: &options, tag: dhcptag_router_e, len_p: &len, NULL); |
472 | if (ip) { |
473 | *router_p = *ip; |
474 | } |
475 | } |
476 | IOBSDRegistryEntryRelease(entry); |
477 | return pkt != NULL; |
478 | } |
479 | |
480 | static int |
481 | route_cmd(int cmd, struct in_addr d, struct in_addr g, |
482 | struct in_addr m, uint32_t more_flags, unsigned int ifscope) |
483 | { |
484 | struct sockaddr_in dst; |
485 | int error; |
486 | uint32_t flags = RTF_UP | RTF_STATIC; |
487 | struct sockaddr_in gw; |
488 | struct sockaddr_in mask; |
489 | |
490 | flags |= more_flags; |
491 | |
492 | /* destination */ |
493 | bzero(s: (caddr_t)&dst, n: sizeof(dst)); |
494 | dst.sin_len = sizeof(dst); |
495 | dst.sin_family = AF_INET; |
496 | dst.sin_addr = d; |
497 | |
498 | /* gateway */ |
499 | bzero(s: (caddr_t)&gw, n: sizeof(gw)); |
500 | gw.sin_len = sizeof(gw); |
501 | gw.sin_family = AF_INET; |
502 | gw.sin_addr = g; |
503 | |
504 | /* mask */ |
505 | bzero(s: &mask, n: sizeof(mask)); |
506 | mask.sin_len = sizeof(mask); |
507 | mask.sin_family = AF_INET; |
508 | mask.sin_addr = m; |
509 | |
510 | error = rtrequest_scoped(cmd, (struct sockaddr *)&dst, |
511 | (struct sockaddr *)&gw, (struct sockaddr *)&mask, flags, NULL, ifscope); |
512 | |
513 | return error; |
514 | } |
515 | |
516 | static int |
517 | default_route_add(struct in_addr router, boolean_t proxy_arp) |
518 | { |
519 | uint32_t flags = 0; |
520 | struct in_addr zeroes = { .s_addr = 0 }; |
521 | |
522 | if (proxy_arp == FALSE) { |
523 | flags |= RTF_GATEWAY; |
524 | } |
525 | return route_cmd(RTM_ADD, d: zeroes, g: router, m: zeroes, more_flags: flags, IFSCOPE_NONE); |
526 | } |
527 | |
528 | static struct ifnet * |
529 | find_interface(void) |
530 | { |
531 | struct ifnet * ifp = NULL; |
532 | |
533 | dlil_if_lock(); |
534 | if (rootdevice[0]) { |
535 | ifp = ifunit((char *)rootdevice); |
536 | } |
537 | if (ifp == NULL) { |
538 | ifnet_head_lock_shared(); |
539 | TAILQ_FOREACH(ifp, &ifnet_head, if_link) |
540 | if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) { |
541 | break; |
542 | } |
543 | ifnet_head_done(); |
544 | } |
545 | dlil_if_unlock(); |
546 | return ifp; |
547 | } |
548 | |
549 | static const struct sockaddr_in blank_sin = { |
550 | .sin_len = sizeof(struct sockaddr_in), |
551 | .sin_family = AF_INET, |
552 | .sin_port = 0, |
553 | .sin_addr = { .s_addr = 0 }, |
554 | .sin_zero = { 0, 0, 0, 0, 0, 0, 0, 0 } |
555 | }; |
556 | |
557 | static int |
558 | inet_aifaddr(struct socket * so, const char * name, |
559 | const struct in_addr * addr, |
560 | const struct in_addr * mask, |
561 | const struct in_addr * broadcast) |
562 | { |
563 | struct ifaliasreq ifra; |
564 | |
565 | bzero(s: &ifra, n: sizeof(ifra)); |
566 | strlcpy(dst: ifra.ifra_name, src: name, n: sizeof(ifra.ifra_name)); |
567 | if (addr) { |
568 | *((struct sockaddr_in *)(void *)&ifra.ifra_addr) = blank_sin; |
569 | ((struct sockaddr_in *)(void *)&ifra.ifra_addr)->sin_addr = *addr; |
570 | } |
571 | if (mask) { |
572 | *((struct sockaddr_in *)(void *)&ifra.ifra_mask) = blank_sin; |
573 | ((struct sockaddr_in *)(void *)&ifra.ifra_mask)->sin_addr = *mask; |
574 | } |
575 | if (broadcast) { |
576 | *((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr) = blank_sin; |
577 | ((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr)->sin_addr = *broadcast; |
578 | } |
579 | return ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc()); |
580 | } |
581 | |
582 | |
583 | int |
584 | netboot_mountroot(void) |
585 | { |
586 | int error = 0; |
587 | struct in_addr iaddr = { .s_addr = 0 }; |
588 | struct ifreq ifr; |
589 | struct ifnet * ifp; |
590 | struct in_addr netmask = { .s_addr = 0 }; |
591 | proc_t procp = current_proc(); |
592 | struct in_addr router = { .s_addr = 0 }; |
593 | struct socket * so = NULL; |
594 | |
595 | bzero(s: &ifr, n: sizeof(ifr)); |
596 | |
597 | /* find the interface */ |
598 | ifp = find_interface(); |
599 | if (ifp == NULL) { |
600 | printf("netboot: no suitable interface\n" ); |
601 | error = ENXIO; |
602 | goto failed; |
603 | } |
604 | snprintf(ifr.ifr_name, count: sizeof(ifr.ifr_name), "%s" , if_name(ifp)); |
605 | printf("netboot: using network interface '%s'\n" , ifr.ifr_name); |
606 | |
607 | /* bring it up */ |
608 | if ((error = socreate(AF_INET, aso: &so, SOCK_DGRAM, proto: 0)) != 0) { |
609 | printf("netboot: socreate, error=%d\n" , error); |
610 | goto failed; |
611 | } |
612 | ifr.ifr_flags = ifp->if_flags | IFF_UP; |
613 | error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp); |
614 | if (error) { |
615 | printf("netboot: SIFFLAGS, error=%d\n" , error); |
616 | goto failed; |
617 | } |
618 | |
619 | /* grab information from the registry */ |
620 | if (get_ip_parameters(iaddr_p: &iaddr, netmask_p: &netmask, router_p: &router) == FALSE) { |
621 | printf("netboot: can't retrieve IP parameters\n" ); |
622 | goto failed; |
623 | } |
624 | OS_ANALYZER_SUPPRESS("12641116" ) printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr)); |
625 | if (netmask.s_addr) { |
626 | printf(" netmask " IP_FORMAT, IP_LIST(&netmask)); |
627 | } |
628 | if (router.s_addr) { |
629 | printf(" router " IP_FORMAT, IP_LIST(&router)); |
630 | } |
631 | printf("\n" ); |
632 | error = inet_aifaddr(so, name: ifr.ifr_name, addr: &iaddr, mask: &netmask, NULL); |
633 | if (error) { |
634 | printf("netboot: inet_aifaddr failed, %d\n" , error); |
635 | goto failed; |
636 | } |
637 | if (router.s_addr == 0) { |
638 | /* enable proxy arp if we don't have a router */ |
639 | router.s_addr = iaddr.s_addr; |
640 | } |
641 | printf("netboot: adding default route " IP_FORMAT "\n" , |
642 | IP_LIST(&router)); |
643 | error = default_route_add(router, proxy_arp: router.s_addr == iaddr.s_addr); |
644 | if (error) { |
645 | printf("netboot: default_route_add failed %d\n" , error); |
646 | } |
647 | |
648 | soclose(so); |
649 | |
650 | S_netboot_info_p = netboot_info_init(iaddr); |
651 | switch (S_netboot_info_p->image_type) { |
652 | default: |
653 | case kNetBootImageTypeNFS: |
654 | /* kNetBootImageTypeNFS is deprecated */ |
655 | error = ENOTSUP; |
656 | break; |
657 | case kNetBootImageTypeHTTP: |
658 | error = netboot_setup(); |
659 | break; |
660 | } |
661 | if (error == 0) { |
662 | S_netboot = 1; |
663 | } else { |
664 | S_netboot = 0; |
665 | } |
666 | return error; |
667 | failed: |
668 | if (so != NULL) { |
669 | soclose(so); |
670 | } |
671 | return error; |
672 | } |
673 | |
674 | int |
675 | netboot_setup(void) |
676 | { |
677 | int error = 0; |
678 | |
679 | if (S_netboot_info_p == NULL |
680 | || S_netboot_info_p->image_path == NULL) { |
681 | goto done; |
682 | } |
683 | printf("netboot_setup: calling imageboot_mount_image\n" ); |
684 | error = imageboot_mount_image(root_path: S_netboot_info_p->image_path, height: -1, type: IMAGEBOOT_DMG); |
685 | if (error != 0) { |
686 | printf("netboot: failed to mount root image, %d\n" , error); |
687 | } else if (S_netboot_info_p->second_image_path != NULL) { |
688 | error = imageboot_mount_image(root_path: S_netboot_info_p->second_image_path, height: 0, type: IMAGEBOOT_DMG); |
689 | if (error != 0) { |
690 | printf("netboot: failed to mount second root image, %d\n" , error); |
691 | } |
692 | } |
693 | |
694 | done: |
695 | netboot_info_free(info_p: &S_netboot_info_p); |
696 | return error; |
697 | } |
698 | |
699 | int |
700 | netboot_root(void) |
701 | { |
702 | return S_netboot; |
703 | } |
704 | |