sync
[bitrig.git] / usr.sbin / npppd / npppd / npppd_pool.c
blob1deb0aa1f8afb6d734722ae0f535f6aaa0874793
1 /* $OpenBSD: npppd_pool.c,v 1.7 2012/09/18 13:14:08 yasuoka Exp $ */
3 /*-
4 * Copyright (c) 2009 Internet Initiative Japan Inc.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
28 /**@file */
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
33 #include <net/route.h>
34 #include <netinet/ip.h>
35 #include <arpa/inet.h>
36 #include <net/if_dl.h>
37 #include <stdio.h>
38 #include <time.h>
39 #include <event.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <syslog.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <netdb.h>
48 #include "slist.h"
49 #include "debugutil.h"
50 #include "addr_range.h"
51 #include "radish.h"
52 #include "npppd_local.h"
53 #include "npppd_pool.h"
54 #include "npppd_subr.h"
55 #include "net_utils.h"
57 #ifdef NPPPD_POOL_DEBUG
58 #define NPPPD_POOL_DBG(x) npppd_pool_log x
59 #define NPPPD_POOL_ASSERT(cond) \
60 if (!(cond)) { \
61 fprintf(stderr, \
62 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
63 , __func__, __FILE__, __LINE__); \
64 abort(); \
66 #else
67 #define NPPPD_POOL_ASSERT(cond)
68 #define NPPPD_POOL_DBG(x)
69 #endif
70 #define A(v) ((0xff000000 & (v)) >> 24), ((0x00ff0000 & (v)) >> 16), \
71 ((0x0000ff00 & (v)) >> 8), (0x000000ff & (v))
72 #define SA(sin4) ((struct sockaddr *)(sin4))
74 #define SHUFLLE_MARK 0xffffffffL
75 static int npppd_pool_log __P((npppd_pool *, int, const char *, ...)) __printflike(3, 4);
76 static int is_valid_host_address (uint32_t);
77 static int npppd_pool_regist_radish(npppd_pool *, struct in_addr_range *,
78 struct sockaddr_npppd *, int );
81 /***********************************************************************
82 * npppd_pool object management
83 ***********************************************************************/
84 /** Initialize npppd_poll. */
85 int
86 npppd_pool_init(npppd_pool *_this, npppd *base, const char *name)
88 memset(_this, 0, sizeof(npppd_pool));
90 strlcpy(_this->ipcp_name, name, sizeof(_this->ipcp_name));
91 _this->npppd = base;
92 slist_init(&_this->dyna_addrs);
94 _this->initialized = 1;
96 return 0;
99 /** Start to use npppd_pool. */
101 npppd_pool_start(npppd_pool *_this)
103 return 0; /* nothing to do */
106 /** Finalize npppd_poll. */
107 void
108 npppd_pool_uninit(npppd_pool *_this)
110 _this->initialized = 0;
112 slist_fini(&_this->dyna_addrs);
113 if (_this->addrs != NULL)
114 free(_this->addrs);
115 _this->addrs = NULL;
116 _this->addrs_size = 0;
117 _this->npppd = NULL;
120 /** Reload configuration. */
122 npppd_pool_reload(npppd_pool *_this)
124 int i, count, addrs_size;
125 struct sockaddr_npppd *addrs;
126 struct in_addr_range *pool, *dyna_pool, *range;
127 char buf0[BUFSIZ], buf1[BUFSIZ];
128 struct ipcpconf *ipcp;
130 addrs = NULL;
131 pool = NULL;
132 dyna_pool = NULL;
133 buf0[0] = '\0';
135 TAILQ_FOREACH(ipcp, &_this->npppd->conf.ipcpconfs, entry) {
136 if (strcmp(ipcp->name, _this->ipcp_name) == 0) {
137 dyna_pool = ipcp->dynamic_pool;
138 pool = ipcp->static_pool;
142 addrs_size = 0;
143 for (range = dyna_pool; range != NULL; range = range->next)
144 addrs_size++;
145 for (range = pool; range != NULL; range = range->next)
146 addrs_size++;
148 if ((addrs = calloc(addrs_size + 1, sizeof(struct sockaddr_npppd)))
149 == NULL) {
150 /* addr_size + 1 because of avoiding calloc(0). */
151 npppd_pool_log(_this, LOG_WARNING,
152 "calloc() failed in %s: %m", __func__);
153 goto fail;
156 /* Register dynamic pool address with RADISH. */
157 count = 0;
158 for (i = 0, range = dyna_pool; range != NULL; range = range->next, i++){
159 if (npppd_pool_regist_radish(_this, range, &addrs[count], 1))
160 goto fail;
161 if (count == 0)
162 strlcat(buf0, "dyn_pool=[", sizeof(buf0));
163 else
164 strlcat(buf0, ",", sizeof(buf0));
165 snprintf(buf1, sizeof(buf1), "%d.%d.%d.%d/%d",
166 A(range->addr), netmask2prefixlen(range->mask));
167 strlcat(buf0, buf1, sizeof(buf0));
168 count++;
170 if (i > 0)
171 strlcat(buf0, "] ", sizeof(buf0));
173 /* Register static pool address with RADISH. */
174 for (i = 0, range = pool; range != NULL; range = range->next, i++) {
175 if (npppd_pool_regist_radish(_this, range, &addrs[count], 0))
176 goto fail;
177 if (i == 0)
178 strlcat(buf0, "pool=[", sizeof(buf0));
179 else
180 strlcat(buf0, ",", sizeof(buf0));
181 snprintf(buf1, sizeof(buf1), "%d.%d.%d.%d/%d",
182 A(range->addr), netmask2prefixlen(range->mask));
183 strlcat(buf0, buf1, sizeof(buf0));
184 count++;
186 if (i > 0)
187 strlcat(buf0, "]", sizeof(buf0));
189 npppd_pool_log(_this, LOG_INFO, "%s", buf0);
191 count = 0;
192 slist_add(&_this->dyna_addrs, (void *)SHUFLLE_MARK);
193 for (range = dyna_pool; range != NULL; range = range->next) {
194 if (count >= NPPPD_MAX_POOLED_ADDRS)
195 break;
196 for (i = 0; i <= ~(range->mask); i++) {
197 if (!is_valid_host_address(range->addr + i))
198 continue;
199 if (count >= NPPPD_MAX_POOLED_ADDRS)
200 break;
201 slist_add(&_this->dyna_addrs,
202 (void *)(uintptr_t)(range->addr + i));
203 count++;
206 if (_this->addrs != NULL)
207 free(_this->addrs);
208 _this->addrs = addrs;
209 _this->addrs_size = addrs_size;
211 return 0;
212 fail:
213 if (addrs != NULL)
214 free(addrs);
216 return 1;
219 static int
220 npppd_pool_regist_radish(npppd_pool *_this, struct in_addr_range *range,
221 struct sockaddr_npppd *snp, int is_dynamic)
223 int rval;
224 struct sockaddr_in sin4a, sin4b;
225 struct sockaddr_npppd *snp0;
226 npppd_pool *npool0;
228 memset(&sin4a, 0, sizeof(sin4a));
229 memset(&sin4b, 0, sizeof(sin4b));
230 sin4a.sin_len = sin4b.sin_len = sizeof(sin4a);
231 sin4a.sin_family = sin4b.sin_family = AF_INET;
232 sin4a.sin_addr.s_addr = htonl(range->addr);
233 sin4b.sin_addr.s_addr = htonl(range->mask);
235 snp->snp_len = sizeof(struct sockaddr_npppd);
236 snp->snp_family = AF_INET;
237 snp->snp_addr.s_addr = htonl(range->addr);
238 snp->snp_mask.s_addr = htonl(range->mask);
239 snp->snp_data_ptr = _this;
240 if (is_dynamic)
241 snp->snp_type = SNP_DYN_POOL;
242 else
243 snp->snp_type = SNP_POOL;
245 if ((snp0 = rd_lookup(SA(&sin4a), SA(&sin4b),
246 _this->npppd->rd)) != NULL) {
248 * Immediately after the radish tree is initialized,
249 * assuming that it has only POOL entry.
251 NPPPD_POOL_ASSERT(snp0->snp_type != SNP_PPP);
252 npool0 = snp0->snp_data_ptr;
254 if (!is_dynamic && npool0 == _this)
255 /* Already registered as dynamic pool address. */
256 return 0;
258 npppd_pool_log(_this, LOG_WARNING,
259 "%d.%d.%d.%d/%d is already defined as '%s'(%s)",
260 A(range->addr), netmask2prefixlen(range->mask),
261 npool0->ipcp_name, (snp0->snp_type == SNP_POOL)
262 ? "static" : "dynamic");
263 goto fail;
265 if ((rval = rd_insert(SA(&sin4a), SA(&sin4b), _this->npppd->rd,
266 snp)) != 0) {
267 errno = rval;
268 npppd_pool_log(_this, LOG_WARNING,
269 "rd_insert(%d.%d.%d.%d/%d) failed: %m",
270 A(range->addr), netmask2prefixlen(range->mask));
271 goto fail;
274 return 0;
275 fail:
276 return 1;
280 /***********************************************************************
281 * API
282 ***********************************************************************/
283 /** Assign dynamic pool address. */
284 uint32_t
285 npppd_pool_get_dynamic(npppd_pool *_this, npppd_ppp *ppp)
287 int shuffle_cnt;
288 uintptr_t result = 0;
289 struct sockaddr_npppd *snp;
290 npppd_ppp *ppp0;
292 shuffle_cnt = 0;
293 slist_itr_first(&_this->dyna_addrs);
294 while (slist_length(&_this->dyna_addrs) > 1 &&
295 slist_itr_has_next(&_this->dyna_addrs)) {
296 result = (uintptr_t)slist_itr_next(&_this->dyna_addrs);
297 if (result == 0)
298 break;
299 /* shuffle */
300 if ((uint32_t)result == SHUFLLE_MARK) {
302 * When the free list is empty, SHUFLLE_MARK is
303 * retrieved twice sequentially. This means there is
304 * no address to use.
306 if (shuffle_cnt++ > 0) {
307 result = 0;
308 break;
310 NPPPD_POOL_DBG((_this, LOG_DEBUG, "shuffle"));
311 slist_itr_remove(&_this->dyna_addrs);
312 slist_shuffle(&_this->dyna_addrs);
313 slist_add(&_this->dyna_addrs, (void *)result);
314 slist_itr_first(&_this->dyna_addrs);
315 continue;
317 slist_itr_remove(&_this->dyna_addrs);
319 switch (npppd_pool_get_assignability(_this, (uint32_t)result,
320 0xffffffffL, &snp)) {
321 case ADDRESS_OK:
322 /* only succeed here */
323 return (uint32_t)result;
324 default:
326 * Used as a interface address
328 continue;
329 case ADDRESS_BUSY:
331 * Used by the previous configuration.
333 NPPPD_POOL_ASSERT(snp != NULL);
334 NPPPD_POOL_ASSERT(snp->snp_type == SNP_PPP);
335 ppp0 = snp->snp_data_ptr;
336 ppp0->assigned_pool = _this;
337 ppp0->assign_dynapool = 1; /* need to return */
338 continue;
340 break;
342 return (uint32_t)0;
345 inline static int
346 npppd_is_ifcace_ip4addr(npppd *_this, uint32_t ip4addr)
348 int i;
350 for (i = 0; i < countof(_this->iface); i++) {
351 if (npppd_iface_ip_is_ready(&_this->iface[i]) &&
352 _this->iface[i].ip4addr.s_addr == ip4addr)
353 return 1;
356 return 0;
359 /** Assign IP address. */
361 npppd_pool_assign_ip(npppd_pool *_this, npppd_ppp *ppp)
363 int rval;
364 uint32_t ip4;
365 void *rtent;
366 struct sockaddr_in addr = {
367 .sin_family = AF_INET,
368 .sin_len = sizeof(struct sockaddr_in)
369 }, mask = {
370 .sin_family = AF_INET,
371 .sin_len = sizeof(struct sockaddr_in),
373 struct sockaddr_npppd *snp;
375 ip4 = ntohl(ppp->ppp_framed_ip_address.s_addr);
377 /* If the address contains dynamic pool address list, delete it. */
378 slist_itr_first(&_this->dyna_addrs);
379 while (slist_itr_has_next(&_this->dyna_addrs)) {
380 if ((uintptr_t)slist_itr_next(&_this->dyna_addrs) != ip4)
381 continue;
382 slist_itr_remove(&_this->dyna_addrs);
383 break;
386 addr.sin_addr = ppp->ppp_framed_ip_address;
387 mask.sin_addr = ppp->ppp_framed_ip_netmask;
388 addr.sin_addr.s_addr &= mask.sin_addr.s_addr;
390 if (rd_delete(SA(&addr), SA(&mask), _this->npppd->rd, &rtent) == 0) {
391 snp = rtent;
392 /* It has duplicate address entry. change from pool to PPP. */
393 NPPPD_POOL_ASSERT(snp != NULL);
394 NPPPD_POOL_ASSERT(snp->snp_type != SNP_PPP);
395 ppp->snp.snp_next = snp;
396 NPPPD_POOL_DBG((_this, DEBUG_LEVEL_2,
397 "pool %s/32 => %s(ppp=%d)",
398 inet_ntoa(ppp->ppp_framed_ip_address), ppp->username,
399 ppp->id));
401 NPPPD_POOL_DBG((_this, LOG_DEBUG, "rd_insert(%s) %s",
402 inet_ntoa(addr.sin_addr), ppp->username));
403 if ((rval = rd_insert((struct sockaddr *)&addr,
404 (struct sockaddr *)&mask, _this->npppd->rd, &ppp->snp)) != 0) {
405 errno = rval;
406 log_printf(LOG_INFO, "rd_insert(%s) failed: %m",
407 inet_ntoa(ppp->ppp_framed_ip_address));
408 return 1;
411 return 0;
414 /** Release IP address. */
415 void
416 npppd_pool_release_ip(npppd_pool *_this, npppd_ppp *ppp)
418 void *item;
419 int rval;
420 struct sockaddr_npppd *snp;
421 struct sockaddr_in addr = {
422 .sin_family = AF_INET,
423 .sin_len = sizeof(struct sockaddr_in)
424 }, mask = {
425 .sin_family = AF_INET,
426 .sin_len = sizeof(struct sockaddr_in),
430 * `_this' may be NULL. It was gone because of a configuration change.
432 if (!ppp_ip_assigned(ppp))
433 return;
435 addr.sin_addr = ppp->ppp_framed_ip_address;
436 mask.sin_addr = ppp->ppp_framed_ip_netmask;
437 addr.sin_addr.s_addr &= mask.sin_addr.s_addr;
439 if ((rval = rd_delete((struct sockaddr *)&addr,
440 (struct sockaddr *)&mask, ppp->pppd->rd, &item)) != 0) {
441 errno = rval;
442 log_printf(LOG_INFO, "Unexpected error: "
443 "rd_delete(%s) failed: %m",
444 inet_ntoa(ppp->ppp_framed_ip_address));
446 snp = item;
448 if (_this != NULL && ppp->assign_dynapool != 0) {
449 NPPPD_POOL_ASSERT(_this == ppp->assigned_pool);
450 /* return to dynamic address pool list */
451 slist_add(&((npppd_pool *)ppp->assigned_pool)->dyna_addrs,
452 (void *)(uintptr_t)ntohl(
453 ppp->ppp_framed_ip_address.s_addr));
456 if (snp != NULL && snp->snp_next != NULL) {
458 * The radish entry is registered as a list. Insert the next
459 * of the list to the radish tree.
461 if (rd_insert(SA(&addr), SA(&mask), ppp->pppd->rd,
462 snp->snp_next) != 0) {
463 log_printf(LOG_INFO, "Unexpected error: "
464 "rd_insert(%s) failed: %m",
465 inet_ntoa(ppp->ppp_framed_ip_address));
467 NPPPD_POOL_DBG((_this, DEBUG_LEVEL_2,
468 "pool %s/%d <= %s(ppp=%d)",
469 inet_ntoa(ppp->ppp_framed_ip_address),
470 netmask2prefixlen(ntohl(ppp->ppp_framed_ip_netmask.s_addr)),
471 ppp->username, ppp->id));
472 snp->snp_next = NULL;
477 * Check if specified address is assignable.
478 * @return {@link ::#ADDRESS_OK} or {@link ::#ADDRESS_RESERVED} or
479 * {@link ::#ADDRESS_BUSY} or {@link ::#ADDRESS_INVALID} or
480 * {@link ::#ADDRESS_OUT_OF_POOL}
483 npppd_pool_get_assignability(npppd_pool *_this, uint32_t ip4addr,
484 uint32_t ip4mask, struct sockaddr_npppd **psnp)
486 struct radish *radish;
487 struct sockaddr_in sin4;
488 struct sockaddr_npppd *snp;
490 NPPPD_POOL_ASSERT(ip4mask != 0);
491 NPPPD_POOL_DBG((_this, LOG_DEBUG, "%s(%08x,%08x)", __func__, ip4addr,
492 ip4mask));
494 if (netmask2prefixlen(htonl(ip4mask)) == 32) {
495 if (!is_valid_host_address(ip4addr))
496 return ADDRESS_INVALID;
499 memset(&sin4, 0, sizeof(sin4));
501 sin4.sin_len = sizeof(sin4);
502 sin4.sin_family = AF_INET;
503 sin4.sin_addr.s_addr = htonl(ip4addr);
505 if (npppd_is_ifcace_ip4addr(_this->npppd, sin4.sin_addr.s_addr))
506 return ADDRESS_RESERVED;
507 /* Not to assign interface address */
509 if (rd_match(SA(&sin4), _this->npppd->rd, &radish)) {
510 do {
511 snp = radish->rd_rtent;
512 if (snp->snp_type == SNP_POOL ||
513 snp->snp_type == SNP_DYN_POOL) {
514 if (psnp != NULL)
515 *psnp = snp;
516 if (snp->snp_data_ptr == _this)
517 return ADDRESS_OK;
518 else
519 return ADDRESS_RESERVED;
521 if (snp->snp_type == SNP_PPP) {
522 if (psnp != NULL)
523 *psnp = snp;
524 return ADDRESS_BUSY;
526 } while (rd_match_next(SA(&sin4), _this->npppd->rd, &radish,
527 radish));
530 return ADDRESS_OUT_OF_POOL;
532 /***********************************************************************
533 * miscellaneous functions
534 ***********************************************************************/
536 * Check if valid host address.
537 * <pre>
538 * There are some issues that it uses host address as broadcast address
539 * in natural mask, so it is not correct.
540 * The issue is as follows:
541 * (1) BSDs treat the following packet as it is not forwarded and
542 * is received as the packet to myself.
543 * (2) The issue that Windows can't use L2TP/IPsec when Windows is assigned
544 * IP address .255.</pre>
546 static int
547 is_valid_host_address(uint32_t addr)
549 if (IN_CLASSA(addr))
550 return ((IN_CLASSA_HOST & addr) == 0 ||
551 (IN_CLASSA_HOST & addr) == IN_CLASSA_HOST)? 0 : 1;
552 if (IN_CLASSB(addr))
553 return ((IN_CLASSB_HOST & addr) == 0 ||
554 (IN_CLASSB_HOST & addr) == IN_CLASSB_HOST)? 0 : 1;
555 if (IN_CLASSC(addr))
556 return ((IN_CLASSC_HOST & addr) == 0 ||
557 (IN_CLASSC_HOST & addr) == IN_CLASSC_HOST)? 0 : 1;
559 return 0;
562 /** Record log that begins the label based this instance. */
563 static int
564 npppd_pool_log(npppd_pool *_this, int prio, const char *fmt, ...)
566 int status;
567 char logbuf[BUFSIZ];
568 va_list ap;
571 * npppd_pool_release_ip is called as _this == NULL,
572 * so it can't NPPPD_POOL_ASSERT(_this != NULL).
574 va_start(ap, fmt);
575 snprintf(logbuf, sizeof(logbuf), "ipcp=%s pool %s",
576 (_this == NULL)? "null" : _this->ipcp_name, fmt);
577 status = vlog_printf(prio, logbuf, ap);
578 va_end(ap);
580 return status;