Blob


1 /*
2 * Copyright (c) 2010-2014 Peter J. Philipp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28 #include "include.h"
29 #include "dns.h"
30 #include "db.h"
32 u_int8_t find_region(struct sockaddr_storage *, int);
33 in_addr_t getmask(int);
34 int getmask6(int, struct sockaddr_in6 *);
35 void init_region(void);
36 int insert_region(char *, char *, u_int8_t);
38 SLIST_HEAD(listhead, entry) head;
40 static struct entry {
41 char name[INET6_ADDRSTRLEN];
42 int family;
43 struct sockaddr_storage hostmask;
44 struct sockaddr_storage netmask;
45 u_int8_t region;
46 u_int8_t prefixlen;
47 SLIST_ENTRY(entry) region_entry;
48 } *n2, *np;
51 static const char rcsid[] = "$Id: region.c,v 1.11 2014/05/18 17:14:05 pjp Exp $";
53 /*
54 * INIT_REGION - initialize the region singly linked list
55 */
57 void
58 init_region(void)
59 {
60 SLIST_INIT(&head);
61 return;
62 }
64 /*
65 * INSERT_REGION - insert particular address and prefix length and region
66 * into the
67 * singly linked list at "head", if the address contains
68 * a colon then it is assumed to be an IPv6 address.
69 * return -1 on error, 0 on successful insertion
70 */
72 int
73 insert_region(char *address, char *prefixlen, u_int8_t region)
74 {
75 struct sockaddr_in *sin;
76 struct sockaddr_in6 *sin6;
77 int pnum;
78 int ret;
80 pnum = atoi(prefixlen);
81 n2 = malloc(sizeof(struct entry)); /* Insert after. */
83 if (strchr(address, ':') != NULL) {
84 n2->family = AF_INET6;
85 sin6 = (struct sockaddr_in6 *)&n2->hostmask;
86 if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
87 return (-1);
88 sin6->sin6_family = AF_INET6;
89 sin6 = (struct sockaddr_in6 *)&n2->netmask;
90 sin6->sin6_family = AF_INET6;
91 if (getmask6(pnum, sin6) < 0)
92 return(-1);
93 n2->region = region;
94 n2->prefixlen = pnum;
95 } else {
97 n2->family = AF_INET;
98 sin = (struct sockaddr_in *)&n2->hostmask;
99 sin->sin_family = AF_INET;
100 sin->sin_addr.s_addr = inet_addr(address);
101 sin = (struct sockaddr_in *)&n2->netmask;
102 sin->sin_family = AF_INET;
103 sin->sin_addr.s_addr = getmask(pnum);
104 n2->region = region;
105 n2->prefixlen = pnum;
109 SLIST_INSERT_HEAD(&head, n2, region_entry);
111 return (0);
114 /*
115 * FIND_REGION - walk the region list and find the correponding network with
116 * the highest prefix length, so that a /24 has more precedence
117 * than
118 * a /8 for example. IPv6 and IPv4 addresses are kept seperate
119 */
121 u_int8_t
122 find_region(struct sockaddr_storage *sst, int family)
124 struct sockaddr_in *sin, *sin0;
125 struct sockaddr_in6 *sin6, *sin60, *sin61;
126 u_int32_t hostmask, netmask;
127 u_int32_t a;
128 #ifdef __amd64
129 u_int64_t *hm[2], *nm[2], *a6[2];
130 #else
131 u_int32_t *hm[4], *nm[4], *a6[4];
132 #endif
133 u_int8_t region = 0xff;
134 u_int8_t prefixlen = 0;
136 SLIST_FOREACH(np, &head, region_entry) {
137 if (np->family == AF_INET) {
138 if (family != AF_INET)
139 continue;
140 sin = (struct sockaddr_in *)sst;
141 a = sin->sin_addr.s_addr;
142 sin = (struct sockaddr_in *)&np->hostmask;
143 sin0 = (struct sockaddr_in *)&np->netmask;
144 hostmask = sin->sin_addr.s_addr;
145 netmask = sin0->sin_addr.s_addr;
146 if ((hostmask & netmask) == (a & netmask)) {
147 if (np->prefixlen >= prefixlen) {
148 region = np->region;
149 prefixlen = np->prefixlen;
151 } /* if hostmask */
152 } else if (np->family == AF_INET6) {
153 if (family != AF_INET6)
154 continue;
155 sin6 = (struct sockaddr_in6 *)sst;
156 sin60 = (struct sockaddr_in6 *)&np->hostmask;
157 sin61 = (struct sockaddr_in6 *)&np->netmask;
158 #ifdef __amd64
159 /*
160 * If this is on a 64 bit machine, we'll benefit
161 * by using 64 bit registers, this should make it
162 * a tad faster...
163 */
164 hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
165 hm[1] = (hm[0] + 1);
166 nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
167 nm[1] = (nm[0] + 1);
168 a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
169 a6[1] = (a6[0] + 1);
170 if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
171 ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
172 #else
173 hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
174 hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
175 hm[3] = (hm[2] + 1);
176 nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
177 nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
178 nm[3] = (nm[2] + 1);
179 a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
180 a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
181 a6[3] = (a6[2] + 1);
182 if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
183 ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
184 ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
185 ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
186 #endif
188 if (np->prefixlen >= prefixlen) {
189 region = np->region;
190 prefixlen = np->prefixlen;
192 } /* if ip6 address */
194 } /* if AF_INET6 */
195 } /* SLIST */
197 return (region);
200 /*
201 * GETMASK - get the v4 netmask given by prefix length, return netmask in
202 * network byte order, function can't fail unless prefix length
203 * supplied is > 32
204 */
206 in_addr_t
207 getmask(int prefixlen)
209 in_addr_t ret = 0xffffffff;
211 /* I know it's cheating */
212 if (prefixlen > 31)
213 return (htonl(ret));
215 ret >>= prefixlen; /* 0x00ffffff */
216 ret = ~ret; /* 0xff000000 */
218 return (htonl(ret));
221 /*
222 * GETMASK6 - like getmask() but works on a supplied sockaddr_in6 instead of
223 * returning results as return address. Function cannot fail
224 * unless prefix length supplied is > 128. At which point a buffer
225 * overflow is possible.
226 */
228 int
229 getmask6(int prefixlen, struct sockaddr_in6 *sin6)
231 int i, j;
232 u_int32_t *nm[4];
234 if (prefixlen > 128 || prefixlen < 0)
235 return (-1);
237 memset(&sin6->sin6_addr.s6_addr, 0xff, sizeof(sin6->sin6_addr.s6_addr));
238 nm[0] = (u_int32_t *)sin6->sin6_addr.s6_addr;
239 nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
240 nm[3] = (nm[2] + 1);
242 for (i = 0, j = 0; j < prefixlen; j++) {
243 if (*nm[i] == 1) {
244 *nm[i] = 0;
245 i++;
246 } else
247 *nm[i] >>= 1;
249 *nm[0] = htonl(~ *nm[0]);
250 *nm[1] = htonl(~ *nm[1]);
251 *nm[2] = htonl(~ *nm[2]);
252 *nm[3] = htonl(~ *nm[3]);
254 return (0);