xref: /openbsd-src/sys/net/pipex.c (revision f168c03c74a21ba7470da288a336d573b5345f5d)
1*f168c03cSyasuoka /*	$OpenBSD: pipex.c,v 1.157 2025/01/25 02:06:40 yasuoka 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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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 *
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 *
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 *
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
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
720a0ade7e9Syasuoka pipex_timer_stop(void)
721a0ade7e9Syasuoka {
722a0ade7e9Syasuoka 	timeout_del(&pipex_timer_ch);
723a0ade7e9Syasuoka }
724a0ade7e9Syasuoka 
725c02a3381Smvs void
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 *
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
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
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
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
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
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 *
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
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 *
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 *
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
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
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 *
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 	if (session == NULL) {
1511a0ade7e9Syasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
1512a0ade7e9Syasuoka 		    "<%s> session not found (id=%d)", __func__, id));
1513a0ade7e9Syasuoka 		goto not_ours;
1514a0ade7e9Syasuoka 	}
1515*f168c03cSyasuoka 
1516*f168c03cSyasuoka 	if (!(session->peer.sa.sa_family == AF_INET &&
1517*f168c03cSyasuoka 	    session->peer.sin4.sin_addr.s_addr == ip.ip_src.s_addr)) {
1518*f168c03cSyasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
1519*f168c03cSyasuoka 		    "<%s> the source address of the session is not matched",
1520*f168c03cSyasuoka 		    __func__));
1521*f168c03cSyasuoka 		goto not_ours;
1522*f168c03cSyasuoka 	}
1523a0ade7e9Syasuoka 
152463d4736dSyasuoka 	return (session);
1525a0ade7e9Syasuoka 
1526a0ade7e9Syasuoka not_ours:
152763d4736dSyasuoka 	return (NULL);
1528a0ade7e9Syasuoka }
1529a0ade7e9Syasuoka 
1530a0ade7e9Syasuoka struct mbuf *
1531a0ade7e9Syasuoka pipex_pptp_input(struct mbuf *m0, struct pipex_session *session)
1532a0ade7e9Syasuoka {
1533d5af01deSyasuoka 	int hlen, has_seq, has_ack, nseq;
1534a0ade7e9Syasuoka 	const char *reason = "";
1535d5af01deSyasuoka 	u_char *cp, *seqp = NULL, *ackp = NULL;
1536a0ade7e9Syasuoka 	uint32_t flags, seq = 0, ack = 0;
1537a0ade7e9Syasuoka 	struct ip *ip;
1538a0ade7e9Syasuoka 	struct pipex_gre_header *gre;
1539a0ade7e9Syasuoka 	struct pipex_pptp_session *pptp_session;
1540e405d423Syasuoka 	int rewind = 0;
1541a0ade7e9Syasuoka 
1542a0ade7e9Syasuoka 	KASSERT(m0->m_pkthdr.len >= PIPEX_IPGRE_HDRLEN);
1543a0ade7e9Syasuoka 	pptp_session = &session->proto.pptp;
1544a0ade7e9Syasuoka 
1545a0ade7e9Syasuoka 	/* get ip header */
1546a0ade7e9Syasuoka 	ip = mtod(m0, struct ip *);
1547a0ade7e9Syasuoka 	hlen = ip->ip_hl << 2;
1548a0ade7e9Syasuoka 
1549a0ade7e9Syasuoka 	/* seek gre header */
1550a0ade7e9Syasuoka 	gre = PIPEX_SEEK_NEXTHDR(ip, hlen, struct pipex_gre_header *);
1551a0ade7e9Syasuoka 	flags = ntohs(gre->flags);
1552a0ade7e9Syasuoka 
1553a0ade7e9Syasuoka 	/* pullup for seek sequences in header */
1554a0ade7e9Syasuoka 	has_seq = (flags & PIPEX_GRE_SFLAG) ? 1 : 0;
1555a0ade7e9Syasuoka 	has_ack = (flags & PIPEX_GRE_AFLAG) ? 1 : 0;
1556a0ade7e9Syasuoka 	hlen = PIPEX_IPGRE_HDRLEN + 4 * (has_seq + has_ack);
1557a0ade7e9Syasuoka 	if (m0->m_len < hlen) {
1558a0ade7e9Syasuoka 		m0 = m_pullup(m0, hlen);
1559a0ade7e9Syasuoka 		if (m0 == NULL) {
1560a0ade7e9Syasuoka 			PIPEX_DBG((session, LOG_DEBUG, "pullup failed."));
1561a0ade7e9Syasuoka 			goto drop;
1562a0ade7e9Syasuoka 		}
1563a0ade7e9Syasuoka 	}
1564a0ade7e9Syasuoka 
1565a0ade7e9Syasuoka 	/* check sequence */
1566a0ade7e9Syasuoka 	cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *);
1567a0ade7e9Syasuoka 	if (has_seq) {
1568a0ade7e9Syasuoka 		seqp = cp;
1569a0ade7e9Syasuoka 		GETLONG(seq, cp);
1570a0ade7e9Syasuoka 	}
1571707c5f41Smvs 
1572707c5f41Smvs 	mtx_enter(&session->pxs_mtx);
1573707c5f41Smvs 
1574a0ade7e9Syasuoka 	if (has_ack) {
1575a0ade7e9Syasuoka 		ackp = cp;
1576a0ade7e9Syasuoka 		GETLONG(ack, cp);
1577a0ade7e9Syasuoka 		if (ack + 1 == pptp_session->snd_una) {
1578a0ade7e9Syasuoka 			/* ack has not changed before */
1579a0ade7e9Syasuoka 		} else if (SEQ32_LT(ack, pptp_session->snd_una)) {
1580e405d423Syasuoka 			/* OoO ack packets should not be dropped. */
1581e405d423Syasuoka 			rewind = 1;
1582a0ade7e9Syasuoka 		} else if (SEQ32_GT(ack, pptp_session->snd_nxt)) {
1583a0ade7e9Syasuoka 			reason = "ack for unknown sequence";
1584e405d423Syasuoka 			goto out_seq;
15856117a981Syasuoka 		} else
15866117a981Syasuoka 			pptp_session->snd_una = ack + 1;
1587a0ade7e9Syasuoka 	}
1588a0ade7e9Syasuoka 	if (!has_seq) {
1589a0ade7e9Syasuoka 		/* ack only packet */
1590a0ade7e9Syasuoka 		goto not_ours;
1591a0ade7e9Syasuoka 	}
1592a0ade7e9Syasuoka 	if (SEQ32_LT(seq, pptp_session->rcv_nxt)) {
1593e405d423Syasuoka 		rewind = 1;
1594e405d423Syasuoka 		if (SEQ32_LT(seq,
1595e405d423Syasuoka 		    pptp_session->rcv_nxt - PIPEX_REWIND_LIMIT)) {
1596a0ade7e9Syasuoka 			reason = "out of sequence";
1597e405d423Syasuoka 			goto out_seq;
1598e405d423Syasuoka 		}
1599a0ade7e9Syasuoka 	} else if (SEQ32_GE(seq, pptp_session->rcv_nxt +
1600a0ade7e9Syasuoka 	    pptp_session->maxwinsz)) {
1601a0ade7e9Syasuoka 		pipex_session_log(session, LOG_DEBUG,
1602a0ade7e9Syasuoka 		    "received packet caused window overflow. seq=%u(%u-%u)"
1603a0ade7e9Syasuoka 		    "may lost %d packets.", seq, pptp_session->rcv_nxt,
1604a0ade7e9Syasuoka 		    pptp_session->rcv_nxt + pptp_session->maxwinsz,
1605a0ade7e9Syasuoka 		    (int)SEQ32_SUB(seq, pptp_session->rcv_nxt));
1606a0ade7e9Syasuoka 	}
1607a0ade7e9Syasuoka 
1608a0ade7e9Syasuoka 	seq++;
1609a0ade7e9Syasuoka 	nseq = SEQ32_SUB(seq, pptp_session->rcv_nxt);
1610e405d423Syasuoka 	if (!rewind) {
1611a0ade7e9Syasuoka 		pptp_session->rcv_nxt = seq;
1612a0ade7e9Syasuoka 		if (SEQ32_SUB(seq, pptp_session->rcv_acked) >
161363d4736dSyasuoka 		    roundup(pptp_session->winsz, 2) / 2) /* Send ack only packet. */
1614a0ade7e9Syasuoka 			pipex_pptp_output(NULL, session, 0, 1);
1615e405d423Syasuoka 	}
1616a0ade7e9Syasuoka 
1617707c5f41Smvs 	/*
1618707c5f41Smvs 	 * The following pipex_common_input() will release `pxs_mtx'
1619707c5f41Smvs 	 * deep within if the packet will be consumed. In the error
1620707c5f41Smvs 	 * path lock will be held all the time. So increment `rcv_gap'
1621707c5f41Smvs 	 * here, and on the error path back it out, no atomicity will
1622707c5f41Smvs 	 * be lost in all cases.
1623707c5f41Smvs 	 */
1624e405d423Syasuoka 	if (!rewind)
1625a0ade7e9Syasuoka 		session->proto.pptp.rcv_gap += nseq;
1626707c5f41Smvs 	m0 = pipex_common_input(session, m0, hlen, ntohs(gre->len), 1);
1627707c5f41Smvs 	if (m0 == NULL) {
1628707c5f41Smvs 		/*
1629707c5f41Smvs 		 * pipex_common_input() releases lock if the
1630707c5f41Smvs 		 * packet was consumed.
1631707c5f41Smvs 		 */
1632e405d423Syasuoka 		return (NULL);
1633a0ade7e9Syasuoka 	}
1634e405d423Syasuoka 
1635e405d423Syasuoka 	if (rewind)
1636e405d423Syasuoka 		goto out_seq;
1637707c5f41Smvs 	else {
1638707c5f41Smvs 		/* The packet is not ours, back out `rcv_gap'. */
1639707c5f41Smvs 		session->proto.pptp.rcv_gap -= nseq;
1640707c5f41Smvs 	}
1641e405d423Syasuoka 
1642a0ade7e9Syasuoka not_ours:
16436117a981Syasuoka 	seq--;	/* revert original seq value */
164463d4736dSyasuoka 
1645a0ade7e9Syasuoka 	/*
1646a0ade7e9Syasuoka 	 * overwrite sequence numbers to adjust a gap between pipex and
1647a0ade7e9Syasuoka 	 * userland.
1648a0ade7e9Syasuoka 	 */
1649a0ade7e9Syasuoka 	if (seqp != NULL) {
1650a0ade7e9Syasuoka 		seq -= pptp_session->rcv_gap;
1651a0ade7e9Syasuoka 		PUTLONG(seq, seqp);
1652a0ade7e9Syasuoka 	}
1653a0ade7e9Syasuoka 	if (ackp != NULL) {
1654a0ade7e9Syasuoka 		if (pptp_session->snd_nxt == pptp_session->snd_una) {
1655a0ade7e9Syasuoka 			ack -= session->proto.pptp.snd_gap;
1656a0ade7e9Syasuoka 			pptp_session->ul_snd_una = ack;
1657a0ade7e9Syasuoka 		} else {
1658a0ade7e9Syasuoka 			/*
1659a0ade7e9Syasuoka 			 * There are sending packets they are not acked.
1660a0ade7e9Syasuoka 			 * In this situation, (ack - snd_gap) may points
1661a0ade7e9Syasuoka 			 * before sending window of userland.  So we don't
1662a0ade7e9Syasuoka 			 * update the ack number.
1663a0ade7e9Syasuoka 			 */
1664a0ade7e9Syasuoka 			ack = pptp_session->ul_snd_una;
1665a0ade7e9Syasuoka 		}
1666a0ade7e9Syasuoka 		PUTLONG(ack, ackp);
1667a0ade7e9Syasuoka 	}
1668a0ade7e9Syasuoka 
1669707c5f41Smvs 	mtx_leave(&session->pxs_mtx);
1670707c5f41Smvs 
167163d4736dSyasuoka 	return (m0);
1672e405d423Syasuoka out_seq:
1673a0ade7e9Syasuoka 	pipex_session_log(session, LOG_DEBUG,
1674a0ade7e9Syasuoka 	    "Received bad data packet: %s: seq=%u(%u-%u) ack=%u(%u-%u)",
1675a0ade7e9Syasuoka 	    reason, seq, pptp_session->rcv_nxt,
1676a0ade7e9Syasuoka 	    pptp_session->rcv_nxt + pptp_session->maxwinsz,
1677a0ade7e9Syasuoka 	    ack, pptp_session->snd_una,
1678a0ade7e9Syasuoka 	    pptp_session->snd_nxt);
1679707c5f41Smvs 	mtx_leave(&session->pxs_mtx);
1680a0ade7e9Syasuoka 
1681a0ade7e9Syasuoka 	/* FALLTHROUGH */
1682a0ade7e9Syasuoka drop:
1683a0ade7e9Syasuoka 	m_freem(m0);
1684df21f681Smvs 	counters_inc(session->stat_counters, pxc_ierrors);
1685a0ade7e9Syasuoka 
168663d4736dSyasuoka 	return (NULL);
1687a0ade7e9Syasuoka }
1688a0ade7e9Syasuoka 
1689a0ade7e9Syasuoka struct pipex_session *
16908876230bShsuenaga pipex_pptp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst)
16918876230bShsuenaga {
16929c794535Sbluhm 	struct sockaddr_in sin;
16938876230bShsuenaga 
16949c794535Sbluhm 	memset(&sin, 0, sizeof(sin));
16959c794535Sbluhm 	sin.sin_len = sizeof(sin);
16969c794535Sbluhm 	sin.sin_family = AF_INET;
16979c794535Sbluhm 	sin.sin_addr = dst;
16988876230bShsuenaga 
16999c794535Sbluhm 	return pipex_pptp_userland_lookup_session(m0, sintosa(&sin));
17008876230bShsuenaga }
17018876230bShsuenaga 
17028876230bShsuenaga #ifdef INET6
17038876230bShsuenaga struct pipex_session *
17048876230bShsuenaga pipex_pptp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst)
17058876230bShsuenaga {
17068876230bShsuenaga 	struct sockaddr_in6 sin6;
17078876230bShsuenaga 
17089c794535Sbluhm 	memset(&sin6, 0, sizeof(sin6));
17099c794535Sbluhm 	sin6.sin6_len = sizeof(sin6);
17108876230bShsuenaga 	sin6.sin6_family = AF_INET6;
17116c8dd0e9Sclaudio 	in6_recoverscope(&sin6, &dst);
17128876230bShsuenaga 
17139c794535Sbluhm 	return pipex_pptp_userland_lookup_session(m0, sin6tosa(&sin6));
17148876230bShsuenaga }
17158876230bShsuenaga #endif
17168876230bShsuenaga 
1717c02a3381Smvs struct pipex_session *
17188876230bShsuenaga pipex_pptp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa)
1719a0ade7e9Syasuoka {
1720a0ade7e9Syasuoka 	struct pipex_gre_header gre;
1721a0ade7e9Syasuoka 	struct pipex_hash_head *list;
1722a0ade7e9Syasuoka 	struct pipex_session *session;
1723a0ade7e9Syasuoka 	uint16_t id, flags;
1724a0ade7e9Syasuoka 
1725a0ade7e9Syasuoka 	/* pullup */
1726a0ade7e9Syasuoka 	if (m0->m_pkthdr.len < sizeof(gre)) {
1727a0ade7e9Syasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
1728a0ade7e9Syasuoka 		    "<%s> packet length is too short", __func__));
172963d4736dSyasuoka 		return (NULL);
1730a0ade7e9Syasuoka 	}
1731a0ade7e9Syasuoka 
1732a0ade7e9Syasuoka 	/* get flags */
17335c7fed39Sdlg 	m_copydata(m0, 0, sizeof(struct pipex_gre_header), &gre);
1734a0ade7e9Syasuoka 	flags = ntohs(gre.flags);
1735a0ade7e9Syasuoka 
1736a0ade7e9Syasuoka 	/* gre version must be '1' */
1737a0ade7e9Syasuoka 	if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) {
1738a0ade7e9Syasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
1739a0ade7e9Syasuoka 		    "<%s> gre header wrong version.", __func__));
174063d4736dSyasuoka 		return (NULL);
1741a0ade7e9Syasuoka 	}
1742a0ade7e9Syasuoka 
1743a0ade7e9Syasuoka 	/* gre keys must be present */
1744a0ade7e9Syasuoka 	if ((flags & PIPEX_GRE_KFLAG) == 0) {
1745a0ade7e9Syasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
1746a0ade7e9Syasuoka 		    "<%s> gre header has no keys.", __func__));
174763d4736dSyasuoka 		return (NULL);
1748a0ade7e9Syasuoka 	}
1749a0ade7e9Syasuoka 
1750a0ade7e9Syasuoka 	/* lookup pipex session table */
1751a0ade7e9Syasuoka 	id = ntohs(gre.call_id);
1752a0ade7e9Syasuoka 
1753f6e6f8e7Smvs 	mtx_enter(&pipex_list_mtx);
1754f6e6f8e7Smvs 
17558876230bShsuenaga 	list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa));
1756a0ade7e9Syasuoka 	LIST_FOREACH(session, list, peer_addr_chain) {
17579c681c75Sbluhm 		if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0)
1758a0ade7e9Syasuoka 			continue;
1759a0ade7e9Syasuoka 		if (session->peer_session_id == id)
1760a0ade7e9Syasuoka 			break;
1761a0ade7e9Syasuoka 	}
1762f6e6f8e7Smvs 
1763f6e6f8e7Smvs 	if (session != NULL)
1764f6e6f8e7Smvs 		refcnt_take(&session->pxs_refcnt);
1765f6e6f8e7Smvs 
1766f6e6f8e7Smvs 	mtx_leave(&pipex_list_mtx);
1767f6e6f8e7Smvs 
1768a0ade7e9Syasuoka #ifdef PIPEX_DEBUG
1769a0ade7e9Syasuoka 	if (session == NULL) {
1770a0ade7e9Syasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
17718876230bShsuenaga 		    "<%s> session not found (,call_id=%d)",
17728876230bShsuenaga 		    __func__, (int)gre.call_id));
1773a0ade7e9Syasuoka 	}
1774a0ade7e9Syasuoka #endif
177563d4736dSyasuoka 	return (session);
1776a0ade7e9Syasuoka }
1777a0ade7e9Syasuoka 
1778a0ade7e9Syasuoka /*
1779a0ade7e9Syasuoka  * pipex_pptp_userland_output
1780a0ade7e9Syasuoka  */
1781a0ade7e9Syasuoka struct mbuf *
1782a0ade7e9Syasuoka pipex_pptp_userland_output(struct mbuf *m0, struct pipex_session *session)
1783a0ade7e9Syasuoka {
17849f9300a4Syasuoka 	int len;
17859f9300a4Syasuoka 	struct pipex_gre_header *gre, gre0;
1786a0ade7e9Syasuoka 	uint16_t flags;
1787a0ade7e9Syasuoka 	u_char *cp, *cp0;
1788a0ade7e9Syasuoka 	uint32_t val32;
1789a0ade7e9Syasuoka 
17909f9300a4Syasuoka 	len = sizeof(struct pipex_gre_header);
17915c7fed39Sdlg 	m_copydata(m0, 0, len, &gre0);
17929f9300a4Syasuoka 	gre = &gre0;
17939f9300a4Syasuoka 	flags = ntohs(gre->flags);
17949f9300a4Syasuoka 	if ((flags & PIPEX_GRE_SFLAG) != 0)
17959f9300a4Syasuoka 		len += 4;
17969f9300a4Syasuoka 	if ((flags & PIPEX_GRE_AFLAG) != 0)
17979f9300a4Syasuoka 		len += 4;
17989f9300a4Syasuoka 
1799a0ade7e9Syasuoka 	/* check length */
18009f9300a4Syasuoka 	PIPEX_PULLUP(m0, len);
1801a0ade7e9Syasuoka 	if (m0 == NULL) {
18029f9300a4Syasuoka 		PIPEX_DBG((session, LOG_DEBUG, "gre header is too short."));
180363d4736dSyasuoka 		return (NULL);
1804a0ade7e9Syasuoka 	}
1805a0ade7e9Syasuoka 
1806a0ade7e9Syasuoka 	gre = mtod(m0, struct pipex_gre_header *);
1807a0ade7e9Syasuoka 	cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header), u_char *);
1808a0ade7e9Syasuoka 
1809707c5f41Smvs 	mtx_enter(&session->pxs_mtx);
1810707c5f41Smvs 
1811a0ade7e9Syasuoka 	/*
1812a0ade7e9Syasuoka 	 * overwrite sequence numbers to adjust a gap between pipex and
1813a0ade7e9Syasuoka 	 * userland.
1814a0ade7e9Syasuoka 	 */
1815a0ade7e9Syasuoka 	if ((flags & PIPEX_GRE_SFLAG) != 0) {
1816a0ade7e9Syasuoka 		cp0 = cp;
1817a0ade7e9Syasuoka 		GETLONG(val32, cp);
1818a0ade7e9Syasuoka 		val32 += session->proto.pptp.snd_gap;
1819a0ade7e9Syasuoka 		PUTLONG(val32, cp0);
1820a0ade7e9Syasuoka 		session->proto.pptp.snd_nxt++;
1821a0ade7e9Syasuoka 	}
1822a0ade7e9Syasuoka 	if ((flags & PIPEX_GRE_AFLAG) != 0) {
1823a0ade7e9Syasuoka 		cp0 = cp;
1824a0ade7e9Syasuoka 		GETLONG(val32, cp);
1825a0ade7e9Syasuoka 		val32 += session->proto.pptp.rcv_gap;
1826a0ade7e9Syasuoka 		PUTLONG(val32, cp0);
1827a0ade7e9Syasuoka 		if (SEQ32_GT(val32, session->proto.pptp.rcv_acked))
1828a0ade7e9Syasuoka 			session->proto.pptp.rcv_acked = val32;
1829a0ade7e9Syasuoka 	}
1830a0ade7e9Syasuoka 
1831707c5f41Smvs 	mtx_leave(&session->pxs_mtx);
1832707c5f41Smvs 
183363d4736dSyasuoka 	return (m0);
1834a0ade7e9Syasuoka }
1835a0ade7e9Syasuoka #endif /* PIPEX_PPTP */
1836a0ade7e9Syasuoka 
18378876230bShsuenaga #ifdef PIPEX_L2TP
18388876230bShsuenaga /***********************************************************************
18398876230bShsuenaga  * L2TP support
18408876230bShsuenaga  ***********************************************************************/
1841c02a3381Smvs void
18428876230bShsuenaga pipex_l2tp_output(struct mbuf *m0, struct pipex_session *session)
18438876230bShsuenaga {
18448876230bShsuenaga 	int hlen, plen, datalen;
18458876230bShsuenaga 	struct pipex_l2tp_header *l2tp = NULL;
18468876230bShsuenaga 	struct pipex_l2tp_seq_header *seq = NULL;
18478876230bShsuenaga 	struct udphdr *udp;
18488876230bShsuenaga 	struct ip *ip;
18498876230bShsuenaga #ifdef INET6
18508876230bShsuenaga 	struct ip6_hdr *ip6;
18518876230bShsuenaga #endif
1852a1c674a4Syasuoka 	struct m_tag *mtag;
18538876230bShsuenaga 
18548876230bShsuenaga 	hlen = sizeof(struct pipex_l2tp_header) +
18558876230bShsuenaga 	    ((pipex_session_is_l2tp_data_sequencing_on(session))
18568876230bShsuenaga 		    ? sizeof(struct pipex_l2tp_seq_header) : 0) +
18578876230bShsuenaga 	    sizeof(struct udphdr) +
18589e07f154Syasuoka #ifdef INET6
18598876230bShsuenaga 	    ((session->peer.sin6.sin6_family == AF_INET6)
18608876230bShsuenaga 		    ? sizeof(struct ip6_hdr) : sizeof(struct ip));
18619e07f154Syasuoka #else
18629e07f154Syasuoka 	    sizeof(struct ip);
18639e07f154Syasuoka #endif
18648876230bShsuenaga 
18658876230bShsuenaga 	datalen = 0;
18668876230bShsuenaga 	if (m0 != NULL) {
18678876230bShsuenaga 		datalen = m0->m_pkthdr.len;
18688876230bShsuenaga 		M_PREPEND(m0, hlen, M_NOWAIT);
18698876230bShsuenaga 		if (m0 == NULL)
18708876230bShsuenaga 			goto drop;
18718876230bShsuenaga 	} else {
18728876230bShsuenaga 		MGETHDR(m0, M_DONTWAIT, MT_DATA);
18738876230bShsuenaga 		if (m0 == NULL)
18748876230bShsuenaga 			goto drop;
18758876230bShsuenaga 		KASSERT(hlen <= MHLEN);
18768876230bShsuenaga 		m0->m_pkthdr.len = m0->m_len = hlen;
18778876230bShsuenaga 	}
18788876230bShsuenaga 
18799e07f154Syasuoka #ifdef INET6
18808876230bShsuenaga 	hlen = (session->peer.sin6.sin6_family == AF_INET6)
18818876230bShsuenaga 	    ? sizeof(struct ip6_hdr) : sizeof(struct ip);
18829e07f154Syasuoka #else
18839e07f154Syasuoka 	hlen = sizeof(struct ip);
18849e07f154Syasuoka #endif
18858876230bShsuenaga 	plen = datalen + sizeof(struct pipex_l2tp_header) +
18868876230bShsuenaga 	    ((pipex_session_is_l2tp_data_sequencing_on(session))
18878876230bShsuenaga 		    ? sizeof(struct pipex_l2tp_seq_header) : 0);
18888876230bShsuenaga 
18898876230bShsuenaga 	l2tp = (struct pipex_l2tp_header *)
18908876230bShsuenaga 	    (mtod(m0, caddr_t) + hlen + sizeof(struct udphdr));
18918876230bShsuenaga 	l2tp->flagsver = PIPEX_L2TP_VER | PIPEX_L2TP_FLAG_LENGTH;
18928876230bShsuenaga 	l2tp->length = htons(plen);
18938876230bShsuenaga 	l2tp->tunnel_id = htons(session->proto.l2tp.peer_tunnel_id);
18948876230bShsuenaga 	l2tp->session_id = htons(session->peer_session_id);
18958876230bShsuenaga 	if (pipex_session_is_l2tp_data_sequencing_on(session)) {
18968876230bShsuenaga 		seq = (struct pipex_l2tp_seq_header *)(l2tp + 1);
18978876230bShsuenaga 		l2tp->flagsver |= PIPEX_L2TP_FLAG_SEQUENCE;
1898707c5f41Smvs 
1899707c5f41Smvs 		mtx_enter(&session->pxs_mtx);
19008876230bShsuenaga 		seq->ns = htons(session->proto.l2tp.ns_nxt);
19018876230bShsuenaga 		session->proto.l2tp.ns_nxt++;
19028876230bShsuenaga 		session->proto.l2tp.ns_gap++;
19038876230bShsuenaga 		session->proto.l2tp.nr_acked = session->proto.l2tp.nr_nxt - 1;
19048876230bShsuenaga 		seq->nr = htons(session->proto.l2tp.nr_acked);
1905707c5f41Smvs 		mtx_leave(&session->pxs_mtx);
19068876230bShsuenaga 	}
1907faf7e06fSmpi 	l2tp->flagsver = htons(l2tp->flagsver);
19088876230bShsuenaga 
19098876230bShsuenaga 	plen += sizeof(struct udphdr);
19108876230bShsuenaga 	udp = (struct udphdr *)(mtod(m0, caddr_t) + hlen);
19118876230bShsuenaga 	udp->uh_sport = session->local.sin6.sin6_port;
19128876230bShsuenaga 	udp->uh_dport = session->peer.sin6.sin6_port;
19138876230bShsuenaga 	udp->uh_ulen = htons(plen);
191411d6b1faShenning 	udp->uh_sum = 0;
19158876230bShsuenaga 
191611d6b1faShenning 	m0->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
1917002baba0Smvs 	m0->m_pkthdr.ph_ifidx = session->ifindex;
191885d4897fSmarkus #if NPF > 0
191985d4897fSmarkus 	pf_pkt_addr_changed(m0);
192085d4897fSmarkus #endif
19218876230bShsuenaga 	switch (session->peer.sin6.sin6_family) {
19228876230bShsuenaga 	case AF_INET:
19238876230bShsuenaga 		ip = mtod(m0, struct ip *);
19248876230bShsuenaga 		ip->ip_p = IPPROTO_UDP;
19258876230bShsuenaga 		ip->ip_src = session->local.sin4.sin_addr;
19268876230bShsuenaga 		ip->ip_dst = session->peer.sin4.sin_addr;
19278876230bShsuenaga 		ip->ip_len = htons(hlen + plen);
19288876230bShsuenaga 		ip->ip_ttl = MAXTTL;
19298876230bShsuenaga 		ip->ip_tos = 0;
19305cd62ff4Syasuoka 		ip->ip_off = 0;
19318876230bShsuenaga 
1932707c5f41Smvs 		mtx_enter(&session->pxs_mtx);
1933a1c674a4Syasuoka 		if (session->proto.l2tp.ipsecflowinfo > 0) {
1934a1c674a4Syasuoka 			if ((mtag = m_tag_get(PACKET_TAG_IPSEC_FLOWINFO,
1935707c5f41Smvs 			    sizeof(u_int32_t), M_NOWAIT)) == NULL) {
1936707c5f41Smvs 				mtx_leave(&session->pxs_mtx);
1937a1c674a4Syasuoka 				goto drop;
1938707c5f41Smvs 			}
1939707c5f41Smvs 
1940a1c674a4Syasuoka 			*(u_int32_t *)(mtag + 1) =
1941a1c674a4Syasuoka 			    session->proto.l2tp.ipsecflowinfo;
1942a1c674a4Syasuoka 			m_tag_prepend(m0, mtag);
1943a1c674a4Syasuoka 		}
1944707c5f41Smvs 		mtx_leave(&session->pxs_mtx);
1945a1c674a4Syasuoka 
19460315bf92Sdlg 		ip_send(m0);
19478876230bShsuenaga 		break;
19488876230bShsuenaga #ifdef INET6
19498876230bShsuenaga 	case AF_INET6:
19508876230bShsuenaga 		ip6 = mtod(m0, struct ip6_hdr *);
19518876230bShsuenaga 
19528876230bShsuenaga 		ip6->ip6_flow = 0;
19538876230bShsuenaga 		ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
19548876230bShsuenaga 		ip6->ip6_vfc |= IPV6_VERSION;
19558876230bShsuenaga 		ip6->ip6_nxt = IPPROTO_UDP;
19568876230bShsuenaga 		ip6->ip6_src = session->local.sin6.sin6_addr;
1957952c6363Sbluhm 		in6_embedscope(&ip6->ip6_dst, &session->peer.sin6, NULL, NULL);
19588876230bShsuenaga 		/* ip6->ip6_plen will be filled in ip6_output. */
19598876230bShsuenaga 
19600315bf92Sdlg 		ip6_send(m0);
19618876230bShsuenaga 		break;
19628876230bShsuenaga #endif
19638876230bShsuenaga 	}
1964967e9010Sdlg 	udpstat_inc(udps_opackets);
19658876230bShsuenaga 
19668876230bShsuenaga 	if (datalen > 0) {	/* network layer only */
19678876230bShsuenaga 		/* countup statistics */
1968df21f681Smvs 		counters_pkt(session->stat_counters, pxc_opackets,
1969df21f681Smvs 		    pxc_obytes, datalen);
19708876230bShsuenaga 	}
19718876230bShsuenaga 
19728876230bShsuenaga 	return;
19738876230bShsuenaga drop:
1974a1c674a4Syasuoka 	m_freem(m0);
1975df21f681Smvs 	counters_inc(session->stat_counters, pxc_oerrors);
19768876230bShsuenaga }
19778876230bShsuenaga 
19788876230bShsuenaga struct pipex_session *
1979*f168c03cSyasuoka pipex_l2tp_lookup_session(struct mbuf *m0, int off, struct sockaddr *sasrc)
19808876230bShsuenaga {
19818876230bShsuenaga 	struct pipex_session *session;
19828876230bShsuenaga 	uint16_t flags, session_id, ver;
19838876230bShsuenaga 	u_char *cp, buf[PIPEX_L2TP_MINLEN];
1984*f168c03cSyasuoka 	int srcmatch = 0;
19858876230bShsuenaga 
19868876230bShsuenaga 	if (m0->m_pkthdr.len < off + PIPEX_L2TP_MINLEN) {
19878876230bShsuenaga 		PIPEX_DBG((NULL, LOG_DEBUG,
19888876230bShsuenaga 		    "<%s> packet length is too short", __func__));
19898876230bShsuenaga 		goto not_ours;
19908876230bShsuenaga 	}
19918876230bShsuenaga 
19928876230bShsuenaga 	/* get first 16bits of L2TP */
19938876230bShsuenaga 	m_copydata(m0, off, sizeof(buf), buf);
19948876230bShsuenaga 	cp = buf;
19958876230bShsuenaga 	GETSHORT(flags, cp);
19968876230bShsuenaga 	ver = flags & PIPEX_L2TP_VER_MASK;
19978876230bShsuenaga 
19988876230bShsuenaga 	/* l2tp version must be '2' */
19998876230bShsuenaga 	if (ver != PIPEX_L2TP_VER) {
20008876230bShsuenaga 		PIPEX_DBG((NULL, LOG_DEBUG,
20018876230bShsuenaga 		    "<%s> l2tp header wrong version %u.", __func__, ver));
20028876230bShsuenaga 		goto not_ours;
20038876230bShsuenaga 	}
20048876230bShsuenaga 	if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0)
20058876230bShsuenaga 		goto not_ours;
20068876230bShsuenaga 
20078876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_LENGTH)
20088876230bShsuenaga 		cp += 2;			/* skip length field */
20098876230bShsuenaga 	cp += 2;				/* skip tunnel-id field */
20108876230bShsuenaga 	GETSHORT(session_id, cp);		/* get session-id field */
20118876230bShsuenaga 
20128876230bShsuenaga 	/* lookup pipex session table */
20138876230bShsuenaga 	session = pipex_lookup_by_session_id(PIPEX_PROTO_L2TP, session_id);
20148876230bShsuenaga 	if (session == NULL) {
20158876230bShsuenaga 		PIPEX_DBG((NULL, LOG_DEBUG,
20168876230bShsuenaga 		    "<%s> session not found (id=%d)", __func__, session_id));
20178876230bShsuenaga 		goto not_ours;
20188876230bShsuenaga 	}
2019*f168c03cSyasuoka 	switch (sasrc->sa_family) {
2020*f168c03cSyasuoka 	case AF_INET:
2021*f168c03cSyasuoka 		if (session->peer.sa.sa_family == AF_INET &&
2022*f168c03cSyasuoka 		    session->peer.sin4.sin_addr.s_addr ==
2023*f168c03cSyasuoka 		    ((struct sockaddr_in *)sasrc)->sin_addr.s_addr)
2024*f168c03cSyasuoka 			srcmatch = 1;
2025*f168c03cSyasuoka 		break;
2026*f168c03cSyasuoka #ifdef INET6
2027*f168c03cSyasuoka 	case AF_INET6:
2028*f168c03cSyasuoka 		if (session->peer.sa.sa_family == AF_INET6 &&
2029*f168c03cSyasuoka 		    IN6_ARE_ADDR_EQUAL(&session->peer.sin6.sin6_addr,
2030*f168c03cSyasuoka 		    &((struct sockaddr_in6 *)sasrc)->sin6_addr))
2031*f168c03cSyasuoka 			srcmatch = 1;
2032*f168c03cSyasuoka 		break;
20338876230bShsuenaga #endif
2034*f168c03cSyasuoka 	}
2035*f168c03cSyasuoka 	if (!srcmatch) {
2036*f168c03cSyasuoka 		PIPEX_DBG((NULL, LOG_DEBUG,
2037*f168c03cSyasuoka 		    "<%s> the source address of the session is not matched",
2038*f168c03cSyasuoka 		    __func__));
2039*f168c03cSyasuoka 		goto not_ours;
2040*f168c03cSyasuoka 	}
20418876230bShsuenaga 
20428876230bShsuenaga 	return (session);
20438876230bShsuenaga 
20448876230bShsuenaga not_ours:
20458876230bShsuenaga 	return (NULL);
20468876230bShsuenaga }
20478876230bShsuenaga 
20488876230bShsuenaga struct mbuf *
2049a5018c90Syasuoka pipex_l2tp_input(struct mbuf *m0, int off0, struct pipex_session *session,
2050a5018c90Syasuoka     uint32_t ipsecflowinfo)
20518876230bShsuenaga {
20528876230bShsuenaga 	struct pipex_l2tp_session *l2tp_session;
2053707c5f41Smvs 	int length = 0, offset = 0, hlen, nseq;
2054707c5f41Smvs 	u_char *cp, *nsp = NULL, *nrp = NULL;
20558876230bShsuenaga 	uint16_t flags, ns = 0, nr = 0;
2056e405d423Syasuoka 	int rewind = 0;
20578876230bShsuenaga 
2058707c5f41Smvs 	mtx_enter(&session->pxs_mtx);
2059f6e6f8e7Smvs 
20608876230bShsuenaga 	l2tp_session = &session->proto.l2tp;
2061b985d824Syasuoka 	if (l2tp_session->ipsecflowinfo > 0 &&
2062b985d824Syasuoka 	    l2tp_session->ipsecflowinfo != ipsecflowinfo) {
2063207bd73aSyasuoka 		pipex_session_log(session, LOG_DEBUG,
2064207bd73aSyasuoka 		    "received message is %s",
2065207bd73aSyasuoka 		    (ipsecflowinfo != 0)? "from invalid ipsec flow" :
2066207bd73aSyasuoka 		    "without ipsec");
2067207bd73aSyasuoka 		goto drop;
2068207bd73aSyasuoka 	}
20698876230bShsuenaga 
20705c7fed39Sdlg 	m_copydata(m0, off0, sizeof(flags), &flags);
20718876230bShsuenaga 
20728876230bShsuenaga 	flags = ntohs(flags) & PIPEX_L2TP_FLAG_MASK;
20738876230bShsuenaga 	KASSERT((flags & PIPEX_L2TP_FLAG_TYPE) == 0);
20748876230bShsuenaga 
20758876230bShsuenaga 	hlen = 2;				/* flags and version fields */
20768876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_LENGTH)	/* length */
20778876230bShsuenaga 		hlen += 2;
20788876230bShsuenaga 	hlen += 4;				/* tunnel-id and session-id */
20798876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_SEQUENCE)	/* ns and nr */
20808876230bShsuenaga 		hlen += 4;
20818876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_OFFSET)	/* offset */
20828876230bShsuenaga 		hlen += 2;
20838876230bShsuenaga 
20848876230bShsuenaga 	PIPEX_PULLUP(m0, off0 + hlen);
20858876230bShsuenaga 	if (m0  == NULL)
20868876230bShsuenaga 		goto drop;
20878876230bShsuenaga 
20888876230bShsuenaga 	cp = mtod(m0, u_char *) + off0;
20898876230bShsuenaga 	cp += 2;	/* flags and version */
20908876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_LENGTH)
20918876230bShsuenaga 		GETSHORT(length, cp);
20928876230bShsuenaga 	else
20938876230bShsuenaga 		length = m0->m_pkthdr.len - off0;
20948876230bShsuenaga 	cp += 4;	/* skip tunnel-id and session-id field */
20958876230bShsuenaga 
20968876230bShsuenaga 	/* pullup for seek sequences in header */
20978876230bShsuenaga 	nseq = 0;
20988876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_SEQUENCE) {
20998876230bShsuenaga 		nsp = cp;
21008876230bShsuenaga 		GETSHORT(ns, cp);
21018876230bShsuenaga 		nrp = cp;
21028876230bShsuenaga 		GETSHORT(nr, cp);
21038876230bShsuenaga 
21048876230bShsuenaga 		nr++;
21058876230bShsuenaga 		if (SEQ16_GT(nr, l2tp_session->ns_una) &&
21068876230bShsuenaga 		    SEQ16_LE(nr, l2tp_session->ns_nxt))
21078876230bShsuenaga 			/* update 'ns_una' only if the ns is in valid range */
21088876230bShsuenaga 			l2tp_session->ns_una = nr;
2109e405d423Syasuoka 		if (SEQ16_LT(ns, l2tp_session->nr_nxt)) {
2110e405d423Syasuoka 			rewind = 1;
2111e405d423Syasuoka 			if (SEQ16_LT(ns,
2112e405d423Syasuoka 			    l2tp_session->nr_nxt - PIPEX_REWIND_LIMIT))
21138876230bShsuenaga 				goto out_seq;
2114e405d423Syasuoka 		}
21158876230bShsuenaga 
21168876230bShsuenaga 		ns++;
21178876230bShsuenaga 		nseq = SEQ16_SUB(ns, l2tp_session->nr_nxt);
2118e405d423Syasuoka 		if (!rewind)
21198876230bShsuenaga 			l2tp_session->nr_nxt = ns;
21208876230bShsuenaga 	}
21218876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_OFFSET)
21228876230bShsuenaga 		GETSHORT(offset, cp);
21238876230bShsuenaga 
2124d5af01deSyasuoka 	length -= hlen + offset;
2125d5af01deSyasuoka 	hlen += off0 + offset;
2126707c5f41Smvs 
2127707c5f41Smvs 	/*
2128707c5f41Smvs 	 * The following pipex_common_input() will release `pxs_mtx'
2129707c5f41Smvs 	 * deep within if the packet will be consumed. In the error
2130707c5f41Smvs 	 * path lock will be held all the time. So increment `nr_gap'
2131707c5f41Smvs 	 * here, and on the error path back it out, no atomicity will
2132707c5f41Smvs 	 * be lost in all cases.
2133707c5f41Smvs 	 */
2134e405d423Syasuoka 	if (!rewind)
21358876230bShsuenaga 		session->proto.l2tp.nr_gap += nseq;
2136707c5f41Smvs 	m0 = pipex_common_input(session, m0, hlen, length, 1);
2137707c5f41Smvs 	if (m0 == NULL) {
2138707c5f41Smvs 		/*
2139707c5f41Smvs 		 * pipex_common_input() releases lock if the
2140707c5f41Smvs 		 * packet was consumed.
2141707c5f41Smvs 		 */
2142d5af01deSyasuoka 		return (NULL);
21438876230bShsuenaga 	}
21448876230bShsuenaga 
2145e405d423Syasuoka 	if (rewind)
2146e405d423Syasuoka 		goto out_seq;
2147707c5f41Smvs 	else {
2148707c5f41Smvs 		/* The packet is not ours, backout `nr_gap'. */
2149707c5f41Smvs 		session->proto.l2tp.nr_gap -= nseq;
2150707c5f41Smvs 	}
2151e405d423Syasuoka 
21528876230bShsuenaga 	/*
21538876230bShsuenaga 	 * overwrite sequence numbers to adjust a gap between pipex and
21548876230bShsuenaga 	 * userland.
21558876230bShsuenaga 	 */
21568876230bShsuenaga 	if (flags & PIPEX_L2TP_FLAG_SEQUENCE) {
21578876230bShsuenaga 		--ns; --nr;	/* revert original values */
21588876230bShsuenaga 		ns -= l2tp_session->nr_gap;
21598876230bShsuenaga 		PUTSHORT(ns, nsp);
21608876230bShsuenaga 
21618876230bShsuenaga 		if (l2tp_session->ns_nxt == l2tp_session->ns_una) {
21628876230bShsuenaga 			nr -= l2tp_session->ns_gap;
21638876230bShsuenaga 			l2tp_session->ul_ns_una = nr;
21648876230bShsuenaga 		} else {
21658876230bShsuenaga 			/*
21668876230bShsuenaga 			 * There are sending packets they are not acked.
21678876230bShsuenaga 			 * In this situation, (ack - snd_gap) may points
21688876230bShsuenaga 			 * before sending window of userland.  So we don't
21698876230bShsuenaga 			 * update the ack number.
21708876230bShsuenaga 			 */
21718876230bShsuenaga 			nr = l2tp_session->ul_ns_una;
21728876230bShsuenaga 		}
21738876230bShsuenaga 		PUTSHORT(nr, nrp);
21748876230bShsuenaga 	}
21758876230bShsuenaga 
2176707c5f41Smvs 	mtx_leave(&session->pxs_mtx);
2177707c5f41Smvs 
21788876230bShsuenaga 	return (m0);
21798876230bShsuenaga out_seq:
21808876230bShsuenaga 	pipex_session_log(session, LOG_DEBUG,
21818876230bShsuenaga 	    "Received bad data packet: out of sequence: seq=%u(%u-) "
21828876230bShsuenaga 	    "ack=%u(%u-%u)", ns, l2tp_session->nr_nxt, nr, l2tp_session->ns_una,
21838876230bShsuenaga 	    l2tp_session->ns_nxt);
21848876230bShsuenaga 	/* FALLTHROUGH */
21858876230bShsuenaga drop:
2186707c5f41Smvs 	mtx_leave(&session->pxs_mtx);
2187707c5f41Smvs 
21888876230bShsuenaga 	m_freem(m0);
2189df21f681Smvs 	counters_inc(session->stat_counters, pxc_ierrors);
21908876230bShsuenaga 
21918876230bShsuenaga 	return (NULL);
21928876230bShsuenaga }
21938876230bShsuenaga 
21948876230bShsuenaga struct pipex_session *
21958876230bShsuenaga pipex_l2tp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst)
21968876230bShsuenaga {
21979c794535Sbluhm 	struct sockaddr_in sin;
21988876230bShsuenaga 
21999c794535Sbluhm 	memset(&sin, 0, sizeof(sin));
22009c794535Sbluhm 	sin.sin_len = sizeof(sin);
22019c794535Sbluhm 	sin.sin_family = AF_INET;
22029c794535Sbluhm 	sin.sin_addr = dst;
22038876230bShsuenaga 
22049c794535Sbluhm 	return pipex_l2tp_userland_lookup_session(m0, sintosa(&sin));
22058876230bShsuenaga }
22068876230bShsuenaga 
22078876230bShsuenaga #ifdef INET6
22088876230bShsuenaga struct pipex_session *
22098876230bShsuenaga pipex_l2tp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst)
22108876230bShsuenaga {
22118876230bShsuenaga 	struct sockaddr_in6 sin6;
22128876230bShsuenaga 
22139c794535Sbluhm 	memset(&sin6, 0, sizeof(sin6));
22149c794535Sbluhm 	sin6.sin6_len = sizeof(sin6);
22158876230bShsuenaga 	sin6.sin6_family = AF_INET6;
22166c8dd0e9Sclaudio 	in6_recoverscope(&sin6, &dst);
22178876230bShsuenaga 
22189c794535Sbluhm 	return pipex_l2tp_userland_lookup_session(m0, sin6tosa(&sin6));
22198876230bShsuenaga }
22208876230bShsuenaga #endif
22218876230bShsuenaga 
22227b163281Syasuoka struct pipex_session *
22238876230bShsuenaga pipex_l2tp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa)
22248876230bShsuenaga {
22258876230bShsuenaga 	struct pipex_l2tp_header l2tp;
22268876230bShsuenaga 	struct pipex_hash_head *list;
22278876230bShsuenaga 	struct pipex_session *session;
22288876230bShsuenaga 	uint16_t session_id, tunnel_id, flags;
22298876230bShsuenaga 
22307b163281Syasuoka 	if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
22317b163281Syasuoka 		return (NULL);
22327b163281Syasuoka 
22338876230bShsuenaga 	/* pullup */
22348876230bShsuenaga 	if (m0->m_pkthdr.len < sizeof(l2tp)) {
22358876230bShsuenaga 		PIPEX_DBG((NULL, LOG_DEBUG,
22368876230bShsuenaga 		    "<%s> packet length is too short", __func__));
22378876230bShsuenaga 		return (NULL);
22388876230bShsuenaga 	}
22398876230bShsuenaga 
22408876230bShsuenaga 	/* get flags */
22415c7fed39Sdlg 	m_copydata(m0, 0, sizeof(l2tp), &l2tp);
22428876230bShsuenaga 	flags = ntohs(l2tp.flagsver);
22438876230bShsuenaga 
22448876230bShsuenaga 	/* l2tp version must be '2' */
22458876230bShsuenaga 	if ((flags & PIPEX_L2TP_VER_MASK) != PIPEX_L2TP_VER) {
22468876230bShsuenaga 		PIPEX_DBG((NULL, LOG_DEBUG,
22478876230bShsuenaga 		    "<%s> l2tp header wrong version.", __func__));
22488876230bShsuenaga 		return (NULL);
22498876230bShsuenaga 	}
22508876230bShsuenaga 	/* We need L2TP data messages only */
22518876230bShsuenaga 	if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0)
22528876230bShsuenaga 		return (NULL);
22538876230bShsuenaga 	/* No need to hook packets that don't have the sequence field */
22548876230bShsuenaga 	if ((flags & PIPEX_L2TP_FLAG_SEQUENCE) == 0)
22558876230bShsuenaga 		return (NULL);
22568876230bShsuenaga 
22578876230bShsuenaga 	session_id = ntohs(l2tp.session_id);
22588876230bShsuenaga 	tunnel_id = ntohs(l2tp.tunnel_id);
22598876230bShsuenaga 
2260f6e6f8e7Smvs 	mtx_enter(&pipex_list_mtx);
2261f6e6f8e7Smvs 
22628876230bShsuenaga 	list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa));
22638876230bShsuenaga 	LIST_FOREACH(session, list, peer_addr_chain) {
22649c681c75Sbluhm 		if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0)
22658876230bShsuenaga 			continue;
22668876230bShsuenaga 		if (session->proto.l2tp.peer_tunnel_id != tunnel_id)
22678876230bShsuenaga 			continue;
22688876230bShsuenaga 		if (session->peer_session_id == session_id)
22698876230bShsuenaga 			break;
22708876230bShsuenaga 	}
2271f6e6f8e7Smvs 
2272f6e6f8e7Smvs 	if (session != NULL)
2273f6e6f8e7Smvs 		refcnt_take(&session->pxs_refcnt);
2274f6e6f8e7Smvs 
2275f6e6f8e7Smvs 	mtx_leave(&pipex_list_mtx);
2276f6e6f8e7Smvs 
22778876230bShsuenaga #ifdef PIPEX_DEBUG
22788876230bShsuenaga 	if (session == NULL) {
22798876230bShsuenaga 		PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found "
22808876230bShsuenaga 		    "(tunnel_id=%d, session_id=%d)", __func__,
22818876230bShsuenaga 		    tunnel_id, session_id));
22828876230bShsuenaga 	}
22838876230bShsuenaga #endif
22848876230bShsuenaga 
22858876230bShsuenaga 	return (session);
22868876230bShsuenaga }
22878876230bShsuenaga 
22888876230bShsuenaga struct mbuf *
22898876230bShsuenaga pipex_l2tp_userland_output(struct mbuf *m0, struct pipex_session *session)
22908876230bShsuenaga {
22918876230bShsuenaga 	struct pipex_l2tp_header *l2tp;
22928876230bShsuenaga 	struct pipex_l2tp_seq_header *seq;
22938876230bShsuenaga 	uint16_t ns, nr;
22948876230bShsuenaga 
22958876230bShsuenaga 	/* check length */
22968876230bShsuenaga 	PIPEX_PULLUP(m0, sizeof(struct pipex_l2tp_header) +
22978876230bShsuenaga 	    sizeof(struct pipex_l2tp_seq_header));
22988876230bShsuenaga 	if (m0 == NULL)
22998876230bShsuenaga 		return (NULL);
23008876230bShsuenaga 
23018876230bShsuenaga 	l2tp = mtod(m0, struct pipex_l2tp_header *);
23028876230bShsuenaga 	KASSERT(ntohs(l2tp->flagsver) & PIPEX_L2TP_FLAG_SEQUENCE);
23038876230bShsuenaga 
23048876230bShsuenaga 	/*
23058876230bShsuenaga 	 * overwrite sequence numbers to adjust a gap between pipex and
23068876230bShsuenaga 	 * userland.
23078876230bShsuenaga 	 */
23088876230bShsuenaga 	seq = (struct pipex_l2tp_seq_header *)(l2tp + 1);
23098876230bShsuenaga 	ns = ntohs(seq->ns);
23108876230bShsuenaga 	nr = ntohs(seq->nr);
23118876230bShsuenaga 
2312707c5f41Smvs 	mtx_enter(&session->pxs_mtx);
2313707c5f41Smvs 
23148876230bShsuenaga 	ns += session->proto.l2tp.ns_gap;
23158876230bShsuenaga 	seq->ns = htons(ns);
23168876230bShsuenaga 	session->proto.l2tp.ns_nxt++;
23178876230bShsuenaga 
23188876230bShsuenaga 	nr += session->proto.l2tp.nr_gap;
23198876230bShsuenaga 	seq->nr = htons(nr);
23208876230bShsuenaga 	if (SEQ16_GT(nr, session->proto.l2tp.nr_acked))
23218876230bShsuenaga 		session->proto.l2tp.nr_acked = nr;
23228876230bShsuenaga 
2323707c5f41Smvs 	mtx_leave(&session->pxs_mtx);
2324707c5f41Smvs 
23258876230bShsuenaga 	return (m0);
23268876230bShsuenaga }
23278876230bShsuenaga #endif /* PIPEX_L2TP */
23288876230bShsuenaga 
2329a0ade7e9Syasuoka #ifdef PIPEX_MPPE
2330a0ade7e9Syasuoka /**********************************************************************
2331a0ade7e9Syasuoka  * MPPE
2332a0ade7e9Syasuoka  ***********************************************************************/
2333a0ade7e9Syasuoka #define	PIPEX_COHERENCY_CNT_MASK		0x0fff
2334f7863625Smillert 
2335f7863625Smillert static inline int
2336f7863625Smillert pipex_mppe_setkey(struct pipex_mppe *mppe)
2337f7863625Smillert {
2338f7863625Smillert 	rc4_keysetup(&mppe->rc4ctx, mppe->session_key, mppe->keylen);
2339f7863625Smillert 
2340f7863625Smillert 	return (0);
2341f7863625Smillert }
2342f7863625Smillert 
2343f7863625Smillert static inline int
2344f7863625Smillert pipex_mppe_setoldkey(struct pipex_mppe *mppe, uint16_t coher_cnt)
2345f7863625Smillert {
2346f7863625Smillert 	KASSERT(mppe->old_session_keys != NULL);
2347f7863625Smillert 
2348f7863625Smillert 	rc4_keysetup(&mppe->rc4ctx,
2349f7863625Smillert 	    mppe->old_session_keys[coher_cnt & PIPEX_MPPE_OLDKEYMASK],
2350f7863625Smillert 	    mppe->keylen);
2351f7863625Smillert 
2352f7863625Smillert 	return (0);
2353f7863625Smillert }
2354f7863625Smillert 
2355f7863625Smillert static inline void
2356f7863625Smillert pipex_mppe_crypt(struct pipex_mppe *mppe, int len, u_char *indata,
2357f7863625Smillert     u_char *outdata)
2358f7863625Smillert {
2359f7863625Smillert 	rc4_crypt(&mppe->rc4ctx, indata, outdata, len);
2360f7863625Smillert }
2361f7863625Smillert 
2362c02a3381Smvs void
2363e405d423Syasuoka pipex_mppe_init(struct pipex_mppe *mppe, int stateless, int keylenbits,
2364e405d423Syasuoka     u_char *master_key, int has_oldkey)
2365a0ade7e9Syasuoka {
2366e405d423Syasuoka 	memset(mppe, 0, sizeof(struct pipex_mppe));
23672960d3c8Smvs 	mtx_init(&mppe->pxm_mtx, IPL_SOFTNET);
2368e405d423Syasuoka 	if (stateless)
23691935ef3dSmvs 		mppe->flags |= PIPEX_MPPE_STATELESS;
2370e405d423Syasuoka 	if (has_oldkey)
2371e405d423Syasuoka 		mppe->old_session_keys =
2372edccd885Suebayasi 		    pool_get(&mppe_key_pool, PR_WAITOK);
2373e405d423Syasuoka 	else
2374e405d423Syasuoka 		mppe->old_session_keys = NULL;
2375e405d423Syasuoka 	memcpy(mppe->master_key, master_key, sizeof(mppe->master_key));
2376a0ade7e9Syasuoka 
2377e405d423Syasuoka 	mppe->keylenbits = keylenbits;
2378e405d423Syasuoka 	switch (keylenbits) {
2379a0ade7e9Syasuoka 	case 40:
2380a0ade7e9Syasuoka 	case 56:
2381a0ade7e9Syasuoka 		mppe->keylen = 8;
2382a0ade7e9Syasuoka 		break;
2383a0ade7e9Syasuoka 	case 128:
2384a0ade7e9Syasuoka 		mppe->keylen = 16;
2385a0ade7e9Syasuoka 		break;
2386a0ade7e9Syasuoka 	}
2387a0ade7e9Syasuoka 
238863d4736dSyasuoka 	GetNewKeyFromSHA(mppe->master_key, mppe->master_key, mppe->keylen,
238963d4736dSyasuoka 	    mppe->session_key);
2390a0ade7e9Syasuoka 	pipex_mppe_reduce_key(mppe);
2391e405d423Syasuoka 	pipex_mppe_setkey(mppe);
2392e405d423Syasuoka }
2393e405d423Syasuoka 
2394e405d423Syasuoka void
2395e405d423Syasuoka pipex_session_init_mppe_recv(struct pipex_session *session, int stateless,
2396e405d423Syasuoka     int keylenbits, u_char *master_key)
2397e405d423Syasuoka {
2398e405d423Syasuoka 	pipex_mppe_init(&session->mppe_recv, stateless, keylenbits,
2399e405d423Syasuoka 	    master_key, stateless);
2400e405d423Syasuoka 	session->ppp_flags |= PIPEX_PPP_MPPE_ACCEPTED;
2401e405d423Syasuoka }
2402e405d423Syasuoka 
2403e405d423Syasuoka void
2404e405d423Syasuoka pipex_session_init_mppe_send(struct pipex_session *session, int stateless,
2405e405d423Syasuoka     int keylenbits, u_char *master_key)
2406e405d423Syasuoka {
2407e405d423Syasuoka 	pipex_mppe_init(&session->mppe_send, stateless, keylenbits,
2408e405d423Syasuoka 	    master_key, 0);
2409e405d423Syasuoka 	session->ppp_flags |= PIPEX_PPP_MPPE_ENABLED;
2410a0ade7e9Syasuoka }
2411a0ade7e9Syasuoka 
2412a0ade7e9Syasuoka #include <crypto/sha1.h>
2413a0ade7e9Syasuoka 
2414a0ade7e9Syasuoka static u_char SHAPad1[] = {
2415a0ade7e9Syasuoka 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2416a0ade7e9Syasuoka 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2417a0ade7e9Syasuoka 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2418a0ade7e9Syasuoka 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2419a0ade7e9Syasuoka 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2420a0ade7e9Syasuoka }, SHAPad2[] = {
2421a0ade7e9Syasuoka 	0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2422a0ade7e9Syasuoka 	0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2423a0ade7e9Syasuoka 	0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2424a0ade7e9Syasuoka 	0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2425a0ade7e9Syasuoka 	0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
2426a0ade7e9Syasuoka };
2427a0ade7e9Syasuoka 
2428c02a3381Smvs void
242963d4736dSyasuoka GetNewKeyFromSHA(u_char *StartKey, u_char *SessionKey, int SessionKeyLength,
243063d4736dSyasuoka     u_char *InterimKey)
2431a0ade7e9Syasuoka {
2432a0ade7e9Syasuoka 	u_char Digest[20];
243363d4736dSyasuoka 	SHA1_CTX Context;
2434a0ade7e9Syasuoka 
243563d4736dSyasuoka 	SHA1Init(&Context);
243663d4736dSyasuoka 	SHA1Update(&Context, StartKey, SessionKeyLength);
243763d4736dSyasuoka 	SHA1Update(&Context, SHAPad1, 40);
243863d4736dSyasuoka 	SHA1Update(&Context, SessionKey, SessionKeyLength);
243963d4736dSyasuoka 	SHA1Update(&Context, SHAPad2, 40);
244063d4736dSyasuoka 	SHA1Final(Digest, &Context);
2441a0ade7e9Syasuoka 
244263d4736dSyasuoka 	memcpy(InterimKey, Digest, SessionKeyLength);
2443a0ade7e9Syasuoka }
2444a0ade7e9Syasuoka 
2445c02a3381Smvs void
2446a0ade7e9Syasuoka pipex_mppe_reduce_key(struct pipex_mppe *mppe)
2447a0ade7e9Syasuoka {
2448a0ade7e9Syasuoka 	switch (mppe->keylenbits) {
2449a0ade7e9Syasuoka 	case 40:
24507704014bSmestre 		mppe->session_key[0] = 0xd1;
2451a0ade7e9Syasuoka 		mppe->session_key[1] = 0x26;
2452a0ade7e9Syasuoka 		mppe->session_key[2] = 0x9e;
24537704014bSmestre 		break;
2454a0ade7e9Syasuoka 	case 56:
2455a0ade7e9Syasuoka 		mppe->session_key[0] = 0xd1;
24567704014bSmestre 		break;
2457a0ade7e9Syasuoka 	}
2458a0ade7e9Syasuoka }
2459a0ade7e9Syasuoka 
2460c02a3381Smvs void
2461a0ade7e9Syasuoka mppe_key_change(struct pipex_mppe *mppe)
2462a0ade7e9Syasuoka {
2463a0ade7e9Syasuoka 	u_char interim[16];
2464e405d423Syasuoka 	struct rc4_ctx keychg;
2465a0ade7e9Syasuoka 
2466a0ade7e9Syasuoka 	memset(&keychg, 0, sizeof(keychg));
2467a0ade7e9Syasuoka 
246863d4736dSyasuoka 	GetNewKeyFromSHA(mppe->master_key, mppe->session_key, mppe->keylen,
246963d4736dSyasuoka 	    interim);
2470a0ade7e9Syasuoka 
2471e405d423Syasuoka 	rc4_keysetup(&keychg, interim, mppe->keylen);
2472e405d423Syasuoka 	rc4_crypt(&keychg, interim, mppe->session_key, mppe->keylen);
247363d4736dSyasuoka 
2474a0ade7e9Syasuoka 	pipex_mppe_reduce_key(mppe);
2475e405d423Syasuoka 
2476e405d423Syasuoka 	if (mppe->old_session_keys) {
2477e405d423Syasuoka 		int idx = mppe->coher_cnt & PIPEX_MPPE_OLDKEYMASK;
2478e405d423Syasuoka 		memcpy(mppe->old_session_keys[idx],
2479e405d423Syasuoka 		    mppe->session_key, PIPEX_MPPE_KEYLEN);
2480e405d423Syasuoka 	}
2481a0ade7e9Syasuoka }
2482a0ade7e9Syasuoka 
2483707c5f41Smvs struct mbuf *
2484a0ade7e9Syasuoka pipex_mppe_input(struct mbuf *m0, struct pipex_session *session)
2485a0ade7e9Syasuoka {
2486a0ade7e9Syasuoka 	int pktloss, encrypt, flushed, m, n, len;
2487a0ade7e9Syasuoka 	struct pipex_mppe *mppe;
2488a0ade7e9Syasuoka 	uint16_t coher_cnt;
2489a0ade7e9Syasuoka 	struct mbuf *m1;
2490a0ade7e9Syasuoka 	u_char *cp;
2491e405d423Syasuoka 	int rewind = 0;
2492a0ade7e9Syasuoka 
2493a0ade7e9Syasuoka 	/* pullup */
2494a0ade7e9Syasuoka 	PIPEX_PULLUP(m0, sizeof(coher_cnt));
2495a0ade7e9Syasuoka 	if (m0 == NULL)
2496a0ade7e9Syasuoka 		goto drop;
2497a0ade7e9Syasuoka 
2498a0ade7e9Syasuoka 	mppe = &session->mppe_recv;
2499a0ade7e9Syasuoka 	/* get header information */
2500a0ade7e9Syasuoka 	cp = mtod(m0, u_char *);
2501a0ade7e9Syasuoka 	GETSHORT(coher_cnt, cp);
2502a0ade7e9Syasuoka 	flushed = ((coher_cnt & 0x8000) != 0) ? 1 : 0;
2503a0ade7e9Syasuoka 	encrypt = ((coher_cnt & 0x1000) != 0) ? 1 : 0;
2504a0ade7e9Syasuoka 	coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2505a0ade7e9Syasuoka 	pktloss = 0;
2506a0ade7e9Syasuoka 
25072960d3c8Smvs 	mtx_enter(&mppe->pxm_mtx);
25082960d3c8Smvs 
2509a0ade7e9Syasuoka 	PIPEX_MPPE_DBG((session, LOG_DEBUG, "in coher_cnt=%03x %s%s",
2510a0ade7e9Syasuoka 	    mppe->coher_cnt, (flushed) ? "[flushed]" : "",
2511a0ade7e9Syasuoka 	    (encrypt) ? "[encrypt]" : ""));
2512a0ade7e9Syasuoka 
2513a0ade7e9Syasuoka 	if (encrypt == 0) {
25142960d3c8Smvs 		mtx_leave(&mppe->pxm_mtx);
2515a0ade7e9Syasuoka 		pipex_session_log(session, LOG_DEBUG,
2516a0ade7e9Syasuoka 		    "Received unexpected MPPE packet.(no ecrypt)");
2517a0ade7e9Syasuoka 		goto drop;
2518a0ade7e9Syasuoka 	}
2519a0ade7e9Syasuoka 
2520a0ade7e9Syasuoka 	/* adjust mbuf */
2521a0ade7e9Syasuoka 	m_adj(m0, sizeof(coher_cnt));
2522a0ade7e9Syasuoka 
2523a0ade7e9Syasuoka 	/*
2524a0ade7e9Syasuoka 	 * L2TP data session may be used without sequencing, PPP frames may
2525a0ade7e9Syasuoka 	 * arrive in disorder.  The 'coherency counter' of MPPE detects such
2526a0ade7e9Syasuoka 	 * situations, but we cannot distinguish between 'disorder' and
2527a0ade7e9Syasuoka 	 * 'packet loss' exactly.
2528a0ade7e9Syasuoka 	 *
2529a0ade7e9Syasuoka 	 * When 'coherency counter' detects lost packets greater than
2530a0ade7e9Syasuoka 	 * (4096 - 256), we treat as 'disorder' otherwise treat as
2531a0ade7e9Syasuoka 	 * 'packet loss'.
2532a0ade7e9Syasuoka 	 */
2533a0ade7e9Syasuoka     {
2534a0ade7e9Syasuoka 	int coher_cnt0;
2535a0ade7e9Syasuoka 
2536a0ade7e9Syasuoka 	coher_cnt0 = coher_cnt;
2537a0ade7e9Syasuoka 	if (coher_cnt < mppe->coher_cnt)
2538a0ade7e9Syasuoka 		coher_cnt0 += 0x1000;
2539a0ade7e9Syasuoka 	if (coher_cnt0 - mppe->coher_cnt > 0x0f00) {
25401935ef3dSmvs 		if ((mppe->flags & PIPEX_MPPE_STATELESS) == 0 ||
2541e405d423Syasuoka 		    coher_cnt0 - mppe->coher_cnt
2542e405d423Syasuoka 		    <= 0x1000 - PIPEX_MPPE_NOLDKEY) {
2543a0ade7e9Syasuoka 			pipex_session_log(session, LOG_DEBUG,
2544a0ade7e9Syasuoka 			    "Workaround the out-of-sequence PPP framing problem: "
2545a0ade7e9Syasuoka 			    "%d => %d", mppe->coher_cnt, coher_cnt);
25462960d3c8Smvs 			mtx_leave(&mppe->pxm_mtx);
2547a0ade7e9Syasuoka 			goto drop;
2548a0ade7e9Syasuoka 		}
2549e405d423Syasuoka 		rewind = 1;
2550a0ade7e9Syasuoka 	}
2551e405d423Syasuoka     }
2552e405d423Syasuoka 
25531935ef3dSmvs 	if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) {
2554e405d423Syasuoka 		if (!rewind) {
2555a0ade7e9Syasuoka 			mppe_key_change(mppe);
2556a0ade7e9Syasuoka 			while (mppe->coher_cnt != coher_cnt) {
2557a0ade7e9Syasuoka 				mppe->coher_cnt++;
2558a0ade7e9Syasuoka 				mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2559e405d423Syasuoka 				mppe_key_change(mppe);
2560a0ade7e9Syasuoka 				pktloss++;
2561a0ade7e9Syasuoka 			}
2562e405d423Syasuoka 		}
2563e405d423Syasuoka 		pipex_mppe_setoldkey(mppe, coher_cnt);
2564a0ade7e9Syasuoka 	} else {
2565a0ade7e9Syasuoka 		if (flushed) {
2566a0ade7e9Syasuoka 			if (coher_cnt < mppe->coher_cnt) {
2567a0ade7e9Syasuoka 				coher_cnt += 0x1000;
2568a0ade7e9Syasuoka 			}
2569a0ade7e9Syasuoka 			pktloss += coher_cnt - mppe->coher_cnt;
2570a0ade7e9Syasuoka 			m = mppe->coher_cnt / 256;
2571a0ade7e9Syasuoka 			n = coher_cnt / 256;
2572a0ade7e9Syasuoka 			while (m++ < n)
2573a0ade7e9Syasuoka 				mppe_key_change(mppe);
2574a0ade7e9Syasuoka 
2575a0ade7e9Syasuoka 			coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2576a0ade7e9Syasuoka 			mppe->coher_cnt = coher_cnt;
2577a0ade7e9Syasuoka 		} else if (mppe->coher_cnt != coher_cnt) {
25782960d3c8Smvs 			int ccp_id;
25792960d3c8Smvs 
25802960d3c8Smvs 			mtx_leave(&mppe->pxm_mtx);
25812960d3c8Smvs 
2582a0ade7e9Syasuoka 			/* Send CCP ResetReq */
2583a0ade7e9Syasuoka 			PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetReq"));
25842960d3c8Smvs 
25852960d3c8Smvs 			mtx_enter(&session->pxs_mtx);
25862960d3c8Smvs 			ccp_id = session->ccp_id;
25872960d3c8Smvs 			session->ccp_id++;
25882960d3c8Smvs 			mtx_leave(&session->pxs_mtx);
25892960d3c8Smvs 
25902960d3c8Smvs 			pipex_ccp_output(session, CCP_RESETREQ, ccp_id);
2591a0ade7e9Syasuoka 			goto drop;
2592a0ade7e9Syasuoka 		}
2593a0ade7e9Syasuoka 		if ((coher_cnt & 0xff) == 0xff) {
2594a0ade7e9Syasuoka 			mppe_key_change(mppe);
2595a0ade7e9Syasuoka 			flushed = 1;
2596a0ade7e9Syasuoka 		}
2597e405d423Syasuoka 		if (flushed)
2598e405d423Syasuoka 			pipex_mppe_setkey(mppe);
2599a0ade7e9Syasuoka 	}
2600e405d423Syasuoka 
2601a0ade7e9Syasuoka 	if (pktloss > 1000) {
2602a0ade7e9Syasuoka 		pipex_session_log(session, LOG_DEBUG,
2603a0ade7e9Syasuoka 		    "%d packets loss.", pktloss);
2604a0ade7e9Syasuoka 	}
2605a0ade7e9Syasuoka 
2606a0ade7e9Syasuoka 	/* decrypt ppp payload */
2607a0ade7e9Syasuoka 	for (m1 = m0; m1; m1 = m1->m_next) {
2608a0ade7e9Syasuoka 		cp = mtod(m1, u_char *);
2609a0ade7e9Syasuoka 		len = m1->m_len;
2610e405d423Syasuoka 		pipex_mppe_crypt(mppe, len, cp, cp);
2611a0ade7e9Syasuoka 	}
2612a0ade7e9Syasuoka 
2613e405d423Syasuoka 	if (!rewind) {
2614a0ade7e9Syasuoka 		/* update coher_cnt */
2615a0ade7e9Syasuoka 		mppe->coher_cnt++;
2616a0ade7e9Syasuoka 		mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2617e405d423Syasuoka 	}
26182960d3c8Smvs 
26192960d3c8Smvs 	mtx_leave(&mppe->pxm_mtx);
26202960d3c8Smvs 
2621363bb3c1Syasuoka 	if (m0->m_pkthdr.len < PIPEX_PPPMINLEN)
2622363bb3c1Syasuoka 		goto drop;
2623a0ade7e9Syasuoka 
2624707c5f41Smvs 	return (m0);
2625a0ade7e9Syasuoka drop:
2626a0ade7e9Syasuoka 	m_freem(m0);
2627707c5f41Smvs 	return (NULL);
2628a0ade7e9Syasuoka }
2629a0ade7e9Syasuoka 
2630707c5f41Smvs struct mbuf *
263137f64355Syasuoka pipex_mppe_output(struct mbuf *m0, struct pipex_session *session,
263237f64355Syasuoka     uint16_t protocol)
2633a0ade7e9Syasuoka {
2634a0ade7e9Syasuoka 	int encrypt, flushed, len;
263537f64355Syasuoka 	struct mppe_header {
2636a0ade7e9Syasuoka 		uint16_t coher_cnt;
263737f64355Syasuoka 		uint16_t protocol;
263837f64355Syasuoka 	} __packed *hdr;
2639a0ade7e9Syasuoka 	u_char *cp;
2640a0ade7e9Syasuoka 	struct pipex_mppe *mppe;
2641a0ade7e9Syasuoka 	struct mbuf *m;
2642a0ade7e9Syasuoka 
2643a0ade7e9Syasuoka 	mppe = &session->mppe_send;
2644a0ade7e9Syasuoka 
2645a0ade7e9Syasuoka 	/*
2646a0ade7e9Syasuoka 	 * create a deep-copy if the mbuf has a shared mbuf cluster.
2647678831beSjsg 	 * this is required to handle cases of tcp retransmission.
2648a0ade7e9Syasuoka 	 */
2649a0ade7e9Syasuoka 	for (m = m0; m != NULL; m = m->m_next) {
2650a0ade7e9Syasuoka 		if (M_READONLY(m)) {
26517bb226caSdlg 			m = m_dup_pkt(m0, max_linkhdr, M_NOWAIT);
2652c31e5583Sdlg 			m_freem(m0);
2653a0ade7e9Syasuoka 			if (m == NULL)
2654707c5f41Smvs 				return (NULL);
2655a0ade7e9Syasuoka 			m0 = m;
2656a0ade7e9Syasuoka 			break;
2657a0ade7e9Syasuoka 		}
2658a0ade7e9Syasuoka 	}
2659cb76d290Syasuoka 	/* prepend mppe header */
2660cb76d290Syasuoka 	M_PREPEND(m0, sizeof(struct mppe_header), M_NOWAIT);
2661cb76d290Syasuoka 	if (m0 == NULL)
2662707c5f41Smvs 		return (NULL);
2663cb76d290Syasuoka 	hdr = mtod(m0, struct mppe_header *);
2664cb76d290Syasuoka 	hdr->protocol = protocol;
2665a0ade7e9Syasuoka 
2666a0ade7e9Syasuoka 	/* check coherency counter */
2667a0ade7e9Syasuoka 	flushed = 0;
2668a0ade7e9Syasuoka 	encrypt = 1;
2669a0ade7e9Syasuoka 
26702960d3c8Smvs 	mtx_enter(&mppe->pxm_mtx);
26712960d3c8Smvs 
26721935ef3dSmvs 	if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) {
2673a0ade7e9Syasuoka 		flushed = 1;
2674a0ade7e9Syasuoka 		mppe_key_change(mppe);
2675a0ade7e9Syasuoka 	} else {
2676a0ade7e9Syasuoka 		if ((mppe->coher_cnt % 0x100) == 0xff) {
2677a0ade7e9Syasuoka 			flushed = 1;
2678a0ade7e9Syasuoka 			mppe_key_change(mppe);
26791935ef3dSmvs 		} else if ((mppe->flags & PIPEX_MPPE_RESETREQ) != 0) {
2680a0ade7e9Syasuoka 			flushed = 1;
26811935ef3dSmvs 			mppe->flags &= ~PIPEX_MPPE_RESETREQ;
2682a0ade7e9Syasuoka 		}
2683a0ade7e9Syasuoka 	}
2684a0ade7e9Syasuoka 
2685a0ade7e9Syasuoka 	if (flushed)
2686e405d423Syasuoka 		pipex_mppe_setkey(mppe);
2687a0ade7e9Syasuoka 
2688a0ade7e9Syasuoka 	PIPEX_MPPE_DBG((session, LOG_DEBUG, "out coher_cnt=%03x %s%s",
2689a0ade7e9Syasuoka 	    mppe->coher_cnt, (flushed) ? "[flushed]" : "",
2690a0ade7e9Syasuoka 	    (encrypt) ? "[encrypt]" : ""));
2691a0ade7e9Syasuoka 
2692a0ade7e9Syasuoka 	/* setup header information */
269337f64355Syasuoka 	hdr->coher_cnt = (mppe->coher_cnt++) & PIPEX_COHERENCY_CNT_MASK;
269437f64355Syasuoka 	hdr->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
2695a0ade7e9Syasuoka 	if (flushed)
269637f64355Syasuoka 		hdr->coher_cnt |= 0x8000;
2697a0ade7e9Syasuoka 	if (encrypt)
269837f64355Syasuoka 		hdr->coher_cnt |= 0x1000;
2699a0ade7e9Syasuoka 
2700faf7e06fSmpi 	hdr->protocol = htons(hdr->protocol);
2701faf7e06fSmpi 	hdr->coher_cnt = htons(hdr->coher_cnt);
2702a0ade7e9Syasuoka 
2703a0ade7e9Syasuoka 	/* encrypt chain */
270437f64355Syasuoka 	for (m = m0; m; m = m->m_next) {
2705a0ade7e9Syasuoka 		cp = mtod(m, u_char *);
2706a0ade7e9Syasuoka 		len = m->m_len;
270737f64355Syasuoka 		if (m == m0 && len > offsetof(struct mppe_header, protocol)) {
270837f64355Syasuoka 			len -= offsetof(struct mppe_header, protocol);
270937f64355Syasuoka 			cp += offsetof(struct mppe_header, protocol);
271037f64355Syasuoka 		}
2711e405d423Syasuoka 		pipex_mppe_crypt(mppe, len, cp, cp);
2712a0ade7e9Syasuoka 	}
2713a0ade7e9Syasuoka 
27142960d3c8Smvs 	mtx_leave(&mppe->pxm_mtx);
27152960d3c8Smvs 
2716707c5f41Smvs 	return (m0);
2717a0ade7e9Syasuoka }
2718a0ade7e9Syasuoka 
2719c02a3381Smvs void
2720a0ade7e9Syasuoka pipex_ccp_input(struct mbuf *m0, struct pipex_session *session)
2721a0ade7e9Syasuoka {
2722a0ade7e9Syasuoka 	u_char *cp;
2723a0ade7e9Syasuoka 	int code, id, len;
2724a0ade7e9Syasuoka 
2725a0ade7e9Syasuoka 	if (m0->m_pkthdr.len < PPP_HDRLEN)
2726a0ade7e9Syasuoka 		goto drop;
2727a0ade7e9Syasuoka 	if ((m0 = m_pullup(m0, PPP_HDRLEN)) == NULL)
2728a0ade7e9Syasuoka 		goto drop;
2729a0ade7e9Syasuoka 
2730a0ade7e9Syasuoka 	cp = mtod(m0, u_char *);
2731a0ade7e9Syasuoka 	GETCHAR(code, cp);
2732a0ade7e9Syasuoka 	GETCHAR(id, cp);
2733a0ade7e9Syasuoka 	GETSHORT(len, cp);
2734a0ade7e9Syasuoka 
2735a0ade7e9Syasuoka 	switch (code) {
2736a0ade7e9Syasuoka 	case CCP_RESETREQ:
2737a0ade7e9Syasuoka 		PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetReq"));
27382960d3c8Smvs 		mtx_enter(&session->mppe_send.pxm_mtx);
27391935ef3dSmvs 		session->mppe_send.flags |= PIPEX_MPPE_RESETREQ;
27402960d3c8Smvs 		mtx_leave(&session->mppe_send.pxm_mtx);
2741a0ade7e9Syasuoka #ifndef PIPEX_NO_CCP_RESETACK
2742a0ade7e9Syasuoka 		PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetAck"));
2743a0ade7e9Syasuoka 		pipex_ccp_output(session, CCP_RESETACK, id);
2744a0ade7e9Syasuoka #endif
2745a0ade7e9Syasuoka 		/* ignore error */
2746a0ade7e9Syasuoka 		break;
2747a0ade7e9Syasuoka 	case CCP_RESETACK:
2748a0ade7e9Syasuoka 		PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetAck"));
2749a0ade7e9Syasuoka 		break;
2750a0ade7e9Syasuoka 	default:
2751a0ade7e9Syasuoka 		PIPEX_DBG((session, LOG_DEBUG, "CCP Recv code=%d", code));
2752a0ade7e9Syasuoka 		goto drop;
2753a0ade7e9Syasuoka 	}
2754a0ade7e9Syasuoka 	m_freem(m0);
2755a0ade7e9Syasuoka 
2756a0ade7e9Syasuoka 	return;
2757a0ade7e9Syasuoka drop:
2758a0ade7e9Syasuoka 	m_freem(m0);
2759df21f681Smvs 	counters_inc(session->stat_counters, pxc_ierrors);
2760a0ade7e9Syasuoka }
2761a0ade7e9Syasuoka 
2762c02a3381Smvs int
2763a0ade7e9Syasuoka pipex_ccp_output(struct pipex_session *session, int code, int id)
2764a0ade7e9Syasuoka {
2765a0ade7e9Syasuoka 	u_char *cp;
2766a0ade7e9Syasuoka 	struct mbuf *m;
2767a0ade7e9Syasuoka 
2768a0ade7e9Syasuoka 	MGETHDR(m, M_DONTWAIT, MT_DATA);
2769a0ade7e9Syasuoka 	if (m == NULL) {
2770df21f681Smvs 		counters_inc(session->stat_counters, pxc_oerrors);
277163d4736dSyasuoka 		return (1);
2772a0ade7e9Syasuoka 	}
2773a0ade7e9Syasuoka 	m->m_pkthdr.len = m->m_len = 4;
2774a0ade7e9Syasuoka 	cp = mtod(m, u_char *);
2775a0ade7e9Syasuoka 	PUTCHAR(code, cp);
2776a0ade7e9Syasuoka 	PUTCHAR(id, cp);
2777a0ade7e9Syasuoka 	PUTSHORT(4, cp);
2778a0ade7e9Syasuoka 
2779a0ade7e9Syasuoka 	pipex_ppp_output(m, session, PPP_CCP);
2780a0ade7e9Syasuoka 
278163d4736dSyasuoka 	return (0);
2782a0ade7e9Syasuoka }
2783a0ade7e9Syasuoka #endif
2784a0ade7e9Syasuoka /***********************************************************************
2785678831beSjsg  * Miscellaneous functions
2786a0ade7e9Syasuoka  ***********************************************************************/
2787a0ade7e9Syasuoka /* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */
2788a0ade7e9Syasuoka /*
2789a0ade7e9Syasuoka  * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
2790a0ade7e9Syasuoka  * All rights reserved.
2791a0ade7e9Syasuoka  *
2792a0ade7e9Syasuoka  * Redistribution and use in source and binary forms, with or without
2793a0ade7e9Syasuoka  * modification, are permitted provided that the following conditions
2794a0ade7e9Syasuoka  * are met:
2795a0ade7e9Syasuoka  * 1. Redistributions of source code must retain the above copyright
2796a0ade7e9Syasuoka  *    notice, this list of conditions and the following disclaimer.
2797a0ade7e9Syasuoka  * 2. Redistributions in binary form must reproduce the above copyright
2798a0ade7e9Syasuoka  *    notice, this list of conditions and the following disclaimer in the
2799a0ade7e9Syasuoka  *    documentation and/or other materials provided with the distribution.
2800a0ade7e9Syasuoka  *
2801a0ade7e9Syasuoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2802a0ade7e9Syasuoka  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2803a0ade7e9Syasuoka  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2804a0ade7e9Syasuoka  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2805a0ade7e9Syasuoka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2806a0ade7e9Syasuoka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2807a0ade7e9Syasuoka  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2808a0ade7e9Syasuoka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2809a0ade7e9Syasuoka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2810a0ade7e9Syasuoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2811a0ade7e9Syasuoka  * SUCH DAMAGE.
2812a0ade7e9Syasuoka  *
2813a0ade7e9Syasuoka  * $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $
2814a0ade7e9Syasuoka  */
2815a0ade7e9Syasuoka #define TCP_OPTLEN_IN_SEGMENT	12	/* timestamp option and padding */
2816a0ade7e9Syasuoka #define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \
2817a0ade7e9Syasuoka     TCP_OPTLEN_IN_SEGMENT)
2818a0ade7e9Syasuoka /*
2819a0ade7e9Syasuoka  * The following macro is used to update an internet checksum.  "acc" is a
2820a0ade7e9Syasuoka  * 32-bit accumulation of all the changes to the checksum (adding in old
2821a0ade7e9Syasuoka  * 16-bit words and subtracting out new words), and "cksum" is the checksum
2822a0ade7e9Syasuoka  * value to be updated.
2823a0ade7e9Syasuoka  */
2824a0ade7e9Syasuoka #define ADJUST_CHECKSUM(acc, cksum) {			\
2825a0ade7e9Syasuoka 	acc += cksum;					\
2826a0ade7e9Syasuoka 	if (acc < 0) {					\
2827a0ade7e9Syasuoka 		acc = -acc;				\
2828a0ade7e9Syasuoka 		acc = (acc >> 16) + (acc & 0xffff);	\
2829a0ade7e9Syasuoka 		acc += acc >> 16;			\
2830a0ade7e9Syasuoka 		cksum = (u_short) ~acc;			\
2831a0ade7e9Syasuoka 	} else {					\
2832a0ade7e9Syasuoka 		acc = (acc >> 16) + (acc & 0xffff);	\
2833a0ade7e9Syasuoka 		acc += acc >> 16;			\
2834a0ade7e9Syasuoka 		cksum = (u_short) acc;			\
2835a0ade7e9Syasuoka 	}						\
2836a0ade7e9Syasuoka }
2837a0ade7e9Syasuoka 
2838a0ade7e9Syasuoka /*
2839a0ade7e9Syasuoka  * Rewrite max-segment-size TCP option to avoid PMTU blackhole issues.
2840a0ade7e9Syasuoka  * The mtu parameter should be the MTU bottleneck (as far as we know)
2841a0ade7e9Syasuoka  * on the link between the source and the destination.
2842a0ade7e9Syasuoka  */
2843c02a3381Smvs struct mbuf *
2844a0ade7e9Syasuoka adjust_tcp_mss(struct mbuf *m0, int mtu)
2845a0ade7e9Syasuoka {
2846a0ade7e9Syasuoka 	int opt, optlen, acc, mss, maxmss, lpktp;
2847a0ade7e9Syasuoka 	struct ip *pip;
2848a0ade7e9Syasuoka 	struct tcphdr *th;
2849a0ade7e9Syasuoka 	u_char *pktp, *mssp;
2850a0ade7e9Syasuoka 	u_int16_t ip_off;
2851a0ade7e9Syasuoka 
2852a0ade7e9Syasuoka 	lpktp = sizeof(struct ip) + sizeof(struct tcphdr) + PIPEX_TCP_OPTLEN;
2853a0ade7e9Syasuoka 	lpktp = MIN(lpktp, m0->m_pkthdr.len);
2854a0ade7e9Syasuoka 
2855a0ade7e9Syasuoka 	PIPEX_PULLUP(m0, lpktp);
2856a0ade7e9Syasuoka 	if (m0 == NULL)
2857a0ade7e9Syasuoka 		goto drop;
2858a0ade7e9Syasuoka 
2859a0ade7e9Syasuoka 	pktp = mtod(m0, char *);
2860a0ade7e9Syasuoka 	pip = (struct ip *)pktp;
2861a0ade7e9Syasuoka 	ip_off = ntohs(pip->ip_off);
2862a0ade7e9Syasuoka 
2863a0ade7e9Syasuoka 	/* Non TCP or fragmented packet must not have a MSS option */
2864a0ade7e9Syasuoka 	if (pip->ip_p != IPPROTO_TCP ||
2865a0ade7e9Syasuoka 	    (ip_off & IP_MF) != 0 || (ip_off & IP_OFFMASK) != 0)
2866a0ade7e9Syasuoka 		goto handled;
2867a0ade7e9Syasuoka 
2868a0ade7e9Syasuoka 	pktp += pip->ip_hl << 2;
2869a0ade7e9Syasuoka 	lpktp -= pip->ip_hl << 2;
2870a0ade7e9Syasuoka 
2871a0ade7e9Syasuoka 	/* packet is broken */
2872a0ade7e9Syasuoka 	if (sizeof(struct tcphdr) > lpktp)
2873a0ade7e9Syasuoka 		goto drop;
2874a0ade7e9Syasuoka 	th = (struct tcphdr *)pktp;
2875a0ade7e9Syasuoka 
2876a0ade7e9Syasuoka 	/*
2877a0ade7e9Syasuoka 	 * As RFC 973, a MSS field must only be sent in the initial
2878a0ade7e9Syasuoka 	 * connection request(it must be with SYN).
2879a0ade7e9Syasuoka 	 */
2880a0ade7e9Syasuoka 	if ((th->th_flags & TH_SYN) == 0)
2881a0ade7e9Syasuoka 		goto handled;
2882a0ade7e9Syasuoka 
2883e8c0e2e5Syasuoka 	lpktp = MIN(th->th_off << 4, lpktp);
2884e8c0e2e5Syasuoka 
2885a0ade7e9Syasuoka 	pktp += sizeof(struct tcphdr);
2886a0ade7e9Syasuoka 	lpktp -= sizeof(struct tcphdr);
2887a0ade7e9Syasuoka 	while (lpktp >= TCPOLEN_MAXSEG) {
2888a0ade7e9Syasuoka 		GETCHAR(opt, pktp);
2889a0ade7e9Syasuoka 		switch (opt) {
2890a0ade7e9Syasuoka 		case TCPOPT_MAXSEG:
2891a0ade7e9Syasuoka 			GETCHAR(optlen, pktp);
2892a0ade7e9Syasuoka 			mssp = pktp;		/* mss place holder */
2893a0ade7e9Syasuoka 			GETSHORT(mss, pktp);
2894a0ade7e9Syasuoka 			maxmss = MAXMSS(mtu);
2895a0ade7e9Syasuoka 			if (mss > maxmss) {
2896a0ade7e9Syasuoka 				PIPEX_DBG((NULL, LOG_DEBUG,
2897a0ade7e9Syasuoka 				    "change tcp-mss %d => %d", mss, maxmss));
2898a0ade7e9Syasuoka 				PUTSHORT(maxmss, mssp);
2899a0ade7e9Syasuoka 				acc = htons(mss);
2900a0ade7e9Syasuoka 				acc -= htons(maxmss);
2901a0ade7e9Syasuoka 				ADJUST_CHECKSUM(acc, th->th_sum);
2902a0ade7e9Syasuoka 			}
2903a0ade7e9Syasuoka 			goto handled;
2904a0ade7e9Syasuoka 			/* NOTREACHED */
2905a0ade7e9Syasuoka 		case TCPOPT_EOL:
2906a0ade7e9Syasuoka 			goto handled;
2907a0ade7e9Syasuoka 			/* NOTREACHED */
2908a0ade7e9Syasuoka 		case TCPOPT_NOP:
2909a0ade7e9Syasuoka 			lpktp--;
2910a0ade7e9Syasuoka 			break;
2911a0ade7e9Syasuoka 		default:
2912a0ade7e9Syasuoka 			GETCHAR(optlen, pktp);
2913e8c0e2e5Syasuoka 			if (optlen < 2)	/* packet is broken */
2914e8c0e2e5Syasuoka 				goto drop;
2915e8c0e2e5Syasuoka 			pktp += optlen - 2;
2916a0ade7e9Syasuoka 			lpktp -= optlen;
2917a0ade7e9Syasuoka 			break;
2918a0ade7e9Syasuoka 		}
2919a0ade7e9Syasuoka 	}
2920a0ade7e9Syasuoka 
2921a0ade7e9Syasuoka handled:
292263d4736dSyasuoka 	return (m0);
2923a0ade7e9Syasuoka 
2924a0ade7e9Syasuoka drop:
2925a0ade7e9Syasuoka 	m_freem(m0);
292663d4736dSyasuoka 	return (NULL);
2927a0ade7e9Syasuoka }
2928a0ade7e9Syasuoka 
2929a0ade7e9Syasuoka /*
2930a0ade7e9Syasuoka  *  Check whether a packet should reset idle timer
2931a0ade7e9Syasuoka  *  Returns 1 to don't reset timer (i.e. the packet is "idle" packet)
2932a0ade7e9Syasuoka  */
2933c02a3381Smvs struct mbuf *
2934a0ade7e9Syasuoka ip_is_idle_packet(struct mbuf *m0, int *ris_idle)
2935a0ade7e9Syasuoka {
2936a0ade7e9Syasuoka 	u_int16_t ip_off;
2937a0ade7e9Syasuoka 	const struct udphdr *uh;
2938a0ade7e9Syasuoka 	struct ip *pip;
2939a0ade7e9Syasuoka 	int len;
2940a0ade7e9Syasuoka 
2941a0ade7e9Syasuoka 	/* pullup ip header */
2942a0ade7e9Syasuoka 	len = sizeof(struct ip);
2943a0ade7e9Syasuoka 	PIPEX_PULLUP(m0, len);
2944a0ade7e9Syasuoka 	if (m0 == NULL)
2945a0ade7e9Syasuoka 		goto error;
2946a0ade7e9Syasuoka 	pip = mtod(m0, struct ip *);
2947a0ade7e9Syasuoka 
2948a0ade7e9Syasuoka 	/*
2949a0ade7e9Syasuoka 	 * the packet which fragmentations was not the idle packet.
2950a0ade7e9Syasuoka 	 */
2951a0ade7e9Syasuoka 	ip_off = ntohs(pip->ip_off);
2952a0ade7e9Syasuoka 	if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0))
2953a0ade7e9Syasuoka 		goto is_active;
2954a0ade7e9Syasuoka 
2955a0ade7e9Syasuoka 	switch (pip->ip_p) {
2956a0ade7e9Syasuoka 	case IPPROTO_IGMP:
2957a0ade7e9Syasuoka 		goto is_active;
2958a0ade7e9Syasuoka 	case IPPROTO_ICMP:
2959a0ade7e9Syasuoka 		len = pip->ip_hl * 4 + 8;
2960a0ade7e9Syasuoka 		PIPEX_PULLUP(m0, len);
2961a0ade7e9Syasuoka 		if (m0 == NULL)
2962a0ade7e9Syasuoka 			goto error;
2963581bf0faSyasuoka 		pip = mtod(m0, struct ip *);
2964a0ade7e9Syasuoka 
2965a0ade7e9Syasuoka 		switch (((unsigned char *) pip)[pip->ip_hl * 4]) {
2966a0ade7e9Syasuoka 		case 0:	/* Echo Reply */
2967a0ade7e9Syasuoka 		case 8:	/* Echo Request */
2968a0ade7e9Syasuoka 			goto is_active;
2969a0ade7e9Syasuoka 		default:
2970a0ade7e9Syasuoka 			goto is_idle;
2971a0ade7e9Syasuoka 		}
2972a0ade7e9Syasuoka 
2973a0ade7e9Syasuoka 	case IPPROTO_UDP:
2974a0ade7e9Syasuoka 	case IPPROTO_TCP:
2975a0ade7e9Syasuoka 		len = pip->ip_hl * 4 + sizeof(struct udphdr);
2976a0ade7e9Syasuoka 		PIPEX_PULLUP(m0, len);
2977a0ade7e9Syasuoka 		if (m0 == NULL)
2978a0ade7e9Syasuoka 			goto error;
2979581bf0faSyasuoka 		pip = mtod(m0, struct ip *);
2980581bf0faSyasuoka 		uh = (struct udphdr *)(mtod(m0, caddr_t) + pip->ip_hl * 4);
2981a0ade7e9Syasuoka 
2982a0ade7e9Syasuoka 		switch (ntohs(uh->uh_sport)) {
2983a0ade7e9Syasuoka 		case 53:	/* DOMAIN */
2984a0ade7e9Syasuoka 		case 67:	/* BOOTPS */
2985a0ade7e9Syasuoka 		case 68:	/* BOOTPC */
2986a0ade7e9Syasuoka 		case 123:	/* NTP */
2987a0ade7e9Syasuoka 		case 137:	/* NETBIOS-NS */
2988a0ade7e9Syasuoka 		case 520:	/* RIP */
2989a0ade7e9Syasuoka 			goto is_idle;
2990a0ade7e9Syasuoka 		}
2991a0ade7e9Syasuoka 		switch (ntohs(uh->uh_dport)) {
2992a0ade7e9Syasuoka 		case 53:	/* DOMAIN */
2993a0ade7e9Syasuoka 		case 67:	/* BOOTPS */
2994a0ade7e9Syasuoka 		case 68:	/* BOOTPC */
2995a0ade7e9Syasuoka 		case 123:	/* NTP */
2996a0ade7e9Syasuoka 		case 137:	/* NETBIOS-NS */
2997a0ade7e9Syasuoka 		case 520:	/* RIP */
2998a0ade7e9Syasuoka 			goto is_idle;
2999a0ade7e9Syasuoka 		}
3000a0ade7e9Syasuoka 		goto is_active;
3001a0ade7e9Syasuoka 	default:
3002a0ade7e9Syasuoka 		goto is_active;
3003a0ade7e9Syasuoka 	}
3004a0ade7e9Syasuoka 
3005a0ade7e9Syasuoka is_active:
3006a0ade7e9Syasuoka 	*ris_idle = 0;
300763d4736dSyasuoka 	return (m0);
3008a0ade7e9Syasuoka 
3009a0ade7e9Syasuoka is_idle:
3010a0ade7e9Syasuoka 	*ris_idle = 1;
301163d4736dSyasuoka 	return (m0);
3012a0ade7e9Syasuoka 
3013a0ade7e9Syasuoka error:
301463d4736dSyasuoka 	return (NULL);
3015a0ade7e9Syasuoka }
3016a0ade7e9Syasuoka 
3017c02a3381Smvs void
3018a0ade7e9Syasuoka pipex_session_log(struct pipex_session *session, int prio, const char *fmt, ...)
3019a0ade7e9Syasuoka {
3020a0ade7e9Syasuoka 	char logbuf[1024];
3021a0ade7e9Syasuoka 	va_list ap;
3022a0ade7e9Syasuoka 
3023a0ade7e9Syasuoka 	logpri(prio);
3024a0ade7e9Syasuoka 	if (session != NULL) {
3025002baba0Smvs 		struct ifnet *ifp;
3026002baba0Smvs 
3027002baba0Smvs 		ifp = if_get(session->ifindex);
3028a0ade7e9Syasuoka 		addlog("pipex: ppp=%d iface=%s protocol=%s id=%d ",
3029002baba0Smvs 		    session->ppp_id,
3030002baba0Smvs 		    ifp? ifp->if_xname : "Unknown",
3031a0ade7e9Syasuoka 		    (session->protocol == PIPEX_PROTO_PPPOE)? "PPPoE" :
30328876230bShsuenaga 		    (session->protocol == PIPEX_PROTO_PPTP)? "PPTP" :
30338876230bShsuenaga 		    (session->protocol == PIPEX_PROTO_L2TP) ? "L2TP" :
30348876230bShsuenaga 		    "Unknown", session->session_id);
3035002baba0Smvs 		if_put(ifp);
3036a0ade7e9Syasuoka 	} else
3037a0ade7e9Syasuoka 		addlog("pipex: ");
3038a0ade7e9Syasuoka 
3039a0ade7e9Syasuoka 	va_start(ap, fmt);
3040a0ade7e9Syasuoka 	vsnprintf(logbuf, sizeof(logbuf), fmt, ap);
3041a0ade7e9Syasuoka 	va_end(ap);
3042a0ade7e9Syasuoka 	addlog("%s\n", logbuf);
3043a0ade7e9Syasuoka }
30448876230bShsuenaga 
3045c02a3381Smvs uint32_t
30468876230bShsuenaga pipex_sockaddr_hash_key(struct sockaddr *sa)
30478876230bShsuenaga {
30488876230bShsuenaga 	switch (sa->sa_family) {
30498876230bShsuenaga 	case AF_INET:
30503e303383Sbluhm 		return ntohl(satosin(sa)->sin_addr.s_addr);
30518876230bShsuenaga 	case AF_INET6:
30523e303383Sbluhm 		return ntohl(satosin6(sa)->sin6_addr.s6_addr32[3]);
30538876230bShsuenaga 	}
30548876230bShsuenaga 	panic("pipex_sockaddr_hash_key: unknown address family");
3055503a8a27Syasuoka 	return (0);
30568876230bShsuenaga }
30578876230bShsuenaga 
30588876230bShsuenaga /*
30598876230bShsuenaga  * Compare struct sockaddr_in{,6} with the address only.
30608876230bShsuenaga  * The port number is not covered.
30618876230bShsuenaga  */
3062c02a3381Smvs int
30638876230bShsuenaga pipex_sockaddr_compar_addr(struct sockaddr *a, struct sockaddr *b)
30648876230bShsuenaga {
30658876230bShsuenaga 	int cmp;
30668876230bShsuenaga 
30678876230bShsuenaga 	cmp = b->sa_family - a->sa_family;
30688876230bShsuenaga 	if (cmp != 0)
30698876230bShsuenaga 		return cmp;
30708876230bShsuenaga 	switch (a->sa_family) {
30718876230bShsuenaga 	case AF_INET:
30723e303383Sbluhm 		return (satosin(b)->sin_addr.s_addr -
30733e303383Sbluhm 		    satosin(a)->sin_addr.s_addr);
30748876230bShsuenaga 	case AF_INET6:
30753e303383Sbluhm 		cmp = (satosin6(b)->sin6_scope_id - satosin6(a)->sin6_scope_id);
30768876230bShsuenaga 		if (cmp != 0)
30778876230bShsuenaga 			return cmp;
30783e303383Sbluhm 		return (memcmp(&satosin6(a)->sin6_addr,
30793e303383Sbluhm 		    &satosin6(b)->sin6_addr,
30803e303383Sbluhm 		    sizeof(struct in6_addr)));
30818876230bShsuenaga 	}
30828876230bShsuenaga 	panic("pipex_sockaddr_compar_addr: unknown address family");
3083e405d423Syasuoka 
3084e405d423Syasuoka 	return (-1);
3085e405d423Syasuoka }
3086e405d423Syasuoka 
308790b03482Syasuoka int
308890b03482Syasuoka pipex_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
308990b03482Syasuoka     size_t newlen)
309090b03482Syasuoka {
309190b03482Syasuoka 	switch (name[0]) {
309290b03482Syasuoka 	case PIPEXCTL_ENABLE:
30939326fce4Syasuoka 		if (namelen != 1)
30949326fce4Syasuoka 			return (ENOTDIR);
30959e5eed67Sgnezdo 		return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
30969e5eed67Sgnezdo 		    &pipex_enable, 0, 1));
309790b03482Syasuoka 	default:
309890b03482Syasuoka 		return (ENOPROTOOPT);
309990b03482Syasuoka 	}
310090b03482Syasuoka 	/* NOTREACHED */
310190b03482Syasuoka }
3102