1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 * 11 * Copyright 2011 Nexenta Systems, Inc. All rights reserved. 12 */ 13 14 /* 15 * inet_matchaddr 16 * 17 * Match IPv4 or IPv6 address provided in sa (sockaddr_in/sockaddr_in6) 18 * against standard text representation specified in name: 19 * 20 * IPv4: 21 * IPv4 22 * IPv4/netmask 23 * 24 * IPv6: 25 * [IPv6] 26 * [IPv6]/prefix 27 */ 28 29 #include <sys/socket.h> 30 #include <sys/types.h> 31 32 #include <arpa/inet.h> 33 34 #include <netinet/in.h> 35 36 #include <ctype.h> 37 #include <err.h> 38 #include <netdb.h> 39 #include <stdlib.h> 40 #include <strings.h> 41 42 43 boolean_t 44 inet_matchaddr(const void *sa, const char *name) 45 { 46 boolean_t ret = B_FALSE; 47 char *lname, *mp, *p; 48 uint32_t claddr4 = 0; 49 50 if ((p = lname = strdup(name)) == NULL) 51 err(1, "strdup"); 52 53 if ((mp = strchr(p, '/')) != NULL) 54 *mp++ = '\0'; 55 56 switch (((struct sockaddr_in *)sa)->sin_family) { 57 case AF_INET6: { 58 char *pp; 59 int prefix6; 60 ipaddr_t ipaddr4; 61 struct in6_addr hcaddr6; 62 struct in6_addr *claddr6 = 63 &((struct sockaddr_in6 *)sa)->sin6_addr; 64 65 if (!IN6_IS_ADDR_V4MAPPED(claddr6)) { 66 /* IPv6 address */ 67 if ((p = strchr(p, '[')) == NULL) 68 break; 69 p++; 70 71 if ((pp = strchr(p, ']')) == NULL) 72 break; 73 *pp = '\0'; 74 75 if (inet_pton(AF_INET6, p, &hcaddr6) != 1) 76 break; 77 78 if (mp != NULL) { 79 /* Match only first prefix bits */ 80 if ((prefix6 = (int)strtol(mp, 81 (char **)NULL, 10)) == 0) 82 break; 83 ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6, 84 &hcaddr6, prefix6); 85 break; 86 } else { 87 /* No prefix, exact match */ 88 ret = IN6_ARE_ADDR_EQUAL(claddr6, &hcaddr6); 89 break; 90 } 91 } else { 92 /* IPv4-mapped IPv6 address, fallthrough to IPv4 */ 93 IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4); 94 claddr4 = ntohl(ipaddr4); 95 } 96 /*FALLTHROUGH*/ 97 } 98 case AF_INET: { 99 int bits, i; 100 uint32_t hcaddr4 = 0, mask4; 101 102 if (claddr4 == 0) 103 claddr4 = ntohl( 104 ((struct sockaddr_in *)sa)->sin_addr.s_addr); 105 106 for (i = 0; i < 4; i++) { 107 hcaddr4 |= (int)strtol(p, (char **)NULL, 10) << 108 ((3 - i) * 8); 109 if ((p = strchr(p, '.')) == NULL) 110 break; 111 p++; 112 } 113 114 if (hcaddr4 == 0) 115 break; 116 117 if (mp != NULL) { 118 /* Mask is specified explicitly */ 119 if ((bits = (int)strtol(mp, (char **)NULL, 10)) == 0) 120 break; 121 mask4 = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) 122 - bits) : 0; 123 hcaddr4 &= mask4; 124 } else { 125 /* 126 * Use old-fashioned implicit netmasking by checking 127 * for lower-end zeroes. On the off chance we don't 128 * match any well-known prefixes, return an exact- 129 * match prefix which is misleadingly labelled as 130 * IN_CLASSE_NET. 131 */ 132 if ((hcaddr4 & IN_CLASSA_HOST) == 0) 133 mask4 = IN_CLASSA_NET; 134 else if ((hcaddr4 & IN_CLASSB_HOST) == 0) 135 mask4 = IN_CLASSB_NET; 136 else if ((hcaddr4 & IN_CLASSC_HOST) == 0) 137 mask4 = IN_CLASSC_NET; 138 else 139 mask4 = IN_CLASSE_NET; 140 } 141 142 ret = ((claddr4 & mask4) == hcaddr4); 143 break; 144 } 145 } 146 147 free(lname); 148 149 return (ret); 150 } 151