From f24f75f44d582e005fed41d187261a034bb7628a Mon Sep 17 00:00:00 2001
From: YASUOKA Masahiko
Date: Mon, 11 Jan 2010 04:20:58 +0000
Subject: 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@.
---
usr.sbin/npppd/Makefile | 7 +
usr.sbin/npppd/Makefile.inc | 7 +
usr.sbin/npppd/common/addr_range.c | 432 ++++++
usr.sbin/npppd/common/addr_range.h | 51 +
usr.sbin/npppd/common/bytebuf.c | 420 ++++++
usr.sbin/npppd/common/bytebuf.h | 64 +
usr.sbin/npppd/common/config_helper.c | 318 +++++
usr.sbin/npppd/common/config_helper.h | 154 ++
usr.sbin/npppd/common/csvreader.c | 366 +++++
usr.sbin/npppd/common/csvreader.h | 66 +
usr.sbin/npppd/common/csvreader_test.c | 272 ++++
usr.sbin/npppd/common/debugmacro.h | 53 +
usr.sbin/npppd/common/debugutil.c | 306 ++++
usr.sbin/npppd/common/debugutil.h | 80 ++
usr.sbin/npppd/common/hash.c | 227 +++
usr.sbin/npppd/common/hash.h | 65 +
usr.sbin/npppd/common/ipsec_util.c | 360 +++++
usr.sbin/npppd/common/ipsec_util.h | 43 +
usr.sbin/npppd/common/ipsec_util_local.h | 33 +
usr.sbin/npppd/common/net_utils.c | 214 +++
usr.sbin/npppd/common/net_utils.h | 41 +
usr.sbin/npppd/common/properties.c | 566 ++++++++
usr.sbin/npppd/common/properties.h | 59 +
usr.sbin/npppd/common/properties_test.c | 188 +++
usr.sbin/npppd/common/radish.c | 822 +++++++++++
usr.sbin/npppd/common/radish.h | 128 ++
usr.sbin/npppd/common/recvfromto.c | 175 +++
usr.sbin/npppd/common/recvfromto.h | 37 +
usr.sbin/npppd/common/rt_zebra.c | 389 +++++
usr.sbin/npppd/common/rt_zebra.h | 30 +
usr.sbin/npppd/common/rt_zebra_local.h | 46 +
usr.sbin/npppd/common/rtev.h | 57 +
usr.sbin/npppd/common/rtev_common.c | 517 +++++++
usr.sbin/npppd/common/rtev_libevent.c | 312 ++++
usr.sbin/npppd/common/rtev_local.h | 57 +
usr.sbin/npppd/common/slist.c | 498 +++++++
usr.sbin/npppd/common/slist.h | 67 +
usr.sbin/npppd/common/slist_test.c | 590 ++++++++
usr.sbin/npppd/common/time_utils.c | 26 +
usr.sbin/npppd/common/time_utils.h | 19 +
usr.sbin/npppd/l2tp/l2tp.h | 485 +++++++
usr.sbin/npppd/l2tp/l2tp_call.c | 1067 ++++++++++++++
usr.sbin/npppd/l2tp/l2tp_ctrl.c | 1751 +++++++++++++++++++++++
usr.sbin/npppd/l2tp/l2tp_local.h | 75 +
usr.sbin/npppd/l2tp/l2tp_subr.c | 344 +++++
usr.sbin/npppd/l2tp/l2tp_subr.h | 129 ++
usr.sbin/npppd/l2tp/l2tpd.c | 811 +++++++++++
usr.sbin/npppd/npppd/Makefile | 58 +
usr.sbin/npppd/npppd/ccp.c | 369 +++++
usr.sbin/npppd/npppd/chap.c | 987 +++++++++++++
usr.sbin/npppd/npppd/chap_ms.c | 479 +++++++
usr.sbin/npppd/npppd/chap_ms.h | 54 +
usr.sbin/npppd/npppd/eap.c | 966 +++++++++++++
usr.sbin/npppd/npppd/fsm.c | 823 +++++++++++
usr.sbin/npppd/npppd/fsm.h | 169 +++
usr.sbin/npppd/npppd/ipcp.c | 423 ++++++
usr.sbin/npppd/npppd/lcp.c | 1318 +++++++++++++++++
usr.sbin/npppd/npppd/mppe.c | 624 ++++++++
usr.sbin/npppd/npppd/nint.h | 189 +++
usr.sbin/npppd/npppd/npppd.c | 2285 ++++++++++++++++++++++++++++++
usr.sbin/npppd/npppd/npppd.h | 125 ++
usr.sbin/npppd/npppd/npppd_auth.c | 945 ++++++++++++
usr.sbin/npppd/npppd/npppd_auth.h | 72 +
usr.sbin/npppd/npppd/npppd_auth_local.h | 123 ++
usr.sbin/npppd/npppd/npppd_config.c | 794 +++++++++++
usr.sbin/npppd/npppd/npppd_ctl.c | 585 ++++++++
usr.sbin/npppd/npppd/npppd_ctl.h | 113 ++
usr.sbin/npppd/npppd/npppd_defs.h | 129 ++
usr.sbin/npppd/npppd/npppd_iface.c | 607 ++++++++
usr.sbin/npppd/npppd/npppd_iface.h | 78 +
usr.sbin/npppd/npppd/npppd_local.h | 275 ++++
usr.sbin/npppd/npppd/npppd_pool.c | 642 +++++++++
usr.sbin/npppd/npppd/npppd_pool.h | 55 +
usr.sbin/npppd/npppd/npppd_subr.c | 613 ++++++++
usr.sbin/npppd/npppd/npppd_subr.h | 48 +
usr.sbin/npppd/npppd/npppd_tun.c | 218 +++
usr.sbin/npppd/npppd/npppd_tun.h | 43 +
usr.sbin/npppd/npppd/pap.c | 527 +++++++
usr.sbin/npppd/npppd/pathnames.h | 45 +
usr.sbin/npppd/npppd/ppp.c | 1130 +++++++++++++++
usr.sbin/npppd/npppd/ppp.h | 843 +++++++++++
usr.sbin/npppd/npppd/psm-opt.h | 128 ++
usr.sbin/npppd/npppd/radius+.cc | 761 ++++++++++
usr.sbin/npppd/npppd/radius+.h | 144 ++
usr.sbin/npppd/npppd/radius+_local.h | 107 ++
usr.sbin/npppd/npppd/radius_chap_const.h | 68 +
usr.sbin/npppd/npppd/radius_common.c | 74 +
usr.sbin/npppd/npppd/radius_common.h | 40 +
usr.sbin/npppd/npppd/radius_req.c | 373 +++++
usr.sbin/npppd/npppd/radius_req.h | 101 ++
usr.sbin/npppd/npppd/radiusconst.h | 205 +++
usr.sbin/npppd/npppd/version.h | 29 +
usr.sbin/npppd/npppdctl/npppdctl.c | 482 +++++++
usr.sbin/npppd/pppoe/pppoe.h | 233 +++
usr.sbin/npppd/pppoe/pppoe_local.h | 73 +
usr.sbin/npppd/pppoe/pppoe_session.c | 529 +++++++
usr.sbin/npppd/pppoe/pppoed.c | 1204 ++++++++++++++++
usr.sbin/npppd/pptp/pptp.h | 423 ++++++
usr.sbin/npppd/pptp/pptp_call.c | 852 +++++++++++
usr.sbin/npppd/pptp/pptp_ctrl.c | 1168 +++++++++++++++
usr.sbin/npppd/pptp/pptp_local.h | 177 +++
usr.sbin/npppd/pptp/pptp_subr.c | 144 ++
usr.sbin/npppd/pptp/pptp_subr.h | 50 +
usr.sbin/npppd/pptp/pptpd.c | 1151 +++++++++++++++
104 files changed, 38094 insertions(+)
create mode 100644 usr.sbin/npppd/Makefile
create mode 100644 usr.sbin/npppd/Makefile.inc
create mode 100644 usr.sbin/npppd/common/addr_range.c
create mode 100644 usr.sbin/npppd/common/addr_range.h
create mode 100644 usr.sbin/npppd/common/bytebuf.c
create mode 100644 usr.sbin/npppd/common/bytebuf.h
create mode 100644 usr.sbin/npppd/common/config_helper.c
create mode 100644 usr.sbin/npppd/common/config_helper.h
create mode 100644 usr.sbin/npppd/common/csvreader.c
create mode 100644 usr.sbin/npppd/common/csvreader.h
create mode 100644 usr.sbin/npppd/common/csvreader_test.c
create mode 100644 usr.sbin/npppd/common/debugmacro.h
create mode 100644 usr.sbin/npppd/common/debugutil.c
create mode 100644 usr.sbin/npppd/common/debugutil.h
create mode 100644 usr.sbin/npppd/common/hash.c
create mode 100644 usr.sbin/npppd/common/hash.h
create mode 100644 usr.sbin/npppd/common/ipsec_util.c
create mode 100644 usr.sbin/npppd/common/ipsec_util.h
create mode 100644 usr.sbin/npppd/common/ipsec_util_local.h
create mode 100644 usr.sbin/npppd/common/net_utils.c
create mode 100644 usr.sbin/npppd/common/net_utils.h
create mode 100644 usr.sbin/npppd/common/properties.c
create mode 100644 usr.sbin/npppd/common/properties.h
create mode 100644 usr.sbin/npppd/common/properties_test.c
create mode 100644 usr.sbin/npppd/common/radish.c
create mode 100644 usr.sbin/npppd/common/radish.h
create mode 100644 usr.sbin/npppd/common/recvfromto.c
create mode 100644 usr.sbin/npppd/common/recvfromto.h
create mode 100644 usr.sbin/npppd/common/rt_zebra.c
create mode 100644 usr.sbin/npppd/common/rt_zebra.h
create mode 100644 usr.sbin/npppd/common/rt_zebra_local.h
create mode 100644 usr.sbin/npppd/common/rtev.h
create mode 100644 usr.sbin/npppd/common/rtev_common.c
create mode 100644 usr.sbin/npppd/common/rtev_libevent.c
create mode 100644 usr.sbin/npppd/common/rtev_local.h
create mode 100644 usr.sbin/npppd/common/slist.c
create mode 100644 usr.sbin/npppd/common/slist.h
create mode 100644 usr.sbin/npppd/common/slist_test.c
create mode 100644 usr.sbin/npppd/common/time_utils.c
create mode 100644 usr.sbin/npppd/common/time_utils.h
create mode 100644 usr.sbin/npppd/l2tp/l2tp.h
create mode 100644 usr.sbin/npppd/l2tp/l2tp_call.c
create mode 100644 usr.sbin/npppd/l2tp/l2tp_ctrl.c
create mode 100644 usr.sbin/npppd/l2tp/l2tp_local.h
create mode 100644 usr.sbin/npppd/l2tp/l2tp_subr.c
create mode 100644 usr.sbin/npppd/l2tp/l2tp_subr.h
create mode 100644 usr.sbin/npppd/l2tp/l2tpd.c
create mode 100644 usr.sbin/npppd/npppd/Makefile
create mode 100644 usr.sbin/npppd/npppd/ccp.c
create mode 100644 usr.sbin/npppd/npppd/chap.c
create mode 100644 usr.sbin/npppd/npppd/chap_ms.c
create mode 100644 usr.sbin/npppd/npppd/chap_ms.h
create mode 100644 usr.sbin/npppd/npppd/eap.c
create mode 100644 usr.sbin/npppd/npppd/fsm.c
create mode 100644 usr.sbin/npppd/npppd/fsm.h
create mode 100644 usr.sbin/npppd/npppd/ipcp.c
create mode 100644 usr.sbin/npppd/npppd/lcp.c
create mode 100644 usr.sbin/npppd/npppd/mppe.c
create mode 100644 usr.sbin/npppd/npppd/nint.h
create mode 100644 usr.sbin/npppd/npppd/npppd.c
create mode 100644 usr.sbin/npppd/npppd/npppd.h
create mode 100644 usr.sbin/npppd/npppd/npppd_auth.c
create mode 100644 usr.sbin/npppd/npppd/npppd_auth.h
create mode 100644 usr.sbin/npppd/npppd/npppd_auth_local.h
create mode 100644 usr.sbin/npppd/npppd/npppd_config.c
create mode 100644 usr.sbin/npppd/npppd/npppd_ctl.c
create mode 100644 usr.sbin/npppd/npppd/npppd_ctl.h
create mode 100644 usr.sbin/npppd/npppd/npppd_defs.h
create mode 100644 usr.sbin/npppd/npppd/npppd_iface.c
create mode 100644 usr.sbin/npppd/npppd/npppd_iface.h
create mode 100644 usr.sbin/npppd/npppd/npppd_local.h
create mode 100644 usr.sbin/npppd/npppd/npppd_pool.c
create mode 100644 usr.sbin/npppd/npppd/npppd_pool.h
create mode 100644 usr.sbin/npppd/npppd/npppd_subr.c
create mode 100644 usr.sbin/npppd/npppd/npppd_subr.h
create mode 100644 usr.sbin/npppd/npppd/npppd_tun.c
create mode 100644 usr.sbin/npppd/npppd/npppd_tun.h
create mode 100644 usr.sbin/npppd/npppd/pap.c
create mode 100644 usr.sbin/npppd/npppd/pathnames.h
create mode 100644 usr.sbin/npppd/npppd/ppp.c
create mode 100644 usr.sbin/npppd/npppd/ppp.h
create mode 100644 usr.sbin/npppd/npppd/psm-opt.h
create mode 100644 usr.sbin/npppd/npppd/radius+.cc
create mode 100644 usr.sbin/npppd/npppd/radius+.h
create mode 100644 usr.sbin/npppd/npppd/radius+_local.h
create mode 100644 usr.sbin/npppd/npppd/radius_chap_const.h
create mode 100644 usr.sbin/npppd/npppd/radius_common.c
create mode 100644 usr.sbin/npppd/npppd/radius_common.h
create mode 100644 usr.sbin/npppd/npppd/radius_req.c
create mode 100644 usr.sbin/npppd/npppd/radius_req.h
create mode 100644 usr.sbin/npppd/npppd/radiusconst.h
create mode 100644 usr.sbin/npppd/npppd/version.h
create mode 100644 usr.sbin/npppd/npppdctl/npppdctl.c
create mode 100644 usr.sbin/npppd/pppoe/pppoe.h
create mode 100644 usr.sbin/npppd/pppoe/pppoe_local.h
create mode 100644 usr.sbin/npppd/pppoe/pppoe_session.c
create mode 100644 usr.sbin/npppd/pppoe/pppoed.c
create mode 100644 usr.sbin/npppd/pptp/pptp.h
create mode 100644 usr.sbin/npppd/pptp/pptp_call.c
create mode 100644 usr.sbin/npppd/pptp/pptp_ctrl.c
create mode 100644 usr.sbin/npppd/pptp/pptp_local.h
create mode 100644 usr.sbin/npppd/pptp/pptp_subr.c
create mode 100644 usr.sbin/npppd/pptp/pptp_subr.h
create mode 100644 usr.sbin/npppd/pptp/pptpd.c
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
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
+
+.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
+ *
+ * $Id: addr_range.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $
+ */
+#ifdef ADDR_RANGE_DEBUG
+#define IIJDEBUG
+#endif
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef IIJDEBUG
+#include
+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
+
+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:
+ * 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);
+ * }
+ *
+ * @author Yasuoka Masahiko
+ * $Id: bytebuf.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $
+ */
+#include
+#include
+#include
+
+#ifdef BYTEBUF_DEBUG
+#include
+#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 NULL
and
+ * errno
is ENOBUFS
+ *
+ * @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 NULL
+ * and errno
is ENOBUFS
+ *
+ * @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 コンフィグヘルパ。
+ *
+ * しています。
+ */
+#include
+#include
+#include
+#include
+
+#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) を読み込むための補助的な関数
+ *
+ * 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);
+ *
+ */
+/* $Id: csvreader.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */
+#include
+#include
+#include
+
+#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;
+}
+
+/**
+ * パース途中のカラムのパースを完了します。
+ *
+ * CSVファイルの末尾など、続く行
+ * が存在しない場合や、CSV としての行の区切りであることが確実な場合に呼び
+ * だします。フィールドが終了していない場合などにはエラーが返ります。
+ */
+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,
+ /**
+ * 処理中の列が存在する
+ *
+ * csvreader_get_columns で取り出す前に、次の行をパースすると
+ * このエラーが発生します。
+ */
+ 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
+#include
+#include
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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
+
+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
+#include
+#include
+#include
+
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ipsec_util.h"
+#include "ipsec_util_local.h"
+
+/**
+ * Delete the IPsec-SA for transport-mode ESP that matches specified sock and
+ * peer.
+ *
+ * For deleting IPsec-SA for NAT-T, port numbers and protocol must
+ * be specified.
+ *
+ * @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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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
+#include
+#include
+#include
+#include
+#include
+
+#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
+
+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
+#include
+#include
+#include
+
+#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
+#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
+#include
+#include
+#include
+#include
+#include
+
+#if !defined(GENERIC_USE) && !defined(_ROUTE_H_)
+#include
+#endif
+
+#ifdef KERNEL
+#include
+#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
+#ifdef GENERIC_USE
+#include
+#include
+#include
+#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
+#include
+#include
+#include
+#include
+
+#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).
+ *
+ * example:
+ *
+ 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);
+ *
+ */
+/* 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "debugutil.h"
+#include "bytebuf.h"
+#include "net_utils.h"
+
+#include
+#include
+#include
+
+#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.
+ *
+ * When use with libevent, call rtev_libevent_init() to initialize this
+ * library.
+ * usage:
+ *
+ * #include
+ * #include
+ * #include
+ * #include
+ * #include
+ *
+ * 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);
+ * }
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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 /* 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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
+
+#include
+#include
+#include
+
+#include "slist.h"
+
+#define GROW_SIZE 256
+#define PTR_SIZE (sizeof(intptr_t))
+
+#ifdef SLIST_DEBUG
+#include
+#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;
+}
+
+/**
+ * シャッフルします。
+ *
+ * slist_shuffle は random(3) を使ってます。使用前に srandom(3) してく
+ * ださい。
+ */
+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
+#include
+#include
+#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
+#include
+#include
+
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "debugutil.h"
+#include "bytebuf.h"
+#include "hash.h"
+#include "slist.h"
+#include "l2tp.h"
+#include "l2tp_subr.h"
+
+#ifndef L2TPD_TEST
+#include
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_LIBSOCKUTIL
+#include
+#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;
+}
+
+/**
+ * コントロールコネクションを終了します。
+ *
+ *
+ * アクティブクローズの (StopCCN を送信する) 場合には、StopCCN の
+ * ResultCode AVP ように result に 1 以上の適切な値を指定してください。
+ *
+ * 戻り値が 0 の場合、_this は解放されていますので、l2tp_ctrl の処理を
+ * 続行することはできません。また、1 の場合(解放されていない場合) は、
+ * タイマはリセットされています。
+ *
+ * @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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_LIBSOCKUTIL
+#include
+#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のサイズをチェックするマクロ。
+ *
+ * 準備
+ *
+ * - エラーメッセージ用の char emes[256] を準備する。
+ *
- reigai ラベルを準備する。
+ *
+ */
+#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
+#include
+#include
+#include
+#include
+#if 0
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_LIBSOCKUTIL
+#include
+#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デーモンインスタンスを初期化します。
+ *
+ * {@link _l2tpd#bind_sin} は、.sin_family = AF_INET、.sin_port = 1701、
+ * .sin_len が設定された状態で返ります。
+ */
+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
+.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
+ *
+ * 対応するオプション
+ *
+ * - MPPE
+ * $Id: ccp.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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 の実装
+ *
+ * RFC 1994 PPP Challenge Handshake Authentication Protocol(CHAP) の実装です。
+ * 現在認証する側の実装のみです。
+ *
+ * 対応しているプロトコルは、
+ *
+ * - MD5-CHAP
+ * - MS-CHAP v2
+ *
です。
+ */
+/* RFC 1994, 2433 */
+// $Id: chap.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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="
+ * - pppd on Mac OS 10.4 hungs up if it received a failure packet
+ * with "M=".
+ * - 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
+ * 1997 - 2001 Brian Somers
+ * 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
+#ifndef LINT
+__COPYRIGHT(
+"@(#) Copyright (c) 1997 Gabor Kincses \n"
+"@(#) 1997 - 2001 Brian Somers \n"
+"@(#) based on work by Eric Rosenquist\n"
+"@(#) Strata Software Limited.\n"
+"@(#) All rights reserved.\n"
+);
+#endif
+#include
+#ifdef __FreeBSD__
+#include
+#include
+#else
+#include
+#include
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include
+#else
+#include
+#endif
+#include
+#endif
+#include
+#include
+#include
+#include
+
+#include
+#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
+ * 1997 - 2001 Brian Somers
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_NPPPD_RADIUS
+#include
+#include
+#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
+ *
+ * 無駄な実装もなく、ほとんど修正せずに使えるので、なるべくオリジナルの
+ * 状態に近いように実装しています。(2005/04 yasuoka)
+ */
+/*
+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
+#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
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * npppd 関連
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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 に関する処理を提供します。
+ *
+ * RFC1661: The Point-to-Point Protocol (PPP)
+ * RFC1570: PPP LCP Extensions
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef WITH_OPENSSL
+#include
+#include
+#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
+#include
+
+#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
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include