1*baaf996cSjsg /* $OpenBSD: pipex.c,v 1.154 2024/06/07 13:43:21 jsg Exp $ */
24bda48b6Syasuoka
3a0ade7e9Syasuoka /*-
4a0ade7e9Syasuoka * Copyright (c) 2009 Internet Initiative Japan Inc.
5a0ade7e9Syasuoka * All rights reserved.
6a0ade7e9Syasuoka *
7a0ade7e9Syasuoka * Redistribution and use in source and binary forms, with or without
8a0ade7e9Syasuoka * modification, are permitted provided that the following conditions
9a0ade7e9Syasuoka * are met:
10a0ade7e9Syasuoka * 1. Redistributions of source code must retain the above copyright
11a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer.
12a0ade7e9Syasuoka * 2. Redistributions in binary form must reproduce the above copyright
13a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer in the
14a0ade7e9Syasuoka * documentation and/or other materials provided with the distribution.
15a0ade7e9Syasuoka *
16a0ade7e9Syasuoka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17a0ade7e9Syasuoka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a0ade7e9Syasuoka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19a0ade7e9Syasuoka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20a0ade7e9Syasuoka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21a0ade7e9Syasuoka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22a0ade7e9Syasuoka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23a0ade7e9Syasuoka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24a0ade7e9Syasuoka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25a0ade7e9Syasuoka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26a0ade7e9Syasuoka * SUCH DAMAGE.
27a0ade7e9Syasuoka */
28a0ade7e9Syasuoka
29a0ade7e9Syasuoka #include <sys/param.h>
30a0ade7e9Syasuoka #include <sys/systm.h>
31a0ade7e9Syasuoka #include <sys/mbuf.h>
32a0ade7e9Syasuoka #include <sys/socket.h>
33a0ade7e9Syasuoka #include <sys/ioctl.h>
3490b03482Syasuoka #include <sys/sysctl.h>
35a0ade7e9Syasuoka #include <sys/syslog.h>
36a0ade7e9Syasuoka #include <sys/conf.h>
37a0ade7e9Syasuoka #include <sys/time.h>
38638c25e3Stedu #include <sys/timeout.h>
39a0ade7e9Syasuoka #include <sys/kernel.h>
40edccd885Suebayasi #include <sys/pool.h>
41df21f681Smvs #include <sys/percpu.h>
42f6e6f8e7Smvs #include <sys/mutex.h>
43a0ade7e9Syasuoka
44a0ade7e9Syasuoka #include <net/if.h>
458876230bShsuenaga #include <net/if_types.h>
46a0ade7e9Syasuoka #include <netinet/in.h>
47a0ade7e9Syasuoka #include <netinet/if_ether.h>
488876230bShsuenaga #include <net/if_dl.h>
49a0ade7e9Syasuoka
50a0ade7e9Syasuoka #include <net/radix.h>
51a0ade7e9Syasuoka #include <net/route.h>
52a0ade7e9Syasuoka #include <net/ppp_defs.h>
53a0ade7e9Syasuoka #include <net/ppp-comp.h>
540cd5c6faSmvs #include <net/netisr.h>
55a0ade7e9Syasuoka
5685d4897fSmarkus #include "pf.h"
5785d4897fSmarkus #if NPF > 0
5885d4897fSmarkus #include <net/pfvar.h>
5985d4897fSmarkus #endif
6085d4897fSmarkus
61a0ade7e9Syasuoka #include "bpfilter.h"
62a0ade7e9Syasuoka #if NBPFILTER > 0
63a0ade7e9Syasuoka #include <net/bpf.h>
64a0ade7e9Syasuoka #endif
65a0ade7e9Syasuoka
66a0ade7e9Syasuoka #include <netinet/ip.h>
67a0ade7e9Syasuoka #include <netinet/ip_var.h>
688876230bShsuenaga #ifdef INET6
698876230bShsuenaga #include <netinet/ip6.h>
708876230bShsuenaga #include <netinet6/ip6_var.h>
718876230bShsuenaga #endif
72a0ade7e9Syasuoka #include <netinet/tcp.h>
73a0ade7e9Syasuoka #include <netinet/udp.h>
748876230bShsuenaga #include <netinet/udp_var.h>
7563d4736dSyasuoka #include <crypto/arc4.h>
7663d4736dSyasuoka
77a0ade7e9Syasuoka #include <net/pipex.h>
78a0ade7e9Syasuoka #include "pipex_local.h"
79a0ade7e9Syasuoka
80f6e6f8e7Smvs struct mutex pipex_list_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
81f6e6f8e7Smvs
82edccd885Suebayasi struct pool pipex_session_pool;
83edccd885Suebayasi struct pool mppe_key_pool;
84edccd885Suebayasi
85a0ade7e9Syasuoka /*
8675e871e2Smvs * Global data
8775e871e2Smvs * Locks used to protect global data
8875e871e2Smvs * A atomic operation
8975e871e2Smvs * I immutable after creation
90f6e6f8e7Smvs * L pipex_list_mtx
91a0ade7e9Syasuoka */
9268b260a2Sclaudio
93317fc6b7Smvs int pipex_enable = 0; /* [A] */
9475e871e2Smvs struct pipex_hash_head
95f6e6f8e7Smvs pipex_session_list, /* [L] master session list */
96f6e6f8e7Smvs pipex_close_wait_list, /* [L] expired session list */
97f6e6f8e7Smvs pipex_peer_addr_hashtable[PIPEX_HASH_SIZE], /* [L] peer's address hash */
98f6e6f8e7Smvs pipex_id_hashtable[PIPEX_HASH_SIZE]; /* [L] peer id hash */
9975e871e2Smvs
100f6e6f8e7Smvs struct radix_node_head *pipex_rd_head4 = NULL; /* [L] */
10168b260a2Sclaudio struct timeout pipex_timer_ch; /* callout timer context */
10275e871e2Smvs int pipex_prune = 1; /* [I] walk list every seconds */
103a0ade7e9Syasuoka
1040cd5c6faSmvs struct mbuf_queue pipexoutq = MBUF_QUEUE_INITIALIZER(
1050cd5c6faSmvs IFQ_MAXLEN, IPL_SOFTNET);
1060cd5c6faSmvs
107a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
10875e871e2Smvs int pipex_debug = 0; /* [A] systcl net.inet.ip.pipex_debug */
109a0ade7e9Syasuoka #endif
110a0ade7e9Syasuoka
111a0ade7e9Syasuoka /* PPP compression == MPPE is assumed, so don't answer CCP Reset-Request. */
112a0ade7e9Syasuoka #define PIPEX_NO_CCP_RESETACK 1
113a0ade7e9Syasuoka
114a0ade7e9Syasuoka /************************************************************************
115a0ade7e9Syasuoka * Core functions
116a0ade7e9Syasuoka ************************************************************************/
117a0ade7e9Syasuoka void
pipex_init(void)118a0ade7e9Syasuoka pipex_init(void)
119a0ade7e9Syasuoka {
1202a747eefSyasuoka int i;
121f930cf62Syasuoka static int pipex_init_done = 0;
122a0ade7e9Syasuoka
123f930cf62Syasuoka if (pipex_init_done++)
124d3be6ca9Sdlg return;
125d3be6ca9Sdlg
12698afcbd2Smpi rn_init(sizeof(struct sockaddr_in6));
12798afcbd2Smpi
1281378bae2Sdlg pool_init(&pipex_session_pool, sizeof(struct pipex_session), 0,
129b450e1dfSmpi IPL_SOFTNET, PR_WAITOK, "ppxss", NULL);
1301378bae2Sdlg pool_init(&mppe_key_pool, PIPEX_MPPE_KEYLEN * PIPEX_MPPE_NOLDKEY, 0,
131b450e1dfSmpi IPL_SOFTNET, PR_WAITOK, "mppekey", NULL);
132edccd885Suebayasi
133a0ade7e9Syasuoka LIST_INIT(&pipex_session_list);
1348876230bShsuenaga LIST_INIT(&pipex_close_wait_list);
135a0ade7e9Syasuoka
1362a747eefSyasuoka for (i = 0; i < nitems(pipex_id_hashtable); i++)
1372a747eefSyasuoka LIST_INIT(&pipex_id_hashtable[i]);
1382a747eefSyasuoka for (i = 0; i < nitems(pipex_peer_addr_hashtable); i++)
1392a747eefSyasuoka LIST_INIT(&pipex_peer_addr_hashtable[i]);
140a0ade7e9Syasuoka }
141a0ade7e9Syasuoka
142a0ade7e9Syasuoka void
pipex_destroy_all_sessions(void * ownersc)143bc65dfeeSyasuoka pipex_destroy_all_sessions(void *ownersc)
144a0ade7e9Syasuoka {
145a672ee3dSclaudio struct pipex_session *session, *session_tmp;
146a0ade7e9Syasuoka
147f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
148bc65dfeeSyasuoka
149a672ee3dSclaudio LIST_FOREACH_SAFE(session, &pipex_session_list, session_list,
150a672ee3dSclaudio session_tmp) {
151aa95fb55Smvs if (session->flags & PIPEX_SFLAGS_ITERATOR)
152aa95fb55Smvs continue;
153bc65dfeeSyasuoka if (session->ownersc == ownersc) {
1541935ef3dSmvs KASSERT((session->flags & PIPEX_SFLAGS_PPPX) == 0);
155f6e6f8e7Smvs pipex_unlink_session_locked(session);
156bc65dfeeSyasuoka pipex_rele_session(session);
157a0ade7e9Syasuoka }
158116d48bfSyasuoka }
159f6e6f8e7Smvs
160f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
161a0ade7e9Syasuoka }
162a0ade7e9Syasuoka
163a0ade7e9Syasuoka int
pipex_ioctl(void * ownersc,u_long cmd,caddr_t data)164bc65dfeeSyasuoka pipex_ioctl(void *ownersc, u_long cmd, caddr_t data)
165a0ade7e9Syasuoka {
166bc65dfeeSyasuoka int ret = 0;
167a0ade7e9Syasuoka
168a0ade7e9Syasuoka switch (cmd) {
169a0ade7e9Syasuoka case PIPEXGSTAT:
170b9b0ec53Sclaudio ret = pipex_get_stat((struct pipex_session_stat_req *)data,
171bc65dfeeSyasuoka ownersc);
172f930cf62Syasuoka break;
173a0ade7e9Syasuoka
174a0ade7e9Syasuoka case PIPEXGCLOSED:
175b9b0ec53Sclaudio ret = pipex_get_closed((struct pipex_session_list_req *)data,
176bc65dfeeSyasuoka ownersc);
177f930cf62Syasuoka break;
178a0ade7e9Syasuoka
179a0ade7e9Syasuoka default:
180f930cf62Syasuoka ret = ENOTTY;
181f930cf62Syasuoka break;
182a0ade7e9Syasuoka }
183f930cf62Syasuoka
184f930cf62Syasuoka return (ret);
185a0ade7e9Syasuoka }
186a0ade7e9Syasuoka
187a0ade7e9Syasuoka /************************************************************************
1880cd5c6faSmvs * Software Interrupt Handler
1890cd5c6faSmvs ************************************************************************/
1900cd5c6faSmvs
1910cd5c6faSmvs void
pipexintr(void)1920cd5c6faSmvs pipexintr(void)
1930cd5c6faSmvs {
1940cd5c6faSmvs struct mbuf_list ml;
1950cd5c6faSmvs struct mbuf *m;
1960cd5c6faSmvs struct pipex_session *session;
1970cd5c6faSmvs
1980cd5c6faSmvs NET_ASSERT_LOCKED();
1990cd5c6faSmvs
2000cd5c6faSmvs mq_delist(&pipexoutq, &ml);
2010cd5c6faSmvs
2020cd5c6faSmvs while ((m = ml_dequeue(&ml)) != NULL) {
2030cd5c6faSmvs struct ifnet *ifp;
2040cd5c6faSmvs
2050cd5c6faSmvs session = m->m_pkthdr.ph_cookie;
2060cd5c6faSmvs
2070cd5c6faSmvs ifp = if_get(session->proto.pppoe.over_ifidx);
2080cd5c6faSmvs if (ifp != NULL) {
2090cd5c6faSmvs struct pipex_pppoe_header *pppoe;
2100cd5c6faSmvs int len;
2110cd5c6faSmvs
2120cd5c6faSmvs pppoe = mtod(m, struct pipex_pppoe_header *);
2130cd5c6faSmvs len = ntohs(pppoe->length);
2140cd5c6faSmvs ifp->if_output(ifp, m, &session->peer.sa, NULL);
2150cd5c6faSmvs counters_pkt(session->stat_counters, pxc_opackets,
2160cd5c6faSmvs pxc_obytes, len);
2170cd5c6faSmvs } else {
2180cd5c6faSmvs m_freem(m);
2190cd5c6faSmvs counters_inc(session->stat_counters, pxc_oerrors);
2200cd5c6faSmvs }
2210cd5c6faSmvs if_put(ifp);
2220cd5c6faSmvs
2230cd5c6faSmvs pipex_rele_session(session);
2240cd5c6faSmvs }
2250cd5c6faSmvs }
2260cd5c6faSmvs
2270cd5c6faSmvs /************************************************************************
228a0ade7e9Syasuoka * Session management functions
229a0ade7e9Syasuoka ************************************************************************/
230d70a8765Smvs int
pipex_init_session(struct pipex_session ** rsession,struct pipex_session_req * req)231d70a8765Smvs pipex_init_session(struct pipex_session **rsession,
232d70a8765Smvs struct pipex_session_req *req)
233a0ade7e9Syasuoka {
234a0ade7e9Syasuoka struct pipex_session *session;
235a0ade7e9Syasuoka #ifdef PIPEX_PPPOE
236a0ade7e9Syasuoka struct ifnet *over_ifp = NULL;
237a0ade7e9Syasuoka #endif
238a0ade7e9Syasuoka
239df8d9afdSjsg /* Checks requested parameters. */
240a0ade7e9Syasuoka switch (req->pr_protocol) {
241a0ade7e9Syasuoka #ifdef PIPEX_PPPOE
242a0ade7e9Syasuoka case PIPEX_PROTO_PPPOE:
243bdfe8fd0Syasuoka if (req->pr_peer_address.ss_family != AF_UNSPEC)
2448876230bShsuenaga return (EINVAL);
245a0ade7e9Syasuoka break;
246a0ade7e9Syasuoka #endif
2478876230bShsuenaga #if defined(PIPEX_L2TP) || defined(PIPEX_PPTP)
248a0ade7e9Syasuoka case PIPEX_PROTO_PPTP:
2498876230bShsuenaga case PIPEX_PROTO_L2TP:
250bdfe8fd0Syasuoka switch (req->pr_peer_address.ss_family) {
2518876230bShsuenaga case AF_INET:
252bdfe8fd0Syasuoka if (req->pr_peer_address.ss_len !=
253bdfe8fd0Syasuoka sizeof(struct sockaddr_in))
2548876230bShsuenaga return (EINVAL);
2558876230bShsuenaga break;
256dcc75356Sdlg #ifdef INET6
257dcc75356Sdlg case AF_INET6:
258bdfe8fd0Syasuoka if (req->pr_peer_address.ss_len !=
259bdfe8fd0Syasuoka sizeof(struct sockaddr_in6))
260dcc75356Sdlg return (EINVAL);
261dcc75356Sdlg break;
262dcc75356Sdlg #endif
2638876230bShsuenaga default:
2648876230bShsuenaga return (EPROTONOSUPPORT);
2658876230bShsuenaga }
266bdfe8fd0Syasuoka if (req->pr_peer_address.ss_family !=
267bdfe8fd0Syasuoka req->pr_local_address.ss_family ||
268bdfe8fd0Syasuoka req->pr_peer_address.ss_len !=
269bdfe8fd0Syasuoka req->pr_local_address.ss_len)
270dcc75356Sdlg return (EINVAL);
271a0ade7e9Syasuoka break;
27294a607b5Smpi #endif /* defined(PIPEX_PPTP) || defined(PIPEX_L2TP) */
273a0ade7e9Syasuoka default:
27463d4736dSyasuoka return (EPROTONOSUPPORT);
275a0ade7e9Syasuoka }
276d70a8765Smvs #ifdef PIPEX_MPPE
277d70a8765Smvs if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) {
278b18f9d9cSbluhm switch (req->pr_mppe_recv.keylenbits) {
279b18f9d9cSbluhm case 40:
280b18f9d9cSbluhm case 56:
281b18f9d9cSbluhm case 128:
282b18f9d9cSbluhm break;
283b18f9d9cSbluhm default:
284d70a8765Smvs return (EINVAL);
285d70a8765Smvs }
286b18f9d9cSbluhm }
287d70a8765Smvs if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) {
288b18f9d9cSbluhm switch (req->pr_mppe_send.keylenbits) {
289b18f9d9cSbluhm case 40:
290b18f9d9cSbluhm case 56:
291b18f9d9cSbluhm case 128:
292b18f9d9cSbluhm break;
293b18f9d9cSbluhm default:
294d70a8765Smvs return (EINVAL);
295d70a8765Smvs }
296b18f9d9cSbluhm }
297d70a8765Smvs if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_REQUIRED) != 0) {
298d70a8765Smvs if ((req->pr_ppp_flags &
299d70a8765Smvs (PIPEX_PPP_MPPE_ACCEPTED | PIPEX_PPP_MPPE_ENABLED)) !=
300d70a8765Smvs (PIPEX_PPP_MPPE_ACCEPTED | PIPEX_PPP_MPPE_ENABLED))
301d70a8765Smvs return (EINVAL);
302d70a8765Smvs }
303d70a8765Smvs #endif
304972a3eacSclaudio
3052f7f6135Smvs #ifdef PIPEX_PPPOE
3062f7f6135Smvs if (req->pr_protocol == PIPEX_PROTO_PPPOE) {
3072f7f6135Smvs over_ifp = if_unit(req->pr_proto.pppoe.over_ifname);
3082f7f6135Smvs if (over_ifp == NULL)
3092f7f6135Smvs return (EINVAL);
3102f7f6135Smvs }
3112f7f6135Smvs #endif
3122f7f6135Smvs
313a0ade7e9Syasuoka /* prepare a new session */
314edccd885Suebayasi session = pool_get(&pipex_session_pool, PR_WAITOK | PR_ZERO);
315f6e6f8e7Smvs refcnt_init(&session->pxs_refcnt);
3162960d3c8Smvs mtx_init(&session->pxs_mtx, IPL_SOFTNET);
317d70a8765Smvs session->state = PIPEX_STATE_INITIAL;
318a0ade7e9Syasuoka session->protocol = req->pr_protocol;
319a0ade7e9Syasuoka session->session_id = req->pr_session_id;
320a0ade7e9Syasuoka session->peer_session_id = req->pr_peer_session_id;
321a0ade7e9Syasuoka session->peer_mru = req->pr_peer_mru;
322a0ade7e9Syasuoka session->timeout_sec = req->pr_timeout_sec;
323a0ade7e9Syasuoka session->ppp_flags = req->pr_ppp_flags;
324a0ade7e9Syasuoka session->ppp_id = req->pr_ppp_id;
325a0ade7e9Syasuoka
326df21f681Smvs session->stat_counters = counters_alloc(pxc_ncounters);
327df21f681Smvs
328a0ade7e9Syasuoka session->ip_address.sin_family = AF_INET;
329a0ade7e9Syasuoka session->ip_address.sin_len = sizeof(struct sockaddr_in);
330a0ade7e9Syasuoka session->ip_address.sin_addr = req->pr_ip_address;
331a0ade7e9Syasuoka
332a0ade7e9Syasuoka session->ip_netmask.sin_family = AF_INET;
333a0ade7e9Syasuoka session->ip_netmask.sin_len = sizeof(struct sockaddr_in);
334a0ade7e9Syasuoka session->ip_netmask.sin_addr = req->pr_ip_netmask;
335a0ade7e9Syasuoka
336a0ade7e9Syasuoka if (session->ip_netmask.sin_addr.s_addr == 0L)
337a0ade7e9Syasuoka session->ip_netmask.sin_addr.s_addr = 0xffffffffL;
338a0ade7e9Syasuoka session->ip_address.sin_addr.s_addr &=
339a0ade7e9Syasuoka session->ip_netmask.sin_addr.s_addr;
340a0ade7e9Syasuoka
341bdfe8fd0Syasuoka if (req->pr_peer_address.ss_len > 0)
342bdfe8fd0Syasuoka memcpy(&session->peer, &req->pr_peer_address,
343bdfe8fd0Syasuoka MIN(req->pr_peer_address.ss_len, sizeof(session->peer)));
344bdfe8fd0Syasuoka if (req->pr_local_address.ss_len > 0)
345bdfe8fd0Syasuoka memcpy(&session->local, &req->pr_local_address,
346bdfe8fd0Syasuoka MIN(req->pr_local_address.ss_len, sizeof(session->local)));
347a0ade7e9Syasuoka #ifdef PIPEX_PPPOE
3482f7f6135Smvs if (req->pr_protocol == PIPEX_PROTO_PPPOE) {
3497790b521Syasuoka session->proto.pppoe.over_ifidx = over_ifp->if_index;
3502f7f6135Smvs if_put(over_ifp);
3512f7f6135Smvs }
352a0ade7e9Syasuoka #endif
353a0ade7e9Syasuoka #ifdef PIPEX_PPTP
354a0ade7e9Syasuoka if (req->pr_protocol == PIPEX_PROTO_PPTP) {
3558876230bShsuenaga struct pipex_pptp_session *sess_pptp = &session->proto.pptp;
3568876230bShsuenaga
3578876230bShsuenaga sess_pptp->snd_gap = 0;
3588876230bShsuenaga sess_pptp->rcv_gap = 0;
3598876230bShsuenaga sess_pptp->snd_una = req->pr_proto.pptp.snd_una;
3608876230bShsuenaga sess_pptp->snd_nxt = req->pr_proto.pptp.snd_nxt;
3618876230bShsuenaga sess_pptp->rcv_nxt = req->pr_proto.pptp.rcv_nxt;
3628876230bShsuenaga sess_pptp->rcv_acked = req->pr_proto.pptp.rcv_acked;
3638876230bShsuenaga
3648876230bShsuenaga sess_pptp->winsz = req->pr_proto.pptp.winsz;
3658876230bShsuenaga sess_pptp->maxwinsz = req->pr_proto.pptp.maxwinsz;
3668876230bShsuenaga sess_pptp->peer_maxwinsz = req->pr_proto.pptp.peer_maxwinsz;
3678876230bShsuenaga /* last ack number */
3688876230bShsuenaga sess_pptp->ul_snd_una = sess_pptp->snd_una - 1;
3698876230bShsuenaga }
3708876230bShsuenaga #endif
3718876230bShsuenaga #ifdef PIPEX_L2TP
3728876230bShsuenaga if (req->pr_protocol == PIPEX_PROTO_L2TP) {
3738876230bShsuenaga struct pipex_l2tp_session *sess_l2tp = &session->proto.l2tp;
3748876230bShsuenaga
3758876230bShsuenaga /* session keys */
3768876230bShsuenaga sess_l2tp->tunnel_id = req->pr_proto.l2tp.tunnel_id;
3778876230bShsuenaga sess_l2tp->peer_tunnel_id = req->pr_proto.l2tp.peer_tunnel_id;
3788876230bShsuenaga
3798876230bShsuenaga /* protocol options */
3808876230bShsuenaga sess_l2tp->option_flags = req->pr_proto.l2tp.option_flags;
3818876230bShsuenaga
3828876230bShsuenaga /* initial state of dynamic context */
3838876230bShsuenaga sess_l2tp->ns_gap = sess_l2tp->nr_gap = 0;
3848876230bShsuenaga sess_l2tp->ns_nxt = req->pr_proto.l2tp.ns_nxt;
3858876230bShsuenaga sess_l2tp->nr_nxt = req->pr_proto.l2tp.nr_nxt;
3868876230bShsuenaga sess_l2tp->ns_una = req->pr_proto.l2tp.ns_una;
3878876230bShsuenaga sess_l2tp->nr_acked = req->pr_proto.l2tp.nr_acked;
3888876230bShsuenaga /* last ack number */
3898876230bShsuenaga sess_l2tp->ul_ns_una = sess_l2tp->ns_una - 1;
390a5018c90Syasuoka sess_l2tp->ipsecflowinfo = req->pr_proto.l2tp.ipsecflowinfo;
391a0ade7e9Syasuoka }
392a0ade7e9Syasuoka #endif
393a0ade7e9Syasuoka #ifdef PIPEX_MPPE
394de0a2dd6Syasuoka if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) {
395e405d423Syasuoka pipex_session_init_mppe_recv(session,
396e405d423Syasuoka req->pr_mppe_recv.stateless, req->pr_mppe_recv.keylenbits,
397e405d423Syasuoka req->pr_mppe_recv.master_key);
398de0a2dd6Syasuoka }
399de0a2dd6Syasuoka if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) {
400e405d423Syasuoka pipex_session_init_mppe_send(session,
401e405d423Syasuoka req->pr_mppe_send.stateless, req->pr_mppe_send.keylenbits,
402e405d423Syasuoka req->pr_mppe_send.master_key);
403de0a2dd6Syasuoka }
404a0ade7e9Syasuoka #endif
405a0ade7e9Syasuoka
406d70a8765Smvs *rsession = session;
407d70a8765Smvs
408d70a8765Smvs return 0;
409a0ade7e9Syasuoka }
410a0ade7e9Syasuoka
411d70a8765Smvs void
pipex_rele_session(struct pipex_session * session)412d70a8765Smvs pipex_rele_session(struct pipex_session *session)
413d70a8765Smvs {
414f6e6f8e7Smvs if (refcnt_rele(&session->pxs_refcnt) == 0)
415f6e6f8e7Smvs return;
416f6e6f8e7Smvs
417d70a8765Smvs if (session->mppe_recv.old_session_keys)
418d70a8765Smvs pool_put(&mppe_key_pool, session->mppe_recv.old_session_keys);
419df21f681Smvs counters_free(session->stat_counters, pxc_ncounters);
420edccd885Suebayasi pool_put(&pipex_session_pool, session);
4218876230bShsuenaga }
422a0ade7e9Syasuoka
423d70a8765Smvs int
pipex_link_session(struct pipex_session * session,struct ifnet * ifp,void * ownersc)424bc65dfeeSyasuoka pipex_link_session(struct pipex_session *session, struct ifnet *ifp,
425bc65dfeeSyasuoka void *ownersc)
426d70a8765Smvs {
427d70a8765Smvs struct pipex_hash_head *chain;
428bc65dfeeSyasuoka struct radix_node *rn;
429f6e6f8e7Smvs int error = 0;
430d70a8765Smvs
431f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
43275e871e2Smvs
433bc65dfeeSyasuoka if (pipex_rd_head4 == NULL) {
434bc65dfeeSyasuoka if (!rn_inithead((void **)&pipex_rd_head4,
435bc65dfeeSyasuoka offsetof(struct sockaddr_in, sin_addr)))
436bc65dfeeSyasuoka panic("rn_inithead() failed on pipex_link_session()");
437bc65dfeeSyasuoka }
438f6e6f8e7Smvs if (pipex_lookup_by_session_id_locked(session->protocol,
439f6e6f8e7Smvs session->session_id)) {
440f6e6f8e7Smvs error = EEXIST;
441f6e6f8e7Smvs goto out;
442f6e6f8e7Smvs }
443d70a8765Smvs
444bc65dfeeSyasuoka session->ownersc = ownersc;
445bc65dfeeSyasuoka session->ifindex = ifp->if_index;
446bc65dfeeSyasuoka if (ifp->if_flags & IFF_POINTOPOINT)
4471935ef3dSmvs session->flags |= PIPEX_SFLAGS_PPPX;
448bc65dfeeSyasuoka
4491935ef3dSmvs if ((session->flags & PIPEX_SFLAGS_PPPX) == 0 &&
450bc65dfeeSyasuoka !in_nullhost(session->ip_address.sin_addr)) {
451f6e6f8e7Smvs if (pipex_lookup_by_ip_address_locked(
452f6e6f8e7Smvs session->ip_address.sin_addr) != NULL) {
453f6e6f8e7Smvs error = EADDRINUSE;
454f6e6f8e7Smvs goto out;
455f6e6f8e7Smvs }
456bc65dfeeSyasuoka rn = rn_addroute(&session->ip_address, &session->ip_netmask,
457bc65dfeeSyasuoka pipex_rd_head4, session->ps4_rn, RTP_STATIC);
458f6e6f8e7Smvs if (rn == NULL) {
459f6e6f8e7Smvs error = ENOMEM;
460f6e6f8e7Smvs goto out;
461f6e6f8e7Smvs }
462bc65dfeeSyasuoka }
463d70a8765Smvs
464d70a8765Smvs LIST_INSERT_HEAD(&pipex_session_list, session, session_list);
465a0ade7e9Syasuoka chain = PIPEX_ID_HASHTABLE(session->session_id);
466a0ade7e9Syasuoka LIST_INSERT_HEAD(chain, session, id_chain);
46794a607b5Smpi #if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
468d70a8765Smvs switch (session->protocol) {
4698876230bShsuenaga case PIPEX_PROTO_PPTP:
4708876230bShsuenaga case PIPEX_PROTO_L2TP:
471a0ade7e9Syasuoka chain = PIPEX_PEER_ADDR_HASHTABLE(
4729c681c75Sbluhm pipex_sockaddr_hash_key(&session->peer.sa));
473a0ade7e9Syasuoka LIST_INSERT_HEAD(chain, session, peer_addr_chain);
474a0ade7e9Syasuoka }
47594a607b5Smpi #endif
476a0ade7e9Syasuoka
477a0ade7e9Syasuoka /* if first session is added, start timer */
478a0ade7e9Syasuoka if (LIST_NEXT(session, session_list) == NULL)
479a0ade7e9Syasuoka pipex_timer_start();
480d70a8765Smvs session->state = PIPEX_STATE_OPENED;
481d70a8765Smvs
482f6e6f8e7Smvs out:
483f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
484f6e6f8e7Smvs
485f6e6f8e7Smvs return error;
486d70a8765Smvs }
487d70a8765Smvs
488d70a8765Smvs void
pipex_unlink_session_locked(struct pipex_session * session)489f6e6f8e7Smvs pipex_unlink_session_locked(struct pipex_session *session)
490d70a8765Smvs {
491bc65dfeeSyasuoka struct radix_node *rn;
492bc65dfeeSyasuoka
493f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
494f6e6f8e7Smvs
495002baba0Smvs session->ifindex = 0;
496002baba0Smvs
497bc65dfeeSyasuoka if (session->state == PIPEX_STATE_CLOSED)
498bc65dfeeSyasuoka return;
4991935ef3dSmvs if ((session->flags & PIPEX_SFLAGS_PPPX) == 0 &&
500bc65dfeeSyasuoka !in_nullhost(session->ip_address.sin_addr)) {
501bc65dfeeSyasuoka KASSERT(pipex_rd_head4 != NULL);
502bc65dfeeSyasuoka rn = rn_delete(&session->ip_address, &session->ip_netmask,
503bc65dfeeSyasuoka pipex_rd_head4, (struct radix_node *)session);
504bc65dfeeSyasuoka KASSERT(rn != NULL);
505bc65dfeeSyasuoka }
506bc65dfeeSyasuoka
507d70a8765Smvs LIST_REMOVE(session, id_chain);
508d70a8765Smvs #if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
509d70a8765Smvs switch (session->protocol) {
510d70a8765Smvs case PIPEX_PROTO_PPTP:
511d70a8765Smvs case PIPEX_PROTO_L2TP:
512d70a8765Smvs LIST_REMOVE(session, peer_addr_chain);
513d70a8765Smvs break;
514d70a8765Smvs }
515d70a8765Smvs #endif
5160dd8107dSmvs if (session->state == PIPEX_STATE_CLOSE_WAIT)
5170dd8107dSmvs LIST_REMOVE(session, state_list);
518d70a8765Smvs LIST_REMOVE(session, session_list);
5190dd8107dSmvs session->state = PIPEX_STATE_CLOSED;
520d70a8765Smvs
521d70a8765Smvs /* if final session is destroyed, stop timer */
522d70a8765Smvs if (LIST_EMPTY(&pipex_session_list))
523d70a8765Smvs pipex_timer_stop();
524d70a8765Smvs }
525d70a8765Smvs
526f6e6f8e7Smvs void
pipex_unlink_session(struct pipex_session * session)527f6e6f8e7Smvs pipex_unlink_session(struct pipex_session *session)
528f6e6f8e7Smvs {
529f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
530f6e6f8e7Smvs pipex_unlink_session_locked(session);
531f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
532f6e6f8e7Smvs }
533f6e6f8e7Smvs
534a0ade7e9Syasuoka int
pipex_notify_close_session(struct pipex_session * session)535a0ade7e9Syasuoka pipex_notify_close_session(struct pipex_session *session)
536a0ade7e9Syasuoka {
537f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
538f6e6f8e7Smvs
539a0ade7e9Syasuoka session->state = PIPEX_STATE_CLOSE_WAIT;
540df21f681Smvs session->idle_time = 0;
541a0ade7e9Syasuoka LIST_INSERT_HEAD(&pipex_close_wait_list, session, state_list);
542a0ade7e9Syasuoka
54363d4736dSyasuoka return (0);
544a0ade7e9Syasuoka }
545a0ade7e9Syasuoka
546df21f681Smvs void
pipex_export_session_stats(struct pipex_session * session,struct pipex_statistics * stats)547df21f681Smvs pipex_export_session_stats(struct pipex_session *session,
548df21f681Smvs struct pipex_statistics *stats)
549df21f681Smvs {
550df21f681Smvs uint64_t counters[pxc_ncounters];
551df21f681Smvs
552df21f681Smvs memset(stats, 0, sizeof(*stats));
553df21f681Smvs
554bf0d449cSmpi counters_read(session->stat_counters, counters, pxc_ncounters, NULL);
555df21f681Smvs stats->ipackets = counters[pxc_ipackets];
556df21f681Smvs stats->ierrors = counters[pxc_ierrors];
557df21f681Smvs stats->ibytes = counters[pxc_ibytes];
558df21f681Smvs stats->opackets = counters[pxc_opackets];
559df21f681Smvs stats->oerrors = counters[pxc_oerrors];
560df21f681Smvs stats->obytes = counters[pxc_obytes];
561df21f681Smvs stats->idle_time = session->idle_time;
562df21f681Smvs }
563df21f681Smvs
564c02a3381Smvs int
pipex_get_stat(struct pipex_session_stat_req * req,void * ownersc)565bc65dfeeSyasuoka pipex_get_stat(struct pipex_session_stat_req *req, void *ownersc)
566a0ade7e9Syasuoka {
567a0ade7e9Syasuoka struct pipex_session *session;
568f6e6f8e7Smvs int error = 0;
569a0ade7e9Syasuoka
570a0ade7e9Syasuoka session = pipex_lookup_by_session_id(req->psr_protocol,
571a0ade7e9Syasuoka req->psr_session_id);
572b9b0ec53Sclaudio if (session == NULL)
57363d4736dSyasuoka return (EINVAL);
574a0ade7e9Syasuoka
575f6e6f8e7Smvs if (session->ownersc == ownersc)
576f6e6f8e7Smvs pipex_export_session_stats(session, &req->psr_stat);
577f6e6f8e7Smvs else
578f6e6f8e7Smvs error = EINVAL;
579f6e6f8e7Smvs
580f6e6f8e7Smvs pipex_rele_session(session);
581f6e6f8e7Smvs
582f6e6f8e7Smvs return error;
583a0ade7e9Syasuoka }
584a0ade7e9Syasuoka
585c02a3381Smvs int
pipex_get_closed(struct pipex_session_list_req * req,void * ownersc)586bc65dfeeSyasuoka pipex_get_closed(struct pipex_session_list_req *req, void *ownersc)
587a0ade7e9Syasuoka {
588a672ee3dSclaudio struct pipex_session *session, *session_tmp;
589a0ade7e9Syasuoka
59063d4736dSyasuoka bzero(req, sizeof(*req));
591f6e6f8e7Smvs
592f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
593f6e6f8e7Smvs
594a672ee3dSclaudio LIST_FOREACH_SAFE(session, &pipex_close_wait_list, state_list,
595a672ee3dSclaudio session_tmp) {
596aa95fb55Smvs if (session->flags & PIPEX_SFLAGS_ITERATOR)
597aa95fb55Smvs continue;
598bc65dfeeSyasuoka if (session->ownersc != ownersc)
599b9b0ec53Sclaudio continue;
600a0ade7e9Syasuoka req->plr_ppp_id[req->plr_ppp_id_count++] = session->ppp_id;
6011938ab6fSyasuoka LIST_REMOVE(session, state_list);
6021938ab6fSyasuoka session->state = PIPEX_STATE_CLOSE_WAIT2;
603a0ade7e9Syasuoka if (req->plr_ppp_id_count >= PIPEX_MAX_LISTREQ) {
6041938ab6fSyasuoka if (!LIST_EMPTY(&pipex_close_wait_list))
605a0ade7e9Syasuoka req->plr_flags |= PIPEX_LISTREQ_MORE;
606a0ade7e9Syasuoka break;
607a0ade7e9Syasuoka }
608a0ade7e9Syasuoka }
609a0ade7e9Syasuoka
610f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
611f6e6f8e7Smvs
61263d4736dSyasuoka return (0);
613a0ade7e9Syasuoka }
614a0ade7e9Syasuoka
615c02a3381Smvs struct pipex_session *
pipex_lookup_by_ip_address_locked(struct in_addr addr)616f6e6f8e7Smvs pipex_lookup_by_ip_address_locked(struct in_addr addr)
617a0ade7e9Syasuoka {
618a0ade7e9Syasuoka struct pipex_session *session;
61963d4736dSyasuoka struct sockaddr_in pipex_in4, pipex_in4mask;
620a0ade7e9Syasuoka
621f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
622f6e6f8e7Smvs
623bc65dfeeSyasuoka if (pipex_rd_head4 == NULL)
624bc65dfeeSyasuoka return (NULL);
62550bc8e1fSyasuoka bzero(&pipex_in4, sizeof(pipex_in4));
626a0ade7e9Syasuoka pipex_in4.sin_addr = addr;
62763d4736dSyasuoka pipex_in4.sin_family = AF_INET;
62863d4736dSyasuoka pipex_in4.sin_len = sizeof(pipex_in4);
62950bc8e1fSyasuoka
63050bc8e1fSyasuoka bzero(&pipex_in4mask, sizeof(pipex_in4mask));
63163d4736dSyasuoka pipex_in4mask.sin_addr.s_addr = htonl(0xFFFFFFFFL);
63263d4736dSyasuoka pipex_in4mask.sin_family = AF_INET;
63363d4736dSyasuoka pipex_in4mask.sin_len = sizeof(pipex_in4mask);
634a0ade7e9Syasuoka
635efe187c6Smpi session = (struct pipex_session *)rn_lookup(&pipex_in4, &pipex_in4mask,
636a718d138Syasuoka pipex_rd_head4);
637a0ade7e9Syasuoka
638a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
639bbcf0337Smpi if (session == NULL) {
640bbcf0337Smpi char buf[INET_ADDRSTRLEN];
641bbcf0337Smpi
642a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found (addr=%s)",
643bbcf0337Smpi __func__, inet_ntop(AF_INET, &addr, buf, sizeof(buf))));
644bbcf0337Smpi }
645a0ade7e9Syasuoka #endif
646a0ade7e9Syasuoka
64763d4736dSyasuoka return (session);
648a0ade7e9Syasuoka }
649a0ade7e9Syasuoka
650f6e6f8e7Smvs struct pipex_session *
pipex_lookup_by_ip_address(struct in_addr addr)651f6e6f8e7Smvs pipex_lookup_by_ip_address(struct in_addr addr)
652f6e6f8e7Smvs {
653f6e6f8e7Smvs struct pipex_session *session;
654f6e6f8e7Smvs
655f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
656f6e6f8e7Smvs
657f6e6f8e7Smvs session = pipex_lookup_by_ip_address_locked(addr);
658f6e6f8e7Smvs if (session != NULL)
659f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt);
660f6e6f8e7Smvs
661f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
662f6e6f8e7Smvs
663f6e6f8e7Smvs return (session);
664f6e6f8e7Smvs }
665f6e6f8e7Smvs
666f6e6f8e7Smvs
667c02a3381Smvs struct pipex_session *
pipex_lookup_by_session_id_locked(int protocol,int session_id)668f6e6f8e7Smvs pipex_lookup_by_session_id_locked(int protocol, int session_id)
669a0ade7e9Syasuoka {
670a0ade7e9Syasuoka struct pipex_hash_head *list;
671a0ade7e9Syasuoka struct pipex_session *session;
672a0ade7e9Syasuoka
673f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
674f6e6f8e7Smvs
675a0ade7e9Syasuoka list = PIPEX_ID_HASHTABLE(session_id);
676a0ade7e9Syasuoka LIST_FOREACH(session, list, id_chain) {
677a0ade7e9Syasuoka if (session->protocol == protocol &&
678a0ade7e9Syasuoka session->session_id == session_id)
679a0ade7e9Syasuoka break;
680a0ade7e9Syasuoka }
681a0ade7e9Syasuoka
682a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
683a0ade7e9Syasuoka if (session == NULL)
684a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
685a0ade7e9Syasuoka "<%s> session not found (session_id=%d)", __func__,
686a0ade7e9Syasuoka session_id));
687a0ade7e9Syasuoka #endif
688a0ade7e9Syasuoka
68963d4736dSyasuoka return (session);
690a0ade7e9Syasuoka }
691a0ade7e9Syasuoka
692f6e6f8e7Smvs struct pipex_session *
pipex_lookup_by_session_id(int protocol,int session_id)693f6e6f8e7Smvs pipex_lookup_by_session_id(int protocol, int session_id)
694f6e6f8e7Smvs {
695f6e6f8e7Smvs struct pipex_session *session;
696f6e6f8e7Smvs
697f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
698f6e6f8e7Smvs
699f6e6f8e7Smvs session = pipex_lookup_by_session_id_locked(protocol, session_id);
700f6e6f8e7Smvs if (session != NULL)
701f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt);
702f6e6f8e7Smvs
703f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
704f6e6f8e7Smvs
705f6e6f8e7Smvs return (session);
706f6e6f8e7Smvs }
707f6e6f8e7Smvs
708a0ade7e9Syasuoka /***********************************************************************
709a0ade7e9Syasuoka * Timer functions
710a0ade7e9Syasuoka ***********************************************************************/
711c02a3381Smvs void
pipex_timer_start(void)712a0ade7e9Syasuoka pipex_timer_start(void)
713a0ade7e9Syasuoka {
714651f1b45Smvs timeout_set_flags(&pipex_timer_ch, pipex_timer, NULL,
715651f1b45Smvs KCLOCK_NONE, TIMEOUT_PROC | TIMEOUT_MPSAFE);
716a0ade7e9Syasuoka timeout_add_sec(&pipex_timer_ch, pipex_prune);
717a0ade7e9Syasuoka }
718a0ade7e9Syasuoka
719c02a3381Smvs void
pipex_timer_stop(void)720a0ade7e9Syasuoka pipex_timer_stop(void)
721a0ade7e9Syasuoka {
722a0ade7e9Syasuoka timeout_del(&pipex_timer_ch);
723a0ade7e9Syasuoka }
724a0ade7e9Syasuoka
725c02a3381Smvs void
pipex_timer(void * ignored_arg)726a0ade7e9Syasuoka pipex_timer(void *ignored_arg)
727a0ade7e9Syasuoka {
728a672ee3dSclaudio struct pipex_session *session, *session_tmp;
729a0ade7e9Syasuoka
730f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
731a0ade7e9Syasuoka /* walk through */
732a672ee3dSclaudio LIST_FOREACH_SAFE(session, &pipex_session_list, session_list,
733a672ee3dSclaudio session_tmp) {
734aa95fb55Smvs if (session->flags & PIPEX_SFLAGS_ITERATOR)
735aa95fb55Smvs continue;
736a0ade7e9Syasuoka switch (session->state) {
737a0ade7e9Syasuoka case PIPEX_STATE_OPENED:
738a0ade7e9Syasuoka if (session->timeout_sec == 0)
739a0ade7e9Syasuoka continue;
740a0ade7e9Syasuoka
741df21f681Smvs session->idle_time++;
742df21f681Smvs if (session->idle_time < session->timeout_sec)
743a0ade7e9Syasuoka continue;
744a0ade7e9Syasuoka
745a0ade7e9Syasuoka pipex_notify_close_session(session);
746a0ade7e9Syasuoka break;
747a0ade7e9Syasuoka
748a0ade7e9Syasuoka case PIPEX_STATE_CLOSE_WAIT:
7491938ab6fSyasuoka case PIPEX_STATE_CLOSE_WAIT2:
750bc65dfeeSyasuoka /* Waiting PIPEXDSESSION from userland */
751df21f681Smvs session->idle_time++;
752df21f681Smvs if (session->idle_time < PIPEX_CLOSE_TIMEOUT)
753a0ade7e9Syasuoka continue;
754bc65dfeeSyasuoka /* Release the sessions when timeout */
755f6e6f8e7Smvs pipex_unlink_session_locked(session);
7561935ef3dSmvs KASSERTMSG((session->flags & PIPEX_SFLAGS_PPPX) == 0,
757bc65dfeeSyasuoka "FIXME session must not be released when pppx");
758bc65dfeeSyasuoka pipex_rele_session(session);
759a0ade7e9Syasuoka break;
760a0ade7e9Syasuoka
761a0ade7e9Syasuoka default:
762a0ade7e9Syasuoka break;
763a0ade7e9Syasuoka }
764a0ade7e9Syasuoka }
765a0ade7e9Syasuoka
766651f1b45Smvs if (LIST_FIRST(&pipex_session_list))
767651f1b45Smvs timeout_add_sec(&pipex_timer_ch, pipex_prune);
768651f1b45Smvs
769f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
770a0ade7e9Syasuoka }
771a0ade7e9Syasuoka
772a0ade7e9Syasuoka /***********************************************************************
773a0ade7e9Syasuoka * Common network I/O functions. (tunnel protocol independent)
774a0ade7e9Syasuoka ***********************************************************************/
775aa95fb55Smvs
776aa95fb55Smvs struct pipex_session *
pipex_iterator(struct pipex_session * session,struct pipex_session_iterator * iter,void * ownersc)777aa95fb55Smvs pipex_iterator(struct pipex_session *session,
778aa95fb55Smvs struct pipex_session_iterator *iter, void *ownersc)
779aa95fb55Smvs {
780aa95fb55Smvs struct pipex_session *session_tmp;
781aa95fb55Smvs
782aa95fb55Smvs mtx_enter(&pipex_list_mtx);
783aa95fb55Smvs
784aa95fb55Smvs if (session)
785aa95fb55Smvs session_tmp = LIST_NEXT(session, session_list);
786aa95fb55Smvs else
787aa95fb55Smvs session_tmp = LIST_FIRST(&pipex_session_list);
788aa95fb55Smvs
789aa95fb55Smvs while (session_tmp) {
790aa95fb55Smvs if (session_tmp->flags & PIPEX_SFLAGS_ITERATOR)
791aa95fb55Smvs goto next;
792aa95fb55Smvs if (session_tmp->ownersc != ownersc)
793aa95fb55Smvs goto next;
794aa95fb55Smvs break;
795aa95fb55Smvs next:
796aa95fb55Smvs session_tmp = LIST_NEXT(session_tmp, session_list);
797aa95fb55Smvs }
798aa95fb55Smvs
799aa95fb55Smvs if (session)
800aa95fb55Smvs LIST_REMOVE(iter, session_list);
801aa95fb55Smvs
802aa95fb55Smvs if (session_tmp) {
803aa95fb55Smvs LIST_INSERT_AFTER(session_tmp,
804aa95fb55Smvs (struct pipex_session *)&iter, session_list);
805aa95fb55Smvs refcnt_take(&session_tmp->pxs_refcnt);
806aa95fb55Smvs }
807aa95fb55Smvs
808aa95fb55Smvs mtx_leave(&pipex_list_mtx);
809aa95fb55Smvs
810aa95fb55Smvs if (session)
811aa95fb55Smvs pipex_rele_session(session);
812aa95fb55Smvs
813aa95fb55Smvs return (session_tmp);
814aa95fb55Smvs }
815aa95fb55Smvs
816c02a3381Smvs void
pipex_ip_output(struct mbuf * m0,struct pipex_session * session)817a0ade7e9Syasuoka pipex_ip_output(struct mbuf *m0, struct pipex_session *session)
818a0ade7e9Syasuoka {
819a0ade7e9Syasuoka int is_idle;
820a0ade7e9Syasuoka
8211935ef3dSmvs if ((session->flags & PIPEX_SFLAGS_MULTICAST) == 0) {
822a0ade7e9Syasuoka /*
823a0ade7e9Syasuoka * Multicast packet is a idle packet and it's not TCP.
824a0ade7e9Syasuoka */
82534e858ecSmvs
826a0ade7e9Syasuoka /* reset idle timer */
827a0ade7e9Syasuoka if (session->timeout_sec != 0) {
828a0ade7e9Syasuoka is_idle = 0;
829a0ade7e9Syasuoka m0 = ip_is_idle_packet(m0, &is_idle);
830a0ade7e9Syasuoka if (m0 == NULL)
831b3d47573Sdlg goto dropped;
832f6e6f8e7Smvs if (is_idle == 0) {
833f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
834a0ade7e9Syasuoka /* update expire time */
835a5953208Smvs if (session->state == PIPEX_STATE_OPENED)
836df21f681Smvs session->idle_time = 0;
837f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
838f6e6f8e7Smvs }
839a0ade7e9Syasuoka }
840a0ade7e9Syasuoka
841a0ade7e9Syasuoka /* adjust tcpmss */
842a0ade7e9Syasuoka if ((session->ppp_flags & PIPEX_PPP_ADJUST_TCPMSS) != 0) {
843a0ade7e9Syasuoka m0 = adjust_tcp_mss(m0, session->peer_mru);
844a0ade7e9Syasuoka if (m0 == NULL)
845b3d47573Sdlg goto dropped;
846a0ade7e9Syasuoka }
84753c2c62eSmvs
84853c2c62eSmvs pipex_ppp_output(m0, session, PPP_IP);
84953c2c62eSmvs } else {
850aa95fb55Smvs struct pipex_session_iterator iter = {
851aa95fb55Smvs .flags = PIPEX_SFLAGS_ITERATOR,
852aa95fb55Smvs };
853aa95fb55Smvs
85453c2c62eSmvs struct pipex_session *session_tmp;
85553c2c62eSmvs struct mbuf *m;
85653c2c62eSmvs
857a0ade7e9Syasuoka m0->m_flags &= ~(M_BCAST|M_MCAST);
858a0ade7e9Syasuoka
859aa95fb55Smvs session_tmp = pipex_iterator(NULL, &iter, session->ownersc);
860aa95fb55Smvs while (session_tmp) {
86153c2c62eSmvs m = m_copym(m0, 0, M_COPYALL, M_NOWAIT);
8629e0c672eSmvs if (m != NULL)
86353c2c62eSmvs pipex_ppp_output(m, session_tmp, PPP_IP);
8649e0c672eSmvs else
8659e0c672eSmvs counters_inc(session_tmp->stat_counters,
8669e0c672eSmvs pxc_oerrors);
8679e0c672eSmvs
868aa95fb55Smvs session_tmp = pipex_iterator(session_tmp,
869aa95fb55Smvs &iter, session->ownersc);
87053c2c62eSmvs }
8719e0c672eSmvs
87253c2c62eSmvs m_freem(m0);
87353c2c62eSmvs }
874a0ade7e9Syasuoka
875a0ade7e9Syasuoka return;
876b3d47573Sdlg dropped:
877df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
878a0ade7e9Syasuoka }
879a0ade7e9Syasuoka
880c02a3381Smvs void
pipex_ppp_output(struct mbuf * m0,struct pipex_session * session,int proto)881a0ade7e9Syasuoka pipex_ppp_output(struct mbuf *m0, struct pipex_session *session, int proto)
882a0ade7e9Syasuoka {
883a0ade7e9Syasuoka u_char *cp, hdr[16];
884a0ade7e9Syasuoka
885a0ade7e9Syasuoka #ifdef PIPEX_MPPE
886a0ade7e9Syasuoka if (pipex_session_is_mppe_enabled(session)) {
88737f64355Syasuoka if (proto == PPP_IP) {
888707c5f41Smvs m0 = pipex_mppe_output(m0, session, PPP_IP);
889707c5f41Smvs if (m0 == NULL)
890707c5f41Smvs goto drop;
891707c5f41Smvs
892707c5f41Smvs proto = PPP_COMP;
89337f64355Syasuoka }
894a0ade7e9Syasuoka }
895a0ade7e9Syasuoka #endif /* PIPEX_MPPE */
896a0ade7e9Syasuoka cp = hdr;
897c3479817Syasuoka if (session->protocol != PIPEX_PROTO_PPPOE) {
898c3479817Syasuoka /* PPPoE has not address and control field */
899a0ade7e9Syasuoka PUTCHAR(PPP_ALLSTATIONS, cp);
900a0ade7e9Syasuoka PUTCHAR(PPP_UI, cp);
901c3479817Syasuoka }
902a0ade7e9Syasuoka PUTSHORT(proto, cp);
903a0ade7e9Syasuoka
904a0ade7e9Syasuoka M_PREPEND(m0, cp - hdr, M_NOWAIT);
905a0ade7e9Syasuoka if (m0 == NULL)
906a0ade7e9Syasuoka goto drop;
907a0ade7e9Syasuoka memcpy(mtod(m0, u_char *), hdr, cp - hdr);
908a0ade7e9Syasuoka
909a0ade7e9Syasuoka switch (session->protocol) {
910a0ade7e9Syasuoka #ifdef PIPEX_PPPOE
911a0ade7e9Syasuoka case PIPEX_PROTO_PPPOE:
912a0ade7e9Syasuoka pipex_pppoe_output(m0, session);
913a0ade7e9Syasuoka break;
914a0ade7e9Syasuoka #endif
915a0ade7e9Syasuoka #ifdef PIPEX_PPTP
916a0ade7e9Syasuoka case PIPEX_PROTO_PPTP:
917707c5f41Smvs mtx_enter(&session->pxs_mtx);
918a0ade7e9Syasuoka pipex_pptp_output(m0, session, 1, 1);
919707c5f41Smvs mtx_leave(&session->pxs_mtx);
920a0ade7e9Syasuoka break;
921a0ade7e9Syasuoka #endif
9228876230bShsuenaga #ifdef PIPEX_L2TP
9238876230bShsuenaga case PIPEX_PROTO_L2TP:
9248876230bShsuenaga pipex_l2tp_output(m0, session);
9258876230bShsuenaga break;
9268876230bShsuenaga #endif
927a0ade7e9Syasuoka default:
928a0ade7e9Syasuoka goto drop;
929a0ade7e9Syasuoka }
930a0ade7e9Syasuoka
931a0ade7e9Syasuoka return;
932a0ade7e9Syasuoka drop:
933a0ade7e9Syasuoka m_freem(m0);
934df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
935a0ade7e9Syasuoka }
936a0ade7e9Syasuoka
937c02a3381Smvs void
pipex_ppp_input(struct mbuf * m0,struct pipex_session * session,int decrypted)938a0ade7e9Syasuoka pipex_ppp_input(struct mbuf *m0, struct pipex_session *session, int decrypted)
939a0ade7e9Syasuoka {
940a0ade7e9Syasuoka int proto, hlen = 0;
941a19b4e4eSyasuoka struct mbuf *n;
942a0ade7e9Syasuoka
943707c5f41Smvs #ifdef PIPEX_MPPE
944707c5f41Smvs again:
945707c5f41Smvs #endif
946707c5f41Smvs
947363bb3c1Syasuoka KASSERT(m0->m_pkthdr.len >= PIPEX_PPPMINLEN);
9488876230bShsuenaga proto = pipex_ppp_proto(m0, session, 0, &hlen);
949a0ade7e9Syasuoka #ifdef PIPEX_MPPE
950fcff09efSyasuoka if (proto == PPP_COMP) {
951a0ade7e9Syasuoka if (decrypted)
952a0ade7e9Syasuoka goto drop;
953fcff09efSyasuoka
954fcff09efSyasuoka /* checked this on ppp_common_input() already. */
955fcff09efSyasuoka KASSERT(pipex_session_is_mppe_accepted(session));
956fcff09efSyasuoka
957a0ade7e9Syasuoka m_adj(m0, hlen);
958707c5f41Smvs m0 = pipex_mppe_input(m0, session);
959707c5f41Smvs if (m0 == NULL)
960707c5f41Smvs goto drop;
961707c5f41Smvs decrypted = 1;
962707c5f41Smvs
963707c5f41Smvs goto again;
9648876230bShsuenaga }
9658876230bShsuenaga if (proto == PPP_CCP) {
9668876230bShsuenaga if (decrypted)
9678876230bShsuenaga goto drop;
9688876230bShsuenaga
9698876230bShsuenaga #if NBPFILTER > 0
9708876230bShsuenaga {
971002baba0Smvs struct ifnet *ifp;
972002baba0Smvs
973002baba0Smvs if ((ifp = if_get(session->ifindex)) != NULL) {
9748876230bShsuenaga if (ifp->if_bpf && ifp->if_type == IFT_PPP)
9758876230bShsuenaga bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_IN);
9768876230bShsuenaga }
977002baba0Smvs if_put(ifp);
978002baba0Smvs }
979a0ade7e9Syasuoka #endif
9808876230bShsuenaga m_adj(m0, hlen);
9818876230bShsuenaga pipex_ccp_input(m0, session);
9828876230bShsuenaga return;
9838876230bShsuenaga }
9848876230bShsuenaga #endif
985a19b4e4eSyasuoka m_adj(m0, hlen);
986a19b4e4eSyasuoka if (!ALIGNED_POINTER(mtod(m0, caddr_t), uint32_t)) {
987a19b4e4eSyasuoka n = m_dup_pkt(m0, 0, M_NOWAIT);
988a19b4e4eSyasuoka if (n == NULL)
989a19b4e4eSyasuoka goto drop;
990a19b4e4eSyasuoka m_freem(m0);
991a19b4e4eSyasuoka m0 = n;
992a19b4e4eSyasuoka }
993a19b4e4eSyasuoka
9948876230bShsuenaga switch (proto) {
995a0ade7e9Syasuoka case PPP_IP:
996a0ade7e9Syasuoka if (!decrypted && pipex_session_is_mppe_required(session))
997a0ade7e9Syasuoka /*
998a0ade7e9Syasuoka * if ip packet received when mppe
999a0ade7e9Syasuoka * is required, discard it.
1000a0ade7e9Syasuoka */
1001a0ade7e9Syasuoka goto drop;
1002a0ade7e9Syasuoka pipex_ip_input(m0, session);
1003a0ade7e9Syasuoka return;
10048876230bShsuenaga #ifdef INET6
10058876230bShsuenaga case PPP_IPV6:
100689162bfcSyasuoka if (!decrypted && pipex_session_is_mppe_required(session))
100789162bfcSyasuoka /*
100889162bfcSyasuoka * if ip packet received when mppe
100989162bfcSyasuoka * is required, discard it.
101089162bfcSyasuoka */
101189162bfcSyasuoka goto drop;
10128876230bShsuenaga pipex_ip6_input(m0, session);
10138876230bShsuenaga return;
10148876230bShsuenaga #endif
1015a0ade7e9Syasuoka default:
1016a0ade7e9Syasuoka if (decrypted)
1017a0ade7e9Syasuoka goto drop;
10187f983f02Syasuoka /* protocol must be checked on pipex_common_input() already */
10197f983f02Syasuoka KASSERT(0);
10207f983f02Syasuoka goto drop;
1021a0ade7e9Syasuoka }
1022a0ade7e9Syasuoka
1023a0ade7e9Syasuoka return;
1024707c5f41Smvs
1025a0ade7e9Syasuoka drop:
1026a0ade7e9Syasuoka m_freem(m0);
1027df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
1028a0ade7e9Syasuoka }
1029a0ade7e9Syasuoka
1030c02a3381Smvs void
pipex_ip_input(struct mbuf * m0,struct pipex_session * session)1031a0ade7e9Syasuoka pipex_ip_input(struct mbuf *m0, struct pipex_session *session)
1032a0ade7e9Syasuoka {
1033a0ade7e9Syasuoka struct ifnet *ifp;
1034a0ade7e9Syasuoka struct ip *ip;
103598a920fdSdlg int len;
1036a0ade7e9Syasuoka int is_idle;
1037a0ade7e9Syasuoka
1038a0ade7e9Syasuoka /* change recvif */
1039002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex;
1040a0ade7e9Syasuoka
1041a19b4e4eSyasuoka if (ISSET(session->ppp_flags, PIPEX_PPP_INGRESS_FILTER)) {
1042a0ade7e9Syasuoka PIPEX_PULLUP(m0, sizeof(struct ip));
1043a0ade7e9Syasuoka if (m0 == NULL)
1044a0ade7e9Syasuoka goto drop;
1045a0ade7e9Syasuoka /* ingress filter */
1046a0ade7e9Syasuoka ip = mtod(m0, struct ip *);
104763d4736dSyasuoka if ((ip->ip_src.s_addr & session->ip_netmask.sin_addr.s_addr) !=
104863d4736dSyasuoka session->ip_address.sin_addr.s_addr) {
1049bbcf0337Smpi char src[INET_ADDRSTRLEN];
1050bbcf0337Smpi
1051a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG,
1052a0ade7e9Syasuoka "ip packet discarded by ingress filter (src %s)",
1053bbcf0337Smpi inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src)));
1054a0ade7e9Syasuoka goto drop;
1055a0ade7e9Syasuoka }
10563b5b2d97Syasuoka }
1057a0ade7e9Syasuoka
1058a0ade7e9Syasuoka /* idle timer */
1059a0ade7e9Syasuoka if (session->timeout_sec != 0) {
1060a0ade7e9Syasuoka is_idle = 0;
1061a0ade7e9Syasuoka m0 = ip_is_idle_packet(m0, &is_idle);
1062a0ade7e9Syasuoka if (m0 == NULL)
1063a0ade7e9Syasuoka goto drop;
1064f6e6f8e7Smvs if (is_idle == 0) {
1065a0ade7e9Syasuoka /* update expire time */
1066f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
1067a5953208Smvs if (session->state == PIPEX_STATE_OPENED)
1068df21f681Smvs session->idle_time = 0;
1069f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
1070f6e6f8e7Smvs }
1071a0ade7e9Syasuoka }
1072a0ade7e9Syasuoka
1073a0ade7e9Syasuoka /* adjust tcpmss */
1074a0ade7e9Syasuoka if (session->ppp_flags & PIPEX_PPP_ADJUST_TCPMSS) {
1075a0ade7e9Syasuoka m0 = adjust_tcp_mss(m0, session->peer_mru);
1076a0ade7e9Syasuoka if (m0 == NULL)
1077a0ade7e9Syasuoka goto drop;
1078a0ade7e9Syasuoka }
1079a0ade7e9Syasuoka
1080723b7749Ssashan #if NPF > 0
1081723b7749Ssashan pf_pkt_addr_changed(m0);
1082723b7749Ssashan #endif
1083723b7749Ssashan
1084a0ade7e9Syasuoka len = m0->m_pkthdr.len;
1085a0ade7e9Syasuoka
1086002baba0Smvs if ((ifp = if_get(session->ifindex)) == NULL)
1087002baba0Smvs goto drop;
1088002baba0Smvs
1089a0ade7e9Syasuoka #if NBPFILTER > 0
1090a0ade7e9Syasuoka if (ifp->if_bpf)
1091a0ade7e9Syasuoka bpf_mtap_af(ifp->if_bpf, AF_INET, m0, BPF_DIRECTION_IN);
1092a0ade7e9Syasuoka #endif
1093a0ade7e9Syasuoka
1094583e9cb0Smvs counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len);
1095df21f681Smvs counters_pkt(session->stat_counters, pxc_ipackets, pxc_ibytes, len);
10963f9b99eeSmpi ipv4_input(ifp, m0);
1097a0ade7e9Syasuoka
1098002baba0Smvs if_put(ifp);
1099002baba0Smvs
1100a0ade7e9Syasuoka return;
1101a0ade7e9Syasuoka drop:
1102a0ade7e9Syasuoka m_freem(m0);
1103df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
1104a0ade7e9Syasuoka }
1105a0ade7e9Syasuoka
11068876230bShsuenaga #ifdef INET6
1107c02a3381Smvs void
pipex_ip6_input(struct mbuf * m0,struct pipex_session * session)11088876230bShsuenaga pipex_ip6_input(struct mbuf *m0, struct pipex_session *session)
11098876230bShsuenaga {
11108876230bShsuenaga struct ifnet *ifp;
111198a920fdSdlg int len;
11128876230bShsuenaga
11138876230bShsuenaga /* change recvif */
1114002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex;
11158876230bShsuenaga
11168876230bShsuenaga /*
11178876230bShsuenaga * XXX: what is reasonable ingress filter ???
11188876230bShsuenaga * only one address is enough ??
11198876230bShsuenaga */
11208876230bShsuenaga
11218876230bShsuenaga /* XXX: we must define idle packet for IPv6(ICMPv6). */
11228876230bShsuenaga
11238876230bShsuenaga /*
11248876230bShsuenaga * XXX: tcpmss adjustment for IPv6 is required???
11258876230bShsuenaga * We may use PMTUD in IPv6....
11268876230bShsuenaga */
11278876230bShsuenaga
112885d4897fSmarkus #if NPF > 0
112985d4897fSmarkus pf_pkt_addr_changed(m0);
113085d4897fSmarkus #endif
113185d4897fSmarkus
11328876230bShsuenaga len = m0->m_pkthdr.len;
11338876230bShsuenaga
1134002baba0Smvs if ((ifp = if_get(session->ifindex)) == NULL)
1135002baba0Smvs goto drop;
1136002baba0Smvs
11378876230bShsuenaga #if NBPFILTER > 0
11388876230bShsuenaga if (ifp->if_bpf)
1139908147e1Smarkus bpf_mtap_af(ifp->if_bpf, AF_INET6, m0, BPF_DIRECTION_IN);
11408876230bShsuenaga #endif
11418876230bShsuenaga
1142583e9cb0Smvs counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len);
1143df21f681Smvs counters_pkt(session->stat_counters, pxc_ipackets, pxc_ibytes, len);
11443f9b99eeSmpi ipv6_input(ifp, m0);
1145002baba0Smvs
1146002baba0Smvs if_put(ifp);
1147002baba0Smvs
1148002baba0Smvs return;
1149002baba0Smvs drop:
1150002baba0Smvs m_freem(m0);
1151df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
11528876230bShsuenaga }
11539e07f154Syasuoka #endif
11548876230bShsuenaga
1155c02a3381Smvs struct mbuf *
pipex_common_input(struct pipex_session * session,struct mbuf * m0,int hlen,int plen,int locked)1156d5af01deSyasuoka pipex_common_input(struct pipex_session *session, struct mbuf *m0, int hlen,
1157707c5f41Smvs int plen, int locked)
1158d5af01deSyasuoka {
1159d5af01deSyasuoka int proto, ppphlen;
1160d5af01deSyasuoka u_char code;
1161d5af01deSyasuoka
1162363bb3c1Syasuoka if ((m0->m_pkthdr.len < hlen + PIPEX_PPPMINLEN) ||
1163707c5f41Smvs (plen < PIPEX_PPPMINLEN)) {
1164707c5f41Smvs if (locked)
1165707c5f41Smvs mtx_leave(&session->pxs_mtx);
1166d5af01deSyasuoka goto drop;
1167707c5f41Smvs }
1168d5af01deSyasuoka
1169d5af01deSyasuoka proto = pipex_ppp_proto(m0, session, hlen, &ppphlen);
1170d5af01deSyasuoka switch (proto) {
1171d5af01deSyasuoka #ifdef PIPEX_MPPE
1172d5af01deSyasuoka case PPP_CCP:
1173d5af01deSyasuoka code = 0;
1174d5af01deSyasuoka KASSERT(m0->m_pkthdr.len >= hlen + ppphlen + 1);
11755c7fed39Sdlg m_copydata(m0, hlen + ppphlen, 1, &code);
1176d5af01deSyasuoka if (code != CCP_RESETREQ && code != CCP_RESETACK)
1177d5af01deSyasuoka goto not_ours;
1178d5af01deSyasuoka break;
1179d5af01deSyasuoka
1180d5af01deSyasuoka case PPP_COMP:
1181fcff09efSyasuoka if (pipex_session_is_mppe_accepted(session))
1182fcff09efSyasuoka break;
1183fcff09efSyasuoka goto not_ours;
1184d5af01deSyasuoka #endif
1185d5af01deSyasuoka case PPP_IP:
1186d5af01deSyasuoka #ifdef INET6
1187d5af01deSyasuoka case PPP_IPV6:
1188d5af01deSyasuoka #endif
1189d5af01deSyasuoka break;
1190d5af01deSyasuoka default:
1191d5af01deSyasuoka goto not_ours;
1192d5af01deSyasuoka }
1193d5af01deSyasuoka
1194707c5f41Smvs if (locked)
1195707c5f41Smvs mtx_leave(&session->pxs_mtx);
1196707c5f41Smvs
1197d5af01deSyasuoka /* ok, The packet is for PIPEX */
1198df8d9afdSjsg m_adj(m0, hlen);/* cut off the tunnel protocol header */
1199d5af01deSyasuoka
1200d5af01deSyasuoka /* ensure the mbuf length equals the PPP frame length */
1201d5af01deSyasuoka if (m0->m_pkthdr.len < plen)
1202d5af01deSyasuoka goto drop;
1203d5af01deSyasuoka if (m0->m_pkthdr.len > plen) {
1204d5af01deSyasuoka if (m0->m_len == m0->m_pkthdr.len) {
1205d5af01deSyasuoka m0->m_len = plen;
1206d5af01deSyasuoka m0->m_pkthdr.len = plen;
1207d5af01deSyasuoka } else
1208d5af01deSyasuoka m_adj(m0, plen - m0->m_pkthdr.len);
1209d5af01deSyasuoka }
1210d5af01deSyasuoka
121177a7190bSyasuoka pipex_ppp_input(m0, session, 0);
121277a7190bSyasuoka
1213d5af01deSyasuoka return (NULL);
121453c2c62eSmvs
1215d5af01deSyasuoka drop:
1216d5af01deSyasuoka m_freem(m0);
1217df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
1218d5af01deSyasuoka return (NULL);
1219d5af01deSyasuoka
1220d5af01deSyasuoka not_ours:
1221d5af01deSyasuoka return (m0); /* Not to be handled by PIPEX */
1222d5af01deSyasuoka }
1223d5af01deSyasuoka
1224a0ade7e9Syasuoka /*
1225a0ade7e9Syasuoka * pipex_ppp_proto
1226a0ade7e9Syasuoka */
1227c02a3381Smvs int
pipex_ppp_proto(struct mbuf * m0,struct pipex_session * session,int off,int * hlenp)1228a0ade7e9Syasuoka pipex_ppp_proto(struct mbuf *m0, struct pipex_session *session, int off,
1229a0ade7e9Syasuoka int *hlenp)
1230a0ade7e9Syasuoka {
1231a0ade7e9Syasuoka int proto;
1232a0ade7e9Syasuoka u_char *cp, pktbuf[4];
1233a0ade7e9Syasuoka
1234363bb3c1Syasuoka KASSERT(m0->m_pkthdr.len > sizeof(pktbuf));
1235a0ade7e9Syasuoka m_copydata(m0, off, sizeof(pktbuf), pktbuf);
1236a0ade7e9Syasuoka cp = pktbuf;
1237a0ade7e9Syasuoka
1238a0ade7e9Syasuoka if (pipex_session_has_acf(session)) {
123963d4736dSyasuoka if (cp[0] == PPP_ALLSTATIONS && cp[1] == PPP_UI)
1240a0ade7e9Syasuoka cp += 2;
1241a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
124263d4736dSyasuoka else if (!pipex_session_is_acfc_accepted(session))
1243a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG,
1244a0ade7e9Syasuoka "no acf but acfc is not accepted by the peer."));
1245a0ade7e9Syasuoka #endif
1246a0ade7e9Syasuoka }
1247a0ade7e9Syasuoka if ((*cp & 0x01) != 0) {
1248a0ade7e9Syasuoka if (!pipex_session_is_pfc_accepted(session)) {
1249a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "Received a broken ppp "
1250a0ade7e9Syasuoka "frame. No protocol field. %02x-%02x",
1251a0ade7e9Syasuoka cp[0], cp[1]));
125263d4736dSyasuoka return (-1);
1253a0ade7e9Syasuoka }
1254a0ade7e9Syasuoka GETCHAR(proto, cp);
125563d4736dSyasuoka } else
1256a0ade7e9Syasuoka GETSHORT(proto, cp);
1257a0ade7e9Syasuoka
1258a0ade7e9Syasuoka if (hlenp != NULL)
1259a0ade7e9Syasuoka *hlenp = cp - pktbuf;
1260a0ade7e9Syasuoka
126163d4736dSyasuoka return (proto);
1262a0ade7e9Syasuoka }
1263a0ade7e9Syasuoka
1264a0ade7e9Syasuoka #ifdef PIPEX_PPPOE
1265a0ade7e9Syasuoka /***********************************************************************
1266a0ade7e9Syasuoka * PPPoE
1267a0ade7e9Syasuoka ***********************************************************************/
1268c02a3381Smvs static const u_char pipex_pppoe_padding[ETHERMIN];
1269a0ade7e9Syasuoka /*
1270a0ade7e9Syasuoka * pipex_pppoe_lookup_session
1271a0ade7e9Syasuoka */
1272a0ade7e9Syasuoka struct pipex_session *
pipex_pppoe_lookup_session(struct mbuf * m0)1273a0ade7e9Syasuoka pipex_pppoe_lookup_session(struct mbuf *m0)
1274a0ade7e9Syasuoka {
1275a0ade7e9Syasuoka struct pipex_session *session;
1276a0ade7e9Syasuoka struct pipex_pppoe_header pppoe;
1277a0ade7e9Syasuoka
1278a0ade7e9Syasuoka /* short packet */
1279a0ade7e9Syasuoka if (m0->m_pkthdr.len < (sizeof(struct ether_header) + sizeof(pppoe)))
128063d4736dSyasuoka return (NULL);
1281a0ade7e9Syasuoka
1282a0ade7e9Syasuoka m_copydata(m0, sizeof(struct ether_header),
12835c7fed39Sdlg sizeof(struct pipex_pppoe_header), &pppoe);
1284faf7e06fSmpi pppoe.session_id = ntohs(pppoe.session_id);
1285a0ade7e9Syasuoka session = pipex_lookup_by_session_id(PIPEX_PROTO_PPPOE,
1286a0ade7e9Syasuoka pppoe.session_id);
1287a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
128863d4736dSyasuoka if (session == NULL)
1289a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found (id=%d)",
1290a0ade7e9Syasuoka __func__, pppoe.session_id));
1291a0ade7e9Syasuoka #endif
1292f6e6f8e7Smvs if (session && session->proto.pppoe.over_ifidx !=
1293f6e6f8e7Smvs m0->m_pkthdr.ph_ifidx) {
1294f6e6f8e7Smvs pipex_rele_session(session);
12951489e7e8Syasuoka session = NULL;
1296f6e6f8e7Smvs }
1297a0ade7e9Syasuoka
129863d4736dSyasuoka return (session);
1299a0ade7e9Syasuoka }
1300a0ade7e9Syasuoka
1301a0ade7e9Syasuoka struct mbuf *
pipex_pppoe_input(struct mbuf * m0,struct pipex_session * session)1302a0ade7e9Syasuoka pipex_pppoe_input(struct mbuf *m0, struct pipex_session *session)
1303a0ade7e9Syasuoka {
1304d5af01deSyasuoka int hlen;
1305a0ade7e9Syasuoka struct pipex_pppoe_header pppoe;
1306a0ade7e9Syasuoka
1307a0ade7e9Syasuoka /* already checked at pipex_pppoe_lookup_session */
1308a0ade7e9Syasuoka KASSERT(m0->m_pkthdr.len >= (sizeof(struct ether_header) +
1309a0ade7e9Syasuoka sizeof(pppoe)));
1310a0ade7e9Syasuoka
1311a0ade7e9Syasuoka m_copydata(m0, sizeof(struct ether_header),
13125c7fed39Sdlg sizeof(struct pipex_pppoe_header), &pppoe);
1313a0ade7e9Syasuoka
1314d5af01deSyasuoka hlen = sizeof(struct ether_header) + sizeof(struct pipex_pppoe_header);
1315707c5f41Smvs m0 = pipex_common_input(session, m0, hlen, ntohs(pppoe.length), 0);
1316707c5f41Smvs if (m0 == NULL)
131763d4736dSyasuoka return (NULL);
1318a0ade7e9Syasuoka m_freem(m0);
1319df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
132063d4736dSyasuoka return (NULL);
1321a0ade7e9Syasuoka }
1322a0ade7e9Syasuoka
1323a0ade7e9Syasuoka /*
1324a0ade7e9Syasuoka * pipex_ppope_output
1325a0ade7e9Syasuoka */
1326c02a3381Smvs void
pipex_pppoe_output(struct mbuf * m0,struct pipex_session * session)1327a0ade7e9Syasuoka pipex_pppoe_output(struct mbuf *m0, struct pipex_session *session)
1328a0ade7e9Syasuoka {
1329a0ade7e9Syasuoka struct pipex_pppoe_header *pppoe;
1330a0ade7e9Syasuoka int len, padlen;
1331a0ade7e9Syasuoka
1332a0ade7e9Syasuoka /* save length for pppoe header */
1333a0ade7e9Syasuoka len = m0->m_pkthdr.len;
1334a0ade7e9Syasuoka
1335a0ade7e9Syasuoka /* prepend protocol header */
1336a0ade7e9Syasuoka M_PREPEND(m0, sizeof(struct pipex_pppoe_header), M_NOWAIT);
1337a0ade7e9Syasuoka if (m0 == NULL) {
1338a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_ERR,
1339a0ade7e9Syasuoka "<%s> cannot prepend header.", __func__));
1340df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
1341a0ade7e9Syasuoka return;
1342a0ade7e9Syasuoka }
1343a0ade7e9Syasuoka padlen = ETHERMIN - m0->m_pkthdr.len;
134463d4736dSyasuoka if (padlen > 0)
134541b18b7eSblambert m_copyback(m0, m0->m_pkthdr.len, padlen, pipex_pppoe_padding,
134641b18b7eSblambert M_NOWAIT);
1347a0ade7e9Syasuoka
1348a0ade7e9Syasuoka /* setup pppoe header information */
1349a0ade7e9Syasuoka pppoe = mtod(m0, struct pipex_pppoe_header *);
1350a0ade7e9Syasuoka pppoe->vertype = PIPEX_PPPOE_VERTYPE;
1351a0ade7e9Syasuoka pppoe->code = PIPEX_PPPOE_CODE_SESSION;
1352a0ade7e9Syasuoka pppoe->session_id = htons(session->session_id);
1353a0ade7e9Syasuoka pppoe->length = htons(len);
1354a0ade7e9Syasuoka
13557790b521Syasuoka m0->m_pkthdr.ph_ifidx = session->proto.pppoe.over_ifidx;
13560cd5c6faSmvs refcnt_take(&session->pxs_refcnt);
13570cd5c6faSmvs m0->m_pkthdr.ph_cookie = session;
1358a0ade7e9Syasuoka m0->m_flags &= ~(M_BCAST|M_MCAST);
1359a0ade7e9Syasuoka
13600cd5c6faSmvs if (mq_enqueue(&pipexoutq, m0) != 0) {
1361df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
13620cd5c6faSmvs pipex_rele_session(session);
13630cd5c6faSmvs } else
13640cd5c6faSmvs schednetisr(NETISR_PIPEX);
1365a0ade7e9Syasuoka }
1366a0ade7e9Syasuoka #endif /* PIPEX_PPPOE */
1367a0ade7e9Syasuoka
1368a0ade7e9Syasuoka #ifdef PIPEX_PPTP
1369a0ade7e9Syasuoka /***********************************************************************
1370a0ade7e9Syasuoka * PPTP
1371a0ade7e9Syasuoka ***********************************************************************/
1372c02a3381Smvs void
pipex_pptp_output(struct mbuf * m0,struct pipex_session * session,int has_seq,int has_ack)1373a0ade7e9Syasuoka pipex_pptp_output(struct mbuf *m0, struct pipex_session *session,
1374a0ade7e9Syasuoka int has_seq, int has_ack)
1375a0ade7e9Syasuoka {
1376a0ade7e9Syasuoka int len, reqlen;
1377a0ade7e9Syasuoka struct pipex_gre_header *gre = NULL;
1378a0ade7e9Syasuoka struct ip *ip;
1379a0ade7e9Syasuoka u_char *cp;
1380a0ade7e9Syasuoka
1381707c5f41Smvs MUTEX_ASSERT_LOCKED(&session->pxs_mtx);
1382707c5f41Smvs
1383a0ade7e9Syasuoka reqlen = PIPEX_IPGRE_HDRLEN + (has_seq + has_ack) * 4;
1384a0ade7e9Syasuoka
1385a0ade7e9Syasuoka len = 0;
1386a0ade7e9Syasuoka if (m0 != NULL) {
1387a0ade7e9Syasuoka /* save length for gre header */
1388a0ade7e9Syasuoka len = m0->m_pkthdr.len;
1389a0ade7e9Syasuoka /* prepend protocol header */
1390a0ade7e9Syasuoka M_PREPEND(m0, reqlen, M_NOWAIT);
1391a0ade7e9Syasuoka if (m0 == NULL)
1392a0ade7e9Syasuoka goto drop;
1393a0ade7e9Syasuoka } else {
1394a0ade7e9Syasuoka MGETHDR(m0, M_DONTWAIT, MT_DATA);
1395a0ade7e9Syasuoka if (m0 && reqlen > MHLEN) {
1396a0ade7e9Syasuoka MCLGET(m0, M_DONTWAIT);
1397a0ade7e9Syasuoka if ((m0->m_flags & M_EXT) == 0) {
1398a0ade7e9Syasuoka m_freem(m0);
1399a0ade7e9Syasuoka m0 = NULL;
1400a0ade7e9Syasuoka }
1401a0ade7e9Syasuoka }
1402a0ade7e9Syasuoka if (m0 == NULL)
1403a0ade7e9Syasuoka goto drop;
1404a0ade7e9Syasuoka m0->m_pkthdr.len = m0->m_len = reqlen;
1405a0ade7e9Syasuoka }
1406a0ade7e9Syasuoka
1407a0ade7e9Syasuoka /* setup ip header information */
1408a0ade7e9Syasuoka ip = mtod(m0, struct ip *);
1409a0ade7e9Syasuoka
1410a0ade7e9Syasuoka ip->ip_len = htons(m0->m_pkthdr.len);
1411a0ade7e9Syasuoka ip->ip_off = 0;
1412a0ade7e9Syasuoka ip->ip_ttl = MAXTTL;
1413a0ade7e9Syasuoka ip->ip_p = IPPROTO_GRE;
1414a0ade7e9Syasuoka ip->ip_tos = 0;
1415a0ade7e9Syasuoka
14168876230bShsuenaga ip->ip_src = session->local.sin4.sin_addr;
14178876230bShsuenaga ip->ip_dst = session->peer.sin4.sin_addr;
141885d4897fSmarkus #if NPF > 0
141985d4897fSmarkus pf_pkt_addr_changed(m0);
142085d4897fSmarkus #endif
1421a0ade7e9Syasuoka
1422a0ade7e9Syasuoka /* setup gre(ver1) header information */
1423a0ade7e9Syasuoka gre = PIPEX_SEEK_NEXTHDR(ip, sizeof(struct ip),
1424a0ade7e9Syasuoka struct pipex_gre_header *);
1425a0ade7e9Syasuoka gre->type = htons(PIPEX_GRE_PROTO_PPP);
1426a0ade7e9Syasuoka gre->call_id = htons(session->peer_session_id);
1427a0ade7e9Syasuoka gre->flags = PIPEX_GRE_KFLAG | PIPEX_GRE_VER; /* do htons later */
1428a0ade7e9Syasuoka gre->len = htons(len);
1429a0ade7e9Syasuoka
1430a0ade7e9Syasuoka cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *);
1431a0ade7e9Syasuoka if (has_seq) {
1432a0ade7e9Syasuoka gre->flags |= PIPEX_GRE_SFLAG;
1433a0ade7e9Syasuoka PUTLONG(session->proto.pptp.snd_nxt, cp);
1434a0ade7e9Syasuoka session->proto.pptp.snd_nxt++;
1435a0ade7e9Syasuoka session->proto.pptp.snd_gap++;
1436a0ade7e9Syasuoka }
1437a0ade7e9Syasuoka if (has_ack) {
1438a0ade7e9Syasuoka gre->flags |= PIPEX_GRE_AFLAG;
14398876230bShsuenaga session->proto.pptp.rcv_acked = session->proto.pptp.rcv_nxt - 1;
14408876230bShsuenaga PUTLONG(session->proto.pptp.rcv_acked, cp);
1441a0ade7e9Syasuoka }
1442a0ade7e9Syasuoka gre->flags = htons(gre->flags);
1443a0ade7e9Syasuoka
1444002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex;
14450315bf92Sdlg ip_send(m0);
1446a0ade7e9Syasuoka if (len > 0) { /* network layer only */
1447a0ade7e9Syasuoka /* countup statistics */
1448df21f681Smvs counters_pkt(session->stat_counters, pxc_opackets,
1449df21f681Smvs pxc_obytes, len);
1450a0ade7e9Syasuoka }
1451a0ade7e9Syasuoka
1452a0ade7e9Syasuoka return;
1453a0ade7e9Syasuoka drop:
1454df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
1455a0ade7e9Syasuoka }
1456a0ade7e9Syasuoka
1457a0ade7e9Syasuoka struct pipex_session *
pipex_pptp_lookup_session(struct mbuf * m0)1458a0ade7e9Syasuoka pipex_pptp_lookup_session(struct mbuf *m0)
1459a0ade7e9Syasuoka {
1460a0ade7e9Syasuoka struct pipex_session *session;
1461a0ade7e9Syasuoka struct pipex_gre_header gre;
1462a0ade7e9Syasuoka struct ip ip;
1463a0ade7e9Syasuoka uint16_t flags;
1464a0ade7e9Syasuoka uint16_t id;
1465a0ade7e9Syasuoka int hlen;
1466a0ade7e9Syasuoka
1467a0ade7e9Syasuoka if (m0->m_pkthdr.len < PIPEX_IPGRE_HDRLEN) {
1468a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1469a0ade7e9Syasuoka "<%s> packet length is too short", __func__));
1470a0ade7e9Syasuoka goto not_ours;
1471a0ade7e9Syasuoka }
1472a0ade7e9Syasuoka
1473a0ade7e9Syasuoka /* get ip header info */
14745c7fed39Sdlg m_copydata(m0, 0, sizeof(struct ip), &ip);
1475a0ade7e9Syasuoka hlen = ip.ip_hl << 2;
1476a0ade7e9Syasuoka
1477a0ade7e9Syasuoka /*
1478a0ade7e9Syasuoka * m0 has already passed ip_input(), so there is
1479a0ade7e9Syasuoka * no necessity for ip packet inspection.
1480a0ade7e9Syasuoka */
1481a0ade7e9Syasuoka
1482a0ade7e9Syasuoka /* get gre flags */
14835c7fed39Sdlg m_copydata(m0, hlen, sizeof(gre), &gre);
1484a0ade7e9Syasuoka flags = ntohs(gre.flags);
1485a0ade7e9Syasuoka
1486a0ade7e9Syasuoka /* gre version must be '1' */
1487a0ade7e9Syasuoka if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) {
1488a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1489a0ade7e9Syasuoka "<%s> gre header wrong version.", __func__));
1490a0ade7e9Syasuoka goto not_ours;
1491a0ade7e9Syasuoka }
1492a0ade7e9Syasuoka
1493a0ade7e9Syasuoka /* gre keys must be present */
1494a0ade7e9Syasuoka if ((flags & PIPEX_GRE_KFLAG) == 0) {
1495a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1496a0ade7e9Syasuoka "<%s> gre header has no keys.", __func__));
1497a0ade7e9Syasuoka goto not_ours;
1498a0ade7e9Syasuoka }
1499a0ade7e9Syasuoka
1500363bb3c1Syasuoka /* flag check */
1501363bb3c1Syasuoka if ((flags & PIPEX_GRE_UNUSEDFLAGS) != 0) {
1502363bb3c1Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1503363bb3c1Syasuoka "<%s> gre header has unused flags at pptp.", __func__));
1504363bb3c1Syasuoka goto not_ours;
1505363bb3c1Syasuoka }
1506363bb3c1Syasuoka
1507a0ade7e9Syasuoka /* lookup pipex session table */
1508a0ade7e9Syasuoka id = ntohs(gre.call_id);
1509a0ade7e9Syasuoka session = pipex_lookup_by_session_id(PIPEX_PROTO_PPTP, id);
1510a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
1511a0ade7e9Syasuoka if (session == NULL) {
1512a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1513a0ade7e9Syasuoka "<%s> session not found (id=%d)", __func__, id));
1514a0ade7e9Syasuoka goto not_ours;
1515a0ade7e9Syasuoka }
1516a0ade7e9Syasuoka #endif
1517a0ade7e9Syasuoka
151863d4736dSyasuoka return (session);
1519a0ade7e9Syasuoka
1520a0ade7e9Syasuoka not_ours:
152163d4736dSyasuoka return (NULL);
1522a0ade7e9Syasuoka }
1523a0ade7e9Syasuoka
1524a0ade7e9Syasuoka struct mbuf *
pipex_pptp_input(struct mbuf * m0,struct pipex_session * session)1525a0ade7e9Syasuoka pipex_pptp_input(struct mbuf *m0, struct pipex_session *session)
1526a0ade7e9Syasuoka {
1527d5af01deSyasuoka int hlen, has_seq, has_ack, nseq;
1528a0ade7e9Syasuoka const char *reason = "";
1529d5af01deSyasuoka u_char *cp, *seqp = NULL, *ackp = NULL;
1530a0ade7e9Syasuoka uint32_t flags, seq = 0, ack = 0;
1531a0ade7e9Syasuoka struct ip *ip;
1532a0ade7e9Syasuoka struct pipex_gre_header *gre;
1533a0ade7e9Syasuoka struct pipex_pptp_session *pptp_session;
1534e405d423Syasuoka int rewind = 0;
1535a0ade7e9Syasuoka
1536a0ade7e9Syasuoka KASSERT(m0->m_pkthdr.len >= PIPEX_IPGRE_HDRLEN);
1537a0ade7e9Syasuoka pptp_session = &session->proto.pptp;
1538a0ade7e9Syasuoka
1539a0ade7e9Syasuoka /* get ip header */
1540a0ade7e9Syasuoka ip = mtod(m0, struct ip *);
1541a0ade7e9Syasuoka hlen = ip->ip_hl << 2;
1542a0ade7e9Syasuoka
1543a0ade7e9Syasuoka /* seek gre header */
1544a0ade7e9Syasuoka gre = PIPEX_SEEK_NEXTHDR(ip, hlen, struct pipex_gre_header *);
1545a0ade7e9Syasuoka flags = ntohs(gre->flags);
1546a0ade7e9Syasuoka
1547a0ade7e9Syasuoka /* pullup for seek sequences in header */
1548a0ade7e9Syasuoka has_seq = (flags & PIPEX_GRE_SFLAG) ? 1 : 0;
1549a0ade7e9Syasuoka has_ack = (flags & PIPEX_GRE_AFLAG) ? 1 : 0;
1550a0ade7e9Syasuoka hlen = PIPEX_IPGRE_HDRLEN + 4 * (has_seq + has_ack);
1551a0ade7e9Syasuoka if (m0->m_len < hlen) {
1552a0ade7e9Syasuoka m0 = m_pullup(m0, hlen);
1553a0ade7e9Syasuoka if (m0 == NULL) {
1554a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "pullup failed."));
1555a0ade7e9Syasuoka goto drop;
1556a0ade7e9Syasuoka }
1557a0ade7e9Syasuoka }
1558a0ade7e9Syasuoka
1559a0ade7e9Syasuoka /* check sequence */
1560a0ade7e9Syasuoka cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *);
1561a0ade7e9Syasuoka if (has_seq) {
1562a0ade7e9Syasuoka seqp = cp;
1563a0ade7e9Syasuoka GETLONG(seq, cp);
1564a0ade7e9Syasuoka }
1565707c5f41Smvs
1566707c5f41Smvs mtx_enter(&session->pxs_mtx);
1567707c5f41Smvs
1568a0ade7e9Syasuoka if (has_ack) {
1569a0ade7e9Syasuoka ackp = cp;
1570a0ade7e9Syasuoka GETLONG(ack, cp);
1571a0ade7e9Syasuoka if (ack + 1 == pptp_session->snd_una) {
1572a0ade7e9Syasuoka /* ack has not changed before */
1573a0ade7e9Syasuoka } else if (SEQ32_LT(ack, pptp_session->snd_una)) {
1574e405d423Syasuoka /* OoO ack packets should not be dropped. */
1575e405d423Syasuoka rewind = 1;
1576a0ade7e9Syasuoka } else if (SEQ32_GT(ack, pptp_session->snd_nxt)) {
1577a0ade7e9Syasuoka reason = "ack for unknown sequence";
1578e405d423Syasuoka goto out_seq;
15796117a981Syasuoka } else
15806117a981Syasuoka pptp_session->snd_una = ack + 1;
1581a0ade7e9Syasuoka }
1582a0ade7e9Syasuoka if (!has_seq) {
1583a0ade7e9Syasuoka /* ack only packet */
1584a0ade7e9Syasuoka goto not_ours;
1585a0ade7e9Syasuoka }
1586a0ade7e9Syasuoka if (SEQ32_LT(seq, pptp_session->rcv_nxt)) {
1587e405d423Syasuoka rewind = 1;
1588e405d423Syasuoka if (SEQ32_LT(seq,
1589e405d423Syasuoka pptp_session->rcv_nxt - PIPEX_REWIND_LIMIT)) {
1590a0ade7e9Syasuoka reason = "out of sequence";
1591e405d423Syasuoka goto out_seq;
1592e405d423Syasuoka }
1593a0ade7e9Syasuoka } else if (SEQ32_GE(seq, pptp_session->rcv_nxt +
1594a0ade7e9Syasuoka pptp_session->maxwinsz)) {
1595a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG,
1596a0ade7e9Syasuoka "received packet caused window overflow. seq=%u(%u-%u)"
1597a0ade7e9Syasuoka "may lost %d packets.", seq, pptp_session->rcv_nxt,
1598a0ade7e9Syasuoka pptp_session->rcv_nxt + pptp_session->maxwinsz,
1599a0ade7e9Syasuoka (int)SEQ32_SUB(seq, pptp_session->rcv_nxt));
1600a0ade7e9Syasuoka }
1601a0ade7e9Syasuoka
1602a0ade7e9Syasuoka seq++;
1603a0ade7e9Syasuoka nseq = SEQ32_SUB(seq, pptp_session->rcv_nxt);
1604e405d423Syasuoka if (!rewind) {
1605a0ade7e9Syasuoka pptp_session->rcv_nxt = seq;
1606a0ade7e9Syasuoka if (SEQ32_SUB(seq, pptp_session->rcv_acked) >
160763d4736dSyasuoka roundup(pptp_session->winsz, 2) / 2) /* Send ack only packet. */
1608a0ade7e9Syasuoka pipex_pptp_output(NULL, session, 0, 1);
1609e405d423Syasuoka }
1610a0ade7e9Syasuoka
1611707c5f41Smvs /*
1612707c5f41Smvs * The following pipex_common_input() will release `pxs_mtx'
1613707c5f41Smvs * deep within if the packet will be consumed. In the error
1614707c5f41Smvs * path lock will be held all the time. So increment `rcv_gap'
1615707c5f41Smvs * here, and on the error path back it out, no atomicity will
1616707c5f41Smvs * be lost in all cases.
1617707c5f41Smvs */
1618e405d423Syasuoka if (!rewind)
1619a0ade7e9Syasuoka session->proto.pptp.rcv_gap += nseq;
1620707c5f41Smvs m0 = pipex_common_input(session, m0, hlen, ntohs(gre->len), 1);
1621707c5f41Smvs if (m0 == NULL) {
1622707c5f41Smvs /*
1623707c5f41Smvs * pipex_common_input() releases lock if the
1624707c5f41Smvs * packet was consumed.
1625707c5f41Smvs */
1626e405d423Syasuoka return (NULL);
1627a0ade7e9Syasuoka }
1628e405d423Syasuoka
1629e405d423Syasuoka if (rewind)
1630e405d423Syasuoka goto out_seq;
1631707c5f41Smvs else {
1632707c5f41Smvs /* The packet is not ours, back out `rcv_gap'. */
1633707c5f41Smvs session->proto.pptp.rcv_gap -= nseq;
1634707c5f41Smvs }
1635e405d423Syasuoka
1636a0ade7e9Syasuoka not_ours:
16376117a981Syasuoka seq--; /* revert original seq value */
163863d4736dSyasuoka
1639a0ade7e9Syasuoka /*
1640a0ade7e9Syasuoka * overwrite sequence numbers to adjust a gap between pipex and
1641a0ade7e9Syasuoka * userland.
1642a0ade7e9Syasuoka */
1643a0ade7e9Syasuoka if (seqp != NULL) {
1644a0ade7e9Syasuoka seq -= pptp_session->rcv_gap;
1645a0ade7e9Syasuoka PUTLONG(seq, seqp);
1646a0ade7e9Syasuoka }
1647a0ade7e9Syasuoka if (ackp != NULL) {
1648a0ade7e9Syasuoka if (pptp_session->snd_nxt == pptp_session->snd_una) {
1649a0ade7e9Syasuoka ack -= session->proto.pptp.snd_gap;
1650a0ade7e9Syasuoka pptp_session->ul_snd_una = ack;
1651a0ade7e9Syasuoka } else {
1652a0ade7e9Syasuoka /*
1653a0ade7e9Syasuoka * There are sending packets they are not acked.
1654a0ade7e9Syasuoka * In this situation, (ack - snd_gap) may points
1655a0ade7e9Syasuoka * before sending window of userland. So we don't
1656a0ade7e9Syasuoka * update the ack number.
1657a0ade7e9Syasuoka */
1658a0ade7e9Syasuoka ack = pptp_session->ul_snd_una;
1659a0ade7e9Syasuoka }
1660a0ade7e9Syasuoka PUTLONG(ack, ackp);
1661a0ade7e9Syasuoka }
1662a0ade7e9Syasuoka
1663707c5f41Smvs mtx_leave(&session->pxs_mtx);
1664707c5f41Smvs
166563d4736dSyasuoka return (m0);
1666e405d423Syasuoka out_seq:
1667a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG,
1668a0ade7e9Syasuoka "Received bad data packet: %s: seq=%u(%u-%u) ack=%u(%u-%u)",
1669a0ade7e9Syasuoka reason, seq, pptp_session->rcv_nxt,
1670a0ade7e9Syasuoka pptp_session->rcv_nxt + pptp_session->maxwinsz,
1671a0ade7e9Syasuoka ack, pptp_session->snd_una,
1672a0ade7e9Syasuoka pptp_session->snd_nxt);
1673707c5f41Smvs mtx_leave(&session->pxs_mtx);
1674a0ade7e9Syasuoka
1675a0ade7e9Syasuoka /* FALLTHROUGH */
1676a0ade7e9Syasuoka drop:
1677a0ade7e9Syasuoka m_freem(m0);
1678df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
1679a0ade7e9Syasuoka
168063d4736dSyasuoka return (NULL);
1681a0ade7e9Syasuoka }
1682a0ade7e9Syasuoka
1683a0ade7e9Syasuoka struct pipex_session *
pipex_pptp_userland_lookup_session_ipv4(struct mbuf * m0,struct in_addr dst)16848876230bShsuenaga pipex_pptp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst)
16858876230bShsuenaga {
16869c794535Sbluhm struct sockaddr_in sin;
16878876230bShsuenaga
16889c794535Sbluhm memset(&sin, 0, sizeof(sin));
16899c794535Sbluhm sin.sin_len = sizeof(sin);
16909c794535Sbluhm sin.sin_family = AF_INET;
16919c794535Sbluhm sin.sin_addr = dst;
16928876230bShsuenaga
16939c794535Sbluhm return pipex_pptp_userland_lookup_session(m0, sintosa(&sin));
16948876230bShsuenaga }
16958876230bShsuenaga
16968876230bShsuenaga #ifdef INET6
16978876230bShsuenaga struct pipex_session *
pipex_pptp_userland_lookup_session_ipv6(struct mbuf * m0,struct in6_addr dst)16988876230bShsuenaga pipex_pptp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst)
16998876230bShsuenaga {
17008876230bShsuenaga struct sockaddr_in6 sin6;
17018876230bShsuenaga
17029c794535Sbluhm memset(&sin6, 0, sizeof(sin6));
17039c794535Sbluhm sin6.sin6_len = sizeof(sin6);
17048876230bShsuenaga sin6.sin6_family = AF_INET6;
17056c8dd0e9Sclaudio in6_recoverscope(&sin6, &dst);
17068876230bShsuenaga
17079c794535Sbluhm return pipex_pptp_userland_lookup_session(m0, sin6tosa(&sin6));
17088876230bShsuenaga }
17098876230bShsuenaga #endif
17108876230bShsuenaga
1711c02a3381Smvs struct pipex_session *
pipex_pptp_userland_lookup_session(struct mbuf * m0,struct sockaddr * sa)17128876230bShsuenaga pipex_pptp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa)
1713a0ade7e9Syasuoka {
1714a0ade7e9Syasuoka struct pipex_gre_header gre;
1715a0ade7e9Syasuoka struct pipex_hash_head *list;
1716a0ade7e9Syasuoka struct pipex_session *session;
1717a0ade7e9Syasuoka uint16_t id, flags;
1718a0ade7e9Syasuoka
1719a0ade7e9Syasuoka /* pullup */
1720a0ade7e9Syasuoka if (m0->m_pkthdr.len < sizeof(gre)) {
1721a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1722a0ade7e9Syasuoka "<%s> packet length is too short", __func__));
172363d4736dSyasuoka return (NULL);
1724a0ade7e9Syasuoka }
1725a0ade7e9Syasuoka
1726a0ade7e9Syasuoka /* get flags */
17275c7fed39Sdlg m_copydata(m0, 0, sizeof(struct pipex_gre_header), &gre);
1728a0ade7e9Syasuoka flags = ntohs(gre.flags);
1729a0ade7e9Syasuoka
1730a0ade7e9Syasuoka /* gre version must be '1' */
1731a0ade7e9Syasuoka if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) {
1732a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1733a0ade7e9Syasuoka "<%s> gre header wrong version.", __func__));
173463d4736dSyasuoka return (NULL);
1735a0ade7e9Syasuoka }
1736a0ade7e9Syasuoka
1737a0ade7e9Syasuoka /* gre keys must be present */
1738a0ade7e9Syasuoka if ((flags & PIPEX_GRE_KFLAG) == 0) {
1739a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
1740a0ade7e9Syasuoka "<%s> gre header has no keys.", __func__));
174163d4736dSyasuoka return (NULL);
1742a0ade7e9Syasuoka }
1743a0ade7e9Syasuoka
1744a0ade7e9Syasuoka /* lookup pipex session table */
1745a0ade7e9Syasuoka id = ntohs(gre.call_id);
1746a0ade7e9Syasuoka
1747f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
1748f6e6f8e7Smvs
17498876230bShsuenaga list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa));
1750a0ade7e9Syasuoka LIST_FOREACH(session, list, peer_addr_chain) {
17519c681c75Sbluhm if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0)
1752a0ade7e9Syasuoka continue;
1753a0ade7e9Syasuoka if (session->peer_session_id == id)
1754a0ade7e9Syasuoka break;
1755a0ade7e9Syasuoka }
1756f6e6f8e7Smvs
1757f6e6f8e7Smvs if (session != NULL)
1758f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt);
1759f6e6f8e7Smvs
1760f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
1761f6e6f8e7Smvs
1762a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
1763a0ade7e9Syasuoka if (session == NULL) {
1764a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
17658876230bShsuenaga "<%s> session not found (,call_id=%d)",
17668876230bShsuenaga __func__, (int)gre.call_id));
1767a0ade7e9Syasuoka }
1768a0ade7e9Syasuoka #endif
176963d4736dSyasuoka return (session);
1770a0ade7e9Syasuoka }
1771a0ade7e9Syasuoka
1772a0ade7e9Syasuoka /*
1773a0ade7e9Syasuoka * pipex_pptp_userland_output
1774a0ade7e9Syasuoka */
1775a0ade7e9Syasuoka struct mbuf *
pipex_pptp_userland_output(struct mbuf * m0,struct pipex_session * session)1776a0ade7e9Syasuoka pipex_pptp_userland_output(struct mbuf *m0, struct pipex_session *session)
1777a0ade7e9Syasuoka {
17789f9300a4Syasuoka int len;
17799f9300a4Syasuoka struct pipex_gre_header *gre, gre0;
1780a0ade7e9Syasuoka uint16_t flags;
1781a0ade7e9Syasuoka u_char *cp, *cp0;
1782a0ade7e9Syasuoka uint32_t val32;
1783a0ade7e9Syasuoka
17849f9300a4Syasuoka len = sizeof(struct pipex_gre_header);
17855c7fed39Sdlg m_copydata(m0, 0, len, &gre0);
17869f9300a4Syasuoka gre = &gre0;
17879f9300a4Syasuoka flags = ntohs(gre->flags);
17889f9300a4Syasuoka if ((flags & PIPEX_GRE_SFLAG) != 0)
17899f9300a4Syasuoka len += 4;
17909f9300a4Syasuoka if ((flags & PIPEX_GRE_AFLAG) != 0)
17919f9300a4Syasuoka len += 4;
17929f9300a4Syasuoka
1793a0ade7e9Syasuoka /* check length */
17949f9300a4Syasuoka PIPEX_PULLUP(m0, len);
1795a0ade7e9Syasuoka if (m0 == NULL) {
17969f9300a4Syasuoka PIPEX_DBG((session, LOG_DEBUG, "gre header is too short."));
179763d4736dSyasuoka return (NULL);
1798a0ade7e9Syasuoka }
1799a0ade7e9Syasuoka
1800a0ade7e9Syasuoka gre = mtod(m0, struct pipex_gre_header *);
1801a0ade7e9Syasuoka cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header), u_char *);
1802a0ade7e9Syasuoka
1803707c5f41Smvs mtx_enter(&session->pxs_mtx);
1804707c5f41Smvs
1805a0ade7e9Syasuoka /*
1806a0ade7e9Syasuoka * overwrite sequence numbers to adjust a gap between pipex and
1807a0ade7e9Syasuoka * userland.
1808a0ade7e9Syasuoka */
1809a0ade7e9Syasuoka if ((flags & PIPEX_GRE_SFLAG) != 0) {
1810a0ade7e9Syasuoka cp0 = cp;
1811a0ade7e9Syasuoka GETLONG(val32, cp);
1812a0ade7e9Syasuoka val32 += session->proto.pptp.snd_gap;
1813a0ade7e9Syasuoka PUTLONG(val32, cp0);
1814a0ade7e9Syasuoka session->proto.pptp.snd_nxt++;
1815a0ade7e9Syasuoka }
1816a0ade7e9Syasuoka if ((flags & PIPEX_GRE_AFLAG) != 0) {
1817a0ade7e9Syasuoka cp0 = cp;
1818a0ade7e9Syasuoka GETLONG(val32, cp);
1819a0ade7e9Syasuoka val32 += session->proto.pptp.rcv_gap;
1820a0ade7e9Syasuoka PUTLONG(val32, cp0);
1821a0ade7e9Syasuoka if (SEQ32_GT(val32, session->proto.pptp.rcv_acked))
1822a0ade7e9Syasuoka session->proto.pptp.rcv_acked = val32;
1823a0ade7e9Syasuoka }
1824a0ade7e9Syasuoka
1825707c5f41Smvs mtx_leave(&session->pxs_mtx);
1826707c5f41Smvs
182763d4736dSyasuoka return (m0);
1828a0ade7e9Syasuoka }
1829a0ade7e9Syasuoka #endif /* PIPEX_PPTP */
1830a0ade7e9Syasuoka
18318876230bShsuenaga #ifdef PIPEX_L2TP
18328876230bShsuenaga /***********************************************************************
18338876230bShsuenaga * L2TP support
18348876230bShsuenaga ***********************************************************************/
1835c02a3381Smvs void
pipex_l2tp_output(struct mbuf * m0,struct pipex_session * session)18368876230bShsuenaga pipex_l2tp_output(struct mbuf *m0, struct pipex_session *session)
18378876230bShsuenaga {
18388876230bShsuenaga int hlen, plen, datalen;
18398876230bShsuenaga struct pipex_l2tp_header *l2tp = NULL;
18408876230bShsuenaga struct pipex_l2tp_seq_header *seq = NULL;
18418876230bShsuenaga struct udphdr *udp;
18428876230bShsuenaga struct ip *ip;
18438876230bShsuenaga #ifdef INET6
18448876230bShsuenaga struct ip6_hdr *ip6;
18458876230bShsuenaga #endif
1846a1c674a4Syasuoka struct m_tag *mtag;
18478876230bShsuenaga
18488876230bShsuenaga hlen = sizeof(struct pipex_l2tp_header) +
18498876230bShsuenaga ((pipex_session_is_l2tp_data_sequencing_on(session))
18508876230bShsuenaga ? sizeof(struct pipex_l2tp_seq_header) : 0) +
18518876230bShsuenaga sizeof(struct udphdr) +
18529e07f154Syasuoka #ifdef INET6
18538876230bShsuenaga ((session->peer.sin6.sin6_family == AF_INET6)
18548876230bShsuenaga ? sizeof(struct ip6_hdr) : sizeof(struct ip));
18559e07f154Syasuoka #else
18569e07f154Syasuoka sizeof(struct ip);
18579e07f154Syasuoka #endif
18588876230bShsuenaga
18598876230bShsuenaga datalen = 0;
18608876230bShsuenaga if (m0 != NULL) {
18618876230bShsuenaga datalen = m0->m_pkthdr.len;
18628876230bShsuenaga M_PREPEND(m0, hlen, M_NOWAIT);
18638876230bShsuenaga if (m0 == NULL)
18648876230bShsuenaga goto drop;
18658876230bShsuenaga } else {
18668876230bShsuenaga MGETHDR(m0, M_DONTWAIT, MT_DATA);
18678876230bShsuenaga if (m0 == NULL)
18688876230bShsuenaga goto drop;
18698876230bShsuenaga KASSERT(hlen <= MHLEN);
18708876230bShsuenaga m0->m_pkthdr.len = m0->m_len = hlen;
18718876230bShsuenaga }
18728876230bShsuenaga
18739e07f154Syasuoka #ifdef INET6
18748876230bShsuenaga hlen = (session->peer.sin6.sin6_family == AF_INET6)
18758876230bShsuenaga ? sizeof(struct ip6_hdr) : sizeof(struct ip);
18769e07f154Syasuoka #else
18779e07f154Syasuoka hlen = sizeof(struct ip);
18789e07f154Syasuoka #endif
18798876230bShsuenaga plen = datalen + sizeof(struct pipex_l2tp_header) +
18808876230bShsuenaga ((pipex_session_is_l2tp_data_sequencing_on(session))
18818876230bShsuenaga ? sizeof(struct pipex_l2tp_seq_header) : 0);
18828876230bShsuenaga
18838876230bShsuenaga l2tp = (struct pipex_l2tp_header *)
18848876230bShsuenaga (mtod(m0, caddr_t) + hlen + sizeof(struct udphdr));
18858876230bShsuenaga l2tp->flagsver = PIPEX_L2TP_VER | PIPEX_L2TP_FLAG_LENGTH;
18868876230bShsuenaga l2tp->length = htons(plen);
18878876230bShsuenaga l2tp->tunnel_id = htons(session->proto.l2tp.peer_tunnel_id);
18888876230bShsuenaga l2tp->session_id = htons(session->peer_session_id);
18898876230bShsuenaga if (pipex_session_is_l2tp_data_sequencing_on(session)) {
18908876230bShsuenaga seq = (struct pipex_l2tp_seq_header *)(l2tp + 1);
18918876230bShsuenaga l2tp->flagsver |= PIPEX_L2TP_FLAG_SEQUENCE;
1892707c5f41Smvs
1893707c5f41Smvs mtx_enter(&session->pxs_mtx);
18948876230bShsuenaga seq->ns = htons(session->proto.l2tp.ns_nxt);
18958876230bShsuenaga session->proto.l2tp.ns_nxt++;
18968876230bShsuenaga session->proto.l2tp.ns_gap++;
18978876230bShsuenaga session->proto.l2tp.nr_acked = session->proto.l2tp.nr_nxt - 1;
18988876230bShsuenaga seq->nr = htons(session->proto.l2tp.nr_acked);
1899707c5f41Smvs mtx_leave(&session->pxs_mtx);
19008876230bShsuenaga }
1901faf7e06fSmpi l2tp->flagsver = htons(l2tp->flagsver);
19028876230bShsuenaga
19038876230bShsuenaga plen += sizeof(struct udphdr);
19048876230bShsuenaga udp = (struct udphdr *)(mtod(m0, caddr_t) + hlen);
19058876230bShsuenaga udp->uh_sport = session->local.sin6.sin6_port;
19068876230bShsuenaga udp->uh_dport = session->peer.sin6.sin6_port;
19078876230bShsuenaga udp->uh_ulen = htons(plen);
190811d6b1faShenning udp->uh_sum = 0;
19098876230bShsuenaga
191011d6b1faShenning m0->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
1911002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex;
191285d4897fSmarkus #if NPF > 0
191385d4897fSmarkus pf_pkt_addr_changed(m0);
191485d4897fSmarkus #endif
19158876230bShsuenaga switch (session->peer.sin6.sin6_family) {
19168876230bShsuenaga case AF_INET:
19178876230bShsuenaga ip = mtod(m0, struct ip *);
19188876230bShsuenaga ip->ip_p = IPPROTO_UDP;
19198876230bShsuenaga ip->ip_src = session->local.sin4.sin_addr;
19208876230bShsuenaga ip->ip_dst = session->peer.sin4.sin_addr;
19218876230bShsuenaga ip->ip_len = htons(hlen + plen);
19228876230bShsuenaga ip->ip_ttl = MAXTTL;
19238876230bShsuenaga ip->ip_tos = 0;
19245cd62ff4Syasuoka ip->ip_off = 0;
19258876230bShsuenaga
1926707c5f41Smvs mtx_enter(&session->pxs_mtx);
1927a1c674a4Syasuoka if (session->proto.l2tp.ipsecflowinfo > 0) {
1928a1c674a4Syasuoka if ((mtag = m_tag_get(PACKET_TAG_IPSEC_FLOWINFO,
1929707c5f41Smvs sizeof(u_int32_t), M_NOWAIT)) == NULL) {
1930707c5f41Smvs mtx_leave(&session->pxs_mtx);
1931a1c674a4Syasuoka goto drop;
1932707c5f41Smvs }
1933707c5f41Smvs
1934a1c674a4Syasuoka *(u_int32_t *)(mtag + 1) =
1935a1c674a4Syasuoka session->proto.l2tp.ipsecflowinfo;
1936a1c674a4Syasuoka m_tag_prepend(m0, mtag);
1937a1c674a4Syasuoka }
1938707c5f41Smvs mtx_leave(&session->pxs_mtx);
1939a1c674a4Syasuoka
19400315bf92Sdlg ip_send(m0);
19418876230bShsuenaga break;
19428876230bShsuenaga #ifdef INET6
19438876230bShsuenaga case AF_INET6:
19448876230bShsuenaga ip6 = mtod(m0, struct ip6_hdr *);
19458876230bShsuenaga
19468876230bShsuenaga ip6->ip6_flow = 0;
19478876230bShsuenaga ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
19488876230bShsuenaga ip6->ip6_vfc |= IPV6_VERSION;
19498876230bShsuenaga ip6->ip6_nxt = IPPROTO_UDP;
19508876230bShsuenaga ip6->ip6_src = session->local.sin6.sin6_addr;
1951952c6363Sbluhm in6_embedscope(&ip6->ip6_dst, &session->peer.sin6, NULL, NULL);
19528876230bShsuenaga /* ip6->ip6_plen will be filled in ip6_output. */
19538876230bShsuenaga
19540315bf92Sdlg ip6_send(m0);
19558876230bShsuenaga break;
19568876230bShsuenaga #endif
19578876230bShsuenaga }
1958967e9010Sdlg udpstat_inc(udps_opackets);
19598876230bShsuenaga
19608876230bShsuenaga if (datalen > 0) { /* network layer only */
19618876230bShsuenaga /* countup statistics */
1962df21f681Smvs counters_pkt(session->stat_counters, pxc_opackets,
1963df21f681Smvs pxc_obytes, datalen);
19648876230bShsuenaga }
19658876230bShsuenaga
19668876230bShsuenaga return;
19678876230bShsuenaga drop:
1968a1c674a4Syasuoka m_freem(m0);
1969df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
19708876230bShsuenaga }
19718876230bShsuenaga
19728876230bShsuenaga struct pipex_session *
pipex_l2tp_lookup_session(struct mbuf * m0,int off)19738876230bShsuenaga pipex_l2tp_lookup_session(struct mbuf *m0, int off)
19748876230bShsuenaga {
19758876230bShsuenaga struct pipex_session *session;
19768876230bShsuenaga uint16_t flags, session_id, ver;
19778876230bShsuenaga u_char *cp, buf[PIPEX_L2TP_MINLEN];
19788876230bShsuenaga
19798876230bShsuenaga if (m0->m_pkthdr.len < off + PIPEX_L2TP_MINLEN) {
19808876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG,
19818876230bShsuenaga "<%s> packet length is too short", __func__));
19828876230bShsuenaga goto not_ours;
19838876230bShsuenaga }
19848876230bShsuenaga
19858876230bShsuenaga /* get first 16bits of L2TP */
19868876230bShsuenaga m_copydata(m0, off, sizeof(buf), buf);
19878876230bShsuenaga cp = buf;
19888876230bShsuenaga GETSHORT(flags, cp);
19898876230bShsuenaga ver = flags & PIPEX_L2TP_VER_MASK;
19908876230bShsuenaga
19918876230bShsuenaga /* l2tp version must be '2' */
19928876230bShsuenaga if (ver != PIPEX_L2TP_VER) {
19938876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG,
19948876230bShsuenaga "<%s> l2tp header wrong version %u.", __func__, ver));
19958876230bShsuenaga goto not_ours;
19968876230bShsuenaga }
19978876230bShsuenaga if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0)
19988876230bShsuenaga goto not_ours;
19998876230bShsuenaga
20008876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_LENGTH)
20018876230bShsuenaga cp += 2; /* skip length field */
20028876230bShsuenaga cp += 2; /* skip tunnel-id field */
20038876230bShsuenaga GETSHORT(session_id, cp); /* get session-id field */
20048876230bShsuenaga
20058876230bShsuenaga /* lookup pipex session table */
20068876230bShsuenaga session = pipex_lookup_by_session_id(PIPEX_PROTO_L2TP, session_id);
20078876230bShsuenaga #ifdef PIPEX_DEBUG
20088876230bShsuenaga if (session == NULL) {
20098876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG,
20108876230bShsuenaga "<%s> session not found (id=%d)", __func__, session_id));
20118876230bShsuenaga goto not_ours;
20128876230bShsuenaga }
20138876230bShsuenaga #endif
20148876230bShsuenaga
20158876230bShsuenaga return (session);
20168876230bShsuenaga
20178876230bShsuenaga not_ours:
20188876230bShsuenaga return (NULL);
20198876230bShsuenaga }
20208876230bShsuenaga
20218876230bShsuenaga struct mbuf *
pipex_l2tp_input(struct mbuf * m0,int off0,struct pipex_session * session,uint32_t ipsecflowinfo)2022a5018c90Syasuoka pipex_l2tp_input(struct mbuf *m0, int off0, struct pipex_session *session,
2023a5018c90Syasuoka uint32_t ipsecflowinfo)
20248876230bShsuenaga {
20258876230bShsuenaga struct pipex_l2tp_session *l2tp_session;
2026707c5f41Smvs int length = 0, offset = 0, hlen, nseq;
2027707c5f41Smvs u_char *cp, *nsp = NULL, *nrp = NULL;
20288876230bShsuenaga uint16_t flags, ns = 0, nr = 0;
2029e405d423Syasuoka int rewind = 0;
20308876230bShsuenaga
2031707c5f41Smvs mtx_enter(&session->pxs_mtx);
2032f6e6f8e7Smvs
20338876230bShsuenaga l2tp_session = &session->proto.l2tp;
2034a5018c90Syasuoka l2tp_session->ipsecflowinfo = ipsecflowinfo;
20358876230bShsuenaga
20365c7fed39Sdlg m_copydata(m0, off0, sizeof(flags), &flags);
20378876230bShsuenaga
20388876230bShsuenaga flags = ntohs(flags) & PIPEX_L2TP_FLAG_MASK;
20398876230bShsuenaga KASSERT((flags & PIPEX_L2TP_FLAG_TYPE) == 0);
20408876230bShsuenaga
20418876230bShsuenaga hlen = 2; /* flags and version fields */
20428876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_LENGTH) /* length */
20438876230bShsuenaga hlen += 2;
20448876230bShsuenaga hlen += 4; /* tunnel-id and session-id */
20458876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_SEQUENCE) /* ns and nr */
20468876230bShsuenaga hlen += 4;
20478876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_OFFSET) /* offset */
20488876230bShsuenaga hlen += 2;
20498876230bShsuenaga
20508876230bShsuenaga PIPEX_PULLUP(m0, off0 + hlen);
20518876230bShsuenaga if (m0 == NULL)
20528876230bShsuenaga goto drop;
20538876230bShsuenaga
20548876230bShsuenaga cp = mtod(m0, u_char *) + off0;
20558876230bShsuenaga cp += 2; /* flags and version */
20568876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_LENGTH)
20578876230bShsuenaga GETSHORT(length, cp);
20588876230bShsuenaga else
20598876230bShsuenaga length = m0->m_pkthdr.len - off0;
20608876230bShsuenaga cp += 4; /* skip tunnel-id and session-id field */
20618876230bShsuenaga
20628876230bShsuenaga /* pullup for seek sequences in header */
20638876230bShsuenaga nseq = 0;
20648876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_SEQUENCE) {
20658876230bShsuenaga nsp = cp;
20668876230bShsuenaga GETSHORT(ns, cp);
20678876230bShsuenaga nrp = cp;
20688876230bShsuenaga GETSHORT(nr, cp);
20698876230bShsuenaga
20708876230bShsuenaga nr++;
20718876230bShsuenaga if (SEQ16_GT(nr, l2tp_session->ns_una) &&
20728876230bShsuenaga SEQ16_LE(nr, l2tp_session->ns_nxt))
20738876230bShsuenaga /* update 'ns_una' only if the ns is in valid range */
20748876230bShsuenaga l2tp_session->ns_una = nr;
2075e405d423Syasuoka if (SEQ16_LT(ns, l2tp_session->nr_nxt)) {
2076e405d423Syasuoka rewind = 1;
2077e405d423Syasuoka if (SEQ16_LT(ns,
2078e405d423Syasuoka l2tp_session->nr_nxt - PIPEX_REWIND_LIMIT))
20798876230bShsuenaga goto out_seq;
2080e405d423Syasuoka }
20818876230bShsuenaga
20828876230bShsuenaga ns++;
20838876230bShsuenaga nseq = SEQ16_SUB(ns, l2tp_session->nr_nxt);
2084e405d423Syasuoka if (!rewind)
20858876230bShsuenaga l2tp_session->nr_nxt = ns;
20868876230bShsuenaga }
20878876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_OFFSET)
20888876230bShsuenaga GETSHORT(offset, cp);
20898876230bShsuenaga
2090d5af01deSyasuoka length -= hlen + offset;
2091d5af01deSyasuoka hlen += off0 + offset;
2092707c5f41Smvs
2093707c5f41Smvs /*
2094707c5f41Smvs * The following pipex_common_input() will release `pxs_mtx'
2095707c5f41Smvs * deep within if the packet will be consumed. In the error
2096707c5f41Smvs * path lock will be held all the time. So increment `nr_gap'
2097707c5f41Smvs * here, and on the error path back it out, no atomicity will
2098707c5f41Smvs * be lost in all cases.
2099707c5f41Smvs */
2100e405d423Syasuoka if (!rewind)
21018876230bShsuenaga session->proto.l2tp.nr_gap += nseq;
2102707c5f41Smvs m0 = pipex_common_input(session, m0, hlen, length, 1);
2103707c5f41Smvs if (m0 == NULL) {
2104707c5f41Smvs /*
2105707c5f41Smvs * pipex_common_input() releases lock if the
2106707c5f41Smvs * packet was consumed.
2107707c5f41Smvs */
2108d5af01deSyasuoka return (NULL);
21098876230bShsuenaga }
21108876230bShsuenaga
2111e405d423Syasuoka if (rewind)
2112e405d423Syasuoka goto out_seq;
2113707c5f41Smvs else {
2114707c5f41Smvs /* The packet is not ours, backout `nr_gap'. */
2115707c5f41Smvs session->proto.l2tp.nr_gap -= nseq;
2116707c5f41Smvs }
2117e405d423Syasuoka
21188876230bShsuenaga /*
21198876230bShsuenaga * overwrite sequence numbers to adjust a gap between pipex and
21208876230bShsuenaga * userland.
21218876230bShsuenaga */
21228876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_SEQUENCE) {
21238876230bShsuenaga --ns; --nr; /* revert original values */
21248876230bShsuenaga ns -= l2tp_session->nr_gap;
21258876230bShsuenaga PUTSHORT(ns, nsp);
21268876230bShsuenaga
21278876230bShsuenaga if (l2tp_session->ns_nxt == l2tp_session->ns_una) {
21288876230bShsuenaga nr -= l2tp_session->ns_gap;
21298876230bShsuenaga l2tp_session->ul_ns_una = nr;
21308876230bShsuenaga } else {
21318876230bShsuenaga /*
21328876230bShsuenaga * There are sending packets they are not acked.
21338876230bShsuenaga * In this situation, (ack - snd_gap) may points
21348876230bShsuenaga * before sending window of userland. So we don't
21358876230bShsuenaga * update the ack number.
21368876230bShsuenaga */
21378876230bShsuenaga nr = l2tp_session->ul_ns_una;
21388876230bShsuenaga }
21398876230bShsuenaga PUTSHORT(nr, nrp);
21408876230bShsuenaga }
21418876230bShsuenaga
2142707c5f41Smvs mtx_leave(&session->pxs_mtx);
2143707c5f41Smvs
21448876230bShsuenaga return (m0);
21458876230bShsuenaga out_seq:
21468876230bShsuenaga pipex_session_log(session, LOG_DEBUG,
21478876230bShsuenaga "Received bad data packet: out of sequence: seq=%u(%u-) "
21488876230bShsuenaga "ack=%u(%u-%u)", ns, l2tp_session->nr_nxt, nr, l2tp_session->ns_una,
21498876230bShsuenaga l2tp_session->ns_nxt);
21508876230bShsuenaga /* FALLTHROUGH */
21518876230bShsuenaga drop:
2152707c5f41Smvs mtx_leave(&session->pxs_mtx);
2153707c5f41Smvs
21548876230bShsuenaga m_freem(m0);
2155df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
21568876230bShsuenaga
21578876230bShsuenaga return (NULL);
21588876230bShsuenaga }
21598876230bShsuenaga
21608876230bShsuenaga struct pipex_session *
pipex_l2tp_userland_lookup_session_ipv4(struct mbuf * m0,struct in_addr dst)21618876230bShsuenaga pipex_l2tp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst)
21628876230bShsuenaga {
21639c794535Sbluhm struct sockaddr_in sin;
21648876230bShsuenaga
21659c794535Sbluhm memset(&sin, 0, sizeof(sin));
21669c794535Sbluhm sin.sin_len = sizeof(sin);
21679c794535Sbluhm sin.sin_family = AF_INET;
21689c794535Sbluhm sin.sin_addr = dst;
21698876230bShsuenaga
21709c794535Sbluhm return pipex_l2tp_userland_lookup_session(m0, sintosa(&sin));
21718876230bShsuenaga }
21728876230bShsuenaga
21738876230bShsuenaga #ifdef INET6
21748876230bShsuenaga struct pipex_session *
pipex_l2tp_userland_lookup_session_ipv6(struct mbuf * m0,struct in6_addr dst)21758876230bShsuenaga pipex_l2tp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst)
21768876230bShsuenaga {
21778876230bShsuenaga struct sockaddr_in6 sin6;
21788876230bShsuenaga
21799c794535Sbluhm memset(&sin6, 0, sizeof(sin6));
21809c794535Sbluhm sin6.sin6_len = sizeof(sin6);
21818876230bShsuenaga sin6.sin6_family = AF_INET6;
21826c8dd0e9Sclaudio in6_recoverscope(&sin6, &dst);
21838876230bShsuenaga
21849c794535Sbluhm return pipex_l2tp_userland_lookup_session(m0, sin6tosa(&sin6));
21858876230bShsuenaga }
21868876230bShsuenaga #endif
21878876230bShsuenaga
21887b163281Syasuoka struct pipex_session *
pipex_l2tp_userland_lookup_session(struct mbuf * m0,struct sockaddr * sa)21898876230bShsuenaga pipex_l2tp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa)
21908876230bShsuenaga {
21918876230bShsuenaga struct pipex_l2tp_header l2tp;
21928876230bShsuenaga struct pipex_hash_head *list;
21938876230bShsuenaga struct pipex_session *session;
21948876230bShsuenaga uint16_t session_id, tunnel_id, flags;
21958876230bShsuenaga
21967b163281Syasuoka if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
21977b163281Syasuoka return (NULL);
21987b163281Syasuoka
21998876230bShsuenaga /* pullup */
22008876230bShsuenaga if (m0->m_pkthdr.len < sizeof(l2tp)) {
22018876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG,
22028876230bShsuenaga "<%s> packet length is too short", __func__));
22038876230bShsuenaga return (NULL);
22048876230bShsuenaga }
22058876230bShsuenaga
22068876230bShsuenaga /* get flags */
22075c7fed39Sdlg m_copydata(m0, 0, sizeof(l2tp), &l2tp);
22088876230bShsuenaga flags = ntohs(l2tp.flagsver);
22098876230bShsuenaga
22108876230bShsuenaga /* l2tp version must be '2' */
22118876230bShsuenaga if ((flags & PIPEX_L2TP_VER_MASK) != PIPEX_L2TP_VER) {
22128876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG,
22138876230bShsuenaga "<%s> l2tp header wrong version.", __func__));
22148876230bShsuenaga return (NULL);
22158876230bShsuenaga }
22168876230bShsuenaga /* We need L2TP data messages only */
22178876230bShsuenaga if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0)
22188876230bShsuenaga return (NULL);
22198876230bShsuenaga /* No need to hook packets that don't have the sequence field */
22208876230bShsuenaga if ((flags & PIPEX_L2TP_FLAG_SEQUENCE) == 0)
22218876230bShsuenaga return (NULL);
22228876230bShsuenaga
22238876230bShsuenaga session_id = ntohs(l2tp.session_id);
22248876230bShsuenaga tunnel_id = ntohs(l2tp.tunnel_id);
22258876230bShsuenaga
2226f6e6f8e7Smvs mtx_enter(&pipex_list_mtx);
2227f6e6f8e7Smvs
22288876230bShsuenaga list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa));
22298876230bShsuenaga LIST_FOREACH(session, list, peer_addr_chain) {
22309c681c75Sbluhm if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0)
22318876230bShsuenaga continue;
22328876230bShsuenaga if (session->proto.l2tp.peer_tunnel_id != tunnel_id)
22338876230bShsuenaga continue;
22348876230bShsuenaga if (session->peer_session_id == session_id)
22358876230bShsuenaga break;
22368876230bShsuenaga }
2237f6e6f8e7Smvs
2238f6e6f8e7Smvs if (session != NULL)
2239f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt);
2240f6e6f8e7Smvs
2241f6e6f8e7Smvs mtx_leave(&pipex_list_mtx);
2242f6e6f8e7Smvs
22438876230bShsuenaga #ifdef PIPEX_DEBUG
22448876230bShsuenaga if (session == NULL) {
22458876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found "
22468876230bShsuenaga "(tunnel_id=%d, session_id=%d)", __func__,
22478876230bShsuenaga tunnel_id, session_id));
22488876230bShsuenaga }
22498876230bShsuenaga #endif
22508876230bShsuenaga
22518876230bShsuenaga return (session);
22528876230bShsuenaga }
22538876230bShsuenaga
22548876230bShsuenaga struct mbuf *
pipex_l2tp_userland_output(struct mbuf * m0,struct pipex_session * session)22558876230bShsuenaga pipex_l2tp_userland_output(struct mbuf *m0, struct pipex_session *session)
22568876230bShsuenaga {
22578876230bShsuenaga struct pipex_l2tp_header *l2tp;
22588876230bShsuenaga struct pipex_l2tp_seq_header *seq;
22598876230bShsuenaga uint16_t ns, nr;
22608876230bShsuenaga
22618876230bShsuenaga /* check length */
22628876230bShsuenaga PIPEX_PULLUP(m0, sizeof(struct pipex_l2tp_header) +
22638876230bShsuenaga sizeof(struct pipex_l2tp_seq_header));
22648876230bShsuenaga if (m0 == NULL)
22658876230bShsuenaga return (NULL);
22668876230bShsuenaga
22678876230bShsuenaga l2tp = mtod(m0, struct pipex_l2tp_header *);
22688876230bShsuenaga KASSERT(ntohs(l2tp->flagsver) & PIPEX_L2TP_FLAG_SEQUENCE);
22698876230bShsuenaga
22708876230bShsuenaga /*
22718876230bShsuenaga * overwrite sequence numbers to adjust a gap between pipex and
22728876230bShsuenaga * userland.
22738876230bShsuenaga */
22748876230bShsuenaga seq = (struct pipex_l2tp_seq_header *)(l2tp + 1);
22758876230bShsuenaga ns = ntohs(seq->ns);
22768876230bShsuenaga nr = ntohs(seq->nr);
22778876230bShsuenaga
2278707c5f41Smvs mtx_enter(&session->pxs_mtx);
2279707c5f41Smvs
22808876230bShsuenaga ns += session->proto.l2tp.ns_gap;
22818876230bShsuenaga seq->ns = htons(ns);
22828876230bShsuenaga session->proto.l2tp.ns_nxt++;
22838876230bShsuenaga
22848876230bShsuenaga nr += session->proto.l2tp.nr_gap;
22858876230bShsuenaga seq->nr = htons(nr);
22868876230bShsuenaga if (SEQ16_GT(nr, session->proto.l2tp.nr_acked))
22878876230bShsuenaga session->proto.l2tp.nr_acked = nr;
22888876230bShsuenaga
2289707c5f41Smvs mtx_leave(&session->pxs_mtx);
2290707c5f41Smvs
22918876230bShsuenaga return (m0);
22928876230bShsuenaga }
22938876230bShsuenaga #endif /* PIPEX_L2TP */
22948876230bShsuenaga
2295a0ade7e9Syasuoka #ifdef PIPEX_MPPE
2296a0ade7e9Syasuoka /**********************************************************************
2297a0ade7e9Syasuoka * MPPE
2298a0ade7e9Syasuoka ***********************************************************************/
2299a0ade7e9Syasuoka #define PIPEX_COHERENCY_CNT_MASK 0x0fff
2300f7863625Smillert
2301f7863625Smillert static inline int
pipex_mppe_setkey(struct pipex_mppe * mppe)2302f7863625Smillert pipex_mppe_setkey(struct pipex_mppe *mppe)
2303f7863625Smillert {
2304f7863625Smillert rc4_keysetup(&mppe->rc4ctx, mppe->session_key, mppe->keylen);
2305f7863625Smillert
2306f7863625Smillert return (0);
2307f7863625Smillert }
2308f7863625Smillert
2309f7863625Smillert static inline int
pipex_mppe_setoldkey(struct pipex_mppe * mppe,uint16_t coher_cnt)2310f7863625Smillert pipex_mppe_setoldkey(struct pipex_mppe *mppe, uint16_t coher_cnt)
2311f7863625Smillert {
2312f7863625Smillert KASSERT(mppe->old_session_keys != NULL);
2313f7863625Smillert
2314f7863625Smillert rc4_keysetup(&mppe->rc4ctx,
2315f7863625Smillert mppe->old_session_keys[coher_cnt & PIPEX_MPPE_OLDKEYMASK],
2316f7863625Smillert mppe->keylen);
2317f7863625Smillert
2318f7863625Smillert return (0);
2319f7863625Smillert }
2320f7863625Smillert
2321f7863625Smillert static inline void
pipex_mppe_crypt(struct pipex_mppe * mppe,int len,u_char * indata,u_char * outdata)2322f7863625Smillert pipex_mppe_crypt(struct pipex_mppe *mppe, int len, u_char *indata,
2323f7863625Smillert u_char *outdata)
2324f7863625Smillert {
2325f7863625Smillert rc4_crypt(&mppe->rc4ctx, indata, outdata, len);
2326f7863625Smillert }
2327f7863625Smillert
2328c02a3381Smvs void
pipex_mppe_init(struct pipex_mppe * mppe,int stateless,int keylenbits,u_char * master_key,int has_oldkey)2329e405d423Syasuoka pipex_mppe_init(struct pipex_mppe *mppe, int stateless, int keylenbits,
2330e405d423Syasuoka u_char *master_key, int has_oldkey)
2331a0ade7e9Syasuoka {
2332e405d423Syasuoka memset(mppe, 0, sizeof(struct pipex_mppe));
23332960d3c8Smvs mtx_init(&mppe->pxm_mtx, IPL_SOFTNET);
2334e405d423Syasuoka if (stateless)
23351935ef3dSmvs mppe->flags |= PIPEX_MPPE_STATELESS;
2336e405d423Syasuoka if (has_oldkey)
2337e405d423Syasuoka mppe->old_session_keys =
2338edccd885Suebayasi pool_get(&mppe_key_pool, PR_WAITOK);
2339e405d423Syasuoka else
2340e405d423Syasuoka mppe->old_session_keys = NULL;
2341e405d423Syasuoka memcpy(mppe->master_key, master_key, sizeof(mppe->master_key));
2342a0ade7e9Syasuoka
2343e405d423Syasuoka mppe->keylenbits = keylenbits;
2344e405d423Syasuoka switch (keylenbits) {
2345a0ade7e9Syasuoka case 40:
2346a0ade7e9Syasuoka case 56:
2347a0ade7e9Syasuoka mppe->keylen = 8;
2348a0ade7e9Syasuoka break;
2349a0ade7e9Syasuoka case 128:
2350a0ade7e9Syasuoka mppe->keylen = 16;
2351a0ade7e9Syasuoka break;
2352a0ade7e9Syasuoka }
2353a0ade7e9Syasuoka
235463d4736dSyasuoka GetNewKeyFromSHA(mppe->master_key, mppe->master_key, mppe->keylen,
235563d4736dSyasuoka mppe->session_key);
2356a0ade7e9Syasuoka pipex_mppe_reduce_key(mppe);
2357e405d423Syasuoka pipex_mppe_setkey(mppe);
2358e405d423Syasuoka }
2359e405d423Syasuoka
2360e405d423Syasuoka void
pipex_session_init_mppe_recv(struct pipex_session * session,int stateless,int keylenbits,u_char * master_key)2361e405d423Syasuoka pipex_session_init_mppe_recv(struct pipex_session *session, int stateless,
2362e405d423Syasuoka int keylenbits, u_char *master_key)
2363e405d423Syasuoka {
2364e405d423Syasuoka pipex_mppe_init(&session->mppe_recv, stateless, keylenbits,
2365e405d423Syasuoka master_key, stateless);
2366e405d423Syasuoka session->ppp_flags |= PIPEX_PPP_MPPE_ACCEPTED;
2367e405d423Syasuoka }
2368e405d423Syasuoka
2369e405d423Syasuoka void
pipex_session_init_mppe_send(struct pipex_session * session,int stateless,int keylenbits,u_char * master_key)2370e405d423Syasuoka pipex_session_init_mppe_send(struct pipex_session *session, int stateless,
2371e405d423Syasuoka int keylenbits, u_char *master_key)
2372e405d423Syasuoka {
2373e405d423Syasuoka pipex_mppe_init(&session->mppe_send, stateless, keylenbits,
2374e405d423Syasuoka master_key, 0);
2375e405d423Syasuoka session->ppp_flags |= PIPEX_PPP_MPPE_ENABLED;
2376a0ade7e9Syasuoka }
2377a0ade7e9Syasuoka
2378a0ade7e9Syasuoka #include <crypto/sha1.h>
2379a0ade7e9Syasuoka
2380a0ade7e9Syasuoka static u_char SHAPad1[] = {
2381a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2382a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2383a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2384a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2385a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2386a0ade7e9Syasuoka }, SHAPad2[] = {
2387a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2388a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2389a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2390a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2391a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2392a0ade7e9Syasuoka };
2393a0ade7e9Syasuoka
2394c02a3381Smvs void
GetNewKeyFromSHA(u_char * StartKey,u_char * SessionKey,int SessionKeyLength,u_char * InterimKey)239563d4736dSyasuoka GetNewKeyFromSHA(u_char *StartKey, u_char *SessionKey, int SessionKeyLength,
239663d4736dSyasuoka u_char *InterimKey)
2397a0ade7e9Syasuoka {
2398a0ade7e9Syasuoka u_char Digest[20];
239963d4736dSyasuoka SHA1_CTX Context;
2400a0ade7e9Syasuoka
240163d4736dSyasuoka SHA1Init(&Context);
240263d4736dSyasuoka SHA1Update(&Context, StartKey, SessionKeyLength);
240363d4736dSyasuoka SHA1Update(&Context, SHAPad1, 40);
240463d4736dSyasuoka SHA1Update(&Context, SessionKey, SessionKeyLength);
240563d4736dSyasuoka SHA1Update(&Context, SHAPad2, 40);
240663d4736dSyasuoka SHA1Final(Digest, &Context);
2407a0ade7e9Syasuoka
240863d4736dSyasuoka memcpy(InterimKey, Digest, SessionKeyLength);
2409a0ade7e9Syasuoka }
2410a0ade7e9Syasuoka
2411c02a3381Smvs void
pipex_mppe_reduce_key(struct pipex_mppe * mppe)2412a0ade7e9Syasuoka pipex_mppe_reduce_key(struct pipex_mppe *mppe)
2413a0ade7e9Syasuoka {
2414a0ade7e9Syasuoka switch (mppe->keylenbits) {
2415a0ade7e9Syasuoka case 40:
24167704014bSmestre mppe->session_key[0] = 0xd1;
2417a0ade7e9Syasuoka mppe->session_key[1] = 0x26;
2418a0ade7e9Syasuoka mppe->session_key[2] = 0x9e;
24197704014bSmestre break;
2420a0ade7e9Syasuoka case 56:
2421a0ade7e9Syasuoka mppe->session_key[0] = 0xd1;
24227704014bSmestre break;
2423a0ade7e9Syasuoka }
2424a0ade7e9Syasuoka }
2425a0ade7e9Syasuoka
2426c02a3381Smvs void
mppe_key_change(struct pipex_mppe * mppe)2427a0ade7e9Syasuoka mppe_key_change(struct pipex_mppe *mppe)
2428a0ade7e9Syasuoka {
2429a0ade7e9Syasuoka u_char interim[16];
2430e405d423Syasuoka struct rc4_ctx keychg;
2431a0ade7e9Syasuoka
2432a0ade7e9Syasuoka memset(&keychg, 0, sizeof(keychg));
2433a0ade7e9Syasuoka
243463d4736dSyasuoka GetNewKeyFromSHA(mppe->master_key, mppe->session_key, mppe->keylen,
243563d4736dSyasuoka interim);
2436a0ade7e9Syasuoka
2437e405d423Syasuoka rc4_keysetup(&keychg, interim, mppe->keylen);
2438e405d423Syasuoka rc4_crypt(&keychg, interim, mppe->session_key, mppe->keylen);
243963d4736dSyasuoka
2440a0ade7e9Syasuoka pipex_mppe_reduce_key(mppe);
2441e405d423Syasuoka
2442e405d423Syasuoka if (mppe->old_session_keys) {
2443e405d423Syasuoka int idx = mppe->coher_cnt & PIPEX_MPPE_OLDKEYMASK;
2444e405d423Syasuoka memcpy(mppe->old_session_keys[idx],
2445e405d423Syasuoka mppe->session_key, PIPEX_MPPE_KEYLEN);
2446e405d423Syasuoka }
2447a0ade7e9Syasuoka }
2448a0ade7e9Syasuoka
2449707c5f41Smvs struct mbuf *
pipex_mppe_input(struct mbuf * m0,struct pipex_session * session)2450a0ade7e9Syasuoka pipex_mppe_input(struct mbuf *m0, struct pipex_session *session)
2451a0ade7e9Syasuoka {
2452a0ade7e9Syasuoka int pktloss, encrypt, flushed, m, n, len;
2453a0ade7e9Syasuoka struct pipex_mppe *mppe;
2454a0ade7e9Syasuoka uint16_t coher_cnt;
2455a0ade7e9Syasuoka struct mbuf *m1;
2456a0ade7e9Syasuoka u_char *cp;
2457e405d423Syasuoka int rewind = 0;
2458a0ade7e9Syasuoka
2459a0ade7e9Syasuoka /* pullup */
2460a0ade7e9Syasuoka PIPEX_PULLUP(m0, sizeof(coher_cnt));
2461a0ade7e9Syasuoka if (m0 == NULL)
2462a0ade7e9Syasuoka goto drop;
2463a0ade7e9Syasuoka
2464a0ade7e9Syasuoka mppe = &session->mppe_recv;
2465a0ade7e9Syasuoka /* get header information */
2466a0ade7e9Syasuoka cp = mtod(m0, u_char *);
2467a0ade7e9Syasuoka GETSHORT(coher_cnt, cp);
2468a0ade7e9Syasuoka flushed = ((coher_cnt & 0x8000) != 0) ? 1 : 0;
2469a0ade7e9Syasuoka encrypt = ((coher_cnt & 0x1000) != 0) ? 1 : 0;
2470a0ade7e9Syasuoka coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2471a0ade7e9Syasuoka pktloss = 0;
2472a0ade7e9Syasuoka
24732960d3c8Smvs mtx_enter(&mppe->pxm_mtx);
24742960d3c8Smvs
2475a0ade7e9Syasuoka PIPEX_MPPE_DBG((session, LOG_DEBUG, "in coher_cnt=%03x %s%s",
2476a0ade7e9Syasuoka mppe->coher_cnt, (flushed) ? "[flushed]" : "",
2477a0ade7e9Syasuoka (encrypt) ? "[encrypt]" : ""));
2478a0ade7e9Syasuoka
2479a0ade7e9Syasuoka if (encrypt == 0) {
24802960d3c8Smvs mtx_leave(&mppe->pxm_mtx);
2481a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG,
2482a0ade7e9Syasuoka "Received unexpected MPPE packet.(no ecrypt)");
2483a0ade7e9Syasuoka goto drop;
2484a0ade7e9Syasuoka }
2485a0ade7e9Syasuoka
2486a0ade7e9Syasuoka /* adjust mbuf */
2487a0ade7e9Syasuoka m_adj(m0, sizeof(coher_cnt));
2488a0ade7e9Syasuoka
2489a0ade7e9Syasuoka /*
2490a0ade7e9Syasuoka * L2TP data session may be used without sequencing, PPP frames may
2491a0ade7e9Syasuoka * arrive in disorder. The 'coherency counter' of MPPE detects such
2492a0ade7e9Syasuoka * situations, but we cannot distinguish between 'disorder' and
2493a0ade7e9Syasuoka * 'packet loss' exactly.
2494a0ade7e9Syasuoka *
2495a0ade7e9Syasuoka * When 'coherency counter' detects lost packets greater than
2496a0ade7e9Syasuoka * (4096 - 256), we treat as 'disorder' otherwise treat as
2497a0ade7e9Syasuoka * 'packet loss'.
2498a0ade7e9Syasuoka */
2499a0ade7e9Syasuoka {
2500a0ade7e9Syasuoka int coher_cnt0;
2501a0ade7e9Syasuoka
2502a0ade7e9Syasuoka coher_cnt0 = coher_cnt;
2503a0ade7e9Syasuoka if (coher_cnt < mppe->coher_cnt)
2504a0ade7e9Syasuoka coher_cnt0 += 0x1000;
2505a0ade7e9Syasuoka if (coher_cnt0 - mppe->coher_cnt > 0x0f00) {
25061935ef3dSmvs if ((mppe->flags & PIPEX_MPPE_STATELESS) == 0 ||
2507e405d423Syasuoka coher_cnt0 - mppe->coher_cnt
2508e405d423Syasuoka <= 0x1000 - PIPEX_MPPE_NOLDKEY) {
2509a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG,
2510a0ade7e9Syasuoka "Workaround the out-of-sequence PPP framing problem: "
2511a0ade7e9Syasuoka "%d => %d", mppe->coher_cnt, coher_cnt);
25122960d3c8Smvs mtx_leave(&mppe->pxm_mtx);
2513a0ade7e9Syasuoka goto drop;
2514a0ade7e9Syasuoka }
2515e405d423Syasuoka rewind = 1;
2516a0ade7e9Syasuoka }
2517e405d423Syasuoka }
2518e405d423Syasuoka
25191935ef3dSmvs if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) {
2520e405d423Syasuoka if (!rewind) {
2521a0ade7e9Syasuoka mppe_key_change(mppe);
2522a0ade7e9Syasuoka while (mppe->coher_cnt != coher_cnt) {
2523a0ade7e9Syasuoka mppe->coher_cnt++;
2524a0ade7e9Syasuoka mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2525e405d423Syasuoka mppe_key_change(mppe);
2526a0ade7e9Syasuoka pktloss++;
2527a0ade7e9Syasuoka }
2528e405d423Syasuoka }
2529e405d423Syasuoka pipex_mppe_setoldkey(mppe, coher_cnt);
2530a0ade7e9Syasuoka } else {
2531a0ade7e9Syasuoka if (flushed) {
2532a0ade7e9Syasuoka if (coher_cnt < mppe->coher_cnt) {
2533a0ade7e9Syasuoka coher_cnt += 0x1000;
2534a0ade7e9Syasuoka }
2535a0ade7e9Syasuoka pktloss += coher_cnt - mppe->coher_cnt;
2536a0ade7e9Syasuoka m = mppe->coher_cnt / 256;
2537a0ade7e9Syasuoka n = coher_cnt / 256;
2538a0ade7e9Syasuoka while (m++ < n)
2539a0ade7e9Syasuoka mppe_key_change(mppe);
2540a0ade7e9Syasuoka
2541a0ade7e9Syasuoka coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2542a0ade7e9Syasuoka mppe->coher_cnt = coher_cnt;
2543a0ade7e9Syasuoka } else if (mppe->coher_cnt != coher_cnt) {
25442960d3c8Smvs int ccp_id;
25452960d3c8Smvs
25462960d3c8Smvs mtx_leave(&mppe->pxm_mtx);
25472960d3c8Smvs
2548a0ade7e9Syasuoka /* Send CCP ResetReq */
2549a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetReq"));
25502960d3c8Smvs
25512960d3c8Smvs mtx_enter(&session->pxs_mtx);
25522960d3c8Smvs ccp_id = session->ccp_id;
25532960d3c8Smvs session->ccp_id++;
25542960d3c8Smvs mtx_leave(&session->pxs_mtx);
25552960d3c8Smvs
25562960d3c8Smvs pipex_ccp_output(session, CCP_RESETREQ, ccp_id);
2557a0ade7e9Syasuoka goto drop;
2558a0ade7e9Syasuoka }
2559a0ade7e9Syasuoka if ((coher_cnt & 0xff) == 0xff) {
2560a0ade7e9Syasuoka mppe_key_change(mppe);
2561a0ade7e9Syasuoka flushed = 1;
2562a0ade7e9Syasuoka }
2563e405d423Syasuoka if (flushed)
2564e405d423Syasuoka pipex_mppe_setkey(mppe);
2565a0ade7e9Syasuoka }
2566e405d423Syasuoka
2567a0ade7e9Syasuoka if (pktloss > 1000) {
2568a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG,
2569a0ade7e9Syasuoka "%d packets loss.", pktloss);
2570a0ade7e9Syasuoka }
2571a0ade7e9Syasuoka
2572a0ade7e9Syasuoka /* decrypt ppp payload */
2573a0ade7e9Syasuoka for (m1 = m0; m1; m1 = m1->m_next) {
2574a0ade7e9Syasuoka cp = mtod(m1, u_char *);
2575a0ade7e9Syasuoka len = m1->m_len;
2576e405d423Syasuoka pipex_mppe_crypt(mppe, len, cp, cp);
2577a0ade7e9Syasuoka }
2578a0ade7e9Syasuoka
2579e405d423Syasuoka if (!rewind) {
2580a0ade7e9Syasuoka /* update coher_cnt */
2581a0ade7e9Syasuoka mppe->coher_cnt++;
2582a0ade7e9Syasuoka mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2583e405d423Syasuoka }
25842960d3c8Smvs
25852960d3c8Smvs mtx_leave(&mppe->pxm_mtx);
25862960d3c8Smvs
2587363bb3c1Syasuoka if (m0->m_pkthdr.len < PIPEX_PPPMINLEN)
2588363bb3c1Syasuoka goto drop;
2589a0ade7e9Syasuoka
2590707c5f41Smvs return (m0);
2591a0ade7e9Syasuoka drop:
2592a0ade7e9Syasuoka m_freem(m0);
2593707c5f41Smvs return (NULL);
2594a0ade7e9Syasuoka }
2595a0ade7e9Syasuoka
2596707c5f41Smvs struct mbuf *
pipex_mppe_output(struct mbuf * m0,struct pipex_session * session,uint16_t protocol)259737f64355Syasuoka pipex_mppe_output(struct mbuf *m0, struct pipex_session *session,
259837f64355Syasuoka uint16_t protocol)
2599a0ade7e9Syasuoka {
2600a0ade7e9Syasuoka int encrypt, flushed, len;
260137f64355Syasuoka struct mppe_header {
2602a0ade7e9Syasuoka uint16_t coher_cnt;
260337f64355Syasuoka uint16_t protocol;
260437f64355Syasuoka } __packed *hdr;
2605a0ade7e9Syasuoka u_char *cp;
2606a0ade7e9Syasuoka struct pipex_mppe *mppe;
2607a0ade7e9Syasuoka struct mbuf *m;
2608a0ade7e9Syasuoka
2609a0ade7e9Syasuoka mppe = &session->mppe_send;
2610a0ade7e9Syasuoka
2611a0ade7e9Syasuoka /*
2612a0ade7e9Syasuoka * create a deep-copy if the mbuf has a shared mbuf cluster.
2613678831beSjsg * this is required to handle cases of tcp retransmission.
2614a0ade7e9Syasuoka */
2615a0ade7e9Syasuoka for (m = m0; m != NULL; m = m->m_next) {
2616a0ade7e9Syasuoka if (M_READONLY(m)) {
26177bb226caSdlg m = m_dup_pkt(m0, max_linkhdr, M_NOWAIT);
2618c31e5583Sdlg m_freem(m0);
2619a0ade7e9Syasuoka if (m == NULL)
2620707c5f41Smvs return (NULL);
2621a0ade7e9Syasuoka m0 = m;
2622a0ade7e9Syasuoka break;
2623a0ade7e9Syasuoka }
2624a0ade7e9Syasuoka }
2625cb76d290Syasuoka /* prepend mppe header */
2626cb76d290Syasuoka M_PREPEND(m0, sizeof(struct mppe_header), M_NOWAIT);
2627cb76d290Syasuoka if (m0 == NULL)
2628707c5f41Smvs return (NULL);
2629cb76d290Syasuoka hdr = mtod(m0, struct mppe_header *);
2630cb76d290Syasuoka hdr->protocol = protocol;
2631a0ade7e9Syasuoka
2632a0ade7e9Syasuoka /* check coherency counter */
2633a0ade7e9Syasuoka flushed = 0;
2634a0ade7e9Syasuoka encrypt = 1;
2635a0ade7e9Syasuoka
26362960d3c8Smvs mtx_enter(&mppe->pxm_mtx);
26372960d3c8Smvs
26381935ef3dSmvs if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) {
2639a0ade7e9Syasuoka flushed = 1;
2640a0ade7e9Syasuoka mppe_key_change(mppe);
2641a0ade7e9Syasuoka } else {
2642a0ade7e9Syasuoka if ((mppe->coher_cnt % 0x100) == 0xff) {
2643a0ade7e9Syasuoka flushed = 1;
2644a0ade7e9Syasuoka mppe_key_change(mppe);
26451935ef3dSmvs } else if ((mppe->flags & PIPEX_MPPE_RESETREQ) != 0) {
2646a0ade7e9Syasuoka flushed = 1;
26471935ef3dSmvs mppe->flags &= ~PIPEX_MPPE_RESETREQ;
2648a0ade7e9Syasuoka }
2649a0ade7e9Syasuoka }
2650a0ade7e9Syasuoka
2651a0ade7e9Syasuoka if (flushed)
2652e405d423Syasuoka pipex_mppe_setkey(mppe);
2653a0ade7e9Syasuoka
2654a0ade7e9Syasuoka PIPEX_MPPE_DBG((session, LOG_DEBUG, "out coher_cnt=%03x %s%s",
2655a0ade7e9Syasuoka mppe->coher_cnt, (flushed) ? "[flushed]" : "",
2656a0ade7e9Syasuoka (encrypt) ? "[encrypt]" : ""));
2657a0ade7e9Syasuoka
2658a0ade7e9Syasuoka /* setup header information */
265937f64355Syasuoka hdr->coher_cnt = (mppe->coher_cnt++) & PIPEX_COHERENCY_CNT_MASK;
266037f64355Syasuoka hdr->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2661a0ade7e9Syasuoka if (flushed)
266237f64355Syasuoka hdr->coher_cnt |= 0x8000;
2663a0ade7e9Syasuoka if (encrypt)
266437f64355Syasuoka hdr->coher_cnt |= 0x1000;
2665a0ade7e9Syasuoka
2666faf7e06fSmpi hdr->protocol = htons(hdr->protocol);
2667faf7e06fSmpi hdr->coher_cnt = htons(hdr->coher_cnt);
2668a0ade7e9Syasuoka
2669a0ade7e9Syasuoka /* encrypt chain */
267037f64355Syasuoka for (m = m0; m; m = m->m_next) {
2671a0ade7e9Syasuoka cp = mtod(m, u_char *);
2672a0ade7e9Syasuoka len = m->m_len;
267337f64355Syasuoka if (m == m0 && len > offsetof(struct mppe_header, protocol)) {
267437f64355Syasuoka len -= offsetof(struct mppe_header, protocol);
267537f64355Syasuoka cp += offsetof(struct mppe_header, protocol);
267637f64355Syasuoka }
2677e405d423Syasuoka pipex_mppe_crypt(mppe, len, cp, cp);
2678a0ade7e9Syasuoka }
2679a0ade7e9Syasuoka
26802960d3c8Smvs mtx_leave(&mppe->pxm_mtx);
26812960d3c8Smvs
2682707c5f41Smvs return (m0);
2683a0ade7e9Syasuoka }
2684a0ade7e9Syasuoka
2685c02a3381Smvs void
pipex_ccp_input(struct mbuf * m0,struct pipex_session * session)2686a0ade7e9Syasuoka pipex_ccp_input(struct mbuf *m0, struct pipex_session *session)
2687a0ade7e9Syasuoka {
2688a0ade7e9Syasuoka u_char *cp;
2689a0ade7e9Syasuoka int code, id, len;
2690a0ade7e9Syasuoka
2691a0ade7e9Syasuoka if (m0->m_pkthdr.len < PPP_HDRLEN)
2692a0ade7e9Syasuoka goto drop;
2693a0ade7e9Syasuoka if ((m0 = m_pullup(m0, PPP_HDRLEN)) == NULL)
2694a0ade7e9Syasuoka goto drop;
2695a0ade7e9Syasuoka
2696a0ade7e9Syasuoka cp = mtod(m0, u_char *);
2697a0ade7e9Syasuoka GETCHAR(code, cp);
2698a0ade7e9Syasuoka GETCHAR(id, cp);
2699a0ade7e9Syasuoka GETSHORT(len, cp);
2700a0ade7e9Syasuoka
2701a0ade7e9Syasuoka switch (code) {
2702a0ade7e9Syasuoka case CCP_RESETREQ:
2703a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetReq"));
27042960d3c8Smvs mtx_enter(&session->mppe_send.pxm_mtx);
27051935ef3dSmvs session->mppe_send.flags |= PIPEX_MPPE_RESETREQ;
27062960d3c8Smvs mtx_leave(&session->mppe_send.pxm_mtx);
2707a0ade7e9Syasuoka #ifndef PIPEX_NO_CCP_RESETACK
2708a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetAck"));
2709a0ade7e9Syasuoka pipex_ccp_output(session, CCP_RESETACK, id);
2710a0ade7e9Syasuoka #endif
2711a0ade7e9Syasuoka /* ignore error */
2712a0ade7e9Syasuoka break;
2713a0ade7e9Syasuoka case CCP_RESETACK:
2714a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetAck"));
2715a0ade7e9Syasuoka break;
2716a0ade7e9Syasuoka default:
2717a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP Recv code=%d", code));
2718a0ade7e9Syasuoka goto drop;
2719a0ade7e9Syasuoka }
2720a0ade7e9Syasuoka m_freem(m0);
2721a0ade7e9Syasuoka
2722a0ade7e9Syasuoka return;
2723a0ade7e9Syasuoka drop:
2724a0ade7e9Syasuoka m_freem(m0);
2725df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors);
2726a0ade7e9Syasuoka }
2727a0ade7e9Syasuoka
2728c02a3381Smvs int
pipex_ccp_output(struct pipex_session * session,int code,int id)2729a0ade7e9Syasuoka pipex_ccp_output(struct pipex_session *session, int code, int id)
2730a0ade7e9Syasuoka {
2731a0ade7e9Syasuoka u_char *cp;
2732a0ade7e9Syasuoka struct mbuf *m;
2733a0ade7e9Syasuoka
2734a0ade7e9Syasuoka MGETHDR(m, M_DONTWAIT, MT_DATA);
2735a0ade7e9Syasuoka if (m == NULL) {
2736df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors);
273763d4736dSyasuoka return (1);
2738a0ade7e9Syasuoka }
2739a0ade7e9Syasuoka m->m_pkthdr.len = m->m_len = 4;
2740a0ade7e9Syasuoka cp = mtod(m, u_char *);
2741a0ade7e9Syasuoka PUTCHAR(code, cp);
2742a0ade7e9Syasuoka PUTCHAR(id, cp);
2743a0ade7e9Syasuoka PUTSHORT(4, cp);
2744a0ade7e9Syasuoka
2745a0ade7e9Syasuoka pipex_ppp_output(m, session, PPP_CCP);
2746a0ade7e9Syasuoka
274763d4736dSyasuoka return (0);
2748a0ade7e9Syasuoka }
2749a0ade7e9Syasuoka #endif
2750a0ade7e9Syasuoka /***********************************************************************
2751678831beSjsg * Miscellaneous functions
2752a0ade7e9Syasuoka ***********************************************************************/
2753a0ade7e9Syasuoka /* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */
2754a0ade7e9Syasuoka /*
2755a0ade7e9Syasuoka * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
2756a0ade7e9Syasuoka * All rights reserved.
2757a0ade7e9Syasuoka *
2758a0ade7e9Syasuoka * Redistribution and use in source and binary forms, with or without
2759a0ade7e9Syasuoka * modification, are permitted provided that the following conditions
2760a0ade7e9Syasuoka * are met:
2761a0ade7e9Syasuoka * 1. Redistributions of source code must retain the above copyright
2762a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer.
2763a0ade7e9Syasuoka * 2. Redistributions in binary form must reproduce the above copyright
2764a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer in the
2765a0ade7e9Syasuoka * documentation and/or other materials provided with the distribution.
2766a0ade7e9Syasuoka *
2767a0ade7e9Syasuoka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2768a0ade7e9Syasuoka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2769a0ade7e9Syasuoka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2770a0ade7e9Syasuoka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2771a0ade7e9Syasuoka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2772a0ade7e9Syasuoka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2773a0ade7e9Syasuoka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2774a0ade7e9Syasuoka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2775a0ade7e9Syasuoka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2776a0ade7e9Syasuoka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2777a0ade7e9Syasuoka * SUCH DAMAGE.
2778a0ade7e9Syasuoka *
2779a0ade7e9Syasuoka * $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $
2780a0ade7e9Syasuoka */
2781a0ade7e9Syasuoka #define TCP_OPTLEN_IN_SEGMENT 12 /* timestamp option and padding */
2782a0ade7e9Syasuoka #define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \
2783a0ade7e9Syasuoka TCP_OPTLEN_IN_SEGMENT)
2784a0ade7e9Syasuoka /*
2785a0ade7e9Syasuoka * The following macro is used to update an internet checksum. "acc" is a
2786a0ade7e9Syasuoka * 32-bit accumulation of all the changes to the checksum (adding in old
2787a0ade7e9Syasuoka * 16-bit words and subtracting out new words), and "cksum" is the checksum
2788a0ade7e9Syasuoka * value to be updated.
2789a0ade7e9Syasuoka */
2790a0ade7e9Syasuoka #define ADJUST_CHECKSUM(acc, cksum) { \
2791a0ade7e9Syasuoka acc += cksum; \
2792a0ade7e9Syasuoka if (acc < 0) { \
2793a0ade7e9Syasuoka acc = -acc; \
2794a0ade7e9Syasuoka acc = (acc >> 16) + (acc & 0xffff); \
2795a0ade7e9Syasuoka acc += acc >> 16; \
2796a0ade7e9Syasuoka cksum = (u_short) ~acc; \
2797a0ade7e9Syasuoka } else { \
2798a0ade7e9Syasuoka acc = (acc >> 16) + (acc & 0xffff); \
2799a0ade7e9Syasuoka acc += acc >> 16; \
2800a0ade7e9Syasuoka cksum = (u_short) acc; \
2801a0ade7e9Syasuoka } \
2802a0ade7e9Syasuoka }
2803a0ade7e9Syasuoka
2804a0ade7e9Syasuoka /*
2805a0ade7e9Syasuoka * Rewrite max-segment-size TCP option to avoid PMTU blackhole issues.
2806a0ade7e9Syasuoka * The mtu parameter should be the MTU bottleneck (as far as we know)
2807a0ade7e9Syasuoka * on the link between the source and the destination.
2808a0ade7e9Syasuoka */
2809c02a3381Smvs struct mbuf *
adjust_tcp_mss(struct mbuf * m0,int mtu)2810a0ade7e9Syasuoka adjust_tcp_mss(struct mbuf *m0, int mtu)
2811a0ade7e9Syasuoka {
2812a0ade7e9Syasuoka int opt, optlen, acc, mss, maxmss, lpktp;
2813a0ade7e9Syasuoka struct ip *pip;
2814a0ade7e9Syasuoka struct tcphdr *th;
2815a0ade7e9Syasuoka u_char *pktp, *mssp;
2816a0ade7e9Syasuoka u_int16_t ip_off;
2817a0ade7e9Syasuoka
2818a0ade7e9Syasuoka lpktp = sizeof(struct ip) + sizeof(struct tcphdr) + PIPEX_TCP_OPTLEN;
2819a0ade7e9Syasuoka lpktp = MIN(lpktp, m0->m_pkthdr.len);
2820a0ade7e9Syasuoka
2821a0ade7e9Syasuoka PIPEX_PULLUP(m0, lpktp);
2822a0ade7e9Syasuoka if (m0 == NULL)
2823a0ade7e9Syasuoka goto drop;
2824a0ade7e9Syasuoka
2825a0ade7e9Syasuoka pktp = mtod(m0, char *);
2826a0ade7e9Syasuoka pip = (struct ip *)pktp;
2827a0ade7e9Syasuoka ip_off = ntohs(pip->ip_off);
2828a0ade7e9Syasuoka
2829a0ade7e9Syasuoka /* Non TCP or fragmented packet must not have a MSS option */
2830a0ade7e9Syasuoka if (pip->ip_p != IPPROTO_TCP ||
2831a0ade7e9Syasuoka (ip_off & IP_MF) != 0 || (ip_off & IP_OFFMASK) != 0)
2832a0ade7e9Syasuoka goto handled;
2833a0ade7e9Syasuoka
2834a0ade7e9Syasuoka pktp += pip->ip_hl << 2;
2835a0ade7e9Syasuoka lpktp -= pip->ip_hl << 2;
2836a0ade7e9Syasuoka
2837a0ade7e9Syasuoka /* packet is broken */
2838a0ade7e9Syasuoka if (sizeof(struct tcphdr) > lpktp)
2839a0ade7e9Syasuoka goto drop;
2840a0ade7e9Syasuoka th = (struct tcphdr *)pktp;
2841a0ade7e9Syasuoka
2842a0ade7e9Syasuoka /*
2843a0ade7e9Syasuoka * As RFC 973, a MSS field must only be sent in the initial
2844a0ade7e9Syasuoka * connection request(it must be with SYN).
2845a0ade7e9Syasuoka */
2846a0ade7e9Syasuoka if ((th->th_flags & TH_SYN) == 0)
2847a0ade7e9Syasuoka goto handled;
2848a0ade7e9Syasuoka
2849e8c0e2e5Syasuoka lpktp = MIN(th->th_off << 4, lpktp);
2850e8c0e2e5Syasuoka
2851a0ade7e9Syasuoka pktp += sizeof(struct tcphdr);
2852a0ade7e9Syasuoka lpktp -= sizeof(struct tcphdr);
2853a0ade7e9Syasuoka while (lpktp >= TCPOLEN_MAXSEG) {
2854a0ade7e9Syasuoka GETCHAR(opt, pktp);
2855a0ade7e9Syasuoka switch (opt) {
2856a0ade7e9Syasuoka case TCPOPT_MAXSEG:
2857a0ade7e9Syasuoka GETCHAR(optlen, pktp);
2858a0ade7e9Syasuoka mssp = pktp; /* mss place holder */
2859a0ade7e9Syasuoka GETSHORT(mss, pktp);
2860a0ade7e9Syasuoka maxmss = MAXMSS(mtu);
2861a0ade7e9Syasuoka if (mss > maxmss) {
2862a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG,
2863a0ade7e9Syasuoka "change tcp-mss %d => %d", mss, maxmss));
2864a0ade7e9Syasuoka PUTSHORT(maxmss, mssp);
2865a0ade7e9Syasuoka acc = htons(mss);
2866a0ade7e9Syasuoka acc -= htons(maxmss);
2867a0ade7e9Syasuoka ADJUST_CHECKSUM(acc, th->th_sum);
2868a0ade7e9Syasuoka }
2869a0ade7e9Syasuoka goto handled;
2870a0ade7e9Syasuoka /* NOTREACHED */
2871a0ade7e9Syasuoka case TCPOPT_EOL:
2872a0ade7e9Syasuoka goto handled;
2873a0ade7e9Syasuoka /* NOTREACHED */
2874a0ade7e9Syasuoka case TCPOPT_NOP:
2875a0ade7e9Syasuoka lpktp--;
2876a0ade7e9Syasuoka break;
2877a0ade7e9Syasuoka default:
2878a0ade7e9Syasuoka GETCHAR(optlen, pktp);
2879e8c0e2e5Syasuoka if (optlen < 2) /* packet is broken */
2880e8c0e2e5Syasuoka goto drop;
2881e8c0e2e5Syasuoka pktp += optlen - 2;
2882a0ade7e9Syasuoka lpktp -= optlen;
2883a0ade7e9Syasuoka break;
2884a0ade7e9Syasuoka }
2885a0ade7e9Syasuoka }
2886a0ade7e9Syasuoka
2887a0ade7e9Syasuoka handled:
288863d4736dSyasuoka return (m0);
2889a0ade7e9Syasuoka
2890a0ade7e9Syasuoka drop:
2891a0ade7e9Syasuoka m_freem(m0);
289263d4736dSyasuoka return (NULL);
2893a0ade7e9Syasuoka }
2894a0ade7e9Syasuoka
2895a0ade7e9Syasuoka /*
2896a0ade7e9Syasuoka * Check whether a packet should reset idle timer
2897a0ade7e9Syasuoka * Returns 1 to don't reset timer (i.e. the packet is "idle" packet)
2898a0ade7e9Syasuoka */
2899c02a3381Smvs struct mbuf *
ip_is_idle_packet(struct mbuf * m0,int * ris_idle)2900a0ade7e9Syasuoka ip_is_idle_packet(struct mbuf *m0, int *ris_idle)
2901a0ade7e9Syasuoka {
2902a0ade7e9Syasuoka u_int16_t ip_off;
2903a0ade7e9Syasuoka const struct udphdr *uh;
2904a0ade7e9Syasuoka struct ip *pip;
2905a0ade7e9Syasuoka int len;
2906a0ade7e9Syasuoka
2907a0ade7e9Syasuoka /* pullup ip header */
2908a0ade7e9Syasuoka len = sizeof(struct ip);
2909a0ade7e9Syasuoka PIPEX_PULLUP(m0, len);
2910a0ade7e9Syasuoka if (m0 == NULL)
2911a0ade7e9Syasuoka goto error;
2912a0ade7e9Syasuoka pip = mtod(m0, struct ip *);
2913a0ade7e9Syasuoka
2914a0ade7e9Syasuoka /*
2915a0ade7e9Syasuoka * the packet which fragmentations was not the idle packet.
2916a0ade7e9Syasuoka */
2917a0ade7e9Syasuoka ip_off = ntohs(pip->ip_off);
2918a0ade7e9Syasuoka if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0))
2919a0ade7e9Syasuoka goto is_active;
2920a0ade7e9Syasuoka
2921a0ade7e9Syasuoka switch (pip->ip_p) {
2922a0ade7e9Syasuoka case IPPROTO_IGMP:
2923a0ade7e9Syasuoka goto is_active;
2924a0ade7e9Syasuoka case IPPROTO_ICMP:
2925a0ade7e9Syasuoka len = pip->ip_hl * 4 + 8;
2926a0ade7e9Syasuoka PIPEX_PULLUP(m0, len);
2927a0ade7e9Syasuoka if (m0 == NULL)
2928a0ade7e9Syasuoka goto error;
2929581bf0faSyasuoka pip = mtod(m0, struct ip *);
2930a0ade7e9Syasuoka
2931a0ade7e9Syasuoka switch (((unsigned char *) pip)[pip->ip_hl * 4]) {
2932a0ade7e9Syasuoka case 0: /* Echo Reply */
2933a0ade7e9Syasuoka case 8: /* Echo Request */
2934a0ade7e9Syasuoka goto is_active;
2935a0ade7e9Syasuoka default:
2936a0ade7e9Syasuoka goto is_idle;
2937a0ade7e9Syasuoka }
2938a0ade7e9Syasuoka
2939a0ade7e9Syasuoka case IPPROTO_UDP:
2940a0ade7e9Syasuoka case IPPROTO_TCP:
2941a0ade7e9Syasuoka len = pip->ip_hl * 4 + sizeof(struct udphdr);
2942a0ade7e9Syasuoka PIPEX_PULLUP(m0, len);
2943a0ade7e9Syasuoka if (m0 == NULL)
2944a0ade7e9Syasuoka goto error;
2945581bf0faSyasuoka pip = mtod(m0, struct ip *);
2946581bf0faSyasuoka uh = (struct udphdr *)(mtod(m0, caddr_t) + pip->ip_hl * 4);
2947a0ade7e9Syasuoka
2948a0ade7e9Syasuoka switch (ntohs(uh->uh_sport)) {
2949a0ade7e9Syasuoka case 53: /* DOMAIN */
2950a0ade7e9Syasuoka case 67: /* BOOTPS */
2951a0ade7e9Syasuoka case 68: /* BOOTPC */
2952a0ade7e9Syasuoka case 123: /* NTP */
2953a0ade7e9Syasuoka case 137: /* NETBIOS-NS */
2954a0ade7e9Syasuoka case 520: /* RIP */
2955a0ade7e9Syasuoka goto is_idle;
2956a0ade7e9Syasuoka }
2957a0ade7e9Syasuoka switch (ntohs(uh->uh_dport)) {
2958a0ade7e9Syasuoka case 53: /* DOMAIN */
2959a0ade7e9Syasuoka case 67: /* BOOTPS */
2960a0ade7e9Syasuoka case 68: /* BOOTPC */
2961a0ade7e9Syasuoka case 123: /* NTP */
2962a0ade7e9Syasuoka case 137: /* NETBIOS-NS */
2963a0ade7e9Syasuoka case 520: /* RIP */
2964a0ade7e9Syasuoka goto is_idle;
2965a0ade7e9Syasuoka }
2966a0ade7e9Syasuoka goto is_active;
2967a0ade7e9Syasuoka default:
2968a0ade7e9Syasuoka goto is_active;
2969a0ade7e9Syasuoka }
2970a0ade7e9Syasuoka
2971a0ade7e9Syasuoka is_active:
2972a0ade7e9Syasuoka *ris_idle = 0;
297363d4736dSyasuoka return (m0);
2974a0ade7e9Syasuoka
2975a0ade7e9Syasuoka is_idle:
2976a0ade7e9Syasuoka *ris_idle = 1;
297763d4736dSyasuoka return (m0);
2978a0ade7e9Syasuoka
2979a0ade7e9Syasuoka error:
298063d4736dSyasuoka return (NULL);
2981a0ade7e9Syasuoka }
2982a0ade7e9Syasuoka
2983c02a3381Smvs void
pipex_session_log(struct pipex_session * session,int prio,const char * fmt,...)2984a0ade7e9Syasuoka pipex_session_log(struct pipex_session *session, int prio, const char *fmt, ...)
2985a0ade7e9Syasuoka {
2986a0ade7e9Syasuoka char logbuf[1024];
2987a0ade7e9Syasuoka va_list ap;
2988a0ade7e9Syasuoka
2989a0ade7e9Syasuoka logpri(prio);
2990a0ade7e9Syasuoka if (session != NULL) {
2991002baba0Smvs struct ifnet *ifp;
2992002baba0Smvs
2993002baba0Smvs ifp = if_get(session->ifindex);
2994a0ade7e9Syasuoka addlog("pipex: ppp=%d iface=%s protocol=%s id=%d ",
2995002baba0Smvs session->ppp_id,
2996002baba0Smvs ifp? ifp->if_xname : "Unknown",
2997a0ade7e9Syasuoka (session->protocol == PIPEX_PROTO_PPPOE)? "PPPoE" :
29988876230bShsuenaga (session->protocol == PIPEX_PROTO_PPTP)? "PPTP" :
29998876230bShsuenaga (session->protocol == PIPEX_PROTO_L2TP) ? "L2TP" :
30008876230bShsuenaga "Unknown", session->session_id);
3001002baba0Smvs if_put(ifp);
3002a0ade7e9Syasuoka } else
3003a0ade7e9Syasuoka addlog("pipex: ");
3004a0ade7e9Syasuoka
3005a0ade7e9Syasuoka va_start(ap, fmt);
3006a0ade7e9Syasuoka vsnprintf(logbuf, sizeof(logbuf), fmt, ap);
3007a0ade7e9Syasuoka va_end(ap);
3008a0ade7e9Syasuoka addlog("%s\n", logbuf);
3009a0ade7e9Syasuoka }
30108876230bShsuenaga
3011c02a3381Smvs uint32_t
pipex_sockaddr_hash_key(struct sockaddr * sa)30128876230bShsuenaga pipex_sockaddr_hash_key(struct sockaddr *sa)
30138876230bShsuenaga {
30148876230bShsuenaga switch (sa->sa_family) {
30158876230bShsuenaga case AF_INET:
30163e303383Sbluhm return ntohl(satosin(sa)->sin_addr.s_addr);
30178876230bShsuenaga case AF_INET6:
30183e303383Sbluhm return ntohl(satosin6(sa)->sin6_addr.s6_addr32[3]);
30198876230bShsuenaga }
30208876230bShsuenaga panic("pipex_sockaddr_hash_key: unknown address family");
3021503a8a27Syasuoka return (0);
30228876230bShsuenaga }
30238876230bShsuenaga
30248876230bShsuenaga /*
30258876230bShsuenaga * Compare struct sockaddr_in{,6} with the address only.
30268876230bShsuenaga * The port number is not covered.
30278876230bShsuenaga */
3028c02a3381Smvs int
pipex_sockaddr_compar_addr(struct sockaddr * a,struct sockaddr * b)30298876230bShsuenaga pipex_sockaddr_compar_addr(struct sockaddr *a, struct sockaddr *b)
30308876230bShsuenaga {
30318876230bShsuenaga int cmp;
30328876230bShsuenaga
30338876230bShsuenaga cmp = b->sa_family - a->sa_family;
30348876230bShsuenaga if (cmp != 0)
30358876230bShsuenaga return cmp;
30368876230bShsuenaga switch (a->sa_family) {
30378876230bShsuenaga case AF_INET:
30383e303383Sbluhm return (satosin(b)->sin_addr.s_addr -
30393e303383Sbluhm satosin(a)->sin_addr.s_addr);
30408876230bShsuenaga case AF_INET6:
30413e303383Sbluhm cmp = (satosin6(b)->sin6_scope_id - satosin6(a)->sin6_scope_id);
30428876230bShsuenaga if (cmp != 0)
30438876230bShsuenaga return cmp;
30443e303383Sbluhm return (memcmp(&satosin6(a)->sin6_addr,
30453e303383Sbluhm &satosin6(b)->sin6_addr,
30463e303383Sbluhm sizeof(struct in6_addr)));
30478876230bShsuenaga }
30488876230bShsuenaga panic("pipex_sockaddr_compar_addr: unknown address family");
3049e405d423Syasuoka
3050e405d423Syasuoka return (-1);
3051e405d423Syasuoka }
3052e405d423Syasuoka
305390b03482Syasuoka int
pipex_sysctl(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)305490b03482Syasuoka pipex_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
305590b03482Syasuoka size_t newlen)
305690b03482Syasuoka {
305790b03482Syasuoka switch (name[0]) {
305890b03482Syasuoka case PIPEXCTL_ENABLE:
30599326fce4Syasuoka if (namelen != 1)
30609326fce4Syasuoka return (ENOTDIR);
30619e5eed67Sgnezdo return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
30629e5eed67Sgnezdo &pipex_enable, 0, 1));
306390b03482Syasuoka default:
306490b03482Syasuoka return (ENOPROTOOPT);
306590b03482Syasuoka }
306690b03482Syasuoka /* NOTREACHED */
306790b03482Syasuoka }
3068