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