19b241b4eSYuri Pankov /* 29b241b4eSYuri Pankov * This file and its contents are supplied under the terms of the 39b241b4eSYuri Pankov * Common Development and Distribution License ("CDDL"), version 1.0. 49b241b4eSYuri Pankov * You may only use this file in accordance with the terms of version 59b241b4eSYuri Pankov * 1.0 of the CDDL. 69b241b4eSYuri Pankov * 79b241b4eSYuri Pankov * A full copy of the text of the CDDL should have accompanied this 89b241b4eSYuri Pankov * source. A copy of the CDDL is also available via the Internet at 99b241b4eSYuri Pankov * http://www.illumos.org/license/CDDL. 10043dd586SYuri Pankov */ 11043dd586SYuri Pankov 12043dd586SYuri Pankov /* 1303b59f78SYuri Pankov * Copyright 2016 Nexenta Systems, Inc. 149b241b4eSYuri Pankov */ 159b241b4eSYuri Pankov 169b241b4eSYuri Pankov /* 179b241b4eSYuri Pankov * inet_matchaddr 189b241b4eSYuri Pankov * 199b241b4eSYuri Pankov * Match IPv4 or IPv6 address provided in sa (sockaddr_in/sockaddr_in6) 209b241b4eSYuri Pankov * against standard text representation specified in name: 219b241b4eSYuri Pankov * 229b241b4eSYuri Pankov * IPv4: 239b241b4eSYuri Pankov * IPv4 249b241b4eSYuri Pankov * IPv4/netmask 259b241b4eSYuri Pankov * 269b241b4eSYuri Pankov * IPv6: 279b241b4eSYuri Pankov * [IPv6] 289b241b4eSYuri Pankov * [IPv6]/prefix 2903b59f78SYuri Pankov * 3003b59f78SYuri Pankov * Return values: 3103b59f78SYuri Pankov * 3203b59f78SYuri Pankov * 0 mismatch 3303b59f78SYuri Pankov * 1 match 3403b59f78SYuri Pankov * -1 error occured, caller should check errno: 3503b59f78SYuri Pankov * EINVAL access list entry is invalid 3603b59f78SYuri Pankov * ENOMEM failed to allocate memory 379b241b4eSYuri Pankov */ 389b241b4eSYuri Pankov 399b241b4eSYuri Pankov #include <sys/socket.h> 409b241b4eSYuri Pankov #include <sys/types.h> 419b241b4eSYuri Pankov 429b241b4eSYuri Pankov #include <arpa/inet.h> 439b241b4eSYuri Pankov 449b241b4eSYuri Pankov #include <netinet/in.h> 459b241b4eSYuri Pankov 469b241b4eSYuri Pankov #include <ctype.h> 47043dd586SYuri Pankov #include <errno.h> 489b241b4eSYuri Pankov #include <netdb.h> 499b241b4eSYuri Pankov #include <stdlib.h> 509b241b4eSYuri Pankov #include <strings.h> 519b241b4eSYuri Pankov 5203b59f78SYuri Pankov int 539b241b4eSYuri Pankov inet_matchaddr(const void *sa, const char *name) 549b241b4eSYuri Pankov { 5503b59f78SYuri Pankov int ret = -1; 569b241b4eSYuri Pankov char *lname, *mp, *p; 5703b59f78SYuri Pankov char *ep; 5803b59f78SYuri Pankov int serrno = errno; 599b241b4eSYuri Pankov uint32_t claddr4 = 0; 609b241b4eSYuri Pankov 6103b59f78SYuri Pankov if ((p = lname = strdup(name)) == NULL) { 6203b59f78SYuri Pankov errno = ENOMEM; 6303b59f78SYuri Pankov return (-1); 6403b59f78SYuri Pankov } 659b241b4eSYuri Pankov 669b241b4eSYuri Pankov if ((mp = strchr(p, '/')) != NULL) 679b241b4eSYuri Pankov *mp++ = '\0'; 689b241b4eSYuri Pankov 699b241b4eSYuri Pankov switch (((struct sockaddr_in *)sa)->sin_family) { 709b241b4eSYuri Pankov case AF_INET6: { 719b241b4eSYuri Pankov char *pp; 729b241b4eSYuri Pankov ipaddr_t ipaddr4; 739b241b4eSYuri Pankov struct in6_addr hcaddr6; 749b241b4eSYuri Pankov struct in6_addr *claddr6 = 759b241b4eSYuri Pankov &((struct sockaddr_in6 *)sa)->sin6_addr; 769b241b4eSYuri Pankov 779b241b4eSYuri Pankov if (!IN6_IS_ADDR_V4MAPPED(claddr6)) { 789b241b4eSYuri Pankov /* IPv6 address */ 7903b59f78SYuri Pankov if (*p != '[') { 8003b59f78SYuri Pankov errno = EINVAL; 819b241b4eSYuri Pankov break; 8203b59f78SYuri Pankov } 839b241b4eSYuri Pankov p++; 849b241b4eSYuri Pankov 8503b59f78SYuri Pankov if ((pp = strchr(p, ']')) == NULL || 8603b59f78SYuri Pankov (mp != NULL && pp != mp - 2) || 8703b59f78SYuri Pankov (mp == NULL && *(pp + 1) != '\0')) { 8803b59f78SYuri Pankov errno = EINVAL; 899b241b4eSYuri Pankov break; 9003b59f78SYuri Pankov } 919b241b4eSYuri Pankov *pp = '\0'; 929b241b4eSYuri Pankov 9303b59f78SYuri Pankov if (inet_pton(AF_INET6, p, &hcaddr6) != 1) { 9403b59f78SYuri Pankov errno = EINVAL; 959b241b4eSYuri Pankov break; 9603b59f78SYuri Pankov } 979b241b4eSYuri Pankov 989b241b4eSYuri Pankov if (mp != NULL) { 999b241b4eSYuri Pankov /* Match only first prefix bits */ 10003b59f78SYuri Pankov long prefix6; 10103b59f78SYuri Pankov 10203b59f78SYuri Pankov errno = 0; 10303b59f78SYuri Pankov prefix6 = strtol(mp, &ep, 10); 10403b59f78SYuri Pankov if (errno != 0 || prefix6 < 0 || 10503b59f78SYuri Pankov prefix6 > 128 || *ep != '\0') { 10603b59f78SYuri Pankov errno = EINVAL; 1079b241b4eSYuri Pankov break; 10803b59f78SYuri Pankov } 1099b241b4eSYuri Pankov ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6, 11003b59f78SYuri Pankov &hcaddr6, prefix6) ? 1 : 0; 1119b241b4eSYuri Pankov break; 1129b241b4eSYuri Pankov } else { 1139b241b4eSYuri Pankov /* No prefix, exact match */ 11403b59f78SYuri Pankov ret = IN6_ARE_ADDR_EQUAL(claddr6, 11503b59f78SYuri Pankov &hcaddr6) ? 1 : 0; 1169b241b4eSYuri Pankov break; 1179b241b4eSYuri Pankov } 1189b241b4eSYuri Pankov } else { 1199b241b4eSYuri Pankov /* IPv4-mapped IPv6 address, fallthrough to IPv4 */ 1209b241b4eSYuri Pankov IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4); 1219b241b4eSYuri Pankov claddr4 = ntohl(ipaddr4); 1229b241b4eSYuri Pankov } 1239b241b4eSYuri Pankov } 124*a69e76caSToomas Soome /*FALLTHROUGH*/ 1259b241b4eSYuri Pankov case AF_INET: { 12603b59f78SYuri Pankov int i; 1279b241b4eSYuri Pankov uint32_t hcaddr4 = 0, mask4; 1289b241b4eSYuri Pankov 129043dd586SYuri Pankov if (claddr4 == 0) { 1309b241b4eSYuri Pankov claddr4 = ntohl( 1319b241b4eSYuri Pankov ((struct sockaddr_in *)sa)->sin_addr.s_addr); 132043dd586SYuri Pankov } 1339b241b4eSYuri Pankov 1349b241b4eSYuri Pankov for (i = 0; i < 4; i++) { 13503b59f78SYuri Pankov long qaddr4; 13603b59f78SYuri Pankov 13703b59f78SYuri Pankov errno = 0; 13803b59f78SYuri Pankov qaddr4 = strtol(p, &ep, 10); 13903b59f78SYuri Pankov if (errno != 0 || qaddr4 < 0 || qaddr4 > 255 || 14003b59f78SYuri Pankov (*ep != '.' && *ep != '\0')) { 14103b59f78SYuri Pankov errno = EINVAL; 1429b241b4eSYuri Pankov break; 14303b59f78SYuri Pankov } 14403b59f78SYuri Pankov hcaddr4 |= qaddr4 << ((3 - i) * 8); 14503b59f78SYuri Pankov if (*ep == '\0') 14603b59f78SYuri Pankov break; 14703b59f78SYuri Pankov p = ep + 1; 1489b241b4eSYuri Pankov } 1499b241b4eSYuri Pankov 15003b59f78SYuri Pankov if (errno != 0) 1519b241b4eSYuri Pankov break; 1529b241b4eSYuri Pankov 1539b241b4eSYuri Pankov if (mp != NULL) { 1549b241b4eSYuri Pankov /* Mask is specified explicitly */ 15503b59f78SYuri Pankov long mb; 15603b59f78SYuri Pankov 15703b59f78SYuri Pankov errno = 0; 15803b59f78SYuri Pankov mb = strtol(mp, &ep, 10); 15903b59f78SYuri Pankov if (errno != 0 || mb < 0 || mb > 32 || *ep != '\0') { 16003b59f78SYuri Pankov errno = EINVAL; 1619b241b4eSYuri Pankov break; 16203b59f78SYuri Pankov } 16303b59f78SYuri Pankov mask4 = mb ? ~0 << ((sizeof (struct in_addr) * NBBY) 16403b59f78SYuri Pankov - mb) : 0; 1659b241b4eSYuri Pankov hcaddr4 &= mask4; 1669b241b4eSYuri Pankov } else { 1679b241b4eSYuri Pankov /* 1689b241b4eSYuri Pankov * Use old-fashioned implicit netmasking by checking 1699b241b4eSYuri Pankov * for lower-end zeroes. On the off chance we don't 1709b241b4eSYuri Pankov * match any well-known prefixes, return an exact- 1719b241b4eSYuri Pankov * match prefix which is misleadingly labelled as 1729b241b4eSYuri Pankov * IN_CLASSE_NET. 1739b241b4eSYuri Pankov */ 1749b241b4eSYuri Pankov if ((hcaddr4 & IN_CLASSA_HOST) == 0) 1759b241b4eSYuri Pankov mask4 = IN_CLASSA_NET; 1769b241b4eSYuri Pankov else if ((hcaddr4 & IN_CLASSB_HOST) == 0) 1779b241b4eSYuri Pankov mask4 = IN_CLASSB_NET; 1789b241b4eSYuri Pankov else if ((hcaddr4 & IN_CLASSC_HOST) == 0) 1799b241b4eSYuri Pankov mask4 = IN_CLASSC_NET; 1809b241b4eSYuri Pankov else 1819b241b4eSYuri Pankov mask4 = IN_CLASSE_NET; 1829b241b4eSYuri Pankov } 1839b241b4eSYuri Pankov 18403b59f78SYuri Pankov ret = ((claddr4 & mask4) == hcaddr4) ? 1 : 0; 1859b241b4eSYuri Pankov break; 1869b241b4eSYuri Pankov } 1879b241b4eSYuri Pankov } 1889b241b4eSYuri Pankov 1899b241b4eSYuri Pankov free(lname); 1909b241b4eSYuri Pankov 19103b59f78SYuri Pankov if (ret != -1) 19203b59f78SYuri Pankov errno = serrno; 1939b241b4eSYuri Pankov return (ret); 1949b241b4eSYuri Pankov } 195