1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This RCM module adds support to the RCM framework for IP managed
31  * interfaces.
32  */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <assert.h>
38 #include <string.h>
39 #include <synch.h>
40 #include <libintl.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #include <net/if.h>
48 #include <netinet/in.h>
49 #include <netinet/tcp.h>
50 #include <arpa/inet.h>
51 #include <stropts.h>
52 #include <strings.h>
53 #include <libdevinfo.h>
54 #include <sys/systeminfo.h>
55 #include <netdb.h>
56 #include <inet/ip.h>
57 #include <libinetutil.h>
58 
59 #include <ipmp_mpathd.h>
60 #include "rcm_module.h"
61 
62 /*
63  * Definitions
64  */
65 #ifndef lint
66 #define	_(x)	gettext(x)
67 #else
68 #define	_(x)	x
69 #endif
70 
71 /* Some generic well-knowns and defaults used in this module */
72 #define	ARP_MOD_NAME		"arp"		/* arp module */
73 #define	IP_MAX_MODS		9		/* max modules pushed on intr */
74 #define	MAX_RECONFIG_SIZE	1024		/* Max. reconfig string size */
75 
76 #define	RCM_NET_PREFIX		"SUNW_network"	/* RCM network name prefix */
77 #define	RCM_NET_RESOURCE_MAX	(13 + LIFNAMSIZ) /* RCM_NET_PREFIX+LIFNAMSIZ */
78 
79 #define	RCM_STR_SUNW_IP		"SUNW_ip/"	/* IP address export prefix */
80 #define	RCM_SIZE_SUNW_IP	9		/* strlen("SUNW_ip/") + 1 */
81 
82 /* ifconfig(1M) */
83 #define	USR_SBIN_IFCONFIG	"/usr/sbin/ifconfig" /* ifconfig command */
84 #define	CFGFILE_FMT_IPV4	"/etc/hostname."  /* IPV4 config file */
85 #define	CFGFILE_FMT_IPV6	"/etc/hostname6." /* IPV6 config file */
86 #define	CFG_CMDS_STD	" netmask + broadcast + up" /* Normal config string */
87 #define	CONFIG_AF_INET		0x1		/* Post-configure IPv4 */
88 #define	CONFIG_AF_INET6		0x2		/* Post-configure IPv6 */
89 #define	MAXLINE			1024		/* Max. line length */
90 #define	MAXARGS			512		/* Max. args in ifconfig cmd */
91 
92 /* Physical interface flags mask */
93 #define	RCM_PIF_FLAGS		(IFF_OFFLINE | IFF_INACTIVE | IFF_FAILED | \
94 				    IFF_STANDBY)
95 
96 /* Some useful macros */
97 #ifndef MAX
98 #define	MAX(a, b)	(((a) > (b))?(a):(b))
99 #endif /* MAX */
100 
101 #ifndef ISSPACE
102 #define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
103 #endif
104 
105 #ifndef	ISEOL
106 #define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
107 #endif
108 
109 #ifndef	STREQ
110 #define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
111 #endif
112 
113 #ifndef ADDSPACE
114 #define	ADDSPACE(a)	((void) strcat((a), " "))
115 #endif
116 
117 /* Interface Cache state flags */
118 #define	CACHE_IF_STALE		0x1		/* stale cached data */
119 #define	CACHE_IF_NEW		0x2		/* new cached interface */
120 #define	CACHE_IF_OFFLINED	0x4		/* interface offlined */
121 #define	CACHE_IF_UPDATED	0x8		/* interface props. updated */
122 #define	CACHE_IF_IGNORE		0x10		/* state held elsewhere */
123 
124 /* Network Cache lookup options */
125 #define	CACHE_NO_REFRESH	0x1		/* cache refresh not needed */
126 #define	CACHE_REFRESH		0x2		/* refresh cache */
127 
128 /* RCM IPMP Module specific property definitions */
129 #define	RCM_IPMP_MIN_REDUNDANCY	1		/* default min. redundancy */
130 
131 /* in.mpathd(1M) specifics */
132 #define	MPATHD_MAX_RETRIES	5	/* Max. offline retries */
133 
134 /* Stream module operations */
135 #define	MOD_INSERT		0	/* Insert a mid-stream module */
136 #define	MOD_REMOVE		1	/* Remove a mid-stream module */
137 #define	MOD_CHECK		2	/* Check mid-stream module safety */
138 
139 /* VLAN format support */
140 #define	VLAN_MAX_PPA_ALLOWED	1000
141 #define	VLAN_GET_PPA(ppa)	(ppa % VLAN_MAX_PPA_ALLOWED)
142 
143 /* devfsadm attach nvpair values */
144 #define	PROP_NV_DDI_NETWORK	"ddi_network"
145 
146 /*
147  * in.mpathd(1M) message passing formats
148  */
149 typedef struct mpathd_cmd {
150 	uint32_t	cmd_command;		/* message command */
151 	char		cmd_ifname[LIFNAMSIZ];	/* this interface name */
152 	char		cmd_movetoif[LIFNAMSIZ]; /* move to interface */
153 	uint32_t	cmd_min_red;		/* min. redundancy */
154 /* Message passing values for MI_SETOINDEX */
155 #define	from_lifname	cmd_ifname		/* current logical interface */
156 #define	to_pifname	cmd_movetoif		/* new physical interface */
157 #define	addr_family	cmd_min_red		/* address family */
158 } mpathd_cmd_t;
159 
160 /* This is needed since mpathd checks message size for offline */
161 typedef struct mpathd_unoffline {
162 	uint32_t	cmd_command;		/* offline / undo offline */
163 	char		cmd_ifname[LIFNAMSIZ];	/* this interface name */
164 } mpathd_unoffline_t;
165 
166 typedef struct mpathd_response {
167 	uint32_t	resp_sys_errno;		/* system errno */
168 	uint32_t	resp_mpathd_err;	/* mpathd error information */
169 } mpathd_response_t;
170 
171 /*
172  * IP module data types
173  */
174 
175 /* Physical interface representation */
176 typedef struct ip_pif {
177 	char			pi_ifname[LIFNAMSIZ+1];	/* interface name */
178 	char			pi_grpname[LIFNAMSIZ+1]; /* IPMP group name */
179 	struct ip_lif		*pi_lifs;	/* ptr to logical interfaces */
180 } ip_pif_t;
181 
182 /* Logical interface representation */
183 typedef struct ip_lif
184 {
185 	struct ip_lif		*li_next;	/* ptr to next lif */
186 	struct ip_lif		*li_prev;  	/* previous next ptr */
187 	ip_pif_t		*li_pif;	/* back ptr to phy int */
188 	ushort_t		li_ifnum;	/* interface number */
189 	union {
190 		sa_family_t		family;
191 		struct sockaddr_storage storage;
192 		struct sockaddr_in	ip4;    /* IPv4 */
193 		struct sockaddr_in6	ip6;    /* IPv6 */
194 	} li_addr;
195 	uint64_t		li_ifflags;	/* current IFF_* flags */
196 	int			li_modcnt;	/* # of modules */
197 	char	*li_modules[IP_MAX_MODS];	/* module list pushed */
198 	char	*li_reconfig;			/* Reconfiguration string */
199 	int32_t			li_cachestate;	/* cache state flags */
200 } ip_lif_t;
201 
202 /* Cache element */
203 typedef struct ip_cache
204 {
205 	struct ip_cache		*ip_next;	/* next cached resource */
206 	struct ip_cache		*ip_prev;	/* prev cached resource */
207 	char			*ip_resource;	/* resource name */
208 	ip_pif_t		*ip_pif;	/* ptr to phy int */
209 	int32_t			ip_ifred;	/* min. redundancy */
210 	int			ip_cachestate;	/* cache state flags */
211 } ip_cache_t;
212 
213 /*
214  * Global cache for network interfaces
215  */
216 static ip_cache_t	cache_head;
217 static ip_cache_t	cache_tail;
218 static mutex_t		cache_lock;
219 static int		events_registered = 0;
220 
221 /*
222  * Global NIC list to be configured after DR-attach
223  */
224 #define	NIL_NULL	((struct ni_list *)0)
225 
226 struct net_interface {
227 	char *type;	/* Name of type of interface  (le, ie, etc.)    */
228 	char *name;	/* Qualified name of interface (le0, ie0, etc.) */
229 };
230 
231 struct ni_list {
232 	struct net_interface *nifp;
233 	struct ni_list *next;
234 };
235 
236 static mutex_t nil_lock;	/* NIC list lock */
237 static int num_ni = 0;		/* Global new interface count */
238 static struct ni_list *nil_head = NIL_NULL;	/* Global new if list */
239 
240 struct devfs_minor_data {
241 	int32_t minor_type;
242 	char *minor_name;
243 	char *minor_node_type;
244 };
245 
246 /*
247  * RCM module interface prototypes
248  */
249 static int ip_register(rcm_handle_t *);
250 static int ip_unregister(rcm_handle_t *);
251 static int ip_get_info(rcm_handle_t *, char *, id_t, uint_t,
252 			char **, char **, nvlist_t *, rcm_info_t **);
253 static int ip_suspend(rcm_handle_t *, char *, id_t,
254 			timespec_t *, uint_t, char **, rcm_info_t **);
255 static int ip_resume(rcm_handle_t *, char *, id_t, uint_t,
256 			char **, rcm_info_t **);
257 static int ip_offline(rcm_handle_t *, char *, id_t, uint_t,
258 			char **, rcm_info_t **);
259 static int ip_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
260 			char **, rcm_info_t **);
261 static int ip_remove(rcm_handle_t *, char *, id_t, uint_t,
262 			char **, rcm_info_t **);
263 static int ip_notify_event(rcm_handle_t *, char *, id_t, uint_t,
264 			char **, nvlist_t *, rcm_info_t **);
265 
266 /* Module private routines */
267 static void 	free_cache();
268 static int 	update_cache(rcm_handle_t *);
269 static void 	cache_remove(ip_cache_t *);
270 static ip_cache_t *cache_lookup(rcm_handle_t *, char *, char);
271 static void 	free_node(ip_cache_t *);
272 static void 	cache_insert(ip_cache_t *);
273 static char 	*ip_usage(ip_cache_t *);
274 static int 	update_pif(rcm_handle_t *, int, int, struct lifreq *);
275 static int 	ip_ipmp_offline(ip_cache_t *, ip_cache_t *);
276 static int	ip_ipmp_undo_offline(ip_cache_t *);
277 static int	if_cfginfo(ip_cache_t *, uint_t);
278 static int	if_unplumb(ip_cache_t *);
279 static int	if_replumb(ip_cache_t *);
280 static void 	ip_log_err(ip_cache_t *, char **, char *);
281 static char	*get_physical_resource(const char *);
282 static void	clr_cfg_state(ip_pif_t *);
283 static uint64_t	if_get_flags(ip_pif_t *);
284 static int	mpathd_send_cmd(mpathd_cmd_t *);
285 static int	connect_to_mpathd(int);
286 #ifdef RCM_IPMP_DEBUG
287 static void	dump_node(ip_cache_t *);
288 #endif
289 static int	modop(char *, char *, int, char);
290 static int	get_modlist(char *, ip_lif_t *);
291 static int	ip_domux2fd(int *, int *, struct lifreq *);
292 static int	ip_plink(int, int, struct lifreq *);
293 static int	ip_onlinelist(rcm_handle_t *, ip_cache_t *, char **, uint_t,
294 			rcm_info_t **);
295 static int	ip_offlinelist(rcm_handle_t *, ip_cache_t *, char **, uint_t,
296 			rcm_info_t **);
297 static char 	**ip_get_addrlist(ip_cache_t *);
298 static void	ip_free_addrlist(char **);
299 static void	ip_consumer_notify(rcm_handle_t *, char *, char **, uint_t,
300 			rcm_info_t **);
301 
302 static int process_nvlist(nvlist_t *);
303 static void process_minor(char *, char *, int32_t, struct devfs_minor_data *);
304 static int if_configure(char *);
305 static int isgrouped(char *);
306 static int if_ipmp_config(char *, int, int);
307 static int if_mpathd_configure(char *, char *, int, int);
308 static char *get_mpathd_dest(char *, int);
309 static int if_getcount(int);
310 static void tokenize(char *, char **, char *, int *);
311 
312 
313 /* Module-Private data */
314 static struct rcm_mod_ops ip_ops =
315 {
316 	RCM_MOD_OPS_VERSION,
317 	ip_register,
318 	ip_unregister,
319 	ip_get_info,
320 	ip_suspend,
321 	ip_resume,
322 	ip_offline,
323 	ip_undo_offline,
324 	ip_remove,
325 	NULL,
326 	NULL,
327 	ip_notify_event
328 };
329 
330 /*
331  * rcm_mod_init() - Update registrations, and return the ops structure.
332  */
333 struct rcm_mod_ops *
334 rcm_mod_init(void)
335 {
336 	rcm_log_message(RCM_TRACE1, "IP: mod_init\n");
337 
338 	cache_head.ip_next = &cache_tail;
339 	cache_head.ip_prev = NULL;
340 	cache_tail.ip_prev = &cache_head;
341 	cache_tail.ip_next = NULL;
342 	(void) mutex_init(&cache_lock, NULL, NULL);
343 	(void) mutex_init(&nil_lock, NULL, NULL);
344 
345 	/* Return the ops vectors */
346 	return (&ip_ops);
347 }
348 
349 /*
350  * rcm_mod_info() - Return a string describing this module.
351  */
352 const char *
353 rcm_mod_info(void)
354 {
355 	rcm_log_message(RCM_TRACE1, "IP: mod_info\n");
356 
357 	return ("IP Multipathing module version %I%");
358 }
359 
360 /*
361  * rcm_mod_fini() - Destroy the network interfaces cache.
362  */
363 int
364 rcm_mod_fini(void)
365 {
366 	rcm_log_message(RCM_TRACE1, "IP: mod_fini\n");
367 
368 	free_cache();
369 	(void) mutex_destroy(&nil_lock);
370 	(void) mutex_destroy(&cache_lock);
371 	return (RCM_SUCCESS);
372 }
373 
374 /*
375  * ip_register() - Make sure the cache is properly sync'ed, and its
376  *		 registrations are in order.
377  */
378 static int
379 ip_register(rcm_handle_t *hd)
380 {
381 	rcm_log_message(RCM_TRACE1, "IP: register\n");
382 
383 	/* Guard against bad arguments */
384 	assert(hd != NULL);
385 
386 	if (update_cache(hd) < 0)
387 		return (RCM_FAILURE);
388 
389 	/*
390 	 * Need to register interest in all new resources
391 	 * getting attached, so we get attach event notifications
392 	 */
393 	if (!events_registered) {
394 		if (rcm_register_event(hd, RCM_RESOURCE_NETWORK_NEW, 0, NULL)
395 		    != RCM_SUCCESS) {
396 			rcm_log_message(RCM_ERROR,
397 			    _("IP: failed to register %s\n"),
398 			    RCM_RESOURCE_NETWORK_NEW);
399 			return (RCM_FAILURE);
400 		} else {
401 			rcm_log_message(RCM_DEBUG, "IP: registered %s\n",
402 			    RCM_RESOURCE_NETWORK_NEW);
403 			events_registered++;
404 		}
405 	}
406 
407 	return (RCM_SUCCESS);
408 }
409 
410 /*
411  * ip_unregister() - Walk the cache, unregistering all the networks.
412  */
413 static int
414 ip_unregister(rcm_handle_t *hd)
415 {
416 	ip_cache_t *probe;
417 
418 	rcm_log_message(RCM_TRACE1, "IP: unregister\n");
419 
420 	/* Guard against bad arguments */
421 	assert(hd != NULL);
422 
423 	/* Walk the cache, unregistering everything */
424 	(void) mutex_lock(&cache_lock);
425 	probe = cache_head.ip_next;
426 	while (probe != &cache_tail) {
427 		if (rcm_unregister_interest(hd, probe->ip_resource, 0)
428 		    != RCM_SUCCESS) {
429 			/* unregister failed for whatever reason */
430 			(void) mutex_unlock(&cache_lock);
431 			return (RCM_FAILURE);
432 		}
433 		cache_remove(probe);
434 		free_node(probe);
435 		probe = cache_head.ip_next;
436 	}
437 	(void) mutex_unlock(&cache_lock);
438 
439 	/*
440 	 * Need to unregister interest in all new resources
441 	 */
442 	if (events_registered) {
443 		if (rcm_unregister_event(hd, RCM_RESOURCE_NETWORK_NEW, 0)
444 		    != RCM_SUCCESS) {
445 			rcm_log_message(RCM_ERROR,
446 			    _("IP: failed to unregister %s\n"),
447 			    RCM_RESOURCE_NETWORK_NEW);
448 			return (RCM_FAILURE);
449 		} else {
450 			rcm_log_message(RCM_DEBUG, "IP: unregistered %s\n",
451 			    RCM_RESOURCE_NETWORK_NEW);
452 			events_registered--;
453 		}
454 	}
455 
456 	return (RCM_SUCCESS);
457 }
458 
459 /*
460  * ip_offline() - Offline an interface.
461  */
462 static int
463 ip_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
464     char **errorp, rcm_info_t **depend_info)
465 {
466 	ip_cache_t *node;
467 	ip_pif_t *pif;
468 	int detachable = 0;
469 	int nofailover = 0;
470 	int ipmp = 0;
471 
472 	rcm_log_message(RCM_TRACE1, "IP: offline(%s)\n", rsrc);
473 
474 	/* Guard against bad arguments */
475 	assert(hd != NULL);
476 	assert(rsrc != NULL);
477 	assert(id == (id_t)0);
478 	assert(errorp != NULL);
479 	assert(depend_info != NULL);
480 
481 	/* Lock the cache and lookup the resource */
482 	(void) mutex_lock(&cache_lock);
483 	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
484 	if (node == NULL) {
485 		ip_log_err(node, errorp, "Unrecognized resource");
486 		errno = ENOENT;
487 		(void) mutex_unlock(&cache_lock);
488 		return (RCM_SUCCESS);
489 	}
490 
491 	pif = node->ip_pif;
492 
493 	/* Establish default detachability criteria */
494 	if (flags & RCM_FORCE) {
495 		detachable++;
496 	}
497 
498 	/* Check if the interface is an IPMP grouped interface */
499 	if (strcmp(pif->pi_grpname, "")) {
500 		ipmp++;
501 	}
502 
503 	if (if_get_flags(pif) & IFF_NOFAILOVER) {
504 		nofailover++;
505 	}
506 
507 	/*
508 	 * Even if the interface is not in an IPMP group, it's possible that
509 	 * it's still okay to offline it as long as there are higher-level
510 	 * failover mechanisms for the addresses it owns (e.g., clustering).
511 	 * In this case, ip_offlinelist() will return RCM_SUCCESS, and we
512 	 * charge on.
513 	 */
514 	if (!ipmp && !detachable) {
515 		/* Inform consumers of IP addresses being offlined */
516 		if (ip_offlinelist(hd, node, errorp, flags, depend_info) ==
517 		    RCM_SUCCESS) {
518 			rcm_log_message(RCM_DEBUG,
519 				"IP: consumers agree on detach");
520 		} else {
521 			ip_log_err(node, errorp,
522 				"Device consumers prohibit offline");
523 			(void) mutex_unlock(&cache_lock);
524 			return (RCM_FAILURE);
525 		}
526 	}
527 
528 	/*
529 	 * Cannot remove an IPMP interface if IFF_NOFAILOVER is set.
530 	 */
531 	if (ipmp && nofailover) {
532 		/* Interface is part of an IPMP group, and cannot failover */
533 		ip_log_err(node, errorp, "Failover disabled");
534 		errno = EBUSY;
535 		(void) mutex_unlock(&cache_lock);
536 		return (RCM_FAILURE);
537 	}
538 
539 	/* Check if it's a query */
540 	if (flags & RCM_QUERY) {
541 		rcm_log_message(RCM_TRACE1, "IP: offline query success(%s)\n",
542 		    rsrc);
543 		(void) mutex_unlock(&cache_lock);
544 		return (RCM_SUCCESS);
545 	}
546 
547 	/* Check detachability, save configuration if detachable */
548 	if (if_cfginfo(node, (flags & RCM_FORCE)) < 0) {
549 		node->ip_cachestate |= CACHE_IF_IGNORE;
550 		rcm_log_message(RCM_TRACE1, "IP: Ignoring node(%s)\n", rsrc);
551 		(void) mutex_unlock(&cache_lock);
552 		return (RCM_SUCCESS);
553 	}
554 
555 	/* standalone detachable device */
556 	if (!ipmp) {
557 		if (if_unplumb(node) < 0) {
558 			ip_log_err(node, errorp,
559 			    "Failed to unplumb the device");
560 
561 			errno = EIO;
562 			(void) mutex_unlock(&cache_lock);
563 			return (RCM_FAILURE);
564 		}
565 
566 		node->ip_cachestate |= CACHE_IF_OFFLINED;
567 		rcm_log_message(RCM_TRACE1, "IP: Offline success(%s)\n", rsrc);
568 		(void) mutex_unlock(&cache_lock);
569 		return (RCM_SUCCESS);
570 	}
571 
572 	/*
573 	 * This an IPMP interface that can be failed over.
574 	 * Request in.mpathd(1M) to failover the physical interface.
575 	 */
576 
577 	/* Failover to "any", let mpathd determine best failover candidate */
578 	if (ip_ipmp_offline(node, NULL) < 0) {
579 		ip_log_err(node, errorp, "in.mpathd failover failed");
580 		/*
581 		 * Odds are that in.mpathd(1M) could not offline the device
582 		 * because it was the last interface in the group.  However,
583 		 * it's possible that it's still okay to offline it as long as
584 		 * there are higher-level failover mechanisms for the
585 		 * addresses it owns (e.g., clustering).  In this case,
586 		 * ip_offlinelist() will return RCM_SUCCESS, and we charge on.
587 		 *
588 		 * TODO: change ip_ipmp_offline() to return the actual failure
589 		 * from in.mpathd so that we can verify that it did indeed
590 		 * fail with IPMP_EMINRED.
591 		 */
592 		if (!detachable) {
593 			/* Inform consumers of IP addresses being offlined */
594 			if (ip_offlinelist(hd, node, errorp, flags,
595 			    depend_info) == RCM_SUCCESS) {
596 				rcm_log_message(RCM_DEBUG,
597 					"IP: consumers agree on detach");
598 			} else {
599 				ip_log_err(node, errorp,
600 					"Device consumers prohibit offline");
601 				(void) mutex_unlock(&cache_lock);
602 				errno = EBUSY;
603 				return (RCM_FAILURE);
604 			}
605 		}
606 	}
607 
608 	if (if_unplumb(node) < 0) {
609 		rcm_log_message(RCM_ERROR,
610 		    _("IP: Unplumb failed (%s)\n"),
611 		    pif->pi_ifname);
612 
613 		/* Request mpathd to undo the offline */
614 		if (ip_ipmp_undo_offline(node) < 0) {
615 			ip_log_err(node, errorp, "Undo offline failed");
616 			(void) mutex_unlock(&cache_lock);
617 			return (RCM_FAILURE);
618 		}
619 		(void) mutex_unlock(&cache_lock);
620 		return (RCM_FAILURE);
621 	}
622 
623 	node->ip_cachestate |= CACHE_IF_OFFLINED;
624 	rcm_log_message(RCM_TRACE1, "IP: offline success(%s)\n", rsrc);
625 	(void) mutex_unlock(&cache_lock);
626 	return (RCM_SUCCESS);
627 }
628 
629 /*
630  * ip_undo_offline() - Undo offline of a previously offlined device.
631  */
632 /*ARGSUSED*/
633 static int
634 ip_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
635     char **errorp, rcm_info_t **depend_info)
636 {
637 	ip_cache_t *node;
638 
639 	rcm_log_message(RCM_TRACE1, "IP: online(%s)\n", rsrc);
640 
641 	/* Guard against bad arguments */
642 	assert(hd != NULL);
643 	assert(rsrc != NULL);
644 	assert(id == (id_t)0);
645 	assert(errorp != NULL);
646 	assert(depend_info != NULL);
647 
648 	(void) mutex_lock(&cache_lock);
649 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
650 
651 	if (node == NULL) {
652 		ip_log_err(node, errorp, "No such device");
653 		(void) mutex_unlock(&cache_lock);
654 		errno = ENOENT;
655 		return (RCM_FAILURE);
656 	}
657 
658 	/* Check if no attempt should be made to online the device here */
659 	if (node->ip_cachestate & CACHE_IF_IGNORE) {
660 		node->ip_cachestate &= ~(CACHE_IF_IGNORE);
661 		(void) mutex_unlock(&cache_lock);
662 		return (RCM_SUCCESS);
663 	}
664 
665 	/* Check if the interface was previously offlined */
666 	if (!(node->ip_cachestate & CACHE_IF_OFFLINED)) {
667 		ip_log_err(node, errorp, "Device not offlined");
668 		(void) mutex_unlock(&cache_lock);
669 		errno = ENOTSUP;
670 		return (RCM_FAILURE);
671 	}
672 
673 	if (if_replumb(node) == -1) {
674 		/* re-plumb failed */
675 		ip_log_err(node, errorp, "Replumb failed");
676 		(void) mutex_unlock(&cache_lock);
677 		errno = EIO;
678 		return (RCM_FAILURE);
679 
680 	}
681 
682 	/* Inform consumers about IP addresses being un-offlined */
683 	(void) ip_onlinelist(hd, node, errorp, flags, depend_info);
684 
685 	node->ip_cachestate &= ~(CACHE_IF_OFFLINED);
686 	rcm_log_message(RCM_TRACE1, "IP: online success(%s)\n", rsrc);
687 	(void) mutex_unlock(&cache_lock);
688 	return (RCM_SUCCESS);
689 }
690 
691 /*
692  * ip_get_info() - Gather usage information for this resource.
693  */
694 /*ARGSUSED*/
695 int
696 ip_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
697     char **usagep, char **errorp, nvlist_t *props, rcm_info_t **depend_info)
698 {
699 	ip_cache_t *node;
700 	char *infostr;
701 
702 	/* Guard against bad arguments */
703 	assert(hd != NULL);
704 	assert(rsrc != NULL);
705 	assert(id == (id_t)0);
706 	assert(usagep != NULL);
707 	assert(errorp != NULL);
708 	assert(depend_info != NULL);
709 
710 	rcm_log_message(RCM_TRACE1, "IP: get_info(%s)\n", rsrc);
711 
712 	(void) mutex_lock(&cache_lock);
713 	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
714 	if (!node) {
715 		rcm_log_message(RCM_INFO,
716 		    _("IP: get_info(%s) unrecognized resource\n"), rsrc);
717 		(void) mutex_unlock(&cache_lock);
718 		errno = ENOENT;
719 		return (RCM_FAILURE);
720 	}
721 
722 	infostr = ip_usage(node);
723 
724 	if (infostr == NULL) {
725 		/* most likely malloc failure */
726 		rcm_log_message(RCM_ERROR,
727 		    _("IP: get_info(%s) malloc failure\n"), rsrc);
728 		(void) mutex_unlock(&cache_lock);
729 		errno = ENOMEM;
730 		*errorp = NULL;
731 		return (RCM_FAILURE);
732 	}
733 
734 	/* Set client/role properties */
735 	(void) nvlist_add_string(props, RCM_CLIENT_NAME, "IP");
736 
737 	/* Set usage property, infostr will be freed by caller */
738 	*usagep = infostr;
739 
740 	rcm_log_message(RCM_TRACE1, "IP: get_info(%s) info = %s \n",
741 	    rsrc, infostr);
742 
743 	(void) mutex_unlock(&cache_lock);
744 	return (RCM_SUCCESS);
745 }
746 
747 /*
748  * ip_suspend() - Nothing to do, always okay
749  */
750 /*ARGSUSED*/
751 static int
752 ip_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
753 			uint_t flags, char **errorp, rcm_info_t **depend_info)
754 {
755 	/* Guard against bad arguments */
756 	assert(hd != NULL);
757 	assert(rsrc != NULL);
758 	assert(id == (id_t)0);
759 	assert(interval != NULL);
760 	assert(errorp != NULL);
761 	assert(depend_info != NULL);
762 
763 	rcm_log_message(RCM_TRACE1, "IP: suspend(%s)\n", rsrc);
764 	return (RCM_SUCCESS);
765 }
766 
767 /*
768  * ip_resume() - Nothing to do, always okay
769  */
770 /*ARGSUSED*/
771 static int
772 ip_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
773 		char **errorp, rcm_info_t ** depend_info)
774 {
775 	/* Guard against bad arguments */
776 	assert(hd != NULL);
777 	assert(rsrc != NULL);
778 	assert(id == (id_t)0);
779 	assert(errorp != NULL);
780 	assert(depend_info != NULL);
781 
782 	rcm_log_message(RCM_TRACE1, "IP: resume(%s)\n", rsrc);
783 
784 	return (RCM_SUCCESS);
785 }
786 
787 /*
788  * ip_remove() - remove a resource from cache
789  */
790 /*ARGSUSED*/
791 static int
792 ip_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
793 		char **errorp, rcm_info_t **depend_info)
794 {
795 	ip_cache_t *node;
796 
797 	/* Guard against bad arguments */
798 	assert(hd != NULL);
799 	assert(rsrc != NULL);
800 	assert(id == (id_t)0);
801 	assert(errorp != NULL);
802 	assert(depend_info != NULL);
803 
804 	rcm_log_message(RCM_TRACE1, "IP: remove(%s)\n", rsrc);
805 
806 	(void) mutex_lock(&cache_lock);
807 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
808 	if (!node) {
809 		rcm_log_message(RCM_INFO,
810 		    _("IP: remove(%s) unrecognized resource\n"), rsrc);
811 		(void) mutex_unlock(&cache_lock);
812 		errno = ENOENT;
813 		return (RCM_FAILURE);
814 	}
815 
816 	/* remove the cached entry for the resource */
817 	cache_remove(node);
818 
819 	(void) mutex_unlock(&cache_lock);
820 	return (RCM_SUCCESS);
821 }
822 
823 /*
824  * ip_notify_event - Project private implementation to receive new resource
825  *		   events. It intercepts all new resource events. If the
826  *		   new resource is a network resource, pass up a notify
827  *		   for it too. The new resource need not be cached, since
828  *		   it is done at register again.
829  */
830 /*ARGSUSED*/
831 static int
832 ip_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
833 			char **errorp, nvlist_t *nvl, rcm_info_t **depend_info)
834 {
835 	struct ni_list	*nilp, *onilp;
836 	struct net_interface *nip;
837 	int		n;
838 
839 	assert(hd != NULL);
840 	assert(rsrc != NULL);
841 	assert(id == (id_t)0);
842 	assert(nvl != NULL);
843 
844 	rcm_log_message(RCM_TRACE1, "IP: notify_event(%s)\n", rsrc);
845 
846 	if (!STREQ(rsrc, RCM_RESOURCE_NETWORK_NEW)) {
847 		rcm_log_message(RCM_INFO,
848 		    _("IP: unrecognized event for %s\n"), rsrc);
849 		ip_log_err(NULL, errorp, "unrecognized event");
850 		errno = EINVAL;
851 		return (RCM_FAILURE);
852 	}
853 
854 	/* Update cache to  reflect latest interfaces */
855 	if (update_cache(hd) < 0) {
856 		rcm_log_message(RCM_ERROR, _("IP: update_cache failed\n"));
857 		ip_log_err(NULL, errorp, "Private Cache update failed");
858 		return (RCM_FAILURE);
859 	}
860 
861 	/* Process the nvlist for the event */
862 	if (process_nvlist(nvl) != 0) {
863 		rcm_log_message(RCM_WARNING,
864 		    _("IP: Error processing resource attributes(%s)\n"), rsrc);
865 		rcm_log_message(RCM_WARNING,
866 		    _("IP: One or more devices may not be configured.\n"));
867 		ip_log_err(NULL, errorp, "Error processing device properties");
868 		/* Continue processing interfaces that were valid */
869 	}
870 
871 	(void) mutex_lock(&nil_lock);
872 
873 	/* Configure all new interfaces found */
874 	for (nilp = nil_head, n = 0; n < num_ni; nilp = nilp->next, n++) {
875 		nip = nilp->nifp;
876 		if (if_configure(nip->name) != 0) {
877 			rcm_log_message(RCM_ERROR,
878 			    _("IP: Configuration failed (%s)\n"), nip->name);
879 			ip_log_err(NULL, errorp,
880 			    "Failed configuring one or more IP addresses");
881 			/* continue configuring rest of the interfaces */
882 		}
883 	}
884 
885 	/* Notify all IP address consumers and clean up interface list */
886 	for (nilp = nil_head; nilp; ) {
887 		nip = nilp->nifp;
888 		if (nip != (struct net_interface *)0) {
889 			if (nip->name != 0) {
890 				ip_consumer_notify(hd, nip->name, errorp, flags,
891 					depend_info);
892 				free(nip->name);
893 			}
894 			if (nip->type != 0)
895 				free(nip->type);
896 			free((char *)nip);
897 		}
898 
899 		onilp = nilp;
900 		nilp = nilp->next;
901 		free((char *)onilp);
902 	}
903 
904 	num_ni = 0;		/* reset new if count */
905 	nil_head = NIL_NULL;	/* reset list head */
906 
907 	(void) mutex_unlock(&nil_lock);
908 
909 	rcm_log_message(RCM_TRACE1,
910 	    "IP: notify_event: device configuration complete\n");
911 
912 	return (RCM_SUCCESS);
913 }
914 
915 /*
916  * ip_usage - Determine the usage of a device.  Call with cache_lock held.
917  *	    The returned buffer is owned by caller, and the caller
918  *	    must free it up when done.
919  */
920 static char *
921 ip_usage(ip_cache_t *node)
922 {
923 	ip_lif_t *lif;
924 	int numifs;
925 	char *buf;
926 	char *nic;
927 	const char *fmt;
928 	char *sep;
929 	char addrstr[INET6_ADDRSTRLEN];
930 	int offline = 0;
931 	size_t bufsz;
932 
933 	rcm_log_message(RCM_TRACE2, "IP: usage(%s)\n", node->ip_resource);
934 
935 	nic = strchr(node->ip_resource, '/');
936 	nic = nic ? nic + 1 : node->ip_resource;
937 
938 	/* TRANSLATION_NOTE: separator used between IP addresses */
939 	sep = _(", ");
940 
941 	numifs = 0;
942 	for (lif = node->ip_pif->pi_lifs; lif != NULL; lif = lif->li_next) {
943 		if (lif->li_ifflags & IFF_UP) {
944 			numifs++;
945 		}
946 	}
947 
948 	if (node->ip_cachestate & CACHE_IF_OFFLINED) {
949 		offline++;
950 	}
951 
952 	if (!offline && numifs) {
953 		fmt = _("%1$s hosts IP addresses: ");
954 	} else if (offline) {
955 		fmt = _("%1$s offlined");
956 	} else {
957 		fmt = _("%1$s plumbed but down");
958 	}
959 
960 	/* space for addresses and separators, plus message */
961 	bufsz = ((numifs * (INET6_ADDRSTRLEN + strlen(sep))) +
962 	    strlen(fmt) + strlen(nic) + 1);
963 	if ((buf = malloc(bufsz)) == NULL) {
964 		rcm_log_message(RCM_ERROR,
965 		    _("IP: usage(%s) malloc failure(%s)\n"),
966 		    node->ip_resource, strerror(errno));
967 		return (NULL);
968 	}
969 	bzero(buf, bufsz);
970 	(void) sprintf(buf, fmt, nic);
971 
972 	if (offline || (numifs == 0)) {	/* Nothing else to do */
973 		rcm_log_message(RCM_TRACE2, "IP: usage (%s) info = %s\n",
974 		    node->ip_resource, buf);
975 
976 		return (buf);
977 	}
978 
979 	for (lif = node->ip_pif->pi_lifs; lif != NULL; lif = lif->li_next) {
980 
981 		void *addr;
982 		int af;
983 
984 		if (!(lif->li_ifflags & IFF_UP)) {
985 			/* ignore interfaces not up */
986 			continue;
987 		}
988 		af = lif->li_addr.family;
989 		if (af == AF_INET6) {
990 			addr = &lif->li_addr.ip6.sin6_addr;
991 		} else if (af == AF_INET) {
992 			addr = &lif->li_addr.ip4.sin_addr;
993 		} else {
994 			rcm_log_message(RCM_DEBUG,
995 			    "IP: unknown addr family %d, assuming AF_INET\n",
996 			    af);
997 			af = AF_INET;
998 			addr = &lif->li_addr.ip4.sin_addr;
999 		}
1000 		if (inet_ntop(af, addr, addrstr, INET6_ADDRSTRLEN) == NULL) {
1001 			rcm_log_message(RCM_ERROR,
1002 			    _("IP: inet_ntop: %s\n"), strerror(errno));
1003 			continue;
1004 		}
1005 		rcm_log_message(RCM_DEBUG, "IP addr := %s\n", addrstr);
1006 
1007 		(void) strcat(buf, addrstr);
1008 		numifs--;
1009 		if (numifs > 0) {
1010 			(void) strcat(buf, ", ");
1011 		}
1012 	}
1013 
1014 	rcm_log_message(RCM_TRACE2, "IP: usage (%s) info = %s\n",
1015 	    node->ip_resource, buf);
1016 
1017 	return (buf);
1018 }
1019 
1020 /*
1021  * Cache management routines, all cache management functions should be
1022  * be called with cache_lock held.
1023  */
1024 
1025 /*
1026  * cache_lookup() - Get a cache node for a resource. Supports VLAN interfaces.
1027  *		  Call with cache lock held.
1028  *
1029  * This ensures that the cache is consistent with the system state and
1030  * returns a pointer to the cache element corresponding to the resource.
1031  */
1032 static ip_cache_t *
1033 cache_lookup(rcm_handle_t *hd, char *rsrc, char options)
1034 {
1035 	ip_cache_t *probe;
1036 	char *resource;		/* physical resource */
1037 
1038 	rcm_log_message(RCM_TRACE2, "IP: cache lookup(%s)\n", rsrc);
1039 
1040 	if ((options & CACHE_REFRESH) && (hd != NULL)) {
1041 		/* drop lock since update locks cache again */
1042 		(void) mutex_unlock(&cache_lock);
1043 		(void) update_cache(hd);
1044 		(void) mutex_lock(&cache_lock);
1045 	}
1046 
1047 	if ((resource = get_physical_resource(rsrc)) == NULL) {
1048 		errno = ENOENT;
1049 		return (NULL);
1050 	}
1051 
1052 	probe = cache_head.ip_next;
1053 	while (probe != &cache_tail) {
1054 		if (probe->ip_resource &&
1055 		    STREQ(resource, probe->ip_resource)) {
1056 			rcm_log_message(RCM_TRACE2,
1057 			    "IP: cache lookup success(%s)\n", rsrc);
1058 			free(resource);
1059 			return (probe);
1060 		}
1061 		probe = probe->ip_next;
1062 	}
1063 	free(resource);
1064 	return (NULL);
1065 }
1066 
1067 /*
1068  * free_node - Free a node from the cache
1069  *	     Call with cache_lock held.
1070  */
1071 static void
1072 free_node(ip_cache_t *node)
1073 {
1074 	ip_pif_t *pif;
1075 	ip_lif_t *lif, *tmplif;
1076 
1077 	if (node) {
1078 		if (node->ip_resource) {
1079 			free(node->ip_resource);
1080 		}
1081 
1082 		/* free the pif */
1083 		pif = node->ip_pif;
1084 		if (pif) {
1085 			/* free logical interfaces */
1086 			lif = pif->pi_lifs;
1087 			while (lif) {
1088 				tmplif = lif->li_next;
1089 				free(lif);
1090 				lif = tmplif;
1091 			}
1092 			free(pif);
1093 		}
1094 		free(node);
1095 	}
1096 }
1097 
1098 /*
1099  * cache_insert - Insert a resource node in cache
1100  *		Call with the cache_lock held.
1101  */
1102 static void
1103 cache_insert(ip_cache_t *node)
1104 {
1105 	/* insert at the head for best performance */
1106 	node->ip_next = cache_head.ip_next;
1107 	node->ip_prev = &cache_head;
1108 
1109 	node->ip_next->ip_prev = node;
1110 	node->ip_prev->ip_next = node;
1111 }
1112 
1113 /*
1114  * cache_remove() - Remove a resource node from cache.
1115  *		  Call with the cache_lock held.
1116  */
1117 static void
1118 cache_remove(ip_cache_t *node)
1119 {
1120 	node->ip_next->ip_prev = node->ip_prev;
1121 	node->ip_prev->ip_next = node->ip_next;
1122 	node->ip_next = NULL;
1123 	node->ip_prev = NULL;
1124 }
1125 
1126 /*
1127  * update_pif() - Update physical interface properties
1128  *		Call with cache_lock held
1129  */
1130 /*ARGSUSED*/
1131 static int
1132 update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr)
1133 {
1134 	char	ifname[RCM_NET_RESOURCE_MAX];
1135 	ifspec_t ifspec;
1136 	ushort_t ifnumber = 0;
1137 	ip_cache_t *probe;
1138 	ip_pif_t pif;
1139 	ip_pif_t *probepif;
1140 	ip_lif_t *probelif;
1141 	struct lifreq lifreq;
1142 	struct sockaddr_storage ifaddr;
1143 	uint64_t ifflags;
1144 	int lif_listed = 0;
1145 
1146 	rcm_log_message(RCM_TRACE1, "IP: update_pif(%s)\n", lifr->lifr_name);
1147 
1148 	if (!ifparse_ifspec(lifr->lifr_name, &ifspec)) {
1149 		rcm_log_message(RCM_ERROR, _("IP: bad network interface: %s\n"),
1150 		    lifr->lifr_name);
1151 		return (-1);
1152 	}
1153 
1154 	(void) snprintf(pif.pi_ifname, sizeof (pif.pi_ifname), "%s%d",
1155 	    ifspec.ifsp_devnm, ifspec.ifsp_ppa);
1156 	if (ifspec.ifsp_lunvalid)
1157 		ifnumber = ifspec.ifsp_lun;
1158 
1159 	/* Get the interface flags */
1160 	(void) strcpy(lifreq.lifr_name, lifr->lifr_name);
1161 	if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
1162 		rcm_log_message(RCM_ERROR,
1163 		    _("IP: SIOCGLIFFLAGS(%s): %s\n"),
1164 		    pif.pi_ifname, strerror(errno));
1165 		return (-1);
1166 	}
1167 	(void) memcpy(&ifflags, &lifreq.lifr_flags, sizeof (ifflags));
1168 
1169 	/* Ignore loopback and multipoint interfaces */
1170 	if (!(ifflags & IFF_MULTICAST) || (ifflags & IFF_LOOPBACK)) {
1171 		rcm_log_message(RCM_TRACE3, "IP: if ignored (%s)\n",
1172 		    pif.pi_ifname);
1173 		return (0);
1174 	}
1175 
1176 	/* Get the interface group name for this interface */
1177 	if (ioctl(sock, SIOCGLIFGROUPNAME, (char *)&lifreq) < 0) {
1178 		rcm_log_message(RCM_ERROR,
1179 		    _("IP: SIOCGLIFGROUPNAME(%s): %s\n"),
1180 		    lifreq.lifr_name, strerror(errno));
1181 		return (-1);
1182 	}
1183 
1184 	/* copy the group name */
1185 	(void) memcpy(&pif.pi_grpname, &lifreq.lifr_groupname,
1186 	    sizeof (pif.pi_grpname));
1187 	pif.pi_grpname[sizeof (pif.pi_grpname) - 1] = '\0';
1188 
1189 	/* Get the interface address for this interface */
1190 	if (ioctl(sock, SIOCGLIFADDR, (char *)&lifreq) < 0) {
1191 		rcm_log_message(RCM_ERROR,
1192 		    _("IP: SIOCGLIFADDR(%s): %s\n"),
1193 		    lifreq.lifr_name, strerror(errno));
1194 		return (-1);
1195 	}
1196 	(void) memcpy(&ifaddr, &lifreq.lifr_addr, sizeof (ifaddr));
1197 
1198 	/* Search for the interface in our cache */
1199 	(void) snprintf(ifname, sizeof (ifname), "%s/%s", RCM_NET_PREFIX,
1200 	    pif.pi_ifname);
1201 
1202 	probe = cache_lookup(hd, ifname, CACHE_NO_REFRESH);
1203 	if (probe != NULL) {
1204 		probe->ip_cachestate &= ~(CACHE_IF_STALE);
1205 	} else {
1206 		if ((probe = calloc(1, sizeof (ip_cache_t))) == NULL) {
1207 			/* malloc errors are bad */
1208 			rcm_log_message(RCM_ERROR, _("IP: calloc: %s\n"),
1209 			    strerror(errno));
1210 			return (-1);
1211 		}
1212 
1213 		probe->ip_resource = get_physical_resource(ifname);
1214 		if (!probe->ip_resource) {
1215 			rcm_log_message(RCM_ERROR, _("IP: strdup: %s\n"),
1216 			    strerror(errno));
1217 			free(probe);
1218 			return (-1);
1219 		}
1220 
1221 		probe->ip_pif = NULL;
1222 		probe->ip_ifred = RCM_IPMP_MIN_REDUNDANCY;
1223 		probe->ip_cachestate |= CACHE_IF_NEW;
1224 
1225 		cache_insert(probe);
1226 	}
1227 
1228 	probepif = probe->ip_pif;
1229 	if (probepif != NULL) {
1230 		/* Check if lifs need to be updated */
1231 		probelif = probepif->pi_lifs;
1232 		while (probelif != NULL) {
1233 			if ((probelif->li_ifnum == ifnumber) &&
1234 			    (probelif->li_addr.family == ifaddr.ss_family)) {
1235 
1236 				rcm_log_message(RCM_TRACE2,
1237 				    "IP: refreshing lifs for %s, ifnum=%d\n",
1238 				    pif.pi_ifname, probelif->li_ifnum);
1239 
1240 				/* refresh lif properties */
1241 				(void) memcpy(&probelif->li_addr, &ifaddr,
1242 				    sizeof (probelif->li_addr));
1243 
1244 				probelif->li_ifflags = ifflags;
1245 
1246 				lif_listed++;
1247 				probe->ip_cachestate |= CACHE_IF_UPDATED;
1248 				probelif->li_cachestate &= ~(CACHE_IF_STALE);
1249 				break;
1250 			}
1251 			probelif = probelif->li_next;
1252 		}
1253 	}
1254 
1255 	if (probepif == NULL) {
1256 		if ((probepif = calloc(1, sizeof (ip_pif_t))) == NULL) {
1257 			rcm_log_message(RCM_ERROR, _("IP: malloc: %s\n"),
1258 			    strerror(errno));
1259 			if (probe->ip_pif == NULL) {
1260 				/* we created it, so clean it up */
1261 				free(probe);
1262 			}
1263 			return (-1);
1264 		}
1265 
1266 		probe->ip_pif = probepif;
1267 
1268 		probe->ip_cachestate |= CACHE_IF_UPDATED;
1269 
1270 		/* Save interface name */
1271 		(void) memcpy(&probepif->pi_ifname, &pif.pi_ifname,
1272 		    sizeof (pif.pi_ifname));
1273 	}
1274 
1275 	/* save pif properties */
1276 	(void) memcpy(&probepif->pi_grpname, &pif.pi_grpname,
1277 	    sizeof (pif.pi_grpname));
1278 
1279 	/* add lif, if this is a lif and it is not in cache */
1280 	if (!lif_listed) {
1281 		rcm_log_message(RCM_TRACE2, "IP: adding lifs to %s\n",
1282 		    pif.pi_ifname);
1283 
1284 		if ((probelif = calloc(1, sizeof (ip_lif_t))) == NULL) {
1285 			rcm_log_message(RCM_ERROR, _("IP: malloc: %s\n"),
1286 			    strerror(errno));
1287 			return (-1);
1288 		}
1289 
1290 		/* save lif properties */
1291 		(void) memcpy(&probelif->li_addr, &ifaddr,
1292 		    sizeof (probelif->li_addr));
1293 
1294 		probelif->li_ifnum = ifnumber;
1295 		probelif->li_ifflags = ifflags;
1296 
1297 		/* insert us at the head of the lif list */
1298 		probelif->li_next = probepif->pi_lifs;
1299 		if (probelif->li_next != NULL) {
1300 			probelif->li_next->li_prev = probelif;
1301 		}
1302 		probelif->li_prev = NULL;
1303 		probelif->li_pif = probepif;
1304 
1305 		probepif->pi_lifs = probelif;
1306 		probelif->li_cachestate = CACHE_IF_NEW;
1307 
1308 		probe->ip_cachestate |= CACHE_IF_UPDATED;
1309 	}
1310 
1311 	rcm_log_message(RCM_TRACE3, "IP: update_pif: (%s) success\n",
1312 	    probe->ip_resource);
1313 
1314 	return (0);
1315 }
1316 
1317 /*
1318  * update_ipifs() - Determine all network interfaces in the system
1319  *		  Call with cache_lock held
1320  */
1321 static int
1322 update_ipifs(rcm_handle_t *hd, int af)
1323 {
1324 	int sock;
1325 	char *buf;
1326 	struct lifnum lifn;
1327 	struct lifconf lifc;
1328 	struct lifreq *lifrp;
1329 	int i;
1330 
1331 	rcm_log_message(RCM_TRACE2, "IP: update_ipifs\n");
1332 
1333 	if ((sock = socket(af, SOCK_DGRAM, 0)) == -1) {
1334 		rcm_log_message(RCM_ERROR,
1335 		    _("IP: failure opening %s socket: %s\n"),
1336 		    af == AF_INET6 ? "IPv6" : "IPv4", strerror(errno));
1337 		return (-1);
1338 	}
1339 
1340 	lifn.lifn_family = af;
1341 	lifn.lifn_flags = 0;
1342 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
1343 		rcm_log_message(RCM_ERROR,
1344 		    _("IP: SIOCLGIFNUM failed: %s\n"),
1345 		    strerror(errno));
1346 		(void) close(sock);
1347 		return (-1);
1348 	}
1349 
1350 	if ((buf = calloc(lifn.lifn_count, sizeof (struct lifreq))) == NULL) {
1351 		rcm_log_message(RCM_ERROR, _("IP: calloc: %s\n"),
1352 		    strerror(errno));
1353 		(void) close(sock);
1354 		return (-1);
1355 	}
1356 
1357 	lifc.lifc_family = af;
1358 	lifc.lifc_flags = 0;
1359 	lifc.lifc_len = sizeof (struct lifreq) * lifn.lifn_count;
1360 	lifc.lifc_buf = buf;
1361 
1362 	if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
1363 		rcm_log_message(RCM_ERROR,
1364 		    _("IP: SIOCGLIFCONF failed: %s\n"),
1365 		    strerror(errno));
1366 		free(buf);
1367 		(void) close(sock);
1368 		return (-1);
1369 	}
1370 
1371 	/* now we need to search for active interfaces */
1372 	lifrp = lifc.lifc_req;
1373 	for (i = 0; i < lifn.lifn_count; i++) {
1374 		(void) update_pif(hd, af, sock, lifrp);
1375 		lifrp++;
1376 	}
1377 
1378 	free(buf);
1379 	(void) close(sock);
1380 	return (0);
1381 }
1382 
1383 /*
1384  * update_cache() - Update cache with latest interface info
1385  */
1386 static int
1387 update_cache(rcm_handle_t *hd)
1388 {
1389 	ip_cache_t *probe;
1390 	struct ip_lif *lif;
1391 	struct ip_lif *nextlif;
1392 	int rv;
1393 	int i;
1394 
1395 	rcm_log_message(RCM_TRACE2, "IP: update_cache\n");
1396 
1397 	(void) mutex_lock(&cache_lock);
1398 
1399 	/* first we walk the entire cache, marking each entry stale */
1400 	probe = cache_head.ip_next;
1401 	while (probe != &cache_tail) {
1402 		probe->ip_cachestate |= CACHE_IF_STALE;
1403 		if ((probe->ip_pif != NULL) &&
1404 		    ((lif = probe->ip_pif->pi_lifs) != NULL)) {
1405 			while (lif != NULL) {
1406 				lif->li_cachestate |= CACHE_IF_STALE;
1407 				lif = lif->li_next;
1408 			}
1409 		}
1410 		probe = probe->ip_next;
1411 	}
1412 
1413 	rcm_log_message(RCM_TRACE2, "IP: scanning IPv4 interfaces\n");
1414 	if (update_ipifs(hd, AF_INET) < 0) {
1415 		(void) mutex_unlock(&cache_lock);
1416 		return (-1);
1417 	}
1418 
1419 	rcm_log_message(RCM_TRACE2, "IP: scanning IPv6 interfaces\n");
1420 	if (update_ipifs(hd, AF_INET6) < 0) {
1421 		(void) mutex_unlock(&cache_lock);
1422 		return (-1);
1423 	}
1424 
1425 	probe = cache_head.ip_next;
1426 	/* unregister devices that are not offlined and still in cache */
1427 	while (probe != &cache_tail) {
1428 		ip_cache_t *freeit;
1429 		if ((probe->ip_pif != NULL) &&
1430 		    ((lif = probe->ip_pif->pi_lifs) != NULL)) {
1431 			/* clear stale lifs */
1432 			while (lif != NULL) {
1433 				if (lif->li_cachestate & CACHE_IF_STALE) {
1434 					nextlif = lif->li_next;
1435 					if (lif->li_prev != NULL)
1436 						lif->li_prev->li_next = nextlif;
1437 					if (nextlif != NULL)
1438 						nextlif->li_prev = lif->li_prev;
1439 					if (probe->ip_pif->pi_lifs == lif)
1440 						probe->ip_pif->pi_lifs =
1441 						    nextlif;
1442 					for (i = 0; i < IP_MAX_MODS; i++) {
1443 						free(lif->li_modules[i]);
1444 					}
1445 					free(lif->li_reconfig);
1446 					free(lif);
1447 					lif = nextlif;
1448 				} else {
1449 					lif = lif->li_next;
1450 				}
1451 			}
1452 		}
1453 		if ((probe->ip_cachestate & CACHE_IF_STALE) &&
1454 		    !(probe->ip_cachestate & CACHE_IF_OFFLINED)) {
1455 			(void) rcm_unregister_interest(hd, probe->ip_resource,
1456 			    0);
1457 			rcm_log_message(RCM_DEBUG, "IP: unregistered %s\n",
1458 			    probe->ip_resource);
1459 			freeit = probe;
1460 			probe = probe->ip_next;
1461 			cache_remove(freeit);
1462 			free_node(freeit);
1463 			continue;
1464 		}
1465 
1466 		if (!(probe->ip_cachestate & CACHE_IF_NEW)) {
1467 			probe = probe->ip_next;
1468 			continue;
1469 		}
1470 
1471 		rv = rcm_register_interest(hd, probe->ip_resource, 0, NULL);
1472 		if (rv != RCM_SUCCESS) {
1473 			rcm_log_message(RCM_ERROR,
1474 			    _("IP: failed to register %s\n"),
1475 			    probe->ip_resource);
1476 			(void) mutex_unlock(&cache_lock);
1477 			return (-1);
1478 		} else {
1479 			rcm_log_message(RCM_DEBUG, "IP: registered %s\n",
1480 			    probe->ip_resource);
1481 			probe->ip_cachestate &= ~(CACHE_IF_NEW);
1482 		}
1483 		probe = probe->ip_next;
1484 	}
1485 
1486 	(void) mutex_unlock(&cache_lock);
1487 	return (0);
1488 }
1489 
1490 /*
1491  * free_cache() - Empty the cache
1492  */
1493 static void
1494 free_cache()
1495 {
1496 	ip_cache_t *probe;
1497 
1498 	rcm_log_message(RCM_TRACE2, "IP: free_cache\n");
1499 
1500 	(void) mutex_lock(&cache_lock);
1501 	probe = cache_head.ip_next;
1502 	while (probe != &cache_tail) {
1503 		cache_remove(probe);
1504 		free_node(probe);
1505 		probe = cache_head.ip_next;
1506 	}
1507 	(void) mutex_unlock(&cache_lock);
1508 }
1509 
1510 /*
1511  * ip_log_err() - RCM error log wrapper
1512  */
1513 static void
1514 ip_log_err(ip_cache_t *node, char **errorp, char *errmsg)
1515 {
1516 	char *nic = NULL;
1517 	int len;
1518 	const char *errfmt;
1519 	char *error;
1520 
1521 	if ((node != NULL) && (node->ip_pif != NULL) &&
1522 	    (node->ip_pif->pi_ifname != NULL)) {
1523 		nic = strrchr(node->ip_pif->pi_ifname, '/');
1524 		nic = nic ? nic + 1 : node->ip_pif->pi_ifname;
1525 	}
1526 
1527 	if (errorp != NULL)
1528 		*errorp = NULL;
1529 
1530 	if (nic == NULL) {
1531 		rcm_log_message(RCM_ERROR, _("IP: %s\n"), errmsg);
1532 		errfmt = _("IP: %s");
1533 		len = strlen(errfmt) + strlen(errmsg) + 1;
1534 		if (error = (char *)calloc(1, len)) {
1535 			(void) sprintf(error, errfmt, errmsg);
1536 		}
1537 	} else {
1538 		rcm_log_message(RCM_ERROR, _("IP: %s(%s)\n"), errmsg, nic);
1539 		errfmt = _("IP: %s(%s)");
1540 		len = strlen(errfmt) + strlen(errmsg) + strlen(nic) + 1;
1541 		if (error = (char *)calloc(1, len)) {
1542 			(void) sprintf(error, errfmt, errmsg, nic);
1543 		}
1544 	}
1545 
1546 	if (errorp != NULL)
1547 		*errorp = error;
1548 }
1549 
1550 
1551 /*
1552  * if_cfginfo() - Save off the config info for all interfaces
1553  */
1554 static int
1555 if_cfginfo(ip_cache_t *node, uint_t force)
1556 {
1557 	ip_lif_t *lif;
1558 	ip_pif_t *pif;
1559 	int i;
1560 	FILE *fp;
1561 	char syscmd[MAX_RECONFIG_SIZE + LIFNAMSIZ];
1562 	char buf[MAX_RECONFIG_SIZE];
1563 
1564 	rcm_log_message(RCM_TRACE2, "IP: if_cfginfo(%s)\n", node->ip_resource);
1565 
1566 	pif = node->ip_pif;
1567 	lif = pif->pi_lifs;
1568 
1569 	while (lif != NULL) {
1570 		/* Make a list of modules pushed and save */
1571 		if (lif->li_ifnum == 0) {	/* physical instance */
1572 			if (get_modlist(pif->pi_ifname, lif) == -1) {
1573 				rcm_log_message(RCM_ERROR,
1574 				    _("IP: get modlist error (%s) %s\n"),
1575 				    pif->pi_ifname, strerror(errno));
1576 				(void) clr_cfg_state(pif);
1577 				return (-1);
1578 			}
1579 
1580 			if (!force) {
1581 				/* Look if unknown modules have been inserted */
1582 				for (i = (lif->li_modcnt - 2); i > 0; i--) {
1583 					if (modop(pif->pi_ifname,
1584 					    lif->li_modules[i],
1585 					    i, MOD_CHECK) == -1) {
1586 						rcm_log_message(RCM_ERROR,
1587 						    _("IP: module %s@%d\n"),
1588 						    lif->li_modules[i], i);
1589 						(void) clr_cfg_state(pif);
1590 						return (-1);
1591 					}
1592 				}
1593 			}
1594 
1595 			/* Last module is the device driver, so ignore that */
1596 			for (i = (lif->li_modcnt - 2); i > 0; i--) {
1597 				rcm_log_message(RCM_TRACE2,
1598 				    "IP: modremove Pos = %d, Module = %s \n",
1599 				    i, lif->li_modules[i]);
1600 				if (modop(pif->pi_ifname, lif->li_modules[i],
1601 				    i, MOD_REMOVE) == -1) {
1602 					while (i != (lif->li_modcnt - 2)) {
1603 						if (modop(pif->pi_ifname,
1604 						    lif->li_modules[i],
1605 						    i, MOD_INSERT) == -1) {
1606 							/* Gross error */
1607 							rcm_log_message(
1608 							    RCM_ERROR,
1609 							    _("IP: if_cfginfo"
1610 							    "(%s) %s\n"),
1611 							    pif->pi_ifname,
1612 							    strerror(errno));
1613 							    clr_cfg_state(pif);
1614 							return (-1);
1615 						}
1616 						i++;
1617 					}
1618 					rcm_log_message(
1619 					    RCM_ERROR,
1620 					    _("IP: if_cfginfo(%s): modremove "
1621 					    "%s failed: %s\n"), pif->pi_ifname,
1622 					    lif->li_modules[i],
1623 					    strerror(errno));
1624 					    clr_cfg_state(pif);
1625 					return (-1);
1626 				}
1627 			}
1628 		}
1629 
1630 		/* Save reconfiguration information */
1631 		if (lif->li_ifflags & IFF_IPV4) {
1632 			(void) snprintf(syscmd, sizeof (syscmd),
1633 			    "%s %s:%d configinfo\n", USR_SBIN_IFCONFIG,
1634 			    pif->pi_ifname, lif->li_ifnum);
1635 		} else if (lif->li_ifflags & IFF_IPV6) {
1636 			(void) snprintf(syscmd, sizeof (syscmd),
1637 			    "%s %s:%d inet6 configinfo\n", USR_SBIN_IFCONFIG,
1638 			    pif->pi_ifname, lif->li_ifnum);
1639 		}
1640 		rcm_log_message(RCM_TRACE2, "IP: %s\n", syscmd);
1641 
1642 		/* open a pipe to retrieve reconfiguration info */
1643 		if ((fp = popen(syscmd, "r")) == NULL) {
1644 			rcm_log_message(RCM_ERROR,
1645 			    _("IP: ifconfig configinfo error (%s:%d) %s\n"),
1646 			    pif->pi_ifname, lif->li_ifnum, strerror(errno));
1647 			(void) clr_cfg_state(pif);
1648 			return (-1);
1649 		}
1650 		bzero(buf, MAX_RECONFIG_SIZE);
1651 
1652 		if (fgets(buf, MAX_RECONFIG_SIZE, fp) == NULL) {
1653 			rcm_log_message(RCM_ERROR,
1654 			    _("IP: ifconfig configinfo error (%s:%d) %s\n"),
1655 			    pif->pi_ifname, lif->li_ifnum, strerror(errno));
1656 			(void) pclose(fp);
1657 			(void) clr_cfg_state(pif);
1658 			return (-1);
1659 		}
1660 		(void) pclose(fp);
1661 
1662 		lif->li_reconfig = malloc(strlen(buf)+1);
1663 		if (lif->li_reconfig == NULL) {
1664 			rcm_log_message(RCM_ERROR,
1665 			    _("IP: malloc error (%s) %s\n"),
1666 			    pif->pi_ifname, strerror(errno));
1667 			(void) clr_cfg_state(pif);
1668 			return (-1);
1669 		}
1670 		(void) strcpy(lif->li_reconfig, buf);
1671 		rcm_log_message(RCM_DEBUG,
1672 		    "IP: if_cfginfo: reconfig string(%s:%d) = %s\n",
1673 		    pif->pi_ifname, lif->li_ifnum, lif->li_reconfig);
1674 
1675 		lif = lif->li_next;
1676 	}
1677 
1678 	return (0);
1679 }
1680 
1681 /*
1682  * if_unplumb() - Unplumb the interface
1683  *		Save off the modlist, ifconfig options and unplumb.
1684  *		Fail, if an unknown module lives between IP and driver and
1685  *		force is not set
1686  *		Call with cache_lock held
1687  */
1688 static int
1689 if_unplumb(ip_cache_t *node)
1690 {
1691 	ip_lif_t *lif;
1692 	ip_pif_t *pif;
1693 	int ipv4 = 0, ipv6 = 0;
1694 	char syscmd[MAX_RECONFIG_SIZE + LIFNAMSIZ];
1695 
1696 	rcm_log_message(RCM_TRACE2, "IP: if_unplumb(%s)\n", node->ip_resource);
1697 
1698 	pif = node->ip_pif;
1699 	lif = pif->pi_lifs;
1700 
1701 	while (lif != NULL) {
1702 		if (lif->li_ifflags & IFF_IPV4) {
1703 			ipv4++;
1704 		} else if (lif->li_ifflags & IFF_IPV6) {
1705 			ipv6++;
1706 		} else {
1707 			/* Unlikely case */
1708 			rcm_log_message(RCM_DEBUG,
1709 			    _("IP: Unplumb ignored (%s:%d)\n"),
1710 			    pif->pi_ifname, lif->li_ifnum);
1711 			lif = lif->li_next;
1712 			continue;
1713 		}
1714 		lif = lif->li_next;
1715 	}
1716 
1717 	/* Unplumb the physical interface */
1718 	if (ipv4) {
1719 		rcm_log_message(RCM_TRACE2,
1720 		    "IP: if_unplumb: ifconfig %s unplumb\n", pif->pi_ifname);
1721 		(void) snprintf(syscmd, sizeof (syscmd), "%s %s unplumb\n",
1722 		    USR_SBIN_IFCONFIG, pif->pi_ifname);
1723 		if (rcm_exec_cmd(syscmd) != 0) {
1724 			rcm_log_message(RCM_ERROR,
1725 			    _("IP: Cannot unplumb (%s) %s\n"),
1726 				    pif->pi_ifname, strerror(errno));
1727 			return (-1);
1728 		}
1729 	}
1730 	if (ipv6) {
1731 		rcm_log_message(RCM_TRACE2,
1732 		    "IP: if_unplumb: ifconfig %s inet6 unplumb\n",
1733 		    pif->pi_ifname);
1734 		(void) snprintf(syscmd, sizeof (syscmd),
1735 		    "%s %s inet6 unplumb\n", USR_SBIN_IFCONFIG, pif->pi_ifname);
1736 		if (rcm_exec_cmd(syscmd) != 0) {
1737 			rcm_log_message(RCM_ERROR,
1738 			    _("IP: Cannot unplumb (%s) %s\n"),
1739 				    pif->pi_ifname, strerror(errno));
1740 			return (-1);
1741 		}
1742 	}
1743 	rcm_log_message(RCM_TRACE2, "IP: if_unplumb(%s) success\n",
1744 	    node->ip_resource);
1745 
1746 	return (0);
1747 }
1748 
1749 /*
1750  * if_replumb() - Undo previous unplumb i.e. plumb back the physical interface
1751  *		instances and the logical interfaces in order, restoring
1752  *		all ifconfig options
1753  *		Call with cache_lock held
1754  */
1755 static int
1756 if_replumb(ip_cache_t *node)
1757 {
1758 	ip_lif_t *lif;
1759 	ip_pif_t *pif;
1760 	int i;
1761 	char syscmd[LIFNAMSIZ+MAXPATHLEN];	/* must be big enough */
1762 	int max_ipv4 = 0, max_ipv6 = 0;
1763 
1764 	rcm_log_message(RCM_TRACE2, "IP: if_replumb(%s)\n", node->ip_resource);
1765 
1766 	/*
1767 	 * Be extra careful about bringing up the interfaces in the
1768 	 * correct order:
1769 	 * - First plumb in the physical interface instances
1770 	 * - modinsert the necessary modules@pos
1771 	 * - Next, add the logical interfaces being careful about
1772 	 *   the order, (follow the cached interface number li_ifnum order)
1773 	 */
1774 
1775 	pif = node->ip_pif;
1776 	lif = pif->pi_lifs;
1777 
1778 	/*
1779 	 * Make a first pass to plumb in physical interfaces and get a count
1780 	 * of the max logical interfaces
1781 	 */
1782 	while (lif != NULL) {
1783 		if (lif->li_ifflags & IFF_IPV4) {
1784 			if (lif->li_ifnum > max_ipv4) {
1785 				max_ipv4 = lif->li_ifnum;
1786 			}
1787 		} else if (lif->li_ifflags & IFF_IPV6) {
1788 			if (lif->li_ifnum > max_ipv6) {
1789 				max_ipv6 = lif->li_ifnum;
1790 			}
1791 		} else {
1792 			/* Unlikely case */
1793 			rcm_log_message(RCM_DEBUG,
1794 			    _("IP: Re-plumb ignored (%s:%d)\n"),
1795 			    pif->pi_ifname, lif->li_ifnum);
1796 			lif = lif->li_next;
1797 			continue;
1798 		}
1799 
1800 		if (lif->li_ifnum == 0) { /* physical interface instance */
1801 			if ((lif->li_ifflags & IFF_NOFAILOVER) ||
1802 			    (strcmp(pif->pi_grpname, "") == 0)) {
1803 				(void) snprintf(syscmd, sizeof (syscmd),
1804 				    "%s %s\n", USR_SBIN_IFCONFIG,
1805 				    lif->li_reconfig);
1806 			} else if (lif->li_ifflags & IFF_IPV4) {
1807 				(void) snprintf(syscmd, sizeof (syscmd),
1808 				    "%s %s inet plumb group %s\n",
1809 				    USR_SBIN_IFCONFIG,
1810 				    pif->pi_ifname, pif->pi_grpname);
1811 			} else if (lif->li_ifflags & IFF_IPV6) {
1812 				(void) snprintf(syscmd, sizeof (syscmd),
1813 				    "%s %s inet6 plumb group %s\n",
1814 				    USR_SBIN_IFCONFIG,
1815 				    pif->pi_ifname, pif->pi_grpname);
1816 			}
1817 
1818 			rcm_log_message(RCM_TRACE2,
1819 			    "IP: if_replumb: %s\n", syscmd);
1820 			if (rcm_exec_cmd(syscmd) != 0) {
1821 				rcm_log_message(RCM_ERROR,
1822 				    _("IP: Cannot plumb (%s) %s\n"),
1823 				    pif->pi_ifname, strerror(errno));
1824 				return (-1);
1825 			}
1826 
1827 			rcm_log_message(RCM_TRACE2,
1828 			    "IP: if_replumb: Modcnt = %d\n", lif->li_modcnt);
1829 			/* modinsert modules in order, ignore driver(last) */
1830 			for (i = 0; i < (lif->li_modcnt - 1); i++) {
1831 				rcm_log_message(RCM_TRACE2,
1832 				    "IP: modinsert: Pos = %d Mod = %s\n",
1833 				    i, lif->li_modules[i]);
1834 				if (modop(pif->pi_ifname, lif->li_modules[i], i,
1835 				    MOD_INSERT) == -1) {
1836 					rcm_log_message(RCM_ERROR,
1837 					    _("IP: modinsert error(%s)\n"),
1838 					    pif->pi_ifname);
1839 					return (-1);
1840 				}
1841 			}
1842 		}
1843 
1844 		lif = lif->li_next;
1845 	}
1846 
1847 	/* Now, add all the logical interfaces in the correct order */
1848 	for (i = 1; i <= MAX(max_ipv6, max_ipv4); i++) {
1849 		/* reset lif through every iteration */
1850 		lif = pif->pi_lifs;
1851 		while (lif != NULL) {
1852 			if (((lif->li_ifflags & IFF_NOFAILOVER) ||
1853 			    (strcmp(pif->pi_grpname, "") == 0)) &&
1854 			    (lif->li_ifnum == i)) {
1855 				/* Plumb in the logical interface */
1856 				(void) snprintf(syscmd, sizeof (syscmd),
1857 				    "%s %s\n", USR_SBIN_IFCONFIG,
1858 				    lif->li_reconfig);
1859 				rcm_log_message(RCM_TRACE2,
1860 				    "IP: if_replumb: %s\n", syscmd);
1861 				if (rcm_exec_cmd(syscmd) != 0) {
1862 					rcm_log_message(RCM_ERROR,
1863 					    _("IP: Cannot addif (%s:%d) "
1864 						"%s\n"),
1865 					    pif->pi_ifname, i, strerror(errno));
1866 					return (-1);
1867 				}
1868 			}
1869 			lif = lif->li_next;
1870 		}
1871 	}
1872 
1873 	rcm_log_message(RCM_TRACE2, "IP: if_replumb(%s) success \n",
1874 	    node->ip_resource);
1875 
1876 	return (0);
1877 }
1878 
1879 /*
1880  * clr_cfg_state() - Cleanup after errors in unplumb
1881  */
1882 static void
1883 clr_cfg_state(ip_pif_t *pif)
1884 {
1885 	ip_lif_t *lif;
1886 	int i;
1887 
1888 	lif = pif->pi_lifs;
1889 
1890 	while (lif != NULL) {
1891 		lif->li_modcnt = 0;
1892 		free(lif->li_reconfig);
1893 		lif->li_reconfig = NULL;
1894 		for (i = 0; i < IP_MAX_MODS; i++) {
1895 			free(lif->li_modules[i]);
1896 			lif->li_modules[i] = NULL;
1897 		}
1898 		lif = lif->li_next;
1899 	}
1900 }
1901 
1902 /*
1903  * ip_ipmp_offline() - Failover from if_from to if_to using a
1904  *		     minimum redudancy of min_red. This uses IPMPs
1905  *		     "offline" mechanism to achieve the failover.
1906  */
1907 static int
1908 ip_ipmp_offline(ip_cache_t *if_from, ip_cache_t *if_to)
1909 {
1910 	mpathd_cmd_t mpdcmd;
1911 
1912 	if ((if_from == NULL) || (if_from->ip_pif == NULL) ||
1913 	    (if_from->ip_pif->pi_ifname == NULL)) {
1914 		return (-1);
1915 	}
1916 
1917 	rcm_log_message(RCM_TRACE1, "IP: ip_ipmp_offline\n");
1918 
1919 	mpdcmd.cmd_command = MI_OFFLINE;
1920 	(void) strcpy(mpdcmd.cmd_ifname, if_from->ip_pif->pi_ifname);
1921 
1922 	if ((if_to != NULL) && (if_to->ip_pif != NULL) &&
1923 	    (if_to->ip_pif->pi_ifname != NULL)) {
1924 		rcm_log_message(RCM_TRACE1, "IP: ip_ipmp_offline (%s)->(%s)\n",
1925 		    if_from->ip_pif->pi_ifname, if_to->ip_pif->pi_ifname);
1926 		(void) strncpy(mpdcmd.cmd_movetoif, if_to->ip_pif->pi_ifname,
1927 		    sizeof (mpdcmd.cmd_movetoif));
1928 		mpdcmd.cmd_movetoif[sizeof (mpdcmd.cmd_movetoif) - 1] = '\0';
1929 	} else {
1930 		rcm_log_message(RCM_TRACE1, "IP: ip_ipmp_offline (%s)->(any)\n",
1931 		    if_from->ip_pif->pi_ifname);
1932 		(void) strcpy(mpdcmd.cmd_movetoif, "");	/* signifies any */
1933 	}
1934 	mpdcmd.cmd_min_red = if_from->ip_ifred;
1935 
1936 	if (mpathd_send_cmd(&mpdcmd) < 0) {
1937 		rcm_log_message(RCM_ERROR,
1938 		    _("IP: mpathd offline error: %s\n"),
1939 		    strerror(errno));
1940 		return (-1);
1941 	}
1942 
1943 	rcm_log_message(RCM_TRACE1, "IP: ipmp offline success\n");
1944 	return (0);
1945 }
1946 
1947 /*
1948  * ip_ipmp_undo_offline() - Undo prior offline of the interface.
1949  *			  This uses IPMPs "undo offline" feature.
1950  */
1951 static int
1952 ip_ipmp_undo_offline(ip_cache_t *node)
1953 {
1954 	mpathd_cmd_t mpdcmd;
1955 
1956 	mpdcmd.cmd_command = MI_UNDO_OFFLINE;
1957 	(void) strcpy(mpdcmd.cmd_ifname, node->ip_pif->pi_ifname);
1958 
1959 	if (mpathd_send_cmd(&mpdcmd) < 0) {
1960 		rcm_log_message(RCM_ERROR,
1961 		    _("IP: mpathd error: %s\n"),
1962 		    strerror(errno));
1963 		return (-1);
1964 	}
1965 
1966 	rcm_log_message(RCM_TRACE1, "IP: ipmp undo offline success\n");
1967 	return (0);
1968 }
1969 
1970 /*
1971  * get_physical_resource() - Convert a name (e.g., "SUNW_network/hme0:1" or
1972  * "SUNW_network/hme1000") into a dynamically allocated string containing the
1973  * associated physical device resource name ("SUNW_network/hme0").  Since we
1974  * assume that interface names map directly to device names, this is a
1975  * pass-through operation, with the exception that logical interface numbers
1976  * and VLANs encoded in the PPA are stripped.  This logic will need to be
1977  * revisited to support administratively-chosen interface names.
1978  */
1979 static char *
1980 get_physical_resource(const char *rsrc)
1981 {
1982 	char		*rsrc_ifname, *ifname;
1983 	ifspec_t	ifspec;
1984 
1985 	rsrc_ifname = strchr(rsrc, '/');
1986 	if (rsrc_ifname == NULL || !ifparse_ifspec(rsrc_ifname + 1, &ifspec)) {
1987 		rcm_log_message(RCM_ERROR, _("IP: bad resource: %s\n"), rsrc);
1988 		return (NULL);
1989 	}
1990 
1991 	ifname = malloc(RCM_NET_RESOURCE_MAX);
1992 	if (ifname == NULL) {
1993 		rcm_log_message(RCM_ERROR, _("IP: malloc error(%s): %s\n"),
1994 		    strerror(errno), rsrc);
1995 		return (NULL);
1996 	}
1997 
1998 	(void) snprintf(ifname, RCM_NET_RESOURCE_MAX, "%s/%s%d", RCM_NET_PREFIX,
1999 	    ifspec.ifsp_devnm, VLAN_GET_PPA(ifspec.ifsp_ppa));
2000 
2001 	return (ifname);
2002 }
2003 
2004 /*
2005  * if_get_flags() - Return the cached physical interface flags
2006  *		  Call with cache_lock held
2007  */
2008 static uint64_t
2009 if_get_flags(ip_pif_t *pif)
2010 {
2011 	ip_lif_t *lif;
2012 
2013 	for (lif = pif->pi_lifs; lif != NULL; lif = lif->li_next) {
2014 		if (lif->li_ifnum == 0) {
2015 			return (lif->li_ifflags & RCM_PIF_FLAGS);
2016 		}
2017 	}
2018 	return (0);
2019 }
2020 
2021 /*
2022  * mpathd_send_cmd() - Sends the command to in.mpathd.
2023  */
2024 static int
2025 mpathd_send_cmd(mpathd_cmd_t *mpd)
2026 {
2027 	mpathd_unoffline_t mpc;
2028 	struct mpathd_response mpr;
2029 	int i;
2030 	int s;
2031 
2032 	rcm_log_message(RCM_TRACE1, "IP: mpathd_send_cmd \n");
2033 
2034 	for (i = 0; i < MPATHD_MAX_RETRIES; i++) {
2035 		s = connect_to_mpathd(AF_INET);
2036 		if (s == -1) {
2037 			s = connect_to_mpathd(AF_INET6);
2038 			if (s == -1) {
2039 				rcm_log_message(RCM_ERROR,
2040 				    _("IP: Cannot talk to mpathd\n"));
2041 				return (-1);
2042 			}
2043 		}
2044 		switch (mpd->cmd_command) {
2045 		case MI_OFFLINE :
2046 			rcm_log_message(RCM_TRACE1, "IP: MI_OFFLINE: "
2047 			    "(%s)->(%s) redundancy = %d\n", mpd->cmd_ifname,
2048 			    mpd->cmd_movetoif, mpd->cmd_min_red);
2049 
2050 			if (write(s, mpd, sizeof (mpathd_cmd_t)) !=
2051 			    sizeof (mpathd_cmd_t)) {
2052 				rcm_log_message(RCM_ERROR,
2053 				    _("IP: mpathd write: %s\n"),
2054 				    strerror(errno));
2055 				(void) close(s);
2056 				return (-1);
2057 			}
2058 			break;
2059 
2060 		case MI_SETOINDEX :
2061 			rcm_log_message(RCM_TRACE1, "IP: MI_SETOINDEX: "
2062 			    "(%s)->(%s) family = %d\n", mpd->from_lifname,
2063 			    mpd->to_pifname, mpd->addr_family);
2064 
2065 			if (write(s, mpd, sizeof (mpathd_cmd_t)) !=
2066 			    sizeof (mpathd_cmd_t)) {
2067 				rcm_log_message(RCM_ERROR,
2068 				    _("IP: mpathd write: %s\n"),
2069 				    strerror(errno));
2070 				(void) close(s);
2071 				return (-1);
2072 			}
2073 			break;
2074 
2075 		case MI_UNDO_OFFLINE:
2076 			/* mpathd checks for exact size of the message */
2077 			mpc.cmd_command = mpd->cmd_command;
2078 			(void) strcpy(mpc.cmd_ifname, mpd->cmd_ifname);
2079 
2080 			rcm_log_message(RCM_TRACE1, "IP: MI_UNDO_OFFLINE: "
2081 			    "(%s)\n", mpd->cmd_ifname);
2082 
2083 			if (write(s, &mpc, sizeof (mpathd_unoffline_t)) !=
2084 			    sizeof (mpathd_unoffline_t)) {
2085 				rcm_log_message(RCM_ERROR,
2086 				    _("IP: mpathd write: %s\n"),
2087 				    strerror(errno));
2088 				(void) close(s);
2089 				return (-1);
2090 			}
2091 			break;
2092 		default :
2093 			rcm_log_message(RCM_ERROR,
2094 			    _("IP: unsupported mpathd command\n"));
2095 			(void) close(s);
2096 			return (-1);
2097 		}
2098 
2099 		bzero(&mpr, sizeof (struct mpathd_response));
2100 		/* Read the result from mpathd */
2101 		if (read(s, &mpr, sizeof (struct mpathd_response)) !=
2102 		    sizeof (struct mpathd_response)) {
2103 			rcm_log_message(RCM_ERROR,
2104 			    _("IP: mpathd read : %s\n"), strerror(errno));
2105 			(void) close(s);
2106 			return (-1);
2107 		}
2108 
2109 		(void) close(s);
2110 		if (mpr.resp_mpathd_err == 0) {
2111 			rcm_log_message(RCM_TRACE1,
2112 			    "IP: mpathd_send_cmd success\n");
2113 			return (0);			/* Successful */
2114 		}
2115 
2116 		if (mpr.resp_mpathd_err == MPATHD_SYS_ERROR) {
2117 			if (mpr.resp_sys_errno == EAGAIN) {
2118 				(void) sleep(1);
2119 				rcm_log_message(RCM_DEBUG,
2120 				    _("IP: mpathd retrying\n"));
2121 				continue;		/* Retry */
2122 			}
2123 			errno = mpr.resp_sys_errno;
2124 			rcm_log_message(RCM_WARNING,
2125 			    _("IP: mpathd_send_cmd error: %s\n"),
2126 			    strerror(errno));
2127 		} else if (mpr.resp_mpathd_err == MPATHD_MIN_RED_ERROR) {
2128 			errno = EIO;
2129 			rcm_log_message(RCM_ERROR, _("IP: in.mpathd(1M): "
2130 			    "Minimum redundancy not met\n"));
2131 		} else {
2132 			rcm_log_message(RCM_ERROR,
2133 			    _("IP: mpathd_send_cmd error\n"));
2134 		}
2135 		/* retry */
2136 	}
2137 
2138 	rcm_log_message(RCM_ERROR,
2139 	    _("IP: mpathd_send_cmd failed %d retries\n"), MPATHD_MAX_RETRIES);
2140 	return (-1);
2141 }
2142 
2143 /*
2144  * Returns -1 on failure. Returns the socket file descriptor on
2145  * success.
2146  */
2147 static int
2148 connect_to_mpathd(int family)
2149 {
2150 	int s;
2151 	struct sockaddr_storage ss;
2152 	struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
2153 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
2154 	struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
2155 	int addrlen;
2156 	int ret;
2157 	int on;
2158 
2159 	rcm_log_message(RCM_TRACE1, "IP: connect_to_mpathd\n");
2160 
2161 	s = socket(family, SOCK_STREAM, 0);
2162 	if (s < 0) {
2163 		rcm_log_message(RCM_ERROR,
2164 		    _("IP: mpathd socket: %s\n"), strerror(errno));
2165 		return (-1);
2166 	}
2167 	bzero((char *)&ss, sizeof (ss));
2168 	ss.ss_family = family;
2169 	/*
2170 	 * Need to bind to a privelged port. For non-root, this
2171 	 * will fail. in.mpathd verifies that only commands coming
2172 	 * from priveleged ports succeed so that the ordinary user
2173 	 * can't issue offline commands.
2174 	 */
2175 	on = 1;
2176 	if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
2177 	    sizeof (on)) < 0) {
2178 		rcm_log_message(RCM_ERROR,
2179 		    _("IP: mpathd setsockopt: TCP_ANONPRIVBIND: %s\n"),
2180 		    strerror(errno));
2181 		return (-1);
2182 	}
2183 	switch (family) {
2184 	case AF_INET:
2185 		sin->sin_port = 0;
2186 		sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2187 		addrlen = sizeof (struct sockaddr_in);
2188 		break;
2189 	case AF_INET6:
2190 		sin6->sin6_port = 0;
2191 		sin6->sin6_addr = loopback_addr;
2192 		addrlen = sizeof (struct sockaddr_in6);
2193 		break;
2194 	}
2195 	ret = bind(s, (struct sockaddr *)&ss, addrlen);
2196 	if (ret != 0) {
2197 		rcm_log_message(RCM_ERROR,
2198 		    _("IP: mpathd bind: %s\n"), strerror(errno));
2199 		return (-1);
2200 	}
2201 	switch (family) {
2202 	case AF_INET:
2203 		sin->sin_port = htons(MPATHD_PORT);
2204 		break;
2205 	case AF_INET6:
2206 		sin6->sin6_port = htons(MPATHD_PORT);
2207 		break;
2208 	}
2209 	ret = connect(s, (struct sockaddr *)&ss, addrlen);
2210 	if (ret != 0) {
2211 		if (errno == ECONNREFUSED) {
2212 			/* in.mpathd is not running, start it */
2213 			if (rcm_exec_cmd(MPATHD_PATH) == -1) {
2214 				rcm_log_message(RCM_ERROR,
2215 				    _("IP: mpathd exec: %s\n"),
2216 				    strerror(errno));
2217 				return (-1);
2218 			}
2219 			ret = connect(s, (struct sockaddr *)&ss, addrlen);
2220 		}
2221 		if (ret != 0) {
2222 			rcm_log_message(RCM_ERROR,
2223 			    _("IP: mpathd connect: %s\n"), strerror(errno));
2224 			return (-1);
2225 		}
2226 	}
2227 	on = 0;
2228 	if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
2229 	    sizeof (on)) < 0) {
2230 		rcm_log_message(RCM_ERROR,
2231 		    _("IP: mpathd setsockopt TCP_ANONPRIVBIND: %s\n"),
2232 		    strerror(errno));
2233 		return (-1);
2234 	}
2235 
2236 	rcm_log_message(RCM_TRACE1, "IP: connect_to_mpathd success\n");
2237 
2238 	return (s);
2239 }
2240 
2241 /*
2242  * modop() - Remove/insert a module
2243  */
2244 static int
2245 modop(char *name, char *arg, int pos, char op)
2246 {
2247 	char syscmd[LIFNAMSIZ+MAXPATHLEN];	/* must be big enough */
2248 
2249 	rcm_log_message(RCM_TRACE1, "IP: modop(%s)\n", name);
2250 
2251 	/* Nothing to do with "ip", "arp" */
2252 	if ((arg == NULL) || (strcmp(arg, "") == 0) ||
2253 	    STREQ(arg, IP_MOD_NAME) || STREQ(arg, ARP_MOD_NAME)) {
2254 		rcm_log_message(RCM_TRACE1, "IP: modop success\n");
2255 		return (0);
2256 	}
2257 
2258 	if (op == MOD_CHECK) {
2259 		/*
2260 		 * No known good modules (yet) apart from ip and arp
2261 		 * which are handled above
2262 		 */
2263 		return (-1);
2264 	}
2265 
2266 	if (op == MOD_REMOVE) {
2267 		(void) snprintf(syscmd, sizeof (syscmd),
2268 		    "%s %s modremove %s@%d\n", USR_SBIN_IFCONFIG, name, arg,
2269 		    pos);
2270 	} else if (op == MOD_INSERT) {
2271 		(void) snprintf(syscmd, sizeof (syscmd),
2272 		    "%s %s modinsert %s@%d\n", USR_SBIN_IFCONFIG, name, arg,
2273 		    pos);
2274 	} else {
2275 		rcm_log_message(RCM_ERROR,
2276 		    _("IP: modop(%s): unknown operation\n"), name);
2277 		return (-1);
2278 	}
2279 
2280 	rcm_log_message(RCM_TRACE1, "IP: modop(%s): %s\n", name, syscmd);
2281 	if (rcm_exec_cmd(syscmd) == -1) {
2282 		rcm_log_message(RCM_ERROR,
2283 		    _("IP: modop(%s): %s\n"), name, strerror(errno));
2284 		return (-1);
2285 	}
2286 
2287 	rcm_log_message(RCM_TRACE1, "IP: modop success\n");
2288 	return (0);
2289 }
2290 
2291 /*
2292  * get_modlist() - return a list of pushed mid-stream modules
2293  *		 Required memory is malloced to construct the list,
2294  *		 Caller must free this memory list
2295  *		 Call with cache_lock held
2296  */
2297 static int
2298 get_modlist(char *name, ip_lif_t *lif)
2299 {
2300 	int udp_fd;
2301 	int fd;
2302 	int i;
2303 	int num_mods;
2304 	struct lifreq lifr;
2305 	struct str_list strlist;
2306 
2307 	rcm_log_message(RCM_TRACE1, "IP: getmodlist(%s)\n", name);
2308 
2309 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2310 	lifr.lifr_flags = lif->li_ifflags;
2311 	if (ip_domux2fd(&udp_fd, &fd, &lifr) < 0) {
2312 		rcm_log_message(RCM_ERROR, _("IP: ip_domux2fd(%s)\n"), name);
2313 		return (-1);
2314 	}
2315 
2316 	if ((num_mods = ioctl(fd, I_LIST, NULL)) < 0) {
2317 		rcm_log_message(RCM_ERROR,
2318 		    _("IP: get_modlist(%s): I_LIST(%s) \n"),
2319 		    name, strerror(errno));
2320 		(void) ip_plink(udp_fd, fd, &lifr);
2321 		return (-1);
2322 	}
2323 
2324 	strlist.sl_nmods = num_mods;
2325 	strlist.sl_modlist = malloc(sizeof (struct str_mlist) * num_mods);
2326 
2327 	if (strlist.sl_modlist == NULL) {
2328 		rcm_log_message(RCM_ERROR, _("IP: get_modlist(%s): %s\n"),
2329 		    name, strerror(errno));
2330 		(void) ip_plink(udp_fd, fd, &lifr);
2331 		return (-1);
2332 	}
2333 
2334 	if (ioctl(fd, I_LIST, (caddr_t)&strlist) < 0) {
2335 		rcm_log_message(RCM_ERROR,
2336 		    _("IP: get_modlist(%s): I_LIST error: %s\n"),
2337 		    name, strerror(errno));
2338 		(void) ip_plink(udp_fd, fd, &lifr);
2339 		return (-1);
2340 	}
2341 
2342 	for (i = 0; i < strlist.sl_nmods; i++) {
2343 		lif->li_modules[i] =
2344 		    malloc(strlen(strlist.sl_modlist[i].l_name)+1);
2345 		if (lif->li_modules[i] == NULL) {
2346 			rcm_log_message(RCM_ERROR,
2347 			    _("IP: get_modlist(%s): %s\n"),
2348 			    name, strerror(errno));
2349 			(void) ip_plink(udp_fd, fd, &lifr);
2350 			return (-1);
2351 		}
2352 		(void) strcpy(lif->li_modules[i], strlist.sl_modlist[i].l_name);
2353 	}
2354 
2355 	lif->li_modcnt = strlist.sl_nmods;
2356 	free(strlist.sl_modlist);
2357 
2358 	rcm_log_message(RCM_TRACE1, "IP: getmodlist(%s) success\n", name);
2359 	return (ip_plink(udp_fd, fd, &lifr));
2360 }
2361 
2362 /*
2363  * ip_domux2fd() - Helper function for mod*() functions
2364  *		 Stolen from ifconfig.c
2365  */
2366 static int
2367 ip_domux2fd(int *udp_fd, int *fd, struct lifreq *lifr)
2368 {
2369 	int ip_fd;
2370 	char	*udp_dev_name;
2371 	char	*ip_dev_name;
2372 
2373 	if (lifr->lifr_flags & IFF_IPV6) {
2374 		udp_dev_name = UDP6_DEV_NAME;
2375 		ip_dev_name  = IP6_DEV_NAME;
2376 	} else {
2377 		udp_dev_name = UDP_DEV_NAME;
2378 		ip_dev_name  = IP_DEV_NAME;
2379 	}
2380 
2381 	if ((ip_fd = open(ip_dev_name, O_RDWR)) < 0) {
2382 		rcm_log_message(RCM_ERROR, _("IP: ip_domux2fd: open(%s) %s\n"),
2383 		    ip_dev_name, strerror(errno));
2384 		return (-1);
2385 	}
2386 	if ((*udp_fd = open(udp_dev_name, O_RDWR)) < 0) {
2387 		rcm_log_message(RCM_ERROR, _("IP: ip_domux2fd: open(%s) %s\n"),
2388 		    udp_dev_name, strerror(errno));
2389 		(void) close(ip_fd);
2390 		return (-1);
2391 	}
2392 	if (ioctl(ip_fd, SIOCGLIFMUXID, (caddr_t)lifr) < 0) {
2393 		rcm_log_message(RCM_ERROR,
2394 		    _("IP: ip_domux2fd: SIOCGLIFMUXID(%s): %s\n"),
2395 		    ip_dev_name, strerror(errno));
2396 		(void) close(*udp_fd);
2397 		(void) close(ip_fd);
2398 		return (-1);
2399 	}
2400 
2401 	rcm_log_message(RCM_TRACE2,
2402 	    "IP: ip_domux2fd: ARP_muxid %d IP_muxid %d\n",
2403 	    lifr->lifr_arp_muxid, lifr->lifr_ip_muxid);
2404 
2405 	if ((*fd = ioctl(*udp_fd, _I_MUXID2FD, lifr->lifr_ip_muxid)) < 0) {
2406 		rcm_log_message(RCM_ERROR,
2407 		    _("IP: ip_domux2fd: _I_MUXID2FD(%s): %s\n"),
2408 		    udp_dev_name, strerror(errno));
2409 		(void) close(*udp_fd);
2410 		(void) close(ip_fd);
2411 		return (-1);
2412 	}
2413 	if (ioctl(*udp_fd, I_PUNLINK, lifr->lifr_ip_muxid) < 0) {
2414 		rcm_log_message(RCM_ERROR,
2415 		    _("IP: ip_domux2fd: I_PUNLINK(%s): %s\n"),
2416 		    udp_dev_name, strerror(errno));
2417 		(void) close(*udp_fd);
2418 		(void) close(ip_fd);
2419 		return (-1);
2420 	}
2421 
2422 	/* Note: udp_fd is closed in ip_plink below */
2423 	(void) close(ip_fd);
2424 	return (0);
2425 }
2426 
2427 /*
2428  * ip_plink() - Helper function for mod*() functions.
2429  *	      Stolen from ifconfig.c
2430  */
2431 static int
2432 ip_plink(int udp_fd, int fd, struct lifreq *lifr)
2433 {
2434 	int mux_id;
2435 
2436 	if ((mux_id = ioctl(udp_fd, I_PLINK, fd)) < 0) {
2437 		rcm_log_message(RCM_ERROR, _("IP: ip_plink I_PLINK(%s): %s\n"),
2438 		    UDP_DEV_NAME, strerror(errno));
2439 		(void) close(udp_fd);
2440 		(void) close(fd);
2441 		return (-1);
2442 	}
2443 
2444 	lifr->lifr_ip_muxid = mux_id;
2445 	if (ioctl(udp_fd, SIOCSLIFMUXID, (caddr_t)lifr) < 0) {
2446 		rcm_log_message(RCM_ERROR,
2447 		    _("IP: ip_plink SIOCSLIFMUXID(%s): %s\n"),
2448 		    UDP_DEV_NAME, strerror(errno));
2449 		(void) close(udp_fd);
2450 		(void) close(fd);
2451 		return (-1);
2452 	}
2453 
2454 	(void) close(udp_fd);
2455 	(void) close(fd);
2456 	return (0);
2457 }
2458 
2459 /*
2460  * ip_onlinelist()
2461  *
2462  *	Notify online to IP address consumers.
2463  */
2464 static int
2465 ip_onlinelist(rcm_handle_t *hd, ip_cache_t *node, char **errorp, uint_t flags,
2466 		rcm_info_t **depend_info)
2467 {
2468 	char **addrlist;
2469 	int ret = RCM_SUCCESS;
2470 
2471 	rcm_log_message(RCM_TRACE2, "IP: ip_onlinelist\n");
2472 
2473 	addrlist = ip_get_addrlist(node);
2474 	if (addrlist == NULL || addrlist[0] == NULL) {
2475 		rcm_log_message(RCM_TRACE2, "IP: ip_onlinelist none\n");
2476 		ip_free_addrlist(addrlist);
2477 		return (ret);
2478 	}
2479 
2480 	ret = rcm_notify_online_list(hd, addrlist, 0, depend_info);
2481 
2482 	ip_free_addrlist(addrlist);
2483 	rcm_log_message(RCM_TRACE2, "IP: ip_onlinelist done\n");
2484 	return (ret);
2485 }
2486 
2487 /*
2488  * ip_offlinelist()
2489  *
2490  *	Offline IP address consumers.
2491  */
2492 static int
2493 ip_offlinelist(rcm_handle_t *hd, ip_cache_t *node, char **errorp, uint_t flags,
2494 	rcm_info_t **depend_info)
2495 {
2496 	char **addrlist;
2497 	int ret = RCM_SUCCESS;
2498 
2499 	rcm_log_message(RCM_TRACE2, "IP: ip_offlinelist\n");
2500 
2501 	addrlist = ip_get_addrlist(node);
2502 	if (addrlist == NULL || addrlist[0] == NULL) {
2503 		rcm_log_message(RCM_TRACE2, "IP: ip_offlinelist none\n");
2504 		ip_free_addrlist(addrlist);
2505 		return (RCM_SUCCESS);
2506 	}
2507 
2508 	if ((ret = rcm_request_offline_list(hd, addrlist, flags, depend_info))
2509 	    != RCM_SUCCESS) {
2510 		if (ret == RCM_FAILURE)
2511 			(void) rcm_notify_online_list(hd, addrlist, 0, NULL);
2512 
2513 		ret = RCM_FAILURE;
2514 	}
2515 
2516 	ip_free_addrlist(addrlist);
2517 	rcm_log_message(RCM_TRACE2, "IP: ip_offlinelist done\n");
2518 	return (ret);
2519 }
2520 
2521 /*
2522  * ip_get_addrlist() -	Compile list of IP addresses hosted on this NIC (node)
2523  *			This routine malloc() required memeory for the list
2524  *			Returns list on success, NULL if failed
2525  *			Call with cache_lock held.
2526  */
2527 static char **
2528 ip_get_addrlist(ip_cache_t *node)
2529 {
2530 	ip_lif_t *lif;
2531 	char **addrlist = NULL;
2532 	int numifs;
2533 	char addrstr[INET6_ADDRSTRLEN];
2534 	void *addr;
2535 	int af;
2536 	int i;
2537 
2538 	rcm_log_message(RCM_TRACE2, "IP: ip_get_addrlist(%s)\n",
2539 	    node->ip_resource);
2540 
2541 	numifs = 0;
2542 	for (lif = node->ip_pif->pi_lifs; lif != NULL; lif = lif->li_next) {
2543 		numifs++;
2544 	}
2545 
2546 	/*
2547 	 * Allocate space for resource names list; add 1 and use calloc()
2548 	 * so that the list is NULL-terminated.
2549 	 */
2550 	if ((addrlist = calloc(numifs + 1, sizeof (char *))) == NULL) {
2551 		rcm_log_message(RCM_ERROR,
2552 		    _("IP: ip_get_addrlist(%s) malloc failure(%s)\n"),
2553 		    node->ip_resource, strerror(errno));
2554 		return (NULL);
2555 	}
2556 
2557 	for (lif = node->ip_pif->pi_lifs, i = 0; lif != NULL;
2558 	    lif = lif->li_next, i++) {
2559 
2560 		af = lif->li_addr.family;
2561 		if (af == AF_INET6) {
2562 			addr = &lif->li_addr.ip6.sin6_addr;
2563 		} else if (af == AF_INET) {
2564 			addr = &lif->li_addr.ip4.sin_addr;
2565 		} else {
2566 			rcm_log_message(RCM_DEBUG,
2567 			    "IP: unknown addr family %d, assuming AF_INET\n",
2568 			    af);
2569 			af = AF_INET;
2570 			addr = &lif->li_addr.ip4.sin_addr;
2571 		}
2572 		if (inet_ntop(af, addr, addrstr, INET6_ADDRSTRLEN) == NULL) {
2573 			rcm_log_message(RCM_ERROR,
2574 			    _("IP: inet_ntop: %s\n"), strerror(errno));
2575 			ip_free_addrlist(addrlist);
2576 			return (NULL);
2577 		}
2578 
2579 		if ((addrlist[i] = malloc(strlen(addrstr) + RCM_SIZE_SUNW_IP))
2580 		    == NULL) {
2581 			rcm_log_message(RCM_ERROR,
2582 			    _("IP: ip_get_addrlist(%s) malloc failure(%s)\n"),
2583 			    node->ip_resource, strerror(errno));
2584 			ip_free_addrlist(addrlist);
2585 			return (NULL);
2586 		}
2587 		(void) strcpy(addrlist[i], RCM_STR_SUNW_IP);	/* SUNW_ip/ */
2588 		(void) strcat(addrlist[i], addrstr);	/* SUNW_ip/<address> */
2589 
2590 		rcm_log_message(RCM_DEBUG, "Anon Address: %s\n", addrlist[i]);
2591 	}
2592 
2593 	rcm_log_message(RCM_TRACE2, "IP: get_addrlist (%s) done\n",
2594 	    node->ip_resource);
2595 
2596 	return (addrlist);
2597 }
2598 
2599 static void
2600 ip_free_addrlist(char **addrlist)
2601 {
2602 	int i;
2603 
2604 	if (addrlist == NULL)
2605 		return;
2606 
2607 	for (i = 0; addrlist[i] != NULL; i++)
2608 		free(addrlist[i]);
2609 	free(addrlist);
2610 }
2611 
2612 /*
2613  * ip_consumer_notify() - Notify consumers of IP addresses coming back online.
2614  */
2615 
2616 static void
2617 ip_consumer_notify(rcm_handle_t *hd, char *ifinst, char **errorp, uint_t flags,
2618 	rcm_info_t **depend_info)
2619 {
2620 	char ifname[LIFNAMSIZ + 1];
2621 	char cached_name[RCM_NET_RESOURCE_MAX];
2622 	ip_cache_t *node;
2623 	char *cp;
2624 
2625 	rcm_log_message(RCM_TRACE1, "IP: ip_consumer_notify(%s)\n", ifinst);
2626 
2627 	if (ifinst == NULL)
2628 		return;
2629 
2630 	(void) memcpy(&ifname, ifinst, sizeof (ifname));
2631 	ifname[sizeof (ifname) - 1] = '\0';
2632 
2633 	/* remove LIF component */
2634 	cp = strchr(ifname, ':');
2635 	if (cp) {
2636 		*cp = 0;
2637 	}
2638 
2639 	/* Check for the interface in the cache */
2640 	(void) snprintf(cached_name, sizeof (cached_name), "%s/%s",
2641 	    RCM_NET_PREFIX, ifname);
2642 
2643 	(void) mutex_lock(&cache_lock);
2644 	if ((node = cache_lookup(hd, cached_name, CACHE_REFRESH)) == NULL) {
2645 		rcm_log_message(RCM_TRACE1, "IP: Skipping interface(%s) \n",
2646 		    ifname);
2647 		(void) mutex_unlock(&cache_lock);
2648 		return;
2649 	}
2650 	/*
2651 	 * Inform anonymous consumers about IP addresses being
2652 	 * onlined
2653 	 */
2654 	(void) ip_onlinelist(hd, node, errorp, flags, depend_info);
2655 
2656 	(void) mutex_unlock(&cache_lock);
2657 
2658 	rcm_log_message(RCM_TRACE2, "IP: ip_consumer_notify success\n");
2659 	return;
2660 
2661 }
2662 /*
2663  * process_nvlist() - Determine network interfaces on a new attach by
2664  *			processing the nvlist
2665  */
2666 /*ARGSUSED*/
2667 static int
2668 process_nvlist(nvlist_t *nvl)
2669 {
2670 	nvpair_t	*nvp = NULL;
2671 	char *driver_name;
2672 	char *devfs_path;
2673 	int32_t instance;
2674 	char *minor_byte_array;	/* packed nvlist of minor_data */
2675 	uint_t nminor;			/* # of minor nodes */
2676 	struct devfs_minor_data *mdata;
2677 	nvlist_t *mnvl;
2678 	nvpair_t *mnvp = NULL;
2679 
2680 	rcm_log_message(RCM_TRACE1, "IP: process_nvlist\n");
2681 
2682 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
2683 		/* Get driver name */
2684 		if (STREQ(nvpair_name(nvp), RCM_NV_DRIVER_NAME)) {
2685 			if (nvpair_value_string(nvp, &driver_name) != 0) {
2686 				rcm_log_message(RCM_WARNING,
2687 				    _("IP: cannot get driver name\n"));
2688 				return (-1);
2689 			}
2690 		}
2691 		/* Get instance */
2692 		if (STREQ(nvpair_name(nvp), RCM_NV_INSTANCE)) {
2693 			if (nvpair_value_int32(nvp, &instance) != 0) {
2694 				rcm_log_message(RCM_WARNING,
2695 				    _("IP: cannot get device instance\n"));
2696 				return (-1);
2697 			}
2698 		}
2699 		/* Get devfs_path */
2700 		if (STREQ(nvpair_name(nvp), RCM_NV_DEVFS_PATH)) {
2701 			if (nvpair_value_string(nvp, &devfs_path) != 0) {
2702 				rcm_log_message(RCM_WARNING,
2703 				    _("IP: cannot get device path\n"));
2704 				return (-1);
2705 			}
2706 		}
2707 		/* Get minor data */
2708 		if (STREQ(nvpair_name(nvp), RCM_NV_MINOR_DATA)) {
2709 			if (nvpair_value_byte_array(nvp,
2710 			    (uchar_t **)&minor_byte_array, &nminor) != 0) {
2711 				rcm_log_message(RCM_WARNING,
2712 				    _("IP: cannot get device minor data\n"));
2713 				return (-1);
2714 			}
2715 			if (nvlist_unpack(minor_byte_array,
2716 			    nminor, &mnvl, 0) != 0) {
2717 				rcm_log_message(RCM_WARNING,
2718 				    _("IP: cannot get minor node data\n"));
2719 				return (-1);
2720 			}
2721 			mdata = (struct devfs_minor_data *)calloc(1,
2722 			    sizeof (struct devfs_minor_data));
2723 			if (mdata == NULL) {
2724 				rcm_log_message(RCM_WARNING,
2725 				    _("IP: calloc error(%s)\n"),
2726 				    strerror(errno));
2727 				nvlist_free(mnvl);
2728 				return (-1);
2729 			}
2730 			/* Enumerate minor node data */
2731 			while ((mnvp = nvlist_next_nvpair(mnvl, mnvp)) !=
2732 			    NULL) {
2733 				/* Get minor type */
2734 				if (STREQ(nvpair_name(mnvp),
2735 				    RCM_NV_MINOR_TYPE)) {
2736 					if (nvpair_value_int32(mnvp,
2737 					    &mdata->minor_type) != 0) {
2738 						rcm_log_message(RCM_WARNING,
2739 						    _("IP: cannot get minor "
2740 						    "type \n"));
2741 						nvlist_free(mnvl);
2742 						return (-1);
2743 					}
2744 				}
2745 				/* Get minor name */
2746 				if (STREQ(nvpair_name(mnvp),
2747 				    RCM_NV_MINOR_NAME)) {
2748 					if (nvpair_value_string(mnvp,
2749 					    &mdata->minor_name) != 0) {
2750 						rcm_log_message(RCM_WARNING,
2751 						    _("IP: cannot get minor "
2752 						    "name \n"));
2753 						nvlist_free(mnvl);
2754 						return (-1);
2755 					}
2756 				}
2757 				/* Get minor node type */
2758 				if (STREQ(nvpair_name(mnvp),
2759 				    RCM_NV_MINOR_NODE_TYPE)) {
2760 					if (nvpair_value_string(mnvp,
2761 					    &mdata->minor_node_type) != 0) {
2762 						rcm_log_message(RCM_WARNING,
2763 						    _("IP: cannot get minor "
2764 						    "node type \n"));
2765 						nvlist_free(mnvl);
2766 						return (-1);
2767 					}
2768 				}
2769 			}
2770 			(void) process_minor(devfs_path, driver_name, instance,
2771 			    mdata);
2772 			nvlist_free(mnvl);
2773 		}
2774 	}
2775 
2776 	rcm_log_message(RCM_TRACE1, "IP: process_nvlist success\n");
2777 	return (0);
2778 }
2779 
2780 static void
2781 process_minor(char *devfs_path, char *name, int instance,
2782     struct devfs_minor_data *mdata)
2783 {
2784 	struct net_interface *nip;
2785 	struct ni_list *nilp;
2786 	struct ni_list *p;
2787 	struct ni_list **pp;
2788 	char *cname;
2789 	size_t cnamelen;
2790 
2791 	rcm_log_message(RCM_TRACE1, "IP: process_minor\n");
2792 
2793 	if ((mdata->minor_node_type != NULL) &&
2794 	    !STREQ(mdata->minor_node_type, PROP_NV_DDI_NETWORK)) {
2795 		/* Process network devices only */
2796 		return;
2797 	}
2798 
2799 	rcm_log_message(RCM_TRACE1, "IP: Examining %s (%s)\n",
2800 	    devfs_path, mdata->minor_name);
2801 
2802 	/* Sanity check, instances > 999 are illegal */
2803 	if (instance > 999) {
2804 		errno = EINVAL;
2805 		rcm_log_message(RCM_ERROR, _("IP: invalid instance %d(%s)\n"),
2806 		    instance, strerror(errno));
2807 		return;
2808 	}
2809 
2810 	/* Now, let's add the node to the interface list */
2811 	if ((nip = malloc(sizeof (struct net_interface))) == NULL) {
2812 		rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"),
2813 		    strerror(errno));
2814 		return;
2815 	}
2816 	(void) memset(nip, 0, sizeof (struct net_interface));
2817 
2818 	cnamelen = strlen(name) + 1;
2819 	/* Set NIC type */
2820 	if ((nip->type = (char *)malloc(cnamelen)) == NULL) {
2821 		free(nip);
2822 		rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"),
2823 		    strerror(errno));
2824 		return;
2825 	}
2826 	(void) memcpy(nip->type, name, cnamelen);
2827 
2828 	cnamelen += 3;
2829 	if ((cname = (char *)malloc(cnamelen)) == NULL) {
2830 		free(nip->type);
2831 		free(nip);
2832 		rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"),
2833 		    strerror(errno));
2834 		return;
2835 	}
2836 	(void) snprintf(cname, cnamelen, "%s%d", name, instance);
2837 
2838 	rcm_log_message(RCM_TRACE1, "IP: Found SUNW_network/%s%d\n", name,
2839 	    instance);
2840 
2841 	/* Set NIC name */
2842 	if ((nip->name = strdup(cname)) == NULL) {
2843 		free(nip->type);
2844 		free(nip);
2845 		free(cname);
2846 		rcm_log_message(RCM_ERROR, _("IP: strdup failure(%s)\n"),
2847 		    strerror(errno));
2848 		return;
2849 	}
2850 	free(cname);
2851 
2852 	/* Add new interface to the list */
2853 	(void) mutex_lock(&nil_lock);
2854 	for (pp = &nil_head; (p = *pp) != NULL; pp = &(p->next)) {
2855 		cname = p->nifp->name;
2856 		if (strcmp(cname, nip->name) == 0)
2857 			break;
2858 	}
2859 
2860 	if (p != NULL) {
2861 		(void) mutex_unlock(&nil_lock);
2862 		free(nip->name);
2863 		free(nip->type);
2864 		free(nip);
2865 		rcm_log_message(RCM_TRACE1, "IP: secondary node - ignoring\n");
2866 		return;
2867 	}
2868 
2869 	if ((nilp = malloc(sizeof (struct ni_list))) == NULL) {
2870 		(void) mutex_unlock(&nil_lock);
2871 		free(nip->name);
2872 		free(nip->type);
2873 		free(nip);
2874 		rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"),
2875 		    strerror(errno));
2876 		return;
2877 	}
2878 
2879 	nilp->nifp = nip;
2880 	nilp->next = NULL;
2881 	*pp = nilp;
2882 
2883 	num_ni++;	/* Increment interface count */
2884 
2885 	(void) mutex_unlock(&nil_lock);
2886 	rcm_log_message(RCM_TRACE1, "IP: added new node\n");
2887 }
2888 
2889 /*
2890  * if_configure() - Configure a physical interface after attach
2891  */
2892 static int
2893 if_configure(char *ifinst)
2894 {
2895 	char cfgfile[MAXPATHLEN];
2896 	char ifname[LIFNAMSIZ + 1];
2897 	char cached_name[RCM_NET_RESOURCE_MAX];
2898 	struct stat statbuf;
2899 	ip_cache_t *node;
2900 	char *cp;
2901 	int af = 0;
2902 	int ipmp = 0;
2903 
2904 	if (ifinst == NULL)
2905 		return (0);
2906 
2907 	rcm_log_message(RCM_TRACE1, "IP: if_configure(%s)\n", ifinst);
2908 
2909 	/*
2910 	 * Check if the interface is already configured
2911 	 */
2912 
2913 	(void) memcpy(&ifname, ifinst, sizeof (ifname));
2914 	ifname[sizeof (ifname) - 1] = '\0';
2915 
2916 	/* remove LIF component */
2917 	cp = strchr(ifname, ':');
2918 	if (cp) {
2919 		*cp = 0;
2920 	}
2921 
2922 	/* Check for the interface in the cache */
2923 	(void) snprintf(cached_name, sizeof (cached_name), "%s/%s",
2924 	    RCM_NET_PREFIX, ifname);
2925 
2926 	/* Check if the interface is new or was previously offlined */
2927 	(void) mutex_lock(&cache_lock);
2928 	if (((node = cache_lookup(NULL, cached_name, CACHE_REFRESH)) != NULL) &&
2929 	    (!(node->ip_cachestate & CACHE_IF_OFFLINED))) {
2930 		rcm_log_message(RCM_TRACE1,
2931 		    "IP: Skipping configured interface(%s) \n", ifname);
2932 		(void) mutex_unlock(&cache_lock);
2933 		return (0);
2934 	}
2935 	(void) mutex_unlock(&cache_lock);
2936 
2937 	/* Scan IPv4 configuration first */
2938 	(void) snprintf(cfgfile, MAXPATHLEN, "%s%s", CFGFILE_FMT_IPV4, ifinst);
2939 	cfgfile[MAXPATHLEN - 1] = '\0';
2940 
2941 	rcm_log_message(RCM_TRACE1, "IP: Scanning %s\n", cfgfile);
2942 	if (stat(cfgfile, &statbuf) == 0) {
2943 		af |= CONFIG_AF_INET;
2944 		if (isgrouped(cfgfile)) {
2945 			ipmp++;
2946 		}
2947 	}
2948 
2949 	/* Scan IPv6 configuration details */
2950 	(void) snprintf(cfgfile, MAXPATHLEN, "%s%s", CFGFILE_FMT_IPV6, ifinst);
2951 	cfgfile[MAXPATHLEN - 1] = '\0';
2952 	rcm_log_message(RCM_TRACE1, "IP: Scanning %s\n", cfgfile);
2953 	if (stat(cfgfile, &statbuf) == 0) {
2954 		af |= CONFIG_AF_INET6;
2955 		if ((ipmp == 0) && isgrouped(cfgfile)) {
2956 			ipmp++;
2957 		}
2958 	}
2959 
2960 	if (af & CONFIG_AF_INET) {
2961 		if (if_ipmp_config(ifinst, CONFIG_AF_INET, ipmp) == -1) {
2962 			rcm_log_message(RCM_ERROR,
2963 			    _("IP: IPv4 Post-attach failed (%s)\n"), ifinst);
2964 			return (-1);
2965 		}
2966 	}
2967 
2968 	if (af & CONFIG_AF_INET6) {
2969 		if (if_ipmp_config(ifinst, CONFIG_AF_INET6, ipmp) == -1) {
2970 			rcm_log_message(RCM_ERROR,
2971 			    _("IP: IPv6 Post-attach failed(%s)\n"), ifinst);
2972 			return (-1);
2973 		}
2974 	}
2975 
2976 	rcm_log_message(RCM_TRACE1, "IP: if_configure(%s) success\n", ifinst);
2977 
2978 	return (0);
2979 
2980 }
2981 
2982 /*
2983  * isgrouped() - Scans the given config file to see if this is a grouped
2984  *	       interface
2985  *	       Returns non-zero if true; 0 if false
2986  */
2987 static int
2988 isgrouped(char *cfgfile)
2989 {
2990 	FILE *fp;
2991 	struct stat statb;
2992 	char *buf = NULL;
2993 	char *tokens[MAXARGS];		/* token pointers */
2994 	char tspace[MAXLINE];		/* token space */
2995 	int ntok;
2996 	int group = 0;
2997 
2998 	if (cfgfile == NULL)
2999 		return (0);
3000 
3001 	rcm_log_message(RCM_TRACE1, "IP: isgrouped(%s)\n", cfgfile);
3002 
3003 	if (stat(cfgfile, &statb) != 0) {
3004 		rcm_log_message(RCM_TRACE1,
3005 		    _("IP: No config file(%s)\n"), cfgfile);
3006 		return (0);
3007 	}
3008 
3009 	/*
3010 	 * We also ignore single-byte config files because the file should
3011 	 * always be newline-terminated, so we know there's nothing of
3012 	 * interest.  Further, a single-byte file would cause the fgets() loop
3013 	 * below to spin forever.
3014 	 */
3015 	if (statb.st_size <= 1) {
3016 		rcm_log_message(RCM_TRACE1,
3017 		    _("IP: Empty config file(%s)\n"), cfgfile);
3018 		return (0);
3019 	}
3020 
3021 	if ((fp = fopen(cfgfile, "r")) == NULL) {
3022 		rcm_log_message(RCM_ERROR,
3023 		    _("IP: Cannot open configuration file(%s): %s\n"), cfgfile,
3024 		    strerror(errno));
3025 		return (0);
3026 	}
3027 
3028 	if ((buf = calloc(1, statb.st_size)) == NULL) {
3029 		rcm_log_message(RCM_ERROR,
3030 		    _("IP: calloc failure(%s): %s\n"), cfgfile,
3031 		    strerror(errno));
3032 		(void) fclose(fp);
3033 		return (0);
3034 	}
3035 
3036 	while (fgets(buf, statb.st_size, fp) != NULL) {
3037 		if (*buf == '\0')
3038 			continue;
3039 
3040 		tokenize(buf, tokens, tspace, &ntok);
3041 		while (ntok) {
3042 			if (STREQ("group", tokens[ntok - 1])) {
3043 				if (tokens[ntok] != NULL) {
3044 					group++;
3045 				}
3046 			}
3047 			ntok--;
3048 		}
3049 	}
3050 
3051 	free(buf);
3052 
3053 	(void) fclose(fp);
3054 
3055 	if (group <= 0) {
3056 		rcm_log_message(RCM_TRACE1, "IP: isgrouped(%s) non-grouped\n",
3057 		    cfgfile);
3058 		return (0);
3059 	} else {
3060 		rcm_log_message(RCM_TRACE1, "IP: isgrouped(%s) grouped\n",
3061 		    cfgfile);
3062 		return (1);
3063 	}
3064 }
3065 
3066 
3067 /*
3068  * if_ipmp_config() - Configure an interface instance as specified by the
3069  *		    address family af and if it is grouped (ipmp).
3070  */
3071 static int
3072 if_ipmp_config(char *ifinst, int af, int ipmp)
3073 {
3074 	char cfgfile[MAXPATHLEN];	/* configuration file */
3075 	FILE *fp;
3076 	struct stat statb;
3077 	char *buf;
3078 	char *tokens[MAXARGS];		/* list of config attributes */
3079 	char tspace[MAXLINE];		/* token space */
3080 	char syscmd[MAX_RECONFIG_SIZE + MAXPATHLEN + 1];
3081 	char grpcmd[MAX_RECONFIG_SIZE + MAXPATHLEN + 1];
3082 	char fstr[8];		/* address family string inet or inet6 */
3083 	int nofailover = 0;
3084 	int newattach = 0;
3085 	int cmdvalid = 0;
3086 	int ntok;
3087 	int n;
3088 	int stdif = 0;
3089 
3090 	if (ifinst == NULL)
3091 		return (0);
3092 
3093 	rcm_log_message(RCM_TRACE1, "IP: if_ipmp_config(%s) ipmp = %d\n",
3094 	    ifinst, ipmp);
3095 
3096 	if (af & CONFIG_AF_INET) {
3097 		(void) snprintf(cfgfile, MAXPATHLEN, "%s%s", CFGFILE_FMT_IPV4,
3098 		    ifinst);
3099 		(void) strcpy(fstr, "inet");
3100 	} else if (af & CONFIG_AF_INET6) {
3101 		(void) snprintf(cfgfile, MAXPATHLEN, "%s%s", CFGFILE_FMT_IPV6,
3102 		    ifinst);
3103 		(void) strcpy(fstr, "inet6");
3104 	} else {
3105 		return (0);		/* nothing to do */
3106 	}
3107 
3108 	cfgfile[MAXPATHLEN - 1] = '\0';
3109 	grpcmd[0] = '\0';
3110 
3111 	if (stat(cfgfile, &statb) != 0) {
3112 		rcm_log_message(RCM_TRACE1,
3113 		    _("IP: No config file(%s)\n"), ifinst);
3114 		return (0);
3115 	}
3116 
3117 	/* Config file exists, plumb in the physical interface */
3118 	if (af & CONFIG_AF_INET6) {
3119 		if (if_getcount(AF_INET6) == 0) {
3120 			/*
3121 			 * Configure software loopback driver if this is the
3122 			 * first IPv6 interface plumbed
3123 			 */
3124 			newattach++;
3125 			(void) snprintf(syscmd, sizeof (syscmd),
3126 			    "%s lo0 %s plumb ::1 up", USR_SBIN_IFCONFIG, fstr);
3127 			if (rcm_exec_cmd(syscmd) != 0) {
3128 				rcm_log_message(RCM_ERROR,
3129 				    _("IP: Cannot plumb (%s) %s\n"),
3130 				    ifinst, strerror(errno));
3131 				return (-1);
3132 			}
3133 		}
3134 		(void) snprintf(syscmd, sizeof (syscmd), "%s %s %s plumb up",
3135 		    USR_SBIN_IFCONFIG, ifinst, fstr);
3136 	} else {
3137 		(void) snprintf(syscmd, sizeof (syscmd), "%s %s %s plumb ",
3138 		    USR_SBIN_IFCONFIG, ifinst, fstr);
3139 		if (if_getcount(AF_INET) == 0) {
3140 			newattach++;
3141 		}
3142 	}
3143 	rcm_log_message(RCM_TRACE1, "IP: Exec: %s\n", syscmd);
3144 
3145 	if (rcm_exec_cmd(syscmd) != 0) {
3146 		rcm_log_message(RCM_ERROR,
3147 		    _("IP: Cannot plumb (%s) %s\n"), ifinst, strerror(errno));
3148 		return (-1);
3149 	}
3150 
3151 	/* Check if config file is empty, if so, nothing else to do */
3152 	if (statb.st_size == 0) {
3153 		rcm_log_message(RCM_TRACE1,
3154 		    _("IP: Zero size config file(%s)\n"), ifinst);
3155 		return (0);
3156 	}
3157 
3158 	if ((fp = fopen(cfgfile, "r")) == NULL) {
3159 		rcm_log_message(RCM_ERROR,
3160 		    _("IP: Open error(%s): %s\n"), cfgfile, strerror(errno));
3161 		return (-1);
3162 	}
3163 
3164 	if ((buf = calloc(1, statb.st_size)) == NULL) {
3165 		rcm_log_message(RCM_ERROR,
3166 		    _("IP: calloc(%s): %s\n"), ifinst, strerror(errno));
3167 		(void) fclose(fp);
3168 		return (-1);
3169 	}
3170 
3171 	/* a single line with one token implies a classical if */
3172 	if (fgets(buf, statb.st_size, fp) != NULL) {
3173 		tokenize(buf, tokens, tspace, &ntok);
3174 		if (ntok == 1) {
3175 			rcm_log_message(RCM_TRACE1, "IP: Standard interface\n");
3176 			stdif++;
3177 		}
3178 	}
3179 	if (fseek(fp, 0L, SEEK_SET) == -1) {
3180 		rcm_log_message(RCM_ERROR, _("IP: fseek: %s\n"),
3181 		    strerror(errno));
3182 		return (-1);
3183 	}
3184 
3185 	/*
3186 	 * Process the config command
3187 	 * This loop also handles multiple logical interfaces that may
3188 	 * be configured on a single line
3189 	 */
3190 	while (fgets(buf, statb.st_size, fp) != NULL) {
3191 		nofailover = 0;
3192 		cmdvalid = 0;
3193 
3194 		if (*buf == '\0')
3195 			continue;
3196 
3197 		tokenize(buf, tokens, tspace, &ntok);
3198 		if (ntok <= 0)
3199 			continue;
3200 
3201 		/* Reset the config command */
3202 		(void) snprintf(syscmd, sizeof (syscmd), "%s %s %s ",
3203 		    USR_SBIN_IFCONFIG, ifinst, fstr);
3204 
3205 		/* No parsing if this is first interface of its kind */
3206 		if (newattach) {
3207 			(void) strcat(syscmd, buf);
3208 			/* Classic if */
3209 			if ((af & CONFIG_AF_INET) && (stdif == 1)) {
3210 				(void) strcat(syscmd, CFG_CMDS_STD);
3211 			}
3212 			rcm_log_message(RCM_TRACE1, "IP: New: %s\n", syscmd);
3213 			if (rcm_exec_cmd(syscmd) != 0) {
3214 				rcm_log_message(RCM_ERROR,
3215 				    _("IP: Error: %s (%s): %s\n"),
3216 				    syscmd, ifinst, strerror(errno));
3217 			}
3218 			continue;
3219 		}
3220 
3221 		/* Parse the tokens to determine nature of the interface */
3222 		for (n = 0; n < ntok; n++) {
3223 			/* Handle pathological failover cases */
3224 			if (STREQ("-failover", tokens[n]))
3225 				nofailover++;
3226 			if (STREQ("failover", tokens[n]))
3227 				nofailover--;
3228 
3229 			/* group attribute requires special processing */
3230 			if (STREQ("group", tokens[n])) {
3231 				if (tokens[n + 1] != NULL) {
3232 					(void) snprintf(grpcmd, sizeof (grpcmd),
3233 					    "%s %s %s %s %s", USR_SBIN_IFCONFIG,
3234 					    ifinst, fstr,
3235 					    tokens[n], tokens[n + 1]);
3236 					n++;		/* skip next token */
3237 					continue;
3238 				}
3239 			}
3240 
3241 			/* Execute buffered command ? */
3242 			if (STREQ("set", tokens[n]) ||
3243 			    STREQ("addif", tokens[n]) ||
3244 			    STREQ("removeif", tokens[n]) ||
3245 			    (n == (ntok -1))) {
3246 
3247 				/* config command complete ? */
3248 				if (n == (ntok -1)) {
3249 					ADDSPACE(syscmd);
3250 					(void) strcat(syscmd, tokens[n]);
3251 					cmdvalid++;
3252 				}
3253 
3254 				if (!cmdvalid) {
3255 					ADDSPACE(syscmd);
3256 					(void) strcat(syscmd, tokens[n]);
3257 					cmdvalid++;
3258 					continue;
3259 				}
3260 				/* Classic if ? */
3261 				if ((af & CONFIG_AF_INET) && (stdif == 1)) {
3262 					(void) strcat(syscmd, CFG_CMDS_STD);
3263 				}
3264 
3265 				if (nofailover > 0) {
3266 					rcm_log_message(RCM_TRACE1,
3267 					    "IP: Interim exec: %s\n", syscmd);
3268 					if (rcm_exec_cmd(syscmd) != 0) {
3269 						rcm_log_message(RCM_ERROR,
3270 						    _("IP: %s fail(%s): %s\n"),
3271 						    syscmd, ifinst,
3272 						    strerror(errno));
3273 					}
3274 				} else {
3275 					/* Have mpathd configure the address */
3276 					if (if_mpathd_configure(syscmd, ifinst,
3277 					    af, ipmp) != 0) {
3278 						rcm_log_message(RCM_ERROR,
3279 						    _("IP: %s fail(%s): %s\n"),
3280 						    syscmd, ifinst,
3281 						    strerror(errno));
3282 					}
3283 				}
3284 
3285 				/* Reset config command */
3286 				(void) snprintf(syscmd, sizeof (syscmd),
3287 				    "%s %s %s ", USR_SBIN_IFCONFIG, ifinst,
3288 				    fstr);
3289 				nofailover = 0;
3290 				cmdvalid = 0;
3291 			}
3292 			/*
3293 			 * Note: No explicit command validation is required
3294 			 *	since ifconfig to does it for us
3295 			 */
3296 			ADDSPACE(syscmd);
3297 			(void) strcat(syscmd, tokens[n]);
3298 			cmdvalid++;
3299 		}
3300 	}
3301 
3302 	free(buf);
3303 	(void) fclose(fp);
3304 
3305 	/*
3306 	 * The group name needs to be set after all the test/nofailover
3307 	 * addresses have been configured. Otherwise, if IPMP detects that the
3308 	 * interface is failed, the addresses will be moved to a working
3309 	 * interface before the '-failover' flag can be set.
3310 	 */
3311 	if (grpcmd[0] != '\0') {
3312 		rcm_log_message(RCM_TRACE1, "IP: set group name: %s\n", grpcmd);
3313 		if (rcm_exec_cmd(grpcmd) != 0) {
3314 			rcm_log_message(RCM_ERROR, _("IP: %s fail(%s): %s\n"),
3315 			    grpcmd, ifinst, strerror(errno));
3316 		}
3317 	}
3318 
3319 	rcm_log_message(RCM_TRACE1, "IP: if_ipmp_config(%s) success\n", ifinst);
3320 
3321 	return (0);
3322 }
3323 
3324 /*
3325  * if_mpathd_configure() - Determine configuration disposition of the interface
3326  */
3327 static int
3328 if_mpathd_configure(char *syscmd, char *ifinst, int af, int ipmp)
3329 {
3330 	char *tokens[MAXARGS];
3331 	char tspace[MAXLINE];
3332 	int ntok;
3333 	char *addr;
3334 	char *from_lifname;
3335 	mpathd_cmd_t mpdcmd;
3336 	int n;
3337 
3338 	rcm_log_message(RCM_TRACE1, "IP: if_mpathd_configure(%s): %s\n",
3339 	    ifinst, syscmd);
3340 
3341 	tokenize(syscmd, tokens, tspace, &ntok);
3342 	if (ntok <= 0)
3343 		return (0);
3344 
3345 	addr = tokens[3];	/* by default, third token is valid address */
3346 	for (n = 0; n < ntok; n++) {
3347 		if (STREQ("set", tokens[n]) ||
3348 		    STREQ("addif", tokens[n])) {
3349 			addr = tokens[n+1];
3350 			if (addr == NULL) {	/* invalid format */
3351 				return (-1);
3352 			} else
3353 				break;
3354 		}
3355 	}
3356 
3357 	/* Check std. commands or no failed over address */
3358 	if (STREQ("removeif", addr) || STREQ("group", addr) ||
3359 	    ((from_lifname = get_mpathd_dest(addr, af)) == NULL)) {
3360 		rcm_log_message(RCM_TRACE1,
3361 		    "IP: No failed-over host, exec %s\n", syscmd);
3362 		if (rcm_exec_cmd(syscmd) != 0) {
3363 			rcm_log_message(RCM_ERROR,
3364 			    _("IP: %s failed(%s): %s\n"),
3365 			    syscmd, ifinst, strerror(errno));
3366 			return (-1);
3367 		}
3368 		return (0);
3369 	}
3370 
3371 	/* Check for non-IPMP failover scenarios */
3372 	if ((ipmp <= 0) && (from_lifname != NULL)) {
3373 		/* Address already hosted on another NIC, return */
3374 		rcm_log_message(RCM_TRACE1,
3375 		    "IP: Non-IPMP failed-over host(%s): %s\n",
3376 		    ifinst, addr);
3377 		return (0);
3378 	}
3379 
3380 	/*
3381 	 * Valid failed-over host; have mpathd set the original index
3382 	 */
3383 	mpdcmd.cmd_command = MI_SETOINDEX;
3384 	(void) strcpy(mpdcmd.from_lifname, from_lifname);
3385 	(void) strcpy(mpdcmd.to_pifname, ifinst);
3386 	if (af & CONFIG_AF_INET6) {
3387 		mpdcmd.addr_family = AF_INET6;
3388 	} else {
3389 		mpdcmd.addr_family = AF_INET;
3390 	}
3391 
3392 	/* Send command to in.mpathd(1M) */
3393 	rcm_log_message(RCM_TRACE1,
3394 	    "IP: Attempting setoindex from (%s) to (%s) ....\n",
3395 	    from_lifname, ifinst);
3396 
3397 	if (mpathd_send_cmd(&mpdcmd) < 0) {
3398 		rcm_log_message(RCM_TRACE1,
3399 		    _("IP: mpathd set original index unsuccessful: %s\n"),
3400 		    strerror(errno));
3401 		return (-1);
3402 	}
3403 
3404 	rcm_log_message(RCM_TRACE1,
3405 	    "IP: setoindex success (%s) to (%s)\n",
3406 	    from_lifname, ifinst);
3407 
3408 	return (0);
3409 }
3410 
3411 /*
3412  * get_mpathd_addr() - Return current destination for lif; caller is
3413  *		     responsible to free memory allocated for address
3414  */
3415 static char *
3416 get_mpathd_dest(char *addr, int family)
3417 {
3418 	int sock;
3419 	char *buf;
3420 	struct lifnum lifn;
3421 	struct lifconf lifc;
3422 	struct lifreq *lifrp;
3423 	sa_family_t af = AF_INET;	/* IPv4 by default */
3424 	int i;
3425 	struct lifreq lifreq;
3426 	struct sockaddr_in *sin;
3427 	struct sockaddr_in6 *sin6;
3428 	struct hostent *hp;
3429 	char *ifname = NULL;
3430 	char *prefix = NULL;
3431 	char addrstr[INET6_ADDRSTRLEN];
3432 	char ifaddr[INET6_ADDRSTRLEN];
3433 	int err;
3434 
3435 	if (addr == NULL) {
3436 		return (NULL);
3437 	}
3438 
3439 	rcm_log_message(RCM_TRACE2, "IP: get_mpathd_dest(%s)\n", addr);
3440 
3441 	if (family & CONFIG_AF_INET6) {
3442 		af = AF_INET6;
3443 	} else {
3444 		af = AF_INET;
3445 	}
3446 
3447 	if ((sock = socket(af, SOCK_DGRAM, 0)) == -1) {
3448 		rcm_log_message(RCM_ERROR,
3449 		    _("IP: failure opening %s socket: %s\n"),
3450 		    af == AF_INET6 ? "IPv6" : "IPv4", strerror(errno));
3451 		return (NULL);
3452 	}
3453 
3454 	lifn.lifn_family = af;
3455 	lifn.lifn_flags = 0;
3456 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
3457 		rcm_log_message(RCM_ERROR,
3458 		    _("IP: SIOCLGIFNUM failed: %s\n"),
3459 		    strerror(errno));
3460 		(void) close(sock);
3461 		return (NULL);
3462 	}
3463 
3464 	if ((buf = calloc(lifn.lifn_count, sizeof (struct lifreq))) == NULL) {
3465 		rcm_log_message(RCM_ERROR, _("IP: calloc: %s\n"),
3466 		    strerror(errno));
3467 		(void) close(sock);
3468 		return (NULL);
3469 	}
3470 
3471 	lifc.lifc_family = af;
3472 	lifc.lifc_flags = 0;
3473 	lifc.lifc_len = sizeof (struct lifreq) * lifn.lifn_count;
3474 	lifc.lifc_buf = buf;
3475 
3476 	if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
3477 		rcm_log_message(RCM_ERROR,
3478 		    _("IP: SIOCGLIFCONF failed: %s\n"),
3479 		    strerror(errno));
3480 		free(buf);
3481 		(void) close(sock);
3482 		return (NULL);
3483 	}
3484 
3485 	/* Filter out prefix address from netmask */
3486 	(void) strcpy(ifaddr, addr);
3487 	if ((prefix = strchr(ifaddr, '/')) != NULL) {
3488 		*prefix = '\0';	/* We care about the address part only */
3489 	}
3490 
3491 	/* Check for aliases */
3492 	hp = getipnodebyname(ifaddr, af, AI_DEFAULT, &err);
3493 	if (hp) {
3494 		if (inet_ntop(af, (void *)hp->h_addr_list[0],
3495 		    ifaddr, sizeof (ifaddr)) == NULL) {
3496 			/* Restore original address and use it */
3497 			(void) strcpy(ifaddr, addr);
3498 			if ((prefix = strchr(ifaddr, '/')) != NULL) {
3499 				*prefix = '\0';
3500 			}
3501 		}
3502 		freehostent(hp);
3503 	}
3504 	rcm_log_message(RCM_TRACE2, "IP: ifaddr(%s) = %s\n",
3505 		    addr, ifaddr);
3506 
3507 	/* now search the interfaces */
3508 	lifrp = lifc.lifc_req;
3509 	for (i = 0; i < lifn.lifn_count; i++, lifrp++) {
3510 		(void) strcpy(lifreq.lifr_name, lifrp->lifr_name);
3511 		/* Get the interface address for this interface */
3512 		if (ioctl(sock, SIOCGLIFADDR, (char *)&lifreq) < 0) {
3513 			rcm_log_message(RCM_ERROR,
3514 			    _("IP: SIOCGLIFADDR: %s\n"), strerror(errno));
3515 			free(buf);
3516 			(void) close(sock);
3517 			return (NULL);
3518 		}
3519 
3520 		if (af == AF_INET6) {
3521 			sin6 = (struct sockaddr_in6 *)&lifreq.lifr_addr;
3522 			if (inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3523 			    addrstr, sizeof (addrstr)) == NULL) {
3524 				continue;
3525 			}
3526 		} else {
3527 			sin = (struct sockaddr_in *)&lifreq.lifr_addr;
3528 			if (inet_ntop(AF_INET, (void *)&sin->sin_addr,
3529 			    addrstr, sizeof (addrstr)) == NULL) {
3530 				continue;
3531 			}
3532 		}
3533 
3534 		if (STREQ(addrstr, ifaddr)) {
3535 			/* Allocate memory to hold interface name */
3536 			if ((ifname = (char *)malloc(LIFNAMSIZ)) == NULL) {
3537 				rcm_log_message(RCM_ERROR,
3538 				    _("IP: malloc: %s\n"), strerror(errno));
3539 				free(buf);
3540 				(void) close(sock);
3541 				return (NULL);
3542 			}
3543 
3544 			/* Copy the interface name */
3545 			/*
3546 			 * (void) memcpy(ifname, lifrp->lifr_name,
3547 			 *  sizeof (ifname));
3548 			 * ifname[sizeof (ifname) - 1] = '\0';
3549 			 */
3550 			(void) strcpy(ifname, lifrp->lifr_name);
3551 			break;
3552 		}
3553 	}
3554 
3555 	(void) close(sock);
3556 	free(buf);
3557 
3558 	if (ifname == NULL)
3559 		rcm_log_message(RCM_TRACE2, "IP: get_mpathd_dest(%s): none\n",
3560 		    addr);
3561 	else
3562 		rcm_log_message(RCM_TRACE2, "IP: get_mpathd_dest(%s): %s\n",
3563 		    addr, ifname);
3564 
3565 	return (ifname);
3566 }
3567 
3568 static int
3569 if_getcount(int af)
3570 {
3571 	int sock;
3572 	struct lifnum lifn;
3573 
3574 	rcm_log_message(RCM_TRACE1, "IP: if_getcount\n");
3575 
3576 	if ((sock = socket(af, SOCK_DGRAM, 0)) == -1) {
3577 		rcm_log_message(RCM_ERROR,
3578 		    _("IP: failure opening %s socket: %s\n"),
3579 		    af == AF_INET6 ? "IPv6" : "IPv4", strerror(errno));
3580 		return (-1);
3581 	}
3582 
3583 	lifn.lifn_family = af;
3584 	lifn.lifn_flags = 0;
3585 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
3586 		rcm_log_message(RCM_ERROR,
3587 		    _("IP: SIOCLGIFNUM failed: %s\n"),
3588 		    strerror(errno));
3589 		(void) close(sock);
3590 		return (-1);
3591 	}
3592 	(void) close(sock);
3593 
3594 	rcm_log_message(RCM_TRACE1, "IP: if_getcount success: %d\n",
3595 	    lifn.lifn_count);
3596 
3597 	return (lifn.lifn_count);
3598 }
3599 
3600 /*
3601  * tokenize() - turn a command line into tokens; caller is responsible to
3602  *	      provide enough memory to hold all tokens
3603  */
3604 static void
3605 tokenize(char *line, char **tokens, char *tspace, int *ntok)
3606 {
3607 	char *cp;
3608 	char *sp;
3609 
3610 	sp = tspace;
3611 	cp = line;
3612 	for (*ntok = 0; *ntok < MAXARGS; (*ntok)++) {
3613 		tokens[*ntok] = sp;
3614 		while (ISSPACE(*cp))
3615 			cp++;
3616 		if (ISEOL(*cp))
3617 			break;
3618 		do {
3619 			*sp++ = *cp++;
3620 		} while (!ISSPACE(*cp) && !ISEOL(*cp));
3621 
3622 		*sp++ = '\0';
3623 	}
3624 }
3625 
3626 #ifdef RCM_IPMP_DEBUG
3627 static void
3628 dump_node(ip_cache_t *node)
3629 {
3630 	ip_pif_t *pif = node->ip_pif;
3631 	ip_lif_t *lif = pif->pi_lifs;
3632 
3633 	rcm_log_message(RCM_TRACE1, "Node dump:\n");
3634 	rcm_log_message(RCM_TRACE1, "resource = %s\t cache flags = 0x%x\n",
3635 	    node->ip_resource, node->ip_cachestate);
3636 	rcm_log_message(RCM_TRACE1, "ifname = %s\n", pif->pi_ifname);
3637 	rcm_log_message(RCM_TRACE1, "groupname = %s\n", pif->pi_grpname);
3638 
3639 	if (lif == NULL) {
3640 		rcm_log_message(RCM_TRACE1, "No lifs hosted on this device.\n");
3641 		return;
3642 	}
3643 
3644 	rcm_log_message(RCM_TRACE1,
3645 	    "Logical interfaces hosted on this device - \n");
3646 	while (lif != NULL) {
3647 		rcm_log_message(RCM_TRACE1, "\t ifnum = %d \t ifflags = 0x%x",
3648 		    lif->li_ifnum, lif->li_ifflags);
3649 		if (lif->li_addr.family == AF_INET)
3650 			rcm_log_message(RCM_TRACE1, "\t Family = IPv4");
3651 		else if (lif->li_addr.family == AF_INET6)
3652 			rcm_log_message(RCM_TRACE1, "\t Family = IPv6");
3653 		else rcm_log_message(RCM_TRACE1, "\t Family = <Unknown>");
3654 		rcm_log_message(RCM_TRACE1, "\n");
3655 
3656 		lif = lif->li_next;
3657 	}
3658 }
3659 #endif /* RCM_IPMP_DEBUG */
3660