1 /* $OpenBSD: npppd_ctl.c,v 1.10 2012/09/18 13:14:08 yasuoka Exp $ */
4 * Copyright (c) 2009 Internet Initiative Japan Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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>
39 #include <netinet/in.h>
40 #include <net/if_dl.h>
41 #include <arpa/inet.h>
55 #include "npppd_local.h"
56 #include "debugutil.h"
58 #include "pathnames.h"
60 #include "npppd_ctl.h"
62 #include "net_utils.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>
71 #include <netinet/if_ether.h>
73 #include <netinet/ip_var.h>
74 #include <sys/ioctl.h>
75 #include <net/pipex.h>
78 #ifndef NPPPD_CTL_SOCK_FILE_MODE
79 #define NPPPD_CTL_SOCK_FILE_MODE 0660
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) \
88 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
89 , __func__, __FILE__, __LINE__); \
93 #define NPPPD_CTL_DBG(x)
94 #define NPPPD_CTL_ASSERT(cond)
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
*);
106 static int npppd_ppp_compar(const void *, const void *);
108 /** initialize npppd management */
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
));
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__
);
131 val
= NPPPD_CTL_MSGSZ
;
132 if (setsockopt(_this
->sock
, SOL_SOCKET
, SO_SNDBUF
, &val
, sizeof(val
))
134 if (errno
== ENOBUFS
)
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
145 "setsockopt(,SOL_SOCKET, SO_SNDBUF,%d) "
146 "failed in %s(): %m", val
, __func__
);
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__
);
162 if ((flags
= fcntl(_this
->sock
, F_GETFL
, &dummy
)) < 0) {
163 log_printf(LOG_ERR
, "fcntl(,F_GETFL) failed in %s(): %m",
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()"
171 if (chmod(_this
->pathname
, NPPPD_CTL_SOCK_FILE_MODE
) != 0) {
172 log_printf(LOG_ERR
, "chmod() failed in %s(): %m", __func__
);
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
);
184 if (_this
->sock
>= 0)
191 /** stop npppd management */
193 npppd_ctl_stop(npppd_ctl
*_this
)
195 if (_this
->sock
>= 0) {
196 event_del(&_this
->ev_sock
);
199 log_printf(LOG_INFO
, "Shutdown %s (npppd_ctl)",
204 /** execute management procedure on each command */
206 npppd_ctl_command(npppd_ctl
*_this
, u_char
*pkt
, int pktlen
,
207 struct sockaddr
*peer
)
209 u_char respbuf
[NPPPD_CTL_MSGSZ
];
212 if (pktlen
< sizeof(int)) {
213 npppd_ctl_log(_this
, LOG_ERR
, "Packet too small.");
216 command
= *(int *)pkt
;
218 case NPPPD_CTL_CMD_WHO
: {
219 int i
, c
, idx
, msgsz
;
221 struct npppd_ctl_who_response
*res
;
225 _npppd
= _this
->npppd
;
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__
);
232 #ifdef NPPPD_CTL_DEBUG
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));
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
));
255 /* the last entry this chunk */
256 msgsz
= offsetof(struct npppd_ctl_who_response
,
258 if (sendto(_this
->sock
, res
, msgsz
, 0, peer
,
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
,
267 goto 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",
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.");
283 npppd_ctl_log(_this
, LOG_ERR
,
284 "sendto() failed in %s: %m",
290 case NPPPD_CTL_CMD_DISCONNECT_USER
: {
293 struct npppd_ctl_disconnect_user_request
*req
;
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
));
308 for (i
= 0; i
< sizeof(req
->username
); i
++) {
309 if (req
->username
[i
] == '\0')
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");
319 if ((ppplist
= npppd_get_ppp_by_user(_npppd
, req
->username
))
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__
);
327 slist_itr_first(ppplist
);
328 while (slist_itr_has_next(ppplist
)) {
329 ppp
= slist_itr_next(ppplist
);
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
,
346 npppd_ctl_log(_this
, LOG_ERR
,
347 "sendto() failed in %s: %m", __func__
);
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.",
357 snprintf(respbuf
, sizeof(respbuf
),
358 "Failed to reset the routing table.:%s",
361 if (sendto(_this
->sock
, respbuf
, strlen(respbuf
), 0, peer
,
363 npppd_ctl_log(_this
, LOG_ERR
,
364 "sendto() failed in %s: %m", __func__
);
368 case NPPPD_CTL_CMD_DISCONNECT
:
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
);
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",
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
]) {
400 slist_itr_remove(&users
);
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" : "");
413 if (sendto(_this
->sock
, &resp
, sizeof(resp
), 0, peer
,
415 npppd_ctl_log(_this
, LOG_ERR
,
416 "sendto() failed in %s: %m", __func__
);
420 npppd_ctl_log(_this
, LOG_ERR
,
421 "Received unknown command %04x", command
);
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) {
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",
476 #ifdef USE_NPPPD_PIPEX
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
;
485 #ifdef USE_NPPPD_PPTP
488 #ifdef USE_NPPPD_L2TP
492 if (ppp
->pipex_enabled
== 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
;
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
;
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
;
530 /* update statistics in kernel */
531 if (ioctl(iface
->devf
, PIPEXGSTAT
, &req
) != 0)
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
;
545 /** IO event handler */
547 npppd_ctl_io_event(int fd
, short evmask
, void *ctx
)
550 u_char buf
[NPPPD_CTL_MSGSZ
];
552 struct sockaddr_storage ss
;
556 if ((evmask
& EV_READ
) != 0) {
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
);
566 npppd_ctl_command(_this
, buf
, sz
, (struct sockaddr
*)&ss
);
571 /** Record log that begins the label based this instance. */
573 npppd_ctl_log(npppd_ctl
*_this
, int prio
, const char *fmt
, ...)
579 NPPPD_CTL_ASSERT(_this
!= NULL
);
582 snprintf(logbuf
, sizeof(logbuf
), "npppdctl: %s", fmt
);
583 status
= vlog_printf(prio
, logbuf
, ap
);
590 npppd_ppp_compar(const void *a0
, const void *b0
)
592 npppd_ppp
const *a
, *b
;
597 return a
->id
- b
->id
;