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