1/*
2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * bootp.c
26 * - BOOTP configuration thread
27 * - contains bootp_thread()
28 * - configures an interface's address using BOOTP
29 * - once the address is retrieved, the client probes the address using
30 *   ARP to ensure that another client isn't already using the address
31 */
32/*
33 * Modification History
34 *
35 * May 9, 2000		Dieter Siegmund (dieter@apple.com)
36 * - reworked to fit within the new event-driven framework
37 *
38 * October 4, 2000	Dieter Siegmund (dieter@apple.com)
39 * - added code to unpublish interface state if the link goes
40 *   down and stays down for more than 4 seconds
41 */
42
43#include <stdlib.h>
44#include <unistd.h>
45#include <string.h>
46#include <stdio.h>
47#include <sys/types.h>
48#include <sys/wait.h>
49#include <sys/errno.h>
50#include <sys/socket.h>
51#include <sys/ioctl.h>
52#include <sys/sockio.h>
53#include <ctype.h>
54#include <net/if.h>
55#include <net/ethernet.h>
56#include <netinet/in.h>
57#include <netinet/udp.h>
58#include <netinet/in_systm.h>
59#include <netinet/ip.h>
60#include <netinet/bootp.h>
61#include <arpa/inet.h>
62#include <net/if_types.h>
63#include <syslog.h>
64
65#include "rfc_options.h"
66#include "dhcp_options.h"
67#include "dhcp.h"
68#include "interfaces.h"
69#include "util.h"
70#include "host_identifier.h"
71#include "dhcplib.h"
72#include "ipconfigd_threads.h"
73
74typedef struct {
75    boolean_t			gathering;
76    struct bootp		request;
77    struct saved_pkt		saved;
78    long			start_secs;
79    int				try;
80    int				wait_secs;
81    u_int32_t			xid;
82    timer_callout_t *		timer;
83    arp_client_t *		arp;
84    bootp_client_t *		client;
85    boolean_t			user_warned;
86    boolean_t			enable_arp_collision_detection;
87    boolean_t			resolve_router_timed_out;
88} Service_bootp_t;
89
90/* tags_search: these are the tags we look for using BOOTP */
91static const uint8_t       	bootp_params[] = {
92    dhcptag_host_name_e,
93    dhcptag_subnet_mask_e,
94    dhcptag_router_e,
95    dhcptag_domain_name_server_e,
96    dhcptag_domain_name_e,
97};
98#define N_BOOTP_PARAMS	(sizeof(bootp_params) / sizeof(bootp_params[0]))
99#define IDEAL_RATING	N_BOOTP_PARAMS
100
101static void
102bootp_request(ServiceRef service_p, IFEventID_t evid, void * event_data);
103
104/*
105 * Function: make_bootp_request
106 * Purpose:
107 *   Create a "blank" bootp packet.
108 */
109static void
110make_bootp_request(struct bootp * pkt,
111		   uint8_t * hwaddr, uint8_t hwtype, uint8_t hwlen)
112{
113    bzero(pkt, sizeof (*pkt));
114    pkt->bp_op = BOOTREQUEST;
115    pkt->bp_htype = hwtype;
116    pkt->bp_hlen = hwlen;
117    if (G_must_broadcast)
118	pkt->bp_unused = htons(DHCP_FLAGS_BROADCAST);
119    bcopy(hwaddr, pkt->bp_chaddr, hwlen);
120    bcopy(G_rfc_magic, pkt->bp_vend, sizeof(G_rfc_magic));
121    pkt->bp_vend[4] = dhcptag_end_e;
122    return;
123}
124
125static void
126bootp_set_dhcp_info(Service_bootp_t * bootp, dhcp_info_t * dhcp_info_p)
127{
128    dhcp_info_p->pkt = (uint8_t *)bootp->saved.pkt;
129    dhcp_info_p->pkt_size = bootp->saved.pkt_size;
130    dhcp_info_p->options = &bootp->saved.options;
131    dhcp_info_p->lease_start = 0;
132    dhcp_info_p->lease_expiration = 0;
133    return;
134}
135
136static void
137bootp_publish_success(ServiceRef service_p)
138{
139    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
140    dhcp_info_t		dhcp_info;
141
142    bootp_set_dhcp_info(bootp, &dhcp_info);
143    ServicePublishSuccessIPv4(service_p, &dhcp_info);
144    return;
145}
146
147static void
148S_cancel_pending_events(ServiceRef service_p)
149{
150    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
151
152    if (bootp == NULL)
153	return;
154    if (bootp->timer) {
155	timer_cancel(bootp->timer);
156    }
157    if (bootp->client) {
158	bootp_client_disable_receive(bootp->client);
159    }
160    if (bootp->arp) {
161	arp_client_cancel(bootp->arp);
162    }
163    return;
164}
165
166static void
167bootp_resolve_router_callback(ServiceRef service_p,
168			      router_arp_status_t status);
169
170static void
171bootp_resolve_router_retry(void * arg0, void * arg1, void * arg2)
172{
173    Service_bootp_t *	bootp;
174    ServiceRef 		service_p = (ServiceRef)arg0;
175
176    bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
177    service_resolve_router(service_p, bootp->arp,
178			   bootp_resolve_router_callback,
179			   bootp->saved.our_ip);
180    return;
181}
182
183static void
184bootp_resolve_router_callback(ServiceRef service_p,
185			      router_arp_status_t status)
186{
187    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
188    struct timeval	tv;
189
190    switch (status) {
191    case router_arp_status_no_response_e:
192	/* try again in 60 seconds */
193	tv.tv_sec = 60;
194	tv.tv_usec = 0;
195	timer_set_relative(bootp->timer, tv,
196			   (timer_func_t *)bootp_resolve_router_retry,
197			   service_p, NULL, NULL);
198	if (bootp->resolve_router_timed_out) {
199	    break;
200	}
201	/* publish what we have so far */
202	bootp->resolve_router_timed_out = TRUE;
203	bootp_publish_success(service_p);
204	break;
205    case router_arp_status_success_e:
206	bootp->resolve_router_timed_out = FALSE;
207	bootp_publish_success(service_p);
208	break;
209    default:
210    case router_arp_status_failed_e:
211	break;
212    }
213}
214
215static void
216bootp_success(ServiceRef service_p)
217{
218    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
219    struct in_addr	mask = {0};
220    void *		option;
221    /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
222    struct bootp *	reply = (struct bootp *)(void *)bootp->saved.pkt;
223
224    S_cancel_pending_events(service_p);
225    option = dhcpol_find_with_length(&bootp->saved.options,
226				     dhcptag_subnet_mask_e,
227				     sizeof(mask));
228    if (option != NULL) {
229	mask = *((struct in_addr *)option);
230    }
231    if (bootp->saved.our_ip.s_addr
232	&& reply->bp_yiaddr.s_addr != bootp->saved.our_ip.s_addr) {
233	(void)service_remove_address(service_p);
234    }
235    bootp->try = 0;
236    bootp->enable_arp_collision_detection = TRUE;
237    bootp->saved.our_ip = reply->bp_yiaddr;
238    (void)service_set_address(service_p, bootp->saved.our_ip,
239			      mask, G_ip_zeroes);
240    bootp->resolve_router_timed_out = FALSE;
241    if (service_update_router_address(service_p, &bootp->saved.options,
242				      bootp->saved.our_ip)
243	&& service_resolve_router(service_p, bootp->arp,
244				  bootp_resolve_router_callback,
245				  bootp->saved.our_ip)) {
246	/* router resolution started */
247    }
248    else {
249	bootp_publish_success(service_p);
250    }
251    return;
252}
253
254static void
255bootp_failed(ServiceRef service_p, ipconfig_status_t status, char * msg)
256{
257    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
258    struct timeval	tv;
259
260    S_cancel_pending_events(service_p);
261    dhcpol_free(&bootp->saved.options);
262    service_remove_address(service_p);
263    (void)service_disable_autoaddr(service_p);
264    bootp->saved.our_ip.s_addr = 0;
265    bootp->try = 0;
266    bootp->enable_arp_collision_detection = FALSE;
267    service_publish_failure(service_p, status);
268
269    if (status != ipconfig_status_media_inactive_e) {
270	/* retry BOOTP again in a bit */
271#define RETRY_INTERVAL_SECS      (2 * 60)
272	tv.tv_sec = RETRY_INTERVAL_SECS;
273	tv.tv_usec = 0;
274	timer_set_relative(bootp->timer, tv,
275			   (timer_func_t *)bootp_request,
276			   service_p, (void *)IFEventID_start_e, NULL);
277    }
278
279    return;
280}
281
282static void
283bootp_arp_probe(ServiceRef service_p,  IFEventID_t evid, void * event_data)
284{
285    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
286    interface_t *	if_p = service_interface(service_p);
287
288    switch (evid) {
289      case IFEventID_start_e: {
290	  /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
291	  struct bootp *	reply = (struct bootp *)(void *)bootp->saved.pkt;
292
293	  my_log(LOG_DEBUG, "BOOTP %s: ended at %d", if_name(if_p),
294		 timer_current_secs() - bootp->start_secs);
295	  (void)service_disable_autoaddr(service_p);
296	  bootp_client_disable_receive(bootp->client);
297	  timer_cancel(bootp->timer);
298	  arp_client_cancel(bootp->arp);
299	  arp_client_probe(bootp->arp,
300			   (arp_result_func_t *)bootp_arp_probe, service_p,
301			   (void *)IFEventID_arp_e, G_ip_zeroes,
302			   reply->bp_yiaddr);
303	  return;
304      }
305      case IFEventID_arp_e: {
306	  arp_result_t *	result = (arp_result_t *)event_data;
307
308	  if (result->error) {
309	      my_log(LOG_ERR, "BOOTP %s: arp probe failed, %s",
310		     if_name(if_p),
311		     arp_client_errmsg(bootp->arp));
312	      bootp_failed(service_p, ipconfig_status_internal_error_e, NULL);
313	      return;
314	  }
315	  else {
316	      /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
317	      struct bootp *	reply =
318			(struct bootp *)(void *)bootp->saved.pkt;
319	      if (result->in_use) {
320		  char	msg[128];
321
322		  snprintf(msg, sizeof(msg),
323			   IP_FORMAT " in use by "
324			   EA_FORMAT ", BOOTP Server " IP_FORMAT,
325			   IP_LIST(&reply->bp_yiaddr),
326			   EA_LIST(result->addr.target_hardware),
327			   IP_LIST(&reply->bp_siaddr));
328		  if (bootp->user_warned == FALSE) {
329		      ServiceReportIPv4AddressConflict(service_p,
330						       reply->bp_yiaddr);
331		      bootp->user_warned = TRUE;
332		  }
333		  syslog(LOG_ERR, "BOOTP %s: %s", if_name(if_p), msg);
334		  bootp_failed(service_p, ipconfig_status_address_in_use_e,
335			       msg);
336		  break;
337	      }
338	  }
339	  bootp_success(service_p);
340	  break;
341      }
342      default:
343	  break;
344    }
345    return;
346}
347
348static void
349bootp_request(ServiceRef service_p, IFEventID_t evid, void * event_data)
350{
351    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
352    interface_t *	if_p = service_interface(service_p);
353    struct timeval 	tv;
354
355    switch (evid) {
356      case IFEventID_start_e:
357	  my_log(LOG_DEBUG, "BOOTP %s: starting", if_name(if_p));
358	  (void)service_enable_autoaddr(service_p);
359	  S_cancel_pending_events(service_p);
360	  bootp->start_secs = timer_current_secs();
361	  bootp->wait_secs = G_initial_wait_secs;
362	  bootp->gathering = FALSE;
363	  bootp->saved.pkt_size = 0;
364	  bootp->saved.rating = 0;
365	  bootp->enable_arp_collision_detection = FALSE;
366	  dhcpol_free(&bootp->saved.options);
367	  make_bootp_request(&bootp->request, if_link_address(if_p),
368			     if_link_arptype(if_p),
369			     if_link_length(if_p));
370	  bootp->try = 0;
371	  bootp->xid++;
372	  bootp_client_enable_receive(bootp->client,
373				      (bootp_receive_func_t *)bootp_request,
374				      service_p, (void *)IFEventID_data_e);
375	  /* FALL THROUGH */
376      case IFEventID_timeout_e:
377	  if (bootp->gathering == TRUE) {
378	      bootp_arp_probe(service_p, IFEventID_start_e, NULL);
379	      break;
380	  }
381	  bootp->try++;
382	  if (bootp->try > 1) {
383	      link_status_t	link_status = service_link_status(service_p);
384
385	      if (link_status.valid
386		  && link_status.active == FALSE) {
387		  bootp_failed(service_p, ipconfig_status_media_inactive_e,
388			       NULL);
389		  break;
390	      }
391	  }
392	  if (bootp->try > (G_max_retries + 1)) {
393	      bootp_failed(service_p, ipconfig_status_no_server_e, NULL);
394	      break;
395	  }
396	  bootp->request.bp_secs
397	    = htons((uint16_t)(timer_current_secs() - bootp->start_secs));
398	  bootp->request.bp_xid = htonl(bootp->xid);
399	  /* send the packet */
400	  if (bootp_client_transmit(bootp->client,
401				    G_ip_broadcast, G_ip_zeroes,
402				    G_server_port, G_client_port,
403				    &bootp->request,
404				    sizeof(bootp->request)) < 0) {
405	      my_log(LOG_ERR,
406		     "BOOTP %s: transmit failed", if_name(if_p));
407	  }
408	  /* wait for responses */
409	  tv.tv_sec = bootp->wait_secs;
410	  tv.tv_usec = (suseconds_t)random_range(0, USECS_PER_SEC - 1);
411	  my_log(LOG_DEBUG, "BOOTP %s: waiting at %d for %d.%06d",
412		 if_name(if_p),
413		 timer_current_secs() - bootp->start_secs,
414		 tv.tv_sec, tv.tv_usec);
415	  timer_set_relative(bootp->timer, tv,
416			     (timer_func_t *)bootp_request,
417			     service_p, (void *)IFEventID_timeout_e, NULL);
418	  /* next time wait twice as long */
419	  bootp->wait_secs = (int)tv.tv_sec * 2;
420	  if (bootp->wait_secs > G_max_wait_secs)
421	      bootp->wait_secs = G_max_wait_secs;
422	  break;
423
424      case IFEventID_data_e: {
425	  bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data;
426	  unsigned 		rating;
427	  struct bootp *	reply;
428
429	  reply = (struct bootp *)pkt->data;
430	  if ((ip_valid(reply->bp_yiaddr) == FALSE
431	       && ip_valid(reply->bp_ciaddr) == FALSE)
432	      || dhcp_packet_match(reply, bootp->xid,
433				   (uint8_t) if_link_arptype(if_p),
434				   if_link_address(if_p),
435				   if_link_length(if_p)) == FALSE) {
436	      /* not an interesting packet, drop the packet */
437	      break; /* out of case */
438	  }
439	  rating = dhcpol_count_params(&pkt->options,
440				       bootp_params, N_BOOTP_PARAMS);
441	  if (bootp->saved.pkt_size == 0
442	      || rating > bootp->saved.rating) {
443	      dhcpol_free(&bootp->saved.options);
444	      bcopy(pkt->data, bootp->saved.pkt, pkt->size);
445	      bootp->saved.pkt_size = pkt->size;
446	      bootp->saved.rating = rating;
447	      dhcpol_parse_packet(&bootp->saved.options,
448				  (void *)bootp->saved.pkt,
449				  bootp->saved.pkt_size,
450				  NULL);
451	      if (rating == IDEAL_RATING) {
452		  bootp_arp_probe(service_p, IFEventID_start_e, NULL);
453		  break;
454	      }
455	      if (bootp->gathering == FALSE) {
456		  struct timeval t = {0,0};
457		  t.tv_sec = G_gather_secs;
458		  my_log(LOG_DEBUG, "BOOTP %s: gathering began at %d",
459			 if_name(if_p),
460			 timer_current_secs() - bootp->start_secs);
461		  bootp->gathering = TRUE;
462		  timer_set_relative(bootp->timer, t,
463				     (timer_func_t *)bootp_request,
464				     service_p, (void *)IFEventID_timeout_e,
465				     NULL);
466	      }
467	  }
468	  break;
469      }
470      default:
471	  break;
472    }
473    return;
474}
475
476static void
477bootp_inactive(ServiceRef service_p)
478{
479    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
480
481    S_cancel_pending_events(service_p);
482    service_remove_address(service_p);
483    (void)service_disable_autoaddr(service_p);
484    dhcpol_free(&bootp->saved.options);
485    service_publish_failure(service_p, ipconfig_status_media_inactive_e);
486    return;
487}
488
489ipconfig_status_t
490bootp_thread(ServiceRef service_p, IFEventID_t evid, void * event_data)
491{
492    Service_bootp_t *	bootp = (Service_bootp_t *)ServiceGetPrivate(service_p);
493    interface_t *	if_p = service_interface(service_p);
494    ipconfig_status_t	status = ipconfig_status_success_e;
495
496    switch (evid) {
497      case IFEventID_start_e:
498	  if (bootp != NULL) {
499	      my_log(LOG_ERR, "BOOTP %s: re-entering start state",
500		     if_name(if_p));
501	      return (ipconfig_status_internal_error_e);
502	  }
503	  bootp = malloc(sizeof(*bootp));
504	  if (bootp == NULL) {
505	      my_log(LOG_ERR, "BOOTP %s: malloc failed",
506		     if_name(if_p));
507	      return (ipconfig_status_allocation_failed_e);
508	  }
509	  ServiceSetPrivate(service_p, bootp);
510	  bzero(bootp, sizeof(*bootp));
511	  dhcpol_init(&bootp->saved.options);
512	  bootp->xid = arc4random();
513	  bootp->timer = timer_callout_init();
514	  if (bootp->timer == NULL) {
515	      my_log(LOG_ERR, "BOOTP %s: timer_callout_init failed",
516		     if_name(if_p));
517	      status = ipconfig_status_allocation_failed_e;
518	      goto stop;
519	  }
520	  (void)service_enable_autoaddr(service_p);
521	  bootp->client = bootp_client_init(G_bootp_session, if_p);
522	  if (bootp->client == NULL) {
523	      my_log(LOG_ERR, "BOOTP %s: bootp_client_init failed",
524		     if_name(if_p));
525	      status = ipconfig_status_allocation_failed_e;
526	      goto stop;
527	  }
528	  bootp->arp = arp_client_init(G_arp_session, if_p);
529	  if (bootp->arp == NULL) {
530	      my_log(LOG_ERR, "BOOTP %s: arp_client_init failed",
531		     if_name(if_p));
532	      status = ipconfig_status_allocation_failed_e;
533	      goto stop;
534	  }
535	  bootp_request(service_p, IFEventID_start_e, NULL);
536	  break;
537      case IFEventID_stop_e: {
538      stop:
539	  my_log(LOG_DEBUG, "BOOTP %s: stop", if_name(if_p));
540
541	  if (bootp == NULL) { /* already stopped */
542	      my_log(LOG_DEBUG, "BOOTP %s: already stopped",
543		     if_name(if_p));
544	      status = ipconfig_status_internal_error_e; /* shouldn't happen */
545	      break;
546	  }
547
548	  /* remove IP address */
549	  service_remove_address(service_p);
550
551	  /* disable reception of packets */
552	  (void)service_disable_autoaddr(service_p);
553
554	  /* clean-up resources */
555	  if (bootp->timer) {
556	      timer_callout_free(&bootp->timer);
557	  }
558	  if (bootp->client) {
559	      bootp_client_free(&bootp->client);
560	  }
561	  if (bootp->arp) {
562	      arp_client_free(&bootp->arp);
563	  }
564	  dhcpol_free(&bootp->saved.options);
565	  if (bootp)
566	      free(bootp);
567	  ServiceSetPrivate(service_p, NULL);
568	  break;
569      }
570      case IFEventID_arp_collision_e: {
571	  arp_collision_data_t *	arpc;
572	  char				msg[128];
573	  struct bootp *		reply;
574
575	  arpc = (arp_collision_data_t *)event_data;
576
577	  if (bootp == NULL) {
578	      status = ipconfig_status_internal_error_e;
579	      break;
580	  }
581	  if (bootp->saved.pkt_size == 0
582	      || bootp->saved.our_ip.s_addr != arpc->ip_addr.s_addr
583	      || bootp->enable_arp_collision_detection == FALSE) {
584	      break;
585	  }
586
587	  /* defend our address, don't just give it up */
588	  if (ServiceDefendIPv4Address(service_p, arpc)) {
589	      break;
590	  }
591
592	  /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
593	  reply = (struct bootp *)(void *)bootp->saved.pkt;
594	  snprintf(msg, sizeof(msg),
595		   IP_FORMAT " in use by "
596		   EA_FORMAT ", BOOTP Server " IP_FORMAT,
597		   IP_LIST(&reply->bp_yiaddr),
598		   EA_LIST(arpc->hwaddr),
599		   IP_LIST(&reply->bp_siaddr));
600	  if (bootp->user_warned == FALSE) {
601	      ServiceReportIPv4AddressConflict(service_p,
602					       reply->bp_yiaddr);
603	      bootp->user_warned = TRUE;
604	  }
605	  syslog(LOG_ERR, "BOOTP %s: %s", if_name(if_p), msg);
606	  bootp_failed(service_p, ipconfig_status_address_in_use_e,
607		       msg);
608	  break;
609      }
610      case IFEventID_renew_e:
611      case IFEventID_link_status_changed_e: {
612	  link_status_t	link_status;
613	  void *	network_changed = event_data;
614
615	  if (bootp == NULL) {
616	      status = ipconfig_status_internal_error_e;
617	      break;
618	  }
619	  if (network_changed != NULL) {
620	      /* switched networks, remove IP address to avoid IP collisions */
621	      (void)service_remove_address(service_p);
622	  }
623	  link_status = service_link_status(service_p);
624	  if (link_status.valid == TRUE) {
625	      if (link_status.active == TRUE) {
626		  /* confirm an address, get a new one, or timeout */
627		  bootp->user_warned = FALSE;
628		  if (bootp->try != 1) {
629		      bootp_request(service_p, IFEventID_start_e, NULL);
630		  }
631	      }
632	      else {
633		  /* ensure that we'll retry if the link goes back up */
634		  bootp->try = 0;
635
636		  /* disallow collision detection while disconnected */
637		  bootp->enable_arp_collision_detection = FALSE;
638
639		  S_cancel_pending_events(service_p);
640	      }
641	  }
642	  break;
643      }
644      case IFEventID_link_timer_expired_e:
645	  bootp_inactive(service_p);
646	  break;
647      case IFEventID_get_dhcp_info_e:
648	  if (ServiceGetActiveIPAddress(service_p).s_addr == 0
649	      || bootp->saved.pkt_size == 0) {
650	      break;
651	  }
652	  bootp_set_dhcp_info(bootp, (dhcp_info_t *)event_data);
653	  break;
654      default:
655	  break;
656    } /* switch */
657    return (status);
658}
659