prefix.c revision 1.6
1/* $NetBSD: prefix.c,v 1.6 2003/09/02 22:56:11 itojun Exp $ */ 2/* $KAME: prefix.c,v 1.13 2003/09/02 22:50:17 itojun Exp $ */ 3 4/* 5 * Copyright (C) 2000 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/types.h> 34#include <sys/socket.h> 35#include <netinet/in.h> 36#include <stdio.h> 37#include <netdb.h> 38#include <string.h> 39#include <stddef.h> 40#include <stdlib.h> 41#include <limits.h> 42 43#ifndef offsetof 44#define offsetof(type, member) ((size_t)(u_long)(&((type *)0)->member)) 45#endif 46 47#include "faithd.h" 48#include "prefix.h" 49 50static int prefix_set __P((const char *, struct prefix *, int)); 51static struct config *config_load1 __P((const char *)); 52#if 0 53static void config_show1 __P((const struct config *)); 54static void config_show __P((void)); 55#endif 56 57struct config *config_list = NULL; 58const int niflags = NI_NUMERICHOST; 59 60static int 61prefix_set(const char *s, struct prefix *prefix, int slash) 62{ 63 char *p = NULL, *q, *r; 64 struct addrinfo hints, *res = NULL; 65 int max; 66 char *a; 67 68 p = strdup(s); 69 if (!p) 70 goto fail; 71 q = strchr(p, '/'); 72 if (q) { 73 if (!slash) 74 goto fail; 75 *q++ = '\0'; 76 } 77 78 memset(&hints, 0, sizeof(hints)); 79 hints.ai_family = PF_UNSPEC; 80 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 81 hints.ai_flags = AI_NUMERICHOST; 82 if (getaddrinfo(p, "0", &hints, &res)) 83 goto fail; 84 if (res->ai_next || res->ai_addrlen > sizeof(prefix->a)) 85 goto fail; 86 memcpy(&prefix->a, res->ai_addr, res->ai_addrlen); 87 88 switch (prefix->a.ss_family) { 89 case AF_INET: 90 max = 32; 91 a = (char *)&((struct sockaddr_in *)&prefix->a)->sin_addr; 92 break; 93 case AF_INET6: 94 max = 128; 95 a = (char *)&((struct sockaddr_in6 *)&prefix->a)->sin6_addr; 96 break; 97 default: 98 a = NULL; 99 max = -1; 100 break; 101 } 102 103 if (q) { 104 r = NULL; 105 prefix->l = (int)strtoul(q, &r, 10); 106 if (!*q || *r) 107 goto fail; 108 if (prefix->l < 0 || prefix->l > max) 109 goto fail; 110 } else 111 prefix->l = max; 112 113 if (p) 114 free(p); 115 if (res) 116 freeaddrinfo(res); 117 return 0; 118 119fail: 120 if (p) 121 free(p); 122 if (res) 123 freeaddrinfo(res); 124 return -1; 125} 126 127const char * 128prefix_string(const struct prefix *prefix) 129{ 130 static char buf[NI_MAXHOST + 20]; 131 char hbuf[NI_MAXHOST]; 132 133 if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len, 134 hbuf, sizeof(hbuf), NULL, 0, niflags)) 135 return NULL; 136 snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l); 137 return buf; 138} 139 140int 141prefix_match(const struct prefix *prefix, const struct sockaddr *sa) 142{ 143 struct sockaddr_storage a, b; 144 char *pa, *pb; 145 int off, l; 146 147 if (prefix->a.ss_family != sa->sa_family || 148 prefix->a.ss_len != sa->sa_len) 149 return 0; 150 151 if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b)) 152 return 0; 153 154 switch (prefix->a.ss_family) { 155 case AF_INET: 156 off = offsetof(struct sockaddr_in, sin_addr); 157 break; 158 case AF_INET6: 159 off = offsetof(struct sockaddr_in6, sin6_addr); 160 break; 161 default: 162 if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0) 163 return 0; 164 else 165 return 1; 166 } 167 168 memcpy(&a, &prefix->a, prefix->a.ss_len); 169 memcpy(&b, sa, sa->sa_len); 170 l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0); 171 172 /* overrun check */ 173 if (off + l > a.ss_len) 174 return 0; 175 176 pa = ((char *)&a) + off; 177 pb = ((char *)&b) + off; 178 if (prefix->l % 8) { 179 pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8); 180 pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8); 181 } 182 if (memcmp(pa, pb, l) != 0) 183 return 0; 184 else 185 return 1; 186} 187 188/* 189 * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr] 190 * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1 191 */ 192static struct config * 193config_load1(const char *line) 194{ 195 struct config *conf; 196 char buf[BUFSIZ]; 197 char *p; 198 char *token[4]; 199 int i; 200 201 if (strlen(line) + 1 > sizeof(buf)) 202 return NULL; 203 strlcpy(buf, line, sizeof(buf)); 204 205 p = strchr(buf, '\n'); 206 if (!p) 207 return NULL; 208 *p = '\0'; 209 p = strchr(buf, '#'); 210 if (p) 211 *p = '\0'; 212 if (strlen(buf) == 0) 213 return NULL; 214 215 p = buf; 216 memset(token, 0, sizeof(token)); 217 for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) { 218 token[i] = strtok(p, "\t "); 219 p = NULL; 220 if (token[i] == NULL) 221 break; 222 } 223 /* extra tokens? */ 224 if (strtok(p, "\t ") != NULL) 225 return NULL; 226 /* insufficient tokens */ 227 switch (i) { 228 case 3: 229 case 4: 230 break; 231 default: 232 return NULL; 233 } 234 235 conf = (struct config *)malloc(sizeof(*conf)); 236 if (conf == NULL) 237 return NULL; 238 memset(conf, 0, sizeof(*conf)); 239 240 if (strcasecmp(token[1], "permit") == 0) 241 conf->permit = 1; 242 else if (strcasecmp(token[1], "deny") == 0) 243 conf->permit = 0; 244 else { 245 /* invalid keyword is considered as "deny" */ 246 conf->permit = 0; 247 } 248 249 if (prefix_set(token[0], &conf->match, 1) < 0) 250 goto fail; 251 if (prefix_set(token[2], &conf->dest, 1) < 0) 252 goto fail; 253 if (token[3]) { 254 if (prefix_set(token[3], &conf->src, 0) < 0) 255 goto fail; 256 } 257 258 return conf; 259 260fail: 261 free(conf); 262 return NULL; 263} 264 265int 266config_load(const char *configfile) 267{ 268 FILE *fp; 269 char buf[BUFSIZ]; 270 struct config *conf, *p; 271 struct config sentinel; 272 273 config_list = NULL; 274 275 if (!configfile) 276 configfile = _PATH_PREFIX_CONF; 277 fp = fopen(configfile, "r"); 278 if (fp == NULL) 279 return -1; 280 281 p = &sentinel; 282 sentinel.next = NULL; 283 while (fgets(buf, sizeof(buf), fp) != NULL) { 284 conf = config_load1(buf); 285 if (conf) { 286 p->next = conf; 287 p = p->next; 288 } 289 } 290 config_list = sentinel.next; 291 292 fclose(fp); 293 return 0; 294} 295 296#if 0 297static void 298config_show1(const struct config *conf) 299{ 300 const char *p; 301 302 p = prefix_string(&conf->match); 303 printf("%s", p ? p : "?"); 304 305 if (conf->permit) 306 printf(" permit"); 307 else 308 printf(" deny"); 309 310 p = prefix_string(&conf->dest); 311 printf(" %s", p ? p : "?"); 312 313 printf("\n"); 314} 315 316static void 317config_show() 318{ 319 struct config *conf; 320 321 for (conf = config_list; conf; conf = conf->next) 322 config_show1(conf); 323} 324#endif 325 326const struct config * 327config_match(struct sockaddr *sa1, struct sockaddr *sa2) 328{ 329 static struct config conf; 330 const struct config *p; 331 332 if (sa1->sa_len > sizeof(conf.match.a) || 333 sa2->sa_len > sizeof(conf.dest.a)) 334 return NULL; 335 336 memset(&conf, 0, sizeof(conf)); 337 if (!config_list) { 338 conf.permit = 1; 339 memcpy(&conf.match.a, sa1, sa1->sa_len); 340 memcpy(&conf.dest.a, sa2, sa2->sa_len); 341 return &conf; 342 } 343 344 for (p = config_list; p; p = p->next) 345 if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2)) 346 return p; 347 348 return NULL; 349} 350