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:
35*e00bdde3SToomas Soome * 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>
48*e00bdde3SToomas Soome #include <stdint.h>
499b241b4eSYuri Pankov #include <netdb.h>
509b241b4eSYuri Pankov #include <stdlib.h>
519b241b4eSYuri Pankov #include <strings.h>
529b241b4eSYuri Pankov
5303b59f78SYuri Pankov int
inet_matchaddr(const void * sa,const char * name)549b241b4eSYuri Pankov inet_matchaddr(const void *sa, const char *name)
559b241b4eSYuri Pankov {
5603b59f78SYuri Pankov int ret = -1;
579b241b4eSYuri Pankov char *lname, *mp, *p;
5803b59f78SYuri Pankov char *ep;
5903b59f78SYuri Pankov int serrno = errno;
609b241b4eSYuri Pankov uint32_t claddr4 = 0;
619b241b4eSYuri Pankov
6203b59f78SYuri Pankov if ((p = lname = strdup(name)) == NULL) {
6303b59f78SYuri Pankov errno = ENOMEM;
6403b59f78SYuri Pankov return (-1);
6503b59f78SYuri Pankov }
669b241b4eSYuri Pankov
679b241b4eSYuri Pankov if ((mp = strchr(p, '/')) != NULL)
689b241b4eSYuri Pankov *mp++ = '\0';
699b241b4eSYuri Pankov
709b241b4eSYuri Pankov switch (((struct sockaddr_in *)sa)->sin_family) {
719b241b4eSYuri Pankov case AF_INET6: {
729b241b4eSYuri Pankov char *pp;
739b241b4eSYuri Pankov ipaddr_t ipaddr4;
749b241b4eSYuri Pankov struct in6_addr hcaddr6;
759b241b4eSYuri Pankov struct in6_addr *claddr6 =
769b241b4eSYuri Pankov &((struct sockaddr_in6 *)sa)->sin6_addr;
779b241b4eSYuri Pankov
789b241b4eSYuri Pankov if (!IN6_IS_ADDR_V4MAPPED(claddr6)) {
799b241b4eSYuri Pankov /* IPv6 address */
8003b59f78SYuri Pankov if (*p != '[') {
8103b59f78SYuri Pankov errno = EINVAL;
829b241b4eSYuri Pankov break;
8303b59f78SYuri Pankov }
849b241b4eSYuri Pankov p++;
859b241b4eSYuri Pankov
8603b59f78SYuri Pankov if ((pp = strchr(p, ']')) == NULL ||
8703b59f78SYuri Pankov (mp != NULL && pp != mp - 2) ||
8803b59f78SYuri Pankov (mp == NULL && *(pp + 1) != '\0')) {
8903b59f78SYuri Pankov errno = EINVAL;
909b241b4eSYuri Pankov break;
9103b59f78SYuri Pankov }
929b241b4eSYuri Pankov *pp = '\0';
939b241b4eSYuri Pankov
9403b59f78SYuri Pankov if (inet_pton(AF_INET6, p, &hcaddr6) != 1) {
9503b59f78SYuri Pankov errno = EINVAL;
969b241b4eSYuri Pankov break;
9703b59f78SYuri Pankov }
989b241b4eSYuri Pankov
999b241b4eSYuri Pankov if (mp != NULL) {
1009b241b4eSYuri Pankov /* Match only first prefix bits */
10103b59f78SYuri Pankov long prefix6;
10203b59f78SYuri Pankov
10303b59f78SYuri Pankov errno = 0;
10403b59f78SYuri Pankov prefix6 = strtol(mp, &ep, 10);
10503b59f78SYuri Pankov if (errno != 0 || prefix6 < 0 ||
10603b59f78SYuri Pankov prefix6 > 128 || *ep != '\0') {
10703b59f78SYuri Pankov errno = EINVAL;
1089b241b4eSYuri Pankov break;
10903b59f78SYuri Pankov }
1109b241b4eSYuri Pankov ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6,
11103b59f78SYuri Pankov &hcaddr6, prefix6) ? 1 : 0;
1129b241b4eSYuri Pankov break;
1139b241b4eSYuri Pankov } else {
1149b241b4eSYuri Pankov /* No prefix, exact match */
11503b59f78SYuri Pankov ret = IN6_ARE_ADDR_EQUAL(claddr6,
11603b59f78SYuri Pankov &hcaddr6) ? 1 : 0;
1179b241b4eSYuri Pankov break;
1189b241b4eSYuri Pankov }
1199b241b4eSYuri Pankov } else {
1209b241b4eSYuri Pankov /* IPv4-mapped IPv6 address, fallthrough to IPv4 */
1219b241b4eSYuri Pankov IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4);
1229b241b4eSYuri Pankov claddr4 = ntohl(ipaddr4);
1239b241b4eSYuri Pankov }
1249b241b4eSYuri Pankov }
125a69e76caSToomas Soome /*FALLTHROUGH*/
1269b241b4eSYuri Pankov case AF_INET: {
12703b59f78SYuri Pankov int i;
1289b241b4eSYuri Pankov uint32_t hcaddr4 = 0, mask4;
1299b241b4eSYuri Pankov
130043dd586SYuri Pankov if (claddr4 == 0) {
1319b241b4eSYuri Pankov claddr4 = ntohl(
1329b241b4eSYuri Pankov ((struct sockaddr_in *)sa)->sin_addr.s_addr);
133043dd586SYuri Pankov }
1349b241b4eSYuri Pankov
1359b241b4eSYuri Pankov for (i = 0; i < 4; i++) {
13603b59f78SYuri Pankov long qaddr4;
13703b59f78SYuri Pankov
13803b59f78SYuri Pankov errno = 0;
13903b59f78SYuri Pankov qaddr4 = strtol(p, &ep, 10);
14003b59f78SYuri Pankov if (errno != 0 || qaddr4 < 0 || qaddr4 > 255 ||
14103b59f78SYuri Pankov (*ep != '.' && *ep != '\0')) {
14203b59f78SYuri Pankov errno = EINVAL;
1439b241b4eSYuri Pankov break;
14403b59f78SYuri Pankov }
14503b59f78SYuri Pankov hcaddr4 |= qaddr4 << ((3 - i) * 8);
14603b59f78SYuri Pankov if (*ep == '\0')
14703b59f78SYuri Pankov break;
14803b59f78SYuri Pankov p = ep + 1;
1499b241b4eSYuri Pankov }
1509b241b4eSYuri Pankov
15103b59f78SYuri Pankov if (errno != 0)
1529b241b4eSYuri Pankov break;
1539b241b4eSYuri Pankov
1549b241b4eSYuri Pankov if (mp != NULL) {
1559b241b4eSYuri Pankov /* Mask is specified explicitly */
15603b59f78SYuri Pankov long mb;
15703b59f78SYuri Pankov
15803b59f78SYuri Pankov errno = 0;
15903b59f78SYuri Pankov mb = strtol(mp, &ep, 10);
16003b59f78SYuri Pankov if (errno != 0 || mb < 0 || mb > 32 || *ep != '\0') {
16103b59f78SYuri Pankov errno = EINVAL;
1629b241b4eSYuri Pankov break;
16303b59f78SYuri Pankov }
164*e00bdde3SToomas Soome if (mb != 0) {
165*e00bdde3SToomas Soome mask4 =
166*e00bdde3SToomas Soome UINT32_MAX <<
167*e00bdde3SToomas Soome ((sizeof (struct in_addr) * NBBY) - mb);
168*e00bdde3SToomas Soome } else {
169*e00bdde3SToomas Soome mask4 = 0;
170*e00bdde3SToomas Soome }
1719b241b4eSYuri Pankov hcaddr4 &= mask4;
1729b241b4eSYuri Pankov } else {
1739b241b4eSYuri Pankov /*
1749b241b4eSYuri Pankov * Use old-fashioned implicit netmasking by checking
1759b241b4eSYuri Pankov * for lower-end zeroes. On the off chance we don't
1769b241b4eSYuri Pankov * match any well-known prefixes, return an exact-
1779b241b4eSYuri Pankov * match prefix which is misleadingly labelled as
1789b241b4eSYuri Pankov * IN_CLASSE_NET.
1799b241b4eSYuri Pankov */
1809b241b4eSYuri Pankov if ((hcaddr4 & IN_CLASSA_HOST) == 0)
1819b241b4eSYuri Pankov mask4 = IN_CLASSA_NET;
1829b241b4eSYuri Pankov else if ((hcaddr4 & IN_CLASSB_HOST) == 0)
1839b241b4eSYuri Pankov mask4 = IN_CLASSB_NET;
1849b241b4eSYuri Pankov else if ((hcaddr4 & IN_CLASSC_HOST) == 0)
1859b241b4eSYuri Pankov mask4 = IN_CLASSC_NET;
1869b241b4eSYuri Pankov else
1879b241b4eSYuri Pankov mask4 = IN_CLASSE_NET;
1889b241b4eSYuri Pankov }
1899b241b4eSYuri Pankov
19003b59f78SYuri Pankov ret = ((claddr4 & mask4) == hcaddr4) ? 1 : 0;
1919b241b4eSYuri Pankov break;
1929b241b4eSYuri Pankov }
1939b241b4eSYuri Pankov }
1949b241b4eSYuri Pankov
1959b241b4eSYuri Pankov free(lname);
1969b241b4eSYuri Pankov
19703b59f78SYuri Pankov if (ret != -1)
19803b59f78SYuri Pankov errno = serrno;
1999b241b4eSYuri Pankov return (ret);
2009b241b4eSYuri Pankov }
201