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