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
inet_matchaddr(const void * sa,const char * name)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