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