xref: /openbsd/usr.sbin/npppd/npppd/npppd_pool.c (revision c50e9b22)
1 /*	$OpenBSD: npppd_pool.c,v 1.11 2022/08/29 02:58:13 jsg Exp $ */
2 
3 /*-
4  * Copyright (c) 2009 Internet Initiative Japan Inc.
5  * All rights reserved.
6  *
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.
15  *
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.
27  */
28 /**@file */
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <net/route.h>
33 #include <netinet/ip.h>
34 #include <arpa/inet.h>
35 #include <net/if_dl.h>
36 #include <stdio.h>
37 #include <time.h>
38 #include <event.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <syslog.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <netdb.h>
46 
47 #include "slist.h"
48 #include "debugutil.h"
49 #include "addr_range.h"
50 #include "radish.h"
51 #include "npppd_local.h"
52 #include "npppd_pool.h"
53 #include "npppd_subr.h"
54 #include "net_utils.h"
55 
56 #ifdef	NPPPD_POOL_DEBUG
57 #define	NPPPD_POOL_DBG(x)	npppd_pool_log x
58 #define	NPPPD_POOL_ASSERT(cond)					\
59 	if (!(cond)) {						\
60 	    fprintf(stderr,					\
61 		"\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
62 		, __func__, __FILE__, __LINE__);		\
63 	    abort(); 						\
64 	}
65 #else
66 #define	NPPPD_POOL_ASSERT(cond)
67 #define	NPPPD_POOL_DBG(x)
68 #endif
69 #define	A(v) ((0xff000000 & (v)) >> 24), ((0x00ff0000 & (v)) >> 16),	\
70 	    ((0x0000ff00 & (v)) >> 8), (0x000000ff & (v))
71 #define	SA(sin4)	((struct sockaddr *)(sin4))
72 
73 #define SHUFLLE_MARK 0xffffffffL
74 static int  npppd_pool_log(npppd_pool *, int, const char *, ...) __printflike(3, 4);
75 static int  is_valid_host_address (uint32_t);
76 static int  npppd_pool_regist_radish(npppd_pool *, struct in_addr_range *,
77     struct sockaddr_npppd *, int );
78 
79 
80 /***********************************************************************
81  * npppd_pool object management
82  ***********************************************************************/
83 /** Initialize npppd_poll. */
84 int
npppd_pool_init(npppd_pool * _this,npppd * base,const char * name)85 npppd_pool_init(npppd_pool *_this, npppd *base, const char *name)
86 {
87 	memset(_this, 0, sizeof(npppd_pool));
88 
89 	strlcpy(_this->ipcp_name, name, sizeof(_this->ipcp_name));
90 	_this->npppd = base;
91 	slist_init(&_this->dyna_addrs);
92 
93 	_this->initialized = 1;
94 
95 	return 0;
96 }
97 
98 /** Start to use npppd_pool. */
99 int
npppd_pool_start(npppd_pool * _this)100 npppd_pool_start(npppd_pool *_this)
101 {
102 	return 0;	/* nothing to do */
103 }
104 
105 /** Finalize npppd_poll. */
106 void
npppd_pool_uninit(npppd_pool * _this)107 npppd_pool_uninit(npppd_pool *_this)
108 {
109 	_this->initialized = 0;
110 
111 	slist_fini(&_this->dyna_addrs);
112 	free(_this->addrs);
113 	_this->addrs = NULL;
114 	_this->addrs_size = 0;
115 	_this->npppd = NULL;
116 }
117 
118 /** Reload configuration. */
119 int
npppd_pool_reload(npppd_pool * _this)120 npppd_pool_reload(npppd_pool *_this)
121 {
122 	int i, count, addrs_size;
123 	struct sockaddr_npppd *addrs;
124 	struct in_addr_range *pool, *dyna_pool, *range;
125 	char buf0[BUFSIZ], buf1[BUFSIZ];
126 	struct ipcpconf *ipcp;
127 
128 	addrs = NULL;
129 	pool = NULL;
130 	dyna_pool = NULL;
131 	buf0[0] = '\0';
132 
133 	TAILQ_FOREACH(ipcp, &_this->npppd->conf.ipcpconfs, entry) {
134 		if (strcmp(ipcp->name, _this->ipcp_name) == 0) {
135 			dyna_pool = ipcp->dynamic_pool;
136 			pool = ipcp->static_pool;
137 		}
138 	}
139 
140 	addrs_size = 0;
141 	for (range = dyna_pool; range != NULL; range = range->next)
142 		addrs_size++;
143 	for (range = pool; range != NULL; range = range->next)
144 		addrs_size++;
145 
146 	if ((addrs = calloc(addrs_size + 1, sizeof(struct sockaddr_npppd)))
147 	    == NULL) {
148 		/* addr_size + 1 because of avoiding calloc(0). */
149 		npppd_pool_log(_this, LOG_WARNING,
150 		    "calloc() failed in %s: %m", __func__);
151 		goto fail;
152 	}
153 
154 	/* Register dynamic pool address with RADISH. */
155 	count = 0;
156 	for (i = 0, range = dyna_pool; range != NULL; range = range->next, i++){
157 		if (npppd_pool_regist_radish(_this, range, &addrs[count], 1))
158 			goto fail;
159 		if (count == 0)
160 			strlcat(buf0, "dyn_pool=[", sizeof(buf0));
161 		else
162 			strlcat(buf0, ",", sizeof(buf0));
163 		snprintf(buf1, sizeof(buf1), "%d.%d.%d.%d/%d",
164 		    A(range->addr), netmask2prefixlen(range->mask));
165 		strlcat(buf0, buf1, sizeof(buf0));
166 		count++;
167 	}
168 	if (i > 0)
169 		strlcat(buf0, "] ", sizeof(buf0));
170 
171 	/* Register static pool address with RADISH. */
172 	for (i = 0, range = pool; range != NULL; range = range->next, i++) {
173 		if (npppd_pool_regist_radish(_this, range, &addrs[count], 0))
174 			goto fail;
175 		if (i == 0)
176 			strlcat(buf0, "pool=[", sizeof(buf0));
177 		else
178 			strlcat(buf0, ",", sizeof(buf0));
179 		snprintf(buf1, sizeof(buf1), "%d.%d.%d.%d/%d",
180 		    A(range->addr), netmask2prefixlen(range->mask));
181 		strlcat(buf0, buf1, sizeof(buf0));
182 		count++;
183 	}
184 	if (i > 0)
185 		strlcat(buf0, "]", sizeof(buf0));
186 
187 	npppd_pool_log(_this, LOG_INFO, "%s", buf0);
188 
189 	count = 0;
190 	slist_add(&_this->dyna_addrs, (void *)SHUFLLE_MARK);
191 	for (range = dyna_pool; range != NULL; range = range->next) {
192 		if (count >= NPPPD_MAX_POOLED_ADDRS)
193 			break;
194 		for (i = 0; i <= ~(range->mask); i++) {
195 			if (!is_valid_host_address(range->addr + i))
196 				continue;
197 			if (count >= NPPPD_MAX_POOLED_ADDRS)
198 				break;
199 			slist_add(&_this->dyna_addrs,
200 			    (void *)(uintptr_t)(range->addr + i));
201 			count++;
202 		}
203 	}
204 	free(_this->addrs);
205 	_this->addrs = addrs;
206 	_this->addrs_size = addrs_size;
207 
208 	return 0;
209 fail:
210 	free(addrs);
211 
212 	return 1;
213 }
214 
215 static int
npppd_pool_regist_radish(npppd_pool * _this,struct in_addr_range * range,struct sockaddr_npppd * snp,int is_dynamic)216 npppd_pool_regist_radish(npppd_pool *_this, struct in_addr_range *range,
217     struct sockaddr_npppd *snp, int is_dynamic)
218 {
219 	int rval;
220 	struct sockaddr_in sin4a, sin4b;
221 	struct sockaddr_npppd *snp0;
222 	npppd_pool *npool0;
223 
224 	memset(&sin4a, 0, sizeof(sin4a));
225 	memset(&sin4b, 0, sizeof(sin4b));
226 	sin4a.sin_len = sin4b.sin_len = sizeof(sin4a);
227 	sin4a.sin_family = sin4b.sin_family = AF_INET;
228 	sin4a.sin_addr.s_addr = htonl(range->addr);
229 	sin4b.sin_addr.s_addr = htonl(range->mask);
230 
231 	snp->snp_len = sizeof(struct sockaddr_npppd);
232 	snp->snp_family = AF_INET;
233 	snp->snp_addr.s_addr = htonl(range->addr);
234 	snp->snp_mask.s_addr = htonl(range->mask);
235 	snp->snp_data_ptr = _this;
236 	if (is_dynamic)
237 		snp->snp_type = SNP_DYN_POOL;
238 	else
239 		snp->snp_type = SNP_POOL;
240 
241 	if ((snp0 = rd_lookup(SA(&sin4a), SA(&sin4b),
242 	    _this->npppd->rd)) != NULL) {
243 		/*
244 		 * Immediately after the radish tree is initialized,
245 		 * assuming that it has only POOL entry.
246 		 */
247 		NPPPD_POOL_ASSERT(snp0->snp_type != SNP_PPP);
248 		npool0 = snp0->snp_data_ptr;
249 
250 		if (!is_dynamic && npool0 == _this)
251 			/* Already registered as dynamic pool address. */
252 			return 0;
253 
254 		npppd_pool_log(_this, LOG_WARNING,
255 		    "%d.%d.%d.%d/%d is already defined as '%s'(%s)",
256 		    A(range->addr), netmask2prefixlen(range->mask),
257 		    npool0->ipcp_name, (snp0->snp_type == SNP_POOL)
258 			? "static" : "dynamic");
259 		goto fail;
260 	}
261 	if ((rval = rd_insert(SA(&sin4a), SA(&sin4b), _this->npppd->rd,
262 	    snp)) != 0) {
263 		errno = rval;
264 		npppd_pool_log(_this, LOG_WARNING,
265 		    "rd_insert(%d.%d.%d.%d/%d) failed: %m",
266 		    A(range->addr), netmask2prefixlen(range->mask));
267 		goto fail;
268 	}
269 
270 	return 0;
271 fail:
272 	return 1;
273 
274 }
275 
276 /***********************************************************************
277  * API
278  ***********************************************************************/
279 /** Assign dynamic pool address. */
280 uint32_t
npppd_pool_get_dynamic(npppd_pool * _this,npppd_ppp * ppp)281 npppd_pool_get_dynamic(npppd_pool *_this, npppd_ppp *ppp)
282 {
283 	int shuffle_cnt;
284 	uintptr_t result = 0;
285 	struct sockaddr_npppd *snp;
286 	npppd_ppp *ppp0;
287 
288 	shuffle_cnt = 0;
289 	slist_itr_first(&_this->dyna_addrs);
290 	while (slist_length(&_this->dyna_addrs) > 1 &&
291 	    slist_itr_has_next(&_this->dyna_addrs)) {
292 		result = (uintptr_t)slist_itr_next(&_this->dyna_addrs);
293 		if (result == 0)
294 			break;
295 		/* shuffle */
296 		if ((uint32_t)result == SHUFLLE_MARK) {
297 			/*
298 			 * When the free list is empty, SHUFLLE_MARK is
299 			 * retrieved twice sequentially.  This means there is
300 			 * no address to use.
301 			 */
302 			if (shuffle_cnt++ > 0) {
303 				result = 0;
304 				break;
305 			}
306 			NPPPD_POOL_DBG((_this, LOG_DEBUG, "shuffle"));
307 			slist_itr_remove(&_this->dyna_addrs);
308 			slist_shuffle(&_this->dyna_addrs);
309 			slist_add(&_this->dyna_addrs, (void *)result);
310 			slist_itr_first(&_this->dyna_addrs);
311 			continue;
312 		}
313 		slist_itr_remove(&_this->dyna_addrs);
314 
315 		switch (npppd_pool_get_assignability(_this, (uint32_t)result,
316 		    0xffffffffL, &snp)) {
317 		case ADDRESS_OK:
318 			/* only succeed here */
319 			return (uint32_t)result;
320 		default:
321 			/*
322 			 * Used as a interface address
323 			 */
324 			continue;
325 		case ADDRESS_BUSY:
326 			/*
327 			 * Used by the previous configuration.
328 			 */
329 			NPPPD_POOL_ASSERT(snp != NULL);
330 			NPPPD_POOL_ASSERT(snp->snp_type == SNP_PPP);
331 			ppp0 = snp->snp_data_ptr;
332 			ppp0->assigned_pool = _this;
333 			ppp0->assign_dynapool = 1;	/* need to return */
334 			continue;
335 		}
336 		break;
337 	}
338 	return (uint32_t)0;
339 }
340 
341 static inline int
npppd_is_ifcace_ip4addr(npppd * _this,uint32_t ip4addr)342 npppd_is_ifcace_ip4addr(npppd *_this, uint32_t ip4addr)
343 {
344 	int i;
345 
346 	for (i = 0; i < countof(_this->iface); i++) {
347 		if (npppd_iface_ip_is_ready(&_this->iface[i]) &&
348 		    _this->iface[i].ip4addr.s_addr == ip4addr)
349 			return 1;
350 	}
351 
352 	return 0;
353 }
354 
355 /** Assign IP address. */
356 int
npppd_pool_assign_ip(npppd_pool * _this,npppd_ppp * ppp)357 npppd_pool_assign_ip(npppd_pool *_this, npppd_ppp *ppp)
358 {
359 	int rval;
360 	uint32_t ip4;
361 	void *rtent;
362 	struct sockaddr_in addr = {
363 		.sin_family = AF_INET,
364 		.sin_len = sizeof(struct sockaddr_in)
365 	}, mask = {
366 		.sin_family = AF_INET,
367 		.sin_len = sizeof(struct sockaddr_in),
368 	};
369 	struct sockaddr_npppd *snp;
370 
371 	ip4 = ntohl(ppp->ppp_framed_ip_address.s_addr);
372 
373 	/* If the address contains dynamic pool address list, delete it. */
374 	slist_itr_first(&_this->dyna_addrs);
375 	while (slist_itr_has_next(&_this->dyna_addrs)) {
376 		if ((uintptr_t)slist_itr_next(&_this->dyna_addrs) != ip4)
377 			continue;
378 		slist_itr_remove(&_this->dyna_addrs);
379 		break;
380 	}
381 
382 	addr.sin_addr = ppp->ppp_framed_ip_address;
383 	mask.sin_addr = ppp->ppp_framed_ip_netmask;
384 	addr.sin_addr.s_addr &= mask.sin_addr.s_addr;
385 
386 	if (rd_delete(SA(&addr), SA(&mask), _this->npppd->rd, &rtent) == 0) {
387 		snp = rtent;
388 		/* It has duplicate address entry. change from pool to PPP. */
389 		NPPPD_POOL_ASSERT(snp != NULL);
390 		NPPPD_POOL_ASSERT(snp->snp_type != SNP_PPP);
391 		ppp->snp.snp_next = snp;
392 		NPPPD_POOL_DBG((_this, DEBUG_LEVEL_2,
393 		    "pool %s/32 => %s(ppp=%d)",
394 		    inet_ntoa(ppp->ppp_framed_ip_address), ppp->username,
395 		    ppp->id));
396 	}
397 	NPPPD_POOL_DBG((_this, LOG_DEBUG, "rd_insert(%s) %s",
398 	    inet_ntoa(addr.sin_addr), ppp->username));
399 	if ((rval = rd_insert((struct sockaddr *)&addr,
400 	    (struct sockaddr *)&mask, _this->npppd->rd, &ppp->snp)) != 0) {
401 		errno = rval;
402 		log_printf(LOG_INFO, "rd_insert(%s) failed: %m",
403 		    inet_ntoa(ppp->ppp_framed_ip_address));
404 		return 1;
405 	}
406 
407 	return 0;
408 }
409 
410 /** Release IP address. */
411 void
npppd_pool_release_ip(npppd_pool * _this,npppd_ppp * ppp)412 npppd_pool_release_ip(npppd_pool *_this, npppd_ppp *ppp)
413 {
414 	void *item;
415 	int rval;
416 	struct sockaddr_npppd *snp;
417 	struct sockaddr_in addr = {
418 		.sin_family = AF_INET,
419 		.sin_len = sizeof(struct sockaddr_in)
420 	}, mask = {
421 		.sin_family = AF_INET,
422 		.sin_len = sizeof(struct sockaddr_in),
423 	};
424 
425 	/*
426 	 * `_this' may be NULL.  It was gone because of a configuration change.
427 	 */
428 	if (!ppp_ip_assigned(ppp))
429 		return;
430 
431 	addr.sin_addr = ppp->ppp_framed_ip_address;
432 	mask.sin_addr = ppp->ppp_framed_ip_netmask;
433 	addr.sin_addr.s_addr &= mask.sin_addr.s_addr;
434 
435 	if ((rval = rd_delete((struct sockaddr *)&addr,
436 	    (struct sockaddr *)&mask, ppp->pppd->rd, &item)) != 0) {
437 		errno = rval;
438 		log_printf(LOG_INFO, "Unexpected error: "
439 		    "rd_delete(%s) failed: %m",
440 		    inet_ntoa(ppp->ppp_framed_ip_address));
441 	}
442 	snp = item;
443 
444 	if (_this != NULL && ppp->assign_dynapool != 0) {
445 		NPPPD_POOL_ASSERT(_this == ppp->assigned_pool);
446 		/* return to dynamic address pool list */
447 		slist_add(&((npppd_pool *)ppp->assigned_pool)->dyna_addrs,
448 		    (void *)(uintptr_t)ntohl(
449 			    ppp->ppp_framed_ip_address.s_addr));
450 	}
451 
452 	if (snp != NULL && snp->snp_next != NULL) {
453 		/*
454 		 * The radish entry is registered as a list.  Insert the next
455 		 * of the list to the radish tree.
456 		 */
457 		if (rd_insert(SA(&addr), SA(&mask), ppp->pppd->rd,
458 		    snp->snp_next) != 0) {
459 			log_printf(LOG_INFO, "Unexpected error: "
460 			    "rd_insert(%s) failed: %m",
461 			    inet_ntoa(ppp->ppp_framed_ip_address));
462 		}
463 		NPPPD_POOL_DBG((_this, DEBUG_LEVEL_2,
464 		    "pool %s/%d <= %s(ppp=%d)",
465 		    inet_ntoa(ppp->ppp_framed_ip_address),
466 		    netmask2prefixlen(ntohl(ppp->ppp_framed_ip_netmask.s_addr)),
467 		    ppp->username, ppp->id));
468 		snp->snp_next = NULL;
469 	}
470 }
471 
472 /**
473  * Check if specified address is assignable.
474  * @return {@link ::#ADDRESS_OK} or {@link ::#ADDRESS_RESERVED} or
475  * {@link ::#ADDRESS_BUSY} or {@link ::#ADDRESS_INVALID}  or
476  * {@link ::#ADDRESS_OUT_OF_POOL}
477  */
478 int
npppd_pool_get_assignability(npppd_pool * _this,uint32_t ip4addr,uint32_t ip4mask,struct sockaddr_npppd ** psnp)479 npppd_pool_get_assignability(npppd_pool *_this, uint32_t ip4addr,
480     uint32_t ip4mask, struct sockaddr_npppd **psnp)
481 {
482 	struct radish *radish;
483 	struct sockaddr_in sin4;
484 	struct sockaddr_npppd *snp;
485 
486 	NPPPD_POOL_ASSERT(ip4mask != 0);
487 	NPPPD_POOL_DBG((_this, LOG_DEBUG, "%s(%08x,%08x)", __func__, ip4addr,
488 	    ip4mask));
489 
490 	if (netmask2prefixlen(htonl(ip4mask)) == 32) {
491 		if (!is_valid_host_address(ip4addr))
492 			return ADDRESS_INVALID;
493 	}
494 
495 	memset(&sin4, 0, sizeof(sin4));
496 
497 	sin4.sin_len = sizeof(sin4);
498 	sin4.sin_family = AF_INET;
499 	sin4.sin_addr.s_addr = htonl(ip4addr);
500 
501 	if (npppd_is_ifcace_ip4addr(_this->npppd, sin4.sin_addr.s_addr))
502 		return ADDRESS_RESERVED;
503 		/* Not to assign interface address */
504 
505 	if (rd_match(SA(&sin4), _this->npppd->rd, &radish)) {
506 		do {
507 			snp = radish->rd_rtent;
508 			if (snp->snp_type == SNP_POOL ||
509 			    snp->snp_type == SNP_DYN_POOL) {
510 				if (psnp != NULL)
511 					*psnp = snp;
512 				if (snp->snp_data_ptr == _this)
513 					return  ADDRESS_OK;
514 				else
515 					return ADDRESS_RESERVED;
516 			}
517 			if (snp->snp_type == SNP_PPP) {
518 				if (psnp != NULL)
519 					*psnp = snp;
520 				return ADDRESS_BUSY;
521 			}
522 		} while (rd_match_next(SA(&sin4), _this->npppd->rd, &radish,
523 		    radish));
524 	}
525 
526 	return ADDRESS_OUT_OF_POOL;
527 }
528 /***********************************************************************
529  * miscellaneous functions
530  ***********************************************************************/
531 /**
532  * Check if valid host address.
533  * <pre>
534  * There are some issues that it uses host address as broadcast address
535  * in natural mask, so it is not correct.
536  * The issue is as follows:
537  * (1) BSDs treat the following packet as it is not forwarded and
538  *     is received as the packet to myself.
539  * (2) The issue that Windows can't use L2TP/IPsec when Windows is assigned
540  *     IP address .255.</pre>
541  */
542 static int
is_valid_host_address(uint32_t addr)543 is_valid_host_address(uint32_t addr)
544 {
545 	if (IN_CLASSA(addr))
546 		return ((IN_CLASSA_HOST & addr) == 0 ||
547 		    (IN_CLASSA_HOST & addr) == IN_CLASSA_HOST)? 0 : 1;
548 	if (IN_CLASSB(addr))
549 		return ((IN_CLASSB_HOST & addr) == 0 ||
550 		    (IN_CLASSB_HOST & addr) == IN_CLASSB_HOST)? 0 : 1;
551 	if (IN_CLASSC(addr))
552 		return ((IN_CLASSC_HOST & addr) == 0 ||
553 		    (IN_CLASSC_HOST & addr) == IN_CLASSC_HOST)? 0 : 1;
554 
555 	return 0;
556 }
557 
558 /** Record log that begins the label based this instance. */
559 static int
npppd_pool_log(npppd_pool * _this,int prio,const char * fmt,...)560 npppd_pool_log(npppd_pool *_this, int prio, const char *fmt, ...)
561 {
562 	int status;
563 	char logbuf[BUFSIZ];
564 	va_list ap;
565 
566 	/*
567 	 * npppd_pool_release_ip is called as _this == NULL,
568 	 * so it can't NPPPD_POOL_ASSERT(_this != NULL).
569 	 */
570 	va_start(ap, fmt);
571 	snprintf(logbuf, sizeof(logbuf), "ipcp=%s pool %s",
572 	    (_this == NULL)? "null" : _this->ipcp_name, fmt);
573 	status = vlog_printf(prio, logbuf, ap);
574 	va_end(ap);
575 
576 	return status;
577 }
578