/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * RCM module to prevent plumbed IP addresses from being removed. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rcm_module.h" #define SUNW_IP "SUNW_ip/" #define IP_REG_SIZE (9 + INET6_ADDRSTRLEN) #define IP_ANON_USAGE gettext("Plumbed IP Address") #define IP_SUSPEND_ERR gettext("Plumbed IP Addresses cannot be suspended") #define IP_OFFLINE_ERR gettext("Invalid operation: IP cannot be offlined") #define IP_REMOVE_ERR gettext("Invalid operation: IP cannot be removed") #define IP_REG_FAIL gettext("Registration Failed") #define IP_NO_CLUSTER gettext("Could not read cluster network addresses") #define IP_FLAG_NEW 0x00 #define IP_FLAG_REG 0x01 #define IP_FLAG_CL 0x02 #define IP_FLAG_IGNORE 0x04 #define IP_FLAG_DELETE 0x08 static int ip_anon_register(rcm_handle_t *); static int ip_anon_unregister(rcm_handle_t *); static int ip_anon_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, char **, nvlist_t *, rcm_info_t **); static int ip_anon_suspend(rcm_handle_t *, char *, id_t, timespec_t *, uint_t, char **, rcm_info_t **); static int ip_anon_resume(rcm_handle_t *, char *, id_t, uint_t, char **, rcm_info_t **); static int ip_anon_offline(rcm_handle_t *, char *, id_t, uint_t, char **, rcm_info_t **); static int ip_anon_online(rcm_handle_t *, char *, id_t, uint_t, char **, rcm_info_t **); static int ip_anon_remove(rcm_handle_t *, char *, id_t, uint_t, char **, rcm_info_t **); static int exclude_ipv4(cladm_netaddrs_t exclude_addrs, ipaddr_t address); static int exclude_ipv6(cladm_netaddrs_t exclude_addrs, uint32_t address[4]); typedef struct ip_status { int flags; char device[IP_REG_SIZE]; struct ip_status *next; } ip_status_t; static ip_status_t *findreg(char *reg); static ip_status_t *addreg(char *reg); static int deletereg(ip_status_t *entry); static ip_status_t *ip_list = NULL; static mutex_t ip_list_lock; static struct rcm_mod_ops ip_anon_ops = { RCM_MOD_OPS_VERSION, ip_anon_register, ip_anon_unregister, ip_anon_getinfo, ip_anon_suspend, ip_anon_resume, ip_anon_offline, ip_anon_online, ip_anon_remove, NULL, NULL, NULL }; struct rcm_mod_ops * rcm_mod_init() { return (&ip_anon_ops); } const char * rcm_mod_info() { return ("RCM IP address module 1.4"); } int rcm_mod_fini() { ip_status_t *tlist; /* free the registration list */ (void) mutex_lock(&ip_list_lock); while (ip_list != NULL) { tlist = ip_list->next; free(ip_list); ip_list = tlist; } (void) mutex_unlock(&ip_list_lock); (void) mutex_destroy(&ip_list_lock); return (RCM_SUCCESS); } static int ip_anon_register(rcm_handle_t *hdl) { int bootflags; struct ifaddrlist *al = NULL, *al6 = NULL; char errbuf[ERRBUFSIZE]; char treg[IP_REG_SIZE], tstr[IP_REG_SIZE]; cladm_netaddrs_t exclude_addrs; int num_ifs, num_ifs6, i, ret; uint32_t num_exclude_addrs = 0; ip_status_t *tlist, *tentry; (void) mutex_lock(&ip_list_lock); rcm_log_message(RCM_DEBUG, "ip_anon: registration refresh.\n"); exclude_addrs.cladm_num_netaddrs = 0; if (_cladm(CL_INITIALIZE, CL_GET_BOOTFLAG, &bootflags) != 0) { rcm_log_message(RCM_ERROR, gettext("unable to check cluster status\n")); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } rcm_log_message(RCM_DEBUG, "ip_anon: cladm bootflags=%d\n", bootflags); if (bootflags == 3) { /* build the exclusion list */ if ((ret = _cladm(CL_CONFIG, CL_GET_NUM_NETADDRS, &num_exclude_addrs)) == 0) { exclude_addrs.cladm_num_netaddrs = num_exclude_addrs; if (num_exclude_addrs == 0) rcm_log_message(RCM_DEBUG, "ip_anon: no addresses excluded\n"); else { if ((exclude_addrs.cladm_netaddrs_array = malloc(sizeof (cladm_netaddr_entry_t) * (num_exclude_addrs))) == NULL) { rcm_log_message(RCM_ERROR, gettext("out of memory\n")); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } if ((ret = _cladm(CL_CONFIG, CL_GET_NETADDRS, &exclude_addrs)) != 0) { rcm_log_message(RCM_ERROR, IP_NO_CLUSTER); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } } } else { if ((ret != 0) && (errno == EINVAL)) { rcm_log_message(RCM_DEBUG, "no _cladm() backend to get addrs\n"); } else { rcm_log_message(RCM_ERROR, IP_NO_CLUSTER); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } } rcm_log_message(RCM_DEBUG, "cladm returned %d errno=%d\n", ret, errno); rcm_log_message(RCM_DEBUG, "ip_anon: num exclude addrs: %d\n", exclude_addrs.cladm_num_netaddrs); /* print the exclusion list for debugging purposes */ for (i = 0; i < exclude_addrs.cladm_num_netaddrs; i++) { (void) strcpy(treg, ""); (void) strcpy(tstr, ""); if (exclude_addrs.cladm_netaddrs_array[i].\ cl_ipversion == IPV4_VERSION) { (void) inet_ntop(AF_INET, &exclude_addrs.cladm_netaddrs_array[i]. cl_ipv_un.cl_ipv4.ipv4_netaddr, treg, INET_ADDRSTRLEN); (void) inet_ntop(AF_INET, &exclude_addrs.cladm_netaddrs_array[i]. cl_ipv_un.cl_ipv4.ipv4_netmask, tstr, INET_ADDRSTRLEN); } if (exclude_addrs.cladm_netaddrs_array[i].\ cl_ipversion == IPV6_VERSION) { (void) inet_ntop(AF_INET6, &exclude_addrs.cladm_netaddrs_array[i]. cl_ipv_un.cl_ipv6.ipv6_netaddr, treg, INET6_ADDRSTRLEN); (void) inet_ntop(AF_INET6, &exclude_addrs.cladm_netaddrs_array[i]. cl_ipv_un.cl_ipv6.ipv6_netmask, tstr, INET6_ADDRSTRLEN); } rcm_log_message(RCM_DEBUG, "IPV%d: %s %s\n", exclude_addrs.cladm_netaddrs_array[i]. cl_ipversion, treg, tstr); } } /* obtain a list of all IPv4 and IPv6 addresses in the system */ rcm_log_message(RCM_DEBUG, "ip_anon: obtaining list of IPv4 addresses.\n"); num_ifs = ifaddrlist(&al, AF_INET, LIFC_UNDER_IPMP, errbuf); if (num_ifs == -1) { rcm_log_message(RCM_ERROR, gettext("cannot get IPv4 address list errno=%d (%s)\n"), errno, errbuf); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } rcm_log_message(RCM_DEBUG, "ip_anon: obtaining list of IPv6 addresses.\n"); num_ifs6 = ifaddrlist(&al6, AF_INET6, LIFC_UNDER_IPMP, errbuf); if (num_ifs6 == -1) { rcm_log_message(RCM_ERROR, gettext("cannot get IPv6 address list errno=%d (%s)\n"), errno, errbuf); free(al); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } /* check the state of outstanding registrations against the list */ rcm_log_message(RCM_DEBUG, "ip_anon: checking outstanding registrations.\n"); tlist = ip_list; while (tlist != NULL) { tlist->flags |= IP_FLAG_DELETE; tlist = tlist->next; } /* IPv4 */ rcm_log_message(RCM_DEBUG, "ip_anon: checking IPv4 addresses.\n"); for (i = 0; i < num_ifs; i++) { (void) inet_ntop(AF_INET, &al[i].addr.addr, tstr, INET_ADDRSTRLEN); (void) strcpy(treg, SUNW_IP); (void) strcat(treg, tstr); if ((tlist = findreg(treg)) == NULL) tlist = addreg(treg); else tlist->flags &= (~IP_FLAG_DELETE); if (tlist == NULL) { rcm_log_message(RCM_ERROR, gettext("out of memory\n")); free(al); free(al6); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } if (exclude_ipv4(exclude_addrs, al[i].addr.addr.s_addr)) tlist->flags |= IP_FLAG_CL; } /* IPv6 */ rcm_log_message(RCM_DEBUG, "ip_anon: checking IPv6 addresses.\n"); for (i = 0; i < num_ifs6; i++) { (void) inet_ntop(AF_INET6, &al6[i].addr.addr, tstr, INET6_ADDRSTRLEN); (void) strcpy(treg, SUNW_IP); (void) strcat(treg, tstr); if ((tlist = findreg(treg)) == NULL) tlist = addreg(treg); else tlist->flags &= (~IP_FLAG_DELETE); if (tlist == NULL) { rcm_log_message(RCM_ERROR, gettext("out of memory\n")); free(al); free(al6); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } if (exclude_ipv6(exclude_addrs, al6[i].addr.addr6._S6_un.\ _S6_u32)) tlist->flags |= IP_FLAG_CL; } rcm_log_message(RCM_DEBUG, "ip_anon: updating reg. state.\n"); /* examine the list of ip address registrations and their state */ tlist = ip_list; while (tlist != NULL) { tentry = tlist; tlist = tlist->next; if (tentry->flags & IP_FLAG_DELETE) { if (tentry->flags & IP_FLAG_REG) { rcm_log_message(RCM_DEBUG, "ip_anon: unregistering interest in %s\n", tentry->device); if (rcm_unregister_interest(hdl, tentry->device, 0) != 0) { rcm_log_message(RCM_ERROR, gettext("failed to unregister")); } } (void) deletereg(tentry); } else if (!(tentry->flags & IP_FLAG_IGNORE)) { /* * If the registration is not a clustered devices and * not already registered, then RCM doesn't * currently know about it. */ if (!(tentry->flags & IP_FLAG_CL) && !(tentry->flags & IP_FLAG_REG)) { tentry->flags |= IP_FLAG_REG; rcm_log_message(RCM_DEBUG, "ip_anon: registering interest in %s\n", tentry->device); if (rcm_register_interest(hdl, tentry->device, 0, NULL) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, IP_REG_FAIL); free(al); free(al6); (void) mutex_unlock(&ip_list_lock); return (RCM_FAILURE); } else { rcm_log_message(RCM_DEBUG, "ip_anon: registered %s\n", tentry->device); } } /* * If the entry is registered and clustered, then * the configuration has been changed and it * should be unregistered. */ if ((tentry->flags & IP_FLAG_REG) & (tentry->flags & IP_FLAG_CL)) { rcm_log_message(RCM_DEBUG, "ip_anon: unregistering in %s\n", tentry->device); if (rcm_unregister_interest(hdl, tentry->device, 0) != 0) { rcm_log_message(RCM_ERROR, gettext("failed to unregister")); } tentry->flags &= (~IP_FLAG_REG); } } } tlist = ip_list; while (tlist != NULL) { rcm_log_message(RCM_DEBUG, "ip_anon: %s (%Xh)\n", tlist->device, tlist->flags); tlist = tlist->next; } rcm_log_message(RCM_DEBUG, "ip_anon: registration complete.\n"); free(al); free(al6); (void) mutex_unlock(&ip_list_lock); return (RCM_SUCCESS); } static int ip_anon_unregister(rcm_handle_t *hdl) { ip_status_t *tlist; (void) mutex_lock(&ip_list_lock); tlist = ip_list; while (tlist != NULL) { if ((tlist->flags & IP_FLAG_REG)) { if (rcm_unregister_interest(hdl, tlist->device, 0) != 0) { rcm_log_message(RCM_ERROR, gettext("failed to unregister")); } tlist->flags &= (~IP_FLAG_REG); } tlist = tlist->next; } (void) mutex_unlock(&ip_list_lock); return (RCM_SUCCESS); } /*ARGSUSED*/ static int ip_anon_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent) { assert(rsrcname != NULL && infostr != NULL); if ((*infostr = strdup(IP_ANON_USAGE)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_SUCCESS); } /*ARGSUSED*/ static int ip_anon_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval, uint_t flags, char **errstr, rcm_info_t **dependent) { if ((*errstr = strdup(IP_SUSPEND_ERR)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_FAILURE); } /*ARGSUSED*/ static int ip_anon_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **errstr, rcm_info_t **dependent) { return (RCM_SUCCESS); } /*ARGSUSED*/ static int ip_anon_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **errstr, rcm_info_t **dependent) { if ((*errstr = strdup(IP_OFFLINE_ERR)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_FAILURE); } /*ARGSUSED*/ static int ip_anon_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **errstr, rcm_info_t **dependent) { return (RCM_SUCCESS); } /*ARGSUSED*/ static int ip_anon_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **errstr, rcm_info_t **dependent) { if ((*errstr = strdup(IP_REMOVE_ERR)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_FAILURE); } /* * Call with ip_list_lock held. */ static ip_status_t * findreg(char *reg) { ip_status_t *tlist; int done; tlist = ip_list; done = 0; while ((tlist != NULL) && (!done)) { if (strcmp(tlist->device, reg) == 0) done = 1; else tlist = tlist->next; } return (tlist); } static ip_status_t * addreg(char *reg) { ip_status_t *tlist, *tentry; tentry = (ip_status_t *)malloc(sizeof (ip_status_t)); if (tentry == NULL) return (tentry); tentry->flags = IP_FLAG_NEW; tentry->next = NULL; (void) strcpy(tentry->device, reg); if (ip_list == NULL) ip_list = tentry; else { tlist = ip_list; while (tlist->next != NULL) tlist = tlist->next; tlist->next = tentry; } return (tentry); } static int deletereg(ip_status_t *entry) { ip_status_t *tlist; if (entry == NULL) return (-1); if (entry == ip_list) { ip_list = ip_list->next; free(entry); } else { tlist = ip_list; while ((tlist->next != NULL) && (tlist->next != entry)) tlist = tlist->next; if (tlist->next != entry) return (-1); tlist->next = entry->next; free(entry); } return (0); } static int exclude_ipv4(cladm_netaddrs_t exclude_addrs, ipaddr_t address) { int i; char taddr[IP_REG_SIZE], tmask[IP_REG_SIZE], tmatch[IP_REG_SIZE]; ipaddr_t ipv4_netaddr, ipv4_netmask; (void) inet_ntop(AF_INET, &address, taddr, INET_ADDRSTRLEN); rcm_log_message(RCM_DEBUG, "ip_anon: exclude_ipv4 (%s, %d)\n", taddr, exclude_addrs.cladm_num_netaddrs); /* * If this falls in the exclusion list, the IP_FLAG_CL * bit should be set for the adapter. */ for (i = 0; i < exclude_addrs.cladm_num_netaddrs; i++) { if (exclude_addrs.cladm_netaddrs_array[i].\ cl_ipversion == IPV4_VERSION) { ipv4_netaddr = exclude_addrs.\ cladm_netaddrs_array[i].cl_ipv_un.cl_ipv4.\ ipv4_netaddr; ipv4_netmask = exclude_addrs.\ cladm_netaddrs_array[i].cl_ipv_un.cl_ipv4.\ ipv4_netmask; (void) inet_ntop(AF_INET, &ipv4_netaddr, tmatch, INET_ADDRSTRLEN); (void) inet_ntop(AF_INET, &ipv4_netmask, tmask, INET_ADDRSTRLEN); if ((address & ipv4_netmask) == ipv4_netaddr) { rcm_log_message(RCM_DEBUG, "ip_anon: matched %s:%s => %s\n", taddr, tmask, tmatch); return (1); } } } rcm_log_message(RCM_DEBUG, "ip_anon: no match for %s\n", taddr); return (0); } static int exclude_ipv6(cladm_netaddrs_t exclude_addrs, uint32_t address[4]) { int i, j, numequal; uint32_t addr[4], ipv6_netaddr[4], ipv6_netmask[4]; char taddr[IP_REG_SIZE], tmask[IP_REG_SIZE], tmatch[IP_REG_SIZE]; (void) inet_ntop(AF_INET6, address, taddr, INET6_ADDRSTRLEN); /* * If this falls in the exclusion list, the IP_FLAG_CL * bit should be set for the adapter. */ for (i = 0; i < exclude_addrs.cladm_num_netaddrs; i++) { if (exclude_addrs.cladm_netaddrs_array[i].\ cl_ipversion == IPV6_VERSION) { numequal = 0; for (j = 0; j < 4; j++) { ipv6_netaddr[j] = exclude_addrs.\ cladm_netaddrs_array[i].\ cl_ipv_un.cl_ipv6.ipv6_netaddr[j]; ipv6_netmask[j] = exclude_addrs.\ cladm_netaddrs_array[i].\ cl_ipv_un.cl_ipv6.ipv6_netmask[j]; addr[j] = address[j] & ipv6_netmask[j]; if (addr[j] == ipv6_netaddr[j]) numequal++; } (void) inet_ntop(AF_INET6, ipv6_netaddr, tmatch, INET6_ADDRSTRLEN); (void) inet_ntop(AF_INET6, ipv6_netmask, tmask, INET6_ADDRSTRLEN); if (numequal == 4) return (1); } } rcm_log_message(RCM_DEBUG, "ip_anon: no match for %s\n", taddr); return (0); }