sync
[bitrig.git] / usr.sbin / npppd / npppd / npppd_ctl.c
blobb99993e750d136de30affff01c3948338ce765e3
1 /* $OpenBSD: npppd_ctl.c,v 1.10 2012/09/18 13:14:08 yasuoka Exp $ */
3 /*-
4 * Copyright (c) 2009 Internet Initiative Japan Inc.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
28 /**@file
29 * npppd management.
30 * This file provides to open UNIX domain socket which located in
31 * /var/run/npppd_ctl and accept commmands from the npppdctl command.
33 /* $Id: npppd_ctl.c,v 1.10 2012/09/18 13:14:08 yasuoka Exp $ */
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38 #include <sys/stat.h>
39 #include <netinet/in.h>
40 #include <net/if_dl.h>
41 #include <arpa/inet.h>
43 #include <errno.h>
44 #include <event.h>
45 #include <fcntl.h>
46 #include <netdb.h>
47 #include <stdarg.h>
48 #include <stddef.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <syslog.h>
53 #include <unistd.h>
55 #include "npppd_local.h"
56 #include "debugutil.h"
58 #include "pathnames.h"
59 #include "radish.h"
60 #include "npppd_ctl.h"
62 #include "net_utils.h"
63 #include "privsep.h"
64 #define sendto(_s, _msg, _len, _flags, _to, _tolen) \
65 priv_sendto((_s), (_msg), (_len), (_flags), (_to), (_tolen))
67 #ifdef USE_NPPPD_PIPEX
68 #if defined(__NetBSD__)
69 #include <net/if_ether.h>
70 #else
71 #include <netinet/if_ether.h>
72 #endif
73 #include <netinet/ip_var.h>
74 #include <sys/ioctl.h>
75 #include <net/pipex.h>
76 #endif
78 #ifndef NPPPD_CTL_SOCK_FILE_MODE
79 #define NPPPD_CTL_SOCK_FILE_MODE 0660
80 #endif
81 #define MSG_SZ_RESERVED 256
83 #ifdef NPPPD_CTL_DEBUG
84 #define NPPPD_CTL_DBG(x) npppd_ctl_log x
85 #define NPPPD_CTL_ASSERT(cond) \
86 if (!(cond)) { \
87 fprintf(stderr, \
88 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
89 , __func__, __FILE__, __LINE__); \
90 abort(); \
92 #else
93 #define NPPPD_CTL_DBG(x)
94 #define NPPPD_CTL_ASSERT(cond)
95 #endif
96 #include "debugutil.h"
98 static void npppd_ctl_command (npppd_ctl *, u_char *, int, struct sockaddr *);
99 static void npppd_ctl_io_event (int, short, void *);
100 static int npppd_ctl_log (npppd_ctl *, int, const char *, ...) __printflike(3,4);
101 static void npppd_who_init(struct npppd_who *, npppd_ctl *, npppd_ppp *);
103 #ifdef USE_NPPPD_PIPEX
104 static int npppd_ppp_get_pipex_stat(struct npppd_who *, npppd_ppp *);
105 #endif
106 static int npppd_ppp_compar(const void *, const void *);
108 /** initialize npppd management */
109 void
110 npppd_ctl_init(npppd_ctl *_this, npppd *_npppd, const char *pathname)
112 memset(_this, 0, sizeof(npppd_ctl));
113 if (pathname != NULL)
114 strlcat(_this->pathname, pathname, sizeof(_this->pathname));
115 _this->sock = -1;
116 _this->npppd = _npppd;
119 /** start npppd management */
121 npppd_ctl_start(npppd_ctl *_this)
123 int flags, dummy, val;
124 struct sockaddr_un sun;
126 if ((_this->sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
127 log_printf(LOG_ERR, "socket() failed in %s(): %m", __func__);
128 goto fail;
131 val = NPPPD_CTL_MSGSZ;
132 if (setsockopt(_this->sock, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val))
133 != 0) {
134 if (errno == ENOBUFS)
135 log_printf(LOG_ERR,
136 "ctl.max_msgbuf may beyonds kernel limit. "
137 "setsockopt(,SOL_SOCKET, SO_SNDBUF,%d) "
138 "failed in %s(): %m", val, __func__);
140 * on NetBSD, need to set value which is less than or equal
141 * to kern.sbmax.
143 else
144 log_printf(LOG_ERR,
145 "setsockopt(,SOL_SOCKET, SO_SNDBUF,%d) "
146 "failed in %s(): %m", val, __func__);
148 goto fail;
150 unlink(_this->pathname);
151 memset(&sun, 0, sizeof(sun));
152 sun.sun_family = AF_UNIX;
153 sun.sun_len = sizeof(sun);
154 strlcpy(sun.sun_path, _this->pathname, sizeof(sun.sun_path));
156 if (bind(_this->sock, (struct sockaddr *)&sun, sizeof(sun)) != 0) {
157 log_printf(LOG_ERR, "bind() failed in %s(): %m", __func__);
158 goto fail;
161 dummy = 0;
162 if ((flags = fcntl(_this->sock, F_GETFL, &dummy)) < 0) {
163 log_printf(LOG_ERR, "fcntl(,F_GETFL) failed in %s(): %m",
164 __func__);
165 goto fail;
166 } else if (fcntl(_this->sock, F_SETFL, flags | O_NONBLOCK) < 0) {
167 log_printf(LOG_ERR, "fcntl(,F_SETFL,O_NONBLOCK) failed in %s()"
168 ": %m", __func__);
169 goto fail;
171 if (chmod(_this->pathname, NPPPD_CTL_SOCK_FILE_MODE) != 0) {
172 log_printf(LOG_ERR, "chmod() failed in %s(): %m", __func__);
173 goto fail;
176 event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST,
177 npppd_ctl_io_event, _this);
178 event_add(&_this->ev_sock, NULL);
180 log_printf(LOG_INFO, "Listening %s (npppd_ctl)", _this->pathname);
182 return 0;
183 fail:
184 if (_this->sock >= 0)
185 close(_this->sock);
186 _this->sock = -1;
188 return -1;
191 /** stop npppd management */
192 void
193 npppd_ctl_stop(npppd_ctl *_this)
195 if (_this->sock >= 0) {
196 event_del(&_this->ev_sock);
197 close(_this->sock);
198 _this->sock = -1;
199 log_printf(LOG_INFO, "Shutdown %s (npppd_ctl)",
200 _this->pathname);
204 /** execute management procedure on each command */
205 static void
206 npppd_ctl_command(npppd_ctl *_this, u_char *pkt, int pktlen,
207 struct sockaddr *peer)
209 u_char respbuf[NPPPD_CTL_MSGSZ];
210 int command;
212 if (pktlen < sizeof(int)) {
213 npppd_ctl_log(_this, LOG_ERR, "Packet too small.");
214 return;
216 command = *(int *)pkt;
217 switch (command) {
218 case NPPPD_CTL_CMD_WHO: {
219 int i, c, idx, msgsz;
220 npppd *_npppd;
221 struct npppd_ctl_who_response *res;
222 slist users;
224 res = NULL;
225 _npppd = _this->npppd;
226 slist_init(&users);
227 if (npppd_get_all_users(_npppd, &users) != 0) {
228 npppd_ctl_log(_this, LOG_ERR,
229 "npppd_get_all_users() failed in %s: %m", __func__);
230 goto cmd_who_out;
232 #ifdef NPPPD_CTL_DEBUG
233 #if 0
234 /* for debug, copy the first user 1600 times. */
235 if (slist_length(&users) > 0) {
236 for (i = 0; i < 1600; i++)
237 slist_add(&users, slist_get(&users, 0));
239 #endif
240 #endif
241 res = (struct npppd_ctl_who_response *)respbuf;
243 /* number of entry per chunk */
244 c = NPPPD_CTL_MSGSZ - sizeof(struct npppd_ctl_who_response);
245 c /= sizeof(struct npppd_who);
247 slist_qsort(&users, npppd_ppp_compar);
248 res->count = slist_length(&users);
249 slist_itr_first(&users);
250 for (i = 0, idx = 0; slist_itr_has_next(&users); i++) {
251 npppd_who_init(&res->entry[idx++], _this,
252 slist_itr_next(&users));
253 idx %= c;
254 if (idx == 0) {
255 /* the last entry this chunk */
256 msgsz = offsetof(struct npppd_ctl_who_response,
257 entry[c]);
258 if (sendto(_this->sock, res, msgsz, 0, peer,
259 peer->sa_len) < 0)
260 goto cmd_who_send_error;
263 if (i == 0 || idx != 0) {
264 msgsz = offsetof(struct npppd_ctl_who_response, entry[(i % c)]);
265 if (sendto(_this->sock, res, msgsz, 0, peer,
266 peer->sa_len) < 0)
267 goto cmd_who_send_error;
269 cmd_who_out:
270 slist_fini(&users);
271 break;
272 cmd_who_send_error:
274 * FIXME: we should wait until the buffer is available.
276 NPPPD_CTL_DBG((_this, LOG_DEBUG, "sendto() failed in %s: %m",
277 __func__));
278 if (errno == ENOBUFS || errno == EMSGSIZE || errno == EINVAL) {
279 npppd_ctl_log(_this, LOG_INFO,
280 "'who' is requested, but "
281 "the buffer is not enough.");
282 } else {
283 npppd_ctl_log(_this, LOG_ERR,
284 "sendto() failed in %s: %m",
285 __func__);
287 slist_fini(&users);
288 break;
290 case NPPPD_CTL_CMD_DISCONNECT_USER: {
291 int i, stopped;
292 npppd *_npppd;
293 struct npppd_ctl_disconnect_user_request *req;
294 npppd_ppp *ppp;
295 slist *ppplist;
297 stopped = 0;
298 _npppd = _this->npppd;
299 req = (struct npppd_ctl_disconnect_user_request *)pkt;
301 if (sizeof(struct npppd_ctl_disconnect_user_request) > pktlen) {
302 npppd_ctl_log(_this, LOG_ERR,
303 "'disconnect by user' is requested, "
304 " but the request has invalid data length"
305 "(%d:%d)", pktlen, (int)sizeof(req->username));
306 break;
308 for (i = 0; i < sizeof(req->username); i++) {
309 if (req->username[i] == '\0')
310 break;
312 if (i >= sizeof(req->username)) {
313 npppd_ctl_log(_this, LOG_ERR,
314 "'disconnect by user' is requested, "
315 " but the request has invalid user name");
316 break;
319 if ((ppplist = npppd_get_ppp_by_user(_npppd, req->username))
320 == NULL) {
321 npppd_ctl_log(_this, LOG_INFO,
322 "npppd_get_ppp_by_user() could't find user \"%s\" in %s: %m",
323 req->username, __func__);
324 goto user_end;
325 break;
327 slist_itr_first(ppplist);
328 while (slist_itr_has_next(ppplist)) {
329 ppp = slist_itr_next(ppplist);
330 if (ppp == NULL)
331 continue;
333 ppp_stop(ppp, NULL);
334 stopped++;
336 user_end:
338 npppd_ctl_log(_this, LOG_NOTICE,
339 "'disconnect by user' is requested, "
340 "stopped %d connections.", stopped);
341 snprintf(respbuf, sizeof(respbuf),
342 "Disconnected %d ppp connections", stopped);
344 if (sendto(_this->sock, respbuf, strlen(respbuf), 0, peer,
345 peer->sa_len) < 0) {
346 npppd_ctl_log(_this, LOG_ERR,
347 "sendto() failed in %s: %m", __func__);
349 break;
351 case NPPPD_CTL_CMD_RESET_ROUTING_TABLE:
353 if (npppd_reset_routing_table(_this->npppd, 0) == 0)
354 strlcpy(respbuf, "Reset the routing table successfully.",
355 sizeof(respbuf));
356 else
357 snprintf(respbuf, sizeof(respbuf),
358 "Failed to reset the routing table.:%s",
359 strerror(errno));
361 if (sendto(_this->sock, respbuf, strlen(respbuf), 0, peer,
362 peer->sa_len) < 0) {
363 npppd_ctl_log(_this, LOG_ERR,
364 "sendto() failed in %s: %m", __func__);
366 break;
368 case NPPPD_CTL_CMD_DISCONNECT:
370 int i, n;
371 slist users;
372 npppd_ppp *ppp;
373 struct npppd_ctl_disconnect_request *req;
374 struct npppd_ctl_disconnect_response resp;
376 req = (struct npppd_ctl_disconnect_request *)pkt;
377 if (sizeof(struct npppd_ctl_disconnect_request) > pktlen ||
378 offsetof(struct npppd_ctl_disconnect_request,
379 ppp_id[req->count]) > pktlen) {
380 npppd_ctl_log(_this, LOG_ERR,
381 "Disconnect is requested, but the request has "
382 "invalid data length(%d).", pktlen);
383 break;
386 n = 0;
387 slist_init(&users);
388 if (npppd_get_all_users(_this->npppd, &users) != 0) {
389 npppd_ctl_log(_this, LOG_ERR,
390 "npppd_get_all_users() failed at %s(): %m",
391 __func__);
392 goto cmd_disconnect_fail;
394 for (i = 0; i < req->count; i++) {
395 slist_itr_first(&users);
396 while (slist_itr_has_next(&users)) {
397 ppp = slist_itr_next(&users);
398 if (ppp->id == req->ppp_id[i]) {
399 n++;
400 slist_itr_remove(&users);
401 ppp_stop(ppp, NULL);
402 break;
406 cmd_disconnect_fail:
407 npppd_ctl_log(_this, LOG_INFO,
408 "Disconnect is requested. Requested %d session%s, "
409 "disconnected %d session%s.", req->count,
410 (req->count > 1)? "s" : "", n, (n > 1)? "s" : "");
411 slist_fini(&users);
412 resp.count = n;
413 if (sendto(_this->sock, &resp, sizeof(resp), 0, peer,
414 peer->sa_len) < 0)
415 npppd_ctl_log(_this, LOG_ERR,
416 "sendto() failed in %s: %m", __func__);
418 break;
419 default:
420 npppd_ctl_log(_this, LOG_ERR,
421 "Received unknown command %04x", command);
424 return;
427 static void
428 npppd_who_init(struct npppd_who *_this, npppd_ctl *ctl, npppd_ppp *ppp)
430 struct timespec curr_time;
431 npppd_auth_base *realm = ppp->realm;
432 npppd_iface *iface = ppp_iface(ppp);
434 strlcpy(_this->username, ppp->username, sizeof(_this->username));
435 _this->time = ppp->start_time;
436 if (clock_gettime(CLOCK_MONOTONIC, &curr_time) < 0) {
437 NPPPD_CTL_ASSERT(0);
439 _this->duration_sec = curr_time.tv_sec - ppp->start_monotime;
440 strlcpy(_this->tunnel_proto, npppd_ppp_tunnel_protocol_name(
441 ppp->pppd, ppp), sizeof(_this->tunnel_proto));
443 _this->tunnel_peer.peer_in4.sin_family = AF_UNSPEC;
444 if (((struct sockaddr *)&ppp->phy_info)->sa_len > 0) {
445 NPPPD_CTL_ASSERT(sizeof(_this->tunnel_peer) >=
446 ((struct sockaddr *)&ppp->phy_info)->sa_len);
447 memcpy(&_this->tunnel_peer, &ppp->phy_info,
448 MIN(sizeof(_this->tunnel_peer),
449 ((struct sockaddr *)&ppp->phy_info)->sa_len));
452 strlcpy(_this->ifname, iface->ifname, sizeof(_this->ifname));
453 strlcpy(_this->rlmname, npppd_auth_get_name(realm),
454 sizeof(_this->rlmname));
456 _this->framed_ip_address = ppp->ppp_framed_ip_address;
457 _this->ipackets = ppp->ipackets;
458 _this->opackets = ppp->opackets;
459 _this->ierrors = ppp->ierrors;
460 _this->oerrors = ppp->oerrors;
461 _this->ibytes = ppp->ibytes;
462 _this->obytes = ppp->obytes;
463 _this->ppp_id = ppp->id;
465 #ifdef USE_NPPPD_PIPEX
466 if (ppp->pipex_enabled != 0) {
467 if (npppd_ppp_get_pipex_stat(_this, ppp) != 0) {
468 npppd_ctl_log(ctl, LOG_NOTICE,
469 "npppd_ppp_get_pipex_stat() failed in %s: %m",
470 __func__);
473 #endif
476 #ifdef USE_NPPPD_PIPEX
477 static int
478 npppd_ppp_get_pipex_stat(struct npppd_who *_this, npppd_ppp *ppp)
480 npppd_iface *iface = ppp_iface(ppp);
481 struct pipex_session_stat_req req;
482 #ifdef USE_NPPPD_PPPOE
483 pppoe_session *pppoe;
484 #endif
485 #ifdef USE_NPPPD_PPTP
486 pptp_call *call;
487 #endif
488 #ifdef USE_NPPPD_L2TP
489 l2tp_call *l2tp;
490 #endif
492 if (ppp->pipex_enabled == 0)
493 return 0;
495 memset(&req, 0, sizeof(req));
496 switch(ppp->tunnel_type) {
497 #ifdef USE_NPPPD_PPPOE
498 case NPPPD_TUNNEL_PPPOE:
499 pppoe = (pppoe_session *)ppp->phy_context;
501 /* PPPOE specific information */
502 req.psr_protocol = PIPEX_PROTO_PPPOE;
503 req.psr_session_id = pppoe->session_id;
504 break;
505 #endif
506 #ifdef USE_NPPPD_PPTP
507 case NPPPD_TUNNEL_PPTP:
508 call = (pptp_call *)ppp->phy_context;
510 /* PPTP specific information */
511 req.psr_session_id = call->id;
512 req.psr_protocol = PIPEX_PROTO_PPTP;
513 break;
514 #endif
515 #ifdef USE_NPPPD_L2TP
516 case NPPPD_TUNNEL_L2TP:
517 l2tp = (l2tp_call *)ppp->phy_context;
519 /* L2TP specific information */
520 req.psr_session_id = l2tp->session_id;
521 req.psr_protocol = PIPEX_PROTO_L2TP;
522 break;
523 #endif
524 default:
525 NPPPD_CTL_ASSERT(0);
526 errno = EINVAL;
527 return 1;
530 /* update statistics in kernel */
531 if (ioctl(iface->devf, PIPEXGSTAT, &req) != 0)
532 return 1;
534 _this->ipackets += req.psr_stat.ipackets;
535 _this->opackets += req.psr_stat.opackets;
536 _this->ierrors += req.psr_stat.ierrors;
537 _this->oerrors += req.psr_stat.oerrors;
538 _this->ibytes += req.psr_stat.ibytes;
539 _this->obytes += req.psr_stat.obytes;
541 return 0;
543 #endif
545 /** IO event handler */
546 static void
547 npppd_ctl_io_event(int fd, short evmask, void *ctx)
549 int sz;
550 u_char buf[NPPPD_CTL_MSGSZ];
551 npppd_ctl *_this;
552 struct sockaddr_storage ss;
553 socklen_t sslen;
555 _this = ctx;
556 if ((evmask & EV_READ) != 0) {
557 sslen = sizeof(ss);
558 if ((sz = recvfrom(_this->sock, buf, sizeof(buf), 0,
559 (struct sockaddr *)&ss, &sslen)) < 0) {
560 npppd_ctl_log(_this, LOG_ERR,
561 "recvfrom() failed in %s(): %m", __func__);
562 npppd_ctl_stop(_this);
564 return;
566 npppd_ctl_command(_this, buf, sz, (struct sockaddr *)&ss);
568 return;
571 /** Record log that begins the label based this instance. */
572 static int
573 npppd_ctl_log(npppd_ctl *_this, int prio, const char *fmt, ...)
575 int status;
576 char logbuf[BUFSIZ];
577 va_list ap;
579 NPPPD_CTL_ASSERT(_this != NULL);
581 va_start(ap, fmt);
582 snprintf(logbuf, sizeof(logbuf), "npppdctl: %s", fmt);
583 status = vlog_printf(prio, logbuf, ap);
584 va_end(ap);
586 return status;
589 static int
590 npppd_ppp_compar(const void *a0, const void *b0)
592 npppd_ppp const *a, *b;
594 a = a0;
595 b = b0;
597 return a->id - b->id;