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