xref: /openbsd/usr.sbin/npppd/pppoe/pppoed.c (revision 891d7ab6)
1 /* $OpenBSD: pppoed.c,v 1.7 2011/02/28 02:31:55 dlg 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  * This file provides the PPPoE(RFC2516) server(access concentrator)
30  * implementaion.
31  * $Id: pppoed.c,v 1.7 2011/02/28 02:31:55 dlg Exp $
32  */
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/endian.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <sys/uio.h>
39 #include <netinet/in.h>
40 #include <net/if.h>
41 #include <net/if_types.h>
42 #if defined(__NetBSD__)
43 #include <net/if_ether.h>
44 #else
45 #include <netinet/if_ether.h>
46 #endif
47 #include <net/if_dl.h>
48 #include <net/ethertypes.h>
49 #include <net/bpf.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <stdio.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <time.h>
56 #include <event.h>
57 #include <signal.h>
58 #include <stdlib.h>
59 #include <ifaddrs.h>
60 #include <stdarg.h>
61 #include <errno.h>
62 
63 #include "debugutil.h"
64 #include "slist.h"
65 #include "bytebuf.h"
66 #include "hash.h"
67 #include "properties.h"
68 #include "config_helper.h"
69 #include "rtev.h"
70 #include "privsep.h"
71 
72 #include "pppoe.h"
73 #include "pppoe_local.h"
74 
75 static int pppoed_seqno = 0;
76 
77 #ifdef	PPPOED_DEBUG
78 #define	PPPOED_ASSERT(x)	ASSERT(x)
79 #define	PPPOED_DBG(x)	pppoed_log x
80 #else
81 #define	PPPOED_ASSERT(x)
82 #define	PPPOED_DBG(x)
83 #endif
84 
85 static void      pppoed_log (pppoed *, int, const char *, ...) __printflike(3,4);
86 static void      pppoed_listener_init(pppoed *, pppoed_listener *);
87 static int       pppoed_output (pppoed_listener *, u_char *, u_char *, int);
88 static int       pppoed_listener_start (pppoed_listener *, int);
89 static void      pppoed_io_event (int, short, void *);
90 static void      pppoed_input (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], int, u_char *, int);
91 static void      pppoed_recv_PADR (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], slist *);
92 static void      pppoed_recv_PADI (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], slist *);
93 static int       session_id_cmp (void *, void *);
94 static uint32_t  session_id_hash (void *, size_t);
95 
96 #ifdef PPPOE_TEST
97 static void      pppoed_on_sigterm (int, short, void *);
98 static void      usage (void);
99 #endif
100 static const char *pppoe_code_string(int);
101 #ifdef	PPPOED_DEBUG
102 static const char *pppoe_tag_string(int);
103 #endif
104 
105 /*
106  * daemon
107  */
108 
109 /* initialize PPPoE daemon */
110 int
111 pppoed_init(pppoed *_this)
112 {
113 	int i, off, id;
114 
115 	memset(_this, 0, sizeof(pppoed));
116 	_this->id = pppoed_seqno++;
117 
118 	if ((_this->session_hash = hash_create(
119 	    (int (*) (const void *, const void *))session_id_cmp,
120 	    (uint32_t (*) (const void *, int))session_id_hash,
121 	    PPPOE_SESSION_HASH_SIZ)) == NULL) {
122 		pppoed_log(_this, LOG_ERR, "hash_create() failed on %s(): %m",
123 		    __func__);
124 		goto fail;
125 	}
126 
127 	slist_init(&_this->session_free_list);
128 	if (slist_add(&_this->session_free_list,
129 	    (void *)PPPOED_SESSION_SHUFFLE_MARK) == NULL) {
130 		pppoed_log(_this, LOG_ERR, "slist_add() failed on %s(): %m",
131 		    __func__);
132 		goto fail;
133 	}
134 
135 	/* XXX initialize hash of cookies */
136 	if ((_this->acookie_hash = hash_create(
137 	    (int (*) (const void *, const void *))session_id_cmp,
138 	    (uint32_t (*) (const void *, int))session_id_hash,
139 	    PPPOE_SESSION_HASH_SIZ)) == NULL) {
140 		pppoed_log(_this, LOG_WARNING,
141 		    "hash_create() failed on %s(): %m", __func__);
142 		pppoed_log(_this, LOG_WARNING, "hash_create() failed on %s(): "
143 		    "ac-cookie hash create failed.", __func__);
144 		_this->acookie_hash = NULL;
145 	}
146 	_this->acookie_next = random();
147 
148 #if PPPOE_NSESSION > 0xffff
149 #error PPPOE_NSESSION must be less than 65536
150 #endif
151 	off = random() % 0xffff;
152 	for (i = 0; i < PPPOE_NSESSION; i++) {
153 		id = (i + off) % 0xffff;
154 		if (id == 0)
155 			id = (off - 1) % 0xffff;
156 		if (slist_add(&_this->session_free_list, (void *)id) == NULL) {
157 			pppoed_log(_this, LOG_ERR,
158 			    "slist_add() failed on %s(): %m", __func__);
159 			goto fail;
160 		}
161 	}
162 
163 	_this->state = PPPOED_STATE_INIT;
164 
165 	return 0;
166 fail:
167 	pppoed_uninit(_this);
168 	return 1;
169 }
170 
171 static void
172 pppoed_listener_init(pppoed *_this, pppoed_listener *listener)
173 {
174 	memset(listener, 0, sizeof(pppoed_listener));
175 	listener->bpf = -1;
176 	listener->self = _this;
177 	listener->index = PPPOED_LISTENER_INVALID_INDEX;
178 }
179 
180 /* reload listner */
181 int
182 pppoed_reload_listeners(pppoed *_this)
183 {
184 	int rval = 0;
185 
186 	if (_this->state == PPPOED_STATE_RUNNING &&
187 	    _this->listen_incomplete != 0)
188 		rval = pppoed_start(_this);
189 
190 	return rval;
191 }
192 
193 /*
194  * Reject any packet except the packet to self and broadcasts,
195  * as bpf(4) potentially receive packets for others.
196  */
197 #define	REJECT_FOREIGN_ADDRESS 1
198 
199 #define ETHER_FIRST_INT(e)	((e)[0]<<24|(e)[1]<<16|(e)[2]<<8|(e)[3])
200 #define ETHER_LAST_SHORT(e)	((e)[4]<<8|(e)[5])
201 
202 static int
203 pppoed_listener_start(pppoed_listener *_this, int restart)
204 {
205 	int i;
206 	int log_level;
207 	char buf[BUFSIZ];
208 	struct ifreq ifreq;
209 	int ival;
210 	int found;
211 	struct ifaddrs *ifa0, *ifa;
212 	struct sockaddr_dl *sdl;
213 	struct bpf_insn insns[] = {
214 	    /* check etyer type = PPPOEDESC or PPPOE */
215 		BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
216 		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_PPPOEDISC, 2, 0),
217 		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_PPPOE, 1, 0),
218 		BPF_STMT(BPF_RET+BPF_K, (u_int)0),
219 #ifndef	REJECT_FOREIGN_ADDRESS
220 		BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
221 #else
222 	/* to ff:ff:ff:ff:ff:ff */
223 		BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0),
224 		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffffffff, 0, 3),
225 		BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4),
226 		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffff, 0, 1),
227 		BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
228 	/* to self */
229 		BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0),
230 		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K,
231 		    ETHER_FIRST_INT(_this->ether_addr), 0, 3),
232 		BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4),
233 		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K,
234 		    ETHER_LAST_SHORT(_this->ether_addr), 0, 1),
235 		BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
236 		BPF_STMT(BPF_RET+BPF_K, (u_int)0),
237 #endif
238 	};
239 	struct bpf_program bf_filter = {
240 		.bf_len = countof(insns),
241 		.bf_insns = insns
242 	};
243 	pppoed *_pppoed;
244 
245 	if (restart == 0)
246 		log_level = LOG_ERR;
247 	else
248 		log_level = LOG_INFO;
249 
250 	_pppoed = _this->self;
251 
252 	ifa0 = NULL;
253 	if (getifaddrs(&ifa0) != 0) {
254 		pppoed_log(_pppoed, log_level,
255 		    "getifaddrs() failed on %s(): %m", __func__);
256 		return -1;
257 	}
258 	found = 0;
259 	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
260 		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
261 		if (sdl->sdl_family != AF_LINK || sdl->sdl_type != IFT_ETHER ||
262 		    sdl->sdl_alen != ETHER_ADDR_LEN)
263 			continue;
264 		if (strcmp(ifa->ifa_name, _this->listen_ifname) == 0) {
265 			memcpy(_this->ether_addr,
266 			    (caddr_t)LLADDR(sdl), ETHER_ADDR_LEN);
267 			found = 1;
268 			break;
269 		}
270 	}
271 	freeifaddrs(ifa0);
272 	if (!found) {
273 		pppoed_log(_pppoed, log_level, "%s is not available.",
274 		    _this->listen_ifname);
275 		goto fail;
276 	}
277 
278 	/* Open /dev/bpfXX */
279 	/* FIXME: /dev/bpf of NetBSD3.0 can simultaneity open */
280 	for (i = 0; i < 256; i++) {
281 		snprintf(buf, sizeof(buf), "/dev/bpf%d", i);
282 		if ((_this->bpf = priv_open(buf, O_RDWR, 0600)) >= 0) {
283 			break;
284 		} else if (errno == ENXIO || errno == ENOENT)
285 			break;	/* no more entries */
286 	}
287 	if (_this->bpf < 0) {
288 		pppoed_log(_pppoed, log_level, "Cannot open bpf");
289 		goto fail;
290 	}
291 
292 	ival = BPF_CAPTURE_SIZ;
293 	if (ioctl(_this->bpf, BIOCSBLEN, &ival) != 0) {
294 		pppoed_log(_pppoed, log_level, "ioctl(bpf, BIOCSBLEN(%d)): %m",
295 		    ival);
296 		goto fail;
297 	}
298 	ival = 1;
299 	if (ioctl(_this->bpf, BIOCIMMEDIATE, &ival) != 0) {
300 		pppoed_log(_pppoed, log_level, "Cannot start bpf on %s: %m",
301 		    _this->listen_ifname);
302 		goto fail;
303 	}
304 
305 	/* bind interface */
306 	memset(&ifreq, 0, sizeof(ifreq));
307 	strlcpy(ifreq.ifr_name, _this->listen_ifname, sizeof(ifreq.ifr_name));
308 	if (ioctl(_this->bpf, BIOCSETIF, &ifreq) != 0) {
309 		pppoed_log(_pppoed, log_level, "Cannot start bpf on %s: %m",
310 		    _this->listen_ifname);
311 		goto fail;
312 	}
313 
314 	/* set linklocal address */
315 #ifdef	REJECT_FOREIGN_ADDRESS
316 	insns[10].k = ETHER_FIRST_INT(_this->ether_addr);
317 	insns[12].k = ETHER_LAST_SHORT(_this->ether_addr);
318 #endif
319 
320 	/* set filter */
321 	if (ioctl(_this->bpf, BIOCSETF, &bf_filter) != 0) {
322 		pppoed_log(_pppoed, log_level, "ioctl(bpf, BIOCSETF()): %m");
323 		goto fail;
324 	}
325 
326 	event_set(&_this->ev_bpf, _this->bpf, EV_READ | EV_PERSIST,
327 	    pppoed_io_event, _this);
328 	event_add(&_this->ev_bpf, NULL);
329 
330 	pppoed_log(_pppoed, LOG_INFO, "Listening on %s (PPPoE) [%s] using=%s "
331 	    "address=%02x:%02x:%02x:%02x:%02x:%02x", _this->listen_ifname,
332 	    _this->phy_label, buf, _this->ether_addr[0], _this->ether_addr[1],
333 	    _this->ether_addr[2], _this->ether_addr[3], _this->ether_addr[4],
334 	    _this->ether_addr[5]);
335 
336 	return 0;
337 fail:
338 	if (_this->bpf >= 0) {
339 		close(_this->bpf);
340 		_this->bpf = -1;
341 	}
342 
343 	return 1;
344 }
345 
346 /* start PPPoE daemon */
347 int
348 pppoed_start(pppoed *_this)
349 {
350 	int rval = 0;
351 	int nlistener_fail = 0;
352 	pppoed_listener *plistener;
353 
354 	slist_itr_first(&_this->listener);
355 	while (slist_itr_has_next(&_this->listener)) {
356 		plistener = slist_itr_next(&_this->listener);
357 		PPPOED_ASSERT(plistener != NULL);
358 		if (plistener->bpf < 0) {
359 			if (pppoed_listener_start(plistener,
360 			    _this->listen_incomplete) != 0)
361 				nlistener_fail++;
362 		}
363 	}
364 	if (nlistener_fail > 0)
365 		_this->listen_incomplete = 1;
366 	else
367 		_this->listen_incomplete = 0;
368 
369 	_this->state = PPPOED_STATE_RUNNING;
370 
371 	return rval;
372 }
373 
374 /* stop listener */
375 static void
376 pppoed_listener_stop(pppoed_listener *_this)
377 {
378 	pppoed *_pppoed;
379 
380 	PPPOED_ASSERT(_this != NULL);
381 	_pppoed = _this->self;
382 	PPPOED_ASSERT(_pppoed != NULL);
383 
384 	if (_this->bpf >= 0) {
385 		event_del(&_this->ev_bpf);
386 		close(_this->bpf);
387 		pppoed_log(_pppoed, LOG_INFO, "Shutdown %s (PPPoE) [%s] "
388 		    "address=%02x:%02x:%02x:%02x:%02x:%02x",
389 		    _this->listen_ifname, _this->phy_label,
390 		    _this->ether_addr[0], _this->ether_addr[1],
391 		    _this->ether_addr[2], _this->ether_addr[3],
392 		    _this->ether_addr[4], _this->ether_addr[5]);
393 		_this->bpf = -1;
394 	}
395 }
396 
397 /* stop PPPoE daemon */
398 void
399 pppoed_stop(pppoed *_this)
400 {
401 	pppoed_listener *plistener;
402 	hash_link *hl;
403 	pppoe_session *session;
404 
405 	if (!pppoed_is_running(_this))
406 		return;
407 
408 	_this->state = PPPOED_STATE_STOPPED;
409 	if (_this->session_hash != NULL) {
410 		for (hl = hash_first(_this->session_hash);
411 		    hl != NULL;
412 		    hl = hash_next(_this->session_hash)) {
413 			session = (pppoe_session *)hl->item;
414 			pppoe_session_disconnect(session);
415 			pppoe_session_stop(session);
416 		}
417 	}
418 	for (slist_itr_first(&_this->listener);
419 	    slist_itr_has_next(&_this->listener);) {
420 		plistener = slist_itr_next(&_this->listener);
421 		pppoed_listener_stop(plistener);
422 		free(plistener);
423 		slist_itr_remove(&_this->listener);
424 	}
425 	pppoed_log(_this, LOG_NOTICE, "Stopped");
426 }
427 
428 /* uninitialize (free) PPPoE daemon */
429 void
430 pppoed_uninit(pppoed *_this)
431 {
432 	if (_this->session_hash != NULL) {
433 		hash_free(_this->session_hash);
434 		_this->session_hash = NULL;
435 	}
436 	if (_this->acookie_hash != NULL) {
437 		hash_free(_this->acookie_hash);
438 		_this->acookie_hash = NULL;
439 	}
440 	slist_fini(&_this->session_free_list);
441 	/* listener themself has been released already */
442 	slist_fini(&_this->listener);
443 
444 	_this->config = NULL;
445 }
446 
447 /* it called when the PPPoE session was closed */
448 void
449 pppoed_pppoe_session_close_notify(pppoed *_this, pppoe_session *session)
450 {
451 	slist_add(&_this->session_free_list, (void *)session->session_id);
452 
453 	if (_this->acookie_hash != NULL)
454 		hash_delete(_this->acookie_hash, (void *)session->acookie, 0);
455 	if (_this->session_hash != NULL)
456 		hash_delete(_this->session_hash, (void *)session->session_id,
457 		    0);
458 
459 	pppoe_session_fini(session);
460 	free(session);
461 }
462 
463 /*
464  * PPPoE Configuration
465  */
466 #define	CFG_KEY(p, s)	config_key_prefix((p), (s))
467 #define	VAL_SEP		" \t\r\n"
468 
469 CONFIG_FUNCTIONS(pppoed_config, pppoed, config);
470 PREFIXED_CONFIG_FUNCTIONS(pppoed_listener_config, pppoed_listener, self->config,
471     phy_label);
472 
473 /* reload configurations for the PPPoE daemon */
474 int
475 pppoed_reload(pppoed *_this, struct properties *config, const char *name,
476     int default_enabled)
477 {
478 	struct sockaddr_dl *sdl;
479 	int i, count, found;
480 	hash_link *hl;
481 	const char *val;
482 	char *tok, *cp, buf[PPPOED_CONFIG_BUFSIZ], *label;
483 	pppoed_listener *l;
484 	int do_start;
485 	struct {
486 		char ifname[IF_NAMESIZE];
487 		char label[PPPOED_PHY_LABEL_SIZE];
488 	} listeners[PPPOE_NLISTENER];
489 	struct ifaddrs *ifa0, *ifa;
490 	slist rmlist, newlist;
491 	pppoe_session *session;
492 
493 	do_start = 0;
494 
495 	_this->config = config;
496 	if (pppoed_config_str_equal(_this, CFG_KEY(name, "enabled"), "true",
497 	    default_enabled)) {
498 		/* avoid false->true flapping */
499 		if (pppoed_is_stopped(_this) || !pppoed_is_running(_this))
500 			do_start = 1;
501 	} else {
502 		if (!pppoed_is_stopped(_this))
503 			pppoed_stop(_this);
504 		return 0;
505 	}
506 
507 	if (do_start) {
508 		if (pppoed_init(_this) != 0)
509 			return 1;
510 		_this->config = config;
511 	}
512 
513 	ifa0 = NULL;
514 	slist_init(&rmlist);
515 	slist_init(&newlist);
516 
517 	_this->desc_in_pktdump = pppoed_config_str_equal(_this,
518 	    "log.pppoe.desc.in.pktdump", "true", 0);
519 	_this->desc_out_pktdump = pppoed_config_str_equal(_this,
520 	    "log.pppoe.desc.out.pktdump", "true", 0);
521 
522 	_this->session_in_pktdump = pppoed_config_str_equal(_this,
523 	    "log.pppoe.session.in.pktdump", "true", 0);
524 	_this->session_out_pktdump = pppoed_config_str_equal(_this,
525 	    "log.pppoe.session.out.pktdump", "true", 0);
526 
527 	if (getifaddrs(&ifa0) != 0) {
528 		pppoed_log(_this, LOG_ERR,
529 		    "getifaddrs() failed on %s(): %m", __func__);
530 		goto fail;
531 	}
532 	count = 0;
533 	val = pppoed_config_str(_this, CFG_KEY(name, "interface"));
534 	if (val != NULL) {
535 		if (strlen(val) >= sizeof(buf)) {
536 			log_printf(LOG_ERR, "configuration error at "
537 			    "%s: too long", CFG_KEY(name, "interface"));
538 			return 1;
539 		}
540 		strlcpy(buf, val, sizeof(buf));
541 
542 		label = NULL;
543 		/* it accepts multiple entries with tab/space separation */
544 		for (i = 0, cp = buf;
545 		    (tok = strsep(&cp, VAL_SEP)) != NULL;) {
546 			if (*tok == '\0')
547 				continue;
548 			if (label == NULL) {
549 				label = tok;
550 				continue;
551 			}
552 			PPPOED_ASSERT(count < countof(listeners));
553 			if (count >= countof(listeners)) {
554 				pppoed_log(_this, LOG_ERR,
555 				    "Too many listeners");
556 				goto fail;
557 			}
558 			/* check the interface exist or not */
559 			found = 0;
560 			for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
561 				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
562 				if (sdl->sdl_family == AF_LINK &&
563 				    IFTYPE_IS_LAN(sdl->sdl_type) &&
564 				    strcmp(ifa->ifa_name, tok) == 0) {
565 					found = 1;
566 					break;
567 				}
568 			}
569 			if (!found) {
570 				pppoed_log(_this, LOG_ERR,
571 				    "interface %s is not found", tok);
572 				goto fail;
573 			}
574 			strlcpy(listeners[count].ifname, tok,
575 			    sizeof(listeners[count].ifname));
576 			strlcpy(listeners[count].label, label,
577 			    sizeof(listeners[count].label));
578 
579 			label = NULL;
580 			count++;
581 		}
582 		if (label != NULL) {
583 			log_printf(LOG_ERR, "configuration error at %s: %s",
584 			    CFG_KEY(name, "interface"), label);
585 			return 1;
586 		}
587 	}
588 
589 	if (slist_add_all(&rmlist, &_this->listener) != 0)
590 		goto fail;
591 
592 	for (i = 0; i < count; i++) {
593 		found = 0;
594 		l = NULL;
595 		slist_itr_first(&rmlist);
596 		while (slist_itr_has_next(&rmlist)) {
597 			l = slist_itr_next(&rmlist);
598 			if (strcmp(l->listen_ifname, listeners[i].ifname) == 0){
599 				slist_itr_remove(&rmlist);
600 				found = 1;
601 				break;
602 			}
603 		}
604 		if (!found) {
605 			if ((l = malloc(sizeof(pppoed_listener))) == NULL)
606 				goto fail;
607 			pppoed_listener_init(_this, l);
608 		}
609 		l->self = _this;
610 		strlcpy(l->phy_label, listeners[i].label,
611 		    sizeof(l->phy_label));
612 		strlcpy(l->listen_ifname, listeners[i].ifname,
613 		    sizeof(l->listen_ifname));
614 		if (slist_add(&newlist, l) == NULL) {
615 			pppoed_log(_this, LOG_ERR,
616 			    "slist_add() failed in %s(): %m", __func__);
617 			goto fail;
618 		}
619 	}
620 
621 	if (slist_set_size(&_this->listener, count) != 0)
622 		goto fail;
623 
624 	/* garbage collection of listner context */
625 	slist_itr_first(&rmlist);
626 	while (slist_itr_has_next(&rmlist)) {
627 		l = slist_itr_next(&rmlist);
628 		/* handle child PPPoE session */
629 		if (_this->session_hash != NULL) {
630 			for (hl = hash_first(_this->session_hash); hl != NULL;
631 			    hl = hash_next(_this->session_hash)) {
632 				session = (pppoe_session *)hl->item;
633 				if (session->listener_index == l->index)
634 					pppoe_session_stop(session);
635 			}
636 		}
637 		pppoed_listener_stop(l);
638 		free(l);
639 	}
640 	slist_remove_all(&_this->listener);
641 	/* as slist_set_size-ed, it must not fail */
642 	(void)slist_add_all(&_this->listener, &newlist);
643 
644 	/* reset indexes */
645 	slist_itr_first(&newlist);
646 	for (i = 0; slist_itr_has_next(&newlist); i++) {
647 		l = slist_itr_next(&newlist);
648 		if (l->index != i && l->index != PPPOED_LISTENER_INVALID_INDEX){
649 			PPPOED_DBG((_this, LOG_DEBUG, "listener %d => %d",
650 			    l->index, i));
651 			for (hl = hash_first(_this->session_hash); hl != NULL;
652 			    hl = hash_next(_this->session_hash)) {
653 				session = (pppoe_session *)hl->item;
654 				if (session->listener_index == l->index)
655 					session->listener_index = i;
656 			}
657 		}
658 		l->index = i;
659 	}
660 
661 	slist_fini(&rmlist);
662 	slist_fini(&newlist);
663 	if (ifa0 != NULL)
664 		freeifaddrs(ifa0);
665 
666 	if (pppoed_start(_this) != 0)
667 		return 1;
668 
669 	return 0;
670 fail:
671 	slist_fini(&rmlist);
672 	slist_fini(&newlist);
673 	if (ifa0 != NULL)
674 		freeifaddrs(ifa0);
675 
676 	return 1;
677 }
678 
679 /*
680  * I/O
681  */
682 
683 static void
684 pppoed_io_event(int fd, short evmask, void *ctx)
685 {
686 	u_char buf[BPF_CAPTURE_SIZ], *pkt;
687 	int lpkt, off;
688 	pppoed_listener *_this;
689 	struct ether_header *ether;
690 	struct bpf_hdr *bpf;
691 
692 	_this = ctx;
693 
694 	PPPOED_ASSERT(_this != NULL);
695 
696 	lpkt = read(_this->bpf, buf, sizeof(buf));
697 	pkt = buf;
698 	while (lpkt > 0) {
699 		if (lpkt < sizeof(struct bpf_hdr)) {
700 			pppoed_log(_this->self, LOG_WARNING,
701 			    "Received bad PPPoE packet: packet too short(%d)",
702 			    lpkt);
703 			break;
704 		}
705 		bpf = (struct bpf_hdr *)pkt;
706 		ether = (struct ether_header *)(pkt + bpf->bh_hdrlen);
707 		ether->ether_type = ntohs(ether->ether_type);
708 		if (memcmp(ether->ether_shost, _this->ether_addr,
709 		    ETHER_ADDR_LEN) == 0)
710 			/* the packet is from myself */
711 			goto next_pkt;
712 		off = bpf->bh_hdrlen + sizeof(struct ether_header);
713 		if (lpkt < off + sizeof(struct pppoe_header)) {
714 			pppoed_log(_this->self, LOG_WARNING,
715 			    "Received bad PPPoE packet: packet too short(%d)",
716 			    lpkt);
717 			break;
718 		}
719 		pppoed_input(_this, ether->ether_shost,
720 		    (ether->ether_type == ETHERTYPE_PPPOEDISC)? 1 : 0,
721 		    pkt + off, lpkt - off);
722 next_pkt:
723 		pkt = pkt + BPF_WORDALIGN(bpf->bh_hdrlen +
724 		    bpf->bh_caplen);
725 		lpkt -= BPF_WORDALIGN(bpf->bh_hdrlen + bpf->bh_caplen);
726 	}
727 	return;
728 }
729 
730 static void
731 pppoed_input(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], int is_disc,
732     u_char *pkt, int lpkt)
733 {
734 	hash_link *hl;
735 	pppoe_session *session;
736 	struct pppoe_header *pppoe;
737 	struct pppoe_tlv *tlv;
738 	u_char tlvspace[2048], *p_tlvspace;
739 	int session_id;
740 	slist tag_list;
741 	const char *reason;
742 #define tlvspace_remaining() (sizeof(tlvspace) - (p_tlvspace - tlvspace))
743 
744 	reason = "";
745 	p_tlvspace = tlvspace;
746 	session = NULL;
747 
748 	pppoe = (struct pppoe_header *)pkt;
749 	session_id = pppoe->session_id = ntohs(pppoe->session_id);
750 	pppoe->length = ntohs(pppoe->length);
751 
752 #ifdef PPPOED_DEBUG
753 	if (is_disc) {
754 		PPPOED_DBG((_this->self, DEBUG_LEVEL_1,
755 		    "Recv%s(%02x) ver=%d type=%d session-id=%d if=%s",
756 		    pppoe_code_string(pppoe->code), pppoe->code,
757 		    pppoe->ver, pppoe->type, pppoe->session_id,
758 		    _this->listen_ifname));
759 	}
760 #endif
761 	pkt += sizeof(struct pppoe_header);
762 	lpkt -= sizeof(struct pppoe_header);
763 
764 	if (lpkt < pppoe->length) {
765 		reason = "received packet is shorter than "
766 		    "pppoe length field.";
767 		goto bad_packet;
768 	}
769 	/* use PPPoE heade value as lpkt */
770 	lpkt = pppoe->length;
771 
772 	if (pppoe->type != PPPOE_RFC2516_TYPE ||
773 	    pppoe->ver != PPPOE_RFC2516_VER) {
774 		reason = "received packet has wrong version or type.";
775 		goto bad_packet;
776 	}
777 
778 	if (session_id != 0) {
779 		hl = hash_lookup(_this->self->session_hash, (void *)session_id);
780 		if (hl != NULL)
781 			session = (pppoe_session *)hl->item;
782 	}
783 	if (!is_disc) {
784 		if (session != NULL)
785 			pppoe_session_input(session, pkt, pppoe->length);
786 		return;
787 	}
788 
789 	/*
790 	 * PPPoE-Discovery Packet proccessing.
791 	 */
792 	slist_init(&tag_list);
793 	while (lpkt > 0) {
794 		if (lpkt < 4) {
795 			reason = "tlv list is broken.  "
796 			    "Remaining octet is too short.";
797 			goto fail;
798 		}
799 		if (tlvspace_remaining() < 4) {
800 			reason = "parsing TAGs reached the buffer size limit.";
801 			goto fail;
802 		}
803 		tlv = (struct pppoe_tlv *)p_tlvspace;
804 		GETSHORT(tlv->type, pkt);
805 		GETSHORT(tlv->length, pkt);
806 		p_tlvspace += 4;
807 		lpkt -= 4;
808 		if (tlv->length > lpkt) {
809 			reason = "tlv list is broken.  length is wrong.";
810 			goto fail;
811 		}
812 		if (tlvspace_remaining() < tlv->length) {
813 			reason = "parsing TAGs reached the buffer size limit.";
814 			goto fail;
815 		}
816 		if (tlv->length > 0) {
817 			memcpy(tlv->value, pkt, tlv->length);
818 			pkt += tlv->length;
819 			lpkt -= tlv->length;
820 			p_tlvspace += tlv->length;
821 			p_tlvspace = (u_char *)ALIGN(p_tlvspace);
822 		}
823 #ifdef	PPPOED_DEBUG
824 		if (debuglevel >= 2)
825 			pppoed_log(_this->self, DEBUG_LEVEL_2,
826 			    "Recv%s tag %s(%04x)=%s",
827 			    pppoe_code_string(pppoe->code),
828 			    pppoe_tag_string(tlv->type), tlv->type,
829 			    pppoed_tlv_value_string(tlv));
830 #endif
831 		if (tlv->type == PPPOE_TAG_END_OF_LIST)
832 			break;
833 		if (slist_add(&tag_list, tlv) == NULL) {
834 			goto fail;
835 		}
836 	}
837 	switch (pppoe->code) {
838 	case PPPOE_CODE_PADI:
839 		if (_this->self->state != PPPOED_STATE_RUNNING)
840 			break;
841 		pppoed_recv_PADI(_this, shost, &tag_list);
842 		break;
843 	case PPPOE_CODE_PADR:
844 		if (_this->self->state != PPPOED_STATE_RUNNING)
845 			break;
846 		pppoed_recv_PADR(_this, shost, &tag_list);
847 		break;
848 	case PPPOE_CODE_PADT:
849 		PPPOED_DBG((_this->self, LOG_DEBUG, "RecvPADT"));
850 		if (session != NULL)
851 			pppoe_session_recv_PADT(session, &tag_list);
852 		break;
853 	}
854 	slist_fini(&tag_list);
855 
856 	return;
857 fail:
858 	slist_fini(&tag_list);
859 bad_packet:
860 	pppoed_log(_this->self, LOG_INFO,
861 	    "Received a bad packet: code=%s(%02x) ver=%d type=%d session-id=%d"
862 	    " if=%s: %s", pppoe_code_string(pppoe->code), pppoe->code,
863 	    pppoe->ver, pppoe->type, pppoe->session_id, _this->listen_ifname,
864 	    reason);
865 }
866 
867 static int
868 pppoed_output(pppoed_listener *_this, u_char *dhost, u_char *pkt, int lpkt)
869 {
870 	int sz, iovc;
871 	struct iovec iov[3];
872 	struct ether_header ether;
873 	struct pppoe_header *pppoe;
874 	u_char pad[ETHERMIN];
875 
876 	memcpy(ether.ether_dhost, dhost, ETHER_ADDR_LEN);
877 	memcpy(ether.ether_shost, _this->ether_addr, ETHER_ADDR_LEN);
878 
879 	iov[0].iov_base = &ether;
880 	iov[0].iov_len = sizeof(struct ether_header);
881 	ether.ether_type = htons(ETHERTYPE_PPPOEDISC);
882 	iov[1].iov_base = pkt;
883 	iov[1].iov_len = lpkt;
884 	pppoe = (struct pppoe_header *)pkt;
885 	pppoe->length = htons(lpkt - sizeof(struct pppoe_header));
886 
887 	iovc = 2;
888 
889 	if (lpkt < ETHERMIN) {
890 		memset(pad, 0, ETHERMIN - lpkt);
891 		iov[2].iov_base = pad;
892 		iov[2].iov_len = ETHERMIN - lpkt;
893 		iovc++;
894 	}
895 
896 	sz = writev(_this->bpf, iov, iovc);
897 
898 	return (sz > 0)? 0 : -1;
899 }
900 
901 static void
902 pppoed_recv_PADR(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN],
903     slist *tag_list)
904 {
905 	int session_id, shuffle_cnt;
906 	pppoe_session *session;
907 	pppoed *_pppoed;
908 
909 	_pppoed = _this->self;
910 	if ((session = malloc(sizeof(pppoe_session))) == NULL) {
911 		pppoed_log(_pppoed, LOG_ERR, "malloc() failed on %s(): %m",
912 		    __func__);
913 		goto fail;
914 	}
915 
916 	/* create session_id */
917 	shuffle_cnt = 0;
918 	do {
919 		session_id = (int)slist_remove_first(
920 		    &_pppoed->session_free_list);
921 		if (session_id != PPPOED_SESSION_SHUFFLE_MARK)
922 			break;
923 		PPPOED_ASSERT(shuffle_cnt == 0);
924 		if (shuffle_cnt++ > 0) {
925 			pppoed_log(_pppoed, LOG_ERR,
926 			    "unexpected errror in %s(): session_free_list full",
927 			    __func__);
928 			slist_add(&_pppoed->session_free_list,
929 			    (void *)PPPOED_SESSION_SHUFFLE_MARK);
930 			goto fail;
931 		}
932 		slist_shuffle(&_pppoed->session_free_list);
933 		slist_add(&_pppoed->session_free_list,
934 		    (void *)PPPOED_SESSION_SHUFFLE_MARK);
935 	} while (1);
936 
937 	if (pppoe_session_init(session, _pppoed, _this->index, session_id,
938 	    shost) != 0)
939 		goto fail;
940 
941 	hash_insert(_pppoed->session_hash, (void *)session_id, session);
942 
943 	if (pppoe_session_recv_PADR(session, tag_list) != 0)
944 		goto fail;
945 
946 	session = NULL;	/* don't free */
947 	/* FALLTHROUGH */
948 fail:
949 	if (session != NULL)
950 		pppoe_session_fini(session);
951 	return;
952 }
953 
954 static void
955 pppoed_recv_PADI(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN],
956     slist *tag_list)
957 {
958 	int len, accept_any_service_req;
959 	const char *val, *service_name, *ac_name;
960 	u_char bufspace[2048];
961 	u_char sn[2048], ac_name0[40];
962 	struct pppoe_header pppoe;
963 	struct pppoe_tlv tlv, *tlv_hostuniq, *tlv0, *tlv_service_name;
964 	bytebuffer *buf;
965 
966 	if ((buf = bytebuffer_wrap(bufspace, sizeof(bufspace))) == NULL) {
967 		pppoed_log(_this->self, LOG_ERR,
968 		"bytebuffer_wrap() failed on %s(): %m", __func__);
969 		return;
970 	}
971 	bytebuffer_clear(buf);
972 
973 	tlv_hostuniq = NULL;
974 	tlv_service_name = NULL;
975 
976 	service_name = "";
977 	if ((val = pppoed_listener_config_str(_this, "pppoe.service_name"))
978 	    != NULL)
979 		service_name = val;
980 	accept_any_service_req = pppoed_listener_config_str_equal(_this,
981 		"pppoe.accept_any_service_request", "true", 1);
982 
983 	for (slist_itr_first(tag_list); slist_itr_has_next(tag_list);) {
984 		tlv0 = slist_itr_next(tag_list);
985 		if (tlv0->type == PPPOE_TAG_HOST_UNIQ)
986 			tlv_hostuniq = tlv0;
987 		if (tlv0->type == PPPOE_TAG_SERVICE_NAME) {
988 
989 			len = tlv0->length;
990 			if (len >= sizeof(sn))
991 				goto fail;
992 
993 			memcpy(sn, tlv0->value, len);
994 			sn[len] = '\0';
995 
996 			if (strcmp(service_name, sn) == 0 ||
997 			    (sn[0] == '\0' && accept_any_service_req))
998 				tlv_service_name = tlv0;
999 		}
1000 	}
1001 	if (tlv_service_name == NULL) {
1002 		pppoed_log(_this->self, LOG_INFO,
1003 		    "Deny PADI from=%02x:%02x:%02x:%02x:%02x:%02x "
1004 		    "service-name=%s host-uniq=%s if=%s: serviceName is "
1005 		    "not allowed.", shost[0], shost[1],
1006 		    shost[2], shost[3], shost[4], shost[5], sn, tlv_hostuniq?
1007 		    pppoed_tlv_value_string(tlv_hostuniq) : "none",
1008 		    _this->listen_ifname);
1009 		goto fail;
1010 	}
1011 
1012 	pppoed_log(_this->self, LOG_INFO,
1013 	    "RecvPADI from=%02x:%02x:%02x:%02x:%02x:%02x service-name=%s "
1014 	    "host-uniq=%s if=%s", shost[0], shost[1], shost[2], shost[3],
1015 	    shost[4], shost[5], sn, tlv_hostuniq?
1016 	    pppoed_tlv_value_string(tlv_hostuniq) : "none",
1017 	    _this->listen_ifname);
1018 
1019 	/*
1020 	 * PPPoE Header
1021 	 */
1022 	memset(&pppoe, 0, sizeof(pppoe));
1023 	pppoe.ver = PPPOE_RFC2516_VER;
1024 	pppoe.type = PPPOE_RFC2516_TYPE;
1025 	pppoe.code = PPPOE_CODE_PADO;
1026 	bytebuffer_put(buf, &pppoe, sizeof(pppoe));
1027 
1028 	/*
1029 	 * Tag - Service-Name
1030 	 */
1031 	tlv.type = htons(PPPOE_TAG_SERVICE_NAME);
1032 	len = strlen(service_name);
1033 	tlv.length = htons(len);
1034 	bytebuffer_put(buf, &tlv, sizeof(tlv));
1035 	if (len > 0)
1036 		bytebuffer_put(buf, service_name, len);
1037 
1038 	/*
1039 	 * Tag - Access Concentrator Name
1040 	 */
1041 	ac_name = pppoed_listener_config_str(_this, "pppoe.ac_name");
1042 	if (ac_name == NULL) {
1043 		/*
1044 		 * use the ethernet address as default AC-Name.
1045 		 * suggested by RFC 2516.
1046 		 */
1047 		snprintf(ac_name0, sizeof(ac_name0),
1048 		    "%02x:%02x:%02x:%02x:%02x:%02x", _this->ether_addr[0],
1049 		    _this->ether_addr[1], _this->ether_addr[2],
1050 		    _this->ether_addr[3], _this->ether_addr[4],
1051 		    _this->ether_addr[5]);
1052 		ac_name = ac_name0;
1053 	}
1054 
1055 	tlv.type = htons(PPPOE_TAG_AC_NAME);
1056 	len = strlen(ac_name);
1057 	tlv.length = htons(len);
1058 	bytebuffer_put(buf, &tlv, sizeof(tlv));
1059 	bytebuffer_put(buf, ac_name, len);
1060 
1061 	/*
1062 	 * Tag - ac-cookie
1063 	 */
1064 	if (_this->self->acookie_hash != NULL) {
1065 		/*
1066 		 * search next ac-cookie.
1067 		 * (XXX it will loop in uint32_t boundaly)
1068 		 */
1069 		do {
1070 			_this->self->acookie_next += 1;
1071 		}
1072 		while(hash_lookup(_this->self->acookie_hash,
1073 		    (void *)_this->self->acookie_next) != NULL);
1074 
1075 		tlv.type = htons(PPPOE_TAG_AC_COOKIE);
1076 		tlv.length = ntohs(sizeof(uint32_t));
1077 		bytebuffer_put(buf, &tlv, sizeof(tlv));
1078 		bytebuffer_put(buf, &_this->self->acookie_next,
1079 		    sizeof(uint32_t));
1080 	}
1081 
1082 	/*
1083 	 * Tag - Host-Uniq
1084 	 */
1085 	if (tlv_hostuniq != NULL) {
1086 		tlv.type = htons(PPPOE_TAG_HOST_UNIQ);
1087 		tlv.length = ntohs(tlv_hostuniq->length);
1088 		bytebuffer_put(buf, &tlv, sizeof(tlv));
1089 		bytebuffer_put(buf, tlv_hostuniq->value,
1090 		    tlv_hostuniq->length);
1091 	}
1092 
1093 	/*
1094 	 * Tag - End-Of-List
1095 	 */
1096 	tlv.type = htons(PPPOE_TAG_END_OF_LIST);
1097 	tlv.length = ntohs(0);
1098 	bytebuffer_put(buf, &tlv, sizeof(tlv));
1099 
1100 	bytebuffer_flip(buf);
1101 
1102 	if (pppoed_output(_this, shost, bytebuffer_pointer(buf),
1103 	    bytebuffer_remaining(buf)) != 0) {
1104 		pppoed_log(_this->self, LOG_ERR, "pppoed_output() failed:%m");
1105 	}
1106 	pppoed_log(_this->self, LOG_INFO,
1107 	    "SendPADO to=%02x:%02x:%02x:%02x:%02x:%02x serviceName=%s "
1108 	    "acName=%s hostUniq=%s eol if=%s", shost[0], shost[1], shost[2],
1109 	    shost[3], shost[4], shost[5], service_name, ac_name,
1110 	    tlv_hostuniq? pppoed_tlv_value_string(tlv_hostuniq) : "none",
1111 		_this->listen_ifname);
1112 	/* FALLTHROUGH */
1113 fail:
1114 	bytebuffer_unwrap(buf);
1115 	bytebuffer_destroy(buf);
1116 }
1117 
1118 /*
1119  * log
1120  */
1121 static void
1122 pppoed_log(pppoed *_this, int prio, const char *fmt, ...)
1123 {
1124 	char logbuf[BUFSIZ];
1125 	va_list ap;
1126 
1127 	PPPOED_ASSERT(_this != NULL);
1128 	va_start(ap, fmt);
1129 #ifdef	PPPOED_MULITPLE
1130 	snprintf(logbuf, sizeof(logbuf), "pppoed id=%u %s", _this->id, fmt);
1131 #else
1132 	snprintf(logbuf, sizeof(logbuf), "pppoed %s", fmt);
1133 #endif
1134 	vlog_printf(prio, logbuf, ap);
1135 	va_end(ap);
1136 }
1137 
1138 #define	NAME_VAL(x)	{ x, #x }
1139 static struct _label_name {
1140 	int		label;
1141 	const char	*name;
1142 } pppoe_code_labels[] = {
1143 	NAME_VAL(PPPOE_CODE_PADI),
1144 	NAME_VAL(PPPOE_CODE_PADO),
1145 	NAME_VAL(PPPOE_CODE_PADR),
1146 	NAME_VAL(PPPOE_CODE_PADS),
1147 	NAME_VAL(PPPOE_CODE_PADT),
1148 #ifdef PPPOED_DEBUG
1149 }, pppoe_tlv_labels[] = {
1150 	NAME_VAL(PPPOE_TAG_END_OF_LIST),
1151 	NAME_VAL(PPPOE_TAG_SERVICE_NAME),
1152 	NAME_VAL(PPPOE_TAG_AC_NAME),
1153 	NAME_VAL(PPPOE_TAG_HOST_UNIQ),
1154 	NAME_VAL(PPPOE_TAG_AC_COOKIE),
1155 	NAME_VAL(PPPOE_TAG_VENDOR_SPECIFIC),
1156 	NAME_VAL(PPPOE_TAG_RELAY_SESSION_ID),
1157 	NAME_VAL(PPPOE_TAG_SERVICE_NAME_ERROR),
1158 	NAME_VAL(PPPOE_TAG_AC_SYSTEM_ERROR),
1159 	NAME_VAL(PPPOE_TAG_GENERIC_ERROR)
1160 #endif
1161 };
1162 #define LABEL_TO_STRING(func_name, label_names, prefix_len)		\
1163 	static const char *						\
1164 	func_name(int code)						\
1165 	{								\
1166 		int i;							\
1167 									\
1168 		for (i = 0; i < countof(label_names); i++) {		\
1169 			if (label_names[i].label == code)		\
1170 				return label_names[i].name + prefix_len;\
1171 		}							\
1172 									\
1173 		return "UNKNOWN";					\
1174 	}
1175 LABEL_TO_STRING(pppoe_code_string, pppoe_code_labels, 11)
1176 #ifdef PPPOED_DEBUG
1177 LABEL_TO_STRING(pppoe_tag_string, pppoe_tlv_labels, 10)
1178 #endif
1179 
1180 const char *
1181 pppoed_tlv_value_string(struct pppoe_tlv *tlv)
1182 {
1183 	int i;
1184 	char buf[3];
1185 	static char _tlv_string_value[8192];
1186 
1187 	_tlv_string_value[0] = '\0';
1188 	for (i = 0; i < tlv->length; i++) {
1189 		snprintf(buf, sizeof(buf), "%02x", tlv->value[i]);
1190 		strlcat(_tlv_string_value, buf,
1191 		    sizeof(_tlv_string_value));
1192 	}
1193 	return _tlv_string_value;
1194 }
1195 
1196 /*
1197  * misc
1198  */
1199 static int
1200 session_id_cmp(void *a, void *b)
1201 {
1202 	int ia, ib;
1203 
1204 	ia = (int)a;
1205 	ib = (int)b;
1206 
1207 	return ib - ia;
1208 }
1209 
1210 static uint32_t
1211 session_id_hash(void *a, size_t siz)
1212 {
1213 	int ia;
1214 
1215 	ia = (int)a;
1216 
1217 	return ia % siz;
1218 }
1219