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