xref: /openbsd-src/usr.sbin/radiusd/radiusd_ipcp.c (revision aae7f36381328726cc239ee8b85892f031c82d8c)
1*aae7f363Syasuoka /*	$OpenBSD: radiusd_ipcp.c,v 1.23 2025/01/29 10:16:05 yasuoka Exp $	*/
2842565f2Syasuoka 
3842565f2Syasuoka /*
4842565f2Syasuoka  * Copyright (c) 2024 Internet Initiative Japan Inc.
5842565f2Syasuoka  *
6842565f2Syasuoka  * Permission to use, copy, modify, and distribute this software for any
7842565f2Syasuoka  * purpose with or without fee is hereby granted, provided that the above
8842565f2Syasuoka  * copyright notice and this permission notice appear in all copies.
9842565f2Syasuoka  *
10842565f2Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11842565f2Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12842565f2Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13842565f2Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14842565f2Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15842565f2Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16842565f2Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17842565f2Syasuoka  */
18842565f2Syasuoka 
19842565f2Syasuoka #include <sys/types.h>
20842565f2Syasuoka #include <sys/queue.h>
21842565f2Syasuoka #include <sys/socket.h>
22842565f2Syasuoka #include <sys/time.h>
23842565f2Syasuoka #include <sys/tree.h>
24842565f2Syasuoka #include <arpa/inet.h>
25842565f2Syasuoka 
26842565f2Syasuoka #include <inttypes.h>
27842565f2Syasuoka #include <netdb.h>
28842565f2Syasuoka #include <db.h>
29842565f2Syasuoka #include <err.h>
30842565f2Syasuoka #include <errno.h>
31842565f2Syasuoka #include <event.h>
32842565f2Syasuoka #include <fcntl.h>
33842565f2Syasuoka #include <pwd.h>
34842565f2Syasuoka #include <radius.h>
35842565f2Syasuoka #include <stdbool.h>
36842565f2Syasuoka #include <stddef.h>
37842565f2Syasuoka #include <stdint.h>
38842565f2Syasuoka #include <stdio.h>
39842565f2Syasuoka #include <stdlib.h>
40842565f2Syasuoka #include <string.h>
41842565f2Syasuoka #include <time.h>
42842565f2Syasuoka #include <unistd.h>
43842565f2Syasuoka #include <imsg.h>
44842565f2Syasuoka 
45842565f2Syasuoka #include "radiusd.h"
46842565f2Syasuoka #include "radiusd_module.h"
47842565f2Syasuoka #include "radiusd_ipcp.h"
48842565f2Syasuoka #include "log.h"
49842565f2Syasuoka 
50842565f2Syasuoka #define RADIUSD_IPCP_START_WAIT	60
51842565f2Syasuoka 
52842565f2Syasuoka enum ipcp_address_type {
53842565f2Syasuoka 	ADDRESS_TYPE_POOL,
54842565f2Syasuoka 	ADDRESS_TYPE_STATIC
55842565f2Syasuoka };
56842565f2Syasuoka 
57842565f2Syasuoka struct ipcp_address {
58842565f2Syasuoka 	enum ipcp_address_type		 type;
59842565f2Syasuoka 	struct in_addr			 start;
60842565f2Syasuoka 	struct in_addr			 end;
61842565f2Syasuoka 	int				 naddrs;
62842565f2Syasuoka 	TAILQ_ENTRY(ipcp_address)	 next;
63842565f2Syasuoka };
64842565f2Syasuoka 
65842565f2Syasuoka struct user {
66842565f2Syasuoka 	TAILQ_HEAD(, assigned_ipv4)	 ipv4s;
67842565f2Syasuoka 	RB_ENTRY(user)			 tree;
68842565f2Syasuoka 	char				 name[0];
69842565f2Syasuoka };
70842565f2Syasuoka 
71c0127aecSyasuoka struct radiusctl_client {
72c0127aecSyasuoka 	int				 peerid;
73c0127aecSyasuoka 	TAILQ_ENTRY(radiusctl_client)	 entry;
74c0127aecSyasuoka };
75c0127aecSyasuoka 
76842565f2Syasuoka struct module_ipcp_dae;
77842565f2Syasuoka 
78842565f2Syasuoka struct assigned_ipv4 {
79842565f2Syasuoka 	struct in_addr			 ipv4;
80842565f2Syasuoka 	unsigned			 seq;
81842565f2Syasuoka 	char				 session_id[256];
82842565f2Syasuoka 	char				 auth_method[16];
83842565f2Syasuoka 	struct user			*user;
84842565f2Syasuoka 	uint32_t			 session_timeout;
85842565f2Syasuoka 	struct timespec			 start;
86842565f2Syasuoka 	struct timespec			 timeout;
87842565f2Syasuoka 	struct in_addr			 nas_ipv4;
88842565f2Syasuoka 	struct in6_addr			 nas_ipv6;
89842565f2Syasuoka 	char				 nas_id[256];
90842565f2Syasuoka 	const char			*tun_type;
91842565f2Syasuoka 	union {
92842565f2Syasuoka 		struct sockaddr_in	 sin4;
93842565f2Syasuoka 		struct sockaddr_in6	 sin6;
94842565f2Syasuoka 	}				 tun_client;
95842565f2Syasuoka 
96842565f2Syasuoka 	struct timespec			 authtime;
97842565f2Syasuoka 	RB_ENTRY(assigned_ipv4)		 tree;
98842565f2Syasuoka 	TAILQ_ENTRY(assigned_ipv4)	 next;
99842565f2Syasuoka 
100842565f2Syasuoka 	/* RFC 5176 Dynamic Authorization Extensions for RADIUS */
101842565f2Syasuoka 	struct module_ipcp_dae		*dae;
102842565f2Syasuoka 	RADIUS_PACKET			*dae_reqpkt;
103842565f2Syasuoka 	TAILQ_ENTRY(assigned_ipv4)	 dae_next;
104842565f2Syasuoka 	int				 dae_ntry;
105842565f2Syasuoka 	struct event			 dae_evtimer;
106c0127aecSyasuoka 	TAILQ_HEAD(, radiusctl_client)	 dae_clients;
107842565f2Syasuoka };
108842565f2Syasuoka 
109842565f2Syasuoka struct module_ipcp_ctrlconn {
110842565f2Syasuoka 	uint32_t			 peerid;
111842565f2Syasuoka 	TAILQ_ENTRY(module_ipcp_ctrlconn)
112842565f2Syasuoka 					 next;
113842565f2Syasuoka };
114842565f2Syasuoka 
115842565f2Syasuoka struct module_ipcp_dae {
116842565f2Syasuoka 	struct module_ipcp		*ipcp;
117842565f2Syasuoka 	int				 sock;
118842565f2Syasuoka 	char				 nas_id[256];
119842565f2Syasuoka 	char				 secret[80];
120842565f2Syasuoka 	union {
121842565f2Syasuoka 		struct sockaddr_in	 sin4;
122842565f2Syasuoka 		struct sockaddr_in6	 sin6;
123842565f2Syasuoka 	}				 nas_addr;
124842565f2Syasuoka 	struct event			 ev_sock;
125cfa11a85Syasuoka 	struct event			 ev_reqs;
126842565f2Syasuoka 	TAILQ_ENTRY(module_ipcp_dae)	 next;
127842565f2Syasuoka 	TAILQ_HEAD(, assigned_ipv4)	 reqs;
128cfa11a85Syasuoka 	int				 ninflight;
129842565f2Syasuoka };
130842565f2Syasuoka 
131842565f2Syasuoka struct module_ipcp {
132842565f2Syasuoka 	struct module_base		*base;
133842565f2Syasuoka 	int				 nsessions;
134842565f2Syasuoka 	unsigned			 seq;
135842565f2Syasuoka 	int				 max_sessions;
136842565f2Syasuoka 	int				 user_max_sessions;
137842565f2Syasuoka 	int				 start_wait;
138842565f2Syasuoka 	int				 session_timeout;
139842565f2Syasuoka 	bool				 no_session_timeout;
140842565f2Syasuoka 	struct timespec			 uptime;
141842565f2Syasuoka 	struct in_addr			 name_server[2];
142842565f2Syasuoka 	struct in_addr			 netbios_server[2];
143842565f2Syasuoka 	RB_HEAD(assigned_ipv4_tree, assigned_ipv4)
144842565f2Syasuoka 					 ipv4s;
145842565f2Syasuoka 	RB_HEAD(user_tree, user)	 users;
146842565f2Syasuoka 	int				 npools;
147842565f2Syasuoka 	TAILQ_HEAD(,ipcp_address)	 addrs;
148842565f2Syasuoka 	TAILQ_HEAD(,module_ipcp_ctrlconn)
149842565f2Syasuoka 					 ctrls;
150842565f2Syasuoka 	TAILQ_HEAD(,module_ipcp_dae)	 daes;
151842565f2Syasuoka 	struct event			 ev_timer;
152842565f2Syasuoka };
153842565f2Syasuoka 
154842565f2Syasuoka #ifndef nitems
155842565f2Syasuoka #define nitems(_x)    (sizeof((_x)) / sizeof((_x)[0]))
156842565f2Syasuoka #endif
157842565f2Syasuoka 
158842565f2Syasuoka #ifndef MAXIMUM
159842565f2Syasuoka #define MAXIMUM(_a, _b)	(((_a) > (_b))? (_a) : (_b))
160842565f2Syasuoka #endif
161842565f2Syasuoka 
162842565f2Syasuoka static void	 ipcp_init(struct module_ipcp *);
163842565f2Syasuoka static void	 ipcp_start(void *);
164842565f2Syasuoka static void	 ipcp_stop(void *);
165842565f2Syasuoka static void	 ipcp_fini(struct module_ipcp *);
166842565f2Syasuoka static void	 ipcp_config_set(void *, const char *, int, char * const *);
167842565f2Syasuoka static void	 ipcp_dispatch_control(void *, struct imsg *);
168842565f2Syasuoka static int	 ipcp_notice_startstop(struct module_ipcp *,
169842565f2Syasuoka 		    struct assigned_ipv4 *, int,
170842565f2Syasuoka 		    struct radiusd_ipcp_statistics *);
171842565f2Syasuoka static void	 ipcp_resdeco(void *, u_int, const u_char *, size_t reqlen,
172842565f2Syasuoka 		    const u_char *, size_t reslen);
173842565f2Syasuoka static void	 ipcp_reject(struct module_ipcp *, RADIUS_PACKET *,
174842565f2Syasuoka 		    unsigned int, RADIUS_PACKET *, int);
175842565f2Syasuoka static void	 ipcp_accounting_request(void *, u_int, const u_char *,
176842565f2Syasuoka 		    size_t);
177842565f2Syasuoka 
178842565f2Syasuoka struct assigned_ipv4
179842565f2Syasuoka 		*ipcp_ipv4_assign(struct module_ipcp *, struct user *,
180842565f2Syasuoka 		    struct in_addr);
181842565f2Syasuoka static struct assigned_ipv4
182842565f2Syasuoka 		*ipcp_ipv4_find(struct module_ipcp *, struct in_addr);
183eff8f878Syasuoka static void	 ipcp_ipv4_delete(struct module_ipcp *,
184eff8f878Syasuoka 		    struct assigned_ipv4 *, const char *);
185842565f2Syasuoka static void	 ipcp_ipv4_release(struct module_ipcp *,
186842565f2Syasuoka 		    struct assigned_ipv4 *);
187842565f2Syasuoka static int	 assigned_ipv4_compar(struct assigned_ipv4 *,
188842565f2Syasuoka 		    struct assigned_ipv4 *);
189842565f2Syasuoka static struct user
190842565f2Syasuoka 		*ipcp_user_get(struct module_ipcp *, const char *);
191842565f2Syasuoka static int	 user_compar(struct user *, struct user *);
192842565f2Syasuoka static int	 ipcp_prepare_db(void);
193842565f2Syasuoka static int	 ipcp_restore_from_db(struct module_ipcp *);
194842565f2Syasuoka static void	 ipcp_put_db(struct module_ipcp *, struct assigned_ipv4 *);
195842565f2Syasuoka static void	 ipcp_del_db(struct module_ipcp *, struct assigned_ipv4 *);
196842565f2Syasuoka static void	 ipcp_db_dump_fill_record(struct radiusd_ipcp_db_dump *, int,
197842565f2Syasuoka 		    struct assigned_ipv4 *);
198e1af567eSyasuoka static void	 ipcp_update_time(struct module_ipcp *);
199842565f2Syasuoka static void	 ipcp_on_timer(int, short, void *);
200842565f2Syasuoka static void	 ipcp_schedule_timer(struct module_ipcp *);
201842565f2Syasuoka static void	 ipcp_dae_send_disconnect_request(struct assigned_ipv4 *);
202842565f2Syasuoka static void	 ipcp_dae_request_on_timeout(int, short, void *);
203842565f2Syasuoka static void	 ipcp_dae_on_event(int, short, void *);
2042617e43bSyasuoka static void	 ipcp_dae_reset_request(struct assigned_ipv4 *);
205cfa11a85Syasuoka static void	 ipcp_dae_send_pending_requests(int, short, void *);
206842565f2Syasuoka static struct ipcp_address
207842565f2Syasuoka 		*parse_address_range(const char *);
208842565f2Syasuoka static const char
209842565f2Syasuoka 		*radius_tunnel_type_string(unsigned, const char *);
210842565f2Syasuoka static const char
211842565f2Syasuoka 		*radius_terminate_cause_string(unsigned);
212842565f2Syasuoka static const char
213842565f2Syasuoka 		*radius_error_cause_string(unsigned);
214842565f2Syasuoka static int	 parse_addr(const char *, int, struct sockaddr *, socklen_t);
215842565f2Syasuoka static const char
216842565f2Syasuoka 		*print_addr(struct sockaddr *, char *, size_t);
217842565f2Syasuoka 
218842565f2Syasuoka RB_PROTOTYPE_STATIC(assigned_ipv4_tree, assigned_ipv4, tree,
219842565f2Syasuoka     assigned_ipv4_compar);
220842565f2Syasuoka RB_PROTOTYPE_STATIC(user_tree, user, tree, user_compar);
221842565f2Syasuoka 
222842565f2Syasuoka int
223842565f2Syasuoka main(int argc, char *argv[])
224842565f2Syasuoka {
225842565f2Syasuoka 	struct module_ipcp	 module_ipcp;
226842565f2Syasuoka 	struct module_handlers	 handlers = {
227842565f2Syasuoka 		.start =		ipcp_start,
228842565f2Syasuoka 		.stop =			ipcp_stop,
229842565f2Syasuoka 		.config_set =		ipcp_config_set,
230842565f2Syasuoka 		.response_decoration =	ipcp_resdeco,
231842565f2Syasuoka 		.accounting_request =	ipcp_accounting_request,
232842565f2Syasuoka 		.dispatch_control =	ipcp_dispatch_control
233842565f2Syasuoka 	};
234842565f2Syasuoka 
235842565f2Syasuoka 	ipcp_init(&module_ipcp);
236842565f2Syasuoka 
237842565f2Syasuoka 	if ((module_ipcp.base = module_create(STDIN_FILENO, &module_ipcp,
238842565f2Syasuoka 	    &handlers)) == NULL)
239842565f2Syasuoka 		err(1, "Could not create a module instance");
240842565f2Syasuoka 
241842565f2Syasuoka 	if (ipcp_prepare_db() == -1)
242842565f2Syasuoka 		err(1, "ipcp_prepare_db");
243842565f2Syasuoka 
244842565f2Syasuoka 	module_drop_privilege(module_ipcp.base, 1);
245842565f2Syasuoka 	if (unveil(_PATH_RADIUSD_IPCP_DB, "rw") == -1)
246842565f2Syasuoka 		err(1, "unveil");
247842565f2Syasuoka 	if (pledge("stdio inet rpath wpath flock", NULL) == -1)
248842565f2Syasuoka 		err(1, "pledge");
249842565f2Syasuoka 	setproctitle("[main]");
250842565f2Syasuoka 
251842565f2Syasuoka 	module_load(module_ipcp.base);
252842565f2Syasuoka 	log_init(0);
253842565f2Syasuoka 	event_init();
254842565f2Syasuoka 
255842565f2Syasuoka 	module_start(module_ipcp.base);
256842565f2Syasuoka 	event_loop(0);
257842565f2Syasuoka 
258842565f2Syasuoka 	ipcp_fini(&module_ipcp);
259842565f2Syasuoka 
260842565f2Syasuoka 	event_loop(0);
261c0c32a87Syasuoka 	event_base_free(NULL);
262842565f2Syasuoka 
263842565f2Syasuoka 	exit(EXIT_SUCCESS);
264842565f2Syasuoka }
265842565f2Syasuoka 
266842565f2Syasuoka void
267842565f2Syasuoka ipcp_init(struct module_ipcp *self)
268842565f2Syasuoka {
269842565f2Syasuoka 	memset(self, 0, sizeof(struct module_ipcp));
270842565f2Syasuoka 	TAILQ_INIT(&self->addrs);
271842565f2Syasuoka 	RB_INIT(&self->ipv4s);
272842565f2Syasuoka 	RB_INIT(&self->users);
273842565f2Syasuoka 	TAILQ_INIT(&self->ctrls);
274842565f2Syasuoka 	TAILQ_INIT(&self->daes);
275842565f2Syasuoka 	self->seq = 1;
276842565f2Syasuoka 	self->no_session_timeout = true;
277e1af567eSyasuoka 	ipcp_update_time(self);
278842565f2Syasuoka }
279842565f2Syasuoka 
280842565f2Syasuoka void
281842565f2Syasuoka ipcp_start(void *ctx)
282842565f2Syasuoka {
283842565f2Syasuoka 	struct module_ipcp	*self = ctx;
284842565f2Syasuoka 	struct ipcp_address	*addr;
285842565f2Syasuoka 	struct module_ipcp_dae	*dae;
286842565f2Syasuoka 	int			 sock;
287842565f2Syasuoka 
288e1af567eSyasuoka 	ipcp_update_time(self);
289842565f2Syasuoka 	if (self->start_wait == 0)
290842565f2Syasuoka 		self->start_wait = RADIUSD_IPCP_START_WAIT;
291842565f2Syasuoka 
292842565f2Syasuoka 	/* count pool address*/
293842565f2Syasuoka 	TAILQ_FOREACH(addr, &self->addrs, next) {
294842565f2Syasuoka 		if (addr->type == ADDRESS_TYPE_POOL)
295842565f2Syasuoka 			self->npools += addr->naddrs;
296842565f2Syasuoka 	}
297842565f2Syasuoka 	log_info("number of pooled IP addresses = %d", self->npools);
298842565f2Syasuoka 
299842565f2Syasuoka 	if (ipcp_restore_from_db(self) == -1) {
300842565f2Syasuoka 		module_send_message(self->base, IMSG_NG,
301842565f2Syasuoka 		    "Restoring the database failed: %s", strerror(errno));
302842565f2Syasuoka 		module_stop(self->base);
303842565f2Syasuoka 		return;
304842565f2Syasuoka 	}
305842565f2Syasuoka 	ipcp_schedule_timer(self);
306842565f2Syasuoka 
307842565f2Syasuoka 	/* prepare socket for DAE */
308842565f2Syasuoka 	TAILQ_FOREACH(dae, &self->daes, next) {
309842565f2Syasuoka 		if ((sock = socket(dae->nas_addr.sin4.sin_family,
310842565f2Syasuoka 		    SOCK_DGRAM, IPPROTO_UDP)) == -1) {
3114946f694Syasuoka 			log_warn("%s: could not start dae: socket()", __func__);
312842565f2Syasuoka 			return;
313842565f2Syasuoka 		}
314842565f2Syasuoka 		if (connect(sock, (struct sockaddr *)&dae->nas_addr,
315842565f2Syasuoka 		    dae->nas_addr.sin4.sin_len) == -1) {
3164946f694Syasuoka 			log_warn("%s: could not start dae: connect()",
3174946f694Syasuoka 			    __func__);
318842565f2Syasuoka 			return;
319842565f2Syasuoka 		}
320842565f2Syasuoka 		dae->sock = sock;
321842565f2Syasuoka 		event_set(&dae->ev_sock, sock, EV_READ | EV_PERSIST,
322842565f2Syasuoka 		    ipcp_dae_on_event, dae);
323842565f2Syasuoka 		event_add(&dae->ev_sock, NULL);
324cfa11a85Syasuoka 		evtimer_set(&dae->ev_reqs, ipcp_dae_send_pending_requests, dae);
325842565f2Syasuoka 	}
326842565f2Syasuoka 
327842565f2Syasuoka 	module_send_message(self->base, IMSG_OK, NULL);
328842565f2Syasuoka }
329842565f2Syasuoka 
330842565f2Syasuoka void
331842565f2Syasuoka ipcp_stop(void *ctx)
332842565f2Syasuoka {
333842565f2Syasuoka 	struct module_ipcp		*self = ctx;
334842565f2Syasuoka 	struct module_ipcp_dae		*dae;
335842565f2Syasuoka 
336e1af567eSyasuoka 	ipcp_update_time(self);
337842565f2Syasuoka 	/* stop the sockets for DAE */
338842565f2Syasuoka 	TAILQ_FOREACH(dae, &self->daes, next) {
339842565f2Syasuoka 		if (dae->sock >= 0) {
340842565f2Syasuoka 			event_del(&dae->ev_sock);
341842565f2Syasuoka 			close(dae->sock);
342842565f2Syasuoka 			dae->sock = -1;
343842565f2Syasuoka 		}
344cfa11a85Syasuoka 		if (evtimer_pending(&dae->ev_reqs, NULL))
345cfa11a85Syasuoka 			event_del(&dae->ev_reqs);
346842565f2Syasuoka 	}
347842565f2Syasuoka 	if (evtimer_pending(&self->ev_timer, NULL))
348842565f2Syasuoka 		evtimer_del(&self->ev_timer);
349842565f2Syasuoka }
350842565f2Syasuoka 
351842565f2Syasuoka void
352842565f2Syasuoka ipcp_fini(struct module_ipcp *self)
353842565f2Syasuoka {
354842565f2Syasuoka 	struct assigned_ipv4		*assign, *assignt;
355842565f2Syasuoka 	struct user			*user, *usert;
356842565f2Syasuoka 	struct module_ipcp_ctrlconn	*ctrl, *ctrlt;
357842565f2Syasuoka 	struct module_ipcp_dae		*dae, *daet;
358c0c32a87Syasuoka 	struct ipcp_address		*addr, *addrt;
359842565f2Syasuoka 
360842565f2Syasuoka 	RB_FOREACH_SAFE(assign, assigned_ipv4_tree, &self->ipv4s, assignt)
361842565f2Syasuoka 		ipcp_ipv4_release(self, assign);
362c0c32a87Syasuoka 	RB_FOREACH_SAFE(user, user_tree, &self->users, usert) {
363c0c32a87Syasuoka 		RB_REMOVE(user_tree, &self->users, user);
364842565f2Syasuoka 		free(user);
365c0c32a87Syasuoka 	}
366842565f2Syasuoka 	TAILQ_FOREACH_SAFE(ctrl, &self->ctrls, next, ctrlt)
367842565f2Syasuoka 		free(ctrl);
368842565f2Syasuoka 	TAILQ_FOREACH_SAFE(dae, &self->daes, next, daet) {
369842565f2Syasuoka 		if (dae->sock >= 0) {
370842565f2Syasuoka 			event_del(&dae->ev_sock);
371842565f2Syasuoka 			close(dae->sock);
372842565f2Syasuoka 		}
373842565f2Syasuoka 		free(dae);
374842565f2Syasuoka 	}
375c0c32a87Syasuoka 	TAILQ_FOREACH_SAFE(addr, &self->addrs, next, addrt)
376c0c32a87Syasuoka 		free(addr);
377842565f2Syasuoka 	if (evtimer_pending(&self->ev_timer, NULL))
378842565f2Syasuoka 		evtimer_del(&self->ev_timer);
379842565f2Syasuoka 	module_destroy(self->base);
380842565f2Syasuoka }
381842565f2Syasuoka 
382842565f2Syasuoka void
383842565f2Syasuoka ipcp_config_set(void *ctx, const char *name, int argc, char * const * argv)
384842565f2Syasuoka {
385842565f2Syasuoka 	struct module_ipcp	*module = ctx;
386842565f2Syasuoka 	const char		*errmsg = "none";
387842565f2Syasuoka 	int			 i;
388842565f2Syasuoka 	struct ipcp_address	*addr;
389842565f2Syasuoka 	struct in_addr		 ina;
390842565f2Syasuoka 	struct module_ipcp_dae	 dae, *dae0;
391842565f2Syasuoka 
392842565f2Syasuoka 	if (strcmp(name, "address") == 0) {
393842565f2Syasuoka 		SYNTAX_ASSERT(argc >= 1,
394842565f2Syasuoka 		    "specify one of pool, server, nas-select, or user-select");
395842565f2Syasuoka 		if (strcmp(argv[0], "pool") == 0) {
396842565f2Syasuoka 			SYNTAX_ASSERT(argc >= 2,
397842565f2Syasuoka 			    "`address pool' must have one address range at "
398842565f2Syasuoka 			    "least");
399842565f2Syasuoka 			addr = TAILQ_FIRST(&module->addrs);
400842565f2Syasuoka 			for (i = 0; i < argc - 1; i++) {
401842565f2Syasuoka 				if ((addr = parse_address_range(argv[i + 1]))
402842565f2Syasuoka 				    == NULL) {
403842565f2Syasuoka 					module_send_message(module->base,
404842565f2Syasuoka 					    IMSG_NG, "Invalid address range: "
405842565f2Syasuoka 					    "%s", argv[i + 1]);
406842565f2Syasuoka 					return;
407842565f2Syasuoka 				}
408842565f2Syasuoka 				addr->type = ADDRESS_TYPE_POOL;
409842565f2Syasuoka 				TAILQ_INSERT_TAIL(&module->addrs, addr, next);
410842565f2Syasuoka 			}
411842565f2Syasuoka 		} else if (strcmp(argv[0], "static") == 0) {
412842565f2Syasuoka 			SYNTAX_ASSERT(argc >= 2,
413842565f2Syasuoka 			    "`address static' must have one address range at "
414842565f2Syasuoka 			    "least");
415842565f2Syasuoka 			addr = TAILQ_FIRST(&module->addrs);
416842565f2Syasuoka 			for (i = 0; i < argc - 1; i++) {
417842565f2Syasuoka 				if ((addr = parse_address_range(argv[i + 1]))
418842565f2Syasuoka 				    == NULL) {
419842565f2Syasuoka 					module_send_message(module->base,
420842565f2Syasuoka 					    IMSG_NG, "Invalid address range: "
421842565f2Syasuoka 					    "%s", argv[i + 1]);
422842565f2Syasuoka 					return;
423842565f2Syasuoka 				}
424842565f2Syasuoka 				addr->type = ADDRESS_TYPE_STATIC;
425842565f2Syasuoka 				TAILQ_INSERT_TAIL(&module->addrs, addr, next);
426842565f2Syasuoka 			}
427842565f2Syasuoka 		} else
428842565f2Syasuoka 			SYNTAX_ASSERT(0, "specify pool or static");
429842565f2Syasuoka 	} else if (strcmp(name, "max-sessions") == 0) {
430842565f2Syasuoka 		SYNTAX_ASSERT(argc == 1,
431842565f2Syasuoka 		    "`max-sessions' must have an argument");
432842565f2Syasuoka 		module->max_sessions = strtonum(argv[0], 0, INT_MAX, &errmsg);
433842565f2Syasuoka 		if (errmsg != NULL) {
434842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
435842565f2Syasuoka 			    "could not parse `max-sessions': %s", errmsg);
436842565f2Syasuoka 			return;
437842565f2Syasuoka 		}
438842565f2Syasuoka 	} else if (strcmp(name, "user-max-sessions") == 0) {
439842565f2Syasuoka 		SYNTAX_ASSERT(argc == 1, "`max-session' must have an argument");
440842565f2Syasuoka 		module->user_max_sessions = strtonum(argv[0], 0, INT_MAX,
441842565f2Syasuoka 		    &errmsg);
442842565f2Syasuoka 		if (errmsg != NULL) {
443842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
444842565f2Syasuoka 			    "could not parse `user-max-session': %s", errmsg);
445842565f2Syasuoka 			return;
446842565f2Syasuoka 		}
447842565f2Syasuoka 	} else if (strcmp(name, "start-wait") == 0) {
448842565f2Syasuoka 		SYNTAX_ASSERT(argc == 1, "`start-wait' must have an argument");
449842565f2Syasuoka 		module->start_wait = strtonum(argv[0], 1, INT_MAX, &errmsg);
450842565f2Syasuoka 		if (errmsg != NULL) {
451842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
452842565f2Syasuoka 			    "could not parse `start-wait': %s", errmsg);
453842565f2Syasuoka 			return;
454842565f2Syasuoka 		}
455842565f2Syasuoka 	} else if (strcmp(name, "name-server") == 0) {
456842565f2Syasuoka 		SYNTAX_ASSERT(argc == 1 || argc == 2,
457842565f2Syasuoka 		    "specify 1 or 2 addresses for `name-server'");
458842565f2Syasuoka 		for (i = 0; i < argc; i++) {
45984b182f8Sflorian 			if (inet_pton(AF_INET, argv[i], &ina) != 1) {
460842565f2Syasuoka 				module_send_message(module->base, IMSG_NG,
461842565f2Syasuoka 				    "Invalid IP address: %s", argv[i]);
462842565f2Syasuoka 				return;
463842565f2Syasuoka 			}
464842565f2Syasuoka 			if (module->name_server[0].s_addr == 0)
465842565f2Syasuoka 				module->name_server[0] = ina;
466842565f2Syasuoka 			else if (module->name_server[1].s_addr == 0)
467842565f2Syasuoka 				module->name_server[1] = ina;
468842565f2Syasuoka 			else
469842565f2Syasuoka 				SYNTAX_ASSERT(0,
470842565f2Syasuoka 				    "too many `name-server' is configured");
471842565f2Syasuoka 		}
472842565f2Syasuoka 	} else if (strcmp(name, "netbios-server") == 0) {
473842565f2Syasuoka 		SYNTAX_ASSERT(argc == 1 || argc == 2,
474842565f2Syasuoka 		    "specify 1 or 2 addresses for `name-server'");
475842565f2Syasuoka 		for (i = 0; i < argc; i++) {
47684b182f8Sflorian 			if (inet_pton(AF_INET, argv[i], &ina) != 1) {
477842565f2Syasuoka 				module_send_message(module->base, IMSG_NG,
478842565f2Syasuoka 				    "Invalid IP address: %s", argv[i]);
479842565f2Syasuoka 				return;
480842565f2Syasuoka 			}
481842565f2Syasuoka 			if (module->netbios_server[0].s_addr == 0)
482842565f2Syasuoka 				module->netbios_server[0] = ina;
483842565f2Syasuoka 			else if (module->netbios_server[1].s_addr == 0)
484842565f2Syasuoka 				module->netbios_server[1] = ina;
485842565f2Syasuoka 			else
486842565f2Syasuoka 				SYNTAX_ASSERT(0,
487842565f2Syasuoka 				    "too many `name-server' is configured");
488842565f2Syasuoka 		}
489842565f2Syasuoka 	} else if (strcmp(name, "session-timeout") == 0) {
490842565f2Syasuoka 		SYNTAX_ASSERT(argc == 1,
491842565f2Syasuoka 		    "`session-timeout' must have an argument");
492842565f2Syasuoka 		if (strcmp(argv[0], "radius") == 0) {
493842565f2Syasuoka 			module->no_session_timeout = false;
494842565f2Syasuoka 			module->session_timeout = 0;
495842565f2Syasuoka 		} else {
496842565f2Syasuoka 			module->no_session_timeout = false;
497842565f2Syasuoka 			module->session_timeout = strtonum(argv[0], 1, INT_MAX,
498842565f2Syasuoka 			    &errmsg);
499842565f2Syasuoka 			if (errmsg != NULL) {
500842565f2Syasuoka 				module_send_message(module->base, IMSG_NG,
501842565f2Syasuoka 				    "could not parse `session-timeout': %s",
502842565f2Syasuoka 				    errmsg);
503842565f2Syasuoka 				return;
504842565f2Syasuoka 			}
505842565f2Syasuoka 		}
506842565f2Syasuoka 	} else if (strcmp(name, "dae") == 0) {
50732ed9376Syasuoka 		memset(&dae, 0, sizeof(dae));
50832ed9376Syasuoka 		dae.sock = -1;
509842565f2Syasuoka 		if (!(argc >= 1 || strcmp(argv[1], "server") == 0)) {
510842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
511842565f2Syasuoka 			    "`%s' is unknown", argv[1]);
512842565f2Syasuoka 			return;
513842565f2Syasuoka 		}
514842565f2Syasuoka 		i = 1;
515842565f2Syasuoka 		SYNTAX_ASSERT(i < argc, "no address[:port] for dae server");
516842565f2Syasuoka 		if (i < argc &&
517842565f2Syasuoka 		    parse_addr(argv[i], AF_UNSPEC, (struct sockaddr *)
518842565f2Syasuoka 		    &dae.nas_addr, sizeof(dae.nas_addr)) == -1) {
519842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
520842565f2Syasuoka 			    "failed to parse dae server's address, %s",
521842565f2Syasuoka 			    argv[i]);
522842565f2Syasuoka 			return;
523842565f2Syasuoka 		}
524842565f2Syasuoka 		if (ntohs(dae.nas_addr.sin4.sin_port) == 0)
525842565f2Syasuoka 			dae.nas_addr.sin4.sin_port =
526842565f2Syasuoka 			    htons(RADIUS_DAE_DEFAULT_PORT);
527842565f2Syasuoka 		i++;
528842565f2Syasuoka 		SYNTAX_ASSERT(i < argc, "no secret for dae server");
529842565f2Syasuoka 		if (strlcpy(dae.secret, argv[i++], sizeof(dae.secret)) >=
530842565f2Syasuoka 		    sizeof(dae.secret)) {
531842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
532842565f2Syasuoka 			    "dae server's secret must be < %d bytes",
533842565f2Syasuoka 			    (int)sizeof(dae.secret) - 1);
534842565f2Syasuoka 			return;
535842565f2Syasuoka 		}
536842565f2Syasuoka 		if (i < argc)
537842565f2Syasuoka 			strlcpy(dae.nas_id, argv[i++], sizeof(dae.nas_id));
538842565f2Syasuoka 		if ((dae0 = calloc(1, sizeof(struct module_ipcp_dae))) == NULL)
539842565f2Syasuoka 		{
540842565f2Syasuoka 			module_send_message(module->base, IMSG_NG,
541842565f2Syasuoka 			    "%s", strerror(errno));
542842565f2Syasuoka 			return;
543842565f2Syasuoka 		}
544842565f2Syasuoka 		*dae0 = dae;
545842565f2Syasuoka 		TAILQ_INIT(&dae0->reqs);
546842565f2Syasuoka 		TAILQ_INSERT_TAIL(&module->daes, dae0, next);
547c0127aecSyasuoka 		dae0->ipcp = module;
548842565f2Syasuoka 	} else if (strcmp(name, "_debug") == 0)
549842565f2Syasuoka 		log_init(1);
550842565f2Syasuoka 	else if (strncmp(name, "_", 1) == 0)
551842565f2Syasuoka 		/* ignore */;
552842565f2Syasuoka 	else {
553842565f2Syasuoka 		module_send_message(module->base, IMSG_NG,
554842565f2Syasuoka 		    "Unknown config parameter name `%s'", name);
555842565f2Syasuoka 		return;
556842565f2Syasuoka 	}
557842565f2Syasuoka 	module_send_message(module->base, IMSG_OK, NULL);
558842565f2Syasuoka 
559842565f2Syasuoka 	return;
560842565f2Syasuoka  syntax_error:
561842565f2Syasuoka 	module_send_message(module->base, IMSG_NG, "%s", errmsg);
562842565f2Syasuoka }
563842565f2Syasuoka 
564842565f2Syasuoka void
565842565f2Syasuoka ipcp_dispatch_control(void *ctx, struct imsg *imsg)
566842565f2Syasuoka {
567842565f2Syasuoka 	struct module_ipcp		*self = ctx;
568842565f2Syasuoka 	struct assigned_ipv4		*assign;
569842565f2Syasuoka 	struct radiusd_ipcp_db_dump	*dump;
570842565f2Syasuoka 	struct module_ipcp_ctrlconn	*ctrl, *ctrlt;
571842565f2Syasuoka 	int				 i;
572842565f2Syasuoka 	size_t				 dumpsiz;
573842565f2Syasuoka 	u_int				 datalen;
574842565f2Syasuoka 	unsigned			 seq;
575c0127aecSyasuoka 	struct radiusctl_client		*client;
576c0127aecSyasuoka 	const char			*cause;
577842565f2Syasuoka 
578e1af567eSyasuoka 	ipcp_update_time(self);
579842565f2Syasuoka 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
580842565f2Syasuoka 	switch (imsg->hdr.type) {
581842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_CTRL_UNBIND:
582842565f2Syasuoka 		TAILQ_FOREACH_SAFE(ctrl, &self->ctrls, next, ctrlt) {
583842565f2Syasuoka 			if (ctrl->peerid == imsg->hdr.peerid) {
584842565f2Syasuoka 				TAILQ_REMOVE(&self->ctrls, ctrl, next);
585842565f2Syasuoka 				free(ctrl);
586842565f2Syasuoka 				break;
587842565f2Syasuoka 			}
588842565f2Syasuoka 		}
589842565f2Syasuoka 		break;
590842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_MONITOR:
591842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_DUMP_AND_MONITOR:
592842565f2Syasuoka 		if ((ctrl = calloc(1, sizeof(struct module_ipcp_ctrlconn)))
593842565f2Syasuoka 		    == NULL) {
594842565f2Syasuoka 			log_warn("%s: calloc()", __func__);
595842565f2Syasuoka 			goto fail;
596842565f2Syasuoka 		}
597842565f2Syasuoka 		ctrl->peerid = imsg->hdr.peerid;
598842565f2Syasuoka 		TAILQ_INSERT_TAIL(&self->ctrls, ctrl, next);
599842565f2Syasuoka 		module_imsg_compose(self->base, IMSG_RADIUSD_MODULE_CTRL_BIND,
600842565f2Syasuoka 		    imsg->hdr.peerid, 0, -1, NULL, 0);
601842565f2Syasuoka 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_IPCP_MONITOR)
602842565f2Syasuoka 			break;
6037f966493Sjsg 		/* FALLTHROUGH */
604842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_DUMP:
605842565f2Syasuoka 		dumpsiz = MAX_IMSGSIZE;
606842565f2Syasuoka 		if ((dump = calloc(1, dumpsiz)) == NULL) {
607842565f2Syasuoka 			log_warn("%s: calloc()", __func__);
608842565f2Syasuoka 			goto fail;
609842565f2Syasuoka 		}
610842565f2Syasuoka 		i = 0;
611842565f2Syasuoka 		RB_FOREACH(assign, assigned_ipv4_tree, &self->ipv4s) {
612842565f2Syasuoka 			if (!timespecisset(&assign->start))
613842565f2Syasuoka 				/* not started yet */
614842565f2Syasuoka 				continue;
615842565f2Syasuoka 			ipcp_db_dump_fill_record(dump, i++, assign);
616842565f2Syasuoka 			if (RB_NEXT(assigned_ipv4_tree, &self->ipv4s, assign)
617842565f2Syasuoka 			    == NULL)
618842565f2Syasuoka 				break;
619842565f2Syasuoka 			if (offsetof(struct radiusd_ipcp_db_dump,
620842565f2Syasuoka 			    records[i + 1]) >= dumpsiz) {
621842565f2Syasuoka 				module_imsg_compose(self->base,
622842565f2Syasuoka 				    IMSG_RADIUSD_MODULE_IPCP_DUMP,
623842565f2Syasuoka 				    imsg->hdr.peerid, 0, -1,
624842565f2Syasuoka 				    dump, offsetof(struct radiusd_ipcp_db_dump,
625842565f2Syasuoka 				    records[i]));
626842565f2Syasuoka 				i = 0;
627842565f2Syasuoka 			}
628842565f2Syasuoka 		}
629842565f2Syasuoka 		dump->islast = 1;
630842565f2Syasuoka 		module_imsg_compose(self->base, IMSG_RADIUSD_MODULE_IPCP_DUMP,
631842565f2Syasuoka 		    imsg->hdr.peerid, 0, -1, dump, offsetof(
632842565f2Syasuoka 		    struct radiusd_ipcp_db_dump, records[i]));
633842565f2Syasuoka 		freezero(dump ,dumpsiz);
634842565f2Syasuoka 		break;
635842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_DISCONNECT:
636eff8f878Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_DELETE:
637842565f2Syasuoka 		if (datalen < sizeof(unsigned)) {
638842565f2Syasuoka 			log_warn("%s: received "
639eff8f878Syasuoka 			    "%s message size is wrong", __func__,
640eff8f878Syasuoka 			    (imsg->hdr.type ==
641eff8f878Syasuoka 			    IMSG_RADIUSD_MODULE_IPCP_DISCONNECT)
642eff8f878Syasuoka 			    ? "IMSG_RADIUSD_MODULE_IPCP_DISCONNECT"
643eff8f878Syasuoka 			    : "IMSG_RADIUSD_MODULE_IPCP_DELETE");
644842565f2Syasuoka 			goto fail;
645842565f2Syasuoka 		}
646842565f2Syasuoka 		seq = *(unsigned *)imsg->data;
647842565f2Syasuoka 		RB_FOREACH(assign, assigned_ipv4_tree, &self->ipv4s) {
648842565f2Syasuoka 			if (!timespecisset(&assign->start))
649842565f2Syasuoka 				/* not started yet */
650842565f2Syasuoka 				continue;
651842565f2Syasuoka 			if (assign->seq == seq)
652842565f2Syasuoka 				break;
653842565f2Syasuoka 		}
654c0127aecSyasuoka 		if (assign == NULL) {
655c0127aecSyasuoka 			cause = "session not found";
656eff8f878Syasuoka 			log_warnx("%s seq=%u requested, but the "
657eff8f878Syasuoka 			    "session is not found",
658eff8f878Syasuoka 			    (imsg->hdr.type ==
659eff8f878Syasuoka 			    IMSG_RADIUSD_MODULE_IPCP_DISCONNECT)? "Disconnect"
660eff8f878Syasuoka 			    : "Delete", seq);
661c0127aecSyasuoka 			module_imsg_compose(self->base, IMSG_NG,
662c0127aecSyasuoka 			    imsg->hdr.peerid, 0, -1, cause, strlen(cause) + 1);
663eff8f878Syasuoka 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_IPCP_DELETE) {
664eff8f878Syasuoka 			log_info("Delete seq=%u by request", assign->seq);
665eff8f878Syasuoka 			ipcp_ipv4_delete(self,  assign, "By control");
666eff8f878Syasuoka 			module_imsg_compose(self->base, IMSG_OK,
667eff8f878Syasuoka 			    imsg->hdr.peerid, 0, -1, NULL, 0);
668eff8f878Syasuoka 		} else {
669842565f2Syasuoka 			if (assign->dae == NULL)
670842565f2Syasuoka 				log_warnx("Disconnect seq=%u requested, but "
671842565f2Syasuoka 				    "DAE is not configured", assign->seq);
672842565f2Syasuoka 			else {
673003dbb67Syasuoka 				log_info("Disconnect seq=%u requested",
674842565f2Syasuoka 				    assign->seq);
675c0127aecSyasuoka 				if ((client = calloc(1, sizeof(struct
676c0127aecSyasuoka 				    radiusctl_client))) == NULL) {
677c0127aecSyasuoka 					log_warn("%s: calloc: %m",
678c0127aecSyasuoka 					    __func__);
679c0127aecSyasuoka 					goto fail;
680c0127aecSyasuoka 				}
681c0127aecSyasuoka 				client->peerid = imsg->hdr.peerid;
6822617e43bSyasuoka 				if (assign->dae_ntry == 0)
6832617e43bSyasuoka 					ipcp_dae_send_disconnect_request(
6842617e43bSyasuoka 					    assign);
685c0127aecSyasuoka 				TAILQ_INSERT_TAIL(&assign->dae_clients,
686c0127aecSyasuoka 				    client, entry);
687842565f2Syasuoka 			}
688842565f2Syasuoka 		}
689842565f2Syasuoka 		break;
690842565f2Syasuoka 	}
691842565f2Syasuoka 	return;
692842565f2Syasuoka  fail:
693842565f2Syasuoka 	module_stop(self->base);
694842565f2Syasuoka }
695842565f2Syasuoka 
696842565f2Syasuoka int
697842565f2Syasuoka ipcp_notice_startstop(struct module_ipcp *self, struct assigned_ipv4 *assign,
698842565f2Syasuoka     int start, struct radiusd_ipcp_statistics *stat)
699842565f2Syasuoka {
700842565f2Syasuoka 	struct module_ipcp_ctrlconn	*ctrl;
701842565f2Syasuoka 	struct radiusd_ipcp_db_dump	*dump;
702842565f2Syasuoka 	size_t				 dumpsiz;
703842565f2Syasuoka 	struct iovec			 iov[2];
704842565f2Syasuoka 	int				 niov = 0;
705842565f2Syasuoka 
706842565f2Syasuoka 	dumpsiz = offsetof(struct radiusd_ipcp_db_dump, records[1]);
707842565f2Syasuoka 	if ((dump = calloc(1, dumpsiz)) == NULL) {
708842565f2Syasuoka 		log_warn("%s: calloc()", __func__);
709842565f2Syasuoka 		return (-1);
710842565f2Syasuoka 	}
711842565f2Syasuoka 	dump->islast = 1;
712842565f2Syasuoka 	ipcp_db_dump_fill_record(dump, 0, assign);
713842565f2Syasuoka 
714842565f2Syasuoka 	iov[niov].iov_base = dump;
715842565f2Syasuoka 	iov[niov].iov_len = dumpsiz;
716842565f2Syasuoka 	if (start == 0) {
717842565f2Syasuoka 		iov[++niov].iov_base = stat;
718842565f2Syasuoka 		iov[niov].iov_len = sizeof(struct radiusd_ipcp_statistics);
719842565f2Syasuoka 	}
720842565f2Syasuoka 	TAILQ_FOREACH(ctrl, &self->ctrls, next)
721842565f2Syasuoka 		module_imsg_composev(self->base,
722842565f2Syasuoka 		    (start)? IMSG_RADIUSD_MODULE_IPCP_START :
723842565f2Syasuoka 		    IMSG_RADIUSD_MODULE_IPCP_STOP, ctrl->peerid, 0, -1, iov,
724842565f2Syasuoka 		    niov + 1);
725842565f2Syasuoka 	freezero(dump, dumpsiz);
726842565f2Syasuoka 	return (0);
727842565f2Syasuoka }
728842565f2Syasuoka 
729842565f2Syasuoka void
730842565f2Syasuoka ipcp_resdeco(void *ctx, u_int q_id, const u_char *req, size_t reqlen,
731842565f2Syasuoka     const u_char *res, size_t reslen)
732842565f2Syasuoka {
733842565f2Syasuoka 	struct module_ipcp	*self = ctx;
734842565f2Syasuoka 	RADIUS_PACKET		*radres = NULL, *radreq = NULL;
735842565f2Syasuoka 	struct in_addr		 addr4;
736842565f2Syasuoka 	const struct in_addr	 mask4 = { .s_addr = 0xffffffffUL };
737842565f2Syasuoka 	int			 res_code, msraserr = 935;
738842565f2Syasuoka 	struct ipcp_address	*addr;
7391669612dSyasuoka 	int			 i, n;
740842565f2Syasuoka 	bool			 found = false;
741842565f2Syasuoka 	char			 username[256], buf[128];
742842565f2Syasuoka 	struct user		*user = NULL;
743842565f2Syasuoka 	struct assigned_ipv4	*assigned = NULL, *assign;
744842565f2Syasuoka 
745e1af567eSyasuoka 	ipcp_update_time(self);
746842565f2Syasuoka 
747842565f2Syasuoka 	if ((radres = radius_convert_packet(res, reslen)) == NULL) {
748842565f2Syasuoka 		log_warn("%s: radius_convert_packet() failed", __func__);
749842565f2Syasuoka 		goto fatal;
750842565f2Syasuoka 	}
751842565f2Syasuoka 	res_code = radius_get_code(radres);
752842565f2Syasuoka 	if (res_code != RADIUS_CODE_ACCESS_ACCEPT)
753842565f2Syasuoka 		goto accept;
754842565f2Syasuoka 
755842565f2Syasuoka 	if ((radreq = radius_convert_packet(req, reqlen)) == NULL) {
756842565f2Syasuoka 		log_warn("%s: radius_convert_packet() failed", __func__);
757842565f2Syasuoka 		goto fatal;
758842565f2Syasuoka 	}
759842565f2Syasuoka 
760842565f2Syasuoka 	/*
761842565f2Syasuoka 	 * prefer User-Name of the response rather than the request,
762842565f2Syasuoka 	 * since it must be the authenticated user.
763842565f2Syasuoka 	 */
764842565f2Syasuoka 	if (radius_get_string_attr(radres, RADIUS_TYPE_USER_NAME, username,
765842565f2Syasuoka 	    sizeof(username)) != 0 &&
766842565f2Syasuoka 	    radius_get_string_attr(radreq, RADIUS_TYPE_USER_NAME, username,
767842565f2Syasuoka 	    sizeof(username)) != 0) {
768842565f2Syasuoka 		log_warnx("q=%u unexpected request: no user-name", q_id);
769842565f2Syasuoka 		goto fatal;
770842565f2Syasuoka 	}
771842565f2Syasuoka 
772842565f2Syasuoka 	if ((addr = TAILQ_FIRST(&self->addrs)) != NULL) {
773842565f2Syasuoka 		/* The address assignment is configured */
774842565f2Syasuoka 
775842565f2Syasuoka 		if ((user = ipcp_user_get(self, username)) == NULL) {
776842565f2Syasuoka 			log_warn("%s: ipcp_user_get()", __func__);
777842565f2Syasuoka 			goto fatal;
778842565f2Syasuoka 		}
779842565f2Syasuoka 
780842565f2Syasuoka 		msraserr = 935;
781842565f2Syasuoka 		if (self->max_sessions != 0) {
782842565f2Syasuoka 			if (self->nsessions >= self->max_sessions) {
7837af3c895Syasuoka 				log_info("q=%u user=%s rejected: number of "
784842565f2Syasuoka 				    "sessions reached the limit(%d)", q_id,
7857af3c895Syasuoka 				    user->name, self->max_sessions);
786842565f2Syasuoka 				goto reject;
787842565f2Syasuoka 			}
788842565f2Syasuoka 		}
789842565f2Syasuoka 		if (self->user_max_sessions != 0) {
790842565f2Syasuoka 			n = 0;
791842565f2Syasuoka 			TAILQ_FOREACH(assign, &user->ipv4s, next)
792842565f2Syasuoka 				n++;
793842565f2Syasuoka 			if (n >= self->user_max_sessions) {
7947af3c895Syasuoka 				log_info("q=%u user=%s rejected: number of "
795842565f2Syasuoka 				    "sessions per a user reached the limit(%d)",
7967af3c895Syasuoka 				    q_id, user->name, self->user_max_sessions);
797842565f2Syasuoka 				goto reject;
798842565f2Syasuoka 			}
799842565f2Syasuoka 		}
800842565f2Syasuoka 
801842565f2Syasuoka 		msraserr = 716;
802842565f2Syasuoka 		if (radius_get_ipv4_attr(radres,
803842565f2Syasuoka 		    RADIUS_TYPE_FRAMED_IP_ADDRESS, &addr4) == 0) {
804842565f2Syasuoka 			if (ipcp_ipv4_find(self, addr4) != NULL)
8057af3c895Syasuoka 				log_info("q=%u user=%s rejected: server "
8067af3c895Syasuoka 				    "requested IP address is busy", q_id,
8077af3c895Syasuoka 				    user->name);
808842565f2Syasuoka 			else {
809842565f2Syasuoka 				/* compare in host byte order */
810842565f2Syasuoka 				addr4.s_addr = ntohl(addr4.s_addr);
811842565f2Syasuoka 				TAILQ_FOREACH(addr, &self->addrs, next) {
812842565f2Syasuoka 					if (addr->type != ADDRESS_TYPE_STATIC &&
813842565f2Syasuoka 					    addr->type != ADDRESS_TYPE_POOL)
814842565f2Syasuoka 						continue;
815842565f2Syasuoka 					if (addr->start.s_addr <= addr4.s_addr
816842565f2Syasuoka 					    && addr4.s_addr <= addr->end.s_addr)
817842565f2Syasuoka 						break;
818842565f2Syasuoka 				}
819842565f2Syasuoka 				if (addr == NULL)
8207af3c895Syasuoka 					log_info("q=%u user=%s rejected: "
8217af3c895Syasuoka 					    "server requested IP address is "
8227af3c895Syasuoka 					    "out of the range", q_id,
8237af3c895Syasuoka 					    user->name);
824842565f2Syasuoka 				else
825842565f2Syasuoka 					found = true;
826842565f2Syasuoka 				/* revert the addr to the network byte order */
827842565f2Syasuoka 				addr4.s_addr = htonl(addr4.s_addr);
828842565f2Syasuoka 			}
829842565f2Syasuoka 			if (!found)
830842565f2Syasuoka 				goto reject;
831842565f2Syasuoka 		} else {
832266241f9Syasuoka 			int inpool_idx = 0;
833266241f9Syasuoka 
834266241f9Syasuoka 			/* select a random address */
83577474275Syasuoka 			n = arc4random_uniform(self->npools);
836842565f2Syasuoka 			i = 0;
837842565f2Syasuoka 			TAILQ_FOREACH(addr, &self->addrs, next) {
838842565f2Syasuoka 				if (addr->type == ADDRESS_TYPE_POOL) {
839842565f2Syasuoka 					if (i <= n && n < i + addr->naddrs) {
840266241f9Syasuoka 						inpool_idx = n - i;
841842565f2Syasuoka 						break;
842842565f2Syasuoka 					}
843842565f2Syasuoka 					i += addr->naddrs;
844842565f2Syasuoka 				}
845842565f2Syasuoka 			}
846266241f9Syasuoka 			/* loop npools times until a free address is found */
847266241f9Syasuoka 			for (i = 0; i < self->npools && addr != NULL; i++) {
848266241f9Syasuoka 				addr4.s_addr = htonl(
849266241f9Syasuoka 				    addr->start.s_addr + inpool_idx);
850842565f2Syasuoka 				if (ipcp_ipv4_find(self, addr4) == NULL) {
851842565f2Syasuoka 					found = true;
852842565f2Syasuoka 					break;
853842565f2Syasuoka 				}
854266241f9Syasuoka 				/* try inpool_idx if it's in the range */
855266241f9Syasuoka 				if (++inpool_idx < addr->naddrs)
856266241f9Syasuoka 					continue;
857266241f9Syasuoka 				/* iterate addr to the next pool */
858266241f9Syasuoka 				do {
859266241f9Syasuoka 					addr = TAILQ_NEXT(addr, next);
860266241f9Syasuoka 					if (addr == NULL)
861266241f9Syasuoka 						addr = TAILQ_FIRST(
862266241f9Syasuoka 						    &self->addrs);
863266241f9Syasuoka 				} while (addr->type != ADDRESS_TYPE_POOL);
864266241f9Syasuoka 				inpool_idx = 0;	/* try the first */
865842565f2Syasuoka 			}
866842565f2Syasuoka 			if (!found) {
8677af3c895Syasuoka 				log_info("q=%u user=%s rejected: ran out of "
8687af3c895Syasuoka 				    "the address pool", q_id, user->name);
869842565f2Syasuoka 				goto reject;
870842565f2Syasuoka 			}
871842565f2Syasuoka 		}
872842565f2Syasuoka 		if ((assigned = ipcp_ipv4_assign(self, user, addr4)) == NULL) {
873842565f2Syasuoka 			log_warn("%s: ipcp_ipv4_assign()", __func__);
874842565f2Syasuoka 			goto fatal;
875842565f2Syasuoka 		}
876842565f2Syasuoka 		radius_set_ipv4_attr(radres, RADIUS_TYPE_FRAMED_IP_NETMASK,
877842565f2Syasuoka 		    mask4);
878842565f2Syasuoka 		radius_del_attr_all(radres, RADIUS_TYPE_FRAMED_IP_ADDRESS);
879842565f2Syasuoka 		radius_put_ipv4_attr(radres, RADIUS_TYPE_FRAMED_IP_ADDRESS,
880842565f2Syasuoka 		    addr4);
881842565f2Syasuoka 		log_info("q=%u Assign %s for %s", q_id,
882842565f2Syasuoka 		    inet_ntop(AF_INET, &addr4, buf, sizeof(buf)), username);
883842565f2Syasuoka 		if (radius_has_attr(radreq, RADIUS_TYPE_USER_PASSWORD))
884842565f2Syasuoka 			strlcpy(assigned->auth_method, "PAP",
885842565f2Syasuoka 			    sizeof(assigned->auth_method));
886842565f2Syasuoka 		else if (radius_has_attr(radreq, RADIUS_TYPE_CHAP_PASSWORD))
887842565f2Syasuoka 			strlcpy(assigned->auth_method, "CHAP",
888842565f2Syasuoka 			    sizeof(assigned->auth_method));
889842565f2Syasuoka 		else if (radius_has_vs_attr(radreq, RADIUS_VENDOR_MICROSOFT,
890842565f2Syasuoka 		    RADIUS_VTYPE_MS_CHAP_RESPONSE))
891842565f2Syasuoka 			strlcpy(assigned->auth_method, "MS-CHAP",
892842565f2Syasuoka 			    sizeof(assigned->auth_method));
893842565f2Syasuoka 		else if (radius_has_vs_attr(radreq, RADIUS_VENDOR_MICROSOFT,
894842565f2Syasuoka 		    RADIUS_VTYPE_MS_CHAP2_RESPONSE))
895842565f2Syasuoka 			strlcpy(assigned->auth_method, "MS-CHAP-V2",
896842565f2Syasuoka 			    sizeof(assigned->auth_method));
897842565f2Syasuoka 		else if (radius_has_attr(radreq, RADIUS_TYPE_EAP_MESSAGE))
898842565f2Syasuoka 			strlcpy(assigned->auth_method, "EAP",
899842565f2Syasuoka 			    sizeof(assigned->auth_method));
900*aae7f363Syasuoka 
901*aae7f363Syasuoka 		radius_get_ipv4_attr(radreq, RADIUS_TYPE_NAS_IP_ADDRESS,
902*aae7f363Syasuoka 		    &assigned->nas_ipv4);
903*aae7f363Syasuoka 		radius_get_ipv6_attr(radreq, RADIUS_TYPE_NAS_IPV6_ADDRESS,
904*aae7f363Syasuoka 		    &assigned->nas_ipv6);
905*aae7f363Syasuoka 		radius_get_string_attr(radreq, RADIUS_TYPE_NAS_IDENTIFIER,
906*aae7f363Syasuoka 		    assigned->nas_id, sizeof(assigned->nas_id));
907842565f2Syasuoka 	}
908842565f2Syasuoka 
909842565f2Syasuoka 	if (self->name_server[0].s_addr != 0) {
910842565f2Syasuoka 		addr4.s_addr = htonl(self->name_server[0].s_addr);
911842565f2Syasuoka 		radius_del_vs_attr_all(radres,
912842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
913842565f2Syasuoka 		    RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER);
914842565f2Syasuoka 		radius_put_vs_ipv4_attr(radres,
915842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
916842565f2Syasuoka 		    RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER, self->name_server[0]);
917842565f2Syasuoka 	}
918842565f2Syasuoka 	if (self->name_server[1].s_addr != 0) {
919842565f2Syasuoka 		addr4.s_addr = htonl(self->name_server[1].s_addr);
920842565f2Syasuoka 		radius_del_vs_attr_all(radres,
921842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
922842565f2Syasuoka 		    RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER);
923842565f2Syasuoka 		radius_put_vs_ipv4_attr(radres,
924842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
925842565f2Syasuoka 		    RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER, self->name_server[1]);
926842565f2Syasuoka 	}
927842565f2Syasuoka 	if (self->netbios_server[0].s_addr != 0) {
928842565f2Syasuoka 		addr4.s_addr = htonl(self->netbios_server[0].s_addr);
929842565f2Syasuoka 		radius_del_vs_attr_all(radres,
930842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
931842565f2Syasuoka 		    RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER);
932842565f2Syasuoka 		radius_put_vs_ipv4_attr(radres,
933842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
934842565f2Syasuoka 		    RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER,
935842565f2Syasuoka 		    self->netbios_server[0]);
936842565f2Syasuoka 	}
937842565f2Syasuoka 	if (self->netbios_server[1].s_addr != 0) {
938842565f2Syasuoka 		addr4.s_addr = htonl(self->netbios_server[1].s_addr);
939842565f2Syasuoka 		radius_del_vs_attr_all(radres,
940842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
941842565f2Syasuoka 		    RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER);
942842565f2Syasuoka 		radius_put_vs_ipv4_attr(radres,
943842565f2Syasuoka 		    RADIUS_VENDOR_MICROSOFT,
944842565f2Syasuoka 		    RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER,
945842565f2Syasuoka 		    self->netbios_server[1]);
946842565f2Syasuoka 	}
9477a099e4dSyasuoka 	if (!self->no_session_timeout && assigned != NULL &&
948842565f2Syasuoka 	    radius_has_attr(radres, RADIUS_TYPE_SESSION_TIMEOUT)) {
949842565f2Syasuoka 		radius_get_uint32_attr(radres, RADIUS_TYPE_SESSION_TIMEOUT,
950842565f2Syasuoka 		    &assigned->session_timeout);
951842565f2Syasuoka 		/* we handle this session-timeout */
952842565f2Syasuoka 		radius_del_attr_all(radres, RADIUS_TYPE_SESSION_TIMEOUT);
953842565f2Syasuoka 	}
954842565f2Syasuoka 
955842565f2Syasuoka  accept:
956842565f2Syasuoka 	if (module_resdeco_done(self->base, q_id, radius_get_data(radres),
957842565f2Syasuoka 	    radius_get_length(radres)) == -1) {
958842565f2Syasuoka 		log_warn("%s: module_resdeco_done() failed", __func__);
959842565f2Syasuoka 		module_stop(self->base);
960842565f2Syasuoka 	}
961842565f2Syasuoka 	if (radreq != NULL)
962842565f2Syasuoka 		radius_delete_packet(radreq);
963842565f2Syasuoka 	radius_delete_packet(radres);
964842565f2Syasuoka 	return;
965842565f2Syasuoka  reject:
966842565f2Syasuoka 	ipcp_reject(self, radreq, q_id, radres, msraserr);
967842565f2Syasuoka 	radius_delete_packet(radreq);
968842565f2Syasuoka 	radius_delete_packet(radres);
969842565f2Syasuoka 	return;
970842565f2Syasuoka  fatal:
971842565f2Syasuoka 	if (radreq != NULL)
972842565f2Syasuoka 		radius_delete_packet(radreq);
973842565f2Syasuoka 	if (radres != NULL)
974842565f2Syasuoka 		radius_delete_packet(radres);
975842565f2Syasuoka 	module_stop(self->base);
976842565f2Syasuoka }
977842565f2Syasuoka 
978842565f2Syasuoka void
979842565f2Syasuoka ipcp_reject(struct module_ipcp *self, RADIUS_PACKET *reqp, unsigned int q_id,
980842565f2Syasuoka     RADIUS_PACKET *orig_resp, int mserr)
981842565f2Syasuoka {
982842565f2Syasuoka 	bool			 is_eap, is_mschap, is_mschap2;
983842565f2Syasuoka 	uint8_t			 attr[256];
984842565f2Syasuoka 	size_t			 attrlen;
985842565f2Syasuoka 	RADIUS_PACKET		*resp;
986842565f2Syasuoka 	struct {
987842565f2Syasuoka 		uint8_t		 code;
988842565f2Syasuoka 		uint8_t		 id;
989842565f2Syasuoka 		uint16_t	 length;
990842565f2Syasuoka 	} __packed		 eap;
991842565f2Syasuoka 
992842565f2Syasuoka 	resp = radius_new_response_packet(RADIUS_CODE_ACCESS_REJECT, reqp);
993842565f2Syasuoka 	if (resp == NULL) {
994842565f2Syasuoka 		log_warn("%s: radius_new_response_packet() failed", __func__);
995842565f2Syasuoka 		module_accsreq_aborted(self->base, q_id);
996842565f2Syasuoka 		return;
997842565f2Syasuoka 	}
998842565f2Syasuoka 
999842565f2Syasuoka 	is_eap = radius_has_attr(reqp, RADIUS_TYPE_EAP_MESSAGE);
1000842565f2Syasuoka 	if (radius_get_vs_raw_attr(reqp, RADIUS_VENDOR_MICROSOFT,
1001842565f2Syasuoka 	    RADIUS_VTYPE_MS_CHAP_RESPONSE, attr, &attrlen) == 0)
1002842565f2Syasuoka 		is_mschap = true;
1003842565f2Syasuoka 	else if (radius_get_vs_raw_attr(reqp, RADIUS_VENDOR_MICROSOFT,
1004842565f2Syasuoka 	    RADIUS_VTYPE_MS_CHAP2_RESPONSE, attr, &attrlen) == 0)
1005842565f2Syasuoka 		is_mschap2 = true;
1006842565f2Syasuoka 
1007842565f2Syasuoka 	if (is_eap) {
1008842565f2Syasuoka 		memset(&eap, 0, sizeof(eap));	/* just in case */
1009842565f2Syasuoka 		eap.code = 1;	/* EAP Request */
1010842565f2Syasuoka 		attrlen = sizeof(attr);
1011842565f2Syasuoka 		if (orig_resp != NULL && radius_get_raw_attr(orig_resp,
1012842565f2Syasuoka 		    RADIUS_TYPE_EAP_MESSAGE, &attr, &attrlen) == 0)
1013842565f2Syasuoka 			eap.id = attr[1];
1014842565f2Syasuoka 		else
1015842565f2Syasuoka 			eap.id = 0;
1016842565f2Syasuoka 		eap.length = htons(sizeof(eap));
1017842565f2Syasuoka 		radius_put_raw_attr(resp, RADIUS_TYPE_EAP_MESSAGE, &eap,
1018842565f2Syasuoka 		    ntohs(eap.length));
1019842565f2Syasuoka 	} else if (is_mschap || is_mschap2) {
1020842565f2Syasuoka 		attr[0] = attr[1];	/* Copy the ident of the request */
1021842565f2Syasuoka 		snprintf(attr + 1, sizeof(attr) - 1, "E=%d R=0 V=3", mserr);
1022842565f2Syasuoka 		radius_put_vs_raw_attr(resp, RADIUS_VENDOR_MICROSOFT,
1023842565f2Syasuoka 		    RADIUS_VTYPE_MS_CHAP_ERROR, attr, strlen(attr + 1) + 1);
1024842565f2Syasuoka 	}
1025842565f2Syasuoka 
1026842565f2Syasuoka 	module_resdeco_done(self->base, q_id, radius_get_data(resp),
1027842565f2Syasuoka 	    radius_get_length(resp));
1028842565f2Syasuoka 	radius_delete_packet(resp);
1029842565f2Syasuoka }
1030842565f2Syasuoka 
1031842565f2Syasuoka /***********************************************************************
1032842565f2Syasuoka  * RADIUS Accounting
1033842565f2Syasuoka  ***********************************************************************/
1034842565f2Syasuoka void
1035842565f2Syasuoka ipcp_accounting_request(void *ctx, u_int q_id, const u_char *pkt,
1036842565f2Syasuoka     size_t pktlen)
1037842565f2Syasuoka {
1038842565f2Syasuoka 	RADIUS_PACKET		*radpkt = NULL;
1039842565f2Syasuoka 	int			 code, af;
1040842565f2Syasuoka 	uint32_t		 type, delay, uval;
1041842565f2Syasuoka 	struct in_addr		 addr4, nas_ipv4;
1042842565f2Syasuoka 	struct in6_addr		 nas_ipv6, ipv6_zero;
1043842565f2Syasuoka 	struct module_ipcp	*self = ctx;
1044842565f2Syasuoka 	struct assigned_ipv4	*assign, *assignt;
1045842565f2Syasuoka 	char			 username[256], nas_id[256], buf[256],
1046a6d690ccSyasuoka 				    buf1[384];
1047842565f2Syasuoka 	struct timespec		 dur;
1048842565f2Syasuoka 	struct radiusd_ipcp_statistics
1049842565f2Syasuoka 				 stat;
1050842565f2Syasuoka 	struct module_ipcp_dae	*dae;
1051842565f2Syasuoka 
1052e1af567eSyasuoka 	ipcp_update_time(self);
1053842565f2Syasuoka 
1054842565f2Syasuoka 	if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) {
1055842565f2Syasuoka 		log_warn("%s: radius_convert_packet() failed", __func__);
1056842565f2Syasuoka 		module_stop(self->base);
1057842565f2Syasuoka 		return;
1058842565f2Syasuoka 	}
1059842565f2Syasuoka 	code = radius_get_code(radpkt);
1060842565f2Syasuoka 	if (code != RADIUS_CODE_ACCOUNTING_REQUEST &&
1061842565f2Syasuoka 	    code != RADIUS_CODE_ACCOUNTING_RESPONSE)
1062842565f2Syasuoka 		goto out;
1063842565f2Syasuoka 
1064842565f2Syasuoka 	if (radius_get_uint32_attr(radpkt, RADIUS_TYPE_ACCT_STATUS_TYPE, &type)
1065842565f2Syasuoka 	    != 0)
1066842565f2Syasuoka 		goto out;
1067842565f2Syasuoka 
1068842565f2Syasuoka 	/* identifier for the NAS */
1069842565f2Syasuoka 	memset(&ipv6_zero, 0, sizeof(ipv6_zero));
1070842565f2Syasuoka 	memset(&nas_ipv4, 0, sizeof(nas_ipv4));
1071842565f2Syasuoka 	memset(&nas_ipv6, 0, sizeof(nas_ipv6));
1072842565f2Syasuoka 	memset(&nas_id, 0, sizeof(nas_id));
1073842565f2Syasuoka 
1074842565f2Syasuoka 	radius_get_ipv4_attr(radpkt, RADIUS_TYPE_NAS_IP_ADDRESS, &nas_ipv4);
1075842565f2Syasuoka 	radius_get_ipv6_attr(radpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, &nas_ipv6);
1076842565f2Syasuoka 	radius_get_string_attr(radpkt, RADIUS_TYPE_NAS_IDENTIFIER, nas_id,
1077842565f2Syasuoka 	    sizeof(nas_id));
1078842565f2Syasuoka 
1079842565f2Syasuoka 	if (nas_ipv4.s_addr == 0 && IN6_ARE_ADDR_EQUAL(&nas_ipv6, &ipv6_zero) &&
1080842565f2Syasuoka 	    nas_id[0] == '\0') {
1081842565f2Syasuoka 		log_warnx("q=%u no NAS-IP-Address, NAS-IPV6-Address, or "
1082842565f2Syasuoka 		    "NAS-Identifier", q_id);
1083842565f2Syasuoka 		goto out;
1084842565f2Syasuoka 	}
1085842565f2Syasuoka 
1086842565f2Syasuoka 	if (type == RADIUS_ACCT_STATUS_TYPE_ACCT_ON ||
1087842565f2Syasuoka 	    type == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) {
1088842565f2Syasuoka 		/*
1089842565f2Syasuoka 		 * NAS or daemon is restarted.  Delete all assigned records
1090842565f2Syasuoka 		 * from it
1091842565f2Syasuoka 		 */
1092842565f2Syasuoka 		RB_FOREACH_SAFE(assign, assigned_ipv4_tree, &self->ipv4s,
1093842565f2Syasuoka 		    assignt) {
1094842565f2Syasuoka 			if (assign->nas_ipv4.s_addr != nas_ipv4.s_addr ||
1095842565f2Syasuoka 			    !IN6_ARE_ADDR_EQUAL(&assign->nas_ipv6, &nas_ipv6) ||
1096842565f2Syasuoka 			    strcmp(assign->nas_id, nas_id) != 0)
1097842565f2Syasuoka 				continue;
10984946f694Syasuoka 			log_info("q=%u Delete record for %s", q_id,
10994946f694Syasuoka 			    inet_ntop(AF_INET, &assign->ipv4, buf,
11004946f694Syasuoka 			    sizeof(buf)));
1101eff8f878Syasuoka 			ipcp_ipv4_delete(self, assign,
1102eff8f878Syasuoka 			    (type == RADIUS_ACCT_STATUS_TYPE_ACCT_ON)
1103eff8f878Syasuoka 			    ? "Receive Acct-On" : "Receive Acct-Off");
1104842565f2Syasuoka 		}
1105842565f2Syasuoka 		return;
1106842565f2Syasuoka 	}
1107842565f2Syasuoka 
1108842565f2Syasuoka 	if (radius_get_ipv4_attr(radpkt, RADIUS_TYPE_FRAMED_IP_ADDRESS, &addr4)
11093a9fd06cSyasuoka 	    != 0) {
11103a9fd06cSyasuoka 		log_warnx("q=%u no Framed-IP-Address-Address attribute", q_id);
1111842565f2Syasuoka 		goto out;
11123a9fd06cSyasuoka 	}
1113842565f2Syasuoka 	if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, username,
11143a9fd06cSyasuoka 	    sizeof(username)) != 0) {
11153a9fd06cSyasuoka 		log_warnx("q=%u no User-Name attribute", q_id);
1116842565f2Syasuoka 		goto out;
11173a9fd06cSyasuoka 	}
11183a9fd06cSyasuoka 	if ((assign = ipcp_ipv4_find(self, addr4)) == NULL) {
1119842565f2Syasuoka 		/* not assigned by this */
11203a9fd06cSyasuoka 		log_warnx("q=%u %s is not assigned by us", q_id,
11213a9fd06cSyasuoka 		    inet_ntop(AF_INET, &addr4, buf, sizeof(buf)));
1122842565f2Syasuoka 		goto out;
11233a9fd06cSyasuoka 	}
1124842565f2Syasuoka 
1125842565f2Syasuoka 	if (radius_get_uint32_attr(radpkt, RADIUS_TYPE_ACCT_DELAY_TIME, &delay)
1126842565f2Syasuoka 	    != 0)
1127842565f2Syasuoka 		delay = 0;
1128842565f2Syasuoka 
1129842565f2Syasuoka 	if (type == RADIUS_ACCT_STATUS_TYPE_START) {
1130842565f2Syasuoka 		assign->start = self->uptime;
1131842565f2Syasuoka 		assign->start.tv_sec -= delay;
1132842565f2Syasuoka 
1133842565f2Syasuoka 		if (!self->no_session_timeout && (self->session_timeout > 0 ||
1134842565f2Syasuoka 		    assign->session_timeout > 0)) {
1135842565f2Syasuoka 			assign->timeout = assign->start;
1136842565f2Syasuoka 			if (self->session_timeout > 0)
1137842565f2Syasuoka 				assign->timeout.tv_sec += self->session_timeout;
1138842565f2Syasuoka 			else
1139842565f2Syasuoka 				assign->timeout.tv_sec +=
1140842565f2Syasuoka 				    assign->session_timeout;
1141842565f2Syasuoka 		}
1142842565f2Syasuoka 		assign->nas_ipv4 = nas_ipv4;
114392278a01Syasuoka 		assign->nas_ipv6 = nas_ipv6;
1144842565f2Syasuoka 		strlcpy(assign->nas_id, nas_id, sizeof(assign->nas_id));
1145842565f2Syasuoka 
1146842565f2Syasuoka 		if (radius_get_string_attr(radpkt, RADIUS_TYPE_ACCT_SESSION_ID,
1147842565f2Syasuoka 		    assign->session_id, sizeof(assign->session_id)) != 0)
1148842565f2Syasuoka 			assign->session_id[0] = '\0';
1149842565f2Syasuoka 		if (radius_get_uint32_attr(radpkt, RADIUS_TYPE_TUNNEL_TYPE,
1150842565f2Syasuoka 		    &uval) == 0)
1151842565f2Syasuoka 			assign->tun_type = radius_tunnel_type_string(uval,
1152842565f2Syasuoka 			    NULL);
1153842565f2Syasuoka 		if (assign->tun_type == NULL)
1154842565f2Syasuoka 			assign->tun_type = "";
1155842565f2Syasuoka 
1156842565f2Syasuoka 		/*
1157842565f2Syasuoka 		 * Get "tunnel from" from Tunnel-Client-Endpoint or Calling-
1158842565f2Syasuoka 		 * Station-Id
1159842565f2Syasuoka 		 */
1160842565f2Syasuoka 		af = AF_UNSPEC;
1161842565f2Syasuoka 		if (radius_get_string_attr(radpkt,
1162842565f2Syasuoka 		    RADIUS_TYPE_TUNNEL_CLIENT_ENDPOINT, buf, sizeof(buf)) == 0)
1163842565f2Syasuoka 		    {
1164842565f2Syasuoka 			if (radius_get_uint32_attr(radpkt,
1165842565f2Syasuoka 			    RADIUS_TYPE_TUNNEL_MEDIUM_TYPE, &uval) == 0) {
1166842565f2Syasuoka 				if (uval == RADIUS_TUNNEL_MEDIUM_TYPE_IPV4)
1167842565f2Syasuoka 					af = AF_INET;
1168842565f2Syasuoka 				else if (uval == RADIUS_TUNNEL_MEDIUM_TYPE_IPV6)
1169842565f2Syasuoka 					af = AF_INET6;
1170842565f2Syasuoka 			}
1171842565f2Syasuoka 			parse_addr(buf, af, (struct sockaddr *)
1172842565f2Syasuoka 			    &assign->tun_client, sizeof(assign->tun_client));
1173842565f2Syasuoka 		}
1174842565f2Syasuoka 		if (assign->tun_client.sin4.sin_family == 0 &&
1175842565f2Syasuoka 		    radius_get_string_attr(radpkt,
1176842565f2Syasuoka 		    RADIUS_TYPE_CALLING_STATION_ID, buf, sizeof(buf)) == 0)
1177842565f2Syasuoka 			parse_addr(buf, af, (struct sockaddr *)
1178842565f2Syasuoka 			    &assign->tun_client, sizeof(assign->tun_client));
1179842565f2Syasuoka 
1180842565f2Syasuoka 		TAILQ_FOREACH(dae, &self->daes, next) {
1181842565f2Syasuoka 			if (dae->nas_id[0] == '\0' ||
1182842565f2Syasuoka 			    strcmp(dae->nas_id, assign->nas_id) == 0)
1183842565f2Syasuoka 				break;
1184842565f2Syasuoka 		}
1185842565f2Syasuoka 		assign->dae = dae;
1186842565f2Syasuoka 
1187842565f2Syasuoka 		ipcp_put_db(self, assign);
1188842565f2Syasuoka 		ipcp_schedule_timer(self);
1189842565f2Syasuoka 
1190842565f2Syasuoka 		if (ipcp_notice_startstop(self, assign, 1, NULL) != 0)
1191842565f2Syasuoka 			goto fail;
11924946f694Syasuoka 		log_info("q=%u Start seq=%u user=%s duration=%dsec "
11934946f694Syasuoka 		    "session=%s tunnel=%s from=%s auth=%s ip=%s", q_id,
11944946f694Syasuoka 		    assign->seq, assign->user->name, delay, assign->session_id,
1195842565f2Syasuoka 		    assign->tun_type, print_addr((struct sockaddr *)
1196842565f2Syasuoka 		    &assign->tun_client, buf1, sizeof(buf1)),
1197842565f2Syasuoka 		    assign->auth_method, inet_ntop(AF_INET, &addr4, buf,
1198842565f2Syasuoka 		    sizeof(buf)));
1199842565f2Syasuoka 	} else if (type == RADIUS_ACCT_STATUS_TYPE_STOP) {
1200842565f2Syasuoka 		memset(&stat, 0, sizeof(stat));
1201842565f2Syasuoka 
1202842565f2Syasuoka 		dur = self->uptime;
1203842565f2Syasuoka 		dur.tv_sec -= delay;
1204842565f2Syasuoka 		timespecsub(&dur, &assign->start, &dur);
1205842565f2Syasuoka 
1206842565f2Syasuoka 		if (radius_get_uint32_attr(radpkt,
1207842565f2Syasuoka 		    RADIUS_TYPE_ACCT_INPUT_OCTETS, &uval) == 0)
1208842565f2Syasuoka 			stat.ibytes = uval;
1209842565f2Syasuoka 		if (radius_get_uint32_attr(radpkt,
1210842565f2Syasuoka 		    RADIUS_TYPE_ACCT_INPUT_GIGAWORDS, &uval) == 0)
1211842565f2Syasuoka 			stat.ibytes = ((uint64_t)uval << 32) | stat.ibytes;
1212842565f2Syasuoka 		if (radius_get_uint32_attr(radpkt,
1213842565f2Syasuoka 		    RADIUS_TYPE_ACCT_OUTPUT_OCTETS, &uval) == 0)
1214842565f2Syasuoka 			stat.obytes = uval;
1215842565f2Syasuoka 		if (radius_get_uint32_attr(radpkt,
1216842565f2Syasuoka 		    RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS, &uval) == 0)
1217842565f2Syasuoka 			stat.obytes = ((uint64_t)uval << 32) | stat.obytes;
1218842565f2Syasuoka 		radius_get_uint32_attr(radpkt, RADIUS_TYPE_ACCT_INPUT_PACKETS,
1219842565f2Syasuoka 		    &stat.ipackets);
1220842565f2Syasuoka 		radius_get_uint32_attr(radpkt, RADIUS_TYPE_ACCT_OUTPUT_PACKETS,
1221842565f2Syasuoka 		    &stat.opackets);
1222842565f2Syasuoka 
1223842565f2Syasuoka 		if (radius_get_uint32_attr(radpkt,
1224842565f2Syasuoka 		    RADIUS_TYPE_ACCT_TERMINATE_CAUSE, &uval) == 0)
1225842565f2Syasuoka 			strlcpy(stat.cause, radius_terminate_cause_string(uval),
1226842565f2Syasuoka 			    sizeof(stat.cause));
1227842565f2Syasuoka 
12284946f694Syasuoka 		log_info("q=%u Stop seq=%u user=%s duration=%lldsec "
12294946f694Syasuoka 		    "session=%s tunnel=%s from=%s auth=%s ip=%s "
12304946f694Syasuoka 		    "datain=%"PRIu64"bytes,%" PRIu32"packets dataout=%"PRIu64
12314946f694Syasuoka 		    "bytes,%"PRIu32"packets cause=\"%s\"", q_id,
1232842565f2Syasuoka 		    assign->seq, assign->user->name, dur.tv_sec,
1233842565f2Syasuoka 		    assign->session_id, assign->tun_type, print_addr(
1234842565f2Syasuoka 		    (struct sockaddr *)&assign->tun_client, buf1, sizeof(buf1)),
1235842565f2Syasuoka 		    assign->auth_method, inet_ntop(AF_INET, &addr4, buf,
1236842565f2Syasuoka 		    sizeof(buf)), stat.ibytes, stat.ipackets, stat.obytes,
1237842565f2Syasuoka 		    stat.opackets, stat.cause);
1238842565f2Syasuoka 
1239842565f2Syasuoka 		ipcp_del_db(self, assign);
1240842565f2Syasuoka 		if (ipcp_notice_startstop(self, assign, 0, &stat) != 0)
1241842565f2Syasuoka 			goto fail;
1242842565f2Syasuoka 		ipcp_ipv4_release(self, ipcp_ipv4_find(self, addr4));
1243842565f2Syasuoka 	}
1244842565f2Syasuoka  out:
1245842565f2Syasuoka 	radius_delete_packet(radpkt);
1246842565f2Syasuoka 	return;
1247842565f2Syasuoka  fail:
1248842565f2Syasuoka 	module_stop(self->base);
1249842565f2Syasuoka 	radius_delete_packet(radpkt);
1250842565f2Syasuoka 	return;
1251842565f2Syasuoka }
1252842565f2Syasuoka 
1253842565f2Syasuoka /***********************************************************************
1254842565f2Syasuoka  * On memory database to manage IP address assignment
1255842565f2Syasuoka  ***********************************************************************/
1256842565f2Syasuoka struct assigned_ipv4 *
1257842565f2Syasuoka ipcp_ipv4_assign(struct module_ipcp *self, struct user *user,
1258842565f2Syasuoka     struct in_addr ina)
1259842565f2Syasuoka {
1260842565f2Syasuoka 	struct assigned_ipv4 *ip;
1261842565f2Syasuoka 
1262842565f2Syasuoka 	ip = calloc(1, sizeof(struct assigned_ipv4));
1263842565f2Syasuoka 	if (ip == NULL) {
1264842565f2Syasuoka 		log_warn("%s: calloc()", __func__);
1265842565f2Syasuoka 		return (NULL);
1266842565f2Syasuoka 	}
1267842565f2Syasuoka 	ip->ipv4 = ina;
1268842565f2Syasuoka 	ip->user = user;
1269842565f2Syasuoka 	ip->authtime = self->uptime;
1270842565f2Syasuoka 	RB_INSERT(assigned_ipv4_tree, &self->ipv4s, ip);
1271842565f2Syasuoka 	TAILQ_INSERT_TAIL(&user->ipv4s, ip, next);
1272c0127aecSyasuoka 	TAILQ_INIT(&ip->dae_clients);
1273842565f2Syasuoka 	self->nsessions++;
1274842565f2Syasuoka 	ip->seq = self->seq++;
1275842565f2Syasuoka 
1276842565f2Syasuoka 	return (ip);
1277842565f2Syasuoka }
1278842565f2Syasuoka 
1279842565f2Syasuoka struct assigned_ipv4 *
1280842565f2Syasuoka ipcp_ipv4_find(struct module_ipcp *self, struct in_addr ina)
1281842565f2Syasuoka {
1282842565f2Syasuoka 	struct assigned_ipv4	 key, *ret;
1283842565f2Syasuoka 	struct timespec		 dif;
1284842565f2Syasuoka 
1285842565f2Syasuoka 	key.ipv4 = ina;
1286842565f2Syasuoka 	ret = RB_FIND(assigned_ipv4_tree, &self->ipv4s, &key);
1287842565f2Syasuoka 	if (ret != NULL && ret->start.tv_sec == 0) {
1288842565f2Syasuoka 		/* not yet assigned */
1289842565f2Syasuoka 		timespecsub(&self->uptime, &ret->authtime, &dif);
1290842565f2Syasuoka 		if (dif.tv_sec >= self->start_wait) {
1291842565f2Syasuoka 			/* assumed NAS finally didn't use the address */
1292842565f2Syasuoka 			TAILQ_REMOVE(&ret->user->ipv4s, ret, next);
1293842565f2Syasuoka 			RB_REMOVE(assigned_ipv4_tree, &self->ipv4s, ret);
1294842565f2Syasuoka 			free(ret);
1295842565f2Syasuoka 			ret = NULL;
1296842565f2Syasuoka 			self->nsessions--;
1297842565f2Syasuoka 		}
1298842565f2Syasuoka 	}
1299842565f2Syasuoka 	return (ret);
1300842565f2Syasuoka }
1301842565f2Syasuoka 
1302842565f2Syasuoka void
1303eff8f878Syasuoka ipcp_ipv4_delete(struct module_ipcp *self, struct assigned_ipv4 *assign,
1304eff8f878Syasuoka     const char *cause)
1305eff8f878Syasuoka {
13061669612dSyasuoka 	struct radiusd_ipcp_statistics stat;
1307eff8f878Syasuoka 
13081669612dSyasuoka 	memset(&stat, 0, sizeof(stat));
1309eff8f878Syasuoka 	strlcpy(stat.cause, cause, sizeof(stat.cause));
1310eff8f878Syasuoka 
1311eff8f878Syasuoka 	ipcp_del_db(self, assign);
1312eff8f878Syasuoka 	ipcp_notice_startstop(self, assign, 0, &stat);
1313eff8f878Syasuoka 	ipcp_ipv4_release(self, assign);
1314eff8f878Syasuoka }
1315eff8f878Syasuoka 
1316eff8f878Syasuoka void
1317842565f2Syasuoka ipcp_ipv4_release(struct module_ipcp *self, struct assigned_ipv4 *assign)
1318842565f2Syasuoka {
1319842565f2Syasuoka 	if (assign != NULL) {
1320842565f2Syasuoka 		TAILQ_REMOVE(&assign->user->ipv4s, assign, next);
1321842565f2Syasuoka 		RB_REMOVE(assigned_ipv4_tree, &self->ipv4s, assign);
1322842565f2Syasuoka 		self->nsessions--;
13232617e43bSyasuoka 		ipcp_dae_reset_request(assign);
1324842565f2Syasuoka 		free(assign);
1325842565f2Syasuoka 	}
1326842565f2Syasuoka }
1327842565f2Syasuoka 
1328842565f2Syasuoka int
1329842565f2Syasuoka assigned_ipv4_compar(struct assigned_ipv4 *a, struct assigned_ipv4 *b)
1330842565f2Syasuoka {
13313a9fd06cSyasuoka 	if (a->ipv4.s_addr > b->ipv4.s_addr)
13323a9fd06cSyasuoka 		return (1);
13333a9fd06cSyasuoka 	else if (a->ipv4.s_addr < b->ipv4.s_addr)
13343a9fd06cSyasuoka 		return (-1);
13353a9fd06cSyasuoka 	return (0);
1336842565f2Syasuoka }
1337842565f2Syasuoka 
1338842565f2Syasuoka struct user *
1339842565f2Syasuoka ipcp_user_get(struct module_ipcp *self, const char *username)
1340842565f2Syasuoka {
1341842565f2Syasuoka 	struct {
1342842565f2Syasuoka 		struct user	 user;
1343842565f2Syasuoka 		char		 name[256];
1344842565f2Syasuoka 	} key;
1345842565f2Syasuoka 	struct user		*elm;
1346842565f2Syasuoka 
1347842565f2Syasuoka 	strlcpy(key.user.name, username, 256);
1348842565f2Syasuoka 	elm = RB_FIND(user_tree, &self->users, &key.user);
1349842565f2Syasuoka 	if (elm == NULL) {
1350842565f2Syasuoka 		if ((elm = calloc(1, offsetof(struct user, name[
1351842565f2Syasuoka 		    strlen(username) + 1]))) == NULL)
1352842565f2Syasuoka 			return (NULL);
1353842565f2Syasuoka 		memcpy(elm->name, username, strlen(username));
1354842565f2Syasuoka 		RB_INSERT(user_tree, &self->users, elm);
1355842565f2Syasuoka 		TAILQ_INIT(&elm->ipv4s);
1356842565f2Syasuoka 	}
1357842565f2Syasuoka 
1358842565f2Syasuoka 	return (elm);
1359842565f2Syasuoka }
1360842565f2Syasuoka 
1361842565f2Syasuoka int
1362842565f2Syasuoka user_compar(struct user *a, struct user *b)
1363842565f2Syasuoka {
1364842565f2Syasuoka 	return (strcmp(a->name, b->name));
1365842565f2Syasuoka }
1366842565f2Syasuoka 
1367842565f2Syasuoka RB_GENERATE_STATIC(assigned_ipv4_tree, assigned_ipv4, tree,
1368842565f2Syasuoka     assigned_ipv4_compar);
1369842565f2Syasuoka RB_GENERATE_STATIC(user_tree, user, tree, user_compar);
1370842565f2Syasuoka 
1371842565f2Syasuoka /***********************************************************************
1372842565f2Syasuoka  * DB for the persistent over processes
1373842565f2Syasuoka  ***********************************************************************/
1374842565f2Syasuoka int
1375842565f2Syasuoka ipcp_prepare_db(void)
1376842565f2Syasuoka {
1377842565f2Syasuoka 	struct passwd	*pw;
1378842565f2Syasuoka 	DB		*db;
1379842565f2Syasuoka 
1380842565f2Syasuoka 	if ((db = dbopen(_PATH_RADIUSD_IPCP_DB, O_CREAT | O_RDWR | O_EXLOCK,
1381842565f2Syasuoka 	    0600, DB_BTREE, NULL)) == NULL)
1382842565f2Syasuoka 		return (-1);
1383842565f2Syasuoka 	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
1384842565f2Syasuoka 		return (-1);
1385842565f2Syasuoka 	fchown(db->fd(db), pw->pw_uid, pw->pw_gid);
1386842565f2Syasuoka 	db->close(db);
1387842565f2Syasuoka 
1388842565f2Syasuoka 	return (0);
1389842565f2Syasuoka }
1390842565f2Syasuoka 
1391842565f2Syasuoka int
1392842565f2Syasuoka ipcp_restore_from_db(struct module_ipcp *self)
1393842565f2Syasuoka {
1394842565f2Syasuoka 	DB			*db;
1395842565f2Syasuoka 	DBT			 key, val;
1396842565f2Syasuoka 	char			 keybuf[128];
1397842565f2Syasuoka 	struct user		*user;
1398842565f2Syasuoka 	struct radiusd_ipcp_db_record
1399842565f2Syasuoka 				*record;
1400842565f2Syasuoka 	struct assigned_ipv4	*assigned;
1401842565f2Syasuoka 	struct in_addr		 ipv4;
1402842565f2Syasuoka 	struct module_ipcp_dae	*dae;
1403842565f2Syasuoka 
1404842565f2Syasuoka 	if ((db = dbopen(_PATH_RADIUSD_IPCP_DB, O_RDONLY | O_SHLOCK, 0600,
1405842565f2Syasuoka 	    DB_BTREE, NULL)) == NULL)
1406842565f2Syasuoka 		return (-1);
1407842565f2Syasuoka 
1408842565f2Syasuoka 	key.data = "ipv4/";
1409842565f2Syasuoka 	key.size = 5;
1410842565f2Syasuoka 	if (db->seq(db, &key, &val, R_CURSOR) == 0) {
1411842565f2Syasuoka 		do {
1412842565f2Syasuoka 			if (key.size >= sizeof(keybuf))
1413842565f2Syasuoka 				break;
1414842565f2Syasuoka 			memcpy(keybuf, key.data, key.size);
1415842565f2Syasuoka 			keybuf[key.size] = '\0';
1416842565f2Syasuoka 			if (strncmp(keybuf, "ipv4/", 5) != 0)
1417842565f2Syasuoka 				break;
1418842565f2Syasuoka 			inet_pton(AF_INET, keybuf + 5, &ipv4);
1419842565f2Syasuoka 			record = (struct radiusd_ipcp_db_record *)val.data;
1420842565f2Syasuoka 			if ((user = ipcp_user_get(self, record->username))
1421842565f2Syasuoka 			    == NULL)
1422842565f2Syasuoka 				return (-1);
1423842565f2Syasuoka 			if ((assigned = ipcp_ipv4_assign(self, user, ipv4))
1424842565f2Syasuoka 			    == NULL)
1425842565f2Syasuoka 				return (-1);
1426842565f2Syasuoka 			assigned->seq = record->seq;
14278185dbe7Syasuoka 			self->seq = MAXIMUM(assigned->seq + 1, self->seq);
1428842565f2Syasuoka 			strlcpy(assigned->auth_method, record->auth_method,
1429842565f2Syasuoka 			    sizeof(assigned->auth_method));
1430842565f2Syasuoka 			strlcpy(assigned->session_id, record->session_id,
1431842565f2Syasuoka 			    sizeof(assigned->session_id));
1432842565f2Syasuoka 			assigned->start = record->start;
1433842565f2Syasuoka 			assigned->timeout = record->timeout;
1434842565f2Syasuoka 			assigned->nas_ipv4 = record->nas_ipv4;
1435842565f2Syasuoka 			assigned->nas_ipv6 = record->nas_ipv6;
1436842565f2Syasuoka 			strlcpy(assigned->nas_id, record->nas_id,
1437842565f2Syasuoka 			    sizeof(assigned->nas_id));
1438842565f2Syasuoka 			assigned->tun_type = radius_tunnel_type_string(0,
1439842565f2Syasuoka 			    record->tun_type);
1440842565f2Syasuoka 			memcpy(&assigned->tun_client, &record->tun_client,
1441842565f2Syasuoka 			    sizeof(assigned->tun_client));
1442842565f2Syasuoka 
1443842565f2Syasuoka 			TAILQ_FOREACH(dae, &self->daes, next) {
1444842565f2Syasuoka 				if (dae->nas_id[0] == '\0' ||
1445842565f2Syasuoka 				    strcmp(dae->nas_id, assigned->nas_id) == 0)
1446842565f2Syasuoka 					break;
1447842565f2Syasuoka 			}
1448842565f2Syasuoka 			assigned->dae = dae;
1449842565f2Syasuoka 		} while (db->seq(db, &key, &val, R_NEXT) == 0);
1450842565f2Syasuoka 	}
1451842565f2Syasuoka 	db->close(db);
1452842565f2Syasuoka 
1453842565f2Syasuoka 	return (0);
1454842565f2Syasuoka }
1455842565f2Syasuoka 
1456842565f2Syasuoka void
1457842565f2Syasuoka ipcp_put_db(struct module_ipcp *self, struct assigned_ipv4 *assigned)
1458842565f2Syasuoka {
1459842565f2Syasuoka 	DB			*db;
1460842565f2Syasuoka 	DBT			 key, val;
1461842565f2Syasuoka 	char			 keybuf[128];
1462842565f2Syasuoka 	struct radiusd_ipcp_db_record
1463842565f2Syasuoka 				 record;
1464842565f2Syasuoka 
1465842565f2Syasuoka 	strlcpy(keybuf, "ipv4/", sizeof(keybuf));
1466842565f2Syasuoka 	inet_ntop(AF_INET, &assigned->ipv4, keybuf + 5, sizeof(keybuf) - 5);
1467842565f2Syasuoka 	key.data = keybuf;
1468842565f2Syasuoka 	key.size = strlen(keybuf);
1469842565f2Syasuoka 	strlcpy(record.session_id, assigned->session_id,
1470842565f2Syasuoka 	    sizeof(record.session_id));
1471842565f2Syasuoka 	strlcpy(record.auth_method, assigned->auth_method,
1472842565f2Syasuoka 	    sizeof(record.auth_method));
1473842565f2Syasuoka 	strlcpy(record.username, assigned->user->name, sizeof(record.username));
1474842565f2Syasuoka 	record.seq = assigned->seq;
1475842565f2Syasuoka 	record.start = assigned->start;
1476842565f2Syasuoka 	record.timeout = assigned->timeout;
1477842565f2Syasuoka 	record.nas_ipv4 = assigned->nas_ipv4;
1478842565f2Syasuoka 	record.nas_ipv6 = assigned->nas_ipv6;
1479842565f2Syasuoka 	strlcpy(record.nas_id, assigned->nas_id, sizeof(record.nas_id));
1480842565f2Syasuoka 	if (assigned->tun_type != NULL)
1481842565f2Syasuoka 		strlcpy(record.tun_type, assigned->tun_type,
1482842565f2Syasuoka 		    sizeof(record.tun_type));
1483842565f2Syasuoka 	memcpy(&record.tun_client, &assigned->tun_client,
1484842565f2Syasuoka 	    sizeof(record.tun_client));
1485842565f2Syasuoka 
1486842565f2Syasuoka 	val.data = &record;
1487842565f2Syasuoka 	val.size = sizeof(record);
1488842565f2Syasuoka 	if ((db = dbopen(_PATH_RADIUSD_IPCP_DB, O_RDWR | O_EXLOCK, 0600,
1489842565f2Syasuoka 	    DB_BTREE, NULL)) == NULL)
1490842565f2Syasuoka 		return;
1491842565f2Syasuoka 	db->put(db, &key, &val, 0);
1492842565f2Syasuoka 	db->close(db);
1493842565f2Syasuoka }
1494842565f2Syasuoka 
1495842565f2Syasuoka void
1496842565f2Syasuoka ipcp_del_db(struct module_ipcp *self, struct assigned_ipv4 *assigned)
1497842565f2Syasuoka {
1498842565f2Syasuoka 	DB			*db;
1499842565f2Syasuoka 	DBT			 key;
1500842565f2Syasuoka 	char			 keybuf[128];
1501842565f2Syasuoka 
1502842565f2Syasuoka 	strlcpy(keybuf, "ipv4/", sizeof(keybuf));
1503842565f2Syasuoka 	inet_ntop(AF_INET, &assigned->ipv4, keybuf + 5, sizeof(keybuf) - 5);
1504842565f2Syasuoka 	key.data = keybuf;
1505842565f2Syasuoka 	key.size = strlen(keybuf);
1506842565f2Syasuoka 
1507842565f2Syasuoka 	if ((db = dbopen(_PATH_RADIUSD_IPCP_DB, O_RDWR | O_EXLOCK, 0600,
1508842565f2Syasuoka 	    DB_BTREE, NULL)) == NULL)
1509842565f2Syasuoka 		return;
1510842565f2Syasuoka 	db->del(db, &key, 0);
1511842565f2Syasuoka 	db->close(db);
1512842565f2Syasuoka }
1513842565f2Syasuoka 
1514842565f2Syasuoka void
1515842565f2Syasuoka ipcp_db_dump_fill_record(struct radiusd_ipcp_db_dump *dump, int idx,
1516842565f2Syasuoka     struct assigned_ipv4 *assign)
1517842565f2Syasuoka {
1518842565f2Syasuoka 	dump->records[idx].af = AF_INET;
1519842565f2Syasuoka 	dump->records[idx].addr.ipv4 = assign->ipv4;
1520842565f2Syasuoka 	dump->records[idx].rec.seq = assign->seq;
1521842565f2Syasuoka 	strlcpy(dump->records[idx].rec.session_id, assign->session_id,
1522842565f2Syasuoka 	    sizeof(dump->records[idx].rec.session_id));
1523842565f2Syasuoka 	strlcpy(dump->records[idx].rec.auth_method, assign->auth_method,
1524842565f2Syasuoka 	    sizeof(dump->records[idx].rec.auth_method));
1525842565f2Syasuoka 	strlcpy(dump->records[idx].rec.username, assign->user->name,
1526842565f2Syasuoka 	    sizeof(dump->records[idx].rec.username));
1527842565f2Syasuoka 	dump->records[idx].rec.start = assign->start;
1528842565f2Syasuoka 	dump->records[idx].rec.timeout = assign->timeout;
1529842565f2Syasuoka 	dump->records[idx].rec.nas_ipv4 = assign->nas_ipv4;
1530842565f2Syasuoka 	dump->records[idx].rec.nas_ipv6 = assign->nas_ipv6;
1531842565f2Syasuoka 	strlcpy(dump->records[idx].rec.nas_id, assign->nas_id,
1532842565f2Syasuoka 	    sizeof(dump->records[idx].rec.nas_id));
1533842565f2Syasuoka 	if (assign->tun_type != NULL)
1534842565f2Syasuoka 		strlcpy(dump->records[idx].rec.tun_type, assign->tun_type,
1535842565f2Syasuoka 		    sizeof(dump->records[idx].rec.tun_type));
1536842565f2Syasuoka 	memcpy(&dump->records[idx].rec.tun_client, &assign->tun_client,
1537842565f2Syasuoka 	    sizeof(dump->records[idx].rec.tun_client));
1538842565f2Syasuoka }
1539842565f2Syasuoka 
1540842565f2Syasuoka /***********************************************************************
1541842565f2Syasuoka  * Timer
1542842565f2Syasuoka  ***********************************************************************/
1543842565f2Syasuoka void
1544e1af567eSyasuoka ipcp_update_time(struct module_ipcp *self)
1545e1af567eSyasuoka {
1546e1af567eSyasuoka 	clock_gettime(CLOCK_BOOTTIME, &self->uptime);
1547e1af567eSyasuoka }
1548e1af567eSyasuoka 
1549e1af567eSyasuoka void
1550842565f2Syasuoka ipcp_on_timer(int fd, short ev, void *ctx)
1551842565f2Syasuoka {
1552842565f2Syasuoka 	struct module_ipcp *self = ctx;
1553842565f2Syasuoka 
1554e1af567eSyasuoka 	ipcp_update_time(self);
1555842565f2Syasuoka 	ipcp_schedule_timer(self);
1556842565f2Syasuoka }
1557842565f2Syasuoka 
1558842565f2Syasuoka void
1559842565f2Syasuoka ipcp_schedule_timer(struct module_ipcp *self)
1560842565f2Syasuoka {
1561842565f2Syasuoka 	struct assigned_ipv4	*assign, *min_assign = NULL;
1562842565f2Syasuoka 	struct timespec		 tsd;
1563842565f2Syasuoka 	struct timeval		 tv;
1564842565f2Syasuoka 
1565842565f2Syasuoka 	/* check session timeout */
1566842565f2Syasuoka 	RB_FOREACH(assign, assigned_ipv4_tree, &self->ipv4s) {
1567842565f2Syasuoka 		if (assign->timeout.tv_sec == 0)
1568842565f2Syasuoka 			continue;
1569842565f2Syasuoka 		if (timespeccmp(&assign->timeout, &self->uptime, <=)) {
1570842565f2Syasuoka 			log_info("Reached session timeout seq=%u", assign->seq);
1571842565f2Syasuoka 			ipcp_dae_send_disconnect_request(assign);
1572842565f2Syasuoka 			memset(&assign->timeout, 0, sizeof(assign->timeout));
1573842565f2Syasuoka 			ipcp_put_db(self, assign);
1574842565f2Syasuoka 		}
1575842565f2Syasuoka 		if (min_assign == NULL ||
1576842565f2Syasuoka 		    timespeccmp(&min_assign->timeout, &assign->timeout, >))
1577842565f2Syasuoka 			min_assign = assign;
1578842565f2Syasuoka 	}
1579842565f2Syasuoka 	if (evtimer_pending(&self->ev_timer, NULL))
1580842565f2Syasuoka 		evtimer_del(&self->ev_timer);
1581842565f2Syasuoka 
1582842565f2Syasuoka 	if (min_assign != NULL) {
1583842565f2Syasuoka 		timespecsub(&min_assign->timeout, &self->uptime, &tsd);
1584842565f2Syasuoka 		TIMESPEC_TO_TIMEVAL(&tv, &tsd);
1585842565f2Syasuoka 		evtimer_set(&self->ev_timer, ipcp_on_timer, self);
1586842565f2Syasuoka 		evtimer_add(&self->ev_timer, &tv);
1587842565f2Syasuoka 	}
1588842565f2Syasuoka }
1589842565f2Syasuoka 
1590842565f2Syasuoka /***********************************************************************
1591842565f2Syasuoka  * Dynamic Authorization Extension for RAIDUS (RFC 5176)
1592842565f2Syasuoka  ***********************************************************************/
1593842565f2Syasuoka static const int dae_request_timeouts[] = { 2, 4, 8, 8 };
1594842565f2Syasuoka 
1595842565f2Syasuoka void
1596842565f2Syasuoka ipcp_dae_send_disconnect_request(struct assigned_ipv4 *assign)
1597842565f2Syasuoka {
1598842565f2Syasuoka 	RADIUS_PACKET		*reqpkt = NULL;
1599842565f2Syasuoka 	struct timeval		 tv;
1600842565f2Syasuoka 	char			 buf[80];
1601842565f2Syasuoka 
1602842565f2Syasuoka 	if (assign->dae == NULL)
1603842565f2Syasuoka 		return;		/* DAE is not configured */
1604842565f2Syasuoka 
16052617e43bSyasuoka 	if (assign->dae_reqpkt == NULL) {
16062617e43bSyasuoka 		if ((reqpkt = radius_new_request_packet(
16072617e43bSyasuoka 		    RADIUS_CODE_DISCONNECT_REQUEST)) == NULL) {
16082617e43bSyasuoka 			log_warn("%s: radius_new_request_packet(): %m",
16092617e43bSyasuoka 			    __func__);
16102617e43bSyasuoka 			return;
1611842565f2Syasuoka 		}
1612842565f2Syasuoka 		radius_put_string_attr(reqpkt, RADIUS_TYPE_ACCT_SESSION_ID,
1613842565f2Syasuoka 		    assign->session_id);
161492278a01Syasuoka 		/*
161592278a01Syasuoka 		 * RFC 5176 Section 3, "either the User-Name or
161692278a01Syasuoka 		 * Chargeable-User-Identity attribute SHOULD be present in
161792278a01Syasuoka 		 * Disconnect-Request and CoA-Request packets."
161892278a01Syasuoka 		 */
161992278a01Syasuoka 		radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME,
162092278a01Syasuoka 		    assign->user->name);
162192278a01Syasuoka 		if (assign->nas_id[0] != '\0')
162292278a01Syasuoka 			radius_put_string_attr(reqpkt,
162392278a01Syasuoka 			    RADIUS_TYPE_NAS_IDENTIFIER, assign->nas_id);
162492278a01Syasuoka 		if (ntohl(assign->nas_ipv4.s_addr) != 0)
162592278a01Syasuoka 			radius_put_ipv4_attr(reqpkt,
162692278a01Syasuoka 			    RADIUS_TYPE_NAS_IP_ADDRESS, assign->nas_ipv4);
162792278a01Syasuoka 		if (!IN6_IS_ADDR_UNSPECIFIED(&assign->nas_ipv6))
162892278a01Syasuoka 			radius_put_ipv6_attr(reqpkt,
162992278a01Syasuoka 			    RADIUS_TYPE_NAS_IPV6_ADDRESS, &assign->nas_ipv6);
1630842565f2Syasuoka 		radius_set_accounting_request_authenticator(reqpkt,
1631842565f2Syasuoka 		    assign->dae->secret);
16322617e43bSyasuoka 		assign->dae_reqpkt = reqpkt;
1633cfa11a85Syasuoka 		TAILQ_INSERT_TAIL(&assign->dae->reqs, assign, dae_next);
16342617e43bSyasuoka 	}
1635842565f2Syasuoka 
16362617e43bSyasuoka 	if (assign->dae_ntry == 0) {
1637cfa11a85Syasuoka 		if (assign->dae->ninflight >= RADIUSD_IPCP_DAE_MAX_INFLIGHT)
1638cfa11a85Syasuoka 			return;
1639842565f2Syasuoka 		log_info("Sending Disconnect-Request seq=%u to %s",
1640842565f2Syasuoka 		    assign->seq, print_addr((struct sockaddr *)
1641842565f2Syasuoka 		    &assign->dae->nas_addr, buf, sizeof(buf)));
16422617e43bSyasuoka 	}
1643842565f2Syasuoka 
16442617e43bSyasuoka 	if (radius_send(assign->dae->sock, assign->dae_reqpkt, 0) < 0)
16452617e43bSyasuoka 		log_warn("%s: sendto: %m", __func__);
16462617e43bSyasuoka 
1647cfa11a85Syasuoka 	tv.tv_sec = dae_request_timeouts[assign->dae_ntry];
1648842565f2Syasuoka 	tv.tv_usec = 0;
1649842565f2Syasuoka 	evtimer_set(&assign->dae_evtimer, ipcp_dae_request_on_timeout, assign);
1650842565f2Syasuoka 	evtimer_add(&assign->dae_evtimer, &tv);
1651cfa11a85Syasuoka 	if (assign->dae_ntry == 0)
1652cfa11a85Syasuoka 		assign->dae->ninflight++;
1653cfa11a85Syasuoka 	assign->dae_ntry++;
1654842565f2Syasuoka }
1655842565f2Syasuoka 
1656842565f2Syasuoka void
1657842565f2Syasuoka ipcp_dae_request_on_timeout(int fd, short ev, void *ctx)
1658842565f2Syasuoka {
1659842565f2Syasuoka 	struct assigned_ipv4	*assign = ctx;
1660842565f2Syasuoka 	char			 buf[80];
1661a06d4bddSyasuoka 	struct radiusctl_client	*client;
1662842565f2Syasuoka 
16632617e43bSyasuoka 	if (assign->dae_ntry >= (int)nitems(dae_request_timeouts)) {
1664842565f2Syasuoka 		log_warnx("No answer for Disconnect-Request seq=%u from %s",
1665842565f2Syasuoka 		    assign->seq, print_addr((struct sockaddr *)
1666842565f2Syasuoka 		    &assign->dae->nas_addr, buf, sizeof(buf)));
1667a06d4bddSyasuoka 		TAILQ_FOREACH(client, &assign->dae_clients, entry)
1668a06d4bddSyasuoka 			module_imsg_compose(assign->dae->ipcp->base, IMSG_NG,
1669a06d4bddSyasuoka 			    client->peerid, 0, -1, NULL, 0);
16702617e43bSyasuoka 		ipcp_dae_reset_request(assign);
16712617e43bSyasuoka 	} else
1672842565f2Syasuoka 		ipcp_dae_send_disconnect_request(assign);
1673842565f2Syasuoka }
1674842565f2Syasuoka 
1675842565f2Syasuoka void
1676842565f2Syasuoka ipcp_dae_on_event(int fd, short ev, void *ctx)
1677842565f2Syasuoka {
1678842565f2Syasuoka 	struct module_ipcp_dae	*dae = ctx;
1679c0127aecSyasuoka 	struct module_ipcp	*self = dae->ipcp;
1680842565f2Syasuoka 	RADIUS_PACKET		*radres = NULL;
1681842565f2Syasuoka 	int			 code;
1682842565f2Syasuoka 	uint32_t		 u32;
1683842565f2Syasuoka 	struct assigned_ipv4	*assign;
1684842565f2Syasuoka 	char			 buf[80], causestr[80];
16852617e43bSyasuoka 	const char		*cause = "";
1686c0127aecSyasuoka 	struct radiusctl_client	*client;
1687842565f2Syasuoka 
1688e1af567eSyasuoka 	ipcp_update_time(self);
1689e1af567eSyasuoka 
1690842565f2Syasuoka 	if ((ev & EV_READ) == 0)
1691842565f2Syasuoka 		return;
1692842565f2Syasuoka 
1693842565f2Syasuoka 	if ((radres = radius_recv(dae->sock, 0)) == NULL) {
1694842565f2Syasuoka 		if (errno == EAGAIN)
1695842565f2Syasuoka 			return;
16964946f694Syasuoka 		log_warn("%s: Failed to receive from %s", __func__, print_addr(
1697842565f2Syasuoka 		    (struct sockaddr *)&dae->nas_addr, buf, sizeof(buf)));
1698842565f2Syasuoka 		return;
1699842565f2Syasuoka 	}
1700842565f2Syasuoka 	TAILQ_FOREACH(assign, &dae->reqs, dae_next) {
1701842565f2Syasuoka 		if (radius_get_id(assign->dae_reqpkt) == radius_get_id(radres))
1702842565f2Syasuoka 			break;
1703842565f2Syasuoka 	}
1704842565f2Syasuoka 	if (assign == NULL) {
17054946f694Syasuoka 		log_warnx("%s: Received RADIUS packet from %s has unknown "
17064946f694Syasuoka 		    "id=%d", __func__, print_addr((struct sockaddr *)
17074946f694Syasuoka 		    &dae->nas_addr, buf, sizeof(buf)), radius_get_id(radres));
17082617e43bSyasuoka 		goto out;
1709842565f2Syasuoka 	}
1710842565f2Syasuoka 
1711842565f2Syasuoka 	radius_set_request_packet(radres, assign->dae_reqpkt);
1712842565f2Syasuoka 	if ((radius_check_response_authenticator(radres, dae->secret)) != 0) {
17134946f694Syasuoka 		log_warnx("%s: Received RADIUS packet for seq=%u from %s has "
17144946f694Syasuoka 		    "a bad authenticator", __func__, assign->seq, print_addr(
1715842565f2Syasuoka 			(struct sockaddr *)&dae->nas_addr, buf,
1716842565f2Syasuoka 		    sizeof(buf)));
17172617e43bSyasuoka 		goto out;
1718842565f2Syasuoka 	}
1719842565f2Syasuoka 	causestr[0] = '\0';
1720842565f2Syasuoka 	if (radius_get_uint32_attr(radres, RADIUS_TYPE_ERROR_CAUSE, &u32) == 0){
1721842565f2Syasuoka 		cause = radius_error_cause_string(u32);
1722842565f2Syasuoka 		if (cause != NULL)
1723842565f2Syasuoka 			snprintf(causestr, sizeof(causestr), " cause=%u(%s)",
1724842565f2Syasuoka 			    u32, cause);
1725842565f2Syasuoka 		else
1726842565f2Syasuoka 			snprintf(causestr, sizeof(causestr), " cause=%u", u32);
17272617e43bSyasuoka 		cause = causestr;
1728842565f2Syasuoka 	}
1729842565f2Syasuoka 
1730842565f2Syasuoka 	code = radius_get_code(radres);
1731842565f2Syasuoka 	switch (code) {
1732842565f2Syasuoka 	case RADIUS_CODE_DISCONNECT_ACK:
1733842565f2Syasuoka 		log_info("Received Disconnect-ACK for seq=%u from %s%s",
1734842565f2Syasuoka 		    assign->seq, print_addr((struct sockaddr *)
1735842565f2Syasuoka 		    &dae->nas_addr, buf, sizeof(buf)), cause);
1736842565f2Syasuoka 		break;
1737842565f2Syasuoka 	case RADIUS_CODE_DISCONNECT_NAK:
17384946f694Syasuoka 		log_info("Received Disconnect-NAK for seq=%u from %s%s",
1739842565f2Syasuoka 		    assign->seq, print_addr((struct sockaddr *)
1740842565f2Syasuoka 		    &dae->nas_addr, buf, sizeof(buf)), cause);
1741842565f2Syasuoka 		break;
1742842565f2Syasuoka 	default:
17434946f694Syasuoka 		log_warn("%s: Received unknown code=%d for id=%u from %s",
17444946f694Syasuoka 		    __func__, code, assign->seq, print_addr((struct sockaddr *)
1745842565f2Syasuoka 		    &dae->nas_addr, buf, sizeof(buf)));
1746842565f2Syasuoka 		break;
1747842565f2Syasuoka 	}
1748c0127aecSyasuoka 
1749c0127aecSyasuoka 	TAILQ_FOREACH(client, &assign->dae_clients, entry) {
1750c0127aecSyasuoka 		if (*cause != '\0')
1751c0127aecSyasuoka 			module_imsg_compose(self->base,
1752c0127aecSyasuoka 			    (code == RADIUS_CODE_DISCONNECT_ACK)
1753c0127aecSyasuoka 			    ? IMSG_OK : IMSG_NG, client->peerid, 0, -1,
1754c0127aecSyasuoka 			    cause + 1, strlen(cause + 1) + 1);
1755c0127aecSyasuoka 		else
1756c0127aecSyasuoka 			module_imsg_compose(self->base,
1757c0127aecSyasuoka 			    (code == RADIUS_CODE_DISCONNECT_ACK)
1758c0127aecSyasuoka 			    ? IMSG_OK : IMSG_NG, client->peerid, 0, -1,
1759c0127aecSyasuoka 			    NULL, 0);
1760c0127aecSyasuoka 	}
17612617e43bSyasuoka 	ipcp_dae_reset_request(assign);
17622617e43bSyasuoka  out:
17632617e43bSyasuoka 	if (radres != NULL)
17642617e43bSyasuoka 		radius_delete_packet(radres);
17652617e43bSyasuoka }
17662617e43bSyasuoka 
17672617e43bSyasuoka void
17682617e43bSyasuoka ipcp_dae_reset_request(struct assigned_ipv4 *assign)
17692617e43bSyasuoka {
1770c0127aecSyasuoka 	struct radiusctl_client		*client, *clientt;
1771cfa11a85Syasuoka 	const struct timeval		 zero = { 0, 0 };
1772c0127aecSyasuoka 
17732617e43bSyasuoka 	if (assign->dae != NULL) {
1774cfa11a85Syasuoka 		if (assign->dae_reqpkt != NULL)
17752617e43bSyasuoka 			TAILQ_REMOVE(&assign->dae->reqs, assign, dae_next);
1776cfa11a85Syasuoka 		if (assign->dae_ntry > 0) {
1777cfa11a85Syasuoka 			assign->dae->ninflight--;
1778cfa11a85Syasuoka 			if (!evtimer_pending(&assign->dae->ev_reqs, NULL))
1779cfa11a85Syasuoka 				evtimer_add(&assign->dae->ev_reqs, &zero);
1780cfa11a85Syasuoka 		}
17812617e43bSyasuoka 	}
17822617e43bSyasuoka 	if (assign->dae_reqpkt != NULL)
17832617e43bSyasuoka 		radius_delete_packet(assign->dae_reqpkt);
17842617e43bSyasuoka 	assign->dae_reqpkt = NULL;
17852617e43bSyasuoka 	if (evtimer_pending(&assign->dae_evtimer, NULL))
17862617e43bSyasuoka 		evtimer_del(&assign->dae_evtimer);
1787c0127aecSyasuoka 	TAILQ_FOREACH_SAFE(client, &assign->dae_clients, entry, clientt) {
1788c0127aecSyasuoka 		TAILQ_REMOVE(&assign->dae_clients, client, entry);
1789c0127aecSyasuoka 		free(client);
1790c0127aecSyasuoka 	}
17912617e43bSyasuoka 	assign->dae_ntry = 0;
1792842565f2Syasuoka }
1793842565f2Syasuoka 
1794cfa11a85Syasuoka void
1795cfa11a85Syasuoka ipcp_dae_send_pending_requests(int fd, short ev, void *ctx)
1796cfa11a85Syasuoka {
1797cfa11a85Syasuoka 	struct module_ipcp_dae	*dae = ctx;
1798cfa11a85Syasuoka 	struct module_ipcp	*self = dae->ipcp;
1799cfa11a85Syasuoka 	struct assigned_ipv4	*assign, *assignt;
1800cfa11a85Syasuoka 
1801cfa11a85Syasuoka 	ipcp_update_time(self);
1802cfa11a85Syasuoka 
1803cfa11a85Syasuoka 	TAILQ_FOREACH_SAFE(assign, &dae->reqs, dae_next, assignt) {
1804cfa11a85Syasuoka 		if (dae->ninflight >= RADIUSD_IPCP_DAE_MAX_INFLIGHT)
1805cfa11a85Syasuoka 			break;
1806cfa11a85Syasuoka 		if (assign->dae_ntry == 0)	/* pending */
1807cfa11a85Syasuoka 			ipcp_dae_send_disconnect_request(assign);
1808cfa11a85Syasuoka 	}
1809cfa11a85Syasuoka }
1810cfa11a85Syasuoka 
1811842565f2Syasuoka /***********************************************************************
1812842565f2Syasuoka  * Miscellaneous functions
1813842565f2Syasuoka  ***********************************************************************/
1814842565f2Syasuoka struct ipcp_address *
1815842565f2Syasuoka parse_address_range(const char *range)
1816842565f2Syasuoka {
1817842565f2Syasuoka 	char			*buf, *sep;
1818842565f2Syasuoka 	int			 masklen;
1819842565f2Syasuoka 	uint32_t		 mask;
1820842565f2Syasuoka 	struct in_addr		 start, end;
1821842565f2Syasuoka 	struct ipcp_address	*ret;
1822842565f2Syasuoka 	const char		*errstr;
1823842565f2Syasuoka 
1824842565f2Syasuoka 	buf = strdup(range);
1825842565f2Syasuoka 	if (buf == NULL)
1826842565f2Syasuoka 		goto error;
1827842565f2Syasuoka 	if ((sep = strchr(buf, '-')) != NULL) {
1828842565f2Syasuoka 		*sep = '\0';
182984b182f8Sflorian 		if (inet_pton(AF_INET, buf, &start) != 1)
1830842565f2Syasuoka 			goto error;
183184b182f8Sflorian 		else if (inet_pton(AF_INET, ++sep, &end) != 1)
1832842565f2Syasuoka 			goto error;
1833842565f2Syasuoka 		start.s_addr = ntohl(start.s_addr);
1834842565f2Syasuoka 		end.s_addr = ntohl(end.s_addr);
1835266241f9Syasuoka 		if (end.s_addr < start.s_addr)
1836266241f9Syasuoka 			goto error;
1837842565f2Syasuoka 	} else {
1838842565f2Syasuoka 		if ((sep = strchr(buf, '/')) != NULL) {
1839842565f2Syasuoka 			*sep = '\0';
184084b182f8Sflorian 			if (inet_pton(AF_INET, buf, &start) != 1)
1841842565f2Syasuoka 				goto error;
1842842565f2Syasuoka 			masklen = strtonum(++sep, 0, 32, &errstr);
1843842565f2Syasuoka 			if (errstr != NULL)
1844842565f2Syasuoka 				goto error;
1845842565f2Syasuoka 		} else {
184684b182f8Sflorian 			if (inet_pton(AF_INET, buf, &start) != 1)
1847842565f2Syasuoka 				goto error;
1848842565f2Syasuoka 			masklen = 32;
1849842565f2Syasuoka 		}
1850842565f2Syasuoka 		mask = 0xFFFFFFFFUL;
1851842565f2Syasuoka 		if (masklen < 32)
1852842565f2Syasuoka 			mask <<= (32 - masklen);
1853842565f2Syasuoka 		start.s_addr = ntohl(start.s_addr) & mask;
1854842565f2Syasuoka 		if (masklen == 32)
1855842565f2Syasuoka 			end = start;
1856842565f2Syasuoka 		else if (masklen == 31)
1857842565f2Syasuoka 			end.s_addr = start.s_addr + 1;
1858842565f2Syasuoka 		else {
1859842565f2Syasuoka 			end.s_addr = start.s_addr + (1 << (32 - masklen)) - 2;
1860842565f2Syasuoka 			start.s_addr = start.s_addr + 1;
1861842565f2Syasuoka 		}
1862842565f2Syasuoka 	}
1863842565f2Syasuoka 	free(buf);
1864842565f2Syasuoka 	if ((ret = calloc(1, sizeof(struct ipcp_address))) == NULL)
1865842565f2Syasuoka 		return (NULL);
1866842565f2Syasuoka 	ret->start = start;
1867842565f2Syasuoka 	ret->end = end;
1868842565f2Syasuoka 	ret->naddrs = end.s_addr - start.s_addr + 1;
1869842565f2Syasuoka 	return (ret);
1870842565f2Syasuoka  error:
1871842565f2Syasuoka 	free(buf);
1872842565f2Syasuoka 	return (NULL);
1873842565f2Syasuoka }
1874842565f2Syasuoka 
1875842565f2Syasuoka const char *
1876842565f2Syasuoka radius_tunnel_type_string(unsigned val, const char *label)
1877842565f2Syasuoka {
1878842565f2Syasuoka 	unsigned int		 i;
1879842565f2Syasuoka 	struct {
1880842565f2Syasuoka 		const unsigned	 constval;
1881842565f2Syasuoka 		const char	*label;
1882842565f2Syasuoka 	} tunnel_types[] = {
1883842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_PPTP,	"PPTP" },
1884842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_L2F,	"L2F" },
1885842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_L2TP,	"L2TP" },
1886842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_ATMP,	"ATMP" },
1887842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_VTP,	"VTP" },
1888842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_AH,	"AH" },
1889842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_IP,	"IP" },
1890842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_MOBILE,	"MIN-IP-IP" },
1891842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_ESP,	"ESP" },
1892842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_GRE,	"GRE" },
1893842565f2Syasuoka 		{ RADIUS_TUNNEL_TYPE_VDS,	"DVS" },
1894842565f2Syasuoka 		/* [MS-RNAS] 3.3.5.1.9 Tunnel-Type */
1895842565f2Syasuoka 		{ RADIUS_VENDOR_MICROSOFT << 8 | 1,
1896842565f2Syasuoka 						"SSTP" }
1897842565f2Syasuoka 	};
1898842565f2Syasuoka 
1899842565f2Syasuoka 	if (label != NULL) {	/* for conversion to the const value */
1900842565f2Syasuoka 		for (i = 0; i < nitems(tunnel_types); i++) {
1901842565f2Syasuoka 			if (strcmp(tunnel_types[i].label, label) == 0)
1902842565f2Syasuoka 				return (tunnel_types[i].label);
1903842565f2Syasuoka 		}
1904842565f2Syasuoka 	}
1905842565f2Syasuoka 
1906842565f2Syasuoka 	for (i = 0; i < nitems(tunnel_types); i++) {
1907842565f2Syasuoka 		if (tunnel_types[i].constval == val)
1908842565f2Syasuoka 			return (tunnel_types[i].label);
1909842565f2Syasuoka 	}
1910842565f2Syasuoka 
1911842565f2Syasuoka 	return (NULL);
1912842565f2Syasuoka }
1913842565f2Syasuoka 
1914842565f2Syasuoka const char *
1915842565f2Syasuoka radius_terminate_cause_string(unsigned val)
1916842565f2Syasuoka {
1917842565f2Syasuoka 	unsigned int		 i;
1918842565f2Syasuoka 	struct {
1919842565f2Syasuoka 		const unsigned	 constval;
1920842565f2Syasuoka 		const char	*label;
1921842565f2Syasuoka 	} terminate_causes[] = {
1922842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_USER_REQUEST,	"User Request" },
1923842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_LOST_CARRIER,	"Lost Carrier" },
1924842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_LOST_SERVICE,	"Lost Service" },
1925842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_IDLE_TIMEOUT,	"Idle Timeout" },
1926842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_SESSION_TIMEOUT,	"Session Timeout" },
1927842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_ADMIN_RESET,	"Admin Reset" },
1928842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_ADMIN_REBOOT,	"Admin Reboot" },
1929842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_PORT_ERROR,		"Port Error" },
1930842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_NAS_ERROR,		"NAS Error" },
1931842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_NAS_RESET,		"NAS Request" },
1932842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_NAS_REBOOT,		"NAS Reboot" },
1933842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_PORT_UNNEEDED,	"Port Unneeded" },
1934842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_PORT_PREEMPTED,	"Port Preempted" },
1935842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_PORT_SUSPENDED,	"Port Suspended" },
1936842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_SERVICE_UNAVAIL,	"Service Unavailable" },
1937842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_CALLBACK,		"Callback" },
1938842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_USER_ERROR,		"User Error" },
1939842565f2Syasuoka 	    { RADIUS_TERMNATE_CAUSE_HOST_REQUEST,	"Host Request" },
1940842565f2Syasuoka 	};
1941842565f2Syasuoka 
1942842565f2Syasuoka 	for (i = 0; i < nitems(terminate_causes); i++) {
1943842565f2Syasuoka 		if (terminate_causes[i].constval == val)
1944842565f2Syasuoka 			return (terminate_causes[i].label);
1945842565f2Syasuoka 	}
1946842565f2Syasuoka 
1947842565f2Syasuoka 	return (NULL);
1948842565f2Syasuoka }
1949842565f2Syasuoka 
1950842565f2Syasuoka const char *
1951842565f2Syasuoka radius_error_cause_string(unsigned val)
1952842565f2Syasuoka {
1953842565f2Syasuoka 	unsigned int		 i;
1954842565f2Syasuoka 	struct {
1955842565f2Syasuoka 		const unsigned	 constval;
1956842565f2Syasuoka 		const char	*label;
1957842565f2Syasuoka 	} error_causes[] = {
1958842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_RESIDUAL_SESSION_REMOVED,
1959842565f2Syasuoka 	      "Residual Session Context Removed" },
1960842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_INVALID_EAP_PACKET,
1961842565f2Syasuoka 	      "Invalid EAP Packet (Ignored)" },
1962842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_UNSUPPORTED_ATTRIBUTE,
1963842565f2Syasuoka 	      "Unsupported Attribute" },
1964842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_MISSING_ATTRIBUTE,
1965842565f2Syasuoka 	      "Missing Attribute" },
1966842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH,
1967842565f2Syasuoka 	      "NAS Identification Mismatch" },
1968842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_INVALID_REQUEST,
1969842565f2Syasuoka 	      "Invalid Request" },
1970842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_UNSUPPORTED_SERVICE,
1971842565f2Syasuoka 	      "Unsupported Service" },
1972842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_UNSUPPORTED_EXTENSION,
1973842565f2Syasuoka 	      "Unsupported Extension" },
1974842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE,
1975a06d4bddSyasuoka 	      "Invalid Attribute Value" },
1976842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED,
1977842565f2Syasuoka 	      "Administratively Prohibited" },
1978842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_REQUEST_NOT_ROUTABLE,
1979842565f2Syasuoka 	      "Request Not Routable (Proxy)" },
1980842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_SESSION_NOT_FOUND,
1981842565f2Syasuoka 	      "Session Context Not Found" },
1982842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_SESSION_NOT_REMOVABLE,
1983842565f2Syasuoka 	      "Session Context Not Removable" },
1984842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_OTHER_PROXY_PROCESSING_ERROR,
1985842565f2Syasuoka 	      "Other Proxy Processing Error" },
1986842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_RESOURCES_UNAVAILABLE,
1987842565f2Syasuoka 	      "Resources Unavailable" },
1988842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_REQUEST_INITIATED,
1989842565f2Syasuoka 	      "equest Initiated" },
1990842565f2Syasuoka 	    { RADIUS_ERROR_CAUSE_MULTI_SELECTION_UNSUPPORTED,
1991842565f2Syasuoka 	      "Multiple Session Selection Unsupported" }
1992842565f2Syasuoka 	};
1993842565f2Syasuoka 
1994842565f2Syasuoka 	for (i = 0; i < nitems(error_causes); i++) {
1995842565f2Syasuoka 		if (error_causes[i].constval == val)
1996842565f2Syasuoka 			return (error_causes[i].label);
1997842565f2Syasuoka 	}
1998842565f2Syasuoka 
1999842565f2Syasuoka 	return (NULL);
2000842565f2Syasuoka }
2001842565f2Syasuoka 
2002842565f2Syasuoka int
2003842565f2Syasuoka parse_addr(const char *str0, int af, struct sockaddr *sa, socklen_t salen)
2004842565f2Syasuoka {
2005842565f2Syasuoka 	int		 error;
2006842565f2Syasuoka 	char		*str, *end, *colon, *colon0, *addr = NULL, *port = NULL;
2007842565f2Syasuoka 	char		*sb, *sb0;
2008842565f2Syasuoka 	struct addrinfo	 hints, *ai;
2009842565f2Syasuoka 
2010842565f2Syasuoka 	if ((str = strdup(str0)) == NULL)
2011842565f2Syasuoka 		return (-1);
2012842565f2Syasuoka 	if (*str == '[' && (end = strchr(str + 1, ']')) != NULL) {
2013842565f2Syasuoka 		addr = str + 1;
2014842565f2Syasuoka 		*end = '\0';
2015842565f2Syasuoka 		if (*(end + 1) == ':')
2016842565f2Syasuoka 			port = end + 2;
2017842565f2Syasuoka 		else if (*(end + 1) == '[' && (sb = strrchr(end + 2, ']'))
2018842565f2Syasuoka 		    != NULL) {
2019842565f2Syasuoka 			port = end + 2;
2020842565f2Syasuoka 			*sb = '\0';
2021842565f2Syasuoka 		}
2022842565f2Syasuoka 	} else if ((sb0 = strchr(str, '[')) != NULL &&
2023842565f2Syasuoka 	    (sb = strrchr(sb0 + 1, ']')) != NULL && sb0 < sb) {
2024842565f2Syasuoka 		addr = str;
2025842565f2Syasuoka 		*sb0 = '\0';
2026842565f2Syasuoka 		port = sb0 + 1;
2027842565f2Syasuoka 		*sb = '\0';
2028842565f2Syasuoka 	} else if ((colon0 = strchr(str, ':')) != NULL &&
2029842565f2Syasuoka 	    (colon = strrchr(str, ':')) != NULL && colon0 == colon) {
2030842565f2Syasuoka 		/* has one : */
2031842565f2Syasuoka 		addr = str;
2032842565f2Syasuoka 		*colon = '\0';
2033842565f2Syasuoka 		port = colon + 1;
2034842565f2Syasuoka 	} else {
2035842565f2Syasuoka 		addr = str;
2036842565f2Syasuoka 		port = NULL;
2037842565f2Syasuoka 	}
2038842565f2Syasuoka 
2039842565f2Syasuoka 	memset(&hints, 0, sizeof(hints));
2040842565f2Syasuoka 	hints.ai_family = af;
2041842565f2Syasuoka 	hints.ai_socktype = SOCK_DGRAM;
2042842565f2Syasuoka 	hints.ai_flags = AI_NUMERICHOST;
2043842565f2Syasuoka 	if (port != NULL)
2044842565f2Syasuoka 		hints.ai_flags |= AI_NUMERICSERV;
2045842565f2Syasuoka 	if ((error = getaddrinfo(addr, port, &hints, &ai)) != 0) {
2046842565f2Syasuoka 		free(str);
2047842565f2Syasuoka 		return (-1);
2048842565f2Syasuoka 	}
2049c0c32a87Syasuoka 	free(str);
2050842565f2Syasuoka 	if (salen < ai->ai_addrlen) {
2051842565f2Syasuoka 		freeaddrinfo(ai);
2052842565f2Syasuoka 		return (-1);
2053842565f2Syasuoka 	}
2054842565f2Syasuoka 	memcpy(sa, ai->ai_addr, ai->ai_addrlen);
2055842565f2Syasuoka 	freeaddrinfo(ai);
2056842565f2Syasuoka 
2057842565f2Syasuoka 	return (0);
2058842565f2Syasuoka }
2059842565f2Syasuoka 
2060842565f2Syasuoka const char *
2061842565f2Syasuoka print_addr(struct sockaddr *sa, char *buf, size_t bufsiz)
2062842565f2Syasuoka {
2063842565f2Syasuoka 	int	noport, ret;
2064842565f2Syasuoka 	char	hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
2065842565f2Syasuoka 
2066842565f2Syasuoka 	if (ntohs(((struct sockaddr_in *)sa)->sin_port) == 0) {
2067842565f2Syasuoka 		noport = 1;
2068842565f2Syasuoka 		ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0,
2069842565f2Syasuoka 		    NI_NUMERICHOST);
2070842565f2Syasuoka 	} else {
2071842565f2Syasuoka 		noport = 0;
2072842565f2Syasuoka 		ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf,
2073842565f2Syasuoka 		    sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
2074842565f2Syasuoka 	}
2075842565f2Syasuoka 	if (ret != 0)
2076842565f2Syasuoka 		return "";
2077842565f2Syasuoka 	if (noport)
2078842565f2Syasuoka 		strlcpy(buf, hbuf, bufsiz);
2079842565f2Syasuoka 	else if (sa->sa_family == AF_INET6)
2080842565f2Syasuoka 		snprintf(buf, bufsiz, "[%s]:%s", hbuf, sbuf);
2081842565f2Syasuoka 	else
2082842565f2Syasuoka 		snprintf(buf, bufsiz, "%s:%s", hbuf, sbuf);
2083842565f2Syasuoka 
2084842565f2Syasuoka 	return (buf);
2085842565f2Syasuoka }
2086