diff options
author | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2010-01-11 04:20:58 +0000 |
---|---|---|
committer | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2010-01-11 04:20:58 +0000 |
commit | f24f75f44d582e005fed41d187261a034bb7628a (patch) | |
tree | c2b790f0da2c27916ed9341222df263ee87ec950 /usr.sbin | |
parent | 1087250c10f476d6aedd9c44ca4ba96ce45792f6 (diff) |
Initial import npppd(8). npppd is a new PPP daemon that handles many
ppp sessions as a server. It supports L2TP, PPTP and PPPoE as
tunneling.
ok mcbride@ dlg@ deraadt@ reyk@.
Diffstat (limited to 'usr.sbin')
104 files changed, 38094 insertions, 0 deletions
diff --git a/usr.sbin/npppd/Makefile b/usr.sbin/npppd/Makefile new file mode 100644 index 00000000000..c1a5a6582fd --- /dev/null +++ b/usr.sbin/npppd/Makefile @@ -0,0 +1,7 @@ +# +# $Id: Makefile,v 1.1 2010/01/11 04:20:56 yasuoka Exp $ +# + +SUBDIR+= npppd npppdctl + +.include <bsd.subdir.mk> diff --git a/usr.sbin/npppd/Makefile.inc b/usr.sbin/npppd/Makefile.inc new file mode 100644 index 00000000000..26762ad1caa --- /dev/null +++ b/usr.sbin/npppd/Makefile.inc @@ -0,0 +1,7 @@ +# $Id: Makefile.inc,v 1.1 2010/01/11 04:20:56 yasuoka Exp $ + +.include <bsd.own.mk> + +.if exists(${.CURDIR}/../../Makefile.inc) +.include "../../Makefile.inc" +.endif diff --git a/usr.sbin/npppd/common/addr_range.c b/usr.sbin/npppd/common/addr_range.c new file mode 100644 index 00000000000..f4df149fe45 --- /dev/null +++ b/usr.sbin/npppd/common/addr_range.c @@ -0,0 +1,432 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * addr_range.c - Parser/aggregator for varios address/network expressions. + * + * Input: 192.168.0.0/24 192.168.1.0/24 + * Output: 192.168.0.0/23 + * + * Input: 192.168.0.128-192.168.0.255 + * Output: 192.168.0.128/25 + * + * Notice: + * - Byte order of 'addr' and 'mask' (members of struct in_addr_range) + * are host byte order. + * - Parsing address range like 192.168.0.0-192.168.255.255 costs much of + * cpu/memory. + * + * Example code: + * + * struct in_addr_range *list = NULL, *cur; + * + * in_addr_range_list_add(&list, "192.168.0.128-192.168.0.255"); + * in_addr_range_list_add(&list, "192.168.1.128-192.168.1.255"); + * for (cur = list; cur != NULL; cur = cur->next) { + * // do something + * struct in_addr a, m; + * a.s_addr = htonl(cur->addr); + * m.s_addr = htonl(cur->mask); + * } + * in_addr_range_list_remove_all(&list); + * + * Author: + * Yasuoka Masahiko <yasuoka@iij.ad.jp> + * + * $Id: addr_range.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#ifdef ADDR_RANGE_DEBUG +#define IIJDEBUG +#endif + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <syslog.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#ifdef IIJDEBUG +#include <debugutil.h> +static int saved_errno; +#define INT4_CHARS(x) \ + ((x) & 0xff000000) >> 24, ((x) & 0x00ff0000) >> 16, \ + ((x) & 0x0000ff00) >> 8, ((x) & 0x000000ff) +#endif +#include "addr_range.h" + +static void in_addr_range_list_uniq __P((struct in_addr_range **)); +static int in_addr_range_list_add0 __P((struct in_addr_range **, u_int32_t, u_int32_t)); +static int bitmask2masklen __P((u_int32_t)); + +struct in_addr_range * +in_addr_range_create() +{ + struct in_addr_range *_this; + if ((_this = (struct in_addr_range *)malloc( + sizeof(struct in_addr_range))) == NULL) + return NULL; + memset(_this, 0xff, sizeof(struct in_addr_range)); + _this->next = NULL; + return _this; +} + + +void +in_addr_range_destroy(struct in_addr_range *_this) +{ + free(_this); +} + +void +in_addr_range_list_remove_all(struct in_addr_range **list) +{ + struct in_addr_range *cur0, *cur; + + cur = *list; + while (cur != NULL) { + cur0 = cur; + cur = cur->next; + in_addr_range_destroy(cur0); + } + *list = NULL; +} + +static void +in_addr_range_list_uniq(struct in_addr_range **list) +{ + u_int32_t m0; + struct in_addr_range **cur, *a, *b; + +restart: + for (cur = list; *cur != NULL; cur = &(*cur)->next) { + if ((*cur)->next == NULL) + continue; + a = *cur; + b = (*cur)->next; + m0 = 0xffffffff ^ a->mask; + if (a->mask == b->mask && ( + a->addr - b->addr == m0 + 1 || + b->addr - a->addr == m0 + 1)) { + m0 = m0 * 2 + 1; + m0 ^= 0xffffffff; + if ((a->addr & m0) != (b->addr & m0)) + continue; + if (a->addr > b->addr) { +#ifdef IIJDEBUG + log_printf(LOG_DL_2, + "%d.%d.%d.%d/%d + %d.%d.%d.%d/%d = %d.%d.%d.%d/%d" + , INT4_CHARS(a->addr), bitmask2masklen(a->mask) + , INT4_CHARS(b->addr), bitmask2masklen(b->mask) + , INT4_CHARS(b->addr), bitmask2masklen(m0) + ); +#endif + b->mask = m0; + *cur = b; + in_addr_range_destroy(a); + } else { +#ifdef IIJDEBUG + log_printf(LOG_DL_2, + "==>%d.%d.%d.%d/%d + %d.%d.%d.%d/%d = %d.%d.%d.%d/%d" + , INT4_CHARS(a->addr), bitmask2masklen(a->mask) + , INT4_CHARS(b->addr), bitmask2masklen(b->mask) + , INT4_CHARS(a->addr), bitmask2masklen(m0) + ); +#endif + a->mask = m0; + (*cur)->next = b->next; + in_addr_range_destroy(b); + } + goto restart; + } + } +} + +int +in_addr_range_list_includes(struct in_addr_range **list, struct in_addr *addr) +{ + struct in_addr_range *cur; + u_int32_t addr0 = ntohl(addr->s_addr); + + for (cur = *list; cur != NULL; cur = cur->next) { + if ((addr0 & cur->mask) == (cur->addr & cur->mask)) + return 1; + } + return 0; +} + +static int +in_addr_range_list_add0(struct in_addr_range **list, u_int32_t addr, + u_int32_t mask) +{ + struct in_addr_range *ent; + struct in_addr_range **cur; + + if ((ent = in_addr_range_create()) == NULL) + return -1; + ent->addr = addr; + ent->mask = mask; + + for (cur = list; *cur != NULL; cur = &(*cur)->next) { + if ((ent->addr & (*cur)->mask) == + ((*cur)->addr & (*cur)->mask)) { + in_addr_range_destroy(ent); + in_addr_range_list_uniq(list); + return 0; + } + if ((ent->addr & ent->mask) == ((*cur)->addr & ent->mask)) { + ent->next = (*cur)->next; + free(*cur); + *cur = ent; + in_addr_range_list_uniq(list); + return 0; + } + if ((*cur)->addr > ent->addr) + break; + } + if (cur != NULL) { + ent->next = *cur; + *cur = ent; + in_addr_range_list_uniq(list); + } + return 0; +} + +int +in_addr_range_list_add(struct in_addr_range **list, const char *str) +{ + int is_range = 0, is_masklen = 0, is_maskaddr = 0, mask; + char *p0, *p1; + struct in_addr a0, a1; + u_int32_t i0, i1; + + if ((p0 = strdup(str)) == NULL) { +#ifdef IIJDEBUG + saved_errno = errno; + log_printf(LOG_DL_1, "malloc() failes: %m"); + errno = saved_errno; +#endif + return -1; + } + if ((p1 = strchr(p0, '-')) != NULL) { + *p1++ = '\0'; + is_range = 1; + } else if ((p1 = strchr(p0, '/')) != NULL) { + *p1++ = '\0'; + + if (sscanf(p1, "%d", &mask) != 1) { +#ifdef IIJDEBUG + saved_errno = errno; + log_printf(LOG_DL_1, "sscanf(%s) failes: %m", + p1); + errno = saved_errno; +#endif + free(p0); + return -1; + } + if (mask < 0 || 32 < mask) { +#ifdef IIJDEBUG + log_printf(LOG_DL_1, "must be 0 <= masklen <= 32: %d", + mask); + errno = EINVAL; +#endif + free(p0); + return -1; + } + is_masklen = 1; + } else if ((p1 = strchr(p0, ':')) != NULL) { + *p1++ = '\0'; + is_maskaddr = 1; + } + + if (inet_aton(p0, &a0) != 1) { + if (errno == 0) + errno = EINVAL; +#ifdef IIJDEBUG + saved_errno = errno; + log_printf(LOG_DL_1, "inet_aton(%s) failes: %m", p0); + errno = saved_errno; +#endif + free(p0); + return -1; + } + if ((is_range || is_maskaddr) && inet_aton(p1, &a1) != 1) { + if (errno == 0) + errno = EINVAL; +#ifdef IIJDEBUG + saved_errno = errno; + log_printf(LOG_DL_1, "inet_aton(%s) failes: %m", p1); + errno = saved_errno; +#endif + free(p0); + return -1; + } + free(p0); + if (is_range) { + i0 = ntohl(a0.s_addr); + i1 = ntohl(a1.s_addr); + for (; i0 <= i1; i0++) + in_addr_range_list_add0(list, i0, 0xffffffff); + } else if (is_masklen) { + i0 = ntohl(a0.s_addr); + if (mask == 0) + i1 = 0x0; + else + i1 = 0xffffffffL << (32 - mask); + if ((i0 & i1) != i0) { +#ifdef IIJDEBUG + log_printf(LOG_DL_1, "invalid addr/mask pair: " + "%d.%d.%d.%d/%d", + INT4_CHARS(i0), bitmask2masklen(i1)); +#endif + errno = EINVAL; + return -1; + } + in_addr_range_list_add0(list, i0, i1); + } else if (is_maskaddr) { + i0 = ntohl(a0.s_addr); + i1 = ntohl(a1.s_addr); + if ((i0 & i1) != i0 || bitmask2masklen(i1) < 0) { +#ifdef IIJDEBUG + log_printf(LOG_DL_1, "invalid addr/mask pair: " + "%d.%d.%d.%d/%d", + INT4_CHARS(i0), bitmask2masklen(i1)); +#endif + errno = EINVAL; + return -1; + } + in_addr_range_list_add0(list, i0, i1); + } else { + i0 = ntohl(a0.s_addr); + in_addr_range_list_add0(list, i0, 0xffffffff); + } + + return 0; +} + +static int bitmask2masklen(mask) + u_int32_t mask; +{ + switch(mask) { + case 0x00000000: return 0; + case 0x80000000: return 1; + case 0xC0000000: return 2; + case 0xE0000000: return 3; + case 0xF0000000: return 4; + case 0xF8000000: return 5; + case 0xFC000000: return 6; + case 0xFE000000: return 7; + case 0xFF000000: return 8; + case 0xFF800000: return 9; + case 0xFFC00000: return 10; + case 0xFFE00000: return 11; + case 0xFFF00000: return 12; + case 0xFFF80000: return 13; + case 0xFFFC0000: return 14; + case 0xFFFE0000: return 15; + case 0xFFFF0000: return 16; + case 0xFFFF8000: return 17; + case 0xFFFFC000: return 18; + case 0xFFFFE000: return 19; + case 0xFFFFF000: return 20; + case 0xFFFFF800: return 21; + case 0xFFFFFC00: return 22; + case 0xFFFFFE00: return 23; + case 0xFFFFFF00: return 24; + case 0xFFFFFF80: return 25; + case 0xFFFFFFC0: return 26; + case 0xFFFFFFE0: return 27; + case 0xFFFFFFF0: return 28; + case 0xFFFFFFF8: return 29; + case 0xFFFFFFFC: return 30; + case 0xFFFFFFFE: return 31; + case 0xFFFFFFFF: return 32; + } + return -1; +} + +#ifdef ADDR_RANGE_DEBUG +#include <unistd.h> + +static void usage __P ((void)); + +static void +usage() +{ + fprintf(stderr, + "usage: addr_range [-d] [addr_exp]...\n" + "\taddr_exp: 192.168.0.1 (equals 192.168.0.1/32) or \n" + "\t 192.168.32.1-192.168.32.255 or \n" + "\t 192.168.4.0:255.255.254.0 or \n" + "\t 10.0.0.1/24\n" + ); +} + +int +main(int argc, char *argv[]) +{ + int i, ch; + struct in_addr_range *list = NULL, *cur; + + debugfp = stderr; + debuglevel = 0; + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch (ch) { + case 'd': + debuglevel++; + break; + default: + usage(); + exit(1); + } + } + + argc -= optind; + argv += optind; + + if (argc <= 0) { + usage(); + exit(1); + } + + for (i = 0; i < argc; i++) { + if (in_addr_range_list_add(&list, argv[i])) { + perror(argv[i]); + } + } + for (cur = list; cur != NULL; cur = cur->next) { + fprintf(stderr, "%d.%d.%d.%d/%d\n" + , INT4_CHARS(cur->addr), bitmask2masklen(cur->mask) + ); + } + in_addr_range_list_remove_all(&list); + + return 0; +} +#endif diff --git a/usr.sbin/npppd/common/addr_range.h b/usr.sbin/npppd/common/addr_range.h new file mode 100644 index 00000000000..7a6745dbd8d --- /dev/null +++ b/usr.sbin/npppd/common/addr_range.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef ADDR_RANGE_H +#define ADDR_RANGE_H + +struct in_addr_range { + u_int32_t addr; // !! host byte order + u_int32_t mask; // !! Host byte order + struct in_addr_range *next; +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +struct in_addr_range *in_addr_range_create __P((void)); +void in_addr_range_destroy __P((struct in_addr_range *)); +void in_addr_range_list_remove_all __P((struct in_addr_range **)); +int in_addr_range_list_includes __P((struct in_addr_range **, struct in_addr *)); +int in_addr_range_list_add __P((struct in_addr_range **, const char *)); +int main __P((int, char *[])); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/common/bytebuf.c b/usr.sbin/npppd/common/bytebuf.c new file mode 100644 index 00000000000..2f8c91ec96b --- /dev/null +++ b/usr.sbin/npppd/common/bytebuf.c @@ -0,0 +1,420 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * bytebuffer provides 'byte buffer' helper methods. + * + * Example:<pre> + * bytebuffer *buf = bytebuffer_create(BUFSIZ); + * int sz = read(STDIN_FILENO, bytebuffer_pointer(buf), + * bytebuffer_remaining(buf)); + * if (sz > 0) { + * bytebuffer_put(buf, BYTEBUFFER_PUT_DIRECT, sz); + * bytebuffer_flip(buf); + * + * sz = write(STDOUT_FILENO, bytebuffer_pointer(buf), + * bytebuffer_remaining(buf)); + * bytebuffer_compact(buf); + * }</pre> + * + * @author Yasuoka Masahiko + * $Id: bytebuf.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef BYTEBUF_DEBUG +#include <stdio.h> +#endif + +#ifndef BYTEBUF_ASSERT +#ifdef BYTEBUF_DEBUG +#define BYTEBUF_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define BYTEBUF_ASSERT(cond) +#endif +#endif + +#include "bytebuf.h" + +struct _bytebuffer { + /** current position */ + int position; + /** current limit */ + int limit; + /** position mark*/ + int mark; + /** capacity of buffer */ + size_t capacity; + /** allocated memory area */ + void *data; +}; + +#ifndef MIN +#define MIN(m,n) ((m) < (n)? (m) : (n)) +#endif + +/** + * Create a bytebuffer and allocate memory area. + * + * @param capacity Capacity of allocating memory. If zero or + * negative value is specified, this function don't allocate + * memory. + */ +bytebuffer * +bytebuffer_create(size_t capacity) +{ + bytebuffer *_this = NULL; + + if ((_this = malloc(sizeof(bytebuffer))) == NULL) + return NULL; + + memset(_this, 0, sizeof(bytebuffer)); + + if (capacity > 0) { + if ((_this->data = malloc(capacity)) == NULL) + goto reigai; + memset(_this->data, 0, capacity); + _this->capacity = capacity; + } else + _this->capacity = 0; + + _this->limit = _this->capacity; + _this->mark = -1; + return _this; +reigai: + if (_this != NULL) + free(_this); + return NULL; +} + +/** + * Create a bytebuffer using existing memory area. This memory area will + * be freed by bytebuffer's destructor. + * + * @data the pointer to existing memory area + * @param capacity capacity of 'data'. + */ +bytebuffer * +bytebuffer_wrap(void *data, size_t capacity) +{ + bytebuffer *_this; + + if ((_this = bytebuffer_create(0)) == NULL) + return NULL; + + _this->data = data; + _this->capacity = capacity; + _this->mark = -1; + + return _this; +} + +/** + * Unwrap memory from bytebuffer. + * + * @param _this the bytebuffer object. + */ +void * +bytebuffer_unwrap(bytebuffer *_this) +{ + void *rval; + + rval = _this->data; + _this->data = NULL; + _this->capacity = 0; + _this->position = 0; + _this->limit = 0; + _this->mark = -1; + + return rval; +} + +/** + * Change capacity of this buffer. + * + * @param _this the bytebuffer object. + * @param capacity new capacity. + */ +int +bytebuffer_realloc(bytebuffer *_this, size_t capacity) +{ + void *new_data; + + BYTEBUF_ASSERT(_this->limit <= capacity); + + if (_this->limit > capacity) { + errno = EINVAL; + return -1; + } + + if ((new_data = realloc(_this->data, capacity)) == NULL) + return -1; + + _this->data = new_data; + if (_this->limit == _this->capacity) + _this->limit = capacity; + _this->capacity = capacity; + + return 0; +} + +/** + * Compact this buffer. the bytes between position and limit are copied + * to the beginning of the buffer. + * + * @param _this the bytebuffer object. + */ +void +bytebuffer_compact(bytebuffer *_this) +{ + int len; + + len = bytebuffer_remaining(_this); + + if (len <= 0) + len = 0; + else if (_this->position != 0) + memmove(_this->data, + (const char *)_this->data + _this->position, (size_t)len); + + _this->position = len; + _this->limit = _this->capacity; + _this->mark = -1; +} + +static int bytebuffer_direct0; +/** + * BYTEBUFFER_PUT_DIRECT specifies the data that has been written already by + * direct access. + */ +const void * BYTEBUFFER_PUT_DIRECT = &bytebuffer_direct0; + +/** + * BYTEBUFFER_GET_DIRECT specifies the data that has been read already by + * direct access. + */ +void * BYTEBUFFER_GET_DIRECT = &bytebuffer_direct0; + +/** + * Write the given data to the buffer. + * If buffer is too small, this function returns <code>NULL</code> and + * <code>errno</code> is <code>ENOBUFS</code> + * + * @param _this the bytebuffer object. + * @param src source data. To specify the data that has been + * written already by direct access, use + * {@link ::BYTEBUFFER_PUT_DIRECT} for putting the data. + * NULL is the same {@link ::BYTEBUFFER_PUT_DIRECT} + * at least on this version, but using NULL is deprecated. + * @param srclen length of the source data. + * @see ::BYTEBUFFER_PUT_DIRECT + */ +void * +bytebuffer_put(bytebuffer *_this, const void *src, size_t srclen) +{ + void *rval; + + BYTEBUF_ASSERT(_this != NULL); + BYTEBUF_ASSERT(srclen > 0); + + if (srclen > bytebuffer_remaining(_this)) { + errno = ENOBUFS; + return NULL; + } + rval = (char *)_this->data + _this->position; + + if (src != NULL && src != BYTEBUFFER_PUT_DIRECT) + memcpy(rval, src, srclen); + + _this->position += srclen; + + return rval; +} + +/* + * Transfer data from this buffer to the given destination memory. + * If the given buffer is too small, this function returns <code>NULL</code> + * and <code>errno</code> is <code>ENOBUFS</code> + * + * @param dst pointer of the destination memory. Specify NULL + * to skip transfering the data. + * @param dstlne memory size of the destination. + */ +void * +bytebuffer_get(bytebuffer *_this, void *dst, size_t dstlen) +{ + BYTEBUF_ASSERT(_this != NULL); + BYTEBUF_ASSERT(dstlen > 0); + + if (dstlen > bytebuffer_remaining(_this)) { + errno = ENOBUFS; + return NULL; + } + if (dst != NULL && dst != BYTEBUFFER_GET_DIRECT) + memcpy(dst, (char *)_this->data + _this->position, dstlen); + + _this->position += dstlen; + + return dst; +} + +/** Returns this buffer's position */ +int +bytebuffer_position(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + return _this->position; +} + +/** Returns this buffer's limit */ +int +bytebuffer_limit(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + return _this->limit; +} + +/** Returns this buffer's capacity */ +int +bytebuffer_capacity(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + return _this->capacity; +} + +/** Returns a pointer to current position */ +void * +bytebuffer_pointer(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + return (char *)_this->data + _this->position; +} + +/** Returns the number of byte between current position and the limit*/ +size_t +bytebuffer_remaining(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + BYTEBUF_ASSERT(_this->limit >= _this->position); + + return _this->limit - _this->position; +} + +/** Returns whether there are data between current position and the limit */ +int +bytebuffer_has_remaining(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + return bytebuffer_remaining(_this) > 0; +} + +/** + * Flip this buffer. + * The limit is set to the position and the position is set zero. + */ +void +bytebuffer_flip(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + _this->limit = _this->position; + _this->position = 0; + _this->mark = -1; +} + +/** + * Rewind this buffer. + * The position is set to zero. + */ +void +bytebuffer_rewind(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + _this->position = 0; + _this->mark = -1; +} + +/** + * Clear this buffer. + * The position is set to zero. + */ +void +bytebuffer_clear(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + _this->limit = _this->capacity; + _this->position = 0; + _this->mark = -1; +} + +/** mark the current position. */ +void +bytebuffer_mark(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + _this->mark = _this->position; +} + +/** reset the position to the mark. */ +void +bytebuffer_reset(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + BYTEBUF_ASSERT(_this->mark >= 0); + + if (_this->mark >= 0) + _this->position = _this->mark; +} + +/** + * Destroy bytebuffer object. + */ +void +bytebuffer_destroy(bytebuffer *_this) +{ + BYTEBUF_ASSERT(_this != NULL); + + if (_this != NULL) { + if (_this->data != NULL) + free(_this->data); + free(_this); + } +} diff --git a/usr.sbin/npppd/common/bytebuf.h b/usr.sbin/npppd/common/bytebuf.h new file mode 100644 index 00000000000..c0ced991426 --- /dev/null +++ b/usr.sbin/npppd/common/bytebuf.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef BYTEBUF_H +#define BYTEBUF_H 1 + +/* $Id: bytebuf.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ + +typedef struct _bytebuffer bytebuffer; + +extern const void * BYTEBUFFER_PUT_DIRECT; +extern void * BYTEBUFFER_GET_DIRECT; + +#ifdef __cplusplus +extern "C" { +#endif + +bytebuffer *bytebuffer_create (size_t); +bytebuffer *bytebuffer_wrap (void *, size_t); +void *bytebuffer_unwrap (bytebuffer *); +int bytebuffer_realloc (bytebuffer *, size_t); +void bytebuffer_compact (bytebuffer *); +void *bytebuffer_put (bytebuffer *, const void *, size_t); +void *bytebuffer_get (bytebuffer *, void *, size_t); +int bytebuffer_position (bytebuffer *); +int bytebuffer_limit (bytebuffer *); +int bytebuffer_capacity (bytebuffer *); +void *bytebuffer_pointer (bytebuffer *); +size_t bytebuffer_remaining (bytebuffer *); +int bytebuffer_has_remaining (bytebuffer *); +void bytebuffer_flip (bytebuffer *); +void bytebuffer_rewind (bytebuffer *); +void bytebuffer_clear (bytebuffer *); +void bytebuffer_destroy (bytebuffer *); +void bytebuffer_mark (bytebuffer *); +void bytebuffer_reset (bytebuffer *); + +#ifdef __cplusplus +} +#endif + +#endif /* !BYTEBUF_H */ diff --git a/usr.sbin/npppd/common/config_helper.c b/usr.sbin/npppd/common/config_helper.c new file mode 100644 index 00000000000..5fbf057ea9c --- /dev/null +++ b/usr.sbin/npppd/common/config_helper.c @@ -0,0 +1,318 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: config_helper.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file コンフィグヘルパ。 + * <p> + * しています。</p> + */ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "properties.h" +#include "config_helper.h" +#include "debugmacro.h" + +#define KEYBUFSZ 512 + +/** + * コンフィグキーを作成するための文字列連結 + * (("prefix", "suffix") => "prefix.suffix") を、行います。内部で固定のバッファ + * 領域を返します。 + */ +const char * +config_key_prefix(const char *prefix, const char *suffix) +{ + static char keybuf[KEYBUFSZ]; + + strlcpy(keybuf, prefix, sizeof(keybuf)); + strlcat(keybuf, ".", sizeof(keybuf)); + strlcat(keybuf, suffix, sizeof(keybuf)); + + return keybuf; +} + +/** + * 設定を文字列で返します。 + * + * @param _this {@link ::properties}へのポインタ。 + * @param confKey 設定ファイルの設定項目名 + * @return 設定値。設定が存在しない場合には NULL が返ります。 + */ +const char * +config_str(struct properties *_this, const char *confKey) +{ + ASSERT(_this != NULL) + + return properties_get(_this, confKey); +} + +/** + * 設定を int で返します。 + * + * @param _this {@link ::properties}へのポインタ。 + * @param confKey 設定ファイルの設定項目名 + * @param defValue 設定が省略されている場合のデフォルトの値 + */ +int +config_int(struct properties *_this, const char *confKey, int defValue) +{ + int rval, x; + const char *val; + + val = config_str(_this, confKey); + + if (val == NULL) + return defValue; + + x = sscanf(val, "%d", &rval); + + if (x != 1) + /* 関数のインタフェースを変更して、エラーは区別すべきかも */ + return defValue; + + return rval; +} + +/** + * 設定があたえられた文字列と一致するかどうかを返します。 + * + * @param _this {@link ::properties}へのポインタ。 + * @param confKey 設定ファイルの設定項目名 + * @param defValue 設定が省略されている場合のデフォルトの値 + * @return 一致する場合には 1、一致しない場合には 0 が返ります。 + */ +int +config_str_equal(struct properties *_this, const char *confKey, + const char *str, int defValue) +{ + const char *val; + + val = config_str(_this, confKey); + + if (val == NULL) + return defValue; + + return (strcmp(val, str) == 0)? 1 : 0; +} + +/** + * 設定があたえられた文字列と一致するかどうかを返します。ASCII 文字の + * 大文字小文字は無視します。 + * + * @param _this {@link ::properties}へのポインタ。 + * @param confKey 設定ファイルの設定項目名 + * @param defValue 設定が省略されている場合のデフォルトの値 + * @return 一致する場合には 1、一致しない場合には 0 が返ります。 + */ +int +config_str_equali(struct properties *_this, const char *confKey, + const char *str, int defValue) +{ + const char *val; + + val = config_str(_this, confKey); + + if (val == NULL) + return defValue; + + return (strcasecmp(val, str) == 0)? 1 : 0; +} + +/*********************************************************************** + * 設定項目名に指定したプレフィックスをつけて設定を取得し、設定がなければ + * プレフィックスなしの設定項目で設定を取得するための関数です。 + * + * たとえば + * + * pppoe.service_name: default_service + * PPPoE0.pppoe.service_name: my_service + * + * という設定があった場合、 + * config_prefixed_str(prop, "PPPoE0", "service_name") + * を呼び出すと "my_service" が取得できます。設定に、 + * + * PPPoE0.pppoe.service_name: my_service + * + * がない場合には、"default_service" が取得できます。 + * + * config_helper.h に定義されている PREFIXED_CONFIG_FUNCTIONS マクロを + * 使って、プレフィックス部分の指定方法を固定して使うこともできます。 + ***********************************************************************/ +const char * +config_prefixed_str(struct properties *_this, const char *prefix, const char *confKey) +{ + char keybuf[KEYBUFSZ]; + const char *val; + + if (prefix != NULL) { + snprintf(keybuf, sizeof(keybuf), "%s.%s", prefix, confKey); + val = config_str(_this, keybuf); + if (val != NULL) + return val; + } + + return config_str(_this, confKey); +} + +int +config_prefixed_int(struct properties *_this, const char *prefix, const char *confKey, int defValue) +{ + char keybuf[KEYBUFSZ]; + const char *val; + + if (prefix != NULL) { + snprintf(keybuf, sizeof(keybuf), "%s.%s", prefix, confKey); + val = config_str(_this, keybuf); + if (val != NULL) + return config_int(_this, keybuf, defValue); + } + + return config_int(_this, confKey, defValue); +} + +int +config_prefixed_str_equal(struct properties *_this, const char *prefix, const char *confKey, const char *str, + int defValue) +{ + char keybuf[KEYBUFSZ]; + const char *val; + + if (prefix != NULL) { + snprintf(keybuf, sizeof(keybuf), "%s.%s", prefix, confKey); + val = config_str(_this, keybuf); + if (val != NULL) + return config_str_equal(_this, keybuf, str, + defValue); + } + + return config_str_equal(_this, confKey, str, defValue); +} + +int +config_prefixed_str_equali(struct properties *_this, const char *prefix, + const char *confKey, const char *str, int defValue) +{ + char keybuf[KEYBUFSZ]; + const char *val; + + ASSERT(_this != NULL); + + if (prefix != NULL) { + snprintf(keybuf, sizeof(keybuf), "%s.%s", prefix, confKey); + val = config_str(_this, keybuf); + if (val != NULL) + return config_str_equali(_this, keybuf, str, + defValue); + } + + return config_str_equali(_this, confKey, str, defValue); +} + +/*********************************************************************** + * 設定項目名に指定したプレフィックスと指定した名前をつけて設定を取得し、 + * 設定がなければプレフィックスに設定項目で設定を取得するための関数です。 + * + * たとえば + * + * ipcp.dns_primary: 192.168.0.1 + * ipcp.ipcp0.dns_primary: 192.168.0.2 + * + * という設定があった場合、 + * config_named_prefix_str(prop, "ipcp", "ipcp0", "dns_primary"); + * を呼び出すと "192.168.0.2" が取得できます。設定に、 + * + * ipcp.ipcp0.dns_primary: 192.168.0.2 + * + * がない場合には、"192.168.0.1" が取得できます。 + * + * config_helper.h に定義されている NAMED_PREFIX_CONFIG_FUNCTIONS マクロ + * を使って、プレフィックス部分の指定方法を固定して使うこともできます。 + ***********************************************************************/ +const char * +config_named_prefix_str(struct properties *_this, const char *prefix, + const char *name, const char *confKey) +{ + char keybuf[KEYBUFSZ]; + const char *val; + + if (name != NULL && name[0] != '\0') { + snprintf(keybuf, sizeof(keybuf), "%s.%s.%s", prefix, name, + confKey); + val = config_str(_this, keybuf); + if (val != NULL) + return val; + } + + snprintf(keybuf, sizeof(keybuf), "%s.%s", prefix, confKey); + return config_str(_this, keybuf); +} + +int +config_named_prefix_int(struct properties *_this, const char *prefix, + const char *name, const char *confKey, int defValue) +{ + char keybuf[KEYBUFSZ]; + const char *val; + + if (name != NULL && name[0] != '\0') { + snprintf(keybuf, sizeof(keybuf), "%s.%s.%s", prefix, name, + confKey); + val = config_str(_this, keybuf); + if (val != NULL) + return config_int(_this, keybuf, defValue); + } + + snprintf(keybuf, sizeof(keybuf), "%s.%s", prefix, confKey); + return config_int(_this, keybuf, defValue); +} + +int +config_named_prefix_str_equal(struct properties *_this, const char *prefix, + const char *name, const char *confKey, const char *str, int defValue) +{ + const char *val; + + val = config_named_prefix_str(_this, prefix, name, confKey); + if (val == NULL) + return defValue; + + return (strcmp(val, str) == 0)? 1 : 0; +} + +int +config_named_prefix_str_equali(struct properties *_this, const char *prefix, + const char *name, const char *confKey, const char *str, int defValue) +{ + const char *val; + + val = config_named_prefix_str(_this, prefix, name, confKey); + if (val == NULL) + return defValue; + + return (strcasecmp(val, str) == 0)? 1 : 0; +} diff --git a/usr.sbin/npppd/common/config_helper.h b/usr.sbin/npppd/common/config_helper.h new file mode 100644 index 00000000000..8f618727a9b --- /dev/null +++ b/usr.sbin/npppd/common/config_helper.h @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef CONFIG_HELPER_H +#define CONFIG_HELPER_H 1 + +#ifdef CONFIG_HELPER_NO_INLINE +#define INLINE +#else +#define INLINE inline +#endif + +#define CONFIG_DECL(fn_pre, basetype, prop) \ + INLINE const char *fn_pre ## _str(basetype *, const char *); \ + INLINE int fn_pre ## _int(basetype *, const char *, int); \ + INLINE int fn_pre ## _str_equal(basetype *, const char *, \ + const char *, int); \ + INLINE int fn_pre ## _str_equali(basetype *, const char *, \ + const char *, int); + +#define PREFIXED_CONFIG_DECL(fn_pre, basetype, prop, prefix) \ + INLINE const char *fn_pre ## _str(basetype *, const char *); \ + INLINE int fn_pre ## _int(basetype *, const char *, int); \ + INLINE int fn_pre ## _str_equal(basetype *, const char *, \ + const char *, int); \ + INLINE int fn_pre ## _str_equali(basetype *, const char *, \ + const char *, int); + +#define NAMED_PREFIX_CONFIG_DECL(fn_pre, basetype, prop, prefix, name) \ + INLINE const char *fn_pre ## _str(basetype *, const char *); \ + INLINE int fn_pre ## _int(basetype *, const char *, int); \ + INLINE int fn_pre ## _str_equal(basetype *, const char *, \ + const char *, int); \ + INLINE int fn_pre ## _str_equali(basetype *, const char *, \ + const char *, int); + +#define CONFIG_FUNCTIONS(fn_pre, basetype, prop) \ + INLINE const char * \ + fn_pre ## _str(basetype *_this, const char *confKey) { \ + return config_str(_this->prop, confKey); \ + } \ + INLINE int \ + fn_pre ## _int(basetype *_this, const char *confKey, \ + int defVal) { \ + return config_int(_this->prop, confKey, defVal); \ + } \ + int \ + fn_pre ## _str_equal(basetype *_this, const char *confKey, \ + const char *confVal, int defVal) { \ + return config_str_equal(_this->prop, confKey, confVal, \ + defVal); \ + } \ + int \ + fn_pre ## _str_equali(basetype *_this, const char *confKey, \ + const char *confVal, int defVal) { \ + return config_str_equali(_this->prop, confKey, confVal, \ + defVal); \ + } +#define PREFIXED_CONFIG_FUNCTIONS(fn_pre, basetype, prop, prefix) \ + INLINE const char * \ + fn_pre ## _str(basetype *_this, const char *confKey) { \ + return config_prefixed_str(_this->prop, _this->prefix, \ + confKey); \ + } \ + INLINE int \ + fn_pre ## _int(basetype *_this, const char *confKey, \ + int defVal) { \ + return config_prefixed_int(_this->prop, _this->prefix, \ + confKey, defVal); \ + } \ + int \ + fn_pre ## _str_equal(basetype *_this, const char *confKey, \ + const char *confVal, int defVal) { \ + return config_prefixed_str_equal(_this->prop, \ + _this->prefix, confKey, confVal, defVal); \ + } \ + int \ + fn_pre ## _str_equali(basetype *_this, const char *confKey, \ + const char *confVal, int defVal) { \ + return config_prefixed_str_equali(_this->prop, \ + _this->prefix, confKey, confVal, defVal); \ + } + +#define NAMED_PREFIX_CONFIG_FUNCTIONS(fn_pre, basetype, prop, prefix, \ + name) \ + INLINE const char * \ + fn_pre ## _str(basetype *_this, const char *confKey) { \ + return config_named_prefix_str(_this->prop, \ + prefix, _this->name, \ + confKey); \ + } \ + INLINE int \ + fn_pre ## _int(basetype *_this, const char *confKey, \ + int defVal) { \ + return config_named_prefix_int(_this->prop, prefix, \ + _this->name, confKey, defVal); \ + } \ + int \ + fn_pre ## _str_equal(basetype *_this, const char *confKey, \ + const char *confVal, int defVal) { \ + return config_named_prefix_str_equal(_this->prop, \ + prefix, _this->name, confKey, confVal, defVal); \ + } \ + int \ + fn_pre ## _str_equali(basetype *_this, const char *confKey, \ + const char *confVal, int defVal) { \ + return config_named_prefix_str_equali(_this->prop, \ + prefix, _this->name, confKey, confVal, defVal); \ + } + +#ifdef __cplusplus +extern "C" { +#endif + +const char *config_key_prefix (const char *, const char *); +const char *config_str (struct properties *, const char *); +int config_int (struct properties *, const char *, int); +int config_str_equal (struct properties *, const char *, const char *, int); +int config_str_equali (struct properties *, const char *, const char *, int); +const char *config_prefixed_str (struct properties *, const char *, const char *); +int config_prefixed_int (struct properties *, const char *, const char *, int); +int config_prefixed_str_equal (struct properties *, const char *, const char *, const char *, int); +int config_prefixed_str_equali (struct properties *, const char *, const char *, const char *, int); +const char *config_named_prefix_str (struct properties *, const char *, const char *, const char *); +int config_named_prefix_int (struct properties *, const char *, const char *, const char *, int); +int config_named_prefix_str_equal (struct properties *, const char *, const char *, const char *, const char *, int); +int config_named_prefix_str_equali (struct properties *, const char *, const char *, const char *, const char *, int); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/common/csvreader.c b/usr.sbin/npppd/common/csvreader.c new file mode 100644 index 00000000000..ce8a852018f --- /dev/null +++ b/usr.sbin/npppd/common/csvreader.c @@ -0,0 +1,366 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* original version は CSVReader.java */ +/* @file + * CSV(RFC4180) を読み込むための補助的な関数 + * <pre> + * csvreader *csv; + * const char **cols; + * char buf[1024]; + * + * csv = csvreader_create(); + * while (fgets(buf, sizeof(buf), stdin) != NULL) { + * if (csvreader_parse(csv, buf) != CSVREADER_NO_ERROR) { + * // error handling + * break; + * } + * cols = csv_reader_get_column(csv) + * if (cols == NULL) + * continue; + * // your code here. col[0] is the first column. + * } + * if (csvreader_parse(csv, buf) == CSVREADER_NO_ERROR) { + * cols = csv_reader_get_column(csv) + * if (cols != NULL) { + * // your code here. col[0] is the first column. + * } + * } else { + * // error handling + * } + * csvreader_destroy(csv); + *</pre> + */ +/* $Id: csvreader.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include "csvreader.h" + +struct _csvreader { + char *buffer; + const char **cols; + int cap; + int pos; + int start_pos; + int col_cap; + int col_pos; + int state; + int column_start_with_quote:1; +}; + +#define CSV_BUFSIZ 256 +#define CSV_COLSIZ 32 + +#define CSV_INIT 0 +#define CSV_IN_DATA 1 +#define CSV_WAIT_DELIM 2 +#define CSV_HAS_DQUOTE 3 +#define CSV_FLUSH_WAIT 4 + +#define DQUOTE '"' +#define COLON ',' +#define CR '\r' +#define LF '\n' + +static int csvreader_buffer_append (csvreader *, const char *, int); +static int csvreader_flush_column (csvreader *); +static CSVREADER_STATUS csvreader_parse_flush0 (csvreader *, int); + +/** + * cvsreader コンテキストを作成して返却します。メモリ割り当てに失敗すると、 + * NULL が返ります。 + */ +csvreader * +csvreader_create(void) +{ + csvreader *_this; + + if ((_this = malloc(sizeof(csvreader))) == NULL) + return NULL; + memset(_this, 0, sizeof(*_this)); + _this->state = CSV_INIT; + + return _this; +} + + +/** + * cvsreader コンテキストを解放します。 + */ +void +csvreader_destroy(csvreader *_this) +{ + if (_this->buffer != NULL) + free(_this->buffer); + if (_this->cols != NULL) + free(_this->cols); + free(_this); +} + +/** + * パース済みのカラム配列の数を取得します。 + */ +int +csvreader_get_number_of_column(csvreader *_this) +{ + if (_this->state != CSV_FLUSH_WAIT) + return 0; + return _this->col_pos; +} + +/** + * パース済みのカラム配列(=行) を取得します。 + */ +const char ** +csvreader_get_column(csvreader *_this) +{ + if (_this->state != CSV_FLUSH_WAIT) + return NULL; + + _this->col_pos = 0; + _this->pos = 0; + _this->start_pos = 0; + _this->state = CSV_INIT; + + return _this->cols; +} + +/** + * cvsreader コンテキストをリセットします。 + */ +void +csvreader_reset(csvreader *_this) +{ + _this->cap = 0; + _this->pos = 0; + _this->start_pos = 0; + _this->col_cap = 0; + _this->col_pos = 0; + _this->state = 0; + _this->column_start_with_quote = 0; +} + +/** + * パース途中のカラムのパースを完了します。 + * <p> + * CSVファイルの末尾など、続く行 + * が存在しない場合や、CSV としての行の区切りであることが確実な場合に呼び + * だします。フィールドが終了していない場合などにはエラーが返ります。</p> + */ +CSVREADER_STATUS +csvreader_parse_flush(csvreader *_this) +{ + return csvreader_parse_flush0(_this, 1); +} + +/** + * 行をパースします。 + * + * @param csvreader コンテキスト + * @param パースする行。 + */ +CSVREADER_STATUS +csvreader_parse(csvreader *_this, const char *line) +{ + int off, lline, append; + + lline = strlen(line); + + if (_this->state == CSV_FLUSH_WAIT) + return CSVREADER_HAS_PENDING_COLUMN; + + if (csvreader_buffer_append(_this, line, lline) != 0) + return CSVREADER_OUT_OF_MEMORY; + + for (off = 0; off < lline; off++) { + append = 1; + switch (_this->state) { + case CSV_INIT: + _this->state = CSV_IN_DATA; + if (line[off] == DQUOTE) { + _this->column_start_with_quote = 1; + break; + } + /* FALL THROUGH */ + case CSV_IN_DATA: + if (_this->column_start_with_quote != 0) { + if (line[off] == DQUOTE) + _this->state = CSV_HAS_DQUOTE; + break; + } + if (line[off] == COLON) { + append = 0; + csvreader_flush_column(_this); + } + if (_this->column_start_with_quote == 0 && + (line[off] == CR || line[off] == LF)) + goto eol; + break; + case CSV_HAS_DQUOTE: + if (line[off] == DQUOTE) { + _this->state = CSV_IN_DATA; + append = 0; + break; + } + _this->state = CSV_WAIT_DELIM; + /* FALL THROUGH */ + case CSV_WAIT_DELIM: + if (line[off] == CR || line[off] == LF) + goto eol; + append = 0; + if (line[off] != COLON) + return CSVREADER_PARSE_ERROR; + csvreader_flush_column(_this); + break; + } + if (append) + _this->buffer[_this->pos++] = line[off]; + } +eol: + + return csvreader_parse_flush0(_this, 0); +} + +static CSVREADER_STATUS +csvreader_parse_flush0(csvreader *_this, int is_final) +{ + if (_this->state == CSV_FLUSH_WAIT) + return CSVREADER_NO_ERROR; + switch (_this->state) { + case CSV_IN_DATA: + if (_this->column_start_with_quote != 0) { + if (is_final) + return CSVREADER_PARSE_ERROR; + /* wait next line */ + return CSVREADER_NO_ERROR; + } + /* FALL THROUGH */ + case CSV_INIT: + if (is_final && _this->col_pos == 0) + return CSVREADER_NO_ERROR; + /* FALL THROUGH */ + case CSV_HAS_DQUOTE: + case CSV_WAIT_DELIM: + csvreader_flush_column(_this); + _this->state = CSV_FLUSH_WAIT; + return CSVREADER_NO_ERROR; + } + return CSVREADER_PARSE_ERROR; +} + +/** + * char ポインタの配列に格納された列を CSV の表現の行文字列に変換します。 + + * @param cols 変換する列。NULL 要素は、列の終わりとみなします。 + * @param ncols 列の数。 + * @param buffer 変換後の行文字列を書き込むスペース + * @param lbuffer 変換後の行文字列を書き込むスペースの大きさ。 + */ +int +csvreader_toline(const char **cols, int ncols, char *buffer, int lbuffer) +{ + int i, j, off; + + off = 0; +#define checksize() if (off + 1 > lbuffer) { goto enobufs; } + for (i = 0; i < ncols && cols[i] != NULL; i++) { + if (i != 0) { + checksize(); + buffer[off++] = ','; + } + for (j = 0; cols[i][j] != '\0'; j++) { + if (j == 0) { + checksize(); + buffer[off++] = '"'; + } + if (cols[i][j] == '"') { + checksize(); + buffer[off++] = '"'; + } + checksize(); + buffer[off++] = cols[i][j]; + } + checksize(); + buffer[off++] = '"'; + } + checksize(); + buffer[off++] = '\0'; + + return 0; +enobufs: + return 1; +} + +static int +csvreader_buffer_append(csvreader *_this, const char *buf, int lbuf) +{ + int ncap; + char *nbuffer; + + if (_this->pos + lbuf > _this->cap) { + ncap = _this->cap + lbuf; + if ((ncap % CSV_BUFSIZ) != 0) + ncap += CSV_BUFSIZ - (ncap % CSV_BUFSIZ); + if ((nbuffer = realloc(_this->buffer, ncap)) == NULL) + return 1; + _this->cap = ncap; + _this->buffer = nbuffer; + } + + return 0; +} + +static int +csvreader_flush_column(csvreader *_this) +{ + int ncap; + const char **ncols; + + if (_this->col_pos + 1 >= _this->col_cap) { + ncap = _this->col_cap + CSV_COLSIZ; + if ((ncols = realloc(_this->cols, ncap * sizeof(char *))) + == NULL) + return CSVREADER_OUT_OF_MEMORY; + _this->col_cap = ncap; + _this->cols = ncols; + } + + if (_this->column_start_with_quote != 0) { + _this->start_pos++; + _this->buffer[_this->pos - 1] = '\0'; + } else { + _this->buffer[_this->pos++] = '\0'; + } + + _this->cols[_this->col_pos++] = _this->buffer + _this->start_pos; + _this->cols[_this->col_pos] = NULL; + _this->start_pos = _this->pos; + _this->column_start_with_quote = 0; + _this->state = CSV_INIT; + + return CSVREADER_NO_ERROR; +} diff --git a/usr.sbin/npppd/common/csvreader.h b/usr.sbin/npppd/common/csvreader.h new file mode 100644 index 00000000000..8dd46f8c481 --- /dev/null +++ b/usr.sbin/npppd/common/csvreader.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef CSVREADER_H +#define CSVREADER_H 1 +/* $Id: csvreader.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ + +/** cvsreader のステータスを表す */ +typedef enum _CSVREADER_STATUS { + /** 正常に処理が完了した */ + CSVREADER_NO_ERROR = 0, + /** + * 処理中の列が存在する + * <p> + * csvreader_get_columns で取り出す前に、次の行をパースすると + * このエラーが発生します。</p> + */ + CSVREADER_HAS_PENDING_COLUMN = 10001, + /** メモリー割り当てに失敗した */ + CSVREADER_OUT_OF_MEMORY = 10002, + /** パースエラー */ + CSVREADER_PARSE_ERROR = 10003 +} CSVREADER_STATUS; + +typedef struct _csvreader csvreader; + +#ifdef __cplusplus +extern "C" { +#endif + +int csvreader_toline (const char **, int, char *, int); +csvreader * csvreader_create (void); +void csvreader_destroy (csvreader *); +void csvreader_reset (csvreader *); +const char ** csvreader_get_column (csvreader *); +int csvreader_get_number_of_column (csvreader *); +CSVREADER_STATUS csvreader_parse_flush (csvreader *); +CSVREADER_STATUS csvreader_parse (csvreader *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/common/csvreader_test.c b/usr.sbin/npppd/common/csvreader_test.c new file mode 100644 index 00000000000..865fcd73018 --- /dev/null +++ b/usr.sbin/npppd/common/csvreader_test.c @@ -0,0 +1,272 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * cc -o csvreader_test csvreader.c csvreader_test.c + */ +/* $Id: csvreader_test.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "csvreader.h" + +#define ASSERT(x) \ + if (!(x)) { \ + fprintf(stderr, \ + "\nASSERT(%s) failed on %s() at %s:%d.\n" \ + , #x, __func__, __FILE__, __LINE__); \ + abort(); \ + } +#define countof(x) (sizeof((x)) / sizeof((x)[0])) +#define test(x) \ + { \ + fprintf(stderr, "%-10s ... ", #x); \ + fflush(stderr); \ + x(); \ + fprintf(stderr, "ok\n"); \ + } + + +static void +test01(void) +{ + int i; + CSVREADER_STATUS status; + const char ** column; + csvreader *csv; + char *csv_data[] = { + "hogehoge,fugafuga\n", + "hogehoge,fugafuga\n", + "hogehoge,fugafuga\n" + }; + + csv = csvreader_create(); + ASSERT(csv != NULL); + + for (i = 0; i < countof(csv_data); i++) { + status = csvreader_parse(csv, csv_data[i]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fugafuga") == 0); + ASSERT(column[2] == NULL); + } + csvreader_parse_flush(csv); + column = csvreader_get_column(csv); + ASSERT(column == NULL); + + csvreader_destroy(csv); +} + +static void +test02(void) +{ + CSVREADER_STATUS status; + const char ** column; + csvreader *csv; + char *csv_data[] = { + "\"hogehoge\",\"fugafuga\"\n", + "hogehoge,\"fugafuga\"\n", + "\"hogehoge\",fugafuga\n", + "\"hogehoge\",fuga\"fuga\n", + "\"hogehoge\",fuga\nfuga\n", + "\"hogehoge\",\"fuga\nfuga\"\n", + "\"hogehoge\",\"fuga\rfuga\"\n", + "\",fugafuga\n", + "hogehoge\",\n", + "\"hogehoge\n", + "hogehoge,fugafuga", + }; + + csv = csvreader_create(); + ASSERT(csv != NULL); + + status = csvreader_parse(csv, csv_data[0]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fugafuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[1]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fugafuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[2]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fugafuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[3]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fuga\"fuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[4]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[5]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + //printf("**%s**\n", column[1]); + ASSERT(strcmp(column[1], "fuga\nfuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[6]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + //printf("**%s**\n", column[1]); + ASSERT(strcmp(column[1], "fuga\rfuga") == 0); + ASSERT(column[2] == NULL); + + status = csvreader_parse(csv, csv_data[7]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column == NULL); + status = csvreader_parse(csv, csv_data[8]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], ",fugafuga\nhogehoge") == 0); + + csvreader_parse_flush(csv); + column = csvreader_get_column(csv); + ASSERT(column == NULL); + + status = csvreader_parse(csv, csv_data[9]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column == NULL); + + status = csvreader_parse_flush(csv); + ASSERT(status == CSVREADER_PARSE_ERROR); + column = csvreader_get_column(csv); + ASSERT(column == NULL); + + csvreader_reset(csv); + + status = csvreader_parse(csv, csv_data[10]); + ASSERT(status == CSVREADER_NO_ERROR); + column = csvreader_get_column(csv); + ASSERT(column != NULL); + ASSERT(strcmp(column[0], "hogehoge") == 0); + ASSERT(strcmp(column[1], "fugafuga") == 0); + + csvreader_destroy(csv); +} + +static void +test03(void) +{ + int i; + CSVREADER_STATUS status; + const char ** column; + csvreader *csv; + char *csv_data[] = { + "yasuoka,hogehoge,\"10.0.0.1\n", + "\n" + }; + + csv = csvreader_create(); + ASSERT(csv != NULL); + + status = csvreader_parse(csv, csv_data[0]); + ASSERT(status == CSVREADER_NO_ERROR); + + status = csvreader_parse_flush(csv); + ASSERT(status == CSVREADER_PARSE_ERROR); + column = csvreader_get_column(csv); + ASSERT(column == NULL); + + csvreader_destroy(csv); +} +static void +test04(void) +{ + int rval; + char line[8]; + const char *test[5]; + + memset(line, 0x55, sizeof(line)); + test[0] = "xxxxx"; + test[1] = NULL; + rval = csvreader_toline(test, 5, line, sizeof(line)); + ASSERT(rval == 0); + ASSERT(strcmp(line, "\"xxxxx\"") == 0); + + memset(line, 0x55, sizeof(line)); + test[0] = "xxxxxx"; + test[1] = NULL; + rval = csvreader_toline(test, 5, line, sizeof(line)); + ASSERT(rval != 0); + + memset(line, 0x55, sizeof(line)); + test[0] = "xx"; /* 5 */ + test[1] = "x"; /* 4 */ + test[2] = NULL; + rval = csvreader_toline(test, 5, line, sizeof(line)); + ASSERT(rval != 0); + + memset(line, 0x55, sizeof(line)); + test[0] = "x"; /* 5 */ + test[1] = "x"; /* 4 */ + test[2] = NULL; + rval = csvreader_toline(test, 5, line, sizeof(line)); + ASSERT(rval == 0); + ASSERT(strcmp(line, "\"x\",\"x\"") == 0); +} + +int +main(int argc, char *argv[]) +{ + test(test01); + test(test02); + test(test03); + test(test04); + + return 0; +} + diff --git a/usr.sbin/npppd/common/debugmacro.h b/usr.sbin/npppd/common/debugmacro.h new file mode 100644 index 00000000000..9bbd676a0da --- /dev/null +++ b/usr.sbin/npppd/common/debugmacro.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef DEBUG_MACRO_H +#define DEBUG_MACRO_H 1 + +#if ((__STDC_VERSION__ - 0) >= 199901L) +#define _FUNC_ __func__ +#else +#define _FUNC_ "" +#endif + +#ifndef ASSERT +#ifdef DEBUG +#define ASSERT(x) \ + if (!(x)) { \ + fprintf(stderr, \ + "\nASSERT(%s) failed on %s() at %s:%d.\n" \ + , #x, _FUNC_, __FILE__, __LINE__); \ + abort(); \ + } +#else +#ifdef lint +#define ASSERT(x) +#else +#define ASSERT(x) ((void)0); +#endif +#endif +#endif + +#endif diff --git a/usr.sbin/npppd/common/debugutil.c b/usr.sbin/npppd/common/debugutil.c new file mode 100644 index 00000000000..270972e6bf4 --- /dev/null +++ b/usr.sbin/npppd/common/debugutil.c @@ -0,0 +1,306 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <stdlib.h> +#include <time.h> + +#include "debugutil.h" + +int debuglevel = 0; +FILE *debugfp = NULL; +static int prio_idx_inititialized = 0; + +static void set_prio_idx_init __P((void)); + +#ifndef countof +#define countof(x) (sizeof((x)) / sizeof((x)[0])) +#endif +#define VAL_NAME(x) { (x), #x} + +#ifndef LOG_PRI +#define LOG_PRI(p) ((p) & LOG_PRIMASK) +#endif + +static int use_syslog = 1; +static int no_debuglog = 0; +static int syslog_level_adjust = 0; + +static struct { + int prio; + const char *name; +} prio_name[] = { + VAL_NAME(LOG_EMERG), + VAL_NAME(LOG_ALERT), + VAL_NAME(LOG_CRIT), + VAL_NAME(LOG_ERR), + VAL_NAME(LOG_WARNING), + VAL_NAME(LOG_NOTICE), + VAL_NAME(LOG_INFO), + VAL_NAME(LOG_DEBUG) +}; + +static const char *prio_name_idx[16]; + +static void +set_prio_idx_init() +{ + int i; + + if (prio_idx_inititialized) + return; + for (i = 0; i < (int)countof(prio_name); i++) { + ASSERT(prio_name[i].prio < countof(prio_name_idx)); + if (prio_name[i].prio >= (int)countof(prio_name_idx)) + continue; + prio_name_idx[prio_name[i].prio] = &prio_name[i].name[4]; + } + prio_idx_inititialized = 1; +} + +void +debug_set_debugfp(fp) + FILE *fp; +{ + debugfp = fp; +} + +void +debug_use_syslog(b) + int b; +{ + if (b) + use_syslog = 1; + else + use_syslog = 0; +} + +void +debug_set_no_debuglog(int no_debuglog0) +{ + if (no_debuglog0) + no_debuglog = 1; + else + no_debuglog = 0; +} + +FILE * +debug_get_debugfp() +{ + return debugfp; +} + +#define DL(p) ((p) >> 24 & 0xff) +int +vlog_printf(uint32_t prio, const char *format, va_list ap) +{ + int status = 0, i, fmtoff = 0, state = 0, saved_errno, level; + char fmt[8192]; + struct tm *lt; + time_t now; + + if (DL(prio) > 0 && debuglevel < (int)DL(prio)) + return -1; + if (no_debuglog && LOG_PRI(prio) >= LOG_DEBUG) + return -1; + + if (!prio_idx_inititialized) + set_prio_idx_init(); + if (use_syslog && DL(prio) == 0) { + level = LOG_PRI(prio) + syslog_level_adjust; + if (!no_debuglog || level < LOG_DEBUG) { + level = MIN(LOG_DEBUG, level); + level = MAX(LOG_EMERG, level); + level |= (prio & LOG_FACMASK); + vsyslog(level, format, ap); + } + } + + if (debugfp == NULL) + return -1; + + time(&now); + lt = localtime(&now); + + for (i = 0; i < (int)strlen(format); i++) { + switch(state) { + case 0: + switch(format[i]) { + case '%': + state = 1; + goto copy_loop; + case '\n': + fmt[fmtoff++] = '\n'; + fmt[fmtoff++] = '\t'; + goto copy_loop; + } + break; + case 1: + switch(format[i]) { + default: + case '%': + fmt[fmtoff++] = '%'; + state = 0; + break; + case 'm': + fmt[fmtoff] = '\0'; + saved_errno = errno; + strlcat(fmt, strerror(errno), sizeof(fmt)); + errno = saved_errno; + fmtoff = strlen(fmt); + state = 0; + goto copy_loop; + } + } + fmt[fmtoff++] = format[i]; +copy_loop: + continue; + } + if (fmt[fmtoff-1] == '\t') + fmtoff--; + if (fmt[fmtoff-1] != '\n') + fmt[fmtoff++] = '\n'; + + fmt[fmtoff] = '\0'; + + ASSERT(0 <= LOG_PRI(prio) + && LOG_PRI(prio) < countof(prio_name_idx) + && prio_name_idx[LOG_PRI(prio)] != NULL); + ftell(debugfp); + fprintf(debugfp, + "%04d-%02d-%02d %02d:%02d:%02d:%s: " + , lt->tm_year + 1900 + , lt->tm_mon + 1 + , lt->tm_mday + , lt->tm_hour + , lt->tm_min + , lt->tm_sec + , (prio & 0xff000000) ? "DEBUG" : prio_name_idx[LOG_PRI(prio)] + ); + status = vfprintf(debugfp, fmt, ap); + fflush(debugfp); + + return status; +} + +int +log_printf(int prio, const char *fmt, ...) +{ + int status; + va_list ap; + + va_start(ap, fmt); + status = vlog_printf((uint32_t)prio, fmt, ap); + va_end(ap); + + return status; +} + +void +debug_set_syslog_level_adjust(int adjust) +{ + syslog_level_adjust = adjust; +} + +int +debug_get_syslog_level_adjust(void) +{ + return syslog_level_adjust; +} + + +/* + * show_hd - + * print hexadecimal/ascii dump for debug + * + * usage: + * show_hd(stderr, buf, sizeof(buf)); + */ +void +show_hd(FILE *file, const u_char *buf, int len) +{ + int i, o = 0; + int hd_cnt = 0; + char linebuf[80]; + char asciibuf[17]; + + memset(asciibuf, ' ', sizeof(asciibuf)); + asciibuf[sizeof(asciibuf)-1] = '\0'; + + for (i = 0; i < len; i++) { + if (0x20 <= *(buf+i) && *(buf+i) <= 0x7e) + asciibuf[hd_cnt % 16] = *(buf+i); + else + asciibuf[hd_cnt % 16] = '.'; + + switch (hd_cnt % 16) { + case 0: + o += snprintf(linebuf + o, sizeof(linebuf) - o, + "%04x %02x", hd_cnt, + (unsigned char)*(buf+i)); + break; + case 15: + o += snprintf(linebuf + o, sizeof(linebuf) - o, + "%02x", (unsigned char)*(buf+i)); + if (file) + fprintf(file, "\t%-47s |%s|\n", linebuf, + asciibuf); + else + syslog(LOG_ERR, "%-47s |%s|\n", linebuf, + asciibuf); + memset(asciibuf, ' ', sizeof(asciibuf)); + asciibuf[sizeof(asciibuf)-1] = '\0'; + o = 0; + break; + case 8: + o += snprintf(linebuf + o, sizeof(linebuf) - o, + "- %02x", (unsigned char)*(buf+i)); + break; + default: + if (hd_cnt % 2 == 1) + o += snprintf(linebuf + o, sizeof(linebuf) - o, + "%02x ", (unsigned char)*(buf+i)); + else + o += snprintf(linebuf + o, sizeof(linebuf) - o, + "%02x", (unsigned char)*(buf+i)); + break; + } + hd_cnt++; + } + if (hd_cnt > 0 && (hd_cnt % 16) != 0) { + if (file) + fprintf(file, "\t%-47s |%s|\n", linebuf, asciibuf); + else + syslog(LOG_ERR, "%-47s |%s|\n", linebuf, asciibuf); + } + if (file) + fflush(file); +} diff --git a/usr.sbin/npppd/common/debugutil.h b/usr.sbin/npppd/common/debugutil.h new file mode 100644 index 00000000000..bd095bbc981 --- /dev/null +++ b/usr.sbin/npppd/common/debugutil.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef DEBUG_UTIL_H +#define DEBUG_UTIL_H 1 + +#include "debugmacro.h" + +#define DEBUG_LEVEL_1 ( 1 << 24) +#define DEBUG_LEVEL_2 ( 2 << 24) +#define DEBUG_LEVEL_3 ( 3 << 24) +#define DEBUG_LEVEL_4 ( 4 << 24) +#define DEBUG_LEVEL_5 ( 5 << 24) +#define DEBUG_LEVEL_6 ( 6 << 24) +#define DEBUG_LEVEL_7 ( 7 << 24) +#define DEBUG_LEVEL_8 ( 8 << 24) +#define DEBUG_LEVEL_9 ( 9 << 24) +#define DEBUG_LEVEL_10 (10 << 24) +#define DEBUG_LEVEL_11 (11 << 24) +#define DEBUG_LEVEL_12 (12 << 24) +#define DEBUG_LEVEL_13 (13 << 24) +#define DEBUG_LEVEL_14 (14 << 24) +#define DEBUG_LEVEL_15 (15 << 24) + +extern int debuglevel; + +/* adapted from FreeBSD:/usr/include/sys/cdefs */ +#ifndef __printflike +#if __GNUC__ < 2 || __GNUC__ == 2 && __GNUC_MINOR__ < 7 +#define __printflike(fmtarg, firstvararg) +#else +#define __printflike(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> + +void debug_set_debugfp (FILE *); +FILE *debug_get_debugfp (void); +int vlog_printf (uint32_t, const char *, va_list); +int log_printf (int, const char *, ...) __printflike(2, 3); +void show_hd (FILE *, const u_char *, int); +void debug_use_syslog (int); +void debug_set_syslog_level_adjust (int); +int debug_get_syslog_level_adjust (void); +void debug_set_no_debuglog (int); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/usr.sbin/npppd/common/hash.c b/usr.sbin/npppd/common/hash.c new file mode 100644 index 00000000000..612a4ff4664 --- /dev/null +++ b/usr.sbin/npppd/common/hash.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "hash.h" + +/* hash_create - Create a new hash table. + * Returns a pointer of new hash_table on success. Otherwise returns + * NULL. + */ +hash_table * +hash_create(cmp_func, hash_func, hsz) + int (*cmp_func) (const void *, const void *); + uint32_t (*hash_func) (const void *, int); + int hsz; +{ + hash_table *htbl; + + htbl = (hash_table *)malloc(sizeof(hash_table)); + if (htbl == NULL) + return NULL; + + if (hsz < 1) + htbl->size = HASH_SIZE; + else + htbl->size = hsz; + + htbl->bucket = calloc(htbl->size, sizeof(hash_link *)); + htbl->cmp = cmp_func; + htbl->hash = hash_func; + htbl->cur = 0; + htbl->bucket_cur = NULL; + + return htbl; +} + +/* hash_first - Get first item from hash table. + * Returns a pointer of first bucket on success. Otherwise returns + * NULL. + */ +hash_link * +hash_first(htbl) + hash_table *htbl; +{ + htbl->cur = 0; + htbl->bucket_cur = NULL; + return hash_next(htbl); +} + +/* hash_next - Get next item from hash table. + * Returns a pointer of next bucket on success. Otherwise returns + * NULL. + */ +hash_link * +hash_next(htbl) + hash_table *htbl; +{ + hash_link *hlink; + + if (htbl->bucket_cur != NULL) { + hlink = htbl->bucket_cur; + htbl->bucket_cur = hlink->next; + return hlink; + } + while (htbl->cur < htbl->size) { + if (htbl->bucket[htbl->cur] != NULL) { + hlink = htbl->bucket[htbl->cur++]; + htbl->bucket_cur = hlink->next; + return hlink; + } + htbl->cur++; + } + return NULL; +} + +/* hash_lookup - Lookup item under the key in hash table. + * Return a pointer of the bucket on success. Otherwise returns + * NULL + */ +hash_link * +hash_lookup(htbl, k) + hash_table *htbl; + const void *k; +{ + int c; + hash_link *w; + + if (htbl == NULL || k == NULL) + return NULL; + c = (htbl->hash) (k, (int)htbl->size); + for (w = htbl->bucket[c]; w != NULL; w = w->next) + if (htbl->cmp(w->key, k) == 0) + return w; + return NULL; +} + +/* hash_insert - Insert a item into hash table. + * Return 0 on success. Return -1 on failure. + */ +int +hash_insert(htbl, k, i) + hash_table *htbl; + const void *k; + void *i; +{ + int c; + hash_link *n; + + if (htbl == NULL || k == NULL) + return -1; + + if ((n = (hash_link *)malloc(sizeof(hash_link))) == NULL) { + return -1; + } + + c = (htbl->hash) (k, (int)htbl->size); + + n->item = i; + n->key = k; + n->next = htbl->bucket[c]; + htbl->bucket[c] = n; + + return 0; +} + +/* hash_delete - Remove a item from hash table. + * If memfree then free the item. Return 0 on success. Return -1 + * on failure. + */ +int +hash_delete(htbl, k, memfree) + hash_table *htbl; + const void *k; + int memfree; +{ + int i; + hash_link *b, *w; + + if (htbl == NULL || k == NULL) + return -1; + + i = (htbl->hash) (k, (int)htbl->size); + + for (w = htbl->bucket[i], b = NULL; w != NULL; w = w->next) { + if (htbl->cmp(w->key, k) == 0) { + if (b != NULL) + b->next = w->next; + else + htbl->bucket[i] = w->next; + + if (htbl->bucket_cur == w) + htbl->bucket_cur = w->next; + + if (w->item != NULL && memfree) { + free(w->item); + } + free(w); + return 0; + } + b = w; + } + return -1; +} + +/* + * delete all items from this hash_table. + * If memfree != 0 then free items. + */ +void +hash_delete_all(htbl, memfree) + hash_table *htbl; + int memfree; +{ + int i; + hash_link *w, *hl; + + for (i = 0; i < htbl->size; i++) { + hl = htbl->bucket[i]; + htbl->bucket[i] = NULL; + while (hl != NULL) { + w = hl; + hl = hl->next; + if (memfree && w->item != NULL) + free(w->item); + free(w); + } + } +} + +/* hash_free - Free hash table and all buckets. + */ +void +hash_free(htbl) + hash_table *htbl; +{ + if (htbl != NULL) { + if (htbl->bucket != NULL) + free(htbl->bucket); + free(htbl); + } +} diff --git a/usr.sbin/npppd/common/hash.h b/usr.sbin/npppd/common/hash.h new file mode 100644 index 00000000000..eb3012dea21 --- /dev/null +++ b/usr.sbin/npppd/common/hash.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: hash.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#ifndef HASH_H +#define HASH_H + +#ifndef HASH_SIZE +#define HASH_SIZE 127 +#endif + +typedef struct HASH_LINK { + const void *key; + struct HASH_LINK *next; + void *item; +} hash_link; + +typedef struct { + int (*cmp) (const void *, const void *); + uint32_t (*hash) (const void *, int); + hash_link **bucket; + size_t size; + int cur; + hash_link *bucket_cur; +} hash_table; + +#ifdef __cplusplus +extern "C" { +#endif +hash_table *hash_create(int (*)(const void *, const void *), uint32_t (*) (const void *, int), int); +hash_link *hash_first __P((hash_table *)); +hash_link *hash_next __P((hash_table *)); +hash_link *hash_lookup __P((hash_table *, const void *)); +int hash_insert __P((hash_table *, const void *, void *)); +int hash_delete __P((hash_table *, const void *, int)); +void hash_delete_all __P((hash_table *, int)); +void hash_free __P((hash_table *)); + +#ifdef __cplusplus +} +#endif + +#endif /* HASH_H */ diff --git a/usr.sbin/npppd/common/ipsec_util.c b/usr.sbin/npppd/common/ipsec_util.c new file mode 100644 index 00000000000..10c634f0250 --- /dev/null +++ b/usr.sbin/npppd/common/ipsec_util.c @@ -0,0 +1,360 @@ +/*- + * Copyright 2007, 2009 + * Internet Initiative Japan Inc. All rights reserved. + */ +/* $Id: ipsec_util.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/*@file IPsec 関連ユーティリティ */ +/* + * RFC 2367 PF_KEY Key Management API, Version 2 + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <net/pfkeyv2.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ipsec_util.h" +#include "ipsec_util_local.h" + +/** + * Delete the IPsec-SA for transport-mode ESP that matches specified sock and + * peer. + * <p> + * For deleting IPsec-SA for NAT-T, port numbers and protocol must + * be specified.</p> + * + * @param sock localy bounded address of the IPsec-SA. + * @param peer remote address of the IPsec-SA. + * @param proto protocol of IPsec-SA. Specify this only if IPsec-SA is for + * NAT-T peer. + * @param dir IPsec-SA's direction by choosing + * {@link ::IPSEC_UTIL_DIRECTION_IN}, {@link ::IPSEC_UTIL_DIRECTION_OUT} + * or {@link ::IPSEC_UTIL_DIRECTION_BOTH} + * @return 0 if the function success, otherwise return non-zero value; + */ +int +ipsec_util_purge_transport_sa(struct sockaddr *sock, struct sockaddr *peer, + int proto, int dir) +{ + int key_sock; + struct timeval tv; + struct sadb_del_args del_in, del_out; + + /* + * Assumes address family is (AF_INET|AF_INET6) and has valid length + */ + if (sock == NULL || peer == NULL || + !sockaddr_is_valid(peer) || !sockaddr_is_valid(peer)) + return -1; + + if ((key_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) + return -1; + + tv = KEYSOCK_RCVTIMEO; + if (setsockopt(key_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) + goto reigai; + + del_in.is_valid = del_out.is_valid = 0; + if (delete_prepare(key_sock, sock, peer, proto, &del_in, &del_out) != 0) + goto reigai; + + if (del_in.is_valid && (dir & IPSEC_UTIL_DIRECTION_IN) != 0) { + if (send_sadb_delete(key_sock, &del_in)) + goto reigai; + } + if (del_out.is_valid && (dir & IPSEC_UTIL_DIRECTION_OUT) != 0) { + if (send_sadb_delete(key_sock, &del_out)) + goto reigai; + } + close(key_sock); + + return 0; + +reigai: + close(key_sock); + + return -1; +} + +/*********************************************************************** + * private functions + ***********************************************************************/ +static void +ipsec_util_prepare(void) +{ + + /* + * for sadb_msg_seq. As RFC 2367, it must be used to uniquely + * identify request to a proccess. + */ + while (++ipsec_util_seq == 0) + /* empty */; + + if (ipsec_util_pid == -1) + ipsec_util_pid = getpid(); +} + +/* + * Find IPsec-SA to delete using SADB_DUMP + */ +static int +delete_prepare(int key_sock, struct sockaddr *sock, struct sockaddr *peer, + int proto, struct sadb_del_args *in, struct sadb_del_args *out) +{ + int sz, dump_end, res_count; + struct sadb_msg req_msg = { + .sadb_msg_version = PF_KEY_V2, + .sadb_msg_type = SADB_DUMP, + .sadb_msg_satype = SADB_SATYPE_ESP, + .sadb_msg_len = PFKEY_UNIT64(sizeof(struct sadb_msg)) + }, *res_msg; + u_char buffer[2048]; + + /* Dump the SADB to search the SA that matches sock/peer. */ + ipsec_util_prepare(); + req_msg.sadb_msg_seq = ipsec_util_seq; + req_msg.sadb_msg_pid = ipsec_util_pid; + sz = send(key_sock, &req_msg, sizeof(req_msg), 0); + if (sz <= 0) + return -1; + + for (res_count = 0, dump_end = 0; !dump_end;) { + int off = 0; + uint32_t spi; + struct sadb_ext *res_ext; + struct sadb_address *res_src, *res_dst; + + sz = recv(key_sock, buffer, sizeof(buffer), 0); + if (sz == 0 && res_count == 0) + return 0; /* empty */ + if (sz <= 0) + return -1; + if (sz < sizeof(struct sadb_msg)) + return -1; + res_msg = (struct sadb_msg *)buffer; + if (res_msg->sadb_msg_errno != 0) { + if (res_msg->sadb_msg_errno == ENOENT) + return 0; + return -1; + } + + dump_end = (res_msg->sadb_msg_seq == 0)? 1 : 0; + if (res_msg->sadb_msg_version != req_msg.sadb_msg_version || + res_msg->sadb_msg_type != req_msg.sadb_msg_type || + res_msg->sadb_msg_pid != req_msg.sadb_msg_pid) + continue; + res_count++; + + spi = 0; res_src = res_dst = NULL; + for (off = sizeof(struct sadb_msg); off < sz;) { + res_ext = (struct sadb_ext *)(buffer + off); + off += PFKEY_UNUNIT64(res_ext->sadb_ext_len); + + switch (res_ext->sadb_ext_type) { + case SADB_EXT_SA: + if (((struct sadb_sa *)res_ext)->sadb_sa_state + != SADB_SASTATE_MATURE) + break; + spi = ((struct sadb_sa *)res_ext)->sadb_sa_spi; + break; + + case SADB_EXT_ADDRESS_SRC: + res_src = (struct sadb_address *)res_ext; + break; + + case SADB_EXT_ADDRESS_DST: + res_dst = (struct sadb_address *)res_ext; + break; + } + } + if (res_src == NULL || res_dst == NULL || spi == 0) + continue; + + if (address_compar(res_src, sock, proto) == 0 && + address_compar(res_dst, peer, proto) == 0) { + (void)sadb_del_args_init(out, spi, res_src, res_dst, + proto); + /* continue anyway */ + } else + if (address_compar(res_src, peer, proto) == 0 && + address_compar(res_dst, sock, proto) == 0) { + (void)sadb_del_args_init(in, spi, res_src, res_dst, + proto); + /* continue anyway */ + } + } + + return 0; +} + +static int +send_sadb_delete(int key_sock, struct sadb_del_args *args) +{ + int i; + + for (i = 0; i < args->spiidx; i++) { + int iovidx, sz; + struct iovec iov[10]; + struct msghdr msg; + struct sadb_msg req_msg = { + .sadb_msg_version = PF_KEY_V2, + .sadb_msg_type = SADB_DELETE, + .sadb_msg_satype = SADB_SATYPE_ESP + }, *res_msg; + struct sadb_sa sa; + u_char buffer[1024]; + + ipsec_util_prepare(); + iovidx = 0; + req_msg.sadb_msg_seq = ipsec_util_seq; + req_msg.sadb_msg_pid = ipsec_util_pid; + req_msg.sadb_msg_len = PFKEY_UNIT64(sizeof(req_msg) + + sizeof(struct sadb_sa) + + PFKEY_UNUNIT64(args->src.sadb_address_len) + + PFKEY_UNUNIT64(args->dst.sadb_address_len)); + iov[iovidx].iov_base = &req_msg; + iov[iovidx].iov_len = sizeof(req_msg); + iovidx++; + + sa.sadb_sa_exttype = SADB_EXT_SA; + sa.sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa)); + sa.sadb_sa_spi = args->spi[i]; + iov[iovidx].iov_base = &sa; + iov[iovidx].iov_len = sizeof(sa); + iovidx++; + + iov[iovidx].iov_base = &args->src; + iov[iovidx].iov_len = sizeof(args->src); + iovidx++; + iov[iovidx].iov_base = &args->src_sa; + iov[iovidx].iov_len = + PFKEY_ALIGN8(((struct sockaddr *)&args->src_sa)->sa_len); + iovidx++; + + iov[iovidx].iov_base = &args->dst; + iov[iovidx].iov_len = sizeof(args->dst); + iovidx++; + iov[iovidx].iov_base = &args->dst_sa; + iov[iovidx].iov_len = + PFKEY_ALIGN8(((struct sockaddr *)&args->dst_sa)->sa_len); + iovidx++; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = iovidx; + + if ((sz = sendmsg(key_sock, &msg, 0)) <= 0) + return 1; + + if ((sz = recv(key_sock, buffer, sizeof(buffer), 0)) < + sizeof(struct sadb_msg)) + return 1; + + res_msg = (struct sadb_msg *)buffer; + if (res_msg->sadb_msg_pid != req_msg.sadb_msg_pid || + res_msg->sadb_msg_version != req_msg.sadb_msg_version || + res_msg->sadb_msg_type != req_msg.sadb_msg_type || + res_msg->sadb_msg_errno != 0) + return 1; + } + + return 0; +} + +/*********************************************************************** + * Utility functions + ***********************************************************************/ +static inline int +address_compar(struct sadb_address *sadb, struct sockaddr *sa, int proto) +{ + u_short porta, portb; + int cmp; + struct sockaddr *sb = (struct sockaddr *)(sadb + 1); + + if ((cmp = sa->sa_family - sb->sa_family) != 0) return cmp; + if ((cmp = sa->sa_len - sb->sa_len) != 0) return cmp; + if (proto != 0 && + (cmp = proto - sadb->sadb_address_proto) != 0) return cmp; + + switch (sa->sa_family) { + case AF_INET: + if (sadb->sadb_address_prefixlen != sizeof(struct in_addr) << 3) + return -1; + if ((cmp = memcmp(&((struct sockaddr_in *)sa)->sin_addr, + &((struct sockaddr_in *)sb)->sin_addr, + sizeof(struct in_addr))) != 0) + return cmp; + porta = ((struct sockaddr_in *)sa)->sin_port; + portb = ((struct sockaddr_in *)sb)->sin_port; + break; + + case AF_INET6: + if (sadb->sadb_address_prefixlen != sizeof(struct in6_addr) << 3) + return -1; + if ((cmp = memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr, + &((struct sockaddr_in6 *)sb)->sin6_addr, + sizeof(struct in6_addr))) != 0) + return cmp; + porta = ((struct sockaddr_in6 *)sa)->sin6_port; + portb = ((struct sockaddr_in6 *)sb)->sin6_port; + break; + + default: + return -1; + } + if (porta == 0) { + if (ntohs(portb) != 500 && portb != 0) + return porta - portb; + } else { + if ((cmp = porta - portb) != 0) return cmp; + } + + return 0; +} + + +static int +sadb_del_args_init(struct sadb_del_args *args, uint32_t spi, + struct sadb_address *src, struct sadb_address *dst, int proto) +{ + if (!args->is_valid) { + memset(args, 0, sizeof(struct sadb_del_args)); + + args->src = *src; + args->dst = *dst; + args->src.sadb_address_prefixlen = + args->dst.sadb_address_prefixlen = 0; +#define SADB2SA(_base) ((struct sockaddr *)((_base) + 1)) + memcpy(&args->src_sa, SADB2SA(src), SADB2SA(src)->sa_len); + memcpy(&args->dst_sa, SADB2SA(dst), SADB2SA(dst)->sa_len); +#undef SADB2SA + if (proto != 0) { + args->src.sadb_address_proto = proto; + args->dst.sadb_address_proto = proto; + } + args->is_valid = 1; + } + if (args->spiidx < countof(args->spi)) { + args->spi[args->spiidx++] = spi; + return 0; + } + + return 1; +} + +static int +sockaddr_is_valid(struct sockaddr *sa) +{ + return + ((sa->sa_family == AF_INET && + sa->sa_len == sizeof(struct sockaddr_in)) || + (sa->sa_family == AF_INET6 && + sa->sa_len == sizeof(struct sockaddr_in6)))? 1 : 0; +} diff --git a/usr.sbin/npppd/common/ipsec_util.h b/usr.sbin/npppd/common/ipsec_util.h new file mode 100644 index 00000000000..77d8b08dff6 --- /dev/null +++ b/usr.sbin/npppd/common/ipsec_util.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef IPSEC_UTIL_H +#define IPSEC_UTIL_H + +#define IPSEC_UTIL_DIRECTION_IN 1 +#define IPSEC_UTIL_DIRECTION_OUT 2 +#define IPSEC_UTIL_DIRECTION_BOTH 3 + +#ifdef __cplusplus +extern "C" { +#endif + +int ipsec_util_purge_transport_sa (struct sockaddr *, struct sockaddr *, int, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/common/ipsec_util_local.h b/usr.sbin/npppd/common/ipsec_util_local.h new file mode 100644 index 00000000000..0379092d4d3 --- /dev/null +++ b/usr.sbin/npppd/common/ipsec_util_local.h @@ -0,0 +1,33 @@ +static uint32_t ipsec_util_seq = 0; +static int ipsec_util_pid = -1; + +struct sadb_del_args { + int is_valid; + uint32_t spi[128]; + int spiidx; + struct sadb_address src; + union { + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } src_sa; + u_char src_pad[8]; /* for PFKEY_ALIGN8 */ + struct sadb_address dst; + union { + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } dst_sa; + u_char dst_pad[8]; /* for PFKEY_ALIGN8 */ +}; + +static void ipsec_util_prepare (void); +static int delete_prepare (int, struct sockaddr *, struct sockaddr *, int, struct sadb_del_args *, struct sadb_del_args *); +static int send_sadb_delete (int, struct sadb_del_args *); +static inline int address_compar (struct sadb_address *, struct sockaddr *, int); +static int sadb_del_args_init (struct sadb_del_args *, uint32_t, struct sadb_address *, struct sadb_address *, int); +static int sockaddr_is_valid (struct sockaddr *); + +#ifndef countof +#define countof(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +struct timeval const KEYSOCK_RCVTIMEO = { .tv_sec = 0, .tv_usec = 500000L }; diff --git a/usr.sbin/npppd/common/net_utils.c b/usr.sbin/npppd/common/net_utils.c new file mode 100644 index 00000000000..14e4cd22fee --- /dev/null +++ b/usr.sbin/npppd/common/net_utils.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: net_utils.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> + +#include "net_utils.h" + +#ifdef NPPPD_USE_RTEV +#include "rtev.h" +#endif + +/** struct sockaddr から、インタフェース名を取得します */ +const char * +get_ifname_by_sockaddr(struct sockaddr *sa, char *ifname) +{ + struct ifaddrs *addr, *addr0; + struct in_addr *in4a, *in4b; + const char *ifname0 = NULL; +#ifdef INET6 + struct in6_addr *in6a, *in6b; +#endif + + ifname0 = NULL; + /* リニアサーチしかないなんて... */ + getifaddrs(&addr0); + for (addr = addr0; ifname0 == NULL&& addr != NULL; + addr = addr->ifa_next) { + if (addr->ifa_addr->sa_family != sa->sa_family || + addr->ifa_addr->sa_len != sa->sa_len) + continue; + switch (addr->ifa_addr->sa_family) { + default: + continue; + case AF_INET: + in4a = &((struct sockaddr_in *)addr->ifa_addr) + ->sin_addr; + in4b = &((struct sockaddr_in *)sa)->sin_addr; + if (in4a->s_addr == in4b->s_addr) { + strlcpy(ifname, addr->ifa_name, IF_NAMESIZE); + ifname0 = ifname; + } + break; +#ifdef INET6 + case AF_INET6: + in6a = &((struct sockaddr_in6 *)addr->ifa_addr) + ->sin6_addr; + in6b = &((struct sockaddr_in6 *)sa)->sin6_addr; + if (IN6_ARE_ADDR_EQUAL(in6a, in6b)) { + strlcpy(ifname, addr->ifa_name, IF_NAMESIZE); + ifname0 = ifname; + } + break; +#endif + } + } + freeifaddrs(addr0); + + return ifname0; +} +/** + * "192.168.160.1:1723/tcp" や "[::1]:1723/tcp" という文字列を、getaddrinfo(3) + * の引数の仕様に併せて実行する。現在は、"/tcp" の部分は無視します。 + */ +int +addrport_parse(const char *addrport, int proto, struct addrinfo **p_ai) +{ + char buf[256]; + char *servp, *nodep, *slash; + struct addrinfo hints; + + strlcpy(buf, addrport, sizeof(buf)); + if (buf[0] == '[' && (servp = strchr(buf, ']')) != NULL) { + nodep = buf + 1; + *servp++ = '\0'; + if (*servp != ':') + servp = NULL; + } else { + nodep = buf; + servp = strrchr(nodep, ':'); + } + if (servp != NULL) { + *servp = '\0'; + servp++; + slash = strrchr(servp, '/'); + if (slash != NULL) { + /* + * "/tcp" などは無視する。 + */ + *slash = '\0'; + slash++; + } + } else + servp = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_UNSPEC; + switch (proto) { + case IPPROTO_TCP: + hints.ai_socktype = SOCK_STREAM; + break; + case IPPROTO_UDP: + hints.ai_socktype = SOCK_DGRAM; + break; + } + hints.ai_protocol = proto; + + return getaddrinfo(nodep, servp, &hints, p_ai); +} + +/** + * struct sockaddr から、 "192.168.160.1:1723" や "[::1]:1723" という文字列 + * を作成します。 + * @param buf 文字列を格納するバッファ + * @param lbuf 文字列を格納するバッファの長さ + */ +const char * +addrport_tostring(struct sockaddr *sa, socklen_t salen, char *buf, int lbuf) +{ + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + + if (getnameinfo(sa, salen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + return NULL; + + switch (sa->sa_family) { + case AF_INET6: + strlcpy(buf, "[", lbuf); + strlcat(buf, hbuf, lbuf); + strlcat(buf, "]:", lbuf); + strlcat(buf, sbuf, lbuf); + break; + case AF_INET: + strlcpy(buf, hbuf, lbuf); + strlcat(buf, ":", lbuf); + strlcat(buf, sbuf, lbuf); + break; + default: + return NULL; + } + + return buf; +} + +/** IPv4 ネットマスクをプレフィックス長に変換します。ホストバイトオーダーで */ +int +netmask2prefixlen(uint32_t mask) +{ + switch(mask) { + case 0x00000000: return 0; + case 0x80000000: return 1; + case 0xC0000000: return 2; + case 0xE0000000: return 3; + case 0xF0000000: return 4; + case 0xF8000000: return 5; + case 0xFC000000: return 6; + case 0xFE000000: return 7; + case 0xFF000000: return 8; + case 0xFF800000: return 9; + case 0xFFC00000: return 10; + case 0xFFE00000: return 11; + case 0xFFF00000: return 12; + case 0xFFF80000: return 13; + case 0xFFFC0000: return 14; + case 0xFFFE0000: return 15; + case 0xFFFF0000: return 16; + case 0xFFFF8000: return 17; + case 0xFFFFC000: return 18; + case 0xFFFFE000: return 19; + case 0xFFFFF000: return 20; + case 0xFFFFF800: return 21; + case 0xFFFFFC00: return 22; + case 0xFFFFFE00: return 23; + case 0xFFFFFF00: return 24; + case 0xFFFFFF80: return 25; + case 0xFFFFFFC0: return 26; + case 0xFFFFFFE0: return 27; + case 0xFFFFFFF0: return 28; + case 0xFFFFFFF8: return 29; + case 0xFFFFFFFC: return 30; + case 0xFFFFFFFE: return 31; + case 0xFFFFFFFF: return 32; + } + return -1; +} diff --git a/usr.sbin/npppd/common/net_utils.h b/usr.sbin/npppd/common/net_utils.h new file mode 100644 index 00000000000..72f67ce293e --- /dev/null +++ b/usr.sbin/npppd/common/net_utils.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NET_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +const char *get_ifname_by_sockaddr (struct sockaddr *, char *); +int addrport_parse(const char *, int, struct addrinfo **); +const char *addrport_tostring(struct sockaddr *, socklen_t, char *, int); +int netmask2prefixlen(uint32_t); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/common/properties.c b/usr.sbin/npppd/common/properties.c new file mode 100644 index 00000000000..56cdcd474f9 --- /dev/null +++ b/usr.sbin/npppd/common/properties.c @@ -0,0 +1,566 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * $Id: properties.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +/* LINTLIBRARY */ +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "hash.h" +#include "properties.h" +#include "debugmacro.h" + +struct properties { + hash_table *hash_tbl; +}; + +#ifdef _WIN32 +#define snprintf _snprintf +#define u_int32_t unsigned long +#endif + +#ifndef MAX +#define MAX(m,n) (((m) > (n))? (m) : (n)) +#endif +#ifndef MIN +#define MIN(m,n) (((m) < (n))? (m) : (n)) +#endif + +static int continue_line __P((const char *)); +static char *convert_in_save __P((const char *)); +static void convert_in_load __P((char *)); +static uint32_t str_hash __P((const void *, int)); +static const char * properties_put0(struct properties *, const char *, const char *, int); + +static void chomp __P((char *)); +static char * trim __P((char *)); +static const char * skip_space __P((const char *)); + +/** + * Create properties object + * + * @param sz size of hash (sould be prime number) + * @return pointer to created object + */ +struct properties * +properties_create(int sz) +{ + struct properties *_this; + + if ((_this = (struct properties *)malloc(sizeof(struct properties))) + == NULL) + return NULL; + memset(_this, 0, sizeof(struct properties)); + + if ((_this->hash_tbl = hash_create( + (int (*)(const void *, const void *))strcmp, + str_hash, sz)) == NULL) { + free(_this); + return NULL; + } + + return _this; +} + +/** + * get property value by key + * + * @param _this pointer to properties object + * @param key property key + * @returns property value + */ +const char * +properties_get(struct properties *_this, const char *key) +{ + hash_link *hl; + + if ((hl = hash_lookup(_this->hash_tbl, key)) != NULL) + return hl->item; + return NULL; +} + +/** + * remove property entry. + * + * @param _this pointer to properties object + * @param key property key + */ +void +properties_remove(struct properties *_this, const char *key) +{ + char *key0; + hash_link *hl; + + if ((hl = hash_lookup(_this->hash_tbl, key)) == NULL) + return; + + key0 = /* NOSTRICT */(char *)hl->key; + + hash_delete(_this->hash_tbl, key, 1); + free(key0); +} + +/** + * remove all items from the properties. + * + * @param _this pointer to properties object + */ +void +properties_remove_all(struct properties *_this) +{ + char *key0; + hash_link *hl; + + for (hl = hash_first(_this->hash_tbl); hl != NULL; + hl = hash_next(_this->hash_tbl)) { + key0 = /* NOSTRICT */(char *)hl->key; + hash_delete(_this->hash_tbl, hl->key, 1); + free(key0); + } +} + +/** + * put all items of 'props' properties to the properties. + * + * @param _this pointer to properties object + */ +void +properties_put_all(struct properties *_this, struct properties *props) +{ + hash_link *hl; + + for (hl = hash_first(props->hash_tbl); hl != NULL; + hl = hash_next(props->hash_tbl)) { + properties_put(_this, hl->key, hl->item); + } +} + +/** + * put(add) property entry. + * + * @param _this pointer to properties object + * @param key property key + * @param value property value + * @return pointer to property value that is stored in hash table. + */ +const char * +properties_put(struct properties *_this, const char *key, const char *value) +{ + return properties_put0(_this, key, value, 1); +} + +static const char * +properties_put0(struct properties *_this, const char *key, const char *value, + int do_trim) +{ + char *key0 = NULL, *value0 = NULL; + char *key1 = NULL, *value1 = NULL; + + if (key[0] == '\0') + return NULL; + + if ((key0 = strdup(key)) == NULL) + goto exception; + + key1 = key0; + if (do_trim) + key1 = trim(key1); + if (key1[0] == '\0') + goto exception; + + if ((value0 = strdup(value)) == NULL) + goto exception; + value1 = value0; + if (do_trim) { + value1 = trim(value1); + if (value1 != value0) { + /* value1 must point the beginning of the buffer */ + if ((value1 = strdup(value1)) == NULL) + goto exception; + free(value0); + value0 = value1; + } + } + properties_remove(_this, key1); + if (hash_insert(_this->hash_tbl, key1, value1) != 0) + goto exception; + + return value; +exception: + if (key0 != NULL) + free(key0); + if (value0 != NULL) + free(value0); + return NULL; +} + +/** + * Destroy properties object. + * @param _this pointer to properties object + */ +void +properties_destroy(struct properties *_this) +{ + if (_this->hash_tbl != NULL) { + properties_remove_all(_this); + hash_free(_this->hash_tbl); + _this->hash_tbl = NULL; + } + free(_this); +} + +/** + * get first key value of properties. + * + * @param _this pointer to properties object + * @return the first key value of properties. + * @see #properties_next_key + */ +const char * +properties_first_key(struct properties *_this) +{ + hash_link *hl; + + hl = hash_first(_this->hash_tbl); + if (hl != NULL) + return hl->key; + return NULL; +} + +/** + * get next key value of properties. + * + * @param _this pointer to properties object + * @return the next key value of properties. + * @see properties_first_key + */ +const char * +properties_next_key(struct properties *_this) +{ + hash_link *hl; + + hl = hash_next(_this->hash_tbl); + if (hl != NULL) + return hl->key; + return NULL; +} + +/** + * Store this object to a file + * + * @param _this pointer to properties object + * @param fp FILE stream to store. + * @return returns 0 in succeed. + */ +int +properties_save(struct properties *_this, FILE *fp) +{ + char *value; + const char *key; + + for (key = properties_first_key(_this); key != NULL; + key = properties_next_key(_this)) { + if ((value = convert_in_save(properties_get(_this, key))) + == NULL) + return -1; + + fprintf(fp, "%s: %s\n", key, value); + free(value); + } + return 0; +} + +static int +continue_line(const char *line) +{ + int eol; + int slash_cnt = 0; + + eol = strlen(line); + + while (--eol > 0) { + if (*(line + eol) == '\\') + slash_cnt++; + else + break; + } + if (slash_cnt % 2 == 1) + return 1; + + return 0; +} + +static char * +convert_in_save(const char *value) +{ + size_t outsz = 128; + int i, j; + char *out, *out0; + + if ((out = (char *)malloc(outsz)) == NULL) + return NULL; + + for (i = 0, j = 0; value[i] != '\0'; i++) { + if (j + 2 > outsz) { + if ((out0 = (char *)realloc(out, outsz * 2)) == NULL) { + free(out); + return NULL; + } + out = out0; + outsz *= 2; + } + switch (value[i]) { + case '\n': out[j++] = '\\'; out[j++] = 'n'; break; + case '\r': out[j++] = '\\'; out[j++] = 'r'; break; + case '\t': out[j++] = '\\'; out[j++] = 't'; break; + case '\\': out[j++] = '\\'; out[j++] = '\\'; break; + default: out[j++] = value[i]; break; + } + } + out[j] = '\0'; + + return out; +} + +static void +convert_in_load(char *value) +{ + int i, j; + + for (i = 0, j = 0; value[i] != '\0'; i++, j++) { + if (value[i] == '\\') { + switch (value[++i]) { + case '\\': value[j] = '\\'; continue; + case 'r': value[j] = '\r'; continue; + case 'n': value[j] = '\n'; continue; + case 't': value[j] = '\t'; continue; + default: break; + } + } + value[j] = value[i]; + } + value[j] = '\0'; +} + +/* + * Load properties from the given file pointer(FILE) using the given charset + * decoder. We use EUC-JP encoding internally. + * + * @param _this pointer to the properties object. + * @param fp FILE stream to load. + * @return return 0 in succeed. + */ +int +properties_load(struct properties *_this, FILE *fp) +{ + char *key, *value, *delim; + char buf0[BUFSIZ]; + size_t linesz = 128, linelen; + + int lineoff = 0, hasnl, linecont; + char *line = NULL, *line0, *line1; + + if ((line = (char *)malloc(linesz)) == NULL) + goto reigai; + + linecont = 0; + while (fgets(buf0, sizeof(buf0), fp) != NULL) { + hasnl = 0; + linelen = strlen(buf0); + if (linelen > 0 && buf0[linelen - 1] == '\n') { + hasnl = 1; + chomp(buf0); + } + if (linecont || (lineoff == 0)) + line0 = (char *)skip_space(buf0); + else + line0 = buf0; + linelen = strlen(line0); + while (lineoff + linelen + 128 > linesz) { + if ((line1 = realloc(line, linesz * 2)) + == NULL) + goto reigai; + line = line1; + linesz *= 2; + } + memcpy(line + lineoff, line0, linelen); + line[lineoff + linelen] = '\0'; + lineoff += linelen; + + linecont = 0; + if (!hasnl) + continue; + + if (continue_line(line0)) { + lineoff--; /* delete \(backslash) */ + linecont = 1; + continue; + } + lineoff = 0; + if (*line == '#') + continue; + key = line; + + for (delim = key; *delim != '\0'; delim++) { + if (*delim == '=' || *delim == ':') + break; + } + if (*delim == '\0') + continue; + + *delim = '\0'; + value = trim(delim + 1); + key = trim(key); + + convert_in_load(value); + + properties_put0(_this, key, value, 0); + + lineoff = 0; + } + if (line != NULL) + free(line); + return 0; +reigai: + if (line != NULL) + free(line); + + return -1; +} + +static uint32_t +str_hash(const void *ptr, int sz) +{ + u_int32_t hash = 0; + int i, len; + const char *str; + + str = ptr; + len = strlen(str); + for (i = 0; i < len; i++) + hash = hash*0x1F + str[i]; + hash = (hash << 16) ^ (hash & 0xffff); + + return hash % sz; +} + +#define ISCRLF(x) ((x) == '\r' || (x) == '\n') +#define ISSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || \ + (x) == '\n') +static const char * +skip_space(s) + const char *s; +{ + const char *r; + + for (r = s; *r != '\0' && ISSPACE(*r); r++) + ;; /* skip */ + return r; +} + +static char * +trim(s) + char *s; +{ + char *r; + char *t; + + r = /* NOSTRICT */(char *)skip_space(s); + for (t = r + strlen(r) - 1; r <= t; t--) { + if (ISSPACE(*t)) + *t = '\0'; + else + break; + } + return r; +} + +static void +chomp(s) + char *s; +{ + char *t; + + for (t = s + strlen(s) - 1; s <= t; t--) { + if (ISCRLF(*t)) + *t = '\0'; + else + break; + } +} + +#if PROPGET_CMD +#include <stdio.h> + +static void usage __P((void)); + +static void usage(void) +{ + fprintf(stderr, "usage: propgetcmd prop_file prop_key [prop_key ..]\n"); +} + +int +main(int argc, char *argv[]) +{ + const char *k; + struct properties *prop; + FILE *fp; + + argc--; + argv++; + + if (argc < 2) { + usage(); + return 1; + } + + if ((fp = fopen(*argv, "r")) == NULL) { + perror(*argv); + return 1; + } + argc--; + argv++; + + if ((prop = properties_create(127)) == NULL) { + perror("properties_create() failed"); + return 1; + } + properties_load(prop, fp); + + while (argc--) { + if (properties_get(prop, *argv) != NULL) + printf("%s\n", properties_get(prop, *argv)); + + } + fclose(fp); + properties_destroy(prop); +} +#endif diff --git a/usr.sbin/npppd/common/properties.h b/usr.sbin/npppd/common/properties.h new file mode 100644 index 00000000000..05b798a5724 --- /dev/null +++ b/usr.sbin/npppd/common/properties.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PROPERTIES_H +#define PROPERTIES_H 1 + +struct properties; + +#ifndef __P +#if defined(__STDC__) || defined(_MSC_VER) +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct properties *properties_create __P((int)); +const char *properties_get __P((struct properties *, const char *)); +void properties_remove __P((struct properties *, const char *)); +void properties_remove_all __P((struct properties *)); +const char *properties_put __P((struct properties *, const char *, const char *)); +void properties_put_all __P((struct properties *, struct properties *)); +void properties_destroy __P((struct properties *)); +const char *properties_first_key __P((struct properties *)); +const char *properties_next_key __P((struct properties *)); +int properties_save __P((struct properties *, FILE *)); +int properties_load __P((struct properties *, FILE *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/common/properties_test.c b/usr.sbin/npppd/common/properties_test.c new file mode 100644 index 00000000000..4c31df6f91b --- /dev/null +++ b/usr.sbin/npppd/common/properties_test.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * cc -o properties_test -DNO_KANJI=1 properties_test.c properties.c hash.c + * + * ./properties_test + */ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#include "properties.h" + +#define MIN(m,n) ((m) < (n))? (m) : (n) +#define TEST(f) \ + { \ + printf("%-10s .. ", #f); \ + f(); \ + printf("ok\n"); \ + } + +#define ASSERT(x) \ + if (!(x)) { \ + fprintf(stderr, \ + "\nASSERT(%s) failed on %s() at %s:%d.\n" \ + , #x, __func__, __FILE__, __LINE__); \ + abort(); \ + } + +static void +set_and_get(const char *key, const char *value) +{ + FILE *f; + struct properties *props; + + ASSERT((f = fopen("test.properties", "w")) != NULL); + ASSERT((props = properties_create(512)) != NULL); + ASSERT(properties_put(props, key, value) != NULL); + ASSERT(properties_save(props, f) == 0); + ASSERT(fclose(f) == 0); + properties_destroy(props); + + ASSERT((props = properties_create(512)) != NULL); + ASSERT((f = fopen("test.properties", "r")) != NULL); + ASSERT(properties_load(props, f) == 0); + ASSERT(strcmp(properties_get(props, key), value) == 0); + properties_destroy(props); + + ASSERT(fclose(f) == 0); +} + +void +test0(void) +{ + set_and_get("hoge", "hogehoge"); + set_and_get("hoge", "hogehogehogehoge"); + set_and_get("hoge", "hogehogehogehogehogehoge"); + set_and_get("hoge", "hogehogehogehogehogehogehogehoge"); + set_and_get("hoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); + set_and_get("hoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); + set_and_get("hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge", "hogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoehogehogehogehgoe"); +} +static void test1() +{ + // [IDGW-DEV 5246] 1024 bytes + set_and_get("pptpd.listener_in", "PPTP 10.10.10.50 PPTP 100.100.100.100 PPTP 100.100.100.101 PPTP 100.100.100.102 PPTP 100.100.100.103 PPTP 100.100.100.104 PPTP 100.100.100.105 PPTP 100.100.100.106 PPTP 100.100.100.107 PPTP 100.100.100.108 PPTP 100.100.100.109 PPTP 100.100.100.110 PPTP 100.100.100.111 PPTP 100.100.100.112 PPTP 100.100.100.113 PPTP 100.100.100.114 PPTP 100.100.100.115 PPTP 100.100.100.116 PPTP 100.100.100.117 PPTP 100.100.100.118 PPTP 100.100.100.119 PPTP 100.100.100.120 PPTP 100.100.100.121 PPTP 100.100.100.122 PPTP 100.100.100.123 PPTP 100.100.100.124 PPTP 100.100.100.125 PPTP 100.100.100.126 PPTP 100.100.100.127 PPTP 100.100.100.128 PPTP 100.100.100.129 PPTP 100.100.100.130 PPTP 100.100.100.131 PPTP 100.100.100.132 PPTP 100.100.100.133 PPTP 100.100.100.134 PPTP 100.100.100.135 PPTP 100.100.100.136 PPTP 100.100.100.137 PPTP 100.100.100.138 PPTP 100.100.100.139 PPTP 100.100.100.140 PPTP 100.100.100.141 PPTP 100.100.100.142 PPTP 100.100.100.143 PPTP 100.100.100.144 PPTP 100.100.100.145 PPTP 100.100.100.146 PPTP 100.100.100.147 PPTP 100.100.100.148 PPTP 100.100.100.149 PPTP 100.100.100.150 PPTP 100.100.100.151 PPTP 100.100.100.152 PPTP 100.100.100.153 PPTP 100.100.100.154 PPTP 100.100.100.155 PPTP 100.100.100.156 PPTP 100.100.100.157 PPTP 100.100.100.158 PPTP 100.100.100.159 PPTP 100.100.100.160 PPTP 100.100.100.161 PPTP 100.100.100.162 PPTP 100.100.100.163 PPTP 100.100.100.164 PPTP 100.100.100.165 PPTP 100.100.100.166 PPTP 100.100.100.167 PPTP 100.100.100.168 PPTP 100.100.100.169 PPTP 100.100.100.170 PPTP 100.100.100.171 PPTP 100.100.100.172 PPTP 100.100.100.173 PPTP 100.100.100.174 PPTP 100.100.100.175 PPTP 100.100.100.176 PPTP 100.100.100.177 PPTP 100.100.100.178 PPTP 100.100.100.179 PPTP 100.100.100.180 PPTP 100.100.100.181 PPTP 100.100.100.182 PPTP 100.100.100.183 PPTP 100.100.100.184 PPTP 100.100.100.185 PPTP 100.100.100.186 PPTP 100.100.100.187 PPTP 100.100.100.188 PPTP 100.100.100.189 PPTP 100.100.100.190 PPTP 100.100.100.191 PPTP 100.100.100.192 PPTP 100.100.100.193 PPTP 100.100.100.194 PPTP 100.100.100.195 PPTP 100.100.100.196 PPTP 100.100.100.197 PPTP 100.100.100.198 PPTP 100.100.100.200"); + // [IDGW-CVS 9044] + set_and_get("pptpd.ip4_allow", "pptpd.ip4_allow: 192.168.10.1/32 192.168.10.2/32 192.168.10.3/32 192.168.10.4/32 192.168.10.5/32 192.168.10.6/32 192.168.10.7/32 192.168.10.8/32 192.168.10.9/32 192.168.10.10/32 192.168.10.11/32 192.168.10.12/32 192.168.10.13/32 192.168.10.14/32 192.168.10.15/32 192.168.10.16/32 192.168.10.17/32 192.168.10.18/32 192.168.10.19/32 192.168.10.20/32 192.168.10.21/32 192.168.10.22/32 192.168.10.23/32 192.168.10.24/32 192.168.10.25/32 192.168.10.26/32 192.168.10.27/32 192.168.10.28/32 192.168.10.29/32 192.168.10.30/32 192.168.10.31/32 192.168.10.32/32 192.168.10.33/32 192.168.10.34/32 192.168.10.35/32 192.168.10.36/32 192.168.10.37/32 192.168.10.38/32 192.168.10.39/32 192.168.10.40/32 192.168.10.41/32 192.168.10.42/32 192.168.10.43/32 192.168.10.44/32 192.168.10.45/32 192.168.10.46/32 192.168.10.47/32 192.168.10.48/32 192.168.10.49/32 192.168.10.50/32 192.168.10.51/32 192.168.10.52/32 192.168.10.53/32 192.168.10.54/32 192.168.10.55/32 192.168.10.56/32 192.168.1.57/32 192.168.1.58/32 192.168.1.59/32 192.168.10.60/32 192.168.10.61/32 192.168.10.62/32 192.168.10.63/32 192.168.10.64/32 192.168.10.65/32 192.168.10.66/32 192.168.10.67/32 192.168.10.68/32 192.168.10.69/32 192.168.10.70/32 192.168.10.71/32 192.168.10.72/32 192.168.10.73/32 192.168.10.74/32 192.168.10.75/32 192.168.10.76/32 192.168.10.77/32 192.168.10.78/32 192.168.10.79/32 192.168.10.80/32 192.168.10.81/32 192.168.10.82/32 192.168.10.83/32 192.168.10.84/32 192.168.10.85/32 192.168.10.86/32 192.168.10.87/32 192.168.10.88/32 192.168.10.89/32 192.168.10.90/32 192.168.10.91/32 192.168.10.92/32 192.168.10.93/32 192.168.10.94/32 192.168.10.95/32 192.168.10.96/32 192.168.10.97/32 192.168.10.98/32 192.168.10.99/32 192.168.10.100/32 192.168.10.101/32 192.168.10.102/32 192.168.10.103/32 192.168.10.104/32 192.168.10.105/32 192.168.10.106/32 192.168.10.107/32 192.168.10.108/32 192.168.10.109/32 192.168.10.110/32 192.168.10.111/32 192.168.10.112/32 192.168.10.113/32 192.168.10.114/32 192.168.10.115/32 192.168.10.116/32 192.168.10.117/32 192.168.10.118/32 192.168.1.119/32 192.168.1.120/32 192.168.1.121/32"); + +} + + +static void test2() +{ + // [IDGW-DEV 5246] 1024 bytes + set_and_get("hogehoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); + // [IDGW-DEV 5246] 1024 bytes - 1024 bytes + set_and_get("hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); + // [IDGW-DEV 5246] 1024 bytes - 1024 bytes + set_and_get("hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); + // [IDGW-DEV 5246] 2048 bytes + set_and_get("hogehoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); + // [IDGW-DEV 5246] 2048 bytes + set_and_get("hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge", "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"); +} +void +test3(void) +{ + FILE *f; + struct properties *props; + + ASSERT((f = fopen("test.properties", "w")) != NULL); + ASSERT((props = properties_create(512)) != NULL); + ASSERT(properties_put(props, " ", "HOGEHOGE") == NULL); + ASSERT(properties_put(props, "", "HOGEHOGE") == NULL); + ASSERT(properties_put(props, "HOGEHOGE", "") != NULL); + ASSERT(properties_save(props, f) == 0); + ASSERT(fclose(f) == 0); +} + +static int test4_off = 0; +static char test4_buf[] = + "test1: Net\\\n" + " BSD\n" + "test2: Made in\\\n" + " \\ Japan\n" + "test3: bbb\\\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaa \n" + "test4: \\r\n" + ; + +static int +test4_read_fn(void *cookie, char *buf, int len) +{ + int rval; + + rval = MIN(len, sizeof(test4_buf) - test4_off); + if (rval == 0) + return 0; // EOF + memcpy(buf, test4_buf + test4_off, rval); + test4_off += rval; + + return rval; +} + + +static void +test4() +{ + FILE *fp; + struct properties *props; + const char *val; + + + fp = fropen(NULL, test4_read_fn); + ASSERT((props = properties_create(512)) != NULL); + ASSERT((properties_load(props, fp)) == 0); + fclose(fp); + + val = properties_get(props, "test1"); + ASSERT(val != NULL); + ASSERT(strcmp(val, "NetBSD") == 0); + + val = properties_get(props, "test2"); + ASSERT(val != NULL); + ASSERT(strcmp(val, "Made in Japan") == 0); + val = properties_get(props, "test3"); + ASSERT(val != NULL); + ASSERT(strcmp(val, "bbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaa") == 0); + + val = properties_get(props, "test4"); + ASSERT(val != NULL); + ASSERT(strcmp(val, "\r") == 0); +} + +int +main(int argc, char *argv[]) +{ + TEST(test0); + TEST(test1); + TEST(test2); + TEST(test3); + TEST(test4); +} diff --git a/usr.sbin/npppd/common/radish.c b/usr.sbin/npppd/common/radish.c new file mode 100644 index 00000000000..44567a7b51e --- /dev/null +++ b/usr.sbin/npppd/common/radish.c @@ -0,0 +1,822 @@ +#ifndef GENERIC_USE +#define GENERIC_USE +#endif +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include <sys/cdefs.h> +#ifndef LINT +__COPYRIGHT( +"@(#) Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.\n" +"@(#) All rights reserved.\n" +); +#endif + +/* + * radish.c + * + * Version: 0.9 + * Created: May 27, 1995 + * Modified: January 28, 1997 + * Author: Kazu YAMAMOTO + * Email: kazu@is.aist-nara.ac.jp + */ + +#ifdef RADISH +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#if !defined(GENERIC_USE) && !defined(_ROUTE_H_) +#include <net/route.h> +#endif + +#ifdef KERNEL +#include <net/radish.h> +#else /* KERNEL */ +#include "radish.h" +#endif /* KERNEL */ + +#if defined(NO_SA_LEN) && !defined(SA_LEN) +#error requires sockaddr.sa_len or SA_LEN macro +#endif + + +#include <netinet/in.h> +#ifdef GENERIC_USE +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#endif + +#define M_DONTWAIT M_NOWAIT +#ifndef NULL +#define NULL 0 +#endif + +#ifdef KERNEL +#define FATAL(x) panic(x) +#elif defined(GENERIC_USE) +#define FATAL(x) return(-1); +#else +#define FATAL(x) exit(1) +#endif + +static u_char rd_bmask [] = { + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, +}; + +static u_char rd_btest [] = { + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, +}; + +u_char rd_deleted_km[1024]; + +/* + * return 1 if success + * return 0 if error + */ +int +rd_inithead(headp, family, slen, off, alen, match) + void **headp; + int family, slen, off, alen; + int (*match)(void *, void *); +{ + struct radish_head *head; + struct radish *new; + struct sockaddr *masks; + u_char *m; + int num = alen * 8 + 1, i, j, q, r; + int len = sizeof(*head) + sizeof(*new) + slen * num; + + if (*headp) return (1); + R_Malloc(head, struct radish_head *, len); + if (head == NULL) + return 0; + Bzero(head, len); + new = (struct radish *)(head + 1); + masks = (struct sockaddr *)(new +1); + *headp = head; + + /* + * prepare all continuous masks + */ + m = (u_char *)masks; + for (i = 0; i < num; i++, m += slen) { + *m = slen; + *(m + 1) = family; + q = i >> 3; + r = i & 7; + for(j = 0; j < q; j++) + *(m + off + j) = 0xff; + *(m + off + j) = rd_bmask[r]; + } + + head->rdh_slen = slen; + head->rdh_offset = off; + head->rdh_alen = alen; + head->rdh_masks = masks; + head->rdh_match = match; + head->rdh_top = new; + + new->rd_route = masks; + new->rd_mask = masks; + new->rd_btest = rd_btest[0]; + /* other nembers are 0 */ + + return(1); +} + +struct sockaddr * +rd_mask(m_arg, head, maskp) + struct sockaddr *m_arg; + struct radish_head *head; + int *maskp; /* return value */ +{ + u_char *mp, *masks = (u_char *)head->rdh_masks; + int off = head->rdh_offset; + int slen = head->rdh_slen; + int alen = head->rdh_alen; + int i = 0, masklen = 0; + + if (m_arg == NULL) { + masklen = alen * 8; + *maskp = masklen; + return((struct sockaddr *)(masks + slen * masklen)); + } + mp = (u_char *)m_arg + off; + while ((i < alen) && (mp[i] == 0xff)) { + masklen += 8; + i++; + } + if (i < alen) + switch (mp[i]) { + case 0xfe: masklen += 7; break; + case 0xfc: masklen += 6; break; + case 0xf8: masklen += 5; break; + case 0xf0: masklen += 4; break; + case 0xe0: masklen += 3; break; + case 0xc0: masklen += 2; break; + case 0x80: masklen += 1; break; + case 0x00: break; + } + *maskp = masklen; + return((struct sockaddr *)(masks + slen * masklen)); +} + +int +rd_insert(d_arg, m_arg, head, rt) + struct sockaddr *d_arg; + struct sockaddr *m_arg; + struct radish_head *head; +#ifdef GENERIC_USE + void *rt; +#else /* GENERIC_USE */ + struct rtentry *rt; +#endif /* GENERIC_USE */ +{ + struct radish *cur = head->rdh_top, *parent, *new; + int off = head->rdh_offset; + int slen = head->rdh_slen; + int alen = head->rdh_alen; + int i, lim, q, r, masklen; + u_char *dp, *np, *rp; + struct sockaddr *mask; + + mask = rd_mask(m_arg, head, &masklen); + q = masklen >> 3; + r = masklen & 7; + + /* Allocate a new radish. + * This may be overhead in the case that + * masklen == cur->rd_masklen + * and + * route == dest. + */ + R_Malloc(new, struct radish *, sizeof(*new) + slen); + if (new == NULL) + return ENOBUFS; + Bzero(new, sizeof(*new) + slen); + new->rd_route = (struct sockaddr *)(new + 1); + new->rd_mask = mask; + new->rd_masklen = masklen; + new->rd_masklim = q; + new->rd_bmask = rd_bmask[r]; + new->rd_btest = rd_btest[r]; + new->rd_rtent = rt; + +#ifndef GENERIC_USE + rt->rt_radish = new; +#endif /* GENERIC_USE */ + + /* masked copy from dest to route */ + np = (u_char *)new->rd_route; + dp = (u_char *)d_arg; + *np = *dp; /* sa_len */ + np[1] = dp[1]; /* sa_family */ + dp += off; + np += off; + i = 0; + while (i < q) { + np[i] = dp[i]; + i++; + } + np[i] = dp[i] & rd_bmask[r]; /* just in case */ + + while (cur) { + if (masklen == cur->rd_masklen) { + rp = (u_char *)cur->rd_route + off; + for (i = 0; i < alen; i++) + if (np[i] != rp[i]) { + /* + * masklen == cur->rd_masklen + * dest != route + */ + return rd_glue(cur, new, i, head); + } + /* + * masklen == cur->rd_masklen + * dest == route + */ + Free(new); + if (cur->rd_rtent != NULL) + return EEXIST; + cur->rd_rtent = rt; +#ifndef GENERIC_USE + rt->rt_radish = cur; +#endif /* GENERIC_USE */ + return 0; + } + /* + * masklen != cur->rd_masklen + */ + if (masklen > cur->rd_masklen) { + /* + * See if dest matches with cur node. + * (dest & mask) == route + */ + rp = (u_char *)cur->rd_route + off; + lim = cur->rd_masklim; + + /* mask is continuous, thus mask is 0xff here. */ + for (i = 0; i < lim; i++) + if(np[i] != rp[i]) { + /* + * masklen > cur->rd_masklen + * (dest & mask) != route + */ + return rd_glue(cur, new, i, head); + } + if (cur->rd_bmask) + if ((np[lim] & cur->rd_bmask) != rp[lim]) { + /* + * masklen > cur->rd_masklen + * (dest & mask) != route + */ + return rd_glue(cur, new, lim, head); + } + /* + * masklen > cur->rd_masklen + * (dest & mask) == route + */ + if (cur->rd_btest & np[cur->rd_masklim]) { + if (cur->rd_r != NULL) { + cur = cur->rd_r; + continue; + } + cur->rd_r = new; + new->rd_p = cur; + return 0; + } else { + if (cur->rd_l != NULL) { + cur = cur->rd_l; + continue; + } + cur->rd_l = new; + new->rd_p = cur; + return 0; + } + } + /* + * masklen < cur->rd_masklen + */ + + /* See if route matches with dest, be carefull! + * dest == (route & dest_mask) + */ + rp = (u_char *)cur->rd_route + off; + lim = new->rd_masklim; + + /* mask is continuous, thus mask is 0xff here. */ + for (i = 0; i < lim; i++) + if(np[i] != rp[i]) { + /* + * masklen < cur->rd_masklen + * dest != (route & dest_mask) + */ + return rd_glue(cur, new, i, head); + } + if (new->rd_bmask) + if (np[lim] != (rp[lim] & new->rd_bmask)) { + /* + * masklen < cur->rd_masklen + * dest != (route & dest_mask) + */ + return rd_glue(cur, new, lim, head); + } + /* + * masklen < cur->rd_masklen + * dest == (route & dest_mask) + */ + + /* put the new radish between cur and its parent */ + parent = cur->rd_p; + new->rd_p = parent; + if (parent->rd_l == cur) + parent->rd_l = new; + else if (parent->rd_r == cur) + parent->rd_r = new; + else + FATAL("rd_insert"); + if (new->rd_btest & rp[new->rd_masklim]) + new->rd_r = cur; + else + new->rd_l = cur; + + cur->rd_p = new; + return 0; + } + return 1; +} + +/* + * Insert a glue radish between the current and its parent. + * Let the current radish one child of glue radish. + * Let the new radish the other child of glue radish. + */ +int +rd_glue(cur, new, misbyte, head) + struct radish *cur, *new; + int misbyte; + struct radish_head *head; +{ + struct radish *parent = cur->rd_p, *glue; + u_char *cp = (u_char *)cur->rd_route; + u_char *np = (u_char *)new->rd_route; + u_char *gp; + int off = head->rdh_offset, slen = head->rdh_slen; + int maskb, xor, i; + + /* + * Glue radish + */ + R_Malloc(glue, struct radish *, sizeof(*glue) + slen); + if (glue == NULL) { + Free (new); + return ENOBUFS; + } + Bzero(glue, sizeof(*glue) + slen); + + /* calculate a bit to test */ + xor = (*(cp + off + misbyte) ^ *(np + off + misbyte)) & 0xff; + maskb = 8; + while(xor) { + xor >>= 1; + maskb--; + } + + glue->rd_route = (struct sockaddr *)(glue + 1); + glue->rd_masklen = 8 * misbyte + maskb; + glue->rd_masklim = misbyte; + glue->rd_bmask = rd_bmask[maskb]; + glue->rd_btest = rd_btest[maskb]; + glue->rd_rtent = NULL; + glue->rd_p = parent; + glue->rd_mask = (struct sockaddr *) + ((u_char *)head->rdh_masks + slen * glue->rd_masklen); + + /* masked copy of route */ + gp = (u_char *)glue->rd_route; + *gp = *cp; /* sa_len */ + *(gp + 1) = *(cp + 1); /* sa_family */ + cp += off; + gp += off; + for(i = 0; i < misbyte; i++) + gp[i] = cp[i]; + gp[misbyte] = cp[misbyte] & glue->rd_bmask; + + if (glue->rd_btest & cp[misbyte]) { + glue->rd_r = cur; + glue->rd_l = new; + } else { + glue->rd_r = new; + glue->rd_l = cur; + } + + /* + * Children + */ + new->rd_p = cur->rd_p = glue; + + /* + * Parent + */ + if (parent->rd_l == cur) + parent->rd_l = glue; + else if (parent->rd_r == cur) + parent->rd_r = glue; + else + FATAL("rd_insert"); + return 0; +} + +/* + * Find the longest-match radish with the destination. + * Return 1 if success, otherwise return 0. + */ + +int +rd_match(d_arg, head, rdp) + struct sockaddr *d_arg; + struct radish_head *head; + struct radish **rdp; /* return value */ +{ + return rd_match_next(d_arg, head, rdp, NULL); +} + +int +rd_match_next(d_arg, head, rdp, cur) + struct sockaddr *d_arg; + struct radish_head *head; + struct radish **rdp; /* return value */ + struct radish *cur; /* return value */ +{ + struct radish *target = NULL; + int off = head->rdh_offset, i, lim; + u_char *dp = (u_char *)d_arg + off, *cp; + + if (cur == NULL) { + cur = head->rdh_top; + while (cur) { + target = cur; + if (cur->rd_btest & *(dp + cur->rd_masklim)) + cur = cur->rd_r; + else + cur = cur->rd_l; + } + } else { + target = cur->rd_p; + if (target == NULL) { + *rdp = NULL; + return 0; + } + } + + /* We are now on the leaf radish. Backtrace to find the radish + which contains route to match. */ + do { + /* If this radish doesn't have route, + we skip it and chase the next parent. */ + if (target->rd_rtent != NULL) { + cp = (u_char *)target->rd_route + off; + lim = target->rd_masklim; + + /* Check the edge for slight speed up */ + if (target->rd_bmask) + if ((*(dp + lim) & target->rd_bmask) + != *(cp + lim)){ + nextparent: + continue; + } + + /* mask is always 0xff */ + for (i = 0; i < lim; i++) + if(dp[i] != cp[i]) + /* to break the for loop */ + goto nextparent; + /* Matched to this radish! */ + *rdp = target; + return 1; + } + } while ((target = target->rd_p)); + *rdp = NULL; + return 0; +} + +/* + * Lookup the same radish according to a pair of destination and mask. + * Return a pointer to rtentry if exists. Otherwise, return NULL. + */ + +#ifndef GENERIC_USE +struct rtentry * +#else /* GENERIC_USE */ +void * +#endif /* GENERIC_USE */ +rd_lookup(d_arg, m_arg, head) + struct sockaddr *d_arg, *m_arg; + struct radish_head *head; +{ + struct radish *cur = head->rdh_top; + int off = head->rdh_offset, i, lim, olim = 0, masklen; + u_char *dp = (u_char *)d_arg + off, *cp; + + rd_mask(m_arg, head, &masklen); + + /* Skipping forward search */ + while (cur) { + /* Skip a radish if it doesn't have a route */ + if (cur->rd_rtent != NULL) { + cp = (u_char *)(cur->rd_route) + off; + lim = cur->rd_masklim; + /* check the edge to speed up a bit */ + if (cur->rd_bmask) + if ((*(dp + lim) & cur->rd_bmask) + != *(cp + lim)) + return NULL; + /* mask is always 0xff */ + for (i = olim; i < lim; i++) + if(dp[i] != cp[i]) + return NULL; + if (masklen == cur->rd_masklen) + return cur->rd_rtent; + olim = lim; + } + if (cur->rd_btest & *(dp + cur->rd_masklim)) + cur = cur->rd_r; + else + cur = cur->rd_l; + } + return NULL; +} + +/* + * Delete the radish for dest and mask. + * Return 0 if success. + * Return ENOENT if no such radish exists. + * Return EFAULT if try to delete intermediate radish which doesn't have route. + */ + +int +#ifndef GENERIC_USE +rd_delete(d_arg, m_arg, head, rt) +#else /* GENERIC_USE */ +rd_delete(d_arg, m_arg, head, item) +#endif /* GENERIC_USE */ + struct sockaddr *d_arg; + struct sockaddr *m_arg; + struct radish_head *head; +#ifndef GENERIC_USE + struct rtentry **rt; +#else /* GENERIC_USE */ + void **item; +#endif /* GENERIC_USE */ +{ + struct radish *cur = head->rdh_top; + int off = head->rdh_offset, i, lim, masklen; + u_char *dp = (u_char *)d_arg + off, *cp; + + rd_mask(m_arg, head, &masklen); +#ifndef GENERIC_USE + *rt = NULL; /* just in case */ +#else /* GENERIC_USE */ + *item = NULL; /* just in case */ +#endif /* GENERIC_USE */ + + while (cur) { + /* exit loop if dest does not match with the current node + * (dest & mask) != route + */ + cp = (u_char *)cur->rd_route + off; + /* check the edge to speed up */ + if (cur->rd_bmask) + if ((*(dp + cur->rd_masklim) & cur->rd_bmask) + != *(cp + cur->rd_masklim)) + return ENOENT; + /* mask is always 0xff */ + lim = cur->rd_masklim; + for (i = 0; i < lim; i++) + if(dp[i] != cp[i]) + return ENOENT; + + /* See if cur is exactly what we delete */ + + /* Check mask to speed up */ + if (cur->rd_masklen != masklen) + goto next; + + cp = (u_char *)cur->rd_route + off; + lim = head->rdh_alen; + for (i = 0; i < lim; i++) + if (dp[i] != cp[i]) + goto next; + /* + * Both route and mask are the same. + */ + if (cur->rd_rtent == NULL) { + /* Leaf always has route, so this radish + * must be intermediate. + * Can't delete intermediate radish which + * doesn't have route. + */ + return EFAULT; + } +#ifndef GENERIC_USE + *rt = cur->rd_rtent; +#else /* GENERIC_USE */ + *item = cur->rd_rtent; +#endif /* GENERIC_USE */ + { +#ifndef SA_LEN + /* used to report the deleted entry back */ + u_char rl = cur->rd_route->sa_len; + u_char ml = cur->rd_mask->sa_len; +#else + u_char rl = SA_LEN(cur->rd_route); + u_char ml = SA_LEN(cur->rd_mask); +#endif + + bcopy(cur->rd_route, rd_deleted_km, rl); + bcopy(cur->rd_mask, rd_deleted_km + rl, ml); + } + if (cur->rd_l && cur->rd_r) { + /* This radish has two children */ + cur->rd_rtent = NULL; + return 0; + } + /* Unlink the radish that has 0 or 1 child + * and surely has a route. + */ + rd_unlink(cur, head->rdh_top); + return 0; + + next: + /* seach corresponding subtree */ + if (cur->rd_btest & *(dp + cur->rd_masklim)) { + if (cur->rd_r) { + cur = cur->rd_r; + continue; + } else + break; + } else { + if (cur->rd_l) { + cur = cur->rd_l; + continue; + } else + break; + } + } + return ENOENT; +} + +/* + * Free radish and refine radish tree. + * rd_unlink is called with radish which have 0 or 1 child and route. + * Error causes panic, so return only when success. + */ + +void +rd_unlink(cur, top) + struct radish *cur, *top; +{ + struct radish *parent, *child; + + if (cur == top) { + cur->rd_rtent = NULL; + return; + } + + if ((cur->rd_l == 0) && (cur->rd_r == 0)) { + /* No child, just delete it. */ + parent = cur->rd_p; + if (parent->rd_l == cur) { + parent->rd_l = NULL; + Free(cur); + } else if (parent->rd_r == cur) { + parent->rd_r = NULL; + Free(cur); + } else +#ifndef GENERIC_USE + FATAL("rd_unlink"); +#else + return; +#endif + if (parent->rd_rtent == NULL && parent != top) + /* Parent is not necessary, refine radish. */ + rd_unlink(parent, top); + } else { + /* + * There is one child, never two. + * Make its child its parent's child. + */ + if (cur->rd_l) + child = cur->rd_l; + else + child = cur->rd_r; + parent = cur->rd_p; + child->rd_p = parent; + if (parent->rd_l == cur) { + parent->rd_l = child; + Free(cur); + } else if (parent->rd_r == cur) { + parent->rd_r = child; + Free(cur); + } else +#ifndef GENERIC_USE + FATAL("rd_unlink"); +#else + return; +#endif + } +} + +int +rd_walktree(h, f, w) + struct radish_head *h; + register int (*f)(struct radish *, void *); + void *w; +{ + int error = 0; + struct radish *par = NULL, *cur, *top = h->rdh_top; + + cur = top; + for (;;) { + while (cur) { + if (cur->rd_rtent != NULL) + if ((error = (*f)(cur, w))) + return error; + par = cur; + cur = cur->rd_l; + } + cur = par; + while (cur->rd_r == NULL || par == cur->rd_r) { + par = cur; + cur = cur->rd_p; + if (cur == NULL) return 0; + } + par = cur; + cur = cur->rd_r; + } +} + +/* This function can be mush easier in the context of radish. + * For instance, using rd_mask. But we stay the original because + * it works. + */ +int +rd_refines(m_arg, n_arg) + void *m_arg, *n_arg; +{ + register caddr_t m = m_arg, n = n_arg; + register caddr_t lim, lim2 = lim = n + *(u_char *)n; + int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); + int masks_are_equal = 1; + + if (longer > 0) + lim -= longer; + while (n < lim) { + if (*n & ~(*m)) + return 0; + if (*n++ != *m++) + masks_are_equal = 0; + + } + while (n < lim2) + if (*n++) + return 0; + if (masks_are_equal && (longer < 0)) + for (lim2 = m - longer; m < lim2; ) + if (*m++) + return 1; + return (!masks_are_equal); +} +#endif /* RADISH */ diff --git a/usr.sbin/npppd/common/radish.h b/usr.sbin/npppd/common/radish.h new file mode 100644 index 00000000000..a9f4a804289 --- /dev/null +++ b/usr.sbin/npppd/common/radish.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * radish.h + * + * Version: 0.5 + * Created: May 27, 1995 + * Modified: January 11, 1997 + * Author: Kazu YAMAMOTO + * Email: kazu@is.aist-nara.ac.jp + */ + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif /* __STDC__ */ +#endif /* __P */ + +#ifdef RADISH +#ifndef _NET_RADISH_H_ +#define _NET_RADISH_H_ + +struct radish { + struct sockaddr *rd_route; /* destination route */ + struct sockaddr *rd_mask; /* destination mask */ + u_int rd_masklen; /* length of mask */ + u_short rd_masklim; /* length of mask / 8 : test point */ + u_char rd_bmask; /* byte mask */ + u_char rd_btest; /* bit to test */ + struct radish *rd_p; /* parent */ + struct radish *rd_l; /* left child */ + struct radish *rd_r; /* right child */ +#ifndef GENERIC_USE + struct rtentry *rd_rtent; /* rtentry */ +#else /* GENERIC_USE */ + void *rd_rtent; /* rtentry */ +#endif /* GENERIC_USE */ +}; + +struct radish_head { + int rdh_slen; /* socket address length */ + int rdh_offset; /* address start byte */ + int rdh_alen; /* address length */ + void *rdh_masks; + struct radish *rdh_top; + int (*rdh_match)(void *, void *); +}; + +#ifdef KERNEL +#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned long)(n), M_RTABLE, M_DONTWAIT)) +#define Free(p) free((caddr_t)p, M_RTABLE); +#else /* KERNEL */ +#ifndef Bcmp +#define Bcmp(a, b, n) memcmp(((char *)(a)), ((char *)(b)), (size_t)(n)) +#endif +#ifndef Bzero +#define Bzero(p, n) memset((char *)(p), 0, (size_t)(n)) +#endif +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) +#define Free(p) free((char *)p); +#endif /* KERNEL */ + +/* + * prototype for radish functions + */ + +int rd_inithead __P((void **, int, int, int, int, int (*)(void *, void *))); +struct sockaddr *rd_mask __P((struct sockaddr *, struct radish_head *, int *)); + +#ifndef GENERIC_USE +int rd_insert __P((struct sockaddr *, struct sockaddr *, + struct radish_head *, struct rtentry *)); +#else /* GENERIC_USE */ +int rd_insert __P((struct sockaddr *, struct sockaddr *, + struct radish_head *, void *)); +#endif /* GENERIC_USE */ +int rd_glue __P((struct radish *, struct radish *, int, struct radish_head *)); +int rd_match __P((struct sockaddr *, struct radish_head *, struct radish **)); +int rd_match_next __P((struct sockaddr *, struct radish_head *, struct radish **, struct radish *)); +#ifndef GENERIC_USE +struct rtentry *rd_lookup __P((struct sockaddr *, + struct sockaddr *, struct radish_head *)); +int rd_delete __P((struct sockaddr *, struct sockaddr *, + struct radish_head *, struct rtentry **)); +#else /* GENERIC_USE */ +void *rd_lookup __P((struct sockaddr *, + struct sockaddr *, struct radish_head *)); +int rd_delete __P((struct sockaddr *, struct sockaddr *, + struct radish_head *, void **)); +#endif /* GENERIC_USE */ +void rd_unlink __P((struct radish *, struct radish *)); +int rd_walktree __P((struct radish_head *, int (*)(struct radish *, void *), void *)); +int rd_refines __P((void *, void *)); +#endif /* !_NET_RADISH_H_ */ +#endif /* RADISH */ diff --git a/usr.sbin/npppd/common/recvfromto.c b/usr.sbin/npppd/common/recvfromto.c new file mode 100644 index 00000000000..fae801dafad --- /dev/null +++ b/usr.sbin/npppd/common/recvfromto.c @@ -0,0 +1,175 @@ +/* adapted from ipsec-tools 0.6 src/racoon/sockmisc.c */ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <string.h> + +#include "recvfromto.h" + +/* + * Receive packet, with src/dst information. It is assumed that necessary + * setsockopt() have already performed on socket. + */ +int +recvfromto(s, buf, buflen, flags, from, fromlen, to, tolen) + int s; + void *buf; + size_t buflen; + int flags; + struct sockaddr *from; + u_int *fromlen; + struct sockaddr *to; + u_int *tolen; +{ + int otolen; + ssize_t len; + struct sockaddr_storage ss; + struct msghdr m; + struct cmsghdr *cm; + struct iovec iov[2]; + u_char cmsgbuf[256]; +#if defined(INET6) && defined(INET6_ADVAPI) + struct in6_pktinfo *pi; +#endif /*INET6_ADVAPI*/ + struct sockaddr_in *sin4; + socklen_t sslen; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + sslen = sizeof(ss); + if (getsockname(s, (struct sockaddr *)&ss, &sslen) < 0) + return -1; + + m.msg_name = (caddr_t)from; + m.msg_namelen = *fromlen; + iov[0].iov_base = (caddr_t)buf; + iov[0].iov_len = buflen; + m.msg_iov = iov; + m.msg_iovlen = 1; + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + cm = (struct cmsghdr *)cmsgbuf; + m.msg_control = (caddr_t)cm; + m.msg_controllen = sizeof(cmsgbuf); + if ((len = recvmsg(s, &m, flags)) <= 0) { + return len; + } + *fromlen = m.msg_namelen; + + otolen = *tolen; + *tolen = 0; + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); + m.msg_controllen != 0 && cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { +#if defined(INET6) && defined(INET6_ADVAPI) + if (ss.ss_family == AF_INET6 + && cm->cmsg_level == IPPROTO_IPV6 + && cm->cmsg_type == IPV6_PKTINFO + && otolen >= sizeof(*sin6)) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + *tolen = sizeof(*sin6); + sin6 = (struct sockaddr_in6 *)to; + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; +#ifndef __linux__ + sin6->sin6_len = sizeof(*sin6); +#endif + memcpy(&sin6->sin6_addr, &pi->ipi6_addr, + sizeof(sin6->sin6_addr)); + /* XXX other cases, such as site-local? */ + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_scope_id = pi->ipi6_ifindex; + else + sin6->sin6_scope_id = 0; + sin6->sin6_port = + ((struct sockaddr_in6 *)&ss)->sin6_port; + otolen = -1; /* "to" already set */ + continue; + } +#endif +#ifdef __linux__ + if (ss.ss_family == AF_INET + && cm->cmsg_level == IPPROTO_IP + && cm->cmsg_type == IP_PKTINFO + && otolen >= sizeof(sin4)) { + struct in_pktinfo *pi = (struct in_pktinfo *)(CMSG_DATA(cm)); + *tolen = sizeof(*sin4); + sin4 = (struct sockaddr_in *)to; + memset(sin4, 0, sizeof(*sin4)); + sin4->sin_family = AF_INET; + memcpy(&sin4->sin_addr, &pi->ipi_addr, + sizeof(sin4->sin_addr)); + sin4->sin_port = + ((struct sockaddr_in *)&ss)->sin_port; + otolen = -1; /* "to" already set */ + continue; + } +#endif +#if defined(INET6) && defined(IPV6_RECVDSTADDR) + if (ss.ss_family == AF_INET6 + && cm->cmsg_level == IPPROTO_IPV6 + && cm->cmsg_type == IPV6_RECVDSTADDR + && otolen >= sizeof(*sin6)) { + *tolen = sizeof(*sin6); + sin6 = (struct sockaddr_in6 *)to; + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + memcpy(&sin6->sin6_addr, CMSG_DATA(cm), + sizeof(sin6->sin6_addr)); + sin6->sin6_port = + ((struct sockaddr_in6 *)&ss)->sin6_port; + otolen = -1; /* "to" already set */ + continue; + } +#endif +#ifndef __linux__ + if (ss.ss_family == AF_INET + && cm->cmsg_level == IPPROTO_IP + && cm->cmsg_type == IP_RECVDSTADDR + && otolen >= sizeof(*sin4)) { + *tolen = sizeof(*sin4); + sin4 = (struct sockaddr_in *)to; + memset(sin4, 0, sizeof(*sin4)); + sin4->sin_family = AF_INET; + sin4->sin_len = sizeof(*sin4); + memcpy(&sin4->sin_addr, CMSG_DATA(cm), + sizeof(sin4->sin_addr)); + sin4->sin_port = ((struct sockaddr_in *)&ss)->sin_port; + otolen = -1; /* "to" already set */ + continue; + } +#endif + } + + return len; +} diff --git a/usr.sbin/npppd/common/recvfromto.h b/usr.sbin/npppd/common/recvfromto.h new file mode 100644 index 00000000000..b46b24f0b86 --- /dev/null +++ b/usr.sbin/npppd/common/recvfromto.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef RECVFROMTO_H +#define RECVFROMTO_H 1 +#ifdef __cplusplus +extern "C" { +#endif + +int recvfromto (int, void *, size_t, int, struct sockaddr *, u_int *, struct sockaddr *, u_int *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/common/rt_zebra.c b/usr.sbin/npppd/common/rt_zebra.c new file mode 100644 index 00000000000..726262b6741 --- /dev/null +++ b/usr.sbin/npppd/common/rt_zebra.c @@ -0,0 +1,389 @@ +/*- + * Copyright (c) 2007 + * Internet Initiative Japan Inc. All rights reserved. + */ +/* $Id: rt_zebra.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/* + * @file This file provides utility functions to help add/delete routing + * information with GNU Zebra. This utility uses event(3) and uses a UNIX + * domain socket at communication with the zserv(Zebra). + * <p> + * example: + * <pre> + rt_zebra *rtz; + + rtz = rt_zebra_get_instance(); + rt_zebra_init(rtz); + rt_zebra_start(rtz); + + // add 10.0.0.0/8 to a blackhole + rt_zebra_add_ipv4_blackhole_rt(rtz, 0x0a000000, 0xff00000); + + // delete 10.0.0.0/8 + rt_zebra_delete_ipv4_blackhole_rt(rtz, 0x0a000000, 0xff00000); + + rt_zebra_stop(rtz); + rt_zebra_fini(rtz); + * </pre></p> + */ +/* compile-time options */ +#ifndef RT_ZEBRA_BLACKHOLE_IFNAME +#define RT_ZEBRA_BLACKHOLE_IFNAME "lo0" +#endif +#ifndef DEFAULT_RT_ZEBRA_BLACKHOLE_DISTANCE +#define DEFAULT_RT_ZEBRA_BLACKHOLE_DISTANCE 16 +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> +#include <time.h> +#include <event.h> +#include <fcntl.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> + +#include "debugutil.h" +#include "bytebuf.h" +#include "net_utils.h" + +#include <zebra.h> +#include <prefix.h> +#include <zclient.h> + +#include "rt_zebra.h" +#include "rt_zebra_local.h" + +static void rt_zebra_set_event (rt_zebra *); +static void rt_zebra_io_event (int, short, void *); +static int rt_zebra_log (rt_zebra *, int, const char *, ...) __printflike(3,4); +static int rt_zebra_ipv4_blackhole_rt0(rt_zebra *, uint32_t, uint32_t, int); + +static rt_zebra rt_zebra_singleton; /* singleton */ +static int rt_zebra_blackhole_ifidx = -1; + +/** Returns the only one rt_zebra context. */ +rt_zebra * +rt_zebra_get_instance(void) +{ + return &rt_zebra_singleton; +} + +/** Initialize the given rt_zebra context. */ +int +rt_zebra_init(rt_zebra *_this) +{ + RT_ZEBRA_ASSERT((_this->state == ZEBRA_STATUS_INIT0)); + + memset(_this, 0, sizeof(rt_zebra)); + _this->sock = -1; + if ((_this->buffer = bytebuffer_create(8192)) == NULL) + return 1; + _this->state = ZEBRA_STATUS_INIT; + + if (rt_zebra_blackhole_ifidx == -1) + rt_zebra_blackhole_ifidx = if_nametoindex( + RT_ZEBRA_BLACKHOLE_IFNAME); + + return 0; +} + +/** Finalialize the given rt_zebra context. */ +void +rt_zebra_fini(rt_zebra *_this) +{ + if (_this->state != ZEBRA_STATUS_STOPPED && + _this->state != ZEBRA_STATUS_DISPOSING) + rt_zebra_stop(_this); + + if (_this->buffer != NULL) + bytebuffer_destroy(_this->buffer); + _this->buffer = NULL; + _this->state = ZEBRA_STATUS_DISPOSING; +} + +/** + * Add the specified IPv4 blackhole routing entry. + * @param addr the detination IPv4 address part in host byte-order. + * @param mask the detination IPv4 netmask part in host byte-order. + */ +int +rt_zebra_add_ipv4_blackhole_rt(rt_zebra *_this, uint32_t addr, + uint32_t mask) +{ + return rt_zebra_ipv4_blackhole_rt0(_this, addr, mask, 0); +} + +/** + * Deletes the specified IPv4 blackhole routing entry. + * @param addr the detination IPv4 address part in host byte-order. + * @param mask the detination IPv4 netmask part in host byte-order. + */ +int +rt_zebra_delete_ipv4_blackhole_rt(rt_zebra *_this, uint32_t addr, + uint32_t mask) +{ + return rt_zebra_ipv4_blackhole_rt0(_this, addr, mask, 1); +} + +/** Start processing */ +int +rt_zebra_start(rt_zebra *_this) +{ + int ival, sock; + struct sockaddr_un sun; + + RT_ZEBRA_ASSERT(_this->state == ZEBRA_STATUS_INIT || + _this->state == ZEBRA_STATUS_STOPPED); + + sun.sun_len = sizeof(sun); + sun.sun_family = AF_LOCAL; + strlcpy(sun.sun_path, ZEBRA_SERV_PATH, sizeof(sun.sun_path)); + + sock = -1; + + if ((sock = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { + log_printf(LOG_ERR, + "Creating a socket to the zserv failed: %m"); + return 1; + } + if ((ival = fcntl(sock, F_GETFL, 0)) < 0) { + log_printf(LOG_ERR, "fcntl(,F_GETFL) failed: %m"); + goto reigai; + } else if (fcntl(sock, F_SETFL, ival | O_NONBLOCK) < 0) { + log_printf(LOG_ERR, "fcntl(,F_SETFL, +O_NONBLOCK) failed: %m"); + goto reigai; + } + + _this->state = ZEBRA_STATUS_CONNECTING; + _this->sock = sock; + sock = -1; + if (connect(_this->sock, (struct sockaddr *)&sun, sizeof(sun)) == 0) { + /* + * don't change the state here, but change it on the next + * write ready event. + */ + } else { + switch (errno) { + case EINPROGRESS: + break; + default: + log_printf(LOG_ERR, + "Connection to the zserv failed: %m"); + goto reigai; + } + } + event_set(&_this->ev_sock, _this->sock, EV_READ|EV_WRITE, + rt_zebra_io_event, _this); + event_add(&_this->ev_sock, NULL); + + return 0; +reigai: + if (sock >= 0) + close(sock); + rt_zebra_stop(_this); + + return 1; +} + +/** Stop processing */ +void +rt_zebra_stop(rt_zebra *_this) +{ + if (_this->sock >= 0) { + event_del(&_this->ev_sock); + close(_this->sock); + } + _this->sock = -1; + _this->state = ZEBRA_STATUS_STOPPED; +} + +/** Is processing */ +int +rt_zebra_is_running(rt_zebra *_this) +{ + return (_this->state == ZEBRA_STATUS_INIT || + _this->state == ZEBRA_STATUS_STOPPED || + _this->state == ZEBRA_STATUS_DISPOSING)? 0 : 1; +} + +static int +rt_zebra_ipv4_blackhole_rt0(rt_zebra *_this, uint32_t addr0, + uint32_t mask0, int delete) +{ + int i, prefix, msg_flags, len, flags0, distance; + u_char buf[1024], *cp; + uint32_t addr, mask; + + RT_ZEBRA_DBG((_this, LOG_DEBUG, + "%s %s(%08x,%08x)", __func__, (delete)? "delete" : "add", + addr0, mask0)); + + if (_this->state == ZEBRA_STATUS_INIT || + _this->state == ZEBRA_STATUS_DISPOSING) + return 1; + + distance = DEFAULT_RT_ZEBRA_BLACKHOLE_DISTANCE; + addr = ntohl(addr0); + mask = ntohl(mask0); + + /* + * Create a zebra protocol message. + */ + cp = buf; + msg_flags = ZAPI_MESSAGE_NEXTHOP | ZAPI_MESSAGE_DISTANCE; + flags0 = ZEBRA_FLAG_STATIC | ZEBRA_FLAG_BLACKHOLE; + + /* zebra protocol header */ + PUTSHORT(0, cp); /* length place holder */ + if (delete) { + PUTCHAR(ZEBRA_IPV4_ROUTE_DELETE, cp); /* command */ + } else { + PUTCHAR(ZEBRA_IPV4_ROUTE_ADD, cp); /* command */ + } + + PUTCHAR(ZEBRA_ROUTE_STATIC, cp); /* route type */ + PUTCHAR(flags0, cp); /* flags */ + PUTCHAR(msg_flags, cp); /* message */ + + /* destination address/netmask */ + prefix = netmask2prefixlen(mask); + PUTCHAR(prefix, cp); /* prefix */ + for (i = 0; i < (prefix + 7) / 8; i++) + PUTCHAR(*(((u_char *)&addr0) + i), cp); + + /* nexthop */ + PUTCHAR(1, cp); /* number of message */ + PUTCHAR(ZEBRA_NEXTHOP_IFINDEX, cp); + PUTLONG(rt_zebra_blackhole_ifidx , cp); + + if ((msg_flags & ZAPI_MESSAGE_DISTANCE) != 0) + PUTCHAR(distance, cp); /* distance */ + + len = cp - buf; /* save length */ + cp = buf; /* rewind the position */ + PUTSHORT(len, cp); /* length */ + + if (bytebuffer_put(_this->buffer, buf, len) == NULL) + return 1; + + if (_this->state == ZEBRA_STATUS_CONNECTED) { + if (_this->write_ready) + rt_zebra_io_event(_this->sock, 0, _this); + } + if (_this->state == ZEBRA_STATUS_STOPPED) + rt_zebra_start(_this); + + return 0; +} + +static void +rt_zebra_io_event(int fd, short ev, void *ctx) +{ + int sz; + u_char buf[BUFSIZ]; + rt_zebra *_this; + + _this = ctx; + + RT_ZEBRA_DBG((_this, LOG_DEBUG, "%s [%s%s%s%s]", __func__, + (ev & EV_READ)? "R" : "-", (ev & EV_WRITE)? "W" : "-", + (_this->write_ready)? "w" : "-", + (bytebuffer_position(_this->buffer) != 0)? "P" : "-")); + + if ((ev & EV_WRITE) != 0) { + if (_this->state == ZEBRA_STATUS_CONNECTING) { + rt_zebra_log(_this, LOG_NOTICE, + "Established a new connection to the zserv."); + _this->state = ZEBRA_STATUS_CONNECTED; + } + _this->write_ready = 1; + } + if ((ev & EV_READ) != 0) { + if ((sz = read(_this->sock, buf, sizeof(buf))) <= 0) { + if (sz == 0 || errno == ECONNRESET) { + /* connection closed or reseted by the peer. */ + + rt_zebra_log(_this, LOG_INFO, + "Connection closed by the zserv"); + rt_zebra_stop(_this); + return; + } + if (errno != EAGAIN) { + rt_zebra_log(_this, LOG_INFO, + "read() failed from the zserv: %m"); + rt_zebra_stop(_this); + return; + } + } else { + /* assumes no responce from a zebra. */ + + RT_ZEBRA_ASSERT("NOTREACHED" == NULL); + RT_ZEBRA_DBG((_this, LOG_DEBUG, + "Received unexpected %d bytes message.", sz)); + } + } + if (_this->write_ready != 0) { + bytebuffer_flip(_this->buffer); + while (bytebuffer_has_remaining(_this->buffer)) { + if ((sz = write(_this->sock, + bytebuffer_pointer(_this->buffer), + bytebuffer_remaining(_this->buffer))) < 0) { + if (errno == EAGAIN) + break; + bytebuffer_compact(_this->buffer); + + rt_zebra_log(_this, LOG_ERR, + "write() failed to the zserv.:%m"); + _this->state = ZEBRA_STATUS_CONNECTED; + rt_zebra_stop(_this); + return; + } + _this->write_ready = 0; + bytebuffer_get(_this->buffer, BYTEBUFFER_GET_DIRECT, + sz); + } + bytebuffer_compact(_this->buffer); + } + rt_zebra_set_event(_this); + + return; +} + +static int +rt_zebra_log(rt_zebra *_this, int logprio, const char *fmt, ...) +{ + int rval; + char buf[BUFSIZ]; + va_list ap; + + strlcpy(buf, "rt_zebra ", sizeof(buf)); + strlcat(buf, fmt, sizeof(buf)); + + va_start(ap, fmt); + rval = vlog_printf(logprio, buf, ap); + va_end(ap); + + return rval; +} + +static void +rt_zebra_set_event(rt_zebra *_this) +{ + int evmask; + + RT_ZEBRA_ASSERT(_this->sock >= 0); + evmask = EV_READ; + if (_this->write_ready == 0) + evmask |= EV_WRITE; + + event_del(&_this->ev_sock); + event_set(&_this->ev_sock, _this->sock, evmask, rt_zebra_io_event, + _this); + event_add(&_this->ev_sock, NULL); +} diff --git a/usr.sbin/npppd/common/rt_zebra.h b/usr.sbin/npppd/common/rt_zebra.h new file mode 100644 index 00000000000..1260f469608 --- /dev/null +++ b/usr.sbin/npppd/common/rt_zebra.h @@ -0,0 +1,30 @@ +#ifndef RT_ZEBRA +#define RT_ZEBRA 1 + +/** rt_zebra context */ +typedef struct _rt_zebra { + int sock; + int state; + int write_ready:1, + reserved:31; + struct event ev_sock; + bytebuffer *buffer; +} rt_zebra; + +#ifdef __cplusplus +extern "C" { +#endif + +rt_zebra * rt_zebra_get_instance(void); +int rt_zebra_init (rt_zebra *); +void rt_zebra_fini (rt_zebra *); +int rt_zebra_start (rt_zebra *); +void rt_zebra_stop (rt_zebra *); +int rt_zebra_is_running (rt_zebra *); +int rt_zebra_add_ipv4_blackhole_rt(rt_zebra *, uint32_t, uint32_t ); +int rt_zebra_delete_ipv4_blackhole_rt(rt_zebra *, uint32_t, uint32_t ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/common/rt_zebra_local.h b/usr.sbin/npppd/common/rt_zebra_local.h new file mode 100644 index 00000000000..08a6c1ef7da --- /dev/null +++ b/usr.sbin/npppd/common/rt_zebra_local.h @@ -0,0 +1,46 @@ +#define ZEBRA_STATUS_INIT0 0 +#define ZEBRA_STATUS_INIT 1 +#define ZEBRA_STATUS_CONNECTING 2 +#define ZEBRA_STATUS_CONNECTED 3 +#define ZEBRA_STATUS_STOPPED 4 +#define ZEBRA_STATUS_DISPOSING 5 + + +#ifdef RT_ZEBRA_DEBUG +#define RT_ZEBRA_DBG(x) rt_zebra_log x +#define RT_ZEBRA_ASSERT(x) ASSERT(x) +#else +#define RT_ZEBRA_DBG(x) +#define RT_ZEBRA_ASSERT(x) +#endif + +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} + +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + ()l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + diff --git a/usr.sbin/npppd/common/rtev.h b/usr.sbin/npppd/common/rtev.h new file mode 100644 index 00000000000..ecb989f7a41 --- /dev/null +++ b/usr.sbin/npppd/common/rtev.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef RTEV_H +#define RTEV_H + +#ifndef NO_RTEV_WRAPPER +#define getifaddrs(ifa) rtev_getifaddrs(ifa) +#define if_nametoindex(ifname) rtev_if_nametoindex(ifname); +#define freeifaddrs(ifa) ((void)0) +#endif + +#define RTEV_UPDATE_IFA_ON_DEMAND 0x0001 + +#ifdef __cplusplus +extern "C" { +#endif + +int rtev_libevent_init (int, int, int, int); +void rtev_fini (void); +int rtev_write (void *); +int rtev_getifaddrs (struct ifaddrs **); +int rtev_ifa_is_primary (const char *, struct sockaddr *); +inline int rtev_get_event_serial (void); +struct ifaddrs *rtev_getifaddrs_by_ifname (const char *); +struct ifaddrs *rtev_getifaddrs_by_sockaddr(struct sockaddr const *); +unsigned int rtev_if_nametoindex (const char *); +int rtev_has_write_pending(void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/common/rtev_common.c b/usr.sbin/npppd/common/rtev_common.c new file mode 100644 index 00000000000..ea9403067eb --- /dev/null +++ b/usr.sbin/npppd/common/rtev_common.c @@ -0,0 +1,517 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: rtev_common.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/* + * PF_ROUTE related utility functions. + * <p> + * When use with libevent, call rtev_libevent_init() to initialize this + * library.</p> + * usage: + * <pre> + * #include <sys/types.h> + * #include <time.h> + * #include <event.h> + * #include <ifaddrs.h> + * #include <rtev.h> + * + * int main() + * { + * event_init(); + * rtev_libevent_init(5, 100, 16); // init after event_init() + * + * event_loop(); + * + * rtev_fini(); // fini before exit() + * exit(0); + * } + * + * void hogehoge() + * { + * struct ifaddrs *ifa = NULL; + * + * getifaddrs(&ifa); // rtev.h replaces getifaddrs(3) + * : + * freeifaddrs(&ifa); + * } + * </pre> + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/route.h> +#include <net/if_dl.h> + +#include <ifaddrs.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <errno.h> + +#define NO_RTEV_WRAPPER 1 +#include "bytebuf.h" +#include "rtev.h" +#include "rtev_local.h" + +#ifdef RTEV_DEBUG +#include "debugutil.h" +#endif + +#ifndef RTEV_BUFSIZ +#define RTEV_BUFSIZ 131072 /* 128K */ +#endif + +static int rtev_base_on_rtevent(rtev_impl *); +static int rtev_base_on_write(rtev_impl *, int, int); + +static struct ifaddrs *rtev_cached_ifa = NULL; +static int rtev_event_serial = 0; +static int rtev_send_serial = 0; +static int rtev_ifa_cache_serial = 0; +static bytebuffer *rtev_sndbuf = NULL; +static u_char rtev_buffer_space[RTEV_BUFSIZ]; +static rtev_impl *singleton_impl = NULL; + +static inline int rtev_update_ifa_cache(rtev_impl *); +static int rtev_update_ifa_cache_do(rtev_impl *); +static void ifa_rbentry_init (void); +static void ifa_rbentry_fini (void); +static inline int ifa_rb_ifacenam_insert (struct ifaddrs *); +static inline int ifa_rb_sockaddr_insert (struct ifaddrs *); +static inline struct ifaddrs *ifa_rb_ifacenam_find (const char *); +static inline struct ifaddrs *ifa_rb_sockaddr_find (struct sockaddr const *); + +/** + * Write a routing message. + * @return not zero indicates error. See errno. + */ +int +rtev_write(void *rtm_msg) +{ + int rval; + struct rt_msghdr *rtm; + + rtm = rtm_msg; + rtm->rtm_seq = rtev_send_serial++; + if (bytebuffer_put(rtev_sndbuf, rtm, rtm->rtm_msglen) == NULL) + rval = -1; + else + rval = rtm->rtm_msglen; + + if (singleton_impl->impl_on_write != NULL) + singleton_impl->impl_on_write(singleton_impl); + + return rval; +} + +/** + * same as getifaddrs(3) but returned obeject is cached. + * The cached object may be freed by the event handler of this library, + * so you cannot use it after the event handler returns the event loop. + */ +int +rtev_getifaddrs(struct ifaddrs **ifa) +{ + if (rtev_update_ifa_cache(singleton_impl) != 0) + return 1; + + *ifa = rtev_cached_ifa; + + return 0; +} + +/** + * checks whether given address is the primary address of the interface. + * @return not zero if the address is the primary. + */ +int +rtev_ifa_is_primary(const char *ifname, struct sockaddr *sa) +{ + int count; + struct ifaddrs *ifa; + + for (count = 0, ifa = rtev_getifaddrs_by_ifname(ifname); ifa != NULL; + ifa = ifa->ifa_next) { + + if (strcmp(ifa->ifa_name, ifname) != 0) + break; + if (ifa->ifa_addr->sa_family != sa->sa_family) + continue; + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_addr.s_addr == + ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr + .s_addr) { + if (count == 0) + return 1; + return 0; + } + count++; + break; + case AF_INET6: + if (IN6_ARE_ADDR_EQUAL( + &((struct sockaddr_in6 *)sa)->sin6_addr, + &((struct sockaddr_in6 *)ifa->ifa_addr) + ->sin6_addr)){ + if (count == 0) + return 1; + return 0; + } + count++; + break; + } + } + return 0; +} + +/** returns the routing event serial number */ +int +rtev_get_event_serial() +{ + return rtev_event_serial; +} + +/** same as ifaddrs(3), but returned object is the first entry of 'ifname' */ +struct ifaddrs * +rtev_getifaddrs_by_ifname(const char *ifname) +{ + if (rtev_update_ifa_cache(singleton_impl) != 0) + return NULL; + + return ifa_rb_ifacenam_find(ifname); +} + +struct ifaddrs * +rtev_getifaddrs_by_sockaddr(struct sockaddr const *sa) +{ + if (rtev_update_ifa_cache(singleton_impl) != 0) + return NULL; + + return ifa_rb_sockaddr_find(sa); +} + +/** same as if_nametoindex(3), but fast and no memory allocation. */ +unsigned int +rtev_if_nametoindex(const char *ifname) +{ + unsigned int ni; + struct ifaddrs *ifa; + + /* adapted from lib/libc/net/if_nametoindex.c */ + ni = 0; + + for (ifa = rtev_getifaddrs_by_ifname(ifname); ifa != NULL; + ifa = ifa->ifa_next) { + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_LINK) { + if (strcmp(ifa->ifa_name, ifname) == 0) { + ni = ((struct sockaddr_dl*)ifa->ifa_addr) + ->sdl_index; + break; + } else + break; + } + } + + if (!ni) + errno = ENXIO; + + return ni; +} + +/** API have write pending packet internaly */ +int +rtev_has_write_pending(void) +{ + if (bytebuffer_position(rtev_sndbuf) > 0) + return 1; + return 0; +} + +/** finalize this library. */ +void +rtev_fini(void) +{ + if (singleton_impl != NULL) + singleton_impl->impl_fini(singleton_impl->impl); + singleton_impl = NULL; + + if (rtev_cached_ifa != NULL) + freeifaddrs(rtev_cached_ifa); + rtev_cached_ifa = NULL; + ifa_rbentry_fini(); + + if (rtev_sndbuf != NULL) { + bytebuffer_unwrap(rtev_sndbuf); + bytebuffer_destroy(rtev_sndbuf); + } + rtev_sndbuf = NULL; +} + +/* protected virtual */ +int +rtev_base_init(rtev_impl *impl, int flags) +{ + ifa_rbentry_init(); + + if (rtev_sndbuf == NULL) { + if ((rtev_sndbuf = bytebuffer_wrap(rtev_buffer_space, + sizeof(rtev_buffer_space))) == NULL) + goto reigai; + bytebuffer_clear(rtev_sndbuf); + } + impl->base_on_rtevent = rtev_base_on_rtevent; + impl->base_on_write = rtev_base_on_write; + impl->base_flags = flags; + singleton_impl = impl; + + return 0; +reigai: + rtev_fini(); + + return 1; +} + +static int +rtev_base_on_rtevent(rtev_impl *impl) +{ + + RTEV_DBG((LOG_DEBUG, "%s", __func__)); + + rtev_event_serial++; + + if ((impl->base_flags & RTEV_UPDATE_IFA_ON_DEMAND) == 0) + return rtev_update_ifa_cache_do(impl); + + return 0; +} + +static inline int +rtev_update_ifa_cache(rtev_impl *impl) +{ + if (rtev_event_serial == rtev_ifa_cache_serial && + rtev_cached_ifa != NULL) + return 0; + + return rtev_update_ifa_cache_do(impl); +} + +static int +rtev_update_ifa_cache_do(rtev_impl *impl) +{ + const char *ifname; + struct ifaddrs *ifa; + struct ifaddrs *ifa0; + + RTEV_DBG((LOG_DEBUG, "%s", __func__)); + + ifa0 = NULL; + rtev_ifa_cache_serial = rtev_event_serial; + if (getifaddrs(&ifa0) != 0) + return 1; + if (rtev_cached_ifa != NULL) { + ifa_rbentry_fini(); + freeifaddrs(rtev_cached_ifa); + rtev_cached_ifa = NULL; + } + + for (ifa = ifa0, ifname = NULL; ifa != NULL; ifa = ifa->ifa_next) { + if (ifname == NULL || strcmp(ifa->ifa_name, ifname)) { + ifname = ifa->ifa_name; + if (ifa_rb_ifacenam_find(ifname) == NULL) { + if (ifa_rb_ifacenam_insert(ifa) != 0) + goto on_error; + } + } + if (ifa->ifa_addr != NULL && + ifa_rb_sockaddr_find(ifa->ifa_addr) == NULL) { + if (ifa_rb_sockaddr_insert(ifa) != 0) + goto on_error; + } + } + rtev_cached_ifa = ifa0; + + return 0; + +on_error: + if (ifa0) + freeifaddrs(ifa0); + rtev_cached_ifa = NULL; + ifa_rbentry_fini(); + + return 1; +} + +static int +rtev_base_on_write(rtev_impl *impl, int rtsock, int npackets) +{ + int i, rval; + struct rt_msghdr *rtm; + + rval = 0; + bytebuffer_flip(rtev_sndbuf); + for (i = 0; i < npackets && bytebuffer_remaining(rtev_sndbuf) > 0; i++){ + rtm = bytebuffer_pointer(rtev_sndbuf); + if (send(rtsock, rtm, rtm->rtm_msglen, 0) <= 0 && + !(rtm->rtm_type == RTM_DELETE && errno == ESRCH) && + !(rtm->rtm_type == RTM_ADD && errno == EEXIST)) { + rval = 1; + } + bytebuffer_get(rtev_sndbuf, BYTEBUFFER_GET_DIRECT, + rtm->rtm_msglen); + } + bytebuffer_compact(rtev_sndbuf); + + return rval; +} + +/* + * Red-black trees for interface name and interface address lookups. + */ +#include <sys/tree.h> /* BSD sys/tree.h */ + +struct ifa_rbentry { + struct ifaddrs *ifa; + RB_ENTRY(ifa_rbentry) rbe; +}; + +static inline int ifacenam_compar(struct ifa_rbentry *, struct ifa_rbentry *); +static RB_HEAD(ifa_rb_ifacenam, ifa_rbentry) ifa_rb_ifacenam; +RB_PROTOTYPE(ifa_rb_ifacenam, ifa_rbentry, rbe, ifacenam_compar); +RB_GENERATE(ifa_rb_ifacenam, ifa_rbentry, rbe, ifacenam_compar); + +static inline int sockaddr_compar(struct ifa_rbentry *, struct ifa_rbentry *); +static RB_HEAD(ifa_rb_sockaddr, ifa_rbentry) ifa_rb_sockaddr; +RB_PROTOTYPE(ifa_rb_sockaddr, ifa_rbentry, rbe, sockaddr_compar); +RB_GENERATE(ifa_rb_sockaddr, ifa_rbentry, rbe, sockaddr_compar); + +static void +ifa_rbentry_init(void) +{ + RB_INIT(&ifa_rb_ifacenam); + RB_INIT(&ifa_rb_sockaddr); +} + +static void +ifa_rbentry_fini(void) +{ + struct ifa_rbentry *e, *n; + + for (e = RB_MIN(ifa_rb_ifacenam, &ifa_rb_ifacenam); e; e = n) { + n = RB_NEXT(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); + RB_REMOVE(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); + free(e); + } + for (e = RB_MIN(ifa_rb_sockaddr, &ifa_rb_sockaddr); e; e = n) { + n = RB_NEXT(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); + RB_REMOVE(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); + free(e); + } +} + +static inline int +ifa_rb_ifacenam_insert(struct ifaddrs *ifa) +{ + struct ifa_rbentry *e; + + if ((e = malloc(sizeof(struct ifa_rbentry))) == NULL) + return -1; + + e->ifa = ifa; + RB_INSERT(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); + + return 0; +} + +static inline int +ifa_rb_sockaddr_insert(struct ifaddrs *ifa) +{ + struct ifa_rbentry *e; + + if ((e = malloc(sizeof(struct ifa_rbentry))) == NULL) + return -1; + + e->ifa = ifa; + RB_INSERT(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); + + return 0; +} + +static inline struct ifaddrs * +ifa_rb_ifacenam_find(const char *ifname) +{ + struct ifa_rbentry *e, e0; + struct ifaddrs ifa; + + e = &e0; + e->ifa = &ifa; + e->ifa->ifa_name = (char *)ifname; + + e = RB_FIND(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); + if (e == NULL) + return NULL; + + return e->ifa; +} + +static inline struct ifaddrs * +ifa_rb_sockaddr_find(struct sockaddr const *sa) +{ + struct ifa_rbentry *e, e0; + struct ifaddrs ifa; + + e = &e0; + e->ifa = &ifa; + e->ifa->ifa_addr = (struct sockaddr *)sa; + + e = RB_FIND(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); + if (e == NULL) + return NULL; + + return e->ifa; +} + +static inline int +ifacenam_compar(struct ifa_rbentry *a, struct ifa_rbentry *b) +{ + return strcmp(a->ifa->ifa_name, b->ifa->ifa_name); +} + +static inline int +sockaddr_compar(struct ifa_rbentry *a, struct ifa_rbentry *b) +{ + int cmp; + + cmp = b->ifa->ifa_addr->sa_family - a->ifa->ifa_addr->sa_family; + if (cmp != 0) + return cmp; + return memcmp(a->ifa->ifa_addr->sa_data, b->ifa->ifa_addr->sa_data, + MIN(a->ifa->ifa_addr->sa_len, b->ifa->ifa_addr->sa_len) - + offsetof(struct sockaddr, sa_data)); +} diff --git a/usr.sbin/npppd/common/rtev_libevent.c b/usr.sbin/npppd/common/rtev_libevent.c new file mode 100644 index 00000000000..c22fa80970f --- /dev/null +++ b/usr.sbin/npppd/common/rtev_libevent.c @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: rtev_libevent.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/types.h> +#include <sys/socket.h> +#include <net/route.h> +#include <ifaddrs.h> +#include <time.h> +#include <event.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <stdio.h> +#include <stdlib.h> + +#define NO_RTEV_WRAPPER 1 +#include "rtev_local.h" +#include "rtev.h" + +typedef struct _rtev_libevent rtev_libevent; + +static int rtev_libevent_init0 (rtev_libevent *, int, int, int, int); +static void rtev_libevent_fini (rtev_impl *); +static inline time_t get_monosec(void); + +static rtev_libevent rtev_libevent_this; + +/** + * Initialize 'rtev' with libevent. + * + * @param rt_delay_sec wait given time before we work after routing + * event. + * @param send_delay_millisec wait given time each sending. + * @param send_npackets send give number of packets at once. + */ +int +rtev_libevent_init(int rt_delay_sec, int send_delay_millisec, int send_npackets, + int flags) +{ + RTEV_DBG((LOG_DEBUG, "%s(%d,%d,%d)", + __func__, rt_delay_sec, send_delay_millisec, send_npackets)); + return rtev_libevent_init0(&rtev_libevent_this, rt_delay_sec, + send_delay_millisec, send_npackets, flags); +} + +/*********************************************************************** + * private functions + ***********************************************************************/ +static void rtev_libevent_reset_event (rtev_libevent *); +static void rtev_libevent_timer_event (int, short, void *); +static void rtev_libevent_io_event (int, short, void *); + +struct _rtev_libevent { + rtev_impl impl; + int sock; + int nsend; + int p_delay; + int w_delay; /* milli sec */ + int write_ready:1, write_wait:1; + struct event ev, ev_timer; + time_t last_rtupdate; +}; + +static void +rtev_libevent_reset_event(rtev_libevent *_this) +{ + int evmask; + struct timeval tv; + + if (event_initialized(&_this->ev_timer)) + event_del(&_this->ev_timer); + if (event_initialized(&_this->ev)) + event_del(&_this->ev); + + /* + * I/O Event + */ + evmask = EV_READ; + if (_this->write_ready == 0 && rtev_has_write_pending()) + evmask |= EV_WRITE; + event_set(&_this->ev, _this->sock, evmask, rtev_libevent_io_event, + _this); + event_add(&_this->ev, NULL); + RTEV_DBG((DEBUG_LEVEL_2, "%s I/O [%s%s] Wait", __func__, + ((evmask & EV_READ) != 0)? "R" : "", + ((evmask & EV_WRITE) != 0)? "W" : "")); + + /* + * Timer event + */ + if (_this->write_wait != 0 && _this->w_delay > 0) { + RTEV_DBG((DEBUG_LEVEL_2, "%s Timer %f sec", __func__, + _this->w_delay / 1000.0)); + tv.tv_sec = _this->w_delay / 1000; + tv.tv_usec = (_this->w_delay % 1000) * 1000L; + RTEV_ASSERT(tv.tv_usec < 1000000L); + RTEV_ASSERT(tv.tv_usec >= 0L); + evtimer_set(&_this->ev_timer, rtev_libevent_timer_event, _this); + event_add(&_this->ev_timer, &tv); + } else if (_this->last_rtupdate != 0 && _this->p_delay > 0) { + time_t currtime; + + currtime = get_monosec(); + + tv.tv_sec = _this->last_rtupdate + _this->p_delay - currtime; + tv.tv_usec = 0; + if (tv.tv_sec < 0) + tv.tv_sec = 0; + RTEV_DBG((DEBUG_LEVEL_2, "%s Timer %ld sec", __func__, + (long)tv.tv_sec)); + + evtimer_set(&_this->ev_timer, rtev_libevent_timer_event, _this); + event_add(&_this->ev_timer, &tv); + } +} + +static void +rtev_libevent_timer_event(int sock, short evmask, void *ctx) +{ + time_t currtime; + rtev_libevent *_this; + + _this = ctx; + RTEV_DBG((DEBUG_LEVEL_2, "%s", __func__)); + + currtime = get_monosec(); + if (_this->last_rtupdate + _this->p_delay <= currtime) { + _this->impl.base_on_rtevent(&_this->impl); + _this->last_rtupdate = 0; + } + + if (_this->write_wait != 0) { + _this->write_wait = 0; + if (rtev_has_write_pending() && _this->write_ready != 0) { + RTEV_DBG((DEBUG_LEVEL_1, "rt_send() by timer")); + if (_this->impl.base_on_write(&_this->impl, + _this->sock, _this->nsend) != 0) { + log_printf(LOG_INFO, + "sending message to routing socket failed" + ": %m"); + } + _this->write_ready = 0; + _this->write_wait = 1; /* wait again */ + } + } + + rtev_libevent_reset_event(_this); +} + +static void +rtev_libevent_io_event(int sock, short evmask, void *ctx) +{ + char buf[8192]; + struct rt_msghdr *rt_msg; + int sz, rt_updated; + rtev_libevent *_this; + + _this = ctx; + + RTEV_DBG((DEBUG_LEVEL_2, "%s I/O [%s%s] Ready", __func__, + ((evmask & EV_READ) != 0)? "R" : "", + ((evmask & EV_WRITE) != 0)? "W" : "")); + + if ((evmask & EV_WRITE) != 0) { + if (_this->write_wait != 0 && _this->w_delay > 0) { + _this->write_ready = 1; + } else { + RTEV_DBG((DEBUG_LEVEL_1, "rt_send() by event")); + if (_this->impl.base_on_write(&_this->impl, sock, + _this->nsend) != 0) { + log_printf(LOG_INFO, + "sending message to routing socket failed" + ": %m"); + } + _this->write_ready = 0; + _this->write_wait = 1; + } + } + if ((evmask & EV_READ) != 0) { + rt_updated = 0; + while ((sz = recv(sock, buf, sizeof(buf), 0)) > 0) { + rt_msg = (struct rt_msghdr *)buf; + if (rt_msg->rtm_version != RTM_VERSION) + continue; + switch (rt_msg->rtm_type) { + case RTM_ADD: + case RTM_CHANGE: + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_DELETE: + case RTM_IFINFO: + rt_updated++; + break; + } + } + if (sz < 0) + RTEV_ASSERT(errno == EAGAIN); + if (rt_updated) { + if (_this->p_delay <= 0) + rtev_libevent_timer_event(sock, evmask, ctx); + else if (_this->last_rtupdate == 0) + _this->last_rtupdate = get_monosec(); + } + } + rtev_libevent_reset_event(_this); +} + +static void +rtev_libevent_on_write(rtev_impl *impl) +{ + rtev_libevent *_this = impl->impl; + + rtev_libevent_reset_event(_this); +} + +static int +rtev_libevent_init0(rtev_libevent *_this, int rt_delay, int send_delay, + int nsend, int flags) +{ + int sock, fflags, dummy; + + sock = -1; + memset(_this, 0, sizeof(rtev_libevent)); + + _this->impl.impl = _this; + _this->impl.impl_fini = rtev_libevent_fini; + _this->impl.impl_on_write = rtev_libevent_on_write; + + if (rtev_base_init(&_this->impl, flags) != 0) + goto reigai; + + if ((sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) + goto reigai; + + dummy = 0; + if ((fflags = fcntl(sock, F_GETFL, dummy)) < 0) + goto reigai; + + if (fcntl(sock, F_SETFL, fflags | O_NONBLOCK) < 0) + goto reigai; + + _this->sock = sock; + + _this->w_delay = send_delay; + _this->nsend = nsend; + _this->p_delay = rt_delay; + if (rt_delay <= 0) + rtev_libevent_timer_event(0, EV_TIMEOUT, _this); + rtev_libevent_reset_event(_this); + + return 0; +reigai: + if (sock >= 0) + close(sock); + + if (event_initialized(&_this->ev)) + event_del(&_this->ev); + + _this->impl.impl = NULL; + + return -1; +} + +static void +rtev_libevent_fini(rtev_impl *impl) +{ + rtev_libevent *_this = impl->impl; + + if (_this->sock >= 0) + close(_this->sock); + + if (event_initialized(&_this->ev)) + event_del(&_this->ev); + if (event_initialized(&_this->ev_timer)) + event_del(&_this->ev_timer); + _this->sock = -1; +} + +static inline time_t +get_monosec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) + abort(); + return ts.tv_sec; +} diff --git a/usr.sbin/npppd/common/rtev_local.h b/usr.sbin/npppd/common/rtev_local.h new file mode 100644 index 00000000000..b80f974d14f --- /dev/null +++ b/usr.sbin/npppd/common/rtev_local.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef RTEV_LOCAL_H +#define RTEV_LOCAL_H + +#ifdef RTEV_DEBUG +#include "debugutil.h" +#define RTEV_DBG(x) log_printf x +#define RTEV_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define RTEV_ASSERT(cond) +#define RTEV_DBG(x) +#define log_printf syslog +#endif + +typedef struct _rtev_impl rtev_impl; +struct _rtev_impl { + void *impl; + void (*impl_fini)(rtev_impl *); + void (*impl_on_write)(rtev_impl *); + int (*base_on_rtevent)(rtev_impl *); + int (*base_on_write)(rtev_impl *, int, int); + int base_flags; +}; + +int rtev_base_init (rtev_impl *, int); + +#endif diff --git a/usr.sbin/npppd/common/slist.c b/usr.sbin/npppd/common/slist.c new file mode 100644 index 00000000000..7f6c67b05ce --- /dev/null +++ b/usr.sbin/npppd/common/slist.c @@ -0,0 +1,498 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * 任意のポインタに関するリスト操作を提供します。 + */ +/* + * void **list; + * list_size; // list に割り当てたサイズ。 + * last_idx; // 最初のインデックス + * first_idx; // 最後のインデックス + * + * ・first_idx == last_idx は空を示します。 + * ・fist_idx と last_idx は 0 以上 list_size - 1 以下です。 + * ・使っているサイズは、(last_idx - first_idx) % list_size です。 + * list_size まで使ってしまうと、空と区別ができず区別しようとすると複雑 + * になるので、そういう状況を作りません。このため、list に割り当てたサイズ + * のうち 1個分は使いません。 + * ・XXX itr_curr が削除されると、 + */ +#include <sys/types.h> + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "slist.h" + +#define GROW_SIZE 256 +#define PTR_SIZE (sizeof(intptr_t)) + +#ifdef SLIST_DEBUG +#include <stdio.h> +#define SLIST_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nAssertion failure("#cond") at (%s):%s:%d\n", \ + __func__, __FILE__, __LINE__); \ + } +#else +#define SLIST_ASSERT(cond) +#endif + +/** + * 内部のインデックスが範囲内ならば、1 を範囲を越えている場合には 0 を + * を返すマクロです。 + */ +#define VALID_IDX(_list, _idx) \ + (((_list)->first_idx <= (_list)->last_idx) \ + ? (((_list)->first_idx <= (_idx) && (_idx) < (_list)->last_idx)? 1 : 0)\ + : (((_list)->first_idx <= (_idx) || (_idx) < (_list)->last_idx)? 1 : 0)) + +/** インデックスを内部のインデックスに変換します。 */ +#define REAL_IDX(_list, _idx) \ + (((_list)->first_idx + (_idx)) % (_list)->list_size) + +/** 内部のインデックスをインデックスに変換します。 */ +#define VIRT_IDX(_list, _idx) (((_list)->first_idx <= (_idx)) \ + ? (_idx) - (_list)->first_idx \ + : (_list)->list_size - (_list)->first_idx + (_idx)) + +/** インデックスを示すメンバー変数を decrement します */ +#define DECR_IDX(_list, _memb) \ + (_list)->_memb = ((_list)->list_size + --((_list)->_memb)) \ + % (_list)->list_size +/** インデックスを示すメンバー変数を increment します */ +#define INCR_IDX(_list, _memb) \ + (_list)->_memb = (++((_list)->_memb)) % (_list)->list_size + +static int slist_grow (slist *); +static int slist_grow0 (slist *, int); +static __inline void slist_swap0 (slist *, int, int); + +#define itr_is_valid(list) ((list)->itr_next >= 0) +#define itr_invalidate(list) ((list)->itr_next = -1) + +/** 初期化処理を行います */ +void +slist_init(slist *list) +{ + memset(list, 0, sizeof(slist)); + itr_invalidate(list); +} + +/** + * リストのサイズを指定します。1つの要素は内部で利用されるので、必要なサイズ + * + 1 を指定します。サイズは小さくなりません。 + */ +int +slist_set_size(slist *list, int size) +{ + if (size > list->list_size) + return slist_grow0(list, size - list->list_size); + + return 0; +} + +/** 終了化処理を行います */ +void +slist_fini(slist *list) +{ + if (list->list != NULL) + free(list->list); + slist_init(list); +} + +/** このリストの長さ */ +int +slist_length(slist *list) +{ + return + (list->first_idx <= list->last_idx) + ? (list->last_idx - list->first_idx) + : (list->list_size - list->first_idx + list->last_idx); +} + +/** リストがいっぱいの場合に割り当てサイズを成長させます。 */ +static int +slist_grow0(slist *list, int grow_sz) +{ + int size_new; + void **list_new = NULL; + + /* ひとつ追加できるか。できるならそのまま抜ける。*/ + if (slist_length(list) + 1 < list->list_size) + /* list_size == slist_length() という状況は作らない */ + return 0; + + size_new = list->list_size + grow_sz; + if ((list_new = realloc(list->list, PTR_SIZE * size_new)) + == NULL) + return -1; + + memset(&list_new[list->list_size], 0, + PTR_SIZE * (size_new - list->list_size)); + + list->list = list_new; + if (list->last_idx < list->first_idx && list->last_idx >= 0) { + /* + * 空きが真ん中にある状況で右側に空きを作ったので、左側 + * を右にもっていく。 + */ + if (list->last_idx <= grow_sz) { + /* + * 左側を右側にもっていく場合に十分なスペースがある + * のですべて移動 + */ + memmove(&list->list[list->list_size], + &list->list[0], PTR_SIZE * list->last_idx); + list->last_idx = list->list_size + list->last_idx; + } else { + /* 左側をできるかぎり右端に copy */ + memmove(&list->list[list->list_size], + &list->list[0], PTR_SIZE * grow_sz); + /* 左側、copy した分を左にずらす */ + memmove(&list->list[0], &list->list[grow_sz], + PTR_SIZE *(list->last_idx - grow_sz)); + + list->last_idx -= grow_sz; + } + } + list->list_size = size_new; + + return 0; +} + +static int +slist_grow(slist *list) +{ + return slist_grow0(list, GROW_SIZE); +} + +/** リストの末尾に要素を追加します。*/ +void * +slist_add(slist *list, void *item) +{ + if (slist_grow(list) != 0) + return NULL; + + list->list[list->last_idx] = item; + + if (list->itr_next == -2) { + /* the iterator points the last, update it. */ + list->itr_next = list->last_idx; + } + + INCR_IDX(list, last_idx); + + return item; +} + +#define slist_get0(list_, idx) ((list_)->list[REAL_IDX((list_), (idx))]) + +/** リストの末尾に指定したリストの要素全てを追加します */ +int +slist_add_all(slist *list, slist *add_items) +{ + int i, n; + + n = slist_length(add_items); + for (i = 0; i < n; i++) { + if (slist_add(list, slist_get0(add_items, i)) == NULL) + return 1; + } + + return 0; +} + +/** idx番目の要素を返します。*/ +void * +slist_get(slist *list, int idx) +{ + SLIST_ASSERT(idx >= 0); + SLIST_ASSERT(slist_length(list) > idx); + + if (idx < 0 || slist_length(list) <= idx) + return NULL; + + return slist_get0(list, idx); +} + +/** idx番目の要素をセットします。*/ +int +slist_set(slist *list, int idx, void *item) +{ + SLIST_ASSERT(idx >= 0); + SLIST_ASSERT(slist_length(list) > idx); + + if (idx < 0 || slist_length(list) <= idx) + return -1; + + list->list[REAL_IDX(list, idx)] = item; + + return 0; +} + +/** 1番目の要素を削除して取り出します。*/ +void * +slist_remove_first(slist *list) +{ + void *oldVal; + + if (slist_length(list) <= 0) + return NULL; + + oldVal = list->list[list->first_idx]; + + if (itr_is_valid(list) && list->itr_next == list->first_idx) + INCR_IDX(list, itr_next); + + if (!VALID_IDX(list, list->itr_next)) + itr_invalidate(list); + + INCR_IDX(list, first_idx); + + return oldVal; +} + + + + +/** 最後の要素を削除して取り出します。*/ +void * +slist_remove_last(slist *list) +{ + if (slist_length(list) <= 0) + return NULL; + + DECR_IDX(list, last_idx); + if (!VALID_IDX(list, list->itr_next)) + itr_invalidate(list); + + return list->list[list->last_idx]; +} + +/** 全て要素を削除します */ +void +slist_remove_all(slist *list) +{ + void **list0 = list->list; + + slist_init(list); + + list->list = list0; +} + +/* this doesn't check boudary. */ +static __inline void +slist_swap0(slist *list, int m, int n) +{ + void *m0; + + itr_invalidate(list); /* イテレータ無効 */ + + m0 = list->list[REAL_IDX(list, m)]; + list->list[REAL_IDX(list, m)] = list->list[REAL_IDX(list, n)]; + list->list[REAL_IDX(list, n)] = m0; +} + +/** リストの m 番目の要素と n 番目の要素を入れ換えます。 */ +void +slist_swap(slist *list, int m, int n) +{ + int len; + + len = slist_length(list); + SLIST_ASSERT(m >= 0); + SLIST_ASSERT(n >= 0); + SLIST_ASSERT(len > m); + SLIST_ASSERT(len > n); + + if (m < 0 || n < 0) + return; + if (m >= len || n >= len) + return; + + slist_swap0(list, m, n); +} + +/** idx 番目の要素を削除します */ +void * +slist_remove(slist *list, int idx) +{ + int first, last, idx0, reset_itr; + void *oldVal; + + SLIST_ASSERT(idx >= 0); + SLIST_ASSERT(slist_length(list) > idx); + + if (idx < 0 || slist_length(list) <= idx) + return NULL; + + idx0 = REAL_IDX(list, idx); + oldVal = list->list[idx0]; + reset_itr = 0; + + first = -1; + last = -1; + + if (list->itr_next == idx0) { + INCR_IDX(list, itr_next); + if (!VALID_IDX(list, list->itr_next)) + list->itr_next = -2; /* on the last item */ + } + + /* last 側を縮めるか、first 側を縮めるか。*/ + if (list->first_idx < list->last_idx) { + /* いちおう短い方を選択 */ + if (idx0 - list->first_idx < list->last_idx - idx0) { + first = list->first_idx; + INCR_IDX(list, first_idx); + } else { + last = list->last_idx; + DECR_IDX(list, last_idx); + } + } else { + /* + * 0 < last (未使用) first < idx < size なので first 側を縮める + */ + if (list->first_idx <= idx0) { + first = list->first_idx; + INCR_IDX(list, first_idx); + } else { + last = list->last_idx; + DECR_IDX(list, last_idx); + } + } + + /* last側 */ + if (last != -1 && last != 0 && last != idx0) { + + /* idx0 〜 last を左にひとつずらす */ + if (itr_is_valid(list) && + idx0 <= list->itr_next && list->itr_next <= last) { + DECR_IDX(list, itr_next); + if (!VALID_IDX(list, list->itr_next)) + itr_invalidate(list); + } + + memmove(&list->list[idx0], &list->list[idx0 + 1], + (PTR_SIZE) * (last - idx0)); + } + /* first側 */ + if (first != -1 && first != idx0) { + + /* first 〜 idx0 を右にひとつずらす */ + if (itr_is_valid(list) && + first <= list->itr_next && list->itr_next <= idx0) { + INCR_IDX(list, itr_next); + if (!VALID_IDX(list, list->itr_next)) + itr_invalidate(list); + } + + memmove(&list->list[first + 1], &list->list[first], + (PTR_SIZE) * (idx0 - first)); + } + if (list->first_idx == list->last_idx) { + list->first_idx = 0; + list->last_idx = 0; + } + + return oldVal; +} + +/** + * シャッフルします。 + * <p> + * <b>slist_shuffle は random(3) を使ってます。使用前に srandom(3) してく + * ださい。</b></p> + */ +void +slist_shuffle(slist *list) +{ + int i, len; + + len = slist_length(list); + for (i = len; i > 1; i--) + slist_swap0(list, i - 1, (int)(random() % i)); +} + +/** + * イテレータを初期化します。ひとつの slist インスタンスでひとつしか使えません。 + */ +void +slist_itr_first(slist *list) +{ + list->itr_next = list->first_idx; + if (!VALID_IDX(list, list->itr_next)) + itr_invalidate(list); +} + +/** + * イテレータが次の要素に進めるかどうかを返します。 + * @return イテレータが次の要素を返すことができる場合に 1 を返します。 + * 終端に達したか、イテレータが最後まで達したか、リスト構造の変更があって、 + * 続行不能な場合には 0 が返ります。 + */ +int +slist_itr_has_next(slist *list) +{ + if (list->itr_next < 0) + return 0; + return VALID_IDX(list, list->itr_next); +} + +/** イテレータの次の要素を取り出しつつ、次の要素に進めます。 */ +void * +slist_itr_next(slist *list) +{ + void *rval; + + if (!itr_is_valid(list)) + return NULL; + SLIST_ASSERT(VALID_IDX(list, list->itr_next)); + + if (list->list == NULL) + return NULL; + + rval = list->list[list->itr_next]; + list->itr_curr = list->itr_next; + INCR_IDX(list, itr_next); + + if (!VALID_IDX(list, list->itr_next)) + list->itr_next = -2; /* on the last item */ + + return rval; +} + +/** イテレータの現在の要素を削除します */ +void * +slist_itr_remove(slist *list) +{ + SLIST_ASSERT(list != NULL); + + return slist_remove(list, VIRT_IDX(list, list->itr_curr)); +} diff --git a/usr.sbin/npppd/common/slist.h b/usr.sbin/npppd/common/slist.h new file mode 100644 index 00000000000..10fea5c8929 --- /dev/null +++ b/usr.sbin/npppd/common/slist.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef SLIST_H +#define SLIST_H 1 + +typedef struct { + void **list; + int last_idx; + int first_idx; + int list_size; + + int itr_next; + int itr_curr; +} slist; + +#ifdef __cplusplus +extern "C" { +#endif + +void slist_init (slist *); +void slist_fini (slist *); +int slist_length (slist *); +int slist_set_size (slist *, int); +void *slist_add (slist *, void *); +int slist_add_all (slist *, slist *); +void slist_remove_all (slist *); +void *slist_get (slist *, int); +int slist_set (slist *, int, void *); +void *slist_remove_first (slist *); +void *slist_remove_last (slist *); +void slist_swap (slist *, int, int); +void *slist_remove (slist *, int); +/* slist_shuffle は random(3) を使ってます。使用前に srandom(3) してください。*/ +void slist_shuffle (slist *); +void slist_itr_first (slist *); +int slist_itr_has_next (slist *); +void *slist_itr_next (slist *); +void *slist_itr_remove (slist *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/common/slist_test.c b/usr.sbin/npppd/common/slist_test.c new file mode 100644 index 00000000000..f7ac334be69 --- /dev/null +++ b/usr.sbin/npppd/common/slist_test.c @@ -0,0 +1,590 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + + cc -g -Wall -o slist_test slist_test.c slist.c + ./slist_test + + + */ +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include "slist.h" + +#define TEST(f) \ + { \ + printf("%-10s .. ", #f); \ + f(); \ + printf("ok\n"); \ + } + +#define ASSERT(x) \ + if (!(x)) { \ + fprintf(stderr, \ + "\nASSERT(%s) failed on %s() at %s:%d.\n" \ + , #x, __func__, __FILE__, __LINE__); \ + dump(l); \ + abort(); \ + } + +static void +dump(slist *l) +{ + int i; + + fprintf(stderr, + "\tl->itr_curr = %d\n" + "\tl->itr_next = %d\n" + "\tl->first_idx = %d\n" + "\tl->last_idx = %d\n" + "\tl->list_size = %d\n" + , l->itr_curr, l->itr_next, l->first_idx, l->last_idx + , l->list_size); + for (i = 0; i < slist_length(l); i++) { + if ((i % 16) == 0) + fprintf(stderr, "%08x ", i); + fprintf(stderr, " %3d", (int)slist_get(l, i)); + if ((i % 16) == 7) + fprintf(stderr, " -"); + if ((i % 16) == 15) + fprintf(stderr, "\n"); + } + if ((i % 16) != 0) + fprintf(stderr, "\n"); +} + +// まんなかに空きの場合に削除系のテスト +static void +test_01a() +{ + int i, f; + slist sl; + slist *l = &sl; + + slist_init(&sl); + slist_add(&sl, (void *)1); + + ASSERT(sl.list_size == 256); + +#define SETUP() \ + { \ + l->last_idx = 64; \ + l->first_idx = 192; \ + for (i = 0; i < slist_length(l); i++) { \ + slist_set(l, i, (void *)i); \ + } \ + } + + // 先頭要素削除 + SETUP(); + f = 0; + while (slist_length(l) > 0) { + slist_remove(l, 0); + f++; + for (i = 0; i < slist_length(l); i++) { + ASSERT((int)slist_get(l, i) == i + f); + } + } + + // 最終要素削除 + SETUP(); + while (slist_length(l) > 0) { + slist_remove(l, slist_length(l) - 1); + for (i = 0; i < slist_length(l); i++) { + ASSERT((int)slist_get(l, i) == i); + } + } + // 最終要素-1削除 + SETUP(); + while (slist_length(l) > 1) { + slist_remove(l, slist_length(l) - 2); + for (i = 0; i < slist_length(l) - 1; i++) { + ASSERT((int)slist_get(l, i) == i); + } + if (slist_length(l) > 0) { + ASSERT((int)slist_get(l, slist_length(l) - 1) == 127); + } + } + slist_remove(l, slist_length(l) - 1); + ASSERT(slist_length(l) == 0); +} + +static void +test_01() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(&sl); + + + for (i = 0; i < 255; i++) { + slist_add(&sl, (void *)i); + } + for (i = 0; i < 128; i++) { + slist_remove_first(&sl); + } + for (i = 0; i < 128; i++) { + slist_add(&sl, (void *)(i + 255)); + } + ASSERT((int)slist_get(&sl, 127) == 255); + ASSERT((int)slist_get(&sl, 254) == 129 + 253); + ASSERT((int)slist_length(&sl) == 255); + + //dump(&sl); + //printf("==\n"); + slist_add(&sl, (void *)(128 + 255)); + ASSERT((int)slist_get(&sl, 127) == 255); + //ASSERT((int)slist_get(&sl, 255) == 128 + 255); + ASSERT((int)slist_length(&sl) == 256); + //dump(&sl); +} + +static void +test_02() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(&sl); + + + // 内部配置が、左側に 300 個、右側に 211 個になるように配置 + for (i = 0; i < 511; i++) + slist_add(&sl, (void *)i); + for (i = 0; i <= 300; i++) + slist_remove_first(&sl); + for (i = 0; i <= 300; i++) + slist_add(&sl, (void *)i); + + + // index 番号になるように再度割り当て + for (i = 0; i < slist_length(&sl); i++) + slist_set(&sl, i, (void *)(i + 1)); + + ASSERT(slist_length(&sl) == 511); //論理サイズは511 + ASSERT((int)sl.list[511] == 211); //右端が 211番目 + ASSERT((int)sl.list[0] == 212); //左端が 212番目 + ASSERT(sl.list_size == 512); //物理サイズは 512 + + slist_add(&sl, (void *)512); // 512番めを追加 + + ASSERT(sl.list_size == 768); //物理サイズが拡大 + ASSERT(slist_length(&sl) == 512); //論理サイズは512 + ASSERT((int)sl.list[511] == 211); //繋め + ASSERT((int)sl.list[512] == 212); //繋め + ASSERT((int)sl.list[767] == 467); //右端が 467番目 + ASSERT((int)sl.list[0] == 468); //左端が 468番目 + + //全部チェック + for (i = 0; i < slist_length(&sl); i++) + ASSERT((int)slist_get(&sl, i) == i + 1); // チェック +} + +static void +test_03() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(&sl); + slist_add(&sl, (void *)1); + + for (i = 0; i < 255; i++) { + slist_add(&sl, (void *)1); + ASSERT(sl.last_idx >= 0 && sl.last_idx < sl.list_size); + slist_remove_first(&sl); + ASSERT(sl.last_idx >= 0 && sl.last_idx < sl.list_size); + } + slist_remove(&sl, 0); + ASSERT(slist_length(&sl) == 0); + //dump(&sl); + //TEST(test_02); +} + +static void +test_itr_subr_01(slist *l) +{ + int i; + + for (i = 0; i < slist_length(l); i++) + slist_set(l, i, (void *)(i + 1)); + + slist_itr_first(l); + ASSERT((int)slist_itr_next(l) == 1); // 普通にイテレート + ASSERT((int)slist_itr_next(l) == 2); // 普通にイテレート + slist_remove(l, 2); // next を削除 + // "3" が削除 + ASSERT((int)slist_itr_next(l) == 4); // 削除したものはスキップ + slist_remove(l, 1); // 通りすぎたところを削除 + // "2" を削除 + ASSERT((int)slist_itr_next(l) == 5); // 影響なし + ASSERT((int)slist_get(l, 0) == 1); // 削除確認 + ASSERT((int)slist_get(l, 1) == 4); // 削除確認 + ASSERT((int)slist_get(l, 2) == 5); // 削除確認 + + // 255 アイテム中 2 個削除し、4回イテレートし、1回の削除は通りすぎ + // たあとなので、残り 250回 + + + for (i = 0; i < 249; i++) + ASSERT(slist_itr_next(l) != NULL); + ASSERT(slist_itr_next(l) != NULL); + ASSERT(slist_itr_next(l) == NULL); + + // 上記と同じだが、最後を取り出す前に削除 + + // リセット (253アイテム) + for (i = 0; i < slist_length(l); i++) + slist_set(l, i, (void *)(i + 1)); + slist_itr_first(l); + + ASSERT(slist_length(l) == 253); + + for (i = 0; i < 252; i++) + ASSERT(slist_itr_next(l) != NULL); + + slist_remove(l, 252); + ASSERT(slist_itr_next(l) == NULL); // 最後を指してたけど、NULL + + slist_itr_first(l); + while (slist_length(l) > 0) + slist_remove_first(l); + ASSERT(slist_length(l) == 0); + ASSERT(slist_itr_next(l) == NULL); +} + +static void +test_04() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(&sl); + for (i = 0; i < 255; i++) + slist_add(&sl, (void *)(i + 1)); + + test_itr_subr_01(&sl); + + for (i = 0; i < 256; i++) { + // ローテーションして、どんな物理配置でも成功すること確認 + sl.first_idx = i; + sl.last_idx = sl.first_idx + 255; + sl.last_idx %= sl.list_size; + ASSERT(slist_length(&sl) == 255); + test_itr_subr_01(&sl); + } +} + +// 物理配置の一番最後の要素を削除しても、大丈夫か。 +static void +test_05() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(&sl); + // ぎりぎりまで追加 + for (i = 0; i < 255; i++) { + slist_add(&sl, (void *)i); + } + // 254 個削除 + for (i = 0; i < 254; i++) { + slist_remove_first(&sl); + } + slist_set(l, 0, (void *)0); + // 7個追加 + for (i = 0; i < 8; i++) { + slist_add(&sl, (void *)i + 1); + } + ASSERT(sl.first_idx == 254); + ASSERT(sl.last_idx == 7); + + slist_remove(l, 0); + ASSERT((int)slist_get(l, 0) == 1); + ASSERT((int)slist_get(l, 1) == 2); + ASSERT((int)slist_get(l, 2) == 3); + ASSERT((int)slist_get(l, 3) == 4); + ASSERT((int)slist_get(l, 4) == 5); + ASSERT((int)slist_get(l, 5) == 6); + ASSERT((int)slist_get(l, 6) == 7); + ASSERT((int)slist_get(l, 7) == 8); + ASSERT(l->first_idx == 255); + + slist_remove(l, 0); + ASSERT((int)slist_get(l, 0) == 2); + ASSERT((int)slist_get(l, 1) == 3); + ASSERT((int)slist_get(l, 2) == 4); + ASSERT((int)slist_get(l, 3) == 5); + ASSERT((int)slist_get(l, 4) == 6); + ASSERT((int)slist_get(l, 5) == 7); + ASSERT((int)slist_get(l, 6) == 8); + ASSERT(l->first_idx == 0); +} + +static void +test_06() +{ + int i, j; + slist sl; + slist *l = &sl; + + slist_init(l); + for (i = 0; i < 255; i++) + slist_add(l, (void *)i); + + i = 255; + + for (slist_itr_first(l); slist_itr_has_next(l); ) { + ASSERT(slist_length(l) == i); + slist_itr_next(l); + ASSERT((int)slist_itr_remove(l) == 255 - i); + ASSERT(slist_length(l) == i - 1); + for (j = i; j < slist_length(l); j++) + ASSERT((int)slist_get(l, j) == i + j); + i--; + } +} + +static void +test_07() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(l); + slist_add(l, (void *)1); + slist_remove_first(l); + l->first_idx = 120; + l->last_idx = 120; + for (i = 0; i < 255; i++) + slist_add(l, (void *)i); + + + for (i = 0, slist_itr_first(l); slist_itr_has_next(l); i++) { + ASSERT((int)slist_itr_next(l) == i); + if (i > 200) + ASSERT((int)slist_itr_remove(l) == i); + } +} + +static void +test_08() +{ + //int i, x; + slist sl; + slist *l = &sl; + + slist_init(l); + slist_set_size(l, 4); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + slist_add(l, (void *)3); + + /* [1, 2, 3] */ + + slist_itr_first(l); + slist_itr_has_next(l); + slist_itr_next(l); + slist_itr_remove(l); + /* [2, 3] */ + + slist_add(l, (void *)4); + /* [2, 3, 4] */ + ASSERT((int)slist_get(l, 0) == 2); + ASSERT((int)slist_get(l, 1) == 3); + ASSERT((int)slist_get(l, 2) == 4); + slist_add(l, (void *)5); + + /* [2, 3, 4, 5] */ + ASSERT((int)slist_get(l, 0) == 2); + ASSERT((int)slist_get(l, 1) == 3); + ASSERT((int)slist_get(l, 2) == 4); + ASSERT((int)slist_get(l, 3) == 5); + + //dump(l); +} + +static void +test_09() +{ + slist sl; + slist *l = &sl; + + /* + * #1 + */ + slist_init(l); + slist_set_size(l, 3); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + slist_add(l, (void *)3); + + slist_itr_first(l); + ASSERT((int)slist_itr_next(l) == 1); /* 1 */ + ASSERT((int)slist_itr_next(l) == 2); /* 2 */ + ASSERT((int)slist_itr_next(l) == 3); /* 3 */ + /* reaches the last */ + slist_add(l, (void *)4); /* add a new item */ + ASSERT(slist_itr_has_next(l)); /* iterates the new*/ + ASSERT((int)slist_itr_next(l) == 4); + slist_fini(l); + + + /* + * #2 + */ + slist_init(l); + slist_set_size(l, 3); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + slist_add(l, (void *)3); + + slist_itr_first(l); + ASSERT((int)slist_itr_next(l) == 1); /* 1 */ + ASSERT((int)slist_itr_next(l) == 2); /* 2 */ + ASSERT((int)slist_itr_next(l) == 3); /* 3 */ + /* reaches the last */ + //dump(l); + slist_itr_remove(l); /* and remove the last*/ + //dump(l); + slist_add(l, (void *)4); /* add 4 (new last)*/ + //dump(l); + ASSERT(slist_itr_has_next(l)); /* */ + ASSERT((int)slist_itr_next(l) == 4); /* 4 */ + slist_fini(l); + + /* + * #3 + */ + slist_init(l); + slist_set_size(l, 3); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + slist_add(l, (void *)3); + + slist_itr_first(l); + ASSERT((int)slist_itr_next(l) == 1); /* 1 */ + ASSERT((int)slist_itr_next(l) == 2); /* 2 */ + ASSERT((int)slist_itr_next(l) == 3); /* 3 */ + /* reaches the last */ + slist_add(l, (void *)4); /* add a new */ + slist_itr_remove(l); + ASSERT(slist_itr_has_next(l)); + ASSERT((int)slist_itr_next(l) == 4); /* 4 */ + slist_fini(l); + + /* + * #4 - remove iterator's next and it is the last + */ + slist_init(l); + slist_set_size(l, 3); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + slist_add(l, (void *)3); + + slist_itr_first(l); + ASSERT((int)slist_itr_next(l) == 1); /* 1 */ + ASSERT((int)slist_itr_next(l) == 2); /* 2 */ + slist_remove(l, 2); /* remove the next */ + slist_add(l, (void *)4); /* add the new next */ + ASSERT(slist_itr_has_next(l)); /* iterates the new */ + ASSERT((int)slist_itr_next(l) == 4); + slist_fini(l); +} +static void +test_10() +{ + int i; + slist sl; + slist *l = &sl; + + slist_init(l); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + slist_add(l, (void *)3); + slist_itr_first(l); + ASSERT((int)slist_itr_next(l) == 1); + ASSERT((int)slist_itr_next(l) == 2); + for (i = 4; i < 10000; i++) { + ASSERT(slist_itr_has_next(l)); + ASSERT((int)slist_itr_next(l) == i - 1); + if (i % 3 == 1) + slist_add(l, (void *)i); + if (i % 3 == 0) + ASSERT((int)slist_itr_remove(l) == i - 1); + if (i % 3 != 1) + slist_add(l, (void *)i); + } + slist_itr_first(l); + while (slist_itr_has_next(l)) { + slist_itr_next(l); + slist_itr_remove(l); + } + ASSERT((int)slist_length(l) == 0); + + slist_fini(l); +} + +static void +test_11() +{ + slist sl; + slist *l = &sl; + + slist_init(l); + slist_add(l, (void *)1); + slist_add(l, (void *)2); + ASSERT((int)slist_remove_last(l) == 2); + ASSERT((int)slist_length(l) == 1); + ASSERT((int)slist_remove_last(l) == 1); + ASSERT((int)slist_length(l) == 0); +} + +int +main(int argc, char *argv[]) +{ + TEST(test_01); + TEST(test_01a); + TEST(test_02); + TEST(test_03); + TEST(test_04); + TEST(test_05); + TEST(test_06); + TEST(test_07); + TEST(test_08); + TEST(test_09); + TEST(test_10); + TEST(test_11); + return 0; +} diff --git a/usr.sbin/npppd/common/time_utils.c b/usr.sbin/npppd/common/time_utils.c new file mode 100644 index 00000000000..2271aa07fbd --- /dev/null +++ b/usr.sbin/npppd/common/time_utils.c @@ -0,0 +1,26 @@ +/*- + * Copyright 2008 Internet Initiative Japan Inc. + */ +#include <sys/types.h> +#include <time.h> +#include <stdint.h> + +#include "time_utils.h" + +/** + * Return the value of system timer in nano seconds. + * Returns INT64_MIN on error. + */ +int64_t +get_nanotime(void) +{ + struct timespec ts; + int64_t rval; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) + return INT64_MIN; + rval = (int64_t)ts.tv_sec * (int64_t)1000000000LL; + rval = rval + (int64_t)ts.tv_nsec; + + return rval; +} diff --git a/usr.sbin/npppd/common/time_utils.h b/usr.sbin/npppd/common/time_utils.h new file mode 100644 index 00000000000..8adc05e4d1e --- /dev/null +++ b/usr.sbin/npppd/common/time_utils.h @@ -0,0 +1,19 @@ +/* $Id: time_utils.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#ifndef TIME_UTIL_H +#define TIME_UTIL_H 1 + + +#define get_monosec() ((time_t)(get_nanotime() / 1000000000LL)) + +#ifdef __cplusplus +extern "C" { +#endif + +int64_t get_nanotime (void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/l2tp/l2tp.h b/usr.sbin/npppd/l2tp/l2tp.h new file mode 100644 index 00000000000..c3910f91010 --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tp.h @@ -0,0 +1,485 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef L2TP_H +#define L2TP_H 1 +/*@file + * L2TPモジュールヘッダファイル + */ +/* $Id: l2tp.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ + +/************************************************************************ + * プロトコル上の定数 + ************************************************************************/ + +#define L2TP_RFC2661_VERSION 1 +#define L2TP_RFC2661_REVISION 0 +#define L2TP_AVP_MAXSIZ 1024 + +/* ヘッダ */ + +#define L2TP_HEADER_FLAG_TOM 0x8000 +#define L2TP_HEADER_FLAG_LENGTH 0x4000 +#define L2TP_HEADER_FLAG_SEQUENCE 0x0800 +#define L2TP_HEADER_FLAG_OFFSET 0x0200 +#define L2TP_HEADER_FLAG_PRIORITY 0x0100 +#define L2TP_HEADER_FLAG_VERSION_MASK 0x000f +#define L2TP_HEADER_VERSION_RFC2661 0x02 + +/* AVP Atrribute Types */ + +/* RFC 2661 */ +#define L2TP_AVP_TYPE_MESSAGE_TYPE 0 +#define L2TP_AVP_TYPE_RESULT_CODE 1 +#define L2TP_AVP_TYPE_PROTOCOL_VERSION 2 +#define L2TP_AVP_TYPE_FRAMING_CAPABILITIES 3 +#define L2TP_AVP_TYPE_BEARER_CAPABILITIES 4 +#define L2TP_AVP_TYPE_TIE_BREAKER 5 +#define L2TP_AVP_TYPE_FIRMWARE_REVISION 6 +#define L2TP_AVP_TYPE_HOST_NAME 7 +#define L2TP_AVP_TYPE_VENDOR_NAME 8 +#define L2TP_AVP_TYPE_ASSINGED_TUNNEL_ID 9 +#define L2TP_AVP_TYPE_RECV_WINDOW_SIZE 10 +#define L2TP_AVP_TYPE_CHALLENGE 11 +#define L2TP_AVP_TYPE_CAUSE_CODE 12 +#define L2TP_AVP_TYPE_CHALLENGE_RESPONSE 13 +#define L2TP_AVP_TYPE_ASSIGNED_SESSION_ID 14 +#define L2TP_AVP_TYPE_CALL_SERIAL_NUMBER 15 +#define L2TP_AVP_TYPE_MINIMUM_BPS 16 +#define L2TP_AVP_TYPE_MAXIMUM_BPS 17 +#define L2TP_AVP_TYPE_BEARER_TYPE 18 +#define L2TP_AVP_TYPE_FRAMING_TYPE 19 +#define L2TP_AVP_TYPE_CALLED_NUMBER 21 +#define L2TP_AVP_TYPE_CALLING_NUMBER 22 +#define L2TP_AVP_TYPE_SUB_ADDRESS 23 +#define L2TP_AVP_TYPE_TX_CONNECT_SPEED 24 + +#define L2TP_AVP_TYPE_PHYSICAL_CHANNEL_ID 25 +#define L2TP_AVP_TYPE_INITIAL_RECV_LCP_CONFREQ 26 +#define L2TP_AVP_TYPE_LAST_SENT_LCP_CONFREQ 27 +#define L2TP_AVP_TYPE_LAST_RECV_LCP_CONFREQ 28 +#define L2TP_AVP_TYPE_PROXY_AUTHEN_TYPE 29 +#define L2TP_AVP_TYPE_PROXY_AUTHEN_NAME 30 +#define L2TP_AVP_TYPE_PROXY_AUTHEN_CHALLENGE 31 +#define L2TP_AVP_TYPE_PROXY_AUTHEN_ID 32 +#define L2TP_AVP_TYPE_PROXY_AUTHEN_RESPONSE 33 +#define L2TP_AVP_TYPE_CALL_ERRORS 34 +#define L2TP_AVP_TYPE_ACCM 35 +#define L2TP_AVP_TYPE_RANDOM_VECTOR 36 +#define L2TP_AVP_TYPE_PRIVATE_GROUP_ID 37 +#define L2TP_AVP_TYPE_RX_CONNECT_SPEED 38 +#define L2TP_AVP_TYPE_SEQUENCING_REQUIRED 39 + + +/* RFC 3301 */ +#define L2TP_AVP_TYPE_TX_MINIMUM 40 +#define L2TP_AVP_TYPE_CALLING_SUB_ADDRESS 44 + +/* RFC 3145 */ +#define L2TP_AVP_TYPE_PPP_DISCONNECT_CAUSE_CODE 46 + +/* RFC 3308 */ +#define L2TP_AVP_TYPE_CCDS 47 +#define L2TP_AVP_TYPE_SDS 48 + +/* RFC 3437 */ +#define L2TP_AVP_TYPE_LCP_WANT_OPTIONS 49 +#define L2TP_AVP_TYPE_LCP_ALLOW_OPTIONS 50 +#define L2TP_AVP_TYPE_LNS_LAST_SENT_LCP_CONFREQ 51 +#define L2TP_AVP_TYPE_LNS_LAST_RECV_LCP_CONFREQ 52 + +/* RFC 3573 */ +#define L2TP_AVP_TYPE_MODEM_ON_HOLD_CAPABLE 53 +#define L2TP_AVP_TYPE_MODEM_ON_HOLD_STATUS 54 + +/* RFC 3817 */ +#define L2TP_AVP_TYPE_PPPOE_RELAY 55 +#define L2TP_AVP_TYPE_PPPOE_RELAY_RESP_CAP 56 +#define L2TP_AVP_TYPE_PPPOE_RELAY_FORW_CAP 57 + +/* No RFC yet */ +#define L2TP_AVP_TYPE_EXTENDED_VENDOR_ID 58 +#define L2TP_AVP_TYPE_PSEUDOWIRE_CAP_LIST 62 +#define L2TP_AVP_TYPE_LOCAL_SESSION_ID 63 +#define L2TP_AVP_TYPE_REMOTE_SESSION_ID 64 +#define L2TP_AVP_TYPE_ASSIGNED_COOKIE 65 +#define L2TP_AVP_TYPE_REMOTE_END_ID 66 +#define L2TP_AVP_TYPE_APPLICATION_CODE 67 +#define L2TP_AVP_TYPE_PSEUDOWIRE_TYPE 68 +#define L2TP_AVP_TYPE_L2_SPECIFIC_SUBLAYER 69 +#define L2TP_AVP_TYPE_DATA_SEQUENCING 70 +#define L2TP_AVP_TYPE_CIRCUIT_STATUS 71 +#define L2TP_AVP_TYPE_PREFERRED_LANGUAGE 72 +#define L2TP_AVP_TYPE_CTRL_MSG_AUTH_NONCE 73 +/* #define L2TP_AVP_TYPE_TX_CONNECT_SPEED 74 */ +/* #define L2TP_AVP_TYPE_RX_CONNECT_SPEED 75 */ +#define L2TP_AVP_TYPE_FAILOVER_CAPABILITY 76 +#define L2TP_AVP_TYPE_TUNNEL_RECOVERY 77 +#define L2TP_AVP_TYPE_SUGGESTED_CTRL_SEQUENCE 78 +#define L2TP_AVP_TYPE_FAILOVER_SESSION_STATE 79 + +/* RFC 4045 */ +#define L2TP_AVP_TYPE_MULTICAST_CAPABILITY 80 +#define L2TP_AVP_TYPE_NEW_OUTGOING_SESSIONS 81 +#define L2TP_AVP_TYPE_NEW_OUTGOING_SESSIONS_ACK 82 +#define L2TP_AVP_TYPE_WITHDRAW_OUTGOING_SESSIONS 83 +#define L2TP_AVP_TYPE_MULTICAST_PACKETS_PRIORITY 84 + +/* Control Message Type */ + +#define L2TP_AVP_MESSAGE_TYPE_SCCRQ 1 +#define L2TP_AVP_MESSAGE_TYPE_SCCRP 2 +#define L2TP_AVP_MESSAGE_TYPE_SCCCN 3 +#define L2TP_AVP_MESSAGE_TYPE_StopCCN 4 +#define L2TP_AVP_MESSAGE_TYPE_HELLO 6 +#define L2TP_AVP_MESSAGE_TYPE_OCRQ 7 +#define L2TP_AVP_MESSAGE_TYPE_OCRP 8 +#define L2TP_AVP_MESSAGE_TYPE_OCCN 9 +#define L2TP_AVP_MESSAGE_TYPE_ICRQ 10 +#define L2TP_AVP_MESSAGE_TYPE_ICRP 11 +#define L2TP_AVP_MESSAGE_TYPE_ICCN 12 +#define L2TP_AVP_MESSAGE_TYPE_CDN 14 + +#define L2TP_FRAMING_CAP_FLAGS_SYNC 0x00000001 +#define L2TP_FRAMING_CAP_FLAGS_ASYNC 0x00000002 +#define L2TP_BEARER_CAP_FLAGS_DIGITAL 0x00000001 +#define L2TP_BEARER_CAP_FLAGS_ANALOG 0x00000002 + +/* + * RFC2661 の pp.19 〜 pp.22 の定数 + * ラベル名は不適切かも。 + */ +#define L2TP_STOP_CCN_RCODE_GENERAL 1 +#define L2TP_STOP_CCN_RCODE_GENERAL_ERROR 2 +#define L2TP_STOP_CCN_RCODE_ALREADY_EXISTS 3 +#define L2TP_STOP_CCN_RCODE_UNAUTHORIZED 4 +#define L2TP_STOP_CCN_RCODE_BAD_PROTOCOL_VERSION 5 +#define L2TP_STOP_CCN_RCODE_SHUTTING_DOWN 6 +#define L2TP_STOP_CCN_RCODE_FSM_ERROR 7 + +#define L2TP_CDN_RCODE_LOST_CARRIER 1 +#define L2TP_CDN_RCODE_ERROR_CODE 2 +#define L2TP_CDN_RCODE_ADMINISTRATIVE_REASON 3 +#define L2TP_CDN_RCODE_TEMP_NOT_AVALIABLE 4 +#define L2TP_CDN_RCODE_PERM_NOT_AVALIABLE 5 +#define L2TP_CDN_RCODE_INVALID_DESTINATION 6 +#define L2TP_CDN_RCODE_NO_CARRIER 7 +#define L2TP_CDN_RCODE_BUSY 8 +#define L2TP_CDN_RCODE_NO_DIALTONE 9 +#define L2TP_CDN_RCODE_CALL_TIMEOUT_BY_LAC 10 +#define L2TP_CDN_RCODE_NO_FRAMING_DETECTED 11 + +#define L2TP_ECODE_NO_CONTROL_CONNECTION 1 +#define L2TP_ECODE_WRONG_LENGTH 2 +#define L2TP_ECODE_INVALID_MESSAGE 3 +#define L2TP_ECODE_NO_RESOURCE 4 +#define L2TP_ECODE_INVALID_SESSION_ID 5 +#define L2TP_ECODE_GENERIC_ERROR 6 +#define L2TP_ECODE_TRY_ANOTHER 7 +#define L2TP_ECODE_UNKNOWN_MANDATORY_AVP 8 + +/* Proxy Authen Type */ +#define L2TP_AUTH_TYPE_RESERVED 0 +#define L2TP_AUTH_TYPE_TEXUAL 1 +#define L2TP_AUTH_TYPE_PPP_CHAP 2 +#define L2TP_AUTH_TYPE_PPP_PAP 3 +#define L2TP_AUTH_TYPE_NO_AUTH 4 +#define L2TP_AUTH_TYPE_MS_CHAP_V1 5 + +/************************************************************************ + * この実装の定数 + ************************************************************************/ + +#define L2TPD_BACKLOG 16 +#define L2TPD_TUNNEL_HASH_SIZ 127 +#define L2TPD_SND_BUFSIZ 2048 +#define L2TPD_DEFAULT_SEND_WINSZ 4 +#define L2TPD_DEFAULT_LAYER2_LABEL "L2TP" +#define L2TPD_DIALIN_LAYER2_LABEL "DialIn" +#define L2TPD_CONFIG_BUFSIZ 65535 +#define L2TP_CTRL_WINDOW_SIZE 8 +#ifndef L2TPD_VENDOR_NAME +#define L2TPD_VENDOR_NAME "IIJ" +#endif +#define L2TPD_DEFAULT_UDP_PORT 1701 + +/** アドレスは最大何個 bind 可能か。*/ +#ifndef L2TP_NLISTENER +#define L2TP_NLISTENER 6 +#endif + +/* + * デーモンの状態 + */ +#define L2TPD_STATE_INIT 0 +#define L2TPD_STATE_RUNNING 1 +#define L2TPD_STATE_SHUTTING_DOWN 2 +#define L2TPD_STATE_STOPPED 3 + +/* + * コントロール接続の状態 + */ +#define L2TP_CTRL_STATE_IDLE 0 +#define L2TP_CTRL_STATE_WAIT_CTL_CONN 1 +#define L2TP_CTRL_STATE_WAIT_CTL_REPLY 2 +#define L2TP_CTRL_STATE_ESTABLISHED 3 +#define L2TP_CTRL_STATE_CLEANUP_WAIT 4 + +/* + * コールの状態 + */ +#define L2TP_CALL_STATE_IDLE 0 +#define L2TP_CALL_STATE_WAIT_CONN 1 +#define L2TP_CALL_STATE_ESTABLISHED 2 +#define L2TP_CALL_STATE_CLEANUP_WAIT 3 + +/* + * タイムアウト関連 + */ +#define L2TP_CTRL_CTRL_PKT_TIMEOUT 12 +/** 最初の Call を待つ時間 */ +#define L2TP_CTRL_WAIT_CALL_TIMEOUT 16 +#define L2TP_CTRL_CLEANUP_WAIT_TIME 3 +#define L2TP_CTRL_DEFAULT_HELLO_INTERVAL 60 +#define L2TP_CTRL_DEFAULT_HELLO_TIMEOUT 30 + +#define L2TPD_SHUTDOWN_TIMEOUT 5 + +/** L2TPデーモンが停止したかどうかを返します。 */ +#define l2tpd_is_stopped(l2tpd) \ + (((l2tpd)->state != L2TPD_STATE_SHUTTING_DOWN && \ + (l2tpd)->state != L2TPD_STATE_RUNNING)? 1 : 0) + +/** L2TPデーモンが停止処理中かどうかを返します。 */ +#define l2tpd_is_shutting_down(l2tpd) \ + (((l2tpd)->state == L2TPD_STATE_SHUTTING_DOWN)? 1 : 0) + +/** l2tp_ctrl から、リスナーの物理層のラベルを取り出すマクロ */ +#define L2TP_CTRL_LISTENER_LABEL(ctrl) \ + ((l2tpd_listener *)slist_get(&(ctrl)->l2tpd->listener, \ + (ctrl)->listener_index))->phy_label + + +/** L2TP のデーモンを示す型。*/ +struct _l2tpd; + +typedef struct _l2tpd_listener { + /** イベントコンテキスト */ + struct event ev_sock; + /** L2TPD 自身 */ + struct _l2tpd *self; + /** インデックス番号 */ + uint16_t index; + /** 有効/無効 */ + uint16_t enabled; + /** 待ち受けソケット */ + int sock; + /** 待ち受けアドレス UDP */ + struct sockaddr_in bind_sin; + /** 物理層のラベル */ + char phy_label[16]; +} l2tpd_listener; + +/** L2TP のデーモンを示す型。*/ +typedef struct _l2tpd { + /** タイムアウトイベントコンテキスト */ + struct event ev_timeout; + /** インスタンスの ID */ + unsigned id; + /** 待ち受けリスト */ + slist listener; + /** ステータス */ + int state; + /** トンネル ID と {@link ::_l2tp_ctrl L2TP コントロール} のマップ */ + hash_table *ctrl_map; + + /** 接続を許可するIPv4ネットワーク */ + struct in_addr_range *ip4_allow; + + /** デフォルトのホスト名 */ + char default_hostname[80]; + + /** 設定 */ + struct properties *config; + + /** フラグ */ + uint32_t + require_ipsec:1, + purge_ipsec_sa:1, + ctrl_in_pktdump:1, + ctrl_out_pktdump:1, + data_in_pktdump:1, + data_out_pktdump:1, + phy_label_with_ifname:1; +} l2tpd; + +/** L2TP コントロール接続を示す型。*/ +typedef struct _l2tp_ctrl { + struct event ev_timeout; + /** ID */ + unsigned id; + /** 親 L2TPD */ + l2tpd *l2tpd; + /** リスナー インデックス番号 */ + uint16_t listener_index; + /** 状態 */ + int state; + /** トンネルId。 */ + int tunnel_id; + /** Window サイズ */ + int winsz; + /** 先方のトンネルId */ + int peer_tunnel_id; + /** 先方の Window サイズ */ + int peer_winsz; + /** 次の確認応答 */ + uint16_t snd_una; + /** 次の送信シーケンス番号 */ + uint16_t snd_nxt; + /** 受信シーケンス番号 */ + uint16_t rcv_nxt; + /** 先方のアドレス*/ + struct sockaddr_storage peer; + /** こちらのアドレス */ + struct sockaddr_storage sock; + /** IPSEC NAT-T SA クッキー */ + void *sa_cookie; + /** 物理層のラベル (コピー) */ + char phy_label[16]; + + /** L2TPコールのリスト */ + slist call_list; + /* + * 送信 Window 関連 + * pos == lim は、バッファが一杯であることを示します。 + * pos == -1、lim == 0 はバッファが空であることを示します。 + */ + /** 利用可能な送信バッファ。#winsz 分のリストになってます*/ + bytebuffer **snd_buffers; + /** Sending buffer for ZLB */ + bytebuffer *zlb_buffer; + + /** 最後にコントロールメッセージを送信した時間 */ + time_t last_snd_ctrl; + /** 最後にパケットを受信を送信した時間 */ + time_t last_rcv; + + /** + * アクティブクローズの場合で、StopCCN を未送信の場合は、StopCCN + * で伝える result code が入ります。 + */ + int active_closing; + + /** アイドル状態から HELLO 送信までの秒数。0以下は無効。*/ + int hello_interval; + /** HELLO のタイムアウト */ + int hello_timeout; + /** HELLO 送出時刻 */ + time_t hello_io_time; + /** 確立した call 数 */ + int ncalls; + + int + /* L2TP Data Message でシーケンス番号を使うか */ + data_use_seq:1, + /** HELLO の応答待ちかどうか */ + hello_wait_ack:1; + +} l2tp_ctrl; + +/** + * L2TP コールを示す型。 + */ +typedef struct _l2tp_call { + /** ID */ + unsigned id; + /** 状態 */ + int state; + /** 親コントロールコネクション */ + l2tp_ctrl *ctrl; + /** バインドした {@link ::_npppd_ppp ppp} */ + void *ppp; + /** セッション ID */ + int session_id; + /** 先方のセッション ID */ + int peer_session_id; + /** 次の送信シーケンス番号 */ + uint16_t snd_nxt; + /** 受信シーケンス番号 */ + uint16_t rcv_nxt; + /** Calling number */ + char calling_number[32]; + + uint32_t /** Sequencing required */ + seq_required:1, + /** Use sequencing in the data connection */ + use_seq:1; +} l2tp_call; + +#ifdef __cplusplus +extern "C" { +#endif + +l2tp_call *l2tp_call_create (void); +void l2tp_call_init (l2tp_call *, l2tp_ctrl *); +void l2tp_call_destroy (l2tp_call *, int); +void l2tp_call_admin_disconnect(l2tp_call *); +int l2tp_call_recv_packet (l2tp_ctrl *, l2tp_call *, int, u_char *, int); +void l2tp_call_ppp_input (l2tp_call *, u_char *, int); + +void l2tp_ctrl_destroy (l2tp_ctrl *); +l2tp_ctrl *l2tp_ctrl_create (void); +void l2tp_ctrl_input (l2tpd *, int, struct sockaddr *, struct sockaddr *, void *, u_char *, int); +int l2tp_ctrl_send(l2tp_ctrl *, const void *, int); +int l2tp_ctrl_send_packet(l2tp_ctrl *, int, bytebuffer *, int); +int l2tp_ctrl_stop (l2tp_ctrl *, int); +bytebuffer *l2tp_ctrl_prepare_snd_buffer (l2tp_ctrl *, int); +void l2tp_ctrl_log (l2tp_ctrl *, int, const char *, ...) __attribute__((__format__ (__printf__, 3, 4))); +int l2tpd_init (l2tpd *); +void l2tpd_uninit (l2tpd *); +int l2tpd_start (l2tpd *); +void l2tpd_stop (l2tpd *); +void l2tpd_stop_immediatly (l2tpd *); +l2tp_ctrl *l2tpd_get_ctrl (l2tpd *, int); +void l2tpd_add_ctrl (l2tpd *, l2tp_ctrl *); +void l2tpd_ctrl_finished_notify(l2tpd *); +void l2tpd_remove_ctrl (l2tpd *, int); +int l2tpd_add_listener (l2tpd *, int, const char *, struct sockaddr *); +void l2tpd_log (l2tpd *, int, const char *, ...) __attribute__((__format__ (__printf__, 3, 4))); + +const char *l2tp_ctrl_config_str (l2tp_ctrl *, const char *); +int l2tp_ctrl_config_int (l2tp_ctrl *, const char *, int); +int l2tp_ctrl_config_str_equal (l2tp_ctrl *, const char *, const char *, int); +int l2tp_ctrl_config_str_equali (l2tp_ctrl *, const char *, const char *, int); +const char *l2tpd_config_str (l2tpd *, const char *); +int l2tpd_config_int (l2tpd *, const char *, int); +int l2tpd_config_str_equal (l2tpd *, const char *, const char *, int); +int l2tpd_config_str_equali (l2tpd *, const char *, const char *, int); +int l2tpd_reload(l2tpd *, struct properties *, const char *, int); +void l2tpd_log_access_deny(l2tpd *, const char *, struct sockaddr *); +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/l2tp/l2tp_call.c b/usr.sbin/npppd/l2tp/l2tp_call.c new file mode 100644 index 00000000000..02be6a1649b --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tp_call.c @@ -0,0 +1,1067 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: l2tp_call.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * L2TP LNS のコールの実装。 + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <stdlib.h> +#include <stddef.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <unistd.h> +#include <event.h> + +#include "debugutil.h" +#include "bytebuf.h" +#include "hash.h" +#include "slist.h" +#include "l2tp.h" +#include "l2tp_subr.h" + +#ifndef L2TPD_TEST +#include <net/if_dl.h> +#include "npppd.h" +#else +typedef struct _dialin_proxy_info { } dialin_proxy_info; +#endif +#include "l2tp_local.h" + +#ifdef L2TP_CALL_DEBUG +#define L2TP_CALL_DBG(m) l2tp_call_log m +#define L2TP_CALL_ASSERT(x) ASSERT(x) +#else +#define L2TP_CALL_DBG(m) +#define L2TP_CALL_ASSERT(x) +#endif + +static void l2tp_call_log (l2tp_call *, int, const char *, ...) __printflike(3,4); +static void l2tp_call_disconnect (l2tp_call *, int, int, const char *, struct l2tp_avp *[], int); +static int l2tp_call_recv_ICRQ (l2tp_call *, u_char *, int); +static int l2tp_call_send_ICRP (l2tp_call *); +static int l2tp_call_recv_ICCN (l2tp_call *, u_char *, int, dialin_proxy_info *); +static int l2tp_recv_CDN (l2tp_call *, u_char *, int); +static int l2tp_call_send_CDN (l2tp_call *, int, int, const char *, struct l2tp_avp *[], int); +static int l2tp_call_send_ZLB (l2tp_call *); +static inline const char *l2tp_call_state_string (l2tp_call *); +static int l2tp_call_bind_ppp (l2tp_call *, dialin_proxy_info *); +static void l2tp_call_notify_down (l2tp_call *); +static int l2tp_call_send_data_packet (l2tp_call *, bytebuffer *); + + +/** l2tp_call の ID番号のシーケンス番号 */ +static unsigned l2tp_call_id_seq = 0; + +/** {@link ::_l2tp_call L2TP コール} インスタンスを生成します。*/ +l2tp_call * +l2tp_call_create(void) +{ + l2tp_call *_this; + + if ((_this = malloc(sizeof(l2tp_call))) == NULL) + return NULL; + + return _this; +} + +/** {@link ::_l2tp_call L2TP コール} インスタンスを初期化します。 */ +void +l2tp_call_init(l2tp_call *_this, l2tp_ctrl *ctrl) +{ + memset(_this, 0, sizeof(l2tp_call)); + + _this->ctrl = ctrl; + _this->id = l2tp_call_id_seq++; + _this->use_seq = ctrl->data_use_seq; +} + +/** このインスタンスを解放します。 */ +void +l2tp_call_destroy(l2tp_call *_this, int from_l2tp_ctrl) +{ + free(_this); +} + +/* + * 切断について + * a) npppdctl (vdipwho) から切断要求があった。 + * ppp_stop() で切断します。→ PPP LCP TermReq + * b) npppd が終了する、あるいは設定変更により l2tp.enabled = false + * l2tp_call_disconnect() で切断します。→ L2TP CDN + */ +/** 管理上の理由から切断します。 */ +void +l2tp_call_admin_disconnect(l2tp_call *_this) +{ + l2tp_call_disconnect(_this, L2TP_CDN_RCODE_ADMINISTRATIVE_REASON, 0, + NULL, NULL, 0); +} + +/** + * 切断します。 + * @param result_code CDN を送信せずに切断(解放)する場合には、0 を指定 + * します。 + */ +static void +l2tp_call_disconnect(l2tp_call *_this, int result_code, int error_code, + const char *errmes, struct l2tp_avp *addavp[], int naddavp) +{ + L2TP_CALL_ASSERT(_this != NULL); + + if (_this->state == L2TP_CALL_STATE_CLEANUP_WAIT) { + // 既に CDN を受信、または送信済み + l2tp_call_notify_down(_this); // ねんのため + return; + } + if (result_code > 0) { + if (l2tp_call_send_CDN(_this, result_code, error_code, errmes, + addavp, naddavp) + != 0) + l2tp_call_log(_this, LOG_ERR, "Error sending CDN: %m"); + } + _this->state = L2TP_CALL_STATE_CLEANUP_WAIT; + l2tp_call_notify_down(_this); +} + +/************************************************************************ + * 制御パケットの送受信 + ************************************************************************/ +/** 制御パケットが入力された時に呼び出します。 */ +int +l2tp_call_recv_packet(l2tp_ctrl *ctrl, l2tp_call *_this, int mestype, + u_char *pkt, int pktlen) +{ + int i, len, session_id, send_cdn; + l2tp_call *call; + dialin_proxy_info dpi; + + // ICRQ の時だけ、_this == NULL + L2TP_CALL_ASSERT(_this != NULL || + mestype == L2TP_AVP_MESSAGE_TYPE_ICRQ); + + if (_this == NULL) { + if (mestype != L2TP_AVP_MESSAGE_TYPE_ICRQ) + return 1; + if ((_this = l2tp_call_create()) == NULL) { + l2tp_ctrl_log(ctrl, LOG_ERR, + "l2tp_call_create failed in %s(): %m", __func__); + return 1; + } + l2tp_call_init(_this, ctrl); + + if (l2tp_call_recv_ICRQ(_this, pkt, pktlen) != 0) + return 1; + + len = slist_length(&ctrl->call_list); + session_id = _this->id; + again: + /* セッションIDの割り当て */ + session_id &= 0xffff; + if (session_id == 0) + session_id = 1; + for (i = 0; i < len; i++) { + call = slist_get(&ctrl->call_list, i); + if (call->session_id == session_id) { + session_id++; + goto again; + } + } + _this->session_id = session_id; + + /* この l2tp_call をリストに追加。 */ + slist_add(&_this->ctrl->call_list, _this); + + if (l2tp_call_send_ICRP(_this) != 0) + return 1; + _this->state = L2TP_CALL_STATE_WAIT_CONN; + return 0; + } + + /* ステートマシン */ + send_cdn = 0; + switch (_this->state) { + default: + break; + case L2TP_CALL_STATE_WAIT_CONN: + switch (mestype) { + case L2TP_AVP_MESSAGE_TYPE_ICCN: + memset(&dpi, 0, sizeof(dpi)); + if (l2tp_call_recv_ICCN(_this, pkt, pktlen, &dpi) != 0) + return 1; + l2tp_call_bind_ppp(_this, &dpi); + l2tp_call_send_ZLB(_this); + _this->state = L2TP_CALL_STATE_ESTABLISHED; + _this->ctrl->ncalls++; + return 0; + case L2TP_AVP_MESSAGE_TYPE_ICRQ: + case L2TP_AVP_MESSAGE_TYPE_ICRP: + send_cdn = 1; + // FALL THROUGH + default: + l2tp_call_log(_this, LOG_ERR, + "Waiting ICCN. But received %s", + avp_mes_type_string(mestype)); + if (send_cdn) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_GENERIC_ERROR, "Illegal state.", + NULL, 0); + return 0; + } + } + break; + case L2TP_CALL_STATE_ESTABLISHED: + switch (mestype) { + case L2TP_AVP_MESSAGE_TYPE_CDN: + /* + * peer からの切断。ログに残す + */ + l2tp_recv_CDN(_this, pkt, pktlen); + _this->state = L2TP_CALL_STATE_CLEANUP_WAIT; + l2tp_call_notify_down(_this); + l2tp_call_send_ZLB(_this); + return 0; + case L2TP_AVP_MESSAGE_TYPE_ICRQ: + case L2TP_AVP_MESSAGE_TYPE_ICRP: + case L2TP_AVP_MESSAGE_TYPE_ICCN: + send_cdn = 1; + break; + default: + break; + } + l2tp_call_log(_this, LOG_ERR, + "Call established. But received %s", + avp_mes_type_string(mestype)); + if (send_cdn) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_GENERIC_ERROR, "Illegal state.", + NULL, 0); + return 0; + } + l2tp_call_disconnect(_this, 0, 0, NULL, NULL, 0); + return 1; + } + l2tp_call_log(_this, LOG_INFO, "Received %s in unexpected state=%s", + avp_mes_type_string(mestype), l2tp_call_state_string(_this)); + l2tp_call_disconnect(_this, 0, 0, NULL, NULL, 0); + return 1; +} +/** + * ICRQ受信 + * @return acceptable な ICRQ の場合には、0 を返します。 + * 失敗した場合には、0 以外を返し、CDN は送信済みで、ステータス + * も変更済みです。 + */ +static int +l2tp_call_recv_ICRQ(l2tp_call *_this, u_char *pkt, int pktlen) +{ + int avpsz, slen; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ], emes[256]; + + avp = (struct l2tp_avp *)buf; + while (pktlen >= 6 && (avpsz = avp_enum(avp, pkt, pktlen, 1)) > 0) { + pkt += avpsz; + pktlen -= avpsz; + if (avp->vendor_id != 0) { + L2TP_CALL_DBG((_this, LOG_DEBUG, + "Received a Vendor-specific AVP vendor-id=%d " + "type=%d", avp->vendor_id, avp->attr_type)); + continue; + } + if (avp->is_hidden != 0) { + l2tp_call_log(_this, LOG_WARNING, + "Received AVP (%s/%d) is hidden. But we don't " + "share secret.", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + if (avp->is_mandatory != 0) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_UNKNOWN_MANDATORY_AVP, NULL, + NULL, 0); + return 1; + } + continue; + } + switch (avp->attr_type) { + case L2TP_AVP_TYPE_MESSAGE_TYPE: + AVP_SIZE_CHECK(avp, ==, 8); + continue; + case L2TP_AVP_TYPE_ASSIGNED_SESSION_ID: + AVP_SIZE_CHECK(avp, ==, 8); + _this->peer_session_id = avp_get_val16(avp); + continue; + case L2TP_AVP_TYPE_CALL_SERIAL_NUMBER: + case L2TP_AVP_TYPE_BEARER_TYPE: + case L2TP_AVP_TYPE_PHYSICAL_CHANNEL_ID: + /* + * Windows 98/Me/NT の MS "L2TP/IPsec VPN Client" + * では Physical Channel Id は mandatory ビットがたって + * いる。 + */ + case L2TP_AVP_TYPE_CALLING_NUMBER: + slen = MIN(sizeof(_this->calling_number) - 1, + avp_attr_length(avp)); + memcpy(_this->calling_number, avp->attr_value, slen); + _this->calling_number[slen] = '\0'; + break; + case L2TP_AVP_TYPE_CALLED_NUMBER: + case L2TP_AVP_TYPE_SUB_ADDRESS: + // 使い途あれば。 + continue; + default: + if (avp->is_mandatory) { + l2tp_call_log(_this, LOG_WARNING, + "AVP (%s/%d) is not supported, but it's " + "mandatory", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + if (avp->is_mandatory != 0) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_UNKNOWN_MANDATORY_AVP, + NULL, NULL, 0); + return 1; + } +#ifdef L2TP_CALL_DEBUG + } else { + L2TP_CALL_DBG((_this, LOG_DEBUG, + "AVP (%s/%d) is not handled", + avp_attr_type_string(avp->attr_type), + avp->attr_type)); +#endif + } + } + } + if (_this->peer_session_id == 0) { + l2tp_call_log(_this, LOG_ERR, + "Received a bad ICRP: SessionId = 0"); + l2tp_call_disconnect(_this, L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_INVALID_MESSAGE, "Session Id must not be 0", + NULL, 0); + return 1; + } + l2tp_call_log(_this, LOG_INFO, "RecvICRQ session_id=%u", + _this->peer_session_id); + + return 0; +size_check_failed: + l2tp_call_log(_this, LOG_ERR, "Received bad ICRQ: %s", emes); + l2tp_call_disconnect(_this, L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_WRONG_LENGTH, NULL, NULL, 0); + + return 1; +} + +/** ICRP 送信 */ +static int +l2tp_call_send_ICRP(l2tp_call *_this) +{ + int rval; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ]; + bytebuffer *bytebuf; + + bytebuf = l2tp_ctrl_prepare_snd_buffer(_this->ctrl, 1); + if (bytebuf == NULL) { + l2tp_call_log(_this, LOG_ERR, "sending ICRP failed: no buffer"); + return 1; + } + avp = (struct l2tp_avp *)buf; + + /* Message Type = ICRP */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_MESSAGE_TYPE; + avp_set_val16(avp, L2TP_AVP_MESSAGE_TYPE_ICRP); + bytebuf_add_avp(bytebuf, avp, 2); + + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_ASSIGNED_SESSION_ID; + avp_set_val16(avp, _this->session_id); + bytebuf_add_avp(bytebuf, avp, 2); + + if ((rval = l2tp_ctrl_send_packet(_this->ctrl, _this->peer_session_id, + bytebuf, 1)) != 0) { + l2tp_call_log(_this, LOG_ERR, "failed to SendICRP: %m"); + return 1; + } + l2tp_call_log(_this, LOG_INFO, "SendICRP session_id=%u", + _this->session_id); + return 0; +} + +/** L2TP data messageを送信します。*/ +static int +l2tp_call_send_data_packet(l2tp_call *_this, bytebuffer *buffer) +{ + int rval; + struct l2tp_header *hdr; + + bytebuffer_flip(buffer); + hdr = (struct l2tp_header *)bytebuffer_pointer(buffer); + memset(hdr, 0, sizeof(*hdr) - 4); /* Nr, Ns はオプション */ + + hdr->t = 0; + hdr->ver = L2TP_HEADER_VERSION_RFC2661; + hdr->l = 1; + hdr->length = htons(bytebuffer_remaining(buffer)); + hdr->tunnel_id = htons(_this->ctrl->peer_tunnel_id); + hdr->session_id = htons(_this->peer_session_id); + if (_this->use_seq) { + hdr->s = 1; + hdr->ns = htons(_this->snd_nxt++); + hdr->nr = htons(_this->rcv_nxt); + } + + if (_this->ctrl->l2tpd->data_out_pktdump != 0) { + l2tpd_log(_this->ctrl->l2tpd, LOG_DEBUG, + "ctrl=%u call=%u L2TP Data output packet dump", + _this->ctrl->id, _this->id); + show_hd(debug_get_debugfp(), bytebuffer_pointer(buffer), + bytebuffer_remaining(buffer)); + } + if ((rval = l2tp_ctrl_send(_this->ctrl, bytebuffer_pointer(buffer), + bytebuffer_remaining(buffer))) < 0) { + L2TP_CALL_DBG((_this, LOG_DEBUG, "sendto() failed: %m")); + } + + return (rval == bytebuffer_remaining(buffer))? 0 : 1; +} + +/** + * ICCN 受信 + * @return acceptable な ICCN の場合には、0 を返します。失敗した場合には、0 + * 以外を返し、CDN は送信済みで、ステータスも変更済みです。 + */ +static int +l2tp_call_recv_ICCN(l2tp_call *_this, u_char *pkt, int pktlen, + dialin_proxy_info *dpi) +{ + int avpsz, tx_conn_speed; + uint32_t framing_type = 0; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ], emes[256]; + + tx_conn_speed = 0; + avp = (struct l2tp_avp *)buf; + while (pktlen >= 6 && (avpsz = avp_enum(avp, pkt, pktlen, 1)) > 0) { + pkt += avpsz; + pktlen -= avpsz; + if (avp->vendor_id != 0) { + L2TP_CALL_DBG((_this, LOG_DEBUG, + "Received a Vendor-specific AVP vendor-id=%d " + "type=%d", avp->vendor_id, avp->attr_type)); + continue; + } + if (avp->is_hidden != 0) { + l2tp_call_log(_this, LOG_WARNING, + "Received AVP (%s/%d) is hidden. But we don't " + "share secret.", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + if (avp->is_mandatory != 0) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_UNKNOWN_MANDATORY_AVP, NULL, + NULL, 0); + return 1; + } + continue; + } + switch (avp->attr_type) { + case L2TP_AVP_TYPE_MESSAGE_TYPE: + AVP_SIZE_CHECK(avp, ==, 8); + continue; + case L2TP_AVP_TYPE_TX_CONNECT_SPEED: + AVP_SIZE_CHECK(avp, ==, 10); + tx_conn_speed = avp_get_val32(avp); + continue; + case L2TP_AVP_TYPE_FRAMING_TYPE: + AVP_SIZE_CHECK(avp, ==, 10); + framing_type = avp_get_val32(avp); + continue; + case L2TP_AVP_TYPE_SEQUENCING_REQUIRED: + _this->seq_required = 1; + _this->use_seq = 1; + continue; +#ifndef L2TPD_TEST + /* + * AVP's for Proxy-LCP and Proxy-Authen + */ + case L2TP_AVP_TYPE_LAST_SENT_LCP_CONFREQ: + memcpy(dpi->last_sent_lcp.data, avp->attr_value, + avp_attr_length(avp)); + dpi->last_sent_lcp.ldata = avp_attr_length(avp); + break; + case L2TP_AVP_TYPE_LAST_RECV_LCP_CONFREQ: + memcpy(dpi->last_recv_lcp.data, avp->attr_value, + avp_attr_length(avp)); + dpi->last_recv_lcp.ldata = avp_attr_length(avp); + break; + case L2TP_AVP_TYPE_PROXY_AUTHEN_CHALLENGE: + memcpy(dpi->auth_chall, avp->attr_value, + MIN(avp_attr_length(avp), sizeof(dpi->auth_chall))); + dpi->lauth_chall = avp_attr_length(avp); + break; + case L2TP_AVP_TYPE_PROXY_AUTHEN_ID: + dpi->auth_id = avp_get_val16(avp); + break; + case L2TP_AVP_TYPE_PROXY_AUTHEN_NAME: + memcpy(dpi->username, avp->attr_value, + MIN(sizeof(dpi->username) - 1, + avp_attr_length(avp))); + break; + case L2TP_AVP_TYPE_PROXY_AUTHEN_RESPONSE: + memcpy(dpi->auth_resp, avp->attr_value, + MIN(avp_attr_length(avp), sizeof(dpi->auth_resp))); + dpi->lauth_resp = avp_attr_length(avp); + break; + case L2TP_AVP_TYPE_PROXY_AUTHEN_TYPE: + switch (avp_get_val16(avp)) { + default: + l2tp_call_log(_this, LOG_WARNING, + "RecvICCN Unknown proxy-authen-type=%d", + avp_get_val16(avp)); + /* FALLTHROUGH */ + case L2TP_AUTH_TYPE_NO_AUTH: + dpi->auth_type = 0; + break; + case L2TP_AUTH_TYPE_PPP_CHAP: + dpi->auth_type = PPP_AUTH_CHAP_MD5; + break; + case L2TP_AUTH_TYPE_PPP_PAP: + dpi->auth_type = PPP_AUTH_PAP; + break; + case L2TP_AUTH_TYPE_MS_CHAP_V1: + dpi->auth_type = PPP_AUTH_CHAP_MS; + break; + } + break; +#endif + default: + if (avp->is_mandatory != 0) { + l2tp_call_log(_this, LOG_WARNING, + "AVP (%s/%d) is not supported, but it's " + "mandatory", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_UNKNOWN_MANDATORY_AVP, NULL, + NULL, 0); + return 1; +#ifdef L2TP_CALL_DEBUG + } else { + L2TP_CALL_DBG((_this, LOG_DEBUG, + "AVP (%s/%d) is not handled", + avp_attr_type_string(avp->attr_type), + avp->attr_type)); +#endif + } + } + } + l2tp_call_log(_this, LOG_INFO, "RecvICCN " + "session_id=%u calling_number=%s tx_conn_speed=%u framing=%s", + _this->peer_session_id, _this->calling_number, tx_conn_speed, + ((framing_type & L2TP_FRAMING_CAP_FLAGS_ASYNC) != 0)? "async" : + ((framing_type & L2TP_FRAMING_CAP_FLAGS_SYNC) != 0)? "sync" : + "unknown"); + + return 0; +size_check_failed: + l2tp_call_log(_this, LOG_ERR, "Received bad ICCN: %s", emes); + l2tp_call_disconnect(_this, L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_WRONG_LENGTH, NULL, NULL, 0); + return 1; +} + +/** CDN 受信 */ +static int +l2tp_recv_CDN(l2tp_call *_this, u_char *pkt, int pktlen) +{ + int result, error, avpsz, len, sessid; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ], emes[256], pmes[256]; + + /* 初期化 */ + result = 0; + error = 0; + sessid = 0; + strlcpy(pmes, "(none)", sizeof(pmes)); + + avp = (struct l2tp_avp *)buf; + while (pktlen >= 6 && (avpsz = avp_enum(avp, pkt, pktlen, 1)) > 0) { + pkt += avpsz; + pktlen -= avpsz; + if (avp->vendor_id != 0) { + L2TP_CALL_DBG((_this, LOG_DEBUG, + "Received a Vendor-specific AVP vendor-id=%d " + "type=%d", avp->vendor_id, avp->attr_type)); + continue; + } + if (avp->is_hidden != 0) { + l2tp_call_log(_this, LOG_WARNING, + "Received AVP (%s/%d) is hidden. But we don't " + "share secret.", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + if (avp->is_mandatory != 0) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_UNKNOWN_MANDATORY_AVP, NULL, + NULL, 0); + return 1; + } + continue; + } + switch (avp->attr_type) { + case L2TP_AVP_TYPE_MESSAGE_TYPE: + AVP_SIZE_CHECK(avp, ==, 8); + continue; + case L2TP_AVP_TYPE_RESULT_CODE: + AVP_SIZE_CHECK(avp, >=, 8); + result = avp->attr_value[0] << 8 | avp->attr_value[1]; + if (avp->length >= 10) { + error = avp->attr_value[2] << 8 | + avp->attr_value[3]; + len = avp->length - 12; + if (len > 0) { + len = MIN(len, sizeof(pmes)); + memcpy(pmes, &avp->attr_value[4], len); + pmes[len] = '\0'; + } + } + continue; + case L2TP_AVP_TYPE_ASSIGNED_SESSION_ID: + AVP_SIZE_CHECK(avp, >=, 8); + sessid = avp_get_val16(avp); + continue; + default: + if (avp->is_mandatory) { + l2tp_call_log(_this, LOG_WARNING, + "AVP (%s/%d) is not supported, but it's " + "mandatory", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + if (avp->is_mandatory != 0) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, + L2TP_ECODE_UNKNOWN_MANDATORY_AVP, + NULL, NULL, 0); + return 1; + } +#ifdef L2TP_CALL_DEBUG + } else { + L2TP_CALL_DBG((_this, LOG_DEBUG, + "AVP (%s/%d) is not handled", + avp_attr_type_string(avp->attr_type), + avp->attr_type)); +#endif + } + } + } + if (error == 0) { + l2tp_call_log(_this, LOG_INFO, + "RecvCDN result=%s/%d", l2tp_cdn_rcode_string(result), + result); + } else { + l2tp_call_log(_this, LOG_INFO, + "RecvCDN result=%s/%d error=%s/%d message=%s", + l2tp_cdn_rcode_string(result), result, + l2tp_ecode_string(error), error, pmes); + } + + return 0; + +size_check_failed: + // CDN のメッセージがおかしくても、続行 + l2tp_call_log(_this, LOG_ERR, "Received bad CDN: %s", emes); + + return 0; +} + +/** CDN 送信 */ +static int +l2tp_call_send_CDN(l2tp_call *_this, int result_code, int error_code, const + char *errmes, struct l2tp_avp *addavp[], int naddavp) +{ + uint32_t val32; + int i, avplen, len; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ]; + bytebuffer *bytebuf; + + L2TP_CALL_ASSERT(_this != NULL); + bytebuf = l2tp_ctrl_prepare_snd_buffer(_this->ctrl, 1); + if (bytebuf == NULL) { + l2tp_call_log(_this, LOG_ERR, "sending CDN failed: no buffer"); + return 1; + } + avp = (struct l2tp_avp *)buf; + + /* Message Type = CDN */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_MESSAGE_TYPE; + avp_set_val16(avp, L2TP_AVP_MESSAGE_TYPE_CDN); + bytebuf_add_avp(bytebuf, avp, 2); + + /* Result Code */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_RESULT_CODE; +#if 0 +/* + * エラーコード無しの長さ 8 の AVP を送信すると、Windows 2000 側が StopCCN で + * "2 - Length is wrong" を返してくる。長さ10にして回避。 + */ + if (error_code > 0) { + val32 = (result_code << 16) | (error_code & 0xffff); + avplen = 4; + avp_set_val32(avp, val32); + } else { + avplen = 2; + avp_set_val16(avp, result_code); + } +#else + val32 = (result_code << 16) | (error_code & 0xffff); + avplen = 4; + avp_set_val32(avp, val32); +#endif + + if (errmes != NULL) { + len = MIN(strlen(errmes), L2TP_AVP_MAXSIZ - 128); + memcpy(&avp->attr_value[avplen], errmes, len); + avplen += len; + } + bytebuf_add_avp(bytebuf, avp, avplen); + + /* Assigned Session Id */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_ASSIGNED_SESSION_ID; + if (_this != NULL && _this->session_id != 0) + avp_set_val16(avp, _this->session_id); + else + avp_set_val16(avp, 0); + bytebuf_add_avp(bytebuf, avp, 2); + + for (i = 0; i < naddavp; i++) + bytebuf_add_avp(bytebuf, addavp[i], addavp[i]->length - 6); + + if (l2tp_ctrl_send_packet(_this->ctrl, _this->peer_session_id, + bytebuf, 1) != 0) { + l2tp_call_log(_this, LOG_ERR, "Error sending CDN: %m"); + return 1; + } + + if (error_code > 0) { + l2tp_call_log(_this, LOG_INFO, + "SendCDN result=%s/%d error=%s/%d messsage=%s", + l2tp_cdn_rcode_string(result_code), result_code, + l2tp_ecode_string(error_code), error_code, + (errmes == NULL)? "none" : errmes); + } else { + l2tp_call_log(_this, LOG_INFO, "SendCDN result=%s/%d", + l2tp_cdn_rcode_string(result_code), result_code); + } + + return 0; +} + +/** ZLB の送信 */ +static int +l2tp_call_send_ZLB(l2tp_call *_this) +{ + bytebuffer *bytebuf; + + l2tp_call_log(_this, LOG_INFO, "SendZLB"); + bytebuf = l2tp_ctrl_prepare_snd_buffer(_this->ctrl, 1); + if (bytebuf == NULL) { + l2tp_call_log(_this, LOG_ERR, "sending ZLB failed: no buffer"); + return 1; + } + return l2tp_ctrl_send_packet(_this->ctrl, _this->peer_session_id, + bytebuf, 1); +} + +/************************************************************************ + * その他 + ************************************************************************/ +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static void +l2tp_call_log(l2tp_call *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); +#ifdef L2TPD_MULITPLE + snprintf(logbuf, sizeof(logbuf), "l2tpd id=%u ctrl=%u call=%u %s", + _this->ctrl->l2tpd->id, _this->ctrl->id, _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "l2tpd ctrl=%u call=%u %s", + _this->ctrl->id, _this->id, fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +/** 現在のステータスの文字列表現を返します。 */ +static inline const char * +l2tp_call_state_string(l2tp_call *_this) +{ + switch (_this->state) { + case L2TP_CALL_STATE_IDLE: return "idle"; + case L2TP_CALL_STATE_WAIT_CONN: return "wait-conn"; + case L2TP_CALL_STATE_ESTABLISHED: return "established"; + case L2TP_CALL_STATE_CLEANUP_WAIT: return "cleanup-wait"; + } + return "unknown"; +} + + +#ifdef L2TPD_TEST + +void +l2tp_call_ppp_input(l2tp_call *_this, u_char *pkt, int pktlen) +{ +} +static int +l2tp_call_bind_ppp(l2tp_call *_this, dialin_proxy_info *dpi) +{ + return 0; +} +static void +l2tp_call_notify_down(l2tp_call *_this) +{ +} + +#else +/************************************************************************ + * npppd の物理層として + ************************************************************************/ +#include "npppd.h" + +static int l2tp_call_ppp_output (npppd_ppp *, unsigned char *, int, int); +static void l2tp_call_closed_by_ppp (npppd_ppp *); + +/** ppp にパケットを入力します。 */ +void +l2tp_call_ppp_input(l2tp_call *_this, u_char *pkt, int pktlen) +{ + int rval; + npppd_ppp *ppp; + + ppp = _this->ppp; + rval = ppp->recv_packet(ppp, pkt, pktlen, 0); + + if (_this->ppp == NULL) /* ppp is freed */ + return; + + if (rval != 0) + ppp->ierrors++; + else { + ppp->ipackets++; + ppp->ibytes += pktlen; + } +} + +/** ppp からパケットが出力される時に呼び出されます。 */ +static int +l2tp_call_ppp_output(npppd_ppp *ppp, unsigned char *bytes, int nbytes, + int flags) +{ + l2tp_call *_this; + bytebuffer *bytebuf; + + _this = ppp->phy_context; + + bytebuf = l2tp_ctrl_prepare_snd_buffer(_this->ctrl, _this->use_seq); + + if (bytebuf != NULL) { + bytebuffer_put(bytebuf, bytes, nbytes); + if (l2tp_call_send_data_packet(_this, bytebuf) != 0) + ppp->oerrors++; + else { + ppp->opackets++; + ppp->obytes += nbytes; + } + } else + ppp->oerrors++; + + return 0; +} + +/** ppp で切断された場合に呼び出されます。 */ +static void +l2tp_call_closed_by_ppp(npppd_ppp *ppp) +{ + l2tp_call *_this; + + L2TP_CALL_ASSERT(ppp != NULL); + L2TP_CALL_ASSERT(ppp->phy_context != NULL); + + _this = ppp->phy_context; + + _this->ppp = NULL; // l2tp_call_disconnect より先に。 + + if (_this->state == L2TP_CALL_STATE_CLEANUP_WAIT) { + /* no need to call l2tp_call_disconnect */ + } else if (ppp->disconnect_code == PPP_DISCON_NO_INFORMATION) { + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ADMINISTRATIVE_REASON, 0, NULL, NULL, 0); + } else { + /* + * RFC3145 L2TP Disconnect Cause Information + */ + struct l2tp_avp *avp[1]; + struct _ppp_cause { + struct l2tp_avp avp; + uint16_t code; + uint16_t proto; + uint8_t direction; + char message[128]; + } __attribute__((__packed__)) ppp_cause; + + ppp_cause.avp.is_mandatory = 0; + ppp_cause.avp.is_hidden = 0; + ppp_cause.avp.vendor_id = 0; /* ietf */ + ppp_cause.avp.attr_type = + L2TP_AVP_TYPE_PPP_DISCONNECT_CAUSE_CODE; + ppp_cause.code = htons(ppp->disconnect_code); + ppp_cause.proto = htons(ppp->disconnect_proto); + ppp_cause.direction = ppp->disconnect_direction; + ppp_cause.avp.length = offsetof(struct _ppp_cause, message[0]); + + if (ppp->disconnect_message != NULL) { + strlcpy(ppp_cause.message, ppp->disconnect_message, + sizeof(ppp_cause.message)); + ppp_cause.avp.length += strlen(ppp_cause.message); + } + avp[0] = &ppp_cause.avp; + l2tp_call_disconnect(_this, + L2TP_CDN_RCODE_ERROR_CODE, L2TP_ECODE_GENERIC_ERROR, + "Disconnected by local PPP", avp, 1); + } + l2tp_call_log(_this, LOG_NOTICE, "logtype=PPPUnbind"); +} + +/** ppp に切断した旨を通知し ppp の終了/解放を行います。 */ +static void +l2tp_call_notify_down(l2tp_call *_this) +{ + if (_this->ppp != NULL) + ppp_phy_downed(_this->ppp); +} + +/** ppp の bind。 */ +static int +l2tp_call_bind_ppp(l2tp_call *_this, dialin_proxy_info *dpi) +{ + int code, errcode; + npppd_ppp *ppp; + + code = L2TP_CDN_RCODE_BUSY; + errcode = 0; + ppp = NULL; + if ((ppp = ppp_create()) == NULL) + goto reigai; + + ASSERT(_this->ppp == NULL); + + if (_this->ppp != NULL) + return -1; + + _this->ppp = ppp; + + ppp->tunnel_type = PPP_TUNNEL_L2TP; + ppp->phy_context = _this; + ppp->send_packet = l2tp_call_ppp_output; + ppp->phy_close = l2tp_call_closed_by_ppp; + +#ifdef IDGW_DIALIN + if (DIALIN_PROXY_IS_REQUESTED(dpi)) { + strlcpy(ppp->phy_label, L2TPD_DIALIN_LAYER2_LABEL, + sizeof(ppp->phy_label)); + ppp->phy_info.peer_pn.pn_len = sizeof(npppd_phone_number); + ppp->phy_info.peer_pn.pn_family = NPPPD_AF_PHONE_NUMBER; + strlcpy(ppp->phy_info.peer_pn.pn_number, _this->calling_number, + sizeof(ppp->phy_info.peer_pn.pn_number)); + } else { +#endif + strlcpy(ppp->phy_label, _this->ctrl->phy_label, + sizeof(ppp->phy_label)); + memcpy(&ppp->phy_info.peer_in, &_this->ctrl->peer, + _this->ctrl->peer.ss_len); +#ifdef IDGW_DIALIN + } +#endif + strlcpy(ppp->calling_number, _this->calling_number, + sizeof(ppp->calling_number)); + if (ppp_init(npppd_get_npppd(), ppp) != 0) { + l2tp_call_log(_this, LOG_ERR, "failed binding ppp"); + goto reigai; + } + + l2tp_call_log(_this, LOG_NOTICE, "logtype=PPPBind ppp=%d", ppp->id); + if (DIALIN_PROXY_IS_REQUESTED(dpi)) { + if (!l2tp_ctrl_config_str_equal(_this->ctrl, + "l2tp.accept_dialin", "true", 0)) { + l2tp_call_log(_this, LOG_ERR, + "'accept_dialin' is 'false' in the setting."); + code = L2TP_CDN_RCODE_ERROR_CODE; + errcode = L2TP_ECODE_INVALID_MESSAGE; + goto reigai; + } + + if (ppp_dialin_proxy_prepare(ppp, dpi) != 0) { + code = L2TP_CDN_RCODE_TEMP_NOT_AVALIABLE; + goto reigai; + } + } + ppp_start(ppp); + + return 0; +reigai: + if (ppp != NULL) + ppp_destroy(ppp); + _this->ppp = NULL; + + l2tp_call_disconnect(_this, code, 0, NULL, NULL, 0); + return 1; +} +#endif diff --git a/usr.sbin/npppd/l2tp/l2tp_ctrl.c b/usr.sbin/npppd/l2tp/l2tp_ctrl.c new file mode 100644 index 00000000000..8ba251572d1 --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tp_ctrl.c @@ -0,0 +1,1751 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * L2TP LNS のコントロールコネクションの処理を提供します。 + */ +// $Id: l2tp_ctrl.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/endian.h> +#include <netinet/in.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <syslog.h> +#include <stdio.h> +#include <stdarg.h> +#include <stddef.h> +#include <netdb.h> +#include <time.h> +#include <string.h> +#include <event.h> +#include <ifaddrs.h> + +#ifdef USE_LIBSOCKUTIL +#include <seil/sockfromto.h> +#endif + +#include "time_utils.h" +#include "ipsec_util.h" +#include "bytebuf.h" +#include "hash.h" +#include "debugutil.h" +#include "slist.h" +#include "l2tp.h" +#include "l2tp_local.h" +#include "l2tp_subr.h" +#include "net_utils.h" +#include "config_helper.h" +#include "version.h" +#ifdef _SEIL_EXT_ +#include "rtev.h" +#endif + +static int l2tp_ctrl_init (l2tp_ctrl *, l2tpd *, struct sockaddr *, struct sockaddr *, void *); +static void l2tp_ctrl_reload (l2tp_ctrl *); +static int l2tp_ctrl_send_disconnect_notify (l2tp_ctrl *); +#if 0 +static void l2tp_ctrl_purge_ipsec_sa (l2tp_ctrl *); +#endif +static void l2tp_ctrl_timeout (int, short, void *); +static int l2tp_ctrl_resend_una_packets (l2tp_ctrl *); +static void l2tp_ctrl_destroy_all_calls (l2tp_ctrl *); +static int l2tp_ctrl_disconnect_all_calls (l2tp_ctrl *); +static void l2tp_ctrl_reset_timeout (l2tp_ctrl *); +static inline int l2tp_ctrl_txwin_size (l2tp_ctrl *); +static inline int l2tp_ctrl_txwin_is_full (l2tp_ctrl *); +static int l2tp_ctrl_recv_SCCRQ (l2tp_ctrl *, u_char *, int, l2tpd *, struct sockaddr *); +static int l2tp_ctrl_send_StopCCN (l2tp_ctrl *, int); +static int l2tp_ctrl_recv_StopCCN (l2tp_ctrl *, u_char *, int); +static void l2tp_ctrl_send_SCCRP (l2tp_ctrl *); +static int l2tp_ctrl_send_HELLO (l2tp_ctrl *); +static int l2tp_ctrl_send_ZLB (l2tp_ctrl *); +static inline const char *l2tp_ctrl_state_string (l2tp_ctrl *); + +#ifdef L2TP_CTRL_DEBUG +#define L2TP_CTRL_ASSERT(x) ASSERT(x) +#define L2TP_CTRL_DBG(x) l2tp_ctrl_log x +#else +#define L2TP_CTRL_ASSERT(x) +#define L2TP_CTRL_DBG(x) +#endif + +/** l2tp_ctrl の ID番号のシーケンス番号 */ +static unsigned l2tp_ctrl_id_seq = 0; + +#define SEQ_LT(a,b) ((int16_t)((a) - (b)) < 0) +#define SEQ_GT(a,b) ((int16_t)((a) - (b)) > 0) + +/** + * {@link ::_l2tp_ctrl L2TP LNS コントロールコネクション}のインスタンス生成 + */ +l2tp_ctrl * +l2tp_ctrl_create(void) +{ + l2tp_ctrl *_this; + + if ((_this = malloc(sizeof(l2tp_ctrl))) == NULL) + return NULL; + + memset(_this, 0, sizeof(l2tp_ctrl)); + return (l2tp_ctrl *)_this; +} + +/** + * {@link ::_l2tp_ctrl L2TP LNS コントロールコネクション}のインスタンスの + * 初期化と開始を行います。 + */ +static int +l2tp_ctrl_init(l2tp_ctrl *_this, l2tpd *_l2tpd, struct sockaddr *peer, + struct sockaddr *sock, void *nat_t_ctx) +{ + int tunid, i; + bytebuffer *bytebuf; + time_t curr_time; + + memset(_this, 0, sizeof(l2tp_ctrl)); + + curr_time = get_monosec(); + _this->l2tpd = _l2tpd; + _this->state = L2TP_CTRL_STATE_IDLE; + _this->last_snd_ctrl = curr_time; + + slist_init(&_this->call_list); + /* + * 空いているトンネルIDを探す + */ + i = 0; + _this->id = ++l2tp_ctrl_id_seq; + for (i = 0, tunid = _this->id; ; i++, tunid++) { + tunid &= 0xffff; + _this->tunnel_id = l2tp_ctrl_id_seq & 0xffff; + if (tunid == 0) + continue; + if (l2tpd_get_ctrl(_l2tpd, tunid) == NULL) + break; + if (i > 80000) { + // バグに違いない + l2tpd_log(_l2tpd, LOG_ERR, "Too many l2tp controls"); + return -1; + } + } + + _this->tunnel_id = tunid; + + L2TP_CTRL_ASSERT(peer != NULL); + L2TP_CTRL_ASSERT(sock != NULL); + memcpy(&_this->peer, peer, peer->sa_len); + memcpy(&_this->sock, sock, sock->sa_len); + + /* 送信バッファの準備 */ + _this->winsz = L2TPD_DEFAULT_SEND_WINSZ; + if ((_this->snd_buffers = calloc(_this->winsz, sizeof(bytebuffer *))) + == NULL) { + l2tpd_log(_l2tpd, LOG_ERR, + "calloc() failed in %s(): %m", __func__); + goto reigai; + } + for (i = 0; i < _this->winsz; i++) { + if ((bytebuf = bytebuffer_create(L2TPD_SND_BUFSIZ)) == NULL) { + l2tpd_log(_l2tpd, LOG_ERR, + "bytebuffer_create() failed in %s(): %m", __func__); + goto reigai; + } + _this->snd_buffers[i] = bytebuf; + } + if ((_this->zlb_buffer = bytebuffer_create(sizeof(struct l2tp_header) + + 128)) == NULL) { + l2tpd_log(_l2tpd, LOG_ERR, + "bytebuffer_create() failed in %s(): %m", __func__); + goto reigai; + } +#ifdef USE_LIBSOCKUTIL + if (nat_t_ctx != NULL) { + if ((_this->sa_cookie = malloc( + sizeof(struct in_ipsec_sa_cookie))) != NULL) { + *(struct in_ipsec_sa_cookie *)_this->sa_cookie = + *(struct in_ipsec_sa_cookie *)nat_t_ctx; + } else { + l2tpd_log(_l2tpd, LOG_ERR, + "creating sa_cookie failed: %m"); + goto reigai; + } + } +#endif + _this->hello_interval = L2TP_CTRL_DEFAULT_HELLO_INTERVAL; + _this->hello_timeout = L2TP_CTRL_DEFAULT_HELLO_TIMEOUT; + _this->hello_io_time = curr_time; + + /* タイマーのセット */ + l2tp_ctrl_reset_timeout(_this); + + /* 登録 */ + l2tpd_add_ctrl(_l2tpd, _this); + return 0; +reigai: + l2tp_ctrl_stop(_this, 0); + return -1; +} + +/** + * {@link ::_l2tp_ctrl L2TP LNS コントロールコネクション} のインスタンスの + * 設定を行います。 + */ +static void +l2tp_ctrl_reload(l2tp_ctrl *_this) +{ + int ival; + + _this->data_use_seq = l2tp_ctrl_config_str_equal(_this, + "l2tp.data_use_seq", "true", 1); + + if ((ival = l2tp_ctrl_config_int(_this, "l2tp.hello_interval", 0))!= 0) + _this->hello_interval = ival; + if ((ival = l2tp_ctrl_config_int(_this, "l2tp.hello_timeout", 0)) != 0) + _this->hello_timeout = ival; + + return; +} + +/** + * {@link ::_l2tp_ctrl L2TP LNS コントロールコネクション}のインスタンスを解放 + * します。 + */ +void +l2tp_ctrl_destroy(l2tp_ctrl *_this) +{ + L2TP_CTRL_ASSERT(_this != NULL); +#ifdef USE_LIBSOCKUTIL + if (_this->sa_cookie != NULL) + free(_this->sa_cookie); +#endif + free(_this); +} + +/** + * 切断を先方に通知します。 + * + * @return CDN、StopCCN を送信済みの場合には 0。CDN を送信できていない + * コールが存在する場合にはその数、StopCCN の送信に失敗した場合 + * -1 が返ります。 + */ +static int +l2tp_ctrl_send_disconnect_notify(l2tp_ctrl *_this) +{ + int ncalls; + + L2TP_CTRL_ASSERT(_this != NULL) + L2TP_CTRL_ASSERT(_this->state == L2TP_CTRL_STATE_ESTABLISHED || + _this->state == L2TP_CTRL_STATE_CLEANUP_WAIT); + + // アクティブじゃなかったり、StopCCN を送信済み。 + if (_this->active_closing == 0) + return 0; + + // すべての Call に CDN + ncalls = 0; + if (slist_length(&_this->call_list) != 0) { + ncalls = l2tp_ctrl_disconnect_all_calls(_this); + if (ncalls > 0) { + /* + * 送信 Window が埋まっているかどうかを検査するために + * 再度呼び出す。ゼロになれば、すべての call に CDN を + * 送信し終わった。 + */ + ncalls = l2tp_ctrl_disconnect_all_calls(_this); + } + } + if (ncalls > 0) + return ncalls; + + if (l2tp_ctrl_send_StopCCN(_this, _this->active_closing) != 0) + return -1; + _this->active_closing = 0; + + return 0; +} + +/** + * コントロールコネクションを終了します。 + * + * <p> + * アクティブクローズの (StopCCN を送信する) 場合には、StopCCN の + * ResultCode AVP ように result に 1 以上の適切な値を指定してください。</p> + * <p> + * 戻り値が 0 の場合、_this は解放されていますので、l2tp_ctrl の処理を + * 続行することはできません。また、1 の場合(解放されていない場合) は、 + * タイマはリセットされています。</p> + * + * @return 完全に終了した場合には 0 を、まだ完全に終了していない場合 + * には、0 以外を返します。 + */ +int +l2tp_ctrl_stop(l2tp_ctrl *_this, int result) +{ + int i; + l2tpd *_l2tpd; + + L2TP_CTRL_ASSERT(_this != NULL); + + switch (_this->state) { + case L2TP_CTRL_STATE_ESTABLISHED: + _this->state = L2TP_CTRL_STATE_CLEANUP_WAIT; + if (result > 0) { + _this->active_closing = result; + l2tp_ctrl_send_disconnect_notify(_this); + break; + } + goto cleanup; + default: + l2tp_ctrl_log(_this, LOG_DEBUG, "%s() unexpected state=%s", + __func__, l2tp_ctrl_state_string(_this)); + // FALL THROUGH; + case L2TP_CTRL_STATE_WAIT_CTL_CONN: + // FALL THROUGH; + case L2TP_CTRL_STATE_CLEANUP_WAIT: +cleanup: + if (slist_length(&_this->call_list) != 0) { + if (l2tp_ctrl_disconnect_all_calls(_this) > 0) + break; + } +#if 0 + if (_this->l2tpd->purge_ipsec_sa != 0) + l2tp_ctrl_purge_ipsec_sa(_this); +#endif + + l2tp_ctrl_log(_this, LOG_NOTICE, "logtype=Finished"); + + evtimer_del(&_this->ev_timeout); + + /* 送信バッファの解放 */ + if (_this->snd_buffers != NULL) { + for (i = 0; i < _this->winsz; i++) + bytebuffer_destroy(_this->snd_buffers[i]); + free(_this->snd_buffers); + _this->snd_buffers = NULL; + } + if (_this->zlb_buffer != NULL) { + bytebuffer_destroy(_this->zlb_buffer); + _this->zlb_buffer = NULL; + } + /* l2tp_call の解放 */ + l2tp_ctrl_destroy_all_calls(_this); + slist_fini(&_this->call_list); + + l2tpd_remove_ctrl(_this->l2tpd, _this->tunnel_id); + + _l2tpd = _this->l2tpd; + l2tp_ctrl_destroy(_this); + + l2tpd_ctrl_finished_notify(_l2tpd); + return 0; // stopped + } + l2tp_ctrl_reset_timeout(_this); + + return 1; +} + +#if 0 +/** Delete the IPsec SA for disconnection */ +static void +l2tp_ctrl_purge_ipsec_sa(l2tp_ctrl *_this) +{ + int is_natt, proto; + struct sockaddr_in peer, sock; + hash_link *hl; +#ifdef USE_LIBSOCKUTIL + struct in_ipsec_sa_cookie *ipsec_sa_cookie; +#endif + l2tp_ctrl *anot; + + L2TP_CTRL_ASSERT(_this->peer.ss_family == AF_INET); + L2TP_CTRL_ASSERT(_this->sock.ss_family == AF_INET); + + /* + * Search another tunnel that uses the same IPsec SA + * by lineer. + */ + for (hl = hash_first(_this->l2tpd->ctrl_map); + hl != NULL; hl = hash_next(_this->l2tpd->ctrl_map)) { + anot = hl->item; + if (anot == _this) + continue; + switch (_this->peer.ss_family) { + case AF_INET: + if (SIN(&_this->peer)->sin_addr.s_addr == + SIN(&anot->peer)->sin_addr.s_addr) + break; + continue; + + default: + L2TP_CTRL_ASSERT(0); + /* Not implemented yet */ + } +#ifdef USE_LIBSOCKUTIL + if (_this->sa_cookie != NULL && + anot->sa_cookie != NULL) { + /* Both tunnels belong the same NAT box. */ + + if (memcmp(_this->sa_cookie, anot->sa_cookie, + sizeof(struct in_ipsec_sa_cookie)) != 0) + /* Different hosts behind the NAT box. */ + continue; + + /* The SA is shared by another tunnels by one host. */ + return; /* don't purge the sa */ + + } else if (_this->sa_cookie != NULL || + anot->sa_cookie != NULL) { + /* Only one is behind the NAT */ + continue; + } +#endif + return; /* don't purge the sa */ + } + +#ifdef USE_LIBSOCKUTIL + is_natt = (_this->sa_cookie != NULL)? 1 : 0; +#else + is_natt = 0; +#endif + memcpy(&peer, &_this->peer, sizeof(peer)); + memcpy(&sock, &_this->sock, sizeof(sock)); + if (!is_natt) { + proto = 0; + peer.sin_port = sock.sin_port = 0; + } +#ifdef USE_LIBSOCKUTIL + else { + ipsec_sa_cookie = _this->sa_cookie; + peer.sin_port = ipsec_sa_cookie->remote_port; + sock.sin_port = ipsec_sa_cookie->local_port; +#if 1 + /* + * XXX: As RFC 2367, protocol sould be specified if the port + * XXX: number is non-zero. + */ + proto = 0; +#else + proto = IPPROTO_UDP; +#endif + } +#endif + if (ipsec_util_purge_transport_sa((struct sockaddr *)&peer, + (struct sockaddr *)&sock, proto, IPSEC_UTIL_DIRECTION_BOTH) != 0) { + l2tp_ctrl_log(_this, LOG_NOTICE, "failed to purge IPSec SA"); + } +} +#endif + +/** タイマー関連処理 */ +static void +l2tp_ctrl_timeout(int fd, short evtype, void *ctx) +{ + int next_timeout, need_resend; + time_t curr_time; + l2tp_ctrl *_this; + l2tp_call *call; + + /* + * この関数から抜ける場合は、タイマをリセットしなければならない。 + * l2tp_ctrl_stop は、l2tp_ctrl_stop 内でタイマをリセットする。 + * l2tp_ctrl_stop は、_this を解放する可能性がある点にも注意。 + */ + _this = ctx; + L2TP_CTRL_ASSERT(_this != NULL); + + curr_time = get_monosec(); + + next_timeout = 2; + need_resend = 0; + + if (l2tp_ctrl_txwin_size(_this) > 0) { + if (_this->state == L2TP_CTRL_STATE_ESTABLISHED) { + if (_this->hello_wait_ack != 0) { + /* Hello 応答待ち */ + if (curr_time - _this->hello_io_time >= + _this->hello_timeout) { + l2tp_ctrl_log(_this, LOG_NOTICE, + "timeout waiting ack for hello " + "packets."); + l2tp_ctrl_stop(_this, + L2TP_STOP_CCN_RCODE_GENERAL); + return; + } + } + } else if (curr_time - _this->last_snd_ctrl >= + L2TP_CTRL_CTRL_PKT_TIMEOUT) { + l2tp_ctrl_log(_this, LOG_NOTICE, + "timeout waiting ack for ctrl packets."); + l2tp_ctrl_stop(_this, + L2TP_STOP_CCN_RCODE_GENERAL); + return; + } + need_resend = 1; + } else { + for (slist_itr_first(&_this->call_list); + slist_itr_has_next(&_this->call_list);) { + call = slist_itr_next(&_this->call_list); + if (call->state == L2TP_CALL_STATE_CLEANUP_WAIT) { + l2tp_call_destroy(call, 1); + slist_itr_remove(&_this->call_list); + } + } + } + + switch (_this->state) { + case L2TP_CTRL_STATE_IDLE: + /* + * idle の場合 + * この実装ではあり得ない。 + */ + l2tp_ctrl_log(_this, LOG_ERR, + "Internal error, timeout on illegal state=idle"); + l2tp_ctrl_stop(_this, L2TP_STOP_CCN_RCODE_GENERAL); + break; + case L2TP_CTRL_STATE_WAIT_CTL_CONN: + /* + * wait-ctrl-conn の場合 + * SCCRP に対する確認応答がない場合は、先方は SCCRQ + * を再送するが、この実装側で「再送」であることを検知 + * できない。こちらからは再送しない。 + */ + need_resend = 0; + break; + case L2TP_CTRL_STATE_ESTABLISHED: + if (slist_length(&_this->call_list) == 0 && + curr_time - _this->last_snd_ctrl >= + L2TP_CTRL_WAIT_CALL_TIMEOUT) { + if (_this->ncalls == 0) + /* 最初の call がこない。 */ + l2tp_ctrl_log(_this, LOG_WARNING, + "timeout waiting call"); + l2tp_ctrl_stop(_this, + L2TP_STOP_CCN_RCODE_GENERAL); + return; + } + if (_this->hello_wait_ack == 0 && _this->hello_interval > 0) { + /* + * Hello 送信 + */ + if (curr_time - _this->hello_interval >= + _this->hello_io_time) { + if (l2tp_ctrl_send_HELLO(_this) == 0) + /* 成功した場合 */ + _this->hello_wait_ack = 1; + _this->hello_io_time = curr_time; + need_resend = 0; + } + } + break; + case L2TP_CTRL_STATE_CLEANUP_WAIT: + if (curr_time - _this->last_snd_ctrl >= + L2TP_CTRL_CLEANUP_WAIT_TIME) { + l2tp_ctrl_log(_this, LOG_NOTICE, + "Cleanup timeout state=%d", _this->state); + l2tp_ctrl_stop(_this, 0); + return; + } + if (_this->active_closing != 0) + l2tp_ctrl_send_disconnect_notify(_this); + break; + default: + l2tp_ctrl_log(_this, LOG_ERR, + "Internal error, timeout on illegal state=%d", + _this->state); + l2tp_ctrl_stop(_this, L2TP_STOP_CCN_RCODE_GENERAL); + return; + } + /* 再送の必要があれば、再送 */ + if (need_resend) + l2tp_ctrl_resend_una_packets(_this); + l2tp_ctrl_reset_timeout(_this); +} + +int +l2tp_ctrl_send(l2tp_ctrl *_this, const void *msg, int len) +{ + int rval; + +#ifdef USE_LIBSOCKUTIL + if (_this->sa_cookie != NULL) + rval = sendfromto_nat_t(LISTENER_SOCK(_this), msg, len, 0, + (struct sockaddr *)&_this->sock, + (struct sockaddr *)&_this->peer, _this->sa_cookie); + else + rval = sendfromto(LISTENER_SOCK(_this), msg, len, 0, + (struct sockaddr *)&_this->sock, + (struct sockaddr *)&_this->peer); +#else + rval = sendto(LISTENER_SOCK(_this), msg, len, 0, + (struct sockaddr *)&_this->peer, _this->peer.ss_len); +#endif + return rval; +} + +/** 確認応答待ちのパケットを再送する。 */ +static int +l2tp_ctrl_resend_una_packets(l2tp_ctrl *_this) +{ + uint16_t seq; + bytebuffer *bytebuf; + struct l2tp_header *header; + int nsend; + + nsend = 0; + for (seq = _this->snd_una; SEQ_LT(seq, _this->snd_nxt); seq++) { + bytebuf = _this->snd_buffers[seq % _this->winsz]; + header = bytebuffer_pointer(bytebuf); + header->nr = htons(_this->rcv_nxt); +#ifdef L2TP_CTRL_DEBUG + if (debuglevel >= 3) { + l2tp_ctrl_log(_this, DEBUG_LEVEL_3, "RESEND seq=%u", + ntohs(header->ns)); + show_hd(debug_get_debugfp(), + bytebuffer_pointer(bytebuf), + bytebuffer_remaining(bytebuf)); + } +#endif + if (l2tp_ctrl_send(_this, bytebuffer_pointer(bytebuf), + bytebuffer_remaining(bytebuf)) < 0) { + l2tp_ctrl_log(_this, LOG_ERR, + "sendto() failed in %s: %m", __func__); + return -1; + } + nsend++; + } + return nsend; +} + +/** + * すべてのコールを解放します。 + */ +static void +l2tp_ctrl_destroy_all_calls(l2tp_ctrl *_this) +{ + l2tp_call *call; + + L2TP_CTRL_ASSERT(_this != NULL); + + while ((call = slist_remove_first(&_this->call_list)) != NULL) + l2tp_call_destroy(call, 1); +} + +/** + * このコントロールの全ての call を切断します。 + * @return 解放待ちになっていない call の数を返します。 + */ +static int +l2tp_ctrl_disconnect_all_calls(l2tp_ctrl *_this) +{ + int i, len, ncalls; + l2tp_call *call; + + L2TP_CTRL_ASSERT(_this != NULL); + + ncalls = 0; + len = slist_length(&_this->call_list); + for (i = 0; i < len; i++) { + call = slist_get(&_this->call_list, i); + if (call->state != L2TP_CALL_STATE_CLEANUP_WAIT) { + ncalls++; + + if (l2tp_ctrl_txwin_is_full(_this)) { + L2TP_CTRL_DBG((_this, LOG_INFO, + "Too many calls. Sending window is not " + "enough to send CDN to all clients.")); + /* nothing to do */ + } else + l2tp_call_admin_disconnect(call); + } + } + return ncalls; +} + +/** + * タイムアウトを再設定します。 + */ +static void +l2tp_ctrl_reset_timeout(l2tp_ctrl *_this) +{ + int intvl; + struct timeval tv0; + + L2TP_CTRL_ASSERT(_this != NULL); + + if (evtimer_initialized(&_this->ev_timeout)) + evtimer_del(&_this->ev_timeout); + + switch (_this->state) { + case L2TP_CTRL_STATE_CLEANUP_WAIT: + intvl = 1; + break; + default: + intvl = 2; + break; + } + tv0.tv_usec = 0; + tv0.tv_sec = intvl; + if (!evtimer_initialized(&_this->ev_timeout)) + evtimer_set(&_this->ev_timeout, l2tp_ctrl_timeout, _this); + evtimer_add(&_this->ev_timeout, &tv0); +} + +/*********************************************************************** + * プロトコル - 送受信 + ***********************************************************************/ +/** + * パケット受信 + */ +void +l2tp_ctrl_input(l2tpd *_this, int listener_index, struct sockaddr *peer, + struct sockaddr *sock, void *nat_t_ctx, u_char *pkt, int pktlen) +{ + int i, len, offsiz, reqlen, is_ctrl; + uint16_t mestype; + struct sockaddr_in *peersin, *socksin; + struct l2tp_avp *avp, *avp0; + l2tp_ctrl *ctrl; + l2tp_call *call; + char buf[L2TP_AVP_MAXSIZ], errmsg[256]; + time_t curr_time; + u_char *pkt0; + char ifname[IF_NAMESIZE], phy_label[256]; + struct l2tp_header hdr; + + ctrl = NULL; + curr_time = get_monosec(); + pkt0 = pkt; + + L2TP_CTRL_ASSERT(peer->sa_family == AF_INET); + L2TP_CTRL_ASSERT(sock->sa_family == AF_INET); + + if (peer->sa_family != AF_INET) { + l2tpd_log(_this, LOG_ERR, + "Received a packet peer unknown address " + "family=%d", peer->sa_family); + return; // ここまでは reigai に飛ばさない + } + peersin = (struct sockaddr_in *)peer; + socksin = (struct sockaddr_in *)sock; + + /* + * Parse L2TP Header + */ + memset(&hdr, 0, sizeof(hdr)); + if (pktlen < 2) { + snprintf(errmsg, sizeof(errmsg), "a short packet. " + "length=%d", pktlen); + goto bad_packet; + } + memcpy(&hdr, pkt, 2); + pkt += 2; + if (hdr.ver != L2TP_HEADER_VERSION_RFC2661) { + /* 現在 RFC2661 のみサポートします */ + snprintf(errmsg, sizeof(errmsg), + "Unsupported version at header = %d", hdr.ver); + goto bad_packet; + } + is_ctrl = (hdr.t != 0)? 1 : 0; + + /* calc required length */ + reqlen = 6; /* for Flags, Tunnel-Id, Session-Id field */ + if (hdr.l) reqlen += 2; /* for Length field (opt) */ + if (hdr.s) reqlen += 4; /* for Ns, Nr field (opt) */ + if (hdr.o) reqlen += 2; /* for Offset Size field (opt) */ + if (reqlen > pktlen) { + snprintf(errmsg, sizeof(errmsg), + "a short packet. length=%d", pktlen); + goto bad_packet; + } + + if (hdr.l != 0) { + GETSHORT(hdr.length, pkt); + if (hdr.length > pktlen) { + snprintf(errmsg, sizeof(errmsg), + "Actual packet size is smaller than the length " + "field %d < %d", pktlen, hdr.length); + goto bad_packet; + } + pktlen = hdr.length; /* remove trailing trash */ + } + GETSHORT(hdr.tunnel_id, pkt); + GETSHORT(hdr.session_id, pkt); + if (hdr.s != 0) { + GETSHORT(hdr.ns, pkt); + GETSHORT(hdr.nr, pkt); + } + if (hdr.o != 0) { + GETSHORT(offsiz, pkt); + if (pktlen < offsiz) { + snprintf(errmsg, sizeof(errmsg), + "offset field is bigger than remaining packet " + "length %d > %d", offsiz, pktlen); + goto bad_packet; + } + pkt += offsiz; + } + L2TP_CTRL_ASSERT(pkt - pkt0 == reqlen); + pktlen -= (pkt - pkt0); /* cut down the length of header */ + + ctrl = NULL; + memset(buf, 0, sizeof(buf)); + mestype = 0; + avp = NULL; + + if (is_ctrl) { + avp0 = (struct l2tp_avp *)buf; + avp = avp_find_message_type_avp(avp0, pkt, pktlen); + if (avp != NULL) + mestype = avp->attr_value[0] << 8 | avp->attr_value[1]; + } + ctrl = l2tpd_get_ctrl(_this, hdr.tunnel_id); + + if (ctrl == NULL) { + /* 新しいコントロール */ + if (!is_ctrl) { + snprintf(errmsg, sizeof(errmsg), + "bad data message: tunnelId=%d is not " + "found.", hdr.tunnel_id); + goto bad_packet; + } + if (mestype != L2TP_AVP_MESSAGE_TYPE_SCCRQ) { + snprintf(errmsg, sizeof(errmsg), + "bad control message: tunnelId=%d is not " + "found. mestype=%s", hdr.tunnel_id, + avp_mes_type_string(mestype)); + goto bad_packet; + } + + strlcpy(phy_label, + ((l2tpd_listener *)slist_get(&_this->listener, + listener_index))->phy_label, sizeof(phy_label)); + if (_this->phy_label_with_ifname != 0) { + if (get_ifname_by_sockaddr(sock, ifname) == NULL) { + l2tpd_log_access_deny(_this, + "could not get interface informations", + peer); + goto reigai; + } + if (l2tpd_config_str_equal(_this, + config_key_prefix("l2tpd.interface", ifname), + "accept", 0)){ + strlcat(phy_label, "%", sizeof(phy_label)); + strlcat(phy_label, ifname, sizeof(phy_label)); + } else if (l2tpd_config_str_equal(_this, + config_key_prefix("l2tpd.interface", "any"), + "accept", 0)){ + } else { + /* このインタフェースは許可されていない。*/ + snprintf(errmsg, sizeof(errmsg), + "'%s' is not allowed by config.", ifname); + l2tpd_log_access_deny(_this, errmsg, peer); + goto reigai; + } +#if defined(_SEIL_EXT_) && !defined(USE_LIBSOCKUTIL) + if (!rtev_ifa_is_primary(ifname, sock)) { + char hostbuf[NI_MAXHOST]; + getnameinfo(sock, sock->sa_len, hostbuf, + sizeof(hostbuf), NULL, 0, NI_NUMERICHOST); + snprintf(errmsg, sizeof(errmsg), + "connecting to %s (an alias address of %s)" + " is not allowed by this version.", + hostbuf, ifname); + l2tpd_log_access_deny(_this, errmsg, peer); + goto reigai; + } +#endif + } + + if ((ctrl = l2tp_ctrl_create()) == NULL) { + l2tp_ctrl_log(ctrl, LOG_ERR, + "l2tp_ctrl_create() failed: %m"); + goto reigai; + } + if (l2tp_ctrl_init(ctrl, _this, peer, sock, nat_t_ctx) != 0) { + l2tp_ctrl_log(ctrl, LOG_ERR, + "l2tp_ctrl_start() failed: %m"); + goto reigai; + } + + ctrl->listener_index = listener_index; + strlcpy(ctrl->phy_label, phy_label, sizeof(ctrl->phy_label)); + l2tp_ctrl_reload(ctrl); + } else { + /* + * 始点アドレス/ポートが異なる場合には、エラーとする。(DoS + * の可能性があるので) + */ + L2TP_CTRL_ASSERT(ctrl->peer.ss_family == peer->sa_family); + + switch (peer->sa_family) { + case AF_INET: + { + struct sockaddr_in *peersin1; + + peersin1 = (struct sockaddr_in *)&ctrl->peer; + if (peersin1->sin_addr.s_addr != + peersin->sin_addr.s_addr || + peersin1->sin_port != peersin->sin_port) { + snprintf(errmsg, sizeof(errmsg), + "tunnelId=%u is already assigned for %s:%u", + hdr.tunnel_id, + inet_ntoa(peersin1->sin_addr), + ntohs(peersin1->sin_port)); + goto bad_packet; + } + } + } + } + ctrl->last_rcv = curr_time; + call = NULL; + if (hdr.session_id != 0) { + /* Session Id から l2tp_call を探す。リニアサーチ */ + len = slist_length(&ctrl->call_list); + for (i = 0; i < len; i++) { + call = slist_get(&ctrl->call_list, i); + if (call->session_id == hdr.session_id) + break; + call = NULL; + } + } + if (!is_ctrl) { + /* + * L2TP データ + */ + if (ctrl->state != L2TP_CTRL_STATE_ESTABLISHED) { + l2tp_ctrl_log(ctrl, LOG_WARNING, + "Received Data packet in '%s'", + l2tp_ctrl_state_string(ctrl)); + goto reigai; + } + if (call == NULL) { + l2tp_ctrl_log(ctrl, LOG_WARNING, + "Received a data packet but it has no call. " + "session_id=%u", hdr.session_id); + goto reigai; + } + L2TP_CTRL_DBG((ctrl, DEBUG_LEVEL_2, + "call=%u RECV ns=%u nr=%u snd_nxt=%u rcv_nxt=%u len=%d", + call->id, hdr.ns, hdr.nr, call->snd_nxt, call->rcv_nxt, + pktlen)); + if (call->state != L2TP_CALL_STATE_ESTABLISHED){ + l2tp_ctrl_log(ctrl, LOG_WARNING, + "Received a data packet but call is not " + "established"); + goto reigai; + } + + if (hdr.s != 0) { + if (SEQ_LT(hdr.ns, call->rcv_nxt)) { + /* シーケンスが戻った。*/ + /* 統計情報に残すべきかもしれない */ + L2TP_CTRL_DBG((ctrl, LOG_DEBUG, + "receive a out of sequence data packet: " + "%u < %u. ", hdr.ns, call->rcv_nxt)); + return; + } + call->rcv_nxt = hdr.ns + 1; + } + l2tp_call_ppp_input(call, pkt, pktlen); + + return; + } + if (hdr.s != 0) { + L2TP_CTRL_DBG((ctrl, DEBUG_LEVEL_2, + "RECV %s ns=%u nr=%u snd_nxt=%u snd_una=%u rcv_nxt=%u " + "len=%d", (is_ctrl)? "C" : "", hdr.ns, hdr.nr, + ctrl->snd_nxt, ctrl->snd_una, ctrl->rcv_nxt, pktlen)); + + if (pktlen <= 0) + l2tp_ctrl_log(ctrl, LOG_INFO, "RecvZLB"); + + if (SEQ_GT(hdr.nr, ctrl->snd_una)) { + if (hdr.nr == ctrl->snd_nxt || + SEQ_LT(hdr.nr, ctrl->snd_nxt)) + ctrl->snd_una = hdr.nr; + else { + l2tp_ctrl_log(ctrl, LOG_INFO, + "Received message has bad Nr field: " + "%u < %u.", hdr.ns, ctrl->snd_nxt); + /* XXX Drop with ZLB? */ + goto reigai; + } + } + if (l2tp_ctrl_txwin_size(ctrl) <= 0) { + /* 確認応答待ちなし */ + if (ctrl->hello_wait_ack != 0) { + /* + * Hello に対する Ack が返ったので、Hello + * の状態をリセット + */ + ctrl->hello_wait_ack = 0; + ctrl->hello_io_time = curr_time; + } + switch (ctrl->state) { + case L2TP_CTRL_STATE_CLEANUP_WAIT: + l2tp_ctrl_stop(ctrl, 0); + return; + } + } + if (hdr.ns != ctrl->rcv_nxt) { + // 受信してないパケットがある + if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) { + // 再送または ZLB 送信 + l2tp_ctrl_send_ZLB(ctrl); + } +#ifdef L2TP_CTRL_DEBUG + if (pktlen != 0) { // ZLB ではない。 + L2TP_CTRL_DBG((ctrl, LOG_DEBUG, + "receive out of sequence %u must be %u. " + "mestype=%s", hdr.ns, ctrl->rcv_nxt, + avp_mes_type_string(mestype))); + } +#endif + return; + } + if (pktlen <= 0) + return; /* ZLB */ + + if (l2tp_ctrl_txwin_is_full(ctrl)) { + L2TP_CTRL_DBG((ctrl, LOG_DEBUG, + "Received message cannot be handled. " + "Transmission window is full.")); + l2tp_ctrl_send_ZLB(ctrl); + return; + } + + ctrl->rcv_nxt++; + if (avp == NULL) { + l2tpd_log(_this, LOG_WARNING, + "bad control message: no message-type AVP."); + goto reigai; + } + } + + /* + * ステートマシン (RFC2661 pp. 56-57) + */ + switch (ctrl->state) { + case L2TP_CTRL_STATE_IDLE: + switch (mestype) { + case L2TP_AVP_MESSAGE_TYPE_SCCRQ: + if (l2tp_ctrl_recv_SCCRQ(ctrl, pkt, pktlen, _this, + peer) == 0) { + // acceptable + l2tp_ctrl_send_SCCRP(ctrl); + ctrl->state = L2TP_CTRL_STATE_WAIT_CTL_CONN; + return; + } + /* + * un-accectable な場合は、l2tp_ctrl_recv_SCCRQ 側で + * 処理済みです。 + */ + return; + case L2TP_AVP_MESSAGE_TYPE_SCCRP: + /* + * RFC上は StopCCN を送信するが、この LNS の実装では、 + * Passive Open だけなので、 このパケットは受け取らな + * いはず。 + */ + // FALL THROUGH + case L2TP_AVP_MESSAGE_TYPE_SCCCN: + default: + break; + } + goto fsm_reigai; + + case L2TP_CTRL_STATE_WAIT_CTL_CONN: + /* Wait-Ctl-Conn */ + switch (mestype) { + case L2TP_AVP_MESSAGE_TYPE_SCCCN: + l2tp_ctrl_log(ctrl, LOG_INFO, "RecvSCCN"); + if (l2tp_ctrl_send_ZLB(ctrl) == 0) { + ctrl->state = L2TP_CTRL_STATE_ESTABLISHED; + } + return; + case L2TP_AVP_MESSAGE_TYPE_StopCCN: + goto receive_stop_ccn; + case L2TP_AVP_MESSAGE_TYPE_SCCRQ: + case L2TP_AVP_MESSAGE_TYPE_SCCRP: + default: + break; + } + break; /* fsm_reigai */ + case L2TP_CTRL_STATE_ESTABLISHED: + /* Established */ + switch (mestype) { + case L2TP_AVP_MESSAGE_TYPE_SCCCN: + case L2TP_AVP_MESSAGE_TYPE_SCCRQ: + case L2TP_AVP_MESSAGE_TYPE_SCCRP: + break; +receive_stop_ccn: + case L2TP_AVP_MESSAGE_TYPE_StopCCN: + if (l2tp_ctrl_recv_StopCCN(ctrl, pkt, pktlen) == 0) { + if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) + l2tp_ctrl_send_ZLB(ctrl); + l2tp_ctrl_stop(ctrl, 0); + return; + } + l2tp_ctrl_log(ctrl, LOG_ERR, "Received bad StopCCN"); + l2tp_ctrl_send_ZLB(ctrl); + l2tp_ctrl_stop(ctrl, 0); + return; + + case L2TP_AVP_MESSAGE_TYPE_HELLO: + if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) + l2tp_ctrl_send_ZLB(ctrl); + return; + case L2TP_AVP_MESSAGE_TYPE_CDN: + case L2TP_AVP_MESSAGE_TYPE_ICRP: + case L2TP_AVP_MESSAGE_TYPE_ICCN: + if (call == NULL) { + l2tp_ctrl_log(ctrl, LOG_INFO, + "Unknown call message: %s", + avp_mes_type_string(mestype)); + goto reigai; + } + // FALL THROUGH + case L2TP_AVP_MESSAGE_TYPE_ICRQ: + l2tp_call_recv_packet(ctrl, call, mestype, pkt, + pktlen); + return; + default: + break; + } + break; /* fsm_reigai */ + case L2TP_CTRL_STATE_CLEANUP_WAIT: + if (mestype == L2TP_AVP_MESSAGE_TYPE_StopCCN) { + /* + * StopCCN が交錯したか、Window が埋まっていて + * StopCCN が送信できない間に、StopCCN を受信 + */ + goto receive_stop_ccn; + } + break; /* fsm_reigai */ + } + +fsm_reigai: + /* ステートマシンのエラー */ + l2tp_ctrl_log(ctrl, LOG_WARNING, "Received %s in '%s' state", + avp_mes_type_string(mestype), l2tp_ctrl_state_string(ctrl)); + l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_FSM_ERROR); + + return; +reigai: + if (ctrl != NULL && mestype != 0) { + l2tp_ctrl_log(ctrl, LOG_WARNING, "Received %s in '%s' state", + avp_mes_type_string(mestype), l2tp_ctrl_state_string(ctrl)); + l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_GENERAL_ERROR); + } + return; + +bad_packet: + l2tpd_log(_this, LOG_INFO, "Received from=%s:%u: %s", + inet_ntoa(peersin->sin_addr), ntohs(peersin->sin_port), errmsg); + return; +} + +static inline int +l2tp_ctrl_txwin_size(l2tp_ctrl *_this) +{ + uint16_t sz; + + sz = _this->snd_nxt - _this->snd_una; + + L2TP_CTRL_ASSERT(sz <= _this->winsz); + + return sz; +} + +static inline int +l2tp_ctrl_txwin_is_full(l2tp_ctrl *_this) +{ + return (l2tp_ctrl_txwin_size(_this) >= _this->winsz)? 1 : 0; +} + +/** パケットの送信 */ +int +l2tp_ctrl_send_packet(l2tp_ctrl *_this, int call_id, bytebuffer *bytebuf, + int is_ctrl) +{ + struct l2tp_header *hdr; + int rval, use_seq; + time_t curr_time; + + curr_time = get_monosec(); + +#ifdef L2TP_DATA_WITH_SEQUENCE + use_seq = 1; +#else + use_seq = is_ctrl; +#endif + + bytebuffer_flip(bytebuf); + hdr = (struct l2tp_header *)bytebuffer_pointer(bytebuf); + memset(hdr, 0, sizeof(*hdr)); + + hdr->t = 1; + hdr->ver = L2TP_HEADER_VERSION_RFC2661; + hdr->l = 1; + hdr->length = htons(bytebuffer_remaining(bytebuf)); + hdr->tunnel_id = htons(_this->peer_tunnel_id); + hdr->session_id = htons(call_id); + + hdr->s = 1; + hdr->ns = htons(_this->snd_nxt); + hdr->nr = htons(_this->rcv_nxt); + + if (is_ctrl && + bytebuffer_remaining(bytebuf) > sizeof(struct l2tp_header)) + /* Not ZLB */ + _this->snd_nxt++; + + L2TP_CTRL_DBG((_this, DEBUG_LEVEL_2, + "SEND %s ns=%u nr=%u snd_nxt=%u snd_una=%u rcv_nxt=%u ", + (is_ctrl)? "C" : " ", ntohs(hdr->ns), htons(hdr->nr), + _this->snd_nxt, _this->snd_una, _this->rcv_nxt)); + + if (_this->l2tpd->ctrl_out_pktdump != 0) { + l2tpd_log(_this->l2tpd, LOG_DEBUG, + "L2TP Control output packet dump"); + show_hd(debug_get_debugfp(), bytebuffer_pointer(bytebuf), + bytebuffer_remaining(bytebuf)); + } + + if ((rval = l2tp_ctrl_send(_this, bytebuffer_pointer(bytebuf), + bytebuffer_remaining(bytebuf))) < 0) { + L2TP_CTRL_DBG((_this, LOG_DEBUG, "sendto() failed: %m")); + } + + _this->last_snd_ctrl = curr_time; + + return (rval == bytebuffer_remaining(bytebuf))? 0 : 1; +} + +/** + * SCCRQ の受信 + */ +static int +l2tp_ctrl_recv_SCCRQ(l2tp_ctrl *_this, u_char *pkt, int pktlen, l2tpd *_l2tpd, + struct sockaddr *peer) +{ + int avpsz, len, protover, protorev, firmrev, result; + struct l2tp_avp *avp; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + char buf[L2TP_AVP_MAXSIZ], emes[256], hostname[256], vendorname[256]; + + result = L2TP_STOP_CCN_RCODE_GENERAL_ERROR; + strlcpy(hostname, "(no hostname)", sizeof(hostname)); + strlcpy(vendorname, "(no vendorname)", sizeof(vendorname)); + + firmrev = 0; + protover = 0; + protorev = 0; + avp = (struct l2tp_avp *)buf; + while (pktlen >= 6 && (avpsz = avp_enum(avp, pkt, pktlen, 1)) > 0) { + pkt += avpsz; + pktlen -= avpsz; + if (avp->vendor_id != 0) { + L2TP_CTRL_DBG((_this, LOG_DEBUG, + "Received a Vendor-specific AVP vendor-id=%d " + "type=%d", avp->vendor_id, avp->attr_type)); + continue; + } + switch (avp->attr_type) { + case L2TP_AVP_TYPE_MESSAGE_TYPE: + AVP_SIZE_CHECK(avp, ==, 8); + continue; + case L2TP_AVP_TYPE_PROTOCOL_VERSION: + AVP_SIZE_CHECK(avp, ==, 8); + protover = avp->attr_value[0]; + protorev = avp->attr_value[1]; + + if (protover != L2TP_RFC2661_VERSION || + protorev != L2TP_RFC2661_REVISION) { + result = L2TP_STOP_CCN_RCODE_GENERAL_ERROR; + snprintf(emes, sizeof(emes), + "Peer's protocol version is not supported:" + " %d.%d", protover, protorev); + goto not_acceptable; + } + continue; + case L2TP_AVP_TYPE_FRAMING_CAPABILITIES: + AVP_SIZE_CHECK(avp, ==, 10); + if ((avp_get_val32(avp) & L2TP_FRAMING_CAP_FLAGS_SYNC) + == 0) { + L2TP_CTRL_DBG((_this, LOG_DEBUG, "Peer doesn't " + "support synchronous framing")); + } + continue; + case L2TP_AVP_TYPE_BEARER_CAPABILITIES: + AVP_SIZE_CHECK(avp, ==, 10); + continue; + case L2TP_AVP_TYPE_TIE_BREAKER: + AVP_SIZE_CHECK(avp, ==, 14); + /* + * この実装からは SCCRQ は送らないので常に peer が + * winner。 + */ + continue; + case L2TP_AVP_TYPE_FIRMWARE_REVISION: + AVP_SIZE_CHECK(avp, >=, 6); + firmrev = avp_get_val16(avp); + continue; + case L2TP_AVP_TYPE_HOST_NAME: + AVP_SIZE_CHECK(avp, >, 4); + len = MIN(sizeof(hostname) - 1, avp->length - 6); + memcpy(hostname, avp->attr_value, len); + hostname[len] = '\0'; + continue; + case L2TP_AVP_TYPE_VENDOR_NAME: + AVP_SIZE_CHECK(avp, >, 4); + len = MIN(sizeof(vendorname) - 1, avp->length - 6); + memcpy(vendorname, avp->attr_value, len); + vendorname[len] = '\0'; + continue; + case L2TP_AVP_TYPE_ASSINGED_TUNNEL_ID: + AVP_SIZE_CHECK(avp, ==, 8); + _this->peer_tunnel_id = avp_get_val16(avp); + continue; + case L2TP_AVP_TYPE_RECV_WINDOW_SIZE: + AVP_SIZE_CHECK(avp, ==, 8); + _this->peer_winsz = avp_get_val16(avp); + continue; + } + if (avp->is_mandatory) { + l2tp_ctrl_log(_this, LOG_WARNING, + "Received AVP (%s/%d) is not supported, but it's " + "mandatory", avp_attr_type_string(avp->attr_type), + avp->attr_type); +#ifdef L2TP_CTRL_DEBUG + } else { + L2TP_CTRL_DBG((_this, LOG_DEBUG, + "AVP (%s/%d) is not handled", + avp_attr_type_string(avp->attr_type), + avp->attr_type)); +#endif + } + } + if (getnameinfo((struct sockaddr *)&_this->peer, _this->peer.ss_len, + host, sizeof(host), serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM) != 0) { + l2tp_ctrl_log(_this, LOG_ERR, + "getnameinfo() failed at %s(): %m", __func__); + strlcpy(host, "error", sizeof(host)); + strlcpy(serv, "error", sizeof(serv)); + } + l2tp_ctrl_log(_this, LOG_NOTICE, "logtype=Started RecvSCCRQ " + "from=%s:%s/udp tunnel_id=%u/%u protocol=%d.%d winsize=%d " + "hostname=%s vendor=%s firm=%04X", host, serv, _this->tunnel_id, + _this->peer_tunnel_id, protover, protorev, _this->peer_winsz, + hostname, vendorname, firmrev); + + return 0; +not_acceptable: +size_check_failed: + l2tp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: %s", emes); + l2tp_ctrl_stop(_this, result); + + return 1; +} + +/** + * StopCCN を送信します。 + */ +static int +l2tp_ctrl_send_StopCCN(l2tp_ctrl *_this, int result) +{ + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ]; + bytebuffer *bytebuf; + + if ((bytebuf = l2tp_ctrl_prepare_snd_buffer(_this, 1)) == NULL) { + l2tp_ctrl_log(_this, LOG_ERR, + "sending StopCCN failed: no buffer."); + return -1; + } + avp = (struct l2tp_avp *)buf; + + /* Message Type = StopCCN */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_MESSAGE_TYPE; + avp_set_val16(avp, L2TP_AVP_MESSAGE_TYPE_StopCCN); + bytebuf_add_avp(bytebuf, avp, 2); + + /* Assigned Tunnel Id */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_ASSINGED_TUNNEL_ID; + avp_set_val16(avp, _this->tunnel_id); + bytebuf_add_avp(bytebuf, avp, 2); + + /* Result Code */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_RESULT_CODE; + avp_set_val16(avp, result); + bytebuf_add_avp(bytebuf, avp, 2); + + if (l2tp_ctrl_send_packet(_this, 0, bytebuf, 1) != 0) { + l2tp_ctrl_log(_this, LOG_ERR, "sending CCN failed"); + return - 1; + } + l2tp_ctrl_log(_this, LOG_INFO, "SendStopCCN result=%d", result); + + return 0; +} + +/** + * StopCCN の受信 + */ +static int +l2tp_ctrl_recv_StopCCN(l2tp_ctrl *_this, u_char *pkt, int pktlen) +{ + int avpsz; + uint32_t val32; + uint16_t rcode, tunid, ecode; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ + 16], emes[256], peermes[256]; + + rcode = 0; + ecode = 0; + tunid = 0; + peermes[0] = '\0'; + avp = (struct l2tp_avp *)buf; + while (pktlen >= 6 && (avpsz = avp_enum(avp, pkt, pktlen, 1)) > 0) { + pkt += avpsz; + pktlen -= avpsz; + if (avp->vendor_id != 0) { + L2TP_CTRL_DBG((_this, LOG_DEBUG, + "Received a Vendor-specific AVP vendor-id=%d " + "type=%d", avp->vendor_id, avp->attr_type)); + continue; + } + if (avp->is_hidden != 0) { + l2tp_ctrl_log(_this, LOG_WARNING, + "Received AVP (%s/%d) is hidden. But we don't " + "share secret.", + avp_attr_type_string(avp->attr_type), + avp->attr_type); + if (avp->is_mandatory != 0) { + l2tp_ctrl_stop(_this, + L2TP_STOP_CCN_RCODE_GENERAL_ERROR | + L2TP_ECODE_UNKNOWN_MANDATORY_AVP); + return 1; + } + continue; + } + switch (avp->attr_type) { + case L2TP_AVP_TYPE_MESSAGE_TYPE: + AVP_SIZE_CHECK(avp, ==, 8); + continue; + case L2TP_AVP_TYPE_RESULT_CODE: + AVP_SIZE_CHECK(avp, >=, 10); + val32 = avp_get_val32(avp); + rcode = val32 >> 16; + ecode = val32 & 0xffff; + if (avp->length > 10) { + avp->attr_value[avp->length - 6] = '\0'; + strlcpy(peermes, + (const char *)avp->attr_value + 4, + sizeof(peermes)); + } + continue; + case L2TP_AVP_TYPE_ASSINGED_TUNNEL_ID: + AVP_SIZE_CHECK(avp, ==, 8); + tunid = avp_get_val16(avp); + continue; + default: + if (avp->is_mandatory != 0) { + l2tp_ctrl_log(_this, LOG_WARNING, + "Received AVP (%s/%d) is not supported, " + "but it's mandatory", + avp_attr_type_string(avp->attr_type), + avp->attr_type); +#ifdef L2TP_CTRL_DEBUG + } else { + L2TP_CTRL_DBG((_this, LOG_DEBUG, + "AVP (%s/%d) is not handled", + avp_attr_type_string(avp->attr_type), + avp->attr_type)); +#endif + } + } + } + + if (rcode == L2TP_CDN_RCODE_ERROR_CODE && + ecode == L2TP_ECODE_NO_RESOURCE) { + /* + * 現在観測された状況 + * + * (1) IDGWとWindows が同一 LAN セグメント上にあり、 + * Windows の(そのLAN IP アドレスが、ナチュラルマスク + * で評価した場合のブロードキャストアドレスだった場合 + * (192.168.0.255/23など) + * (2) Windows 2000 を起動しっぱなしで、L2TPの接続切断を繰り + * 返すと、あるタイミングからこの状況に陥り、接続できない。 + * Windows が再起動するまで、問題は継続。 + */ + l2tp_ctrl_log(_this, LOG_WARNING, + "Peer indicates \"No Resource\" error."); + } + + l2tp_ctrl_log(_this, LOG_INFO, "RecvStopCCN result=%s/%u " + "error=%s/%u tunnel_id=%u message=\"%s\"", + l2tp_stopccn_rcode_string(rcode), rcode, l2tp_ecode_string(ecode), + ecode, tunid, peermes); + + return 0; + +size_check_failed: + l2tp_ctrl_log(_this, LOG_ERR, "Received bad StopCCN: %s", emes); + + return -1; +} + +/** + * SCCRP の送信 + */ +static void +l2tp_ctrl_send_SCCRP(l2tp_ctrl *_this) +{ + int len; + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ]; + const char *val; + bytebuffer *bytebuf; + + if ((bytebuf = l2tp_ctrl_prepare_snd_buffer(_this, 1)) == NULL) { + l2tp_ctrl_log(_this, LOG_ERR, + "sending SCCRP failed: no buffer."); + return; + } + avp = (struct l2tp_avp *)buf; + + /* Message Type = SCCRP */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_MESSAGE_TYPE; + avp_set_val16(avp, L2TP_AVP_MESSAGE_TYPE_SCCRP); + bytebuf_add_avp(bytebuf, avp, 2); + + /* Protocol Version = 1.0 */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_PROTOCOL_VERSION; + avp->attr_value[0] = L2TP_RFC2661_VERSION; + avp->attr_value[1] = L2TP_RFC2661_REVISION; + bytebuf_add_avp(bytebuf, avp, 2); + + /* Framing Capability = Async */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_FRAMING_CAPABILITIES; + avp_set_val32(avp, L2TP_FRAMING_CAP_FLAGS_SYNC); + bytebuf_add_avp(bytebuf, avp, 4); + + /* Host Name */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_HOST_NAME; + + if ((val = l2tp_ctrl_config_str(_this, "l2tp.host_name")) == NULL) + val = _this->l2tpd->default_hostname; + if (val[0] == '\0') + val = "G"; /* おまじない。ask yasuoka */ + len = strlen(val); + memcpy(avp->attr_value, val, len); + bytebuf_add_avp(bytebuf, avp, len); + + /* Assigned Tunnel Id */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_ASSINGED_TUNNEL_ID; + avp_set_val16(avp, _this->tunnel_id); + bytebuf_add_avp(bytebuf, avp, 2); + + /* Bearer Capability + * + * この実装は LAC になり得ない LNS なので。 + * + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_BEARER_CAPABILITIES; + avp_set_val32(avp, 0); + bytebuf_add_avp(bytebuf, avp, 4); + */ + + /* Firmware Revision */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_FIRMWARE_REVISION; + avp->attr_value[0] = MAJOR_VERSION; + avp->attr_value[1] = MINOR_VERSION; + bytebuf_add_avp(bytebuf, avp, 2); + + /* Host Name */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_VENDOR_NAME; + + if ((val = l2tp_ctrl_config_str(_this, "l2tp.vendor_name")) == NULL) + val = L2TPD_VENDOR_NAME; + + len = strlen(val); + memcpy(avp->attr_value, val, len); + bytebuf_add_avp(bytebuf, avp, len); + + /* Window Size */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_RECV_WINDOW_SIZE; + avp_set_val16(avp, _this->winsz); + bytebuf_add_avp(bytebuf, avp, 2); + + if ((l2tp_ctrl_send_packet(_this, 0, bytebuf, 1)) != 0) { + l2tp_ctrl_log(_this, LOG_ERR, "sending SCCRP failed"); + l2tp_ctrl_stop(_this, L2TP_STOP_CCN_RCODE_GENERAL); + return; + } + l2tp_ctrl_log(_this, LOG_INFO, "SendSCCRP"); +} + +static int +l2tp_ctrl_send_HELLO(l2tp_ctrl *_this) +{ + struct l2tp_avp *avp; + char buf[L2TP_AVP_MAXSIZ]; + bytebuffer *bytebuf; + + if ((bytebuf = l2tp_ctrl_prepare_snd_buffer(_this, 1)) == NULL) { + l2tp_ctrl_log(_this, LOG_ERR, + "sending SCCRP failed: no buffer."); + return 1; + } + avp = (struct l2tp_avp *)buf; + + /* Message Type = HELLO */ + memset(avp, 0, sizeof(*avp)); + avp->is_mandatory = 1; + avp->attr_type = L2TP_AVP_TYPE_MESSAGE_TYPE; + avp_set_val16(avp, L2TP_AVP_MESSAGE_TYPE_HELLO); + bytebuf_add_avp(bytebuf, avp, 2); + + if ((l2tp_ctrl_send_packet(_this, 0, bytebuf, 1)) != 0) { + l2tp_ctrl_log(_this, LOG_ERR, "sending HELLO failed"); + l2tp_ctrl_stop(_this, L2TP_STOP_CCN_RCODE_GENERAL); + return 1; + } + l2tp_ctrl_log(_this, LOG_DEBUG, "SendHELLO"); + + return 0; +} + +/** ZLB の送信 */ +static int +l2tp_ctrl_send_ZLB(l2tp_ctrl *_this) +{ + int loglevel; + + loglevel = (_this->state == L2TP_CTRL_STATE_ESTABLISHED) + ? LOG_DEBUG : LOG_INFO; + l2tp_ctrl_log(_this, loglevel, "SendZLB"); + bytebuffer_clear(_this->zlb_buffer); + bytebuffer_put(_this->zlb_buffer, BYTEBUFFER_PUT_DIRECT, + sizeof(struct l2tp_header)); + + return l2tp_ctrl_send_packet(_this, 0, _this->zlb_buffer, 1); +} + +/*********************************************************************** + * ユーティリティ関数 + ***********************************************************************/ +/** + * 送信バッファの準備 + * @return 送信バッファが Window を越えている場合には NULL が返ります。 + */ +bytebuffer * +l2tp_ctrl_prepare_snd_buffer(l2tp_ctrl *_this, int with_seq) +{ + bytebuffer *bytebuf; + + L2TP_CTRL_ASSERT(_this != NULL); + + if (l2tp_ctrl_txwin_is_full(_this)) { + l2tp_ctrl_log(_this, LOG_INFO, "sending buffer is full."); + return NULL; + } + bytebuf = _this->snd_buffers[_this->snd_nxt % _this->winsz]; + bytebuffer_clear(bytebuf); + if (with_seq) + bytebuffer_put(bytebuf, BYTEBUFFER_PUT_DIRECT, + sizeof(struct l2tp_header)); + else + bytebuffer_put(bytebuf, BYTEBUFFER_PUT_DIRECT, + offsetof(struct l2tp_header, ns)); + + return bytebuf; +} + +/** + * 現在のステータスの文字列表現を返します。 + */ +static inline const char * +l2tp_ctrl_state_string(l2tp_ctrl *_this) +{ + switch (_this->state) { + case L2TP_CTRL_STATE_IDLE: return "idle"; + case L2TP_CTRL_STATE_WAIT_CTL_CONN: return "wait-ctl-conn"; + case L2TP_CTRL_STATE_WAIT_CTL_REPLY: return "wait-ctl-reply"; + case L2TP_CTRL_STATE_ESTABLISHED: return "established"; + case L2TP_CTRL_STATE_CLEANUP_WAIT: return "cleanup-wait"; + } + return "unknown"; +} + +/** + * このインスタンスに基づいたラベルから始まるログを記録します。 + */ +void +l2tp_ctrl_log(l2tp_ctrl *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); +#ifdef L2TPD_MULITPLE + snprintf(logbuf, sizeof(logbuf), "l2tpd id=%u ctrl=%u %s", + _this->l2tpd->id, _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "l2tpd ctrl=%u %s", _this->id, fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} diff --git a/usr.sbin/npppd/l2tp/l2tp_local.h b/usr.sbin/npppd/l2tp/l2tp_local.h new file mode 100644 index 00000000000..517072db216 --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tp_local.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef L2TP_LOCAL_H +#define L2TP_LOCAL_H 1 +/* $Id: l2tp_local.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ + +#ifndef GETSHORT +#define GETSHORT(s, cp) { \ + s = *(cp)++ << 8; \ + s |= *(cp)++; \ +} +#endif + +struct l2tp_header { +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t p:1, + o:1, + x2:1, + s:1, + x1:2, + l:1, + t:1; + uint8_t ver:4, + x3:4; +#else + uint8_t t:1, + l:1, + x1:2, + s:1, + x2:1, + o:1, + p:1; + uint8_t x3:4, + ver:4; +#endif + uint16_t length; + uint16_t tunnel_id; + uint16_t session_id; + uint16_t ns; + uint16_t nr; +} __attribute__((__packed__)); + +#ifndef countof +#define countof(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +#define LISTENER_SOCK(ctrl) \ + ((l2tpd_listener *)slist_get(&(ctrl)->l2tpd->listener, \ + (ctrl)->listener_index))->sock +#define SIN(ss) ((struct sockaddr_in *)(ss)) + +#endif diff --git a/usr.sbin/npppd/l2tp/l2tp_subr.c b/usr.sbin/npppd/l2tp/l2tp_subr.c new file mode 100644 index 00000000000..62a6326a687 --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tp_subr.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: l2tp_subr.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * L2TP 関連の補助的な関数を提供します。 + */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <event.h> + +#ifdef USE_LIBSOCKUTIL +#include <seil/sockfromto.h> +#endif + +#include "debugutil.h" +#include "hash.h" +#include "bytebuf.h" +#include "slist.h" +#include "l2tp.h" +#include "l2tp_subr.h" +#include "l2tp_local.h" + +#ifdef L2TP_SUBR_DEBUG +#define L2TP_SUBR_ASSERT(x) ASSERT(x) +#else +#define L2TP_SUBR_ASSERT(x) +#endif + +/*********************************************************************** + * AVP関連 + ***********************************************************************/ +int +avp_enum(struct l2tp_avp *avp, const u_char *pkt, int pktlen, int filldata) +{ + uint16_t flags; + + L2TP_SUBR_ASSERT(pktlen >= 6); + + if (pktlen < 6) + return -1; + + GETSHORT(flags, pkt); + + avp->is_mandatory = ((flags & 0x8000) != 0)? 1 : 0; + avp->is_hidden = ((flags & 0x4000) != 0)? 1 : 0; + avp->length = flags & 0x03ff; + + GETSHORT(avp->vendor_id, pkt); + + avp->attr_type = *pkt << 8; + avp->attr_type |= *(pkt + 1); + pkt += 2; + + if (avp->length > pktlen) + return -1; + + if (filldata != 0) + memcpy(avp->attr_value, pkt, avp->length - 6); + + return avp->length; +} + +#define NAME_VAL(x) { x, #x } +static struct _label_name { + int label; + const char *name; +} +l2tp_mes_type_names[] = { + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_SCCRQ), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_SCCRP), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_SCCCN), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_StopCCN), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_HELLO), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_OCRQ), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_OCRP), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_OCCN), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_ICRQ), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_ICRP), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_ICCN), + NAME_VAL(L2TP_AVP_MESSAGE_TYPE_CDN), +}, +l2tp_avp_attribute_names[] = { + NAME_VAL(L2TP_AVP_TYPE_MESSAGE_TYPE), + NAME_VAL(L2TP_AVP_TYPE_RESULT_CODE), + NAME_VAL(L2TP_AVP_TYPE_PROTOCOL_VERSION), + NAME_VAL(L2TP_AVP_TYPE_FRAMING_CAPABILITIES), + NAME_VAL(L2TP_AVP_TYPE_BEARER_CAPABILITIES), + NAME_VAL(L2TP_AVP_TYPE_TIE_BREAKER), + NAME_VAL(L2TP_AVP_TYPE_FIRMWARE_REVISION), + NAME_VAL(L2TP_AVP_TYPE_HOST_NAME), + NAME_VAL(L2TP_AVP_TYPE_VENDOR_NAME), + NAME_VAL(L2TP_AVP_TYPE_ASSINGED_TUNNEL_ID), + NAME_VAL(L2TP_AVP_TYPE_RECV_WINDOW_SIZE), + NAME_VAL(L2TP_AVP_TYPE_CHALLENGE), + NAME_VAL(L2TP_AVP_TYPE_CAUSE_CODE), + NAME_VAL(L2TP_AVP_TYPE_CHALLENGE_RESPONSE), + NAME_VAL(L2TP_AVP_TYPE_ASSIGNED_SESSION_ID), + NAME_VAL(L2TP_AVP_TYPE_CALL_SERIAL_NUMBER), + NAME_VAL(L2TP_AVP_TYPE_MINIMUM_BPS), + NAME_VAL(L2TP_AVP_TYPE_MAXIMUM_BPS), + NAME_VAL(L2TP_AVP_TYPE_BEARER_TYPE), + NAME_VAL(L2TP_AVP_TYPE_FRAMING_TYPE), + NAME_VAL(L2TP_AVP_TYPE_CALLED_NUMBER), + NAME_VAL(L2TP_AVP_TYPE_CALLING_NUMBER), + NAME_VAL(L2TP_AVP_TYPE_SUB_ADDRESS), + NAME_VAL(L2TP_AVP_TYPE_TX_CONNECT_SPEED), + NAME_VAL(L2TP_AVP_TYPE_PHYSICAL_CHANNEL_ID), + NAME_VAL(L2TP_AVP_TYPE_INITIAL_RECV_LCP_CONFREQ), + NAME_VAL(L2TP_AVP_TYPE_LAST_SENT_LCP_CONFREQ), + NAME_VAL(L2TP_AVP_TYPE_LAST_RECV_LCP_CONFREQ), + NAME_VAL(L2TP_AVP_TYPE_PROXY_AUTHEN_TYPE), + NAME_VAL(L2TP_AVP_TYPE_PROXY_AUTHEN_NAME), + NAME_VAL(L2TP_AVP_TYPE_PROXY_AUTHEN_CHALLENGE), + NAME_VAL(L2TP_AVP_TYPE_PROXY_AUTHEN_ID), + NAME_VAL(L2TP_AVP_TYPE_PROXY_AUTHEN_RESPONSE), + NAME_VAL(L2TP_AVP_TYPE_CALL_ERRORS), + NAME_VAL(L2TP_AVP_TYPE_ACCM), + NAME_VAL(L2TP_AVP_TYPE_RANDOM_VECTOR), + NAME_VAL(L2TP_AVP_TYPE_PRIVATE_GROUP_ID), + NAME_VAL(L2TP_AVP_TYPE_RX_CONNECT_SPEED), + NAME_VAL(L2TP_AVP_TYPE_SEQUENCING_REQUIRED), + NAME_VAL(L2TP_AVP_TYPE_TX_MINIMUM), + NAME_VAL(L2TP_AVP_TYPE_CALLING_SUB_ADDRESS), + NAME_VAL(L2TP_AVP_TYPE_PPP_DISCONNECT_CAUSE_CODE), + NAME_VAL(L2TP_AVP_TYPE_CCDS), + NAME_VAL(L2TP_AVP_TYPE_SDS), + NAME_VAL(L2TP_AVP_TYPE_LCP_WANT_OPTIONS), + NAME_VAL(L2TP_AVP_TYPE_LCP_ALLOW_OPTIONS), + NAME_VAL(L2TP_AVP_TYPE_LNS_LAST_SENT_LCP_CONFREQ), + NAME_VAL(L2TP_AVP_TYPE_LNS_LAST_RECV_LCP_CONFREQ), + NAME_VAL(L2TP_AVP_TYPE_MODEM_ON_HOLD_CAPABLE), + NAME_VAL(L2TP_AVP_TYPE_MODEM_ON_HOLD_STATUS), + NAME_VAL(L2TP_AVP_TYPE_PPPOE_RELAY), + NAME_VAL(L2TP_AVP_TYPE_PPPOE_RELAY_RESP_CAP), + NAME_VAL(L2TP_AVP_TYPE_PPPOE_RELAY_FORW_CAP), + NAME_VAL(L2TP_AVP_TYPE_EXTENDED_VENDOR_ID), + NAME_VAL(L2TP_AVP_TYPE_PSEUDOWIRE_CAP_LIST), + NAME_VAL(L2TP_AVP_TYPE_LOCAL_SESSION_ID), + NAME_VAL(L2TP_AVP_TYPE_REMOTE_SESSION_ID), + NAME_VAL(L2TP_AVP_TYPE_ASSIGNED_COOKIE), + NAME_VAL(L2TP_AVP_TYPE_REMOTE_END_ID), + NAME_VAL(L2TP_AVP_TYPE_APPLICATION_CODE), + NAME_VAL(L2TP_AVP_TYPE_PSEUDOWIRE_TYPE), + NAME_VAL(L2TP_AVP_TYPE_L2_SPECIFIC_SUBLAYER), + NAME_VAL(L2TP_AVP_TYPE_DATA_SEQUENCING), + NAME_VAL(L2TP_AVP_TYPE_CIRCUIT_STATUS), + NAME_VAL(L2TP_AVP_TYPE_PREFERRED_LANGUAGE), + NAME_VAL(L2TP_AVP_TYPE_CTRL_MSG_AUTH_NONCE), + NAME_VAL(L2TP_AVP_TYPE_TX_CONNECT_SPEED), + NAME_VAL(L2TP_AVP_TYPE_RX_CONNECT_SPEED), + NAME_VAL(L2TP_AVP_TYPE_FAILOVER_CAPABILITY), + NAME_VAL(L2TP_AVP_TYPE_TUNNEL_RECOVERY), + NAME_VAL(L2TP_AVP_TYPE_SUGGESTED_CTRL_SEQUENCE), + NAME_VAL(L2TP_AVP_TYPE_FAILOVER_SESSION_STATE), + NAME_VAL(L2TP_AVP_TYPE_MULTICAST_CAPABILITY), + NAME_VAL(L2TP_AVP_TYPE_NEW_OUTGOING_SESSIONS), + NAME_VAL(L2TP_AVP_TYPE_NEW_OUTGOING_SESSIONS_ACK), + NAME_VAL(L2TP_AVP_TYPE_WITHDRAW_OUTGOING_SESSIONS), + NAME_VAL(L2TP_AVP_TYPE_MULTICAST_PACKETS_PRIORITY), +}, +l2tp_stopccn_rcode_names[] = { + NAME_VAL(L2TP_STOP_CCN_RCODE_GENERAL), + NAME_VAL(L2TP_STOP_CCN_RCODE_GENERAL_ERROR), + NAME_VAL(L2TP_STOP_CCN_RCODE_ALREADY_EXISTS), + NAME_VAL(L2TP_STOP_CCN_RCODE_UNAUTHORIZED), + NAME_VAL(L2TP_STOP_CCN_RCODE_BAD_PROTOCOL_VERSION), + NAME_VAL(L2TP_STOP_CCN_RCODE_SHUTTING_DOWN), + NAME_VAL(L2TP_STOP_CCN_RCODE_FSM_ERROR), +}, +l2tp_cdn_rcode_names[] = { + NAME_VAL(L2TP_CDN_RCODE_LOST_CARRIER), + NAME_VAL(L2TP_CDN_RCODE_ERROR_CODE), + NAME_VAL(L2TP_CDN_RCODE_ADMINISTRATIVE_REASON), + NAME_VAL(L2TP_CDN_RCODE_TEMP_NOT_AVALIABLE), + NAME_VAL(L2TP_CDN_RCODE_PERM_NOT_AVALIABLE), + NAME_VAL(L2TP_CDN_RCODE_INVALID_DESTINATION), + NAME_VAL(L2TP_CDN_RCODE_NO_CARRIER), + NAME_VAL(L2TP_CDN_RCODE_BUSY), + NAME_VAL(L2TP_CDN_RCODE_NO_DIALTONE), + NAME_VAL(L2TP_CDN_RCODE_CALL_TIMEOUT_BY_LAC), + NAME_VAL(L2TP_CDN_RCODE_NO_FRAMING_DETECTED), +}, +l2tp_ecode_names[] = { + NAME_VAL(L2TP_ECODE_NO_CONTROL_CONNECTION), + NAME_VAL(L2TP_ECODE_WRONG_LENGTH), + NAME_VAL(L2TP_ECODE_INVALID_MESSAGE), + NAME_VAL(L2TP_ECODE_NO_RESOURCE), + NAME_VAL(L2TP_ECODE_INVALID_SESSION_ID), + NAME_VAL(L2TP_ECODE_GENERIC_ERROR), + NAME_VAL(L2TP_ECODE_TRY_ANOTHER), + NAME_VAL(L2TP_ECODE_UNKNOWN_MANDATORY_AVP), +}; +#undef NAME_VAL + +const char * +avp_attr_type_string(int attr_type) +{ + int i; + + for (i = 0; i < countof(l2tp_avp_attribute_names); i++) { + if (attr_type == l2tp_avp_attribute_names[i].label) + return l2tp_avp_attribute_names[i].name + 14; + } + return "UNKNOWN_AVP"; +} + +const char * +l2tp_stopccn_rcode_string(int rcode) +{ + int i; + + for (i = 0; i < countof(l2tp_stopccn_rcode_names); i++) { + if (rcode == l2tp_stopccn_rcode_names[i].label) + return l2tp_stopccn_rcode_names[i].name + 20; + } + return "UNKNOWN"; +} + +const char * +l2tp_cdn_rcode_string(int rcode) +{ + int i; + + for (i = 0; i < countof(l2tp_cdn_rcode_names); i++) { + if (rcode == l2tp_cdn_rcode_names[i].label) + return l2tp_cdn_rcode_names[i].name + 15; + } + return "UNKNOWN"; +} + +const char * +l2tp_ecode_string(int ecode) +{ + int i; + + if (ecode == 0) + return "none"; + for (i = 0; i < countof(l2tp_ecode_names); i++) { + if (ecode == l2tp_ecode_names[i].label) + return l2tp_ecode_names[i].name + 11; + } + return "UNKNOWN"; +} + +/** + * Search the AVP that matches given vendor_id and attr_type and return it + * In case the "fill_data" is specified (non 0 value is specified as the + * "fill_data"), the memory space of the "avp" must be larger than or equal + * to L2TP_AVP_MAXSIZ (1024). + */ +struct l2tp_avp * +avp_find(struct l2tp_avp *avp, const u_char *pkt, int pktlen, + uint16_t vendor_id, uint16_t attr_type, int fill_data) +{ + int avpsz; + + while (pktlen >= 6 && + (avpsz = avp_enum(avp, pkt, pktlen, fill_data)) > 0) { + if (avp->vendor_id != vendor_id || avp->attr_type != attr_type) { + if (avpsz < 6) + return NULL; + pkt += avpsz; + pktlen -= avpsz; + continue; + } + return avp; + } + + return NULL; +} + +/** + * Search the Message-Type AVP and return it. The memory space of the "avp" + * must be larger than or equal to L2TP_AVP_MAXSIZ (1024). + */ +struct l2tp_avp * +avp_find_message_type_avp(struct l2tp_avp *avp, const u_char *pkt, int pktlen) +{ + return avp_find(avp, pkt, pktlen, 0, L2TP_AVP_TYPE_MESSAGE_TYPE, 1); +} + +/** + * bytebuffer に AVP を追加します。 + */ +int +bytebuf_add_avp(bytebuffer *bytebuf, struct l2tp_avp *avp, int value_len) +{ + struct l2tp_avp avp1; + + memcpy(&avp1, avp, sizeof(struct l2tp_avp)); + + avp1.length = value_len + 6; + avp1.vendor_id = htons(avp->vendor_id); + avp1.attr_type = htons(avp->attr_type); + *(uint16_t *)&avp1 = htons(*(uint16_t *)&avp1); + + if (bytebuffer_put(bytebuf, &avp1, 6) == NULL) + return -1; + if (bytebuffer_put(bytebuf, avp->attr_value, value_len) == NULL) + return -1; + + return 0; +} + +const char * +avp_mes_type_string(int mes_type) +{ + int i; + + for (i = 0; i < countof(l2tp_mes_type_names); i++) { + if (mes_type == l2tp_mes_type_names[i].label) + return l2tp_mes_type_names[i].name + 22; + } + return "Unknown"; +} diff --git a/usr.sbin/npppd/l2tp/l2tp_subr.h b/usr.sbin/npppd/l2tp/l2tp_subr.h new file mode 100644 index 00000000000..4aed34c29ff --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tp_subr.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef L2TP_SUBR_H +#define L2TP_SUBR_H 1 +/* $Id: l2tp_subr.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ + +/** + * L2TP の Attribute Value Pair (AVP) のパケットヘッダを示す構造体です。 + */ +struct l2tp_avp +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint16_t length:10, + rsvd:4, + is_hidden:1, + is_mandatory:1; +#else + uint16_t is_mandatory:1, + is_hidden:1, + rsvd:4, + length:10; +#endif + uint16_t vendor_id; + uint16_t attr_type; + u_char attr_value[0]; +} __attribute__((__packed__)) ; + +#define avp_attr_length(avp) ((avp)->length - 6) + +static inline uint16_t +avp_get_val16(struct l2tp_avp *avp) +{ + return (avp->attr_value[0] << 8) | avp->attr_value[1]; +} +static inline uint32_t +avp_get_val32(struct l2tp_avp *avp) +{ + return (avp->attr_value[0] << 24) | (avp->attr_value[1] << 16) | + (avp->attr_value[2] << 8) | avp->attr_value[3]; +} + +static inline void +avp_set_val16(struct l2tp_avp *avp, uint16_t val) +{ + avp->attr_value[0] = val >> 8; + avp->attr_value[1] = val & 0xff; +} + +static inline void +avp_set_val32(struct l2tp_avp *avp, uint32_t val) +{ + avp->attr_value[0] = val >> 24; + avp->attr_value[1] = val >> 16; + avp->attr_value[2] = val >> 8; + avp->attr_value[3] = val & 0xff; +} + +static inline int +short_cmp(const void *m, const void *n) +{ + return (int)((int)m - (int)n); +} + +static inline uint32_t +short_hash(const void *v, int sz) +{ + return (int)v % sz; +} + +/** + * AVPのサイズをチェックするマクロ。 + * <p> + * 準備<br> + * <ul> + * <li>エラーメッセージ用の char emes[256] を準備する。 + * <li>reigai ラベルを準備する。 + * </ul></p> + */ +#define AVP_SIZE_CHECK(avp, op, exp) \ + if (!((avp)->length op (exp))) { \ + snprintf(emes, sizeof(emes), \ + "invalid packet size %s %d" #op "%d)", \ + avp_attr_type_string((avp)->attr_type), \ + (avp)->length, (exp)); \ + goto size_check_failed; \ + } + +#ifdef __cplusplus +extern "C" { +#endif + +int avp_enum (struct l2tp_avp *, const u_char *, int, int); +const char *avp_attr_type_string (int); +struct l2tp_avp *avp_find_message_type_avp(struct l2tp_avp *, const u_char *, int); +struct l2tp_avp *avp_find(struct l2tp_avp *, const u_char *, int, uint16_t, uint16_t, int); +int bytebuf_add_avp (bytebuffer *, struct l2tp_avp *, int); +const char *avp_mes_type_string (int); +const char * l2tp_cdn_rcode_string(int); +const char * l2tp_stopccn_rcode_string(int); +const char * l2tp_ecode_string(int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/l2tp/l2tpd.c b/usr.sbin/npppd/l2tp/l2tpd.c new file mode 100644 index 00000000000..f34f7e1cd2a --- /dev/null +++ b/usr.sbin/npppd/l2tp/l2tpd.c @@ -0,0 +1,811 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * L2TP(Layer Two Tunneling Protocol "L2TP") の実装 + */ +/* + * RFC 2661 + */ +// $Id: l2tpd.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netinet/udp.h> +#if 0 +#include <netinet6/ipsec.h> +#endif +#include <stdlib.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <unistd.h> +#include <netdb.h> +#include <syslog.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <stdarg.h> +#include <event.h> + +#ifdef USE_LIBSOCKUTIL +#include <seil/sockfromto.h> +#else +#include "recvfromto.h" +#endif + +#include "bytebuf.h" +#include "hash.h" +#include "slist.h" +#include "debugutil.h" +#include "l2tp.h" +#include "l2tp_subr.h" +#include "l2tp_local.h" +#include "addr_range.h" +#include "properties.h" +#include "config_helper.h" +#include "net_utils.h" + +#ifdef L2TPD_DEBUG +#define L2TPD_ASSERT(x) ASSERT(x) +#define L2TPD_DBG(x) l2tpd_log x +#else +#define L2TPD_ASSERT(x) +#endif +#define L2TPD_IPSEC_POLICY_IN "in ipsec esp/transport//require" +#define L2TPD_IPSEC_POLICY_OUT "out ipsec esp/transport//require" + +static void l2tpd_io_event (int, short, void *); +static inline int short_cmp (const void *, const void *); +static inline uint32_t short_hash (const void *, int); +/* + * static 変数 + */ + +/** l2tpd の ID番号のシーケンス番号 */ +static unsigned l2tpd_id_seq = 0; + +#ifndef USE_LIBSOCKUTIL +struct in_ipsec_sa_cookie { }; +#endif + + +/*********************************************************************** + * L2TP デーモンインスタンス操作 + ***********************************************************************/ + +/** + * L2TPデーモンインスタンスを初期化します。 + * <p> + * {@link _l2tpd#bind_sin} は、.sin_family = AF_INET、.sin_port = 1701、 + * .sin_len が設定された状態で返ります。 </p> + */ +int +l2tpd_init(l2tpd *_this) +{ + struct sockaddr_in sin0; + + L2TPD_ASSERT(_this != NULL); + memset(_this, 0, sizeof(l2tpd)); + + slist_init(&_this->listener); + memset(&sin0, 0, sizeof(sin0)); + sin0.sin_len = sizeof(sin0); + sin0.sin_family = AF_INET; + if (l2tpd_add_listener(_this, 0, L2TPD_DEFAULT_LAYER2_LABEL, + (struct sockaddr *)&sin0) != 0) { + return 1; + } + + _this->id = l2tpd_id_seq++; + + if ((_this->ctrl_map = hash_create(short_cmp, short_hash, + L2TPD_TUNNEL_HASH_SIZ)) == NULL) { + log_printf(LOG_ERR, "hash_create() failed in %s(): %m", + __func__); + return 1; + } + _this->ip4_allow = NULL; + + _this->require_ipsec = 1; + _this->purge_ipsec_sa = 1; + _this->state = L2TPD_STATE_INIT; + + return 0; +} + +/** + * {@link ::l2tpd L2TPデーモン}に{@link ::l2tpd_listener リスナ}を追加します。 + * @param _this {@link ::l2tpd L2TPデーモン} + * @param idx リスナのインデックス + * @param label 物理層としてのラベル。"L2TP" など + * @param bindaddr 待ち受けるアドレス + */ +int +l2tpd_add_listener(l2tpd *_this, int idx, const char *label, + struct sockaddr *bindaddr) +{ + l2tpd_listener *plistener, *plsnr; + + plistener = NULL; + if (idx == 0 && slist_length(&_this->listener) > 0) { + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + slist_itr_next(&_this->listener); + plsnr = slist_itr_remove(&_this->listener); + L2TPD_ASSERT(plsnr != NULL); + L2TPD_ASSERT(plsnr->sock == -1); + free(plsnr); + } + } + L2TPD_ASSERT(slist_length(&_this->listener) == idx); + if (slist_length(&_this->listener) != idx) { + l2tpd_log(_this, LOG_ERR, + "Invalid argument error on %s(): idx must be %d but %d", + __func__, slist_length(&_this->listener), idx); + goto reigai; + } + if ((plistener = malloc(sizeof(l2tpd_listener))) == NULL) { + l2tpd_log(_this, LOG_ERR, "malloc() failed in %s: %m", + __func__); + goto reigai; + } + memset(plistener, 0, sizeof(l2tpd_listener)); + L2TPD_ASSERT(sizeof(plistener->bind_sin) >= bindaddr->sa_len); + memcpy(&plistener->bind_sin, bindaddr, bindaddr->sa_len); + + /* ポート番号が省略された場合は、デフォルト (1701/udp)を使う */ + if (plistener->bind_sin.sin_port == 0) + plistener->bind_sin.sin_port = htons(L2TPD_DEFAULT_UDP_PORT); + + plistener->sock = -1; + plistener->self = _this; + plistener->index = idx; + strlcpy(plistener->phy_label, label, sizeof(plistener->phy_label)); + + if (slist_add(&_this->listener, plistener) == NULL) { + l2tpd_log(_this, LOG_ERR, "slist_add() failed in %s: %m", + __func__); + goto reigai; + } + return 0; +reigai: + if (plistener != NULL) + free(plistener); + return 1; +} + +/** L2TPデーモンインスタンスの終了処理を行います。*/ +void +l2tpd_uninit(l2tpd *_this) +{ + l2tpd_listener *plsnr; + + L2TPD_ASSERT(_this != NULL); + + if (_this->ctrl_map != NULL) { + hash_free(_this->ctrl_map); + _this->ctrl_map = NULL; + } + + if (_this->ip4_allow != NULL) + in_addr_range_list_remove_all(&_this->ip4_allow); + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plsnr = slist_itr_next(&_this->listener); + L2TPD_ASSERT(plsnr != NULL); + L2TPD_ASSERT(plsnr->sock == -1); + free(plsnr); + } + slist_fini(&_this->listener); + + event_del(&_this->ev_timeout); // ねんのため + _this->state = L2TPD_STATE_STOPPED; + _this->config = NULL; +} + +/** 待ち受けを開始します。*/ +static int +l2tpd_listener_start(l2tpd_listener *_this, char *ipsec_policy_in, + char *ipsec_policy_out) +{ + int sock, ival; + l2tpd *_l2tpd; +#ifdef NPPPD_FAKEBIND + int wildcardbinding = 0; + extern void set_faith(int, int); + + wildcardbinding = + (_this->bind_sin.sin_addr.s_addr == INADDR_ANY)? 1 : 0; +#endif + sock = -1; + _l2tpd = _this->self; + + if (_this->phy_label[0] == '\0') + strlcpy(_this->phy_label, L2TPD_DEFAULT_LAYER2_LABEL, + sizeof(_this->phy_label)); + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + l2tpd_log(_l2tpd, LOG_ERR, + "socket() failed in %s(): %m", __func__); + goto reigai; + } +#ifdef NPPPD_FAKEBIND + if (!wildcardbinding) + set_faith(sock, 1); +#endif +#if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) + ival = 1; + if (setsockopt(sock, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival)) + != 0) + l2tpd_log(_l2tpd, LOG_WARNING, + "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); +#endif + if ((ival = fcntl(sock, F_GETFL, 0)) < 0) { + l2tpd_log(_l2tpd, LOG_ERR, + "fcntl(,F_GETFL) failed in %s(): %m", __func__); + goto reigai; + } else if (fcntl(sock, F_SETFL, ival | O_NONBLOCK) < 0) { + l2tpd_log(_l2tpd, LOG_ERR, "fcntl(,F_SETFL,O_NONBLOCK) failed " + "in %s(): %m", __func__); + goto reigai; + } + ival = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &ival, sizeof(ival)) + != 0) { + l2tpd_log(_l2tpd, LOG_ERR, + "setsockopt(,,SO_REUSEPORT) failed in %s(): %m", __func__); + goto reigai; + } + if (bind(sock, (struct sockaddr *)&_this->bind_sin, + _this->bind_sin.sin_len) != 0) { + l2tpd_log(_l2tpd, LOG_ERR, "Binding %s:%u/udp: %m", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port)); + goto reigai; + } +#ifdef NPPPD_FAKEBIND + if (!wildcardbinding) + set_faith(sock, 0); +#endif +#ifdef UDP_NO_CKSUM + ival = 1; + if (setsockopt(sock, IPPROTO_UDP, UDP_NO_CKSUM, &ival, sizeof(ival)) + != 0) { + l2tpd_log(_l2tpd, LOG_ERR, + "setsockopt(,,UDP_NO_CKSUM) failed in %s(): %m", + __func__); + goto reigai; + } +#endif +#ifdef USE_LIBSOCKUTIL + if (setsockoptfromto(sock) != 0) { + l2tpd_log(_l2tpd, LOG_ERR, + "setsockoptfromto() failed in %s(): %m", __func__); + goto reigai; + } +#else + // recvfromto のために + ival = 1; + if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &ival, sizeof(ival)) + != 0) { + l2tpd_log(_l2tpd, LOG_ERR, + "setsockopt(,,IP_RECVDSTADDR) failed in %s(): %m", + __func__); + goto reigai; + } +#endif +#ifdef IP_IPSEC_POLICY +/*XXX */ + if (ipsec_policy_in != NULL && + setsockopt(sock, IPPROTO_IP, IP_IPSEC_POLICY, + ipsec_policy_in, ipsec_get_policylen(ipsec_policy_in)) < 0) { + l2tpd_log(_l2tpd, LOG_WARNING, + "setsockopt(,,IP_IPSEC_POLICY(in)) failed in %s(): %m", + __func__); + } + if (ipsec_policy_out != NULL && + setsockopt(sock, IPPROTO_IP, IP_IPSEC_POLICY, + ipsec_policy_out, ipsec_get_policylen(ipsec_policy_out)) < 0) { + l2tpd_log(_l2tpd, LOG_WARNING, + "setsockopt(,,IP_IPSEC_POLICY(out)) failed in %s(): %m", + __func__); + } +#endif + _this->sock = sock; + + event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST, + l2tpd_io_event, _this); + event_add(&_this->ev_sock, NULL); + + l2tpd_log(_l2tpd, LOG_INFO, "Listening %s:%u/udp (L2TP LNS) [%s]", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port), _this->phy_label); + + return 0; +reigai: + if (sock >= 0) + close(sock); + + return 1; +} + +/** L2TPデーモンを開始します。*/ +int +l2tpd_start(l2tpd *_this) +{ + int rval; + caddr_t ipsec_policy_in, ipsec_policy_out; + l2tpd_listener *plsnr; + + rval = 0; + ipsec_policy_in = NULL; + ipsec_policy_out = NULL; + + L2TPD_ASSERT(_this->state == L2TPD_STATE_INIT); + if (_this->state != L2TPD_STATE_INIT) { + l2tpd_log(_this, LOG_ERR, "Failed to start l2tpd: illegal " + "state."); + return -1; + } + if (_this->require_ipsec != 0) { +#if 0 + /* + * NOTE ipsec_set_policy() 内で利用する yacc のスタック用の + * バッファは動的に割り当てられますが、解放されません。 + * yasuoka の調査時は 2000 バイトリークします。 + */ + if ((ipsec_policy_in = ipsec_set_policy(L2TPD_IPSEC_POLICY_IN, + strlen(L2TPD_IPSEC_POLICY_IN))) == NULL) { + l2tpd_log(_this, LOG_ERR, + "ipsec_set_policy(L2TPD_IPSEC_POLICY_IN) failed " + "at %s(): %s: %m", __func__, ipsec_strerror()); + goto reigai; + } + if ((ipsec_policy_out = ipsec_set_policy(L2TPD_IPSEC_POLICY_OUT, + strlen(L2TPD_IPSEC_POLICY_OUT))) == NULL) { + l2tpd_log(_this, LOG_ERR, + "ipsec_set_policy(L2TPD_IPSEC_POLICY_OUT) failed " + "at %s(): %s: %m", __func__, ipsec_strerror()); + goto reigai; + } +#endif + } + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plsnr = slist_itr_next(&_this->listener); + rval |= l2tpd_listener_start(plsnr, ipsec_policy_in, + ipsec_policy_out); + } + + if (ipsec_policy_in != NULL) + free(ipsec_policy_in); + if (ipsec_policy_out != NULL) + free(ipsec_policy_out); + + if (rval == 0) + _this->state = L2TPD_STATE_RUNNING; + + return rval; +reigai: + if (ipsec_policy_in != NULL) + free(ipsec_policy_in); + if (ipsec_policy_out != NULL) + free(ipsec_policy_out); + + return 1; +} + +/** 待ち受けを終了します */ +static void +l2tpd_listener_stop(l2tpd_listener *_this) +{ + if (_this->sock >= 0) { + event_del(&_this->ev_sock); + close(_this->sock); + l2tpd_log(_this->self, LOG_INFO, + "Shutdown %s:%u/udp (L2TP LNS)", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port)); + _this->sock = -1; + } +} +/** + * 切断を猶予せずにすぐに停止します。 + */ +void +l2tpd_stop_immediatly(l2tpd *_this) +{ + l2tpd_listener *plsnr; + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plsnr = slist_itr_next(&_this->listener); + l2tpd_listener_stop(plsnr); + } + event_del(&_this->ev_timeout); // ねんのため + _this->state = L2TPD_STATE_STOPPED; +} + +/** + * {@link ::_l2tp_ctrl コントロール} が終了した際にコールされます。 + */ +void +l2tpd_ctrl_finished_notify(l2tpd *_this) +{ + if (_this->state != L2TPD_STATE_SHUTTING_DOWN) + return; + + if (hash_first(_this->ctrl_map) != NULL) + return; + + l2tpd_stop_immediatly(_this); +} + +static void +l2tpd_stop_timeout(int fd, short evtype, void *ctx) +{ + hash_link *hl; + l2tp_ctrl *ctrl; + l2tpd *_this; + + _this = ctx; + l2tpd_log(_this, LOG_INFO, "Shutdown timeout"); + for (hl = hash_first(_this->ctrl_map); hl != NULL; + hl = hash_next(_this->ctrl_map)) { + ctrl = hl->item; + l2tp_ctrl_stop(ctrl, 0); + } + l2tpd_stop_immediatly(_this); +} + +/** + * L2TPデーモンを停止します。 + */ +void +l2tpd_stop(l2tpd *_this) +{ + int nctrls = 0; + hash_link *hl; + l2tp_ctrl *ctrl; + + nctrls = 0; + event_del(&_this->ev_timeout); + if (l2tpd_is_stopped(_this)) + return; + if (l2tpd_is_shutting_down(_this)) { + /* + * 2度目はすぐに終了 + */ + l2tpd_stop_immediatly(_this); + return; + } + for (hl = hash_first(_this->ctrl_map); hl != NULL; + hl = hash_next(_this->ctrl_map)) { + ctrl = hl->item; + l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_SHUTTING_DOWN); + nctrls++; + } + _this->state = L2TPD_STATE_SHUTTING_DOWN; + if (nctrls > 0) { + struct timeval tv0; + + tv0.tv_usec = 0; + tv0.tv_sec = L2TPD_SHUTDOWN_TIMEOUT; + + evtimer_set(&_this->ev_timeout, l2tpd_stop_timeout, _this); + evtimer_add(&_this->ev_timeout, &tv0); + + return; + } + l2tpd_stop_immediatly(_this); +} + +/*********************************************************************** + * 設定関連 + ***********************************************************************/ +#define CFG_KEY(p, s) config_key_prefix((p), (s)) +#define VAL_SEP " \t\r\n" + +CONFIG_FUNCTIONS(l2tpd_config, l2tpd, config); +PREFIXED_CONFIG_FUNCTIONS(l2tp_ctrl_config, l2tp_ctrl, l2tpd->config, + phy_label); + +int +l2tpd_reload(l2tpd *_this, struct properties *config, const char *name, + int default_enabled) +{ + int i, do_start, aierr; + const char *val; + char *tok, *cp, buf[L2TPD_CONFIG_BUFSIZ], *label; + struct addrinfo *ai; + + _this->config = config; + do_start = 0; + if (l2tpd_config_str_equal(_this, CFG_KEY(name, "enabled"), "true", + default_enabled)) { + // false にした直後に true にされるかもしれない。 + if (l2tpd_is_shutting_down(_this)) + l2tpd_stop_immediatly(_this); + if (l2tpd_is_stopped(_this)) + do_start = 1; + } else { + if (!l2tpd_is_stopped(_this)) + l2tpd_stop(_this); + return 0; + } + if (do_start && l2tpd_init(_this) != 0) + return 1; + _this->config = config; + + /* 設定がなかったら使われる */ + gethostname(_this->default_hostname, sizeof(_this->default_hostname)); + + _this->ctrl_in_pktdump = l2tpd_config_str_equal(_this, + "log.l2tp.ctrl.in.pktdump", "true", 0); + _this->data_in_pktdump = l2tpd_config_str_equal(_this, + "log.l2tp.data.in.pktdump", "true", 0); + _this->ctrl_out_pktdump = l2tpd_config_str_equal(_this, + "log.l2tp.ctrl.out.pktdump", "true", 0); + _this->data_out_pktdump = l2tpd_config_str_equal(_this, + "log.l2tp.data.out.pktdump", "true", 0); + _this->phy_label_with_ifname = l2tpd_config_str_equal(_this, + CFG_KEY(name, "label_with_ifname"), "true", 0); + + // ip4_allow をパース + in_addr_range_list_remove_all(&_this->ip4_allow); + val = l2tpd_config_str(_this, CFG_KEY(name, "ip4_allow")); + if (val != NULL) { + if (strlen(val) >= sizeof(buf)) { + l2tpd_log(_this, LOG_ERR, "configuration error at " + "l2tpd.ip4_allow: too long"); + return 1; + } + strlcpy(buf, val, sizeof(buf)); + for (cp = buf; (tok = strsep(&cp, VAL_SEP)) != NULL;) { + if (*tok == '\0') + continue; + if (in_addr_range_list_add(&_this->ip4_allow, tok) + != 0) { + l2tpd_log(_this, LOG_ERR, + "configuration error at " + "l2tpd.ip4_allow: %s", tok); + return 1; + } + } + } + + if (do_start) { + /* + * 起動直後と、l2tpd.enable が false -> true に変更された + * 場合に、do_start。すべてのリスナーが、初期化された状態を + * 仮定できる + */ + // l2tpd.listener_in の読み込む + val = l2tpd_config_str(_this, CFG_KEY(name, "listener_in")); + if (val != NULL) { + if (strlen(val) >= sizeof(buf)) { + l2tpd_log(_this, LOG_ERR, + "configuration error at %s: too long", + CFG_KEY(name, "listener")); + return 1; + } + strlcpy(buf, val, sizeof(buf)); + + label = NULL; + // タブ、スペース区切りで、複数指定可能 + for (i = 0, cp = buf; + (tok = strsep(&cp, VAL_SEP)) != NULL;) { + if (*tok == '\0') + continue; + if (label == NULL) { + label = tok; + continue; + } + if ((aierr = addrport_parse(tok, IPPROTO_UDP, + &ai)) != 0) { + l2tpd_log(_this, LOG_ERR, + "configuration error at " + "l2tpd.listener_in: %s: %s", label, + gai_strerror(aierr)); + label = NULL; + return 1; + } + L2TPD_ASSERT(ai != NULL && + ai->ai_family == AF_INET); + if (l2tpd_add_listener(_this, i, label, + ai->ai_addr) != 0) { + freeaddrinfo(ai); + label = NULL; + break; + } + freeaddrinfo(ai); + label = NULL; + i++; + } + if (label != NULL) { + l2tpd_log(_this, LOG_ERR, "configuration " + "error at l2tpd.listener_in: %s", label); + return 1; + } + } + _this->purge_ipsec_sa = l2tpd_config_str_equal(_this, + CFG_KEY(name, "purge_ipsec_sa"), "true", 1); + _this->require_ipsec = l2tpd_config_str_equal(_this, + CFG_KEY(name, "require_ipsec"), "true", 1); + + if (l2tpd_start(_this) != 0) + return 1; + } + + return 0; +} + +/*********************************************************************** + * I/O 関連 + ***********************************************************************/ +/** アクセスを拒否したことをログに残す */ +void +l2tpd_log_access_deny(l2tpd *_this, const char *reason, struct sockaddr *peer) +{ + char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; + + if (getnameinfo(peer, peer->sa_len, hostbuf, sizeof(hostbuf), + servbuf, sizeof(servbuf), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + l2tpd_log(_this, LOG_ERR, "getnameinfo() failed at %s(): %m", + __func__); + return; + } + l2tpd_log(_this, LOG_ALERT, "Received packet from %s:%s/udp: " + "%s", hostbuf, servbuf, reason); +} + +/** I/Oイベントハンドラ */ +static void +l2tpd_io_event(int fd, short evtype, void *ctx) +{ + int sz; + l2tpd *_l2tpd; + l2tpd_listener *_this; + socklen_t peerlen, socklen; + struct sockaddr_storage peer, sock; + u_char buf[8192]; + void *nat_t; + + _this = ctx; + _l2tpd = _this->self; + if ((evtype & EV_READ) != 0) { + peerlen = sizeof(peer); + socklen = sizeof(sock); + while (!l2tpd_is_stopped(_l2tpd)) { +#ifdef USE_LIBSOCKUTIL + int sa_cookie_len; + struct in_ipsec_sa_cookie sa_cookie; + + sa_cookie_len = sizeof(sa_cookie); + if ((sz = recvfromto_nat_t(_this->sock, buf, + sizeof(buf), 0, + (struct sockaddr *)&peer, &peerlen, + (struct sockaddr *)&sock, &socklen, + &sa_cookie, &sa_cookie_len)) <= 0) { +#else + if ((sz = recvfromto(_this->sock, buf, + sizeof(buf), 0, + (struct sockaddr *)&peer, &peerlen, + (struct sockaddr *)&sock, &socklen)) <= 0) { +#endif + if (errno == EAGAIN || errno == EINTR) + break; + l2tpd_log(_l2tpd, LOG_ERR, + "recvfrom() failed in %s(): %m", + __func__); + l2tpd_stop(_l2tpd); + return; + } + //送信元チェック(allows.in) + switch (peer.ss_family) { + case AF_INET: +#ifdef USE_LIBSOCKUTIL + if (sa_cookie_len > 0) + nat_t = &sa_cookie; + else + nat_t = NULL; +#else + nat_t = NULL; +#endif + /* + * XXX NAT-T の場合の送信元チェック + */ + if (in_addr_range_list_includes( + &_l2tpd->ip4_allow, + &((struct sockaddr_in *)&peer)->sin_addr)) + l2tp_ctrl_input(_l2tpd, _this->index, + (struct sockaddr *)&peer, + (struct sockaddr *)&sock, nat_t, + buf, sz); + else + l2tpd_log_access_deny(_l2tpd, + "not allowed by acl.", + (struct sockaddr *)&peer); + break; + default: + l2tpd_log(_l2tpd, LOG_ERR, + "received from unknown address family = %d", + peer.ss_family); + break; + } + } + } +} + +/*********************************************************************** + * L2TPコントロール関連 + ***********************************************************************/ +l2tp_ctrl * +l2tpd_get_ctrl(l2tpd *_this, int tunid) +{ + hash_link *hl; + + hl = hash_lookup(_this->ctrl_map, (void *)tunid); + if (hl == NULL) + return NULL; + + return hl->item; +} + +void +l2tpd_add_ctrl(l2tpd *_this, l2tp_ctrl *ctrl) +{ + hash_insert(_this->ctrl_map, (void *)ctrl->tunnel_id, ctrl); +} + +void +l2tpd_remove_ctrl(l2tpd *_this, int tunid) +{ + hash_delete(_this->ctrl_map, (void *)tunid, 0); +} + + +/*********************************************************************** + * 雑多 + ***********************************************************************/ + +void +l2tpd_log(l2tpd *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); +#ifdef L2TPD_MULITPLE + snprintf(logbuf, sizeof(logbuf), "l2tpd id=%u %s", _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "l2tpd %s", fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} diff --git a/usr.sbin/npppd/npppd/Makefile b/usr.sbin/npppd/npppd/Makefile new file mode 100644 index 00000000000..cc6c3a0759a --- /dev/null +++ b/usr.sbin/npppd/npppd/Makefile @@ -0,0 +1,58 @@ +# +# +NPPPD_COMMON_DIR= ${.CURDIR}/../common +NOMAN= # + +PROG= npppd +CPPFLAGS+= -I${NPPPD_COMMON_DIR} -I${.CURDIR} +SRCS= ccp.c chap.c chap_ms.c fsm.c ipcp.c lcp.c +SRCS+= mppe.c pap.c ppp.c radius_req.c +SRCS+= npppd.c npppd_config.c npppd_subr.c npppd_auth.c npppd_iface.c +SRCS+= config_helper.c slist.c hash.c properties.c rtev_common.c +SRCS+= rtev_libevent.c bytebuf.c debugutil.c csvreader.c net_utils.c +SRCS+= radish.c time_utils.c npppd_pool.c addr_range.c +SRCS+= radius+.cc +SRCS+= recvfromto.c +#SRCS+= ipsec_util.c + +CPPFLAGS+= -DUSE_NPPPD_NPPPD_CTL=1 +SRCS+= npppd_ctl.c + +CPPFLAGS+= -DUSE_NPPPD_PPTP -I${.CURDIR}/../pptp +SRCS+= pptp_call.c pptp_ctrl.c pptp_subr.c pptpd.c +.PATH: ${.CURDIR}/../pptp + +CPPFLAGS+= -DUSE_NPPPD_L2TP -I${.CURDIR}/../l2tp +SRCS+= l2tp_call.c l2tp_ctrl.c l2tp_subr.c l2tpd.c +.PATH: ${.CURDIR}/../l2tp + +CPPFLAGS+= -DUSE_NPPPD_PPPOE -I${.CURDIR}/../pppoe +SRCS+= pppoe_session.c pppoed.c +.PATH: ${.CURDIR}/../pppoe + +#SRCS+= eap.c radius_common.c + +CPPFLAGS+= -D__COPYRIGHT\(x\)= -D__RCSID=\(x\)= +CPPFLAGS+= -DUSE_NPPPD_MPPE +CPPFLAGS+= -DUSE_NPPPD_PIPEX +CPPFLAGS+= -DUSE_NPPPD_RADIUS + +CPPFLAGS+= -DGENERIC_USE -DRADISH + +LDADD+= -levent -lcrypto +DPADD+= ${LIBEVENT} ${LIBCRYPTO} + +.ifdef DEBUG +CPPFLAGS+= -DDEBUG=1 +CPPFLAGS+= -DNPPPD_DEBUG=1 -DNPPPD_TUN_DEBUG=1 -DNPPPD_CONFIG_DEBUG=1 +CPPFLAGS+= -DNPPPD_CTL_DEBUG=1 -DRADIUS_REQ_DEBUG=1 -DPPP_DEBUG=1 +CPPFLAGS+= -DLCP_DEBUG=1 -DFSM_DEBUG=1 -DMPPE_DEBUG=1 -DTUNDEV_DEBUG=1 +CPPFLAGS+= -DIPCP_DEBUG=1 -DNPPPD_INTERFACE_DEBUG=1 -DNPPPD_POOL_DEBUG=1 +CPPFLAGS+= -DNPPPD_AUTH_DEBUG=1 -DRT_ZEBRA_DEBUG=1 +CPPFLAGS+= -DPAP_DEBUG=1 -DCHAP_DEBUG=1 +CPPFLAGS+= -DNPPPD_IFACE_DEBUG +.endif +# common + +.include <bsd.prog.mk> +.PATH: ${.CURDIR}/../common ${.CURDIR} diff --git a/usr.sbin/npppd/npppd/ccp.c b/usr.sbin/npppd/npppd/ccp.c new file mode 100644 index 00000000000..a8716f2550c --- /dev/null +++ b/usr.sbin/npppd/npppd/ccp.c @@ -0,0 +1,369 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * CCP - Compression Control Protocol + * <p> + * 対応するオプション + * <pre> + * - MPPE</pre></p> + * $Id: ccp.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <event.h> + +#include "slist.h" +#include "npppd.h" +#include "fsm.h" + +#ifdef CCP_DEBUG +#define CCPDEBUG(x) fsm_log(x) +#define CCP_ASSERT(x) ASSERT(x) +#else +#define CCPDEBUG(x) +#define CCP_ASSERT(x) +#endif + +static int ccp_reqci (fsm *, u_char *, int *, int); +static void ccp_open (fsm *); +static void ccp_close (fsm *); +static void ccp_start (fsm *); +static void ccp_stop (fsm *); +static void ccp_resetci (fsm *); +static int ccp_cilen (fsm *); +static void ccp_addci (fsm *, u_char *, int *); +static int ccp_ackci (fsm *, u_char *, int); +static int ccp_rejci (fsm *, u_char *, int); +static int ccp_nakci (fsm *, u_char *, int); +static int ccp_nackackci (fsm *, u_char *, int, int, int); +static int ccp_ext (fsm *, int, int, u_char *, int); + +static struct fsm_callbacks ccp_callbacks = { + .cilen = ccp_cilen, + .resetci = ccp_resetci, + .addci = ccp_addci, + .ackci = ccp_ackci, + .nakci = ccp_nakci, + .rejci = ccp_rejci, + .reqci = ccp_reqci, + .up = ccp_open, + .down = ccp_close, + .starting = ccp_start, + .finished = ccp_stop, + .extcode = ccp_ext, + .proto_name = "ccp", +}; + +/** + * ccp コンテキストを初期化します。 + */ +void +ccp_init(ccp *_this, npppd_ppp *ppp) +{ + memset(_this, 0, sizeof(ccp)); + + _this->ppp = ppp; + _this->fsm.callbacks = &ccp_callbacks; + _this->fsm.protocol = PPP_PROTO_NCP | NCP_CCP; + _this->fsm.ppp = ppp; + + fsm_init(&_this->fsm); + //_this->fsm.flags |= OPT_SILENT; + + PPP_FSM_CONFIG(&_this->fsm, timeouttime, "ccp.timeout"); + PPP_FSM_CONFIG(&_this->fsm, maxconfreqtransmits,"ccp.max_configure"); + PPP_FSM_CONFIG(&_this->fsm, maxtermtransmits, "ccp.max_terminate"); + PPP_FSM_CONFIG(&_this->fsm, maxnakloops, "ccp.max_nak_loop"); +} + +/** + * Request Command Interpreter。 + */ +static int +ccp_reqci(fsm *f, u_char *pktp, int *lpktp, int reject_if_disagree) +{ + int type, len, rcode, lrej, lnak; + u_char *rejbuf, *nakbuf, *nakbuf0, *pktp0; +#ifdef USE_NPPPD_MPPE + uint32_t peer_bits, our_bits; +#endif + npppd_ppp *ppp; + + ppp = f->ppp; + + rejbuf = NULL; + rcode = CONFACK; + pktp0 = pktp; + lrej = 0; + lnak = 0; + + if ((rejbuf = malloc(*lpktp)) == NULL) { + return rcode; + } + if ((nakbuf0 = malloc(*lpktp)) == NULL) { + free(rejbuf); + return rcode; + } + nakbuf = nakbuf0; +#define remlen() (*lpktp - (pktp - pktp0)) + + while (remlen() >= 2) { + GETCHAR(type, pktp); + GETCHAR(len, pktp); + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { +#ifdef USE_NPPPD_MPPE + case CCP_MPPE: + if (len < 6) + goto reigai; + + if (ppp->mppe.enabled == 0) + goto reject; + GETLONG(peer_bits, pktp); + our_bits = mppe_create_our_bits(&ppp->mppe, peer_bits); + if (our_bits != peer_bits) { + if (reject_if_disagree) { + pktp -= 4; + goto reject; + } + if (lrej > 0) { + /* reject があれば、Rej するので Nak しない */ + } else { + PUTCHAR(type, nakbuf); + PUTCHAR(6, nakbuf); + PUTLONG(our_bits, nakbuf); + rcode = CONFNAK; + } + } else + ppp->ccp.mppe_p_bits = our_bits; + break; +reject: +#endif + default: + pktp -= 2; + memcpy(rejbuf + lrej, pktp, len); + lrej += len; + pktp += len; + rcode = CONFREJ; + } + continue; + } +reigai: + switch (rcode) { + case CONFREJ: + memcpy(pktp0, rejbuf, lrej); + *lpktp = lrej; + break; + case CONFNAK: + len = nakbuf - nakbuf0; + memcpy(pktp0, nakbuf0, len); + *lpktp = len; + break; + } + if (rejbuf != NULL) + free(rejbuf); + if (nakbuf0 != NULL) + free(nakbuf0); + + return rcode; +#undef remlen +} + +static void +ccp_open(fsm *f) +{ + ppp_ccp_opened(f->ppp); +} + +static void +ccp_close(fsm *f) +{ +} + +static void +ccp_start(fsm *f) +{ +} + +static void +ccp_stop(fsm *f) +{ +#ifdef USE_NPPPD_MPPE + fsm_log(f, LOG_INFO, "CCP is stopped"); + if (f->ppp->mppe.required) + ppp_stop(f->ppp, NULL); +#endif +} + +static void +ccp_resetci(fsm *f) +{ +#ifdef USE_NPPPD_MPPE + if (f->ppp->mppe_started == 0) + f->ppp->ccp.mppe_o_bits = + mppe_create_our_bits(&f->ppp->mppe, 0); + /* 開始していたらリセットはしない */ +#endif +} + +static int +ccp_cilen(fsm *f) +{ + return f->ppp->mru; +} + +/** + * ConfReq を作ります。 + */ +static void +ccp_addci(fsm *f, u_char *pktp, int *lpktp) +{ + u_char *pktp0; + + pktp0 = pktp; + + if (f->ppp->ccp.mppe_rej == 0) { + PUTCHAR(CCP_MPPE, pktp); + PUTCHAR(6, pktp); + PUTLONG(f->ppp->ccp.mppe_o_bits, pktp); + + *lpktp = pktp - pktp0; + } else + *lpktp = 0; +} + +static int +ccp_ackci(fsm *f, u_char *pktp, int lpkt) +{ + return ccp_nackackci(f, pktp, lpkt, 0, 0); +} + + +static int +ccp_nakci(fsm *f, u_char *pktp, int lpkt) +{ + return ccp_nackackci(f, pktp, lpkt, 1, 0); +} + +static int +ccp_rejci(fsm *f, u_char *pktp, int lpkt) +{ + return ccp_nackackci(f, pktp, lpkt, 0, 1); +} + +static int +ccp_nackackci(fsm *f, u_char *pktp, int lpkt, int is_nak, int is_rej) +{ + int type, len; + u_char *pktp0; +#ifdef USE_NPPPD_MPPE + uint32_t peer_bits, our_bits; +#endif + npppd_ppp *ppp; + + ppp = f->ppp; + + pktp0 = pktp; + +#define remlen() (lpkt - (pktp - pktp0)) + while (remlen() >= 2) { + GETCHAR(type, pktp); + GETCHAR(len, pktp); + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { +#ifdef USE_NPPPD_MPPE + case CCP_MPPE: + if (len < 6) + goto reigai; + if (is_rej) { + f->ppp->ccp.mppe_rej = 1; + return 1; + } + if (ppp->mppe_started != 0) { + // 静かに再送する。 + return 1; + } + GETLONG(peer_bits, pktp); + /* + * RTX-1000 で ppp ccp mppe-any すると、 + * + * IDGW ConfReq (40,56,128) => RTX + * IDGW <= (40,128) ConfNAK RTX + * IDGW ConfReq (40,56,128) => RTX + * IDGW <= (40,128) ConfNAK RTX + * : + * とお互い譲りすぎ。ConfNak されたら、こちらが提案 + * する。 + */ + our_bits = mppe_create_our_bits(&ppp->mppe, peer_bits); + if (peer_bits == our_bits || is_nak) + ppp->ccp.mppe_o_bits = our_bits; + + break; +#endif + default: + goto reigai; + } + } + return 1; +reigai: + return 0; +} + +#define RESET_REQ 0x0e +#define RESET_ACK 0x0f + +static int +ccp_ext(fsm *f, int code, int id, u_char *pktp, int lpktp) +{ + switch (code) { + case RESET_REQ: + fsm_log(f, LOG_DEBUG, "Received ResetReq %d", id); +#ifdef USE_NPPPD_MPPE + mppe_recv_ccp_reset(&f->ppp->mppe); +#endif + /* + * RFC 3078 では、Reset Ack 不要とは書いていないが、送信する + * と Windows が Code Reject を返すので、送信しない。 + */ + return 1; + case RESET_ACK: + fsm_log(f, LOG_DEBUG, "Received ResetAck %d", id); + return 1; + } + return 0; +} diff --git a/usr.sbin/npppd/npppd/chap.c b/usr.sbin/npppd/npppd/chap.c new file mode 100644 index 00000000000..8aa4801bb96 --- /dev/null +++ b/usr.sbin/npppd/npppd/chap.c @@ -0,0 +1,987 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * CHAP の実装 + * <p> + * RFC 1994 PPP Challenge Handshake Authentication Protocol(CHAP) の実装です。 + * 現在認証する側の実装のみです。</p> + * <p> + * 対応しているプロトコルは、 + * <ul> + * <li>MD5-CHAP</li> + * <li>MS-CHAP v2</li> + * </ul> です。 + */ +/* RFC 1994, 2433 */ +// $Id: chap.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <time.h> +#include <event.h> +#include <md5.h> + +#include "slist.h" +#include "npppd.h" +#include "ppp.h" + +#ifdef USE_NPPPD_RADIUS +#include "radius_chap_const.h" +#endif +#include "npppd_defs.h" + +#include "debugutil.h" +#include "chap_ms.h" + +#define HEADERLEN 4 + +#define CHAP_STATE_INITIAL 1 +#define CHAP_STATE_SENT_CHALLENGE 2 +#define CHAP_STATE_AUTHENTICATING 3 +#define CHAP_STATE_SENT_RESPONSE 4 +#define CHAP_STATE_STOPPED 5 +#define CHAP_STATE_PROXY_AUTHENTICATION 6 + +#define CHAP_TIMEOUT 3 /* リトライ間隔 */ +#define CHAP_RETRY 10 /* リトライ間隔 */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +// RFC 2433 +#define ERROR_RESTRICTED_LOGIN_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +// MprError.h +#define ERROR_AUTH_SERVER_TIMEOUT 930 + +#ifdef CHAP_DEBUG +#define CHAP_DBG(x) chap_log x +#define CHAP_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define CHAP_ASSERT(cond) +#define CHAP_DBG(x) +#endif + +static void chap_authenticate(chap *_this, u_char *, int); +static void chap_failure(chap *, const char *, int); +static void chap_response (chap *, int, u_char *, int); +static void chap_create_challenge (chap *); +static void chap_send_error (chap *, const char *); +static void md5chap_authenticate (chap *, int, char *, u_char *, int, u_char *); +static void mschapv2_send_error (chap *, int, int); +static void mschapv2_authenticate (chap *, int, char *, u_char *, int, u_char *); +#ifdef USE_NPPPD_RADIUS +static void chap_radius_authenticate (chap *, int, char *, u_char *, int, u_char *); +static void chap_radius_response (void *, RADIUS_PACKET *, int); +#endif +static char *strip_nt_domain (char *); +static void chap_log (chap *, uint32_t, const char *, ...) __printflike(3,4); + +/** {@link ::_chap CHAPインスタンス}を初期化します。*/ +void +chap_init(chap *_this, npppd_ppp *ppp) +{ + const char *strval; + + CHAP_ASSERT(ppp != NULL); + CHAP_ASSERT(_this != NULL); + + memset(_this, 0, sizeof(chap)); + _this->ppp = ppp; + + if ((strval = npppd_config_str(ppp->pppd, "chap.name")) == NULL) + gethostname(_this->myname, sizeof(_this->myname)); + else + strlcpy(_this->myname, strval, sizeof(_this->myname)); + + _this->timerctx.ctx = _this; + _this->state = CHAP_STATE_INITIAL; + + _this->ntry = CHAP_RETRY; +} + +/** 認証者として、CHAPを開始します。チャレンジを投げます。*/ +void +chap_start(chap *_this) +{ + u_char *challp, *challp0; + int lmyname; + + CHAP_ASSERT(_this != NULL); + CHAP_ASSERT(_this->ppp != NULL); + + if (_this->state == CHAP_STATE_PROXY_AUTHENTICATION) { + _this->type = PPP_AUTH_CHAP_MD5; + _this->state = CHAP_STATE_AUTHENTICATING; + chap_authenticate(_this, _this->ppp->proxy_authen_resp, + _this->ppp->lproxy_authen_resp); + return; + } + + if (_this->state == CHAP_STATE_INITIAL || + _this->state == CHAP_STATE_SENT_CHALLENGE) { + if (_this->ntry > 0) { + _this->ntry--; + _this->type = _this->ppp->peer_auth; + + /* サポートされている認証タイプかどうか。 */ + if (_this->type != PPP_AUTH_CHAP_MS_V2 && + _this->type != PPP_AUTH_CHAP_MD5) { + chap_log(_this, LOG_ALERT, + "Requested authentication type(0x%x) " + "is not supported.", _this->type); + ppp_stop_ex(_this->ppp, + "Authentication Required", + PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, + PPP_PROTO_CHAP, 2 /* local */, NULL); + return; + } + + /* MS-CHAPv2 を強制 */ +#ifdef USE_NPPPD_MPPE + if (MPPE_REQUIRED(_this->ppp) && + _this->type != PPP_AUTH_CHAP_MS_V2) { + chap_log(_this, LOG_ALERT, + "mppe is required but try to start chap " + "type=0x%02x", _this->type); + ppp_stop_ex(_this->ppp, + "Authentication Required", + PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, + PPP_PROTO_CHAP, 2 /* local */, NULL); + return; + } +#endif + + /* チャレンジのパケットを作成、送信。 */ + challp = ppp_packetbuf(_this->ppp, PPP_AUTH_CHAP); + challp += HEADERLEN; + challp0 = challp; + + chap_create_challenge(_this); + + PUTCHAR(_this->lchall, challp); + memcpy(challp, &_this->chall, _this->lchall); + challp += _this->lchall; + + lmyname = strlen(_this->myname); + + memcpy(challp, _this->myname, lmyname); + challp += lmyname; + + _this->challid = ++_this->pktid; + + ppp_output(_this->ppp, PPP_PROTO_CHAP, CHAP_CHALLENGE, + _this->challid, challp0, challp - challp0); + + _this->state = CHAP_STATE_SENT_CHALLENGE; + + TIMEOUT((void (*)(void *))chap_start, _this, + CHAP_TIMEOUT); + } else { + chap_log(_this, LOG_INFO, + "Client did't respond our challenage."); + ppp_stop_ex(_this->ppp, "Authentication Required", + PPP_DISCON_AUTH_FSM_TIMEOUT, + PPP_PROTO_CHAP, 0, NULL); + } + } +} + +/** CHAP を停止します。*/ +void +chap_stop(chap *_this) +{ + _this->state = CHAP_STATE_STOPPED; + UNTIMEOUT(chap_start, _this); +#ifdef USE_NPPPD_RADIUS + if (_this->radctx != NULL) { + radius_cancel_request(_this->radctx); + _this->radctx = NULL; + } +#endif +} + +/** CHAP パケットの入力があった場合に呼び出します。*/ +void +chap_input(chap *_this, u_char *pktp, int len) +{ + int code, id, length, lval, lname, authok; + u_char *pktp1, *val, namebuf[MAX_USERNAME_LENGTH]; + char *name; + + if (_this->state == CHAP_STATE_STOPPED || + _this->state == CHAP_STATE_INITIAL) { + chap_log(_this, LOG_INFO, "Received chap packet. But chap is " + "not started"); + return; + } + + CHAP_ASSERT(_this != NULL); + if (len < 4) { + chap_log(_this, LOG_ERR, "%s: Received broken packet.", + __func__); + return; + } + + pktp1 = pktp; + + GETCHAR(code, pktp1); + GETCHAR(id, pktp1); + GETSHORT(length, pktp1); + if (len < length || len < 5) { + chap_log(_this, LOG_ERR, "%s: Received broken packet.", + __func__); + return; + } + + if (code != CHAP_RESPONSE) { + chap_log(_this, LOG_ERR, "Received unknown code=%d", code); + return; + } + // Chap Response + + if (id != _this->challid) { + chap_log(_this, LOG_ERR, + "Received challege response has unknown id."); + return; + } + if (_this->state == CHAP_STATE_AUTHENTICATING) + return; + + authok = 0; + UNTIMEOUT(chap_start, _this); + + /* ユーザ名を取得する */ + GETCHAR(lval, pktp1); + val = pktp1; + pktp1 += lval; + + if (lval > length) { + chap_log(_this, LOG_ERR, + "Received challege response has invalid Value-Size " + "field. %d", lval); + return; + } + name = pktp1; + lname = len - (pktp1 - pktp); + if (lname <= 0 || sizeof(namebuf) <= lname + 1) { + chap_log(_this, LOG_ERR, + "Received challege response has invalid Name " + "field."); + return; + } + memcpy(namebuf, name, lname); + namebuf[lname] = '\0'; + name = namebuf; + if (_this->state == CHAP_STATE_SENT_RESPONSE) { + if (strcmp(_this->name, name) != 0) { + /* 再送を要求されているが、ユーザ名が変更された */ + chap_log(_this, LOG_ERR, + "Received AuthReq is not same as before. " + "%s != %s", name, _this->name); + return; + } + } else if (_this->state != CHAP_STATE_SENT_CHALLENGE) { + chap_log(_this, LOG_ERR, + "Received AuthReq in illegal state. username=%s", name); + return; + } + _this->state = CHAP_STATE_AUTHENTICATING; + strlcpy(_this->name, name, sizeof(_this->name)); + + return chap_authenticate(_this, val, lval); +} + +static void +chap_failure(chap *_this, const char *msg, int mschapv2err) +{ + + switch(_this->type) { + case PPP_AUTH_CHAP_MD5: + chap_send_error(_this, "FAILED"); + break; + case PPP_AUTH_CHAP_MS_V2: + mschapv2_send_error(_this, mschapv2err, 0); + break; + } +} + +static void +chap_authenticate(chap *_this, u_char *response, int lresponse) +{ + + switch(_this->type) { + case PPP_AUTH_CHAP_MD5: + /* 長さチェック */ + if (lresponse != 16) { + chap_log(_this, LOG_ERR, + "Invalid response length %d != 16", lresponse); + chap_failure(_this, "FAILED", + ERROR_AUTHENTICATION_FAILURE); + return; + } + break; + case PPP_AUTH_CHAP_MS_V2: + /* 長さチェック */ + if (lresponse < 49) { + chap_log(_this, LOG_ERR, "Packet too short."); + chap_failure(_this, "FAILED", + ERROR_AUTHENTICATION_FAILURE); + return; + } + break; + } + if (npppd_ppp_bind_realm(_this->ppp->pppd, _this->ppp, _this->name, 0) + == 0) { + if (!npppd_ppp_is_realm_ready(_this->ppp->pppd, _this->ppp)) { + chap_log(_this, LOG_INFO, + "username=\"%s\" realm is not ready.", _this->name); + chap_failure(_this, "FAILED", + ERROR_AUTH_SERVER_TIMEOUT); + return; + } +#ifdef USE_NPPPD_RADIUS + if (npppd_ppp_is_realm_radius(_this->ppp->pppd, _this->ppp)) { + chap_radius_authenticate(_this, _this->challid, + _this->name, _this->chall, _this->lchall, response); + return; + /* NOTREACHED */ + } else +#endif + if (npppd_ppp_is_realm_local(_this->ppp->pppd, _this->ppp)) { + switch(_this->type) { + case PPP_AUTH_CHAP_MD5: + md5chap_authenticate(_this, _this->challid, + _this->name, _this->chall, _this->lchall, + response); + return; + /* NOTREACHED */ + case PPP_AUTH_CHAP_MS_V2: + mschapv2_authenticate(_this, _this->challid, + strip_nt_domain(_this->name), + _this->chall, _this->lchall, response); + return; + /* NOTREACHED */ + } + } + } + chap_failure(_this, "FAILED", ERROR_AUTHENTICATION_FAILURE); + + return; +} + +static void +chap_response(chap *_this, int authok, u_char *pktp, int lpktp) +{ + const char *realm_name; + + CHAP_ASSERT(_this != NULL); + CHAP_ASSERT(pktp != NULL); + CHAP_ASSERT(_this->type == PPP_AUTH_CHAP_MD5 || + _this->type == PPP_AUTH_CHAP_MS_V2); + + ppp_output(_this->ppp, PPP_PROTO_CHAP, (authok)? 3 : 4, _this->challid, + pktp, lpktp); + + realm_name = npppd_ppp_get_realm_name(_this->ppp->pppd, _this->ppp); + if (!authok) { + chap_log(_this, LOG_ALERT, + "logtype=Failure username=\"%s\" realm=%s", _this->name, + realm_name); + chap_stop(_this); + /* 失敗したら ppp 終了 */ + ppp_stop_ex(_this->ppp, "Authentication Required", + PPP_DISCON_AUTH_FAILED, PPP_PROTO_CHAP, 1 /* peer */, NULL); + } else { + strlcpy(_this->ppp->username, _this->name, + sizeof(_this->ppp->username)); + chap_log(_this, LOG_INFO, + "logtype=Success username=\"%s\" " + "realm=%s", _this->name, realm_name); + chap_stop(_this); + // 再送要求に答えるために chap_stop でのセットを上書きします。 + _this->state = CHAP_STATE_SENT_RESPONSE; + ppp_auth_ok(_this->ppp); + } +} + +/** チャレンジを生成する。*/ +static void +chap_create_challenge(chap *_this) +{ + int i, lchal; + +#if 0 + lchal = (unsigned)(random() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH; +#endif + CHAP_ASSERT(_this->ppp->peer_auth == PPP_AUTH_CHAP_MS_V2 || + _this->ppp->peer_auth == PPP_AUTH_CHAP_MD5); + + lchal = 16; + +#ifdef HAVE_ARC4RANDOM + { + uint32_t r; + + r = 0; /* avoid gcc 3.3.3's -Wuninitialized warning */ + for (i = 0; i < lchal; i++) { + if (i % 4 == 0) + r = arc4random(); + _this->chall[i] = r & 0xff; + r >>= 8; + } + } +#else + for (i = 0; i < lchal; i++) + _this->chall[i] = random() & 0xff; +#endif + + _this->lchall = lchal; +} + +/*********************************************************************** + * Proxy Authentication + ***********************************************************************/ +int +chap_proxy_authen_prepare(chap *_this, dialin_proxy_info *dpi) +{ + + CHAP_ASSERT(dpi->auth_type == PPP_AUTH_CHAP_MD5); + CHAP_ASSERT(_this->state == CHAP_STATE_INITIAL); + + _this->pktid = dpi->auth_id; + +#ifdef USE_NPPPD_MPPE + if (MPPE_REQUIRED(_this->ppp) && + _this->type != PPP_AUTH_CHAP_MS_V2) { + chap_log(_this, LOG_ALERT, + "mppe is required but try to start chap " + "type=0x%02x", dpi->auth_type); + return -1; + } +#endif + /* authentication */ + if (strlen(dpi->username) >= sizeof(_this->name)) { + chap_log(_this, LOG_NOTICE, + "\"Proxy Authen Name\" is too long."); + return -1; + } + if (dpi->lauth_chall >= sizeof(_this->chall)) { + chap_log(_this, LOG_NOTICE, + "\"Proxy Authen Challenge\" is too long."); + return -1; + } + + /* copy the authenticaiton properties */ + CHAP_ASSERT(_this->ppp->proxy_authen_resp == NULL); + if ((_this->ppp->proxy_authen_resp = malloc(dpi->lauth_resp)) == + NULL) { + chap_log(_this, LOG_ERR, "malloc() failed in %s(): %m", + __func__); + return -1; + } + memcpy(_this->ppp->proxy_authen_resp, dpi->auth_resp, + dpi->lauth_resp); + _this->ppp->lproxy_authen_resp = dpi->lauth_resp; + + _this->challid = dpi->auth_id; + strlcpy(_this->name, dpi->username, sizeof(_this->name)); + + memcpy(_this->chall, dpi->auth_chall, dpi->lauth_chall); + _this->lchall = dpi->lauth_chall; + + _this->state = CHAP_STATE_PROXY_AUTHENTICATION; + + return 0; +} + +/************************************************************************ + * MD5 CHAP の実装 (See RFC 1994) + ************************************************************************/ +static void +md5chap_authenticate(chap *_this, int id, char *username, u_char *challenge, + int lchallenge, u_char *response) +{ + MD5_CTX md5ctx; + int rval, passlen; + u_char digest[16]; + char *password, buf[MAX_PASSWORD_LENGTH + 1]; + + buf[0] = id; + passlen = sizeof(buf) - 1; + password = &buf[1]; + + rval = npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, + password, &passlen); + + if (rval != 0) { + switch (rval) { + case 1: + chap_log(_this, LOG_INFO, + "username=\"%s\" user unknown", username); + break; + default: + chap_log(_this, LOG_ERR, + "username=\"%s\" generic error", username); + break; + } + goto auth_failed; + } + passlen = strlen(password); + MD5Init(&md5ctx); + MD5Update(&md5ctx, buf, passlen + 1); + MD5Update(&md5ctx, challenge, 16); + MD5Final(digest, &md5ctx); + + if (memcmp(response, digest, 16) == 0) { + chap_response(_this, 1, "OK", 2); + return; + } + // パスワード一致せず FALL THROUGH +auth_failed: + //ユーザの有無の情報などは与えない + chap_send_error(_this, "FAILED"); + + return; +} + +static void +chap_send_error(chap *_this, const char *msg) +{ + u_char *pkt, *challenge; + int lpkt; + + challenge = _this->chall; + + pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; + lpkt = _this->ppp->mru - HEADERLEN; + + strlcpy(pkt, msg, lpkt); + lpkt = strlen(msg); + + chap_response(_this, 0, pkt, lpkt); +} + +/************************************************************************ + * MS CHAP V2 の実装 (See RFC 2759) + ************************************************************************/ +static void +mschapv2_send_error(chap *_this, int error, int can_retry) +{ + u_char *pkt, *challenge; + int lpkt; + + challenge = _this->chall; + + pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; + lpkt = _this->ppp->mru - HEADERLEN; + + /* + * We don't use "M=<msg>" + * - pppd on Mac OS 10.4 hungs up if it received a failure packet + * with "M=<msg>". + * - RRAS on windows server 2003 never uses "M=". + */ + snprintf(pkt, lpkt, "E=%d R=%d C=%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x V=3", error, can_retry, + challenge[0], challenge[1], challenge[2], challenge[3], + challenge[4], challenge[5], challenge[6], challenge[7], + challenge[8], challenge[9], challenge[10], challenge[11], + challenge[12], challenge[13], challenge[14], challenge[15] + ); + lpkt = strlen(pkt); + + chap_response(_this, 0, pkt, lpkt); +} + +// idradiusd/auth_mschap2.c からコピペ +static void +mschapv2_authenticate(chap *_this, int id, char *username, u_char *challenge, + int lchallenge, u_char *response) +{ + int i, rval, passlen, lpkt; + u_char *pkt; + char password[MAX_PASSWORD_LENGTH * 2], ntresponse[24]; +#ifdef USE_NPPPD_MPPE + char pwdhash[16], pwdhashhash[16]; +#endif + + CHAP_DBG((_this, LOG_DEBUG, "%s()", __func__)); + pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; + lpkt = _this->ppp->mru - HEADERLEN; + + passlen = sizeof(password) / 2; + rval = npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, + password, &passlen); + + if (rval != 0) { + switch (rval) { + case 1: + chap_log(_this, LOG_INFO, + "username=\"%s\" user unknown", username); + break; + default: + chap_log(_this, LOG_ERR, + "username=\"%s\" generic error", username); + break; + } + goto auth_failed; + } + /* + * Unicode 化 + */ + passlen = strlen(password); + for (i = passlen - 1; i >= 0; i--) { + password[i*2] = password[i]; + password[i*2+1] = 0; // LE + } + + GenerateNTResponse(challenge, response, username, strlen(username), + password, passlen * 2, ntresponse); + + if (memcmp(ntresponse, response + 24, 24) != 0) { + chap_log(_this, LOG_INFO, + "username=\"%s\" password mismatch.", username); + goto auth_failed; + } + /* + * 認証成功 + */ + CHAP_DBG((_this, LOG_DEBUG, "%s() OK", __func__)); + + GenerateAuthenticatorResponse(password, passlen * 2, ntresponse, + response, challenge, username, strlen(username), pkt); + lpkt = 42; +#ifdef USE_NPPPD_MPPE + if (_this->ppp->mppe.enabled != 0) { + NtPasswordHash(password, passlen * 2, pwdhash); + HashNtPasswordHash(pwdhash, pwdhashhash); + + GetMasterKey(pwdhashhash, ntresponse, + _this->ppp->mppe.master_key); + + GetAsymetricStartKey(_this->ppp->mppe.master_key, + _this->ppp->mppe.recv.master_key, MPPE_KEYLEN, 0, 1); + GetAsymetricStartKey(_this->ppp->mppe.master_key, + _this->ppp->mppe.send.master_key, MPPE_KEYLEN, 1, 1); + } +#endif + chap_response(_this, 1, pkt, lpkt); + + return; +auth_failed: + //ユーザの有無の情報などは与えない + mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE, 0); + + return; +} + +#ifdef USE_NPPPD_RADIUS +/************************************************************************ + * RADIUS + ************************************************************************/ +/* + RFC 2058: RADIUS + RFC 2548: Microsoft Vendor-specific RADIUS Attributes + */ +static void +chap_radius_authenticate(chap *_this, int id, char *username, + u_char *challenge, int lchallenge, u_char *response) +{ + void *radctx; + RADIUS_PACKET *radpkt; + radius_req_setting *rad_setting; + int lpkt; + u_char *pkt; + char buf0[MAX_USERNAME_LENGTH]; + + radpkt = NULL; + radctx = NULL; + + if ((rad_setting = npppd_get_radius_req_setting(_this->ppp->pppd, + _this->ppp)) == NULL) { + goto reigai; // NO radius + } + pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; + lpkt = _this->ppp->mru - HEADERLEN; + + if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) + == NULL) + goto reigai; + if (radius_prepare(rad_setting, _this, &radctx, chap_radius_response, + _this->ppp->auth_timeout) != 0) { + radius_delete_packet(radpkt); + goto reigai; + } + + if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) + != 0) + goto reigai; + + if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME, + npppd_ppp_get_username_for_auth(_this->ppp->pppd, _this->ppp, + username, buf0)) != 0) + goto reigai; + + switch (_this->type) { + case PPP_AUTH_CHAP_MD5: + { + u_char md5response[17]; + + md5response[0] = _this->challid; + memcpy(&md5response[1], response, 16); + if (radius_put_raw_attr(radpkt, + RADIUS_TYPE_CHAP_PASSWORD, md5response, 17) != 0) + goto reigai; + if (radius_put_raw_attr(radpkt, + RADIUS_TYPE_CHAP_CHALLENGE, challenge, 16) != 0) + goto reigai; + break; + } + case PPP_AUTH_CHAP_MS_V2: + { + struct RADIUS_MS_CHAP2_RESPONSE msresponse; + + /* RADIUS_MS_CHAP2_RESPONSE の準備 */ + memset(&msresponse, 0, sizeof(msresponse)); + msresponse.ident = id; + msresponse.flags = response[48]; + memcpy(&msresponse.peer_challenge, response, 16); + memcpy(&msresponse.response, response + 24, 24); + + if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MS_CHAP_CHALLENGE, challenge, 16) != 0) + goto reigai; + if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MS_CHAP2_RESPONSE, &msresponse, + sizeof(msresponse)) != 0) + goto reigai; + break; + } + + } + radius_get_authenticator(radpkt, _this->authenticator); + + /* 古い要求はキャンセル。*/ + if (_this->radctx != NULL) + radius_cancel_request(_this->radctx); + + /* 要求を投げる */ + _this->radctx = radctx; + radius_request(radctx, radpkt); + + return; +reigai: + //ユーザの有無の情報などは与えない + switch (_this->type) { + case PPP_AUTH_CHAP_MD5: + chap_send_error(_this, "FAILED"); + break; + case PPP_AUTH_CHAP_MS_V2: + mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE, 0); + break; + } + if (radctx != NULL) + radius_cancel_request(radctx); +} + +static void +chap_radius_response(void *context, RADIUS_PACKET *pkt, int flags) +{ + int code, lrespkt; + const char *secret, *reason = ""; + chap *_this; + u_char *respkt, *respkt0; + int errorCode; + RADIUS_REQUEST_CTX radctx; + + CHAP_ASSERT(context != NULL); + + reason = ""; + errorCode = ERROR_AUTH_SERVER_TIMEOUT; + _this = context; + secret = radius_get_server_secret(_this->radctx); + radctx = _this->radctx; + _this->radctx = NULL; /* 大事 */ + + respkt = respkt0 = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + + HEADERLEN; + lrespkt = _this->ppp->mru - HEADERLEN; + if (pkt == NULL) { + if (flags & RADIUS_REQUST_TIMEOUT) { + reason = "timeout"; + npppd_radius_server_failure_notify(_this->ppp->pppd, + _this->ppp, radctx, "request timeout"); + } else { + reason = "error"; + npppd_radius_server_failure_notify(_this->ppp->pppd, + _this->ppp, radctx, "unknown error"); + } + goto auth_failed; + } + + code = radius_get_code(pkt); + if (code == RADIUS_CODE_ACCESS_REJECT) { + reason="reject"; + errorCode = ERROR_AUTHENTICATION_FAILURE; + /* Windows 側のパスワードはリセットされる */ + goto auth_failed; + } else if (code != RADIUS_CODE_ACCESS_ACCEPT) { + reason="error"; + goto auth_failed; + } + if ((flags & RADIUS_REQUST_CHECK_AUTHENTICTOR_OK) == 0 && + (flags & RADIUS_REQUST_CHECK_AUTHENTICTOR_NO_CHECK) == 0) { + reason="bad_authenticator"; + npppd_radius_server_failure_notify(_this->ppp->pppd, _this->ppp, + radctx, "bad authenticator"); + goto auth_failed; + } + /* + * 認証 OK + */ + switch (_this->type) { + case PPP_AUTH_CHAP_MD5: + chap_response(_this, 1, "OK", 2); + break; + case PPP_AUTH_CHAP_MS_V2: + { + struct RADIUS_MS_CHAP2_SUCCESS success; +#ifdef USE_NPPPD_MPPE + struct RADIUS_MPPE_KEY sendkey, recvkey; +#endif + u_char len; + + len = sizeof(success); + if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MS_CHAP2_SUCCESS, &success, &len) != 0) { + chap_log(_this, LOG_ERR, "no ms_chap2_success"); + goto auth_failed; + } +#ifdef USE_NPPPD_MPPE + if (_this->ppp->mppe.enabled != 0) { + len = sizeof(sendkey); + if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MPPE_SEND_KEY, &sendkey, &len) != 0) { + chap_log(_this, LOG_ERR, "no mppe_send_key"); + goto auth_failed; + } + len = sizeof(recvkey); + if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MPPE_RECV_KEY, &recvkey, &len) != 0) { + chap_log(_this, LOG_ERR, "no mppe_recv_key"); + goto auth_failed; + } + + DecryptKeyFromRadius(_this->ppp->mppe.send.master_key, + sendkey.salt, _this->authenticator, secret); + + DecryptKeyFromRadius(_this->ppp->mppe.recv.master_key, + recvkey.salt, _this->authenticator, secret); + } +#endif + chap_response(_this, 1, success.str, sizeof(success.str)); + break; + } + } + ppp_proccess_radius_framed_ip(_this->ppp, pkt); + + return; +auth_failed: + chap_log(_this, LOG_WARNING, "Radius authentication request failed: %s", + reason); + //ユーザの有無の情報などは与えない + chap_failure(_this, "FAILED", errorCode); +} + +#endif + +/************************************************************************ + * ユーティリティ関数 + ************************************************************************/ +static char * +strip_nt_domain(char *username) +{ + char *lastbackslash; + + if ((lastbackslash = strrchr(username, '\\')) != NULL) + return lastbackslash + 1; + + return username; +} + +static void +chap_log(chap *_this, uint32_t prio, const char *fmt, ...) +{ + const char *protostr; + char logbuf[BUFSIZ]; + va_list ap; + + CHAP_ASSERT(_this != NULL); + CHAP_ASSERT(_this->ppp != NULL); + + switch (_this->type) { + case PPP_AUTH_CHAP_MD5: + protostr = "chap"; + break; + case PPP_AUTH_CHAP_MS_V2: + protostr = "mschap_v2"; + break; + default: + protostr = "unknown"; + break; + } + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=chap proto=%s %s", + _this->ppp->id, protostr, fmt); + vlog_printf(prio, logbuf, ap); + va_end(ap); +} diff --git a/usr.sbin/npppd/npppd/chap_ms.c b/usr.sbin/npppd/npppd/chap_ms.c new file mode 100644 index 00000000000..51d4d78e317 --- /dev/null +++ b/usr.sbin/npppd/npppd/chap_ms.c @@ -0,0 +1,479 @@ +/*- + * Copyright (c) 1997 Gabor Kincses <gabor@acm.org> + * 1997 - 2001 Brian Somers <brian@Awfulhak.org> + * based on work by Eric Rosenquist + * Strata Software Limited. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/ppp/chap_ms.c,v 1.9.2.5 2001/08/18 02:46:06 brian Exp $ + */ +#include <sys/cdefs.h> +#ifndef LINT +__COPYRIGHT( +"@(#) Copyright (c) 1997 Gabor Kincses <gabor@acm.org>\n" +"@(#) 1997 - 2001 Brian Somers <brian@Awfulhak.org>\n" +"@(#) based on work by Eric Rosenquist\n" +"@(#) Strata Software Limited.\n" +"@(#) All rights reserved.\n" +); +#endif +#include <ctype.h> +#ifdef __FreeBSD__ +#include <openssl/des.h> +#include <sha.h> +#else +#include <sys/types.h> +#include <stdlib.h> +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <openssl/des.h> +#else +#include <des.h> +#endif +#include <openssl/sha.h> +#endif +#include <md4.h> +#include <md5.h> +#include <string.h> +#include <stdio.h> + +#include <openssl/opensslv.h> +#include "chap_ms.h" + +#if (OPENSSL_VERSION_NUMBER - 0 >= 0x00907000) +/* + * 0.9.7 + */ +#define des_key_schedule DES_key_schedule +#define des_set_odd_parity DES_set_odd_parity +#define des_set_key DES_set_key +#define des_ecb_encrypt DES_ecb_encrypt +#define des_cblock DES_cblock +#define des_set_key DES_set_key +#define des_ebc_encrypt DES_ebc_encrypt +#endif +/* + * Documentation & specifications: + * + * MS-CHAP (CHAP80) rfc2433 + * MS-CHAP-V2 (CHAP81) rfc2759 + * MPPE key management draft-ietf-pppext-mppe-keys-02.txt + */ + +static char SHA1_Pad1[40] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static char SHA1_Pad2[40] = + {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, + 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2}; + +/* unused, for documentation only */ +/* only NTResp is filled in for FreeBSD */ +struct MS_ChapResponse { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +}; + +static u_char +Get7Bits(u_char *input, int startBit) +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +/* IN 56 bit DES key missing parity bits + OUT 64 bit DES key with parity bits added */ +static void +MakeKey(u_char *key, u_char *des_key) +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + + des_set_odd_parity((des_cblock *)des_key); +} + +static void /* IN 8 octets IN 7 octest OUT 8 octets */ +DesEncrypt(u_char *clear, u_char *key, u_char *cipher) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + +#if (OPENSSL_VERSION_NUMBER - 0 >= 0x00907000) + des_set_key(&des_key, &key_schedule); + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, &key_schedule, 1); +#else + des_set_key(&des_key, key_schedule); + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); +#endif +} + +static void /* IN 8 octets IN 16 octets OUT 24 octets */ +ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response) +{ + char ZPasswordHash[21]; + + memset(ZPasswordHash, '\0', sizeof ZPasswordHash); + memcpy(ZPasswordHash, pwHash, 16); + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); +} + +void +NtPasswordHash(char *key, int keylen, char *hash) +{ + MD4_CTX MD4context; + + MD4Init(&MD4context); + MD4Update(&MD4context, key, keylen); + MD4Final(hash, &MD4context); +} + +void +HashNtPasswordHash(char *hash, char *hashhash) +{ + MD4_CTX MD4context; + + MD4Init(&MD4context); + MD4Update(&MD4context, hash, 16); + MD4Final(hashhash, &MD4context); +} + +void +ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge, + char *UserName, int UserNameLen, char *Challenge) +{ + SHA_CTX Context; + char Digest[SHA_DIGEST_LENGTH]; + char *Name; + + Name = strrchr(UserName, '\\'); + if(NULL == Name) + Name = UserName; + else + Name++; + + SHA1_Init(&Context); + + SHA1_Update(&Context, PeerChallenge, 16); + SHA1_Update(&Context, AuthenticatorChallenge, 16); + SHA1_Update(&Context, Name, strlen(Name)); + + SHA1_Final(Digest, &Context); + memcpy(Challenge, Digest, 8); +} + +void +GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge, + char *UserName, int UserNameLen, char *Password, + int PasswordLen, char *Response) +{ + char Challenge[8]; + char PasswordHash[16]; + + ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen, + Challenge); + NtPasswordHash(Password, PasswordLen, PasswordHash); + ChallengeResponse(Challenge, PasswordHash, Response); +} + +#if !defined(__FreeBSD__) +static char *SHA1_End __P((SHA_CTX *, char *)); +#define LENGTH 20 +static char * +SHA1_End(SHA_CTX *ctx, char *buf) +{ + int i; + unsigned char digest[LENGTH]; + static const char hex[]="0123456789abcdef"; + + if (!buf) + buf = malloc(2*LENGTH + 1); + if (!buf) + return 0; + SHA1_Final(digest, ctx); + for (i = 0; i < LENGTH; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + return buf; +} +#endif + +void +GenerateAuthenticatorResponse(char *Password, int PasswordLen, + char *NTResponse, char *PeerChallenge, + char *AuthenticatorChallenge, char *UserName, + int UserNameLen, char *AuthenticatorResponse) +{ + SHA_CTX Context; + char PasswordHash[16]; + char PasswordHashHash[16]; + char Challenge[8]; + u_char Digest[SHA_DIGEST_LENGTH]; + int i; + + /* + * "Magic" constants used in response generation + */ + char Magic1[39] = + {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74}; + + + char Magic2[41] = + {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E}; + /* + * Hash the password with MD4 + */ + NtPasswordHash(Password, PasswordLen, PasswordHash); + /* + * Now hash the hash + */ + HashNtPasswordHash(PasswordHash, PasswordHashHash); + + SHA1_Init(&Context); + SHA1_Update(&Context, PasswordHashHash, 16); + SHA1_Update(&Context, NTResponse, 24); + SHA1_Update(&Context, Magic1, 39); + SHA1_Final(Digest, &Context); + ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen, + Challenge); + SHA1_Init(&Context); + SHA1_Update(&Context, Digest, 20); + SHA1_Update(&Context, Challenge, 8); + SHA1_Update(&Context, Magic2, 41); + + /* + * Encode the value of 'Digest' as "S=" followed by + * 40 ASCII hexadecimal digits and return it in + * AuthenticatorResponse. + * For example, + * "S=0123456789ABCDEF0123456789ABCDEF01234567" + */ + AuthenticatorResponse[0] = 'S'; + AuthenticatorResponse[1] = '='; + SHA1_End(&Context, AuthenticatorResponse + 2); + for (i=2; i<42; i++) + AuthenticatorResponse[i] = toupper((unsigned char)AuthenticatorResponse[i]); + +} + +void +GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey) +{ + char Digest[SHA_DIGEST_LENGTH]; + SHA_CTX Context; + static char Magic1[27] = + {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79}; + + SHA1_Init(&Context); + SHA1_Update(&Context, PasswordHashHash, 16); + SHA1_Update(&Context, NTResponse, 24); + SHA1_Update(&Context, Magic1, 27); + SHA1_Final(Digest, &Context); + memcpy(MasterKey, Digest, 16); +} + +void +GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength, + int IsSend, int IsServer) +{ + char Digest[SHA_DIGEST_LENGTH]; + SHA_CTX Context; + char *s; + + static char Magic2[84] = + {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e}; + + static char Magic3[84] = + {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e}; + + if (IsSend) { + if (IsServer) { + s = Magic3; + } else { + s = Magic2; + } + } else { + if (IsServer) { + s = Magic2; + } else { + s = Magic3; + } + } + + SHA1_Init(&Context); + SHA1_Update(&Context, MasterKey, 16); + SHA1_Update(&Context, SHA1_Pad1, 40); + SHA1_Update(&Context, s, 84); + SHA1_Update(&Context, SHA1_Pad2, 40); + SHA1_Final(Digest, &Context); + + memcpy(SessionKey, Digest, SessionKeyLength); +} + +void +GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength, + char *InterimKey) +{ + SHA_CTX Context; + char Digest[SHA_DIGEST_LENGTH]; + + SHA1_Init(&Context); + SHA1_Update(&Context, StartKey, SessionKeyLength); + SHA1_Update(&Context, SHA1_Pad1, 40); + SHA1_Update(&Context, SessionKey, SessionKeyLength); + SHA1_Update(&Context, SHA1_Pad2, 40); + SHA1_Final(Digest, &Context); + + memcpy(InterimKey, Digest, SessionKeyLength); +} + +#if 0 +static void +Get_Key(char *InitialSessionKey, char *CurrentSessionKey, + int LengthOfDesiredKey) +{ + SHA_CTX Context; + char Digest[SHA_DIGEST_LENGTH]; + + SHA1_Init(&Context); + SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey); + SHA1_Update(&Context, SHA1_Pad1, 40); + SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey); + SHA1_Update(&Context, SHA1_Pad2, 40); + SHA1_Final(Digest, &Context); + + memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey); +} +#endif + +/* passwordHash 16-bytes MD4 hashed password + challenge 8-bytes peer CHAP challenge + since passwordHash is in a 24-byte buffer, response is written in there */ +void +mschap_NT(char *passwordHash, char *challenge) +{ + u_char response[24]; + + ChallengeResponse(challenge, passwordHash, response); + memcpy(passwordHash, response, 24); + passwordHash[24] = 1; /* NT-style response */ +} + +void +mschap_LANMan(char *digest, char *challenge, char *secret) +{ + static u_char salt[] = "KGS!@#$%"; /* RASAPI32.dll */ + char SECRET[14], *ptr, *end; + u_char hash[16]; + + end = SECRET + sizeof SECRET; + for (ptr = SECRET; *secret && ptr < end; ptr++, secret++) + *ptr = toupper((unsigned char)*secret); + if (ptr < end) + memset(ptr, '\0', end - ptr); + + DesEncrypt(salt, SECRET, hash); + DesEncrypt(salt, SECRET + 7, hash + 8); + + ChallengeResponse(challenge, hash, digest); +} + +void +DecryptKeyFromRadius(char* plain, const char* crypted, + const char *authenticator, const char *secret) +{ + char b[16]; + char p[32]; + MD5_CTX ctx; + int i; + + MD5Init(&ctx); + MD5Update(&ctx, secret, strlen(secret)); + MD5Update(&ctx, authenticator, 16); + MD5Update(&ctx, crypted, 2); + MD5Final(b, &ctx); + + for(i=0;i<16;i++) { + p[i] = b[i] ^ crypted[i+2]; + } + + MD5Init(&ctx); + MD5Update(&ctx, secret, strlen(secret)); + MD5Update(&ctx, crypted+2, 16); + MD5Final(b, &ctx); + + for(i=0;i<16;i++) { + p[i+16] = b[i] ^ crypted[i+18]; + } + + memcpy(plain, p+1, 16); +} diff --git a/usr.sbin/npppd/npppd/chap_ms.h b/usr.sbin/npppd/npppd/chap_ms.h new file mode 100644 index 00000000000..a8203884f98 --- /dev/null +++ b/usr.sbin/npppd/npppd/chap_ms.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1997 Gabor Kincses <gabor@acm.org> + * 1997 - 2001 Brian Somers <brian@Awfulhak.org> + * based on work by Eric Rosenquist + * Strata Software Limited. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/ppp/chap_ms.h,v 1.5.2.2 2001/08/18 02:46:06 brian Exp $ + */ + +/* Max # of (Unicode) chars in an NT password */ +#define MAX_NT_PASSWORD 256 + +/* Don't rely on sizeof(MS_ChapResponse) in case of struct padding */ +#define MS_CHAP_RESPONSE_LEN 49 +#define CHAP81_RESPONSE_LEN 49 +#define CHAP81_NTRESPONSE_LEN 24 +#define CHAP81_NTRESPONSE_OFF 24 +#define CHAP81_HASH_LEN 16 +#define CHAP81_AUTHRESPONSE_LEN 42 +#define CHAP81_CHALLENGE_LEN 16 + +extern void mschap_NT(char *, char *); +extern void mschap_LANMan(char *, char *, char *); +extern void GenerateNTResponse(char *, char *, char *, int, char *, int , char *); +extern void HashNtPasswordHash(char *, char *); +extern void NtPasswordHash(char *, int, char *); +extern void ChallengeHash(char *, char *, char *UserName, int, char *); +extern void GenerateAuthenticatorResponse(char *, int, char *, char *, char *, char *, int, char *); +extern void GetAsymetricStartKey(char *, char *, int, int, int); +extern void GetMasterKey(char *, char *, char *); +extern void GetNewKeyFromSHA(char *, char *, long, char *); +extern void DecryptKeyFromRadius(char *, const char *, const char *, const char *); diff --git a/usr.sbin/npppd/npppd/eap.c b/usr.sbin/npppd/npppd/eap.c new file mode 100644 index 00000000000..26fce035a7d --- /dev/null +++ b/usr.sbin/npppd/npppd/eap.c @@ -0,0 +1,966 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * + * EAP Pass-through Authenticator の実装。 + * + * @see RFC3748 + * Extensible Authentication Protocols(EAP) + * @see RFC3579 + * RADIUS (Remote Authentication Dial In User Service) Support For + * Extensible Authentication Protocol (EAP). B. Aboba, P. Calhoun. + */ +// $Id: eap.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + +/* FIXME: コメント/ログの意味がわからない */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <time.h> +#include <event.h> + +#ifdef USE_NPPPD_RADIUS +#include <radius+.h> +#include <radiusconst.h> +#endif + +#include "debugutil.h" +#ifdef USE_NPPPD_RADIUS +#include "radius_chap_const.h" +#endif +#include "npppd_local.h" +#include "chap_ms.h" + +/* initital state */ +#define EAP_STATE_INITIAL 1 +#define EAP_STATE_SEND_REQUEST_TO_PEER 2 +#define EAP_STATE_STOPPED 3 + +#define EAP_HEADERLEN 4 + +#define EAP_TIMEOUT_INIT 3 /* 初期リトライ間隔 */ +#define EAP_TIMEOUT_MAX 20 /* 最大リトライ間隔 */ +#define EAP_RETRY 4 /* リトライ回数 */ + +#define EAP_REQUEST 1 +#define EAP_RESPONSE 2 +#define EAP_SUCCESS 3 +#define EAP_FAILURE 4 + +/* MprError.h */ +#define ERROR_AUTH_SERVER_TIMEOUT 930 + +#define EAP_DEBUG +#ifdef EAP_DEBUG +#define EAP_DBG(x) eap_log x +#define EAP_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define EAP_ASSERT(cond) +#define EAP_DBG(x) +#endif + +#define TMPBUF 256 +#define IDENT_STRING "What your name?" +#define TIMER_CBFUNC void(*)(void *) + +#define INIT_EAPID 1 + +static void eap_restart(eap *_this); +static void eap_forward_to_radius(eap *_this, u_int8_t *data, int datalen); +static void eap_recv_from_radius(void *context, RADIUS_PACKET *pkt, int flags); +static int eap_forward_to_peer(eap *_this, u_int8_t *data, int datalen, int type, u_int8_t id); +static void eap_log(eap *_this, uint32_t prio, const char *fmt, ...) __printflike(3,4); +#ifdef USE_NPPPD_MPPE +static int get_mppe_keys(eap *_this, RADIUS_PACKET *pkt, const char *secret); +#endif + +/** + * {@link ::_eap EAPインスタンス}を初期化します。 + */ +void +eap_init(eap *_this, npppd_ppp *ppp) +{ + EAP_ASSERT(ppp != NULL); + EAP_ASSERT(_this != NULL); + /* initiallize */ + memset(_this, 0, sizeof(eap)); + _this->ntry = EAP_RETRY; + _this->ppp = ppp; + _this->state = EAP_STATE_INITIAL; +} + +/** + * 認証者として、EAPを開始します。Identity Requestを投げます。 + */ +void +eap_start(eap *_this) +{ + u_int8_t *req,*req0; + int len; + + EAP_ASSERT(_this != NULL); + EAP_ASSERT(_this->ppp != NULL); + + /* + * initialize for timeout callback + */ + _this->name_len = 0; + memset(_this->name, 0, sizeof(_this->name)); + _this->attr_state_len = 0; + memset(_this->attr_state, 0, RADIUS_ATTR_STATE_LEN); + + if (_this->state == EAP_STATE_INITIAL || + _this->state == EAP_STATE_SEND_REQUEST_TO_PEER){ + if (_this->ntry > 0) { + _this->ntry--; + + /* eap header + * code: 1 (request) [1 byte] + * ID: 0x01 (sequence) [1 byte] + * length: ? [2 byte] + */ + + /* + * type: Identity [1 byte] + * data: data [ ? ] + */ + + req = ppp_packetbuf(_this->ppp, PPP_AUTH_EAP); + req += PPP_HDRLEN; + req0 = req; + + PUTCHAR(PPP_AUTH_EAP_IDENTITY, req); + BCOPY(IDENT_STRING, req, (len = strlen(IDENT_STRING))); + req += strlen(IDENT_STRING); + + if (_this->eapid == 0) + _this->eapid = INIT_EAPID; + else + _this->eapid++; + + /* + * send eap request + */ + ppp_output(_this->ppp, PPP_PROTO_EAP, EAP_REQUEST, + _this->eapid, req0, req - req0); + _this->state = EAP_STATE_SEND_REQUEST_TO_PEER; + + TIMEOUT((TIMER_CBFUNC)eap_restart, _this, + EAP_TIMEOUT_INIT); + } else { + eap_log(_this, LOG_NOTICE, + "Client didn't respond our EAP request"); + eap_stop(_this); + ppp_stop(_this->ppp, "Authentication Required"); + } + } +} + +void +eap_restart(eap *_this) { + if (_this == NULL) { + log_printf(LOG_INFO, "Failed restart authentication, " + "already peer session closed with eap"); + return; + } + + eap_log(_this, LOG_INFO, "Retry authentication"); + _this->name_len = 0; + _this->attr_state_len = 0; + memset(_this->name, 0, sizeof(_this->name)); + memset(_this->attr_state, 0, sizeof(_this->attr_state)); + if (_this->radctx != NULL) + radius_cancel_request(_this->radctx); + + eap_start(_this); +} + +void +eap_input(eap *_this, unsigned char *pktp, int len){ + u_int8_t *pkthp; + int code, id, length, type; + + if (_this->state == EAP_STATE_INITIAL || + _this->state == EAP_STATE_STOPPED) { + eap_log(_this, LOG_INFO, "Received eap packet. But eap is " + "not started"); + return; + } + pkthp = pktp; + + UNTIMEOUT(eap_restart, _this); + + if(len < EAP_HEADERLEN + 1){ + /* discard */ + eap_log(_this, LOG_NOTICE, "Packet has unexpect length"); + return; + } + + GETCHAR(code, pkthp); + if (code == EAP_FAILURE) { + /* discard */ + eap_log(_this, LOG_NOTICE, + "Recieved unexpected packet from peer (code = %d)", code); + return; + } + + GETCHAR(id, pkthp); + if (id != _this->eapid) { + /* discard */ + eap_log(_this, LOG_NOTICE, + "Not match EAP identifier (request = %d, response = %d)", + _this->eapid, id); + return; + } + + /* + * get user name from itentity response + */ + GETSHORT(length, pkthp); + GETCHAR(type, pkthp); + if (type == PPP_AUTH_EAP_IDENTITY && _this->name_len == 0) { + if (length != len) { + /* discard */ + eap_log(_this, LOG_NOTICE, + "Identity packet has Invalid length"); + return; + } else { + _this->name_len = length - ( EAP_HEADERLEN + 1 ); + if (_this->name_len <= MAX_USERNAME_LENGTH) { + memcpy(_this->name, pkthp, _this->name_len); + _this->name[_this->name_len] = '\0'; + } else { + /* discard */ + _this->name_len = 0; + eap_log(_this, LOG_ERR, + "Identity name is too long"); + return; + } + } + } + + if (type == PPP_AUTH_EAP_NAK){ + /* + * Nak check + */ + _this->flags |= PPP_EAP_FLAG_NAK_RESPONSE; + eap_log(_this, LOG_DEBUG, "peer response is nak"); + } + + if(_this->name_len != 0){ + eap_forward_to_radius(_this, pktp, len); + return; + } + + /* unexpected process + * discard + */ + eap_log(_this, LOG_DEBUG, + "recieve eap length = %d, " + "eap info: code = %d, id = %d, length = %d, type = %d, " + "name length = %d", + len, code, id, length, type, _this->name_len ); + eap_log(_this, LOG_NOTICE, "Recieved unexpected eap packet from peer"); + return; +} + +static void +eap_forward_to_radius(eap *_this, u_int8_t *data, int datalen) +{ + RADIUS_REQUEST_CTX radctx; + RADIUS_PACKET *radpkt = NULL; + const char *secret; + int secretlen; + int rlength = datalen; + radius_req_setting *rad_setting; + int retry = 0; + unsigned int timeout; + char buf0[MAX_USERNAME_LENGTH]; + + /* FIXME: 毎度やる必要なし */ + if (npppd_ppp_bind_realm(_this->ppp->pppd, _this->ppp, _this->name, 1) + != 0) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, "Not found realm"); + retry = 1; + goto reigai; + } + + if (npppd_ppp_is_realm_radius( + _this->ppp->pppd, _this->ppp) == 0) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, "Not found realm"); + retry = 1; + goto reigai; + } + + if ((rad_setting = npppd_get_radius_req_setting( + _this->ppp->pppd, _this->ppp)) == NULL) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, "Not found radius server setting"); + retry = 1; + goto reigai; + } + + /* + * make new request packet + */ + if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) + == NULL){ + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, "Can't make new request packet"); + retry = 1; + goto reigai; + } + + if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) + != 0) { + /* + * internal error + * retry + */ + retry = 1; + goto reigai; + } + + /* avoid EAP fragmentation */ + if (radius_put_uint32_attr(radpkt, RADIUS_TYPE_FRAMED_MTU, + _this->ppp->mru) != 0) { + /* + * internal error + * retry + */ + retry = 1; + goto reigai; + } + + /* + * set user name attribute + */ + if (_this->name_len != 0) { + if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME, + npppd_ppp_get_username_for_auth(_this->ppp->pppd, + _this->ppp, _this->name, buf0)) != 0) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, + "Can't put attribute to radius packet. type = %d", + RADIUS_TYPE_USER_NAME); + retry = 1; + goto reigai; + } + } else { + /* + * none Identity + * discard + */ + eap_log(_this, LOG_NOTICE, "Identity name is not seted"); + goto reigai; + } + + /* + * set state attribute + */ + if (_this->attr_state_len != 0) { + if (radius_put_raw_attr(radpkt, + RADIUS_TYPE_STATE, + _this->attr_state, + _this->attr_state_len) != 0) { + /* + * internal error + * discard + */ + eap_log(_this, LOG_ERR, + "Can't put attribute to radius packet. type = %d", + RADIUS_TYPE_STATE); + goto reigai; + } + } + + /* + * set EAP message attribute + * radius packet has some eap message attribute + */ + while (rlength > 0) { + if (rlength > 253) { + if (radius_put_raw_attr(radpkt, + RADIUS_TYPE_EAP_MESSAGE, + data+(datalen-rlength), 253) != 0) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, + "Can't put attribute to radius packet. " + "type = %d", RADIUS_TYPE_EAP_MESSAGE); + retry = 1; + goto reigai; + } + rlength -= 253; + } else { + if (radius_put_raw_attr(radpkt, + RADIUS_TYPE_EAP_MESSAGE, + data+(datalen-rlength), + rlength) != 0) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, + "Can't put attribute to radius packet. " + "type = %d", RADIUS_TYPE_EAP_MESSAGE); + retry = 1; + goto reigai; + } + rlength -= rlength; + } + } + + /* + * request cancel + */ + if (_this->radctx != NULL) + radius_cancel_request(_this->radctx); + + /* + * prepare request + */ + if (_this->session_timeout != 0) +/* FIXME: 認証タイムアウトと独立したタイマーが必要なのか? */ + timeout = _this->session_timeout/2; + else + timeout = _this->ppp->auth_timeout; + if (radius_prepare(rad_setting, _this, &radctx, + eap_recv_from_radius, timeout) != 0) { + /* + * internal error + * retry + */ + eap_log(_this, LOG_ERR, "Can't prepare to send access request " + "packet to radius"); + if (!npppd_ppp_is_realm_ready(_this->ppp->pppd, _this->ppp)) { + eap_log(_this, LOG_ERR, + "radius server setting is not ready"); + } + retry = 1; + goto reigai; + } + _this->radctx = radctx; + + /* + * get secret password + * for radius + */ + secret = radius_get_server_secret(_this->radctx); + secretlen = strlen(secret); + + /* + * set message authenticator attribute + */ + if (radius_put_message_authenticator(radpkt, secret) != 0) { + eap_log(_this, LOG_ERR, "couldn't put message authentication " + "attribute to radius packet"); + retry = 1; + goto reigai; + } + + radius_get_authenticator(radpkt, _this->authenticator); + + /* + * send request + */ + radius_request(_this->radctx, radpkt); + return; +reigai: + /* + * don't give peer user infomation + */ + if (radpkt != NULL) + radius_delete_packet(radpkt); + eap_log(_this, LOG_NOTICE, "Can't forward packet to radius from peer"); + if (retry) { + eap_restart(_this); + } + return; +} + +static void +eap_recv_from_radius(void *context, RADIUS_PACKET *pkt, int flags) +{ + int code; + eap *_this; + int errorCode; + int finish; + int retry = 0; + char *notify_reason = NULL; + RADIUS_REQUEST_CTX radctx; + u_char msgbuf[4096], *cp; /* FIXME: たぶん十分 */ + int len; + u_int8_t attrlen = 0; + + u_int8_t eap_code = 0; + u_int8_t eap_id = 0; + size_t eap_length; + + const char *secret; + int secretlen; + + EAP_ASSERT(context != NULL); + + _this = context; + radctx = _this->radctx; + errorCode = ERROR_AUTH_SERVER_TIMEOUT; + _this->radctx = NULL; + + if (pkt == NULL) { + if (flags & RADIUS_REQUST_TIMEOUT) { + /* + * timeout + * retry + */ + eap_log(_this, LOG_WARNING, "Timeout radius response"); + retry = 1; + notify_reason = "timeout"; + } else { + /* + * internal error + * retry + */ + eap_log(_this, LOG_WARNING, + "Internal error with radius packet"); + retry = 1; + notify_reason = "intenal error"; + } + goto auth_failed; + } + + if(!(flags && RADIUS_REQUST_CHECK_AUTHENTICTOR_NO_CHECK) && + !(flags && RADIUS_REQUST_CHECK_AUTHENTICTOR_OK)){ + /* discard */ + eap_log(_this, LOG_WARNING, "Header has invalid authticator"); + notify_reason = "bad authenticator"; + retry = 1; + goto auth_failed; + } + + /* + * get secret password from the radius request context + */ + secret = radius_get_server_secret(radctx); + secretlen = strlen(secret); + + /* + * get radius code + */ + code = radius_get_code(pkt); + if (radius_check_message_authenticator(pkt, secret) != 0) { + eap_log(_this, LOG_WARNING, "bad message authenticator."); + goto auth_failed; + } else { + EAP_DBG((_this, LOG_INFO, "good message authenticator.")); + } + + /* + * get first eap message and get length of eap message + */ + if (radius_get_raw_attr(pkt, RADIUS_TYPE_EAP_MESSAGE, msgbuf, &attrlen) + != 0) { + /* + * check reject + */ + if ((_this->flags & PPP_EAP_FLAG_NAK_RESPONSE) + && code == RADIUS_CODE_ACCESS_REJECT) { + /* + * nak and reject + */ + eap_log(_this, LOG_NOTICE, + "Authentication reject with nak"); + } else if (code == RADIUS_CODE_ACCESS_REJECT) { + /* + * reject + */ + eap_log(_this, LOG_NOTICE, "Authentication reject"); + } else { + /* + * discard + */ + eap_log(_this, LOG_WARNING, "Not found eap attribute"); + goto auth_failed; + } + eap_stop(_this); + ppp_stop(_this->ppp, "Authentication reject"); + goto auth_failed; + } + if (attrlen < 4) { + /* + * discard + */ + eap_log(_this, LOG_WARNING, "EAP message is too short"); + goto auth_failed; + } + cp = msgbuf; + GETCHAR(eap_code, cp); + GETCHAR(eap_id, cp); + _this->eapid = eap_id; + GETSHORT(eap_length, cp); + + /* + * if challenge packet, try get state attribute + */ + if (code == RADIUS_CODE_ACCESS_CHALLENGE) { + _this->attr_state_len = RADIUS_ATTR_STATE_LEN; + if (radius_get_raw_attr(pkt, + RADIUS_TYPE_STATE, + _this->attr_state, + &(_this->attr_state_len)) != 0) { + /* discard */ + eap_log(_this, LOG_ERR, "Not found state attribute"); + goto auth_failed; + } + if (_this->attr_state_len < 1) { + /* discard */ + _this->attr_state_len = 0; + eap_log(_this, LOG_WARNING, + "State attribute has invalid length"); + goto auth_failed; + } + } + + /* + * get session timeout field + */ + if (radius_get_uint32_attr(pkt, RADIUS_TYPE_SESSION_TIMEOUT, + &_this->session_timeout) == 0) { + if (_this->session_timeout > EAP_TIMEOUT_MAX) + _this->session_timeout = EAP_TIMEOUT_MAX; + eap_log(_this, LOG_DEBUG, "Found session timeout attribute"); + } + + /* + * get eap message attribute + */ + if (radius_get_raw_attr_all(pkt, RADIUS_TYPE_EAP_MESSAGE, + NULL, &len) != 0) { + eap_log(_this, LOG_INFO, "Failed to get eap-message from the " + "radius"); + retry = 1; + goto auth_failed; + } + + if (len != eap_length) { + eap_log(_this, LOG_INFO, "Received a bad eap-message: " + "length in the header is wrong."); + retry = 1; + goto auth_failed; + } + if (radius_get_raw_attr_all(pkt, RADIUS_TYPE_EAP_MESSAGE, msgbuf, &len) + != 0) { + eap_log(_this, LOG_INFO, + "failed to get eap-message from the radius response."); + retry = 1; + goto auth_failed; + } + + /* + * forwarding validation + * RFC 3579, RFC 3784 + * + */ + finish = 0; + switch (code) { + case RADIUS_CODE_ACCESS_REQUEST: + eap_log(_this, LOG_INFO, + "Invalid radius code (access request) code=%d eap_code=%d", + code, eap_code); + goto auth_failed; + break; + case RADIUS_CODE_ACCESS_REJECT: + switch (eap_code) { + case EAP_REQUEST: + eap_log(_this, LOG_INFO, "Abnormal reject"); + eap_stop(_this); + ppp_stop(_this->ppp, "Authentication failed"); + finish = 1; + break; + case EAP_RESPONSE: + eap_log(_this, LOG_INFO, + "Unexpected eap code(access reject)"); + goto auth_failed; + break; + case EAP_FAILURE: + eap_log(_this, LOG_INFO, "Eap failure"); + eap_stop(_this); + finish = eap_forward_to_peer(_this, + msgbuf+EAP_HEADERLEN, len-EAP_HEADERLEN, + eap_code, eap_id); + break; + case EAP_SUCCESS: + default: + eap_log(_this, LOG_INFO, + "Invalid combination code: radius code = %d and " + "eap code = %d", code ,eap_code); + goto auth_failed; + break; + } + break; + case RADIUS_CODE_ACCESS_ACCEPT: + switch (eap_code) { + case EAP_REQUEST: + finish = eap_forward_to_peer(_this, + msgbuf+EAP_HEADERLEN, len-EAP_HEADERLEN, + eap_code, eap_id); + break; + case EAP_RESPONSE: + eap_log(_this, LOG_INFO, + "unexpected eap code(access accept)"); + goto auth_failed; + break; + case EAP_FAILURE: + eap_log(_this, LOG_INFO, + "Invalid combination code: radius code = %d and " + "eap code = %d", + code ,eap_code); + goto auth_failed; + break; + case EAP_SUCCESS: + ppp_proccess_radius_framed_ip(_this->ppp, pkt); +#ifdef USE_NPPPD_MPPE + if (get_mppe_keys(_this, pkt, secret)) { + if (MPPE_REQUIRED(_this->ppp)) { + eap_log(_this, LOG_ERR, + "mppe is required but can't get " + "mppe keys"); + eap_stop(_this); + ppp_stop(_this->ppp, "can't get mppe " + "attribute"); + } else { + eap_log(_this, LOG_INFO, + "can't get mppe keys, unuse " + "encryption"); + } + } else { + eap_log(_this, LOG_DEBUG, + "Found attribute of mppe keys"); + } + +#endif + finish = eap_forward_to_peer(_this, + msgbuf+EAP_HEADERLEN, len-EAP_HEADERLEN, + eap_code, eap_id); + break; + default: + eap_log(_this, LOG_INFO, + "Invalid combination code: radius code = %d and " + "eap code = %d", code ,eap_code); + goto auth_failed; + break; + } + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + switch (eap_code) { + case EAP_REQUEST: + finish = eap_forward_to_peer(_this, + msgbuf+EAP_HEADERLEN, len-EAP_HEADERLEN, + eap_code, eap_id); + break; + case EAP_RESPONSE: + eap_log(_this, LOG_INFO, + "Unexpected eap code(access challenge)"); + goto auth_failed; + break; + case EAP_FAILURE: + case EAP_SUCCESS: + default: + eap_log(_this, LOG_INFO, + "Invalid combination code: radius code = %d and " + "eap code = %d", code ,eap_code); + goto auth_failed; + break; + } + /* XXX TODO:not forward EAP-START */ + break; + default: + eap_log(_this, LOG_INFO, + "Unknown radius code type code = %d and eap code = %d", + code ,eap_code); + goto auth_failed; + break; + } + + if(!finish) { + if (_this->session_timeout != 0) { + TIMEOUT((TIMER_CBFUNC)eap_restart, _this, + _this->session_timeout/2); + } else { + TIMEOUT((TIMER_CBFUNC)eap_restart, _this, + EAP_TIMEOUT_INIT); + } + } + return; + +auth_failed: + eap_log(_this, LOG_WARNING, + "Can't forward packet to peer from radius"); + if (notify_reason != NULL) { + npppd_radius_server_failure_notify( + _this->ppp->pppd, _this->ppp, radctx, notify_reason); + } + if (retry) { + eap_restart(_this); + } + return; +} + +#ifdef USE_NPPPD_MPPE +int +get_mppe_keys(eap *_this, RADIUS_PACKET *pkt, const char *secret) { + struct RADIUS_MPPE_KEY sendkey, recvkey; + u_int8_t len; + + EAP_ASSERT(_this != NULL); + EAP_ASSERT(_this->ppp != NULL); + + + if (_this->ppp->mppe.enabled == 0) { + return 1; + } + len = sizeof(sendkey); + if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MPPE_SEND_KEY, &sendkey, &len) != 0) { + eap_log(_this, LOG_ERR, "no mppe_send_key"); + return 1; + } + len = sizeof(recvkey); + if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, + RADIUS_VTYPE_MPPE_RECV_KEY, &recvkey, &len) != 0) { + eap_log(_this, LOG_ERR, "no mppe_recv_key"); + return 1; + } + DecryptKeyFromRadius(_this->ppp->mppe.send.master_key, + sendkey.salt, _this->authenticator, secret); + + DecryptKeyFromRadius(_this->ppp->mppe.recv.master_key, + recvkey.salt, _this->authenticator, secret); + + return 0; +} +#endif + +void +eap_stop(eap *_this) +{ + _this->state = EAP_STATE_STOPPED; + UNTIMEOUT(eap_restart, _this); + if (_this->radctx != NULL) { + radius_cancel_request(_this->radctx); + _this->radctx = NULL; + } +} + +static int +eap_forward_to_peer(eap *_this, u_int8_t *data, int datalen, int type, u_int8_t id) +{ + int finish = 0; + EAP_ASSERT(_this != NULL); + EAP_ASSERT(data != NULL); + + switch (type) { + case EAP_REQUEST: + ppp_output(_this->ppp, PPP_PROTO_EAP, EAP_REQUEST, id, data, + datalen); + break; + case EAP_SUCCESS: + ppp_output(_this->ppp, PPP_PROTO_EAP, EAP_SUCCESS, id, data, + datalen); + eap_log(_this, LOG_INFO, "Authentication succeeded"); + eap_stop(_this); + memcpy(_this->ppp->username, _this->name, _this->name_len); + ppp_auth_ok(_this->ppp); + finish = 1; + break; + case EAP_FAILURE: + ppp_output(_this->ppp, PPP_PROTO_EAP, EAP_FAILURE, id, data, + datalen); + eap_log(_this, LOG_INFO, "eap-failure has been received from the peer."); + eap_log(_this, LOG_INFO, "Authentication failed"); + eap_stop(_this); + ppp_stop(_this->ppp, "Authentication failed"); + finish = 1; + break; + default: + break; + } + return finish; +} + +/************************************************************************ + * ユーティリティ関数 + ************************************************************************/ +void +eap_log(eap *_this, uint32_t prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + EAP_ASSERT(_this != NULL); + EAP_ASSERT(_this->ppp != NULL); + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=eap %s", + _this->ppp->id, fmt); + vlog_printf(prio, logbuf, ap); + va_end(ap); +} diff --git a/usr.sbin/npppd/npppd/fsm.c b/usr.sbin/npppd/npppd/fsm.c new file mode 100644 index 00000000000..2ec34810dfc --- /dev/null +++ b/usr.sbin/npppd/npppd/fsm.c @@ -0,0 +1,823 @@ +/**@file + * This file was adapted from NetBSD:/usr/src/usr.sbin/pppd/pppd/fsm.c + * <p> + * 無駄な実装もなく、ほとんど修正せずに使えるので、なるべくオリジナルの + * 状態に近いように実装しています。(2005/04 yasuoka)</p> + */ +/* +XXX 再送の ConfReq で Initial ConfReq を再送するのはどうかと思う。 + */ +/* $NetBSD: fsm.c,v 1.13 2000/09/23 22:39:35 christos Exp $ */ + +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1989 Carnegie Mellon University.\n" +"@(#) All rights reserved.\n" +); +#if 0 +#define RCSID "Id: fsm.c,v 1.17 1999/08/13 06:46:12 paulus Exp " +#else +__RCSID("$NetBSD: fsm.c,v 1.13 2000/09/23 22:39:35 christos Exp $"); +#endif +#endif + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <stdarg.h> +#include <syslog.h> +#include <stdlib.h> + +/* + * npppd 関連 + */ +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <time.h> +#include <sys/time.h> +#include <event.h> +#include "debugutil.h" +#include "slist.h" +#include "npppd.h" +#include "fsm.h" + +#ifdef FSM_DEBUG +#define FSMDEBUG(x) fsm_log x +#define FSM_ASSERT(x) ASSERT(x) +#else +#define FSMDEBUG(x) +#define FSM_ASSERT(x) +#endif + +#define HEADERLEN 4 + +#ifdef RCSID +static const char rcsid[] = RCSID; +#endif + +static void fsm_timeout __P((void *)); +static void fsm_rconfreq __P((fsm *, int, u_char *, int)); +static void fsm_rconfack __P((fsm *, int, u_char *, int)); +static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int)); +static void fsm_rtermreq __P((fsm *, int, u_char *, int)); +static void fsm_rtermack __P((fsm *)); +static void fsm_rcoderej __P((fsm *, u_char *, int)); +static void fsm_sconfreq __P((fsm *, int)); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +void +fsm_evtimer_timeout(int fd, short evtype, void *ctx) +{ + struct evtimer_wrap *wrap; + + wrap = ctx; + wrap->func(wrap->ctx); +} + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(f) + fsm *f; +{ + f->state = INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = DEFTIMEOUT; + f->maxconfreqtransmits = DEFMAXCONFREQS; + f->maxtermtransmits = DEFMAXTERMREQS; + f->maxnakloops = DEFMAXNAKLOOPS; + f->term_reason_len = 0; + memset(&f->timerctx, 0, sizeof(f->timerctx)); + f->timerctx.ctx = f; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = CLOSED; + break; + + case STARTING: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((f, LOG_DEBUG, "Up event in state %d!", f->state)); + } +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(f) + fsm *f; +{ + switch( f->state ){ + case CLOSED: + f->state = INITIAL; + break; + + case STOPPED: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSING: + f->state = INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + f->state = STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + f->state = STARTING; + break; + + default: + FSMDEBUG((f, LOG_DEBUG, "Down event in state %d!", f->state)); + } +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSED: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + case CLOSING: + f->state = STOPPING; + /* fall through */ + case STOPPED: + case OPENED: + if( f->flags & OPT_RESTART ){ + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the CLOSED state. + */ +void +fsm_close(f, reason) + fsm *f; + const char *reason; +{ + f->term_reason = (char *)reason; + f->term_reason_len = (reason == NULL? 0: strlen(reason)); + switch( f->state ){ + case STARTING: + f->state = INITIAL; + break; + case STOPPED: + f->state = CLOSED; + break; + case STOPPING: + f->state = CLOSING; + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + case OPENED: + if( f->state != OPENED ) + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + else if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = CLOSING; + break; + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(arg) + void *arg; +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case CLOSING: + case STOPPING: + if( f->retransmits <= 0 ){ + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == CLOSING)? CLOSED: STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + } else { + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + if (f->retransmits <= 0) { + fsm_log(f, LOG_WARNING, "timeout sending Config-Requests\n"); + f->state = STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) + (*f->callbacks->finished)(f); + + } else { + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == ACKRCVD ) + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((f, LOG_DEBUG, "Timeout event in state %d!", f->state)); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(f, inpacket, l) + fsm *f; + u_char *inpacket; + int l; +{ + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG((f, LOG_DEBUG, "fsm_input(): Rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((f, LOG_DEBUG, "fsm_input(): Rcvd illegal length.")); + return; + } + if (len > l) { + FSMDEBUG((f, LOG_DEBUG, "fsm_input(): Rcvd short packet.")); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == INITIAL || f->state == STARTING ){ + FSMDEBUG((f, LOG_DEBUG, "fsm_input(): Rcvd packet in state %d.", + f->state)); + return; + } + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode + || !(*f->callbacks->extcode)(f, code, id, inp, len) ) + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(f, id, inp, len) + fsm *f; + u_char id; + u_char *inp; + int len; +{ + int code, reject_if_disagree; + + switch( f->state ){ + case CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case CLOSING: + case STOPPING: + return; + + case OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci){ /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) + code = CONFREJ; /* Reject all CI */ + else + code = CONFACK; + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, code, id, inp, len); + + if (code == CONFACK) { + if (f->state == ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = OPENED; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + } else + f->state = ACKSENT; + f->nakloops = 0; + + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != ACKRCVD) + f->state = REQSENT; + if( code == CONFNAK ) + ++f->nakloops; + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(f, id, inp, len) + fsm *f; + int id; + u_char *inp; + int len; +{ + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): + (len == 0)) ){ + /* Ack is bad - ignore it */ + fsm_log(f, LOG_ERR, "Received bad configure-ack: %p(%d)", inp, len); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + f->state = ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(f, code, id, inp, len) + fsm *f; + int code, id; + u_char *inp; + int len; +{ + int (*proc) __P((fsm *, u_char *, int)); + int ret; + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !(ret = proc(f, inp, len))) { + /* Nak/reject is bad - ignore it */ + fsm_log(f, LOG_INFO, "Received bad configure-%s: %p(%d)", + (code == CONFNAK)? "nak" : "rej", inp, len); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + case ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) + f->state = STOPPED; /* kludge for stopping CCP */ + else + fsm_sconfreq(f, 0); /* Send Configure-Request */ + break; + + case ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(f, id, p, len) + fsm *f; + int id; + u_char *p; + int len; +{ + switch (f->state) { + case ACKRCVD: + case ACKSENT: + f->state = REQSENT; /* Start over but keep trying */ + break; + + case OPENED: + fsm_log(f, LOG_INFO, "terminated by peer"); + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->retransmits = 0; + f->state = STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(f) + fsm *f; +{ + switch (f->state) { + case CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + case STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case ACKRCVD: + f->state = REQSENT; + break; + + case OPENED: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + u_char code, id; + + if (len < HEADERLEN) { + FSMDEBUG((f, LOG_DEBUG, + "fsm_rcoderej: Rcvd short Code-Reject packet!")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + fsm_log(f, LOG_INFO, + "%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id); + + if( f->state == ACKRCVD ) + f->state = REQSENT; +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(f) + fsm *f; +{ + switch( f->state ){ + case CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case CLOSED: + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case STOPPED: + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = STOPPING; + break; + + default: + FSMDEBUG((f, LOG_DEBUG, + "Protocol-reject event in state %d!", f->state)); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(f, retransmit) + fsm *f; + int retransmit; +{ + u_char *outp; + int cilen; + + if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) + (*f->callbacks->resetci)(f); + f->nakloops = 0; + } + + if( !retransmit ){ + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = f->ppp->outpacket_buf + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ){ + cilen = (*f->callbacks->cilen)(f); + if( cilen > f->ppp->mru - HEADERLEN ) + cilen = f->ppp->mru - HEADERLEN; + if (f->callbacks->addci) + (*f->callbacks->addci)(f, outp, &cilen); + } else + cilen = 0; + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata(f, code, id, data, datalen) + fsm *f; + u_char code, id; + u_char *data; + int datalen; +{ + ppp_output(f->ppp, f->protocol, code, id, data, datalen); +} + + +void +fsm_log(fsm *f, uint32_t prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + FSM_ASSERT(f != NULL); + FSM_ASSERT(f->callbacks != NULL); + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=%s %s", f->ppp->id, + PROTO_NAME(f), fmt); + vlog_printf(prio, logbuf, ap); + va_end(ap); +} diff --git a/usr.sbin/npppd/npppd/fsm.h b/usr.sbin/npppd/npppd/fsm.h new file mode 100644 index 00000000000..053d64deae9 --- /dev/null +++ b/usr.sbin/npppd/npppd/fsm.h @@ -0,0 +1,169 @@ +#ifndef FSM_H +#define FSM_H 1 +/* $NetBSD: fsm.h,v 1.10 2000/09/23 22:39:35 christos Exp $ */ + +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Id: fsm.h,v 1.8 1999/11/15 01:51:50 paulus Exp + */ + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN 4 + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + +struct evtimer_wrap { + void *ctx; + void (*func)(void *); + struct event ev; +}; +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + //int unit; /* Interface unit number */ + npppd_ppp *ppp; /* npppd's ppp */ + struct evtimer_wrap timerctx;/* context for event(3) */ + int protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks *callbacks; /* Callback routines */ + const char *term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci) /* Reset our Configuration Information */ + __P((fsm *)); + int (*cilen) /* Length of our Configuration Information */ + __P((fsm *)); + void (*addci) /* Add our Configuration Information */ + __P((fsm *, u_char *, int *)); + int (*ackci) /* ACK our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*nakci) /* NAK our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*rejci) /* Reject our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*reqci) /* Request peer's Configuration Information */ + __P((fsm *, u_char *, int *, int)); + void (*up) /* Called when fsm reaches OPENED state */ + __P((fsm *)); + void (*down) /* Called when fsm leaves OPENED state */ + __P((fsm *)); + void (*starting) /* Called when we want the lower layer */ + __P((fsm *)); + void (*finished) /* Called when we don't want the lower layer */ + __P((fsm *)); + void (*protreject) /* Called when Protocol-Reject received */ + __P((int)); + void (*retransmit) /* Retransmission is necessary */ + __P((fsm *)); + int (*extcode) /* Called when unknown code received */ + __P((fsm *, int, int, u_char *, int)); + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define INITIAL 0 /* Down, hasn't been opened */ +#define STARTING 1 /* Down, been opened */ +#define CLOSED 2 /* Up, hasn't been opened */ +#define STOPPED 3 /* Open, waiting for down event */ +#define CLOSING 4 /* Terminating the connection, not open */ +#define STOPPING 5 /* Terminating, but open */ +#define REQSENT 6 /* We've sent a Config Request */ +#define ACKRCVD 7 /* We've received a Config Ack */ +#define ACKSENT 8 /* We've sent a Config Ack */ +#define OPENED 9 /* Connection available */ + + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Timeouts. + */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ + +/** NetBSD(ANU, CMU) の pppd で使われているマクロを npppd 用に定義 */ +#define TIMEOUT(fn, f, t) \ + { \ + struct timeval tv0; \ + \ + tv0.tv_usec = 0; \ + tv0.tv_sec = (t); \ + if (!evtimer_initialized(&(f)->timerctx.ev)) \ + evtimer_set(&(f)->timerctx.ev, fsm_evtimer_timeout,\ + &(f)->timerctx); \ + (f)->timerctx.func = fn; \ + evtimer_del(&(f)->timerctx.ev); \ + evtimer_add(&(f)->timerctx.ev, &tv0); \ + } + +#define UNTIMEOUT(fn, f) evtimer_del(&(f)->timerctx.ev) + +/* + * Prototypes + */ +void fsm_evtimer_timeout __P((int, short, void *)); +void fsm_init __P((fsm *)); +void fsm_lowerup __P((fsm *)); +void fsm_lowerdown __P((fsm *)); +void fsm_open __P((fsm *)); +void fsm_close __P((fsm *, const char *)); +void fsm_input __P((fsm *, u_char *, int)); +void fsm_protreject __P((fsm *)); +void fsm_sdata __P((fsm *, int, int, u_char *, int)); +void fsm_log __P((fsm *, uint32_t, const char *, ...)) __attribute__((__format__ (__printf__, 3, 4))); + + +#endif diff --git a/usr.sbin/npppd/npppd/ipcp.c b/usr.sbin/npppd/npppd/ipcp.c new file mode 100644 index 00000000000..a5cc34cb58c --- /dev/null +++ b/usr.sbin/npppd/npppd/ipcp.c @@ -0,0 +1,423 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * IPCP の実装です。現在ネットワーク提供者としての実装で、こちらの提案を + * 押しつけます。 + */ +/* + * RFC 1332, 1877 + */ +/* $Id: ipcp.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <event.h> + +#include "debugutil.h" +#include "slist.h" +#include "npppd.h" + +#ifdef IPCP_DEBUG +#define IPCP_DBG(x) fsm_log x +#define IPCP_ASSERT(x) ASSERT(x) +#else +#define IPCP_DBG(x) +#define IPCP_ASSERT(x) +#endif + + +#define IPCP_IP_ADDRESSES 1 +#define IPCP_IP_COMP 2 +#define IPCP_IP_ADDRESS 3 +#define IPCP_PRI_DNS 129 /* 0x81 */ +#define IPCP_PRI_NBNS 130 /* 0x82 */ +#define IPCP_SEC_DNS 131 /* 0x83 */ +#define IPCP_SEC_NBNS 132 /* 0x84 */ + +#define u32maskcmp(mask, a, b) (((a) & (mask)) == ((b) & (mask))) + +static void ipcp_resetci (fsm *); +static int ipcp_cilen (fsm *); +static void ipcp_addci (fsm *, u_char *, int *); +static int ipcp_ackci (fsm *, u_char *, int); +static int ipcp_nakci (fsm *, u_char *, int); +static int ipcp_rejci (fsm *, u_char *, int); +static int ipcp_reqci (fsm *, u_char *, int *, int); +static void ipcp_open (fsm *); +static void ipcp_close (fsm *); +static void ipcp_start (fsm *); +static void ipcp_stop (fsm *); + +static struct fsm_callbacks ipcp_callbacks = { + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + + ipcp_open, /* Called when fsm reaches OPENED state */ + ipcp_close, /* Called when fsm leaves OPENED state */ + ipcp_start, /* Called when we want the lower layer up */ + ipcp_stop, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle LCP-specific codes */ + "ipcp" /* String name of protocol */ +}; + +/** + * {@link ::_ipcp IPCP インスタンス} を初期化します。 + */ +void +ipcp_init(ipcp *_this, npppd_ppp *ppp) +{ + memset(_this, 0, sizeof(ipcp)); + + _this->ppp = ppp; + _this->fsm.ppp = ppp; + + fsm_init(&_this->fsm); + + _this->fsm.callbacks = &ipcp_callbacks; + _this->fsm.protocol = PPP_PROTO_NCP | NCP_IPCP; + PPP_FSM_CONFIG(&_this->fsm, timeouttime, "ipcp.timeout"); + PPP_FSM_CONFIG(&_this->fsm, maxconfreqtransmits,"ipcp.max_configure"); + PPP_FSM_CONFIG(&_this->fsm, maxtermtransmits, "ipcp.max_terminate"); + PPP_FSM_CONFIG(&_this->fsm, maxnakloops, "ipcp.max_nak_loop"); +} + +static void +ipcp_resetci(fsm *f) +{ + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); + if (npppd_prepare_ip(f->ppp->pppd, f->ppp) != 0) { + fsm_log(f, LOG_ERR, "failed to assign ip address."); + ppp_stop(f->ppp, NULL); + } +} + +static int +ipcp_cilen(fsm *f) +{ + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); + return f->ppp->mru; +} + +static void +ipcp_addci(fsm *f, u_char *pktp, int *lpktp) +{ + u_char *pktp0; + + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); + pktp0 = pktp; + + PUTCHAR(IPCP_IP_ADDRESS, pktp); + PUTCHAR(6, pktp); + memcpy(pktp, &f->ppp->ipcp.ip4_our.s_addr, 4); + pktp += 4; + *lpktp = pktp - pktp0; +} + + +static int +ipcp_ackci(fsm *f, u_char *pktp, int lpkt) +{ + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); + /* TODO */ + return -1; +} + +static int +ipcp_nakci(fsm *f, u_char *pktp, int lpkt) +{ + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); + + fsm_log(f, LOG_INFO, "Peer refused(ConfNak) our ip=%s.", + inet_ntoa(f->ppp->ipcp.ip4_our)); + fsm_close(f, NULL); + return -1; +} + +static int +ipcp_rejci(fsm *f, u_char *pktp, int lpkt) +{ + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); + + fsm_log(f, LOG_INFO, "Peer refused(ConfRej) our ip=%s.", + inet_ntoa(f->ppp->ipcp.ip4_our)); + fsm_close(f, NULL); + + return 0; +} + +static int +ipcp_reqci(fsm *f, u_char *pktp, int *lpktp, int reject_if_disagree) +{ + int type, len, rcode, lrej, lnak; + u_char rejbuf0[256], nakbuf0[256], *nakbuf, *rejbuf, *pktp0; + char buf0[256]; + struct in_addr ip_addr, *ip_addrp; + npppd_ppp *ppp; + npppd *_npppd; + int ip_address_acked = 0; + + IPCP_DBG((f, LOG_DEBUG, "%s(reject_if_disagree=%d, nakloops=%d)", + __func__, reject_if_disagree, f->nakloops)); + ppp = f->ppp; + _npppd = ppp->pppd; + + nakbuf = nakbuf0; + rejbuf = rejbuf0; + lrej = 0; + lnak = 0; + pktp0 = pktp; + rcode = -1; + + if (*lpktp > 128) { + rcode = CONFREJ; + rejbuf = pktp; + lrej = *lpktp; + goto reigai; + } + +#define remlen() (*lpktp - (pktp - pktp0)) + + ip_address_acked = 0; + while (remlen() >= 2) { + GETCHAR(type, pktp); + GETCHAR(len, pktp); + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case IPCP_IP_ADDRESS: + case IPCP_PRI_DNS: + case IPCP_PRI_NBNS: + case IPCP_SEC_DNS: + case IPCP_SEC_NBNS: + if (remlen() < 4) + goto reigai; + GETLONG(ip_addr.s_addr, pktp); + ip_addr.s_addr = htonl(ip_addr.s_addr); + + switch (type) { + case IPCP_IP_ADDRESS: + if (!ppp_ip_assigned(ppp)) { + if (npppd_assign_ip_addr(ppp->pppd, ppp, + htonl(ip_addr.s_addr)) != 0 && + npppd_assign_ip_addr(ppp->pppd, ppp, + INADDR_ANY) != 0) { + /* + * INADDR_ANY で call しなおす + * のは、user-select が許可さ + * れている場合に動的割り当て + * へのフォールバックを期待す + * るクライアントへの対応のた + * め。[IDGW-DEV 6847] + */ + pktp -= 4; + goto do_reject; + } + strlcpy(buf0, inet_ntoa(ip_addr), + sizeof(buf0)); + fsm_log(f, LOG_INFO, + "IP Address peer=%s our=%s.", buf0, + inet_ntoa( + ppp->ppp_framed_ip_address)); + } + + if (u32maskcmp(ppp->ppp_framed_ip_netmask + .s_addr, ip_addr.s_addr, + ppp->ppp_framed_ip_address.s_addr)) { + /* + * ネットワーク型払出し時は、対抗の + * IP-Address Option が、払い出すネッ + * トワークに含まれる場合には、対抗 + * の提案に従う。 + */ + ip_addrp = &ip_addr; + } else { + ip_addrp = &ppp-> + ppp_framed_ip_address; + } + ip_address_acked = 1; + break; + case IPCP_PRI_DNS: + ip_addrp = &ppp->ipcp.dns_pri; break; + case IPCP_SEC_DNS: + ip_addrp = &ppp->ipcp.dns_sec; break; + case IPCP_PRI_NBNS: + ip_addrp = &ppp->ipcp.nbns_pri; break; + case IPCP_SEC_NBNS: + ip_addrp = &ppp->ipcp.nbns_sec; break; + default: + ip_addrp = NULL; + } + + if (ip_addrp == NULL || + ip_addrp->s_addr == INADDR_NONE) { + pktp -= 4; + goto do_reject; + } + if (ip_addrp->s_addr != ip_addr.s_addr) { + if (reject_if_disagree) { + pktp -= 4; + goto do_reject; + } + if (lrej > 0) { + /* reject があれば、Rej するので Nak しない */ + } else { + PUTCHAR(type, nakbuf); + PUTCHAR(6, nakbuf); + PUTLONG(ntohl(ip_addrp->s_addr), + nakbuf); + lnak += 6; + rcode = CONFNAK; + } + } + break; + case IPCP_IP_COMP: + case IPCP_IP_ADDRESSES: + default: + fsm_log(f, LOG_DEBUG, "Unhandled Option %02x %d", type, + len); +do_reject: + pktp -= 2; + memmove(rejbuf + lrej, pktp, len); + lrej += len; + pktp += len; + rcode = CONFREJ; + } + continue; + } + if (rcode == -1) + rcode = CONFACK; + +reigai: + switch (rcode) { + case CONFREJ: + IPCP_DBG((f, LOG_DEBUG, "SendConfRej")); + memmove(pktp0, rejbuf0, lrej); + *lpktp = lrej; + break; + case CONFNAK: + /* + * Yamaha で、"pp ppp ipcp ip-address off" すると、IP-Adddress + * Option なしで ConfReq が届く。RFC 1332 より + * + * If negotiation about the remote IP-address is required, and + * the peer did not provide the option in its Configure-Request, + * the option SHOULD be appended to a Configure-Nak. + * + * 6バイト lpkt をはみだしても大丈夫か? + * - ppp.c では mru + 64 分確保している。lpkt は mru 以下 + * なので、+6 は大丈夫。 + */ + if (!ip_address_acked) { + /* IPアドレスの割り当ては必須。*/ + if (!ppp_ip_assigned(ppp)) { + if (npppd_assign_ip_addr(ppp->pppd, ppp, + INADDR_ANY) != 0) { + /* ログは npppd_assign_ip_addr で出力済み */ + } + } + PUTCHAR(IPCP_IP_ADDRESS, nakbuf); + PUTCHAR(6, nakbuf); + PUTLONG(ntohl(ppp->ppp_framed_ip_address.s_addr), + nakbuf); + lnak += 6; + } + IPCP_DBG((f, LOG_DEBUG, "SendConfNak")); + memmove(pktp0, nakbuf0, lnak); + *lpktp = lnak; + break; + case CONFACK: + IPCP_DBG((f, LOG_DEBUG, "SendConfAck")); + break; + } + + return rcode; +#undef remlen +} + +static void +ipcp_open(fsm *f) +{ + if (!ppp_ip_assigned(f->ppp)) { + fsm_log(f, LOG_INFO, "the ip-address option from the peer was " + "not agreed."); + /* + * IP-Address Option 無しで合意。固定IPアドレス割当てを + * 試みる + */ + if (f->ppp->realm_framed_ip_address.s_addr + != INADDR_USER_SELECT && + f->ppp->realm_framed_ip_address.s_addr + != INADDR_NAS_SELECT && + f->ppp->realm_framed_ip_address.s_addr != 0) { + npppd_assign_ip_addr(f->ppp->pppd, f->ppp, INADDR_ANY); + } + } + if (!ppp_ip_assigned(f->ppp)) { + fsm_log(f, LOG_NOTICE, + "IPCP opened but no IP address for the peer."); + ppp_stop(f->ppp, NULL); + return; + } + + fsm_log(f, LOG_INFO, "logtype=Opened ip=%s assignType=%s", + inet_ntoa(f->ppp->ppp_framed_ip_address), + (f->ppp->assign_dynapool)? "dynamic" : "static"); + + ppp_ipcp_opened(f->ppp); +} + +static void +ipcp_close(fsm *f) +{ + IPCP_DBG((f, LOG_DEBUG, "%s", __func__)); +} + +static void +ipcp_start(fsm *f) +{ +} + +static void +ipcp_stop(fsm *f) +{ + fsm_log(f, LOG_INFO, "IPCP is stopped"); + ppp_stop(f->ppp, NULL); +} diff --git a/usr.sbin/npppd/npppd/lcp.c b/usr.sbin/npppd/npppd/lcp.c new file mode 100644 index 00000000000..f3f0d63841f --- /dev/null +++ b/usr.sbin/npppd/npppd/lcp.c @@ -0,0 +1,1318 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: lcp.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * LCP に関する処理を提供します。 + *<pre> + * RFC1661: The Point-to-Point Protocol (PPP) + * RFC1570: PPP LCP Extensions + *</pre> + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <strings.h> +#include <event.h> +#include <ctype.h> + +#include "slist.h" +#include "npppd.h" +#include "ppp.h" +#include "psm-opt.h" + +#define SPACE " \t\r\n" + +#include "debugutil.h" + +#ifdef LCP_DEBUG +#define LCP_DBG(x) fsm_log x +#define LCP_ASSERT(x) ASSERT(x) +#else +#define LCP_DBG(x) +#define LCP_ASSERT(x) +#endif + +#define PROTREJ 0x08 +#define ECHOREQ 0x09 +#define ECHOREP 0x0a +#define IDENTIFICATION 0x0c + +static void lcp_resetci __P((fsm *)); +static void lcp_addci __P((fsm *, u_char *, int *)); +static int lcp_reqci __P((fsm *, u_char *, int *, int)); +static int lcp_ackci __P((fsm *, u_char *, int)); +static int lcp_nakci __P((fsm *, u_char *, int)); +static int lcp_rejci __P((fsm *, u_char *, int)); +static int lcp_cilen __P((fsm *)); +static void lcp_open __P((fsm *)); +static void lcp_down __P((fsm *)); +static void lcp_finished __P((fsm *)); +static int lcp_ext __P((fsm *, int, int, u_char *, int)); +static void lcp_timeout(void *); +static void lcp_reset_timeout(void *); +static int lcp_proxy_recv_ci(fsm *, u_char *, int); +static int lcp_proxy_sent_ci(fsm *, u_char *, int); +static void lcp_load_authconfig(fsm *f); +static void lcp_dialin_proxy_open(void *ctx); + +static struct fsm_callbacks lcp_callbacks = { + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_open, /* Called when fsm reaches OPENED state */ + lcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_ext, /* Called to handle LCP-specific codes */ + "lcp" /* String name of protocol */ +}; +#define NO_AUTH_AGREEABLE(lcp) \ + (!psm_opt_is_enabled(lcp, pap) || psm_opt_is_rejected(lcp, pap)) && \ + (!psm_opt_is_enabled(lcp, chap) || psm_opt_is_rejected(lcp, chap)) && \ + (!psm_opt_is_enabled(lcp, chapms) || psm_opt_is_rejected(lcp, chapms)) &&\ + (!psm_opt_is_enabled(lcp, chapms_v2) || psm_opt_is_rejected(lcp, chapms_v2)) && \ + (!psm_opt_is_enabled(lcp, eap) || psm_opt_is_rejected(lcp, eap)) + + +/** LCPのためのコンテキストを初期化します。 */ +void +lcp_init(lcp *_this, npppd_ppp *ppp) +{ + fsm_init(&_this->fsm); + + _this->fsm.ppp = ppp; + _this->fsm.callbacks = &lcp_callbacks; + _this->fsm.protocol = PPP_PROTO_LCP; + _this->fsm.flags |= OPT_SILENT; + _this->timerctx.ctx = _this; + + _this->recv_ress = 0; + _this->recv_reqs = 0; + _this->magic_number = ((0xffff & random()) << 16) | (0xffff & random()); + + PPP_FSM_CONFIG(&_this->fsm, timeouttime, "lcp.timeout"); + PPP_FSM_CONFIG(&_this->fsm, maxconfreqtransmits,"lcp.max_configure"); + PPP_FSM_CONFIG(&_this->fsm, maxtermtransmits, "lcp.max_terminate"); + PPP_FSM_CONFIG(&_this->fsm, maxnakloops, "lcp.max_nak_loop"); + + /* + * デフォルトは LCP ECHO しない。PPTP, L2TP は、lost carrier を検知で + * きるので。 + */ + _this->echo_interval = 0; + _this->echo_failures = 0; + _this->echo_max_retries = 0; + + _this->auth_order[0] = -1; +} + + +/** + * LCPの下のレイヤ (HDLC) が up したときに呼び出します。 + */ +void +lcp_lowerup(lcp *_this) +{ + fsm_lowerup(&_this->fsm); + fsm_open(&_this->fsm); +} + +/** + * Protocol-Reject を送信します。 + */ +void +lcp_send_protrej(lcp *_this, u_char *pktp, int lpktp) +{ + LCP_ASSERT(_this != NULL); + LCP_ASSERT(pktp != NULL); + + fsm_sdata(&_this->fsm, PROTREJ, _this->fsm.id++, pktp, lpktp); +} + +static const char * +lcp_auth_string(int auth) +{ + switch (auth) { + case PPP_AUTH_PAP: return "PAP"; + case PPP_AUTH_CHAP_MD5: return "MD5-CHAP"; + case PPP_AUTH_CHAP_MS: return "MS-CHAP"; + case PPP_AUTH_CHAP_MS_V2: return "MS-CHAP-V2"; + case PPP_AUTH_EAP: return "EAP"; + case 0: return "none"; + default: return "ERROR"; + } +} + +static void +lcp_open(fsm *f) +{ + lcp *_this; + int peer_auth = 0; + + LCP_ASSERT(f != NULL); + _this = &f->ppp->lcp; + + if (psm_opt_is_accepted(_this, pap)) + peer_auth = PPP_AUTH_PAP; + else if (psm_opt_is_accepted(_this, chap)) + peer_auth = PPP_AUTH_CHAP_MD5; + else if (psm_opt_is_accepted(_this, chapms)) + peer_auth = PPP_AUTH_CHAP_MS; + else if (psm_opt_is_accepted(_this, chapms_v2)) + peer_auth = PPP_AUTH_CHAP_MS_V2; + else if (psm_opt_is_accepted(_this, eap)) + peer_auth = PPP_AUTH_EAP; + else { + if (_this->auth_order[0] > 0) { + fsm_log(f, LOG_INFO, + "failed to negotiate a auth protocol."); + fsm_close(f, "Authentication is required"); + ppp_stop(f->ppp, "Authentication is required"); + return; + } + } + f->ppp->peer_auth = peer_auth; + + if (_this->xxxmru > 0 && f->ppp->peer_mru <= 0) + f->ppp->peer_mru = _this->xxxmru; + if (f->ppp->peer_mru <= 0) + f->ppp->peer_mru = f->ppp->mru; + + // ppp->peer_mru のサイズチェック + LCP_ASSERT(f->ppp->peer_mru > 500); + + fsm_log(f, LOG_INFO, "logtype=Opened mru=%d/%d auth=%s magic=%08x/%08x" + , f->ppp->mru, f->ppp->peer_mru + , lcp_auth_string(peer_auth) + , f->ppp->lcp.magic_number, f->ppp->lcp.peer_magic_number + ); + lcp_reset_timeout(_this); + + ppp_lcp_up(f->ppp); +} + +static void +lcp_down(fsm *f) +{ + lcp *_this; + _this = &f->ppp->lcp; + UNTIMEOUT(lcp_timeout, _this); +} + +static void +lcp_finished(fsm *f) +{ + ppp_lcp_finished(f->ppp); +} + +/** + * ConfReq リセット + */ +static void +lcp_resetci(fsm *f) +{ + LCP_ASSERT(f != NULL); + if (f->ppp->lcp.dialin_proxy == 0) { + memset(&f->ppp->lcp.opt, 0, sizeof(f->ppp->lcp.opt)); + f->ppp->lcp.auth_order[0] = -1; + } +} + +/** + * ConfReq の長さ + */ +static int +lcp_cilen(fsm *f) +{ + LCP_ASSERT(f != NULL); + return f->ppp->mru; +} + +/** + * auth_order 順に、まだ reject されていない認証プロトコルを選択して、LCP + * ConfReq パケット領域に Authentication-Protocol オプションを追加する。 + */ +static int +lcp_add_auth(fsm *f, u_char **ucpp) +{ + int i; + u_char *ucp; + lcp *_this; + + ucp = *ucpp; + _this = &f->ppp->lcp; + + for (i = 0; _this->auth_order[i] > 0 && + i < countof(_this->auth_order); i++) { + switch (_this->auth_order[i]) { + case PPP_AUTH_PAP: + if (psm_opt_is_rejected(_this, pap)) + break; + PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp); + PUTCHAR(4, ucp); + PUTSHORT(PPP_AUTH_PAP, ucp); + psm_opt_set_requested(_this, pap, 1); + _this->lastauth = PPP_AUTH_PAP; + goto end_loop; + case PPP_AUTH_CHAP_MD5: + if (psm_opt_is_rejected(_this, chap)) + break; + PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp); + PUTCHAR(5, ucp); + PUTSHORT(PPP_AUTH_CHAP, ucp); + PUTCHAR(PPP_AUTH_CHAP_MD5, ucp); + psm_opt_set_requested(_this, chap, 1); + _this->lastauth = PPP_AUTH_CHAP_MD5; + goto end_loop; + case PPP_AUTH_CHAP_MS: + if (psm_opt_is_rejected(_this, chapms)) + break; + PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp); + PUTCHAR(5, ucp); + PUTSHORT(PPP_AUTH_CHAP, ucp); + PUTCHAR(PPP_AUTH_CHAP_MS, ucp); + psm_opt_set_requested(_this, chapms, 1); + _this->lastauth = PPP_AUTH_CHAP_MS; + goto end_loop; + case PPP_AUTH_CHAP_MS_V2: + if (psm_opt_is_rejected(_this, chapms_v2)) + break; + PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp); + PUTCHAR(5, ucp); + PUTSHORT(PPP_AUTH_CHAP, ucp); + PUTCHAR(PPP_AUTH_CHAP_MS_V2, ucp); + psm_opt_set_requested(_this, chapms_v2,1); + _this->lastauth = PPP_AUTH_CHAP_MS_V2; + goto end_loop; + case PPP_AUTH_EAP: + if (psm_opt_is_rejected(_this, eap)) + break; + PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp); + PUTCHAR(4, ucp); + PUTSHORT(PPP_AUTH_EAP, ucp); + psm_opt_set_requested(_this, eap, 1); + _this->lastauth = PPP_AUTH_EAP; + goto end_loop; + } + } + _this->lastauth = -1; + return -1; +end_loop: + *ucpp = ucp; + + return 0; +} + +/** + * ConfReq 作り + */ +static void +lcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + lcp *_this; + u_char *start_ucp = ucp; + + LCP_ASSERT(f != NULL); + + _this = &f->ppp->lcp; + if (!psm_opt_is_rejected(_this, mru)) { + PUTCHAR(PPP_LCP_MRU, ucp); + PUTCHAR(4, ucp); + + if (_this->xxxmru > 0) { /* Nak でもらった値 */ + PUTSHORT(_this->xxxmru, ucp); + } else { + PUTSHORT(f->ppp->mru, ucp); + } + psm_opt_set_requested(_this, mru, 1); + } + if (f->ppp->has_acf == 1) { + if (!psm_opt_is_rejected(_this, pfc)) { + PUTCHAR(PPP_LCP_PFC, ucp); + PUTCHAR(2, ucp); + psm_opt_set_requested(_this, pfc, 1); + } + if (!psm_opt_is_rejected(_this, acfc)) { + PUTCHAR(PPP_LCP_ACFC, ucp); + PUTCHAR(2, ucp); + psm_opt_set_requested(_this, acfc, 1); + } + } + PUTCHAR(PPP_LCP_MAGICNUMBER, ucp); + PUTCHAR(6, ucp); + PUTLONG(_this->magic_number, ucp); + + if (f->ppp->peer_auth != 0) { + _this->auth_order[0] = f->ppp->peer_auth; + _this->auth_order[1] = -1; + } else if (_this->auth_order[0] < 0) { + lcp_load_authconfig(f); + } + + lcp_add_auth(f, &ucp); + *lenp = ucp - start_ucp; +} + +static int +lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) +{ + uint32_t magic; + int type, len, rcode, mru, lrej; + u_char *inp0, *rejbuf, *nakbuf, *nakbuf0; + lcp *_this; + + _this = &f->ppp->lcp; + rejbuf = NULL; + rcode = -1; + inp0 = inp; + lrej = 0; + + if ((rejbuf = malloc(*lenp)) == NULL) + return -1; + if ((nakbuf0 = malloc(*lenp)) == NULL) { + free(rejbuf); + return -1; + } + nakbuf = nakbuf0; + +#define remlen() (*lenp - (inp - inp0)) +#define LCP_OPT_PEER_ACCEPTED(opt) \ + psm_peer_opt_set_accepted(&f->ppp->lcp, opt, 1); + + f->ppp->lcp.recv_reqs++; + + while (remlen() >= 2) { + GETCHAR(type, inp); + GETCHAR(len, inp); + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case PPP_LCP_MRU: + if (len != 4) + goto reigai; + GETSHORT(mru, inp); + f->ppp->peer_mru = mru; + if (mru < NPPPD_MIN_MRU) { + if (reject_if_disagree) { + inp -= 2; + goto reject; + } + if (lrej > 0) { + /* reject があれば、Rej するので Nak しない */ + } else { + inp -= 2; + memcpy(nakbuf, inp, len); + nakbuf += len; + inp += 2; + PUTSHORT(f->ppp->mru, nakbuf); + + rcode = CONFNAK; + } + } else + LCP_OPT_PEER_ACCEPTED(mru); + break; + case PPP_LCP_MAGICNUMBER: + if (len != 6) + goto reigai; + GETLONG(magic, inp); + if (magic == _this->magic_number) { + inp -= 4; + goto reject; + } + _this->peer_magic_number = magic; + break; + case PPP_LCP_PFC: + if (len != 2) + goto reigai; + LCP_OPT_PEER_ACCEPTED(pfc); + break; + case PPP_LCP_ACFC: + if (len != 2) + goto reigai; + LCP_OPT_PEER_ACCEPTED(acfc); + break; + case PPP_LCP_AUTH_PROTOCOL: + // 認証されることは現在のところない。 + case PPP_LCP_QUALITY_PROTOCOL: // 使用せず + default: +reject: + inp -= 2; + memcpy(rejbuf + lrej, inp, len); + lrej += len; + inp += len; + rcode = CONFREJ; + } + continue; + } + if (rcode == -1) + rcode = CONFACK; +reigai: + switch (rcode) { + case CONFREJ: + memcpy(inp0, rejbuf, lrej); + *lenp = lrej; + break; + case CONFNAK: + memcpy(inp0, nakbuf0, nakbuf - nakbuf0); + *lenp = nakbuf - nakbuf0; + break; + } + if (rcode != CONFACK) { + psm_peer_opt_set_accepted(&f->ppp->lcp, mru, 0); + psm_peer_opt_set_accepted(&f->ppp->lcp, pfc, 0); + psm_peer_opt_set_accepted(&f->ppp->lcp, acfc, 0); + } + if (rejbuf != NULL) + free(rejbuf); + if (nakbuf0 != NULL) + free(nakbuf0); + + return rcode; +#undef remlen +#undef LCP_OPT_PEER_ACCEPTED +} + +/** ConfAck を受け取った */ +static int +lcp_ackci(fsm *f, u_char *inp, int inlen) +{ + int chapalg, authproto, type, len, mru, magic; + u_char *inp0; + +#define remlen() (inlen - (inp - inp0)) +#define LCP_OPT_ACCEPTED(opt) \ + if (!psm_opt_is_requested(&f->ppp->lcp, opt)) \ + goto reigai; \ + psm_opt_set_accepted(&f->ppp->lcp, opt, 1); + + f->ppp->lcp.recv_ress++; + inp0 = inp; + while (remlen() >= 2) { + GETCHAR(type, inp); + GETCHAR(len, inp); + + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case PPP_LCP_MAGICNUMBER: + if (len != 6) + goto reigai; + GETLONG(magic, inp); + if (f->ppp->lcp.magic_number != magic) + goto reigai; + break; + case PPP_LCP_MRU: + if (len != 4) + goto reigai; + LCP_OPT_ACCEPTED(mru); + GETSHORT(mru, inp); + break; + case PPP_LCP_AUTH_PROTOCOL: + if (len < 4) + goto reigai; + GETSHORT(authproto, inp); + switch (authproto) { + case PPP_AUTH_PAP: + if (len != 4) + goto reigai; + LCP_OPT_ACCEPTED(pap); + break; + case PPP_AUTH_CHAP: + if (len != 5) + goto reigai; + GETCHAR(chapalg, inp); + switch (chapalg) { + case PPP_AUTH_CHAP_MD5: + LCP_OPT_ACCEPTED(chap); + break; + case PPP_AUTH_CHAP_MS: + LCP_OPT_ACCEPTED(chapms); + break; + case PPP_AUTH_CHAP_MS_V2: + LCP_OPT_ACCEPTED(chapms_v2); + break; + } + break; + case PPP_AUTH_EAP: + if (len != 4) + goto reigai; + LCP_OPT_ACCEPTED(eap); + break; + } + break; + + /* + * As RFC1661, ConfRej must be used for boolean options, but + * at least RouterTester uses ConfNak for them. + */ + case PPP_LCP_PFC: + if (len != 2) + goto reigai; + LCP_OPT_ACCEPTED(pfc); + break; + case PPP_LCP_ACFC: + if (len != 2) + goto reigai; + LCP_OPT_ACCEPTED(acfc); + break; + + default: + goto reigai; + } + } + return 1; +reigai: + fsm_log(f, LOG_ERR, "Received unexpected ConfAck."); + if (debug_get_debugfp() != NULL) + show_hd(debug_get_debugfp(), inp, remlen()); + return 0; +#undef LCP_OPT_ACCEPTED +} + +/** ConfNak を受け取った */ +static int +lcp_nakci(fsm *f, u_char *inp, int inlen) +{ + int chapalg, authproto, type, len, mru; + u_char *inp0; + lcp *_this; + const char *peer_auth = "unknown"; + +#define remlen() (inlen - (inp - inp0)) +#define LCP_OPT_REJECTED(opt) \ + if (!psm_opt_is_requested(&f->ppp->lcp, opt)) \ + goto reigai; \ + psm_opt_set_rejected(&f->ppp->lcp, opt, 1); + + f->ppp->lcp.recv_ress++; + inp0 = inp; + _this = &f->ppp->lcp; + while (remlen() >= 2) { + GETCHAR(type, inp); + GETCHAR(len, inp); + + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case PPP_LCP_MRU: + if (len < 4) + goto reigai; + GETSHORT(mru, inp); + fsm_log(f, LOG_NOTICE, + "ignored ConfNak from the peer: mru=%d", mru); + _this->xxxmru = mru; + break; + case PPP_LCP_AUTH_PROTOCOL: + if (len < 4) + goto reigai; + switch (_this->lastauth) { + case PPP_AUTH_PAP: + psm_opt_set_rejected(_this, pap, 1); + break; + case PPP_AUTH_CHAP_MD5: + psm_opt_set_rejected(_this, chap, 1); + break; + case PPP_AUTH_CHAP_MS: + psm_opt_set_rejected(_this, chapms, 1); + break; + case PPP_AUTH_CHAP_MS_V2: + psm_opt_set_rejected(_this, chapms_v2, 1); + break; + case PPP_AUTH_EAP: + psm_opt_set_rejected(_this, eap, 1); + break; + } + GETSHORT(authproto, inp); + switch (authproto) { + case PPP_AUTH_PAP: + peer_auth = "pap"; + psm_opt_set_accepted(_this, pap, 1); + break; + case PPP_AUTH_CHAP: + chapalg = 0; + if (len == 5) + GETCHAR(chapalg, inp); + switch (chapalg) { + case PPP_AUTH_CHAP_MD5: + psm_opt_set_accepted(_this, chap, 1); + peer_auth = "chap"; + break; + case PPP_AUTH_CHAP_MS: + psm_opt_set_accepted(_this, chapms, 1); + peer_auth = "mschap"; + break; + case PPP_AUTH_CHAP_MS_V2: + psm_opt_set_accepted(_this, chapms_v2, + 1); + peer_auth = "mschap_v2"; + break; + default: + fsm_log(f, LOG_INFO, + "Nacked chap algorithm is " + "unknown(%d).", chapalg); + peer_auth = "unknown"; + break; + } + break; + case PPP_AUTH_EAP: + if (len != 4) + goto reigai; + peer_auth = "eap"; + psm_opt_set_accepted(_this, eap, 1); + break; + } + if (NO_AUTH_AGREEABLE(_this)) { + fsm_log(f, LOG_INFO, "No authentication " + "protocols are agreeable. peer's " + "auth proto=%s", + peer_auth); + ppp_stop(f->ppp, "Authentication is required"); + return 1; + } + break; + case PPP_LCP_PFC: + if (len != 2) + goto reigai; + LCP_OPT_REJECTED(pfc); + break; + case PPP_LCP_ACFC: + if (len != 2) + goto reigai; + LCP_OPT_REJECTED(acfc); + break; + default: + goto reigai; + } + } + return 1; +reigai: + log_printf(LOG_ERR, "Received unexpected ConfNak."); + if (debug_get_debugfp() != NULL) + show_hd(debug_get_debugfp(), inp, inlen); + return 0; +#undef remlen +#undef LCP_OPT_REJECTED +} + +/** + * ConfRej を受け取った + */ +static int +lcp_rejci(fsm *f, u_char *inp, int inlen) +{ + int chapalg, authproto, type, len, mru; + u_char *inp0; + lcp *_this; + +#define remlen() (inlen - (inp - inp0)) +#define LCP_OPT_REJECTED(opt) \ + if (!psm_opt_is_requested(&f->ppp->lcp, opt)) \ + goto reigai; \ + psm_opt_set_rejected(&f->ppp->lcp, opt, 1); + + f->ppp->lcp.recv_ress++; + inp0 = inp; + _this = &f->ppp->lcp; + while (remlen() >= 2) { + GETCHAR(type, inp); + GETCHAR(len, inp); + + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case PPP_LCP_MAGICNUMBER: + if (f->ppp->lcp.echo_interval > 0) + goto reigai; + inp += 4; + break; + case PPP_LCP_MRU: + LCP_OPT_REJECTED(mru); + GETSHORT(mru, inp); + break; + case PPP_LCP_AUTH_PROTOCOL: + if (len < 4) + goto reigai; + GETSHORT(authproto, inp); + switch (authproto) { + case PPP_AUTH_PAP: + if (len != 4) + goto reigai; + LCP_OPT_REJECTED(pap); + break; + case PPP_AUTH_CHAP: + chapalg = 0; + if (len == 5) + GETCHAR(chapalg, inp); + switch (chapalg) { + case PPP_AUTH_CHAP_MD5: + LCP_OPT_REJECTED(chap); + break; + case PPP_AUTH_CHAP_MS: + LCP_OPT_REJECTED(chapms); + break; + case PPP_AUTH_CHAP_MS_V2: + LCP_OPT_REJECTED(chapms_v2); + break; + default: + fsm_log(f, LOG_INFO, + "Rejected chap algorithm is " + "unknown(%d).", chapalg); + break; + } + break; + case PPP_AUTH_EAP: + if (len != 4) + goto reigai; + LCP_OPT_REJECTED(eap); + break; + } + if (NO_AUTH_AGREEABLE(_this)) { + fsm_log(f, LOG_INFO, "No authentication " + "protocols are agreeable."); + ppp_stop(f->ppp, "Authentication is required"); + return 1; + } + break; + case PPP_LCP_PFC: + if (len != 2) + goto reigai; + LCP_OPT_REJECTED(pfc); + break; + case PPP_LCP_ACFC: + if (len != 2) + goto reigai; + LCP_OPT_REJECTED(acfc); + break; + default: + goto reigai; + } + } + return 1; +reigai: + log_printf(LOG_ERR, "Received unexpected ConfRej."); + if (debug_get_debugfp() != NULL) + show_hd(debug_get_debugfp(), inp, inlen); + return 0; +#undef remlen +} + +static void +lcp_rcoderej(fsm *f, u_char *inp, int inlen) +{ + uint16_t proto; + fsm *rejfsm; + + if (inlen < 2) { + fsm_log(f, LOG_WARNING, "Recevied short ProtRej packet."); + return; + } + GETSHORT(proto, inp); + + rejfsm = NULL; + + switch (proto) { + case PPP_PROTO_LCP: + rejfsm = &f->ppp->lcp.fsm; + break; + case PPP_PROTO_PAP: + fsm_log(f, LOG_WARNING, "our PAP packet is rejected"); + return; + case PPP_PROTO_CHAP: + fsm_log(f, LOG_WARNING, "our CHAP packet is rejected"); + return; + case PPP_PROTO_EAP: + fsm_log(f, LOG_ERR, "our EAP packet is rejected"); + ppp_stop(f->ppp, "Authentication Required"); + break; + case PPP_PROTO_NCP | NCP_IPCP: + rejfsm = &f->ppp->ipcp.fsm; + break; + case PPP_PROTO_NCP | NCP_CCP: + rejfsm = &f->ppp->ccp.fsm; + break; + } + if (rejfsm == NULL) { + fsm_log(f, LOG_WARNING, + "Recevied ProtRej packet for unknown protocol=(%d/%04x)", + proto, proto); + return; + } + fsm_protreject(rejfsm); + + return; +} + +static void +lcp_reset_timeout(void *ctx) +{ + lcp *_this; + + _this = ctx; + + if (_this->echo_interval > 0) { + if (_this->echo_failures == 0) { + TIMEOUT(lcp_timeout, _this, _this->echo_interval); + } else { + TIMEOUT(lcp_timeout, _this, _this->echo_retry_interval); + } + } else { + UNTIMEOUT(lcp_timeout, _this); + } +} + +static void +lcp_timeout(void *ctx) +{ + lcp *_this; + u_char *cp, buf[32]; + + _this = ctx; + if (_this->echo_failures >= _this->echo_max_retries) { + fsm_log(&_this->fsm, LOG_NOTICE, "keepalive failure."); + if (_this->fsm.ppp != NULL) + ppp_stop(_this->fsm.ppp, NULL); + return; + } + cp = buf; + PUTLONG(_this->magic_number, cp); + fsm_sdata(&_this->fsm, ECHOREQ, _this->fsm.id++, buf, 4); + _this->echo_failures++; + + lcp_reset_timeout(_this); +} + +static int +lcp_rechoreq(fsm *f, int id, u_char *inp, int inlen) +{ + u_char *inp0; + lcp *_this; + int len; + + if (inlen < 4) + return 0; + + _this = &f->ppp->lcp; + inp0 = inp; + PUTLONG(_this->magic_number, inp) + + len = MIN(inlen, f->ppp->peer_mru - 8); + fsm_sdata(f, ECHOREP, id, inp0, len); + + return 1; +} + +static int +lcp_ext(fsm *f, int code, int id, u_char *inp, int inlen) +{ + lcp *_this; + uint32_t magic; + char buf[256]; + int i, len; + + _this = &f->ppp->lcp; + + switch (code) { + case IDENTIFICATION: + /* RFC 1570 */ + if (inlen > 4) { + GETLONG(magic, inp); + inlen -= 4; + memset(buf, 0, sizeof(buf)); + len = MIN(inlen, sizeof(buf) - 1); + memcpy(buf, inp, len); + buf[len] = '\0'; + for (i = 0; i < len; i++) { + if (!isprint((unsigned char)buf[i])) + buf[i] = '.'; + } + fsm_log(f, LOG_INFO, + "RecvId magic=%08x text=%s", magic, buf); + } + return 1; + case PROTREJ: + lcp_rcoderej(f, inp, inlen); + return 1; + case ECHOREP: + if (f->state == OPENED) { + if (inlen >= 4) { + GETLONG(magic, inp); + if (_this->peer_magic_number == magic) { + _this->echo_failures = 0; + lcp_reset_timeout(_this); + } + } + } + return 1; + case ECHOREQ: + if (f->state == OPENED) + return lcp_rechoreq(f, id, inp, inlen); + return 1; + } + + return 0; +} + + +/** 認証の設定を読み込んで、記述順に ppp_order に格納していく。*/ +static void +lcp_load_authconfig(fsm *f) +{ + int i, f_none; + const char *val; + lcp *_this; + + _this = &f->ppp->lcp; + i = 0; + f_none = 0; + if ((val = ppp_config_str(f->ppp, "auth.method")) != NULL) { + char *authp0, *authp, authbuf[512]; + + strlcpy(authbuf, val, sizeof(authbuf)); + authp0 = authbuf; + while ((authp = strsep(&authp0, SPACE)) != NULL && + i < countof(_this->auth_order) - 1) { + if (strcasecmp("none", authp) == 0) { + f_none = 1; + } else if (strcasecmp("PAP", authp) == 0) { + _this->auth_order[i++] = PPP_AUTH_PAP; + psm_opt_set_enabled(_this, pap, 1); + } else if (strcasecmp("CHAP", authp) == 0 || + strcasecmp("MD5CHAP", authp) == 0) { + _this->auth_order[i++] = + PPP_AUTH_CHAP_MD5; + psm_opt_set_enabled(_this, chap, 1); + } else if (strcasecmp("CHAPMS", authp) == 0 || + strcasecmp("MSCHAP", authp) == 0) { +#if 0 /* MS-CHAP は、サポートせず。 */ + _this->auth_order[i++] = + PPP_AUTH_CHAP_MS; + psm_opt_set_enabled(_this, chapms, 1); +#endif + } else if (strcasecmp("CHAPMSV2", authp) == 0 || + strcasecmp("MSCHAPV2", authp) == 0 || + strcasecmp("CHAPMS_V2", authp) == 0 || + strcasecmp("MSCHAP_V2", authp) == 0) { + _this->auth_order[i++] = PPP_AUTH_CHAP_MS_V2; + psm_opt_set_enabled(_this,chapms_v2, 1); +#ifdef USE_NPPPD_EAP_RADIUS + } else if (strcasecmp("EAP-RADIUS", authp) == 0) { + _this->auth_order[i++] = PPP_AUTH_EAP; + psm_opt_set_enabled(_this, eap, 1); +#endif + } else + ppp_log(f->ppp, LOG_WARNING, + "unknown auth protocol: %s", authp); + } + } + if (f_none && i != 0) { + ppp_log(f->ppp, LOG_WARNING, "auth protocol 'none' " + "must be specified individually"); + f_none = 0; + } + _this->auth_order[i] = -1; +} + +/*********************************************************************** + * Dialin Proxy 関連 + **********************************************************************/ +/** + * Dialin proxy 情報にしたがって LCP の状態をセットします。LCPの状態が、 + * 受け入れられない場合は、0 以外が返ります。 + */ +int +lcp_dialin_proxy(lcp *_this, dialin_proxy_info *dpi, int renegotiation, + int force_renegotiation) +{ + int i, authok; + + _this->dialin_proxy = 1; + lcp_load_authconfig(&_this->fsm); + + /* 認証方式が設定で許可されているかどうか */ + authok = 0; + if (dpi->auth_type != 0) { + for (i = 0; _this->auth_order[i] > 0; i++) { + if (_this->auth_order[i] != dpi->auth_type) + continue; + authok = 1; + break; + } + } + if (!authok) { + if (!renegotiation) { + fsm_log(&_this->fsm, LOG_NOTICE, + "dialin-proxy failed. auth-method=%s is " + "not enabled. Try 'l2tp.dialin.lcp_renegotion'", + lcp_auth_string(dpi->auth_type)); + return 1; + } + _this->dialin_proxy_lcp_renegotiation = 1; + } + if (force_renegotiation) + _this->dialin_proxy_lcp_renegotiation = 1; + + if (_this->dialin_proxy_lcp_renegotiation == 0) { + _this->fsm.ppp->peer_auth = dpi->auth_type; + /* + * 全て拒否された状態にして、lcp_proxy_sent_ci で合意したも + * のを解釈する。 + */ + psm_opt_set_rejected(_this, mru, 1); + psm_opt_set_rejected(_this, pfc, 1); + psm_opt_set_rejected(_this, acfc, 1); + psm_opt_set_rejected(_this, pap, 1); + psm_opt_set_rejected(_this, chap, 1); + psm_opt_set_rejected(_this, chapms, 1); + psm_opt_set_rejected(_this, chapms_v2, 1); + psm_opt_set_rejected(_this, eap, 1); + + } + switch (dpi->auth_type) { + case PPP_AUTH_PAP: + pap_proxy_authen_prepare(&_this->fsm.ppp->pap, dpi); + break; + case PPP_AUTH_CHAP_MD5: + chap_proxy_authen_prepare(&_this->fsm.ppp->chap, dpi); + break; + } + if (lcp_proxy_sent_ci(&_this->fsm, dpi->last_sent_lcp.data, + dpi->last_sent_lcp.ldata) != 0) { + fsm_log(&_this->fsm, LOG_NOTICE, + "dialin-proxy failed. couldn't use proxied lcp."); + return 1; + } + if (lcp_proxy_recv_ci(&_this->fsm, dpi->last_recv_lcp.data, + dpi->last_recv_lcp.ldata) != 0) { + fsm_log(&_this->fsm, LOG_NOTICE, + "dialin-proxy failed. couldn't use proxied lcp."); + return 1; + } + + fsm_log(&_this->fsm, LOG_INFO, + "dialin-proxy user=%s auth-type=%s renegotiate=%s", + dpi->username, + (dpi->auth_type == 0)? "none" : lcp_auth_string(dpi->auth_type), + (_this->dialin_proxy_lcp_renegotiation != 0)? "yes" : "no"); + + + if (_this->dialin_proxy_lcp_renegotiation == 0) { + /* call lcp_open by another event handler */ + TIMEOUT(lcp_dialin_proxy_open, _this, 0); + } else + _this->fsm.flags &= ~OPT_SILENT; + + return 0; +} + +/* + * lcp_reqci からコピー。以下の点だけが異なる。 + * - LCP_OPT_ACCEPTED を変更 + * - Magic Number や MRU は代入 + */ +static int +lcp_proxy_recv_ci(fsm *f, u_char *inp, int inlen) +{ + int type, mru, len; + uint32_t magic; + u_char *inp0; + lcp *_this; + +#define remlen() (inlen - (inp - inp0)) +#define LCP_OPT_PEER_ACCEPTED(opt) \ + psm_peer_opt_set_rejected(&f->ppp->lcp, opt, 0); \ + psm_peer_opt_set_requested(&f->ppp->lcp, opt, 1); \ + psm_peer_opt_set_accepted(&f->ppp->lcp, opt, 1); + + _this = &f->ppp->lcp; + inp0 = inp; + + while (remlen() >= 2) { + GETCHAR(type, inp); + GETCHAR(len, inp); + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case PPP_LCP_MRU: + if (len != 4) + goto reigai; + GETSHORT(mru, inp); + f->ppp->peer_mru = mru; + if (mru < NPPPD_MIN_MRU) + goto reigai; + else + LCP_OPT_PEER_ACCEPTED(mru); + break; + case PPP_LCP_MAGICNUMBER: + if (len != 6) + goto reigai; + GETLONG(magic, inp); + if (magic == _this->magic_number) + goto reigai; + _this->peer_magic_number = magic; + break; + case PPP_LCP_PFC: + if (len != 2) + goto reigai; + LCP_OPT_PEER_ACCEPTED(pfc); + break; + case PPP_LCP_ACFC: + if (len != 2) + goto reigai; + LCP_OPT_PEER_ACCEPTED(acfc); + break; + default: + goto reigai; + } + } + +#undef remlen +#undef LCP_OPT_PEER_ACCEPTED + return 0; +reigai: + return 1; +} + +static void +lcp_dialin_proxy_open(void *ctx) +{ + lcp *_this; + + _this = ctx; + _this->fsm.state = OPENED; + lcp_open(&_this->fsm); +} + +/* + * lcp_ackci からコピー。以下の点だけが異なる。 + * - recv_reass++ はしない + * - LCP_OPT_ACCEPTED を変更 + * - Magic Number や MRU は代入 + */ +static int +lcp_proxy_sent_ci(fsm *f, u_char *inp, int inlen) +{ + int chapalg, authproto, type, len, mru, magic; + u_char *inp0; + +#define remlen() (inlen - (inp - inp0)) +#define LCP_OPT_ACCEPTED(opt) \ + if (f->ppp->lcp.dialin_proxy_lcp_renegotiation == 0) { \ + psm_opt_set_rejected(&f->ppp->lcp, opt, 0); \ + psm_opt_set_requested(&f->ppp->lcp, opt, 1); \ + psm_opt_set_accepted(&f->ppp->lcp, opt, 1); \ + } + + inp0 = inp; + while (remlen() >= 2) { + GETCHAR(type, inp); + GETCHAR(len, inp); + + if (len <= 0 || remlen() + 2 < len) + goto reigai; + + switch (type) { + case PPP_LCP_MAGICNUMBER: + if (len != 6) + goto reigai; + GETLONG(magic, inp); + f->ppp->lcp.magic_number = magic; + break; + case PPP_LCP_MRU: + if (len != 4) + goto reigai; + LCP_OPT_ACCEPTED(mru); + GETSHORT(mru, inp); + f->ppp->lcp.xxxmru = mru; + break; + case PPP_LCP_AUTH_PROTOCOL: + if (len < 4) + goto reigai; + GETSHORT(authproto, inp); + switch (authproto) { + case PPP_AUTH_PAP: + if (len != 4) + goto reigai; + LCP_OPT_ACCEPTED(pap); + break; + case PPP_AUTH_CHAP: + if (len != 5) + goto reigai; + GETCHAR(chapalg, inp); + switch (chapalg) { + case PPP_AUTH_CHAP_MD5: + LCP_OPT_ACCEPTED(chap); + break; + case PPP_AUTH_CHAP_MS: + LCP_OPT_ACCEPTED(chapms); + break; + case PPP_AUTH_CHAP_MS_V2: + LCP_OPT_ACCEPTED(chapms_v2); + break; + } + break; + case PPP_AUTH_EAP: + if (len != 4) + goto reigai; + LCP_OPT_ACCEPTED(eap); + break; + } + break; + case PPP_LCP_PFC: + if (len != 2) + goto reigai; + LCP_OPT_ACCEPTED(pfc); + break; + case PPP_LCP_ACFC: + if (len != 2) + goto reigai; + LCP_OPT_ACCEPTED(acfc); + break; + default: + goto reigai; + } + } + return 0; +reigai: + return 1; +#undef LCP_OPT_ACCEPTED +} diff --git a/usr.sbin/npppd/npppd/mppe.c b/usr.sbin/npppd/npppd/mppe.c new file mode 100644 index 00000000000..5bb212770d1 --- /dev/null +++ b/usr.sbin/npppd/npppd/mppe.c @@ -0,0 +1,624 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: mppe.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * + * MPPE(Microsoft Point-To-Point Encryption Protocol) の実装。 + */ +/* + * PPPパケット入れ替わり問題の回避。 + * L2TP/IPsec で、フレーム順を復元できなければ、回避した方がベター。 + */ +#define WORKAROUND_OUT_OF_SEQUENCE_PPP_FRAMING 1 + +#include <sys/types.h> +#include <sys/endian.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include <string.h> +#include <event.h> +#ifdef WITH_OPENSSL +#include <openssl/sha.h> +#include <openssl/rc4.h> +#endif + +#include "slist.h" +#include "npppd.h" +#include "debugutil.h" + +#ifdef MPPE_DEBUG +#define MPPE_DBG(x) mppe_log x +#define MPPE_ASSERT(x) \ + if (!(x)) { \ + fprintf(stderr, \ + "\nASSERT(%s) failed on %s() at %s:%d.\n" \ + , #x, __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define MPPE_DBG(x) +#define MPPE_ASSERT(x) +#endif + +#define SESS_KEY_LEN(len) (len < 16)? 8 : 16 + +static const char *mppe_bits_to_string __P((uint32_t)); +static void mppe_log __P((mppe *, uint32_t, const char *, ...)) __printflike(3,4); +static int rc4_key __P((mppe *, mppe_rc4_t *, int, u_char *)); +static void rc4_destroy __P((mppe *, mppe_rc4_t *)); +static void rc4 __P((mppe *, mppe_rc4_t *, int, u_char *, u_char *)); +static void GetNewKeyFromSHA __P((u_char *, u_char *, int, u_char *)); + +/** + * mppe コンテキストを初期化します。 + * - 設定を読み込みます。 + */ +void +mppe_init(mppe *_this, npppd_ppp *ppp) +{ + const char *sval; + int ival; + + MPPE_ASSERT(ppp != NULL); + MPPE_ASSERT(_this != NULL); + + memset(_this, 0, sizeof(mppe)); + + _this->ppp = ppp; + + _this->mode_auto = 1; + _this->mode_stateless = 0; + _this->keylen_auto = 1; + _this->keylenbits = 128; + + _this->enabled = (ppp_config_str_equal(_this->ppp, + "mppe.disabled", "true", 0) != 0)? 0 : 1; + + if (_this->enabled == 0) + goto mppe_config_done; + + _this->required = (ppp_config_str_equal(_this->ppp, + "mppe.required", "true", 0) != 0)? 1 : 0; + + if (_this->required == 0) + goto mppe_config_done; + + sval = ppp_config_str(_this->ppp, "mppe.mode"); + if (sval != NULL) { + if (strcmp(sval, "stateless") == 0) { + _this->mode_auto = 0; + _this->mode_stateless = 1; + } else if (strcmp(sval, "stateful") == 0) { + _this->mode_auto = 0; + _this->mode_stateless = 0; + } else if (strcmp(sval, "auto") == 0 || + strcmp(sval, "*") == 0) { + /* デフォルトのまま */ + } else { + mppe_log(_this, LOG_WARNING, + "configuration \"mppe.mode\" has bad value"); + _this->mode_auto = 1; + _this->mode_stateless = 0; + } + } + if (ppp_config_str_equal(_this->ppp, "mppe.keylen", "auto", 0) || + ppp_config_str_equal(_this->ppp, "mppe.keylen", "*", 0)) { + /* デフォルトのまま */ + } else { + ival = ppp_config_int(_this->ppp, "mppe.keylen", -1); + if (ival != -1) { + switch (ival) { + case 40: + case 56: + case 128: + _this->keylenbits = ival; + _this->keylen_auto = 0; + break; + default: + mppe_log(_this, LOG_WARNING, + "configuration \"mppe.keylen\" has bad " + "value"); + } + } + } +mppe_config_done: + /* nothing */; +} + +void +mppe_fini(mppe *_this) +{ + rc4_destroy(_this, &_this->send); + rc4_destroy(_this, &_this->recv); + rc4_destroy(_this, &_this->keychg); +} + +static void +mppe_reduce_key(mppe_rc4_t *_this) +{ + switch (_this->keybits) { + case 40: + _this->session_key[1] = 0x26; + _this->session_key[2] = 0x9e; + case 56: + _this->session_key[0] = 0xd1; + } +} + +static void +mppe_key_change(mppe *_mppe, mppe_rc4_t *_this) +{ + u_char interim[16]; + + GetNewKeyFromSHA(_this->master_key, _this->session_key, + _this->keylen, interim); + + rc4_key(_mppe, &_mppe->keychg, _this->keylen, interim); + rc4(_mppe, &_mppe->keychg, _this->keylen, interim, _this->session_key); + mppe_reduce_key(_this); +} + +/** + * MPPE プロトコルを開始します。 + */ +void +mppe_start(mppe *_this) +{ + char buf[256]; + + strlcpy(buf, mppe_bits_to_string(_this->ppp->ccp.mppe_o_bits), + sizeof(buf)); + + mppe_log(_this, LOG_INFO, "logtype=Opened our=%s peer=%s", buf, + mppe_bits_to_string(_this->ppp->ccp.mppe_p_bits)); + + _this->ppp->mppe_started = 1; + + _this->send.stateless = + ((_this->ppp->ccp.mppe_o_bits & CCP_MPPE_STATELESS) != 0)? 1 : 0; + + if ((_this->ppp->ccp.mppe_o_bits & CCP_MPPE_NT_40bit) != 0) { + _this->send.keylen = 8; + _this->send.keybits = 40; + } else if ((_this->ppp->ccp.mppe_o_bits & CCP_MPPE_NT_56bit) != 0) { + _this->send.keylen = 8; + _this->send.keybits = 56; + } else if ((_this->ppp->ccp.mppe_o_bits & CCP_MPPE_NT_128bit) != 0) { + _this->send.keylen = 16; + _this->send.keybits = 128; + } + + _this->recv.stateless = + ((_this->ppp->ccp.mppe_p_bits & CCP_MPPE_STATELESS) != 0)? 1 : 0; + if ((_this->ppp->ccp.mppe_p_bits & CCP_MPPE_NT_40bit) != 0) { + _this->recv.keylen = 8; + _this->recv.keybits = 40; + } else if ((_this->ppp->ccp.mppe_p_bits & CCP_MPPE_NT_56bit) != 0) { + _this->recv.keylen = 8; + _this->recv.keybits = 56; + } else if ((_this->ppp->ccp.mppe_p_bits & CCP_MPPE_NT_128bit) != 0) { + _this->recv.keylen = 16; + _this->recv.keybits = 128; + } + + GetNewKeyFromSHA(_this->recv.master_key, _this->recv.master_key, + _this->recv.keylen, _this->recv.session_key); + GetNewKeyFromSHA(_this->send.master_key, _this->send.master_key, + _this->send.keylen, _this->send.session_key); + + mppe_reduce_key(&_this->recv); + mppe_reduce_key(&_this->send); + + rc4_key(_this, &_this->recv, _this->recv.keylen, + _this->recv.session_key); + rc4_key(_this, &_this->send, _this->send.keylen, + _this->send.session_key); +} + + +/** + * mppe bits を作成します。最初の提案を行う場合には、peer_bits に 0 を指定し + * ます。peer_bits が指定されていれば、peer の提案にそった値を返します。 + */ +uint32_t +mppe_create_our_bits(mppe *_this, uint32_t peer_bits) +{ + uint32_t our_bits; + // デフォルトの提案 + our_bits = CCP_MPPE_NT_128bit; + + if (_this->keylen_auto == 0) { + switch (_this->keylenbits) { + case 40: + our_bits = CCP_MPPE_NT_40bit; break; + case 56: + our_bits = CCP_MPPE_NT_56bit; break; + case 128: + our_bits = CCP_MPPE_NT_128bit; break; + } + } else { + // 自動 + our_bits = CCP_MPPE_NT_128bit | CCP_MPPE_NT_56bit + | CCP_MPPE_NT_40bit; + if (peer_bits != 0) { + if ((peer_bits & CCP_MPPE_NT_128bit) != 0) + our_bits = CCP_MPPE_NT_128bit; + else if ((peer_bits & CCP_MPPE_NT_56bit) != 0) + our_bits = CCP_MPPE_NT_56bit; + else if ((peer_bits & CCP_MPPE_NT_40bit) != 0) + our_bits = CCP_MPPE_NT_40bit; + } + } + + if (_this->mode_auto != 0) { + /* 自動の場合 */ + if (peer_bits == 0) { + /* + * 最初の提案は stateless で行う。Windows 9x の場合、 + * 送受信で stateful/stateless が逆になるバグがある。 + * Windows 9x は、stateless を優先してネゴシエーション + * しようとするため、こちらも stateless を優先すること + * で、この Windows のバグを回避する。 + * (idgw-develop 4224) + * + * このバグがなかったにせよ、パケットロスが一定割合 + * 以上発生するような場合に、stateful はユーザからみ + * たコストが高いので、インターネットや無線LAN経由し + * た利用では、よい選択ではない。 + */ + our_bits |= CCP_MPPE_STATELESS; + } else { + /* 譲歩する */ + our_bits |= peer_bits & CCP_MPPE_STATELESS; + } + } else { + /* auto 以外が設定されている場合は、譲歩しない */ + if (_this->mode_stateless != 0) + our_bits |= CCP_MPPE_STATELESS; + } + if (peer_bits != 0 && our_bits != peer_bits) { + char obuf[128], pbuf[128]; + + /* 失敗した場合にログに残らないので。*/ + strlcpy(obuf, mppe_bits_to_string(our_bits), sizeof(obuf)); + strlcpy(pbuf, mppe_bits_to_string(peer_bits), sizeof(pbuf)); + mppe_log(_this, LOG_INFO, + "mismatch our=%s peer=%s", obuf, pbuf); + } + + return our_bits; +} + +#define RESET_REQ 0x0e +#define RESET_ACK 0x0f +#define COHRENCY_CNT_MASK 0x0fff; + +/** + * MPPE 経由でのパケット受信 + */ +void +mppe_input(mppe *_this, u_char *pktp, int len) +{ + int pktloss, encrypt, flushed, m, n; + uint16_t coher_cnt; + u_char *pktp0, *opktp, *opktp0; + + encrypt = 0; + flushed = 0; + + MPPE_ASSERT(len >= 2); + + pktp0 = pktp; + GETSHORT(coher_cnt, pktp); + + flushed = (coher_cnt & 0x8000)? 1 : 0; + encrypt = (coher_cnt & 0x1000)? 1 : 0; + coher_cnt &= COHRENCY_CNT_MASK; + pktloss = 0; + + MPPE_DBG((_this, DEBUG_LEVEL_2, "in coher_cnt=%03x/%03x %s%s", + _this->recv.coher_cnt, coher_cnt, (flushed)? "[flushed]" : "", + (encrypt)? "[encrypt]" : "")); + + if (encrypt == 0) { + mppe_log(_this, LOG_WARNING, + "Received unexpected MPPE packet. (no ecrypt)"); + return; + } +#ifdef WORKAROUND_OUT_OF_SEQUENCE_PPP_FRAMING + /* + * L2TP/IPsec の実装で、PPPフレーム順を復元できず、順番が入れ替わった + * 場合に、大量のパケットロスと区別が付かず、そう判断すると MPPE の + * 鍵がズレる。この問題を回避するため、4096-256 パケット以上落ちてい + * るように見える場合には、パケットが落ちたわけではなく順番が入れ替 + * わったモノとみなす。 + */ + { + int coher_cnt0; + + coher_cnt0 = coher_cnt; + if (coher_cnt < _this->recv.coher_cnt) + coher_cnt0 += 0x1000; + if (coher_cnt0 - _this->recv.coher_cnt > 0x0f00) { + mppe_log(_this, LOG_INFO, + "Workaround the out-of-sequence PPP framing problem: " + "%d => %d", _this->recv.coher_cnt, coher_cnt); + return; + } + } +#endif + if (_this->recv.stateless != 0) { + mppe_key_change(_this, &_this->recv); + while (_this->recv.coher_cnt != coher_cnt) { + mppe_key_change(_this, &_this->recv); + _this->recv.coher_cnt++; + _this->recv.coher_cnt &= COHRENCY_CNT_MASK; + pktloss++; + } + flushed = 1; + } else { + if (flushed) { + if (coher_cnt < _this->recv.coher_cnt) { + // 繰り上がった場合 + coher_cnt += 0x1000; + } + pktloss += coher_cnt - _this->recv.coher_cnt; + m = _this->recv.coher_cnt / 256; + n = coher_cnt / 256; + while (m++ < n) + mppe_key_change(_this, &_this->recv); + + coher_cnt &= COHRENCY_CNT_MASK; + _this->recv.coher_cnt = coher_cnt; + } else if (_this->recv.coher_cnt != coher_cnt) { + _this->recv.resetreq = 1; + + opktp0 = ppp_packetbuf(_this->ppp, + PPP_PROTO_NCP | NCP_CCP); + opktp = opktp0; + + PUTLONG(_this->ppp->ccp.mppe_p_bits, opktp); + + ppp_output(_this->ppp, PPP_PROTO_NCP | NCP_CCP, + RESET_REQ, _this->recv.resetreq, opktp0, + opktp - opktp0); + return; + } + if ((coher_cnt & 0xff) == 0xff) { + mppe_key_change(_this, &_this->recv); + flushed = 1; + } + } +#ifndef WORKAROUND_OUT_OF_SEQUENCE_PPP_FRAMING + if (pktloss > 1000) { + /* + * パケット落ち過ぎ、あるいはパケット順が入れ替わった。 + * 後者の場合、この後鍵がズレているため通信ができなくなる。 + */ + mppe_log(_this, LOG_WARNING, "%d packets loss", pktloss); + } +#endif + if (flushed) { + rc4_key(_this, &_this->recv, _this->recv.keylen, + _this->recv.session_key); + } + + rc4(_this, &_this->recv, len - 2, pktp, pktp); + + _this->recv.coher_cnt++; + _this->recv.coher_cnt &= COHRENCY_CNT_MASK; + + _this->ppp->recv_packet(_this->ppp, pktp, len - 2, + PPP_IO_FLAGS_MPPE_ENCRYPTED); +} + +/** + * CCP Reset (MPPE の場合鍵リセット) を受信した時に呼び出される関数。 + */ +void +mppe_recv_ccp_reset(mppe *_this) +{ + MPPE_DBG((_this, DEBUG_LEVEL_2, "%s() is called.", __func__)); + _this->send.resetreq = 1; +} + +/** + * MPPE 経由でのパケット送信 + */ +void +mppe_pkt_output(mppe *_this, uint16_t proto, u_char *pktp, int len) +{ + int encrypt, flushed; + uint16_t coher_cnt; + u_char *outp, *outp0; + + MPPE_ASSERT(proto == PPP_PROTO_IP); + + flushed = 0; + encrypt = 1; + + outp = ppp_packetbuf(_this->ppp, PPP_PROTO_MPPE); + outp0 = outp; + + if (_this->send.stateless != 0) { + flushed = 1; + mppe_key_change(_this, &_this->send); + } else { + if ((_this->send.coher_cnt % 0x100) == 0xff) { + flushed = 1; + mppe_key_change(_this, &_this->send); + } else if (_this->send.resetreq != 0) { + flushed = 1; + _this->send.resetreq = 0; + } + } + + if (flushed) { + rc4_key(_this, &_this->send, _this->send.keylen, + _this->send.session_key); + } + + MPPE_DBG((_this, DEBUG_LEVEL_2, "out coher_cnt=%03x %s%s", + _this->send.coher_cnt, (flushed)? "[flushed]" : "", + (encrypt)? "[encrypt]" : "")); + + coher_cnt = _this->send.coher_cnt & COHRENCY_CNT_MASK; + if (flushed) + coher_cnt |= 0x8000; + if (encrypt) + coher_cnt |= 0x1000; + + PUTSHORT(coher_cnt, outp); + proto = htons(proto); + rc4(_this, &_this->send, 2, (u_char *)&proto, outp); + rc4(_this, &_this->send, len, pktp, outp + 2); + + ppp_output(_this->ppp, PPP_PROTO_MPPE, 0, 0, outp0, len + 4); + _this->send.coher_cnt++; + _this->send.coher_cnt &= COHRENCY_CNT_MASK; +} + +static void +mppe_log(mppe *_this, uint32_t prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=mppe %s", + _this->ppp->id, fmt); + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +static const char * +mppe_bits_to_string(uint32_t bits) +{ + static char buf[128]; + + snprintf(buf, sizeof(buf), "%s%s%s%s%s%s" + , ((CCP_MPPC_ALONE & bits) != 0)? ",mppc" : "" + , ((CCP_MPPE_LM_40bit& bits) != 0)? ",40bit(LM)" : "" + , ((CCP_MPPE_NT_40bit& bits) != 0)? ",40bit" : "" + , ((CCP_MPPE_NT_128bit& bits) != 0)? ",128bit" : "" + , ((CCP_MPPE_NT_56bit& bits) != 0)? ",56bit" : "" + , ((CCP_MPPE_STATELESS& bits) != 0)? ",stateless" : ",stateful"); + + if (buf[0] == '\0') + return ""; + + return buf + 1; +} + +/************************************************************************ + * 以下、認証/暗号化アルゴリズムの実装 + ************************************************************************/ +static u_char SHAPad1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, SHAPad2[] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, +}; +#define ZeroMemory(dst, len) memset(dst, 0, len) +#define MoveMemory(dst, src, len) memcpy(dst, src, len) + +#if defined(WITH_BSAFE) || defined(WITH_NBSAFE) +#include "mppe_bsafe.c" +#else +#include <openssl/rc4.h> +#include <openssl/sha.h> + +#define SHA_CTX SHA_CTX +#define SHAInit SHA1_Init +#define SHAUpdate SHA1_Update +#define SHAFinal(ctx,digest) SHA1_Final(digest, ctx) + +/************************************************************************ + * OpenSSL 版実装 + ************************************************************************/ + +static int +rc4_key(mppe *_mppe, mppe_rc4_t *_this, int lkey, u_char *key) +{ + if (_this->rc4ctx == NULL) { + if ((_this->rc4ctx = malloc(sizeof(RC4_KEY))) == NULL) { + mppe_log(_mppe, LOG_ERR, "malloc() failed at %s: %m", + __func__); + return 1; + } + } + + RC4_set_key((RC4_KEY *)_this->rc4ctx, lkey, key); + + return 0; +} + +static void +rc4(mppe *_mppe, mppe_rc4_t *_this, int len, u_char *indata, u_char *outdata) +{ + RC4((RC4_KEY *)_this->rc4ctx, len, indata, outdata); +} + +static void +rc4_destroy(mppe *_mppe, mppe_rc4_t *_this) +{ + if (_this->rc4ctx != NULL) + free(_this->rc4ctx); + _this->rc4ctx = NULL; +} +#endif + +static void +GetNewKeyFromSHA(u_char *StartKey, u_char *SessionKey, int SessionKeyLength, + u_char *InterimKey) +{ + u_char Digest[20]; + SHA_CTX Context; + + ZeroMemory(Digest, 20); + + SHAInit(&Context); + SHAUpdate(&Context, StartKey, SessionKeyLength); + SHAUpdate(&Context, SHAPad1, 40); + SHAUpdate(&Context, SessionKey, SessionKeyLength); + SHAUpdate(&Context, SHAPad2, 40); + SHAFinal(&Context, Digest); + + MoveMemory(InterimKey, Digest, SessionKeyLength); +} diff --git a/usr.sbin/npppd/npppd/nint.h b/usr.sbin/npppd/npppd/nint.h new file mode 100644 index 00000000000..bc329c98d43 --- /dev/null +++ b/usr.sbin/npppd/npppd/nint.h @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NINT_H +#define NINT_H + +#pragma pack(1) + +class nint16 +{ +private: + int16_t value; + +public: + nint16() + { + } + + nint16(int16_t x) + { + value = htons(x); + }; + + nint16(const nint16& x) + { + value = x.value; + }; + + operator int16_t() const + { + return ntohs(value); + } + + nint16& operator +=(int16_t x) + { + value = htons(ntohs(value) + x); + return *this; + } + + void setraw(int16_t x) + { + value = x; + } + + int16_t getraw() const + { + return value; + } +}; + +class nuint16 +{ +private: + u_int16_t value; + +public: + nuint16() + { + } + + nuint16(u_int16_t x) + { + value = htons(x); + }; + + nuint16(const nuint16& x) + { + value = x.value; + }; + + nuint16& operator +=(u_int16_t x) + { + value = htons(ntohs(value) + x); + return *this; + } + + operator u_int16_t() const + { + return ntohs(value); + } + + void setraw(u_int16_t x) + { + value = x; + } + + u_int16_t getraw() const + { + return value; + } +}; + +class nint32 +{ +private: + int32_t value; + +public: + nint32() + { + } + + nint32(int32_t x) + { + value = htonl(x); + }; + + nint32(const nint32& x) + { + value = x.value; + }; + + operator int32_t() const + { + return ntohl(value); + } + + void setraw(int32_t x) + { + value = x; + } + + int32_t getraw() const + { + return value; + } +}; + +class nuint32 +{ +private: + u_int32_t value; + +public: + nuint32() + { + } + + nuint32(u_int32_t x) + { + value = htonl(x); + }; + + nuint32(const nuint32& x) + { + value = x.value; + }; + + operator u_int32_t() const + { + return ntohl(value); + } + + void setraw(u_int32_t x) + { + value = x; + } + + u_int32_t getraw() const + { + return value; + } +}; + +#pragma pack() + +#endif // NINT_H diff --git a/usr.sbin/npppd/npppd/npppd.c b/usr.sbin/npppd/npppd/npppd.c new file mode 100644 index 00000000000..294736046d1 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd.c @@ -0,0 +1,2285 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * Next pppd。npppd プロセスと npppdインスタンスの実装。 + * + * @author Yasuoka Masahiko + * $Id: npppd.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#include <sys/cdefs.h> +#include "version.h" +#ifndef LINT +__COPYRIGHT( +"@(#) npppd - PPP daemon for PPP Access Concentrators\n" +"@(#) Version " VERSION "\n" +"@(#) \n" +"@(#) Copyright 2005-2008\n" +"@(#) Internet Initiative Japan Inc. All rights reserved.\n" +"@(#) \n" +"@(#) \n" +"@(#) \n" +); +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <net/route.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <unistd.h> +#include <time.h> +#include <syslog.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <netdb.h> +#include <libgen.h> +#include <fcntl.h> +#include <event.h> +#include <errno.h> +#include <ifaddrs.h> + +#include "pathnames.h" +#include "debugutil.h" +#include "addr_range.h" +#include "npppd_subr.h" +#include "npppd_local.h" +#include "npppd_auth.h" +#include "radish.h" +#include "rtev.h" +#ifdef NPPPD_USE_RT_ZEBRA +#include "rt_zebra.h" +#endif +#include "net_utils.h" +#include "time_utils.h" + +#ifdef USE_NPPPD_LINKID +#include "linkid.h" +#endif + +#ifdef USE_NPPPD_ARP +#include "npppd_arp.h" +#endif + +#ifdef USE_NPPPD_PIPEX +#ifdef USE_NPPPD_PPPOE +#include "pppoe_local.h" +#endif /* USE_NPPPD_PPPOE */ +#include "psm-opt.h" +#include <sys/ioctl.h> +#if defined(__NetBSD__) +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif +#include <net/pipex.h> +#endif /* USE_NPPPD_PIPEX */ + +/* static変数はこちら。 */ +static npppd s_npppd; /* singleton */ + +static void npppd_reload0(npppd *); +static int npppd_rd_walktree_delete(struct radish_head *); +static void usage (void); +static void npppd_start (npppd *); +static void npppd_stop_really (npppd *); +static uint32_t str_hash(const void *, int); +static void npppd_on_sighup (int, short, void *); +static void npppd_on_sigterm (int, short, void *); +static void npppd_on_sigint (int, short, void *); +static void npppd_reset_timer(npppd *); +static void npppd_timer(int, short, void *); +static void npppd_auth_finalizer_periodic(npppd *); +static int rd2slist_walk (struct radish *, void *); +static int rd2slist (struct radish_head *, slist *); +static inline void seed_random(long *); + +#ifndef NO_ROUTE_FOR_POOLED_ADDRESS +static struct in_addr loop; /* initialize at npppd_init() */ +#endif +static uint32_t str_hash(const void *, int); + +#ifdef USE_NPPPD_PIPEX +static int npppd_ppp_pipex_ip_disable(npppd *, npppd_ppp *); +static void pipex_periodic(npppd *); +#endif /* USE_NPPPD_PIPEX */ + +#ifdef NPPPD_DEBUG +#define NPPPD_DBG(x) log_printf x +#define NPPPD_ASSERT(x) ASSERT(x) +#else +#define NPPPD_DBG(x) +#define NPPPD_ASSERT(x) +#endif + +/*********************************************************************** + * エントリポイント + ***********************************************************************/ +int main (int, char *[]); + +int +main(int argc, char *argv[]) +{ + int ch, retval = 0, ll_adjust = 0, runasdaemon = 0; + extern char *optarg; + const char *npppd_conf0 = DEFAULT_NPPPD_CONF; + + while ((ch = getopt(argc, argv, "Dc:dhs")) != -1) { + switch (ch) { + case 's': + ll_adjust++; + break; + case 'c': + npppd_conf0 = optarg; + break; + case 'D': + runasdaemon = 1; + break; + case 'd': + debuglevel++; + break; + case '?': + case 'h': + usage(); + exit(1); + } + } +#if defined(__NetBSD__) && (__NetBSD_Version__ < 300000000) && \ + defined(USE_NPPPD_PPPOE) + /* + * FIXME: BPF + kqueue on NetBSD 2.0.2 は問題あり。パケット受信しても + * すぐに event 飛んでこない。 + */ + setenv("EVENT_NOKQUEUE", "nokqueue", 0); +#endif + + if (debuglevel > 0) { + debug_set_debugfp(stderr); + debug_use_syslog(0); + } else { + debug_set_syslog_level_adjust(ll_adjust); +#ifdef _SEIL_EXT_ + openlog(NULL, 0, LOG_NPPPD); +#else + openlog(NULL, LOG_PID, LOG_NPPPD); +#endif + if (runasdaemon) + daemon(0, 0); + } + + if (npppd_init(&s_npppd, npppd_conf0) != 0) { + retval = 1; + goto reigai; + } + npppd_start(&s_npppd); + npppd_fini(&s_npppd); + // FALL THROUGH +reigai: + log_printf(LOG_NOTICE, "Terminate npppd."); + + return retval; +} + +static void +usage() +{ + fprintf(stderr, + "usage: npppd [-sDdh] [-c config_file]\n" + "\t-d: increase debuglevel. Output log to standard error.\n" + "\t-c: specify configuration file. default=\"%s\".\n" + "\t-s: adjust syslog level to be silent.\n" + "\t-D: run as a daemon.\n" + "\t-h: show usage.\n" + , DEFAULT_NPPPD_CONF + ); +} + +/** 唯一の npppd インスタンスを返します。 */ +npppd * +npppd_get_npppd() +{ + return &s_npppd; +} + +/*********************************************************************** + * npppd 自身の操作 (init/fini/stop/start) + ***********************************************************************/ +/** 初期化します */ +int +npppd_init(npppd *_this, const char *config_file) +{ + int i, status = -1; + char pidpath[MAXPATHLEN]; + const char *pidpath0; + const char *coredir; + FILE *pidfp = NULL; + char cwd[MAXPATHLEN]; + long seed; + + memset(_this, 0, sizeof(npppd)); + loop.s_addr = htonl(INADDR_LOOPBACK); + + NPPPD_ASSERT(config_file != NULL); + + pidpath0 = NULL; + _this->pid = getpid(); + slist_init(&_this->realms); + + log_printf(LOG_NOTICE, "Starting npppd pid=%u version=%s", + _this->pid, VERSION); +#if defined(BUILD_DATE) && defined(BUILD_TIME) + log_printf(LOG_INFO, "Build %s %s ", BUILD_DATE, BUILD_TIME); +#endif + if (get_nanotime() == INT64_MIN) { + log_printf(LOG_ERR, "get_nanotime() failed: %m"); + return 1; + } + + if (realpath(config_file, _this->config_file) == NULL) { + log_printf(LOG_ERR, "realpath(%s,) failed in %s(): %m", + config_file, __func__); + return 1; + } + /* + * NetBSD 2.0 の realpath(3) より + * + * This implementation of realpath() differs slightly from the Solaris + * implementation. The 4.4BSD version always returns absolute + * pathnames, whereas the Solaris implementation will, under certain + * circumstances, return a relative resolvedname when given a relative + * pathname. + * + * FIXME: 4.4BSD互換ではない場合には、確認が必要。 + */ + NPPPD_ASSERT(_this->config_file[0] == '/'); + + /* initialize random seeds */ + seed_random(&seed); + srandom(seed); + + /* 設定読み込み */ + if ((status = npppd_reload_config(_this)) != 0) + return status; + + if ((_this->map_user_ppp = hash_create( + (int (*) (const void *, const void *))strcmp, str_hash, + NPPPD_USER_HASH_SIZ)) == NULL) { + log_printf(LOG_ERR, "hash_create() failed in %s(): %m", + __func__); + return -1; + } + + if (npppd_ifaces_load_config(_this) != 0) { + return -1; + } + + /* PIDファイルの場所 */ + if ((pidpath0 = npppd_config_str(_this, "pidfile")) == NULL) + pidpath0 = DEFAULT_NPPPD_PIDFILE; + + /* 実行時のディレクトリ */ + if ((coredir = npppd_config_str(_this, "coredir")) == NULL) { + /* PIDファイルのディレクトリ */ + strlcpy(pidpath, pidpath0, sizeof(pidpath)); + strlcpy(cwd, dirname(pidpath), sizeof(cwd)); + } + else { + /* core を書くディレクトリ */ + strlcpy(cwd, coredir, sizeof(cwd)); + } + if (chdir(cwd) != 0) { + log_printf(LOG_ERR, "chdir(%s,) failed in %s(): %m", __func__, + cwd); + return -1; + } + + /* イベント関連 */ + event_init(); + + /* Routingソケット関連 */ + rtev_libevent_init( + npppd_config_int(_this, "rtsock.event_delay", + DEFAULT_RTSOCK_EVENT_DELAY), + npppd_config_int(_this, "rtsock.send_wait_millisec", + DEFAULT_RTSOCK_SEND_WAIT_MILLISEC), + npppd_config_int(_this, "rtsock.send_npkts", + DEFAULT_RTSOCK_SEND_NPKTS), 0); +#ifdef NPPPD_USE_RT_ZEBRA + if (rt_zebra_get_instance() == NULL) + return -1; + rt_zebra_init(rt_zebra_get_instance()); + rt_zebra_start(rt_zebra_get_instance()); +#endif + _this->rtev_event_serial = -1; + + /* 無視するシグナル */ + signal(SIGPIPE, SIG_IGN); + signal(SIGURG, SIG_IGN); + /* + * シグナルハンドラ + * EV_PERSIST でセットされる(event(3)参照)ので、1度でよい。 + */ + signal_set(&_this->ev_sigterm, SIGTERM, npppd_on_sigterm, _this); + signal_set(&_this->ev_sigint, SIGINT, npppd_on_sigint, _this); + signal_set(&_this->ev_sighup, SIGHUP, npppd_on_sighup, _this); + signal_add(&_this->ev_sigterm, NULL); + signal_add(&_this->ev_sigint, NULL); + signal_add(&_this->ev_sighup, NULL); + //_npppd + signal_add(&_this->ev_sighup, NULL); + + evtimer_set(&_this->ev_timer, npppd_timer, _this); + + /* + * トンネルデバイス関連 + */ + status = 0; + for (i = 0; i < countof(_this->iface); i++) { + if (_this->iface[i].initialized != 0) + status |= npppd_iface_start(&_this->iface[i]); + } + if (status != 0) + return -1; + + /* + * インタフェースの start (open) できたなら、唯一の nppp プロセス + * と見なし、PIDファイルは上書きする。 + */ + /* pid ファイル */ + if ((pidfp = fopen(pidpath0, "w+")) == NULL) { + log_printf(LOG_ERR, "fopen(%s,w+) failed in %s(): %m", + pidpath0, __func__); + return -1; + } + strlcpy(_this->pidpath, pidpath0, sizeof(_this->pidpath)); + fprintf(pidfp, "%u\n", _this->pid); + fclose(pidfp); + pidfp = NULL; +#ifdef USE_NPPPD_LINKID + linkid_sock_init(); +#endif +#ifdef USE_NPPPD_ARP + arp_set_strictintfnetwork(npppd_config_str_equali(_this, "arpd.strictintfnetwork", "true", ARPD_STRICTINTFNETWORK_DEFAULT)); + if (npppd_config_str_equali(_this, "arpd.enabled", "true", ARPD_DEFAULT) == 1) + arp_sock_init(); +#endif + return npppd_modules_reload(_this); +} + +/** npppd を開始します。 */ +void +npppd_start(npppd *_this) +{ + int rval = 0; + + npppd_reset_timer(_this); + while ((event_loop(EVLOOP_ONCE)) == 0) { + // 終了要求の検査 + if (_this->finalized != 0) + break; + } + if (rval != 0) + log_printf(LOG_CRIT, "event_loop() failed: %m"); +} + +/** npppd を停止します。 */ +void +npppd_stop(npppd *_this) +{ + int i; +#ifdef USE_NPPPD_L2TP + l2tpd_stop(&_this->l2tpd); +#endif +#ifdef USE_NPPPD_PPTP + pptpd_stop(&_this->pptpd); +#endif +#ifdef IDGW_SSLDIP + pptpd_stop(&_this->ssldipd); +#endif +#ifdef USE_NPPPD_PPPOE + pppoed_stop(&_this->pppoed); +#endif +#ifdef USE_NPPPD_NPPPD_CTL + npppd_ctl_stop(&_this->ctl); +#endif +#ifdef USE_NPPPD_LINKID + linkid_sock_fini(); +#endif +#ifdef USE_NPPPD_ARP + arp_sock_fini(); +#endif + for (i = countof(_this->iface) - 1; i >= 0; i--) { + if (_this->iface[i].initialized != 0) + npppd_iface_stop(&_this->iface[i]); + } + npppd_set_radish(_this, NULL); + + _this->finalizing = 1; + npppd_reset_timer(_this); +} + + +static void +npppd_stop_really(npppd *_this) +{ + int i; +#if defined(USE_NPPPD_L2TP) || defined(USE_NPPPD_PPTP) + int wait_again; + + wait_again = 0; + +#ifdef USE_NPPPD_L2TP + if (!l2tpd_is_stopped(&_this->l2tpd)) + wait_again |= 1; +#endif +#ifdef USE_NPPPD_PPTP + if (!pptpd_is_stopped(&_this->pptpd)) + wait_again |= 1; +#ifdef IDGW_SSLDIP + if (!pptpd_is_stopped(&_this->ssldipd)) + wait_again |= 1; +#endif +#endif + if (wait_again != 0) { + npppd_reset_timer(_this); + return; + } +#endif +#ifdef NPPPD_USE_RT_ZEBRA + rt_zebra_stop(rt_zebra_get_instance()); +#endif +#ifdef _SEIL_EXT_ + unlink(_this->pidpath); +#endif + for (i = countof(_this->iface) - 1; i >= 0; i--) { + if (_this->iface[i].initialized != 0) + npppd_iface_fini(&_this->iface[i]); + } + _this->finalized = 1; +} + +/** npppd デーモンの終了化を行います。 */ +void +npppd_fini(npppd *_this) +{ + int i; + npppd_auth_base *auth_base; + +#ifdef USE_NPPPD_L2TP + l2tpd_uninit(&_this->l2tpd); +#endif +#ifdef USE_NPPPD_PPTP + pptpd_uninit(&_this->pptpd); +#ifdef IDGW_SSLDIP + pptpd_uninit(&_this->ssldipd); +#endif +#endif +#ifdef USE_NPPPD_PPPOE + pppoed_uninit(&_this->pppoed); +#endif + for (slist_itr_first(&_this->realms); + slist_itr_has_next(&_this->realms);) { + auth_base = slist_itr_next(&_this->realms); + npppd_auth_destroy(auth_base); + } + for (i = countof(_this->iface) - 1; i >= 0; i--) { + if (_this->iface[i].initialized != 0) + npppd_iface_fini(&_this->iface[i]); + } + for (i = 0; i < countof(_this->iface_bind); i++) + slist_fini(&_this->iface_bind[i].pools); + + for (i = countof(_this->pool) - 1; i >= 0; i--) { + if (_this->pool[i].initialized != 0) + npppd_pool_uninit(&_this->pool[i]); + } + + signal_del(&_this->ev_sigterm); + signal_del(&_this->ev_sigint); + signal_del(&_this->ev_sighup); + + if (_this->properties != NULL) + properties_destroy(_this->properties); + + slist_fini(&_this->realms); + + if (_this->map_user_ppp != NULL) + hash_free(_this->map_user_ppp); + +#ifdef NPPPD_USE_RT_ZEBRA + rt_zebra_fini(rt_zebra_get_instance()); +#endif + rtev_fini(); +} + +/*********************************************************************** + * タイマ関連 + ***********************************************************************/ +static void +npppd_reset_timer(npppd *_this) +{ + struct timeval tv; + + if (_this->finalizing != 0) { + /* 終了化処理の専用 */ + tv.tv_usec = 500000; + tv.tv_sec = 0; + evtimer_add(&_this->ev_timer, &tv); + } else { + tv.tv_usec = 0; + tv.tv_sec = NPPPD_TIMER_TICK_IVAL; + evtimer_add(&_this->ev_timer, &tv); + } +} + +static void +npppd_timer(int fd, short evtype, void *ctx) +{ + npppd *_this; + + _this = ctx; + if (_this->finalizing != 0) { + npppd_stop_really(_this); + /* タイマはリセットされている。*/ + return; /* 終了化処理中は専用 */ + } + _this->secs += NPPPD_TIMER_TICK_IVAL; + if (_this->reloading_count > 0) { + _this->reloading_count -= NPPPD_TIMER_TICK_IVAL; + if (_this->reloading_count <= 0) { + npppd_reload0(_this); + _this->reloading_count = 0; + } + } else { + if ((_this->secs % TIMER_TICK_RUP( + NPPPD_AUTH_REALM_FINALIZER_INTERVAL)) == 0) + npppd_auth_finalizer_periodic(_this); + } + + if (_this->rtev_event_serial != rtev_get_event_serial()) { +#ifdef USE_NPPPD_PPPOE + if (pppoed_need_polling(&_this->pppoed)) + pppoed_reload_listeners(&_this->pppoed); +#endif + } + _this->rtev_event_serial = rtev_get_event_serial(); + +#ifdef USE_NPPPD_PIPEX + pipex_periodic(_this); +#endif +#ifdef NPPPD_USE_RT_ZEBRA + if (!rt_zebra_is_running(rt_zebra_get_instance())) { + rt_zebra_start(rt_zebra_get_instance()); +#ifdef _SEIL_EXT_ + /* zebra@seil は、プールアドレスを消してしまう */ + npppd_reset_routing_table(_this, 1); +#endif + } +#endif + + npppd_reset_timer(_this); +} + +int +npppd_reset_routing_table(npppd *_this, int pool_only) +{ +#ifndef NO_ROUTE_FOR_POOLED_ADDRESS + slist rtlist0; + + slist_init(&rtlist0); + if (rd2slist(_this->rd, &rtlist0) != 0) + return 1; + + for (slist_itr_first(&rtlist0); slist_itr_has_next(&rtlist0); ) { + struct radish *rd; + struct sockaddr_npppd *snp; + npppd_ppp *ppp; + int is_first; + + rd = slist_itr_next(&rtlist0); + snp = rd->rd_rtent; + + is_first = 1; + for (snp = rd->rd_rtent; snp != NULL; snp = snp->snp_next) { + switch (snp->snp_type) { + case SNP_POOL: + case SNP_DYN_POOL: + if (is_first) + in_route_add(&snp->snp_addr, + &snp->snp_mask, &loop, + LOOPBACK_IFNAME, RTF_BLACKHOLE, 0); + break; + + case SNP_PPP: + if (pool_only) + break; + ppp = snp->snp_data_ptr; + if (ppp->ppp_framed_ip_netmask.s_addr + == 0xffffffffL) { + in_host_route_add(&ppp-> + ppp_framed_ip_address, + &ppp_iface(ppp)->ip4addr, + ppp_iface(ppp)->ifname, + MRU_IPMTU(ppp->peer_mru)); + } else { + in_route_add(&ppp-> + ppp_framed_ip_address, + &ppp->ppp_framed_ip_netmask, + &ppp_iface(ppp)->ip4addr, + ppp_iface(ppp)->ifname, 0, + MRU_IPMTU(ppp->peer_mru)); + } + break; + } + is_first = 0; + } + + } + + slist_fini(&rtlist0); +#endif + return 0; +} + +/*********************************************************************** + * npppd その他 API (export) + ***********************************************************************/ +/** + * ユーザのパスワードを取得します。成功すると 0 が返ります。 + * + * @param username パスワードを取得するユーザの名前 + * @param password パスワードを格納する領域。 + * パスワードの長さだけを知りたい場合には NULL + * を指定します。 + * @param lppassword パスワードを格納する領域の長さのポインタ + * @return User unknown の場合は 1、パスワードバッファの長さが足りない場合 + * は 2、その他のエラーは負の数が返ります。 + */ +int +npppd_get_user_password(npppd *_this, npppd_ppp *ppp, + const char *username, char *password, int *plpassword) +{ + char buf0[MAX_USERNAME_LENGTH]; + + NPPPD_ASSERT(ppp->realm != NULL); + return npppd_auth_get_user_password(ppp->realm, + npppd_auth_username_for_auth(ppp->realm, username, buf0), password, + plpassword); +} + +/** ユーザの Framed-IP-Address を取得します。*/ +struct in_addr * +npppd_get_user_framed_ip_address(npppd *_this, npppd_ppp *ppp, + const char *username) +{ + + if (ppp->peer_auth == 0) { + ppp->realm_framed_ip_address.s_addr = 0; + goto do_default; + } + NPPPD_ASSERT(ppp->realm != NULL); + + if (ppp->realm_framed_ip_address.s_addr != 0) { +#if 1 +/* + * FIXME: radius での固定 IPアドレス割り当てが禁止されている場合に、ここで上 + * FIXME: 書きするアドホック修正。radius と acctlist 以外を足すとバグるので、 + * FIXME: それまでに要修正。 + */ + if ((ppp_ipcp(ppp)->ip_assign_flags & NPPPD_IP_ASSIGN_RADIUS) + == 0) { + ppp->realm_framed_ip_netmask.s_addr = 0; + } else +#endif + return &ppp->realm_framed_ip_address; + } + + ppp->realm_framed_ip_netmask.s_addr = 0xffffffffL; + if ((ppp_ipcp(ppp)->ip_assign_flags & NPPPD_IP_ASSIGN_FIXED) != 0) { + /* 認証レルムから指定 */ + if (npppd_auth_get_framed_ip(ppp->realm, username, + &ppp->realm_framed_ip_address, + &ppp->realm_framed_ip_netmask) != 0) + ppp->realm_framed_ip_address.s_addr = 0; + } + +do_default: + /* 認証レルムで指定が無ければ USER_SELECT */ + if (ppp->realm_framed_ip_address.s_addr == 0) + ppp->realm_framed_ip_address.s_addr = INADDR_USER_SELECT; + if (ppp->realm_framed_ip_address.s_addr == INADDR_USER_SELECT) { + /* USER_SELECT は、設定で不許可の場合は NAS_SELECT に */ + if ((ppp_ipcp(ppp)->ip_assign_flags & + NPPPD_IP_ASSIGN_USER_SELECT) == 0) + ppp->realm_framed_ip_address.s_addr = INADDR_NAS_SELECT; + } + NPPPD_DBG((LOG_DEBUG, "%s() = %s", __func__, + inet_ntoa(ppp->realm_framed_ip_address))); + + return &ppp->realm_framed_ip_address; +} + +/** XXX */ +int +npppd_check_calling_number(npppd *_this, npppd_ppp *ppp) +{ + int lnumber, rval, strict, loose; + char number[NPPPD_PHONE_NUMBER_LEN + 1]; + + strict = ppp_config_str_equal(ppp, "check_callnum", "strict", 0); + loose = ppp_config_str_equal(ppp, "check_callnum", "loose", 0); + + if (strict || loose) { + lnumber = sizeof(number); + if ((rval = npppd_auth_get_calling_number(ppp->realm, + ppp->username, + number, &lnumber)) == 0) + return + (strcmp(number, ppp->calling_number) == 0)? 1 : 0; + if (strict) + return 0; + } + + return 1; +} + +/* 使いまわし用 */ +static struct sockaddr_in npppd_get_ppp_by_ip_sin4 = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in) +}; + +/** + * 指定した IPアドレスが割り当てられた {@link npppd_ppp} インスタンスを + * 検索して返します。 + * @param ipaddr IPアドレス。ネットワークバイトオーダーで指定します。 + */ +npppd_ppp * +npppd_get_ppp_by_ip(npppd *_this, struct in_addr ipaddr) +{ + struct sockaddr_npppd *snp; + struct radish *rdp; + + npppd_get_ppp_by_ip_sin4.sin_addr = ipaddr; + if (_this->rd == NULL) + return NULL; /* スタートアップ時 */ + if (rd_match((struct sockaddr *)&npppd_get_ppp_by_ip_sin4, _this->rd, + &rdp)) { + snp = rdp->rd_rtent; + if (snp->snp_type == SNP_PPP) + return snp->snp_data_ptr; + } + return NULL; +} + +/** + * 指定した PPPユーザの {@link npppd_ppp} インスタンスを検索して返します。 + * @param username PPPユーザ名。 + * @param rlist 検索結果を格納する {@link slist}。 + * @retnr 成功したら 0。失敗したら -1。失敗した場合は、rlistのアイテム + * には、中途半端なデータが入っている可能性があります。 + */ +slist * +npppd_get_ppp_by_user(npppd *_this, const char *username) +{ + hash_link *hl; + + if ((hl = hash_lookup(_this->map_user_ppp, username)) != NULL) + return hl->item; + + return NULL; +} + +/** + * 指定した PPP の ID から {@link npppd_ppp} インスタンスを検索して返します。 + * @param id 検索する {@link npppd_ppp#id ppp の id} + * @return id が一致する {@link npppd_ppp} インスタンスが見つかればその + * ポインタを、見つからなければ NULL ポインタを返却します。 + */ +npppd_ppp * +npppd_get_ppp_by_id(npppd *_this, int ppp_id) +{ + slist users; + npppd_ppp *ppp0, *ppp; + + NPPPD_ASSERT(_this != NULL); + + ppp = NULL; + slist_init(&users); + if (npppd_get_all_users(_this, &users) != 0) { + log_printf(LOG_WARNING, + "npppd_get_all_users() failed in %s()", __func__); + } else { + /* FIXME: Id での検索がリニアサーチ */ + for (slist_itr_first(&users); slist_itr_has_next(&users); ) { + ppp0 = slist_itr_next(&users); + if (ppp0->id == ppp_id) { + ppp = ppp0; + break; + } + } + } + slist_fini(&users); + + return ppp; +} + +/** + * 指定したユーザが最大接続数の制限 (user_max_session) を越えているかどうかを + * 検査します。 + * @return 検査に問題無ければ 1 が、あれば 0 を返します。 + */ +int +npppd_check_user_max_session(npppd *_this, npppd_ppp *ppp) +{ + int count; + npppd_ppp *ppp1; + slist *uppp; + + /* user_max_sessionが0のときは無制限とみなす */ + if (ppp_iface(ppp)->user_max_session == 0) { + return 1; + } + + count = 0; + if ((uppp = npppd_get_ppp_by_user(_this, ppp->username)) != NULL) { + for (slist_itr_first(uppp); slist_itr_has_next(uppp); ) { + ppp1 = slist_itr_next(uppp); + if (strcmp(ppp_iface(ppp)->ifname, + ppp_iface(ppp1)->ifname) == 0) + count++; + } + } + + return (count < ppp_iface(ppp)->user_max_session)? 1 : 0; +} + +/*********************************************************************** + * ネットワーク I/O 関連 + ***********************************************************************/ +/** + * ネットワーク(tun)側に出力する際に呼び出します。 + * 現在は IPv4 のパケットを出力することを仮定しています。 + */ +void +npppd_network_output(npppd *_this, npppd_ppp *ppp, int proto, u_char *pktp, + int lpktp) +{ + struct ip *pip; + int lbuf; + u_char buf[256]; /* TCP/IP ヘッダとして十分なサイズ */ + + NPPPD_ASSERT(ppp != NULL); + + if (!ppp_ip_assigned(ppp)) + return; + + if (lpktp < sizeof(struct ip)) { + ppp_log(ppp, LOG_DEBUG, "Received IP packet is too small"); + return; + } + lbuf = MIN(lpktp, sizeof(buf)); + if (!ALIGNED_POINTER(pktp, struct ip)) { + memcpy(buf, pktp, lbuf); + pip = (struct ip *)buf; + } else { + pip = (struct ip *)pktp; + } + +#ifndef NO_INGRES_FILTER + if ((pip->ip_src.s_addr & ppp->ppp_framed_ip_netmask.s_addr) != + (ppp->ppp_framed_ip_address.s_addr & + ppp->ppp_framed_ip_netmask.s_addr)) { + char logbuf[80]; + strlcpy(logbuf, inet_ntoa(pip->ip_dst), sizeof(logbuf)); + ppp_log(ppp, LOG_INFO, + "Drop packet by ingress filter. %s => %s", + inet_ntoa(pip->ip_src), logbuf); + + return; + } +#endif + if (ppp->timeout_sec > 0 && !ip_is_idle_packet(pip, lbuf)) + ppp_reset_idle_timeout(ppp); + +#ifndef NO_ADJUST_MSS + if (ppp->adjust_mss) { + if (lpktp == lbuf) { + /* + * TCP ヘッダまでが sizeof(buf) オクテットに収まって + * いるという仮定。 + */ + if (!ALIGNED_POINTER(pktp, struct ip)) + pktp = buf; + adjust_tcp_mss(pktp, lpktp, MRU_IPMTU(ppp->peer_mru)); + } + } +#endif + npppd_iface_write(ppp_iface(ppp), proto, pktp, lpktp); +} + +/*********************************************************************** + * IPv4 パケットのカーネル折り返し処理 + ***********************************************************************/ +#ifdef USE_NPPPD_PIPEX + +static void +pipex_setup_common(npppd_ppp *ppp, struct pipex_session_req *req) +{ + memset(req, 0, sizeof(*req)); + if (psm_opt_is_accepted(&ppp->lcp, acfc)) + req->pr_ppp_flags |= PIPEX_PPP_ACFC_ENABLED; + if (psm_peer_opt_is_accepted(&ppp->lcp, acfc)) + req->pr_ppp_flags |= PIPEX_PPP_ACFC_ACCEPTED; + + if (psm_peer_opt_is_accepted(&ppp->lcp, pfc)) + req->pr_ppp_flags |= PIPEX_PPP_PFC_ACCEPTED; + if (psm_opt_is_accepted(&ppp->lcp, pfc)) + req->pr_ppp_flags |= PIPEX_PPP_PFC_ENABLED; + + if (ppp->has_acf != 0) + req->pr_ppp_flags |= PIPEX_PPP_HAS_ACF; + + if (ppp->adjust_mss != 0) + req->pr_ppp_flags |= PIPEX_PPP_ADJUST_TCPMSS; + + req->pr_ip_address = ppp->ppp_framed_ip_address; + req->pr_ip_netmask = ppp->ppp_framed_ip_netmask; + req->pr_peer_mru = ppp->peer_mru; + req->pr_ppp_id = ppp->id; + + req->pr_timeout_sec = ppp->timeout_sec; + +#ifdef USE_NPPPD_MPPE + req->pr_ccp_id = ppp->ccp.fsm.id; + memcpy(req->pr_mppe_send.master_key, + ppp->mppe.send.master_key, sizeof(req->pr_mppe_send.master_key)); + req->pr_mppe_send.stateless = ppp->mppe.send.stateless; + req->pr_mppe_send.keylenbits = ppp->mppe.send.keybits; + + memcpy(req->pr_mppe_recv.master_key, + ppp->mppe.recv.master_key, sizeof(req->pr_mppe_recv.master_key)); + req->pr_mppe_recv.stateless = ppp->mppe.recv.stateless; + req->pr_mppe_recv.keylenbits = ppp->mppe.recv.keybits; + + if (ppp->mppe_started != 0) { + req->pr_ppp_flags |= PIPEX_PPP_MPPE_ACCEPTED; + req->pr_ppp_flags |= PIPEX_PPP_MPPE_ENABLED; + } + if (ppp->mppe.required) + req->pr_ppp_flags |= PIPEX_PPP_MPPE_REQUIRED; +#endif /* USE_NPPPD_MPPE */ +} + +/** PPPAC インタフェースの IPv4パケットカーネル折り返しを有効化します */ +int +npppd_ppp_pipex_enable(npppd *_this, npppd_ppp *ppp) +{ + struct pipex_session_req req; +#ifdef USE_NPPPD_PPPOE + pppoe_session *pppoe; +#endif +#ifdef USE_NPPPD_PPTP + pptp_call *call; +#endif + int error; + + NPPPD_ASSERT(ppp != NULL); + NPPPD_ASSERT(ppp->phy_context != NULL); + NPPPD_ASSERT(ppp->use_pipex != 0); + + pipex_setup_common(ppp, &req); + + switch (ppp->tunnel_type) { +#ifdef USE_NPPPD_PPPOE + case PPP_TUNNEL_PPPOE: + pppoe = (pppoe_session *)ppp->phy_context; + + /* PPPOE 固有の情報 */ + req.pr_protocol = PIPEX_PROTO_PPPOE; + req.pr_session_id = pppoe->session_id; + req.pr_peer_session_id = 0; + strlcpy(req.pr_proto.pppoe.over_ifname, + pppoe_session_listen_ifname(pppoe), + sizeof(req.pr_proto.pppoe.over_ifname)); + memcpy(&req.pr_proto.pppoe.peer_address, &pppoe->ether_addr, + ETHER_ADDR_LEN); + + break; +#endif +#ifdef USE_NPPPD_PPTP + case PPP_TUNNEL_PPTP: + call = (pptp_call *)ppp->phy_context; + + /* PPTP 固有の情報 */ + req.pr_session_id = call->id; + req.pr_protocol = PIPEX_PROTO_PPTP; + + req.pr_peer_session_id = call->peers_call_id; + req.pr_proto.pptp.snd_nxt = call->snd_nxt; + req.pr_proto.pptp.snd_una = call->snd_una; + req.pr_proto.pptp.rcv_nxt = call->rcv_nxt; + req.pr_proto.pptp.rcv_acked = call->rcv_acked; + req.pr_proto.pptp.winsz = call->winsz; + req.pr_proto.pptp.maxwinsz = call->maxwinsz; + req.pr_proto.pptp.peer_maxwinsz = call->peers_maxwinsz; + + NPPPD_ASSERT(call->ctrl->peer.ss_family == AF_INET); + req.pr_proto.pptp.peer_address = + ((struct sockaddr_in *)&call->ctrl->peer)->sin_addr; + NPPPD_ASSERT(call->ctrl->our.ss_family == AF_INET); + req.pr_proto.pptp.our_address = + ((struct sockaddr_in *)&call->ctrl->our)->sin_addr; + break; +#endif + default: + return 1; + } + + if ((error = ioctl(_this->iface[ppp->ifidx].devf, PIPEXASESSION, &req)) + != 0) { + if (errno == ENXIO) /* pipex is disabled on runtime */ + error = 0; + ppp->pipex_enabled = 0; + return error; + } + + ppp->pipex_enabled = 1; + if (ppp->timeout_sec > 0) { + /* NPPPD 側の idle timer は停止 */ + ppp->timeout_sec = 0; + ppp_reset_idle_timeout(ppp); + } + + return error; +} + +/** PPPAC インタフェースの IPv4パケットカーネル折り返しを無効化します */ +int +npppd_ppp_pipex_disable(npppd *_this, npppd_ppp *ppp) +{ + struct pipex_session_close_req req; +#ifdef USE_NPPPD_PPPOE + pppoe_session *pppoe; +#endif +#ifdef USE_NPPPD_PPTP + pptp_call *call; +#endif + int error; + + if (ppp->pipex_started == 0) + return 0; /* not started */ + + bzero(&req, sizeof(req)); + switch(ppp->tunnel_type) { +#ifdef USE_NPPPD_PPPOE + case PPP_TUNNEL_PPPOE: + pppoe = (pppoe_session *)ppp->phy_context; + + /* PPPOE 固有の情報 */ + req.pcr_protocol = PIPEX_PROTO_PPPOE; + req.pcr_session_id = pppoe->session_id; + break; +#endif +#ifdef USE_NPPPD_PPTP + case PPP_TUNNEL_PPTP: + call = (pptp_call *)ppp->phy_context; + + /* PPTP 固有の情報 */ + req.pcr_session_id = call->id; + req.pcr_protocol = PIPEX_PROTO_PPTP; + break; +#endif + default: + return 1; + } + + error = ioctl(_this->iface[ppp->ifidx].devf, PIPEXDSESSION, &req); + if (error == 0) { + ppp->ipackets += req.pcr_stat.ipackets; + ppp->opackets += req.pcr_stat.opackets; + ppp->ierrors += req.pcr_stat.ierrors; + ppp->oerrors += req.pcr_stat.oerrors; + ppp->ibytes += req.pcr_stat.ibytes; + ppp->obytes += req.pcr_stat.obytes; + ppp->pipex_enabled = 0; + } + + return error; +} + +/** PPPAC インタフェースの IPv4パケットカーネル折り返しを無効化します */ +static int +npppd_ppp_pipex_ip_disable(npppd *_this, npppd_ppp *ppp) +{ + struct pipex_session_config_req req; +#ifdef USE_NPPPD_PPPOE + pppoe_session *pppoe; +#endif +#ifdef USE_NPPPD_PPTP + pptp_call *call; +#endif + if (ppp->pipex_started == 0) + return 0; /* not started */ + + bzero(&req, sizeof(req)); + switch(ppp->tunnel_type) { +#ifdef USE_NPPPD_PPPOE + case PPP_TUNNEL_PPPOE: + pppoe = (pppoe_session *)ppp->phy_context; + + /* PPPOE 固有の情報 */ + req.pcr_protocol = PIPEX_PROTO_PPPOE; + req.pcr_session_id = pppoe->session_id; + break; +#endif +#ifdef USE_NPPPD_PPTP + case PPP_TUNNEL_PPTP: + call = (pptp_call *)ppp->phy_context; + + /* PPTP 固有の情報 */ + req.pcr_session_id = call->id; + req.pcr_protocol = PIPEX_PROTO_PPTP; + break; +#endif + default: + return 1; + } + req.pcr_ip_forward = 0; + + return ioctl(_this->iface[ppp->ifidx].devf, PIPEXCSESSION, &req); +} + +static void +pipex_periodic(npppd *_this) +{ + struct pipex_session_list_req req; + npppd_ppp *ppp; + int i, error, ppp_id; + slist dlist, users; + + slist_init(&dlist); + slist_init(&users); + do { + error = ioctl(_this->iface[0].devf, PIPEXGCLOSED, &req); + if (error) { + if (errno != ENXIO) + log_printf(LOG_WARNING, + "PIPEXGCLOSED failed: %m"); + break; + } + for (i = 0; i < req.plr_ppp_id_count; i++) { + ppp_id = req.plr_ppp_id[i]; + slist_add(&dlist, (void *)ppp_id); + } + } while (req.plr_flags & PIPEX_LISTREQ_MORE); + + if (slist_length(&dlist) <= 0) + goto pipex_done; + if (npppd_get_all_users(_this, &users) != 0) { + log_printf(LOG_WARNING, + "npppd_get_all_users() failed in %s()", __func__); + slist_fini(&users); + goto pipex_done; + } + + /* 切断要求処理 */ + slist_itr_first(&dlist); + while (slist_itr_has_next(&dlist)) { + /* FIXME: PPP id での検索がリニアサーチの繰り返し */ + ppp_id = (int)slist_itr_next(&dlist); + slist_itr_first(&users); + ppp = NULL; + while (slist_itr_has_next(&users)) { + ppp = slist_itr_next(&users); + if (ppp_id == ppp->id) { + /* found */ + slist_itr_remove(&users); + break; + } + ppp = NULL; + } + if (ppp == NULL) { + log_printf(LOG_WARNING, + "kernel requested a ppp down, but it's not found. " + "ppp=%d", ppp_id); + continue; + } + ppp_log(ppp, LOG_INFO, "Stop requested by the kernel"); + ppp_stop(ppp, NULL); + } +pipex_done: + slist_fini(&users); + slist_fini(&dlist); +} +#endif /* USE_NPPPD_PIPEX */ + +/*********************************************************************** + * IP割り当て関連 + ***********************************************************************/ +/** npppd に IP の利用を要求します。*/ +int +npppd_prepare_ip(npppd *_this, npppd_ppp *ppp) +{ + if (ppp_ipcp(ppp) == NULL) + return 1; + + npppd_get_user_framed_ip_address(_this, ppp, ppp->username); + + if (npppd_iface_ip_is_ready(ppp_iface(ppp))) + ppp->ipcp.ip4_our = ppp_iface(ppp)->ip4addr; + else if (npppd_iface_ip_is_ready(&_this->iface[0])) + ppp->ipcp.ip4_our = _this->iface[0].ip4addr; + else + return -1; + if (ppp_ipcp(ppp)->dns_use_tunnel_end != 0) { + ppp->ipcp.dns_pri = ppp->ipcp.ip4_our; + ppp->ipcp.dns_sec.s_addr = INADDR_NONE; + } else { + ppp->ipcp.dns_pri = ppp_ipcp(ppp)->dns_pri; + ppp->ipcp.dns_sec = ppp_ipcp(ppp)->dns_sec; + } + ppp->ipcp.nbns_pri = ppp_ipcp(ppp)->nbns_pri; + ppp->ipcp.nbns_sec = ppp_ipcp(ppp)->nbns_sec; + + return 0; +} + +/** npppd に IP の利用が終了したことを通知して、リソースを解放します。*/ +void +npppd_release_ip(npppd *_this, npppd_ppp *ppp) +{ + + if (!ppp_ip_assigned(ppp)) + return; + +#ifdef USE_NPPPD_LINKID + linkid_purge(ppp->ppp_framed_ip_address); +#endif + + npppd_set_ip_enabled(_this, ppp, 0); + npppd_pool_release_ip(ppp->assigned_pool, ppp); + ppp->assigned_pool = NULL; + ppp->ppp_framed_ip_address.s_addr = 0; +} + +/** IPアドレスの有効無効を切替えます。enabled が変更されると経路を操作します。*/ +void +npppd_set_ip_enabled(npppd *_this, npppd_ppp *ppp, int enabled) +{ + int was_enabled, found; + slist *u; + hash_link *hl; + npppd_ppp *ppp1; + + NPPPD_ASSERT(ppp_ip_assigned(ppp)); + NPPPD_DBG((LOG_DEBUG, + "npppd_set_ip_enabled(%s/%s, %s)", ppp->username, + inet_ntoa(ppp->ppp_framed_ip_address), + (enabled)?"true" : "false")); + /* + * enabled に変更がなければ、なにもしない。経路変更すると、いろんなプ + * ログラムが wakeup して重くなるので、余計な経路情報の更新は控える。 + */ + enabled = (enabled)? 1 : 0; + was_enabled = (ppp->assigned_ip4_enabled != 0)? 1 : 0; + if (enabled == was_enabled) + return; + ppp->assigned_ip4_enabled = enabled; + if (enabled) { + if (ppp->username[0] != '\0') { + if ((u = npppd_get_ppp_by_user(_this, ppp->username)) + == NULL) { + if ((u = malloc(sizeof(slist))) == NULL) { + ppp_log(ppp, LOG_ERR, + "Out of memory on %s: %m", + __func__); + } else { + slist_init(u); + slist_set_size(u, 4); + hash_insert(_this->map_user_ppp, + ppp->username, u); + NPPPD_DBG((LOG_DEBUG, + "hash_insert(user->ppp, %s)", + ppp->username)); + } + } + if (u != NULL) /* malloc 失敗がある */ + slist_add(u, ppp); + } + +#ifndef NO_ROUTE_FOR_POOLED_ADDRESS + if (ppp->snp.snp_next != NULL) + /* 同じアドレス/マスクでブラックホールあり */ + in_route_delete(&ppp->ppp_framed_ip_address, + &ppp->ppp_framed_ip_netmask, &loop, RTF_BLACKHOLE); + /* See the comment for MRU_IPMTU() on ppp.h */ + if (ppp->ppp_framed_ip_netmask.s_addr == 0xffffffffL) { + in_host_route_add(&ppp->ppp_framed_ip_address, + &ppp_iface(ppp)->ip4addr, ppp_iface(ppp)->ifname, + MRU_IPMTU(ppp->peer_mru)); + } else { + in_route_add(&ppp->ppp_framed_ip_address, + &ppp->ppp_framed_ip_netmask, + &ppp_iface(ppp)->ip4addr, ppp_iface(ppp)->ifname, 0, + MRU_IPMTU(ppp->peer_mru)); + } +#endif + } else { +#ifndef NO_ROUTE_FOR_POOLED_ADDRESS + if (ppp->ppp_framed_ip_netmask.s_addr == 0xffffffffL) { + in_host_route_delete(&ppp->ppp_framed_ip_address, + &ppp_iface(ppp)->ip4addr); + } else { + in_route_delete(&ppp->ppp_framed_ip_address, + &ppp->ppp_framed_ip_netmask, + &ppp_iface(ppp)->ip4addr, 0); + } + if (ppp->snp.snp_next != NULL) + /* 同じアドレス/マスクでブラックホールあり */ + in_route_add(&ppp->snp.snp_addr, &ppp->snp.snp_mask, + &loop, LOOPBACK_IFNAME, RTF_BLACKHOLE, 0); +#endif + if (ppp->username[0] != '\0') { + hl = hash_lookup(_this->map_user_ppp, ppp->username); + NPPPD_ASSERT(hl != NULL); + if (hl == NULL) { + ppp_log(ppp, LOG_ERR, + "Unexpected error: cannot find user(%s) " + "from user database", ppp->username); + return; + } + found = 0; + u = hl->item; + for (slist_itr_first(u); slist_itr_has_next(u);) { + ppp1 = slist_itr_next(u); + if (ppp1 == ppp) { + slist_itr_remove(u); + found++; + break; + } + } + if (found == 0) { + ppp_log(ppp, LOG_ERR, + "Unexpected error: PPP instance is " + "not found in the user's list."); + } + NPPPD_ASSERT(found != 0); + if (slist_length(u) <= 0) { + /* 最後の PPP */ + NPPPD_DBG((LOG_DEBUG, + "hash_delete(user->ppp, %s)", + ppp->username)); + if (hash_delete(_this->map_user_ppp, + ppp->username, 0) != 0) { + ppp_log(ppp, LOG_ERR, + "Unexpected error: cannot delete " + "user(%s) from user database", + ppp->username); + } + slist_fini(u); + free(u); + } else { + /* 参照の差し替え */ + ppp1 = slist_get(u, 0); + hl->key = ppp1->username; + } + } +#ifdef USE_NPPPD_PIPEX + if (npppd_ppp_pipex_ip_disable(_this, ppp) != 0) + ppp_log(ppp, LOG_ERR, + "npppd_ppp_pipex_ip_disable() failed: %m"); +#endif /* USE_NPPPD_PIPEX */ + } +} + +/** + * IPアドレスを割り当てます。返される struct in_addr はネットバイトオーダー + * で格納されています。 + * @param req_ip4 新たに割り当てを要求する IPアドレス。既に同一のア + * ドレスが割り当て済の場合に呼び出すと失敗します。 + */ +int +npppd_assign_ip_addr(npppd *_this, npppd_ppp *ppp, uint32_t req_ip4) +{ + uint32_t ip4, ip4mask; + int flag, dyna, rval, fallback_dyna; + const char *reason = "out of the pool"; + struct sockaddr_npppd *snp; + npppd_pool *pool; + npppd_auth_base *realm; + + NPPPD_DBG((LOG_DEBUG, "%s() assigned=%s", __func__, + (ppp_ip_assigned(ppp))? "true" : "false")); + if (ppp_ip_assigned(ppp)) + return 0; + + ip4 = INADDR_ANY; + ip4mask = 0xffffffffL; + flag = ppp_ipcp(ppp)->ip_assign_flags; + realm = ppp->realm; + dyna = 0; + fallback_dyna = 0; + pool = NULL; + + if (ppp->realm_framed_ip_address.s_addr == INADDR_USER_SELECT) { + if (req_ip4 == INADDR_ANY) + dyna = 1; + } else if (ppp->realm_framed_ip_address.s_addr == INADDR_NAS_SELECT) { + dyna = 1; + } else { + NPPPD_ASSERT(realm != NULL); + /* レルムなしで、固定 IPアドレス割り当てはできない */ + + if ((npppd_auth_get_type(realm) == NPPPD_AUTH_TYPE_RADIUS && + (flag & NPPPD_IP_ASSIGN_RADIUS) == 0 && + (flag & NPPPD_IP_ASSIGN_FIXED) == 0) || + (npppd_auth_get_type(realm) == NPPPD_AUTH_TYPE_LOCAL && + (flag & NPPPD_IP_ASSIGN_FIXED) == 0)) + dyna = 1; + else { + fallback_dyna = 1; + req_ip4 = ntohl(ppp->realm_framed_ip_address.s_addr); + ip4mask = ntohl(ppp->realm_framed_ip_netmask.s_addr); + } + } + if (!dyna) { + /* + * fallback_dyna ... + * Realm で固定割り当てが設定されているがアドレスがプール + * されていない場合には、動的割り当てに fallback する。 + */ + for (slist_itr_first(ppp_pools(ppp)); + slist_itr_has_next(ppp_pools(ppp));){ + pool = slist_itr_next(ppp_pools(ppp)); + rval = npppd_pool_get_assignability(pool, req_ip4, + ip4mask, &snp); + switch (rval) { + case ADDRESS_OK: + if (snp->snp_type == SNP_POOL) { + /* + * 固定アドレスプールを使えるのは、 + * Realm で指定した場合に限る + */ + if (ppp->realm_framed_ip_address + .s_addr != INADDR_USER_SELECT) + ip4 = req_ip4; + break; + } + ppp->assign_dynapool = 1; + ip4 = req_ip4; + break; + case ADDRESS_RESERVED: + reason = "reserved"; + continue; + case ADDRESS_OUT_OF_POOL: + reason = "out of the pool"; + continue; /* 続行 */ + case ADDRESS_BUSY: + fallback_dyna = 0; + reason = "busy"; + break; + default: + case ADDRESS_INVALID: + fallback_dyna = 0; + reason = "invalid"; + break; + } + break; + } +#define IP_4OCT(v) ((0xff000000 & (v)) >> 24), ((0x00ff0000 & (v)) >> 16),\ + ((0x0000ff00 & (v)) >> 8), (0x000000ff & (v)) + if (ip4 == 0) { + ppp_log(ppp, LOG_NOTICE, + "Requested IP address (%d.%d.%d.%d)/%d " + "is %s", IP_4OCT(req_ip4), + netmask2prefixlen(htonl(ip4mask)), reason); + if (fallback_dyna) + goto dyna_assign; + return 1; + } + ppp->assigned_pool = pool; + + ppp->ppp_framed_ip_address.s_addr = htonl(ip4); + ppp->ppp_framed_ip_netmask.s_addr = htonl(ip4mask); + } else { +dyna_assign: + for (slist_itr_first(ppp_pools(ppp)); + slist_itr_has_next(ppp_pools(ppp));){ + pool = slist_itr_next(ppp_pools(ppp)); + ip4 = npppd_pool_get_dynamic(pool, ppp); + if (ip4 != 0) + break; + } + if (ip4 == 0) { + ppp_log(ppp, LOG_NOTICE, + "No free address in the pool."); + return 1; + } + ppp->assigned_pool = pool; + ppp->assign_dynapool = 1; + ppp->ppp_framed_ip_address.s_addr = htonl(ip4); + ppp->ppp_framed_ip_netmask.s_addr = htonl(0xffffffffL); + } + + return npppd_pool_assign_ip(ppp->assigned_pool, ppp); +} + +static void * +rtlist_remove(slist *prtlist, struct radish *radish) +{ + struct radish *r; + + slist_itr_first(prtlist); + while (slist_itr_has_next(prtlist)) { + r = slist_itr_next(prtlist); + if (!sockaddr_npppd_match(radish->rd_route, r->rd_route) || + !sockaddr_npppd_match(radish->rd_mask, r->rd_mask)) + continue; + + return slist_itr_remove(prtlist); + } + + return NULL; +} + +/** {@link ::npppd#rd npppd に唯一の radish} をセットします。*/ +int +npppd_set_radish(npppd *_this, void *radish_head) +{ + int rval, delppp0, count; + struct sockaddr_npppd *snp; + struct radish *radish, *r; + slist rtlist0, rtlist1, delppp; + npppd_ppp *ppp; + void *dummy; + + slist_init(&rtlist0); + slist_init(&rtlist1); + slist_init(&delppp); + + if (radish_head != NULL) { + if (rd2slist(radish_head, &rtlist1) != 0) { + log_printf(LOG_WARNING, "rd2slist failed: %m"); + goto reigai; + } + } + if (_this->rd != NULL) { + if (rd2slist(_this->rd, &rtlist0) != 0) { + log_printf(LOG_WARNING, "rd2slist failed: %m"); + goto reigai; + } + } + if (_this->rd != NULL && radish_head != NULL) { + for (slist_itr_first(&rtlist0); slist_itr_has_next(&rtlist0);) { + radish = slist_itr_next(&rtlist0); + snp = radish->rd_rtent; + /* + * プールアドレスの差し替え + */ + if (snp->snp_type == SNP_POOL || + snp->snp_type == SNP_DYN_POOL) { + if (rd_lookup(radish->rd_route, radish->rd_mask, + radish_head) == NULL) + continue; + /* 追加しない */ + rtlist_remove(&rtlist1, radish); + /* 削除しない */ + slist_itr_remove(&rtlist0); + continue; + } + /* + * アクティブ PPP セッションの処理 + */ + NPPPD_ASSERT(snp->snp_type == SNP_PPP); + ppp = snp->snp_data_ptr; + + /* アクティブ PPP の経路は削除しない。*/ + slist_itr_remove(&rtlist0); + + /* 古いプール設定に関する情報はクリア */ + ppp->assigned_pool = NULL; + snp->snp_next = NULL; + + delppp0 = 0; + if (!rd_match((struct sockaddr *)snp, radish_head, &r)){ + /* + * 新プールに含まれない場合、PPPセッションは + * 切断リスト入り。 + */ + slist_add(&delppp, snp->snp_data_ptr); + delppp0 = 1; + } else { + NPPPD_ASSERT( + ((struct sockaddr_npppd *)r->rd_rtent) + ->snp_type == SNP_POOL || + ((struct sockaddr_npppd *)r->rd_rtent) + ->snp_type == SNP_DYN_POOL); + /* + * アドレス/マスクが等しいプールが存在する場合 + * は、RADISH エントリをリスト化する。リストは + * SNP_PPP が先にするので、現在のエントリは + * snp->snp_next にセットし、現在のエントリを + * 削除。 + */ + if (sockaddr_npppd_match( + radish->rd_route, r->rd_route) && + sockaddr_npppd_match( + radish->rd_mask, r->rd_mask)) { + /* 解放するので、新経路リストから削除 */ + rtlist_remove(&rtlist1, radish); + /* snp_next をセット */ + snp->snp_next = r->rd_rtent; + rval = rd_delete(r->rd_route, + r->rd_mask, radish_head, &dummy); + NPPPD_ASSERT(rval == 0); + } + } + /* 新 Radish に登録。*/ + rval = rd_insert(radish->rd_route, radish->rd_mask, + radish_head, snp); + if (rval != 0) { + errno = rval; + ppp_log(((npppd_ppp *)snp->snp_data_ptr), + LOG_ERR, + "Fatal error on %s, cannot continue " + "this ppp session: %m", __func__); + if (!delppp0) + slist_add(&delppp, snp->snp_data_ptr); + } + } + } + count = 0; +#ifndef NO_ROUTE_FOR_POOLED_ADDRESS + for (slist_itr_first(&rtlist0); slist_itr_has_next(&rtlist0);) { + radish = slist_itr_next(&rtlist0); + in_route_delete(&SIN(radish->rd_route)->sin_addr, + &SIN(radish->rd_mask)->sin_addr, &loop, RTF_BLACKHOLE); + count++; + } + if (count > 0) + log_printf(LOG_INFO, + "Deleted %d routes for old pool addresses", count); + + count = 0; + for (slist_itr_first(&rtlist1); slist_itr_has_next(&rtlist1);) { + radish = slist_itr_next(&rtlist1); + in_route_add(&(SIN(radish->rd_route)->sin_addr), + &SIN(radish->rd_mask)->sin_addr, &loop, LOOPBACK_IFNAME, + RTF_BLACKHOLE, 0); + count++; + } + if (count > 0) + log_printf(LOG_INFO, + "Added %d routes for new pool addresses", count); +#endif + slist_fini(&rtlist0); + slist_fini(&rtlist1); + + if (_this->rd != NULL) + npppd_rd_walktree_delete(_this->rd); + _this->rd = radish_head; + + for (slist_itr_first(&delppp); slist_itr_has_next(&delppp);) { + ppp = slist_itr_next(&delppp); + ppp_log(ppp, LOG_NOTICE, + "stop. IP address of this ppp is out of the pool.: %s", + inet_ntoa(ppp->ppp_framed_ip_address)); + ppp_stop(ppp, NULL); + } + slist_fini(&delppp); + + return 0; +reigai: + slist_fini(&rtlist0); + slist_fini(&rtlist1); + slist_fini(&delppp); + + return 1; +} + +/** + * すべてのユーザを {@link slist} に格納して返却します。users には、 + * {@link ::npppd_ppp} への参照が格納されます。 + */ +int +npppd_get_all_users(npppd *_this, slist *users) +{ + int rval; + struct radish *rd; + struct sockaddr_npppd *snp; + slist list; + + NPPPD_ASSERT(_this != NULL); + + slist_init(&list); + rval = rd2slist(_this->rd, &list); + if (rval != 0) + return rval; + + for (slist_itr_first(&list); slist_itr_has_next(&list);) { + rd = slist_itr_next(&list); + snp = rd->rd_rtent; + if (snp->snp_type == SNP_PPP) { + if (slist_add(users, snp->snp_data_ptr) == NULL) { + log_printf(LOG_ERR, + "slist_add() failed in %s: %m", __func__); + goto reigai; + } + } + } + slist_fini(&list); + + return 0; +reigai: + slist_fini(&list); + + return 1; +} + +static int +rd2slist_walk(struct radish *rd, void *list0) +{ + slist *list = list0; + void *r; + + r = slist_add(list, rd); + if (r == NULL) + return -1; + return 0; +} +static int +rd2slist(struct radish_head *h, slist *list) +{ + return rd_walktree(h, rd2slist_walk, list); +} + +static void +npppd_reload0(npppd *_this) +{ + npppd_reload_config(_this); +#ifdef USE_NPPPD_ARP + arp_set_strictintfnetwork(npppd_config_str_equali(_this, "arpd.strictintfnetwork", "true", ARPD_STRICTINTFNETWORK_DEFAULT)); + if (npppd_config_str_equali(_this, "arpd.enabled", "true", ARPD_DEFAULT) == 1) + arp_sock_init(); + else + arp_sock_fini(); +#endif + npppd_modules_reload(_this); + npppd_ifaces_load_config(_this); +#ifdef NPPPD_RESET_IP_ADDRESS + { + int i; + for (i = 0; i < countof(_this->iface); i++) { + if (_this->iface[i].initialized != 0) + npppd_iface_reinit(&_this->iface[i]); + } + } +#endif + npppd_auth_finalizer_periodic(_this); +} + +/*********************************************************************** + * シグナルハンドラ + ***********************************************************************/ +static void +npppd_on_sighup(int fd, short ev_type, void *ctx) +{ + npppd *_this; + + _this = ctx; +#ifndef NO_DELAYED_RELOAD + if (_this->delayed_reload > 0) + _this->reloading_count = _this->delayed_reload; + else +#endif + npppd_reload0(_this); +} + +static void +npppd_on_sigterm(int fd, short ev_type, void *ctx) +{ + npppd *_this; + + _this = ctx; + npppd_stop(_this); +} + +static void +npppd_on_sigint(int fd, short ev_type, void *ctx) +{ + npppd *_this; + + _this = ctx; + npppd_stop(_this); +} + +/*********************************************************************** + * 雑多な関数 + ***********************************************************************/ +static uint32_t +str_hash(const void *ptr, int sz) +{ + uint32_t hash = 0; + int i, len; + const char *str; + + str = ptr; + len = strlen(str); + for (i = 0; i < len; i++) + hash = hash*0x1F + str[i]; + hash = (hash << 16) ^ (hash & 0xffff); + + return hash % sz; +} + +/** + * 指定した {@link ::npppd_ppp PPP} 用の認証レルムを選択します。 + * 選択に成功した場合には、0 が返ります。 + */ +int +npppd_ppp_bind_realm(npppd *_this, npppd_ppp *ppp, const char *username, int + eap_required) +{ + int lsuffix, lprefix, lusername, lmax; + const char *val; + char *tok, *buf0, buf[NPPPD_CONFIG_BUFSIZ], buf1[MAX_USERNAME_LENGTH]; + npppd_auth_base *realm = NULL, *realm0 = NULL, *realm1 = NULL; + + NPPPD_ASSERT(_this != NULL); + NPPPD_ASSERT(ppp != NULL); + NPPPD_ASSERT(username != NULL); + + /* + * PPPサフィックスが最長で、サフィックスの長さが同じものであれば、 + * 最初に一致したものを返却します。 + */ + lusername = strlen(username); + lmax = -1; + realm = NULL; + + if ((val = ppp_config_str(ppp, "realm_list")) == NULL) { +#ifndef NO_DEFAULT_REALM + /* + * 従来版との互換性のため、レルムがリストになっていなければ + * ローカル=>RADIUS とフォールバックする。 + */ + realm0 = NULL; + slist_itr_first(&_this->realms); + while (slist_itr_has_next(&_this->realms)) { + realm1 = slist_itr_next(&_this->realms); + if (!npppd_auth_is_usable(realm1)) + continue; + switch (npppd_auth_get_type(realm1)) { + case NPPPD_AUTH_TYPE_LOCAL: + if (npppd_auth_get_user_password( + realm1, npppd_auth_username_for_auth( + realm1, username, buf1), + NULL, NULL) == 0) { + realm = realm1; + goto found; + } + break; + + case NPPPD_AUTH_TYPE_RADIUS: + realm = realm1; + goto found; + } + } +#else + /* Nothing to do */ +#endif + } else { + strlcpy(buf, val, sizeof(buf)); + buf0 = buf; + while ((tok = strsep(&buf0, " ,\t\r\n")) != NULL) { + if (tok[0] == '\0') + continue; + realm0 = NULL; + slist_itr_first(&_this->realms); + while (slist_itr_has_next(&_this->realms)) { + realm1 = slist_itr_next(&_this->realms); + if (!npppd_auth_is_usable(realm1)) + continue; + if (eap_required && + !npppd_auth_is_eap_capable(realm1)) + continue; + if (strcmp(npppd_auth_get_label(realm1), tok) + == 0) { + realm0 = realm1; + break; + } + } + if (realm0 == NULL) + continue; + lsuffix = strlen(npppd_auth_get_suffix(realm0)); + if (lsuffix > lmax && + (lsuffix == 0 || (lsuffix < lusername && + strcmp(username + lusername - lsuffix, + npppd_auth_get_suffix(realm0)) == 0))) { +#ifdef IDGW + /* + * ローカル認証でユーザが存在しない場合には、 + * 次のレルムにフォールバックする + */ + if (npppd_auth_get_type(realm0) + == NPPPD_AUTH_TYPE_LOCAL) { + if (!npppd_auth_is_ready(realm0)) + continue; + if (npppd_auth_get_user_password(realm0, + npppd_auth_username_for_auth( + realm0, username, buf1), + NULL, NULL) == 1) { + continue; + } + } +#endif + /* check prefix */ + lprefix = strlen(npppd_auth_get_suffix(realm0)); + if (lprefix > 0 && + strncmp(username, + npppd_auth_get_suffix(realm0), + lprefix) != 0) + continue; + + lmax = lsuffix; + realm = realm0; + } + } + } + if (realm == NULL) { + log_printf(LOG_INFO, "user='%s' could not bind any realms", + username); + return 1; + } +#ifndef NO_DEFAULT_REALM +found: +#endif + NPPPD_DBG((LOG_DEBUG, "%s bind realm %s(%s)", + username, npppd_auth_get_label(realm), npppd_auth_get_name(realm))); + + if (npppd_auth_get_type(realm) == NPPPD_AUTH_TYPE_LOCAL) + /* hook the auto reload */ + npppd_auth_get_user_password(realm, + npppd_auth_username_for_auth(realm1, username, buf1), NULL, + NULL); + ppp->realm = realm; + + return 0; +} + +/** 割当っている認証レルムがローカル認証かどうか。*/ +int +npppd_ppp_is_realm_local(npppd *_this, npppd_ppp *ppp) +{ + NPPPD_ASSERT(_this != NULL); + NPPPD_ASSERT(ppp != NULL); + + if (ppp->realm == NULL) + return 0; + + return (npppd_auth_get_type(ppp->realm) == NPPPD_AUTH_TYPE_LOCAL) + ? 1 : 0; +} + +/** 割当っている認証レルムがRADIUS認証かどうか。*/ +int +npppd_ppp_is_realm_radius(npppd *_this, npppd_ppp *ppp) +{ + NPPPD_ASSERT(_this != NULL); + NPPPD_ASSERT(ppp != NULL); + + if (ppp->realm == NULL) + return 0; + + return (npppd_auth_get_type(ppp->realm) == NPPPD_AUTH_TYPE_RADIUS) + ? 1 : 0; +} + +/** 割当っている認証レルムが使用可能かどうか。*/ +int +npppd_ppp_is_realm_ready(npppd *_this, npppd_ppp *ppp) +{ + if (ppp->realm == NULL) + return 0; + + return npppd_auth_is_ready(ppp->realm); +} + +/** 割当っている認証レルムの名前を返却します。*/ +const char * +npppd_ppp_get_realm_name(npppd *_this, npppd_ppp *ppp) +{ + if (ppp->realm == NULL) + return "(none)"; + return npppd_auth_get_name(ppp->realm); +} + +/** ppp に割当ったインタフェース名をセットします。*/ +const char * +npppd_ppp_get_iface_name(npppd *_this, npppd_ppp *ppp) +{ + if (ppp == NULL || ppp->ifidx < 0) + return "(not binding)"; + return ppp_iface(ppp)->ifname; +} + +/** インタフェースが利用可能かどうか。*/ +int +npppd_ppp_iface_is_ready(npppd *_this, npppd_ppp *ppp) +{ + return (npppd_iface_ip_is_ready(ppp_iface(ppp)) && + ppp_ipcp(ppp) != NULL)? 1 : 0; +} + +/** ppp に適切なインタフェースを紐付けます。*/ +int +npppd_ppp_bind_iface(npppd *_this, npppd_ppp *ppp) +{ + int i, ifidx, ntotal_session; + const char *ifname, *label; + char buf[BUFSIZ]; + npppd_auth_base *realm; + + NPPPD_ASSERT(_this != NULL); + NPPPD_ASSERT(ppp != NULL); + + if (ppp->ifidx >= 0) + return 0; + if (ppp->peer_auth == 0) { + strlcpy(buf, "no_auth.concentrate", sizeof(buf)); + } else { + realm = (npppd_auth_base *)ppp->realm; + strlcpy(buf, "realm.", sizeof(buf)); + NPPPD_ASSERT(ppp->realm != NULL); + label = npppd_auth_get_label(realm); + if (label[0] != '\0') { + strlcat(buf, label, sizeof(buf)); + strlcat(buf, ".concentrate", sizeof(buf)); + } else + strlcat(buf, "concentrate", sizeof(buf)); + } + + ifname = ppp_config_str(ppp, buf); + if (ifname == NULL) + return 1; + + /* インタフェース検索 */ + ifidx = -1; + ntotal_session = 0; + for (i = 0; i < countof(_this->iface); i++) { + if (_this->iface[i].initialized == 0) + continue; + ntotal_session += _this->iface[i].nsession; + if (strcmp(_this->iface[i].ifname, ifname) == 0) + ifidx = i; + } + if (ifidx < 0) + return 1; + + if (ntotal_session >= _this->max_session) { + ppp_log(ppp, LOG_WARNING, + "Number of sessions reaches out of the limit=%d", + _this->max_session); + return 1; + } + if (_this->iface[ifidx].nsession >= _this->iface[ifidx].max_session) { + ppp_log(ppp, LOG_WARNING, + "Number of sessions reaches out of the interface limit=%d", + _this->iface[ifidx].max_session); + return 1; + } + + ppp->ifidx = ifidx; + ppp_iface(ppp)->nsession++; + + return 0; +} + +/** ppp に割当ったインタフェースを解除します */ +void +npppd_ppp_unbind_iface(npppd *_this, npppd_ppp *ppp) +{ + if (ppp->ifidx >= 0) + ppp_iface(ppp)->nsession--; + + ppp->ifidx = -1; +} + +static int +npppd_rd_walktree_delete(struct radish_head *rh) +{ + void *dummy; + struct radish *rd; + slist list; + + slist_init(&list); + if (rd2slist(rh, &list) != 0) + return 1; + for (slist_itr_first(&list); slist_itr_has_next(&list);) { + rd = slist_itr_next(&list); + rd_delete(rd->rd_route, rd->rd_mask, rh, &dummy); + } + slist_fini(&list); + + free(rh); + + return 0; +} + +#ifdef USE_NPPPD_RADIUS +/** @return 使用可能な radius の設定が無い場合は NULL が返ります。 */ +void * +npppd_get_radius_req_setting(npppd *_this, npppd_ppp *ppp) +{ + NPPPD_ASSERT(_this != NULL); + NPPPD_ASSERT(ppp != NULL); + + if (ppp->realm == NULL) + return NULL; + if (!npppd_ppp_is_realm_radius(_this, ppp)) + return NULL; + + return npppd_auth_radius_get_radius_req_setting( + (npppd_auth_radius *)ppp->realm); +} + +/** Radius サーバが問い合わせに失敗したことを通知します。*/ +void +npppd_radius_server_failure_notify(npppd *_this, npppd_ppp *ppp, void *rad_ctx, + const char *reason) +{ + NPPPD_ASSERT(rad_ctx != NULL); + NPPPD_ASSERT(ppp != NULL); + + npppd_auth_radius_server_failure_notify( + (npppd_auth_radius *)ppp->realm, radius_get_server_address(rad_ctx), + reason); +} +#endif + +/** 認証レルムの終了化処理 */ +static void +npppd_auth_finalizer_periodic(npppd *_this) +{ + int ndisposing = 0, refcnt; + slist users; + npppd_auth_base *auth_base; + npppd_ppp *ppp; + + /* + * disposing フラグがセットされた realm について、割当った PPP が + * あれば切断する。全て切断済みなら realm を解放する。 + */ + NPPPD_DBG((DEBUG_LEVEL_2, "%s() called", __func__)); + slist_itr_first(&_this->realms); + while (slist_itr_has_next(&_this->realms)) { + auth_base = slist_itr_next(&_this->realms); + if (!npppd_auth_is_disposing(auth_base)) + continue; + refcnt = 0; + if (ndisposing++ == 0) { + slist_init(&users); + if (npppd_get_all_users(_this, &users) != 0) { + log_printf(LOG_WARNING, + "npppd_get_all_users() failed in %s(): %m", + __func__); + break; + } + } + slist_itr_first(&users); + while (slist_itr_has_next(&users)) { + ppp = slist_itr_next(&users); + if (ppp->realm == auth_base) { + refcnt++; + ppp_stop(ppp, NULL); + ppp_log(ppp, LOG_INFO, + "Stop request by npppd. Binding " + "authentication realm is disposing. " + "realm=%s", npppd_auth_get_name(auth_base)); + slist_itr_remove(&users); + } + } + if (refcnt == 0) + npppd_auth_destroy(auth_base); + } + if (ndisposing > 0) + slist_fini(&users); +} + +/** sockaddr_npppd の比較関数。一致すると 0 が返ります */ +int +sockaddr_npppd_match(void *a0, void *b0) +{ + struct sockaddr_npppd *a, *b; + + a = a0; + b = b0; + + return (a->snp_addr.s_addr == b->snp_addr.s_addr)? 1 : 0; +} + +/** + * 認証に使用するユーザ名を username_buffer で指定した領域に作成して、 + * 返却します。 + * @param username_buffer 認証に使用するユーザ名を格納するバッファ領域 + * を指定します。MAX_USERNAME_LENGTH 以上の領域である必要があります。 + */ +const char * +npppd_ppp_get_username_for_auth(npppd *_this, npppd_ppp *ppp, + const char *username, char *username_buffer) +{ + NPPPD_ASSERT(_this != NULL); + NPPPD_ASSERT(ppp != NULL); + NPPPD_ASSERT(ppp->realm != NULL); + + return npppd_auth_username_for_auth(ppp->realm, username, + username_buffer); +} + +static inline void +seed_random(long *seed) +{ + struct timeval t; +#ifdef KERN_URND + size_t seedsiz; + int mib[] = { CTL_KERN, KERN_URND }; + + seedsiz = sizeof(*seed); + if (sysctl(mib, countof(mib), seed, &seedsiz, NULL, 0) == 0) { + NPPPD_ASSERT(seedsiz == sizeof(long)); + return; + } + log_printf(LOG_WARNING, "Could not set random seed from the system: %m"); +#endif + gettimeofday(&t, NULL); + *seed = gethostid() ^ t.tv_sec ^ t.tv_usec ^ getpid(); +} diff --git a/usr.sbin/npppd/npppd/npppd.h b/usr.sbin/npppd/npppd/npppd.h new file mode 100644 index 00000000000..79423b7876f --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd.h @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_H +#define NPPPD_H 1 + + +#ifndef NPPPD_DEFAULT_TUN_IFNAME +#define NPPPD_DEFAULT_TUN_IFNAME "tun0" +#endif + + +#define DEFAULT_RADIUS_AUTH_IPADDR "127.0.0.1" +#define DEFAULT_RADIUS_AUTH_PORT 1812 +#define DEFAULT_RADIUS_AUTH_TIMEOUT 9 + +#define DEFAULT_AUTH_TIMEOUT 30 + +/** 固定割り当て */ +#define NPPPD_IP_ASSIGN_FIXED 0x0001 + +/** 先方が提案したアドレスを受け入れる */ +#define NPPPD_IP_ASSIGN_USER_SELECT 0x0002 + +/** RADIUS の Framed-IP-Address を使う */ +#define NPPPD_IP_ASSIGN_RADIUS 0x0004 + +/** sockaddr_npppd */ +struct sockaddr_npppd { + struct sockaddr_in sin4; + struct sockaddr_in sin4mask; +#define snp_len sin4.sin_len +#define snp_family sin4.sin_family +#define snp_addr sin4.sin_addr + int snp_type; /* SNP_POOL or SNP_PPP */ +#define snp_mask sin4mask.sin_addr + /** 次のエントリ */ + struct sockaddr_npppd *snp_next; + /** エントリの中身 */ + void *snp_data_ptr; +}; +#define SNP_POOL 1 +#define SNP_DYN_POOL 2 +#define SNP_PPP 3 + +typedef struct _npppd npppd; + +#include "ppp.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +npppd *npppd_get_npppd (void); +int npppd_init (npppd *, const char *); +void npppd_stop (npppd *); +void npppd_fini (npppd *); +int npppd_prepare_ip (npppd *, npppd_ppp *); +void npppd_release_ip (npppd *, npppd_ppp *); +int nppp_load_user_setting(npppd *, npppd_ppp *); +void npppd_set_ip_enabled (npppd *, npppd_ppp *, int); +int npppd_get_user_password (npppd *, npppd_ppp *, const char *, char *, int *); +struct in_addr *npppd_get_user_framed_ip_address(npppd *, npppd_ppp *, const char *); +int npppd_check_calling_number (npppd *, npppd_ppp *); +void npppd_network_output (npppd *, npppd_ppp *, int, u_char *, int); +void npppd_network_input (npppd *, uint8_t *, int); +npppd_ppp *npppd_get_ppp_by_ip (npppd *, struct in_addr); +slist *npppd_get_ppp_by_user (npppd *, const char *); +int npppd_check_user_max_session(npppd *, npppd_ppp *); +int npppd_get_all_users (npppd *, slist *); +int npppd_set_radish (npppd *, void *); +int npppd_ppp_iface_is_ready(npppd *, npppd_ppp *); +int sockaddr_npppd_match(void *, void *); +npppd_ppp *npppd_get_ppp_by_id(npppd *, int); + +const char *npppd_config_str (npppd *, const char *); +int npppd_config_int (npppd *, const char *, int); +int npppd_config_str_equal (npppd *, const char *, const char *, int); +int npppd_config_str_equali (npppd *, const char *, const char *, int); +int npppd_modules_reload (npppd *); +int npppd_ifaces_load_config(npppd *); +int npppd_reload_config (npppd *); +int npppd_assign_ip_addr (npppd *, npppd_ppp *, uint32_t); +void npppd_release_ip_addr (npppd *, npppd_ppp *); + +int npppd_ppp_bind_realm(npppd *, npppd_ppp *, const char *, int); +int npppd_ppp_is_realm_local(npppd *, npppd_ppp *); +int npppd_ppp_is_realm_radius(npppd *, npppd_ppp *); +int npppd_ppp_is_realm_ready(npppd *, npppd_ppp *); +const char *npppd_ppp_get_realm_name(npppd *, npppd_ppp *); +int npppd_ppp_bind_iface(npppd *, npppd_ppp *); +void npppd_ppp_unbind_iface(npppd *, npppd_ppp *); +const char *npppd_ppp_get_iface_name(npppd *, npppd_ppp *); +void npppd_radius_server_failure_notify(npppd *, npppd_ppp *, void *, const char *); +int npppd_ppp_pipex_enable(npppd *, npppd_ppp *); +int npppd_ppp_pipex_disable(npppd *, npppd_ppp *); +const char *npppd_ppp_get_username_for_auth(npppd *, npppd_ppp *, const char *, char *); +int npppd_reset_routing_table(npppd *, int); +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/npppd/npppd_auth.c b/usr.sbin/npppd/npppd/npppd_auth.c new file mode 100644 index 00000000000..a0a80e1d71d --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_auth.c @@ -0,0 +1,945 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file 認証レルム */ +/* $Id: npppd_auth.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/* なるべく npppd に非依存で書いていきたいところ。*/ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <time.h> +#include <event.h> +#include <stdarg.h> +#include <stdlib.h> +#include <netdb.h> +#include <errno.h> + +#include "debugutil.h" +#include "hash.h" +#include "slist.h" +#include "npppd_local.h" +#include "npppd_auth.h" +#include "config_helper.h" +#include "net_utils.h" +#include "csvreader.h" + +#include "npppd_auth_local.h" + +/** + * Create a npppd_auth_base object. + * @param auth_type the authentication type. + * specify {@link ::NPPPD_AUTH_TYPE_LOCAL} to authenticate by the local + * file, or specify {@link ::NPPPD_AUTH_TYPE_RADIUS} for RADIUS + * authentication. + * @param label the configuration label + * @param _npppd the parent {@link ::npppd} object + * @see ::NPPPD_AUTH_TYPE_LOCAL + * @see ::NPPPD_AUTH_TYPE_RADIUS + * @return The pointer to the {@link ::npppd_auth_base} object will be returned + * in case success otherwise NULL wiil be returned. + */ +npppd_auth_base * +npppd_auth_create(int auth_type, const char *label, void *_npppd) +{ + npppd_auth_base *base; + + NPPPD_AUTH_ASSERT(label != NULL); + + switch (auth_type) { + case NPPPD_AUTH_TYPE_LOCAL: + if ((base = malloc(sizeof(npppd_auth_local))) != NULL) { + memset(base, 0, sizeof(npppd_auth_local)); + base->type = NPPPD_AUTH_TYPE_LOCAL; + base->users_hash = NULL; + base->strip_nt_domain = 1; + base->strip_atmark_realm = 0; + strlcpy(base->label, label, sizeof(base->label)); + base->npppd = _npppd; + + return base; + } + break; + +#ifdef USE_NPPPD_RADIUS + case NPPPD_AUTH_TYPE_RADIUS: + if ((base = malloc(sizeof(npppd_auth_radius))) != NULL) { + memset(base, 0, sizeof(npppd_auth_radius)); + base->type = NPPPD_AUTH_TYPE_RADIUS; + base->strip_nt_domain = 0; + strlcpy(base->label, label, sizeof(base->label)); + base->npppd = _npppd; + + return base; + } + break; +#endif + + default: + NPPPD_AUTH_ASSERT(0); + break; + } + + return NULL; +} + +/** + * Call this function to make the object unusable. + * <p> + * {@link ::npppd_auth_base} objects is refered by the {@link ::npppd_ppp} + * object. After this funcation is called, npppd will disconnect the PPP + * links that refers the object, it will call {@link ::npppd_auth_destroy()} + * when all the references to the object are released.</p> + */ +void +npppd_auth_dispose(npppd_auth_base *base) +{ + + base->disposing = 1; + + if (base->users_hash != NULL) + hash_delete_all(base->users_hash, 1); + + return; +} + +/** Destroy the {@link ::npppd_auth_base} object. */ +void +npppd_auth_destroy(npppd_auth_base *base) +{ + + if (base->disposing == 0) + npppd_auth_dispose(base); + + if (base->users_hash != NULL) { + hash_free(base->users_hash); + base->users_hash = NULL; + } + + npppd_auth_base_log(base, LOG_INFO, "Finalized"); + + switch(base->type) { + case NPPPD_AUTH_TYPE_LOCAL: + memset(base, 0, sizeof(npppd_auth_local)); + break; + + case NPPPD_AUTH_TYPE_RADIUS: + memset(base, 0, sizeof(npppd_auth_local)); + break; + } + free(base); + + return; +} + +/** Reload the configuration */ +int +npppd_auth_reload(npppd_auth_base *base) +{ + const char *val; + + val = npppd_auth_config_str(base, "name"); + if (val == NULL) + /* .name の定義がなければ、ラベルを使う */ + strlcpy(base->name, npppd_auth_default_label(base), + sizeof(base->name)); + else + strlcpy(base->name, val, sizeof(base->name)); + + if ((val = npppd_auth_config_str(base, "pppsuffix")) != NULL) + strlcpy(base->pppsuffix, val, sizeof(base->pppsuffix)); + else + base->pppsuffix[0] = '\0'; + + if ((val = npppd_auth_config_str(base, "pppprefix")) != NULL) + strlcpy(base->pppprefix, val, sizeof(base->pppprefix)); + else + base->pppprefix[0] = '\0'; + + base->eap_capable = + npppd_auth_config_str_equal(base, "eap_capable", "true", 1); + base->strip_nt_domain = + npppd_auth_config_str_equal(base, "strip_nt_domain", "true", 1); + base->strip_atmark_realm = + npppd_auth_config_str_equal(base, "strip_atmark_realm", "true", 0); + + base->has_acctlist = 0; + base->acctlist_ready = 0; + base->radius_ready = 0; + if ((val = npppd_auth_config_str(base, "acctlist")) != NULL) { + strlcpy(base->acctlist_path, val, sizeof(base->acctlist_path)); + if (base->users_hash == NULL) { + if ((base->users_hash = hash_create( + (int (*)(const void *, const void *))strcmp, + str_hash, 1021)) == NULL) { + npppd_auth_base_log(base, + LOG_WARNING, "hash_create() failed: %m."); + goto reigai; + } + } + base->reloadable = NPPPD_DEFAULT_AUTH_LOCAL_RELOADABLE; + base->has_acctlist = 1; + if (npppd_auth_reload_acctlist(base) != 0) + goto reigai; + + } else { + if (base->type == NPPPD_AUTH_TYPE_LOCAL) { + npppd_auth_base_log(base, + LOG_WARNING, "missing acctlist property."); + goto reigai; + } + } + + switch (base->type) { +#ifdef USE_NPPPD_RADIUS + case NPPPD_AUTH_TYPE_RADIUS: + if (npppd_auth_radius_reload(base) != 0) + goto reigai; + break; +#endif + } + base->initialized = 1; + + return 0; + +reigai: + base->initialized = 0; + base->has_acctlist = 0; + base->acctlist_ready = 0; + base->radius_ready = 0; + + return 1; +} + +/** + * ユーザのパスワードを取得します。成功すると 0 が返ります。 + * + * @param username パスワードを取得するユーザの名前 + * @param password パスワードを格納する領域。 + * パスワードの長さだけを知りたい場合には NULL + * を指定します。 + * @param lppassword パスワードを格納する領域の長さのポインタ + * @return User unknown の場合は 1、パスワードバッファの長さが足りない場合 + * は 2、その他のエラーは負の数が返ります。 + */ +int +npppd_auth_get_user_password(npppd_auth_base *base, + const char *username, char *password, int *plpassword) +{ + int sz, lpassword; + npppd_auth_user *user; + + NPPPD_AUTH_ASSERT(base != NULL); + NPPPD_AUTH_DBG((base, LOG_DEBUG, "%s(%s)", __func__, username)); + + if (base->has_acctlist == 0 || base->acctlist_ready == 0) + return -1; + + if (base->reloadable != 0) + npppd_auth_reload_acctlist(base); + + if ((user = npppd_auth_find_user(base, username)) == NULL) + return 1; +#ifdef IDGW + /* パスワードを * で潰してあるユーザは、パスワードは使わない */ + if (strcmp(user->password, "*") == 0) + return 1; +#endif + + if (password == NULL && plpassword == NULL) + return 0; + if (plpassword == NULL) + return -1; + lpassword = strlen(user->password) + 1; + sz = *plpassword; + *plpassword = lpassword; + if (password == NULL) + return 0; + if (sz < lpassword) + return 2; + + strlcpy(password, user->password, sz); + return 0; +} + +/** + * ユーザの Framed-IP-{Address,Netmask} を取得します。成功すると 0 が返ります。 + * <p> + * 認証データベースは随時更新されるため、認証直後にこの関数を呼び出さないと、 + * パスワードと世代がズレる可能性があります。したがって認証直後にこの関数 + * を呼び出します。</p> + * @param username パスワードを取得するユーザの名前 + * @param ip4address Framed-IP-Address を格納する struct in_addr + * @param ip4netmask Framed-IP-Netmask を格納する struct in_addr + * のポインタ + */ +int +npppd_auth_get_framed_ip(npppd_auth_base *base, const char *username, + struct in_addr *ip4address, struct in_addr *ip4netmask) +{ + npppd_auth_user *user; + + NPPPD_AUTH_ASSERT(base != NULL); + NPPPD_AUTH_DBG((base, LOG_DEBUG, "%s(%s)", __func__, username)); + if (base->has_acctlist == 0 || base->acctlist_ready == 0) + return -1; + + if ((user = npppd_auth_find_user(base, username)) == NULL) + return 1; + + if (user->framed_ip_address.s_addr != 0) { + *ip4address = user->framed_ip_address; + if (ip4netmask != NULL) + *ip4netmask = user->framed_ip_netmask; + + return 0; + } + + return 1; +} + +/** + * Retribute "Calling-Number" attribute of the user from the realm. + * + * @param username Username. + * @param number Pointer to the space for the Calling-Number. This + * can be NULL in case retributing the Calling-Number only. + * @param plnumber Pointer to the length of the space for the + * Calling-Number. + * @return 0 if the Calling-Number attribute is successfully retributed. + * 1 if the user has no Calling-Number attribute. return -1 if the realm + * doesn't have user attributes or other errors. return 2 if the space + * is not enough. + */ +int +npppd_auth_get_calling_number(npppd_auth_base *base, const char *username, + char *number, int *plnumber) +{ + int lcallnum, sz; + npppd_auth_user *user; + + if (base->has_acctlist == 0 || base->acctlist_ready == 0) + return -1; + + if ((user = npppd_auth_find_user(base, username)) == NULL) + return 1; + + if (number == NULL && plnumber == NULL) + return 0; + if (plnumber == NULL) + return -1; + lcallnum = strlen(user->calling_number) + 1; + sz = *plnumber; + *plnumber = lcallnum; + if (sz < lcallnum) + return 2; + strlcpy(number, user->calling_number, sz); + + return 0; +} + +int +npppd_auth_get_type(npppd_auth_base *base) +{ + return base->type; +} + +int +npppd_auth_is_usable(npppd_auth_base *base) +{ + return (base->initialized != 0 && base->disposing == 0)? 1 : 0; +} + +int +npppd_auth_is_ready(npppd_auth_base *base) +{ + if (!npppd_auth_is_usable(base)) + return 0; + + switch(base->type) { + case NPPPD_AUTH_TYPE_LOCAL: + return (base->acctlist_ready != 0)? 1 : 0; + /* NOTREACHED */ + + case NPPPD_AUTH_TYPE_RADIUS: + return (base->acctlist_ready != 0 || + base->radius_ready != 0)? 1 : 0; + /* NOTREACHED */ + } + NPPPD_AUTH_ASSERT(0); + + return 0; +} + +int +npppd_auth_is_disposing(npppd_auth_base *base) +{ + return (base->disposing != 0)? 1 : 0; +} + +int +npppd_auth_is_eap_capable(npppd_auth_base *base) +{ + return (base->eap_capable != 0)? 1 : 0; +} + +const char * +npppd_auth_get_label(npppd_auth_base *base) +{ + return base->label; +} + +const char * +npppd_auth_get_name(npppd_auth_base *base) +{ + return base->name; +} + +const char * +npppd_auth_get_suffix(npppd_auth_base *base) +{ + return base->pppsuffix; +} + +const char * +npppd_auth_username_for_auth(npppd_auth_base *base, const char *username, + char *username_buffer) +{ + const char *u0; + char *atmark, *u1; + + u0 = NULL; + if (base->strip_nt_domain != 0) { + if ((u0 = strchr(username, '\\')) != NULL) + u0++; + } + if (u0 == NULL) + u0 = username; + u1 = username_buffer; + if (username_buffer != u0) + memmove(username_buffer, u0, MIN(strlen(u0) + 1, + MAX_USERNAME_LENGTH)); + if (base->strip_atmark_realm != 0) { + if ((atmark = strrchr(u1, '@')) != NULL) + *atmark = '\0'; + } + + return username_buffer; +} + +/*********************************************************************** + * Account list related functions + ***********************************************************************/ +/** Reload the account list */ +static int +npppd_auth_reload_acctlist(npppd_auth_base *base) +{ + CSVREADER_STATUS status; + int linno, ncols, usersz, nuser, eof, off; + const char **cols, *passwd, *callnum; + char line[8192]; + csvreader *csv; + npppd_auth_user *user; + struct in_addr ip4, ip4mask; + slist users; + FILE *file; + struct stat st; + + if (base->acctlist_ready != 0 && lstat(base->acctlist_path, &st) == 0) { + if (st.st_mtime == base->last_load) + return 0; + base->last_load = st.st_mtime; + } + + slist_init(&users); + csv = NULL; + if ((file = fopen(base->acctlist_path, "r")) == NULL) { + /* ファイルが存在しない場合は、空とする */ + if (errno == ENOENT) + hash_delete_all(base->users_hash, 1); +#ifdef _SEIL_EXT_ + if (errno == ENOENT) + npppd_auth_base_log(base, LOG_NOTICE, + "Realm is not ready"); + else + npppd_auth_base_log(base, LOG_ERR, + "Open %s failed: %m", base->acctlist_path); + base->acctlist_ready = 0; + return 0; +#else + npppd_auth_base_log(base, + (errno == ENOENT)? LOG_DEBUG : LOG_ERR, + "Open %s failed: %m", base->acctlist_path); + return 0; +#endif + } + if ((csv = csvreader_create()) == NULL) { + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: csvreader_create(): %m"); + goto reigai; + } + + for (linno = 0, eof = 0; !eof;) { + ip4.s_addr = 0; + ip4mask.s_addr = 0xffffffffL; + if (fgets(line, sizeof(line), file) != NULL) { + int linelen; + + linelen = strlen(line); + if (linelen <= 0) { + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: lineno=%d " + "line too short", linno + 1); + goto reigai; + } + if (line[linelen - 1] != '\n' && !feof(file)) { + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: lineno=%d " + "line too long", linno + 1); + goto reigai; + } + + status = csvreader_parse(csv, line); + } else { + if (!feof(file)) { + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: %m"); + goto reigai; + } + status = csvreader_parse_flush(csv); + eof = 1; + } + if (status != CSVREADER_NO_ERROR) { + if (status == CSVREADER_OUT_OF_MEMORY) + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: %m"); + else + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list " + "failed: lineno=%d parse error", linno); + goto reigai; + } + ncols = csvreader_get_number_of_column(csv); + if ((cols = csvreader_get_column(csv)) == NULL) + continue; + linno++; /* CSV としての行番号としたいのでココでカウント */ + if (linno == 1) { + /* skip a title line */ + continue; + } + if (ncols < 1) { + npppd_auth_base_log(base, LOG_ERR, + "account list lineno=%d has only %d fields.", + linno, ncols); + continue; + } + if (strlen(cols[0]) <= 0) + continue; /* skip if the user-name is empty */ + if (ncols >= 3) { + if (*cols[2] != '\0' && inet_aton(cols[2], &ip4) != 1) { + npppd_auth_base_log(base, LOG_ERR, + "account list lineno=%d parse error: " + "invalid 'Framed-IP-Address' field: %s", + linno, cols[2]); + continue; + } + } + if (ncols >= 4) { + if ((*cols[3] != '\0' && + inet_aton(cols[3], &ip4mask) != 1) || + netmask2prefixlen(htonl(ip4mask.s_addr)) < 0) { + npppd_auth_base_log(base, LOG_ERR, + "account list lineno=%d parse error: " + "invalid 'Framed-IP-Netmask' field: %s", + linno, cols[3]); + continue; + } + } + + passwd = ""; + if (cols[1] != NULL) + passwd = cols[1]; + callnum = ""; + if (ncols >= 6 && cols[5] != NULL) + callnum = cols[5]; + + usersz = sizeof(npppd_auth_user); + usersz += strlen(cols[0]) + 1; + usersz += strlen(passwd) + 1; + usersz += strlen(callnum) + 1; + if ((user = malloc(usersz)) == NULL) { + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: %m"); + goto reigai; + } + memset(user, 0, usersz); + + off = 0; + + user->username = user->space + off; + off += strlcpy(user->username, cols[0], usersz - off); + ++off; + + user->password = user->space + off; + off += strlcpy(user->password, passwd, usersz - off); + ++off; + + user->calling_number = user->space + off; + strlcpy(user->calling_number, callnum, usersz - off); + + user->framed_ip_address = ip4; + user->framed_ip_netmask = ip4mask; + + slist_add(&users, user); + } + hash_delete_all(base->users_hash, 1); + + nuser = 0; + for (slist_itr_first(&users); slist_itr_has_next(&users);) { + user = slist_itr_next(&users); + if (hash_lookup(base->users_hash, user->username) != NULL) { + npppd_auth_base_log(base, LOG_WARNING, + "Record for user='%s' is redefined, the first " + "record will be used.", user->username); + free(user); + goto next_user; + } + if (hash_insert(base->users_hash, user->username, user) != 0) { + npppd_auth_base_log(base, LOG_ERR, + "Loading a account list failed: hash_insert(): %m"); + goto reigai; + } + nuser++; +next_user: + slist_itr_remove(&users); + } + slist_fini(&users); + csvreader_destroy(csv); + + fclose(file); + npppd_auth_base_log(base, LOG_INFO, + "Loaded users from='%s' successfully. %d users", + base->acctlist_path, nuser); + base->acctlist_ready = 1; + + return 0; +reigai: + fclose(file); + if (csv != NULL) + csvreader_destroy(csv); + hash_delete_all(base->users_hash, 1); + for (slist_itr_first(&users); slist_itr_has_next(&users);) { + user = slist_itr_next(&users); + free(user); + } + slist_fini(&users); + + return 1; +} + +static npppd_auth_user * +npppd_auth_find_user(npppd_auth_base *base, const char *username) +{ + int lsuffix, lusername; + const char *un; + char buf[MAX_USERNAME_LENGTH]; + hash_link *hl; + + un = username; + lsuffix = strlen(base->pppsuffix); + if (lsuffix > 0) { + /* Strip the suffix */ + lusername = strlen(username); + NPPPD_AUTH_ASSERT(lusername + 1 < sizeof(buf)); + if (lusername + 1 >= sizeof(buf)) + return NULL; + memcpy(buf, username, lusername - lsuffix); + buf[lusername - lsuffix] = '\0'; + un = buf; + } + + if ((hl = hash_lookup(base->users_hash, un)) == NULL) + return NULL; + + return hl->item; +} + +#ifdef USE_NPPPD_RADIUS +/*********************************************************************** + * RADIUS + ***********************************************************************/ + +static int +radius_server_address_load(radius_req_setting *radius, int idx, + const char *address) +{ + struct addrinfo *ai; + + memset(&radius->server[idx], 0, sizeof(radius->server[0])); + + if (addrport_parse(address, IPPROTO_TCP, &ai) !=0) + return 1; + + switch (ai->ai_family) { + default: + freeaddrinfo(ai); + return 1; + case AF_INET: + case AF_INET6: + break; + } + if (sin46_port(ai->ai_addr) == 0) + sin46_port(ai->ai_addr) = htons(DEFAULT_RADIUS_AUTH_PORT); + memcpy(&radius->server[idx].peer, ai->ai_addr, + MIN(sizeof(radius->server[idx].peer), ai->ai_addrlen)); + + freeaddrinfo(ai); + radius->server[idx].enabled = 1; + + return 0; +} + +/** RADIUS認証レルムの設定を読み込みます */ +static int +npppd_auth_radius_reload(npppd_auth_base *base) +{ + npppd_auth_radius *_this = (npppd_auth_radius *)base; + int i, n; + const char *val; + char *tok, *buf0, buf[NPPPD_CONFIG_BUFSIZ], logbuf[BUFSIZ]; + char label[256]; + +#define VAL_SEP " \t\r\n" + n = 0; + _this->rad_setting.timeout = + npppd_auth_config_int(base, "timeout", DEFAULT_RADIUS_AUTH_TIMEOUT); + _this->rad_setting.curr_server = 0; + + if ((val = npppd_auth_config_str(base, "server_list")) != NULL) { + strlcpy(buf, val, sizeof(buf)); + buf0 = buf; + while ((tok = strsep(&buf0, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + snprintf(label, sizeof(label), "server.%s.address",tok); + if ((val = npppd_auth_config_str(base, label)) == NULL) + goto reigai; + if (radius_server_address_load(&_this->rad_setting, n, + val) != 0) { + npppd_auth_base_log(base, LOG_INFO, + "parse error at %s", label); + goto reigai; + } + snprintf(label, sizeof(label), "server.%s.secret", + tok); + if ((val = npppd_auth_config_str(base, label)) != NULL) + strlcpy(_this->rad_setting.server[n].secret, + val, sizeof(_this->rad_setting + .server[n].secret)); + else + _this->rad_setting.server[n].secret[0] = '\0'; + if (n != 0) + strlcat(logbuf, " ", sizeof(logbuf)); + n++; + } + } else if ((val = npppd_auth_config_str(base, "server.address")) + != NULL) { + if (radius_server_address_load(&_this->rad_setting, n, val) + != 0) { + npppd_auth_base_log(base, LOG_INFO, + "parse error at %s", label); + goto reigai; + } + if ((val = npppd_auth_config_str(base, "server.secret"))!= NULL) + strlcpy(_this->rad_setting.server[n].secret, val, + sizeof(_this->rad_setting.server[n].secret)); + else + _this->rad_setting.server[n].secret[0] = '\0'; + n++; + } + for (i = n; i < countof(_this->rad_setting.server); i++) { + memset(&_this->rad_setting.server[i], 0, + sizeof(_this->rad_setting.server[0])); + } + for (i = 0; i < countof(_this->rad_setting.server); i++) { + if (_this->rad_setting.server[i].enabled) + base->radius_ready = 1; + } + + npppd_auth_base_log(base, LOG_INFO, + "Loaded configuration timeout=%d nserver=%d", + _this->rad_setting.timeout, n); + + return 0; +reigai: + npppd_auth_destroy(base); + + return 1; +} + +/** + * Get {@link ::radius_req_setting} of specified {@link ::npppd_auth_base} + * object. + */ +void * +npppd_auth_radius_get_radius_req_setting(npppd_auth_radius *_this) +{ + return &_this->rad_setting; +} + +/** RADIUS サーバが問い合わせに失敗したことを通知します。*/ +void +npppd_auth_radius_server_failure_notify(npppd_auth_radius *_this, + struct sockaddr *server, const char *reason) +{ + int i, n; + radius_req_setting *rad_setting; + char buf0[BUFSIZ]; + + NPPPD_AUTH_ASSERT(_this != NULL); + NPPPD_AUTH_ASSERT(server != NULL); + + if (reason == NULL) + reason = "failure"; + + rad_setting = &_this->rad_setting; + if (memcmp(&rad_setting->server[rad_setting->curr_server].peer, + server, server->sa_len) == 0) { + /* + * 失敗した Radius は現在カレントなので、次の Radius + * に切替える。 + */ + for (i = 1; i < countof(rad_setting->server); i++) { + n = (rad_setting->curr_server + i) % + countof(rad_setting->server); + if (rad_setting->server[n].enabled == 0) + continue; + rad_setting->curr_server = n; + break; + } + } + + npppd_auth_base_log(&_this->nar_base, LOG_NOTICE, + "server=%s request failure: %s", + addrport_tostring(server, server->sa_len, buf0, sizeof(buf0)), + reason); +} +#endif + +/*********************************************************************** + * Helper functions + ***********************************************************************/ +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static int +npppd_auth_base_log(npppd_auth_base *_this, int prio, const char *fmt, ...) +{ + int status; + char logbuf[BUFSIZ]; + va_list ap; + + NPPPD_AUTH_ASSERT(_this != NULL); + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "realm name=%s(%s) %s", + _this->name, (_this->label[0] == '\0')? "default" : _this->label, + fmt); + status = vlog_printf(prio, logbuf, ap); + va_end(ap); + + return status; +} + +static uint32_t +str_hash(const void *ptr, int sz) +{ + u_int32_t hash = 0; + int i, len; + const char *str; + + str = ptr; + len = strlen(str); + for (i = 0; i < len; i++) + hash = hash*0x1F + str[i]; + hash = (hash << 16) ^ (hash & 0xffff); + + return hash % sz; +} + +static const char * +npppd_auth_default_label(npppd_auth_base *base) +{ + switch(base->type) { + case NPPPD_AUTH_TYPE_LOCAL: + return "local"; + case NPPPD_AUTH_TYPE_RADIUS: + return "radius"; + } + NPPPD_AUTH_ASSERT(0); + + return NULL; +} + +static inline const char * +npppd_auth_config_prefix(npppd_auth_base *base) +{ + switch(base->type) { + case NPPPD_AUTH_TYPE_LOCAL: + return "auth.local.realm"; + + case NPPPD_AUTH_TYPE_RADIUS: + return "auth.radius.realm"; + + } + NPPPD_AUTH_ASSERT(0); + + return NULL; +} + +static const char * +npppd_auth_config_str(npppd_auth_base *base, const char *confKey) +{ + return config_named_prefix_str(((npppd *)base->npppd)->properties, + npppd_auth_config_prefix(base), base->label, confKey); +} + +static int +npppd_auth_config_int(npppd_auth_base *base, const char *confKey, int defVal) +{ + return config_named_prefix_int(((npppd *)base->npppd)->properties, + npppd_auth_config_prefix(base), base->label, confKey, defVal); +} + +static int +npppd_auth_config_str_equal(npppd_auth_base *base, const char *confKey, + const char *confVal, int defVal) +{ + return config_named_prefix_str_equal(((npppd *)base->npppd)->properties, + npppd_auth_config_prefix(base), base->label, confKey, confVal, + defVal); +} diff --git a/usr.sbin/npppd/npppd/npppd_auth.h b/usr.sbin/npppd/npppd/npppd_auth.h new file mode 100644 index 00000000000..1a1db6935b0 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_auth.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_AUTH_H +#define NPPPD_AUTH_H 1 + +/** ローカル認証レルム */ +#define NPPPD_AUTH_TYPE_LOCAL 1 +/** RADIUS認証レルム */ +#define NPPPD_AUTH_TYPE_RADIUS 2 + +struct _npppd_auth_base; +struct _npppd_auth_radius; +struct _npppd_auth_local; + +/** ローカル認証レルムの既定の型 */ +typedef struct _npppd_auth_base npppd_auth_base; + +/** RADIUS認証レルムの型 */ +typedef struct _npppd_auth_radius npppd_auth_radius; +typedef struct _npppd_auth_local npppd_auth_local; + + +#ifdef __cplusplus +extern "C" { +#endif + +npppd_auth_base *npppd_auth_create (int, const char *, void *); +void npppd_auth_dispose (npppd_auth_base *); +void npppd_auth_destroy (npppd_auth_base *); +int npppd_auth_reload (npppd_auth_base *); +int npppd_auth_get_user_password (npppd_auth_base *, const char *, char *, int *); +int npppd_auth_get_framed_ip (npppd_auth_base *, const char *, struct in_addr *, struct in_addr *); +int npppd_auth_get_calling_number (npppd_auth_base *, const char *, char *, int *); +int npppd_auth_get_type (npppd_auth_base *); +int npppd_auth_is_usable (npppd_auth_base *); +int npppd_auth_is_ready (npppd_auth_base *); +int npppd_auth_is_disposing (npppd_auth_base *); +int npppd_auth_is_eap_capable (npppd_auth_base *); +const char *npppd_auth_get_label (npppd_auth_base *); +const char *npppd_auth_get_name (npppd_auth_base *); +const char *npppd_auth_get_suffix (npppd_auth_base *); +const char *npppd_auth_username_for_auth (npppd_auth_base *, const char *, char *); +void *npppd_auth_radius_get_radius_req_setting (npppd_auth_radius *); +void npppd_auth_radius_server_failure_notify (npppd_auth_radius *, struct sockaddr *, const char *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/npppd/npppd_auth_local.h b/usr.sbin/npppd/npppd/npppd_auth_local.h new file mode 100644 index 00000000000..5fad21ea7c3 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_auth_local.h @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +struct _npppd_auth_base { + /** ラベル名 */ + char label[NPPPD_GENERIC_NAME_LEN]; + /** レルム名 */ + char name[NPPPD_GENERIC_NAME_LEN]; + /** 親 npppd の参照 */ + void *npppd; + /** 認証レルムのタイプ */ + int type; + /** PPP サフィックス */ + char pppsuffix[64]; + /** PPP プレフィックス */ + char pppprefix[64]; + uint32_t + /** 初期化済み */ + initialized:1, + /** 再読み込み可能 */ + reloadable:1, + /** 廃棄中 */ + disposing:1, + /** Is the account list ready */ + acctlist_ready:1, + /** Is the radius configuration ready */ + radius_ready:1, + /** EAP が利用できるかどうか */ + eap_capable:1, + /** Windows-NT ドメインを強制的に strip するかどうか */ + strip_nt_domain:1, + /** PPPユーザ名の @ 以降を強制的に strip するかどうか。*/ + strip_atmark_realm:1, + /** has account-list */ + has_acctlist:1, + reserved:24; + + /** ユーザ名 => npppd_auth_user hash */ + hash_table *users_hash; + /** アカウントリストのパス名 */ + char acctlist_path[64]; + /** 最終ロード時間 */ + time_t last_load; +}; + +#ifdef USE_NPPPD_RADIUS +struct _npppd_auth_radius { + /** 親 npppd_auth_base */ + npppd_auth_base nar_base; + + /** 現在の利用中のサーバ */ + int curr_server; + + /** RADIUSサーバ */ + radius_req_setting rad_setting; + +}; +#endif + +/** ローカル認証レルムの型 */ +struct _npppd_auth_local { + /* 親 npppd_auth_base */ + npppd_auth_base nal_base; +}; + +/** ユーザのアカウント情報を示す型 */ +typedef struct _npppd_auth_user { + /** ユーザ名 */ + char *username; + /** パスワード */ + char *password; + /** Framed-IP-Address */ + struct in_addr framed_ip_address; + /** Framed-IP-Netmask */ + struct in_addr framed_ip_netmask; + /** Calling-Number */ + char *calling_number; + /** スペース確保用フィールド */ + char space[0]; +} npppd_auth_user; + +static int npppd_auth_reload_acctlist (npppd_auth_base *); +static npppd_auth_user *npppd_auth_find_user (npppd_auth_base *, const char *); +static int radius_server_address_load (radius_req_setting *, int, const char *); +static int npppd_auth_radius_reload (npppd_auth_base *); +static int npppd_auth_base_log (npppd_auth_base *, int, const char *, ...); +static uint32_t str_hash (const void *, int); +static const char * npppd_auth_default_label(npppd_auth_base *); +static inline const char *npppd_auth_config_prefix (npppd_auth_base *); +static const char *npppd_auth_config_str (npppd_auth_base *, const char *); +static int npppd_auth_config_int (npppd_auth_base *, const char *, int); +static int npppd_auth_config_str_equal (npppd_auth_base *, const char *, const char *, int); + +#ifdef NPPPD_AUTH_DEBUG +#define NPPPD_AUTH_DBG(x) npppd_auth_base_log x +#define NPPPD_AUTH_ASSERT(x) ASSERT(x) +#else +#define NPPPD_AUTH_DBG(x) +#define NPPPD_AUTH_ASSERT(x) +#endif diff --git a/usr.sbin/npppd/npppd/npppd_config.c b/usr.sbin/npppd/npppd/npppd_config.c new file mode 100644 index 00000000000..74bc2372c38 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_config.c @@ -0,0 +1,794 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: npppd_config.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/*@file + * npppd 設定関連を操作する関数を格納するファイル。 + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <net/route.h> +#include <arpa/inet.h> +#include <syslog.h> +#include <time.h> +#include <event.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <errno.h> + +#include "addr_range.h" +#include "debugutil.h" +#include "npppd_subr.h" +#include "npppd_local.h" +#include "npppd_ctl.h" +#include "npppd_auth.h" +#include "npppd_iface.h" +#include "config_helper.h" +#include "radish.h" + +#include "pathnames.h" + +#ifdef NPPPD_CONFIG_DEBUG +#define NPPPD_CONFIG_DBG(x) log_printf x +#define NPPPD_CONFIG_ASSERT(x) ASSERT(x) +#else +#define NPPPD_CONFIG_DBG(x) +#define NPPPD_CONFIG_ASSERT(x) +#endif + + +#define CFG_KEY(p, s) config_key_prefix((p), (s)) +#define VAL_SEP " \t\r\n" + +static void npppd_ipcp_config_load (npppd *); +static void npppd_ipcp_config_load0 (npppd_ipcp_config *, const char *); +#ifdef USE_NPPPD_NPPPD_CTL +static int npppd_ctl_reload (npppd *, npppd_ctl *); +#endif +static void npppd_debug_log_reload (npppd *); +static int npppd_ip_addr_pool_load(npppd *); +static int npppd_auth_realm_reload (npppd *); +static void npppd_iface_binding_reload(npppd *, npppd_iface *, npppd_iface_binding *); +static int realm_list_contains (slist *, const char *); +static npppd_auth_base *realm_list_remove (slist *, const char *); + +CONFIG_FUNCTIONS(npppd_config, npppd, properties); +PREFIXED_CONFIG_FUNCTIONS(ppp_config, npppd_ppp, pppd->properties, phy_label); + +NAMED_PREFIX_CONFIG_DECL(npppd_ipcp_config, npppd_ipcp_config, + npppd->properties, "ipcp", label); +NAMED_PREFIX_CONFIG_FUNCTIONS(npppd_ipcp_config, npppd_ipcp_config, + npppd->properties, "ipcp", label); + +/*********************************************************************** + * 設定読み込み。各部の読み込みをまとめた export 関数 + ***********************************************************************/ +/** + * 設定ファイルを再読み込みします。 + * @param _this npppd へのポインタ。 + * @returns 成功時は 0 を返します。設定にエラーがあった場合などには、 + * 0 以外を返します。 + */ +int +npppd_reload_config(npppd *_this) +{ + int retval = -1; + FILE *conffp = NULL; + struct properties *proptmp = NULL; + + if ((conffp = fopen(_this->config_file, "r")) == NULL) { + log_printf(LOG_ERR, "Load configuration from='%s' failed: %m", + _this->config_file); + retval = -1; + goto reigai; + } + if ((proptmp = properties_create(1061)) == NULL) { + log_printf(LOG_ERR, "Load configuration from='%s' failed: %m", + _this->config_file); + retval = -1; + goto reigai; + } + if (properties_load(proptmp, conffp) != 0) { + log_printf(LOG_ERR, "Load configuration from='%s' failed: %m", + _this->config_file); + retval = -1; + goto reigai; + } + + if (_this->properties != NULL) { + /* 入れ換え */ + properties_remove_all(_this->properties); + properties_put_all(_this->properties, proptmp); + properties_destroy(proptmp); + } else + _this->properties = proptmp; + proptmp = NULL; + + /* ログなので処理順としてここ。 */ + npppd_debug_log_reload(_this); + +#ifndef NO_DELAYED_RELOAD + _this->delayed_reload = npppd_config_int(_this, "delayed_reload", 0); + if (_this->delayed_reload < 0) { + log_printf(LOG_WARNING, "Parse error at 'delayed_reload'"); + _this->delayed_reload = 0; + } +#endif + + _this->max_session = npppd_config_int(_this, "max_session", + NPPPD_DEFAULT_MAX_PPP); + + retval = 0; + log_printf(LOG_NOTICE, "Load configuration from='%s' successfully.", + _this->config_file); + + // FALL THROUGH +reigai: + if (conffp != NULL) + fclose(conffp); + if (proptmp != NULL) + properties_destroy(proptmp); + + return retval; +} + +/** モジュール毎の設定の再読み込み。 */ +int +npppd_modules_reload(npppd *_this) +{ + int i, rval; + + rval = 0; + /* アドレスプール */ + if (npppd_ip_addr_pool_load(_this) != 0) + return -1; + + npppd_ipcp_config_load(_this); + + npppd_auth_realm_reload(_this); +#ifdef USE_NPPPD_NPPPD_CTL + npppd_ctl_reload(_this, &_this->ctl); +#endif +#ifdef USE_NPPPD_L2TP + rval |= l2tpd_reload(&_this->l2tpd, _this->properties, "l2tpd", 1); +#endif +#ifdef USE_NPPPD_PPTP + rval |= pptpd_reload(&_this->pptpd, _this->properties, "pptpd", + 1); +#ifdef IDGW_SSLDIP + rval |= pptpd_reload(&_this->ssldipd, _this->properties, "ssldipd", 0); +#endif +#endif +#ifdef USE_NPPPD_PPPOE + rval |= pppoed_reload(&_this->pppoed, _this->properties, "pppoed", 0); +#endif + for (i = 0; i < countof(_this->iface_bind); i++) + npppd_iface_binding_reload(_this, &_this->iface[i], + &_this->iface_bind[i]); + + return rval; +} + +/*********************************************************************** + * 設定読み込み。各部 + ***********************************************************************/ +#ifdef USE_NPPPD_NPPPD_CTL +/** npppd制御機能の設定を読み込んで、必要なら起動します。*/ +static int +npppd_ctl_reload(npppd *_npppd, npppd_ctl *_this) +{ + int enabled; + const char *val; + + enabled = npppd_config_str_equal(_npppd, "ctl.enabled", "true", 1); + if (_this->enabled) + npppd_ctl_stop(_this); + + if (enabled) { + val = npppd_config_str(_npppd, "ctl.path"); + npppd_ctl_init(_this, _npppd, (val != NULL)? + val : DEFAULT_NPPPD_CTL_SOCK_PATH); + _this->enabled = enabled; + _this->max_msgsz = npppd_config_int(_npppd, "ctl.max_msgsz", + DEFAULT_NPPPD_CTL_MAX_MSGSZ); + return npppd_ctl_start(_this); + } + + return 0; +} +#endif + +static void +npppd_ipcp_config_load(npppd *_this) +{ + int n; + const char *val; + char *tok, *buf0, buf[NPPPD_CONFIG_BUFSIZ]; + + for (n = 0; n < countof(_this->ipcp_config); n++) + memset(&_this->ipcp_config[n], 0, sizeof(npppd_ipcp_config)); + n = 0; + if ((val = npppd_config_str(_this, "ipcp_list")) != NULL) { + strlcpy(buf, val, sizeof(buf)); + buf0 = buf; + while ((tok = strsep(&buf0, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + if (n >= countof(_this->ipcp_config)) { + log_printf(LOG_WARNING, + "number of the ipcp configration reached " + "limit=%d", + (int)countof(_this->ipcp_config)); + break; + } + _this->ipcp_config[n].npppd = _this; + npppd_ipcp_config_load0(&_this->ipcp_config[n], tok); + n++; + } + } else { + _this->ipcp_config[n].npppd = _this; + npppd_ipcp_config_load0(&_this->ipcp_config[n++], NULL); + } +} + +/** IPCP設定を読み込みます。 */ +static void +npppd_ipcp_config_load0(npppd_ipcp_config *_this, const char *label) +{ + uint32_t ip_assign_flags; + const char *val; + + if (label != NULL) + strlcpy(_this->label, label, sizeof(_this->label)); + else + memset(_this->label, 0, sizeof(_this->label)); + + _this->initialized = 1; + + val = npppd_ipcp_config_str(_this, "name"); + if (val == NULL) { + if (_this->label[0] == '\0') + val = "default"; + else + val = _this->label; + } + strlcpy(_this->name, val, sizeof(_this->name)); + + /* IPアドレス割り当てポリシー */ + ip_assign_flags = 0; + if (npppd_ipcp_config_str_equal(_this, "assign_userselect", "true", 1)) + ip_assign_flags |= NPPPD_IP_ASSIGN_USER_SELECT; + else + ip_assign_flags &= ~NPPPD_IP_ASSIGN_USER_SELECT; + + if (npppd_ipcp_config_str_equal(_this, "assign_fixed", "true", 1)) + ip_assign_flags |= NPPPD_IP_ASSIGN_FIXED; + else + ip_assign_flags &= ~NPPPD_IP_ASSIGN_FIXED; + + if (npppd_ipcp_config_str_equal(_this, "assign_radius", "true", 0)) + ip_assign_flags |= NPPPD_IP_ASSIGN_RADIUS; + else + ip_assign_flags &= ~NPPPD_IP_ASSIGN_RADIUS; + _this->ip_assign_flags = ip_assign_flags; + +#define LOAD_IPADDR_SETTING(field, conf) \ + if ((val = npppd_ipcp_config_str(_this, conf)) == NULL || \ + strlen(val) <= 0) { \ + _this->field.s_addr = INADDR_NONE; \ + } else { \ + if (inet_aton(val, &_this->field) != 1) { \ + log_printf(LOG_ERR, "configuration error at " \ + conf ": parse error"); \ + } \ + _this->field = _this->field; \ + } + + _this->dns_use_tunnel_end = npppd_ipcp_config_str_equal(_this, + "dns_use_tunnel_end", "true", 0); + if (npppd_ipcp_config_str_equal(_this, "dns_use_resolver", + "true", 0)) { + if (load_resolv_conf(&_this->dns_pri, &_this->dns_sec) != 0) + log_printf(LOG_ERR, "loading resolv.conf failed: %m"); + } else { + LOAD_IPADDR_SETTING(dns_pri, "dns_primary"); + LOAD_IPADDR_SETTING(dns_sec, "dns_secondary"); + } + LOAD_IPADDR_SETTING(nbns_pri, "nbns_primary"); + LOAD_IPADDR_SETTING(nbns_sec, "nbns_secondary"); +#undef LOAD_IPADDR_SETTING +} + +/** デバッグとログファイルについての設定を再読込 */ +static void +npppd_debug_log_reload(npppd *_this) +{ + int ival, oval; + FILE *debugfp; + const char *sval; + + if ((ival = npppd_config_int(_this, "debug.level", debuglevel)) == + debuglevel) + return; + + // デバッグレベル変更 + oval = debuglevel; + debuglevel = ival; + log_printf(LOG_NOTICE, "Debug level is changed %d => %d", oval, ival); + + debugfp = debug_get_debugfp(); + if (debugfp != stderr) { + sval = npppd_config_str(_this, "debug.logpath"); + // フォアグランドモードではない + if (debugfp != NULL) + fclose(debugfp); + if (sval != NULL) { + if ((debugfp = fopen(sval, "a+")) == NULL) { + log_printf(LOG_ERR, + "Failed to open logfile %s: %m", sval); + } else { + log_printf(LOG_INFO, + "open logfile successfully %s", sval); + debug_set_debugfp(debugfp); + } + } + } +} + +/** IPアドレスプールの設定を読み込みます。 */ +static int +npppd_ip_addr_pool_load(npppd *_this) +{ + int n, i, j; + const char *val; + char *tok, *buf0, buf[NPPPD_CONFIG_BUFSIZ]; + npppd_pool pool0[NPPPD_MAX_POOL]; + struct radish_head *rd_curr, *rd_new; + + rd_curr = _this->rd; + rd_new = NULL; + + n = 0; + if (!rd_inithead((void *)&rd_new, 0x41, + sizeof(struct sockaddr_npppd), + offsetof(struct sockaddr_npppd, snp_addr), + sizeof(struct in_addr), sockaddr_npppd_match)) { + goto reigai; + } + _this->rd = rd_new; + + /* 設定ファイル読み込み */ + if ((val = npppd_config_str(_this, "pool_list")) != NULL) { + strlcpy(buf, val, sizeof(buf)); + buf0 = buf; + while ((tok = strsep(&buf0, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + if (n >= countof(_this->pool)) { + log_printf(LOG_WARNING, + "number of the pool reached " + "limit=%d",(int)countof(_this->pool)); + break; + } + if (npppd_pool_init(&pool0[n], _this, tok) != 0) { + log_printf(LOG_WARNING, "Failed to initilize " + "npppd_pool '%s': %m", tok); + goto reigai; + } + if (npppd_pool_reload(&pool0[n]) != 0) + goto reigai; + n++; + } + } else { + if (npppd_pool_init(&pool0[n], _this, "default") != 0) { + log_printf(LOG_WARNING, "Failed to initilize " + "npppd_pool 'default': %m"); + goto reigai; + } + if (npppd_pool_reload(&pool0[n++]) != 0) + goto reigai; + } + for (; n < countof(pool0); n++) + pool0[n].initialized = 0; + + _this->rd = rd_curr; // backup + if (npppd_set_radish(_this, rd_new) != 0) + goto reigai; + + for (i = 0; i < countof(_this->pool); i++) { + if (_this->pool[i].initialized != 0) + npppd_pool_uninit(&_this->pool[i]); + if (pool0[i].initialized == 0) + continue; + _this->pool[i] = pool0[i]; + /* 参照の差し替え */ + for (j = 0; j < _this->pool[i].addrs_size; j++) { + if (_this->pool[i].initialized == 0) + continue; + _this->pool[i].addrs[j].snp_data_ptr = &_this->pool[i]; + } + } + log_printf(LOG_INFO, "Loading pool config successfuly."); + + return 0; +reigai: + /* rollback */ + for (i = 0; i < n; i++) { + if (pool0[i].initialized != 0) + npppd_pool_uninit(&pool0[i]); + } + + if (rd_curr != NULL) + _this->rd = rd_curr; + + if (rd_new != NULL) { + rd_walktree(rd_new, + (int (*)(struct radish *, void *))rd_unlink, + rd_new->rdh_top); + free(rd_new); + } + log_printf(LOG_NOTICE, "Loading pool config failed"); + + return 1; +} + +/* 認証レルム */ +static int +npppd_auth_realm_reload(npppd *_this) +{ + int rval, ndef; + const char *val; + char buf[NPPPD_CONFIG_BUFSIZ * 2], *bufp, *tok; + slist realms0, nrealms; + npppd_auth_base *auth_base; + + rval = 0; + slist_init(&realms0); + slist_init(&nrealms); + + if (slist_add_all(&realms0, &_this->realms) != 0) { + log_printf(LOG_WARNING, "slist_add_all() failed in %s(): %m", + __func__); + goto reigai; + } + + ndef = 0; + /* ローカルレルムのラベルを取得 */ + if ((val = npppd_config_str(_this, "auth.local.realm_list")) != NULL) { + ndef++; + strlcpy(buf, val, sizeof(buf)); + bufp = buf; + while ((tok = strsep(&bufp, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + if (realm_list_contains(&nrealms, tok)) { + log_printf(LOG_WARNING, + "label '%s' for auth.*.realm_list is not " + "unique", tok); + goto reigai; + } + auth_base = realm_list_remove(&realms0, tok); + if (auth_base != NULL && + npppd_auth_get_type(auth_base) + != NPPPD_AUTH_TYPE_LOCAL) { + /* 同じラベル名で認証レルムの型が変わった */ + slist_add(&realms0, auth_base); + auth_base = NULL; + } + if (auth_base == NULL) { + /* 新規作成 */ + if ((auth_base = npppd_auth_create( + NPPPD_AUTH_TYPE_LOCAL, tok, _this)) + == NULL) { + log_printf(LOG_WARNING, + "npppd_auth_create() failed in " + "%s(): %m", __func__); + goto reigai; + } + } + slist_add(&nrealms, auth_base); + } + } +#ifdef USE_NPPPD_RADIUS + /* RADIUSレルムのラベルを取得 */ + if ((val = npppd_config_str(_this, "auth.radius.realm_list")) != NULL) { + ndef++; + strlcpy(buf, val, sizeof(buf)); + bufp = buf; + while ((tok = strsep(&bufp, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + if (realm_list_contains(&nrealms, tok)) { + log_printf(LOG_WARNING, + "label '%s' for auth.*.realm_list is not " + "unique", tok); + goto reigai; + } + auth_base = realm_list_remove(&realms0, tok); + if (auth_base != NULL && + npppd_auth_get_type(auth_base) + != NPPPD_AUTH_TYPE_RADIUS) { + /* 同じラベル名で認証レルムの型が変わった */ + slist_add(&realms0, auth_base); + auth_base = NULL; + } + if (auth_base == NULL) { + /* 新規作成 */ + if ((auth_base = npppd_auth_create( + NPPPD_AUTH_TYPE_RADIUS, tok, _this)) + == NULL) { + log_printf(LOG_WARNING, + "npppd_auth_create() failed in " + "%s(): %m", __func__); + goto reigai; + } + } + slist_add(&nrealms, auth_base); + } + } +#endif +#ifndef NO_DEFAULT_REALM + if (ndef == 0) { + /* + * 従来版との互換性。デフォルトのレルムを使う。 + */ + if (slist_length(&realms0) > 0) { + slist_add_all(&nrealms, &realms0); + slist_remove_all(&realms0); + } else { + if ((auth_base = npppd_auth_create( + NPPPD_AUTH_TYPE_LOCAL, "", _this)) == NULL) { + log_printf(LOG_WARNING, + "malloc() failed in %s(): %m", __func__); + goto reigai; + } + slist_add(&nrealms, auth_base); +#ifdef USE_NPPPD_RADIUS + if ((auth_base = npppd_auth_create( + NPPPD_AUTH_TYPE_RADIUS, "", _this)) == NULL) { + log_printf(LOG_WARNING, + "malloc() failed in %s(): %m", __func__); + goto reigai; + } + slist_add(&nrealms, auth_base); +#endif + } + } +#endif + if (slist_set_size(&_this->realms, slist_length(&nrealms)) != 0) { + log_printf(LOG_WARNING, "slist_set_size() failed in %s(): %m", + __func__); + goto reigai; + } + + slist_itr_first(&realms0); + while (slist_itr_has_next(&realms0)) { + auth_base = slist_itr_next(&realms0); + if (npppd_auth_is_disposing(auth_base)) + continue; + npppd_auth_dispose(auth_base); + } + + slist_itr_first(&nrealms); + while (slist_itr_has_next(&nrealms)) { + auth_base = slist_itr_next(&nrealms); + rval |= npppd_auth_reload(auth_base); + } + slist_remove_all(&_this->realms); + (void)slist_add_all(&_this->realms, &nrealms); + (void)slist_add_all(&_this->realms, &realms0); + + slist_fini(&realms0); + slist_fini(&nrealms); + + return rval; +reigai: + + slist_itr_first(&nrealms); + while (slist_itr_has_next(&nrealms)) { + auth_base = slist_itr_next(&nrealms); + npppd_auth_destroy(auth_base); + } + slist_fini(&realms0); + slist_fini(&nrealms); + + return 1; +} + +static int +realm_list_contains(slist *list0, const char *label) +{ + npppd_auth_base *base; + + for (slist_itr_first(list0); slist_itr_has_next(list0); ) { + base = slist_itr_next(list0); + if (npppd_auth_is_disposing(base)) + continue; + if (strcmp(npppd_auth_get_label(base), label) == 0) + return 1; + } + + return 0; +} + +static npppd_auth_base * +realm_list_remove(slist *list0, const char *label) +{ + npppd_auth_base *base; + + for (slist_itr_first(list0); slist_itr_has_next(list0); ) { + base = slist_itr_next(list0); + if (npppd_auth_is_disposing(base)) + continue; + if (strcmp(npppd_auth_get_label(base), label) == 0) + return slist_itr_remove(list0); + } + + return NULL; +} + +/** インタフェースの設定を読み込みます。*/ +int +npppd_ifaces_load_config(npppd *_this) +{ + int rval, n, nsession; + const char *val; + char *tok, *buf0, buf[BUFSIZ], buf1[128]; + + rval = 0; + n = 0; + if ((val = npppd_config_str(_this, "interface_list")) != NULL) { + strlcpy(buf, val, sizeof(buf)); + buf0 = buf; + while ((tok = strsep(&buf0, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + if (n >= countof(_this->iface)) { + log_printf(LOG_WARNING, + "number of the interface reached " + "limit=%d",(int)countof(_this->iface)); + break; + } + + if (_this->iface[n].initialized != 0) + nsession = _this->iface[n].nsession; + else { + npppd_iface_init(&_this->iface[n], tok); + nsession = 0; + } + + strlcpy(buf1, "interface.", sizeof(buf1)); + strlcat(buf1, tok, sizeof(buf1)); + + _this->iface[n].set_ip4addr = 0; + if ((val = npppd_config_str(_this, + config_key_prefix(buf1, "ip4addr"))) != NULL){ + if (inet_pton(AF_INET, val, + &_this->iface[n].ip4addr) != 1) { + log_printf(LOG_ERR, + "configuration error at %s", + config_key_prefix(buf1, + "ip4addr")); + return 1; + } + _this->iface[n].set_ip4addr = 1; + } + + _this->iface[n].user_max_session = npppd_config_int( + _this, config_key_prefix(buf1, "user_max_session"), + NPPPD_DEFAULT_USER_MAX_PPP); + _this->iface[n].max_session = npppd_config_int(_this, + config_key_prefix(buf1, "max_session"), + _this->max_session); + + _this->iface[n].nsession = nsession; + _this->iface[n].npppd = _this; + _this->iface[n].initialized = 1; + n++; + } + } + + return rval; +} + +static void +npppd_iface_binding_reload(npppd *_this, npppd_iface *iface, + npppd_iface_binding *binding) +{ + int i, using_default, had_ipcp; + const char *val; + char key[128], *tok, *buf0, buf[NPPPD_CONFIG_BUFSIZ]; + + NPPPD_CONFIG_ASSERT(_this != NULL); + NPPPD_CONFIG_ASSERT(iface != NULL); + NPPPD_CONFIG_ASSERT(binding != NULL); + + had_ipcp = (binding->ipcp != NULL)? 1 : 0; + slist_fini(&binding->pools); + memset(binding, 0, sizeof(npppd_iface_binding)); + if (iface->initialized == 0) + return; + + + /* create the key */ + strlcpy(key, "interface.", sizeof(key)); + strlcat(key, iface->ifname, sizeof(key)); + strlcat(key, ".ipcp_configuration", sizeof(key)); + + using_default = 0; + if ((val = npppd_config_str(_this, key)) != NULL) { + for (i = 0; i < countof(_this->ipcp_config); i++){ + if (_this->ipcp_config[i].initialized == 0) + continue; + if (strcmp(_this->ipcp_config[i].label, val) == 0) { + binding->ipcp = &_this->ipcp_config[i]; + break; + } + } + } else if (_this->ipcp_config[0].initialized != 0 && + _this->ipcp_config[0].label[0] == '\0') { +#ifndef NO_DEFAULT_IPCP + /* デフォルト IPCP がある。*/ + binding->ipcp = &_this->ipcp_config[0]; + using_default = 1; +#else + using_default = 0; +#endif + } + slist_init(&binding->pools); + if (binding->ipcp == NULL) { + if (had_ipcp) + log_printf(LOG_INFO, "%s has no ipcp", iface->ifname); + return; + } + if ((val = npppd_ipcp_config_str(binding->ipcp, "pool_list")) != NULL) { + strlcpy(buf, val, sizeof(buf)); + buf0 = buf; + while ((tok = strsep(&buf0, VAL_SEP)) != NULL) { + if (tok[0] == '\0') + continue; + for (i = 0; i < countof(_this->pool); i++) { + if (_this->pool[i].initialized == 0) + continue; + if (strcmp(tok, _this->pool[i].label) != 0) + continue; + slist_add(&binding->pools, &_this->pool[i]); + break; + } + } + } else if (using_default) { + if (_this->pool[0].initialized != 0) + slist_add(&binding->pools, &_this->pool[0]); + } + log_printf(LOG_INFO, "%s is using ipcp=%s(%d pools).", + iface->ifname, binding->ipcp->name, slist_length(&binding->pools)); +} diff --git a/usr.sbin/npppd/npppd/npppd_ctl.c b/usr.sbin/npppd/npppd/npppd_ctl.c new file mode 100644 index 00000000000..db50114e308 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_ctl.c @@ -0,0 +1,585 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * npppd 制御。UNIXドメインソケット /var/run/npppd_ctl を open して、 + * npppdctlコマンドからのコマンドを受け付ける。 + */ +/* $Id: npppd_ctl.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <event.h> +#include <fcntl.h> +#include <netdb.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "npppd_local.h" +#include "debugutil.h" + +#include "pathnames.h" +#include "radish.h" +#include "npppd_ctl.h" + +#include "net_utils.h" + +#ifdef USE_NPPPD_PIPEX +#if defined(__NetBSD__) +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif +#include <sys/ioctl.h> +#include <net/pipex.h> +#endif + +#ifndef NPPPD_CTL_SOCK_FILE_MODE +#define NPPPD_CTL_SOCK_FILE_MODE 0660 +#endif +#define MSG_SZ_RESERVED 256 + +#ifdef NPPPD_CTL_DEBUG +#define NPPPD_CTL_DBG(x) npppd_ctl_log x +#define NPPPD_CTL_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define NPPPD_CTL_DBG(x) +#define NPPPD_CTL_ASSERT(cond) +#endif +#include "debugutil.h" + +#define NPPPD_CTL_WHO_MSGSZ(n) \ + (n) * sizeof(struct npppd_who) + sizeof(struct npppd_who_list) + +static void npppd_ctl_command (npppd_ctl *, u_char *, int, struct sockaddr *); +static void npppd_ctl_io_event (int, short, void *); +static int npppd_ctl_log (npppd_ctl *, int, const char *, ...) __printflike(3,4); +static void npppd_who_init(struct npppd_who *, npppd_ctl *, npppd_ppp *); + +#ifdef USE_NPPPD_PIPEX +static int npppd_ppp_get_pipex_stat(struct npppd_who *, npppd_ppp *); +#endif + +/** npppd制御機能を初期化します。*/ +void +npppd_ctl_init(npppd_ctl *_this, npppd *_npppd, const char *pathname) +{ + memset(_this, 0, sizeof(npppd_ctl)); + if (pathname != NULL) + strlcat(_this->pathname, pathname, sizeof(_this->pathname)); + _this->sock = -1; + _this->npppd = _npppd; + _this->max_msgsz = DEFAULT_NPPPD_CTL_MAX_MSGSZ; +} + +/** npppd制御機能を起動します。*/ +int +npppd_ctl_start(npppd_ctl *_this) +{ + int flags, dummy, val; + struct sockaddr_un sun; + + if ((_this->sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { + log_printf(LOG_ERR, "socket() failed in %s(): %m", __func__); + goto reigai; + } + + val = _this->max_msgsz; + if (setsockopt(_this->sock, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) + != 0) { + if (errno == ENOBUFS) + log_printf(LOG_ERR, + "ctl.max_msgbuf may beyonds kernel limit. " + "setsockopt(,SOL_SOCKET, SO_SNDBUF,%d) " + "failed in %s(): %m", val, __func__); + /* NetBSD の場合は kern.sbmax 以下にする必要あり */ + else + log_printf(LOG_ERR, + "setsockopt(,SOL_SOCKET, SO_SNDBUF,%d) " + "failed in %s(): %m", val, __func__); + + goto reigai; + } + unlink(_this->pathname); + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_INET; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, _this->pathname, sizeof(sun.sun_path)); + + if (bind(_this->sock, (struct sockaddr *)&sun, sizeof(sun)) != 0) { + log_printf(LOG_ERR, "bind() failed in %s(): %m", __func__); + goto reigai; + } + + dummy = 0; + if ((flags = fcntl(_this->sock, F_GETFL, &dummy)) < 0) { + log_printf(LOG_ERR, "fcntl(,F_GETFL) failed in %s(): %m", + __func__); + goto reigai; + } else if (fcntl(_this->sock, F_SETFL, flags | O_NONBLOCK) < 0) { + log_printf(LOG_ERR, "fcntl(,F_SETFL,O_NONBLOCK) failed in %s()" + ": %m", __func__); + goto reigai; + } + chown(_this->pathname, -1, NPPPD_GID); + chmod(_this->pathname, NPPPD_CTL_SOCK_FILE_MODE); + + event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST, + npppd_ctl_io_event, _this); + event_add(&_this->ev_sock, NULL); + + log_printf(LOG_INFO, "Listening %s (npppd_ctl)", _this->pathname); + + return 0; +reigai: + if (_this->sock >= 0) + close(_this->sock); + _this->sock = -1; + + return -1; +} + +/** npppd制御機能を停止します。*/ +void +npppd_ctl_stop(npppd_ctl *_this) +{ + if (_this->sock >= 0) { + event_del(&_this->ev_sock); + close(_this->sock); + _this->sock = -1; + log_printf(LOG_INFO, "Shutdown %s (npppd_ctl)", + _this->pathname); + } +} + +/** コマンド毎に制御手続きを実行します */ +static void +npppd_ctl_command(npppd_ctl *_this, u_char *pkt, int pktlen, + struct sockaddr *peer) +{ + u_char respbuf[BUFSIZ]; + int command; + + if (pktlen < sizeof(int)) { + npppd_ctl_log(_this, LOG_ERR, "Packet too small."); + return; + } + command = *(int *)pkt; + switch (command) { + case NPPPD_CTL_CMD_WHO: { + int i, c, idx, msgsz; + npppd *_npppd; + struct npppd_who_list *l; + slist users; + + l = NULL; + _npppd = _this->npppd; + slist_init(&users); + if (npppd_get_all_users(_npppd, &users) != 0) { + npppd_ctl_log(_this, LOG_ERR, + "npppd_get_all_users() failed in %s: %m", __func__); + goto cmd_who_out; + } +#ifdef NPPPD_CTL_DEBUG +#if 0 + /* for debug, copy the first user 3000 times. */ + if (slist_length(&users) > 0) { + for (i = 0; i < 32000; i++) + slist_add(&users, slist_get(&users, 0)); + } +#endif +#endif + NPPPD_CTL_ASSERT(_this->max_msgsz > 0); + if ((l = malloc(_this->max_msgsz)) == NULL) { + npppd_ctl_log(_this, LOG_ERR, + "malloc() failed in %s: %m", __func__); + goto cmd_who_out; + } + + /* number of entry per chunk */ + c = _this->max_msgsz - sizeof(struct npppd_who_list); + c /= sizeof(struct npppd_who); + + l->count = slist_length(&users); + slist_itr_first(&users); + for (i = 0, idx = 0; slist_itr_has_next(&users); i++) { + npppd_who_init(&l->entry[idx++], _this, + slist_itr_next(&users)); + idx %= c; + if (idx == 0) { + /* the last entry this chunk */ + msgsz = offsetof(struct npppd_who_list, + entry[c]); + if (sendto(_this->sock, l, msgsz, 0, peer, + peer->sa_len) < 0) + goto cmd_who_send_error; + } + } + if (i == 0 || idx != 0) { + msgsz = offsetof(struct npppd_who_list, entry[(i % c)]); + if (sendto(_this->sock, l, msgsz, 0, peer, + peer->sa_len) < 0) + goto cmd_who_send_error; + } +cmd_who_out: + slist_fini(&users); + if (l != NULL) + free(l); + break; +cmd_who_send_error: + /* + * FIXME: we should wait until the buffer is avaliable. + */ + NPPPD_CTL_DBG((_this, LOG_DEBUG, "sendto() failed in %s: %m", + __func__)); + if (errno == ENOBUFS || errno == EMSGSIZE || errno == EINVAL) { + npppd_ctl_log(_this, LOG_INFO, + "'who' is requested, but " + "the buffer is not enough."); + } else { + npppd_ctl_log(_this, LOG_ERR, + "sendto() failed in %s: %m", + __func__); + } + slist_fini(&users); + if (l != NULL) + free(l); + break; + } + case NPPPD_CTL_CMD_DISCONNECT_USER: { + int i, stopped; + npppd *_npppd; + struct npppd_disconnect_user_req *req; + npppd_ppp *ppp; + slist *ppplist; + + stopped = 0; + _npppd = _this->npppd; + req = (struct npppd_disconnect_user_req *)pkt; + + if (sizeof(struct npppd_disconnect_user_req) > pktlen) { + npppd_ctl_log(_this, LOG_ERR, + "'disconnect by user' is requested, " + " but the request has invalid data length" + "(%d:%d)", pktlen, (int)sizeof(req->username)); + break; + } + for (i = 0; i < sizeof(req->username); i++) { + if (req->username[i] == '\0') + break; + } + if (i >= sizeof(req->username)) { + npppd_ctl_log(_this, LOG_ERR, + "'disconnect by user' is requested, " + " but the request has invalid user name"); + break; + } + + if ((ppplist = npppd_get_ppp_by_user(_npppd, req->username)) + == NULL) { + npppd_ctl_log(_this, LOG_INFO, + "npppd_get_ppp_by_user() could't find user \"%s\" in %s: %m", + req->username, __func__); + goto user_end; + break; + } + slist_itr_first(ppplist); + while (slist_itr_has_next(ppplist)) { + ppp = slist_itr_next(ppplist); + if (ppp == NULL) + continue; + + ppp_stop(ppp, NULL); + stopped++; + } +user_end: + + npppd_ctl_log(_this, LOG_NOTICE, + "'disconnect by user' is requested, " + "stopped %d connections.", stopped); + snprintf(respbuf, sizeof(respbuf), + "Disconnected %d ppp connections", stopped); + + if (sendto(_this->sock, respbuf, strlen(respbuf), 0, peer, + peer->sa_len) < 0) { + npppd_ctl_log(_this, LOG_ERR, + "sendto() failed in %s: %m", __func__); + } + break; + } + /* + * 端末認証関連 + */ + case NPPPD_CTL_CMD_TERMID_SET_AUTH: { +#ifndef NPPPD_USE_CLIENT_AUTH + npppd_ctl_log(_this, LOG_ERR, + "NPPPD_CTL_CMD_TERMID_SET_AUTH is requested, but " + "the terminal authentication is disabled."); + goto reigai; +#else + struct npppd_ctl_termid_set_auth_request *req; + npppd_ppp *ppp; + + req = (struct npppd_ctl_termid_set_auth_request *)pkt; + if (pktlen < sizeof(struct npppd_ctl_termid_set_auth_request)) { + npppd_ctl_log(_this, LOG_ERR, + "NPPPD_CTL_CMD_TERMID_SET_AUTH is requested, but " + "the request is invalid."); + goto reigai; + } + + ppp = NULL; + + switch (req->ppp_key_type) { + case NPPPD_CTL_PPP_ID: + if ((ppp = npppd_get_ppp_by_id(npppd_get_npppd(), + req->ppp_key.id)) == NULL) { + npppd_ctl_log(_this, LOG_ERR, + "NPPPD_CTL_CMD_TERMID_SET_AUTH is " + "requested, but the requested ppp(id=%d) " + "is not found.", req->ppp_key.id); + goto reigai; + } + + break; + case NPPPD_CTL_PPP_FRAMED_IP_ADDRESS: + if ((ppp = npppd_get_ppp_by_ip(npppd_get_npppd(), + req->ppp_key.framed_ip_address)) == NULL) { + npppd_ctl_log(_this, LOG_ERR, + "NPPPD_CTL_CMD_TERMID_SET_AUTH is " + "requested, but the requested ppp(ip=%s) " + "is not found.", + inet_ntoa(req->ppp_key.framed_ip_address)); + goto reigai; + } + break; + default: + npppd_ctl_log(_this, LOG_ERR, + "NPPPD_CTL_CMD_TERMID_SET_AUTH is requested, but " + "the ppp_key_type is invalid."); + goto reigai; + } + NPPPD_CTL_ASSERT(ppp != NULL); + + ppp_set_client_auth_id(ppp, req->authid); + strlcpy(respbuf, + "Set the client authentication information successfully.", + sizeof(respbuf)); + + if (sendto(_this->sock, respbuf, strlen(respbuf), 0, peer, + peer->sa_len) < 0) { + npppd_ctl_log(_this, LOG_ERR, + "sendto() failed in %s: %m", __func__); + + } +#endif + break; + } + case NPPPD_CTL_CMD_RESET_ROUTING_TABLE: + { + if (npppd_reset_routing_table(_this->npppd, 0) == 0) + strlcpy(respbuf, "Reset the routing table successfully.", + sizeof(respbuf)); + else + snprintf(respbuf, sizeof(respbuf), + "Failed to reset the routing table.:%s", + strerror(errno)); + + if (sendto(_this->sock, respbuf, strlen(respbuf), 0, peer, + peer->sa_len) < 0) { + npppd_ctl_log(_this, LOG_ERR, + "sendto() failed in %s: %m", __func__); + + } + break; + } + default: + npppd_ctl_log(_this, LOG_ERR, + "Received unknown command %04x", command); + } +reigai: + return; +} + +static void +npppd_who_init(struct npppd_who *_this, npppd_ctl *ctl, npppd_ppp *ppp) +{ + struct timespec curr_time; + npppd_auth_base *realm = ppp->realm; + npppd_iface *iface = ppp_iface(ppp); + + strlcpy(_this->name, ppp->username, sizeof(_this->name)); + _this->time = ppp->start_time; + if (clock_gettime(CLOCK_MONOTONIC, &curr_time) < 0) { + NPPPD_CTL_ASSERT(0); + } + _this->duration_sec = curr_time.tv_sec - ppp->start_monotime; + strlcpy(_this->phy_label, ppp->phy_label, sizeof(_this->phy_label)); + if (((struct sockaddr *)&ppp->phy_info)->sa_len > 0) { + memcpy(&_this->phy_info, &ppp->phy_info, + ((struct sockaddr *)&ppp->phy_info)->sa_len); + } + strlcpy(_this->ifname, iface->ifname, sizeof(_this->ifname)); + strlcpy(_this->rlmname, npppd_auth_get_name(realm), + sizeof(_this->rlmname)); + + _this->assign_ip4 = ppp->ppp_framed_ip_address; + _this->ipackets = ppp->ipackets; + _this->opackets = ppp->opackets; + _this->ierrors = ppp->ierrors; + _this->oerrors = ppp->oerrors; + _this->ibytes = ppp->ibytes; + _this->obytes = ppp->obytes; + _this->id = ppp->id; + +#ifdef USE_NPPPD_PIPEX + if (ppp->pipex_enabled != 0) { + if (npppd_ppp_get_pipex_stat(_this, ppp) != 0) { + npppd_ctl_log(ctl, LOG_NOTICE, + "npppd_ppp_get_pipex_stat() failed in %s: %m", + __func__); + } + } +#endif +} + +#ifdef USE_NPPPD_PIPEX +static int +npppd_ppp_get_pipex_stat(struct npppd_who *_this, npppd_ppp *ppp) +{ + npppd_iface *iface = ppp_iface(ppp); + struct pipex_session_stat_req req; +#ifdef USE_NPPPD_PPPOE + pppoe_session *pppoe; +#endif +#ifdef USE_NPPPD_PPTP + pptp_call *call; +#endif + + if (ppp->pipex_enabled == 0) + return 0; + + memset(&req, 0, sizeof(req)); + switch(ppp->tunnel_type) { +#ifdef USE_NPPPD_PPPOE + case PPP_TUNNEL_PPPOE: + pppoe = (pppoe_session *)ppp->phy_context; + + /* PPPOE 固有の情報 */ + req.psr_protocol = PIPEX_PROTO_PPPOE; + req.psr_session_id = pppoe->session_id; + break; +#endif +#ifdef USE_NPPPD_PPTP + case PPP_TUNNEL_PPTP: + call = (pptp_call *)ppp->phy_context; + + /* PPTP 固有の情報 */ + req.psr_session_id = call->id; + req.psr_protocol = PIPEX_PROTO_PPTP; + break; +#endif + default: + NPPPD_CTL_ASSERT(0); + errno = EINVAL; + return 1; + } + + /* カーネル側の統計情報を反映 */ + if (ioctl(iface->devf, PIPEXGSTAT, &req) != 0) + return 1; + + _this->ipackets += req.psr_stat.ipackets; + _this->opackets += req.psr_stat.opackets; + _this->ierrors += req.psr_stat.ierrors; + _this->oerrors += req.psr_stat.oerrors; + _this->ibytes += req.psr_stat.ibytes; + _this->obytes += req.psr_stat.obytes; + + return 0; +} +#endif + +/** IOイベントハンドラー */ +static void +npppd_ctl_io_event(int fd, short evmask, void *ctx) +{ + int sz; + u_char buf[BUFSIZ]; + npppd_ctl *_this; + struct sockaddr_storage ss; + socklen_t sslen; + + _this = ctx; + if ((evmask & EV_READ) != 0) { + sslen = sizeof(ss); + if ((sz = recvfrom(_this->sock, buf, sizeof(buf), 0, + (struct sockaddr *)&ss, &sslen)) < 0) { + npppd_ctl_log(_this, LOG_ERR, + "recvfrom() failed in %s(): %m", __func__); + npppd_ctl_stop(_this); + + return; + } + npppd_ctl_command(_this, buf, sz, (struct sockaddr *)&ss); + } + return; +} + +/** このインスタンスに基づいたラベルから始まるログを記録します。*/ +static int +npppd_ctl_log(npppd_ctl *_this, int prio, const char *fmt, ...) +{ + int status; + char logbuf[BUFSIZ]; + va_list ap; + + NPPPD_CTL_ASSERT(_this != NULL); + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "npppdctl: %s", fmt); + status = vlog_printf(prio, logbuf, ap); + va_end(ap); + + return status; +} diff --git a/usr.sbin/npppd/npppd/npppd_ctl.h b/usr.sbin/npppd/npppd/npppd_ctl.h new file mode 100644 index 00000000000..d8cb3e2ed04 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_ctl.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_CTL_H +#define NPPPD_CTL_H 1 + +#define NPPPD_CTL_MAX_MSGSIZ 8192 + +#ifndef DEFAULT_NPPPD_CTL_SOCK_PATH +#define DEFAULT_NPPPD_CTL_SOCK_PATH "/var/run/npppd_ctl" +#endif + +/** 利用ユーザの統計情報 */ +#define NPPPD_CTL_CMD_WHO 1 + +#ifndef DEFAULT_NPPPD_CTL_MAX_MSGSZ +#define DEFAULT_NPPPD_CTL_MAX_MSGSZ 51200 +#endif + +struct npppd_who { + /** ppp Id */ + int id; + /** ユーザ名 */ + char name[MAX_USERNAME_LENGTH]; + /** 開始時刻 */ + time_t time; + /** 経過時間 */ + uint32_t duration_sec; + /** 物理層ラベル */ + char phy_label[16]; + /** 集約インターフェイス **/ + char ifname[IF_NAMESIZE]; + + char rlmname[NPPPD_GENERIC_NAME_LEN]; + union { + struct sockaddr_in peer_in; + struct sockaddr_dl peer_dl; + } /** 物理層のアドレス情報 */ + phy_info; + + /** 割り当てた IP アドレス */ + struct in_addr assign_ip4; + /** 入力パケット数 */ + uint32_t ipackets; + /** 出力パケット数 */ + uint32_t opackets; + /** 入力エラーパケット数 */ + uint32_t ierrors; + /** 出力エラーパケット数 */ + uint32_t oerrors; + /** 入力パケットバイト*/ + uint64_t ibytes; + /** 出力パケットバイト*/ + uint64_t obytes; +}; +struct npppd_who_list { + int count; + struct npppd_who entry[0]; +}; + +/** 指定したユーザの接続を切断 */ +#define NPPPD_CTL_CMD_DISCONNECT_USER 2 + +struct npppd_disconnect_user_req { + int command; + char username[MAX_USERNAME_LENGTH]; +}; + +/** 端末認証の認証情報をセットします */ +#define NPPPD_CTL_CMD_TERMID_SET_AUTH 3 + +/** npppd の持つ経路情報をシステムにリセットします */ +#define NPPPD_CTL_CMD_RESET_ROUTING_TABLE 4 + +typedef enum _npppd_ctl_ppp_key { + NPPPD_CTL_PPP_ID = 0, + NPPPD_CTL_PPP_FRAMED_IP_ADDRESS, +} npppd_ctl_ppp_key_type; + +struct npppd_ctl_termid_set_auth_request { + int command; + int reserved; + npppd_ctl_ppp_key_type ppp_key_type; + union { + uint32_t id; + struct in_addr framed_ip_address; + } ppp_key; + char authid[33]; +}; + +#endif diff --git a/usr.sbin/npppd/npppd/npppd_defs.h b/usr.sbin/npppd/npppd/npppd_defs.h new file mode 100644 index 00000000000..5af69a54281 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_defs.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_DEFS_H +#define NPPPD_DEFS_H 1 + +#ifdef _SEIL_EXT_ +#include <seil/features.h> + +#define NPPPD_DEFAULT_MAX_PPP MAX_NUMBER_OF_PPPAC_SESSION +#define DEFAULT_NPPPD_CTL_MAX_MSGSZ \ + (NPPPD_DEFAULT_MAX_PPP*sizeof(struct npppd_who) \ + + sizeof(struct npppd_who_list) + 256) +#endif + +#define NPPPD_MAX_POOLED_ADDRS 8192 +#define NPPPD_USER_HASH_SIZ 1777 +#define NPPPD_GENERIC_NAME_LEN 32 +#ifndef LOG_NPPPD +#define LOG_NPPPD LOG_LOCAL1 +#endif + +#ifndef NPPPD_MAX_SERVERS +/** RADIUSサーバの数 */ +#define NPPPD_MAX_SERVERS 8 +#endif + +#ifndef NPPPD_TIMER_TICK_IVAL +#define NPPPD_TIMER_TICK_IVAL 4 +#endif + +/** 認証レルム終了化処理のインターバル時間(sec) */ +#define NPPPD_AUTH_REALM_FINALIZER_INTERVAL 300 + +#ifndef NPPPD_MAX_IPCP_CONFIG +/** IPCP設定の数 */ +#define NPPPD_MAX_IPCP_CONFIG 1 +#endif + +#ifndef NPPPD_MAX_IFACE +/** PPP集約インタフェース(tun や pppac) の数 */ +#define NPPPD_MAX_IFACE 1 +#endif + +#ifndef NPPPD_MAX_POOL +/** プールの数 */ +#define NPPPD_MAX_POOL 1 +#endif + +#ifndef NPPPD_MAX_PPTP +/** ローカル認証レルムの数 */ +#define NPPPD_MAX_PPTP 2 +#endif + +#ifndef NPPPD_DEFAULT_AUTH_LOCAL_RELOADABLE +#define NPPPD_DEFAULT_AUTH_LOCAL_RELOADABLE 0 +#endif + +/** 同一ユーザが接続できる最大の PPPセッション数のデフォルト */ +#define NPPPD_DEFAULT_USER_MAX_PPP 3 + +#ifndef NPPPD_DEFAULT_MAX_PPP +/** 同時に接続できる最大の PPPセッション数のデフォルト */ +#define NPPPD_DEFAULT_MAX_PPP 8192 +#endif + +#define NPPPD_UID -1 /* 特に指定しない */ +#ifndef NPPPD_GID +/** npppd 実行時のグループID。*/ +#define NPPPD_GID 0 +#endif + +#ifndef LOOPBACK_IFNAME +#define LOOPBACK_IFNAME "lo0" +#endif + +#ifndef NPPPD_DEFAULT_IP_ASSIGN_USER_SELECT +#define NPPPD_DEFAULT_IP_ASSIGN_USER_SELECT 1 +#endif +#ifndef NPPPD_DEFAULT_IP_ASSIGN_FIXED +#define NPPPD_DEFAULT_IP_ASSIGN_FIXED 1 +#endif +#ifndef NPPPD_DEFAULT_IP_ASSIGN_RADIUS +#define NPPPD_DEFAULT_IP_ASSIGN_RADIUS 0 +#endif + +/** rtev_write() を使う */ +#define NPPPD_USE_RTEV_WRITE 1 + +#ifndef DEFAULT_RTSOCK_EVENT_DELAY +/** Routing ソケットイベントを受けてから、処理を開始するまでの待ち時間(秒)*/ +#define DEFAULT_RTSOCK_EVENT_DELAY 5 +#endif +#ifndef DEFAULT_RTSOCK_SEND_NPKTS +/** Routing ソケットに書き込む際に一度に書くパケット数*/ +#define DEFAULT_RTSOCK_SEND_NPKTS 16 +#endif +#ifndef DEFAULT_RTSOCK_SEND_WAIT_MILLISEC +/** Routing ソケットへの連続書き込みで間隔を空ける時間(ミリ秒) */ +#define DEFAULT_RTSOCK_SEND_WAIT_MILLISEC 0 +#endif + +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/npppd_iface.c b/usr.sbin/npppd/npppd/npppd_iface.c new file mode 100644 index 00000000000..7fc370bbab9 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_iface.c @@ -0,0 +1,607 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: npppd_iface.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * npppd がカーネルとインタフェースするためのインタフェース。NetBSD の + * tun(4) や SEIL の pppac(4) を利用するための実装です。 + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <net/if_tun.h> + +#include <fcntl.h> + +#include <syslog.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdarg.h> + +#include <time.h> +#include <event.h> +#include "radish.h" + +#include "npppd_defs.h" +#include "npppd_local.h" +#include "npppd_subr.h" +#include "debugutil.h" +#include "npppd_iface.h" + +#ifdef USE_NPPPD_PIPEX +#include <net/if.h> +#if defined(__NetBSD__) +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif +#include <net/pipex.h> +#endif /* USE_NPPPD_PIPEX */ + +#ifdef NPPPD_IFACE_DEBUG +#define NPPPD_IFACE_DBG(x) npppd_iface_log x +#define NPPPD_IFACE_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define NPPPD_IFACE_ASSERT(cond) +#define NPPPD_IFACE_DBG(x) +#endif + +static void npppd_iface_network_input(npppd_iface *, u_char *, int); +static int npppd_iface_setup_ip(npppd_iface *); +static void npppd_iface_io_event_handler (int, short, void *); +static int npppd_iface_log (npppd_iface *, int, const char *, ...) + __printflike(3,4); + +#ifdef USE_NPPPD_PIPEX +static int npppd_iface_pipex_enable(npppd_iface *_this); +static int npppd_iface_pipex_disable(npppd_iface *_this); +#endif /* USE_NPPPD_PIPEX */ + + +/** npppd_iface の初期化 */ +void +npppd_iface_init(npppd_iface *_this, const char *ifname) +{ + NPPPD_IFACE_ASSERT(_this != NULL); + memset(_this, 0, sizeof(npppd_iface)); + + _this->devf = -1; + strlcpy(_this->ifname, ifname, sizeof(_this->ifname)); +} + +static int +npppd_iface_setup_ip(npppd_iface *_this) +{ + int sock, if_flags, changed; + struct in_addr gw, assigned; + struct sockaddr_in *sin0; + struct ifreq ifr; + struct ifaliasreq ifra; + npppd_ppp *ppp; + + NPPPD_IFACE_ASSERT(_this != NULL); + + sock = -1; + changed = 0; + memset(&ifr, 0, sizeof(ifr)); + + /* インタフェースに割り当てられたアドレスを取得 */ + assigned.s_addr = INADDR_NONE; + memset(&ifr, 0, sizeof(ifr)); + memset(&ifra, 0, sizeof(ifra)); + strlcpy(ifr.ifr_name, _this->ifname, sizeof(ifr.ifr_name)); + strlcpy(ifra.ifra_name, _this->ifname, sizeof(ifra.ifra_name)); + sin0 = (struct sockaddr_in *)&ifr.ifr_addr; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + npppd_iface_log(_this, LOG_ERR, + "socket() failed in %s(): %m", __func__); + goto reigai; + } + if (ioctl(sock, SIOCGIFADDR, &ifr) != 0) { + if (errno != EADDRNOTAVAIL) { + npppd_iface_log(_this, LOG_ERR, + "get ip address failed: %m"); + goto reigai; + } + assigned.s_addr = 0; + } else + assigned = sin0->sin_addr; + + if (assigned.s_addr != _this->ip4addr.s_addr) + changed = 1; + + memset(&ifr.ifr_ifru, 0, sizeof(ifr.ifr_ifru)); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + npppd_iface_log(_this, LOG_ERR, + "ioctl(,SIOCGIFFLAGS) failed: %m"); + goto reigai; + } + if_flags = ifr.ifr_flags; + + if (_this->set_ip4addr != 0 && changed) { + do { + if (ioctl(sock, SIOCDIFADDR, &ifr) != 0) { + if (errno == EADDRNOTAVAIL) + break; + npppd_iface_log(_this, LOG_ERR, + "delete ipaddress %s failed: %m", + _this->ifname); + goto reigai; + } + if (ioctl(sock, SIOCGIFADDR, &ifr) != 0) { + if (errno == EADDRNOTAVAIL) + break; + npppd_iface_log(_this, LOG_ERR, + "cannot get ipaddress %s failed: %m", + _this->ifname); + goto reigai; + } + } while (1); + + + /* ifconfig tun1 down */ + ifr.ifr_flags = if_flags & ~(IFF_UP | IFF_BROADCAST); + if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { + npppd_iface_log(_this, LOG_ERR, + "disabling %s failed: %m", _this->ifname); + goto reigai; + } + + sin0 = (struct sockaddr_in *)&ifra.ifra_addr; + sin0->sin_addr.s_addr = _this->ip4addr.s_addr; + sin0->sin_family = AF_INET; + sin0->sin_len = sizeof(struct sockaddr_in); + + sin0 = (struct sockaddr_in *)&ifra.ifra_mask; + sin0->sin_addr.s_addr = 0xffffffffL; + sin0->sin_family = AF_INET; + sin0->sin_len = sizeof(struct sockaddr_in); + + sin0 = (struct sockaddr_in *)&ifra.ifra_broadaddr; + sin0->sin_addr.s_addr = 0; + sin0->sin_family = AF_INET; + sin0->sin_len = sizeof(struct sockaddr_in); + + if (ioctl(sock, SIOCAIFADDR, &ifra) != 0 && errno != EEXIST) { + /* + * alias リクエストなので EEXIST? + */ + npppd_iface_log(_this, LOG_ERR, + "Cannot assign tun device ip address: %m"); + goto reigai; + } + assigned.s_addr = _this->ip4addr.s_addr; + + } + _this->ip4addr.s_addr = assigned.s_addr; + if (npppd_iface_ip_is_ready(_this)) { + if (changed) { + /* + * I/F アドレスを使っている PPPセッションがあったら、 + * 切断する。 + */ + ppp = npppd_get_ppp_by_ip(_this->npppd, _this->ip4addr); + if (ppp != NULL) { + npppd_iface_log(_this, LOG_ERR, + "Assigning %s, but ppp=%d is using " + "the address. Requested the ppp to stop", + inet_ntoa(_this->ip4addr), ppp->id); + ppp_stop(ppp, "Administrative reason"); + } + } + /* ifconfig tun1 up */ + ifr.ifr_flags = if_flags | IFF_UP | IFF_MULTICAST; + if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { + npppd_iface_log(_this, LOG_ERR, + "enabling %s failed: %m", _this->ifname); + goto reigai; + } + /* + * ホスト自身からの _this->ip4addr への通信ができるように、 + * 経路追加。 + */ + gw.s_addr = htonl(INADDR_LOOPBACK); + in_host_route_add(&_this->ip4addr, &gw, LOOPBACK_IFNAME, 0); + } + close(sock); sock = -1; + + return 0; +reigai: + if (sock >= 0) + close(sock); + + return 1; +} + +/** トンネルエンドアドレスの設定 */ +int +npppd_iface_reinit(npppd_iface *_this) +{ + int rval; + struct in_addr backup; + char buf0[128], buf1[128]; + + backup = _this->ip4addr; + if ((rval = npppd_iface_setup_ip(_this)) != 0) + return rval; + + if (backup.s_addr != _this->ip4addr.s_addr) { + npppd_iface_log(_this, LOG_INFO, "Reinited ip4addr %s=>%s", + (backup.s_addr != INADDR_ANY) + ? inet_ntop(AF_INET, &backup, buf0, sizeof(buf0)) + : "(not assigned)", + (_this->ip4addr.s_addr != INADDR_ANY) + ? inet_ntop(AF_INET, &_this->ip4addr, buf1, + sizeof(buf1)) + : "(not assigned)"); + } + + return 0; +} + +/** npppd_iface の開始 */ +int +npppd_iface_start(npppd_iface *_this) +{ + int x; + char buf[MAXPATHLEN]; + + NPPPD_IFACE_ASSERT(_this != NULL); + + /* デバイスファイルオープン */ + snprintf(buf, sizeof(buf), "/dev/%s", _this->ifname); + if ((_this->devf = open(buf, O_RDWR, 0600)) < 0) { + npppd_iface_log(_this, LOG_ERR, "open(%s) failed: %m", buf); + goto reigai; + } + + x = 1; + if (ioctl(_this->devf, FIONBIO, &x) != 0) { + npppd_iface_log(_this, LOG_ERR, + "ioctl(FIONBIO) failed in %s(): %m", __func__); + goto reigai; + } + + x = IFF_BROADCAST; + if (ioctl(_this->devf, TUNSIFMODE, &x) != 0) { + npppd_iface_log(_this, LOG_ERR, + "ioctl(TUNSIFMODE=IFF_BROADCAST) failed in %s(): %m", + __func__); + goto reigai; + } + + event_set(&_this->ev, _this->devf, EV_READ | EV_PERSIST, + npppd_iface_io_event_handler, _this); + event_add(&_this->ev, NULL); + + if (npppd_iface_setup_ip(_this) != 0) + goto reigai; + +#ifdef USE_NPPPD_PIPEX + if (npppd_iface_pipex_enable(_this) != 0) { + log_printf(LOG_WARNING, + "npppd_iface_pipex_enable() failed: %m"); + } +#endif /* USE_NPPPD_PIPEX */ + + npppd_iface_log(_this, LOG_INFO, "Started ip4addr=%s", + (npppd_iface_ip_is_ready(_this))? + inet_ntop(AF_INET, &_this->ip4addr, buf, sizeof(buf)) + : "(not assigned)"); + + return 0; +reigai: + if (_this->devf >= 0) { + event_del(&_this->ev); + close(_this->devf); + } + _this->devf = -1; + + return -1; +} + +/** npppd_iface の利用を停止します。*/ +void +npppd_iface_stop(npppd_iface *_this) +{ + NPPPD_IFACE_ASSERT(_this != NULL); + + if (_this->devf >= 0) { +#ifdef USE_NPPPD_PIPEX + if (npppd_iface_pipex_disable(_this) != 0) { + log_printf(LOG_CRIT, + "npppd_iface_pipex_disable() failed: %m"); + } +#endif /* USE_NPPPD_PIPEX */ + + event_del(&_this->ev); + close(_this->devf); + npppd_iface_log(_this, LOG_INFO, "Stopped"); + } + _this->devf = -1; + _this->initialized = 0; + event_del(&_this->ev); +} + +/** npppd_iface の終了化 */ +void +npppd_iface_fini(npppd_iface *_this) +{ + NPPPD_IFACE_ASSERT(_this != NULL); + + npppd_iface_stop(_this); +} + + +/*********************************************************************** + * PIPEX 関連 + ***********************************************************************/ +#ifdef USE_NPPPD_PIPEX + +/** PPPAC インタフェースの PIPEX を有効化します */ +int +npppd_iface_pipex_enable(npppd_iface *_this) +{ + int enable = 1; + + return ioctl(_this->devf, PIPEXSMODE, &enable); +} + +/** PPPAC インタフェースの PIPEX を無効化します */ +int +npppd_iface_pipex_disable(npppd_iface *_this) +{ + int disable = 0; + + return ioctl(_this->devf, PIPEXSMODE, &disable); +} + +#endif /* USE_NPPPD_PIPEX */ + + +/*********************************************************************** + * I/O関連 + ***********************************************************************/ +/** I/O イベントハンドラ */ +static void +npppd_iface_io_event_handler(int fd, short evtype, void *data) +{ + int sz; + u_char buffer[8192]; + npppd_iface *_this; + + NPPPD_IFACE_ASSERT((evtype & EV_READ) != 0); + + _this = data; + NPPPD_IFACE_ASSERT(_this->devf >= 0); + do { + sz = read(_this->devf, buffer, sizeof(buffer)); + if (sz <= 0) { + if (sz == 0) + npppd_iface_log(_this, LOG_ERR, + "file is closed"); + else if (errno == EAGAIN) + break; + else + npppd_iface_log(_this, LOG_ERR, + "read failed: %m"); + npppd_iface_stop(_this); + return; + } + npppd_iface_network_input(_this, buffer, sz); + + } while (1 /* CONSTCOND */); + + return; +} + +/** npppd_iface_network_input_delegate の引数を表す型 */ +struct npppd_iface_network_input_arg{ + npppd_iface *_this; + u_char *pktp; + int lpktp; +}; + +/** 個々の PPP についての処理を行うコールバック関数 */ +static int +npppd_iface_network_input_delegate(struct radish *radish, void *args0) +{ + npppd_ppp *ppp; + struct sockaddr_npppd *snp; + struct npppd_iface_network_input_arg *args; + + snp = radish->rd_rtent; + + if (snp->snp_type == SNP_PPP) { + args = args0; + ppp = snp->snp_data_ptr; + if (ppp_iface(ppp) != args->_this) + return 0; +#ifdef USE_NPPPD_MPPE + if (MPPE_READY(ppp)) { + /* MPPE が開始していれば MPPE 経由で */ + mppe_pkt_output(&ppp->mppe, PPP_PROTO_IP, args->pktp, + args->lpktp); + } else if (MPPE_REQUIRED(ppp)) { + /* MPPE 開始してないけど MPPE必須の場合 */ + /* マルチキャストなのでログには残さない. */ + return 0; + } +#endif + ppp_output(ppp, PPP_PROTO_IP, 0, 0, args->pktp, args->lpktp); + } + + return 0; +} + +static void +npppd_iface_network_input_ipv4(npppd_iface *_this, u_char *pktp, int lpktp) +{ + struct ip *iphdr; + npppd *_npppd; + npppd_ppp *ppp; + struct npppd_iface_network_input_arg input_arg; + + NPPPD_IFACE_ASSERT(_this != NULL); + NPPPD_IFACE_ASSERT(pktp != NULL); + + iphdr = (struct ip *)pktp; + _npppd = _this->npppd; + + if (lpktp < sizeof(iphdr)) { + npppd_iface_log(_this, LOG_ERR, "Received short packet."); + return; + } + if (IN_MULTICAST(ntohl(iphdr->ip_dst.s_addr))) { + NPPPD_IFACE_ASSERT(((npppd *)(_this->npppd))->rd != NULL); + input_arg._this = _this; + input_arg.pktp = pktp; + input_arg.lpktp = lpktp; + /* デリゲート */ + rd_walktree(((npppd *)(_this->npppd))->rd, + npppd_iface_network_input_delegate, &input_arg); + return; + } + ppp = npppd_get_ppp_by_ip(_npppd, iphdr->ip_dst); + if (ppp == NULL) { +#ifdef NPPPD_DEBUG + log_printf(LOG_INFO, "%s received a packet to unknown " + "%s.", _this->ifname, inet_ntoa(iphdr->ip_dst)); + show_hd(debug_get_debugfp(), pktp, lpktp); +#endif + return; + } +#ifndef NO_ADJUST_MSS + if (ppp->adjust_mss) { + adjust_tcp_mss(pktp, lpktp, MRU_IPMTU(ppp->peer_mru)); + } +#endif + if (ppp->timeout_sec > 0 && !ip_is_idle_packet(iphdr, lpktp)) + ppp_reset_idle_timeout(ppp); + +#ifdef USE_NPPPD_MPPE + if (MPPE_READY(ppp)) { + /* MPPE が開始していれば MPPE 経由で */ + mppe_pkt_output(&ppp->mppe, PPP_PROTO_IP, pktp, lpktp); + return; + } else if (MPPE_REQUIRED(ppp)) { + /* MPPE 開始してないけど MPPE必須の場合 */ + ppp_log(ppp, LOG_WARNING, "A packet received from network, " + "but MPPE is not started."); + return; + } +#endif + ppp_output(ppp, PPP_PROTO_IP, 0, 0, pktp, lpktp); +} + +/** + * ネットワーク(tun)側 から入力があった場合に呼び出します。現在は IPv4 の + * パケットが入力されることを仮定しています。 + */ +static void +npppd_iface_network_input(npppd_iface *_this, u_char *pktp, int lpktp) +{ + uint32_t af; + + if (lpktp < sizeof(uint32_t)) { + npppd_iface_log(_this, LOG_ERR, "Received short packet."); + return; + } + GETLONG(af, pktp); + lpktp -= sizeof(uint32_t); + + switch (af) { + case AF_INET: + npppd_iface_network_input_ipv4(_this, pktp, lpktp); + break; + + default: + NPPPD_IFACE_ASSERT(0); + break; + + } +} + +/** トンネルデバイスに書き込みます。*/ +void +npppd_iface_write(npppd_iface *_this, int proto, u_char *pktp, int lpktp) +{ + int err; + uint32_t th; + struct iovec iov[2]; + + NPPPD_IFACE_ASSERT(_this != NULL); + NPPPD_IFACE_ASSERT(_this->devf >= 0); + + th = htonl(proto); + + iov[0].iov_base = &th; + iov[0].iov_len = sizeof(th); + iov[1].iov_base = pktp; + iov[1].iov_len = lpktp; + + err = writev(_this->devf, iov, countof(iov)); + + if (err != lpktp + sizeof(th)) + npppd_iface_log(_this, LOG_ERR, "write failed: %m"); +} + +/*********************************************************************** + * 雑多 + ***********************************************************************/ +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static int +npppd_iface_log(npppd_iface *_this, int prio, const char *fmt, ...) +{ + int status; + char logbuf[BUFSIZ]; + va_list ap; + + NPPPD_IFACE_ASSERT(_this != NULL); + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "%s %s", _this->ifname, fmt); + status = vlog_printf(prio, logbuf, ap); + va_end(ap); + + return status; +} diff --git a/usr.sbin/npppd/npppd/npppd_iface.h b/usr.sbin/npppd/npppd/npppd_iface.h new file mode 100644 index 00000000000..1cad06b70ea --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_iface.h @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_INTERFACE_H +#define NPPPD_INTERFACE_H 1 + +typedef struct _npppd_iface { + /** ベースとなる npppd */ + void *npppd; + /** インタフェース名 */ + char ifname[IFNAMSIZ]; + /** デバイスファイルのデスクリプタ */ + int devf; + + /** 割り当てられた IPv4 アドレス */ + struct in_addr ip4addr; + /** event(3) 用メンバー */ + struct event ev; + + /** 同一PPPユーザが接続できる最大の PPPセッション数 */ + int user_max_session; + /** 接続できる最大の PPPセッション数 */ + int max_session; + + /** 接続中の PPPセッション数 */ + int nsession; + + int /** + * npppd_iface の処理としてIPアドレスをセットするか。 + * <p>0 であれば、npppd_iface は、セットされた IPアドレスを + * 参照するだけです。</p> + */ + set_ip4addr:1, + /** 初期化済みフラグ */ + initialized:1; +} npppd_iface; + +/** インタフェースのIPアドレスは使用できるか */ +#define npppd_iface_ip_is_ready(int) \ + ((int)->initialized != 0 && (int)->ip4addr.s_addr != INADDR_ANY) + +#ifdef __cplusplus +extern "C" { +#endif + +void npppd_iface_init (npppd_iface *, const char *); +int npppd_iface_reinit (npppd_iface *); +int npppd_iface_start (npppd_iface *); +void npppd_iface_stop (npppd_iface *); +void npppd_iface_fini (npppd_iface *); +void npppd_iface_write (npppd_iface *, int proto, u_char *, int); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/npppd/npppd_local.h b/usr.sbin/npppd/npppd/npppd_local.h new file mode 100644 index 00000000000..2c92e87bad4 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_local.h @@ -0,0 +1,275 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _NPPPD_LOCAL_H +#define _NPPPD_LOCAL_H 1 + +#ifndef NPPPD_BUFSZ +/** バッファサイズ */ +#define NPPPD_BUFSZ BUFSZ +#endif + +#include <sys/param.h> +#include <net/if.h> + +#include "npppd_defs.h" + +#include "slist.h" +#include "hash.h" +#include "properties.h" + +#ifdef USE_NPPPD_RADIUS +#include <radius+.h> +#include "radius_req.h" +#endif + +#ifdef USE_NPPPD_L2TP +#include "debugutil.h" +#include "bytebuf.h" +#include "l2tp.h" +#endif + +#ifdef USE_NPPPD_PPTP +#include "bytebuf.h" +#include "pptp.h" +#endif +#ifdef USE_NPPPD_PPPOE +#if defined(__NetBSD__) +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif +#include "bytebuf.h" +#include "pppoe.h" +#endif +#include "npppd_auth.h" +#include "npppd_iface.h" +#include "npppd.h" + +#ifdef USE_NPPPD_NPPPD_CTL +typedef struct _npppd_ctl { + /** イベントコンテキスト */ + struct event ev_sock; + /** ソケット */ + int sock; + /** 有効/無効 */ + int enabled; + /** 親 npppd */ + void *npppd; + /** ソケットのパス名 */ + char pathname[MAXPATHLEN]; + /** 最大メッセージ長 */ + int max_msgsz; +} npppd_ctl; +#endif + +#include "addr_range.h" +#include "npppd_pool.h" + +/** プールを示す型 */ +struct _npppd_pool { + /** 基となる npppd */ + npppd *npppd; + /** ラベル名 */ + char label[NPPPD_GENERIC_NAME_LEN]; + /** 名前(name) */ + char name[NPPPD_GENERIC_NAME_LEN]; + /** sockaddr_npppd 配列のサイズ */ + int addrs_size; + /** sockaddr_npppd 配列 */ + struct sockaddr_npppd *addrs; + /** 動的に割り当てるアドレスのリスト */ + slist dyna_addrs; + int /** 初期化済 */ + initialized:1, + /** 利用中 */ + running:1; +}; + +/** IPCP設定を示す型 */ +typedef struct _npppd_ipcp_config { + /** 名前 */ + char name[NPPPD_GENERIC_NAME_LEN]; + /** ラベル(紐付けるため) */ + char label[NPPPD_GENERIC_NAME_LEN]; + /** 親 npppd へのポインタ */ + npppd *npppd; + /** + * プライマリDNSサーバ。先方に通知しない場合は INADDR_NONE。 + * ネットワークバイトオーダー。 + */ + struct in_addr dns_pri; + + /** セカンダリDNSサーバ。先方に通知しない場合は INADDR_NONE。 + * ネットワークバイトオーダー。 + */ + struct in_addr dns_sec; + + /** + * プライマリWINSサーバ。先方に通知しない場合は INADDR_NONE。 + * ネットワークバイトオーダー。 + */ + struct in_addr nbns_pri; + + /** + * セカンダリWINSサーバ。先方に通知しない場合は INADDR_NONE。 + * ネットワークバイトオーダー。 + */ + struct in_addr nbns_sec; + + /** + * IPアドレス割り当て方法のビットフラグ。 + * @see #NPPPD_IP_ASSIGN_FIXED + * @see #NPPPD_IP_ASSIGN_USER_SELECT + * @see #NPPPD_IP_ASSIGN_RADIUS + */ + int ip_assign_flags; + + int /** DNS サーバとしてトンネル終端アドレスを使う */ + dns_use_tunnel_end:1, + /** 初期化済かどうか */ + initialized:1, + reserved:30; +} npppd_ipcp_config; + +/** インタフェースの IPCP 設定やプールアドレスへの参照を保持する型 */ +typedef struct _npppd_iface_binding { + npppd_ipcp_config *ipcp; + slist pools; +} npppd_iface_binding; + +/** + * npppd + */ +struct _npppd { + /** イベントハンドラー */ + struct event ev_sigterm, ev_sigint, ev_sighup, ev_timer; + + /** PPPを集約するインターフェース */ + npppd_iface iface[NPPPD_MAX_IFACE]; + /** インタフェースの IPCP 設定やプールアドレスへの参照 */ + npppd_iface_binding iface_bind[NPPPD_MAX_IFACE]; + + /** アドレスプール */ + npppd_pool pool[NPPPD_MAX_POOL]; + + /** radish プール、割り当てアドレス管理用 */ + struct radish_head *rd; + + /** IPCP 設定 */ + npppd_ipcp_config ipcp_config[NPPPD_MAX_IPCP_CONFIG]; + + /** ユーザ名 → slist of npppd_ppp のマップ */ + hash_table *map_user_ppp; + + /** 認証レルム */ + slist realms; + + /** 認証レルム終了化処理のインターバル時間(sec) */ + int auth_finalizer_itvl; + + /** 設定ファイル名 */ + char config_file[MAXPATHLEN]; + + /** PIDファイル名 */ + char pidpath[MAXPATHLEN]; + + /** プロセス ID */ + pid_t pid; + +#ifdef USE_NPPPD_L2TP + /** L2TP デーモン */ + l2tpd l2tpd; +#endif +#ifdef USE_NPPPD_PPTP + /** PPTP デーモン */ + pptpd pptpd; +#ifdef IDGW_SSLDIP + /** SSLDIP 用 PPTP デーモン */ + pptpd ssldipd; +#endif +#endif +#ifdef USE_NPPPD_PPPOE + /** PPPOE デーモン */ + pppoed pppoed; +#endif + /** 設定ファイル */ + struct properties * properties; + + /** ユーザ設定ファイル */ + struct properties * users_props; + +#ifdef USE_NPPPD_NPPPD_CTL + npppd_ctl ctl; +#endif + /** 起動してからの秒数。*/ + uint32_t secs; + + /** 設定再読み込みを何秒猶予するか */ + int16_t delayed_reload; + /** 設定再読み込みカウンタ */ + int16_t reloading_count; + + /** 処理済みのルーティングイベントシリアル */ + int rtev_event_serial; + + /** 接続できる最大の PPPセッション数 */ + int max_session; + + int /** 終了処理中 */ + finalizing:1, + /** 終了処理完了 */ + finalized:1; +}; + +#ifndef NPPPD_CONFIG_BUFSIZ +#define NPPPD_CONFIG_BUFSIZ 65536 // 64K +#endif +#ifndef NPPPD_KEY_BUFSIZ +#define NPPPD_KEY_BUFSIZ 512 +#endif +#define ppp_iface(ppp) (&(ppp)->pppd->iface[(ppp)->ifidx]) +#define ppp_ipcp(ppp) ((ppp)->pppd->iface_bind[(ppp)->ifidx].ipcp) +#define ppp_pools(ppp) (&(ppp)->pppd->iface_bind[(ppp)->ifidx].pools) + +#define SIN(sa) ((struct sockaddr_in *)(sa)) + +#define TIMER_TICK_RUP(interval) \ + ((((interval) % NPPPD_TIMER_TICK_IVAL) == 0) \ + ? (interval) \ + : (interval) + NPPPD_TIMER_TICK_IVAL \ + - ((interval) % NPPPD_TIMER_TICK_IVAL)) + +#ifdef USE_NPPPD_NPPPD_CTL +void npppd_ctl_init (npppd_ctl *, npppd *, const char *); +int npppd_ctl_start (npppd_ctl *); +void npppd_ctl_stop (npppd_ctl *); +#endif +#define sin46_port(x) (((x)->sa_family == AF_INET6) \ + ? ((struct sockaddr_in6 *)(x))->sin6_port \ + : ((struct sockaddr_in *)(x))->sin_port) + + +#endif diff --git a/usr.sbin/npppd/npppd/npppd_pool.c b/usr.sbin/npppd/npppd/npppd_pool.c new file mode 100644 index 00000000000..69351eea873 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_pool.c @@ -0,0 +1,642 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <net/route.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <net/if_dl.h> +#include <stdio.h> +#include <time.h> +#include <event.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <syslog.h> +#include <stdlib.h> +#include <stdarg.h> +#include <netdb.h> + +#include "slist.h" +#include "debugutil.h" +#include "properties.h" +#include "addr_range.h" +#include "radish.h" +#include "config_helper.h" +#include "npppd_local.h" +#include "npppd_pool.h" +#include "npppd_subr.h" +#include "net_utils.h" + +#ifdef NPPPD_POOL_DEBUG +#define NPPPD_POOL_DBG(x) npppd_pool_log x +#define NPPPD_POOL_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define NPPPD_POOL_ASSERT(cond) +#define NPPPD_POOL_DBG(x) +#endif +#define A(v) ((0xff000000 & (v)) >> 24), ((0x00ff0000 & (v)) >> 16), \ + ((0x0000ff00 & (v)) >> 8), (0x000000ff & (v)) +#define SA(sin4) ((struct sockaddr *)(sin4)) + +#define SHUFLLE_MARK 0xffffffffL +static int npppd_pool_log __P((npppd_pool *, int, const char *, ...)) __printflike(3, 4); +static int in_addr_range_list_add_all (struct in_addr_range **, const char *); +static int is_valid_host_address (uint32_t); +static int npppd_pool_regist_radish(npppd_pool *, struct in_addr_range *, + struct sockaddr_npppd *, int ); + + +/*********************************************************************** + * npppd_pool オブジェクト操作 + ***********************************************************************/ +/** npppd_poll を初期化します */ +int +npppd_pool_init(npppd_pool *_this, npppd *base, const char *name) +{ + memset(_this, 0, sizeof(npppd_pool)); + + strlcpy(_this->label, name, sizeof(_this->label)); + _this->npppd = base; + slist_init(&_this->dyna_addrs); + + _this->initialized = 1; + + return 0; +} + +/** npppd_pool の使用を開始します */ +int +npppd_pool_start(npppd_pool *_this) +{ + return 0; // やることなし。 +} + +/* 設定テンプレート展開 */; +NAMED_PREFIX_CONFIG_DECL(npppd_pool_config, npppd_pool, npppd->properties, + "pool", label); +NAMED_PREFIX_CONFIG_FUNCTIONS(npppd_pool_config, npppd_pool, npppd->properties, + "pool", label); + +/** npppd_poll を終了化します */ +void +npppd_pool_uninit(npppd_pool *_this) +{ + _this->initialized = 0; + + slist_fini(&_this->dyna_addrs); + if (_this->addrs != NULL) + free(_this->addrs); + _this->addrs = NULL; + _this->addrs_size = 0; + _this->npppd = NULL; +} + +/** 設定を再読み込みします。*/ +int +npppd_pool_reload(npppd_pool *_this) +{ + int i, count, addrs_size; + struct sockaddr_npppd *addrs; + struct in_addr_range *pool, *dyna_pool, *range; + const char *val, *val0; + char buf0[BUFSIZ], buf1[BUFSIZ]; + + addrs = NULL; + pool = NULL; + dyna_pool = NULL; + buf0[0] = '\0'; + + val = npppd_pool_config_str(_this, "name"); + if (val == NULL) + val = _this->label; + strlcpy(_this->name, val, sizeof(_this->name)); + + /* 動的アドレスプール */ + val0 = NULL; + val = npppd_pool_config_str(_this, "dyna_pool"); + if (val != NULL) { + if (in_addr_range_list_add_all(&dyna_pool, val) != 0) { + npppd_pool_log(_this, LOG_WARNING, + "parse error at 'dyna_pool': %s", val); + goto reigai; + } + val0 = val; + } + + /* 固定アドレスプール */ + val = npppd_pool_config_str(_this, "pool"); + if (val != NULL) { + if (in_addr_range_list_add_all(&pool, val) != 0) { + npppd_pool_log(_this, LOG_WARNING, + "parse error at 'pool': %s", val); + goto reigai; + } + if (val0 != NULL) + /* Aggregate */ + in_addr_range_list_add_all(&pool, val0); + } + + /* RADISH 登録準備 */ + addrs_size = 0; + for (range = dyna_pool; range != NULL; range = range->next) + addrs_size++; + for (range = pool; range != NULL; range = range->next) + addrs_size++; + + if ((addrs = calloc(addrs_size + 1, sizeof(struct sockaddr_npppd))) + == NULL) { + /* +1 しているのは calloc(0) を回避するため */ + npppd_pool_log(_this, LOG_WARNING, + "calloc() failed in %s: %m", __func__); + goto reigai; + } + + /* 動的プール => RADISH 登録 */ + count = 0; + for (i = 0, range = dyna_pool; range != NULL; range = range->next, i++){ + if (npppd_pool_regist_radish(_this, range, &addrs[count], 1)) + goto reigai; + if (count == 0) + strlcat(buf0, "dyn_pool=[", sizeof(buf0)); + else + strlcat(buf0, ",", sizeof(buf0)); + snprintf(buf1, sizeof(buf1), "%d.%d.%d.%d/%d", + A(range->addr), netmask2prefixlen(range->mask)); + strlcat(buf0, buf1, sizeof(buf0)); + count++; + } + if (i > 0) + strlcat(buf0, "] ", sizeof(buf0)); + + /* 固定プール => RADISH 登録 */ + for (i = 0, range = pool; range != NULL; range = range->next, i++) { + if (npppd_pool_regist_radish(_this, range, &addrs[count], 0)) + goto reigai; + if (i == 0) + strlcat(buf0, "pool=[", sizeof(buf0)); + else + strlcat(buf0, ",", sizeof(buf0)); + snprintf(buf1, sizeof(buf1), "%d.%d.%d.%d/%d", + A(range->addr), netmask2prefixlen(range->mask)); + strlcat(buf0, buf1, sizeof(buf0)); + count++; + } + if (i > 0) + strlcat(buf0, "]", sizeof(buf0)); + + npppd_pool_log(_this, LOG_INFO, "%s", buf0); + + count = 0; + slist_add(&_this->dyna_addrs, (void *)SHUFLLE_MARK); + for (range = dyna_pool; range != NULL; range = range->next) { + if (count >= NPPPD_MAX_POOLED_ADDRS) + break; + for (i = 0; i <= ~(range->mask); i++) { + if (!is_valid_host_address(range->addr + i)) + continue; + if (count >= NPPPD_MAX_POOLED_ADDRS) + break; + slist_add(&_this->dyna_addrs, (void *) + (range->addr + i)); + count++; + } + } + if (_this->addrs != NULL) + free(_this->addrs); + _this->addrs = addrs; + _this->addrs_size = addrs_size; + in_addr_range_list_remove_all(&pool); + in_addr_range_list_remove_all(&dyna_pool); + + return 0; +reigai: + in_addr_range_list_remove_all(&pool); + in_addr_range_list_remove_all(&dyna_pool); + + if (addrs != NULL) + free(addrs); + + return 1; +} + +static int +npppd_pool_regist_radish(npppd_pool *_this, struct in_addr_range *range, + struct sockaddr_npppd *snp, int is_dynamic) +{ + int rval; + struct sockaddr_in sin4a, sin4b; + struct sockaddr_npppd *snp0; + npppd_pool *npool0; + + memset(&sin4a, 0, sizeof(sin4a)); + memset(&sin4b, 0, sizeof(sin4b)); + sin4a.sin_len = sin4b.sin_len = sizeof(sin4a); + sin4a.sin_family = sin4b.sin_family = AF_INET; + sin4a.sin_addr.s_addr = htonl(range->addr); + sin4b.sin_addr.s_addr = htonl(range->mask); + + snp->snp_len = sizeof(struct sockaddr_npppd); + snp->snp_family = AF_INET; + snp->snp_addr.s_addr = htonl(range->addr); + snp->snp_mask.s_addr = htonl(range->mask); + snp->snp_data_ptr = _this; + if (is_dynamic) + snp->snp_type = SNP_DYN_POOL; + else + snp->snp_type = SNP_POOL; + + if ((snp0 = rd_lookup(SA(&sin4a), SA(&sin4b), + _this->npppd->rd)) != NULL) { + /* + * radish ツリーは、初期化直後で POOL のエントリしかないことを + * 仮定。 + */ + NPPPD_POOL_ASSERT(snp0->snp_type != SNP_PPP); + npool0 = snp0->snp_data_ptr; + + if (!is_dynamic && npool0 == _this) + /* 動的アドレスとして登録済 */ + return 0; + + npppd_pool_log(_this, LOG_WARNING, + "%d.%d.%d.%d/%d is already defined as '%s'(%s)", + A(range->addr), netmask2prefixlen(range->mask), + npool0->name, (snp0->snp_type == SNP_POOL) + ? "static" : "dynamic"); + goto reigai; + } + if ((rval = rd_insert(SA(&sin4a), SA(&sin4b), _this->npppd->rd, + snp)) != 0) { + errno = rval; + npppd_pool_log(_this, LOG_WARNING, + "rd_insert(%d.%d.%d.%d/%d) failed: %m", + A(range->addr), netmask2prefixlen(range->mask)); + goto reigai; + } + + return 0; +reigai: + return 1; + +} + +/*********************************************************************** + * API + ***********************************************************************/ +/** 動的アドレスを割り当てます */ +uint32_t +npppd_pool_get_dynamic(npppd_pool *_this, npppd_ppp *ppp) +{ + int shuffle_cnt; + void *result = NULL; + struct sockaddr_in sin4 = { + .sin_len = sizeof(struct sockaddr_in), + .sin_family = AF_INET + }; + struct sockaddr_npppd *snp; + npppd_ppp *ppp0; + + shuffle_cnt = 0; + slist_itr_first(&_this->dyna_addrs); + while (slist_length(&_this->dyna_addrs) > 1 && + slist_itr_has_next(&_this->dyna_addrs)) { + result = slist_itr_next(&_this->dyna_addrs); + if (result == NULL) + break; + /* シャッフル */ + if ((uint32_t)result == SHUFLLE_MARK) { + /* + * 使えるアドレスが無くなると length > 1 でも、 + * shuffle を連続してツモる。2回ツモったら、 + * つまり使えるアドレスがない。 + */ + if (shuffle_cnt++ > 0) { + result = NULL; + break; + } + NPPPD_POOL_DBG((_this, LOG_DEBUG, "shuffle")); + slist_itr_remove(&_this->dyna_addrs); + slist_shuffle(&_this->dyna_addrs); + slist_add(&_this->dyna_addrs, result); + slist_itr_first(&_this->dyna_addrs); + continue; + } + slist_itr_remove(&_this->dyna_addrs); + + switch (npppd_pool_get_assignability(_this, (uint32_t)result, + 0xffffffffL, &snp)) { + case ADDRESS_OK: + /* 成功するのはココだけ。 */ + return (uint32_t)result; + default: + /* インタフェースのアドレスだった場合 */ + /* + * リストから削除しているので、インタフェースのアドレ + * スだけを再変更すると、アドレスをリークしていく問題 + * があるが、現実装では、アドレスだけを変更していくこ + * とはないので問題ない。運用上も、プールを変更せず、 + * tunnel-end-address だけを変更していく、というのは、 + * 定常的に発生するとは考えづらい。 + */ + continue; + case ADDRESS_BUSY: + sin4.sin_addr.s_addr = htonl((uint32_t)result); + /* + * 設定再読み込みにより、アクティブな PPP セッションが + * リセットされた + */ + NPPPD_POOL_ASSERT(snp != NULL); + NPPPD_POOL_ASSERT(snp->snp_type == SNP_PPP); + ppp0 = snp->snp_data_ptr; + ppp0->assigned_pool = _this; + ppp0->assign_dynapool = 1; /* 返却よろしく */ + continue; + } + break; + } + return (uint32_t)0; +} + +inline static int +npppd_is_ifcace_ip4addr(npppd *_this, uint32_t ip4addr) +{ + int i; + + for (i = 0; i < countof(_this->iface); i++) { + if (npppd_iface_ip_is_ready(&_this->iface[i]) && + _this->iface[i].ip4addr.s_addr == ip4addr) + return 1; + } + + return 0; +} + +/** IPアドレスを割り当てます */ +int +npppd_pool_assign_ip(npppd_pool *_this, npppd_ppp *ppp) +{ + int rval; + uint32_t ip4; + void *rtent; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in) + }, mask = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + }; + struct sockaddr_npppd *snp; + + ip4 = ntohl(ppp->ppp_framed_ip_address.s_addr); + + /* 動的アドレスリストに含まれたらそこから取り外す。 */ + slist_itr_first(&_this->dyna_addrs); + while (slist_itr_has_next(&_this->dyna_addrs)) { + if ((uint32_t)slist_itr_next( + &_this->dyna_addrs) != ip4) + continue; + slist_itr_remove(&_this->dyna_addrs); + break; + } + + addr.sin_addr = ppp->ppp_framed_ip_address; + mask.sin_addr = ppp->ppp_framed_ip_netmask; + addr.sin_addr.s_addr &= mask.sin_addr.s_addr; + + if (rd_delete(SA(&addr), SA(&mask), _this->npppd->rd, &rtent) == 0) { + snp = rtent; + /* 重複エントリあり。プールから PPPへの差し替え */ + NPPPD_POOL_ASSERT(snp != NULL); + NPPPD_POOL_ASSERT(snp->snp_type != SNP_PPP); + ppp->snp.snp_next = snp; + NPPPD_POOL_DBG((_this, DEBUG_LEVEL_2, + "pool %s/32 => %s(ppp=%d)", + inet_ntoa(ppp->ppp_framed_ip_address), ppp->username, + ppp->id)); + } + NPPPD_POOL_DBG((_this, LOG_DEBUG, "rd_insert(%s) %s", + inet_ntoa(addr.sin_addr), ppp->username)); + if ((rval = rd_insert((struct sockaddr *)&addr, + (struct sockaddr *)&mask, _this->npppd->rd, &ppp->snp)) != 0) { + errno = rval; + log_printf(LOG_INFO, "rd_insert(%s) failed: %m", + inet_ntoa(ppp->ppp_framed_ip_address)); + return 1; + } + + return 0; +} + +/** IPアドレスを解放します */ +void +npppd_pool_release_ip(npppd_pool *_this, npppd_ppp *ppp) +{ + void *item; + int rval; + struct sockaddr_npppd *snp; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in) + }, mask = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + }; + + /* _this == NULL 設定変更によりプールは解放された */ + if (!ppp_ip_assigned(ppp)) + return; + + addr.sin_addr = ppp->ppp_framed_ip_address; + mask.sin_addr = ppp->ppp_framed_ip_netmask; + addr.sin_addr.s_addr &= mask.sin_addr.s_addr; + + if ((rval = rd_delete((struct sockaddr *)&addr, + (struct sockaddr *)&mask, ppp->pppd->rd, &item)) != 0) { + errno = rval; + log_printf(LOG_INFO, "Unexpected error: " + "rd_delete(%s) failed: %m", + inet_ntoa(ppp->ppp_framed_ip_address)); + } + snp = item; + + if (_this != NULL && ppp->assign_dynapool != 0) + /* 動的リストに返却 */ + slist_add(&((npppd_pool *)ppp->assigned_pool)->dyna_addrs, + (void *)ntohl(ppp->ppp_framed_ip_address.s_addr)); + + if (snp != NULL && snp->snp_next != NULL) { + /* + * radish エントリがリストになっていて、アドレス/マスクが + * 一致していれば、次のエントリを再登録。 + */ + if (rd_insert(SA(&addr), SA(&mask), ppp->pppd->rd, + snp->snp_next) != 0) { + log_printf(LOG_INFO, "Unexpected error: " + "rd_insert(%s) failed: %m", + inet_ntoa(ppp->ppp_framed_ip_address)); + } + NPPPD_POOL_DBG((_this, DEBUG_LEVEL_2, + "pool %s/%d <= %s(ppp=%d)", + inet_ntoa(ppp->ppp_framed_ip_address), + netmask2prefixlen(ntohl(ppp->ppp_framed_ip_netmask.s_addr)), + ppp->username, ppp->id)); + snp->snp_next = NULL; + } +} + +/** + * 指定したアドレスが割り当て可能かどうか。 + * @return {@link ::#ADDRESS_OK}、{@link ::#ADDRESS_RESERVED}、 + * {@link ::#ADDRESS_BUSY}、{@link ::#ADDRESS_INVALID} もしくは + * {@link ::#ADDRESS_OUT_OF_POOL} が返ります。 + */ +int +npppd_pool_get_assignability(npppd_pool *_this, uint32_t ip4addr, + uint32_t ip4mask, struct sockaddr_npppd **psnp) +{ + struct radish *radish; + struct sockaddr_in sin4; + struct sockaddr_npppd *snp; + + NPPPD_POOL_ASSERT(ip4mask != 0); + NPPPD_POOL_DBG((_this, LOG_DEBUG, "%s(%08x,%08x)", __func__, ip4addr, + ip4mask)); + + if (netmask2prefixlen(htonl(ip4mask)) == 32) { + if (!is_valid_host_address(ip4addr)) + return ADDRESS_INVALID; + } + + memset(&sin4, 0, sizeof(sin4)); + + sin4.sin_len = sizeof(sin4); + sin4.sin_family = AF_INET; + sin4.sin_addr.s_addr = htonl(ip4addr); + + if (npppd_is_ifcace_ip4addr(_this->npppd, sin4.sin_addr.s_addr)) + return ADDRESS_RESERVED; + /* インタフェースのアドレスは割り振らない */ + + if (rd_match(SA(&sin4), _this->npppd->rd, &radish)) { + do { + snp = radish->rd_rtent; + if (snp->snp_type == SNP_POOL || + snp->snp_type == SNP_DYN_POOL) { + if (psnp != NULL) + *psnp = snp; + if (snp->snp_data_ptr == _this) + return ADDRESS_OK; + else + return ADDRESS_RESERVED; + } + if (snp->snp_type == SNP_PPP) { + if (psnp != NULL) + *psnp = snp; + return ADDRESS_BUSY; + } + } while (rd_match_next(SA(&sin4), _this->npppd->rd, &radish, + radish)); + } + + return ADDRESS_OUT_OF_POOL; +} +/*********************************************************************** + * 雑多 + ***********************************************************************/ +/** + * ホストアドレスとして正しいか。 + * <pre> + * ナチュラルマスクのブロードキャストアドレスをホストとして利用すると、 + * いくつか問題があるので「正しくない」とする。問題とは、 + * + * (1) BSD系は、該当アドレスは転送せず、自分宛として処理する。 + * (2) [IDGW-DEV 4405]『IPアドレスに .255 が割り当てられた Windows マシン + * から L2TP/IPsec を利用できない問題』</pre> + */ +static int +is_valid_host_address(uint32_t addr) +{ + if (IN_CLASSA(addr)) + return ((IN_CLASSA_HOST & addr) == 0 || + (IN_CLASSA_HOST & addr) == IN_CLASSA_HOST)? 0 : 1; + if (IN_CLASSB(addr)) + return ((IN_CLASSB_HOST & addr) == 0 || + (IN_CLASSB_HOST & addr) == IN_CLASSB_HOST)? 0 : 1; + if (IN_CLASSC(addr)) + return ((IN_CLASSC_HOST & addr) == 0 || + (IN_CLASSC_HOST & addr) == IN_CLASSC_HOST)? 0 : 1; + + return 0; +} + +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static int +npppd_pool_log(npppd_pool *_this, int prio, const char *fmt, ...) +{ + int status; + char logbuf[BUFSIZ]; + va_list ap; + + /* + * npppd_pool_release_ip は _this == NULL で呼ばれるので + * NPPPD_POOL_ASSERT(_this != NULL); + * できない + */ + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "pool name=%s %s", + (_this == NULL)? "null" : _this->name, fmt); + status = vlog_printf(prio, logbuf, ap); + va_end(ap); + + return status; +} + +static int +in_addr_range_list_add_all(struct in_addr_range **range, const char *str) +{ + char *tok, *buf0, buf[NPPPD_CONFIG_BUFSIZ]; + + strlcpy(buf, str, sizeof(buf)); + buf0 = buf; + + while ((tok = strsep(&buf0, " \r\n\t")) != NULL) { + if (tok[0] == '\0') + continue; + if (in_addr_range_list_add(range, tok) != 0) + return 1; + } + return 0; +} diff --git a/usr.sbin/npppd/npppd/npppd_pool.h b/usr.sbin/npppd/npppd/npppd_pool.h new file mode 100644 index 00000000000..f09b9505aac --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_pool.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_POOL_H +#define NPPPD_POOL_H 1 + +typedef struct _npppd_pool npppd_pool; + +#define ADDRESS_OK 0 /** 割り当て可能 */ +#define ADDRESS_RESERVED 1 /** 予約アドレス */ +#define ADDRESS_BUSY 2 /** 使用中 */ +#define ADDRESS_INVALID 3 /** 利用不能アドレス */ +#define ADDRESS_OUT_OF_POOL 4 /** 管理外 */ + +#ifdef __cplusplus +extern "C" { +#endif + + +int npppd_pool_init (npppd_pool *, npppd *, const char *); +int npppd_pool_start (npppd_pool *); +int npppd_pool_reload (npppd_pool *); +void npppd_pool_uninit (npppd_pool *); + +int npppd_pool_get_assignability (npppd_pool *, uint32_t, uint32_t, struct sockaddr_npppd **); +uint32_t npppd_pool_get_dynamic (npppd_pool *, npppd_ppp *); +int npppd_pool_assign_ip (npppd_pool *, npppd_ppp *); +void npppd_pool_release_ip (npppd_pool *, npppd_ppp *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/npppd/npppd_subr.c b/usr.sbin/npppd/npppd/npppd_subr.c new file mode 100644 index 00000000000..fe248b0b84d --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_subr.c @@ -0,0 +1,613 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * npppd の補助的な関数を提供します。 + */ +/* $Id: npppd_subr.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/cdefs.h> +#ifndef LINT +__COPYRIGHT( +"@(#) Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>\n" +"@(#) All rights reserved.\n" +); +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdio.h> +#include <syslog.h> +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <resolv.h> + +#include "debugutil.h" +#include "addr_range.h" + +#include "npppd_defs.h" +#include "npppd_subr.h" +#include "rtev.h" +#ifdef NPPPD_USE_RT_ZEBRA +#include "bytebuf.h" +#include <event.h> +#include "rt_zebra.h" +#endif + +static u_int16_t route_seq = 0; +static int in_route0(int, struct in_addr *, struct in_addr *, struct in_addr *, int, const char *, uint32_t); +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +static const char * +skip_space(const char *s) +{ + const char *r; + for (r = s; *r != '\0' && isspace((unsigned char)*r); r++) + ;; /* skip */ + + return r; +} + +/** + * resolv.conf からネームサーバの IPv4 のエントリを読み出します。 + * resolv.conf のパスは、resolv.h の _PATH_RESCONF を使います。 + */ +int +load_resolv_conf(struct in_addr *pri, struct in_addr *sec) +{ + FILE *filep; + int i; + struct in_addr *addr; + char *ap, *line, buf[BUFSIZ]; + + pri->s_addr = INADDR_NONE; + sec->s_addr = INADDR_NONE; + + filep = NULL; + if ((filep = fopen(_PATH_RESCONF, "r")) == NULL) + return 1; + + i = 0; + while (fgets(buf, sizeof(buf), filep) != NULL) { + line = (char *)skip_space(buf); + if (strncmp(line, "nameserver", 10) != 0) + continue; + line += 10; + if (!isspace((unsigned char)*line)) + continue; + while ((ap = strsep(&line, " \t\r\n")) != NULL) { + if (*ap == '\0') + continue; + if (i == 0) + addr = pri; + else + addr = sec; + if (inet_aton(ap, addr) != 1) { + /* + * FIXME IPv6 使ってるの場合 IPv6 アドレスが入 + * っているかもしれない。とりあえず、continue。 + */ + continue; + //goto reigai; + } + addr->s_addr = addr->s_addr; + if (++i >= 2) + goto end_loop; + } + } +end_loop: + if (filep != NULL) + fclose(filep); + + return 0; +} + +// 経路追加削除 +static int +in_route0(int type, struct in_addr *dest, struct in_addr *mask, + struct in_addr *gate, int mtu, const char *ifname, uint32_t rtm_flags) +{ + struct rt_msghdr *rtm; + struct sockaddr_in sdest, smask, sgate; + struct sockaddr_dl *sdl; + char dl_buf[512]; // enough size + char *cp, buf[sizeof(*rtm) + sizeof(struct sockaddr_in) * 3 + + sizeof(dl_buf) + 128]; + const char *strtype; +#ifndef NPPPD_USE_RTEV_WRITE + int rval, dummy, flags, sock; + + sock = -1; +#endif + + ASSERT(type == RTM_ADD || type == RTM_DELETE); +#ifdef NPPPD_USE_RT_ZEBRA + if ((rtm_flags & RTF_BLACKHOLE) != 0) { + if (type == RTM_ADD) + return rt_zebra_add_ipv4_blackhole_rt( + rt_zebra_get_instance(), dest->s_addr, + (mask == NULL)? 0xffffffffL : mask->s_addr); + else + return rt_zebra_delete_ipv4_blackhole_rt( + rt_zebra_get_instance(), dest->s_addr, + (mask == NULL)? 0xffffffffL : mask->s_addr); + return -1; + } +#endif + if(type == RTM_ADD) + strtype = "RTM_ADD"; + else + strtype = "RTM_DELETE"; + + memset(buf, 0, sizeof(buf)); + memset(&sdest, 0, sizeof(sdest)); + memset(&smask, 0, sizeof(smask)); + memset(&sgate, 0, sizeof(sgate)); + memset(&dl_buf, 0, sizeof(dl_buf)); + + sdl = (struct sockaddr_dl *)dl_buf; + + sdest.sin_addr = *dest; + if (mask != NULL) + smask.sin_addr = *mask; + if (gate != NULL) + sgate.sin_addr = *gate; + + sdest.sin_family = smask.sin_family = sgate.sin_family = AF_INET; + sdest.sin_len = smask.sin_len = sgate.sin_len = sizeof(sgate); + + rtm = (struct rt_msghdr *)buf; + + rtm->rtm_version = RTM_VERSION; + rtm->rtm_type = type; + rtm->rtm_flags = RTF_DONE | rtm_flags; + if (gate != NULL) + rtm->rtm_flags |= RTF_GATEWAY; + if (mask == NULL) + rtm->rtm_flags |= RTF_HOST; +#ifdef RTF_MASK + else + rtm->rtm_flags |= RTF_MASK; +#endif +#ifdef _SEIL_EXT_ + /* + * phash で interface のアドレスを変更されると、route flush される。 + * route flush コマンドに -xpppac (Except pppac) オプションを追加 + * して、RTF_PROTO2 ならば flush しないような修正を行い、phash で + * 使用される の flush_route() では -xpppac 付きで route コマンド + * を実行する。 + */ + rtm->rtm_flags |= RTF_PROTO2; + /* + * RTF_STATIC が立っていないと Zebra が消す場合がある。 + */ + rtm->rtm_flags |= RTF_STATIC; +#endif /* _SEIL_EXT_ */ + + if (type == RTM_ADD && mtu > 0) { + rtm->rtm_inits = RTV_MTU; + rtm->rtm_rmx.rmx_mtu = mtu; + } + + if (type == RTM_ADD) + rtm->rtm_flags |= RTF_UP; + + rtm->rtm_addrs = RTA_DST; + if (gate != NULL) + rtm->rtm_addrs |= RTA_GATEWAY; + if (mask != NULL) + rtm->rtm_addrs |= RTA_NETMASK; +#ifdef RTA_IFP + if (ifname != NULL) + rtm->rtm_addrs |= RTA_IFP; +#endif + + rtm->rtm_pid = getpid(); + route_seq = ((route_seq + 1)&0x0000ffff); + rtm->rtm_seq = route_seq; + + cp = (char *)rtm; + cp += ROUNDUP(sizeof(*rtm)); + + memcpy(cp, &sdest, sdest.sin_len); + cp += ROUNDUP(sdest.sin_len); + if (gate != NULL) { + memcpy(cp, &sgate, sgate.sin_len); + cp += ROUNDUP(sgate.sin_len); + } + if (mask != NULL) { + memcpy(cp, &smask, smask.sin_len); + cp += ROUNDUP(smask.sin_len); + } +#ifdef RTA_IFP + if (ifname != NULL) { + strlcpy(sdl->sdl_data, ifname, IFNAMSIZ); + sdl->sdl_family = AF_LINK; + sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) +IFNAMSIZ; + memcpy(cp, sdl, sdl->sdl_len); + cp += ROUNDUP(sdl->sdl_len); + } +#endif + + rtm->rtm_msglen = cp - buf; + +#ifdef NPPPD_USE_RTEV_WRITE + if (rtev_write(rtm) < 0) + log_printf(LOG_ERR, "rtev_write failed in %s: %m", __func__); +#else + + if ((sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { + log_printf(LOG_ERR, "socket() failed in %s() on %s : %m", + __func__, strtype); + goto reigai; + } + + dummy = 0; + if ((flags = fcntl(sock, F_GETFL, dummy)) < 0) { + log_printf(LOG_ERR, "fcntl(,F_GETFL) failed on %s : %m", + __func__); + goto reigai; + } + + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { + log_printf(LOG_ERR, "fcntl(,F_SETFL) failed on %s : %m", + __func__); + goto reigai; + } + + if ((rval = write(sock, buf, rtm->rtm_msglen)) <= 0) { + if (!(type == RTM_DELETE && errno == ESRCH) && + !(type == RTM_ADD && errno == EEXIST)) { + log_printf(LOG_DEBUG, + "write() failed in %s on %s : %m", __func__, + strtype); + } else { + log_printf(LOG_WARNING, + "write() failed in %s on %s : %m", __func__, + strtype); + } + goto reigai; + } + + close(sock); +#endif + + return 0; + +#ifndef NPPPD_USE_RTEV_WRITE +reigai: + if (sock >= 0) + close(sock); +#endif + + return 1; +} + +/** ホスト経路を追加 */ +int +in_host_route_add(struct in_addr *dest, struct in_addr *gate, + const char *ifname, int mtu) +{ + return in_route0(RTM_ADD, dest, NULL, gate, mtu, ifname, 0); +} + +/** ホスト経路を削除 */ +int +in_host_route_delete(struct in_addr *dest, struct in_addr *gate) +{ + return in_route0(RTM_DELETE, dest, NULL, gate, 0, NULL, 0); +} + +/** ネット経路を追加 */ +int +in_route_add(struct in_addr *dest, struct in_addr *mask, struct in_addr *gate, + const char *ifname, uint32_t rtm_flags, int mtu) +{ + return in_route0(RTM_ADD, dest, mask, gate, mtu, ifname, rtm_flags); +} + +/** ネット経路を削除 */ +int +in_route_delete(struct in_addr *dest, struct in_addr *mask, + struct in_addr *gate, uint32_t rtm_flags) +{ + return in_route0(RTM_DELETE, dest, mask, gate, 0, NULL, rtm_flags); +} + +// 従来 ppp の ip.c より流用 +/** + * Check whether a packet should reset idle timer + * Returns 1 to don't reset timer (i.e. the packet is "idle" packet) + */ +int +ip_is_idle_packet(const struct ip * pip, int len) +{ + u_int16_t ip_off; + const struct udphdr *uh; + + /* + * フラグメントされたパケットはアイドルパケットではない + * (フラグメントするほど長いパケットはアイドルパケットではない) + */ + ip_off = ntohs(pip->ip_off); + if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0)) + return 0; + + switch (pip->ip_p) { + case IPPROTO_IGMP: + return 1; + case IPPROTO_ICMP: + /* 長さは足りている? */ + if (pip->ip_hl * 4 + 8 > len) + return 1; + + switch (((unsigned char *) pip)[pip->ip_hl * 4]) { + case 0: /* Echo Reply */ + case 8: /* Echo Request */ + return 0; + default: + return 1; + } + case IPPROTO_UDP: + case IPPROTO_TCP: + /* + * UDP も TCP も、ポートの部分は同じなので、そこだけは共用で + * きる。 + */ + uh = (const struct udphdr *) (((const char *) pip) + + (pip->ip_hl * 4)); + + /* 長さは足りている? */ + if (pip->ip_hl * 4 + sizeof(struct udphdr) > len) + return 1; + + switch (ntohs(uh->uh_sport)) { + case 53: /* DOMAIN */ + case 67: /* BOOTPS */ + case 68: /* BOOTPC */ + case 123: /* NTP */ + case 137: /* NETBIOS-NS */ + case 520: /* RIP */ + return 1; + } + switch (ntohs(uh->uh_dport)) { + case 53: /* DOMAIN */ + case 67: /* BOOTPS */ + case 68: /* BOOTPC */ + case 123: /* NTP */ + case 137: /* NETBIOS-NS */ + case 520: /* RIP */ + return 1; + } + return 0; + default: + return 0; + } +} + +/*********************************************************************** + * プールしているアドレスへの経路追加/削除 + ***********************************************************************/ +void +in_addr_range_add_route(struct in_addr_range *range) +{ + struct in_addr_range *range0; + struct in_addr dest, mask, loop; + + for (range0 = range; range0 != NULL; range0 = range0->next){ + dest.s_addr = htonl(range0->addr); + mask.s_addr = htonl(range0->mask); + loop.s_addr = htonl(INADDR_LOOPBACK); + in_route_add(&dest, &mask, &loop, LOOPBACK_IFNAME, + RTF_BLACKHOLE, 0); + } + log_printf(LOG_INFO, "Added routes for pooled addresses"); +} + +void +in_addr_range_delete_route(struct in_addr_range *range) +{ + struct in_addr_range *range0; + struct in_addr dest, mask, loop; + + for (range0 = range; range0 != NULL; range0 = range0->next){ + dest.s_addr = htonl(range0->addr); + mask.s_addr = htonl(range0->mask); + loop.s_addr = htonl(INADDR_LOOPBACK); + + in_route_delete(&dest, &mask, &loop, RTF_BLACKHOLE); + } + log_printf(LOG_NOTICE, "Deleted routes for pooled addresses"); +} + + +/* #inlude <arpa/nameser_compat.h> も GETSHORT を定義している */ +#undef GETCHAR +#undef GETSHORT +#undef PUTSHORT + +#define GETCHAR(c, cp) { (c) = *(cp)++; } +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} +#define TCP_OPTLEN_IN_SEGMENT 12 /* timestamp option and padding */ +#define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \ + TCP_OPTLEN_IN_SEGMENT) + +/* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */ +/* + * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $ + */ + +/* + * The following macro is used to update an internet checksum. "acc" is a + * 32-bit accumulation of all the changes to the checksum (adding in old + * 16-bit words and subtracting out new words), and "cksum" is the checksum + * value to be updated. + */ +#define ADJUST_CHECKSUM(acc, cksum) { \ + acc += cksum; \ + if (acc < 0) { \ + acc = -acc; \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) ~acc; \ + } else { \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) acc; \ + } \ +} + +/** + * IPパケットが MTU 以下となるように mss を調整します。 + * @param pktp IPパケットのポインタ + * @param lpktp 長さ + * @param mtu MTU + */ +int +adjust_tcp_mss(u_char *pktp, int lpktp, int mtu) +{ + int opt, optlen, acc, ip_off, mss, maxmss; + struct ip *pip; + struct tcphdr *th; + + if (lpktp < sizeof(struct ip) + sizeof(struct tcphdr)) + return 1; + + pip = (struct ip *)pktp; + ip_off = ntohs(pip->ip_off); + + /* TCP じゃないパケットやフラグメントされたパケットは対象外 */ + if (pip->ip_p != IPPROTO_TCP || (ip_off & IP_MF) != 0 || + (ip_off & IP_OFFMASK) != 0) + return 0; + + pktp += pip->ip_hl << 2; + lpktp -= pip->ip_hl << 2; + + /* 壊れてる */ + if (sizeof(struct tcphdr) > lpktp) + return 1; + + th = (struct tcphdr *)pktp; + /* MSS は SYN がセットされたセグメントに限る。(See RFC 793) */ + if ((th->th_flags & TH_SYN) == 0) + return 0; + + pktp += sizeof(struct tcphdr); + lpktp -= sizeof(struct tcphdr); + + while (lpktp >= TCPOLEN_MAXSEG) { + GETCHAR(opt, pktp); + switch (opt) { + case TCPOPT_MAXSEG: + GETCHAR(optlen, pktp); + GETSHORT(mss, pktp); + maxmss = MAXMSS(mtu); + if (mss > maxmss) { + pktp-=2; + PUTSHORT(maxmss, pktp); + acc = htons(mss); + acc -= htons(maxmss); + ADJUST_CHECKSUM(acc, th->th_sum); + } + return 0; + /* NOTREACHED */ + break; + case TCPOPT_EOL: + return 0; + /* NOTREACHED */ + break; + case TCPOPT_NOP: + lpktp--; + break; + default: + GETCHAR(optlen, pktp); + pktp += 2 - optlen; + lpktp -= optlen; + break; + } + } + return 0; +} + +void +set_faith(int sock, int onoff) +{ +#ifdef IP_FAITH + int ival; + + ival = onoff; + if(setsockopt(sock, IPPROTO_IP, IP_FAITH, &ival, sizeof(ival)) + < 0) { + log_printf(LOG_DEBUG, + "setsockopt(IP_FAITH) failed at %s(): %m", __func__); + } +#endif +} diff --git a/usr.sbin/npppd/npppd/npppd_subr.h b/usr.sbin/npppd/npppd/npppd_subr.h new file mode 100644 index 00000000000..e055fe1b257 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_subr.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_SUBR_H +#define NPPPD_SUBR_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +int load_resolv_conf (struct in_addr *, struct in_addr *); +int in_host_route_add (struct in_addr *, struct in_addr *, const char *, int); +int in_host_route_delete (struct in_addr *, struct in_addr *); +int in_route_add (struct in_addr *, struct in_addr *, struct in_addr *, const char *, uint32_t, int); +int in_route_delete (struct in_addr *, struct in_addr *, struct in_addr *, uint32_t); +int ip_is_idle_packet (const struct ip *, int); +void in_addr_range_add_route (struct in_addr_range *); +void in_addr_range_delete_route (struct in_addr_range *); +int adjust_tcp_mss(u_char *, int, int); +void set_faith(int, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/npppd_tun.c b/usr.sbin/npppd/npppd/npppd_tun.c new file mode 100644 index 00000000000..fe4df1613cf --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_tun.c @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * {@link ::_npppd_tun トンネルデバイス} に関する処理を提供します。 + */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +#include <stdio.h> +#include <syslog.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <event.h> + +#include "npppd_local.h" +#include "debugutil.h" +#include "debugmacro.h" + +#include "npppd_tun.h" + +static void npppd_tundev_io_event_handler (int, short, void *); + +#ifdef NPPPD_TUN_DEBUG +#define NPPPD_TUN_ASSERT(cond) ASSERT(cond) +#else +#define NPPPD_TUN_ASSERT(cond) +#endif + + +/** + * {@link ::_npppd_tun トンネルデバイスインスタンス}を初期化します。 + * @param minor デバイスマイナー番号 + */ +void +npppd_tundev_init(npppd *_this, int minor) +{ + NPPPD_TUN_ASSERT(_this != NULL); + NPPPD_TUN_ASSERT(_this->tun_file <= 0); + + _this->tun_file = -1; + _this->tun_minor = minor; +} + +/** + * {@link ::_npppd_tun トンネルデバイスインスタンス}を開始します。 + */ +int +npppd_tundev_start(npppd *_this) +{ + int x, sock; + char buf[MAXPATHLEN]; + struct ifaliasreq ifra; + struct sockaddr_in *sin0; + + NPPPD_TUN_ASSERT(_this != NULL); + + snprintf(buf, sizeof(buf), "/dev/tun%d", _this->tun_minor); + if ((_this->tun_file = open(buf, O_RDWR, 0600)) < 0) { + log_printf(LOG_ERR, "open(%s) failed in %s(): %m", + buf, __func__); + goto reigai; + } + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + log_printf(LOG_ERR, "socket() failed in %s(): %m", __func__); + goto reigai; + } + // ifconfig tun1 10.0.0.1 netmask 255.255.255.25 + + memset(&ifra, 0, sizeof(ifra)); + snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tun%d", + _this->tun_minor); + + sin0 = (struct sockaddr_in *)&ifra.ifra_addr; + sin0->sin_addr.s_addr = _this->tun_ip4_addr.s_addr; + sin0->sin_family = AF_INET; + sin0->sin_len = sizeof(struct sockaddr_in); + + sin0 = (struct sockaddr_in *)&ifra.ifra_mask; + sin0->sin_addr.s_addr = 0xffffffffL; + sin0->sin_family = AF_INET; + sin0->sin_len = sizeof(struct sockaddr_in); + + sin0 = (struct sockaddr_in *)&ifra.ifra_broadaddr; + sin0->sin_addr.s_addr = 0; + sin0->sin_family = AF_INET; + sin0->sin_len = sizeof(struct sockaddr_in); + + if (ioctl(sock, SIOCAIFADDR, &ifra) != 0 && errno != EEXIST) { + log_printf(LOG_ERR, "Cannot assign tun device ip address: %m"); + goto reigai; + } + close(sock); + + x = 1; + if (ioctl(_this->tun_file, FIONBIO, &x) != 0) { + log_printf(LOG_ERR, "ioctl(FIONBIO) failed in %s(): %m", + __func__); + goto reigai; + } + event_set(&_this->ev_tun, _this->tun_file, EV_READ | EV_PERSIST, + npppd_tundev_io_event_handler, _this); + event_add(&_this->ev_tun, NULL); + + log_printf(LOG_INFO, "Opened /dev/tun%d", _this->tun_minor); + + return 0; +reigai: + if (_this->tun_file >= 0) + close(_this->tun_file); + _this->tun_file = -1; + + return -1; +} + +/** + * トンネルデバイスに関する処理を終了します。 + */ +void +npppd_tundev_stop(npppd *_this) +{ + if (_this->tun_file >= 0) { + event_del(&_this->ev_tun); + close(_this->tun_file); + } + _this->tun_file = -1; + log_printf(LOG_NOTICE, "Closed /dev/tun%d", _this->tun_minor); +} + +/** + * IPアドレスをセットします。 + */ +int +npppd_tundev_set_ip_addr(npppd *_this) +{ + return 0; +} + +static void +npppd_tundev_io_event_handler(int fd, short evtype, void *data) +{ + int sz; + npppd *_this; + uint8_t buffer[8192]; + + NPPPD_TUN_ASSERT((evtype & EV_READ) != 0); + + _this = data; + NPPPD_TUN_ASSERT(_this->tun_file >= 0); + + do { + sz = read(_this->tun_file, buffer, sizeof(buffer)); + if (sz <= 0) { + if (sz == 0) + log_printf(LOG_ERR, "tun%d read failed: %m", + _this->tun_minor); + else if (errno == EAGAIN) + break; + else + log_printf(LOG_ERR, "tun%d file is closed", + _this->tun_minor); + npppd_tundev_stop(_this); + return; + } + npppd_network_input(_this, buffer, sz); + } while (1 /* CONSTCOND */); + + return; +} + +/** + * トンネルデバイスに書き込みます。 + */ +void +npppd_tundev_write(npppd *_this, uint8_t *pktp, int lpktp) +{ + int err; + + NPPPD_TUN_ASSERT(_this != NULL); + NPPPD_TUN_ASSERT(_this->tun_file >= 0); + + err = write(_this->tun_file, pktp, lpktp); + + if (err != lpktp) + log_printf(LOG_ERR, "tun%d write failed in %s(): %m", + _this->tun_minor, __func__); +} diff --git a/usr.sbin/npppd/npppd/npppd_tun.h b/usr.sbin/npppd/npppd/npppd_tun.h new file mode 100644 index 00000000000..12942f7b221 --- /dev/null +++ b/usr.sbin/npppd/npppd/npppd_tun.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NPPPD_TUN_H +#define NPPPD_TUN_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +void npppd_tundev_init (npppd *, int); +int npppd_tundev_start (npppd *); +int npppd_tundev_set_ip_addr (npppd *); +void npppd_tundev_stop (npppd *); +void npppd_tundev_write (npppd *, uint8_t *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/pap.c b/usr.sbin/npppd/npppd/pap.c new file mode 100644 index 00000000000..ae1029d49df --- /dev/null +++ b/usr.sbin/npppd/npppd/pap.c @@ -0,0 +1,527 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: pap.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * Password Authentication Protocol (PAP) の実装 + * @author Yasuoka Masahiko + */ +/* + * Windows 2000 で PAP を行うと、8秒間に10個の AuthReq が届きタイムアウト + * この値は CHAP の場合の半分以下なので、Radius 要求のタイムアウトの値は、 + * CHAP と PAP で別々に設定できたほうが良いかもしれない。 + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if_dl.h> +#include <netinet/in.h> + +#include <event.h> +#include <md5.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "slist.h" +#include "npppd.h" +#include "ppp.h" + +#ifdef USE_NPPPD_RADIUS +#include "radius_chap_const.h" +#endif +#include "debugutil.h" + +#define AUTHREQ 0x01 +#define AUTHACK 0x02 +#define AUTHNAK 0x03 + +#define PAP_STATE_INITIAL 0 +#define PAP_STATE_STARTING 1 +#define PAP_STATE_AUTHENTICATING 2 +#define PAP_STATE_SENT_RESPONSE 3 +#define PAP_STATE_STOPPED 4 +#define PAP_STATE_PROXY_AUTHENTICATION 5 + +#define DEFAULT_SUCCESS_MESSAGE "OK" +#define DEFAULT_FAILURE_MESSAGE "Unknown username or password" +#define DEFAULT_ERROR_MESSAGE "Unknown failure" + +#ifdef PAP_DEBUG +#define PAP_DBG(x) pap_log x +#define PAP_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define PAP_ASSERT(cond) +#define PAP_DBG(x) +#endif + +static void pap_log (pap *, uint32_t, const char *, ...) __printflike(3,4); +static void pap_response (pap *, int, const char *); +static void pap_authenticate(pap *, const char *); +static void pap_local_authenticate (pap *, const char *, const char *); +#ifdef USE_NPPPD_RADIUS +static void pap_radius_authenticate (pap *, const char *, const char *); +static void pap_radius_response (void *, RADIUS_PACKET *, int); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void pap_init (pap *, npppd_ppp *); +int pap_start (pap *); +int pap_stop (pap *); +int pap_input (pap *, u_char *, int); + +#ifdef __cplusplus +} +#endif + +void +pap_init(pap *_this, npppd_ppp *ppp) +{ + _this->ppp = ppp; + _this->state = PAP_STATE_INITIAL; + _this->auth_id = -1; +} + +int +pap_start(pap *_this) +{ + pap_log(_this, LOG_DEBUG, "%s", __func__); + + if (_this->state == PAP_STATE_PROXY_AUTHENTICATION) { + _this->state = PAP_STATE_AUTHENTICATING; + pap_authenticate(_this, _this->ppp->proxy_authen_resp); + return 0; + } + + _this->state = PAP_STATE_STARTING; + return 0; +} + +int +pap_stop(pap *_this) +{ + _this->state = PAP_STATE_STOPPED; + _this->auth_id = -1; + +#ifdef USE_NPPPD_RADIUS + if (_this->radctx != NULL) { + radius_cancel_request(_this->radctx); + _this->radctx = NULL; + } +#endif + return 0; +} + +/** PAP のパケット受信 */ +int +pap_input(pap *_this, u_char *pktp, int lpktp) +{ + int code, id, length, len; + u_char *pktp1; + char name[MAX_USERNAME_LENGTH], password[MAX_PASSWORD_LENGTH]; + + if (_this->state == PAP_STATE_STOPPED || + _this->state == PAP_STATE_INITIAL) { + pap_log(_this, LOG_ERR, "Received pap packet. But pap is " + "not started."); + return -1; + } + pktp1 = pktp; + + GETCHAR(code, pktp1); + GETCHAR(id, pktp1); + GETSHORT(length, pktp1); + + if (code != AUTHREQ) { + pap_log(_this, LOG_ERR, "%s: Received unknown code=%d", + __func__, code); + return -1; + } + if (lpktp < length) { + pap_log(_this, LOG_ERR, "%s: Received broken packet.", + __func__); + return -1; + } + + /* ユーザ名を取り出し */ +#define remlen (lpktp - (pktp1 - pktp)) + if (remlen < 1) + goto reigai; + GETCHAR(len, pktp1); + if (len <= 0) + goto reigai; + if (remlen < len) + goto reigai; + if (len > 0) + memcpy(name, pktp1, len); + name[len] = '\0'; + pktp1 += len; + + if (_this->state != PAP_STATE_STARTING) { + /* + * まったく同じ要求を2度受け取った場合は、先方の再送による + * もの。UserName が同じならば続行する。 + */ + if ((_this->state == PAP_STATE_AUTHENTICATING || + _this->state == PAP_STATE_SENT_RESPONSE) && + strcmp(_this->name, name) == 0) { + /* 続行 */ + } else { + pap_log(_this, LOG_ERR, + "Received AuthReq is not same as before. " + "(%d,%s) != (%d,%s)", id, name, _this->auth_id, + _this->name); + _this->auth_id = id; + goto reigai; + } + } + if (_this->state == PAP_STATE_AUTHENTICATING) + return 0; + _this->auth_id = id; + strlcpy(_this->name, name, sizeof(_this->name)); + + _this->state = PAP_STATE_AUTHENTICATING; + + /* パスワードを取り出し */ + if (remlen < 1) + goto reigai; + GETCHAR(len, pktp1); + if (remlen < len) + goto reigai; + if (len > 0) + memcpy(password, pktp1, len); + + password[len] = '\0'; + pap_authenticate(_this, password); + + return 0; +reigai: + pap_response(_this, 0, DEFAULT_FAILURE_MESSAGE); + return -1; +} + +static void +pap_authenticate(pap *_this, const char *password) +{ + if (npppd_ppp_bind_realm(_this->ppp->pppd, _this->ppp, _this->name, 0) + == 0) { + if (!npppd_ppp_is_realm_ready(_this->ppp->pppd, _this->ppp)) { + pap_log(_this, LOG_INFO, + "username=\"%s\" realm is not ready.", _this->name); + goto reigai; + /* NOTREACHED */ + } +#if USE_NPPPD_RADIUS + if (npppd_ppp_is_realm_radius(_this->ppp->pppd, _this->ppp)) { + pap_radius_authenticate(_this, _this->name, password); + return; + /* NOTREACHED */ + } else +#endif + if (npppd_ppp_is_realm_local(_this->ppp->pppd, _this->ppp)) { + pap_local_authenticate(_this, _this->name, password); + return; + /* NOTREACHED */ + } + } +reigai: + pap_response(_this, 0, DEFAULT_FAILURE_MESSAGE); +} + +static void +pap_log(pap *_this, uint32_t prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=pap %s", + _this->ppp->id, fmt); + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +static void +pap_response(pap *_this, int authok, const char *mes) +{ + int lpktp, lmes; + u_char *pktp, *pktp1; + const char *realm; + + pktp = ppp_packetbuf(_this->ppp, PPP_PROTO_PAP) + HEADERLEN; + lpktp = _this->ppp->mru - HEADERLEN; + realm = npppd_ppp_get_realm_name(_this->ppp->pppd, _this->ppp); + + pktp1 = pktp; + if (mes == NULL) + lmes = 0; + else + lmes = strlen(mes); + lmes = MIN(lmes, lpktp - 1); + + PUTCHAR(lmes, pktp1); + if (lmes > 0) + memcpy(pktp1, mes, lmes); + lpktp = lmes + 1; + + if (authok) + ppp_output(_this->ppp, PPP_PROTO_PAP, AUTHACK, _this->auth_id, + pktp, lpktp); + else + ppp_output(_this->ppp, PPP_PROTO_PAP, AUTHNAK, _this->auth_id, + pktp, lpktp); + + if (!authok) { + pap_log(_this, LOG_ALERT, + "logtype=Failure username=\"%s\" realm=%s", _this->name, + realm); + pap_stop(_this); + /* 失敗したら ppp 終了 */ + ppp_stop_ex(_this->ppp, "Authentication Required", + PPP_DISCON_AUTH_FAILED, PPP_PROTO_PAP, 1 /* peer */, NULL); + } else { + strlcpy(_this->ppp->username, _this->name, + sizeof(_this->ppp->username)); + pap_log(_this, LOG_INFO, + "logtype=Success username=\"%s\" realm=%s", _this->name, + realm); + pap_stop(_this); + ppp_auth_ok(_this->ppp); + // 再送要求に答えるために pap_stop でのセットを上書きします。 + _this->state = PAP_STATE_SENT_RESPONSE; + } +} + +/** PAP認証 */ +static void +pap_local_authenticate(pap *_this, const char *username, const char *password) +{ + int lpassword0; + char password0[MAX_PASSWORD_LENGTH]; + + lpassword0 = sizeof(password0); + + if (npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, + password0, &lpassword0) == 0) { + if (!strcmp(password0, password)) { + pap_response(_this, 1, DEFAULT_SUCCESS_MESSAGE); + return; + } + } + pap_response(_this, 0, DEFAULT_FAILURE_MESSAGE); +} + +/*********************************************************************** + * Proxy Authentication + ***********************************************************************/ +int +pap_proxy_authen_prepare(pap *_this, dialin_proxy_info *dpi) +{ + + PAP_ASSERT(dpi->auth_type == PPP_AUTH_PAP); + PAP_ASSERT(_this->state == PAP_STATE_INITIAL); + + _this->auth_id = dpi->auth_id; + if (strlen(dpi->username) >= sizeof(_this->name)) { + pap_log(_this, LOG_NOTICE, + "\"Proxy Authen Name\" is too long."); + return -1; + } + + /* copy the authenticaiton properties */ + PAP_ASSERT(_this->ppp->proxy_authen_resp == NULL); + if ((_this->ppp->proxy_authen_resp = malloc(dpi->lauth_resp + 1)) == + NULL) { + pap_log(_this, LOG_ERR, "malloc() failed in %s(): %m", + __func__); + return -1; + } + memcpy(_this->ppp->proxy_authen_resp, dpi->auth_resp, + dpi->lauth_resp); + _this->ppp->proxy_authen_resp[dpi->lauth_resp] = '\0'; + strlcpy(_this->name, dpi->username, sizeof(_this->name)); + + _this->state = PAP_STATE_PROXY_AUTHENTICATION; + + return 0; +} + +#ifdef USE_NPPPD_RADIUS +static void +pap_radius_authenticate(pap *_this, const char *username, const char *password) +{ + void *radctx; + RADIUS_PACKET *radpkt; + MD5_CTX md5ctx; + int i, j, s_len, passlen; + u_char ra[16], digest[16], pass[128]; + const char *s; + radius_req_setting *rad_setting = NULL; + char buf0[MAX_USERNAME_LENGTH]; + + if ((rad_setting = npppd_get_radius_req_setting(_this->ppp->pppd, + _this->ppp)) == NULL) + goto reigai; + + if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) + == NULL) + goto reigai; + + if (radius_prepare(rad_setting, _this, &radctx, + pap_radius_response, _this->ppp->auth_timeout) != 0) { + radius_delete_packet(radpkt); + goto reigai; + } + + if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) + != 0) + goto reigai; + + if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME, + npppd_ppp_get_username_for_auth(_this->ppp->pppd, _this->ppp, + username, buf0)) != 0) + goto reigai; + + if (_this->radctx != NULL) + radius_cancel_request(_this->radctx); + + _this->radctx = radctx; + + /* + * RADIUS User-Password アートリビュートの作成 + * (RFC 2865, "5.2. User-Password") + */ + s = radius_get_server_secret(_this->radctx); + s_len = strlen(s); + + memset(pass, 0, sizeof(pass)); // null-padding + passlen = MIN(strlen(password), sizeof(pass)); + memcpy(pass, password, passlen); + if ((passlen % 16) != 0) + passlen += 16 - (passlen % 16); + + radius_get_authenticator(radpkt, ra); + + MD5Init(&md5ctx); + MD5Update(&md5ctx, s, s_len); + MD5Update(&md5ctx, ra, 16); + MD5Final(digest, &md5ctx); + + for (i = 0; i < 16; i++) + pass[i] ^= digest[i]; + + while (i < passlen) { + MD5Init(&md5ctx); + MD5Update(&md5ctx, s, s_len); + MD5Update(&md5ctx, &pass[i - 16], 16); + MD5Final(digest, &md5ctx); + + for (j = 0; j < 16; j++, i++) + pass[i] ^= digest[j]; + } + + if (radius_put_raw_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, pass, + passlen) != 0) + goto reigai; + + radius_request(_this->radctx, radpkt); + + return; +reigai: + if (_this->radctx != NULL) + radius_cancel_request(_this->radctx); + pap_log(_this, LOG_ERR, "%s() failed: %m", __func__); + pap_response(_this, 0, DEFAULT_ERROR_MESSAGE); + + return; +} + +static void +pap_radius_response(void *context, RADIUS_PACKET *pkt, int flags) +{ + int code = -1; + const char *reason = NULL; + RADIUS_REQUEST_CTX radctx; + pap *_this; + + _this = context; + radctx = _this->radctx; + _this->radctx = NULL; /* 大事 */ + + if (pkt == NULL) { + if (flags & RADIUS_REQUST_TIMEOUT) { + reason = "timeout"; + npppd_radius_server_failure_notify(_this->ppp->pppd, + _this->ppp, radctx, "request timeout"); + } else { + reason = "error"; + npppd_radius_server_failure_notify(_this->ppp->pppd, + _this->ppp, radctx, "unknown error"); + } + goto auth_failed; + } + code = radius_get_code(pkt); + if (code == RADIUS_CODE_ACCESS_REJECT) { + reason="reject"; + goto auth_failed; + } else if (code != RADIUS_CODE_ACCESS_ACCEPT) { + reason="error"; + goto auth_failed; + } + if ((flags & RADIUS_REQUST_CHECK_AUTHENTICTOR_OK) == 0 && + (flags & RADIUS_REQUST_CHECK_AUTHENTICTOR_NO_CHECK) == 0) { + reason="bad_authenticator"; + npppd_radius_server_failure_notify(_this->ppp->pppd, _this->ppp, + radctx, "bad authenticator"); + goto auth_failed; + } + // 認証 OK + pap_response(_this, 1, DEFAULT_SUCCESS_MESSAGE); + ppp_proccess_radius_framed_ip(_this->ppp, pkt); + radius_delete_packet(pkt); + + return; +auth_failed: + // 認証 NG + pap_log(_this, LOG_WARNING, "Radius authentication request failed: %s", + reason); + if (pkt != NULL) + radius_delete_packet(pkt); + + pap_response(_this, 0, DEFAULT_FAILURE_MESSAGE); +} +#endif diff --git a/usr.sbin/npppd/npppd/pathnames.h b/usr.sbin/npppd/npppd/pathnames.h new file mode 100644 index 00000000000..0f0778f9507 --- /dev/null +++ b/usr.sbin/npppd/npppd/pathnames.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PATHNAMES_H +#define PATHNAMES_H 1 + +#ifndef DEFAULT_NPPPD_CONF +#define DEFAULT_NPPPD_CONF "/etc/npppd/npppd.conf" +#endif + +#ifndef DEFAULT_NPPPD_USERSFILE +#define DEFAULT_NPPPD_USERSFILE "/etc/npppd/npppd-users.csv" +#endif + +#ifndef DEFAULT_NPPPD_PIDFILE +#define DEFAULT_NPPPD_PIDFILE "/var/run/npppd.pid" +#endif + +#ifndef DEFAULT_NPPPD_CTL_SOCK_PATH +#define DEFAULT_NPPPD_CTL_SOCK_PATH "/var/run/npppd_ctl" +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/ppp.c b/usr.sbin/npppd/npppd/ppp.c new file mode 100644 index 00000000000..09d21df1d49 --- /dev/null +++ b/usr.sbin/npppd/npppd/ppp.c @@ -0,0 +1,1130 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: ppp.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * {@link :: _npppd_ppp PPPインスタンス} に関する処理を提供します。 + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <netdb.h> +#include <stdio.h> +#include <stdarg.h> +#include <strings.h> +#include <unistd.h> +#include <errno.h> +#include <syslog.h> +#include <sys/time.h> +#include <time.h> +#include <event.h> + +#include "slist.h" + +#include "npppd.h" +#include "time_utils.h" +#include "ppp.h" +#include "psm-opt.h" + +#include "debugutil.h" + +#ifdef PPP_DEBUG +#define PPP_DBG(x) ppp_log x +#define PPP_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define PPP_ASSERT(cond) +#define PPP_DBG(x) +#endif + +#include "debugutil.h" + +static u_int32_t ppp_seq = 0; + +static void ppp_stop0 __P((npppd_ppp *)); +static int ppp_recv_packet (npppd_ppp *, unsigned char *, int, int); +static const char * ppp_peer_auth_string(npppd_ppp *); +static void ppp_idle_timeout(int, short, void *); +#ifdef USE_NPPPD_PIPEX +static void ppp_on_network_pipex(npppd_ppp *); +#endif + +#define AUTH_IS_PAP(ppp) ((ppp)->peer_auth == PPP_AUTH_PAP) +#define AUTH_IS_CHAP(ppp) ((ppp)->peer_auth == PPP_AUTH_CHAP_MD5 ||\ + (ppp)->peer_auth == PPP_AUTH_CHAP_MS || \ + (ppp)->peer_auth == PPP_AUTH_CHAP_MS_V2) +#define AUTH_IS_EAP(ppp) ((ppp)->peer_auth == PPP_AUTH_EAP) + +/* + * 終了処理 + * ppp_lcp_finished LCP が終了 + * 先方が TermReq + * こちらが TermReq (ppp_stop から状態遷移で) + * ppp_phy_downed 物理層が切れた + * + * どちらも ppp_stop0、ppp_down_others を呼び出している。 + */ +/** npppd_ppp オブジェクトを生成。 */ +npppd_ppp * +ppp_create() +{ + npppd_ppp *_this; + + if ((_this = malloc(sizeof(npppd_ppp))) == NULL) { + log_printf(LOG_ERR, "malloc() failed in %s(): %m", __func__ ); + return NULL; + } + memset(_this, 0, sizeof(npppd_ppp)); + + _this->snp.snp_family = AF_INET; + _this->snp.snp_len = sizeof(_this->snp); + _this->snp.snp_type = SNP_PPP; + _this->snp.snp_data_ptr = _this; + + return _this; +} + +/** + * npppd_ppp を初期化します。 + * npppd_ppp#mru npppd_ppp#phy_label は呼び出し前にセットしてください。 + */ +int +ppp_init(npppd *pppd, npppd_ppp *_this) +{ + + PPP_ASSERT(_this != NULL); + PPP_ASSERT(strlen(_this->phy_label) > 0); + + _this->id = -1; + _this->ifidx = -1; + _this->has_acf = 1; + _this->recv_packet = ppp_recv_packet; + _this->id = ppp_seq++; + _this->pppd = pppd; + + lcp_init(&_this->lcp, _this); + + _this->mru = ppp_config_int(_this, "lcp.mru", DEFAULT_MRU); + if (_this->outpacket_buf == NULL) { + _this->outpacket_buf = malloc(_this->mru + 64); + if (_this->outpacket_buf == NULL){ + log_printf(LOG_ERR, "malloc() failed in %s(): %m", + __func__); + return -1; + } + } + _this->adjust_mss = ppp_config_str_equal(_this, "ip.adjust_mss", "true", + 0); +#ifdef USE_NPPPD_PIPEX + _this->use_pipex = ppp_config_str_equal(_this, "pipex.enabled", "true", + 1); +#endif + /* + * ログの設定を読み込む。 + */ + _this->log_dump_in = + ppp_config_str_equal(_this, "log.in.pktdump", "true", 0); + _this->log_dump_out = + ppp_config_str_equal(_this, "log.out.pktdump", "true", 0); + + +#ifdef USE_NPPPD_MPPE + mppe_init(&_this->mppe, _this); +#endif + ccp_init(&_this->ccp, _this); + ipcp_init(&_this->ipcp, _this); + pap_init(&_this->pap, _this); + chap_init(&_this->chap, _this); + + /* アイドルタイマー関連 */ + _this->timeout_sec = ppp_config_int(_this, "idle_timeout", 0); + if (!evtimer_initialized(&_this->idle_event)) + evtimer_set(&_this->idle_event, ppp_idle_timeout, _this); + + _this->auth_timeout = ppp_config_int(_this, "auth.timeout", + DEFAULT_AUTH_TIMEOUT); + + _this->lcp.echo_interval = ppp_config_int(_this, + "lcp.echo_interval", DEFAULT_LCP_ECHO_INTERVAL); + _this->lcp.echo_max_retries = ppp_config_int(_this, + "lcp.echo_max_retries", DEFAULT_LCP_ECHO_MAX_RETRIES); + _this->lcp.echo_retry_interval = ppp_config_int(_this, + "lcp.echo_retry_interval", DEFAULT_LCP_ECHO_RETRY_INTERVAL); + + return 0; +} + +static void +ppp_set_tunnel_label(npppd_ppp *_this, char *buf, int lbuf) +{ + int flag, af; + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + + hbuf[0] = 0; + sbuf[0] = 0; + af = ((struct sockaddr *)&_this->phy_info)->sa_family; + if (af < AF_MAX) { + flag = NI_NUMERICHOST; + if (af == AF_INET || af == AF_INET6) + flag |= NI_NUMERICSERV; + if (getnameinfo((struct sockaddr *)&_this->phy_info, + ((struct sockaddr *)&_this->phy_info)->sa_len, hbuf, + sizeof(hbuf), sbuf, sizeof(sbuf), flag) != 0) { + ppp_log(_this, LOG_ERR, "getnameinfo() failed at %s", + __func__); + strlcpy(hbuf, "0.0.0.0", sizeof(hbuf)); + strlcpy(sbuf, "0", sizeof(sbuf)); + } + if (af == AF_INET || af == AF_INET6) + snprintf(buf, lbuf, "%s:%s", hbuf, sbuf); + else + snprintf(buf, lbuf, "%s", hbuf); + } else if (af == NPPPD_AF_PHONE_NUMBER) { + strlcpy(buf, + ((npppd_phone_number *)&_this->phy_info)->pn_number, lbuf); + } +} +/** + * npppd_ppp を開始します。 + * npppd_ppp#phy_context + * npppd_ppp#send_packet + * npppd_ppp#phy_close + * npppd_ppp#phy_info + * は呼び出し前にセットしてください。 + */ +void +ppp_start(npppd_ppp *_this) +{ + char label[512]; + + PPP_ASSERT(_this != NULL); + PPP_ASSERT(_this->recv_packet != NULL); + PPP_ASSERT(_this->send_packet != NULL); + PPP_ASSERT(_this->phy_close != NULL); + + _this->start_time = time(NULL); + _this->start_monotime = get_monosec(); + /* + * 下位レイヤの情報をログに残す。 + */ + ppp_set_tunnel_label(_this, label, sizeof(label)); + ppp_log(_this, LOG_INFO, "logtype=Started tunnel=%s(%s)", + _this->phy_label, label); + + lcp_lowerup(&_this->lcp); +} + +/** + * Dialin proxy の準備をします。dialin proxy できない場合には、0 以外が + * 返ります。 + */ +int +ppp_dialin_proxy_prepare(npppd_ppp *_this, dialin_proxy_info *dpi) +{ + int renego_force, renego; + + renego = (ppp_config_str_equal(_this, + "l2tp.dialin.lcp_renegotiation", "disable", 0))? 0 : 1; + renego_force = ppp_config_str_equal(_this, + "l2tp.dialin.lcp_renegotiation", "force", 0); + if (renego_force) + renego = 1; + + if (lcp_dialin_proxy(&_this->lcp, dpi, renego, renego_force) != 0) { + ppp_log(_this, LOG_ERR, + "Failed to proxy-dialin, proxied lcp is broken."); + return 1; + } + + return 0; +} + +static void +ppp_down_others(npppd_ppp *_this) +{ + fsm_lowerdown(&_this->ccp.fsm); + fsm_lowerdown(&_this->ipcp.fsm); + + npppd_release_ip(_this->pppd, _this); + if (AUTH_IS_PAP(_this)) + pap_stop(&_this->pap); + if (AUTH_IS_CHAP(_this)) + chap_stop(&_this->chap); +#ifdef USE_NPPPD_EAP_RADIUS + if (AUTH_IS_EAP(_this)) + eap_stop(&_this->eap); +#endif + evtimer_del(&_this->idle_event); +} + +void +ppp_stop(npppd_ppp *_this, const char *reason) +{ + ppp_stop_ex(_this, reason, PPP_DISCON_NO_INFORMATION, 0, 0, NULL); +} + +/** + * PPP を停止し、npppd_ppp オブジェクトを破棄します。 + * @param reason 停止の理由。特に理由がなければ NULL を指定します。 + * この値で LCP の TermReq パケットの reason フィールドに格納されて、 + * 先方に通知されます。 + * @param code disconnect code in {@link ::npppd_ppp_disconnect_code}. + * @param proto control protocol number. see RFC3145. + * @param direction disconnect direction. see RFC 3145 + */ +void +ppp_stop_ex(npppd_ppp *_this, const char *reason, + npppd_ppp_disconnect_code code, int proto, int direction, + const char *message) +{ + PPP_ASSERT(_this != NULL); + + if (_this->disconnect_code == PPP_DISCON_NO_INFORMATION) { + _this->disconnect_code = code; + _this->disconnect_proto = proto; + _this->disconnect_direction = direction; + _this->disconnect_message = message; + } + ppp_down_others(_this); + fsm_close(&_this->lcp.fsm, reason); +} + +static void +ppp_stop0(npppd_ppp *_this) +{ + char mppe_str[BUFSIZ]; + char label[512]; + + _this->end_monotime = get_monosec(); + + if (_this->phy_close != NULL) + _this->phy_close(_this); + _this->phy_close = NULL; + + /* + * PPTP(GRE) の NAT/ブラックホール検出 + */ + if (_this->lcp.dialin_proxy != 0 && + _this->lcp.dialin_proxy_lcp_renegotiation == 0) { + /* + * dialin-proxy、再ネゴシエーション無しでは LCPのやりとりは + * ない + */ + } else if (_this->lcp.recv_ress == 0) { // 応答なし + if (_this->lcp.recv_reqs == 0) // 要求なし + ppp_log(_this, LOG_WARNING, "no PPP frames from the " + "peer. router/NAT issue? (may have filtered out)"); + else + ppp_log(_this, LOG_WARNING, "my PPP frames may not " + "have arrived at the peer. router/NAT issue? (may " + "be the only-first-person problem)"); + } +#ifdef USE_NPPPD_PIPEX + if (npppd_ppp_pipex_disable(_this->pppd, _this) != 0) + ppp_log(_this, LOG_ERR, + "npppd_ppp_pipex_disable() failed: %m"); +#endif + + ppp_set_tunnel_label(_this, label, sizeof(label)); +#ifdef USE_NPPPD_MPPE + if (_this->mppe_started) { + snprintf(mppe_str, sizeof(mppe_str), + "mppe=yes mppe_in=%dbits,%s mppe_out=%dbits,%s", + _this->mppe.recv.keybits, + (_this->mppe.recv.stateless)? "stateless" : "stateful", + _this->mppe.send.keybits, + (_this->mppe.send.stateless)? "stateless" : "stateful"); + } else +#endif + snprintf(mppe_str, sizeof(mppe_str), "mppe=no"); + ppp_log(_this, LOG_NOTICE, + "logtype=TUNNELUSAGE user=\"%s\" duration=%ldsec layer2=%s " + "layer2from=%s auth=%s data_in=%qubytes,%upackets " + "data_out=%qubytes,%upackets error_in=%u error_out=%u %s " + "iface=%s", + _this->username[0]? _this->username : "<unknown>", + (long)(_this->end_monotime - _this->start_monotime), + _this->phy_label, label, + _this->username[0]? ppp_peer_auth_string(_this) : "none", + _this->ibytes, _this->ipackets, _this->obytes, _this->opackets, + _this->ierrors, _this->oerrors, mppe_str, + npppd_ppp_get_iface_name(_this->pppd, _this)); + + npppd_ppp_unbind_iface(_this->pppd, _this); +#ifdef USE_NPPPD_MPPE + mppe_fini(&_this->mppe); +#endif + evtimer_del(&_this->idle_event); + + npppd_release_ip(_this->pppd, _this); + ppp_destroy(_this); +} + +/** + * npppd_ppp オブジェクトを破棄します。ppp_start をコール後は、ppp_stop() を + * を使用し、この関数は使いません。 + */ +void +ppp_destroy(void *ctx) +{ + npppd_ppp *_this = ctx; + + if (_this->proxy_authen_resp != NULL) + free(_this->proxy_authen_resp); + /* + * ppp_stop しても、先方から PPP フレームが届き、また開始してしま + * っている場合があるので、再度 down, stop + */ + fsm_lowerdown(&_this->ccp.fsm); + fsm_lowerdown(&_this->ipcp.fsm); + pap_stop(&_this->pap); + chap_stop(&_this->chap); + + if (_this->outpacket_buf != NULL) + free(_this->outpacket_buf); + + free(_this); +} + +/************************************************************************ + * プロトコルに関するイベント + ************************************************************************/ +static const char * +ppp_peer_auth_string(npppd_ppp *_this) +{ + switch(_this->peer_auth) { + case PPP_AUTH_PAP: return "PAP"; + case PPP_AUTH_CHAP_MD5: return "MD5-CHAP"; + case PPP_AUTH_CHAP_MS: return "MS-CHAP"; + case PPP_AUTH_CHAP_MS_V2: return "MS-CHAP-V2"; + case PPP_AUTH_EAP: return "EAP"; + default: return "ERROR"; + } +} + +/** + * LCPがアップした場合に呼び出されます。 + */ +void +ppp_lcp_up(npppd_ppp *_this) +{ +#ifdef USE_NPPPD_MPPE + if (MPPE_REQUIRED(_this) && !MPPE_MUST_NEGO(_this)) { + ppp_log(_this, LOG_ERR, "MPPE is required, auth protocol must " + "be MS-CHAP-V2 or EAP"); + ppp_stop(_this, "Encryption required"); + return; + } +#endif + /* + * 相手が大きな MRU を指定しても、自分の MRU 以下にする。ここで、 + * peer_mtu を縮めれると、経路 MTU が縮むので、MRU を越えるような + * パケットは到達しないようになる。(ことを期待している) + */ + if (_this->peer_mru > _this->mru) + _this->peer_mru = _this->mru; + + if (_this->peer_auth != 0 && _this->auth_runonce == 0) { + if (AUTH_IS_PAP(_this)) { + pap_start(&_this->pap); + _this->auth_runonce = 1; + return; + } + if (AUTH_IS_CHAP(_this)) { + chap_start(&_this->chap); + _this->auth_runonce = 1; + return; + } +#ifdef USE_NPPPD_EAP_RADIUS + if (AUTH_IS_EAP(_this)) { + eap_init(&_this->eap, _this); + eap_start(&_this->eap); + return; + } +#endif + } + if (_this->peer_auth == 0) + ppp_auth_ok(_this); +} + +/** + * LCPが終了した場合に呼び出されます。 + * <p>STOPPED また CLOSED ステートに入った場合に呼び出されます。</p> + */ +void +ppp_lcp_finished(npppd_ppp *_this) +{ + PPP_ASSERT(_this != NULL); + + ppp_down_others(_this); + + fsm_lowerdown(&_this->lcp.fsm); + ppp_stop0(_this); +} + +/** + * 物理層が切断された場合に物理層から呼び出されます。 + * <p> + * 物理層が PPPフレームを入出力できないという状況でこの関数を呼び出して + * ください。紳士的に PPP を切断する場合には、{@link ::#ppp_stop} を使い + * ます。</p> + */ +void +ppp_phy_downed(npppd_ppp *_this) +{ + PPP_ASSERT(_this != NULL); + + ppp_down_others(_this); + fsm_lowerdown(&_this->lcp.fsm); + fsm_close(&_this->lcp.fsm, NULL); + + ppp_stop0(_this); +} + +static const char * +proto_name(uint16_t proto) +{ + switch (proto) { + case PPP_PROTO_IP: return "ip"; + case PPP_PROTO_LCP: return "lcp"; + case PPP_PROTO_PAP: return "pap"; + case PPP_PROTO_CHAP: return "chap"; + case PPP_PROTO_EAP: return "eap"; + case PPP_PROTO_MPPE: return "mppe"; + case PPP_PROTO_NCP | NCP_CCP: return "ccp"; + case PPP_PROTO_NCP | NCP_IPCP: return "ipcp"; + // 以下ログ出力用 + case PPP_PROTO_NCP | NCP_IP6CP: return "ip6cp"; + case PPP_PROTO_ACSP: return "acsp"; + } + return "unknown"; +} + +/** 認証が成功した場合に呼び出されます。*/ +void +ppp_auth_ok(npppd_ppp *_this) +{ + if (npppd_ppp_bind_iface(_this->pppd, _this) != 0) { + ppp_log(_this, LOG_WARNING, "No interface binding."); + ppp_stop(_this, NULL); + + return; + } + if (_this->realm != NULL) { + npppd_ppp_get_username_for_auth(_this->pppd, _this, + _this->username, _this->username); + if (!npppd_check_calling_number(_this->pppd, _this)) { + ppp_log(_this, LOG_ALERT, + "logtype=TUNNELDENY user=\"%s\" " + "reason=\"Calling number check is failed\"", + _this->username); + /* XXX */ + ppp_stop(_this, NULL); + return; + } + } + if (_this->peer_auth != 0) { + /* ユーザ毎の最大接続数を制限する */ + if (!npppd_check_user_max_session(_this->pppd, _this)) { +#ifdef IDGW + ppp_log(_this, LOG_ALERT, "logtype=TUNNELDENY user=\"%s\" " + "reason=\"PPP duplicate login limit exceeded\"", + _this->username); +#else + ppp_log(_this, LOG_WARNING, + "user %s exceeds user-max-session limit", + _this->username); +#endif + ppp_stop(_this, NULL); + + return; + } + PPP_ASSERT(_this->realm != NULL); + } + + if (!npppd_ppp_iface_is_ready(_this->pppd, _this)) { + ppp_log(_this, LOG_WARNING, + "interface '%s' is not ready.", + npppd_ppp_get_iface_name(_this->pppd, _this)); + ppp_stop(_this, NULL); + + return; + } + if (_this->proxy_authen_resp != NULL) { + free(_this->proxy_authen_resp); + _this->proxy_authen_resp = NULL; + } + + fsm_lowerup(&_this->ipcp.fsm); + fsm_open(&_this->ipcp.fsm); +#ifdef USE_NPPPD_MPPE + if (MPPE_MUST_NEGO(_this)) { + fsm_lowerup(&_this->ccp.fsm); + fsm_open(&_this->ccp.fsm); + } +#endif + + return; +} + +/** event からコールバックされるイベントハンドラです */ +static void +ppp_idle_timeout(int fd, short evtype, void *context) +{ + npppd_ppp *_this; + + _this = context; + + ppp_log(_this, LOG_NOTICE, "Idle timeout(%d sec)", _this->timeout_sec); + ppp_stop(_this, NULL); +} + +/** アイドルタイマーをリセットします。アイドルでは無い場合に呼び出します。 */ +void +ppp_reset_idle_timeout(npppd_ppp *_this) +{ + struct timeval tv; + + //PPP_DBG((_this, LOG_INFO, "%s", __func__)); + evtimer_del(&_this->idle_event); + if (_this->timeout_sec > 0) { + tv.tv_usec = 0; + tv.tv_sec = _this->timeout_sec; + + evtimer_add(&_this->idle_event, &tv); + } +} + +/** IPCP が完了した場合に呼び出されます */ +void +ppp_ipcp_opened(npppd_ppp *_this) +{ + time_t curr_time; + + curr_time = get_monosec(); + + npppd_set_ip_enabled(_this->pppd, _this, 1); + if (_this->logged_acct_start == 0) { + char label[512], ipstr[64]; + + ppp_set_tunnel_label(_this, label, sizeof(label)); + + strlcpy(ipstr, " ip=", sizeof(ipstr)); + strlcat(ipstr, inet_ntoa(_this->ppp_framed_ip_address), + sizeof(ipstr)); + if (_this->ppp_framed_ip_netmask.s_addr != 0xffffffffL) { + strlcat(ipstr, ":", sizeof(ipstr)); + strlcat(ipstr, inet_ntoa(_this->ppp_framed_ip_netmask), + sizeof(ipstr)); + } + + ppp_log(_this, LOG_NOTICE, + "logtype=TUNNELSTART user=\"%s\" duration=%lusec layer2=%s " + "layer2from=%s auth=%s %s iface=%s%s", + _this->username[0]? _this->username : "<unknown>", + (long)(curr_time - _this->start_monotime), + _this->phy_label, label, + _this->username[0]? ppp_peer_auth_string(_this) : "none", + ipstr, npppd_ppp_get_iface_name(_this->pppd, _this), + (_this->lcp.dialin_proxy != 0)? " dialin_proxy=yes" : "" + ); + _this->logged_acct_start = 1; + ppp_reset_idle_timeout(_this); + } +#ifdef USE_NPPPD_PIPEX + ppp_on_network_pipex(_this); +#endif +} + +/** CCP が Opened になった場合に呼び出されます。*/ +void +ppp_ccp_opened(npppd_ppp *_this) +{ +#ifdef USE_NPPPD_MPPE + if (_this->ccp.mppe_rej == 0) { + if (_this->mppe_started == 0) { + mppe_start(&_this->mppe); + } + } else { + ppp_log(_this, LOG_INFO, "mppe is rejected by peer"); + if (_this->mppe.required) + ppp_stop(_this, "MPPE is requred"); + } +#endif +#ifdef USE_NPPPD_PIPEX + ppp_on_network_pipex(_this); +#endif +} + +/************************************************************************ + * ネットワーク I/O 関連 + ************************************************************************/ +/** + * パケット受信 + * @param flags 受信したパケットについての情報をフラグで表します。 + * 現在、PPP_IO_FLAGS_MPPE_ENCRYPTED が指定される場合があります。 + * @return 成功した場合に 0 が返り、失敗した場合に 1 が返ります。 + */ +static int +ppp_recv_packet(npppd_ppp *_this, unsigned char *pkt, int lpkt, int flags) +{ + u_char *inp, *inp_proto; + uint16_t proto; + + PPP_ASSERT(_this != NULL); + + inp = pkt; + + if (lpkt < 4) { + ppp_log(_this, LOG_DEBUG, "%s(): Rcvd short header.", __func__); + return 0; + } + + + if (_this->has_acf == 0) { + /* nothing to do */ + } else if (inp[0] == PPP_ALLSTATIONS && inp[1] == PPP_UI) { + inp += 2; + } else { + /* + * Address and Control Field Compression + */ + if (!psm_opt_is_accepted(&_this->lcp, acfc) && + _this->logged_no_address == 0) { + /* + * パケット落ちが発生する環境では、こちらは LCP + * が確立していないのに、Windows 側が完了していて、 + * ACFC されたパケットが届く。 + */ + ppp_log(_this, LOG_INFO, + "%s: Rcvd broken frame. ACFC is not accepted, " + "but received ppp frame that has no address.", + __func__); + /* + * Yamaha RTX-1000 では、ACFC を Reject するのに、 + * パケットにアドレスは入っていないので、ログが + * 大量に出力されてしまう。 + */ + _this->logged_no_address = 1; + } + } + inp_proto = inp; + if ((inp[0] & 0x01) != 0) { + /* + * Protocol Field Compression + */ + if (!psm_opt_is_accepted(&_this->lcp, pfc)) { + ppp_log(_this, LOG_INFO, + "%s: Rcvd broken frame. No protocol field: " + "%02x %02x", __func__, inp[0], inp[1]); + return 1; + } + GETCHAR(proto, inp); + } else { + GETSHORT(proto, inp); + } + + if (_this->log_dump_in != 0 && debug_get_debugfp() != NULL) { + char buf[256]; + + snprintf(buf, sizeof(buf), "log.%s.in.pktdump", + proto_name(proto)); + if (ppp_config_str_equal(_this, buf, "true", 0) != 0) { + ppp_log(_this, LOG_DEBUG, + "PPP input dump proto=%s(%d/%04x)", + proto_name(proto), proto, proto); + show_hd(debug_get_debugfp(), pkt, lpkt); + } + } +#ifdef USE_NPPPD_PIPEX + if (_this->pipex_enabled != 0 && + _this->tunnel_type == PPP_TUNNEL_PPPOE) { + switch (proto) { + case PPP_PROTO_IP: + return 2; /* handled by PIPEX */ + case PPP_PROTO_NCP | NCP_CCP: + if (lpkt - (inp - pkt) < 4) + break; /* エラーだが fsm.c で処理 */ + if (*inp == 0x0e || /* Reset-Request */ + *inp == 0x0f /* Reset-Ack */) { + return 2; /* handled by PIPEX */ + } + /* FALLTHROUGH */ + default: + break; + } + } +#endif /* USE_NPPPD_PIPEX */ + + // MPPE のチェック + switch (proto) { +#ifdef USE_NPPPD_MPPE + case PPP_PROTO_IP: + if ((flags & PPP_IO_FLAGS_MPPE_ENCRYPTED) == 0) { + if (MPPE_REQUIRED(_this)) { + /* MPPE 必須なのに、生 IP。*/ + + if (_this->logged_naked_ip == 0) { + ppp_log(_this, LOG_INFO, + "mppe is required but received " + "naked IP."); + /* ログに残すのは最初の 1 回だけ */ + _this->logged_naked_ip = 1; + } + /* + * Windows は、MPPE 未確立、IPCP 確立の状態で + * 生IPパケットを投げてくる※1。CCP がパケット + * ロスなどで確立が遅れた場合、高確率でこの状 + * 態に陥る。ここで ppp_stop する場合、パケット + * ロスや順序入れ換えが発生する環境では、『繋 + * がらない』現象にみえる。 + * (※1 少なくとも Windows 2000 Pro SP4) + ppp_stop(_this, "Encryption is required."); + */ + return 1; + } + if (MPPE_READY(_this)) { + /* MPPE 確立したのに、生 IP。*/ + ppp_log(_this, LOG_WARNING, + "mppe is avaliable but received naked IP."); + } + } + /* else MPPE からの入力 */ + break; + case PPP_PROTO_MPPE: +#ifdef USE_NPPPD_MPPE + if (_this->mppe_started == 0) { +#else + { +#endif + ppp_log(_this, LOG_ERR, + "mppe packet is received but mppe is stopped."); + return 1; + } + break; +#endif + } + + switch (proto) { + case PPP_PROTO_IP: + npppd_network_output(_this->pppd, _this, AF_INET, inp, + lpkt - (inp - pkt)); + goto handled; + case PPP_PROTO_LCP: + fsm_input(&_this->lcp.fsm, inp, lpkt - (inp - pkt)); + goto handled; + case PPP_PROTO_PAP: + pap_input(&_this->pap, inp, lpkt - (inp - pkt)); + goto handled; + case PPP_PROTO_CHAP: + chap_input(&_this->chap, inp, lpkt - (inp - pkt)); + goto handled; +#ifdef USE_NPPPD_EAP_RADIUS + case PPP_PROTO_EAP: + eap_input(&_this->eap, inp, lpkt - (inp - pkt)); + goto handled; +#endif +#ifdef USE_NPPPD_MPPE + case PPP_PROTO_MPPE: +#ifdef USE_NPPPD_PIPEX + if (_this->pipex_enabled != 0) + return -1; /* silent discard */ +#endif /* USE_NPPPD_PIPEX */ + mppe_input(&_this->mppe, inp, lpkt - (inp - pkt)); + goto handled; +#endif + default: + if ((proto & 0xff00) == PPP_PROTO_NCP) { + switch (proto & 0xff) { + case NCP_CCP: /* Compression */ +#ifdef USE_NPPPD_MPPE + if (MPPE_MUST_NEGO(_this)) { + fsm_input(&_this->ccp.fsm, inp, + lpkt - (inp - pkt)); + goto handled; + } + // ネゴする必要のない場合は Protocol Reject +#endif + break; + case NCP_IPCP: /* IPCP */ + fsm_input(&_this->ipcp.fsm, inp, + lpkt - (inp - pkt)); + goto handled; + } + } + } + /* ProtoRej ログに残す */ + ppp_log(_this, LOG_INFO, "unhandled protocol %s, %d(%04x)", + proto_name(proto), proto, proto); + + if ((flags & PPP_IO_FLAGS_MPPE_ENCRYPTED) != 0) { + /* + * Don't return a protocol-reject for the packet was encrypted, + * because lcp protocol-reject is not encrypted by mppe. + */ + } else { + /* + * as RFC1661: Rejected-Information MUST be truncated to + * comply with the peer's established MRU. + */ + lcp_send_protrej(&_this->lcp, inp_proto, + MIN(lpkt - (inp_proto - pkt), NPPPD_MIN_MRU - 32)); + } + + return 1; +handled: + + return 0; +} + +/** PPPに出力する場合に呼び出します。 */ +inline void +ppp_output(npppd_ppp *_this, uint16_t proto, u_char code, u_char id, + u_char *datap, int ldata) +{ + u_char *outp; + int outlen, hlen, is_lcp = 0; + + outp = _this->outpacket_buf; + + /* LCPは圧縮を使わない */ + is_lcp = (proto == PPP_PROTO_LCP)? 1 : 0; + + + if (_this->has_acf == 0 || + (!is_lcp && psm_peer_opt_is_accepted(&_this->lcp, acfc))) { + /* + * Address and Control Field (ACF) がそもそも無い場合や + * ACFC がネゴされている場合は ACF を追加しない。 + */ + } else { + PUTCHAR(PPP_ALLSTATIONS, outp); + PUTCHAR(PPP_UI, outp); + } + if (!is_lcp && proto <= 0xff && + psm_peer_opt_is_accepted(&_this->lcp, pfc)) { + /* + * Protocol Field Compression + */ + PUTCHAR(proto, outp); + } else { + PUTSHORT(proto, outp); + } + hlen = outp - _this->outpacket_buf; + + if (_this->mru > 0) { + if (MRU_PKTLEN(_this->mru, proto) < ldata) { + PPP_DBG((_this, LOG_ERR, "packet too large %d. mru=%d", + ldata , _this->mru)); + _this->oerrors++; + PPP_ASSERT("NOT REACHED HERE" == NULL); + return; + } + } + + if (code != 0) { + outlen = ldata + HEADERLEN; + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + } else { + outlen = ldata; + } + + if (outp != datap && ldata > 0) + memmove(outp, datap, ldata); + + if (_this->log_dump_out != 0 && debug_get_debugfp() != NULL) { + char buf[256]; + + snprintf(buf, sizeof(buf), "log.%s.out.pktdump", + proto_name(proto)); + if (ppp_config_str_equal(_this, buf, "true", 0) != 0) { + ppp_log(_this, LOG_DEBUG, + "PPP output dump proto=%s(%d/%04x)", + proto_name(proto), proto, proto); + show_hd(debug_get_debugfp(), + _this->outpacket_buf, outlen + hlen); + } + } + _this->send_packet(_this, _this->outpacket_buf, outlen + hlen, 0); +} + +/** + * PPP 出力用のバッファ領域を返します。ヘッダ圧縮によるズレを補正します。 + * バッファ領域の長さは npppd_ppp#mru 以上です。 + */ +u_char * +ppp_packetbuf(npppd_ppp *_this, int proto) +{ + int save; + + save = 0; + if (proto != PPP_PROTO_LCP) { + if (psm_peer_opt_is_accepted(&_this->lcp, acfc)) + save += 2; + if (proto <= 0xff && psm_peer_opt_is_accepted(&_this->lcp, pfc)) + save += 1; + } + return _this->outpacket_buf + (PPP_HDRLEN - save); +} + +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +int +ppp_log(npppd_ppp *_this, int prio, const char *fmt, ...) +{ + int status; + char logbuf[BUFSIZ]; + va_list ap; + + PPP_ASSERT(_this != NULL); + + va_start(ap, fmt); + snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=base %s", + _this->id, fmt); + status = vlog_printf(prio, logbuf, ap); + va_end(ap); + + return status; +} + +#ifdef USE_NPPPD_RADIUS +#define UCHAR_BUFSIZ 255 +/** + * RADIUS パケットの Framed-IP-Address アートリビュートと Framed-IP-Netmask + * アートリビュートを処理します。 + */ +void +ppp_proccess_radius_framed_ip(npppd_ppp *_this, RADIUS_PACKET *pkt) +{ + struct in_addr ip4; + + if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_ADDRESS, &ip4) + == 0) + _this->realm_framed_ip_address = ip4; + + _this->realm_framed_ip_netmask.s_addr = 0xffffffffL; + if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_NETMASK, &ip4) + == 0) + _this->realm_framed_ip_netmask = ip4; +} + +/** + * RADIUS 認証要求用の RADIUSアートリビュートをセットします。 + * 成功した場合には 0 が返ります。 + */ +int +ppp_set_radius_attrs_for_authreq(npppd_ppp *_this, + radius_req_setting *rad_setting, RADIUS_PACKET *radpkt) +{ + /* RFC 2865 "5.4 NAS-IP-Address" or RFC3162 "2.1. NAS-IPv6-Address" */ + if (radius_prepare_nas_address(rad_setting, radpkt) != 0) + goto reigai; + + /* RFC 2865 "5.6. Service-Type" */ + if (radius_put_uint32_attr(radpkt, RADIUS_TYPE_SERVICE_TYPE, + RADIUS_SERVICE_TYPE_FRAMED) != 0) + goto reigai; + + /* RFC 2865 "5.7. Framed-Protocol" */ + if (radius_put_uint32_attr(radpkt, RADIUS_TYPE_FRAMED_PROTOCOL, + RADIUS_FRAMED_PROTOCOL_PPP) != 0) + goto reigai; + + if (_this->calling_number[0] != '\0') { + if (radius_put_string_attr(radpkt, + RADIUS_TYPE_CALLING_STATION_ID, _this->calling_number) != 0) + return 1; + } + return 0; +reigai: + return 1; +} +#endif + +#ifdef USE_NPPPD_PIPEX +/** Network が有効になった時の callback 関数 PIPEX 用*/ +static void +ppp_on_network_pipex(npppd_ppp *_this) +{ + if (_this->use_pipex == 0) + return; + if (_this->tunnel_type != PPP_TUNNEL_PPTP && + _this->tunnel_type != PPP_TUNNEL_PPPOE) + return; + if (_this->pipex_started != 0) + return; /* already started */ + + PPP_DBG((_this, LOG_INFO, "%s() assigned_ip4_enabled = %s, " + "MPPE_MUST_NEGO = %s, ccp.fsm.state = %s", __func__, + (_this->assigned_ip4_enabled != 0)? "true" : "false", + (MPPE_MUST_NEGO(_this))? "true" : "false", + (_this->ccp.fsm.state == OPENED)? "true" : "false")); + + if (_this->assigned_ip4_enabled != 0 && + (!MPPE_MUST_NEGO(_this) || _this->ccp.fsm.state == OPENED)) { + /* IPCP が完了し,MPPE 不要または MPPE 完了した場合 */ + npppd_ppp_pipex_enable(_this->pppd, _this); + ppp_log(_this, LOG_NOTICE, "Using pipex=%s", + (_this->pipex_enabled != 0)? "yes" : "no"); + _this->pipex_started = 1; + } + /* else CCP or IPCP 待ち */ +} +#endif + +#ifdef NPPPD_USE_CLIENT_AUTH +#ifdef USE_NPPPD_LINKID +#include "linkid.h" +#endif +/** 端末IDをセットします */ +void +ppp_set_client_auth_id(npppd_ppp *_this, const char *client_auth_id) +{ + PPP_ASSERT(_this != NULL); + PPP_ASSERT(client_auth_id != NULL); + PPP_ASSERT(strlen(client_auth_id) <= NPPPD_CLIENT_AUTH_ID_MAXLEN); + + strlcpy(_this->client_auth_id, client_auth_id, + sizeof(_this->client_auth_id)); + _this->has_client_auth_id = 1; +#ifdef USE_NPPPD_LINKID + linkid_purge(_this->ppp_framed_ip_address); +#endif + ppp_log(_this, LOG_NOTICE, + "Set client authentication id successfully. linkid=\"%s\" client_auth_id=%s", + _this->username, client_auth_id); +} +#endif diff --git a/usr.sbin/npppd/npppd/ppp.h b/usr.sbin/npppd/npppd/ppp.h new file mode 100644 index 00000000000..32090bc1e59 --- /dev/null +++ b/usr.sbin/npppd/npppd/ppp.h @@ -0,0 +1,843 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _NPPPD_H +#define _NPPPD_H 1 + +#define PPP_HDRLEN 4 +#define PPP_ALLSTATIONS 0xff +#define PPP_UI 0x03 + +#define PPP_PROTO_IP 0x0021 +#define PPP_PROTO_MPPE 0x00FD + +#define PPP_PROTO_LCP 0xC021 +#define PPP_PROTO_PAP 0xC023 +#define PPP_PROTO_LQR 0xC025 +#define PPP_PROTO_CHAP 0xC223 +#define PPP_PROTO_EAP 0xC227 +#define PPP_PROTO_NCP 0x8000 +#define NCP_CCP 0xfd +#define NCP_IPCP 0x21 +#define NCP_IP6CP 0x57 +#define PPP_PROTO_ACSP 0x8235 /* Apple Client Server Protocol + Control */ + +#define PPP_LCP_MRU 1 /* Maximum-Receive-Unit */ +#define PPP_LCP_AUTH_PROTOCOL 3 /* Authentication-Protcol */ +#define PPP_LCP_QUALITY_PROTOCOL 4 /* Quality-Control */ +#define PPP_LCP_MAGICNUMBER 5 /* Magic-Number */ +#define PPP_LCP_PFC 7 /* Protocol Field Compression */ +#define PPP_LCP_ACFC 8 /* Address-and-Control-Field- + Compression */ + +#define DEFAULT_MRU 1400 +#define NPPPD_MIN_MRU 500 + +#define PPP_AUTH_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_AUTH_CHAP 0xc223 /* Challenge Handshake Authentication Protocol*/ +#define PPP_AUTH_EAP 0xc227 /* Extensible Authentication Protocol */ + +/* EAP Type */ +#define PPP_AUTH_EAP_ANY 0x00 +#define PPP_AUTH_EAP_IDENTITY 0x01 +#define PPP_AUTH_EAP_NOTIFICATION 0x02 +#define PPP_AUTH_EAP_NAK 0x03 +#define PPP_AUTH_EAP_MD5_CHALLENGE 0x04 +#define PPP_AUTH_EAP_OTP 0x05 +#define PPP_AUTH_EAP_GTC 0x06 +#define PPP_AUTH_EAP_TLS 0x0d +#define PPP_AUTH_EAP_EXPANDED_TYPES 0xFE +#define PPP_AUTH_EAP_EXPERIMENTAL_USE 0xFF + +/* Chap Algorithms */ +#define PPP_AUTH_CHAP_MD5 0x05 /* MD5 */ +#define PPP_AUTH_CHAP_MS 0x80 /* MS-CHAP */ +#define PPP_AUTH_CHAP_MS_V2 0x81 /* MS-CHAP-V2 */ + +#define MAX_USERNAME_LENGTH 256 +#define MAX_PASSWORD_LENGTH 256 +#define MAX_CHALLENGE_LENGTH 24 + +#define INADDR_IPCP_OBEY_REMOTE_REQ 0x00000000L + +#define MPPE_KEYLEN 16 /* 128bit */ +#define CCP_MPPE 0x12 +#define CCP_MPPC_ALONE 0x00000001 /* See RFC 2118, Hi/fn */ +#define CCP_MPPE_LM_40bit 0x00000010 /* obsolute */ +#define CCP_MPPE_NT_40bit 0x00000020 +#define CCP_MPPE_NT_128bit 0x00000040 +#define CCP_MPPE_NT_56bit 0x00000080 +#define CCP_MPPE_STATELESS 0x01000000 /* Packet-by-packet encryption */ +#define CCP_MPPE_KEYLENMASK 0x00000FF0 +#define CCP_MPPE_HEADER_LEN 4 /* mppe header + protocol */ + +#define INADDR_USER_SELECT (htonl(0xFFFFFFFFL)) +#define INADDR_NAS_SELECT (htonl(0xFFFFFFFEL)) + +/** トンネルタイプ定数 */ +#define PPP_TUNNEL_NONE 0 /** トンネルタイプ無し */ +#define PPP_TUNNEL_L2TP 1 /** L2TP トンネルタイプ */ +#define PPP_TUNNEL_PPTP 2 /** PPTP トンネルタイプ */ +#define PPP_TUNNEL_PPPOE 3 /** PPPoE トンネルタイプ */ + +/** デフォルトの LCP ECHO 試行間隔(sec) */ +#define DEFAULT_LCP_ECHO_INTERVAL 300 + +/** デフォルトの LCP ECHO 再試行間隔(sec) */ +#define DEFAULT_LCP_ECHO_RETRY_INTERVAL 60 + +/** デフォルトの LCP ECHO 再試行回数(sec) */ +#define DEFAULT_LCP_ECHO_MAX_RETRIES 3 + +/** MRU に MPPE/CCP ヘッダ分が含まれているかどうか */ +/* #define MRU_INCLUDES_MPPE_CCP */ + +/** length for phone number */ +#define NPPPD_PHONE_NUMBER_LEN 32 + +/** + * PPP Disconnect Codes based on RFC 3145 + */ +typedef enum _npppd_ppp_disconnect_code { + /* + * 3.1. Global Errors + */ + /** No information available. */ + PPP_DISCON_NO_INFORMATION = 0, + + /** Administrative disconnect. */ + PPP_DISCON_ADMINITRATIVE = 1, + + /** + * Link Control Protocol (LCP) renegotiation at LNS disabled; LNS + * expects proxy LCP information, LAC did not send it. + */ + PPP_DISCON_LCP_RENEGOTIATION_DISABLED = 2, + + /** Normal Disconnection, LCP Terminate-Request sent. */ + PPP_DISCON_NORMAL = 3, + + /* + * 3.2. LCP Errors + */ + /** + * Compulsory encryption required by a PPP peer was refused by the + * other. + */ + PPP_DISCON_COMPULSORY_ENCRYPTION_REQUIRED = 4, + + /** FSM (Finite State Machine) Timeout error. (PPP event "TO-".) */ + PPP_DISCON_LCP_FSM_TIMEOUT = 5, + + /** No recognizable LCP packets were received. */ + PPP_DISCON_RECOGNIZABLE_LCP = 6, + + /** LCP failure: Magic Number error; link possibly looped back. */ + PPP_DISCON_LCP_MAGIC_NUMBER_ERROR = 7, + + /** LCP link failure: Echo Request timeout. */ + PPP_DISCON_LCP_TIMEOUT = 8, + + /** + * Peer has unexpected Endpoint-Discriminator for existing + * Multilink PPP (MP) bundle. + */ + PPP_DISCON_LCP_UNEXPECTED_ENDPOINT_DISC = 9, + + /** Peer has unexpected MRRU for existing MP bundle. */ + PPP_DISCON_LCP_UNEXPECTED_MRRU = 10, + + /** + * Peer has unexpected Short-Sequence-Number option for existing + * MP bundle. + */ + PPP_DISCON_LCP_UNEXPECTED_SHORT_SEQNUM = 11, + + /** + * Compulsory call-back required by a PPP peer was refused by the + * other. + */ + PPP_DISCON_LCP_COMPULSORY_CALL_BACK_REQUIRED = 12, + + /* + * 3.3. Authentication Errors + */ + /** FSM Timeout error. */ + PPP_DISCON_AUTH_FSM_TIMEOUT = 13, + + /** Peer has unexpected authenticated name for existing MP bundle. */ + PPP_DISCON_AUTH_UNEXPECTED_AUTH_NAME = 14, + + /** + * PPP authentication failure: Authentication protocol + * unacceptable. + */ + PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE= 15, + + /** + * PPP authentication failure: Authentication failed (bad name, + * password, or secret). + */ + PPP_DISCON_AUTH_FAILED = 16, + + /* + * 3.4. Network Control Protocol (NCP) Errors + */ + /** FSM Timeout error. */ + PPP_DISCON_NCP_FSM_TIMEOUT = 17, + + /** + * No NCPs available (all disabled or rejected); no NCPs went to + * Opened state. (Control Protocol Number may be zero only if + * neither peer has enabled NCPs.) + */ + PPP_DISCON_NCP_NO_NCP_AVAILABLE = 18, + + /** NCP failure: failed to converge on acceptable addresses. */ + PPP_DISCON_NCP_NO_ACCEPTABLE_ADDRESS= 19, + + /** NCP failure: user not permitted to use any addresses. */ + PPP_DISCON_NCP_NO_PERMITTED_ADDRESS = 20 +} npppd_ppp_disconnect_code; + +typedef struct _npppd_ppp npppd_ppp; + +#include "fsm.h" + +#ifdef USE_NPPPD_RADIUS +#include <radius+.h> +#include <radiusconst.h> +#include <radius_req.h> +#endif + +/** + * LCP の型 + */ +typedef struct _lcp { + fsm fsm; + /** LCP オプション */ + struct _opt { + uint8_t mru; + uint8_t pfc; + uint8_t acfc; + uint8_t pap; + uint8_t chap; + uint8_t chapms; + uint8_t chapms_v2; + uint8_t eap; + } opt; + /** 最後の認証方式の提案 */ + uint32_t lastauth; + /** マジック番号 */ + uint32_t magic_number; + + /** 先方のマジック番号 */ + uint32_t peer_magic_number; + + /** context for event(3) */ + struct evtimer_wrap timerctx; + + /** echo の送信間隔(秒) */ + int echo_interval; + + /** echo の最大連続失敗回数 */ + int echo_max_retries; + + /** echo reply を待つ時間 */ + int echo_retry_interval; + + /** echo の失敗回数 */ + int echo_failures; + + // NAT/ブラックホール検出のために + /** 受信した LCP 要求 */ + int8_t recv_reqs; + /** 受信した LCP の応答 */ + int8_t recv_ress; + /* + * Windows 2000 は ConfigReq に MRU は含まれず、送信した ConfigReq + * にたいして Nak を返してくる。 + */ + uint32_t xxxmru; + + /** 認証メソッド順 */ + int auth_order[16]; + + uint32_t /** dialin proxy したかどうか */ + dialin_proxy:1, + /** LCP を再調停するかどうか */ + dialin_proxy_lcp_renegotiation:1; +} lcp; + +/** + * CHAP の型 + */ +typedef struct _chap { + npppd_ppp *ppp; + /** context for event(3) */ + struct evtimer_wrap timerctx; + uint32_t state; + char myname[80]; + /** challenge */ + u_char name[MAX_USERNAME_LENGTH]; + u_char chall[MAX_CHALLENGE_LENGTH]; + int lchall; /* length of challenge */ + u_char pktid; /* PPP Packet Id */ + u_char challid; /* Id of challange */ + int type; /* chap type */ + int ntry; + u_char authenticator[16]; +#ifdef USE_NPPPD_RADIUS + RADIUS_REQUEST_CTX radctx; +#endif +} chap; + +/** + * PAP の型 + */ +typedef struct _pap { + npppd_ppp *ppp; + uint32_t state; + u_char name[MAX_USERNAME_LENGTH]; + int auth_id; +#ifdef USE_NPPPD_RADIUS + RADIUS_REQUEST_CTX radctx; +#endif +} pap; + +/** + * EAP + */ +#ifdef USE_NPPPD_EAP_RADIUS +#define PPP_EAP_FLAG_NAK_RESPONSE 0x01 +typedef struct _eap { + npppd_ppp *ppp; + struct evtimer_wrap timerctx; + uint32_t state; + u_char eapid; + int ntry; + u_char name[MAX_USERNAME_LENGTH]; + u_char authenticator[16]; +/* FIXME */ +#define RADIUS_ATTR_STATE_LEN 100 + int name_len; + u_char attr_state[RADIUS_ATTR_STATE_LEN]; + u_char attr_state_len; + unsigned int session_timeout; + /* + * nak response 0x01 + */ + u_char flags; + RADIUS_REQUEST_CTX radctx; +} eap; +#endif + +/** + * CCP の型 + */ +typedef struct _ccp { + npppd_ppp *ppp; + fsm fsm; + + uint32_t mppe_o_bits; + uint32_t mppe_p_bits; + uint mppe_rej; +} ccp; + +/** + * IPCP の型 + */ +typedef struct _ipcp { + fsm fsm; + npppd_ppp *ppp; + + struct in_addr ip4_our; + + struct in_addr dns_pri; + struct in_addr dns_sec; + struct in_addr nbns_pri; + struct in_addr nbns_sec; +} ipcp; + +/** パケットの recv/send 等デリゲートする際に使う関数ポインタ */ +typedef int (*npppd_iofunc) ( + npppd_ppp *ppp, + unsigned char *bytes, + int nbytes, + int flags +); + +/** オリジナルのパケットは MPPE で暗号化されていたことを示します。 */ +#define PPP_IO_FLAGS_MPPE_ENCRYPTED 0x0001 + +typedef void (*npppd_voidfunc) ( + npppd_ppp *ppp +); + +#ifdef USE_NPPPD_MPPE + +typedef struct _mppe_rc4 { + void *rc4ctx; + + uint8_t stateless; + uint8_t resetreq; + + /** session key length */ + uint8_t keylen; + /** key length in bits */ + uint8_t keybits; + + /** Cohrency Counter */ + uint16_t coher_cnt; + + uint8_t master_key[MPPE_KEYLEN]; + uint8_t session_key[MPPE_KEYLEN]; +} mppe_rc4_t; + +/** + * MPPE の型 + */ +typedef struct _mppe { + npppd_ppp *ppp; + uint8_t master_key[MPPE_KEYLEN]; + + uint16_t pkt_cnt; + + /* + * configuration parameters. + */ + uint16_t enabled :1, /* if 0 MPPE しない */ + required :1, /* if 1 MPPE なしで通信不可*/ + mode_auto :1, + keylen_auto :1, + mode_stateless :1, + reserved :11; + uint16_t keylenbits; + + mppe_rc4_t send, recv, keychg; +} mppe; +#endif + +/** + * Type for phone number. Can be to use as a struct sockaddr. + */ +typedef struct _npppd_phone_number { +#define NPPPD_AF_PHONE_NUMBER (AF_MAX + 0) + /** total length */ + uint8_t pn_len; + /** address family. this must be NPPPD_AF_PHONE_NUMBER */ + sa_family_t pn_family; + /** phone number */ + char pn_number[NPPPD_PHONE_NUMBER_LEN + 1]; +} npppd_phone_number; + +/** + * npppd_ppp 型。 + */ +struct _npppd_ppp { + npppd *pppd; + int id; + /* + * 入出力 + */ + uint8_t *outpacket_buf; /** 出力バッファ */ + npppd_iofunc send_packet; /** send to phy */ + npppd_iofunc recv_packet; /** recv from phy */ + + /** アイドルタイムアウトイベント */ + struct event idle_event; + /** アイドルタイムアウトの値 */ + int timeout_sec; + /* + * 物理層 + */ + int tunnel_type; /** PPPトンネルタイプ */ + uint16_t mru; /** MRU */ + uint16_t peer_mru; /** Peer's MRU */ + void *phy_context; /** 物理層のコンテキスト */ + char phy_label[16]; /** 物理層のラベル */ + union { + struct sockaddr_in peer_in;/** {L2TP,PPTP}/IPv4 */ +#if defined(USE_NPPPD_PPPOE) + struct sockaddr_dl peer_dl;/** PPPoE */ +#endif + npppd_phone_number peer_pn;/** DialIn */ + } phy_info; /** 物理層の情報 */ + char calling_number[NPPPD_PHONE_NUMBER_LEN + 1]; + npppd_voidfunc phy_close; /** close line */ + /* + * 切断。以下のどのケースでも、phy_close が呼ばれます。 + * - PPP側から切断 + * - 物理層側から ppp_close + * - 物理層側から ppp_phy_downed + * + * phy_close が呼ばれた直後に ppp は解放されるので、以後アクセスして + * はいけない。 + */ + + /** 認証レルム */ + void *realm; + /* + * プロトコル + */ + lcp lcp; /** lcp */ + chap chap; /** chap */ + pap pap; /** pap */ +#ifdef USE_NPPPD_EAP_RADIUS + eap eap; /** eap */ +#endif + ccp ccp; /** ccp */ + ipcp ipcp; /** ccp */ + + char username[MAX_USERNAME_LENGTH]; /** リモートユーザ名 */ + int ifidx; /** 集約インタフェースの index*/ + + /** Proxy Authen Response */ + u_char *proxy_authen_resp; + /** Length of 'Proxy Authen Response' */ + int lproxy_authen_resp; + + /** + * 先方に要求する認証方式 + * <pre> + * PAP 0xC023 + * EAP 0xC227 + * CHAP 0x0005 + * MSCHAP 0x0080 + * MSCHAPv2 0x0081 + * </pre> + */ + uint16_t peer_auth; + u_short auth_timeout; + +#ifdef USE_NPPPD_MPPE + uint8_t mppe_started; + mppe mppe; +#endif + /** 割り当てる/たIPアドレス */ + struct sockaddr_npppd snp; +#define ppp_framed_ip_address snp.snp_addr +#define ppp_framed_ip_netmask snp.snp_mask +#define ppp_ip_assigned(p) (p->ppp_framed_ip_address.s_addr != 0) + + /** 割り当てで使用したプール */ + void *assigned_pool; + + struct in_addr realm_framed_ip_address; + struct in_addr realm_framed_ip_netmask; + + uint8_t /** Address and Control Filed があるか */ + has_acf:1, + /** TCP MSS を MRU 以下に調整するかどうか */ + adjust_mss:1, + /** 認証は一回限り */ + auth_runonce:1, + /** PIPEX を使うかどうか */ + use_pipex:1, + /** PIPEX を開始したかどうか(使えたかどうかは別) */ + pipex_started:1, + /** PIPEX を使っているかどうか */ + pipex_enabled:1, + reserved:3; + uint8_t /** 動的割り当てかどうか */ + assign_dynapool:1, + /** 割り当てたアドレスが有効かどうか */ + assigned_ip4_enabled:1, + assigned_ip4_rcvd:6; + + /** 入力パケットをダンプするか */ + uint8_t log_dump_in:1, + /** 出力パケットをダンプするか */ + log_dump_out:1, + log_rcvd:6; + + uint8_t /** 生IPが流れたというログを出力済みかどうか */ + logged_naked_ip:1, + /** 課金開始のログを出力したかどうか */ + logged_acct_start:1, + /** + * Addressフィルードが無いというログを出力済かどうか。 + */ + logged_no_address:1, + logged_rcvd:5; +#ifdef NPPPD_USE_CLIENT_AUTH +/** 端末認証 ID の長さ */ +#define NPPPD_CLIENT_AUTH_ID_MAXLEN 32 + char client_auth_id[NPPPD_CLIENT_AUTH_ID_MAXLEN + 1]; + int has_client_auth_id; +#endif + /* + * 統計情報 + */ + /** 開始時刻 */ + time_t start_time; + /** 開始時刻(相対時間) */ + time_t start_monotime; + /** 終了時刻(相対時間) */ + time_t end_monotime; + /** 入力パケット数 */ + uint32_t ipackets; + /** 出力パケット数 */ + uint32_t opackets; + /** 入力エラーパケット数 */ + uint32_t ierrors; + /** 出力エラーパケット数 */ + uint32_t oerrors; + /** 入力パケットバイト*/ + uint64_t ibytes; + /** 出力パケットバイト*/ + uint64_t obytes; + + /* + * Disconnect cause information for RFC3145 + */ + /** disconnect code */ + npppd_ppp_disconnect_code disconnect_code; + /** disconnect control protocol */ + int16_t disconnect_proto; + /** disconnect direction */ + int8_t disconnect_direction; + /** disconnect message */ + const char *disconnect_message; +}; + +/** proxied dialin */ +typedef struct _dialin_proxy_info { + /** Proxied LCP */ + struct proxy_lcp { + /** Length of the data */ + int ldata; + /** LCP data */ + u_char data[256]; + } /** the last sent LCP */ last_sent_lcp, + /** the last recevied LCP */ last_recv_lcp; + + /** ID of authentication packet */ + int auth_id; + /** authen type. use same value on npppd_ppp#peer_auth. */ + uint32_t auth_type; + /** Username */ + char username[MAX_USERNAME_LENGTH]; + /** Authentication challenage */ + u_char auth_chall[MAX_CHALLENGE_LENGTH]; + /** Authentication challenge length */ + int lauth_chall; + /** Authentication response */ + u_char auth_resp[MAX_PASSWORD_LENGTH]; + /** Authentication response length */ + int lauth_resp; + +} dialin_proxy_info; + +#define DIALIN_PROXY_IS_REQUESTED(dpi) \ + (((dpi)->last_sent_lcp.ldata > 0)? 1 : 0) + +/** MPPE をネゴすべき */ +#define MPPE_MUST_NEGO(ppp) \ + (((ppp)->mppe.enabled != 0) && \ + (((ppp)->peer_auth == PPP_AUTH_CHAP_MS_V2) || \ + ((ppp)->peer_auth == PPP_AUTH_EAP))) + +/** MPPE 必須 */ +#define MPPE_REQUIRED(ppp) \ + (((ppp)->mppe.enabled != 0) && ((ppp)->mppe.required != 0)) + +/** MPPE 利用可 */ +#define MPPE_READY(ppp) ((ppp)->mppe_started != 0) + +/* NetBSD:/usr/src/usr.sbin/pppd/pppd/pppd.h より。*/ +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} +#define BCOPY(s, d, l) memcpy(d, s, l) +#define BZERO(s, n) memset(s, 0, n) + +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/* + * MAKEHEADER - Add Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/* adapted from FreeBSD:/usr/include/sys/cdefs */ +#ifndef __printflike +#if __GNUC__ < 2 || __GNUC__ == 2 && __GNUC_MINOR__ < 7 +#define __printflike(fmtarg, firstvararg) +#else +#define __printflike(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#endif +#endif + +/* + * MRU は MPPE/CCP ヘッダをカバーするかどうか。 + * + * RFC 1331: + * The Maximum-Receive-Unit covers only the Data Link Layer + * Information field. It does not include the header, + * padding, FCS, nor any transparency bits or bytes. + * + * Windows XP: + * MRU で通知する値と、Windows が TCP MSS として使う値を比較すると、 + * 単純に 40 (IPヘッダとTCPヘッダ) バイトの差だけである。つまり、セ + * グメントサイズが MSS ぴったりの TCP パケットを送信すると、その PPP + * パケットは、MRU + 4 オクテットとなる。MPPE を使わない場合は、ぴっ + * たり MRU となる。 + * + * 「MRU は MPPE/CCP ヘッダをカバーする」と実装した場合、このホスト上で、 + * MRU + 4 とならないような動作を行わねばならないことになる。 + */ +#if !defined(USE_NPPPD_MPPE) +/* MPPE 使わない場合は何もしない */ +#define MRU_IPMTU(mru) (mru) +#define MRU_PKTLEN(mru, proto) (mru) +#else +#ifdef MRU_INCLUDES_MPPE_CCP +/* MRU は MPPE/CCP ヘッダをカバーする */ +#define MRU_IPMTU(mru) ((mru) - CCP_MPPE_HEADER_LEN) +#define MRU_PKTLEN(mru, proto) (mru) +#else +/* MRU は MPPE/CCP ヘッダをカバーしない */ +#define MRU_IPMTU(mru) (mru) +#define MRU_PKTLEN(mru, proto) (((proto) == PPP_PROTO_MPPE) \ + ? (mru) + CCP_MPPE_HEADER_LEN : (mru)) +#endif +#endif + +#define PPP_FSM_CONFIG(fsm, memb, confl) \ + (fsm)->memb = ppp_config_int((fsm)->ppp, confl, (fsm)->memb) + +#ifdef __cplusplus +extern "C" { +#endif + + +npppd_ppp *ppp_create (void); +int ppp_init (npppd *, npppd_ppp *); +void ppp_start (npppd_ppp *); +int ppp_dialin_proxy_prepare (npppd_ppp *, dialin_proxy_info *); +void ppp_stop (npppd_ppp *, const char *); +void ppp_stop_ex (npppd_ppp *, const char *, npppd_ppp_disconnect_code, int, int, const char *); + +void ppp_destroy (void *); +void ppp_lcp_up (npppd_ppp *); +void ppp_lcp_finished (npppd_ppp *); +void ppp_phy_downed (npppd_ppp *); +void ppp_auth_ok (npppd_ppp *); +void ppp_ipcp_opened (npppd_ppp *); +void ppp_ccp_opened (npppd_ppp *); +inline void ppp_output (npppd_ppp *, uint16_t, u_char, u_char, u_char *, int); +u_char *ppp_packetbuf (npppd_ppp *, int); +const char *ppp_config_str (npppd_ppp *, const char *); +int ppp_config_int (npppd_ppp *, const char *, int); +int ppp_config_str_equal (npppd_ppp *, const char *, const char *, int); +int ppp_config_str_equali (npppd_ppp *, const char *, const char *, int); +int ppp_log (npppd_ppp *, int, const char *, ...) __printflike(3,4); +void ppp_reset_idle_timeout(npppd_ppp *); +#ifdef USE_NPPPD_RADIUS +void ppp_proccess_radius_framed_ip (npppd_ppp *, RADIUS_PACKET *); +int ppp_set_radius_attrs_for_authreq (npppd_ppp *, radius_req_setting *, RADIUS_PACKET *); +#endif +void ppp_set_client_auth_id(npppd_ppp *, const char *); + +void ccp_init (ccp *, npppd_ppp *); +void ipcp_init (ipcp *, npppd_ppp *); + +void lcp_init (lcp *, npppd_ppp *); +void lcp_lowerup (lcp *); +void lcp_send_protrej(lcp *, u_char *, int ); +int lcp_dialin_proxy(lcp *, dialin_proxy_info *, int, int); + +void pap_init (pap *, npppd_ppp *); +int pap_start (pap *); +int pap_stop (pap *); +int pap_input (pap *, u_char *, int); +int pap_proxy_authen_prepare (pap *, dialin_proxy_info *); + +void chap_init (chap *, npppd_ppp *); +void chap_stop (chap *); +void chap_start (chap *); +void chap_input (chap *, u_char *, int); +int chap_proxy_authen_prepare (chap *, dialin_proxy_info *); + +#ifdef USE_NPPPD_EAP_RADIUS +void eap_init __P((eap *, npppd_ppp *)); +void eap_stop __P((eap *)); +void eap_start __P((eap *)); +void eap_input __P((eap *, u_char *, int)); +#endif + +#ifdef USE_NPPPD_MPPE +void mppe_init (mppe *, npppd_ppp *); +void mppe_fini (mppe *); +void mppe_start (mppe *); +uint32_t mppe_create_our_bits (mppe *, uint32_t); +void mppe_input (mppe *, u_char *, int); +void mppe_recv_ccp_reset (mppe *); +void mppe_pkt_output (mppe *, uint16_t, u_char *, int); +#endif + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/npppd/psm-opt.h b/usr.sbin/npppd/npppd/psm-opt.h new file mode 100644 index 00000000000..b7a36657381 --- /dev/null +++ b/usr.sbin/npppd/npppd/psm-opt.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PSM_H +#define PSM_H 1 + +/**@file + * <p>PPPステートマシンのオプションのネゴシエーション状態を保持、変更するための + * マクロ。</p> + * <p> + * オプションは LCP、CCP など fsm から導出された型に保持する。 + * <pre> + * struct lcp { + * fsm fsm; + * struct { + * uint8_t pfc; + * uint8_t acfc; + * } opt; + * };</pre></p> + * <p> + * 使用例: + * <pre> + * if (!psm_opt_is_accepted(_this->lcp, pcf)) { + * // LCP の Protocol Field Compression を accept している + * }</pre></p> + * <p> + * fsm ではなく、導出されたクラスに保持することにしたので、fsm という名前 + * は使わず psm のサフィックスを使った。</p> + * $Id: psm-opt.h,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ + +#define PSM_OPT_REQUEST_OURS 0x01 +#define PSM_OPT_ACCEPT_OURS 0x02 +#define PSM_OPT_REJECT_OURS 0x04 +#define PSM_OPT_ENABLED_OURS 0x08 + +#define PSM_OPT_REQUEST_PEERS 0x10 +#define PSM_OPT_ACCEPT_PEERS 0x20 +#define PSM_OPT_REJECT_PEERS 0x40 +#define PSM_OPT_ENABLED_PEERS 0x80 + +#define psm_peer_opt_is_requested(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_REQUEST_PEERS) != 0) +#define psm_peer_opt_set_requested(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_REQUEST_PEERS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_REQUEST_PEERS; \ + } +#define psm_opt_is_requested(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_REQUEST_OURS) != 0) +#define psm_opt_set_requested(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_REQUEST_OURS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_REQUEST_OURS; \ + } +#define psm_peer_opt_is_accepted(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_ACCEPT_PEERS) != 0) +#define psm_peer_opt_set_accepted(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_ACCEPT_PEERS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_ACCEPT_PEERS; \ + } +#define psm_opt_is_accepted(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_ACCEPT_OURS) != 0) +#define psm_opt_set_accepted(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_ACCEPT_OURS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_ACCEPT_OURS; \ + } +#define psm_peer_opt_is_rejected(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_REJECT_PEERS) != 0) +#define psm_peer_opt_set_rejected(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_REJECT_PEERS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_REJECT_PEERS; \ + } +#define psm_opt_is_rejected(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_REJECT_OURS) != 0) +#define psm_opt_set_rejected(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_REJECT_OURS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_REJECT_OURS; \ + } +#define psm_peer_opt_is_enabled(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_ENABLED_PEERS) != 0) +#define psm_peer_opt_set_enabled(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_ENABLED_PEERS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_ENABLED_PEERS; \ + } +#define psm_opt_is_enabled(psm, confopt) \ + (((psm)->opt.confopt & PSM_OPT_ENABLED_OURS) != 0) +#define psm_opt_set_enabled(psm, confopt, boolval) \ + if ((boolval)) { \ + (psm)->opt.confopt |= PSM_OPT_ENABLED_OURS; \ + } else { \ + (psm)->opt.confopt &= ~PSM_OPT_ENABLED_OURS; \ + } +#endif diff --git a/usr.sbin/npppd/npppd/radius+.cc b/usr.sbin/npppd/npppd/radius+.cc new file mode 100644 index 00000000000..8c188102b0d --- /dev/null +++ b/usr.sbin/npppd/npppd/radius+.cc @@ -0,0 +1,761 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE"AUTHOR" AND CONTRIBUTORS AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * radius+.cc : + * yet another RADIUS library + */ +#ifdef WITH_MPATROL +#include <mpatrol.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <md5.h> +#include "radius+.h" +#include "radiusconst.h" + +#include "radius+_local.h" + +u_int8_t radius_id_counter = 0; + +static int radius_check_packet_data(const RADIUS_PACKET_DATA* pdata, + size_t length) +{ + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + + if(length < sizeof(RADIUS_PACKET_DATA)) + return 1; + if(length > 0xffff) + return 1; + if(length != (size_t)(pdata->length)) + return 1; + + attr = ATTRS_BEGIN(pdata); + end = ATTRS_END(pdata); + for(; attr<end; ADVANCE(attr)) + { + if(attr->length < 2) + return 1; + if(attr->type == RADIUS_TYPE_VENDOR_SPECIFIC) + { + if(attr->length < 8) + return 1; + if((attr->vendor & 0xff000000U) != 0) + return 1; + if(attr->length != attr->vlength + 6) + return 1; + } + } + + if(attr != end) + return 1; + + return 0; +} + +static int radius_ensure_add_capacity(RADIUS_PACKET* packet, size_t capacity) +{ + size_t newsize; + void* newptr; + + // 最大サイズは 64KB + // 安全のため(?)、少し小さい値をリミットにしている。 + if(packet->pdata->length + capacity > 0xfe00) + return 1; + + if(packet->pdata->length + capacity > packet->capacity) + { + newsize = packet->pdata->length + capacity + + RADIUS_PACKET_CAPACITY_INCREMENT; + newptr = realloc(packet->pdata, newsize); + if(newptr == NULL) + return 1; + packet->capacity = newsize; + packet->pdata = (RADIUS_PACKET_DATA*)newptr; + } + + return 0; +} + +RADIUS_PACKET* radius_new_request_packet(u_int8_t code) +{ + RADIUS_PACKET* packet; + unsigned int i; + + packet = (RADIUS_PACKET*)malloc(sizeof(RADIUS_PACKET)); + if(packet == NULL) + return NULL; + packet->pdata = (RADIUS_PACKET_DATA*)malloc(RADIUS_PACKET_CAPACITY_INITIAL); + if(packet->pdata == NULL) + { + free(packet); + return NULL; + } + packet->capacity = RADIUS_PACKET_CAPACITY_INITIAL; + packet->request = NULL; + packet->pdata->code = code; + packet->pdata->id = radius_id_counter++; + packet->pdata->length = sizeof(RADIUS_PACKET_DATA); + for(i=0; i<countof(packet->pdata->authenticator); i++) + packet->pdata->authenticator[i] = rand()&0xff; + + return packet; +} + +RADIUS_PACKET* radius_new_response_packet(u_int8_t code, + const RADIUS_PACKET* request) +{ + RADIUS_PACKET* packet; + + packet = radius_new_request_packet(code); + if(packet == NULL) + return NULL; + packet->request = request; + packet->pdata->id = request->pdata->id; + + return packet; +} + +RADIUS_PACKET* radius_convert_packet(const void* pdata, size_t length) +{ + RADIUS_PACKET* packet; + + if(radius_check_packet_data((const RADIUS_PACKET_DATA*)pdata, length) != 0) + return NULL; + packet = (RADIUS_PACKET*)malloc(sizeof(RADIUS_PACKET)); + if(packet == NULL) + return NULL; + packet->pdata = (RADIUS_PACKET_DATA*)malloc(length); + packet->capacity = length; + packet->request = NULL; + if(packet->pdata == NULL) + { + free(packet); + return NULL; + } + memcpy(packet->pdata, pdata, length); + + return packet; +} + +int radius_delete_packet(RADIUS_PACKET* packet) +{ + free(packet->pdata); + free(packet); + return 0; +} + +u_int8_t radius_get_code(const RADIUS_PACKET* packet) +{ + return packet->pdata->code; +} + +u_int8_t radius_get_id(const RADIUS_PACKET* packet) +{ + return packet->pdata->id; +} + +void radius_get_authenticator(const RADIUS_PACKET* packet, char* authenticator) +{ + memcpy(authenticator, packet->pdata->authenticator, 16); +} + +const char* radius_get_authenticator_retval(const RADIUS_PACKET* packet) +{ + return packet->pdata->authenticator; +} + +void radius_set_request_packet(RADIUS_PACKET* packet, + const RADIUS_PACKET* request) +{ + packet->request = request; +} + +int radius_check_response_authenticator(const RADIUS_PACKET* packet, + const char* secret) +{ + MD5_CTX ctx; + unsigned char authenticator0[16]; + + /* 呼び出し前に必ず radius_set_request_packet*/ + if (packet->request == NULL) + return -1; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)packet->pdata, 4); + MD5Update(&ctx, (unsigned char*)packet->request->pdata->authenticator, + 16); + MD5Update(&ctx, + (unsigned char*)packet->pdata->attributes, + packet->pdata->length-20); + MD5Update(&ctx, (unsigned char*)secret, strlen(secret)); + MD5Final((unsigned char *)authenticator0, &ctx); + + return memcmp(authenticator0, packet->pdata->authenticator, 16); +} + +void radius_set_response_authenticator(RADIUS_PACKET* packet, + const char* secret) +{ + MD5_CTX ctx; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)packet->pdata, 4); + MD5Update(&ctx, + (unsigned char*)packet->request->pdata->authenticator, 16); + MD5Update(&ctx, + (unsigned char*)packet->pdata->attributes, + packet->pdata->length-20); + MD5Update(&ctx, (unsigned char*)secret, strlen(secret)); + MD5Final((unsigned char*)packet->pdata->authenticator ,&ctx); +} + +u_int16_t radius_get_length(const RADIUS_PACKET* packet) +{ + return packet->pdata->length; +} + + +const void* radius_get_data(const RADIUS_PACKET* packet) +{ + return packet->pdata; +} + +int radius_get_raw_attr(const RADIUS_PACKET* packet, u_int8_t type, + void* buf, u_int8_t* length) +{ + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + + for(; attr<end; ADVANCE(attr)) + { + if(attr->type != type) + continue; + *length = attr->length - 2; + memcpy(buf, attr->data, attr->length - 2); + return 0; + } + + return 1; +} + +/* + * To determine the length of the data, set the buf = NULL. + */ +int radius_get_raw_attr_all(const RADIUS_PACKET* packet, u_int8_t type, + caddr_t buf, int *length) +{ + int off; + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + + for(off = 0; attr<end; ADVANCE(attr)) + { + if(attr->type != type) + continue; + if (buf != NULL) { + if (off + attr->length - 2 <= *length) + memcpy(buf + off, attr->data, attr->length - 2); + else + return 1; + } + off += attr->length - 2; + } + *length = off; + + return 0; +} + +int radius_put_raw_attr(RADIUS_PACKET* packet, u_int8_t type, + const void* buf, u_int8_t length) +{ + RADIUS_ATTRIBUTE* newattr; + + if(length > 255-2) + return 1; + + if(radius_ensure_add_capacity(packet, length+2) != 0) + return 1; + + newattr = ATTRS_END(packet->pdata); + newattr->type = type; + newattr->length = length + 2; + memcpy(newattr->data, buf, length); + packet->pdata->length += length + 2; + + return 0; +} + +int radius_put_raw_attr_all(RADIUS_PACKET* packet, u_int8_t type, + caddr_t buf, int length) +{ + int off, len0; + RADIUS_ATTRIBUTE* newattr; + + off = 0; + while (off < length) { + len0 = MIN(length - off, 255-2); + + if(radius_ensure_add_capacity(packet, len0+2) != 0) + return 1; + + newattr = ATTRS_END(packet->pdata); + newattr->type = type; + newattr->length = len0 + 2; + memcpy(newattr->data, buf, len0); + packet->pdata->length += len0 + 2; + + off += len0; + } + + return 0; +} + +int radius_get_vs_raw_attr(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, void* buf, u_int8_t* length) +{ + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + + for(; attr<end; ADVANCE(attr)) + { + if(attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) + continue; + if(attr->vendor != vendor) + continue; + if(attr->vtype != vtype) + continue; + + *length = attr->vlength - 2; + memcpy(buf, attr->vdata, attr->vlength - 2); + return 0; + } + + return 1; +} + +/* + * To determine the length of the data, set the buf = NULL. + */ +int radius_get_vs_raw_attr_all(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, caddr_t buf, int *length) +{ + int off; + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + + off = 0; + for(; attr<end; ADVANCE(attr)) + { + if(attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) + continue; + if(attr->vendor != vendor) + continue; + if(attr->vtype != vtype) + continue; + + if (buf != NULL) { + if (off + attr->vlength - 2 <= *length) + memcpy(off + buf, attr->vdata, + attr->vlength - 2); + else + return 1; + } + off += attr->vlength; + } + *length = off; + + return 0; +} + +int radius_get_vs_raw_attr_ptr(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, void** ptr, u_int8_t* length) +{ + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + + for(; attr<end; ADVANCE(attr)) + { + if(attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) + continue; + if(attr->vendor != vendor) + continue; + if(attr->vtype != vtype) + continue; + + *length = attr->vlength - 2; + *ptr = (void *)attr->vdata; + return 0; + } + + return 1; +} + +int radius_put_vs_raw_attr(RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, const void* buf, u_int8_t length) +{ + RADIUS_ATTRIBUTE* newattr; + + if(length > 255-8) + return 1; + + if(radius_ensure_add_capacity(packet, length+8) != 0) + return 1; + + newattr = ATTRS_END(packet->pdata); + newattr->type = RADIUS_TYPE_VENDOR_SPECIFIC; + newattr->length = length + 8; + newattr->vendor = vendor; + newattr->vtype = vtype; + newattr->vlength = length + 2; + memcpy(newattr->vdata, buf, length); + packet->pdata->length += length + 8; + + return 0; +} + +int radius_put_vs_raw_attr_all(RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, const void* buf, int length) +{ + int off, len0; + RADIUS_ATTRIBUTE* newattr; + + off = 0; + while (off < length) { + len0 = MIN(length - off, 255-8); + + if(radius_ensure_add_capacity(packet, len0+8) != 0) + return 1; + + newattr = ATTRS_END(packet->pdata); + newattr->type = RADIUS_TYPE_VENDOR_SPECIFIC; + newattr->length = len0 + 8; + newattr->vendor = vendor; + newattr->vtype = vtype; + newattr->vlength = len0 + 2; + memcpy(newattr->vdata, buf, len0); + packet->pdata->length += len0 + 8; + + off += len0; + } + + return 0; +} + +int radius_get_uint32_attr(const RADIUS_PACKET* packet, u_int8_t type, + u_int32_t* val) +{ + u_int32_t nval; + u_int8_t len; + + if(radius_get_raw_attr(packet, type, &nval, &len) != 0) + return 1; + if(len != sizeof(u_int32_t)) + return 1; + *val = ntohl(nval); + return 0; +} + +u_int32_t radius_get_uint32_attr_retval(const RADIUS_PACKET* packet, + u_int8_t type) +{ + u_int32_t nval; + u_int8_t len; + + if(radius_get_raw_attr(packet, type, &nval, &len) != 0) + return 0xffffffff; + if(len != sizeof(u_int32_t)) + return 0xffffffff; + return ntohl(nval); +} + +int radius_put_uint32_attr(RADIUS_PACKET* packet, u_int8_t type, u_int32_t val) +{ + u_int32_t nval; + + nval = htonl(val); + return radius_put_raw_attr(packet, type, &nval, sizeof(u_int32_t)); +} + +int radius_get_string_attr(const RADIUS_PACKET* packet, u_int8_t type, + char* str) +{ + u_int8_t len; + + if(radius_get_raw_attr(packet, type, str, &len) != 0) + return 1; + str[len] = '\0'; + return 0; +} + +int radius_put_string_attr(RADIUS_PACKET* packet, u_int8_t type, + const char* str) +{ + return radius_put_raw_attr(packet, type, str, strlen(str)); +} + +int radius_get_vs_string_attr(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, char* str) +{ + u_int8_t len; + + if(radius_get_vs_raw_attr(packet, vendor, vtype, str, &len) != 0) + return 1; + str[len] = '\0'; + return 0; +} + +int radius_put_vs_string_attr(RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, const char* str) +{ + return radius_put_vs_raw_attr(packet, vendor, vtype, str, strlen(str)); +} + +int radius_get_ipv4_attr(const RADIUS_PACKET* packet, u_int8_t type, + in_addr* addr) +{ + in_addr tmp; + u_int8_t len; + + if(radius_get_raw_attr(packet, type, &tmp, &len) != 0) + return 1; + if(len != sizeof(in_addr)) + return 1; + *addr = tmp; + return 0; +} + +in_addr radius_get_ipv4_attr_retval(const RADIUS_PACKET* packet, + u_int8_t type) +{ + in_addr addr; + u_int8_t len; + + if(radius_get_raw_attr(packet, type, &addr, &len) != 0) + addr.s_addr = htonl(INADDR_ANY); + if(len != sizeof(in_addr)) + addr.s_addr = htonl(INADDR_ANY); + return addr; +} + +int radius_put_ipv4_attr(RADIUS_PACKET* packet, u_int8_t type, in_addr addr) +{ + return radius_put_raw_attr(packet, type, &addr, sizeof(in_addr)); +} + +RADIUS_PACKET* radius_recvfrom(int s, int flags, sockaddr* addr, socklen_t* len) +{ + char buf[0x10000]; + ssize_t n; + + n = recvfrom(s, buf, sizeof(buf), flags, addr, len); + if(n <= 0) + return NULL; + + return radius_convert_packet(buf, (size_t)n); +} + +int radius_sendto(int s, const RADIUS_PACKET* packet, + int flags, const sockaddr* addr, socklen_t len) +{ + ssize_t n; + + n = sendto(s, packet->pdata, packet->pdata->length, flags, addr, len); + if(n != packet->pdata->length) + return 1; + return 0; +} + +/** + * Calculate keyed-hashing for message authenticaiton using md5. + * + * @param packet pointer to target RADIUS_PACKET. + * @param text_len length of data stream + * @param key pointer to authentication key + * @param key_len length of authentication key + * @param digest caller digest to be filled in + */ +static void +radius_hmac_md5(RADIUS_PACKET *packet, const char * key, int key_len, + caddr_t digest, int check) +{ + const RADIUS_ATTRIBUTE* attr; + const RADIUS_ATTRIBUTE* end; + MD5_CTX context; + u_char k_ipad[65]; /* inner padding - key XORd with ipad */ + u_char k_opad[65]; /* outer padding - key XORd with opad */ + u_char key0[64]; + int i; + u_char zero16[16]; + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) + { + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, (const u_char *)key, key_len); + MD5Final((u_char *)key0, &tctx); + + key_len = 16; + } else + memcpy(key0, key, key_len); + + key = (const char *)key0; + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner MD5 */ + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + + /* + * Traverse the radius packet. + */ + if (check) + { + MD5Update(&context, (const u_char *)packet->pdata, 4); + MD5Update(&context, (unsigned char*)packet->request->pdata + ->authenticator, 16); + } + else + { + MD5Update(&context, (const u_char *)packet->pdata, + sizeof(RADIUS_PACKET_DATA)); + } + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + memset(zero16, 0, sizeof(zero16)); + + for(; attr<end; ADVANCE(attr)) + { + if (attr->type == RADIUS_TYPE_MESSAGE_AUTHENTICATOR) + { + MD5Update(&context, (u_char *)attr, 2); + MD5Update(&context, (u_char *)zero16, sizeof(zero16)); + } else + MD5Update(&context, (u_char *)attr, (int)attr->length); + } + + MD5Final((u_char *)digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, (u_char *)digest, 16);/* then results of 1st hash */ + MD5Final((u_char *)digest, &context); /* finish up 2nd pass */ +} + +/* RFC 3579 */ +int radius_put_message_authenticator(RADIUS_PACKET *packet, const char *secret) +{ + int rval; + u_char md5result[16]; + RADIUS_ATTRIBUTE* attr; + RADIUS_ATTRIBUTE* end; + + if ((rval = radius_put_raw_attr(packet, + RADIUS_TYPE_MESSAGE_AUTHENTICATOR, md5result, sizeof(md5result))) + != 0) + return rval; + + radius_hmac_md5(packet, secret, strlen(secret), (caddr_t)md5result, 0); + + attr = ATTRS_BEGIN(packet->pdata); + end = ATTRS_END(packet->pdata); + + for(; attr<end; ADVANCE(attr)) + { + if (attr->type == RADIUS_TYPE_MESSAGE_AUTHENTICATOR) + { + memcpy(attr->data, md5result, sizeof(md5result)); + break; + } + } + + return 0; +} + +int radius_check_message_authenticator(RADIUS_PACKET *packet, + const char *secret) +{ + int rval; + u_char len, md5result0[16], md5result1[16]; + + radius_hmac_md5(packet, secret, strlen(secret), (caddr_t)md5result0, + 1); + + if ((rval = radius_get_raw_attr(packet, + RADIUS_TYPE_MESSAGE_AUTHENTICATOR, md5result1, &len)) != 0) + return rval; + + if (len != sizeof(md5result1)) + return -1; + + return memcmp(md5result0, md5result1, sizeof(md5result1)); +} diff --git a/usr.sbin/npppd/npppd/radius+.h b/usr.sbin/npppd/npppd/radius+.h new file mode 100644 index 00000000000..ecc5c3a5472 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius+.h @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * radius+.h : + * yet another RADIUS library + */ +#ifndef RADIUS_PLUS_H +#define RADIUS_PLUS_H + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/******* packet manipulation support *******/ + +typedef struct _RADIUS_PACKET RADIUS_PACKET; + +/* constructors */ +RADIUS_PACKET* radius_new_request_packet(u_int8_t code); +RADIUS_PACKET* radius_new_response_packet(u_int8_t code, + const RADIUS_PACKET* request); +RADIUS_PACKET* radius_convert_packet(const void* pdata, size_t length); + +/* destructor */ +int radius_delete_packet(RADIUS_PACKET* packet); + +/* accessors - header values */ +u_int8_t radius_get_id(const RADIUS_PACKET* packet); +u_int8_t radius_get_code(const RADIUS_PACKET* packet); +void radius_get_authenticator(const RADIUS_PACKET* packet, char* authenticator); +void radius_set_request_packet(RADIUS_PACKET* packet, const RADIUS_PACKET* response); +int radius_check_response_authenticator(const RADIUS_PACKET* packet, const char *secret); +const char* radius_get_authenticator_retval(const RADIUS_PACKET* packet); +void radius_set_response_authenticator(RADIUS_PACKET* packet, + const char* secret); +u_int16_t radius_get_length(const RADIUS_PACKET* packet); +const void* radius_get_data(const RADIUS_PACKET* packet); +const char* trim_ppp_username(const char *name); + +/* accessors - raw attributes */ +int radius_get_raw_attr(const RADIUS_PACKET* packet, u_int8_t type, + void* buf, u_int8_t* length); +int radius_put_raw_attr(RADIUS_PACKET* packet, u_int8_t type, + const void* buf, u_int8_t length); +int radius_get_raw_attr_all(const RADIUS_PACKET* packet, u_int8_t type, + caddr_t buf, int* length); +int radius_put_raw_attr_all(RADIUS_PACKET* packet, u_int8_t type, + const caddr_t buf, int length); +int radius_get_vs_raw_attr(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, void* buf, u_int8_t* length); +int radius_get_vs_raw_attr_ptr(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, void** ptr, u_int8_t* length); +int radius_put_vs_raw_attr(RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, const void* buf, u_int8_t length); +int radius_get_vs_raw_attr_all(const RADIUS_PACKET* packet, u_int8_t type, + caddr_t buf, int* length); +int radius_put_vs_raw_attr_all(RADIUS_PACKET* packet, u_int8_t type, + const caddr_t buf, int length); + +/* accessors - typed attributes */ +int radius_get_uint32_attr(const RADIUS_PACKET* packet, u_int8_t type, + u_int32_t* val); +u_int32_t radius_get_uint32_attr_retval(const RADIUS_PACKET* packet, + u_int8_t type); +int radius_put_uint32_attr(RADIUS_PACKET* packet, u_int8_t type, u_int32_t val); + +int radius_get_string_attr(const RADIUS_PACKET* packet, u_int8_t type, + char* str); +int radius_put_string_attr(RADIUS_PACKET* packet, u_int8_t type, + const char* str); +int radius_get_vs_string_attr(const RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, char* str); +int radius_put_vs_string_attr(RADIUS_PACKET* packet, u_int32_t vendor, + u_int8_t vtype, const char* str); + +int radius_get_ipv4_attr(const RADIUS_PACKET* packet, u_int8_t type, + struct in_addr* addr); +struct in_addr radius_get_ipv4_attr_retval(const RADIUS_PACKET* packet, u_int8_t type); +int radius_put_ipv4_attr(RADIUS_PACKET* packet, u_int8_t type, + struct in_addr addr); +int radius_put_message_authenticator(RADIUS_PACKET *packet, const char *secret); +int radius_check_message_authenticator(RADIUS_PACKET *packet, + const char *secret); + +/* helpers */ +RADIUS_PACKET* radius_recvfrom(int s, int flags, + struct sockaddr* saddr, socklen_t* slen); +int radius_sendto(int s, const RADIUS_PACKET* packet, int flags, + const struct sockaddr* saddr, socklen_t slen); + + +/******* client support (sending request / receiving response) *******/ + +typedef struct _RADIUS_SERVER RADIUS_SERVER; + +/* constrcutors */ +RADIUS_SERVER* radius_new_server(void); +RADIUS_SERVER* radius_new_auth_server(void); +RADIUS_SERVER* radius_new_acct_server(void); + +/* destructors */ +int radius_delete_server(RADIUS_SERVER* server); + +/* synchronous requesting */ +RADIUS_PACKET* radius_send_request(RADIUS_SERVER* server, + const RADIUS_PACKET* packet); + +/* asynchronous requesting - use select(2) */ +int radius_async_request_init(RADIUS_SERVER* server, + const RADIUS_PACKET* packet, int* fd); +RADIUS_PACKET* radius_async_request_send(RADIUS_SERVER* server); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/radius+_local.h b/usr.sbin/npppd/npppd/radius+_local.h new file mode 100644 index 00000000000..eb977e87dd9 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius+_local.h @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * radius+.cc : + * yet another RADIUS library + */ +#ifndef RADIUSPLUS_LOCAL_H +#define RADIUSPLUS_LOCAL_H + +#include "nint.h" + +#ifndef countof +#define countof(x) (sizeof(x)/sizeof((x)[0])) +#endif + +#pragma pack(1) +typedef struct _RADIUS_PACKET_DATA +{ + u_int8_t code; + u_int8_t id; + nuint16 length; + char authenticator[16]; + char attributes[0]; +} RADIUS_PACKET_DATA; + +typedef struct _RADIUS_ATTRIBUTE +{ + u_int8_t type; + u_int8_t length; + char data[0]; + nuint32 vendor; + u_int8_t vtype; + u_int8_t vlength; + char vdata[0]; +} RADIUS_ATTRIBUTE; +#pragma pack() + +struct _RADIUS_PACKET +{ + RADIUS_PACKET_DATA* pdata; + size_t capacity; + const RADIUS_PACKET* request; +}; + +#define RADIUS_PACKET_CAPACITY_INITIAL 64 +#define RADIUS_PACKET_CAPACITY_INCREMENT 64 + +extern u_int8_t radius_id_counter; + +inline void ADVANCE(RADIUS_ATTRIBUTE*& rp) +{ + rp = (RADIUS_ATTRIBUTE*)(((char*)rp) + rp->length); +} + +inline void ADVANCE(const RADIUS_ATTRIBUTE*& rp) +{ + rp = (const RADIUS_ATTRIBUTE*)(((const char*)rp) + rp->length); +} + +inline RADIUS_ATTRIBUTE* ATTRS_BEGIN(RADIUS_PACKET_DATA* pdata) +{ + return (RADIUS_ATTRIBUTE*)pdata->attributes; +} + +inline const RADIUS_ATTRIBUTE* ATTRS_BEGIN(const RADIUS_PACKET_DATA* pdata) +{ + return (const RADIUS_ATTRIBUTE*)pdata->attributes; +} + +inline RADIUS_ATTRIBUTE* ATTRS_END(RADIUS_PACKET_DATA* pdata) +{ + return (RADIUS_ATTRIBUTE*)(((char*)pdata) + pdata->length); +} + +inline const RADIUS_ATTRIBUTE* ATTRS_END(const RADIUS_PACKET_DATA* pdata) +{ + return (const RADIUS_ATTRIBUTE*)(((const char*)pdata) + pdata->length); +} + +#ifndef MIN +#define MIN(m,n) (((m) < (n))? (m) : (n)) +#endif + +#endif /* RADIUSPLUS_LOCAL_H */ diff --git a/usr.sbin/npppd/npppd/radius_chap_const.h b/usr.sbin/npppd/npppd/radius_chap_const.h new file mode 100644 index 00000000000..1ab69485fe4 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius_chap_const.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * idsrv/idradiusd より。 + */ +#pragma pack(1) + +struct RADIUS_CHAP_PASSWORD { + u_int8_t ident; + char response[16]; +}; + +struct RADIUS_MS_CHAP_RESPONSE { + u_int8_t ident; + u_int8_t flags; + char lmresponse[24]; + char ntresponse[24]; +}; + +struct RADIUS_MS_CHAP2_RESPONSE { + u_int8_t ident; + u_int8_t flags; + char peer_challenge[16]; + char reserved[8]; + char response[24]; +}; + +struct RADIUS_MS_CHAP2_SUCCESS { + u_int8_t ident; + char str[42]; +}; + +struct RADIUS_MPPE_KEY { + char salt[2]; + char key[253]; /* + * key は RFC では 16 * n byteの長さ, + * ここでは salt + key = 255 byte (radius attribute サイズ) + */ +}; + +struct RADIUS_MS_CHAP2_ERROR { + u_int8_t ident; + char str[15]; +}; +#pragma pack() diff --git a/usr.sbin/npppd/npppd/radius_common.c b/usr.sbin/npppd/npppd/radius_common.c new file mode 100644 index 00000000000..010557c4a60 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius_common.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * RADIUS 関連共通関数。 + * + * @author Yasuoka Masahiko + * $Id: radius_common.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <time.h> +#include <event.h> +#include <stdio.h> + +#include <radius+.h> +#include <radiusconst.h> + +#include "slist.h" +#include "npppd.h" +#include "npppd_local.h" + +#include "radius_common.h" + +/** RADIUS パケットの Framed-IP-Address アートリビュートを処理します。 */ +void +ppp_proccess_radius_framed_ip_address(npppd_ppp *_this, RADIUS_PACKET *pkt) +{ + uint8_t len; + u_char buf[256], *bufp; + + if ((_this->pppd->ip_assign_flags & NPPPD_IP_ASSIGN_RADIUS) == 0) + return; + + if (radius_get_raw_attr(pkt, RADIUS_TYPE_FRAMED_IP_ADDRESS, buf, + &len) != 0) + return; + + bufp = buf; + if (len == 4) + GETLONG(_this->radius_framed_ip_address.s_addr, bufp); + +} + +/** npppd に設定された {@link ::radius_req_setting} を取り出します。 */ +radius_req_setting * +npppd_get_radius_req_setting(npppd *_this) +{ + return &_this->rad_auth; +} diff --git a/usr.sbin/npppd/npppd/radius_common.h b/usr.sbin/npppd/npppd/radius_common.h new file mode 100644 index 00000000000..db7979dfd21 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius_common.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef RADIUS_COMMON_H +#define RADIUS_COMMON_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +void ppp_proccess_radius_framed_ip_address (npppd_ppp *, RADIUS_PACKET *); +radius_req_setting *npppd_get_radius_req_setting (npppd *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/radius_req.c b/usr.sbin/npppd/npppd/radius_req.c new file mode 100644 index 00000000000..f58aef23583 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius_req.c @@ -0,0 +1,373 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: radius_req.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * radius+ ライブラリと event(3) を組み合わせた、RADIUS 要求のためのユーティ + * リティ。 + * @author Yasuoka Masahiko + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +#include <stdio.h> +#include <syslog.h> +#include <stdlib.h> +#include <radius+.h> +#include <radiusconst.h> +#include <debugutil.h> +#include <time.h> +#include <event.h> +#include <string.h> + +#include "radius_req.h" + +struct overlapped { + struct event ev_sock; + int socket; + int ntry; + int timeout; + struct sockaddr_storage ss; + void *context; + radius_response *response_fn; + char secret[MAX_RADIUS_SECRET]; + RADIUS_PACKET *pkt; +}; + +static int radius_request0 (struct overlapped *); +static void radius_request_io_event (int, short, void *); +static int select_srcaddr(struct sockaddr const *, struct sockaddr *, socklen_t *); + +#ifdef RADIUS_REQ_DEBUG +#define RADIUS_REQ_ASSERT(cond) \ + if (!(cond)) { \ + fprintf(stderr, \ + "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ + , __func__, __FILE__, __LINE__); \ + abort(); \ + } +#else +#define RADIUS_REQ_ASSERT(cond) +#endif + +/** + * RADIUS リクエストを送信します。指定した pkt(RADIUS パケット) は、この実装 + * 内部で解放されます。 + * @param pkt 要求する RADIUS パケット。 + */ +void +radius_request(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt) +{ + struct overlapped *lap; + + RADIUS_REQ_ASSERT(pkt != NULL); + RADIUS_REQ_ASSERT(ctx != NULL); + lap = ctx; + lap->pkt = pkt; + if (radius_request0(lap) != 0) { + if (lap->response_fn != NULL) + lap->response_fn(lap->context, NULL, + RADIUS_REQUST_ERROR); + } +} + +/** + * NAS-IP-Address または NAS-IPv6-Address を準備します。 + * setting->server[setting->curr_server].sock にローカルアドレスがセッ + * トされていない場合には、サーバのアドレスから自動でローカルアドレスを + * セットします。 + */ +int +radius_prepare_nas_address(radius_req_setting *setting, + RADIUS_PACKET *pkt) +{ + int af; + struct sockaddr_in *sin4; + struct sockaddr_in6 *sin6; + socklen_t socklen; + + /* See RFC 2765, 3162 */ + RADIUS_REQ_ASSERT(setting != NULL); + + af = setting->server[setting->curr_server].peer.sin6.sin6_family; + RADIUS_REQ_ASSERT(af == AF_INET6 || af == AF_INET); + + sin4 = &setting->server[setting->curr_server].sock.sin4; + sin6 = &setting->server[setting->curr_server].sock.sin6; + + switch (af) { + case AF_INET: + socklen = sizeof(*sin4); + if (sin4->sin_addr.s_addr == INADDR_ANY) { + if (select_srcaddr((struct sockaddr const *) + &setting->server[setting->curr_server].peer, + (struct sockaddr *)sin4, &socklen) != 0) { + RADIUS_REQ_ASSERT("NOTREACHED" == NULL); + goto reigai; + } + } + if (radius_put_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, + sin4->sin_addr) != 0) + goto reigai; + break; + case AF_INET6: + socklen = sizeof(*sin6); + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (select_srcaddr((struct sockaddr const *) + &setting->server[setting->curr_server].peer, + (struct sockaddr *)sin4, &socklen) != 0) { + RADIUS_REQ_ASSERT("NOTREACHED" == NULL); + goto reigai; + } + } + if (radius_put_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, + sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)) + != 0) + goto reigai; + break; + } + + return 0; +reigai: + return 1; +} + +/** + * RADIUS リクエストを送信する準備を行います。 + * <p> + * 応答があった場合やタイムアウトなどのエラーは、指定した関数を呼び出(コール + * バック)して通知します。</p> + * <p> + * pctx に書き出されるコンテキストと応答コールバック関数で指定される + * 応答パケットは、この実装内部で解放されます。</p> + * @param setting RADIUS サーバや問い合わせの設定 + * @param context 呼び出し側コンテキスト + * @param pctx RADIUS リクエストコンテキスト(RADIUS_REQUEST_CTX) + * を書き出す領域を指定します。キャンセルする場合などに、 + * 使用します。NULL を指定しても構いません。 + * @param response_fn 応答を受信あるいはタイムアウトした時に呼び出す関数を + * 関数ポインタで指定します。 + * @param timeout 応答タイムアウトまでの秒数 + */ +int +radius_prepare(radius_req_setting *setting, void *context, + RADIUS_REQUEST_CTX *pctx, radius_response response_fn, int timeout) +{ + int sock; + struct overlapped *lap; + struct sockaddr_in6 *sin6; + + RADIUS_REQ_ASSERT(setting != NULL); + lap = NULL; + + if (setting->server[setting->curr_server].enabled == 0) + return 1; + if ((lap = malloc(sizeof(struct overlapped))) == NULL) { + log_printf(LOG_ERR, "malloc() failed in %s: %m", __func__); + goto reigai; + } + sin6 = &setting->server[setting->curr_server].peer.sin6; + if ((sock = socket(sin6->sin6_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + log_printf(LOG_ERR, "socket() failed in %s: %m", __func__); + goto reigai; + } + memset(lap, 0, sizeof(struct overlapped)); + memcpy(&lap->ss, &setting->server[setting->curr_server].peer, + setting->server[setting->curr_server].peer.sin6.sin6_len); + + lap->socket = sock; + lap->timeout = MIN(setting->timeout, timeout); + lap->ntry = timeout / lap->timeout; + lap->context = context; + lap->response_fn = response_fn; + memcpy(lap->secret, setting->server[setting->curr_server].secret, + sizeof(lap->secret)); + if (pctx != NULL) + *pctx = lap; + + return 0; +reigai: + if (lap != NULL) + free(lap); + + return 1; +} + +/** + * RADIUS 要求をキャンセルします。 + * @param {@link radius_request()} で受け取ったコンテキスト + */ +void +radius_cancel_request(RADIUS_REQUEST_CTX ctx) +{ + struct overlapped *lap; + + lap = ctx; + if (lap->socket >= 0) { + event_del(&lap->ev_sock); + close(lap->socket); + lap->socket = -1; + } + if (lap->pkt != NULL) { + radius_delete_packet(lap->pkt); + lap->pkt = NULL; + } + /* あえて no comment。*/ + memset(lap->secret, 0x41, sizeof(lap->secret)); + + free(lap); +} + +/** このコンテキストで利用している RADIUS サーバの共有秘密鍵を返します。*/ +const char * +radius_get_server_secret(RADIUS_REQUEST_CTX ctx) +{ + struct overlapped *lap; + + lap = ctx; + RADIUS_REQ_ASSERT(lap != NULL); + + return lap->secret; +} + +/** このコンテキストで利用している RADIUS サーバのアドレスを返します。*/ +struct sockaddr * +radius_get_server_address(RADIUS_REQUEST_CTX ctx) +{ + struct overlapped *lap; + + lap = ctx; + RADIUS_REQ_ASSERT(lap != NULL); + + return (struct sockaddr *)&lap->ss; +} + +static int +radius_request0(struct overlapped *lap) +{ + struct timeval tv0; + + RADIUS_REQ_ASSERT(lap->ntry > 0); + + lap->ntry--; + if (radius_sendto(lap->socket, lap->pkt, 0, (struct sockaddr *) + &lap->ss, lap->ss.ss_len) != 0) + return 1; + tv0.tv_usec = 0; + tv0.tv_sec = lap->timeout; + + event_set(&lap->ev_sock, lap->socket, EV_READ, + radius_request_io_event, lap); + event_add(&lap->ev_sock, &tv0); + + return 0; +} + +static void +radius_request_io_event(int fd, short evmask, void *context) +{ + struct overlapped *lap; + struct sockaddr_storage ss; + int flags; + socklen_t len; + RADIUS_PACKET *respkt; + + RADIUS_REQ_ASSERT(context != NULL); + + if ((evmask & EV_READ) != 0) { + lap = context; + flags = 0; + + RADIUS_REQ_ASSERT(lap->socket >= 0); + if (lap->socket < 0) + return; + RADIUS_REQ_ASSERT(lap->pkt != NULL); + + memset(&ss, 0, sizeof(ss)); + len = sizeof(ss); + if ((respkt = radius_recvfrom(lap->socket, 0, + (struct sockaddr *)&ss, &len)) == NULL) { + log_printf(LOG_ERR, "recvfrom() failed in %s: %m", + __func__); + flags |= RADIUS_REQUST_ERROR; + } else if (lap->secret[0] == '\0') { + flags |= RADIUS_REQUST_CHECK_AUTHENTICTOR_NO_CHECK; + } else { + radius_set_request_packet(respkt, lap->pkt); + if (!radius_check_response_authenticator(respkt, + lap->secret)) + flags |= RADIUS_REQUST_CHECK_AUTHENTICTOR_OK; + } + + if (lap->response_fn != NULL) + lap->response_fn(lap->context, respkt, flags); + + if (respkt != NULL) + radius_delete_packet(respkt); + radius_cancel_request(lap); + } else if ((evmask & EV_TIMEOUT) != 0) { + lap = context; + if (lap->ntry > 0) { + if (radius_request0(lap) != 0) { + if (lap->response_fn != NULL) + lap->response_fn(lap->context, NULL, + RADIUS_REQUST_ERROR); + radius_cancel_request(lap); + } + return; + } + if (lap->response_fn != NULL) + lap->response_fn(lap->context, NULL, + RADIUS_REQUST_TIMEOUT); + radius_cancel_request(lap); + } +} + +static int +select_srcaddr(struct sockaddr const *dst, struct sockaddr *src, + socklen_t *srclen) +{ + int sock; + + sock = -1; + if ((sock = socket(dst->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) + goto reigai; + if (connect(sock, dst, dst->sa_len) != 0) + goto reigai; + if (getsockname(sock, src, srclen) != 0) + goto reigai; + + close(sock); + + return 0; +reigai: + if (sock >= 0) + close(sock); + + return 1; +} diff --git a/usr.sbin/npppd/npppd/radius_req.h b/usr.sbin/npppd/npppd/radius_req.h new file mode 100644 index 00000000000..d0a4f0c1016 --- /dev/null +++ b/usr.sbin/npppd/npppd/radius_req.h @@ -0,0 +1,101 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef RADIUS_REQ_H +#define RADIUS_REQ_H 1 + +/** RADIUS 共有秘密鍵の最大長 */ +#define MAX_RADIUS_SECRET 128 + +/** RADIUS サーバの最大数 */ +#define MAX_RADIUS_SERVERS 16 + +/** RADIUS 要求が失敗した */ +#define RADIUS_REQUST_ERROR 0x0001 + +/** RADIUS 要求がタイムアウトした */ +#define RADIUS_REQUST_TIMEOUT 0x0002 + +/** Authenticator チェックは OK */ +#define RADIUS_REQUST_CHECK_AUTHENTICTOR_OK 0x0010 + +/** Authenticator チェックはしていない */ +#define RADIUS_REQUST_CHECK_AUTHENTICTOR_NO_CHECK 0x0020 + +/** RADIUS 応答を受信するコールバック関数の型 */ +typedef void (radius_response)(void *context, RADIUS_PACKET *pkt, int flags); + +/** RADIUS 要求/応答を行うためのコンテキストを示す型 */ +typedef void * RADIUS_REQUEST_CTX; + +/** RADIUS 要求の設定を示す型 */ +typedef struct _radius_req_setting +{ + /** サーバ配列 */ + struct { + union { + /** サーバの IPv6 アドレス */ + struct sockaddr_in6 sin6; + /** サーバの IPv4 アドレス */ + struct sockaddr_in sin4; + } /** サーバのアドレス */ peer; + union { + /** サーバの IPv6 アドレス */ + struct sockaddr_in6 sin6; + /** サーバの IPv4 アドレス */ + struct sockaddr_in sin4; + } /** サーバのアドレス */ sock; + char secret[MAX_RADIUS_SECRET]; + /** このアドレスが有効かどうか */ + int enabled; + } server[MAX_RADIUS_SERVERS]; + /** + * 現在使用しているサーバのインデックス。 + * <p> + * これを変更する仕組みは、radius_req.c では実装していません。npppd + * では npppd.c、npppd_auth.c + * あたりで実装しています。</p> + */ + int curr_server; + /** リクエストタイムアウト(秒) */ + int timeout; +} radius_req_setting; + +#ifdef __cplusplus +extern "C" { +#endif + +int radius_prepare (radius_req_setting *, void *, RADIUS_REQUEST_CTX *, radius_response *, int); +void radius_request (RADIUS_REQUEST_CTX, RADIUS_PACKET *); +void radius_cancel_request (RADIUS_REQUEST_CTX); +const char *radius_get_server_secret(RADIUS_REQUEST_CTX); +struct sockaddr *radius_get_server_address(RADIUS_REQUEST_CTX); +int radius_prepare_nas_address(radius_req_setting *, RADIUS_PACKET *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/npppd/radiusconst.h b/usr.sbin/npppd/npppd/radiusconst.h new file mode 100644 index 00000000000..8d3c18e75fb --- /dev/null +++ b/usr.sbin/npppd/npppd/radiusconst.h @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* 漢字コードはEUC 漢字コードはEUC */ +/* + * radiusconst.h : + * RADIUS constatnts + */ + +#ifndef RADIUS_CONST +#define RADIUS_CONST + + +/* RADIUS codes: see RFC2865 */ +#define RADIUS_CODE_ACCESS_REQUEST 1 +#define RADIUS_CODE_ACCESS_ACCEPT 2 +#define RADIUS_CODE_ACCESS_REJECT 3 +#define RADIUS_CODE_ACCOUNTING_REQUEST 4 +#define RADIUS_CODE_ACCOUNTING_RESPONSE 5 +#define RADIUS_CODE_ACCESS_CHALLENGE 11 +#define RADIUS_CODE_STATUS_SERVER 12 +#define RADIUS_CODE_STATUS_CLIENT 13 + + +/* RADIUS attributes: see RFC2865-2869 */ +#define RADIUS_TYPE_USER_NAME 1 +#define RADIUS_TYPE_USER_PASSWORD 2 +#define RADIUS_TYPE_CHAP_PASSWORD 3 +#define RADIUS_TYPE_NAS_IP_ADDRESS 4 +#define RADIUS_TYPE_NAS_PORT 5 +#define RADIUS_TYPE_SERVICE_TYPE 6 +#define RADIUS_TYPE_FRAMED_PROTOCOL 7 +#define RADIUS_TYPE_FRAMED_IP_ADDRESS 8 +#define RADIUS_TYPE_FRAMED_IP_NETMASK 9 +#define RADIUS_TYPE_FRAMED_ROUTING 10 +#define RADIUS_TYPE_FILTER_ID 11 +#define RADIUS_TYPE_FRAMED_MTU 12 +#define RADIUS_TYPE_FRAMED_COMPRESSION 13 +#define RADIUS_TYPE_LOGIN_IP_HOST 14 +#define RADIUS_TYPE_LOGIN_SERVICE 15 +#define RADIUS_TYPE_LOGIN_TCP_PORT 16 +/* unassigned 17 */ +#define RADIUS_TYPE_REPLY_MESSAGE 18 +#define RADIUS_TYPE_CALLBACK_NUMBER 19 +#define RADIUS_TYPE_CALLBACK_ID 20 +/* unassigned 21 */ +#define RADIUS_TYPE_FRAMED_ROUTE 22 +#define RADIUS_TYPE_FRAMED_IPX_NETWORK 23 +#define RADIUS_TYPE_STATE 24 +#define RADIUS_TYPE_CLASS 25 +#define RADIUS_TYPE_VENDOR_SPECIFIC 26 +#define RADIUS_TYPE_SESSION_TIMEOUT 27 +#define RADIUS_TYPE_IDLE_TIMEOUT 28 +#define RADIUS_TYPE_TERMINATION_ACTION 29 +#define RADIUS_TYPE_CALLED_STATION_ID 30 +#define RADIUS_TYPE_CALLING_STATION_ID 31 +#define RADIUS_TYPE_NAS_IDENTIFIER 32 +#define RADIUS_TYPE_PROXY_STATE 33 +#define RADIUS_TYPE_LOGIN_LAT_SERVICE 34 +#define RADIUS_TYPE_LOGIN_LAT_NODE 35 +#define RADIUS_TYPE_LOGIN_LAT_GROUP 36 +#define RADIUS_TYPE_FRAMED_APPLETALK_LINK 37 +#define RADIUS_TYPE_FRAMED_APPLETALK_NETWORK 38 +#define RADIUS_TYPE_FRAMED_APPLETALK_ZONE 39 +#define RADIUS_TYPE_ACCT_STATUS_TYPE 40 +#define RADIUS_TYPE_ACCT_DELAY_TIME 41 +#define RADIUS_TYPE_ACCT_INPUT_OCTETS 42 +#define RADIUS_TYPE_ACCT_OUTPUT_OCTETS 43 +#define RADIUS_TYPE_ACCT_SESSION_ID 44 +#define RADIUS_TYPE_ACCT_AUTHENTIC 45 +#define RADIUS_TYPE_ACCT_SESSION_TIME 46 +#define RADIUS_TYPE_ACCT_INPUT_PACKETS 47 +#define RADIUS_TYPE_ACCT_OUTPUT_PACKETS 48 +#define RADIUS_TYPE_ACCT_TERMINATE_CAUSE 49 +#define RADIUS_TYPE_ACCT_MULTI_SESSION_ID 50 +#define RADIUS_TYPE_ACCT_LINK_COUNT 51 +#define RADIUS_TYPE_ACCT_INPUT_GIGAWORDS 52 +#define RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS 53 +/* unassigned (for accounting) 54 */ +#define RADIUS_TYPE_EVENT_TIMESTAMP 55 +/* unassigned (for accounting) 56 */ +/* unassigned (for accounting) 57 */ +/* unassigned (for accounting) 58 */ +/* unassigned (for accounting) 59 */ +#define RADIUS_TYPE_CHAP_CHALLENGE 60 +#define RADIUS_TYPE_NAS_PORT_TYPE 61 +#define RADIUS_TYPE_PORT_LIMIT 62 +#define RADIUS_TYPE_LOGIN_LAT_PORT 63 +#define RADIUS_TYPE_TUNNEL_TYPE 64 +#define RADIUS_TYPE_TUNNEL_MEDIUM_TYPE 65 +#define RADIUS_TYPE_TUNNEL_CLIENT_ENDPOINT 66 +#define RADIUS_TYPE_TUNNEL_SERVER_ENDPOINT 67 +#define RADIUS_TYPE_ACCT_TUNNEL_CONNECTION 68 +#define RADIUS_TYPE_TUNNEL_PASSWORD 69 +#define RADIUS_TYPE_ARAP_PASSWORD 70 +#define RADIUS_TYPE_ARAP_FEATURES 71 +#define RADIUS_TYPE_ARAP_ZONE_ACCESS 72 +#define RADIUS_TYPE_ARAP_SECURITY 73 +#define RADIUS_TYPE_ARAP_SECURITY_DATA 74 +#define RADIUS_TYPE_PASSWORD_RETRY 75 +#define RADIUS_TYPE_PROMPT 76 +#define RADIUS_TYPE_CONNECT_INFO 77 +#define RADIUS_TYPE_CONFIGURATION_TOKEN 78 +#define RADIUS_TYPE_EAP_MESSAGE 79 +#define RADIUS_TYPE_MESSAGE_AUTHENTICATOR 80 +#define RADIUS_TYPE_TUNNEL_PRIVATE_GROUP_ID 81 +#define RADIUS_TYPE_TUNNEL_ASSIGNMENT_ID 82 +#define RADIUS_TYPE_TUNNEL_PREFERENCE 83 +#define RADIUS_TYPE_ARAP_CHALLENGE_RESPONSE 84 +#define RADIUS_TYPE_ACCT_INTERIM_INTERVAL 85 +#define RADIUS_TYPE_ACCT_TUNNEL_PACKETS_LOST 86 +#define RADIUS_TYPE_NAS_PORT_ID 87 +#define RADIUS_TYPE_FRAMED_POOL 88 +/* unassigned 89 */ +#define RADIUS_TYPE_TUNNEL_CLIENT_AUTH_ID 90 +#define RADIUS_TYPE_TUNNEL_SERVER_AUTH_ID 91 + +/* RFC 3162 "RADIUS and IPv6" */ +#define RADIUS_TYPE_NAS_IPV6_ADDRESS 95 +#define RADIUS_TYPE_FRAMED_INTERFACE_ID 96 +#define RADIUS_TYPE_FRAMED_IPV6_PREFIX 97 +#define RADIUS_TYPE_LOGIN_IPV6_HOST 98 +#define RADIUS_TYPE_FRAMED_IPV6_ROUTE 99 +#define RADIUS_TYPE_FRAMED_IPV6_POOL 100 + +/* RFC 2865 "5.6. Service-Type" */ +#define RADIUS_FRAMED_PROTOCOL_PPP 1 + +/* RFC 2865 "5.7. Framed-Protocol" */ +#define RADIUS_SERVICE_TYPE_LOGIN 1 +#define RADIUS_SERVICE_TYPE_FRAMED 2 +#define RADIUS_SERVICE_TYPE_CB_LOGIN 3 +#define RADIUS_SERVICE_TYPE_CB_FRAMED 4 +#define RADIUS_SERVICE_TYPE_OUTBOUND 5 +#define RADIUS_SERVICE_TYPE_ADMINISTRATIVE 6 +#define RADIUS_SERVICE_TYPE_NAS_PROMPT 7 +#define RADIUS_SERVICE_TYPE_AUTHENTICAT_ONLY 8 +#define RADIUS_SERVICE_TYPE_CB_NAS_PROMPTi 9 +#define RADIUS_SERVICE_TYPE_CALL_CHECK 10 +#define RADIUS_SERVICE_TYPE_CB_ADMINISTRATIVE 11 + +/* Microsoft vendor specific attributes: see RFC2548*/ +#define RADIUS_VENDOR_MICROSOFT 311 +#define RADIUS_VTYPE_MS_CHAP_RESPONSE 1 +#define RADIUS_VTYPE_MS_CHAP_ERROR 2 +#define RADIUS_VTYPE_MS_CHAP_PW_1 3 +#define RADIUS_VTYPE_MS_CHAP_PW_2 4 +#define RADIUS_VTYPE_MS_CHAP_LM_ENC_PW 5 +#define RADIUS_VTYPE_MS_CHAP_NT_ENC_PW 6 +#define RADIUS_VTYPE_MPPE_ENCRYPTION_POLICY 7 +#define RADIUS_VTYPE_MPPE_ENCRYPTION_TYPES 8 +#define RADIUS_VTYPE_MS_RAS_VENDOR 9 +#define RADIUS_VTYPE_MS_CHAP_CHALLENGE 11 +#define RADIUS_VTYPE_MS_CHAP_MPPE_KEYS 12 +#define RADIUS_VTYPE_MS_BAP_USAGE 13 +#define RADIUS_VTYPE_MS_LINK_UTILIZATION_THRESHOLD 14 +#define RADIUS_VTYPE_MS_LINK_DROP_TIME_LIMIT 15 +#define RADIUS_VTYPE_MPPE_SEND_KEY 16 +#define RADIUS_VTYPE_MPPE_RECV_KEY 17 +#define RADIUS_VTYPE_MS_RAS_VERSION 18 +#define RADIUS_VTYPE_MS_OLD_ARAP_PASSWORD 19 +#define RADIUS_VTYPE_MS_NEW_ARAP_PASSWORD 20 +#define RADIUS_VTYPE_MS_ARAP_PASSWORD_CHANGE_REASON 21 +#define RADIUS_VTYPE_MS_FILTER 22 +#define RADIUS_VTYPE_MS_ACCT_AUTH_TYPE 23 +#define RADIUS_VTYPE_MS_ACCT_EAP_TYPE 24 +#define RADIUS_VTYPE_MS_CHAP2_RESPONSE 25 +#define RADIUS_VTYPE_MS_CHAP2_SUCCESS 26 +#define RADIUS_VTYPE_MS_CHAP2_PW 27 +#define RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER 28 +#define RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER 29 +#define RADIUS_VTYPE_MS_PRIMARY_NBNS_SERVER 30 +#define RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER 31 +/* unassigned? 32 */ +#define RADIUS_VTYPE_MS_ARAP_CHALLENGE 33 + + +/* IIJ vendor specific attributes */ +#define RADIUS_VENDOR_IIJ 770 +#define RADIUS_VTYPE_IIJ_SID 1 + + +#endif diff --git a/usr.sbin/npppd/npppd/version.h b/usr.sbin/npppd/npppd/version.h new file mode 100644 index 00000000000..11b0f56f318 --- /dev/null +++ b/usr.sbin/npppd/npppd/version.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#define MAJOR_VERSION 5 +#define MINOR_VERSION 2 +#define BUGFIX_NO 0 +#define VERSION "5.0.0" diff --git a/usr.sbin/npppd/npppdctl/npppdctl.c b/usr.sbin/npppd/npppdctl/npppdctl.c new file mode 100644 index 00000000000..5d5300a9b8d --- /dev/null +++ b/usr.sbin/npppd/npppdctl/npppdctl.c @@ -0,0 +1,482 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*- + * Copyright (c) 2005 + * Internet Initiative Japan Inc. All rights reserved. + */ +/**@file + * vdipwho との互換性を保ちつつ併せて次の機能を提供します。 + * <ul> + * <li>利用状況<br> + * vdipwho 接続中のユーザ名、割り当てアドレス、開始時刻、レイヤ 2 のプロト + * コル名、レイヤ 2 の接続元アドレス</li> + * <li>(-s オプション) 統計情報を表示します。<br> + * npppd の ppp の Id、ユーザ名、入出力バイト、パケット、エラー数</li> + * <li>(-d オプション) 切断機能<br> + * 指定した PPP ユーザ名の全ての接続を切断します。<br> + * </ul> + */ +/* $Id: npppdctl.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <net/if_dl.h> +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <event.h> +#include <libgen.h> +#include <netdb.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "debugutil.h" +#include "npppd_local.h" +#include "npppd_ctl.h" + +#ifndef MIN +#define MIN(m,n) ((m) < (n))? (m) : (n) +#endif +#define DEFAULT_TIMEOUT 5 + +/** UNIX ドメインデータグラムの待ち受け用ファイル。*/ +char dgramsock[] = "/tmp/npppdctl.XXXXXX"; + +/** npppd 側のソケットアドレス */ +struct sockaddr_un peersock; + +/** ソケット */ +int sock = -1; + +/** UNIXタイム表示 (vdipwho由来) */ +int uflag = 0; + +/** 名前を引かない (vdipwho由来) */ +int nflag = 0; + +/** 受信バッファサイズ */ +int rcvbuf_sz = DEFAULT_NPPPD_CTL_MAX_MSGSZ; + +/** 長い行。80桁抑制しない */ +int lflag = 0; + +static void usage (void); +static void on_exit (void); +static void npppd_who (int); +static void npppd_disconnect (const char *); +static const char *eat_null (const char *); +static void npppd_ctl_termid_authen(const char *, const char *); +static void npppd_ctl_common(int); +static void print_who(struct npppd_who *); +static void print_stat(struct npppd_who *); + +static const char *progname = NULL; + +/** 使い方を表示します。 */ +static void +usage(void) +{ + + fprintf(stderr, + "usage: %s [-slnuh] [-d ppp_user] [-r rcvbuf_sz] [-p npppd_ctl_path]\n" + " %s -c [-r rcvbuf_sz] {ppp_id | ip} auth_id\n" + "usage: %s [-r]\n" + "\t-R: Reset the routing table.\n" + "\t-c: Set the client auth's auth-id.\n" + "\t-d: Disconnect specified user.\n" + "\t-h: Show this usage.\n" + "\t-l: Use long line to display information.\n" + "\t-n: Don't convert addresses/ports to names.\n" + "\t-p: Specify the path to the npppd's control socket.\n" + "\t-r: Receive buffer size (default %d).\n" + "\t-s: Show statistics informations instead of who.\n" + "\t-u: Show 'since' field as unix time.\n", + progname, progname, progname, rcvbuf_sz); +} + +static void +on_signal(int notused) +{ + exit(1); +} + +/** nppdctl エントリポイント */ +int +main(int argc, char *argv[]) +{ + int ch, sflag, fdgramsock, cflag, rtflag; + const char *path = DEFAULT_NPPPD_CTL_SOCK_PATH; + const char *disconn; + struct sockaddr_un sun; + struct timeval tv; + extern int optind; + + progname = basename(argv[0]); + disconn = NULL; + sflag = cflag = rtflag = 0; + while ((ch = getopt(argc, argv, "lcd:sunhp:r:R")) != -1) { + switch (ch) { + case 'n': + nflag = 1; + break; + case 'd': + disconn = optarg; + break; + case 's': + sflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'r': + if (sscanf(optarg, "%d", &rcvbuf_sz) != 1) { + fprintf(stderr, "Invalid argument: %s\n", + optarg); + exit(1); + } + break; + case 'p': + path = optarg; + break; + case 'R': + rtflag = 1; + break; + case 'h': + case '?': + default: + usage(); + exit(1); + } + } + argc -= optind; + argv += optind; + + // UNIX ドメインデータグラムの待ち受け用ファイル。 + if ((fdgramsock = mkstemp(dgramsock)) < 0) + err(1, "mkstemp"); + + // 終了時に削除するフック + if (atexit(on_exit) != 0) + err(1, "atexit"); + signal(SIGINT, on_signal); + signal(SIGHUP, on_signal); + signal(SIGTERM, on_signal); + + if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + err(1, "socket"); + tv.tv_sec = DEFAULT_TIMEOUT; + tv.tv_usec = 0; + + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf_sz, + sizeof(rcvbuf_sz)) < 0) + err(1, "setsockopt(SO_RCVBUF)"); + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + err(1, "setsockopt(SO_RCVTIMEO)"); + + // 待ち受けソケット準備 + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, dgramsock, sizeof(sun.sun_path)); + // mkstemp でファイルを作ってしまっているので、削除 + close(fdgramsock); unlink(dgramsock); + + if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) != 0) + err(1, "bind()"); + if (chmod(dgramsock, 0600) != 0) + err(1, "chmod(%s,%d)", dgramsock, 0600); + + // npppd に接続するソケット + memset(&peersock, 0, sizeof(peersock)); + peersock.sun_family = AF_UNIX; + peersock.sun_len = sizeof(peersock); + strlcpy(peersock.sun_path, path, sizeof(peersock.sun_path)); + + if (disconn != NULL) + npppd_disconnect(disconn); + else if (sflag > 0) + npppd_who(1); + else if (rtflag) + npppd_ctl_common(NPPPD_CTL_CMD_RESET_ROUTING_TABLE); + else if (cflag > 0) { + if (argc < 2) { + usage(); + exit(1); + } + npppd_ctl_termid_authen(argv[0], argv[1]); + } else + npppd_who(0); + + close(sock); + sock = -1; + + return 0; +} + +/** 終了処理。待ち受けソケットを消します。*/ +static void +on_exit(void) +{ + if (sock >= 0) + close(sock); + unlink(dgramsock); +} + +/** + * vdipwho と同じ出力をします。 + * <p> + * 出力例 +<pre>name assigned since proto from +yasuoka3 10.0.0.116 Aug 02 21:10 L2TP 192.168.159.103:1701</pre></p> + */ +static void +npppd_who(int show_stat) +{ + int i, n, sz, command; + struct npppd_who_list *l; + + if ((l = malloc(rcvbuf_sz)) == NULL) + err(1, "malloc(%d)", rcvbuf_sz); + + command = NPPPD_CTL_CMD_WHO; + if (sendto(sock, &command, sizeof(command), 0, + (struct sockaddr *)&peersock, sizeof(peersock)) < 0) + err(1 ,"sendto() failed"); + + if (!show_stat) + printf("name assigned since proto " + "from\n"); + else + printf("id name in(Kbytes/pkts/errs)" + " out(Kbytes/pkts/errs)\n"); + + n = 0; + l->count = -1; + do { + if ((sz = recv(sock, l, rcvbuf_sz, 0)) <= 0) + break; + for (i = 0; n < l->count && + offsetof(struct npppd_who_list, entry[i + 1]) <= sz; + i++, n++) { + if (show_stat) + print_stat(&l->entry[i]); + else + print_who(&l->entry[i]); + } + } while (i < l->count); + + if (l->count >= 0) { + if (n < l->count) + warn("Warning: received message is truncated. " + "Received %d user informations, but %d users are " + "active.", n, l->count); + } else { + warn("recv"); + } + + free(l); +} + +static void +print_who(struct npppd_who *w) +{ + int niflags, hasserv; + char assign_ip4buf[16]; + char timestr[64], hoststr[NI_MAXHOST], servstr[NI_MAXSERV]; + struct sockaddr *sa; + + if(nflag) + niflags = NI_NUMERICHOST; + else + niflags = 0; + + strlcpy(assign_ip4buf, inet_ntoa(w->assign_ip4), sizeof(assign_ip4buf)); + if(!uflag) + strftime(timestr, sizeof(timestr), "%b %d %H:%M", + localtime(&w->time)); + else + snprintf(timestr, sizeof(timestr), "%ld", (long)w->time); + + sa = (struct sockaddr *)&w->phy_info; + + hasserv = (sa->sa_family == AF_INET || sa->sa_family ==AF_INET6)? 1 : 0; + if (sa->sa_family == AF_LINK) { + snprintf(hoststr, sizeof(hoststr), + "%02x:%02x:%02x:%02x:%02x:%02x", + LLADDR((struct sockaddr_dl *)sa)[0] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[1] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[2] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[3] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[4] & 0xff, + LLADDR((struct sockaddr_dl *)sa)[5] & 0xff); + } if (sa->sa_family < AF_MAX) { + getnameinfo((const struct sockaddr *)&w->phy_info, + sa->sa_len, hoststr, sizeof(hoststr), servstr, + sizeof(servstr), + niflags | ((hasserv)? NI_NUMERICSERV :0)); + } else if (sa->sa_family == NPPPD_AF_PHONE_NUMBER) { + strlcpy(hoststr, ((npppd_phone_number *)sa)->pn_number, + sizeof(hoststr)); + } else { + strlcpy(hoststr, "error", sizeof(hoststr)); + } + + if (hasserv) + printf((lflag) + ? "%-15s %-15s %-12s %-6s %s:%s\n" + : "%-15.15s %-15s %-12s %-6s %s:%s\n", + eat_null(w->name), assign_ip4buf, timestr, w->phy_label, + hoststr, servstr); + else + printf((lflag) + ? "%-15s %-15s %-12s %-6s %s\n" + : "%-15.15s %-15s %-12s %-6s %s\n", + eat_null(w->name), assign_ip4buf, timestr, w->phy_label, + hoststr); +} + +static void +print_stat(struct npppd_who *w) +{ + printf((lflag) + ? "%7d %-20s %9.1f %7u %5u %9.1f %7u %5u\n" + : "%7d %-20.20s %9.1f %7u %5u %9.1f %7u %5u\n", w->id, + eat_null(w->name), (double)w->ibytes/1024, w->ipackets, w->ierrors, + (double)w->obytes/1024, w->opackets, w->oerrors); +} + +/** ユーザ名で切断 */ +static void +npppd_disconnect(const char *username) +{ + int sz, len; + u_char buf[BUFSIZ]; + struct npppd_disconnect_user_req *req; + + req = (struct npppd_disconnect_user_req *)buf; + req->command = NPPPD_CTL_CMD_DISCONNECT_USER; + len = sizeof(struct npppd_disconnect_user_req); + len += strlcpy(req->username, username, sizeof(req->username)); + + if (sendto(sock, buf, sizeof(struct npppd_disconnect_user_req), 0, + (struct sockaddr *)&peersock, sizeof(peersock)) < 0) + err(1, "sendto"); + if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0) + err(1, "recv"); + + buf[sz] = '\0'; + printf("%s\n", buf); +} + +/** + * str に指定した文字列が、NULLポインタまたは空文字列の場合 "<none>" に変更し + * て返します。それ以外の場合は、指定した文字列をそのまま返します。 + */ +static const char * +eat_null(const char *str) +{ + if (str == NULL || *str == '\0') + return "<none>"; + return str; +} + +static void +npppd_ctl_termid_authen(const char *ppp_key, const char *authid) +{ + int sz; + char *ep; + long lval; + struct npppd_ctl_termid_set_auth_request req = { + .command = NPPPD_CTL_CMD_TERMID_SET_AUTH, + .reserved = 0, + }; + u_char buf[BUFSIZ]; + struct in_addr ip4; + + if (inet_pton(AF_INET, ppp_key, &ip4) == 1) { + req.ppp_key_type = NPPPD_CTL_PPP_FRAMED_IP_ADDRESS; + req.ppp_key.framed_ip_address.s_addr = ip4.s_addr; + } else { + errno = 0; + lval = strtol(ppp_key, &ep, 10); + if (ppp_key[0] == '\0' || *ep != '\0') { + fprintf(stderr, "not a number: %s\n", ppp_key); + exit(1); + } + if ((errno == ERANGE && (lval == LONG_MAX|| lval == LONG_MIN))|| + lval > UINT_MAX) { + fprintf(stderr, "out of range: %s\n", + ppp_key); + exit(1); + } + req.ppp_key_type = NPPPD_CTL_PPP_ID; + req.ppp_key.id = lval; + } + strlcpy(req.authid, authid, sizeof(req.authid)); + + if (sendto(sock, &req, sizeof(req), 0, (struct sockaddr *)&peersock, + sizeof(peersock)) < 0) { + err(1 ,"sendto() failed"); + } + + if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0) + err(1, "recv"); + buf[sz] = '\0'; + + printf("%s\n", buf); +} + +static void +npppd_ctl_common(int command) +{ + int sz; + u_char buf[BUFSIZ]; + + if (sendto(sock, &command, sizeof(command), 0, + (struct sockaddr *)&peersock, sizeof(peersock)) < 0) { + err(1 ,"sendto() failed"); + } + + if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0) + err(1, "recv"); + buf[sz] = '\0'; + + printf("%s\n", buf); +} diff --git a/usr.sbin/npppd/pppoe/pppoe.h b/usr.sbin/npppd/pppoe/pppoe.h new file mode 100644 index 00000000000..eaa3f63edf9 --- /dev/null +++ b/usr.sbin/npppd/pppoe/pppoe.h @@ -0,0 +1,233 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PPPOE_H +#define PPPOE_H 1 + +/* + * プロトコル上の定数 (RFC 2516) + */ +#define PPPOE_RFC2516_TYPE 0x01 +#define PPPOE_RFC2516_VER 0x01 + +/** The PPPoE Active Discovery Initiation (PADI) packet */ +#define PPPOE_CODE_PADI 0x09 + +/** The PPPoE Active Discovery Offer (PADO) packet */ +#define PPPOE_CODE_PADO 0x07 + +/** The PPPoE Active Discovery Request (PADR) packet */ +#define PPPOE_CODE_PADR 0x19 + +/** The PPPoE Active Discovery Session-confirmation (PADS) packet */ +#define PPPOE_CODE_PADS 0x65 + +/** The PPPoE Active Discovery Terminate (PADT) packet */ +#define PPPOE_CODE_PADT 0xa7 + +#define PPPOE_TAG_END_OF_LIST 0x0000 +#define PPPOE_TAG_SERVICE_NAME 0x0101 +#define PPPOE_TAG_AC_NAME 0x0102 +#define PPPOE_TAG_HOST_UNIQ 0x0103 +#define PPPOE_TAG_AC_COOKIE 0x0104 +#define PPPOE_TAG_VENDOR_SPECIFIC 0x0105 +#define PPPOE_TAG_RELAY_SESSION_ID 0x0110 +#define PPPOE_TAG_SERVICE_NAME_ERROR 0x0201 +#define PPPOE_TAG_AC_SYSTEM_ERROR 0x0202 +#define PPPOE_TAG_GENERIC_ERROR 0x0203 + +/** PPPoE ヘッダ */ +struct pppoe_header { +#if _BYTE_ORDER == _BIG_ENDIAN + uint8_t ver:4, type:4; +#else + uint8_t type:4, ver:4; +#endif + uint8_t code; + uint16_t session_id; + uint16_t length; +} __attribute__((__packed__)); + +/** PPPoE TLV ヘッダ */ +struct pppoe_tlv { + uint16_t type; + uint16_t length; + uint8_t value[0]; +} __attribute__((__packed__)); + +/* + * 実装 + */ +/** デフォルトの物理層ラベル */ +#define PPPOED_DEFAULT_LAYER2_LABEL "PPPoE" + +#define PPPOED_CONFIG_BUFSIZ 65535 +#define PPPOED_HOSTUNIQ_LEN 64 +#define PPPOED_PHY_LABEL_SIZE 16 + +/* + * pppoed ステータス + */ +/** 初期状態 */ +#define PPPOED_STATE_INIT 0 +/** 走行中 */ +#define PPPOED_STATE_RUNNING 1 +/** 停止j状態 */ +#define PPPOED_STATE_STOPPED 2 + +#define pppoed_is_stopped(pppoed) \ + (((pppoed)->state == PPPOED_STATE_STOPPED)? 1 : 0) +#define pppoed_is_running(pppoed) \ + (((pppoed)->state == PPPOED_STATE_RUNNING)? 1 : 0) + +#define PPPOED_LISTENER_INVALID_INDEX UINT16_MAX + +/** PPPoE デーモン待ち受け型 */ +typedef struct _pppoed_listener { + /** bpf(4) デバイスファイルのディスクリプタ */ + int bpf; + /** bpf 用のイベントコンテキスト */ + struct event ev_bpf; + /** PPPoE デーモン自身 */ + struct _pppoed *self; + /** イーサネットアドレス */ + u_char ether_addr[ETHER_ADDR_LEN]; + /** インデックス番号 */ + uint16_t index; + /** 待ち受けるインタフェース名 */ + char listen_ifname[IF_NAMESIZE]; + /** 物理層のラベル */ + char phy_label[PPPOED_PHY_LABEL_SIZE]; +} pppoed_listener; + +/** PPPoE デーモンを示す型です。*/ +typedef struct _pppoed { + /** PPPoE デーモンの Id */ + int id; + /** 待ち受けリスト */ + slist listener; + /** このデーモンの状態 */ + int state; + /** タイムアウトイベント **/ + + /** セッション番号 => pppoe_session のハッシュマップ */ + hash_table *session_hash; + /** 空きセッション番号リスト */ + slist session_free_list; + + /** クッキー所持セッションのハッシュマップ */ + hash_table *acookie_hash; + /** 次のクッキー番号 */ + uint32_t acookie_next; + + /** 設定プロパティ */ + struct properties *config; + + /** フラグ */ + uint32_t + desc_in_pktdump:1, + desc_out_pktdump:1, + session_in_pktdump:1, + session_out_pktdump:1, + listen_incomplete:1, + /* phy_label_with_ifname:1, PPPoE は不要 */ + reserved:27; +} pppoed; + +/** PPPoE セッションを示す型です */ +typedef struct _pppoe_session { + int state; + /** 親 PPPoE デーモン */ + pppoed *pppoed; + /** PPP コンテキスト */ + void *ppp; + /** セッション Id */ + int session_id; + /** クッキー番号 */ + int acookie; + /** 対抗のイーサネットアドレス */ + u_char ether_addr[ETHER_ADDR_LEN]; + /** リスナインデックス */ + uint16_t listener_index; + /** イーサヘッダキャッシュ */ + struct ether_header ehdr; + + /** echo の送信間隔(秒) */ + int lcp_echo_interval; + /** echo の最大連続失敗回数 */ + int lcp_echo_max_failure; + + /** 終了処理のための event コンテキスト */ + struct event ev_disposing; +} pppoe_session; + +/** pppoe_session の状態が初期状態であることを示す定数です */ +#define PPPOE_SESSION_STATE_INIT 0 + +/** pppoe_session の状態が走行状態であることを示す定数です */ +#define PPPOE_SESSION_STATE_RUNNING 1 + +/** pppoe_session の状態が終了中であることを示す定数です */ +#define PPPOE_SESSION_STATE_DISPOSING 2 + +#define pppoed_need_polling(pppoed) (((pppoed)->listen_incomplete != 0)? 1 : 0) + +#ifdef __cplusplus +extern "C" { +#endif + +int pppoe_session_init (pppoe_session *, pppoed *, int, int, u_char *); +void pppoe_session_fini (pppoe_session *); +void pppoe_session_stop (pppoe_session *); +int pppoe_session_recv_PADR (pppoe_session *, slist *); +int pppoe_session_recv_PADT (pppoe_session *, slist *); +void pppoe_session_input (pppoe_session *, u_char *, int); +void pppoe_session_disconnect (pppoe_session *); + + +int pppoed_add_listener (pppoed *, int, const char *, const char *); +int pppoed_reload_listeners(pppoed *); + +int pppoed_init (pppoed *); +int pppoed_start (pppoed *); +void pppoed_stop (pppoed *); +void pppoed_uninit (pppoed *); +void pppoed_pppoe_session_close_notify(pppoed *, pppoe_session *); +const char * pppoed_tlv_value_string(struct pppoe_tlv *); +int pppoed_reload(pppoed *, struct properties *, const char *, int); +const char *pppoed_config_str (pppoed *, const char *); +int pppoed_config_int (pppoed *, const char *, int); +int pppoed_config_str_equal (pppoed *, const char *, const char *, int); +int pppoed_config_str_equali (pppoed *, const char *, const char *, int); + +const char *pppoed_listener_config_str (pppoed_listener *, const char *); +int pppoed_listener_config_int (pppoed_listener *, const char *, int); +int pppoed_listener_config_str_equal (pppoed_listener *, const char *, const char *, int); +int pppoed_listener_config_str_equali (pppoed_listener *, const char *, const char *, int); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/pppoe/pppoe_local.h b/usr.sbin/npppd/pppoe/pppoe_local.h new file mode 100644 index 00000000000..5cd602296ec --- /dev/null +++ b/usr.sbin/npppd/pppoe/pppoe_local.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PPPOE_LOCAL_H +#define PPPOE_LOCAL_H 1 + +#define BPF_CAPTURE_SIZ 32768 +#define PPPOE_SESSION_HASH_SIZ 557 +#define PPPOE_SESSION_BUFSIZ 2048 +#define PPPOED_SESSION_SHUFFLE_MARK 0x10000000 +#define PPPOED_SHUTDOWN_TIMEOUT 5 + +#ifndef PPPOE_NSESSION +/** PPPoE 最大接続数 */ +#define PPPOE_NSESSION 10000 +#endif +#define PPPOE_NLISTENER 512 + +#define pppoe_session_listen_ifname(session) \ + ((pppoed_listener *)slist_get(&(session)->pppoed->listener, \ + (session)->listener_index))->listen_ifname +#define pppoe_session_sock_ether_addr(session) \ + ((pppoed_listener *)slist_get(&(session)->pppoed->listener, \ + (session)->listener_index))->ether_addr +#define pppoe_session_sock_bpf(session) \ + ((pppoed_listener *)slist_get(&(session)->pppoed->listener, \ + (session)->listener_index))->bpf +/** pptp_ctrl から、リスナーの物理層のラベルを取り出すマクロ */ +#define PPPOE_SESSION_LISTENER_LABEL(session) \ + ((pppoed_listener *)slist_get(&(session)->pppoed->listener, \ + (session)->listener_index))->phy_label +#define PPPOE_SESSION_LISTENER_IFNAME(session) \ + ((pppoed_listener *)slist_get(&(session)->pppoed->listener, \ + (session)->listener_index))->listen_ifname + +#ifndef GETSHORT +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#endif +#ifndef countof +#define countof(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +#define IFTYPE_IS_LAN(iftype) \ + ((iftype) == IFT_ETHER || (iftype) == IFT_L2VLAN || \ + (iftype) == IFT_L3IPVLAN || (iftype) == IFT_L3IPXVLAN) + + +#endif diff --git a/usr.sbin/npppd/pppoe/pppoe_session.c b/usr.sbin/npppd/pppoe/pppoe_session.c new file mode 100644 index 00000000000..b1fef50a04d --- /dev/null +++ b/usr.sbin/npppd/pppoe/pppoe_session.c @@ -0,0 +1,529 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * PPPoE セッションの実装。 + * $Id: pppoe_session.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <net/if.h> +#if defined(__NetBSD__) +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif +#include <net/if_dl.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <event.h> +#include <syslog.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "hash.h" +#include "slist.h" +#include "debugutil.h" +#include "bytebuf.h" +#include "pppoe.h" +#include "pppoe_local.h" + +#include "npppd.h" +#include "ppp.h" + +#ifdef PPPOE_SESSION_DEBUG +#define PPPOE_SESSION_ASSERT(x) ASSERT(x) +#define PPPOE_SESSION_DBG(x) pppoe_session_log x +#else +#define PPPOE_SESSION_ASSERT(x) +#define PPPOE_SESSION_DBG(x) +#endif + +#define pppoed_listener_this(sess) \ + ((pppoed_listener *)slist_get(&(sess)->pppoed->listener, \ + (sess)->listener_index)) + +static void pppoe_session_log (pppoe_session *, int, const char *, ...) __printflike(3,4); +static int pppoe_session_send_PADS (pppoe_session *, struct pppoe_tlv *, + struct pppoe_tlv *); +static int pppoe_session_send_PADT (pppoe_session *); +static int pppoe_session_ppp_output (npppd_ppp *, u_char *, int, int); +static void pppoe_session_close_by_ppp(npppd_ppp *); +static int pppoe_session_bind_ppp (pppoe_session *); +static void pppoe_session_dispose_event(int, short, void *); + +/** PPPoE セッションコンテキストを初期化します */ +int +pppoe_session_init(pppoe_session *_this, pppoed *_pppoed, int idx, + int session_id, u_char *ether_addr) +{ + memset(_this, 0, sizeof(pppoe_session)); + + _this->pppoed = _pppoed; + _this->session_id = session_id; + _this->listener_index = idx; + memcpy(_this->ether_addr, ether_addr, ETHER_ADDR_LEN); + + memcpy(_this->ehdr.ether_dhost, ether_addr, ETHER_ADDR_LEN); + memcpy(_this->ehdr.ether_shost, pppoe_session_sock_ether_addr(_this), + ETHER_ADDR_LEN); + + evtimer_set(&_this->ev_disposing, pppoe_session_dispose_event, _this); + + return 0; +} + +/** PPPoE セッションを切断します */ +void +pppoe_session_disconnect(pppoe_session *_this) +{ + struct timeval tv; + + if (_this->state != PPPOE_SESSION_STATE_DISPOSING) { + pppoe_session_send_PADT(_this); + + /* 解放処理は、 単体の event で。*/ + tv.tv_usec = 0; + tv.tv_sec = 0; + evtimer_add(&_this->ev_disposing, &tv); + _this->state = PPPOE_SESSION_STATE_DISPOSING; + } + if (_this->ppp != NULL) + ppp_phy_downed(_this->ppp); +} + +/** PPPoE セッションを停止します */ +void +pppoe_session_stop(pppoe_session *_this) +{ + if (_this->state != PPPOE_SESSION_STATE_DISPOSING) + pppoe_session_disconnect(_this); + +} + +/** PPPoE セッションの終了処理を行います。 */ +void +pppoe_session_fini(pppoe_session *_this) +{ + evtimer_del(&_this->ev_disposing); +} + +/* event(3) からの callback */ +static void +pppoe_session_dispose_event(int fd, short ev, void *ctx) +{ + pppoe_session *_this; + + _this = ctx; + pppoed_pppoe_session_close_notify(_this->pppoed, _this); +} + +/*********************************************************************** + * I/O 関連 + ***********************************************************************/ +void +pppoe_session_input(pppoe_session *_this, u_char *pkt, int lpkt) +{ + int rval; + npppd_ppp *ppp; + + ppp = _this->ppp; + if (_this->ppp == NULL) + return; + + if (_this->state != PPPOE_SESSION_STATE_RUNNING) + return; + + rval = ppp->recv_packet(ppp, pkt, lpkt, 0); + if (_this->ppp == NULL) /* ppp is freed */ + return; + + if (rval == 2) { + /* + * 現在 NPPPD PPPOE は bpf によって PIPEX が転送する + * パケットも同時に受信して処理するため,統計情報が + * 多重にカウントされてしまう. + * PIPEX で処理する種類のパケットだった場合は + * 統計情報をカウントする前に関数を抜ける必要がある. + */ + } else if (rval != 0) { + ppp->ierrors++; + } else { + ppp->ipackets++; + ppp->ibytes += lpkt; + } + + return; +} + +static int +pppoe_session_output(pppoe_session *_this, int is_disc, u_char *pkt, + int lpkt) +{ + int sz, niov, tlen; + struct iovec iov[4]; + struct pppoe_header pppoe0, *pppoe; + char pad[ETHERMIN]; + + + niov = 0; + tlen = 0; + iov[niov].iov_base = &_this->ehdr; + iov[niov++].iov_len = sizeof(_this->ehdr); + + if (is_disc) { + _this->ehdr.ether_type = htons(ETHERTYPE_PPPOEDISC); + iov[niov].iov_base = pkt; + iov[niov++].iov_len = lpkt; + pppoe = (struct pppoe_header *)pkt; + pppoe->length = htons(lpkt - sizeof(pppoe0)); + tlen += lpkt; + } else { + _this->ehdr.ether_type = htons(ETHERTYPE_PPPOE); + pppoe0.ver = PPPOE_RFC2516_VER; + pppoe0.type = PPPOE_RFC2516_TYPE; + pppoe0.code = 0; + pppoe0.session_id = htons(_this->session_id); + pppoe0.length = htons(lpkt); + iov[niov].iov_base = &pppoe0; + iov[niov++].iov_len = sizeof(pppoe0); + tlen += sizeof(pppoe0); + iov[niov].iov_base = pkt; + iov[niov++].iov_len = lpkt; + tlen += lpkt; + } + if (tlen < ETHERMIN) { + memset(pad, 0, ETHERMIN - tlen); + iov[niov].iov_base = pad; + iov[niov++].iov_len = ETHERMIN - tlen; + } + + sz = writev(pppoe_session_sock_bpf(_this), iov, niov); + + return (sz > 0)? 0 : -1; +} + +static int +pppoe_session_send_PADT(pppoe_session *_this) +{ + u_char bufspace[2048]; + bytebuffer *buf; + struct pppoe_header pppoe; + int rval = 0; + struct pppoe_tlv tlv; + + if ((buf = bytebuffer_wrap(bufspace, sizeof(bufspace))) == NULL) { + pppoe_session_log(_this, LOG_ERR, + "bytebuffer_wrap() failed on %s(): %m", __func__); + return -1; + } + bytebuffer_clear(buf); + /* + * PPPoE Header + */ + memset(&pppoe, 0, sizeof(pppoe)); + pppoe.ver = PPPOE_RFC2516_VER; + pppoe.type = PPPOE_RFC2516_TYPE; + pppoe.code = PPPOE_CODE_PADT; + pppoe.session_id = htons(_this->session_id); + bytebuffer_put(buf, &pppoe, sizeof(pppoe)); + + /* + * Tag - End-of-List + */ + tlv.type = htons(PPPOE_TAG_END_OF_LIST); + tlv.length = 0; + bytebuffer_put(buf, &tlv, sizeof(tlv)); + tlv.type = htons(PPPOE_TAG_END_OF_LIST); + tlv.length = 0; + bytebuffer_put(buf, &tlv, sizeof(tlv)); + + bytebuffer_flip(buf); + if (pppoe_session_output(_this, 1, bytebuffer_pointer(buf), + bytebuffer_remaining(buf)) != 0) { + pppoe_session_log(_this, LOG_ERR, "pppoed_output failed: %m"); + rval = 1; + } + pppoe_session_log(_this, LOG_INFO, "SendPADT"); + + bytebuffer_unwrap(buf); + bytebuffer_destroy(buf); + + return rval; +} + +/** PADS を送信します */ +static int +pppoe_session_send_PADS(pppoe_session *_this, struct pppoe_tlv *hostuniq, + struct pppoe_tlv *service_name) +{ + int rval, len; + u_char bufspace[2048], msgbuf[80]; + bytebuffer *buf; + struct pppoe_header pppoe; + struct pppoe_tlv tlv; + + if ((buf = bytebuffer_wrap(bufspace, sizeof(bufspace))) == NULL) { + pppoe_session_log(_this, LOG_ERR, + "bytebuffer_wrap() failed on %s(): %m", __func__); + return -1; + } + bytebuffer_clear(buf); + + /* + * PPPoE Header + */ + memset(&pppoe, 0, sizeof(pppoe)); + pppoe.ver = PPPOE_RFC2516_VER; + pppoe.type = PPPOE_RFC2516_TYPE; + pppoe.code = PPPOE_CODE_PADS; + pppoe.session_id = htons(_this->session_id); + bytebuffer_put(buf, &pppoe, sizeof(pppoe)); + + /* + * Tag - Service-Name + */ + msgbuf[0] = '\0'; + if (service_name != NULL) { + tlv.type = htons(PPPOE_TAG_SERVICE_NAME); + tlv.length = htons(service_name->length); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + + len = service_name->length; + if (len > 0) { + bytebuffer_put(buf, service_name->value, len); + strlcpy(msgbuf, service_name->value, + MIN(len + 1, sizeof(msgbuf))); + } + } + + /* + * Tag - Host-Uniq + */ + if (hostuniq != NULL) { + tlv.type = htons(PPPOE_TAG_HOST_UNIQ); + tlv.length = htons(hostuniq->length); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + bytebuffer_put(buf, hostuniq->value, hostuniq->length); + } + tlv.type = htons(PPPOE_TAG_END_OF_LIST); + tlv.length = 0; + bytebuffer_put(buf, &tlv, sizeof(tlv)); + + bytebuffer_flip(buf); + rval = 0; + if (pppoe_session_output(_this, 1, bytebuffer_pointer(buf), + bytebuffer_remaining(buf)) != 0) { + pppoe_session_log(_this, LOG_ERR, "pppoed_output failed: %m"); + rval = 1; + } + pppoe_session_log(_this, LOG_INFO, "SendPADS serviceName=%s " + "hostUniq=%s", msgbuf, + hostuniq? pppoed_tlv_value_string(hostuniq) : "none"); + + bytebuffer_unwrap(buf); + bytebuffer_destroy(buf); + + return rval; +} + +/** PADR を受信した時に呼び出されます */ +int +pppoe_session_recv_PADR(pppoe_session *_this, slist *tag_list) +{ + pppoed *pppoed0 = _this->pppoed; + struct pppoe_tlv *tlv, *hostuniq, *service_name, *ac_cookie; + + service_name = NULL; + hostuniq = NULL; + ac_cookie = NULL; + for (slist_itr_first(tag_list); slist_itr_has_next(tag_list); ) { + tlv = slist_itr_next(tag_list); + if (tlv->type == PPPOE_TAG_HOST_UNIQ) + hostuniq = tlv; + if (tlv->type == PPPOE_TAG_SERVICE_NAME) + service_name = tlv; + if (tlv->type == PPPOE_TAG_AC_COOKIE) + ac_cookie = tlv; + } + + if (ac_cookie) { + /* + * cookie を既にもっているセッションが + * いるとまずい + */ + if (hash_lookup(pppoed0->acookie_hash, + (void *)ac_cookie->value) != NULL) + goto reigai; + + _this->acookie = *(uint32_t *)(ac_cookie->value); + hash_insert(pppoed0->acookie_hash, (void *)_this->acookie, + _this); + } + + if (pppoe_session_send_PADS(_this, hostuniq, service_name) != 0) + goto reigai; + + if (pppoe_session_bind_ppp(_this) != 0) + goto reigai; + + _this->state = PPPOE_SESSION_STATE_RUNNING; + return 0; +reigai: + return -1; +} + +/** PADT を受信した時に呼び出されます */ +int +pppoe_session_recv_PADT(pppoe_session *_this, slist *tag_list) +{ + pppoe_session_log(_this, LOG_INFO, "RecvPADT"); + + pppoe_session_stop(_this); + _this->state = PPPOE_SESSION_STATE_DISPOSING; + + return 0; +} + +/*********************************************************************** + * ログ関連 + ***********************************************************************/ +static void +pppoe_session_log(pppoe_session *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + PPPOE_SESSION_ASSERT(_this != NULL); + va_start(ap, fmt); +#ifdef PPPOED_MULITPLE + snprintf(logbuf, sizeof(logbuf), "pppoed id=%u session=%d %s", + _this->pppoed->id, _this->session_id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "pppoed if=%s session=%d %s", + pppoe_session_listen_ifname(_this), _this->session_id, fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +/*********************************************************************** + * PPP関連 + ***********************************************************************/ +static int +pppoe_session_ppp_output(npppd_ppp *ppp, u_char *pkt, int lpkt, int flag) +{ + int rval; + pppoe_session *_this; + + _this = ppp->phy_context; + + rval = pppoe_session_output(_this, 0, pkt, lpkt); + + if (_this->ppp == NULL) /* ppp is freed */ + return 0; + + if (rval != 0) { + ppp->oerrors++; + } else { + ppp->opackets++; + ppp->obytes += lpkt; + } + + return 0; +} +static void +pppoe_session_close_by_ppp(npppd_ppp *ppp) +{ + pppoe_session *_this; + + _this = ppp->phy_context; + PPPOE_SESSION_ASSERT(_this != NULL); + if (_this != NULL) + _this->ppp = NULL; // pptp_call_disconnect より先に。 + pppoe_session_disconnect(_this); +} + +/** ppp の bind。*/ +static int +pppoe_session_bind_ppp(pppoe_session *_this) +{ + int len; + npppd_ppp *ppp; + struct sockaddr_dl sdl; + + ppp = NULL; + if ((ppp = ppp_create()) == NULL) + goto reigai; + + PPPOE_SESSION_ASSERT(_this->ppp == NULL); + + if (_this->ppp != NULL) + return -1; + + _this->ppp = ppp; + + ppp->tunnel_type = PPP_TUNNEL_PPPOE; + ppp->phy_context = _this; + ppp->send_packet = pppoe_session_ppp_output; + ppp->phy_close = pppoe_session_close_by_ppp; + + strlcpy(ppp->phy_label, PPPOE_SESSION_LISTENER_LABEL(_this), + sizeof(ppp->phy_label)); + + memset(&sdl, 0, sizeof(sdl)); + sdl.sdl_len = sizeof(sdl); + sdl.sdl_family = AF_LINK; + len = strlen(pppoe_session_listen_ifname(_this)); + memcpy(sdl.sdl_data, pppoe_session_listen_ifname(_this), len); + sdl.sdl_nlen = len; + sdl.sdl_alen = ETHER_ADDR_LEN; + memcpy(sdl.sdl_data + len, _this->ether_addr, ETHER_ADDR_LEN); + + memcpy(&ppp->phy_info.peer_dl, &sdl, sizeof(sdl)); + + if (ppp_init(npppd_get_npppd(), ppp) != 0) + goto reigai; + ppp->has_acf = 0; + + + pppoe_session_log(_this, LOG_NOTICE, "logtype=PPPBind ppp=%d", ppp->id); + ppp_start(ppp); + + return 0; +reigai: + pppoe_session_log(_this, LOG_ERR, "failed binding ppp"); + + if (ppp != NULL) + ppp_destroy(ppp); + _this->ppp = NULL; + + return 1; +} diff --git a/usr.sbin/npppd/pppoe/pppoed.c b/usr.sbin/npppd/pppoe/pppoed.c new file mode 100644 index 00000000000..81d926062ba --- /dev/null +++ b/usr.sbin/npppd/pppoe/pppoed.c @@ -0,0 +1,1204 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * PPPoEサーバの実装。 + * <dl> + * <dt>RFC 2516</dt> + * <dd>A Method for Transmitting PPP Over Ethernet (PPPoE)</dd> + * </dl> + * $Id: pppoed.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ + */ +#ifdef _SEIL_EXT_ +#include <seil/features.h> +#endif +#include <sys/types.h> +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <net/if.h> +#include <net/if_types.h> +#if defined(__NetBSD__) +#include <net/if_ether.h> +#else +#include <netinet/if_ether.h> +#endif +#include <net/if_dl.h> +#include <net/ethertypes.h> +#include <net/bpf.h> +#include <string.h> +#include <syslog.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <event.h> +#include <signal.h> +#include <stdlib.h> +#include <ifaddrs.h> +#include <stdarg.h> +#include <errno.h> + +#include "debugutil.h" +#include "slist.h" +#include "bytebuf.h" +#include "hash.h" +#include "properties.h" +#include "config_helper.h" +#include "rtev.h" + +#include "pppoe.h" +#include "pppoe_local.h" + +static int pppoed_seqno = 0; + +#ifdef PPPOED_DEBUG +#define PPPOED_ASSERT(x) ASSERT(x) +#define PPPOED_DBG(x) pppoed_log x +#else +#define PPPOED_ASSERT(x) +#define PPPOED_DBG(x) +#endif + +static void pppoed_log (pppoed *, int, const char *, ...) __printflike(3,4); +static void pppoed_listener_init(pppoed *, pppoed_listener *); +static int pppoed_output (pppoed_listener *, u_char *, u_char *, int); +static int pppoed_listener_start (pppoed_listener *, int); +static void pppoed_io_event (int, short, void *); +static void pppoed_input (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], int, u_char *, int); +static void pppoed_recv_PADR (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], slist *); +static void pppoed_recv_PADI (pppoed_listener *, uint8_t [ETHER_ADDR_LEN], slist *); +static int session_id_cmp (void *, void *); +static uint32_t session_id_hash (void *, size_t); + +#ifdef PPPOE_TEST +static void pppoed_on_sigterm (int, short, void *); +static void usage (void); +#endif +static const char *pppoe_code_string(int); +#ifdef PPPOED_DEBUG +static const char *pppoe_tag_string(int); +#endif + +/*********************************************************************** + * デーモン関連 + ***********************************************************************/ +/** PPPoE デーモンを初期化します */ +int +pppoed_init(pppoed *_this) +{ + int i, off, id; + + memset(_this, 0, sizeof(pppoed)); + _this->id = pppoed_seqno++; + + if ((_this->session_hash = hash_create( + (int (*) (const void *, const void *))session_id_cmp, + (uint32_t (*) (const void *, int))session_id_hash, + PPPOE_SESSION_HASH_SIZ)) == NULL) { + pppoed_log(_this, LOG_ERR, "hash_create() failed on %s(): %m", + __func__); + goto reigai; + } + + slist_init(&_this->session_free_list); + if (slist_add(&_this->session_free_list, + (void *)PPPOED_SESSION_SHUFFLE_MARK) == NULL) { + pppoed_log(_this, LOG_ERR, "slist_add() failed on %s(): %m", + __func__); + goto reigai; + } + + /* XXX クッキーハッシュの初期化 */ + if ((_this->acookie_hash = hash_create( + (int (*) (const void *, const void *))session_id_cmp, + (uint32_t (*) (const void *, int))session_id_hash, + PPPOE_SESSION_HASH_SIZ)) == NULL) { + pppoed_log(_this, LOG_WARNING, + "hash_create() failed on %s(): %m", __func__); + pppoed_log(_this, LOG_WARNING, "hash_create() failed on %s(): " + "ac-cookie hash create failed.", __func__); + _this->acookie_hash = NULL; + } + _this->acookie_next = random(); + +#if PPPOE_NSESSION > 0xffff +#error PPPOE_NSESSION must be less than 65536 +#endif + off = random() % 0xffff; + for (i = 0; i < PPPOE_NSESSION; i++) { + id = (i + off) % 0xffff; + if (id == 0) + id = (off - 1) % 0xffff; + if (slist_add(&_this->session_free_list, (void *)id) == NULL) { + pppoed_log(_this, LOG_ERR, + "slist_add() failed on %s(): %m", __func__); + goto reigai; + } + } + + _this->state = PPPOED_STATE_INIT; + + return 0; +reigai: + pppoed_uninit(_this); + return 1; +} + +static void +pppoed_listener_init(pppoed *_this, pppoed_listener *listener) +{ + memset(listener, 0, sizeof(pppoed_listener)); + listener->bpf = -1; + listener->self = _this; + listener->index = PPPOED_LISTENER_INVALID_INDEX; +} + +/** リスナをリロードします */ +int +pppoed_reload_listeners(pppoed *_this) +{ + int rval = 0; + + if (_this->state == PPPOED_STATE_RUNNING && + _this->listen_incomplete != 0) + rval = pppoed_start(_this); + + return rval; +} + +/** + * 他人宛のパケットを受信するかも(see bpf(4))。自分宛とブロードキャスト宛 + * は弾く。 + */ +#define REJECT_FOREIGN_ADDRESS 1 + +#define ETHER_FIRST_INT(e) ((e)[0]<<24|(e)[1]<<16|(e)[2]<<8|(e)[3]) +#define ETHER_LAST_SHORT(e) ((e)[4]<<8|(e)[5]) + +static int +pppoed_listener_start(pppoed_listener *_this, int restart) +{ + int i; + int log_level; + char buf[BUFSIZ]; + struct ifreq ifreq; + int ival; + int found; + struct ifaddrs *ifa0, *ifa; + struct sockaddr_dl *sdl; + struct bpf_insn insns[] = { + /* check etyer type = PPPOEDESC or PPPOE */ + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_PPPOEDISC, 2, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_PPPOE, 1, 0), + BPF_STMT(BPF_RET+BPF_K, (u_int)0), +#ifndef REJECT_FOREIGN_ADDRESS + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), +#else + /* ff:ff:ff:ff:ff:ff 宛 */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffffffff, 0, 3), + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffff, 0, 1), + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + /* 自分の Mac 宛 */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, + ETHER_FIRST_INT(_this->ether_addr), 0, 3), + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, + ETHER_LAST_SHORT(_this->ether_addr), 0, 1), + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + BPF_STMT(BPF_RET+BPF_K, (u_int)0), +#endif + }; + struct bpf_program bf_filter = { + .bf_len = countof(insns), + .bf_insns = insns + }; + pppoed *_pppoed; + + if (restart == 0) + log_level = LOG_ERR; + else + log_level = LOG_INFO; + + _pppoed = _this->self; + + ifa0 = NULL; + if (getifaddrs(&ifa0) != 0) { + pppoed_log(_pppoed, log_level, + "getifaddrs() failed on %s(): %m", __func__); + return -1; + } + found = 0; + for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_family != AF_LINK || sdl->sdl_type != IFT_ETHER || + sdl->sdl_alen != ETHER_ADDR_LEN) + continue; + if (strcmp(ifa->ifa_name, _this->listen_ifname) == 0) { + memcpy(_this->ether_addr, + (caddr_t)LLADDR(sdl), ETHER_ADDR_LEN); + found = 1; + break; + } + } + freeifaddrs(ifa0); + if (!found) { + pppoed_log(_pppoed, log_level, "%s is not available.", + _this->listen_ifname); + goto reigai; + } + + /* Open /dev/bpfXX */ + /* FIXME: NetBSD 3.0 では、/dev/bpf 一つで何度も開けるらしい */ + for (i = 0; i < 256; i++) { + snprintf(buf, sizeof(buf), "/dev/bpf%d", i); + if ((_this->bpf = open(buf, O_RDWR, 0600)) >= 0) { + break; + } else if (errno == ENXIO || errno == ENOENT) + break; /* これ以上探してもみつからないはず */ + } + if (_this->bpf < 0) { + pppoed_log(_pppoed, log_level, "Cannot open bpf"); + goto reigai; + } + + ival = BPF_CAPTURE_SIZ; + if (ioctl(_this->bpf, BIOCSBLEN, &ival) != 0) { + pppoed_log(_pppoed, log_level, "ioctl(bpf, BIOCSBLEN(%d)): %m", + ival); + goto reigai; + } + ival = 1; + if (ioctl(_this->bpf, BIOCIMMEDIATE, &ival) != 0) { + pppoed_log(_pppoed, log_level, "Cannot start bpf on %s: %m", + _this->listen_ifname); + goto reigai; + } + + /* bind interface */ + memset(&ifreq, 0, sizeof(ifreq)); + strlcpy(ifreq.ifr_name, _this->listen_ifname, sizeof(ifreq.ifr_name)); + if (ioctl(_this->bpf, BIOCSETIF, &ifreq) != 0) { + pppoed_log(_pppoed, log_level, "Cannot start bpf on %s: %m", + _this->listen_ifname); + goto reigai; + } + + /* set linklocal address */ +#ifdef REJECT_FOREIGN_ADDRESS + insns[10].k = ETHER_FIRST_INT(_this->ether_addr); + insns[12].k = ETHER_LAST_SHORT(_this->ether_addr); +#endif + + /* set filter */ + if (ioctl(_this->bpf, BIOCSETF, &bf_filter) != 0) { + pppoed_log(_pppoed, log_level, "ioctl(bpf, BIOCSETF()): %m"); + goto reigai; + } + + event_set(&_this->ev_bpf, _this->bpf, EV_READ | EV_PERSIST, + pppoed_io_event, _this); + event_add(&_this->ev_bpf, NULL); + + pppoed_log(_pppoed, LOG_INFO, "Listening on %s (PPPoE) [%s] using=%s " + "address=%02x:%02x:%02x:%02x:%02x:%02x", _this->listen_ifname, + _this->phy_label, buf, _this->ether_addr[0], _this->ether_addr[1], + _this->ether_addr[2], _this->ether_addr[3], _this->ether_addr[4], + _this->ether_addr[5]); + + return 0; +reigai: + if (_this->bpf >= 0) { + close(_this->bpf); + _this->bpf = -1; + } + + return 1; +} + +/** PPPoE デーモンを開始します */ +int +pppoed_start(pppoed *_this) +{ + int rval = 0; + int nlistener_fail = 0; + pppoed_listener *plistener; + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plistener = slist_itr_next(&_this->listener); + PPPOED_ASSERT(plistener != NULL); + if (plistener->bpf < 0) { + if (pppoed_listener_start(plistener, + _this->listen_incomplete) != 0) + nlistener_fail++; + } + } + if (nlistener_fail > 0) + _this->listen_incomplete = 1; + else + _this->listen_incomplete = 0; + + _this->state = PPPOED_STATE_RUNNING; + + return rval; +} + +static void +pppoed_listener_stop(pppoed_listener *_this) +{ + pppoed *_pppoed; + + PPPOED_ASSERT(_this != NULL); + _pppoed = _this->self; + PPPOED_ASSERT(_pppoed != NULL); + + if (_this->bpf >= 0) { + event_del(&_this->ev_bpf); + close(_this->bpf); + pppoed_log(_pppoed, LOG_INFO, "Shutdown %s (PPPoE) [%s] " + "address=%02x:%02x:%02x:%02x:%02x:%02x", + _this->listen_ifname, _this->phy_label, + _this->ether_addr[0], _this->ether_addr[1], + _this->ether_addr[2], _this->ether_addr[3], + _this->ether_addr[4], _this->ether_addr[5]); + _this->bpf = -1; + } +} + +/** PPPoE デーモンを停止します */ +void +pppoed_stop(pppoed *_this) +{ + pppoed_listener *plistener; + hash_link *hl; + pppoe_session *session; + + if (!pppoed_is_running(_this)) + return; + + _this->state = PPPOED_STATE_STOPPED; + if (_this->session_hash != NULL) { + for (hl = hash_first(_this->session_hash); + hl != NULL; + hl = hash_next(_this->session_hash)) { + session = (pppoe_session *)hl->item; + pppoe_session_disconnect(session); + pppoe_session_stop(session); + } + } + for (slist_itr_first(&_this->listener); + slist_itr_has_next(&_this->listener);) { + plistener = slist_itr_next(&_this->listener); + pppoed_listener_stop(plistener); + free(plistener); + slist_itr_remove(&_this->listener); + } + pppoed_log(_this, LOG_NOTICE, "Stopped"); +} + +/** PPPoE デーモンを解放します */ +void +pppoed_uninit(pppoed *_this) +{ + if (_this->session_hash != NULL) { + hash_free(_this->session_hash); + _this->session_hash = NULL; + } + if (_this->acookie_hash != NULL) { + hash_free(_this->acookie_hash); + _this->acookie_hash = NULL; + } + slist_fini(&_this->session_free_list); + slist_fini(&_this->listener); // stop メンバは解放済。 + _this->config = NULL; +} + +/** PPPoE セッションが close された時に呼び出されます。*/ +void +pppoed_pppoe_session_close_notify(pppoed *_this, pppoe_session *session) +{ + slist_add(&_this->session_free_list, (void *)session->session_id); + + if (_this->acookie_hash != NULL) + hash_delete(_this->acookie_hash, (void *)session->acookie, 0); + if (_this->session_hash != NULL) + hash_delete(_this->session_hash, (void *)session->session_id, + 0); + + pppoe_session_fini(session); + free(session); +} + +/*********************************************************************** + * 設定関連 + ***********************************************************************/ +#define CFG_KEY(p, s) config_key_prefix((p), (s)) +#define VAL_SEP " \t\r\n" + +CONFIG_FUNCTIONS(pppoed_config, pppoed, config); +PREFIXED_CONFIG_FUNCTIONS(pppoed_listener_config, pppoed_listener, self->config, + phy_label); + +/** PPPoE デーモンの設定を再読み込みします */ +int +pppoed_reload(pppoed *_this, struct properties *config, const char *name, + int default_enabled) +{ + struct sockaddr_dl *sdl; + int i, count, found; + hash_link *hl; + const char *val; + char *tok, *cp, buf[PPPOED_CONFIG_BUFSIZ], *label; + pppoed_listener *l; + int do_start; + struct { + char ifname[IF_NAMESIZE]; + char label[PPPOED_PHY_LABEL_SIZE]; + } listeners[PPPOE_NLISTENER]; + struct ifaddrs *ifa0, *ifa; + slist rmlist, newlist; + pppoe_session *session; + + do_start = 0; + + _this->config = config; + if (pppoed_config_str_equal(_this, CFG_KEY(name, "enabled"), "true", + default_enabled)) { + // false にした直後に true にされるかもしれない。 + if (pppoed_is_stopped(_this) || !pppoed_is_running(_this)) + do_start = 1; + } else { + if (!pppoed_is_stopped(_this)) + pppoed_stop(_this); + return 0; + } + + if (do_start) { + if (pppoed_init(_this) != 0) + return 1; + _this->config = config; + } + + ifa0 = NULL; + slist_init(&rmlist); + slist_init(&newlist); + + _this->desc_in_pktdump = pppoed_config_str_equal(_this, + "log.pppoe.desc.in.pktdump", "true", 0); + _this->desc_out_pktdump = pppoed_config_str_equal(_this, + "log.pppoe.desc.out.pktdump", "true", 0); + + _this->session_in_pktdump = pppoed_config_str_equal(_this, + "log.pppoe.session.in.pktdump", "true", 0); + _this->session_out_pktdump = pppoed_config_str_equal(_this, + "log.pppoe.session.out.pktdump", "true", 0); + + if (getifaddrs(&ifa0) != 0) { + pppoed_log(_this, LOG_ERR, + "getifaddrs() failed on %s(): %m", __func__); + goto reigai; + } + count = 0; + val = pppoed_config_str(_this, CFG_KEY(name, "interface")); + if (val != NULL) { + if (strlen(val) >= sizeof(buf)) { + log_printf(LOG_ERR, "configuration error at " + "%s: too long", CFG_KEY(name, "interface")); + return 1; + } + strlcpy(buf, val, sizeof(buf)); + + label = NULL; + // タブ、スペース区切りで、複数指定可能 + for (i = 0, cp = buf; + (tok = strsep(&cp, VAL_SEP)) != NULL;) { + if (*tok == '\0') + continue; + if (label == NULL) { + label = tok; + continue; + } + PPPOED_ASSERT(count < countof(listeners)); + if (count >= countof(listeners)) { + pppoed_log(_this, LOG_ERR, + "Too many listeners"); + goto reigai; + } + /* インタフェースの実在確認 */ + found = 0; + for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_family == AF_LINK && + IFTYPE_IS_LAN(sdl->sdl_type) && + strcmp(ifa->ifa_name, tok) == 0) { + found = 1; + break; + } + } + if (!found) { + pppoed_log(_this, LOG_ERR, + "interface %s is not found", tok); + goto reigai; + } + strlcpy(listeners[count].ifname, tok, + sizeof(listeners[count].ifname)); + strlcpy(listeners[count].label, label, + sizeof(listeners[count].label)); + + label = NULL; + count++; + } + if (label != NULL) { + log_printf(LOG_ERR, "configuration error at %s: %s", + CFG_KEY(name, "interface"), label); + return 1; + } + } + + if (slist_add_all(&rmlist, &_this->listener) != 0) + goto reigai; + + for (i = 0; i < count; i++) { + found = 0; + l = NULL; + slist_itr_first(&rmlist); + while (slist_itr_has_next(&rmlist)) { + l = slist_itr_next(&rmlist); + if (strcmp(l->listen_ifname, listeners[i].ifname) == 0){ + slist_itr_remove(&rmlist); + found = 1; + break; + } + } + if (!found) { + if ((l = malloc(sizeof(pppoed_listener))) == NULL) + goto reigai; + pppoed_listener_init(_this, l); + } + l->self = _this; + strlcpy(l->phy_label, listeners[i].label, + sizeof(l->phy_label)); + strlcpy(l->listen_ifname, listeners[i].ifname, + sizeof(l->listen_ifname)); + if (slist_add(&newlist, l) == NULL) { + pppoed_log(_this, LOG_ERR, + "slist_add() failed in %s(): %m", __func__); + goto reigai; + } + } + + if (slist_set_size(&_this->listener, count) != 0) + goto reigai; + + /* 使わなくなったリスナの停止 */ + slist_itr_first(&rmlist); + while (slist_itr_has_next(&rmlist)) { + l = slist_itr_next(&rmlist); + /* 派生した PPPoEセッションの考慮 */ + if (_this->session_hash != NULL) { + for (hl = hash_first(_this->session_hash); hl != NULL; + hl = hash_next(_this->session_hash)) { + session = (pppoe_session *)hl->item; + if (session->listener_index == l->index) + pppoe_session_stop(session); + } + } + pppoed_listener_stop(l); + free(l); + } + slist_remove_all(&_this->listener); + /* slist_set_size しているので、失敗しないはず */ + (void)slist_add_all(&_this->listener, &newlist); + + /* インデックスのリセット */ + slist_itr_first(&newlist); + for (i = 0; slist_itr_has_next(&newlist); i++) { + l = slist_itr_next(&newlist); + if (l->index != i && l->index != PPPOED_LISTENER_INVALID_INDEX){ + PPPOED_DBG((_this, LOG_DEBUG, "listener %d => %d", + l->index, i)); + for (hl = hash_first(_this->session_hash); hl != NULL; + hl = hash_next(_this->session_hash)) { + session = (pppoe_session *)hl->item; + if (session->listener_index == l->index) + session->listener_index = i; + } + } + l->index = i; + } + + slist_fini(&rmlist); + slist_fini(&newlist); + if (ifa0 != NULL) + freeifaddrs(ifa0); + + if (pppoed_start(_this) != 0) + return 1; + + return 0; +reigai: + slist_fini(&rmlist); + slist_fini(&newlist); + if (ifa0 != NULL) + freeifaddrs(ifa0); + + return 1; +} +/*********************************************************************** + * I/O 関連 + ***********************************************************************/ +static void +pppoed_io_event(int fd, short evmask, void *ctx) +{ + u_char buf[BPF_CAPTURE_SIZ], *pkt; + int lpkt, off; + pppoed_listener *_this; + struct ether_header *ether; + struct bpf_hdr *bpf; + + _this = ctx; + + PPPOED_ASSERT(_this != NULL); + + lpkt = read(_this->bpf, buf, sizeof(buf)); + pkt = buf; + while (lpkt > 0) { + if (lpkt < sizeof(struct bpf_hdr)) { + pppoed_log(_this->self, LOG_WARNING, + "Received bad PPPoE packet: packet too short(%d)", + lpkt); + break; + } + bpf = (struct bpf_hdr *)pkt; + ether = (struct ether_header *)(pkt + bpf->bh_hdrlen); + ether->ether_type = ntohs(ether->ether_type); + if (memcmp(ether->ether_shost, _this->ether_addr, + ETHER_ADDR_LEN) == 0) + goto next_pkt; // 自分パケット + off = bpf->bh_hdrlen + sizeof(struct ether_header); + if (lpkt < off + sizeof(struct pppoe_header)) { + pppoed_log(_this->self, LOG_WARNING, + "Received bad PPPoE packet: packet too short(%d)", + lpkt); + break; + } + pppoed_input(_this, ether->ether_shost, + (ether->ether_type == ETHERTYPE_PPPOEDISC)? 1 : 0, + pkt + off, lpkt - off); +next_pkt: + pkt = pkt + BPF_WORDALIGN(bpf->bh_hdrlen + + bpf->bh_caplen); + lpkt -= BPF_WORDALIGN(bpf->bh_hdrlen + bpf->bh_caplen); + } + return; +} + +static void +pppoed_input(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], int is_disc, + u_char *pkt, int lpkt) +{ + hash_link *hl; + pppoe_session *session; + struct pppoe_header *pppoe; + struct pppoe_tlv *tlv; + u_char tlvspace[2048], *p_tlvspace; + int session_id; + slist tag_list; + const char *reason; + + reason = ""; + p_tlvspace = tlvspace; + session = NULL; + + pppoe = (struct pppoe_header *)pkt; + session_id = pppoe->session_id = ntohs(pppoe->session_id); + pppoe->length = ntohs(pppoe->length); + +#ifdef PPPOED_DEBUG + if (is_disc) { + PPPOED_DBG((_this->self, DEBUG_LEVEL_1, + "Recv%s(%02x) ver=%d type=%d session-id=%d if=%s", + pppoe_code_string(pppoe->code), pppoe->code, + pppoe->ver, pppoe->type, pppoe->session_id, + _this->listen_ifname)); + } +#endif + pkt += sizeof(struct pppoe_header); + lpkt -= sizeof(struct pppoe_header); + + if (lpkt < pppoe->length) { + reason = "received packet is shorter than " + "pppoe length field."; + goto bad_packet; + } + lpkt = pppoe->length; /* PPPoEヘッダの値を使う */ + + if (pppoe->type != PPPOE_RFC2516_TYPE || + pppoe->ver != PPPOE_RFC2516_VER) { + reason = "received packet has wrong version or type."; + goto bad_packet; + } + + if (session_id != 0) { + hl = hash_lookup(_this->self->session_hash, (void *)session_id); + if (hl != NULL) + session = (pppoe_session *)hl->item; + } + if (!is_disc) { + if (session != NULL) + pppoe_session_input(session, pkt, pppoe->length); + return; + } + + /* + * PPPoE-Discovery Packet proccessing. + */ + slist_init(&tag_list); + while (lpkt > 0) { + if (lpkt < 4) { + reason = "tlv list is broken. " + "Remaining octet is too short."; + goto reigai; + } + tlv = (struct pppoe_tlv *)p_tlvspace; + GETSHORT(tlv->type, pkt); + GETSHORT(tlv->length, pkt); + p_tlvspace += 4; + lpkt -= 4; + if (tlv->length > lpkt) { + reason = "tlv list is broken. length is wrong."; + goto reigai; + } + if (tlv->length > 0) { + memcpy(&tlv->value, pkt, tlv->length); + pkt += tlv->length; + lpkt -= tlv->length; + p_tlvspace += tlv->length; + p_tlvspace = (u_char *)ALIGN(p_tlvspace); + } +#ifdef PPPOED_DEBUG + if (debuglevel >= 2) + pppoed_log(_this->self, DEBUG_LEVEL_2, + "Recv%s tag %s(%04x)=%s", + pppoe_code_string(pppoe->code), + pppoe_tag_string(tlv->type), tlv->type, + pppoed_tlv_value_string(tlv)); +#endif + if (tlv->type == PPPOE_TAG_END_OF_LIST) + break; + if (slist_add(&tag_list, tlv) == NULL) { + goto reigai; + } + } + switch (pppoe->code) { + case PPPOE_CODE_PADI: + if (_this->self->state != PPPOED_STATE_RUNNING) + break; + pppoed_recv_PADI(_this, shost, &tag_list); + break; + case PPPOE_CODE_PADR: + if (_this->self->state != PPPOED_STATE_RUNNING) + break; + pppoed_recv_PADR(_this, shost, &tag_list); + break; + case PPPOE_CODE_PADT: + PPPOED_DBG((_this->self, LOG_DEBUG, "RecvPADT")); + if (session != NULL) + pppoe_session_recv_PADT(session, &tag_list); + break; + } + slist_fini(&tag_list); + + return; +reigai: + slist_fini(&tag_list); +bad_packet: + pppoed_log(_this->self, LOG_INFO, + "Received a bad packet: code=%s(%02x) ver=%d type=%d session-id=%d" + " if=%s: %s", pppoe_code_string(pppoe->code), pppoe->code, + pppoe->ver, pppoe->type, pppoe->session_id, _this->listen_ifname, + reason); +} + +static int +pppoed_output(pppoed_listener *_this, u_char *dhost, u_char *pkt, int lpkt) +{ + int sz, iovc; + struct iovec iov[3]; + struct ether_header ether; + struct pppoe_header *pppoe; + u_char pad[ETHERMIN]; + + memcpy(ether.ether_dhost, dhost, ETHER_ADDR_LEN); + memcpy(ether.ether_shost, _this->ether_addr, ETHER_ADDR_LEN); + + iov[0].iov_base = ðer; + iov[0].iov_len = sizeof(struct ether_header); + ether.ether_type = htons(ETHERTYPE_PPPOEDISC); + iov[1].iov_base = pkt; + iov[1].iov_len = lpkt; + pppoe = (struct pppoe_header *)pkt; + pppoe->length = htons(lpkt - sizeof(struct pppoe_header)); + + iovc = 2; + + if (lpkt < ETHERMIN) { + memset(pad, 0, ETHERMIN - lpkt); + iov[2].iov_base = pad; + iov[2].iov_len = ETHERMIN - lpkt; + iovc++; + } + + sz = writev(_this->bpf, iov, iovc); + + return (sz > 0)? 0 : -1; +} + +static void +pppoed_recv_PADR(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], + slist *tag_list) +{ + int session_id, shuffle_cnt; + pppoe_session *session; + pppoed *_pppoed; + + _pppoed = _this->self; + if ((session = malloc(sizeof(pppoe_session))) == NULL) { + pppoed_log(_pppoed, LOG_ERR, "malloc() failed on %s(): %m", + __func__); + goto reigai; + } + + /* セッション Id の作成 */ + shuffle_cnt = 0; + do { + session_id = (int)slist_remove_first( + &_pppoed->session_free_list); + if (session_id != PPPOED_SESSION_SHUFFLE_MARK) + break; + PPPOED_ASSERT(shuffle_cnt == 0); + if (shuffle_cnt++ > 0) { + pppoed_log(_pppoed, LOG_ERR, + "unexpected errror in %s(): session_free_list full", + __func__); + slist_add(&_pppoed->session_free_list, + (void *)PPPOED_SESSION_SHUFFLE_MARK); + goto reigai; + } + slist_shuffle(&_pppoed->session_free_list); + slist_add(&_pppoed->session_free_list, + (void *)PPPOED_SESSION_SHUFFLE_MARK); + } while (1); + + if (pppoe_session_init(session, _pppoed, _this->index, session_id, + shost) != 0) + goto reigai; + + hash_insert(_pppoed->session_hash, (void *)session_id, session); + + if (pppoe_session_recv_PADR(session, tag_list) != 0) + goto reigai; + + session = NULL; /* don't free */ + /* FALL THROUGH */ +reigai: + if (session != NULL) + pppoe_session_fini(session); + return; +} + +static void +pppoed_recv_PADI(pppoed_listener *_this, uint8_t shost[ETHER_ADDR_LEN], + slist *tag_list) +{ + int len, accept_any_service_req; + const char *val, *service_name, *ac_name; + u_char bufspace[2048]; + u_char sn[2048], ac_name0[40]; + struct pppoe_header pppoe; + struct pppoe_tlv tlv, *tlv_hostuniq, *tlv0, *tlv_service_name; + bytebuffer *buf; + + if ((buf = bytebuffer_wrap(bufspace, sizeof(bufspace))) == NULL) { + pppoed_log(_this->self, LOG_ERR, + "bytebuffer_wrap() failed on %s(): %m", __func__); + return; + } + bytebuffer_clear(buf); + + tlv_hostuniq = NULL; + tlv_service_name = NULL; + + service_name = ""; + if ((val = pppoed_listener_config_str(_this, "pppoe.service_name")) + != NULL) + service_name = val; + accept_any_service_req = pppoed_listener_config_str_equal(_this, + "pppoe.accept_any_service_request", "true", 1); + + for (slist_itr_first(tag_list); slist_itr_has_next(tag_list);) { + tlv0 = slist_itr_next(tag_list); + if (tlv0->type == PPPOE_TAG_HOST_UNIQ) + tlv_hostuniq = tlv0; + if (tlv0->type == PPPOE_TAG_SERVICE_NAME) { + + len = tlv0->length; + if (len > sizeof(sn)) + goto reigai; + + memcpy(sn, tlv0->value, len); + sn[len] = '\0'; + + if (strcmp(service_name, sn) == 0 || + (sn[0] == '\0' && accept_any_service_req)) + tlv_service_name = tlv0; + } + } + if (tlv_service_name == NULL) { + pppoed_log(_this->self, LOG_INFO, + "Deny PADI from=%02x:%02x:%02x:%02x:%02x:%02x " + "service-name=%s host-uniq=%s if=%s: serviceName is " + "not allowed.", shost[0], shost[1], + shost[2], shost[3], shost[4], shost[5], sn, tlv_hostuniq? + pppoed_tlv_value_string(tlv_hostuniq) : "none", + _this->listen_ifname); + goto reigai; + } + + pppoed_log(_this->self, LOG_INFO, + "RecvPADI from=%02x:%02x:%02x:%02x:%02x:%02x service-name=%s " + "host-uniq=%s if=%s", shost[0], shost[1], shost[2], shost[3], + shost[4], shost[5], sn, tlv_hostuniq? + pppoed_tlv_value_string(tlv_hostuniq) : "none", + _this->listen_ifname); + + /* + * PPPoE Header + */ + memset(&pppoe, 0, sizeof(pppoe)); + pppoe.ver = PPPOE_RFC2516_VER; + pppoe.type = PPPOE_RFC2516_TYPE; + pppoe.code = PPPOE_CODE_PADO; + bytebuffer_put(buf, &pppoe, sizeof(pppoe)); + + /* + * Tag - Service-Name + */ + tlv.type = htons(PPPOE_TAG_SERVICE_NAME); + len = strlen(service_name); + tlv.length = htons(len); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + if (len > 0) + bytebuffer_put(buf, service_name, len); + + /* + * Tag - Access Concentrator Name + */ + ac_name = pppoed_listener_config_str(_this, "pppoe.ac_name"); + if (ac_name == NULL) { + /* + * use the ethernet address as default AC-Name. + * suggested by RFC 2516. + */ + snprintf(ac_name0, sizeof(ac_name0), + "%02x:%02x:%02x:%02x:%02x:%02x", _this->ether_addr[0], + _this->ether_addr[1], _this->ether_addr[2], + _this->ether_addr[3], _this->ether_addr[4], + _this->ether_addr[5]); + ac_name = ac_name0; + } + + tlv.type = htons(PPPOE_TAG_AC_NAME); + len = strlen(ac_name); + tlv.length = htons(len); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + bytebuffer_put(buf, ac_name, len); + + /* + * Tag - ac-cookie + */ + if (_this->self->acookie_hash != NULL) { + /* + * ac-cookie の次の値を探す。 + * (uint32_t の空間で値がループします) + */ + do { + _this->self->acookie_next += 1; + } + while(hash_lookup(_this->self->acookie_hash, + (void *)_this->self->acookie_next) != NULL); + + tlv.type = htons(PPPOE_TAG_AC_COOKIE); + tlv.length = ntohs(sizeof(uint32_t)); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + bytebuffer_put(buf, &_this->self->acookie_next, + sizeof(uint32_t)); + } + + /* + * Tag - Host-Uniq + */ + if (tlv_hostuniq != NULL) { + tlv.type = htons(PPPOE_TAG_HOST_UNIQ); + tlv.length = ntohs(tlv_hostuniq->length); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + bytebuffer_put(buf, tlv_hostuniq->value, + tlv_hostuniq->length); + } + + /* + * Tag - End-Of-List + */ + tlv.type = htons(PPPOE_TAG_END_OF_LIST); + tlv.length = ntohs(0); + bytebuffer_put(buf, &tlv, sizeof(tlv)); + + bytebuffer_flip(buf); + + if (pppoed_output(_this, shost, bytebuffer_pointer(buf), + bytebuffer_remaining(buf)) != 0) { + pppoed_log(_this->self, LOG_ERR, "pppoed_output() failed:%m"); + } + pppoed_log(_this->self, LOG_INFO, + "SendPADO to=%02x:%02x:%02x:%02x:%02x:%02x serviceName=%s " + "acName=%s hostUniq=%s eol if=%s", shost[0], shost[1], shost[2], + shost[3], shost[4], shost[5], service_name, ac_name, + tlv_hostuniq? pppoed_tlv_value_string(tlv_hostuniq) : "none", + _this->listen_ifname); + // FALL THROUGH +reigai: + bytebuffer_unwrap(buf); + bytebuffer_destroy(buf); +} + +/*********************************************************************** + * ログ関連 + ***********************************************************************/ +static void +pppoed_log(pppoed *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + PPPOED_ASSERT(_this != NULL); + va_start(ap, fmt); +#ifdef PPPOED_MULITPLE + snprintf(logbuf, sizeof(logbuf), "pppoed id=%u %s", _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "pppoed %s", fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +#define NAME_VAL(x) { x, #x } +static struct _label_name { + int label; + const char *name; +} pppoe_code_labels[] = { + NAME_VAL(PPPOE_CODE_PADI), + NAME_VAL(PPPOE_CODE_PADO), + NAME_VAL(PPPOE_CODE_PADR), + NAME_VAL(PPPOE_CODE_PADS), + NAME_VAL(PPPOE_CODE_PADT), +#ifdef PPPOED_DEBUG +}, pppoe_tlv_labels[] = { + NAME_VAL(PPPOE_TAG_END_OF_LIST), + NAME_VAL(PPPOE_TAG_SERVICE_NAME), + NAME_VAL(PPPOE_TAG_AC_NAME), + NAME_VAL(PPPOE_TAG_HOST_UNIQ), + NAME_VAL(PPPOE_TAG_AC_COOKIE), + NAME_VAL(PPPOE_TAG_VENDOR_SPECIFIC), + NAME_VAL(PPPOE_TAG_RELAY_SESSION_ID), + NAME_VAL(PPPOE_TAG_SERVICE_NAME_ERROR), + NAME_VAL(PPPOE_TAG_AC_SYSTEM_ERROR), + NAME_VAL(PPPOE_TAG_GENERIC_ERROR) +#endif +}; +#define LABEL_TO_STRING(func_name, label_names, prefix_len) \ + static const char * \ + func_name(int code) \ + { \ + int i; \ + \ + for (i = 0; i < countof(label_names); i++) { \ + if (label_names[i].label == code) \ + return label_names[i].name + prefix_len;\ + } \ + \ + return "UNKNOWN"; \ + } +LABEL_TO_STRING(pppoe_code_string, pppoe_code_labels, 11) +#ifdef PPPOED_DEBUG +LABEL_TO_STRING(pppoe_tag_string, pppoe_tlv_labels, 10) +#endif + +const char * +pppoed_tlv_value_string(struct pppoe_tlv *tlv) +{ + int i; + char buf[3]; + static char _tlv_string_value[8192]; + + _tlv_string_value[0] = '\0'; + for (i = 0; i < tlv->length; i++) { + snprintf(buf, sizeof(buf), "%02x", tlv->value[i]); + strlcat(_tlv_string_value, buf, + sizeof(_tlv_string_value)); + } + return _tlv_string_value; +} + +/*********************************************************************** + * 雑多な関数 + ***********************************************************************/ +static int +session_id_cmp(void *a, void *b) +{ + int ia, ib; + + ia = (int)a; + ib = (int)b; + + return ib - ia; +} + +static uint32_t +session_id_hash(void *a, size_t siz) +{ + int ia; + + ia = (int)a; + + return ia % siz; +} diff --git a/usr.sbin/npppd/pptp/pptp.h b/usr.sbin/npppd/pptp/pptp.h new file mode 100644 index 00000000000..d4c7f1e70ac --- /dev/null +++ b/usr.sbin/npppd/pptp/pptp.h @@ -0,0 +1,423 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PPTP_H +#define PPTP_H 1 + +/************************************************************************ + * プロトコル上の定数 + ************************************************************************/ +#define PPTP_MES_TYPE_CTRL 1 +#define PPTP_MAGIC_COOKIE 0x1a2b3c4d +#define PPTP_RFC_2637_VERSION 0x0100 + +#ifndef PPTP_MAX_CALL +#define PPTP_MAX_CALL 8192 +#endif + + +/** Start-Control-Connection-Request */ +#define PPTP_CTRL_MES_CODE_SCCRQ 1 + +/** Start-Control-Connection-Reply */ +#define PPTP_CTRL_MES_CODE_SCCRP 2 + +/** Stop-Control-Connection-Request */ +#define PPTP_CTRL_MES_CODE_StopCCRQ 3 + +/** Stop-Control-Connection-Reply */ +#define PPTP_CTRL_MES_CODE_StopCCRP 4 + +/** Echo-Request */ +#define PPTP_CTRL_MES_CODE_ECHO_RQ 5 + +/** Echo-Reply */ +#define PPTP_CTRL_MES_CODE_ECHO_RP 6 + +/** Outgoing-Call-Request */ +#define PPTP_CTRL_MES_CODE_OCRQ 7 + +/** Outgoing-Call-Reply */ +#define PPTP_CTRL_MES_CODE_OCRP 8 + +/** Incoming-Call-Request */ +#define PPTP_CTRL_MES_CODE_ICRQ 9 + +/** Incoming-Call-Reply */ +#define PPTP_CTRL_MES_CODE_ICRP 10 + +/** Incoming-Call-Connected */ +#define PPTP_CTRL_MES_CODE_ICCN 11 + +/** Call-Clear-Request */ +#define PPTP_CTRL_MES_CODE_CCR 12 + +/** Call-Disconnect-Notify */ +#define PPTP_CTRL_MES_CODE_CDN 13 + +/** Set-Link-Info */ +#define PPTP_CTRL_MES_CODE_SLI 15 + + +#define PPTP_CTRL_FRAMING_ASYNC 1 +#define PPTP_CTRL_FRAMING_SYNC 2 + +#define PPTP_CTRL_BEARER_ANALOG 1 +#define PPTP_CTRL_BEARER_DIGITAL 2 + +/* Start-Control-Connection-Reply の Result Code */ +#define PPTP_SCCRP_RESULT_SUCCESS 1 +#define PPTP_SCCRP_RESULT_GENERIC_ERROR 2 +#define PPTP_SCCRP_RESULT_CHANNEL_EXISTS 3 +#define PPTP_SCCRP_RESULT_NOT_AUTHORIZIZED 4 +#define PPTP_SCCRP_RESULT_BAD_PROTOCOL_VERSION 5 + +/* General Error Code (RFC 2637 2.16 pp.36) */ +#define PPTP_ERROR_NONE 0 +#define PPTP_ERROR_NOT_CONNECTED 1 +#define PPTP_ERROR_BAD_FORMAT 2 +#define PPTP_ERROR_NO_RESOURCE 3 +#define PPTP_ERROR_BAD_CALL 4 +#define PPTP_ERROR_PAC_ERROR 5 + +/* Outgoing-Call-Reply の Result Code */ +#define PPTP_OCRP_RESULT_CONNECTED 1 +#define PPTP_OCRP_RESULT_GENERIC_ERROR 2 +#define PPTP_OCRP_RESULT_NO_CARRIER 3 +#define PPTP_OCRP_RESULT_BUSY 4 +#define PPTP_OCRP_RESULT_NO_DIALTONE 5 +#define PPTP_OCRP_RESULT_TIMEOUT 6 +#define PPTP_OCRP_RESULT_DO_NOT_ACCEPT 7 + +/* Echo-Reply の Result Code */ +#define PPTP_ECHO_RP_RESULT_OK 1 +#define PPTP_ECHO_RP_RESULT_GENERIC_ERROR 2 + +/* Stop-Control-Connection-Request の Reason */ +#define PPTP_StopCCRQ_REASON_NONE 1 +#define PPTP_StopCCRQ_REASON_STOP_PROTOCOL 2 +#define PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN 3 + +/* Stop-Control-Connection-Response の Result */ +#define PPTP_StopCCRP_RESULT_OK 1 +#define PPTP_StopCCRP_RESULT_GENERIC_ERROR 2 + +#define PPTP_CDN_RESULT_LOST_CARRIER 1 +#define PPTP_CDN_RESULT_GENRIC_ERROR 2 +#define PPTP_CDN_RESULT_ADMIN_SHUTDOWN 3 +#define PPTP_CDN_RESULT_REQUEST 4 + +/** デフォルトの待ち受け TCP ポート番号 */ +#define PPTPD_DEFAULT_TCP_PORT 1723 + + +#define PPTP_GRE_PROTOCOL_TYPE 0x880b +#define PPTP_GRE_VERSION 1 + +/************************************************************************ + * この実装の定数 + ************************************************************************/ +/* pptpd ステータス */ +#define PPTPD_STATE_INIT 0 +#define PPTPD_STATE_RUNNING 1 +#define PPTPD_STATE_SHUTTING_DOWN 2 +#define PPTPD_STATE_STOPPED 3 + +#define PPTPD_CONFIG_BUFSIZ 65535 + +#define PPTP_BACKLOG 32 +#define PPTP_BUFSIZ 1024 + +#define PPTPD_DEFAULT_LAYER2_LABEL "PPTP" + +/** ステートマシン */ +#define PPTP_CTRL_STATE_IDLE 0 +#define PPTP_CTRL_STATE_WAIT_CTRL_REPLY 1 +#define PPTP_CTRL_STATE_ESTABLISHED 2 +#define PPTP_CTRL_STATE_WAIT_STOP_REPLY 3 +#define PPTP_CTRL_STATE_DISPOSING 4 + +#ifndef PPTPD_DEFAULT_VENDOR_NAME +#define PPTPD_DEFAULT_VENDOR_NAME "IIJ" +#endif + +#ifndef PPTP_CALL_DEFAULT_MAXWINSZ +#define PPTP_CALL_DEFAULT_MAXWINSZ 64 +#endif + +#ifndef PPTP_CALL_CONNECT_SPEED +/** 接続スピード。OCRP で通知する値で、現在は 10Mbps で固定 */ +#define PPTP_CALL_CONNECT_SPEED 10000000 +#endif + +#ifndef PPTP_CALL_INITIAL_PPD +/** OCRP で通知する。初期 Packet Processing Dealy */ +#define PPTP_CALL_INITIAL_PPD 0 +#endif + +#ifndef PPTP_CALL_NMAX_INSEQ +/** シーケンスが戻った場合に何パケットまでを戻ったとみなすか。*/ +#define PPTP_CALL_NMAX_INSEQ 64 +#endif + +/** call のステートマシン */ +#define PPTP_CALL_STATE_IDLE 0 +#define PPTP_CALL_STATE_WAIT_CONN 1 +#define PPTP_CALL_STATE_ESTABLISHED 2 +#define PPTP_CALL_STATE_CLEANUP_WAIT 3 + +/* タイムアウト関連 */ +#define PPTPD_SHUTDOWN_TIMEOUT 5 + +#define PPTPD_IDLE_TIMEOUT 60 + +#define PPTP_CALL_CLEANUP_WAIT_TIME 3 + +#define PPTP_CTRL_DEFAULT_ECHO_INTERVAL 60 +#define PPTP_CTRL_DEFAULT_ECHO_TIMEOUT 60 +#define PPTP_CTRL_StopCCRP_WAIT_TIME 3 + +/** アドレスは最大何個 bind 可能か。*/ +#ifndef PPTP_NLISTENER +#define PPTP_NLISTENER 6 +#endif + +/** PPTPデーモンが停止したかどうかを返します。 */ +#define pptpd_is_stopped(pptpd) \ + (((pptpd)->state != PPTPD_STATE_SHUTTING_DOWN && \ + (pptpd)->state != PPTPD_STATE_RUNNING)? 1 : 0) + +/** PPTPデーモンが停止処理中かどうかを返します。 */ +#define pptpd_is_shutting_down(pptpd) \ + (((pptpd)->state == PPTPD_STATE_SHUTTING_DOWN)? 1 : 0) + +/************************************************************************ + * 型 + ************************************************************************/ +/** PPTP デーモンの型*/ +struct _pptpd; +/** PPTP デーモン待ち受け型 */ +typedef struct _pptpd_listener { + /** イベントコンテキスト */ + struct event ev_sock; + /** GREイベントコンテキスト */ + struct event ev_sock_gre; + /** PPTPD 自身 */ + struct _pptpd *self; + /** インデックス番号 */ + uint16_t index; + /** 待ち受けソケット */ + int sock; + /** GREソケット */ + int sock_gre; + /** 待ち受けアドレス TCP */ + struct sockaddr_in bind_sin; + /** 待ち受けアドレス GRE */ + struct sockaddr_in bind_sin_gre; + /** 物理層のラベル */ + char phy_label[16]; +} pptpd_listener; + +/** PPTP デーモンの型*/ +typedef struct _pptpd { + /** インスタンスの Id */ + unsigned id; + /** 待ち受けリスト */ + slist listener; + /** ステート */ + int state; + /** タイマーイベントコンテキスト */ + struct event ev_timer; + /** PPTP コントロールのリスト */ + slist ctrl_list; + + /** 設定 */ + struct properties *config; + + /** 空きコールリスト */ + slist call_free_list; + /** コールId => Call マップ */ + hash_table *call_id_map; + /** 接続を許可するIPv4ネットワーク */ + struct in_addr_range *ip4_allow; + + /** フラグ */ + uint32_t + initialized:1, + ctrl_in_pktdump:1, + ctrl_out_pktdump:1, + data_in_pktdump:1, + data_out_pktdump:1, + phy_label_with_ifname:1; +} pptpd; + +#define pptp_ctrl_sock_gre(ctrl) \ + ((pptpd_listener *)slist_get(&(ctrl)->pptpd->listener,\ + (ctrl)->listener_index))->sock_gre + +/** pptp_ctrl から、リスナーの物理層のラベルを取り出すマクロ */ +#define PPTP_CTRL_LISTENER_LABEL(ctrl) \ + ((pptpd_listener *)slist_get(&(ctrl)->pptpd->listener,\ + (ctrl)->listener_index))->phy_label + +/** PPTP コントロールの型*/ +typedef struct _pptp_ctrl { + /** 親 pptpd */ + pptpd *pptpd; + /** リスナー インデックス番号*/ + uint16_t listener_index; + /** インスタンスの Id */ + unsigned id; + /** ステート */ + int state; + /** 物理層のラベル */ + char phy_label[16]; + + /** ソケット */ + int sock; + /** 先方のアドレス */ + struct sockaddr_storage peer; + /** 当方のアドレス */ + struct sockaddr_storage our; + /** イベントコンテキスト */ + struct event ev_sock; + /** タイマーイベントコンテキスト */ + struct event ev_timer; + + /** アイドル状態から ECHO 送信までの秒数。0以下は無効。*/ + int echo_interval; + /** ECHO のタイムアウト */ + int echo_timeout; + + /** 送信可能かどうか。送信バッファが埋まっていないか */ + int send_ready; + /** 受信バッファ */ + bytebuffer *recv_buf; + /** 送信バッファ */ + bytebuffer *send_buf; + + /** コールのリスト */ + slist call_list; + + /** 最後にコントロールメッセージを送信した時間 */ + time_t last_snd_ctrl; + /** 最後にコントロールメッセージを受信を送信した時間 */ + time_t last_rcv_ctrl; + /** Echo Request の identifier */ + uint32_t echo_seq; + + int16_t /** I/O イベント処理中。*/ + on_io_event:1, + reserved:15; + +} pptp_ctrl; + +/** PPTP コールの型 */ +typedef struct _pptp_call { + /** 親コントロール */ + pptp_ctrl *ctrl; + /** コールID*/ + unsigned id; + + /** 受信インタフェース番号 */ + int ifidx; + + /** ステート */ + int state; + + /** 先方のコールID*/ + unsigned peers_call_id; + void *ppp; + + /** 次の確認応答 */ + uint32_t snd_una; + /** 次の送信シーケンス番号 */ + uint32_t snd_nxt; + + /** 受信シーケンス番号 */ + uint32_t rcv_nxt; + /** 応答確認した受信シーケンス番号 */ + uint32_t rcv_acked; + + /** カレントウィンドウサイズ */ + int winsz; + /** 最大ウィンドウサイズ */ + int maxwinsz; + /** 先方の最大ウィンドウサイズ*/ + int peers_maxwinsz; + + time_t last_io; + +} pptp_call; + + +/************************************************************************ + * 関数プロトタイプ + ************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +int pptpd_init (pptpd *); +void pptpd_uninit (pptpd *); +int pptpd_assign_call (pptpd *, pptp_call *); +void pptpd_release_call (pptpd *, pptp_call *); +int pptpd_start (pptpd *); +void pptpd_stop (pptpd *); +void pptpd_stop_immediatly (pptpd *); +void pptpd_ctrl_finished_notify(pptpd *, pptp_ctrl *); +int pptpd_add_listener(pptpd *, int, const char *, struct sockaddr *); + +pptp_ctrl *pptp_ctrl_create (void); +int pptp_ctrl_init (pptp_ctrl *); +int pptp_ctrl_start (pptp_ctrl *); +void pptp_ctrl_stop (pptp_ctrl *, int); +void pptp_ctrl_destroy (pptp_ctrl *); +void pptp_ctrl_output (pptp_ctrl *, u_char *, int); + +pptp_call *pptp_call_create (void); +int pptp_call_init (pptp_call *, pptp_ctrl *); +int pptp_call_start (pptp_call *); +int pptp_call_stop (pptp_call *); +void pptp_call_destroy (pptp_call *); +void pptp_call_input (pptp_call *, int, u_char *, int); +void pptp_call_gre_input (pptp_call *, uint32_t, uint32_t, int, u_char *, int); +void pptp_call_disconnect(pptp_call *, int, int, const char *); +int pptpd_reload(pptpd *, struct properties *, const char *, int); + +/* config_helper */ +const char *pptpd_config_str (pptpd *, const char *); +int pptpd_config_int (pptpd *, const char *, int); +int pptpd_config_str_equal (pptpd *, const char *, const char *, int); +int pptpd_config_str_equali (pptpd *, const char *, const char *, int); +const char *pptp_ctrl_config_str (pptp_ctrl *, const char *); +int pptp_ctrl_config_int (pptp_ctrl *, const char *, int); +int pptp_ctrl_config_str_equal (pptp_ctrl *, const char *, const char *, int); +int pptp_ctrl_config_str_equali (pptp_ctrl *, const char *, const char *, int); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/usr.sbin/npppd/pptp/pptp_call.c b/usr.sbin/npppd/pptp/pptp_call.c new file mode 100644 index 00000000000..0bdc6835638 --- /dev/null +++ b/usr.sbin/npppd/pptp/pptp_call.c @@ -0,0 +1,852 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: pptp_call.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * PPTPコールの実装。PACを仮定しています。 + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <event.h> + +#ifdef USE_LIBSOCKUTIL +#include <seil/sockfromto.h> +#endif + +#include "bytebuf.h" +#include "slist.h" +#include "hash.h" +#include "debugutil.h" +#include "time_utils.h" + +#include "pptp.h" +#include "pptp_local.h" +#include "pptp_subr.h" + +#ifdef PPTP_CALL_DEBUG +#define PPTP_CALL_DBG(x) pptp_call_log x +#define PPTP_CALL_ASSERT(x) ASSERT(x) +#else +#define PPTP_CALL_DBG(x) +#define PPTP_CALL_ASSERT(x) +#endif + +static void pptp_call_log (pptp_call *, int, const char *, ...) __printflike(3,4); + + +static void pptp_call_notify_down (pptp_call *); +static int pptp_call_recv_SLI (pptp_call *, u_char *, int); +static int pptp_call_recv_CCR (pptp_call *, u_char *, int); +static int pptp_call_recv_OCRQ (pptp_call *, u_char *, int); +static void pptp_call_send_CDN (pptp_call *, int, int, int, const char *); +static int pptp_call_send_OCRP (pptp_call *, int, int, int); +static int pptp_call_gre_output (pptp_call *, int, int, u_char *, int); +static int pptp_call_bind_ppp (pptp_call *); +static void pptp_call_log (pptp_call *, int, const char *, ...); +static void pptp_call_OCRQ_string (struct pptp_ocrq *, char *, int); +static void pptp_call_OCRP_string (struct pptp_ocrp *, char *, int); +static void pptp_call_ppp_input (pptp_call *, unsigned char *, int); +static char * pptp_call_state_string(int); + +/* not used +static int pptp_call_send_SLI (pptp_call *); + */ + +#define SEQ_LT(a,b) ((int)((a) - (b)) < 0) +#define SEQ_LE(a,b) ((int)((a) - (b)) <= 0) +#define SEQ_GT(a,b) ((int)((a) - (b)) > 0) +#define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) +#define SEQ_SUB(a,b) ((int32_t)((a) - (b))) + +/** 切り上げ割算 */ +#define RUPDIV(n,d) (((n) + ((d) - ((n) % (d)))) / (d)) + +/*********************************************************************** + * インスタンス操作関連 + ***********************************************************************/ +/** pptp_call インスタンスを生成します */ +pptp_call * +pptp_call_create(void) +{ + pptp_call *_this; + + if ((_this = malloc(sizeof(pptp_call))) == NULL) + return NULL; + + return _this; +} + +/** pptp_call インスタンスを初期化します */ +int +pptp_call_init(pptp_call *_this, pptp_ctrl *ctrl) +{ + memset(_this, 0, sizeof(pptp_call)); + _this->ctrl = ctrl; + + _this->maxwinsz = PPTP_CALL_DEFAULT_MAXWINSZ; + _this->winsz = RUPDIV(_this->maxwinsz, 2); + _this->last_io = get_monosec(); + _this->snd_nxt = 1; + + return 0; + return 1; +} + +/** pptp_call インスタンスを開始します */ +int +pptp_call_start(pptp_call *_this) +{ + if (pptp_call_bind_ppp(_this) != 0) + return 1; + + return 0; +} +/** pptp_call インスタンスを終了します */ +int +pptp_call_stop(pptp_call *_this) +{ + if (_this->state != PPTP_CALL_STATE_CLEANUP_WAIT) + pptp_call_disconnect(_this, 0, 0, NULL); + + pptp_call_log(_this, LOG_NOTICE, "logtype=Terminated"); + pptpd_release_call(_this->ctrl->pptpd, _this); + + return 0; +} + +/** pptp_call インスタンスを解放します */ +void +pptp_call_destroy(pptp_call *_this) +{ + PPTP_CALL_ASSERT(_this != NULL); + free(_this); +} + +/** PPTPコールを切断します。*/ +void +pptp_call_disconnect(pptp_call *_this, int result, int error, const char * + statistics) +{ + if (_this->state == PPTP_CALL_STATE_CLEANUP_WAIT) { + pptp_call_notify_down(_this); + return; + } + if (result > 0) + pptp_call_send_CDN(_this, result, error, 0, statistics); + _this->state = PPTP_CALL_STATE_CLEANUP_WAIT; + pptp_call_notify_down(_this); +} + +/*********************************************************************** + * コントロールパケットの入出力 + ***********************************************************************/ +/** コントロールパケットの入力 */ +void +pptp_call_input(pptp_call *_this, int mes_type, u_char *pkt, int lpkt) +{ + + PPTP_CALL_ASSERT(_this != NULL); + PPTP_CALL_ASSERT(pkt != NULL); + PPTP_CALL_ASSERT(lpkt >= 4); + + _this->last_io = get_monosec(); + switch (mes_type) { + case PPTP_CTRL_MES_CODE_OCRQ: + if (_this->state != PPTP_CALL_STATE_IDLE) { + pptp_call_send_OCRP(_this, + PPTP_OCRP_RESULT_GENERIC_ERROR, + PPTP_ERROR_BAD_CALL, 0); + goto bad_state; + } + if (pptp_call_recv_OCRQ(_this, pkt, lpkt) != 0) { + pptp_call_send_OCRP(_this, + PPTP_OCRP_RESULT_GENERIC_ERROR, + PPTP_ERROR_BAD_CALL, 0); + return; + } + if (pptpd_assign_call(_this->ctrl->pptpd, _this) != 0) { + pptp_call_send_OCRP(_this, PPTP_OCRP_RESULT_BUSY, + PPTP_ERROR_NONE, 0); + return; + } + if (pptp_call_send_OCRP(_this, PPTP_OCRP_RESULT_CONNECTED, + PPTP_ERROR_NONE, 0) != 0) { + pptp_call_disconnect(_this, + PPTP_CDN_RESULT_GENRIC_ERROR, + PPTP_ERROR_PAC_ERROR, NULL); + return; + } + if (pptp_call_start(_this) != 0) { + pptp_call_disconnect(_this, + PPTP_CDN_RESULT_GENRIC_ERROR, + PPTP_ERROR_PAC_ERROR, NULL); + return; + } + _this->state = PPTP_CALL_STATE_ESTABLISHED; + break; + case PPTP_CTRL_MES_CODE_SLI: + if (pptp_call_recv_SLI(_this, pkt, lpkt) != 0) { + return; + } + return; + case PPTP_CTRL_MES_CODE_CCR: + pptp_call_recv_CCR(_this, pkt, lpkt); + if (_this->state == PPTP_CALL_STATE_ESTABLISHED) { + pptp_call_disconnect(_this, PPTP_CDN_RESULT_REQUEST, + PPTP_ERROR_NONE, NULL); + } else { + pptp_call_disconnect(_this, 0, 0, NULL); + if (_this->state != PPTP_CALL_STATE_CLEANUP_WAIT) + goto bad_state; + } + return; + default: + pptp_call_log(_this, LOG_WARNING, + "Unhandled control message type=%s(%d)", + pptp_ctrl_mes_type_string(mes_type), mes_type); + } + return; +bad_state: + pptp_call_log(_this, LOG_WARNING, + "Received control message %s(%d) in bad state=%s", + pptp_ctrl_mes_type_string(mes_type), mes_type, + pptp_call_state_string(_this->state)); +} + +/** + * Set-Link-Info の受信 + * <p> + * この実装では『同期フレーム』しかサポートしないので、ACCM + * (Asynchronous Control Character Map) は、保持しない。</p> + */ +static int +pptp_call_recv_SLI(pptp_call *_this, u_char *pkt, int lpkt) +{ + struct pptp_sli *sli; + + if (lpkt < sizeof(struct pptp_sli)) { + pptp_call_log(_this, LOG_ERR, "Received bad SLI: packet too " + "short: %d < %d", lpkt, (int)sizeof(struct pptp_sli)); + return 1; + } + sli = (struct pptp_sli *)pkt; + sli->send_accm = ntohl(sli->send_accm); + sli->recv_accm = ntohl(sli->recv_accm); + if (sli->send_accm != 0xffffffffL || + sli->recv_accm != 0xffffffffL) { + pptp_call_log(_this, LOG_WARNING, + "RecvSLI Received bad ACCM %08x:%08x: asynchronous framing " + "is not supported.\n", sli->send_accm, sli->recv_accm); + return 1; + } + pptp_call_log(_this, LOG_INFO, "RecvSLI accm=%08x:%08x", + sli->send_accm, sli->recv_accm); + + return 0; +} + +#if 0 +/* + * 『PPTPパススルー』機能をサポートする一部のルータは、PAC から SLI を + * を受信すると、以後 PAC => PNS の 1723/tcp パケットを落すモノがある + * ようです。(今のところ富士通製 FLASHWAVE がそんな感じ) + * + * このため、使い途のわからない SLI は npppd からは送信しないことにしました。 + * See idgw-develop 5916 + */ +/** SLI の送信 */ +static int +pptp_call_send_SLI(pptp_call *_this) +{ + int lpkt; + struct pptp_sli *sli; + + sli = bytebuffer_pointer(_this->ctrl->send_buf); + lpkt = bytebuffer_remaining(_this->ctrl->send_buf); + if (lpkt < sizeof(struct pptp_sli)) { + pptp_call_log(_this, LOG_ERR, + "SendOCRP failed: No buffer space available"); + return -1; + } + memset(sli, 0, sizeof(struct pptp_sli)); + + pptp_init_header(&sli->header, sizeof(struct pptp_sli), + PPTP_CTRL_MES_CODE_SLI); + + sli->peers_call_id = _this->id; + sli->send_accm = 0xffffffff; + sli->recv_accm = 0xffffffff; + + _this->last_io = get_monosec(); + pptp_call_log(_this, LOG_INFO, "SendSLI accm=%08x:%08x", + sli->send_accm, sli->recv_accm); + sli->peers_call_id = htons(sli->peers_call_id); + sli->send_accm = htonl(sli->send_accm); + sli->recv_accm = htonl(sli->recv_accm); + pptp_ctrl_output(_this->ctrl, NULL, sizeof(struct pptp_sli)); + + return 0; +} +#endif + +/** Call-Clear-Request の受信 */ +static int +pptp_call_recv_CCR(pptp_call *_this, u_char *pkt, int lpkt) +{ + struct pptp_ccr *ccr; + + // サイズ検査 + PPTP_CALL_ASSERT(lpkt >= sizeof(struct pptp_ccr)); + if (lpkt < sizeof(struct pptp_ccr)) { + // call_id チェック済みでここに呼ばれるので、起こり得ない。 + return 1; + } + ccr = (struct pptp_ccr *)pkt; + + // バイトオーダー + ccr->call_id = ntohs(ccr->call_id); + pptp_call_log(_this, LOG_INFO, "RecvCCR call_id=%u", ccr->call_id); + + return 0; +} + +/** Call-Disconnect-Notify の送信 */ +static void +pptp_call_send_CDN(pptp_call *_this, int result, int error, int cause, + const char *statistics) +{ + int lpkt; + struct pptp_cdn *cdn; + + cdn = bytebuffer_pointer(_this->ctrl->send_buf); + lpkt = bytebuffer_remaining(_this->ctrl->send_buf); + if (lpkt < sizeof(struct pptp_cdn)) { + pptp_call_log(_this, LOG_ERR, + "SendCCR failed: No buffer space available"); + return; + } + memset(cdn, 0, sizeof(struct pptp_cdn)); + + pptp_init_header(&cdn->header, sizeof(struct pptp_cdn), + PPTP_CTRL_MES_CODE_CDN); + + cdn->call_id = _this->id; + cdn->result_code = result; + cdn->error_code = error; + cdn->cause_code = cause; + if (statistics != NULL) + strlcpy(cdn->statistics, statistics, sizeof(cdn->statistics)); + + pptp_call_log(_this, LOG_INFO, "SendCDN " + "call_id=%u result=%s(%d) error=%s(%d) cause=%d statistics=%s", + cdn->call_id, + pptp_CDN_result_string(cdn->result_code), cdn->result_code, + pptp_general_error_string(cdn->error_code), cdn->error_code, + cdn->cause_code, + (statistics == NULL)? "(none)" : (char *)cdn->statistics); + + cdn->call_id = htons(cdn->call_id); + cdn->cause_code = htons(cdn->cause_code); + + _this->last_io = get_monosec(); + pptp_ctrl_output(_this->ctrl, NULL, sizeof(struct pptp_cdn)); +} + +/** Outgoing-Call-Reply の送信 */ +static int +pptp_call_send_OCRP(pptp_call *_this, int result, int error, int cause) +{ + int lpkt; + struct pptp_ocrp *ocrp; + char logbuf[512]; + + ocrp = bytebuffer_pointer(_this->ctrl->send_buf); + lpkt = bytebuffer_remaining(_this->ctrl->send_buf); + if (lpkt < sizeof(struct pptp_ocrp)) { + pptp_call_log(_this, LOG_ERR, + "SendOCRP failed: No buffer space available"); + return -1; + } + memset(ocrp, 0, sizeof(struct pptp_ocrp)); + + pptp_init_header(&ocrp->header, sizeof(struct pptp_ocrp), + PPTP_CTRL_MES_CODE_OCRP); + + ocrp->call_id = _this->id; + ocrp->peers_call_id = _this->peers_call_id; + ocrp->result_code = result; + ocrp->error_code = error; + ocrp->cause_code = cause; + ocrp->connect_speed = PPTP_CALL_CONNECT_SPEED; + ocrp->recv_winsz = _this->maxwinsz; + ocrp->packet_proccessing_delay = PPTP_CALL_INITIAL_PPD; + ocrp->physical_channel_id = _this->id; + + pptp_call_OCRP_string(ocrp, logbuf, sizeof(logbuf)); + pptp_call_log(_this, LOG_INFO, "SendOCRP %s", logbuf); + + ocrp->call_id = htons(ocrp->call_id); + ocrp->peers_call_id = htons(ocrp->peers_call_id); + ocrp->cause_code = htons(ocrp->cause_code); + ocrp->connect_speed = htons(ocrp->connect_speed); + ocrp->recv_winsz = htons(ocrp->recv_winsz); + ocrp->packet_proccessing_delay = htons(ocrp->packet_proccessing_delay); + ocrp->physical_channel_id = htonl(ocrp->physical_channel_id); + + _this->last_io = get_monosec(); + pptp_ctrl_output(_this->ctrl, NULL, sizeof(struct pptp_ocrp)); + + return 0; +} + +/** Outgoing-Call-Request を受信 */ +static int +pptp_call_recv_OCRQ(pptp_call *_this, u_char *pkt, int lpkt) +{ + char logbuf[512]; + struct pptp_ocrq *ocrq; + + // サイズ検査 + if (lpkt < sizeof(struct pptp_ocrq)) { + pptp_call_log(_this, LOG_ERR, "Received bad OCRQ: packet too " + "short: %d < %d", lpkt, (int)sizeof(struct pptp_ocrq)); + return 1; + } + ocrq = (struct pptp_ocrq *)pkt; + + // バイトオーダー + ocrq->call_id = ntohs(ocrq->call_id); + ocrq->call_serial_number = ntohs(ocrq->call_serial_number); + ocrq->recv_winsz = ntohs(ocrq->recv_winsz); + ocrq->packet_proccessing_delay = + ntohs(ocrq->packet_proccessing_delay); + ocrq->phone_number_length = ntohs(ocrq->phone_number_length); + ocrq->reservied1 = ntohs(ocrq->reservied1); + ocrq->maximum_bps = ntohl(ocrq->maximum_bps); + ocrq->minimum_bps = ntohl(ocrq->minimum_bps); + ocrq->bearer_type = ntohl(ocrq->bearer_type); + ocrq->framing_type = ntohl(ocrq->framing_type); + + _this->peers_call_id = ocrq->call_id; + _this->peers_maxwinsz = ocrq->recv_winsz; + + pptp_call_OCRQ_string(ocrq, logbuf, sizeof(logbuf)); + pptp_call_log(_this, LOG_INFO, "RecvOCRQ %s", logbuf); + + return 0; +} +/*********************************************************************** + * GRE の入出力 + ***********************************************************************/ + +/** GREからパケット受信 */ +void +pptp_call_gre_input(pptp_call *_this, uint32_t seq, uint32_t ack, + int input_flags, u_char *pkt, int pktlen) +{ + int log_prio; + const char *reason; + + PPTP_CALL_ASSERT(_this != NULL); + + log_prio = LOG_INFO; + +#ifdef PPTP_CALL_DEBUG + if (debuglevel >= 2) { + pptp_call_log(_this, LOG_DEBUG, + "Received data packet seq=%u(%u-%u) ack=%u(%u-%u)", + seq, _this->rcv_nxt, + _this->rcv_nxt + _this->peers_maxwinsz - 1, + ack, _this->snd_una, _this->snd_nxt); + } +#endif + if (_this->state != PPTP_CALL_STATE_ESTABLISHED) { + pptp_call_log(_this, LOG_INFO, + "Received data packet in illegal state=%s", + pptp_call_state_string(_this->state)); + return; + } + PPTP_CALL_ASSERT(_this->state == PPTP_CALL_STATE_ESTABLISHED); + + if (input_flags & PPTP_GRE_PKT_ACK_PRESENT) { + if (ack + 1 == _this->snd_una) { + /* 前回受信した ack から進んでいないだけ */ + } else if (SEQ_LT(ack, _this->snd_una)) { + /* ack が戻った */ + if (abs(ack - _this->snd_una) < PPTP_CALL_NMAX_INSEQ) { + /* ちょっと戻るのはパケット順入れ替わり。*/ + log_prio = LOG_DEBUG; + } + reason = "ack out of sequence"; + goto bad_pkt; + // FALL THROUGH + } else if (SEQ_GT(ack, _this->snd_nxt)) { + reason = "ack for unknown sequence."; + goto bad_pkt; + } else { + ack++; + _this->snd_una = ack; + } + } + + if ((input_flags & PPTP_GRE_PKT_SEQ_PRESENT) == 0) + return; /* ack only packet */ + + // seq チェック + if (SEQ_LT(seq, _this->rcv_nxt)) { + /* 順番入れ替わり、届くのが遅れた? */ + if (abs(seq - _this->rcv_nxt) < PPTP_CALL_NMAX_INSEQ) + log_prio = LOG_DEBUG; + reason = "out of sequence"; + goto bad_pkt; + } else if (SEQ_GE(seq, _this->rcv_nxt + _this->maxwinsz)){ + /* + * どんなにおかしくても受信せざるをえない。 + * + * FIXME: パケットロスが 4096 発を超えると MPPE の状態が復元 + * できないが。 + */ + pptp_call_log(_this, LOG_INFO, + "Received packet caused window overflow. seq=%u(%u-%u), " + "may lost %d packets.", seq, _this->rcv_nxt, + _this->rcv_nxt + _this->maxwinsz - 1, + SEQ_SUB(seq, _this->rcv_nxt)); + } + seq++; + /* TODO: パケットロスは ppp->ierrors でカウントすべきか? */ + _this->rcv_nxt = seq; + + if (SEQ_SUB(seq, _this->rcv_acked) > RUPDIV(_this->winsz, 2)) { + /* + * Multi-packet acknowledgement. + * window size の半分を超えた場合のみ ack する + */ + PPTP_CALL_DBG((_this, LOG_DEBUG, "rcv window size=%u %u %u\n", + SEQ_SUB(seq, _this->rcv_acked), seq, _this->rcv_acked)); + pptp_call_gre_output(_this, 0, 1, NULL, 0); + } + pptp_call_ppp_input(_this, pkt, pktlen); + + return; +bad_pkt: + pptp_call_log(_this, log_prio, + "Received bad data packet: %s: seq=%u(%u-%u) ack=%u(%u-%u)", + reason, seq, _this->rcv_nxt, _this->rcv_nxt + _this->maxwinsz - 1, + ack, _this->snd_una, _this->snd_nxt); +} + +/** + * GREに出力 + * @param fseq SEQフィールドを含めるかどうか。 + * @param fack ACKフィールドを含めるかどうか。 + */ +static int +pptp_call_gre_output(pptp_call *_this, int fseq, int fack, u_char *pkt, + int lpkt) +{ + int sz; + struct pptp_gre_header *grehdr; + u_char buf[65535], *opkt; + struct sockaddr_storage peer, sock; +#ifndef USE_LIBSOCKUTIL + socklen_t peerlen; +#endif + + memset(buf, 0, sizeof(buf)); + + opkt = buf; + grehdr = (struct pptp_gre_header *)opkt; + opkt += sizeof(struct pptp_gre_header); + + /* GREヘッダ*/ + grehdr->K = 1; + grehdr->ver = PPTP_GRE_VERSION; + grehdr->protocol_type = htons(PPTP_GRE_PROTOCOL_TYPE); + grehdr->payload_length = htons(lpkt); + grehdr->call_id = htons(_this->peers_call_id); + +#ifdef PPTP_CALL_DEBUG + if (debuglevel >= 2 && (fseq || fack)) { + pptp_call_log(_this, LOG_DEBUG, + "Sending data packet seq=%u ack=%u", + _this->snd_nxt, _this->rcv_nxt - 1); + } +#endif + PPTP_CALL_ASSERT(ALIGNED_POINTER(opkt, uint32_t)); + if (fseq) { + grehdr->S = 1; + *(uint32_t *)opkt = htonl(_this->snd_nxt++); + opkt += 4; + } + if (fack) { + grehdr->A = 1; + _this->rcv_acked = _this->rcv_nxt; + *(uint32_t *)opkt = htonl(_this->rcv_nxt - 1); + opkt += 4; + } + if (lpkt > 0) { + memcpy(opkt, pkt, lpkt); + opkt += lpkt; + } + memcpy(&peer, &_this->ctrl->peer, sizeof(peer)); + memcpy(&sock, &_this->ctrl->our, sizeof(sock)); + switch (peer.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&peer)->sin_port = 0; + ((struct sockaddr_in *)&sock)->sin_port = 0; +#ifndef USE_LIBSOCKUTIL + peerlen = sizeof(struct sockaddr_in); +#endif + break; + default: + return 1; + } + if (_this->ctrl->pptpd->data_out_pktdump != 0) { + pptp_call_log(_this, LOG_DEBUG, "PPTP Data output packet dump"); + show_hd(debug_get_debugfp(), buf, opkt - buf); + } +#ifdef USE_LIBSOCKUTIL + sz = sendfromto(pptp_ctrl_sock_gre(_this->ctrl), buf, opkt - buf, + 0, (struct sockaddr *)&sock, (struct sockaddr *)&peer); +#else + sz = sendto(pptp_ctrl_sock_gre(_this->ctrl), buf, opkt - buf, 0, + (struct sockaddr *)&peer, peerlen); +#endif + + if (sz <= 0) + pptp_call_log(_this, LOG_WARNING, "sendto(%d) failed: %m", + pptp_ctrl_sock_gre(_this->ctrl)); + + return (sz > 0)? 0 : 1; +} + +/************************************************************************ + * npppd の物理層として + ************************************************************************/ +#include <net/if_dl.h> +#include "npppd.h" + +static int pptp_call_ppp_output (npppd_ppp *, unsigned char *, int, int); +static void pptp_call_closed_by_ppp (npppd_ppp *); + +/** PPTP の物理層が終了したことを、PPP に確実に伝える。*/ +static void +pptp_call_notify_down(pptp_call *_this) +{ + if (_this->ppp != NULL) + ppp_phy_downed(_this->ppp); +} + + +/** ppp にパケットを入力します。 */ +static void +pptp_call_ppp_input(pptp_call *_this, u_char *pkt, int pktlen) +{ + int rval; + npppd_ppp *ppp; + + ppp = _this->ppp; + if (ppp == NULL) { + pptp_call_log(_this, LOG_WARNING, + "Received ppp frame but ppp is not assigned yet"); + return; + } + rval = ppp->recv_packet(ppp, pkt, pktlen, 0); + if (_this->ppp == NULL) /* ppp is freed */ + return; + + if (rval != 0) { + ppp->ierrors++; + } else { + ppp->ipackets++; + ppp->ibytes += pktlen; + } +} + +/** ppp からパケットが出力される時に呼び出されます。 */ +static int +pptp_call_ppp_output(npppd_ppp *ppp, unsigned char *bytes, int nbytes, + int flags) +{ + pptp_call *_this; + + _this = ppp->phy_context; + PPTP_CALL_ASSERT(_this != NULL); + + if (_this == NULL) + return 0; + + if (pptp_call_gre_output(_this, 1, 1, bytes, nbytes) != 0) { + ppp->oerrors++; + return 1; + } + ppp->opackets++; + ppp->obytes += nbytes; + + return 0; +} + +/** ppp で切断された場合に呼び出されます。 */ +static void +pptp_call_closed_by_ppp(npppd_ppp *ppp) +{ + pptp_call *_this; + + PPTP_CALL_ASSERT(ppp != NULL); + PPTP_CALL_ASSERT(ppp->phy_context != NULL); + + _this = ppp->phy_context; + + _this->ppp = NULL; // pptp_call_disconnect より先に。 + + if (_this->state != PPTP_CALL_STATE_CLEANUP_WAIT) { + pptp_call_disconnect(_this, PPTP_CDN_RESULT_LOST_CARRIER, 0, + NULL); + } + pptp_call_log(_this, LOG_NOTICE, "logtype=PPPUnbind"); +} + +/** ppp の bind。*/ +static int +pptp_call_bind_ppp(pptp_call *_this) +{ + npppd_ppp *ppp; + + ppp = NULL; + if ((ppp = ppp_create()) == NULL) + goto reigai; + + PPTP_CALL_ASSERT(_this->ppp == NULL); + + if (_this->ppp != NULL) + return -1; + + _this->ppp = ppp; + + ppp->phy_context = _this; + ppp->tunnel_type = PPP_TUNNEL_PPTP; + ppp->send_packet = pptp_call_ppp_output; + ppp->phy_close = pptp_call_closed_by_ppp; + + strlcpy(ppp->phy_label, _this->ctrl->phy_label, sizeof(ppp->phy_label)); + + memcpy(&ppp->phy_info.peer_in, &_this->ctrl->peer, + _this->ctrl->peer.ss_len); + + if (ppp_init(npppd_get_npppd(), ppp) != 0) + goto reigai; + + pptp_call_log(_this, LOG_NOTICE, "logtype=PPPBind ppp=%d", ppp->id); + ppp_start(ppp); + + return 0; +reigai: + pptp_call_log(_this, LOG_ERR, "failed binding ppp"); + + if (ppp != NULL) + ppp_destroy(ppp); + _this->ppp = NULL; + + //pptp_call_disconnect(_this, PPTP_CDN_RCODE_BUSY, 0, NULL); + return 1; +} + +/*********************************************************************** + * その他ユーティリティ関数 + ***********************************************************************/ + +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static void +pptp_call_log(pptp_call *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); +#ifdef PPTPD_MULITPLE + snprintf(logbuf, sizeof(logbuf), "pptpd id=%u ctrl=%u call=%u %s", + _this->ctrl->pptpd->id, _this->ctrl->id, _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "pptpd ctrl=%u call=%u %s", + _this->ctrl->id, _this->id, fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +/** Outgoing-Call-Request パケットを文字列で表現する */ +static void +pptp_call_OCRQ_string(struct pptp_ocrq *ocrq, char *buf, int lbuf) +{ + snprintf(buf, lbuf, + "call_id=%u call_serial_number=%u max_bps=%u min_bps=%u bearer=%s " + "framing=%s recv_winsz=%u packet_proccessing_delay=%u " + "phone_nunmber=%s subaddress=%s", + ocrq->call_id, ocrq->call_serial_number, ocrq->maximum_bps, + ocrq->minimum_bps, pptp_bearer_string(ocrq->bearer_type), + pptp_framing_string(ocrq->framing_type), ocrq->recv_winsz, + ocrq->packet_proccessing_delay, ocrq->phone_number, + ocrq->subaddress); +} + +/** Outgoing-Call-Reply パケットを文字列で表現する */ +void +pptp_call_OCRP_string(struct pptp_ocrp *ocrp, char *buf, int lbuf) +{ + snprintf(buf, lbuf, + "call_id=%u peers_call_id=%u result=%u error=%u cause=%u " + "conn_speed=%u recv_winsz=%u packet_proccessing_delay=%u " + "physical_channel_id=%u", + ocrp->call_id, ocrp->peers_call_id, ocrp->result_code, + ocrp->error_code, ocrp->cause_code, ocrp->connect_speed, + ocrp->recv_winsz, ocrp->packet_proccessing_delay, + ocrp->physical_channel_id); +} + +static char * +pptp_call_state_string(int state) +{ + switch (state) { + case PPTP_CALL_STATE_IDLE: + return "idle"; + case PPTP_CALL_STATE_WAIT_CONN: + return "wait-conn"; + case PPTP_CALL_STATE_ESTABLISHED: + return "established"; + case PPTP_CALL_STATE_CLEANUP_WAIT: + return "cleanup-wait"; + } + return "unknown"; +} + diff --git a/usr.sbin/npppd/pptp/pptp_ctrl.c b/usr.sbin/npppd/pptp/pptp_ctrl.c new file mode 100644 index 00000000000..7d8941bbc11 --- /dev/null +++ b/usr.sbin/npppd/pptp/pptp_ctrl.c @@ -0,0 +1,1168 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * PPTP(RFC 2637) コントロール接続部の実装。PACのみ。 + */ +/* $Id: pptp_ctrl.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <netdb.h> +#include <unistd.h> +#include <syslog.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <event.h> + +#include "bytebuf.h" +#include "debugutil.h" +#include "hash.h" +#include "slist.h" +#include "time_utils.h" + +#include "version.h" + +#include "pptp.h" +#include "pptp_local.h" +#include "pptp_subr.h" + +/** 2 秒毎に pptp_ctrl_timeout */ +#define PPTP_CTRL_TIMEOUT_IVAL_SEC 2 + +#ifdef PPTP_CTRL_DEBUG +#define PPTP_CTRL_ASSERT(x) ASSERT(x) +#define PPTP_CTRL_DBG(x) pptp_ctrl_log x +#else +#define PPTP_CTRL_ASSERT(x) +#define PPTP_CTRL_DBG(x) +#endif + +static unsigned pptp_ctrl_seqno = 0; + +static void pptp_ctrl_log (pptp_ctrl *, int, const char *, ...) __printflike(3,4); +static void pptp_ctrl_timeout (int, short, void *); +static void pptp_ctrl_reset_timeout (pptp_ctrl *); +static void pptp_ctrl_io_event (int, short, void *); +static void pptp_ctrl_set_io_event (pptp_ctrl *); +static int pptp_ctrl_output_flush (pptp_ctrl *); +static void pptp_ctrl_SCCRx_string (struct pptp_scc *, u_char *, int); +static int pptp_ctrl_recv_SCCRQ (pptp_ctrl *, u_char *, int); +static int pptp_ctrl_recv_StopCCRP (pptp_ctrl *, u_char *, int); +static int pptp_ctrl_send_StopCCRQ (pptp_ctrl *, int); +static int pptp_ctrl_recv_StopCCRQ (pptp_ctrl *, u_char *, int); +static int pptp_ctrl_send_StopCCRP (pptp_ctrl *, int, int); +static int pptp_ctrl_send_SCCRP (pptp_ctrl *, int, int); +static void pptp_ctrl_send_CDN (pptp_ctrl *, int, int, int, const char *); +static void pptp_ctrl_process_echo_req (pptp_ctrl *, u_char *, int); +static int pptp_ctrl_recv_echo_rep (pptp_ctrl *, u_char *, int); +static void pptp_ctrl_send_echo_req (pptp_ctrl *); +static int pptp_ctrl_input (pptp_ctrl *, u_char *, int); +static int pptp_ctrl_call_input (pptp_ctrl *, int, u_char *, int); +static const char *pptp_ctrl_state_string (int); +static void pptp_ctrl_fini(pptp_ctrl *); + +/************************************************************************ + * pptp_ctrl インスタンス操作 + ************************************************************************/ +pptp_ctrl * +pptp_ctrl_create(void) +{ + pptp_ctrl *_this; + + if ((_this = malloc(sizeof(pptp_ctrl))) == NULL) + return NULL; + + return _this; +} + +int +pptp_ctrl_init(pptp_ctrl *_this) +{ + time_t curr_time; + + PPTP_CTRL_ASSERT(_this != NULL); + curr_time = get_monosec(); + memset(_this, 0, sizeof(pptp_ctrl)); + _this->id = pptp_ctrl_seqno++; + _this->sock = -1; + + if ((_this->recv_buf = bytebuffer_create(PPTP_BUFSIZ)) == NULL) { + pptp_ctrl_log(_this, LOG_ERR, "bytebuffer_create() failed at " + "%s(): %m", __func__); + goto reigai; + } + if ((_this->send_buf = bytebuffer_create(PPTP_BUFSIZ)) == NULL) { + pptp_ctrl_log(_this, LOG_ERR, "bytebuffer_create() failed at " + "%s(): %m", __func__); + goto reigai; + } + _this->last_rcv_ctrl = curr_time; + _this->last_snd_ctrl = curr_time; + _this->echo_seq = (random() << 16 )| (random() & 0xffff); + _this->echo_interval = PPTP_CTRL_DEFAULT_ECHO_INTERVAL; + _this->echo_timeout = PPTP_CTRL_DEFAULT_ECHO_TIMEOUT; + slist_init(&_this->call_list); + evtimer_set(&_this->ev_timer, pptp_ctrl_timeout, _this); + + return 0; +reigai: + return 1; +} + +int +pptp_ctrl_start(pptp_ctrl *_this) +{ + int ival; + char hbuf0[NI_MAXHOST], sbuf0[NI_MAXSERV]; + char hbuf1[NI_MAXHOST], sbuf1[NI_MAXSERV]; + struct sockaddr_storage sock; + socklen_t socklen; + + PPTP_CTRL_ASSERT(_this != NULL); + PPTP_CTRL_ASSERT(_this->sock >= 0); + + /* ログ用にアドレス=>文字列変換 */ + + strlcpy(hbuf0, "<unknown>", sizeof(hbuf0)); + strlcpy(sbuf0, "<unknown>", sizeof(sbuf0)); + strlcpy(hbuf1, "<unknown>", sizeof(hbuf1)); + strlcpy(sbuf1, "<unknown>", sizeof(sbuf1)); + if (getnameinfo((struct sockaddr *)&_this->peer, _this->peer.ss_len, + hbuf0, sizeof(hbuf0), sbuf0, sizeof(sbuf0), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + pptp_ctrl_log(_this, LOG_ERR, + "getnameinfo() failed at %s(): %m", __func__); + } + socklen = sizeof(sock); + if (getsockname(_this->sock, (struct sockaddr *)&sock, &socklen) != 0) { + pptp_ctrl_log(_this, LOG_ERR, + "getsockname() failed at %s(): %m", __func__); + goto reigai; + } + if (getnameinfo((struct sockaddr *)&sock, sock.ss_len, hbuf1, + sizeof(hbuf1), sbuf1, sizeof(sbuf1), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + pptp_ctrl_log(_this, LOG_ERR, + "getnameinfo() failed at %s(): %m", __func__); + } + pptp_ctrl_log(_this, LOG_INFO, "Starting peer=%s:%s/tcp " + "sock=%s:%s/tcp", hbuf0, sbuf0, hbuf1, sbuf1); + + if ((ival = fcntl(_this->sock, F_GETFL, 0)) < 0) { + pptp_ctrl_log(_this, LOG_ERR, + "fcntl(F_GET_FL) failed at %s(): %m", __func__); + goto reigai; + } else if (fcntl(_this->sock, F_SETFL, ival | O_NONBLOCK) < 0) { + pptp_ctrl_log(_this, LOG_ERR, + "fcntl(F_SET_FL) failed at %s(): %m", __func__); + goto reigai; + } + pptp_ctrl_set_io_event(_this); + pptp_ctrl_reset_timeout(_this); + + return 0; +reigai: + return 1; +} + +/** タイマー処理 */ +static void +pptp_ctrl_timeout(int fd, short event, void *ctx) +{ + int i; + pptp_call *call; + pptp_ctrl *_this; + time_t last, curr_time; + + _this = ctx; + curr_time = get_monosec(); + + PPTP_CTRL_DBG((_this, DEBUG_LEVEL_3, "enter %s()", __func__)); + /* call のクリーンアップ */ + i = 0; + while (i < slist_length(&_this->call_list)) { + call = slist_get(&_this->call_list, i); + if (call->state == PPTP_CALL_STATE_CLEANUP_WAIT && + curr_time - call->last_io > PPTP_CALL_CLEANUP_WAIT_TIME) { + pptp_call_stop(call); + pptp_call_destroy(call); + slist_remove(&_this->call_list, i); + } else + i++; + } + + /* ステートマシン、Timeout処理 */ + switch (_this->state) { + default: + case PPTP_CTRL_STATE_WAIT_CTRL_REPLY: + case PPTP_CTRL_STATE_IDLE: + if (curr_time - _this->last_rcv_ctrl > PPTPD_IDLE_TIMEOUT) { + pptp_ctrl_log(_this, LOG_ERR, + "Timeout in state %s", + pptp_ctrl_state_string(_this->state)); + pptp_ctrl_fini(_this); + return; + } + break; + case PPTP_CTRL_STATE_ESTABLISHED: + last = MAX(_this->last_rcv_ctrl, _this->last_snd_ctrl); + + if (curr_time - _this->last_rcv_ctrl + >= _this->echo_interval + _this->echo_timeout) { + pptp_ctrl_log(_this, LOG_INFO, + "Timeout waiting for echo reply"); + pptp_ctrl_fini(_this); + return; + } + if (curr_time - last >= _this->echo_interval) { + PPTP_CTRL_DBG((_this, LOG_DEBUG, "Echo")); + _this->echo_seq++; + pptp_ctrl_send_echo_req(_this); + } + break; + case PPTP_CTRL_STATE_WAIT_STOP_REPLY: + // お片付け + if (curr_time - _this->last_snd_ctrl > + PPTP_CTRL_StopCCRP_WAIT_TIME) { + pptp_ctrl_log(_this, LOG_WARNING, + "Timeout waiting for StopCCRP"); + pptp_ctrl_fini(_this); + return; + } + break; + case PPTP_CTRL_STATE_DISPOSING: + pptp_ctrl_fini(_this); + return; + } + pptp_ctrl_reset_timeout(_this); +} + +static void +pptp_ctrl_reset_timeout(pptp_ctrl *_this) +{ + struct timeval tv; + + switch (_this->state) { + case PPTP_CTRL_STATE_DISPOSING: + tv.tv_sec = 0; /* すぐに call back */ + tv.tv_usec = 0; + break; + default: + tv.tv_sec = PPTP_CTRL_TIMEOUT_IVAL_SEC; + tv.tv_usec = 0; + break; + } + evtimer_add(&_this->ev_timer, &tv); +} + +/** + * PPTPコントロールコネクションを終了します。 + * @result Stop-Control-Connection-Request(StopCCRQ) の result に指定する + * 値。0 を指定すると、StopCCRQ を送信せずに、終了します。また、 + * プロトコル上、StopCCRQ を送信する必要がない場合も送信しません。 + * @see ::#PPTP_StopCCRQ_REASON_STOP_PROTOCOL + * @see ::#PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN + */ +void +pptp_ctrl_stop(pptp_ctrl *_this, int result) +{ + int i; + pptp_call *call; + + switch (_this->state) { + case PPTP_CTRL_STATE_WAIT_STOP_REPLY: + // 返事待ち。pptp_ctrl_timeout で処理されます。 + break; + case PPTP_CTRL_STATE_ESTABLISHED: + if (result != 0) { + for (i = 0; i < slist_length(&_this->call_list); i++) { + call = slist_get(&_this->call_list, i); + pptp_call_disconnect(call, + PPTP_CDN_RESULT_ADMIN_SHUTDOWN, 0, NULL); + } + pptp_ctrl_send_StopCCRQ(_this, result); + _this->state = PPTP_CTRL_STATE_WAIT_STOP_REPLY; + break; + } + // FALL THROUGH + default: + pptp_ctrl_fini(_this); + } + return; +} + + +/** PPTP コントロールを終了化します。*/ +static void +pptp_ctrl_fini(pptp_ctrl *_this) +{ + pptp_call *call; + + PPTP_CTRL_ASSERT(_this != NULL); + + if (_this->sock >= 0) { + event_del(&_this->ev_sock); + close(_this->sock); + _this->sock = -1; + } + for (slist_itr_first(&_this->call_list); + slist_itr_has_next(&_this->call_list);) { + call = slist_itr_next(&_this->call_list); + pptp_call_stop(call); + pptp_call_destroy(call); + slist_itr_remove(&_this->call_list); + } + + if (_this->on_io_event != 0) { + /* + * I/O イベントハンドラ内での終了化処理は、最後まで行うと + * 例外処理が複雑になるので、途中までしか行わす、続きは、 + * タイマイベントハンドラで行う。 + */ + PPTP_CTRL_DBG((_this, LOG_DEBUG, "Disposing")); + _this->state = PPTP_CTRL_STATE_DISPOSING; + pptp_ctrl_reset_timeout(_this); + return; + } + + evtimer_del(&_this->ev_timer); + slist_fini(&_this->call_list); + + pptp_ctrl_log (_this, LOG_NOTICE, "logtype=Finished"); + + /* この後 _this は使用できない */ + pptpd_ctrl_finished_notify(_this->pptpd, _this); +} + +/* PPTP コントロールコンテキストを解放します。*/ +void +pptp_ctrl_destroy(pptp_ctrl *_this) +{ + if (_this->send_buf != NULL) { + bytebuffer_destroy(_this->send_buf); + _this->send_buf = NULL; + } + if (_this->recv_buf != NULL) { + bytebuffer_destroy(_this->recv_buf); + _this->recv_buf = NULL; + } + free(_this); +} + +/************************************************************************ + * ネットワーク I/O + ************************************************************************/ +/** I/O イベントディスパッチャ */ +static void +pptp_ctrl_io_event(int fd, short evmask, void *ctx) +{ + int sz, lpkt, hdrlen; + u_char *pkt; + pptp_ctrl *_this; + + _this = ctx; + PPTP_CTRL_ASSERT(_this != NULL); + + _this->on_io_event = 1; + if ((evmask & EV_WRITE) != 0) { + if (pptp_ctrl_output_flush(_this) != 0 || + _this->state == PPTP_CTRL_STATE_DISPOSING) + goto reigai; + _this->send_ready = 1; + } + if ((evmask & EV_READ) != 0) { + sz = read(_this->sock, bytebuffer_pointer(_this->recv_buf), + bytebuffer_remaining(_this->recv_buf)); + if (sz <= 0) { + if (errno == ECONNRESET || sz == 0) { + pptp_ctrl_log(_this, LOG_INFO, + "Connection closed by foreign host"); + pptp_ctrl_fini(_this); + goto reigai; + } else if (errno != EAGAIN && errno != EINTR) { + pptp_ctrl_log(_this, LOG_INFO, + "read() failed at %s(): %m", __func__); + pptp_ctrl_fini(_this); + goto reigai; + } + } + bytebuffer_put(_this->recv_buf, BYTEBUFFER_PUT_DIRECT, sz); + bytebuffer_flip(_this->recv_buf); + + for (;;) { + pkt = bytebuffer_pointer(_this->recv_buf); + lpkt = bytebuffer_remaining(_this->recv_buf); + if (pkt == NULL || + lpkt < sizeof(struct pptp_ctrl_header)) + break; /* read again */ + + hdrlen = pkt[0] << 8 | pkt[1]; + if (lpkt < hdrlen) + break; /* read again */ + + bytebuffer_get(_this->recv_buf, NULL, hdrlen); + if (pptp_ctrl_input(_this, pkt, hdrlen) != 0 || + _this->state == PPTP_CTRL_STATE_DISPOSING) { + bytebuffer_compact(_this->recv_buf); + goto reigai; + } + } + bytebuffer_compact(_this->recv_buf); + } + if (pptp_ctrl_output_flush(_this) != 0) + goto reigai; + pptp_ctrl_set_io_event(_this); +reigai: + _this->on_io_event = 0; +} + + +/** イベントマスクを設定する */ +static void +pptp_ctrl_set_io_event(pptp_ctrl *_this) +{ + int evmask; + + PPTP_CTRL_ASSERT(_this != NULL); + PPTP_CTRL_ASSERT(_this->sock >= 0); + + evmask = 0; + if (bytebuffer_remaining(_this->recv_buf) > 128) + evmask |= EV_READ; + if (_this->send_ready == 0) + evmask |= EV_WRITE; + + event_del(&_this->ev_sock); + if (evmask != 0) { + event_set(&_this->ev_sock, _this->sock, evmask, + pptp_ctrl_io_event, _this); + event_add(&_this->ev_sock, NULL); + } +} + +/** + * PPTPコントロールパケットを出力します。 + * @param pkt パケットの領域へのポインタ。 + * bytebuffer を使って、_this->send_buf に追記している場合には、 + * NULL を指定します。 + * @param lpkt パケットの長さ。 + */ +void +pptp_ctrl_output(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + PPTP_CTRL_ASSERT(_this != NULL); + PPTP_CTRL_ASSERT(lpkt > 0); + + bytebuffer_put(_this->send_buf, pkt, lpkt); + /* 実際には書くのは後 */ + + if (_this->on_io_event != 0) { + /* pptp_ctrl_io_event で pptp_ctrl_output_flush を呼び出し */ + } else { + /* + * I/O イベントハンドラからの呼出しではない場合は、 + * pptp_ctrl_output_flush() を呼び出す必要があるが flush => + * write 失敗 => finalize となると、この関数呼び出し側に例外 + * 処理を実装し煩雑になるので、write ready イベントで発行し + * てもらって、そこで flush。 + */ + _this->send_ready = 0; + pptp_ctrl_set_io_event(_this); + } + + return; +} + +/** Stop-Control-Connection-Request の送信 */ + +/** 実際にパケット送信 */ +static int +pptp_ctrl_output_flush(pptp_ctrl *_this) +{ + int sz; + time_t curr_time; + + curr_time = get_monosec(); + + if (bytebuffer_position(_this->send_buf) <= 0) + return 0; // nothing to write + if (_this->send_ready == 0) { + pptp_ctrl_set_io_event(_this); + return 0; // not ready to write + } + + bytebuffer_flip(_this->send_buf); + + if (_this->pptpd->ctrl_out_pktdump != 0) { + pptp_ctrl_log(_this, LOG_DEBUG, "PPTP Control output packet"); + show_hd(debug_get_debugfp(), + bytebuffer_pointer(_this->send_buf), + bytebuffer_remaining(_this->send_buf)); + } + if ((sz = write(_this->sock, bytebuffer_pointer(_this->send_buf), + bytebuffer_remaining(_this->send_buf))) < 0) { + pptp_ctrl_log(_this, LOG_ERR, "write to socket failed: %m"); + pptp_ctrl_fini(_this); + + return 1; + } + _this->last_snd_ctrl = curr_time; + bytebuffer_get(_this->send_buf, NULL, sz); + bytebuffer_compact(_this->send_buf); + _this->send_ready = 0; + + return 0; +} + +/** Start-Control-Connection-Request、-Reply パケットを文字列で表現する */ +static void +pptp_ctrl_SCCRx_string(struct pptp_scc *scc, u_char *buf, int lbuf) +{ + char buf1[128], buf2[128], buf3[128]; + + // 64バイトギリギリまで入っている場合があるので + strlcpy(buf1, scc->host_name, sizeof(buf1)); + strlcpy(buf2, scc->vendor_string, sizeof(buf2)); + + if (scc->result_code != 0) + snprintf(buf3, sizeof(buf3), "result=%d error=%d ", + scc->result_code, scc->error_code); + else + buf3[0] = '\0'; + + snprintf(buf, lbuf, + "protocol_version=%d.%d %sframing=%s bearer=%s max_channels=%d " + "firmware_revision=%d(0x%04x) host_name=\"%s\" " + "vendor_string=\"%s\"", + scc->protocol_version >> 8, scc->protocol_version & 0xff, buf3, + pptp_framing_string(scc->framing_caps), + pptp_bearer_string(scc->bearer_caps), scc->max_channels, + scc->firmware_revision, scc->firmware_revision, buf1, buf2); +} + +/** Start-Control-Connection-Request を受信 */ +static int +pptp_ctrl_recv_SCCRQ(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + char logbuf[512]; + struct pptp_scc *scc; + + // サイズ検査 + if (lpkt < sizeof(struct pptp_scc)) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: packet too " + "short: %d < %d", lpkt, (int)sizeof(struct pptp_scc)); + return 1; + } + scc = (struct pptp_scc *)pkt; + + // バイトオーダー + scc->protocol_version = ntohs(scc->protocol_version); + scc->framing_caps = htonl(scc->framing_caps); + scc->bearer_caps = htonl(scc->bearer_caps); + scc->max_channels = htons(scc->max_channels); + scc->firmware_revision = htons(scc->firmware_revision); + + // プロトコルバージョン + if (scc->protocol_version != PPTP_RFC_2637_VERSION) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: " + "unknown protocol version %d", scc->protocol_version); + return 1; + } + + pptp_ctrl_SCCRx_string(scc, logbuf, sizeof(logbuf)); + pptp_ctrl_log(_this, LOG_INFO, "RecvSCCRQ %s", logbuf); + + return 0; +} + +/** Stop-Control-Connection-Reply の受信 */ +static int +pptp_ctrl_recv_StopCCRP(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + struct pptp_stop_ccrp *stop_ccrp; + + if (lpkt < sizeof(struct pptp_stop_ccrp)) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad StopCCRP: packet " + "too short: %d < %d", lpkt, + (int)sizeof(struct pptp_stop_ccrp)); + return 1; + } + stop_ccrp = (struct pptp_stop_ccrp *)pkt; + + pptp_ctrl_log(_this, LOG_INFO, "RecvStopCCRP reason=%s(%u)", + pptp_StopCCRP_result_string(stop_ccrp->result), stop_ccrp->result); + + return 0; +} + +static int +pptp_ctrl_send_StopCCRQ(pptp_ctrl *_this, int reason) +{ + int lpkt; + struct pptp_stop_ccrq *stop_ccrq; + + stop_ccrq = bytebuffer_pointer(_this->send_buf); + lpkt = bytebuffer_remaining(_this->send_buf); + if (lpkt < sizeof(struct pptp_stop_ccrq)) { + pptp_ctrl_log(_this, LOG_ERR, + "SendCCRP failed: No buffer space available"); + return -1; + } + memset(stop_ccrq, 0, sizeof(struct pptp_stop_ccrq)); + + pptp_init_header(&stop_ccrq->header, sizeof(struct pptp_stop_ccrq), + PPTP_CTRL_MES_CODE_StopCCRQ); + + stop_ccrq->reason = reason; + + pptp_ctrl_log(_this, LOG_INFO, "SendStopCCRQ reason=%s(%u)", + pptp_StopCCRQ_reason_string(stop_ccrq->reason), stop_ccrq->reason); + + pptp_ctrl_output(_this, NULL, sizeof(struct pptp_stop_ccrq)); + + return 0; +} + +/** Stop-Control-Connection-Request を受信 */ +static int +pptp_ctrl_recv_StopCCRQ(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + struct pptp_stop_ccrq *stop_ccrq; + + if (lpkt < sizeof(struct pptp_stop_ccrq)) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad StopCCRQ: packet " + "too short: %d < %d", lpkt, + (int)sizeof(struct pptp_stop_ccrq)); + return 1; + } + stop_ccrq = (struct pptp_stop_ccrq *)pkt; + + pptp_ctrl_log(_this, LOG_INFO, "RecvStopCCRQ reason=%s(%u)", + pptp_StopCCRQ_reason_string(stop_ccrq->reason), stop_ccrq->reason); + + return 0; +} + + + +/** Stop-Control-Connection-Reply を送信 */ +static int +pptp_ctrl_send_StopCCRP(pptp_ctrl *_this, int result, int error) +{ + int lpkt; + struct pptp_stop_ccrp *stop_ccrp; + + stop_ccrp = bytebuffer_pointer(_this->send_buf); + + lpkt = bytebuffer_remaining(_this->send_buf); + if (lpkt < sizeof(struct pptp_stop_ccrp)) { + pptp_ctrl_log(_this, LOG_ERR, + "SendCCRQ failed: No buffer space available"); + return -1; + } + memset(stop_ccrp, 0, sizeof(struct pptp_stop_ccrp)); + + pptp_init_header(&stop_ccrp->header, sizeof(struct pptp_stop_ccrp), + PPTP_CTRL_MES_CODE_StopCCRP); + + stop_ccrp->result = result; + stop_ccrp->error = error; + + pptp_ctrl_log(_this, LOG_INFO, + "SendStopCCRP result=%s(%u) error=%s(%u)", + pptp_StopCCRP_result_string(stop_ccrp->result), stop_ccrp->result, + pptp_general_error_string(stop_ccrp->error), stop_ccrp->error); + + pptp_ctrl_output(_this, NULL, sizeof(struct pptp_stop_ccrp)); + + return 0; +} + +/** Start-Control-Connection-Reply を送信 */ +static int +pptp_ctrl_send_SCCRP(pptp_ctrl *_this, int result, int error) +{ + int lpkt; + struct pptp_scc *scc; + char logbuf[512]; + const char *val; + + scc = bytebuffer_pointer(_this->send_buf); + lpkt = bytebuffer_remaining(_this->send_buf); + if (lpkt < sizeof(struct pptp_scc)) { + pptp_ctrl_log(_this, LOG_ERR, + "SendSCCRP failed: No buffer space available"); + return -1; + } + memset(scc, 0, sizeof(struct pptp_scc)); + + pptp_init_header(&scc->header, sizeof(struct pptp_scc), + PPTP_CTRL_MES_CODE_SCCRP); + + scc->protocol_version = PPTP_RFC_2637_VERSION; + scc->result_code = result; + scc->error_code = error; + // 同期フレームしかサポートせず。 + //scc->framing_caps = PPTP_CTRL_FRAMING_ASYNC; + scc->framing_caps = PPTP_CTRL_FRAMING_SYNC; + scc->bearer_caps = PPTP_CTRL_BEARER_DIGITAL; + + scc->max_channels = 4; // XX 設定? + scc->firmware_revision = MAJOR_VERSION << 8 | MINOR_VERSION; + + // 63文字で切れても気にしない + + // ホスト名 + if ((val = pptp_ctrl_config_str(_this, "pptp.host_name")) == NULL) + val = ""; + strlcpy(scc->host_name, val, sizeof(scc->host_name)); + + // ベンダ名 + if ((val = pptp_ctrl_config_str(_this, "pptp.vendor_name")) == NULL) + val = PPTPD_DEFAULT_VENDOR_NAME; + + strlcpy(scc->vendor_string, val, sizeof(scc->vendor_string)); + + pptp_ctrl_SCCRx_string(scc, logbuf, sizeof(logbuf)); + pptp_ctrl_log(_this, LOG_INFO, "SendSCCRP %s", logbuf); + + scc->protocol_version = htons(scc->protocol_version); + scc->framing_caps = htonl(scc->framing_caps); + scc->bearer_caps = htonl(scc->bearer_caps); + scc->max_channels = htons(scc->max_channels); + scc->firmware_revision = htons(scc->firmware_revision); + + pptp_ctrl_output(_this, NULL, sizeof(struct pptp_scc)); + + return 0; +} + +/** ECHO の受信 => 返信 */ +static void +pptp_ctrl_process_echo_req(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + struct pptp_echo_rq *echo_rq; + struct pptp_echo_rp *echo_rp; + + if (lpkt < sizeof(struct pptp_echo_rq)) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReq: packet " + "too short: %d < %d", lpkt, + (int)sizeof(struct pptp_echo_rq)); + return; + } + echo_rq = (struct pptp_echo_rq *)pkt; + + PPTP_CTRL_DBG((_this, LOG_DEBUG, "RecvEchoReq")); + + echo_rp = bytebuffer_pointer(_this->send_buf); + lpkt = bytebuffer_remaining(_this->send_buf); + if (echo_rp == NULL || lpkt < sizeof(struct pptp_echo_rp)) { + pptp_ctrl_log(_this, LOG_ERR, + "Failed to send EchoReq: No buffer space available"); + return; + } + memset(echo_rp, 0, sizeof(struct pptp_echo_rp)); + + pptp_init_header(&echo_rp->header, sizeof(struct pptp_echo_rp), + PPTP_CTRL_MES_CODE_ECHO_RP); + + echo_rp->identifier = echo_rq->identifier; + echo_rp->result_code = PPTP_ECHO_RP_RESULT_OK; + echo_rp->error_code = PPTP_ERROR_NONE; + echo_rp->reserved1 = htons(0); + + pptp_ctrl_output(_this, NULL, sizeof(struct pptp_echo_rp)); + PPTP_CTRL_DBG((_this, LOG_DEBUG, "SendEchoReply")); +} + +/** Echo-Reply の受信 */ +static int +pptp_ctrl_recv_echo_rep(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + struct pptp_echo_rp *echo_rp; + + if (lpkt < sizeof(struct pptp_echo_rp)) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReq: packet " + "too short: %d < %d", lpkt, + (int)sizeof(struct pptp_echo_rp)); + return 1; + } + echo_rp = (struct pptp_echo_rp *)pkt; + + if (echo_rp->result_code != PPTP_ECHO_RP_RESULT_OK) { + pptp_ctrl_log(_this, LOG_ERR, "Received negative EchoReply: %s", + pptp_general_error_string(echo_rp->error_code)); + return 1; + } + if (_this->echo_seq != ntohl(echo_rp->identifier)) { + pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReply: " + "Identifier mismatch sent=%u recv=%u", + _this->echo_seq , ntohl(echo_rp->identifier)); + return 1; + } + PPTP_CTRL_DBG((_this, LOG_DEBUG, "RecvEchoReply")); + return 0; +} + +/** Echo-Request の送信 */ +static void +pptp_ctrl_send_echo_req(pptp_ctrl *_this) +{ + int lpkt; + struct pptp_echo_rq *echo_rq; + + echo_rq = (struct pptp_echo_rq *)bytebuffer_pointer(_this->send_buf); + lpkt = bytebuffer_remaining(_this->send_buf); + if (echo_rq == NULL || lpkt < sizeof(struct pptp_echo_rq)) { + pptp_ctrl_log(_this, LOG_ERR, + "SendEchoReq failed: No buffer space available"); + return; + } + memset(echo_rq, 0, sizeof(struct pptp_echo_rq)); + + pptp_init_header(&echo_rq->header, sizeof(struct pptp_echo_rq), + PPTP_CTRL_MES_CODE_ECHO_RQ); + + echo_rq->identifier = htonl(_this->echo_seq); + + pptp_ctrl_output(_this, NULL, sizeof(struct pptp_echo_rq)); + PPTP_CTRL_DBG((_this, LOG_DEBUG, "SendEchoReq")); +} + +/* Call-Disconnect-Notify の送信 */ +static void +pptp_ctrl_send_CDN(pptp_ctrl *_this, int result, int error, int cause, + const char *statistics) +{ + int lpkt; + struct pptp_cdn *cdn; + + cdn = bytebuffer_pointer(_this->send_buf); + lpkt = bytebuffer_remaining(_this->send_buf); + if (lpkt < sizeof(struct pptp_cdn)) { + pptp_ctrl_log(_this, LOG_ERR, + "SendCCR failed: No buffer space available"); + return; + } + memset(cdn, 0, sizeof(struct pptp_cdn)); + + pptp_init_header(&cdn->header, sizeof(struct pptp_cdn), + PPTP_CTRL_MES_CODE_CDN); + + cdn->call_id = _this->id; + cdn->result_code = result; + cdn->error_code = error; + cdn->cause_code = cause; + if (statistics != NULL) + strlcpy(cdn->statistics, statistics, sizeof(cdn->statistics)); + + cdn->call_id = htons(cdn->call_id); + cdn->cause_code = htons(cdn->cause_code); + + pptp_ctrl_output(_this, NULL, sizeof(struct pptp_cdn)); +} + +/** コントロールパケット受信 */ +static int +pptp_ctrl_input(pptp_ctrl *_this, u_char *pkt, int lpkt) +{ + char errmes[256]; + time_t curr_time; + struct pptp_ctrl_header *hdr; + + PPTP_CTRL_ASSERT(lpkt >= sizeof(struct pptp_ctrl_header)); + + curr_time = get_monosec(); + hdr = (struct pptp_ctrl_header *)pkt; + + // バイトオーダー + hdr->length = ntohs(hdr->length); + hdr->pptp_message_type = ntohs(hdr->pptp_message_type); + hdr->magic_cookie = ntohl(hdr->magic_cookie); + hdr->control_message_type = ntohs(hdr->control_message_type); + hdr->reserved0 = ntohs(hdr->reserved0); + + // 長さ検査 + PPTP_CTRL_ASSERT(hdr->length <= lpkt); + + _this->last_rcv_ctrl = curr_time; + + if (_this->pptpd->ctrl_in_pktdump != 0) { + pptp_ctrl_log(_this, LOG_DEBUG, + "PPTP Control input packet dump: mestype=%s(%d)", + pptp_ctrl_mes_type_string(hdr->control_message_type), + hdr->control_message_type); + show_hd(debug_get_debugfp(), pkt, lpkt); + } + + /* パケット検査 */ + // メッセージタイプ + if (hdr->pptp_message_type != PPTP_MES_TYPE_CTRL) { + snprintf(errmes, sizeof(errmes), "unknown message type %d", + hdr->pptp_message_type); + goto bad_packet; + } + // マジッククッキー + if (hdr->magic_cookie != PPTP_MAGIC_COOKIE) { + snprintf(errmes, sizeof(errmes), "wrong magic %08x != %08x", + hdr->magic_cookie, PPTP_MAGIC_COOKIE); + goto bad_packet; + } + + // ECHO Reply は別処理。ステートが交錯する可能性があるので。*/ + switch (hdr->control_message_type) { + case PPTP_CTRL_MES_CODE_ECHO_RP: + if (pptp_ctrl_recv_echo_rep(_this, pkt, lpkt) != 0) { + pptp_ctrl_fini(_this); + return 1; + } + return 0; + } + /* + * ステートマシン + * - 正常に処理が終わったら、return する。 + */ + switch (_this->state) { + case PPTP_CTRL_STATE_IDLE: + switch (hdr->control_message_type) { + case PPTP_CTRL_MES_CODE_SCCRQ: + if (pptp_ctrl_recv_SCCRQ(_this, pkt, lpkt) != 0) { + return 0; + } + if (pptp_ctrl_send_SCCRP(_this, + PPTP_SCCRP_RESULT_SUCCESS, PPTP_ERROR_NONE) != 0) { + return 0; + } + _this->state = PPTP_CTRL_STATE_ESTABLISHED; + return 0; + default: + break; + } + break; + case PPTP_CTRL_STATE_ESTABLISHED: + switch (hdr->control_message_type) { + case PPTP_CTRL_MES_CODE_ECHO_RQ: + pptp_ctrl_process_echo_req(_this, pkt, lpkt); + return 0; + //コール関連パケットは、pptp_call_input にディスパッチ + case PPTP_CTRL_MES_CODE_SLI: + case PPTP_CTRL_MES_CODE_ICRQ: + case PPTP_CTRL_MES_CODE_ICRP: + case PPTP_CTRL_MES_CODE_OCRQ: + case PPTP_CTRL_MES_CODE_OCRP: + case PPTP_CTRL_MES_CODE_ICCN: + case PPTP_CTRL_MES_CODE_CDN: + case PPTP_CTRL_MES_CODE_CCR: + return pptp_ctrl_call_input(_this, + hdr->control_message_type, pkt, lpkt); + case PPTP_CTRL_MES_CODE_StopCCRQ: + if (pptp_ctrl_recv_StopCCRQ(_this, pkt, lpkt) != 0) { + pptp_ctrl_stop(_this, + PPTP_StopCCRQ_REASON_STOP_PROTOCOL); + return 0; + } + if (pptp_ctrl_send_StopCCRP(_this, + PPTP_StopCCRP_RESULT_OK, PPTP_ERROR_NONE)!= 0) { + return 0; + } + pptp_ctrl_fini(_this); + return 1; + default: + break; + } + case PPTP_CTRL_STATE_WAIT_STOP_REPLY: + switch (hdr->control_message_type) { + case PPTP_CTRL_MES_CODE_StopCCRP: + pptp_ctrl_recv_StopCCRP(_this, pkt, lpkt); + pptp_ctrl_fini(_this); + return 1; + } + break; + case PPTP_CTRL_STATE_WAIT_CTRL_REPLY: + // PAC の実装だけなので + break; + } + pptp_ctrl_log(_this, LOG_WARNING, + "Unhandled control message type=%s(%d)", + pptp_ctrl_mes_type_string(hdr->control_message_type), + hdr->control_message_type); + return 0; + +bad_packet: + pptp_ctrl_log(_this, LOG_ERR, "Received bad packet: %s", errmes); + pptp_ctrl_stop(_this, PPTP_StopCCRQ_REASON_STOP_PROTOCOL); + + return 0; +} + +/** PPTP Call 関連のメッセージを受信 */ +static int +pptp_ctrl_call_input(pptp_ctrl *_this, int mes_type, u_char *pkt, int lpkt) +{ + int i, call_id, lpkt0; + pptp_call *call; + const char *reason; + u_char *pkt0; + + pkt0 = pkt; + lpkt0 = lpkt; + call_id = -1; + pkt += sizeof(struct pptp_ctrl_header); + lpkt -= sizeof(struct pptp_ctrl_header); + reason = "(no reason)"; + + // callId + if (lpkt < 4) { + reason = "received packet is too short"; + goto badpacket; + } + call = NULL; + call_id = ntohs(*(uint16_t *)pkt); + + switch (mes_type) { + case PPTP_CTRL_MES_CODE_SLI: /* PNS <=> PAC */ + /* SLI だけは、こちらの Call-ID が入っている */ + for (i = 0; i < slist_length(&_this->call_list); i++) { + call = slist_get(&_this->call_list, i); + if (call->id == call_id) + break; + call = NULL; + } + if (call == NULL) { + reason = "Call Id is not associated by this control"; + goto badpacket; + } + goto call_searched; + case PPTP_CTRL_MES_CODE_ICRP: /* PNS => PAC */ + /* + * ICRQ は投げないのでこのメッセージは受信しないが、いちおう + * pptp_call.c 側で処理させる + */ + // FALL THROUGH + case PPTP_CTRL_MES_CODE_OCRQ: /* PNS => PAC */ + case PPTP_CTRL_MES_CODE_CCR: /* PNS => PAC */ + // リニアサーチでよい。 + for (i = 0; i < slist_length(&_this->call_list); i++) { + call = slist_get(&_this->call_list, i); + if (call->peers_call_id == call_id) + break; + call = NULL; + } + if (call == NULL && mes_type == PPTP_CTRL_MES_CODE_CCR) { + pptp_ctrl_send_CDN(_this, PPTP_CDN_RESULT_GENRIC_ERROR, + PPTP_ERROR_BAD_CALL, 0, NULL); + goto call_searched; + } + if (mes_type == PPTP_CTRL_MES_CODE_OCRQ) { + // 新しい Call を作成 + if (call != NULL) { + pptp_call_input(call, mes_type, pkt0, lpkt0); + return 0; + } + if ((call = pptp_call_create()) == NULL) { + pptp_ctrl_log(_this, LOG_ERR, + "pptp_call_create() failed: %m"); + goto reigai; + } + if (pptp_call_init(call, _this) != 0) { + pptp_ctrl_log(_this, LOG_ERR, + "pptp_call_init() failed: %m"); + pptp_call_destroy(call); + goto reigai; + } + slist_add(&_this->call_list, call); + } +call_searched: + if (call == NULL) { + reason = "Call Id is not associated by this control"; + goto badpacket; + } + pptp_call_input(call, mes_type, pkt0, lpkt0); + return 0; + case PPTP_CTRL_MES_CODE_OCRP: /* PAC => PNS */ + case PPTP_CTRL_MES_CODE_ICRQ: /* PAC => PNS */ + case PPTP_CTRL_MES_CODE_ICCN: /* PAC => PNS */ + case PPTP_CTRL_MES_CODE_CDN: /* PAC => PNS */ + /* 以上 PNS 用なので、受信しない */ + default: + break; + } + reason = "Message type is unexpected."; + // FALL THROUGH +badpacket: + pptp_ctrl_log(_this, LOG_INFO, + "Received a bad %s(%d) call_id=%d: %s", + pptp_ctrl_mes_type_string(mes_type), mes_type, call_id, reason); + return 0; +reigai: + pptp_ctrl_stop(_this, PPTP_StopCCRQ_REASON_STOP_PROTOCOL); + return 0; +} + + +/************************************************************************ + * 雑多な関数 + ************************************************************************/ + +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static void +pptp_ctrl_log(pptp_ctrl *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); +#ifdef PPTPD_MULITPLE + snprintf(logbuf, sizeof(logbuf), "pptpd id=%u ctrl=%u %s", + _this->pptpd->id, _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "pptpd ctrl=%u %s", _this->id, fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +static const char * +pptp_ctrl_state_string(int state) +{ + switch (state) { + case PPTP_CTRL_STATE_IDLE: + return "idle"; + case PPTP_CTRL_STATE_WAIT_CTRL_REPLY: + return "wait-ctrl-reply"; + case PPTP_CTRL_STATE_ESTABLISHED: + return "established"; + case PPTP_CTRL_STATE_WAIT_STOP_REPLY: + return "wait-stop-reply"; + } + return "unknown"; +} diff --git a/usr.sbin/npppd/pptp/pptp_local.h b/usr.sbin/npppd/pptp/pptp_local.h new file mode 100644 index 00000000000..1cd6e3755dd --- /dev/null +++ b/usr.sbin/npppd/pptp/pptp_local.h @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PPTP_LOCAL_H +#define PPTP_LOCAL_H 1 + +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#endif + +struct pptp_gre_header { +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t recur:3, + s:1, + S:1, + K:1, + R:1, + C:1; + uint8_t ver:3, + flags:4, + A:1; +#else + uint8_t C:1, + R:1, + K:1, + S:1, + s:1, + recur:3; + uint8_t A:1, + flags:4, + ver:3; +#endif + uint16_t protocol_type; + uint16_t payload_length; + uint16_t call_id; +} __attribute__((__packed__)); + + +/** PPTPコントロールパケットの(ヘッダ)共通部分 */ +struct pptp_ctrl_header { + uint16_t length; + uint16_t pptp_message_type; + uint32_t magic_cookie; + uint16_t control_message_type; + uint16_t reserved0; +} __attribute__((__packed__)); + +/** Start-Control-Connection-{Request,Reply} パケット */ +struct pptp_scc { + struct pptp_ctrl_header header; + uint16_t protocol_version; + uint8_t result_code; + uint8_t error_code; + uint32_t framing_caps; + uint32_t bearer_caps; + uint16_t max_channels; + uint16_t firmware_revision; + u_char host_name[64]; + u_char vendor_string[64]; +} __attribute__((__packed__)); + +/** Outgoing-Call-Request パケット */ +struct pptp_ocrq { + struct pptp_ctrl_header header; + uint16_t call_id; + uint16_t call_serial_number; + uint32_t maximum_bps; + uint32_t minimum_bps; + uint32_t bearer_type; + uint32_t framing_type; + uint16_t recv_winsz; + uint16_t packet_proccessing_delay; + uint16_t phone_number_length; + uint16_t reservied1; + u_char phone_number[64]; + u_char subaddress[64]; +} __attribute__((__packed__)); + +/** Outgoing-Call-Reply パケット */ +struct pptp_ocrp { + struct pptp_ctrl_header header; + uint16_t call_id; + uint16_t peers_call_id; + uint8_t result_code; + uint8_t error_code; + uint16_t cause_code; + uint32_t connect_speed; + uint16_t recv_winsz; + uint16_t packet_proccessing_delay; + uint32_t physical_channel_id; +} __attribute__((__packed__)); + +/** Echo-Request パケット */ +struct pptp_echo_rq { + struct pptp_ctrl_header header; + uint32_t identifier; +} __attribute__((__packed__)); + +/** Echo-Reply パケット */ +struct pptp_echo_rp { + struct pptp_ctrl_header header; + uint32_t identifier; + uint8_t result_code; + uint8_t error_code; + uint16_t reserved1; +} __attribute__((__packed__)); + +/** Set-Link-Info パケット */ +struct pptp_sli { + struct pptp_ctrl_header header; + uint16_t peers_call_id; + uint16_t reserved; + uint32_t send_accm; + uint32_t recv_accm; +} __attribute__((__packed__)); + +struct pptp_stop_ccrq { + struct pptp_ctrl_header header; + uint8_t reason; + uint8_t reserved1; + uint16_t reserved2; +} __attribute__((__packed__)); + +struct pptp_stop_ccrp { + struct pptp_ctrl_header header; + uint8_t result; + uint8_t error; + uint16_t reserved2; +} __attribute__((__packed__)); + + +/** Call-Clear-Request パケット */ +struct pptp_ccr { + struct pptp_ctrl_header header; + uint16_t call_id; + uint16_t reserved1; +} __attribute__((__packed__)); + +/** Call-Disconnect-Notify パケット */ +struct pptp_cdn { + struct pptp_ctrl_header header; + uint16_t call_id; + uint8_t result_code; + uint8_t error_code; + uint16_t cause_code; + uint16_t reserved1; + char statistics[128]; +} __attribute__((__packed__)); + + +#define PPTP_GRE_PKT_SEQ_PRESENT 0x0001 +#define PPTP_GRE_PKT_ACK_PRESENT 0x0002 + +#endif + diff --git a/usr.sbin/npppd/pptp/pptp_subr.c b/usr.sbin/npppd/pptp/pptp_subr.c new file mode 100644 index 00000000000..370fab840fd --- /dev/null +++ b/usr.sbin/npppd/pptp/pptp_subr.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/**@file + * PPTPの補助的な関数 + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <event.h> + +#include "bytebuf.h" +#include "hash.h" +#include "slist.h" + +#include "pptp.h" +#include "pptp_local.h" +#include "pptp_subr.h" + +/** Faming Capability ビットをカンマ区切りの文字列で返す */ +const char * +pptp_framing_string(uint32_t bits) +{ + static char framing_cap_buf[40]; + + snprintf(framing_cap_buf, sizeof(framing_cap_buf), "%s%s", + (bits & PPTP_CTRL_FRAMING_ASYNC)? ",async" : "", + (bits & PPTP_CTRL_FRAMING_SYNC)? ",sync" : ""); + + if (strlen(framing_cap_buf) > 0) + return &framing_cap_buf[1]; + + return ""; +} + +/** Bearer Capability ビットをカンマ区切りの文字列で返す */ +const char * +pptp_bearer_string(uint32_t bits) +{ + static char bearer_cap_buf[40]; + + snprintf(bearer_cap_buf, sizeof(bearer_cap_buf), "%s%s", + (bits &PPTP_CTRL_BEARER_ANALOG)? ",analog" : "", + (bits &PPTP_CTRL_BEARER_DIGITAL)? ",digital" : ""); + + if (strlen(bearer_cap_buf) > 0) + return &bearer_cap_buf[1]; + + return ""; +} + +/** コントロールパケットのヘッダ(共通部分)を作成する */ +void +pptp_init_header(struct pptp_ctrl_header *header, int length, int ctrl_mes_type) +{ + header->length = htons(length); + header->pptp_message_type = htons(PPTP_MES_TYPE_CTRL); + header->magic_cookie = htonl(PPTP_MAGIC_COOKIE); + header->control_message_type = htons(ctrl_mes_type); + header->reserved0 = 0; +} + +#define NAME_VAL(x) { x, #x } +static struct _label_name { + int label; + const char *name; +} pptp_StopCCRQ_reason_labels[] = { + NAME_VAL(PPTP_StopCCRQ_REASON_NONE), + NAME_VAL(PPTP_StopCCRQ_REASON_STOP_PROTOCOL), + NAME_VAL(PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN), +}, pptp_StopCCRP_result_labels[] = { + NAME_VAL(PPTP_StopCCRP_RESULT_OK), + NAME_VAL(PPTP_StopCCRP_RESULT_GENERIC_ERROR), +}, pptp_general_error_labels[] = { + NAME_VAL(PPTP_ERROR_NONE), + NAME_VAL(PPTP_ERROR_NOT_CONNECTED), + NAME_VAL(PPTP_ERROR_BAD_FORMAT), + NAME_VAL(PPTP_ERROR_NO_RESOURCE), + NAME_VAL(PPTP_ERROR_BAD_CALL), +}, pptp_ctrl_mes_type_labels[] = { + NAME_VAL(PPTP_CTRL_MES_CODE_SCCRQ), + NAME_VAL(PPTP_CTRL_MES_CODE_SCCRP), + NAME_VAL(PPTP_CTRL_MES_CODE_StopCCRQ), + NAME_VAL(PPTP_CTRL_MES_CODE_StopCCRP), + NAME_VAL(PPTP_CTRL_MES_CODE_ECHO_RQ), + NAME_VAL(PPTP_CTRL_MES_CODE_ECHO_RP), + NAME_VAL(PPTP_CTRL_MES_CODE_OCRQ), + NAME_VAL(PPTP_CTRL_MES_CODE_OCRP), + NAME_VAL(PPTP_CTRL_MES_CODE_ICRQ), + NAME_VAL(PPTP_CTRL_MES_CODE_ICRP), + NAME_VAL(PPTP_CTRL_MES_CODE_ICCN), + NAME_VAL(PPTP_CTRL_MES_CODE_CCR), + NAME_VAL(PPTP_CTRL_MES_CODE_CDN), + NAME_VAL(PPTP_CTRL_MES_CODE_SLI), +}, pptp_CDN_result_labels[] = { + NAME_VAL(PPTP_CDN_RESULT_LOST_CARRIER), + NAME_VAL(PPTP_CDN_RESULT_GENRIC_ERROR), + NAME_VAL(PPTP_CDN_RESULT_ADMIN_SHUTDOWN), + NAME_VAL(PPTP_CDN_RESULT_REQUEST), +}; +// _label_name を使ったマクロ。値を渡すと文字列で返す。 +#define LABEL_TO_STRING(func_name, label_names, prefix_len) \ + const char * \ + func_name(int code) \ + { \ + int i; \ + \ + for (i = 0; i < countof(label_names); i++) { \ + if (label_names[i].label == code) \ + return label_names[i].name + prefix_len;\ + } \ + \ + return "UNKNOWN"; \ + } +LABEL_TO_STRING(pptp_StopCCRQ_reason_string, pptp_StopCCRQ_reason_labels, 21) +LABEL_TO_STRING(pptp_StopCCRP_result_string, pptp_StopCCRP_result_labels, 21) +LABEL_TO_STRING(pptp_general_error_string, pptp_general_error_labels, 11) +LABEL_TO_STRING(pptp_ctrl_mes_type_string, pptp_ctrl_mes_type_labels, 19) +LABEL_TO_STRING(pptp_CDN_result_string, pptp_CDN_result_labels, 16) diff --git a/usr.sbin/npppd/pptp/pptp_subr.h b/usr.sbin/npppd/pptp/pptp_subr.h new file mode 100644 index 00000000000..a09c82cc98f --- /dev/null +++ b/usr.sbin/npppd/pptp/pptp_subr.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef PPTP_SUBR_H +#define PPTP_SUBR_H 1 +#ifdef __cplusplus +extern "C" { +#endif + +const char *pptp_framing_string (uint32_t); +const char *pptp_bearer_string (uint32_t); +void pptp_init_header (struct pptp_ctrl_header *, int, int); +const char *pptp_StopCCRQ_reason_string(int); +const char *pptp_StopCCRP_result_string(int); +const char *pptp_general_error_string(int); +const char *pptp_ctrl_mes_type_string(int); +const char *pptp_CDN_result_string(int); +#ifdef __cplusplus +} +#endif +#endif + + + + + + + diff --git a/usr.sbin/npppd/pptp/pptpd.c b/usr.sbin/npppd/pptp/pptpd.c new file mode 100644 index 00000000000..82cf429b644 --- /dev/null +++ b/usr.sbin/npppd/pptp/pptpd.c @@ -0,0 +1,1151 @@ +/*- + * Copyright (c) 2009 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id: pptpd.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ +/**@file + * PPTPデーモンの実装。現在は PAC(PPTP Access Concentrator) としての実装 + * のみです。 + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <stdarg.h> +#include <signal.h> +#include <syslog.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <string.h> +#include <event.h> +#include <ifaddrs.h> + +#ifdef USE_LIBSOCKUTIL +#include <seil/sockfromto.h> +#endif + +#include "net_utils.h" +#include "bytebuf.h" +#include "debugutil.h" +#include "hash.h" +#include "slist.h" +#include "addr_range.h" +#include "properties.h" +#include "config_helper.h" +#ifdef _SEIL_EXT_ +#include "rtev.h" +#endif + +#include "pptp.h" +#include "pptp_local.h" + +static int pptpd_seqno = 0; + +#ifdef PPTPD_DEBUG +#define PPTPD_ASSERT(x) ASSERT(x) +#define PPTPD_DBG(x) pptpd_log x +#else +#define PPTPD_ASSERT(x) +#define PPTPD_DBG(x) +#endif + +static void pptpd_log (pptpd *, int, const char *, ...) __printflike(3,4); +static void pptpd_close_gre (pptpd *); +static void pptpd_close_1723 (pptpd *); +static void pptpd_io_event (int, short, void *); +static void pptpd_gre_io_event (int, short, void *); +static void pptpd_gre_input (pptpd_listener *, struct sockaddr *, u_char *, int); +static void pptp_ctrl_start_by_pptpd (pptpd *, int, int, struct sockaddr *); +static int pptp_call_cmp (const void *, const void *); +static uint32_t pptp_call_hash (const void *, int); +static void pptp_gre_header_string (struct pptp_gre_header *, char *, int); + +#define PPTPD_SHUFFLE_MARK -1 + +/** PPTPデーモンを初期化します */ +int +pptpd_init(pptpd *_this) +{ + int i, m; + struct sockaddr_in sin0; + uint16_t call0, call[UINT16_MAX - 1]; + + memset(_this, 0, sizeof(pptpd)); + _this->id = pptpd_seqno++; + + slist_init(&_this->listener); + memset(&sin0, 0, sizeof(sin0)); + sin0.sin_len = sizeof(sin0); + sin0.sin_family = AF_INET; + if (pptpd_add_listener(_this, 0, PPTPD_DEFAULT_LAYER2_LABEL, + (struct sockaddr *)&sin0) != 0) { + return 1; + } + + _this->ip4_allow = NULL; + + slist_init(&_this->ctrl_list); + slist_init(&_this->call_free_list); + + /* Call-ID シャッフル */ + for (i = 0; i < countof(call) ; i++) + call[i] = i + 1; + for (i = countof(call); i > 1; i--) { + m = random() % i; + call0 = call[m]; + call[m] = call[i - 1]; + call[i - 1] = call0; + } + /* 必要個だけを slist に */ + for (i = 0; i < MIN(PPTP_MAX_CALL, countof(call)); i++) + slist_add(&_this->call_free_list, (void *)(uintptr_t)call[i]); + /* 末尾に SHUFFLE_MARK。次回は slist_shuffle で shuflle される */ + slist_add(&_this->call_free_list, (void *)PPTPD_SHUFFLE_MARK); + + if (_this->call_id_map == NULL) + _this->call_id_map = hash_create(pptp_call_cmp, pptp_call_hash, + 0); + + return 0; +} + +/** + * {@link ::pptpd PPTPデーモン}に{@link ::pptpd_listener リスナ}を追加します。 + * @param _this {@link ::pptpd PPTPデーモン} + * @param idx リスナのインデックス + * @param label 物理層としてのラベル。"PPTP" など + * @param bindaddr 待ち受けるアドレス + */ +int +pptpd_add_listener(pptpd *_this, int idx, const char *label, + struct sockaddr *bindaddr) +{ + int inaddr_any; + pptpd_listener *plistener, *plstn; + + plistener = NULL; + if (idx == 0 && slist_length(&_this->listener) > 0) { + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + slist_itr_next(&_this->listener); + plstn = slist_itr_remove(&_this->listener); + PPTPD_ASSERT(plstn != NULL); + PPTPD_ASSERT(plstn->sock == -1); + PPTPD_ASSERT(plstn->sock_gre == -1); + free(plstn); + } + } + PPTPD_ASSERT(slist_length(&_this->listener) == idx); + if (slist_length(&_this->listener) != idx) { + pptpd_log(_this, LOG_ERR, + "Invalid argument error on %s(): idx must be %d but %d", + __func__, slist_length(&_this->listener), idx); + goto reigai; + } + if ((plistener = malloc(sizeof(pptpd_listener))) == NULL) { + pptpd_log(_this, LOG_ERR, "malloc() failed in %s: %m", + __func__); + goto reigai; + } + memset(plistener, 0, sizeof(pptpd_listener)); + + PPTPD_ASSERT(sizeof(plistener->bind_sin) >= bindaddr->sa_len); + memcpy(&plistener->bind_sin, bindaddr, bindaddr->sa_len); + memcpy(&plistener->bind_sin_gre, bindaddr, bindaddr->sa_len); + + /* ポート番号が省略された場合は、デフォルト (1723/tcp)を使う */ + if (plistener->bind_sin.sin_port == 0) + plistener->bind_sin.sin_port = htons(PPTPD_DEFAULT_TCP_PORT); + + /* + * raw ソケットで、INADDR_ANY と明示的な IP アドレス指定したソケット両 + * 方を bind した場合、パケットは両方のソケットで受信される。この状態が + * 発生すると、パケットが重複して受信したように見えてしまうため、このよ + * うな設定は許さないこととした。 + */ + inaddr_any = 0; + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plstn = slist_itr_next(&_this->listener); + if (plstn->bind_sin_gre.sin_addr.s_addr == INADDR_ANY) + inaddr_any++; + } + if (plistener->bind_sin_gre.sin_addr.s_addr == INADDR_ANY) + inaddr_any++; + if (inaddr_any > 0 && idx > 0) { + log_printf(LOG_ERR, "configuration error at pptpd.listener_in: " + "combination 0.0.0.0 and other address is not allowed."); + goto reigai; + } + + plistener->bind_sin_gre.sin_port = 0; + plistener->sock = -1; + plistener->sock_gre = -1; + plistener->self = _this; + plistener->index = idx; + strlcpy(plistener->phy_label, label, sizeof(plistener->phy_label)); + + if (slist_add(&_this->listener, plistener) == NULL) { + pptpd_log(_this, LOG_ERR, "slist_add() failed in %s: %m", + __func__); + goto reigai; + } + return 0; +reigai: + if (plistener != NULL) + free(plistener); + return 1; +} + +void +pptpd_uninit(pptpd *_this) +{ + pptpd_listener *plstn; + + slist_fini(&_this->ctrl_list); + slist_fini(&_this->call_free_list); + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plstn = slist_itr_next(&_this->listener); + PPTPD_ASSERT(plstn != NULL); + PPTPD_ASSERT(plstn->sock == -1); + PPTPD_ASSERT(plstn->sock_gre == -1); + free(plstn); + } + slist_fini(&_this->listener); + if (_this->call_id_map != NULL) { + // アイテムの削除? + hash_free(_this->call_id_map); + } + if (_this->ip4_allow != NULL) + in_addr_range_list_remove_all(&_this->ip4_allow); + _this->call_id_map = NULL; + _this->config = NULL; +} + +#define CALL_MAP_KEY(call) \ + (void *)(call->id | (call->ctrl->listener_index << 16)) +#define CALL_ID(item) ((uint32_t)item & 0xffff) + +/** PPTPを割り当てます */ +int +pptpd_assign_call(pptpd *_this, pptp_call *call) +{ + int shuffle_cnt = 0, call_id; + + shuffle_cnt = 0; + slist_itr_first(&_this->call_free_list); + while (slist_length(&_this->call_free_list) > 1 && + slist_itr_has_next(&_this->call_free_list)) { + call_id = (int)slist_itr_next(&_this->call_free_list); + if (call_id == 0) + break; + slist_itr_remove(&_this->call_free_list); + if (call_id == PPTPD_SHUFFLE_MARK) { + if (shuffle_cnt++ > 0) + break; + slist_shuffle(&_this->call_free_list); + slist_add(&_this->call_free_list, + (void *)PPTPD_SHUFFLE_MARK); + slist_itr_first(&_this->call_free_list); + continue; + } + call->id = call_id; + hash_insert(_this->call_id_map, CALL_MAP_KEY(call), call); + + return 0; + } + errno = EBUSY; + pptpd_log(_this, LOG_ERR, "call request reached limit=%d", + PPTP_MAX_CALL); + return -1; +} + +/** PPTPを解放します。*/ +void +pptpd_release_call(pptpd *_this, pptp_call *call) +{ + if (call->id != 0) + slist_add(&_this->call_free_list, (void *)call->id); + hash_delete(_this->call_id_map, CALL_MAP_KEY(call), 0); + call->id = 0; +} + +static int +pptpd_listener_start(pptpd_listener *_this) +{ + int sock, ival, sock_gre; + struct sockaddr_in bind_sin, bind_sin_gre; + int wildcardbinding; +#ifdef NPPPD_FAKEBIND + extern void set_faith(int, int); +#endif + + wildcardbinding = + (_this->bind_sin.sin_addr.s_addr == INADDR_ANY)? 1 : 0; + sock = -1; + sock_gre = -1; + memcpy(&bind_sin, &_this->bind_sin, sizeof(bind_sin)); + memcpy(&bind_sin_gre, &_this->bind_sin_gre, sizeof(bind_sin_gre)); + + if (_this->phy_label[0] == '\0') + strlcpy(_this->phy_label, PPTPD_DEFAULT_LAYER2_LABEL, + sizeof(_this->phy_label)); + // 1723/tcp + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m", + __func__); + goto reigai; + } + ival = 1; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &ival, sizeof(ival)) < 0){ + pptpd_log(_this->self, LOG_WARNING, + "setsockopt(SO_REUSEPORT) failed at %s(): %m", __func__); + } +#ifdef NPPPD_FAKEBIND + if (!wildcardbinding) + set_faith(sock, 1); +#endif +#if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) + ival = 1; + if (setsockopt(sock, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival)) + != 0) + pptpd_log(_this->self, LOG_WARNING, + "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); +#endif + if ((ival = fcntl(sock, F_GETFL, 0)) < 0) { + pptpd_log(_this->self, LOG_ERR, + "fcntl(F_GET_FL) failed at %s(): %m", __func__); + goto reigai; + } else if (fcntl(sock, F_SETFL, ival | O_NONBLOCK) < 0) { + pptpd_log(_this->self, LOG_ERR, + "fcntl(F_SET_FL) failed at %s(): %m", __func__); + goto reigai; + } + if (bind(sock, (struct sockaddr *)&_this->bind_sin, + _this->bind_sin.sin_len) != 0) { + pptpd_log(_this->self, LOG_ERR, + "bind(%s:%u) failed at %s(): %m", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port), __func__); + goto reigai; + } + if (listen(sock, PPTP_BACKLOG) != 0) { + pptpd_log(_this->self, LOG_ERR, + "listen(%s:%u) failed at %s(): %m", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port), __func__); + goto reigai; + } +#ifdef NPPPD_FAKEBIND + if (!wildcardbinding) + set_faith(sock, 0); +#endif + pptpd_log(_this->self, LOG_INFO, "Listening %s:%u/tcp (PPTP PAC) [%s]", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port), _this->phy_label); + + /* GRE */ + bind_sin_gre.sin_port = 0; + if ((sock_gre = socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) { + pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m", + __func__); + goto reigai; + } +#ifdef NPPPD_FAKEBIND + if (!wildcardbinding) + set_faith(sock_gre, 1); +#endif +#if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) + ival = 1; + if (setsockopt(sock_gre, IPPROTO_IP, IP_STRICT_RCVIF, &ival, + sizeof(ival)) != 0) + pptpd_log(_this->self, LOG_WARNING, + "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); +#endif + if ((ival = fcntl(sock_gre, F_GETFL, 0)) < 0) { + pptpd_log(_this->self, LOG_ERR, + "fcntl(F_GET_FL) failed at %s(): %m", __func__); + goto reigai; + } else if (fcntl(sock_gre, F_SETFL, ival | O_NONBLOCK) < 0) { + pptpd_log(_this->self, LOG_ERR, + "fcntl(F_SET_FL) failed at %s(): %m", __func__); + goto reigai; + } + if (bind(sock_gre, (struct sockaddr *)&bind_sin_gre, + bind_sin_gre.sin_len) != 0) { + pptpd_log(_this->self, LOG_ERR, + "bind(%s:%u) failed at %s(): %m", + inet_ntoa(bind_sin_gre.sin_addr), + ntohs(bind_sin_gre.sin_port), __func__); + goto reigai; + } +#ifdef NPPPD_FAKEBIND + if (!wildcardbinding) + set_faith(sock_gre, 0); +#endif + if (wildcardbinding) { +#ifdef USE_LIBSOCKUTIL + if (setsockoptfromto(sock) != 0) { + pptpd_log(_this->self, LOG_ERR, + "setsockoptfromto() failed in %s(): %m", __func__); + goto reigai; + } +#else + /* nothing to do */ +#endif + } + pptpd_log(_this->self, LOG_INFO, "Listening %s:gre (PPTP PAC)", + inet_ntoa(bind_sin_gre.sin_addr)); + + _this->sock = sock; + _this->sock_gre = sock_gre; + + event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST, + pptpd_io_event, _this); + event_add(&_this->ev_sock, NULL); + + event_set(&_this->ev_sock_gre, _this->sock_gre, EV_READ | EV_PERSIST, + pptpd_gre_io_event, _this); + event_add(&_this->ev_sock_gre, NULL); + + return 0; +reigai: + if (sock >= 0) + close(sock); + if (sock_gre >= 0) + close(sock_gre); + + _this->sock = -1; + _this->sock_gre = -1; + + return 1; +} +/** PPTPデーモンを開始します */ +int +pptpd_start(pptpd *_this) +{ + int rval = 0; + pptpd_listener *plistener; + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plistener = slist_itr_next(&_this->listener); + PPTPD_ASSERT(plistener != NULL); + rval |= pptpd_listener_start(plistener); + } + if (rval == 0) + _this->state = PPTPD_STATE_RUNNING; + + return rval; +} + +static void +pptpd_listener_close_gre(pptpd_listener *_this) +{ + if (_this->sock_gre >= 0) { + event_del(&_this->ev_sock_gre); + close(_this->sock_gre); + pptpd_log(_this->self, LOG_INFO, "Shutdown %s/gre", + inet_ntoa(_this->bind_sin_gre.sin_addr)); + } + _this->sock_gre = -1; +} + +/** GREの待ち受けソケットを close します */ +static void +pptpd_close_gre(pptpd *_this) +{ + pptpd_listener *plistener; + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plistener = slist_itr_next(&_this->listener); + pptpd_listener_close_gre(plistener); + } +} + +/** 1723/tcp の待ち受けソケットを close します */ +static void +pptpd_listener_close_1723(pptpd_listener *_this) +{ + if (_this->sock >= 0) { + event_del(&_this->ev_sock); + close(_this->sock); + pptpd_log(_this->self, LOG_INFO, "Shutdown %s:%u/tcp", + inet_ntoa(_this->bind_sin.sin_addr), + ntohs(_this->bind_sin.sin_port)); + } + _this->sock = -1; +} +/** 1723/tcp の待ち受けソケットを close します */ +static void +pptpd_close_1723(pptpd *_this) +{ + pptpd_listener *plistener; + + slist_itr_first(&_this->listener); + while (slist_itr_has_next(&_this->listener)) { + plistener = slist_itr_next(&_this->listener); + pptpd_listener_close_1723(plistener); + } +} + +/** PPTPデーモンを本当に終了します。**/ +void +pptpd_stop_immediatly(pptpd *_this) +{ + pptp_ctrl *ctrl; + + if (event_initialized(&_this->ev_timer)) + evtimer_del(&_this->ev_timer); + if (_this->state != PPTPD_STATE_STOPPED) { + /* + * pptp_ctrl_stop を呼び出すと、この関数が再度呼ばれる可能 + * 性がある。このため、このstate 変更は重要。 + */ + _this->state = PPTPD_STATE_STOPPED; + + pptpd_close_1723(_this); + for (slist_itr_first(&_this->ctrl_list); + (ctrl = slist_itr_next(&_this->ctrl_list)) != NULL;) { + pptp_ctrl_stop(ctrl, 0); + } + pptpd_close_gre(_this); + slist_fini(&_this->ctrl_list); + slist_fini(&_this->call_free_list); + pptpd_log(_this, LOG_NOTICE, "Stopped"); + } else { + PPTPD_DBG((_this, LOG_DEBUG, "(Already) Stopped")); + } +} + +static void +pptpd_stop_timeout(int fd, short event, void *ctx) +{ + pptpd *_this; + + _this = ctx; + pptpd_stop_immediatly(_this); +} + +/** PPTPデーモンを終了します */ +void +pptpd_stop(pptpd *_this) +{ + int nctrl; + pptp_ctrl *ctrl; + struct timeval tv; + + if (event_initialized(&_this->ev_timer)) + evtimer_del(&_this->ev_timer); + pptpd_close_1723(_this); + /* このあたりの動作は l2tpd_stop とあわせるべき */ + + if (pptpd_is_stopped(_this)) + return; + if (pptpd_is_shutting_down(_this)) { + pptpd_stop_immediatly(_this); + return; + } + _this->state = PPTPD_STATE_SHUTTING_DOWN; + nctrl = 0; + for (slist_itr_first(&_this->ctrl_list); + (ctrl = slist_itr_next(&_this->ctrl_list)) != NULL;) { + pptp_ctrl_stop(ctrl, PPTP_CDN_RESULT_ADMIN_SHUTDOWN); + nctrl++; + } + if (nctrl > 0) { + // タイマーセット + tv.tv_sec = PPTPD_SHUTDOWN_TIMEOUT; + tv.tv_usec = 0; + + evtimer_set(&_this->ev_timer, pptpd_stop_timeout, _this); + evtimer_add(&_this->ev_timer, &tv); + + return; + } + pptpd_stop_immediatly(_this); +} + +/*********************************************************************** + * 設定関連 + ***********************************************************************/ +#define CFG_KEY(p, s) config_key_prefix((p), (s)) +#define VAL_SEP " \t\r\n" + +CONFIG_FUNCTIONS(pptpd_config, pptpd, config); +PREFIXED_CONFIG_FUNCTIONS(pptp_ctrl_config, pptp_ctrl, pptpd->config, + phy_label); +int +pptpd_reload(pptpd *_this, struct properties *config, const char *name, + int default_enabled) +{ + int i, do_start, aierr; + const char *val; + char *tok, *cp, buf[PPTPD_CONFIG_BUFSIZ], *label; + struct addrinfo *ai; + + ASSERT(_this != NULL); + ASSERT(config != NULL); + + _this->config = config; /* 現在は copy しなくて大丈夫 */ + do_start = 0; + if (pptpd_config_str_equal(_this, CFG_KEY(name, "enabled"), "true", + default_enabled)) { + // false にした直後に true にされるかもしれない。 + if (pptpd_is_shutting_down(_this)) + pptpd_stop_immediatly(_this); + if (pptpd_is_stopped(_this)) + do_start = 1; + } else { + if (!pptpd_is_stopped(_this)) + pptpd_stop(_this); + return 0; + } + if (do_start && pptpd_init(_this) != 0) + return 1; + // pptpd_init でリセットされてしまうので。 + _this->config = config; + + _this->ctrl_in_pktdump = pptpd_config_str_equal(_this, + "log.pptp.ctrl.in.pktdump", "true", 0); + _this->data_in_pktdump = pptpd_config_str_equal(_this, + "log.pptp.data.in.pktdump", "true", 0); + _this->ctrl_out_pktdump = pptpd_config_str_equal(_this, + "log.pptp.ctrl.out.pktdump", "true", 0); + _this->data_out_pktdump = pptpd_config_str_equal(_this, + "log.pptp.data.out.pktdump", "true", 0); + _this->phy_label_with_ifname = pptpd_config_str_equal(_this, + CFG_KEY(name, "label_with_ifname"), "true", 0); + + // ip4_allow をパース + in_addr_range_list_remove_all(&_this->ip4_allow); + val = pptpd_config_str(_this, CFG_KEY(name, "ip4_allow")); + if (val != NULL) { + if (strlen(val) >= sizeof(buf)) { + log_printf(LOG_ERR, "configuration error at " + "%s: too long", CFG_KEY(name, "ip4_allow")); + return 1; + } + strlcpy(buf, val, sizeof(buf)); + for (cp = buf; (tok = strsep(&cp, VAL_SEP)) != NULL;) { + if (*tok == '\0') + continue; + if (in_addr_range_list_add(&_this->ip4_allow, tok) + != 0) { + pptpd_log(_this, LOG_ERR, + "configuration error at %s: %s", + CFG_KEY(name, "ip4_allow"), tok); + return 1; + } + } + } + + if (do_start) { + /* + * 起動直後と、pptpd.enable が false -> true に変更された + * 場合に、do_start。すべてのリスナーが、初期化された状態を + * 仮定できる + */ + // pptpd.listener_in の読み込む + val = pptpd_config_str(_this, CFG_KEY(name, "listener_in")); + if (val != NULL) { + if (strlen(val) >= sizeof(buf)) { + pptpd_log(_this, LOG_ERR, + "configuration error at " + "%s: too long", CFG_KEY(name, "listener")); + return 1; + } + strlcpy(buf, val, sizeof(buf)); + + label = NULL; + // タブ、スペース区切りで、複数指定可能 + for (i = 0, cp = buf; + (tok = strsep(&cp, VAL_SEP)) != NULL;) { + if (*tok == '\0') + continue; + if (label == NULL) { + label = tok; + continue; + } + if ((aierr = addrport_parse(tok, IPPROTO_TCP, + &ai)) != 0) { + pptpd_log(_this, LOG_ERR, + "configuration error at " + "%s: %s: %s", + CFG_KEY(name, "listener_in"), tok, + gai_strerror(aierr)); + return 1; + } + PPTPD_ASSERT(ai != NULL && + ai->ai_family == AF_INET); + if (pptpd_add_listener(_this, i, label, + ai->ai_addr) != 0) { + freeaddrinfo(ai); + label = NULL; + break; + } + freeaddrinfo(ai); + label = NULL; + i++; + } + if (label != NULL) { + pptpd_log(_this, LOG_ERR, + "configuration error at %s: %s", + CFG_KEY(name, "listner_in"), label); + return 1; + } + } + if (pptpd_start(_this) != 0) + return 1; + } + + return 0; +} + +/*********************************************************************** + * I/O関連 + ***********************************************************************/ +static void +pptpd_log_access_deny(pptpd *_this, const char *reason, struct sockaddr *peer) +{ + char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; + + if (getnameinfo(peer, peer->sa_len, hostbuf, sizeof(hostbuf), + servbuf, sizeof(servbuf), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + pptpd_log(_this, LOG_ERR, "getnameinfo() failed at %s(): %m", + __func__); + return; + } + pptpd_log(_this, LOG_ALERT, "denied a connection from %s:%s/tcp: %s", + hostbuf, servbuf, reason); +} + +/** 1723/tcp の IOイベントハンドラ */ +static void +pptpd_io_event(int fd, short evmask, void *ctx) +{ + int newsock; + const char *reason; + socklen_t peerlen; + struct sockaddr_storage peer; + pptpd *_this; + pptpd_listener *listener; + + listener = ctx; + PPTPD_ASSERT(listener != NULL); + _this = listener->self; + PPTPD_ASSERT(_this != NULL); + + if ((evmask & EV_READ) != 0) { + for (;;) { // EAGAIN まで 連続して accept + peerlen = sizeof(peer); + if ((newsock = accept(listener->sock, + (struct sockaddr *)&peer, &peerlen)) < 0) { + switch (errno) { + case EAGAIN: + case EINTR: + break; + case ECONNABORTED: + pptpd_log(_this, LOG_WARNING, + "accept() failed at %s(): %m", + __func__); + break; + default: + pptpd_log(_this, LOG_ERR, + "accept() failed at %s(): %m", + __func__); + pptpd_listener_close_1723(listener); + pptpd_stop(_this); + } + break; + } + // 送信元チェック + switch (peer.ss_family) { + case AF_INET: + if (!in_addr_range_list_includes( + &_this->ip4_allow, + &((struct sockaddr_in *)&peer)->sin_addr)) { + reason = "not allowed by acl."; + break; + } + goto accept; + default: + reason = "address family is not supported."; + break; + } + // 許可されていない + pptpd_log_access_deny(_this, reason, + (struct sockaddr *)&peer); + close(newsock); + continue; + // NOTREACHED +accept: + // 許可 + pptp_ctrl_start_by_pptpd(_this, newsock, + listener->index, (struct sockaddr *)&peer); + } + } +} + +/** GRE の IOイベントハンドラー */ +static void +pptpd_gre_io_event(int fd, short evmask, void *ctx) +{ + int sz; + u_char pkt[65535]; + socklen_t peerlen; + struct sockaddr_storage peer; + pptpd *_this; + pptpd_listener *listener; + + listener = ctx; + PPTPD_ASSERT(listener != NULL); + _this = listener->self; + PPTPD_ASSERT(_this != NULL); + + if (evmask & EV_READ) { + for (;;) { + // Block するまで読む + peerlen = sizeof(peer); + if ((sz = recvfrom(listener->sock_gre, pkt, sizeof(pkt), + 0, (struct sockaddr *)&peer, &peerlen)) <= 0) { + if (sz < 0 && + (errno == EAGAIN || errno == EINTR)) + break; + pptpd_log(_this, LOG_INFO, + "read(GRE) failed: %m"); + pptpd_stop(_this); + return; + } + pptpd_gre_input(listener, (struct sockaddr *)&peer, pkt, + sz); + } + } +} + +/** GREの受信 → pptp_call に配送 */ +static void +pptpd_gre_input(pptpd_listener *listener, struct sockaddr *peer, u_char *pkt, + int lpkt) +{ + int hlen, input_flags; + uint32_t seq, ack, call_id; + struct ip *iphdr; + struct pptp_gre_header *grehdr; + char hbuf0[NI_MAXHOST], logbuf[512]; + const char *reason; + pptp_call *call; + hash_link *hl; + pptpd *_this; + + seq = 0; + ack = 0; + input_flags = 0; + reason = "No error"; + _this = listener->self; + + PPTPD_ASSERT(peer->sa_family == AF_INET); + + strlcpy(hbuf0, "<unknown>", sizeof(hbuf0)); + if (getnameinfo(peer, peer->sa_len, hbuf0, sizeof(hbuf0), NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + pptpd_log(_this, LOG_ERR, + "getnameinfo() failed at %s(): %m", __func__); + goto reigai; + } + if (_this->data_in_pktdump != 0) { + pptpd_log(_this, LOG_DEBUG, "PPTP Data input packet dump"); + show_hd(debug_get_debugfp(), pkt, lpkt); + } + if (peer->sa_family != AF_INET) { + pptpd_log(_this, LOG_ERR, + "Received malformed GRE packet: address family is not " + "supported: peer=%s af=%d", hbuf0, peer->sa_family); + goto reigai; + } + + if (lpkt < sizeof(struct ip)) { + pptpd_log(_this, LOG_ERR, + "Received a short length packet length=%d, from %s", lpkt, + hbuf0); + goto reigai; + } + iphdr = (struct ip *)pkt; + + // IPヘッダは ntohs 済み NetBSD の場合 +#if !defined(__NetBSD__) + iphdr->ip_len = ntohs(iphdr->ip_len); +#endif + hlen = iphdr->ip_hl * 4; + + if (iphdr->ip_len > lpkt || + iphdr->ip_len < sizeof(struct pptp_gre_header)) { + pptpd_log(_this, LOG_ERR, + "Received a broken packet: ip_hl=%d iplen=%d lpkt=%d", hlen, + iphdr->ip_len, lpkt); + show_hd(debug_get_debugfp(), pkt, lpkt); + goto reigai; + } + pkt += hlen; + lpkt -= hlen; + grehdr = (struct pptp_gre_header *)pkt; + pkt += sizeof(struct pptp_gre_header); + lpkt -= sizeof(struct pptp_gre_header); + + grehdr->protocol_type = htons(grehdr->protocol_type); + grehdr->payload_length = htons(grehdr->payload_length); + grehdr->call_id = htons(grehdr->call_id); + + if (!(grehdr->protocol_type == PPTP_GRE_PROTOCOL_TYPE && + grehdr->C == 0 && grehdr->R == 0 && grehdr->K != 0 && + grehdr->recur == 0 && grehdr->s == 0 && grehdr->flags == 0 && + grehdr->ver == PPTP_GRE_VERSION)) { + reason = "GRE header is broken"; + goto bad_gre; + } + if (grehdr->S != 0) { + if (lpkt < 2) { + reason = "No enough space for seq number"; + goto bad_gre; + } + input_flags |= PPTP_GRE_PKT_SEQ_PRESENT; + seq = ntohl(*(uint32_t *)pkt); + pkt += 4; + lpkt -= 4; + } + + if (grehdr->A != 0) { + if (lpkt < 2) { + reason = "No enough space for ack number"; + goto bad_gre; + } + input_flags |= PPTP_GRE_PKT_ACK_PRESENT; + ack = ntohl(*(uint32_t *)pkt); + pkt += 4; + lpkt -= 4; + } + + if (grehdr->payload_length > lpkt) { + reason = "'Payload Length' is mismatch from actual length"; + goto bad_gre; + } + + + // pptp_call に配送 + call_id = grehdr->call_id; + + hl = hash_lookup(_this->call_id_map, + (void *)(call_id | (listener->index << 16))); + if (hl == NULL) { + reason = "Received GRE packet has unknown call_id"; + goto bad_gre; + } + call = hl->item; + pptp_call_gre_input(call, seq, ack, input_flags, pkt, lpkt); + + return; +bad_gre: + pptp_gre_header_string(grehdr, logbuf, sizeof(logbuf)); + pptpd_log(_this, LOG_INFO, + "Received malformed GRE packet: %s: peer=%s sock=%s %s seq=%u: " + "ack=%u ifidx=%d", reason, hbuf0, inet_ntoa(iphdr->ip_dst), logbuf, + seq, ack, listener->index); +reigai: + return; +} + +/** PPTPコントロールを開始します。(新しい接続があれば呼び出される。) */ +static void +pptp_ctrl_start_by_pptpd(pptpd *_this, int sock, int listener_index, + struct sockaddr *peer) +{ + int ival; + pptp_ctrl *ctrl; + socklen_t sslen; + char ifname[IF_NAMESIZE], msgbuf[128]; + + ctrl = NULL; + if ((ctrl = pptp_ctrl_create()) == NULL) + goto reigai; + if (pptp_ctrl_init(ctrl) != 0) + goto reigai; + + memset(&ctrl->peer, 0, sizeof(ctrl->peer)); + memcpy(&ctrl->peer, peer, peer->sa_len); + ctrl->pptpd = _this; + ctrl->sock = sock; + ctrl->listener_index = listener_index; + + sslen = sizeof(ctrl->our); + if (getsockname(ctrl->sock, (struct sockaddr *)&ctrl->our, + &sslen) != 0) { + pptpd_log(_this, LOG_WARNING, + "getsockname() failed at %s(): %m", __func__); + goto reigai; + } + /* "L2TP%em0.mru" などと、インタフェースで設定を変更する場合 */ + if (_this->phy_label_with_ifname != 0) { + if (get_ifname_by_sockaddr((struct sockaddr *)&ctrl->our, + ifname) == NULL) { + pptpd_log_access_deny(_this, + "could not get interface informations", peer); + goto reigai; + } + if (pptpd_config_str_equal(_this, + config_key_prefix("pptpd.interface", ifname), "accept", 0)){ + snprintf(ctrl->phy_label, sizeof(ctrl->phy_label), + "%s%%%s", PPTP_CTRL_LISTENER_LABEL(ctrl), ifname); + } else if (pptpd_config_str_equal(_this, + config_key_prefix("pptpd.interface", "any"), "accept", 0)){ + snprintf(ctrl->phy_label, sizeof(ctrl->phy_label), + "%s", PPTP_CTRL_LISTENER_LABEL(ctrl)); + } else { + /* このインタフェースは許可されていない。*/ + snprintf(msgbuf, sizeof(msgbuf), + "'%s' is not allowed by config.", ifname); + pptpd_log_access_deny(_this, msgbuf, peer); + goto reigai; + } +#if defined(_SEIL_EXT_) && !defined(USE_LIBSOCKUTIL) + if (!rtev_ifa_is_primary(ifname, + (struct sockaddr *)&ctrl->our)) { + char hostbuf[NI_MAXHOST]; + + getnameinfo((struct sockaddr *)&ctrl->our, + ctrl->our.ss_len, hostbuf, + sizeof(hostbuf), NULL, 0, NI_NUMERICHOST); + snprintf(msgbuf, sizeof(msgbuf), + "connecting to %s (an alias address of %s)" + " is not allowed by this version.", + hostbuf, ifname); + pptpd_log_access_deny(_this, msgbuf, peer); + + goto reigai; + } +#endif + } else + strlcpy(ctrl->phy_label, PPTP_CTRL_LISTENER_LABEL(ctrl), + sizeof(ctrl->phy_label)); + + if ((ival = pptp_ctrl_config_int(ctrl, "pptp.echo_interval", 0)) != 0) + ctrl->echo_interval = ival; + + if ((ival = pptp_ctrl_config_int(ctrl, "pptp.echo_timeout", 0)) != 0) + ctrl->echo_timeout = ival; + + if (pptp_ctrl_start(ctrl) != 0) + goto reigai; + + slist_add(&_this->ctrl_list, ctrl); + + return; +reigai: + close(sock); + pptp_ctrl_destroy(ctrl); + return; +} + +/** PPTPコントロールが終了後に通知してきます。*/ +void +pptpd_ctrl_finished_notify(pptpd *_this, pptp_ctrl *ctrl) +{ + pptp_ctrl *ctrl1; + int i, nctrl; + + PPTPD_ASSERT(_this != NULL); + PPTPD_ASSERT(ctrl != NULL); + + nctrl = 0; + for (i = 0; i < slist_length(&_this->ctrl_list); i++) { + ctrl1 = slist_get(&_this->ctrl_list, i); + if (ctrl1 == ctrl) { + slist_remove(&_this->ctrl_list, i); + break; + } + } + pptp_ctrl_destroy(ctrl); + + PPTPD_DBG((_this, LOG_DEBUG, "Remains %d ctrls", nctrl)); + if (pptpd_is_shutting_down(_this) && nctrl == 0) + // シャットダウン中最後の一人 + pptpd_stop_immediatly(_this); +} + +/*********************************************************************** + * その他、ユーティリティ関数 + ***********************************************************************/ +/** このインスタンスに基づいたラベルから始まるログを記録します。 */ +static void +pptpd_log(pptpd *_this, int prio, const char *fmt, ...) +{ + char logbuf[BUFSIZ]; + va_list ap; + + PPTPD_ASSERT(_this != NULL); + va_start(ap, fmt); +#ifdef PPTPD_MULITPLE + snprintf(logbuf, sizeof(logbuf), "pptpd id=%u %s", _this->id, fmt); +#else + snprintf(logbuf, sizeof(logbuf), "pptpd %s", fmt); +#endif + vlog_printf(prio, logbuf, ap); + va_end(ap); +} + +static int +pptp_call_cmp(const void *a0, const void *b0) +{ + return ((uint32_t)a0 - (uint32_t)b0); +} + +static uint32_t +pptp_call_hash(const void *ctx, int size) +{ + return (uint32_t)ctx % size; +} + +/** GREパケットヘッダを文字列として */ +static void +pptp_gre_header_string(struct pptp_gre_header *grehdr, char *buf, int lbuf) +{ + snprintf(buf, lbuf, + "[%s%s%s%s%s%s] ver=%d " + "protocol_type=%04x payload_length=%d call_id=%d", + (grehdr->C != 0)? "C" : "", (grehdr->R != 0)? "R" : "", + (grehdr->K != 0)? "K" : "", (grehdr->S != 0)? "S" : "", + (grehdr->s != 0)? "s" : "", (grehdr->A != 0)? "A" : "", grehdr->ver, + grehdr->protocol_type, grehdr->payload_length, grehdr->call_id); +} |