1*f168c03cSyasuoka /* $OpenBSD: pipex.c,v 1.157 2025/01/25 02:06:40 yasuoka Exp $ */ 24bda48b6Syasuoka 3a0ade7e9Syasuoka /*- 4a0ade7e9Syasuoka * Copyright (c) 2009 Internet Initiative Japan Inc. 5a0ade7e9Syasuoka * All rights reserved. 6a0ade7e9Syasuoka * 7a0ade7e9Syasuoka * Redistribution and use in source and binary forms, with or without 8a0ade7e9Syasuoka * modification, are permitted provided that the following conditions 9a0ade7e9Syasuoka * are met: 10a0ade7e9Syasuoka * 1. Redistributions of source code must retain the above copyright 11a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer. 12a0ade7e9Syasuoka * 2. Redistributions in binary form must reproduce the above copyright 13a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer in the 14a0ade7e9Syasuoka * documentation and/or other materials provided with the distribution. 15a0ade7e9Syasuoka * 16a0ade7e9Syasuoka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17a0ade7e9Syasuoka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18a0ade7e9Syasuoka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19a0ade7e9Syasuoka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20a0ade7e9Syasuoka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21a0ade7e9Syasuoka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22a0ade7e9Syasuoka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23a0ade7e9Syasuoka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24a0ade7e9Syasuoka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25a0ade7e9Syasuoka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26a0ade7e9Syasuoka * SUCH DAMAGE. 27a0ade7e9Syasuoka */ 28a0ade7e9Syasuoka 29a0ade7e9Syasuoka #include <sys/param.h> 30a0ade7e9Syasuoka #include <sys/systm.h> 31a0ade7e9Syasuoka #include <sys/mbuf.h> 32a0ade7e9Syasuoka #include <sys/socket.h> 33a0ade7e9Syasuoka #include <sys/ioctl.h> 3490b03482Syasuoka #include <sys/sysctl.h> 35a0ade7e9Syasuoka #include <sys/syslog.h> 36a0ade7e9Syasuoka #include <sys/conf.h> 37a0ade7e9Syasuoka #include <sys/time.h> 38638c25e3Stedu #include <sys/timeout.h> 39a0ade7e9Syasuoka #include <sys/kernel.h> 40edccd885Suebayasi #include <sys/pool.h> 41df21f681Smvs #include <sys/percpu.h> 42f6e6f8e7Smvs #include <sys/mutex.h> 43a0ade7e9Syasuoka 44a0ade7e9Syasuoka #include <net/if.h> 458876230bShsuenaga #include <net/if_types.h> 46a0ade7e9Syasuoka #include <netinet/in.h> 47a0ade7e9Syasuoka #include <netinet/if_ether.h> 488876230bShsuenaga #include <net/if_dl.h> 49a0ade7e9Syasuoka 50a0ade7e9Syasuoka #include <net/radix.h> 51a0ade7e9Syasuoka #include <net/route.h> 52a0ade7e9Syasuoka #include <net/ppp_defs.h> 53a0ade7e9Syasuoka #include <net/ppp-comp.h> 540cd5c6faSmvs #include <net/netisr.h> 55a0ade7e9Syasuoka 5685d4897fSmarkus #include "pf.h" 5785d4897fSmarkus #if NPF > 0 5885d4897fSmarkus #include <net/pfvar.h> 5985d4897fSmarkus #endif 6085d4897fSmarkus 61a0ade7e9Syasuoka #include "bpfilter.h" 62a0ade7e9Syasuoka #if NBPFILTER > 0 63a0ade7e9Syasuoka #include <net/bpf.h> 64a0ade7e9Syasuoka #endif 65a0ade7e9Syasuoka 66a0ade7e9Syasuoka #include <netinet/ip.h> 67a0ade7e9Syasuoka #include <netinet/ip_var.h> 688876230bShsuenaga #ifdef INET6 698876230bShsuenaga #include <netinet/ip6.h> 708876230bShsuenaga #include <netinet6/ip6_var.h> 718876230bShsuenaga #endif 72a0ade7e9Syasuoka #include <netinet/tcp.h> 73a0ade7e9Syasuoka #include <netinet/udp.h> 748876230bShsuenaga #include <netinet/udp_var.h> 7563d4736dSyasuoka #include <crypto/arc4.h> 7663d4736dSyasuoka 77a0ade7e9Syasuoka #include <net/pipex.h> 78a0ade7e9Syasuoka #include "pipex_local.h" 79a0ade7e9Syasuoka 80f6e6f8e7Smvs struct mutex pipex_list_mtx = MUTEX_INITIALIZER(IPL_SOFTNET); 81f6e6f8e7Smvs 82edccd885Suebayasi struct pool pipex_session_pool; 83edccd885Suebayasi struct pool mppe_key_pool; 84edccd885Suebayasi 85a0ade7e9Syasuoka /* 8675e871e2Smvs * Global data 8775e871e2Smvs * Locks used to protect global data 8875e871e2Smvs * A atomic operation 8975e871e2Smvs * I immutable after creation 90f6e6f8e7Smvs * L pipex_list_mtx 91a0ade7e9Syasuoka */ 9268b260a2Sclaudio 93317fc6b7Smvs int pipex_enable = 0; /* [A] */ 9475e871e2Smvs struct pipex_hash_head 95f6e6f8e7Smvs pipex_session_list, /* [L] master session list */ 96f6e6f8e7Smvs pipex_close_wait_list, /* [L] expired session list */ 97f6e6f8e7Smvs pipex_peer_addr_hashtable[PIPEX_HASH_SIZE], /* [L] peer's address hash */ 98f6e6f8e7Smvs pipex_id_hashtable[PIPEX_HASH_SIZE]; /* [L] peer id hash */ 9975e871e2Smvs 100f6e6f8e7Smvs struct radix_node_head *pipex_rd_head4 = NULL; /* [L] */ 10168b260a2Sclaudio struct timeout pipex_timer_ch; /* callout timer context */ 10275e871e2Smvs int pipex_prune = 1; /* [I] walk list every seconds */ 103a0ade7e9Syasuoka 1040cd5c6faSmvs struct mbuf_queue pipexoutq = MBUF_QUEUE_INITIALIZER( 1050cd5c6faSmvs IFQ_MAXLEN, IPL_SOFTNET); 1060cd5c6faSmvs 107a0ade7e9Syasuoka #ifdef PIPEX_DEBUG 10875e871e2Smvs int pipex_debug = 0; /* [A] systcl net.inet.ip.pipex_debug */ 109a0ade7e9Syasuoka #endif 110a0ade7e9Syasuoka 111a0ade7e9Syasuoka /* PPP compression == MPPE is assumed, so don't answer CCP Reset-Request. */ 112a0ade7e9Syasuoka #define PIPEX_NO_CCP_RESETACK 1 113a0ade7e9Syasuoka 114a0ade7e9Syasuoka /************************************************************************ 115a0ade7e9Syasuoka * Core functions 116a0ade7e9Syasuoka ************************************************************************/ 117a0ade7e9Syasuoka void 118a0ade7e9Syasuoka pipex_init(void) 119a0ade7e9Syasuoka { 1202a747eefSyasuoka int i; 121f930cf62Syasuoka static int pipex_init_done = 0; 122a0ade7e9Syasuoka 123f930cf62Syasuoka if (pipex_init_done++) 124d3be6ca9Sdlg return; 125d3be6ca9Sdlg 12698afcbd2Smpi rn_init(sizeof(struct sockaddr_in6)); 12798afcbd2Smpi 1281378bae2Sdlg pool_init(&pipex_session_pool, sizeof(struct pipex_session), 0, 129b450e1dfSmpi IPL_SOFTNET, PR_WAITOK, "ppxss", NULL); 1301378bae2Sdlg pool_init(&mppe_key_pool, PIPEX_MPPE_KEYLEN * PIPEX_MPPE_NOLDKEY, 0, 131b450e1dfSmpi IPL_SOFTNET, PR_WAITOK, "mppekey", NULL); 132edccd885Suebayasi 133a0ade7e9Syasuoka LIST_INIT(&pipex_session_list); 1348876230bShsuenaga LIST_INIT(&pipex_close_wait_list); 135a0ade7e9Syasuoka 1362a747eefSyasuoka for (i = 0; i < nitems(pipex_id_hashtable); i++) 1372a747eefSyasuoka LIST_INIT(&pipex_id_hashtable[i]); 1382a747eefSyasuoka for (i = 0; i < nitems(pipex_peer_addr_hashtable); i++) 1392a747eefSyasuoka LIST_INIT(&pipex_peer_addr_hashtable[i]); 140a0ade7e9Syasuoka } 141a0ade7e9Syasuoka 142a0ade7e9Syasuoka void 143bc65dfeeSyasuoka pipex_destroy_all_sessions(void *ownersc) 144a0ade7e9Syasuoka { 145a672ee3dSclaudio struct pipex_session *session, *session_tmp; 146a0ade7e9Syasuoka 147f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 148bc65dfeeSyasuoka 149a672ee3dSclaudio LIST_FOREACH_SAFE(session, &pipex_session_list, session_list, 150a672ee3dSclaudio session_tmp) { 151aa95fb55Smvs if (session->flags & PIPEX_SFLAGS_ITERATOR) 152aa95fb55Smvs continue; 153bc65dfeeSyasuoka if (session->ownersc == ownersc) { 1541935ef3dSmvs KASSERT((session->flags & PIPEX_SFLAGS_PPPX) == 0); 155f6e6f8e7Smvs pipex_unlink_session_locked(session); 156bc65dfeeSyasuoka pipex_rele_session(session); 157a0ade7e9Syasuoka } 158116d48bfSyasuoka } 159f6e6f8e7Smvs 160f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 161a0ade7e9Syasuoka } 162a0ade7e9Syasuoka 163a0ade7e9Syasuoka int 164bc65dfeeSyasuoka pipex_ioctl(void *ownersc, u_long cmd, caddr_t data) 165a0ade7e9Syasuoka { 166bc65dfeeSyasuoka int ret = 0; 167a0ade7e9Syasuoka 168a0ade7e9Syasuoka switch (cmd) { 169a0ade7e9Syasuoka case PIPEXGSTAT: 170b9b0ec53Sclaudio ret = pipex_get_stat((struct pipex_session_stat_req *)data, 171bc65dfeeSyasuoka ownersc); 172f930cf62Syasuoka break; 173a0ade7e9Syasuoka 174a0ade7e9Syasuoka case PIPEXGCLOSED: 175b9b0ec53Sclaudio ret = pipex_get_closed((struct pipex_session_list_req *)data, 176bc65dfeeSyasuoka ownersc); 177f930cf62Syasuoka break; 178a0ade7e9Syasuoka 179a0ade7e9Syasuoka default: 180f930cf62Syasuoka ret = ENOTTY; 181f930cf62Syasuoka break; 182a0ade7e9Syasuoka } 183f930cf62Syasuoka 184f930cf62Syasuoka return (ret); 185a0ade7e9Syasuoka } 186a0ade7e9Syasuoka 187a0ade7e9Syasuoka /************************************************************************ 1880cd5c6faSmvs * Software Interrupt Handler 1890cd5c6faSmvs ************************************************************************/ 1900cd5c6faSmvs 1910cd5c6faSmvs void 1920cd5c6faSmvs pipexintr(void) 1930cd5c6faSmvs { 1940cd5c6faSmvs struct mbuf_list ml; 1950cd5c6faSmvs struct mbuf *m; 1960cd5c6faSmvs struct pipex_session *session; 1970cd5c6faSmvs 1980cd5c6faSmvs NET_ASSERT_LOCKED(); 1990cd5c6faSmvs 2000cd5c6faSmvs mq_delist(&pipexoutq, &ml); 2010cd5c6faSmvs 2020cd5c6faSmvs while ((m = ml_dequeue(&ml)) != NULL) { 2030cd5c6faSmvs struct ifnet *ifp; 2040cd5c6faSmvs 2050cd5c6faSmvs session = m->m_pkthdr.ph_cookie; 2060cd5c6faSmvs 2070cd5c6faSmvs ifp = if_get(session->proto.pppoe.over_ifidx); 2080cd5c6faSmvs if (ifp != NULL) { 2090cd5c6faSmvs struct pipex_pppoe_header *pppoe; 2100cd5c6faSmvs int len; 2110cd5c6faSmvs 2120cd5c6faSmvs pppoe = mtod(m, struct pipex_pppoe_header *); 2130cd5c6faSmvs len = ntohs(pppoe->length); 2140cd5c6faSmvs ifp->if_output(ifp, m, &session->peer.sa, NULL); 2150cd5c6faSmvs counters_pkt(session->stat_counters, pxc_opackets, 2160cd5c6faSmvs pxc_obytes, len); 2170cd5c6faSmvs } else { 2180cd5c6faSmvs m_freem(m); 2190cd5c6faSmvs counters_inc(session->stat_counters, pxc_oerrors); 2200cd5c6faSmvs } 2210cd5c6faSmvs if_put(ifp); 2220cd5c6faSmvs 2230cd5c6faSmvs pipex_rele_session(session); 2240cd5c6faSmvs } 2250cd5c6faSmvs } 2260cd5c6faSmvs 2270cd5c6faSmvs /************************************************************************ 228a0ade7e9Syasuoka * Session management functions 229a0ade7e9Syasuoka ************************************************************************/ 230d70a8765Smvs int 231d70a8765Smvs pipex_init_session(struct pipex_session **rsession, 232d70a8765Smvs struct pipex_session_req *req) 233a0ade7e9Syasuoka { 234a0ade7e9Syasuoka struct pipex_session *session; 235a0ade7e9Syasuoka #ifdef PIPEX_PPPOE 236a0ade7e9Syasuoka struct ifnet *over_ifp = NULL; 237a0ade7e9Syasuoka #endif 238a0ade7e9Syasuoka 239df8d9afdSjsg /* Checks requested parameters. */ 240a0ade7e9Syasuoka switch (req->pr_protocol) { 241a0ade7e9Syasuoka #ifdef PIPEX_PPPOE 242a0ade7e9Syasuoka case PIPEX_PROTO_PPPOE: 243bdfe8fd0Syasuoka if (req->pr_peer_address.ss_family != AF_UNSPEC) 2448876230bShsuenaga return (EINVAL); 245a0ade7e9Syasuoka break; 246a0ade7e9Syasuoka #endif 2478876230bShsuenaga #if defined(PIPEX_L2TP) || defined(PIPEX_PPTP) 248a0ade7e9Syasuoka case PIPEX_PROTO_PPTP: 2498876230bShsuenaga case PIPEX_PROTO_L2TP: 250bdfe8fd0Syasuoka switch (req->pr_peer_address.ss_family) { 2518876230bShsuenaga case AF_INET: 252bdfe8fd0Syasuoka if (req->pr_peer_address.ss_len != 253bdfe8fd0Syasuoka sizeof(struct sockaddr_in)) 2548876230bShsuenaga return (EINVAL); 2558876230bShsuenaga break; 256dcc75356Sdlg #ifdef INET6 257dcc75356Sdlg case AF_INET6: 258bdfe8fd0Syasuoka if (req->pr_peer_address.ss_len != 259bdfe8fd0Syasuoka sizeof(struct sockaddr_in6)) 260dcc75356Sdlg return (EINVAL); 261dcc75356Sdlg break; 262dcc75356Sdlg #endif 2638876230bShsuenaga default: 2648876230bShsuenaga return (EPROTONOSUPPORT); 2658876230bShsuenaga } 266bdfe8fd0Syasuoka if (req->pr_peer_address.ss_family != 267bdfe8fd0Syasuoka req->pr_local_address.ss_family || 268bdfe8fd0Syasuoka req->pr_peer_address.ss_len != 269bdfe8fd0Syasuoka req->pr_local_address.ss_len) 270dcc75356Sdlg return (EINVAL); 271a0ade7e9Syasuoka break; 27294a607b5Smpi #endif /* defined(PIPEX_PPTP) || defined(PIPEX_L2TP) */ 273a0ade7e9Syasuoka default: 27463d4736dSyasuoka return (EPROTONOSUPPORT); 275a0ade7e9Syasuoka } 276d70a8765Smvs #ifdef PIPEX_MPPE 277d70a8765Smvs if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) { 278b18f9d9cSbluhm switch (req->pr_mppe_recv.keylenbits) { 279b18f9d9cSbluhm case 40: 280b18f9d9cSbluhm case 56: 281b18f9d9cSbluhm case 128: 282b18f9d9cSbluhm break; 283b18f9d9cSbluhm default: 284d70a8765Smvs return (EINVAL); 285d70a8765Smvs } 286b18f9d9cSbluhm } 287d70a8765Smvs if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) { 288b18f9d9cSbluhm switch (req->pr_mppe_send.keylenbits) { 289b18f9d9cSbluhm case 40: 290b18f9d9cSbluhm case 56: 291b18f9d9cSbluhm case 128: 292b18f9d9cSbluhm break; 293b18f9d9cSbluhm default: 294d70a8765Smvs return (EINVAL); 295d70a8765Smvs } 296b18f9d9cSbluhm } 297d70a8765Smvs if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_REQUIRED) != 0) { 298d70a8765Smvs if ((req->pr_ppp_flags & 299d70a8765Smvs (PIPEX_PPP_MPPE_ACCEPTED | PIPEX_PPP_MPPE_ENABLED)) != 300d70a8765Smvs (PIPEX_PPP_MPPE_ACCEPTED | PIPEX_PPP_MPPE_ENABLED)) 301d70a8765Smvs return (EINVAL); 302d70a8765Smvs } 303d70a8765Smvs #endif 304972a3eacSclaudio 3052f7f6135Smvs #ifdef PIPEX_PPPOE 3062f7f6135Smvs if (req->pr_protocol == PIPEX_PROTO_PPPOE) { 3072f7f6135Smvs over_ifp = if_unit(req->pr_proto.pppoe.over_ifname); 3082f7f6135Smvs if (over_ifp == NULL) 3092f7f6135Smvs return (EINVAL); 3102f7f6135Smvs } 3112f7f6135Smvs #endif 3122f7f6135Smvs 313a0ade7e9Syasuoka /* prepare a new session */ 314edccd885Suebayasi session = pool_get(&pipex_session_pool, PR_WAITOK | PR_ZERO); 315f6e6f8e7Smvs refcnt_init(&session->pxs_refcnt); 3162960d3c8Smvs mtx_init(&session->pxs_mtx, IPL_SOFTNET); 317d70a8765Smvs session->state = PIPEX_STATE_INITIAL; 318a0ade7e9Syasuoka session->protocol = req->pr_protocol; 319a0ade7e9Syasuoka session->session_id = req->pr_session_id; 320a0ade7e9Syasuoka session->peer_session_id = req->pr_peer_session_id; 321a0ade7e9Syasuoka session->peer_mru = req->pr_peer_mru; 322a0ade7e9Syasuoka session->timeout_sec = req->pr_timeout_sec; 323a0ade7e9Syasuoka session->ppp_flags = req->pr_ppp_flags; 324a0ade7e9Syasuoka session->ppp_id = req->pr_ppp_id; 325a0ade7e9Syasuoka 326df21f681Smvs session->stat_counters = counters_alloc(pxc_ncounters); 327df21f681Smvs 328a0ade7e9Syasuoka session->ip_address.sin_family = AF_INET; 329a0ade7e9Syasuoka session->ip_address.sin_len = sizeof(struct sockaddr_in); 330a0ade7e9Syasuoka session->ip_address.sin_addr = req->pr_ip_address; 331a0ade7e9Syasuoka 332a0ade7e9Syasuoka session->ip_netmask.sin_family = AF_INET; 333a0ade7e9Syasuoka session->ip_netmask.sin_len = sizeof(struct sockaddr_in); 334a0ade7e9Syasuoka session->ip_netmask.sin_addr = req->pr_ip_netmask; 335a0ade7e9Syasuoka 336a0ade7e9Syasuoka if (session->ip_netmask.sin_addr.s_addr == 0L) 337a0ade7e9Syasuoka session->ip_netmask.sin_addr.s_addr = 0xffffffffL; 338a0ade7e9Syasuoka session->ip_address.sin_addr.s_addr &= 339a0ade7e9Syasuoka session->ip_netmask.sin_addr.s_addr; 340a0ade7e9Syasuoka 341bdfe8fd0Syasuoka if (req->pr_peer_address.ss_len > 0) 342bdfe8fd0Syasuoka memcpy(&session->peer, &req->pr_peer_address, 343bdfe8fd0Syasuoka MIN(req->pr_peer_address.ss_len, sizeof(session->peer))); 344bdfe8fd0Syasuoka if (req->pr_local_address.ss_len > 0) 345bdfe8fd0Syasuoka memcpy(&session->local, &req->pr_local_address, 346bdfe8fd0Syasuoka MIN(req->pr_local_address.ss_len, sizeof(session->local))); 347a0ade7e9Syasuoka #ifdef PIPEX_PPPOE 3482f7f6135Smvs if (req->pr_protocol == PIPEX_PROTO_PPPOE) { 3497790b521Syasuoka session->proto.pppoe.over_ifidx = over_ifp->if_index; 3502f7f6135Smvs if_put(over_ifp); 3512f7f6135Smvs } 352a0ade7e9Syasuoka #endif 353a0ade7e9Syasuoka #ifdef PIPEX_PPTP 354a0ade7e9Syasuoka if (req->pr_protocol == PIPEX_PROTO_PPTP) { 3558876230bShsuenaga struct pipex_pptp_session *sess_pptp = &session->proto.pptp; 3568876230bShsuenaga 3578876230bShsuenaga sess_pptp->snd_gap = 0; 3588876230bShsuenaga sess_pptp->rcv_gap = 0; 3598876230bShsuenaga sess_pptp->snd_una = req->pr_proto.pptp.snd_una; 3608876230bShsuenaga sess_pptp->snd_nxt = req->pr_proto.pptp.snd_nxt; 3618876230bShsuenaga sess_pptp->rcv_nxt = req->pr_proto.pptp.rcv_nxt; 3628876230bShsuenaga sess_pptp->rcv_acked = req->pr_proto.pptp.rcv_acked; 3638876230bShsuenaga 3648876230bShsuenaga sess_pptp->winsz = req->pr_proto.pptp.winsz; 3658876230bShsuenaga sess_pptp->maxwinsz = req->pr_proto.pptp.maxwinsz; 3668876230bShsuenaga sess_pptp->peer_maxwinsz = req->pr_proto.pptp.peer_maxwinsz; 3678876230bShsuenaga /* last ack number */ 3688876230bShsuenaga sess_pptp->ul_snd_una = sess_pptp->snd_una - 1; 3698876230bShsuenaga } 3708876230bShsuenaga #endif 3718876230bShsuenaga #ifdef PIPEX_L2TP 3728876230bShsuenaga if (req->pr_protocol == PIPEX_PROTO_L2TP) { 3738876230bShsuenaga struct pipex_l2tp_session *sess_l2tp = &session->proto.l2tp; 3748876230bShsuenaga 3758876230bShsuenaga /* session keys */ 3768876230bShsuenaga sess_l2tp->tunnel_id = req->pr_proto.l2tp.tunnel_id; 3778876230bShsuenaga sess_l2tp->peer_tunnel_id = req->pr_proto.l2tp.peer_tunnel_id; 3788876230bShsuenaga 3798876230bShsuenaga /* protocol options */ 3808876230bShsuenaga sess_l2tp->option_flags = req->pr_proto.l2tp.option_flags; 3818876230bShsuenaga 3828876230bShsuenaga /* initial state of dynamic context */ 3838876230bShsuenaga sess_l2tp->ns_gap = sess_l2tp->nr_gap = 0; 3848876230bShsuenaga sess_l2tp->ns_nxt = req->pr_proto.l2tp.ns_nxt; 3858876230bShsuenaga sess_l2tp->nr_nxt = req->pr_proto.l2tp.nr_nxt; 3868876230bShsuenaga sess_l2tp->ns_una = req->pr_proto.l2tp.ns_una; 3878876230bShsuenaga sess_l2tp->nr_acked = req->pr_proto.l2tp.nr_acked; 3888876230bShsuenaga /* last ack number */ 3898876230bShsuenaga sess_l2tp->ul_ns_una = sess_l2tp->ns_una - 1; 390a5018c90Syasuoka sess_l2tp->ipsecflowinfo = req->pr_proto.l2tp.ipsecflowinfo; 391a0ade7e9Syasuoka } 392a0ade7e9Syasuoka #endif 393a0ade7e9Syasuoka #ifdef PIPEX_MPPE 394de0a2dd6Syasuoka if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) { 395e405d423Syasuoka pipex_session_init_mppe_recv(session, 396e405d423Syasuoka req->pr_mppe_recv.stateless, req->pr_mppe_recv.keylenbits, 397e405d423Syasuoka req->pr_mppe_recv.master_key); 398de0a2dd6Syasuoka } 399de0a2dd6Syasuoka if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) { 400e405d423Syasuoka pipex_session_init_mppe_send(session, 401e405d423Syasuoka req->pr_mppe_send.stateless, req->pr_mppe_send.keylenbits, 402e405d423Syasuoka req->pr_mppe_send.master_key); 403de0a2dd6Syasuoka } 404a0ade7e9Syasuoka #endif 405a0ade7e9Syasuoka 406d70a8765Smvs *rsession = session; 407d70a8765Smvs 408d70a8765Smvs return 0; 409a0ade7e9Syasuoka } 410a0ade7e9Syasuoka 411d70a8765Smvs void 412d70a8765Smvs pipex_rele_session(struct pipex_session *session) 413d70a8765Smvs { 414f6e6f8e7Smvs if (refcnt_rele(&session->pxs_refcnt) == 0) 415f6e6f8e7Smvs return; 416f6e6f8e7Smvs 417d70a8765Smvs if (session->mppe_recv.old_session_keys) 418d70a8765Smvs pool_put(&mppe_key_pool, session->mppe_recv.old_session_keys); 419df21f681Smvs counters_free(session->stat_counters, pxc_ncounters); 420edccd885Suebayasi pool_put(&pipex_session_pool, session); 4218876230bShsuenaga } 422a0ade7e9Syasuoka 423d70a8765Smvs int 424bc65dfeeSyasuoka pipex_link_session(struct pipex_session *session, struct ifnet *ifp, 425bc65dfeeSyasuoka void *ownersc) 426d70a8765Smvs { 427d70a8765Smvs struct pipex_hash_head *chain; 428bc65dfeeSyasuoka struct radix_node *rn; 429f6e6f8e7Smvs int error = 0; 430d70a8765Smvs 431f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 43275e871e2Smvs 433bc65dfeeSyasuoka if (pipex_rd_head4 == NULL) { 434bc65dfeeSyasuoka if (!rn_inithead((void **)&pipex_rd_head4, 435bc65dfeeSyasuoka offsetof(struct sockaddr_in, sin_addr))) 436bc65dfeeSyasuoka panic("rn_inithead() failed on pipex_link_session()"); 437bc65dfeeSyasuoka } 438f6e6f8e7Smvs if (pipex_lookup_by_session_id_locked(session->protocol, 439f6e6f8e7Smvs session->session_id)) { 440f6e6f8e7Smvs error = EEXIST; 441f6e6f8e7Smvs goto out; 442f6e6f8e7Smvs } 443d70a8765Smvs 444bc65dfeeSyasuoka session->ownersc = ownersc; 445bc65dfeeSyasuoka session->ifindex = ifp->if_index; 446bc65dfeeSyasuoka if (ifp->if_flags & IFF_POINTOPOINT) 4471935ef3dSmvs session->flags |= PIPEX_SFLAGS_PPPX; 448bc65dfeeSyasuoka 4491935ef3dSmvs if ((session->flags & PIPEX_SFLAGS_PPPX) == 0 && 450bc65dfeeSyasuoka !in_nullhost(session->ip_address.sin_addr)) { 451f6e6f8e7Smvs if (pipex_lookup_by_ip_address_locked( 452f6e6f8e7Smvs session->ip_address.sin_addr) != NULL) { 453f6e6f8e7Smvs error = EADDRINUSE; 454f6e6f8e7Smvs goto out; 455f6e6f8e7Smvs } 456bc65dfeeSyasuoka rn = rn_addroute(&session->ip_address, &session->ip_netmask, 457bc65dfeeSyasuoka pipex_rd_head4, session->ps4_rn, RTP_STATIC); 458f6e6f8e7Smvs if (rn == NULL) { 459f6e6f8e7Smvs error = ENOMEM; 460f6e6f8e7Smvs goto out; 461f6e6f8e7Smvs } 462bc65dfeeSyasuoka } 463d70a8765Smvs 464d70a8765Smvs LIST_INSERT_HEAD(&pipex_session_list, session, session_list); 465a0ade7e9Syasuoka chain = PIPEX_ID_HASHTABLE(session->session_id); 466a0ade7e9Syasuoka LIST_INSERT_HEAD(chain, session, id_chain); 46794a607b5Smpi #if defined(PIPEX_PPTP) || defined(PIPEX_L2TP) 468d70a8765Smvs switch (session->protocol) { 4698876230bShsuenaga case PIPEX_PROTO_PPTP: 4708876230bShsuenaga case PIPEX_PROTO_L2TP: 471a0ade7e9Syasuoka chain = PIPEX_PEER_ADDR_HASHTABLE( 4729c681c75Sbluhm pipex_sockaddr_hash_key(&session->peer.sa)); 473a0ade7e9Syasuoka LIST_INSERT_HEAD(chain, session, peer_addr_chain); 474a0ade7e9Syasuoka } 47594a607b5Smpi #endif 476a0ade7e9Syasuoka 477a0ade7e9Syasuoka /* if first session is added, start timer */ 478a0ade7e9Syasuoka if (LIST_NEXT(session, session_list) == NULL) 479a0ade7e9Syasuoka pipex_timer_start(); 480d70a8765Smvs session->state = PIPEX_STATE_OPENED; 481d70a8765Smvs 482f6e6f8e7Smvs out: 483f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 484f6e6f8e7Smvs 485f6e6f8e7Smvs return error; 486d70a8765Smvs } 487d70a8765Smvs 488d70a8765Smvs void 489f6e6f8e7Smvs pipex_unlink_session_locked(struct pipex_session *session) 490d70a8765Smvs { 491bc65dfeeSyasuoka struct radix_node *rn; 492bc65dfeeSyasuoka 493f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx); 494f6e6f8e7Smvs 495002baba0Smvs session->ifindex = 0; 496002baba0Smvs 497bc65dfeeSyasuoka if (session->state == PIPEX_STATE_CLOSED) 498bc65dfeeSyasuoka return; 4991935ef3dSmvs if ((session->flags & PIPEX_SFLAGS_PPPX) == 0 && 500bc65dfeeSyasuoka !in_nullhost(session->ip_address.sin_addr)) { 501bc65dfeeSyasuoka KASSERT(pipex_rd_head4 != NULL); 502bc65dfeeSyasuoka rn = rn_delete(&session->ip_address, &session->ip_netmask, 503bc65dfeeSyasuoka pipex_rd_head4, (struct radix_node *)session); 504bc65dfeeSyasuoka KASSERT(rn != NULL); 505bc65dfeeSyasuoka } 506bc65dfeeSyasuoka 507d70a8765Smvs LIST_REMOVE(session, id_chain); 508d70a8765Smvs #if defined(PIPEX_PPTP) || defined(PIPEX_L2TP) 509d70a8765Smvs switch (session->protocol) { 510d70a8765Smvs case PIPEX_PROTO_PPTP: 511d70a8765Smvs case PIPEX_PROTO_L2TP: 512d70a8765Smvs LIST_REMOVE(session, peer_addr_chain); 513d70a8765Smvs break; 514d70a8765Smvs } 515d70a8765Smvs #endif 5160dd8107dSmvs if (session->state == PIPEX_STATE_CLOSE_WAIT) 5170dd8107dSmvs LIST_REMOVE(session, state_list); 518d70a8765Smvs LIST_REMOVE(session, session_list); 5190dd8107dSmvs session->state = PIPEX_STATE_CLOSED; 520d70a8765Smvs 521d70a8765Smvs /* if final session is destroyed, stop timer */ 522d70a8765Smvs if (LIST_EMPTY(&pipex_session_list)) 523d70a8765Smvs pipex_timer_stop(); 524d70a8765Smvs } 525d70a8765Smvs 526f6e6f8e7Smvs void 527f6e6f8e7Smvs pipex_unlink_session(struct pipex_session *session) 528f6e6f8e7Smvs { 529f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 530f6e6f8e7Smvs pipex_unlink_session_locked(session); 531f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 532f6e6f8e7Smvs } 533f6e6f8e7Smvs 534a0ade7e9Syasuoka int 535a0ade7e9Syasuoka pipex_notify_close_session(struct pipex_session *session) 536a0ade7e9Syasuoka { 537f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx); 538f6e6f8e7Smvs 539a0ade7e9Syasuoka session->state = PIPEX_STATE_CLOSE_WAIT; 540df21f681Smvs session->idle_time = 0; 541a0ade7e9Syasuoka LIST_INSERT_HEAD(&pipex_close_wait_list, session, state_list); 542a0ade7e9Syasuoka 54363d4736dSyasuoka return (0); 544a0ade7e9Syasuoka } 545a0ade7e9Syasuoka 546df21f681Smvs void 547df21f681Smvs pipex_export_session_stats(struct pipex_session *session, 548df21f681Smvs struct pipex_statistics *stats) 549df21f681Smvs { 550df21f681Smvs uint64_t counters[pxc_ncounters]; 551df21f681Smvs 552df21f681Smvs memset(stats, 0, sizeof(*stats)); 553df21f681Smvs 554bf0d449cSmpi counters_read(session->stat_counters, counters, pxc_ncounters, NULL); 555df21f681Smvs stats->ipackets = counters[pxc_ipackets]; 556df21f681Smvs stats->ierrors = counters[pxc_ierrors]; 557df21f681Smvs stats->ibytes = counters[pxc_ibytes]; 558df21f681Smvs stats->opackets = counters[pxc_opackets]; 559df21f681Smvs stats->oerrors = counters[pxc_oerrors]; 560df21f681Smvs stats->obytes = counters[pxc_obytes]; 561df21f681Smvs stats->idle_time = session->idle_time; 562df21f681Smvs } 563df21f681Smvs 564c02a3381Smvs int 565bc65dfeeSyasuoka pipex_get_stat(struct pipex_session_stat_req *req, void *ownersc) 566a0ade7e9Syasuoka { 567a0ade7e9Syasuoka struct pipex_session *session; 568f6e6f8e7Smvs int error = 0; 569a0ade7e9Syasuoka 570a0ade7e9Syasuoka session = pipex_lookup_by_session_id(req->psr_protocol, 571a0ade7e9Syasuoka req->psr_session_id); 572b9b0ec53Sclaudio if (session == NULL) 57363d4736dSyasuoka return (EINVAL); 574a0ade7e9Syasuoka 575f6e6f8e7Smvs if (session->ownersc == ownersc) 576f6e6f8e7Smvs pipex_export_session_stats(session, &req->psr_stat); 577f6e6f8e7Smvs else 578f6e6f8e7Smvs error = EINVAL; 579f6e6f8e7Smvs 580f6e6f8e7Smvs pipex_rele_session(session); 581f6e6f8e7Smvs 582f6e6f8e7Smvs return error; 583a0ade7e9Syasuoka } 584a0ade7e9Syasuoka 585c02a3381Smvs int 586bc65dfeeSyasuoka pipex_get_closed(struct pipex_session_list_req *req, void *ownersc) 587a0ade7e9Syasuoka { 588a672ee3dSclaudio struct pipex_session *session, *session_tmp; 589a0ade7e9Syasuoka 59063d4736dSyasuoka bzero(req, sizeof(*req)); 591f6e6f8e7Smvs 592f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 593f6e6f8e7Smvs 594a672ee3dSclaudio LIST_FOREACH_SAFE(session, &pipex_close_wait_list, state_list, 595a672ee3dSclaudio session_tmp) { 596aa95fb55Smvs if (session->flags & PIPEX_SFLAGS_ITERATOR) 597aa95fb55Smvs continue; 598bc65dfeeSyasuoka if (session->ownersc != ownersc) 599b9b0ec53Sclaudio continue; 600a0ade7e9Syasuoka req->plr_ppp_id[req->plr_ppp_id_count++] = session->ppp_id; 6011938ab6fSyasuoka LIST_REMOVE(session, state_list); 6021938ab6fSyasuoka session->state = PIPEX_STATE_CLOSE_WAIT2; 603a0ade7e9Syasuoka if (req->plr_ppp_id_count >= PIPEX_MAX_LISTREQ) { 6041938ab6fSyasuoka if (!LIST_EMPTY(&pipex_close_wait_list)) 605a0ade7e9Syasuoka req->plr_flags |= PIPEX_LISTREQ_MORE; 606a0ade7e9Syasuoka break; 607a0ade7e9Syasuoka } 608a0ade7e9Syasuoka } 609a0ade7e9Syasuoka 610f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 611f6e6f8e7Smvs 61263d4736dSyasuoka return (0); 613a0ade7e9Syasuoka } 614a0ade7e9Syasuoka 615c02a3381Smvs struct pipex_session * 616f6e6f8e7Smvs pipex_lookup_by_ip_address_locked(struct in_addr addr) 617a0ade7e9Syasuoka { 618a0ade7e9Syasuoka struct pipex_session *session; 61963d4736dSyasuoka struct sockaddr_in pipex_in4, pipex_in4mask; 620a0ade7e9Syasuoka 621f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx); 622f6e6f8e7Smvs 623bc65dfeeSyasuoka if (pipex_rd_head4 == NULL) 624bc65dfeeSyasuoka return (NULL); 62550bc8e1fSyasuoka bzero(&pipex_in4, sizeof(pipex_in4)); 626a0ade7e9Syasuoka pipex_in4.sin_addr = addr; 62763d4736dSyasuoka pipex_in4.sin_family = AF_INET; 62863d4736dSyasuoka pipex_in4.sin_len = sizeof(pipex_in4); 62950bc8e1fSyasuoka 63050bc8e1fSyasuoka bzero(&pipex_in4mask, sizeof(pipex_in4mask)); 63163d4736dSyasuoka pipex_in4mask.sin_addr.s_addr = htonl(0xFFFFFFFFL); 63263d4736dSyasuoka pipex_in4mask.sin_family = AF_INET; 63363d4736dSyasuoka pipex_in4mask.sin_len = sizeof(pipex_in4mask); 634a0ade7e9Syasuoka 635efe187c6Smpi session = (struct pipex_session *)rn_lookup(&pipex_in4, &pipex_in4mask, 636a718d138Syasuoka pipex_rd_head4); 637a0ade7e9Syasuoka 638a0ade7e9Syasuoka #ifdef PIPEX_DEBUG 639bbcf0337Smpi if (session == NULL) { 640bbcf0337Smpi char buf[INET_ADDRSTRLEN]; 641bbcf0337Smpi 642a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found (addr=%s)", 643bbcf0337Smpi __func__, inet_ntop(AF_INET, &addr, buf, sizeof(buf)))); 644bbcf0337Smpi } 645a0ade7e9Syasuoka #endif 646a0ade7e9Syasuoka 64763d4736dSyasuoka return (session); 648a0ade7e9Syasuoka } 649a0ade7e9Syasuoka 650f6e6f8e7Smvs struct pipex_session * 651f6e6f8e7Smvs pipex_lookup_by_ip_address(struct in_addr addr) 652f6e6f8e7Smvs { 653f6e6f8e7Smvs struct pipex_session *session; 654f6e6f8e7Smvs 655f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 656f6e6f8e7Smvs 657f6e6f8e7Smvs session = pipex_lookup_by_ip_address_locked(addr); 658f6e6f8e7Smvs if (session != NULL) 659f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt); 660f6e6f8e7Smvs 661f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 662f6e6f8e7Smvs 663f6e6f8e7Smvs return (session); 664f6e6f8e7Smvs } 665f6e6f8e7Smvs 666f6e6f8e7Smvs 667c02a3381Smvs struct pipex_session * 668f6e6f8e7Smvs pipex_lookup_by_session_id_locked(int protocol, int session_id) 669a0ade7e9Syasuoka { 670a0ade7e9Syasuoka struct pipex_hash_head *list; 671a0ade7e9Syasuoka struct pipex_session *session; 672a0ade7e9Syasuoka 673f6e6f8e7Smvs MUTEX_ASSERT_LOCKED(&pipex_list_mtx); 674f6e6f8e7Smvs 675a0ade7e9Syasuoka list = PIPEX_ID_HASHTABLE(session_id); 676a0ade7e9Syasuoka LIST_FOREACH(session, list, id_chain) { 677a0ade7e9Syasuoka if (session->protocol == protocol && 678a0ade7e9Syasuoka session->session_id == session_id) 679a0ade7e9Syasuoka break; 680a0ade7e9Syasuoka } 681a0ade7e9Syasuoka 682a0ade7e9Syasuoka #ifdef PIPEX_DEBUG 683a0ade7e9Syasuoka if (session == NULL) 684a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 685a0ade7e9Syasuoka "<%s> session not found (session_id=%d)", __func__, 686a0ade7e9Syasuoka session_id)); 687a0ade7e9Syasuoka #endif 688a0ade7e9Syasuoka 68963d4736dSyasuoka return (session); 690a0ade7e9Syasuoka } 691a0ade7e9Syasuoka 692f6e6f8e7Smvs struct pipex_session * 693f6e6f8e7Smvs pipex_lookup_by_session_id(int protocol, int session_id) 694f6e6f8e7Smvs { 695f6e6f8e7Smvs struct pipex_session *session; 696f6e6f8e7Smvs 697f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 698f6e6f8e7Smvs 699f6e6f8e7Smvs session = pipex_lookup_by_session_id_locked(protocol, session_id); 700f6e6f8e7Smvs if (session != NULL) 701f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt); 702f6e6f8e7Smvs 703f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 704f6e6f8e7Smvs 705f6e6f8e7Smvs return (session); 706f6e6f8e7Smvs } 707f6e6f8e7Smvs 708a0ade7e9Syasuoka /*********************************************************************** 709a0ade7e9Syasuoka * Timer functions 710a0ade7e9Syasuoka ***********************************************************************/ 711c02a3381Smvs void 712a0ade7e9Syasuoka pipex_timer_start(void) 713a0ade7e9Syasuoka { 714651f1b45Smvs timeout_set_flags(&pipex_timer_ch, pipex_timer, NULL, 715651f1b45Smvs KCLOCK_NONE, TIMEOUT_PROC | TIMEOUT_MPSAFE); 716a0ade7e9Syasuoka timeout_add_sec(&pipex_timer_ch, pipex_prune); 717a0ade7e9Syasuoka } 718a0ade7e9Syasuoka 719c02a3381Smvs void 720a0ade7e9Syasuoka pipex_timer_stop(void) 721a0ade7e9Syasuoka { 722a0ade7e9Syasuoka timeout_del(&pipex_timer_ch); 723a0ade7e9Syasuoka } 724a0ade7e9Syasuoka 725c02a3381Smvs void 726a0ade7e9Syasuoka pipex_timer(void *ignored_arg) 727a0ade7e9Syasuoka { 728a672ee3dSclaudio struct pipex_session *session, *session_tmp; 729a0ade7e9Syasuoka 730f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 731a0ade7e9Syasuoka /* walk through */ 732a672ee3dSclaudio LIST_FOREACH_SAFE(session, &pipex_session_list, session_list, 733a672ee3dSclaudio session_tmp) { 734aa95fb55Smvs if (session->flags & PIPEX_SFLAGS_ITERATOR) 735aa95fb55Smvs continue; 736a0ade7e9Syasuoka switch (session->state) { 737a0ade7e9Syasuoka case PIPEX_STATE_OPENED: 738a0ade7e9Syasuoka if (session->timeout_sec == 0) 739a0ade7e9Syasuoka continue; 740a0ade7e9Syasuoka 741df21f681Smvs session->idle_time++; 742df21f681Smvs if (session->idle_time < session->timeout_sec) 743a0ade7e9Syasuoka continue; 744a0ade7e9Syasuoka 745a0ade7e9Syasuoka pipex_notify_close_session(session); 746a0ade7e9Syasuoka break; 747a0ade7e9Syasuoka 748a0ade7e9Syasuoka case PIPEX_STATE_CLOSE_WAIT: 7491938ab6fSyasuoka case PIPEX_STATE_CLOSE_WAIT2: 750bc65dfeeSyasuoka /* Waiting PIPEXDSESSION from userland */ 751df21f681Smvs session->idle_time++; 752df21f681Smvs if (session->idle_time < PIPEX_CLOSE_TIMEOUT) 753a0ade7e9Syasuoka continue; 754bc65dfeeSyasuoka /* Release the sessions when timeout */ 755f6e6f8e7Smvs pipex_unlink_session_locked(session); 7561935ef3dSmvs KASSERTMSG((session->flags & PIPEX_SFLAGS_PPPX) == 0, 757bc65dfeeSyasuoka "FIXME session must not be released when pppx"); 758bc65dfeeSyasuoka pipex_rele_session(session); 759a0ade7e9Syasuoka break; 760a0ade7e9Syasuoka 761a0ade7e9Syasuoka default: 762a0ade7e9Syasuoka break; 763a0ade7e9Syasuoka } 764a0ade7e9Syasuoka } 765a0ade7e9Syasuoka 766651f1b45Smvs if (LIST_FIRST(&pipex_session_list)) 767651f1b45Smvs timeout_add_sec(&pipex_timer_ch, pipex_prune); 768651f1b45Smvs 769f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 770a0ade7e9Syasuoka } 771a0ade7e9Syasuoka 772a0ade7e9Syasuoka /*********************************************************************** 773a0ade7e9Syasuoka * Common network I/O functions. (tunnel protocol independent) 774a0ade7e9Syasuoka ***********************************************************************/ 775aa95fb55Smvs 776aa95fb55Smvs struct pipex_session * 777aa95fb55Smvs pipex_iterator(struct pipex_session *session, 778aa95fb55Smvs struct pipex_session_iterator *iter, void *ownersc) 779aa95fb55Smvs { 780aa95fb55Smvs struct pipex_session *session_tmp; 781aa95fb55Smvs 782aa95fb55Smvs mtx_enter(&pipex_list_mtx); 783aa95fb55Smvs 784aa95fb55Smvs if (session) 785aa95fb55Smvs session_tmp = LIST_NEXT(session, session_list); 786aa95fb55Smvs else 787aa95fb55Smvs session_tmp = LIST_FIRST(&pipex_session_list); 788aa95fb55Smvs 789aa95fb55Smvs while (session_tmp) { 790aa95fb55Smvs if (session_tmp->flags & PIPEX_SFLAGS_ITERATOR) 791aa95fb55Smvs goto next; 792aa95fb55Smvs if (session_tmp->ownersc != ownersc) 793aa95fb55Smvs goto next; 794aa95fb55Smvs break; 795aa95fb55Smvs next: 796aa95fb55Smvs session_tmp = LIST_NEXT(session_tmp, session_list); 797aa95fb55Smvs } 798aa95fb55Smvs 799aa95fb55Smvs if (session) 800aa95fb55Smvs LIST_REMOVE(iter, session_list); 801aa95fb55Smvs 802aa95fb55Smvs if (session_tmp) { 803aa95fb55Smvs LIST_INSERT_AFTER(session_tmp, 804aa95fb55Smvs (struct pipex_session *)&iter, session_list); 805aa95fb55Smvs refcnt_take(&session_tmp->pxs_refcnt); 806aa95fb55Smvs } 807aa95fb55Smvs 808aa95fb55Smvs mtx_leave(&pipex_list_mtx); 809aa95fb55Smvs 810aa95fb55Smvs if (session) 811aa95fb55Smvs pipex_rele_session(session); 812aa95fb55Smvs 813aa95fb55Smvs return (session_tmp); 814aa95fb55Smvs } 815aa95fb55Smvs 816c02a3381Smvs void 817a0ade7e9Syasuoka pipex_ip_output(struct mbuf *m0, struct pipex_session *session) 818a0ade7e9Syasuoka { 819a0ade7e9Syasuoka int is_idle; 820a0ade7e9Syasuoka 8211935ef3dSmvs if ((session->flags & PIPEX_SFLAGS_MULTICAST) == 0) { 822a0ade7e9Syasuoka /* 823a0ade7e9Syasuoka * Multicast packet is a idle packet and it's not TCP. 824a0ade7e9Syasuoka */ 82534e858ecSmvs 826a0ade7e9Syasuoka /* reset idle timer */ 827a0ade7e9Syasuoka if (session->timeout_sec != 0) { 828a0ade7e9Syasuoka is_idle = 0; 829a0ade7e9Syasuoka m0 = ip_is_idle_packet(m0, &is_idle); 830a0ade7e9Syasuoka if (m0 == NULL) 831b3d47573Sdlg goto dropped; 832f6e6f8e7Smvs if (is_idle == 0) { 833f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 834a0ade7e9Syasuoka /* update expire time */ 835a5953208Smvs if (session->state == PIPEX_STATE_OPENED) 836df21f681Smvs session->idle_time = 0; 837f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 838f6e6f8e7Smvs } 839a0ade7e9Syasuoka } 840a0ade7e9Syasuoka 841a0ade7e9Syasuoka /* adjust tcpmss */ 842a0ade7e9Syasuoka if ((session->ppp_flags & PIPEX_PPP_ADJUST_TCPMSS) != 0) { 843a0ade7e9Syasuoka m0 = adjust_tcp_mss(m0, session->peer_mru); 844a0ade7e9Syasuoka if (m0 == NULL) 845b3d47573Sdlg goto dropped; 846a0ade7e9Syasuoka } 84753c2c62eSmvs 84853c2c62eSmvs pipex_ppp_output(m0, session, PPP_IP); 84953c2c62eSmvs } else { 850aa95fb55Smvs struct pipex_session_iterator iter = { 851aa95fb55Smvs .flags = PIPEX_SFLAGS_ITERATOR, 852aa95fb55Smvs }; 853aa95fb55Smvs 85453c2c62eSmvs struct pipex_session *session_tmp; 85553c2c62eSmvs struct mbuf *m; 85653c2c62eSmvs 857a0ade7e9Syasuoka m0->m_flags &= ~(M_BCAST|M_MCAST); 858a0ade7e9Syasuoka 859aa95fb55Smvs session_tmp = pipex_iterator(NULL, &iter, session->ownersc); 860aa95fb55Smvs while (session_tmp) { 86153c2c62eSmvs m = m_copym(m0, 0, M_COPYALL, M_NOWAIT); 8629e0c672eSmvs if (m != NULL) 86353c2c62eSmvs pipex_ppp_output(m, session_tmp, PPP_IP); 8649e0c672eSmvs else 8659e0c672eSmvs counters_inc(session_tmp->stat_counters, 8669e0c672eSmvs pxc_oerrors); 8679e0c672eSmvs 868aa95fb55Smvs session_tmp = pipex_iterator(session_tmp, 869aa95fb55Smvs &iter, session->ownersc); 87053c2c62eSmvs } 8719e0c672eSmvs 87253c2c62eSmvs m_freem(m0); 87353c2c62eSmvs } 874a0ade7e9Syasuoka 875a0ade7e9Syasuoka return; 876b3d47573Sdlg dropped: 877df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 878a0ade7e9Syasuoka } 879a0ade7e9Syasuoka 880c02a3381Smvs void 881a0ade7e9Syasuoka pipex_ppp_output(struct mbuf *m0, struct pipex_session *session, int proto) 882a0ade7e9Syasuoka { 883a0ade7e9Syasuoka u_char *cp, hdr[16]; 884a0ade7e9Syasuoka 885a0ade7e9Syasuoka #ifdef PIPEX_MPPE 886a0ade7e9Syasuoka if (pipex_session_is_mppe_enabled(session)) { 88737f64355Syasuoka if (proto == PPP_IP) { 888707c5f41Smvs m0 = pipex_mppe_output(m0, session, PPP_IP); 889707c5f41Smvs if (m0 == NULL) 890707c5f41Smvs goto drop; 891707c5f41Smvs 892707c5f41Smvs proto = PPP_COMP; 89337f64355Syasuoka } 894a0ade7e9Syasuoka } 895a0ade7e9Syasuoka #endif /* PIPEX_MPPE */ 896a0ade7e9Syasuoka cp = hdr; 897c3479817Syasuoka if (session->protocol != PIPEX_PROTO_PPPOE) { 898c3479817Syasuoka /* PPPoE has not address and control field */ 899a0ade7e9Syasuoka PUTCHAR(PPP_ALLSTATIONS, cp); 900a0ade7e9Syasuoka PUTCHAR(PPP_UI, cp); 901c3479817Syasuoka } 902a0ade7e9Syasuoka PUTSHORT(proto, cp); 903a0ade7e9Syasuoka 904a0ade7e9Syasuoka M_PREPEND(m0, cp - hdr, M_NOWAIT); 905a0ade7e9Syasuoka if (m0 == NULL) 906a0ade7e9Syasuoka goto drop; 907a0ade7e9Syasuoka memcpy(mtod(m0, u_char *), hdr, cp - hdr); 908a0ade7e9Syasuoka 909a0ade7e9Syasuoka switch (session->protocol) { 910a0ade7e9Syasuoka #ifdef PIPEX_PPPOE 911a0ade7e9Syasuoka case PIPEX_PROTO_PPPOE: 912a0ade7e9Syasuoka pipex_pppoe_output(m0, session); 913a0ade7e9Syasuoka break; 914a0ade7e9Syasuoka #endif 915a0ade7e9Syasuoka #ifdef PIPEX_PPTP 916a0ade7e9Syasuoka case PIPEX_PROTO_PPTP: 917707c5f41Smvs mtx_enter(&session->pxs_mtx); 918a0ade7e9Syasuoka pipex_pptp_output(m0, session, 1, 1); 919707c5f41Smvs mtx_leave(&session->pxs_mtx); 920a0ade7e9Syasuoka break; 921a0ade7e9Syasuoka #endif 9228876230bShsuenaga #ifdef PIPEX_L2TP 9238876230bShsuenaga case PIPEX_PROTO_L2TP: 9248876230bShsuenaga pipex_l2tp_output(m0, session); 9258876230bShsuenaga break; 9268876230bShsuenaga #endif 927a0ade7e9Syasuoka default: 928a0ade7e9Syasuoka goto drop; 929a0ade7e9Syasuoka } 930a0ade7e9Syasuoka 931a0ade7e9Syasuoka return; 932a0ade7e9Syasuoka drop: 933a0ade7e9Syasuoka m_freem(m0); 934df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 935a0ade7e9Syasuoka } 936a0ade7e9Syasuoka 937c02a3381Smvs void 938a0ade7e9Syasuoka pipex_ppp_input(struct mbuf *m0, struct pipex_session *session, int decrypted) 939a0ade7e9Syasuoka { 940a0ade7e9Syasuoka int proto, hlen = 0; 941a19b4e4eSyasuoka struct mbuf *n; 942a0ade7e9Syasuoka 943707c5f41Smvs #ifdef PIPEX_MPPE 944707c5f41Smvs again: 945707c5f41Smvs #endif 946707c5f41Smvs 947363bb3c1Syasuoka KASSERT(m0->m_pkthdr.len >= PIPEX_PPPMINLEN); 9488876230bShsuenaga proto = pipex_ppp_proto(m0, session, 0, &hlen); 949a0ade7e9Syasuoka #ifdef PIPEX_MPPE 950fcff09efSyasuoka if (proto == PPP_COMP) { 951a0ade7e9Syasuoka if (decrypted) 952a0ade7e9Syasuoka goto drop; 953fcff09efSyasuoka 954fcff09efSyasuoka /* checked this on ppp_common_input() already. */ 955fcff09efSyasuoka KASSERT(pipex_session_is_mppe_accepted(session)); 956fcff09efSyasuoka 957a0ade7e9Syasuoka m_adj(m0, hlen); 958707c5f41Smvs m0 = pipex_mppe_input(m0, session); 959707c5f41Smvs if (m0 == NULL) 960707c5f41Smvs goto drop; 961707c5f41Smvs decrypted = 1; 962707c5f41Smvs 963707c5f41Smvs goto again; 9648876230bShsuenaga } 9658876230bShsuenaga if (proto == PPP_CCP) { 9668876230bShsuenaga if (decrypted) 9678876230bShsuenaga goto drop; 9688876230bShsuenaga 9698876230bShsuenaga #if NBPFILTER > 0 9708876230bShsuenaga { 971002baba0Smvs struct ifnet *ifp; 972002baba0Smvs 973002baba0Smvs if ((ifp = if_get(session->ifindex)) != NULL) { 9748876230bShsuenaga if (ifp->if_bpf && ifp->if_type == IFT_PPP) 9758876230bShsuenaga bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_IN); 9768876230bShsuenaga } 977002baba0Smvs if_put(ifp); 978002baba0Smvs } 979a0ade7e9Syasuoka #endif 9808876230bShsuenaga m_adj(m0, hlen); 9818876230bShsuenaga pipex_ccp_input(m0, session); 9828876230bShsuenaga return; 9838876230bShsuenaga } 9848876230bShsuenaga #endif 985a19b4e4eSyasuoka m_adj(m0, hlen); 986a19b4e4eSyasuoka if (!ALIGNED_POINTER(mtod(m0, caddr_t), uint32_t)) { 987a19b4e4eSyasuoka n = m_dup_pkt(m0, 0, M_NOWAIT); 988a19b4e4eSyasuoka if (n == NULL) 989a19b4e4eSyasuoka goto drop; 990a19b4e4eSyasuoka m_freem(m0); 991a19b4e4eSyasuoka m0 = n; 992a19b4e4eSyasuoka } 993a19b4e4eSyasuoka 9948876230bShsuenaga switch (proto) { 995a0ade7e9Syasuoka case PPP_IP: 996a0ade7e9Syasuoka if (!decrypted && pipex_session_is_mppe_required(session)) 997a0ade7e9Syasuoka /* 998a0ade7e9Syasuoka * if ip packet received when mppe 999a0ade7e9Syasuoka * is required, discard it. 1000a0ade7e9Syasuoka */ 1001a0ade7e9Syasuoka goto drop; 1002a0ade7e9Syasuoka pipex_ip_input(m0, session); 1003a0ade7e9Syasuoka return; 10048876230bShsuenaga #ifdef INET6 10058876230bShsuenaga case PPP_IPV6: 100689162bfcSyasuoka if (!decrypted && pipex_session_is_mppe_required(session)) 100789162bfcSyasuoka /* 100889162bfcSyasuoka * if ip packet received when mppe 100989162bfcSyasuoka * is required, discard it. 101089162bfcSyasuoka */ 101189162bfcSyasuoka goto drop; 10128876230bShsuenaga pipex_ip6_input(m0, session); 10138876230bShsuenaga return; 10148876230bShsuenaga #endif 1015a0ade7e9Syasuoka default: 1016a0ade7e9Syasuoka if (decrypted) 1017a0ade7e9Syasuoka goto drop; 10187f983f02Syasuoka /* protocol must be checked on pipex_common_input() already */ 10197f983f02Syasuoka KASSERT(0); 10207f983f02Syasuoka goto drop; 1021a0ade7e9Syasuoka } 1022a0ade7e9Syasuoka 1023a0ade7e9Syasuoka return; 1024707c5f41Smvs 1025a0ade7e9Syasuoka drop: 1026a0ade7e9Syasuoka m_freem(m0); 1027df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 1028a0ade7e9Syasuoka } 1029a0ade7e9Syasuoka 1030c02a3381Smvs void 1031a0ade7e9Syasuoka pipex_ip_input(struct mbuf *m0, struct pipex_session *session) 1032a0ade7e9Syasuoka { 1033a0ade7e9Syasuoka struct ifnet *ifp; 1034a0ade7e9Syasuoka struct ip *ip; 103598a920fdSdlg int len; 1036a0ade7e9Syasuoka int is_idle; 1037a0ade7e9Syasuoka 1038a0ade7e9Syasuoka /* change recvif */ 1039002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex; 1040a0ade7e9Syasuoka 1041a19b4e4eSyasuoka if (ISSET(session->ppp_flags, PIPEX_PPP_INGRESS_FILTER)) { 1042a0ade7e9Syasuoka PIPEX_PULLUP(m0, sizeof(struct ip)); 1043a0ade7e9Syasuoka if (m0 == NULL) 1044a0ade7e9Syasuoka goto drop; 1045a0ade7e9Syasuoka /* ingress filter */ 1046a0ade7e9Syasuoka ip = mtod(m0, struct ip *); 104763d4736dSyasuoka if ((ip->ip_src.s_addr & session->ip_netmask.sin_addr.s_addr) != 104863d4736dSyasuoka session->ip_address.sin_addr.s_addr) { 1049bbcf0337Smpi char src[INET_ADDRSTRLEN]; 1050bbcf0337Smpi 1051a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG, 1052a0ade7e9Syasuoka "ip packet discarded by ingress filter (src %s)", 1053bbcf0337Smpi inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src))); 1054a0ade7e9Syasuoka goto drop; 1055a0ade7e9Syasuoka } 10563b5b2d97Syasuoka } 1057a0ade7e9Syasuoka 1058a0ade7e9Syasuoka /* idle timer */ 1059a0ade7e9Syasuoka if (session->timeout_sec != 0) { 1060a0ade7e9Syasuoka is_idle = 0; 1061a0ade7e9Syasuoka m0 = ip_is_idle_packet(m0, &is_idle); 1062a0ade7e9Syasuoka if (m0 == NULL) 1063a0ade7e9Syasuoka goto drop; 1064f6e6f8e7Smvs if (is_idle == 0) { 1065a0ade7e9Syasuoka /* update expire time */ 1066f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 1067a5953208Smvs if (session->state == PIPEX_STATE_OPENED) 1068df21f681Smvs session->idle_time = 0; 1069f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 1070f6e6f8e7Smvs } 1071a0ade7e9Syasuoka } 1072a0ade7e9Syasuoka 1073a0ade7e9Syasuoka /* adjust tcpmss */ 1074a0ade7e9Syasuoka if (session->ppp_flags & PIPEX_PPP_ADJUST_TCPMSS) { 1075a0ade7e9Syasuoka m0 = adjust_tcp_mss(m0, session->peer_mru); 1076a0ade7e9Syasuoka if (m0 == NULL) 1077a0ade7e9Syasuoka goto drop; 1078a0ade7e9Syasuoka } 1079a0ade7e9Syasuoka 1080723b7749Ssashan #if NPF > 0 1081723b7749Ssashan pf_pkt_addr_changed(m0); 1082723b7749Ssashan #endif 1083723b7749Ssashan 1084a0ade7e9Syasuoka len = m0->m_pkthdr.len; 1085a0ade7e9Syasuoka 1086002baba0Smvs if ((ifp = if_get(session->ifindex)) == NULL) 1087002baba0Smvs goto drop; 1088002baba0Smvs 1089a0ade7e9Syasuoka #if NBPFILTER > 0 1090a0ade7e9Syasuoka if (ifp->if_bpf) 1091a0ade7e9Syasuoka bpf_mtap_af(ifp->if_bpf, AF_INET, m0, BPF_DIRECTION_IN); 1092a0ade7e9Syasuoka #endif 1093a0ade7e9Syasuoka 1094583e9cb0Smvs counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len); 1095df21f681Smvs counters_pkt(session->stat_counters, pxc_ipackets, pxc_ibytes, len); 10963f9b99eeSmpi ipv4_input(ifp, m0); 1097a0ade7e9Syasuoka 1098002baba0Smvs if_put(ifp); 1099002baba0Smvs 1100a0ade7e9Syasuoka return; 1101a0ade7e9Syasuoka drop: 1102a0ade7e9Syasuoka m_freem(m0); 1103df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 1104a0ade7e9Syasuoka } 1105a0ade7e9Syasuoka 11068876230bShsuenaga #ifdef INET6 1107c02a3381Smvs void 11088876230bShsuenaga pipex_ip6_input(struct mbuf *m0, struct pipex_session *session) 11098876230bShsuenaga { 11108876230bShsuenaga struct ifnet *ifp; 111198a920fdSdlg int len; 11128876230bShsuenaga 11138876230bShsuenaga /* change recvif */ 1114002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex; 11158876230bShsuenaga 11168876230bShsuenaga /* 11178876230bShsuenaga * XXX: what is reasonable ingress filter ??? 11188876230bShsuenaga * only one address is enough ?? 11198876230bShsuenaga */ 11208876230bShsuenaga 11218876230bShsuenaga /* XXX: we must define idle packet for IPv6(ICMPv6). */ 11228876230bShsuenaga 11238876230bShsuenaga /* 11248876230bShsuenaga * XXX: tcpmss adjustment for IPv6 is required??? 11258876230bShsuenaga * We may use PMTUD in IPv6.... 11268876230bShsuenaga */ 11278876230bShsuenaga 112885d4897fSmarkus #if NPF > 0 112985d4897fSmarkus pf_pkt_addr_changed(m0); 113085d4897fSmarkus #endif 113185d4897fSmarkus 11328876230bShsuenaga len = m0->m_pkthdr.len; 11338876230bShsuenaga 1134002baba0Smvs if ((ifp = if_get(session->ifindex)) == NULL) 1135002baba0Smvs goto drop; 1136002baba0Smvs 11378876230bShsuenaga #if NBPFILTER > 0 11388876230bShsuenaga if (ifp->if_bpf) 1139908147e1Smarkus bpf_mtap_af(ifp->if_bpf, AF_INET6, m0, BPF_DIRECTION_IN); 11408876230bShsuenaga #endif 11418876230bShsuenaga 1142583e9cb0Smvs counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len); 1143df21f681Smvs counters_pkt(session->stat_counters, pxc_ipackets, pxc_ibytes, len); 11443f9b99eeSmpi ipv6_input(ifp, m0); 1145002baba0Smvs 1146002baba0Smvs if_put(ifp); 1147002baba0Smvs 1148002baba0Smvs return; 1149002baba0Smvs drop: 1150002baba0Smvs m_freem(m0); 1151df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 11528876230bShsuenaga } 11539e07f154Syasuoka #endif 11548876230bShsuenaga 1155c02a3381Smvs struct mbuf * 1156d5af01deSyasuoka pipex_common_input(struct pipex_session *session, struct mbuf *m0, int hlen, 1157707c5f41Smvs int plen, int locked) 1158d5af01deSyasuoka { 1159d5af01deSyasuoka int proto, ppphlen; 1160d5af01deSyasuoka u_char code; 1161d5af01deSyasuoka 1162363bb3c1Syasuoka if ((m0->m_pkthdr.len < hlen + PIPEX_PPPMINLEN) || 1163707c5f41Smvs (plen < PIPEX_PPPMINLEN)) { 1164707c5f41Smvs if (locked) 1165707c5f41Smvs mtx_leave(&session->pxs_mtx); 1166d5af01deSyasuoka goto drop; 1167707c5f41Smvs } 1168d5af01deSyasuoka 1169d5af01deSyasuoka proto = pipex_ppp_proto(m0, session, hlen, &ppphlen); 1170d5af01deSyasuoka switch (proto) { 1171d5af01deSyasuoka #ifdef PIPEX_MPPE 1172d5af01deSyasuoka case PPP_CCP: 1173d5af01deSyasuoka code = 0; 1174d5af01deSyasuoka KASSERT(m0->m_pkthdr.len >= hlen + ppphlen + 1); 11755c7fed39Sdlg m_copydata(m0, hlen + ppphlen, 1, &code); 1176d5af01deSyasuoka if (code != CCP_RESETREQ && code != CCP_RESETACK) 1177d5af01deSyasuoka goto not_ours; 1178d5af01deSyasuoka break; 1179d5af01deSyasuoka 1180d5af01deSyasuoka case PPP_COMP: 1181fcff09efSyasuoka if (pipex_session_is_mppe_accepted(session)) 1182fcff09efSyasuoka break; 1183fcff09efSyasuoka goto not_ours; 1184d5af01deSyasuoka #endif 1185d5af01deSyasuoka case PPP_IP: 1186d5af01deSyasuoka #ifdef INET6 1187d5af01deSyasuoka case PPP_IPV6: 1188d5af01deSyasuoka #endif 1189d5af01deSyasuoka break; 1190d5af01deSyasuoka default: 1191d5af01deSyasuoka goto not_ours; 1192d5af01deSyasuoka } 1193d5af01deSyasuoka 1194707c5f41Smvs if (locked) 1195707c5f41Smvs mtx_leave(&session->pxs_mtx); 1196707c5f41Smvs 1197d5af01deSyasuoka /* ok, The packet is for PIPEX */ 1198df8d9afdSjsg m_adj(m0, hlen);/* cut off the tunnel protocol header */ 1199d5af01deSyasuoka 1200d5af01deSyasuoka /* ensure the mbuf length equals the PPP frame length */ 1201d5af01deSyasuoka if (m0->m_pkthdr.len < plen) 1202d5af01deSyasuoka goto drop; 1203d5af01deSyasuoka if (m0->m_pkthdr.len > plen) { 1204d5af01deSyasuoka if (m0->m_len == m0->m_pkthdr.len) { 1205d5af01deSyasuoka m0->m_len = plen; 1206d5af01deSyasuoka m0->m_pkthdr.len = plen; 1207d5af01deSyasuoka } else 1208d5af01deSyasuoka m_adj(m0, plen - m0->m_pkthdr.len); 1209d5af01deSyasuoka } 1210d5af01deSyasuoka 121177a7190bSyasuoka pipex_ppp_input(m0, session, 0); 121277a7190bSyasuoka 1213d5af01deSyasuoka return (NULL); 121453c2c62eSmvs 1215d5af01deSyasuoka drop: 1216d5af01deSyasuoka m_freem(m0); 1217df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 1218d5af01deSyasuoka return (NULL); 1219d5af01deSyasuoka 1220d5af01deSyasuoka not_ours: 1221d5af01deSyasuoka return (m0); /* Not to be handled by PIPEX */ 1222d5af01deSyasuoka } 1223d5af01deSyasuoka 1224a0ade7e9Syasuoka /* 1225a0ade7e9Syasuoka * pipex_ppp_proto 1226a0ade7e9Syasuoka */ 1227c02a3381Smvs int 1228a0ade7e9Syasuoka pipex_ppp_proto(struct mbuf *m0, struct pipex_session *session, int off, 1229a0ade7e9Syasuoka int *hlenp) 1230a0ade7e9Syasuoka { 1231a0ade7e9Syasuoka int proto; 1232a0ade7e9Syasuoka u_char *cp, pktbuf[4]; 1233a0ade7e9Syasuoka 1234363bb3c1Syasuoka KASSERT(m0->m_pkthdr.len > sizeof(pktbuf)); 1235a0ade7e9Syasuoka m_copydata(m0, off, sizeof(pktbuf), pktbuf); 1236a0ade7e9Syasuoka cp = pktbuf; 1237a0ade7e9Syasuoka 1238a0ade7e9Syasuoka if (pipex_session_has_acf(session)) { 123963d4736dSyasuoka if (cp[0] == PPP_ALLSTATIONS && cp[1] == PPP_UI) 1240a0ade7e9Syasuoka cp += 2; 1241a0ade7e9Syasuoka #ifdef PIPEX_DEBUG 124263d4736dSyasuoka else if (!pipex_session_is_acfc_accepted(session)) 1243a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, 1244a0ade7e9Syasuoka "no acf but acfc is not accepted by the peer.")); 1245a0ade7e9Syasuoka #endif 1246a0ade7e9Syasuoka } 1247a0ade7e9Syasuoka if ((*cp & 0x01) != 0) { 1248a0ade7e9Syasuoka if (!pipex_session_is_pfc_accepted(session)) { 1249a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "Received a broken ppp " 1250a0ade7e9Syasuoka "frame. No protocol field. %02x-%02x", 1251a0ade7e9Syasuoka cp[0], cp[1])); 125263d4736dSyasuoka return (-1); 1253a0ade7e9Syasuoka } 1254a0ade7e9Syasuoka GETCHAR(proto, cp); 125563d4736dSyasuoka } else 1256a0ade7e9Syasuoka GETSHORT(proto, cp); 1257a0ade7e9Syasuoka 1258a0ade7e9Syasuoka if (hlenp != NULL) 1259a0ade7e9Syasuoka *hlenp = cp - pktbuf; 1260a0ade7e9Syasuoka 126163d4736dSyasuoka return (proto); 1262a0ade7e9Syasuoka } 1263a0ade7e9Syasuoka 1264a0ade7e9Syasuoka #ifdef PIPEX_PPPOE 1265a0ade7e9Syasuoka /*********************************************************************** 1266a0ade7e9Syasuoka * PPPoE 1267a0ade7e9Syasuoka ***********************************************************************/ 1268c02a3381Smvs static const u_char pipex_pppoe_padding[ETHERMIN]; 1269a0ade7e9Syasuoka /* 1270a0ade7e9Syasuoka * pipex_pppoe_lookup_session 1271a0ade7e9Syasuoka */ 1272a0ade7e9Syasuoka struct pipex_session * 1273a0ade7e9Syasuoka pipex_pppoe_lookup_session(struct mbuf *m0) 1274a0ade7e9Syasuoka { 1275a0ade7e9Syasuoka struct pipex_session *session; 1276a0ade7e9Syasuoka struct pipex_pppoe_header pppoe; 1277a0ade7e9Syasuoka 1278a0ade7e9Syasuoka /* short packet */ 1279a0ade7e9Syasuoka if (m0->m_pkthdr.len < (sizeof(struct ether_header) + sizeof(pppoe))) 128063d4736dSyasuoka return (NULL); 1281a0ade7e9Syasuoka 1282a0ade7e9Syasuoka m_copydata(m0, sizeof(struct ether_header), 12835c7fed39Sdlg sizeof(struct pipex_pppoe_header), &pppoe); 1284faf7e06fSmpi pppoe.session_id = ntohs(pppoe.session_id); 1285a0ade7e9Syasuoka session = pipex_lookup_by_session_id(PIPEX_PROTO_PPPOE, 1286a0ade7e9Syasuoka pppoe.session_id); 1287a0ade7e9Syasuoka #ifdef PIPEX_DEBUG 128863d4736dSyasuoka if (session == NULL) 1289a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found (id=%d)", 1290a0ade7e9Syasuoka __func__, pppoe.session_id)); 1291a0ade7e9Syasuoka #endif 1292f6e6f8e7Smvs if (session && session->proto.pppoe.over_ifidx != 1293f6e6f8e7Smvs m0->m_pkthdr.ph_ifidx) { 1294f6e6f8e7Smvs pipex_rele_session(session); 12951489e7e8Syasuoka session = NULL; 1296f6e6f8e7Smvs } 1297a0ade7e9Syasuoka 129863d4736dSyasuoka return (session); 1299a0ade7e9Syasuoka } 1300a0ade7e9Syasuoka 1301a0ade7e9Syasuoka struct mbuf * 1302a0ade7e9Syasuoka pipex_pppoe_input(struct mbuf *m0, struct pipex_session *session) 1303a0ade7e9Syasuoka { 1304d5af01deSyasuoka int hlen; 1305a0ade7e9Syasuoka struct pipex_pppoe_header pppoe; 1306a0ade7e9Syasuoka 1307a0ade7e9Syasuoka /* already checked at pipex_pppoe_lookup_session */ 1308a0ade7e9Syasuoka KASSERT(m0->m_pkthdr.len >= (sizeof(struct ether_header) + 1309a0ade7e9Syasuoka sizeof(pppoe))); 1310a0ade7e9Syasuoka 1311a0ade7e9Syasuoka m_copydata(m0, sizeof(struct ether_header), 13125c7fed39Sdlg sizeof(struct pipex_pppoe_header), &pppoe); 1313a0ade7e9Syasuoka 1314d5af01deSyasuoka hlen = sizeof(struct ether_header) + sizeof(struct pipex_pppoe_header); 1315707c5f41Smvs m0 = pipex_common_input(session, m0, hlen, ntohs(pppoe.length), 0); 1316707c5f41Smvs if (m0 == NULL) 131763d4736dSyasuoka return (NULL); 1318a0ade7e9Syasuoka m_freem(m0); 1319df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 132063d4736dSyasuoka return (NULL); 1321a0ade7e9Syasuoka } 1322a0ade7e9Syasuoka 1323a0ade7e9Syasuoka /* 1324a0ade7e9Syasuoka * pipex_ppope_output 1325a0ade7e9Syasuoka */ 1326c02a3381Smvs void 1327a0ade7e9Syasuoka pipex_pppoe_output(struct mbuf *m0, struct pipex_session *session) 1328a0ade7e9Syasuoka { 1329a0ade7e9Syasuoka struct pipex_pppoe_header *pppoe; 1330a0ade7e9Syasuoka int len, padlen; 1331a0ade7e9Syasuoka 1332a0ade7e9Syasuoka /* save length for pppoe header */ 1333a0ade7e9Syasuoka len = m0->m_pkthdr.len; 1334a0ade7e9Syasuoka 1335a0ade7e9Syasuoka /* prepend protocol header */ 1336a0ade7e9Syasuoka M_PREPEND(m0, sizeof(struct pipex_pppoe_header), M_NOWAIT); 1337a0ade7e9Syasuoka if (m0 == NULL) { 1338a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_ERR, 1339a0ade7e9Syasuoka "<%s> cannot prepend header.", __func__)); 1340df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 1341a0ade7e9Syasuoka return; 1342a0ade7e9Syasuoka } 1343a0ade7e9Syasuoka padlen = ETHERMIN - m0->m_pkthdr.len; 134463d4736dSyasuoka if (padlen > 0) 134541b18b7eSblambert m_copyback(m0, m0->m_pkthdr.len, padlen, pipex_pppoe_padding, 134641b18b7eSblambert M_NOWAIT); 1347a0ade7e9Syasuoka 1348a0ade7e9Syasuoka /* setup pppoe header information */ 1349a0ade7e9Syasuoka pppoe = mtod(m0, struct pipex_pppoe_header *); 1350a0ade7e9Syasuoka pppoe->vertype = PIPEX_PPPOE_VERTYPE; 1351a0ade7e9Syasuoka pppoe->code = PIPEX_PPPOE_CODE_SESSION; 1352a0ade7e9Syasuoka pppoe->session_id = htons(session->session_id); 1353a0ade7e9Syasuoka pppoe->length = htons(len); 1354a0ade7e9Syasuoka 13557790b521Syasuoka m0->m_pkthdr.ph_ifidx = session->proto.pppoe.over_ifidx; 13560cd5c6faSmvs refcnt_take(&session->pxs_refcnt); 13570cd5c6faSmvs m0->m_pkthdr.ph_cookie = session; 1358a0ade7e9Syasuoka m0->m_flags &= ~(M_BCAST|M_MCAST); 1359a0ade7e9Syasuoka 13600cd5c6faSmvs if (mq_enqueue(&pipexoutq, m0) != 0) { 1361df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 13620cd5c6faSmvs pipex_rele_session(session); 13630cd5c6faSmvs } else 13640cd5c6faSmvs schednetisr(NETISR_PIPEX); 1365a0ade7e9Syasuoka } 1366a0ade7e9Syasuoka #endif /* PIPEX_PPPOE */ 1367a0ade7e9Syasuoka 1368a0ade7e9Syasuoka #ifdef PIPEX_PPTP 1369a0ade7e9Syasuoka /*********************************************************************** 1370a0ade7e9Syasuoka * PPTP 1371a0ade7e9Syasuoka ***********************************************************************/ 1372c02a3381Smvs void 1373a0ade7e9Syasuoka pipex_pptp_output(struct mbuf *m0, struct pipex_session *session, 1374a0ade7e9Syasuoka int has_seq, int has_ack) 1375a0ade7e9Syasuoka { 1376a0ade7e9Syasuoka int len, reqlen; 1377a0ade7e9Syasuoka struct pipex_gre_header *gre = NULL; 1378a0ade7e9Syasuoka struct ip *ip; 1379a0ade7e9Syasuoka u_char *cp; 1380a0ade7e9Syasuoka 1381707c5f41Smvs MUTEX_ASSERT_LOCKED(&session->pxs_mtx); 1382707c5f41Smvs 1383a0ade7e9Syasuoka reqlen = PIPEX_IPGRE_HDRLEN + (has_seq + has_ack) * 4; 1384a0ade7e9Syasuoka 1385a0ade7e9Syasuoka len = 0; 1386a0ade7e9Syasuoka if (m0 != NULL) { 1387a0ade7e9Syasuoka /* save length for gre header */ 1388a0ade7e9Syasuoka len = m0->m_pkthdr.len; 1389a0ade7e9Syasuoka /* prepend protocol header */ 1390a0ade7e9Syasuoka M_PREPEND(m0, reqlen, M_NOWAIT); 1391a0ade7e9Syasuoka if (m0 == NULL) 1392a0ade7e9Syasuoka goto drop; 1393a0ade7e9Syasuoka } else { 1394a0ade7e9Syasuoka MGETHDR(m0, M_DONTWAIT, MT_DATA); 1395a0ade7e9Syasuoka if (m0 && reqlen > MHLEN) { 1396a0ade7e9Syasuoka MCLGET(m0, M_DONTWAIT); 1397a0ade7e9Syasuoka if ((m0->m_flags & M_EXT) == 0) { 1398a0ade7e9Syasuoka m_freem(m0); 1399a0ade7e9Syasuoka m0 = NULL; 1400a0ade7e9Syasuoka } 1401a0ade7e9Syasuoka } 1402a0ade7e9Syasuoka if (m0 == NULL) 1403a0ade7e9Syasuoka goto drop; 1404a0ade7e9Syasuoka m0->m_pkthdr.len = m0->m_len = reqlen; 1405a0ade7e9Syasuoka } 1406a0ade7e9Syasuoka 1407a0ade7e9Syasuoka /* setup ip header information */ 1408a0ade7e9Syasuoka ip = mtod(m0, struct ip *); 1409a0ade7e9Syasuoka 1410a0ade7e9Syasuoka ip->ip_len = htons(m0->m_pkthdr.len); 1411a0ade7e9Syasuoka ip->ip_off = 0; 1412a0ade7e9Syasuoka ip->ip_ttl = MAXTTL; 1413a0ade7e9Syasuoka ip->ip_p = IPPROTO_GRE; 1414a0ade7e9Syasuoka ip->ip_tos = 0; 1415a0ade7e9Syasuoka 14168876230bShsuenaga ip->ip_src = session->local.sin4.sin_addr; 14178876230bShsuenaga ip->ip_dst = session->peer.sin4.sin_addr; 141885d4897fSmarkus #if NPF > 0 141985d4897fSmarkus pf_pkt_addr_changed(m0); 142085d4897fSmarkus #endif 1421a0ade7e9Syasuoka 1422a0ade7e9Syasuoka /* setup gre(ver1) header information */ 1423a0ade7e9Syasuoka gre = PIPEX_SEEK_NEXTHDR(ip, sizeof(struct ip), 1424a0ade7e9Syasuoka struct pipex_gre_header *); 1425a0ade7e9Syasuoka gre->type = htons(PIPEX_GRE_PROTO_PPP); 1426a0ade7e9Syasuoka gre->call_id = htons(session->peer_session_id); 1427a0ade7e9Syasuoka gre->flags = PIPEX_GRE_KFLAG | PIPEX_GRE_VER; /* do htons later */ 1428a0ade7e9Syasuoka gre->len = htons(len); 1429a0ade7e9Syasuoka 1430a0ade7e9Syasuoka cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *); 1431a0ade7e9Syasuoka if (has_seq) { 1432a0ade7e9Syasuoka gre->flags |= PIPEX_GRE_SFLAG; 1433a0ade7e9Syasuoka PUTLONG(session->proto.pptp.snd_nxt, cp); 1434a0ade7e9Syasuoka session->proto.pptp.snd_nxt++; 1435a0ade7e9Syasuoka session->proto.pptp.snd_gap++; 1436a0ade7e9Syasuoka } 1437a0ade7e9Syasuoka if (has_ack) { 1438a0ade7e9Syasuoka gre->flags |= PIPEX_GRE_AFLAG; 14398876230bShsuenaga session->proto.pptp.rcv_acked = session->proto.pptp.rcv_nxt - 1; 14408876230bShsuenaga PUTLONG(session->proto.pptp.rcv_acked, cp); 1441a0ade7e9Syasuoka } 1442a0ade7e9Syasuoka gre->flags = htons(gre->flags); 1443a0ade7e9Syasuoka 1444002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex; 14450315bf92Sdlg ip_send(m0); 1446a0ade7e9Syasuoka if (len > 0) { /* network layer only */ 1447a0ade7e9Syasuoka /* countup statistics */ 1448df21f681Smvs counters_pkt(session->stat_counters, pxc_opackets, 1449df21f681Smvs pxc_obytes, len); 1450a0ade7e9Syasuoka } 1451a0ade7e9Syasuoka 1452a0ade7e9Syasuoka return; 1453a0ade7e9Syasuoka drop: 1454df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 1455a0ade7e9Syasuoka } 1456a0ade7e9Syasuoka 1457a0ade7e9Syasuoka struct pipex_session * 1458a0ade7e9Syasuoka pipex_pptp_lookup_session(struct mbuf *m0) 1459a0ade7e9Syasuoka { 1460a0ade7e9Syasuoka struct pipex_session *session; 1461a0ade7e9Syasuoka struct pipex_gre_header gre; 1462a0ade7e9Syasuoka struct ip ip; 1463a0ade7e9Syasuoka uint16_t flags; 1464a0ade7e9Syasuoka uint16_t id; 1465a0ade7e9Syasuoka int hlen; 1466a0ade7e9Syasuoka 1467a0ade7e9Syasuoka if (m0->m_pkthdr.len < PIPEX_IPGRE_HDRLEN) { 1468a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1469a0ade7e9Syasuoka "<%s> packet length is too short", __func__)); 1470a0ade7e9Syasuoka goto not_ours; 1471a0ade7e9Syasuoka } 1472a0ade7e9Syasuoka 1473a0ade7e9Syasuoka /* get ip header info */ 14745c7fed39Sdlg m_copydata(m0, 0, sizeof(struct ip), &ip); 1475a0ade7e9Syasuoka hlen = ip.ip_hl << 2; 1476a0ade7e9Syasuoka 1477a0ade7e9Syasuoka /* 1478a0ade7e9Syasuoka * m0 has already passed ip_input(), so there is 1479a0ade7e9Syasuoka * no necessity for ip packet inspection. 1480a0ade7e9Syasuoka */ 1481a0ade7e9Syasuoka 1482a0ade7e9Syasuoka /* get gre flags */ 14835c7fed39Sdlg m_copydata(m0, hlen, sizeof(gre), &gre); 1484a0ade7e9Syasuoka flags = ntohs(gre.flags); 1485a0ade7e9Syasuoka 1486a0ade7e9Syasuoka /* gre version must be '1' */ 1487a0ade7e9Syasuoka if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) { 1488a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1489a0ade7e9Syasuoka "<%s> gre header wrong version.", __func__)); 1490a0ade7e9Syasuoka goto not_ours; 1491a0ade7e9Syasuoka } 1492a0ade7e9Syasuoka 1493a0ade7e9Syasuoka /* gre keys must be present */ 1494a0ade7e9Syasuoka if ((flags & PIPEX_GRE_KFLAG) == 0) { 1495a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1496a0ade7e9Syasuoka "<%s> gre header has no keys.", __func__)); 1497a0ade7e9Syasuoka goto not_ours; 1498a0ade7e9Syasuoka } 1499a0ade7e9Syasuoka 1500363bb3c1Syasuoka /* flag check */ 1501363bb3c1Syasuoka if ((flags & PIPEX_GRE_UNUSEDFLAGS) != 0) { 1502363bb3c1Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1503363bb3c1Syasuoka "<%s> gre header has unused flags at pptp.", __func__)); 1504363bb3c1Syasuoka goto not_ours; 1505363bb3c1Syasuoka } 1506363bb3c1Syasuoka 1507a0ade7e9Syasuoka /* lookup pipex session table */ 1508a0ade7e9Syasuoka id = ntohs(gre.call_id); 1509a0ade7e9Syasuoka session = pipex_lookup_by_session_id(PIPEX_PROTO_PPTP, id); 1510a0ade7e9Syasuoka if (session == NULL) { 1511a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1512a0ade7e9Syasuoka "<%s> session not found (id=%d)", __func__, id)); 1513a0ade7e9Syasuoka goto not_ours; 1514a0ade7e9Syasuoka } 1515*f168c03cSyasuoka 1516*f168c03cSyasuoka if (!(session->peer.sa.sa_family == AF_INET && 1517*f168c03cSyasuoka session->peer.sin4.sin_addr.s_addr == ip.ip_src.s_addr)) { 1518*f168c03cSyasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1519*f168c03cSyasuoka "<%s> the source address of the session is not matched", 1520*f168c03cSyasuoka __func__)); 1521*f168c03cSyasuoka goto not_ours; 1522*f168c03cSyasuoka } 1523a0ade7e9Syasuoka 152463d4736dSyasuoka return (session); 1525a0ade7e9Syasuoka 1526a0ade7e9Syasuoka not_ours: 152763d4736dSyasuoka return (NULL); 1528a0ade7e9Syasuoka } 1529a0ade7e9Syasuoka 1530a0ade7e9Syasuoka struct mbuf * 1531a0ade7e9Syasuoka pipex_pptp_input(struct mbuf *m0, struct pipex_session *session) 1532a0ade7e9Syasuoka { 1533d5af01deSyasuoka int hlen, has_seq, has_ack, nseq; 1534a0ade7e9Syasuoka const char *reason = ""; 1535d5af01deSyasuoka u_char *cp, *seqp = NULL, *ackp = NULL; 1536a0ade7e9Syasuoka uint32_t flags, seq = 0, ack = 0; 1537a0ade7e9Syasuoka struct ip *ip; 1538a0ade7e9Syasuoka struct pipex_gre_header *gre; 1539a0ade7e9Syasuoka struct pipex_pptp_session *pptp_session; 1540e405d423Syasuoka int rewind = 0; 1541a0ade7e9Syasuoka 1542a0ade7e9Syasuoka KASSERT(m0->m_pkthdr.len >= PIPEX_IPGRE_HDRLEN); 1543a0ade7e9Syasuoka pptp_session = &session->proto.pptp; 1544a0ade7e9Syasuoka 1545a0ade7e9Syasuoka /* get ip header */ 1546a0ade7e9Syasuoka ip = mtod(m0, struct ip *); 1547a0ade7e9Syasuoka hlen = ip->ip_hl << 2; 1548a0ade7e9Syasuoka 1549a0ade7e9Syasuoka /* seek gre header */ 1550a0ade7e9Syasuoka gre = PIPEX_SEEK_NEXTHDR(ip, hlen, struct pipex_gre_header *); 1551a0ade7e9Syasuoka flags = ntohs(gre->flags); 1552a0ade7e9Syasuoka 1553a0ade7e9Syasuoka /* pullup for seek sequences in header */ 1554a0ade7e9Syasuoka has_seq = (flags & PIPEX_GRE_SFLAG) ? 1 : 0; 1555a0ade7e9Syasuoka has_ack = (flags & PIPEX_GRE_AFLAG) ? 1 : 0; 1556a0ade7e9Syasuoka hlen = PIPEX_IPGRE_HDRLEN + 4 * (has_seq + has_ack); 1557a0ade7e9Syasuoka if (m0->m_len < hlen) { 1558a0ade7e9Syasuoka m0 = m_pullup(m0, hlen); 1559a0ade7e9Syasuoka if (m0 == NULL) { 1560a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "pullup failed.")); 1561a0ade7e9Syasuoka goto drop; 1562a0ade7e9Syasuoka } 1563a0ade7e9Syasuoka } 1564a0ade7e9Syasuoka 1565a0ade7e9Syasuoka /* check sequence */ 1566a0ade7e9Syasuoka cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *); 1567a0ade7e9Syasuoka if (has_seq) { 1568a0ade7e9Syasuoka seqp = cp; 1569a0ade7e9Syasuoka GETLONG(seq, cp); 1570a0ade7e9Syasuoka } 1571707c5f41Smvs 1572707c5f41Smvs mtx_enter(&session->pxs_mtx); 1573707c5f41Smvs 1574a0ade7e9Syasuoka if (has_ack) { 1575a0ade7e9Syasuoka ackp = cp; 1576a0ade7e9Syasuoka GETLONG(ack, cp); 1577a0ade7e9Syasuoka if (ack + 1 == pptp_session->snd_una) { 1578a0ade7e9Syasuoka /* ack has not changed before */ 1579a0ade7e9Syasuoka } else if (SEQ32_LT(ack, pptp_session->snd_una)) { 1580e405d423Syasuoka /* OoO ack packets should not be dropped. */ 1581e405d423Syasuoka rewind = 1; 1582a0ade7e9Syasuoka } else if (SEQ32_GT(ack, pptp_session->snd_nxt)) { 1583a0ade7e9Syasuoka reason = "ack for unknown sequence"; 1584e405d423Syasuoka goto out_seq; 15856117a981Syasuoka } else 15866117a981Syasuoka pptp_session->snd_una = ack + 1; 1587a0ade7e9Syasuoka } 1588a0ade7e9Syasuoka if (!has_seq) { 1589a0ade7e9Syasuoka /* ack only packet */ 1590a0ade7e9Syasuoka goto not_ours; 1591a0ade7e9Syasuoka } 1592a0ade7e9Syasuoka if (SEQ32_LT(seq, pptp_session->rcv_nxt)) { 1593e405d423Syasuoka rewind = 1; 1594e405d423Syasuoka if (SEQ32_LT(seq, 1595e405d423Syasuoka pptp_session->rcv_nxt - PIPEX_REWIND_LIMIT)) { 1596a0ade7e9Syasuoka reason = "out of sequence"; 1597e405d423Syasuoka goto out_seq; 1598e405d423Syasuoka } 1599a0ade7e9Syasuoka } else if (SEQ32_GE(seq, pptp_session->rcv_nxt + 1600a0ade7e9Syasuoka pptp_session->maxwinsz)) { 1601a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG, 1602a0ade7e9Syasuoka "received packet caused window overflow. seq=%u(%u-%u)" 1603a0ade7e9Syasuoka "may lost %d packets.", seq, pptp_session->rcv_nxt, 1604a0ade7e9Syasuoka pptp_session->rcv_nxt + pptp_session->maxwinsz, 1605a0ade7e9Syasuoka (int)SEQ32_SUB(seq, pptp_session->rcv_nxt)); 1606a0ade7e9Syasuoka } 1607a0ade7e9Syasuoka 1608a0ade7e9Syasuoka seq++; 1609a0ade7e9Syasuoka nseq = SEQ32_SUB(seq, pptp_session->rcv_nxt); 1610e405d423Syasuoka if (!rewind) { 1611a0ade7e9Syasuoka pptp_session->rcv_nxt = seq; 1612a0ade7e9Syasuoka if (SEQ32_SUB(seq, pptp_session->rcv_acked) > 161363d4736dSyasuoka roundup(pptp_session->winsz, 2) / 2) /* Send ack only packet. */ 1614a0ade7e9Syasuoka pipex_pptp_output(NULL, session, 0, 1); 1615e405d423Syasuoka } 1616a0ade7e9Syasuoka 1617707c5f41Smvs /* 1618707c5f41Smvs * The following pipex_common_input() will release `pxs_mtx' 1619707c5f41Smvs * deep within if the packet will be consumed. In the error 1620707c5f41Smvs * path lock will be held all the time. So increment `rcv_gap' 1621707c5f41Smvs * here, and on the error path back it out, no atomicity will 1622707c5f41Smvs * be lost in all cases. 1623707c5f41Smvs */ 1624e405d423Syasuoka if (!rewind) 1625a0ade7e9Syasuoka session->proto.pptp.rcv_gap += nseq; 1626707c5f41Smvs m0 = pipex_common_input(session, m0, hlen, ntohs(gre->len), 1); 1627707c5f41Smvs if (m0 == NULL) { 1628707c5f41Smvs /* 1629707c5f41Smvs * pipex_common_input() releases lock if the 1630707c5f41Smvs * packet was consumed. 1631707c5f41Smvs */ 1632e405d423Syasuoka return (NULL); 1633a0ade7e9Syasuoka } 1634e405d423Syasuoka 1635e405d423Syasuoka if (rewind) 1636e405d423Syasuoka goto out_seq; 1637707c5f41Smvs else { 1638707c5f41Smvs /* The packet is not ours, back out `rcv_gap'. */ 1639707c5f41Smvs session->proto.pptp.rcv_gap -= nseq; 1640707c5f41Smvs } 1641e405d423Syasuoka 1642a0ade7e9Syasuoka not_ours: 16436117a981Syasuoka seq--; /* revert original seq value */ 164463d4736dSyasuoka 1645a0ade7e9Syasuoka /* 1646a0ade7e9Syasuoka * overwrite sequence numbers to adjust a gap between pipex and 1647a0ade7e9Syasuoka * userland. 1648a0ade7e9Syasuoka */ 1649a0ade7e9Syasuoka if (seqp != NULL) { 1650a0ade7e9Syasuoka seq -= pptp_session->rcv_gap; 1651a0ade7e9Syasuoka PUTLONG(seq, seqp); 1652a0ade7e9Syasuoka } 1653a0ade7e9Syasuoka if (ackp != NULL) { 1654a0ade7e9Syasuoka if (pptp_session->snd_nxt == pptp_session->snd_una) { 1655a0ade7e9Syasuoka ack -= session->proto.pptp.snd_gap; 1656a0ade7e9Syasuoka pptp_session->ul_snd_una = ack; 1657a0ade7e9Syasuoka } else { 1658a0ade7e9Syasuoka /* 1659a0ade7e9Syasuoka * There are sending packets they are not acked. 1660a0ade7e9Syasuoka * In this situation, (ack - snd_gap) may points 1661a0ade7e9Syasuoka * before sending window of userland. So we don't 1662a0ade7e9Syasuoka * update the ack number. 1663a0ade7e9Syasuoka */ 1664a0ade7e9Syasuoka ack = pptp_session->ul_snd_una; 1665a0ade7e9Syasuoka } 1666a0ade7e9Syasuoka PUTLONG(ack, ackp); 1667a0ade7e9Syasuoka } 1668a0ade7e9Syasuoka 1669707c5f41Smvs mtx_leave(&session->pxs_mtx); 1670707c5f41Smvs 167163d4736dSyasuoka return (m0); 1672e405d423Syasuoka out_seq: 1673a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG, 1674a0ade7e9Syasuoka "Received bad data packet: %s: seq=%u(%u-%u) ack=%u(%u-%u)", 1675a0ade7e9Syasuoka reason, seq, pptp_session->rcv_nxt, 1676a0ade7e9Syasuoka pptp_session->rcv_nxt + pptp_session->maxwinsz, 1677a0ade7e9Syasuoka ack, pptp_session->snd_una, 1678a0ade7e9Syasuoka pptp_session->snd_nxt); 1679707c5f41Smvs mtx_leave(&session->pxs_mtx); 1680a0ade7e9Syasuoka 1681a0ade7e9Syasuoka /* FALLTHROUGH */ 1682a0ade7e9Syasuoka drop: 1683a0ade7e9Syasuoka m_freem(m0); 1684df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 1685a0ade7e9Syasuoka 168663d4736dSyasuoka return (NULL); 1687a0ade7e9Syasuoka } 1688a0ade7e9Syasuoka 1689a0ade7e9Syasuoka struct pipex_session * 16908876230bShsuenaga pipex_pptp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst) 16918876230bShsuenaga { 16929c794535Sbluhm struct sockaddr_in sin; 16938876230bShsuenaga 16949c794535Sbluhm memset(&sin, 0, sizeof(sin)); 16959c794535Sbluhm sin.sin_len = sizeof(sin); 16969c794535Sbluhm sin.sin_family = AF_INET; 16979c794535Sbluhm sin.sin_addr = dst; 16988876230bShsuenaga 16999c794535Sbluhm return pipex_pptp_userland_lookup_session(m0, sintosa(&sin)); 17008876230bShsuenaga } 17018876230bShsuenaga 17028876230bShsuenaga #ifdef INET6 17038876230bShsuenaga struct pipex_session * 17048876230bShsuenaga pipex_pptp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst) 17058876230bShsuenaga { 17068876230bShsuenaga struct sockaddr_in6 sin6; 17078876230bShsuenaga 17089c794535Sbluhm memset(&sin6, 0, sizeof(sin6)); 17099c794535Sbluhm sin6.sin6_len = sizeof(sin6); 17108876230bShsuenaga sin6.sin6_family = AF_INET6; 17116c8dd0e9Sclaudio in6_recoverscope(&sin6, &dst); 17128876230bShsuenaga 17139c794535Sbluhm return pipex_pptp_userland_lookup_session(m0, sin6tosa(&sin6)); 17148876230bShsuenaga } 17158876230bShsuenaga #endif 17168876230bShsuenaga 1717c02a3381Smvs struct pipex_session * 17188876230bShsuenaga pipex_pptp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa) 1719a0ade7e9Syasuoka { 1720a0ade7e9Syasuoka struct pipex_gre_header gre; 1721a0ade7e9Syasuoka struct pipex_hash_head *list; 1722a0ade7e9Syasuoka struct pipex_session *session; 1723a0ade7e9Syasuoka uint16_t id, flags; 1724a0ade7e9Syasuoka 1725a0ade7e9Syasuoka /* pullup */ 1726a0ade7e9Syasuoka if (m0->m_pkthdr.len < sizeof(gre)) { 1727a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1728a0ade7e9Syasuoka "<%s> packet length is too short", __func__)); 172963d4736dSyasuoka return (NULL); 1730a0ade7e9Syasuoka } 1731a0ade7e9Syasuoka 1732a0ade7e9Syasuoka /* get flags */ 17335c7fed39Sdlg m_copydata(m0, 0, sizeof(struct pipex_gre_header), &gre); 1734a0ade7e9Syasuoka flags = ntohs(gre.flags); 1735a0ade7e9Syasuoka 1736a0ade7e9Syasuoka /* gre version must be '1' */ 1737a0ade7e9Syasuoka if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) { 1738a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1739a0ade7e9Syasuoka "<%s> gre header wrong version.", __func__)); 174063d4736dSyasuoka return (NULL); 1741a0ade7e9Syasuoka } 1742a0ade7e9Syasuoka 1743a0ade7e9Syasuoka /* gre keys must be present */ 1744a0ade7e9Syasuoka if ((flags & PIPEX_GRE_KFLAG) == 0) { 1745a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 1746a0ade7e9Syasuoka "<%s> gre header has no keys.", __func__)); 174763d4736dSyasuoka return (NULL); 1748a0ade7e9Syasuoka } 1749a0ade7e9Syasuoka 1750a0ade7e9Syasuoka /* lookup pipex session table */ 1751a0ade7e9Syasuoka id = ntohs(gre.call_id); 1752a0ade7e9Syasuoka 1753f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 1754f6e6f8e7Smvs 17558876230bShsuenaga list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa)); 1756a0ade7e9Syasuoka LIST_FOREACH(session, list, peer_addr_chain) { 17579c681c75Sbluhm if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0) 1758a0ade7e9Syasuoka continue; 1759a0ade7e9Syasuoka if (session->peer_session_id == id) 1760a0ade7e9Syasuoka break; 1761a0ade7e9Syasuoka } 1762f6e6f8e7Smvs 1763f6e6f8e7Smvs if (session != NULL) 1764f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt); 1765f6e6f8e7Smvs 1766f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 1767f6e6f8e7Smvs 1768a0ade7e9Syasuoka #ifdef PIPEX_DEBUG 1769a0ade7e9Syasuoka if (session == NULL) { 1770a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 17718876230bShsuenaga "<%s> session not found (,call_id=%d)", 17728876230bShsuenaga __func__, (int)gre.call_id)); 1773a0ade7e9Syasuoka } 1774a0ade7e9Syasuoka #endif 177563d4736dSyasuoka return (session); 1776a0ade7e9Syasuoka } 1777a0ade7e9Syasuoka 1778a0ade7e9Syasuoka /* 1779a0ade7e9Syasuoka * pipex_pptp_userland_output 1780a0ade7e9Syasuoka */ 1781a0ade7e9Syasuoka struct mbuf * 1782a0ade7e9Syasuoka pipex_pptp_userland_output(struct mbuf *m0, struct pipex_session *session) 1783a0ade7e9Syasuoka { 17849f9300a4Syasuoka int len; 17859f9300a4Syasuoka struct pipex_gre_header *gre, gre0; 1786a0ade7e9Syasuoka uint16_t flags; 1787a0ade7e9Syasuoka u_char *cp, *cp0; 1788a0ade7e9Syasuoka uint32_t val32; 1789a0ade7e9Syasuoka 17909f9300a4Syasuoka len = sizeof(struct pipex_gre_header); 17915c7fed39Sdlg m_copydata(m0, 0, len, &gre0); 17929f9300a4Syasuoka gre = &gre0; 17939f9300a4Syasuoka flags = ntohs(gre->flags); 17949f9300a4Syasuoka if ((flags & PIPEX_GRE_SFLAG) != 0) 17959f9300a4Syasuoka len += 4; 17969f9300a4Syasuoka if ((flags & PIPEX_GRE_AFLAG) != 0) 17979f9300a4Syasuoka len += 4; 17989f9300a4Syasuoka 1799a0ade7e9Syasuoka /* check length */ 18009f9300a4Syasuoka PIPEX_PULLUP(m0, len); 1801a0ade7e9Syasuoka if (m0 == NULL) { 18029f9300a4Syasuoka PIPEX_DBG((session, LOG_DEBUG, "gre header is too short.")); 180363d4736dSyasuoka return (NULL); 1804a0ade7e9Syasuoka } 1805a0ade7e9Syasuoka 1806a0ade7e9Syasuoka gre = mtod(m0, struct pipex_gre_header *); 1807a0ade7e9Syasuoka cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header), u_char *); 1808a0ade7e9Syasuoka 1809707c5f41Smvs mtx_enter(&session->pxs_mtx); 1810707c5f41Smvs 1811a0ade7e9Syasuoka /* 1812a0ade7e9Syasuoka * overwrite sequence numbers to adjust a gap between pipex and 1813a0ade7e9Syasuoka * userland. 1814a0ade7e9Syasuoka */ 1815a0ade7e9Syasuoka if ((flags & PIPEX_GRE_SFLAG) != 0) { 1816a0ade7e9Syasuoka cp0 = cp; 1817a0ade7e9Syasuoka GETLONG(val32, cp); 1818a0ade7e9Syasuoka val32 += session->proto.pptp.snd_gap; 1819a0ade7e9Syasuoka PUTLONG(val32, cp0); 1820a0ade7e9Syasuoka session->proto.pptp.snd_nxt++; 1821a0ade7e9Syasuoka } 1822a0ade7e9Syasuoka if ((flags & PIPEX_GRE_AFLAG) != 0) { 1823a0ade7e9Syasuoka cp0 = cp; 1824a0ade7e9Syasuoka GETLONG(val32, cp); 1825a0ade7e9Syasuoka val32 += session->proto.pptp.rcv_gap; 1826a0ade7e9Syasuoka PUTLONG(val32, cp0); 1827a0ade7e9Syasuoka if (SEQ32_GT(val32, session->proto.pptp.rcv_acked)) 1828a0ade7e9Syasuoka session->proto.pptp.rcv_acked = val32; 1829a0ade7e9Syasuoka } 1830a0ade7e9Syasuoka 1831707c5f41Smvs mtx_leave(&session->pxs_mtx); 1832707c5f41Smvs 183363d4736dSyasuoka return (m0); 1834a0ade7e9Syasuoka } 1835a0ade7e9Syasuoka #endif /* PIPEX_PPTP */ 1836a0ade7e9Syasuoka 18378876230bShsuenaga #ifdef PIPEX_L2TP 18388876230bShsuenaga /*********************************************************************** 18398876230bShsuenaga * L2TP support 18408876230bShsuenaga ***********************************************************************/ 1841c02a3381Smvs void 18428876230bShsuenaga pipex_l2tp_output(struct mbuf *m0, struct pipex_session *session) 18438876230bShsuenaga { 18448876230bShsuenaga int hlen, plen, datalen; 18458876230bShsuenaga struct pipex_l2tp_header *l2tp = NULL; 18468876230bShsuenaga struct pipex_l2tp_seq_header *seq = NULL; 18478876230bShsuenaga struct udphdr *udp; 18488876230bShsuenaga struct ip *ip; 18498876230bShsuenaga #ifdef INET6 18508876230bShsuenaga struct ip6_hdr *ip6; 18518876230bShsuenaga #endif 1852a1c674a4Syasuoka struct m_tag *mtag; 18538876230bShsuenaga 18548876230bShsuenaga hlen = sizeof(struct pipex_l2tp_header) + 18558876230bShsuenaga ((pipex_session_is_l2tp_data_sequencing_on(session)) 18568876230bShsuenaga ? sizeof(struct pipex_l2tp_seq_header) : 0) + 18578876230bShsuenaga sizeof(struct udphdr) + 18589e07f154Syasuoka #ifdef INET6 18598876230bShsuenaga ((session->peer.sin6.sin6_family == AF_INET6) 18608876230bShsuenaga ? sizeof(struct ip6_hdr) : sizeof(struct ip)); 18619e07f154Syasuoka #else 18629e07f154Syasuoka sizeof(struct ip); 18639e07f154Syasuoka #endif 18648876230bShsuenaga 18658876230bShsuenaga datalen = 0; 18668876230bShsuenaga if (m0 != NULL) { 18678876230bShsuenaga datalen = m0->m_pkthdr.len; 18688876230bShsuenaga M_PREPEND(m0, hlen, M_NOWAIT); 18698876230bShsuenaga if (m0 == NULL) 18708876230bShsuenaga goto drop; 18718876230bShsuenaga } else { 18728876230bShsuenaga MGETHDR(m0, M_DONTWAIT, MT_DATA); 18738876230bShsuenaga if (m0 == NULL) 18748876230bShsuenaga goto drop; 18758876230bShsuenaga KASSERT(hlen <= MHLEN); 18768876230bShsuenaga m0->m_pkthdr.len = m0->m_len = hlen; 18778876230bShsuenaga } 18788876230bShsuenaga 18799e07f154Syasuoka #ifdef INET6 18808876230bShsuenaga hlen = (session->peer.sin6.sin6_family == AF_INET6) 18818876230bShsuenaga ? sizeof(struct ip6_hdr) : sizeof(struct ip); 18829e07f154Syasuoka #else 18839e07f154Syasuoka hlen = sizeof(struct ip); 18849e07f154Syasuoka #endif 18858876230bShsuenaga plen = datalen + sizeof(struct pipex_l2tp_header) + 18868876230bShsuenaga ((pipex_session_is_l2tp_data_sequencing_on(session)) 18878876230bShsuenaga ? sizeof(struct pipex_l2tp_seq_header) : 0); 18888876230bShsuenaga 18898876230bShsuenaga l2tp = (struct pipex_l2tp_header *) 18908876230bShsuenaga (mtod(m0, caddr_t) + hlen + sizeof(struct udphdr)); 18918876230bShsuenaga l2tp->flagsver = PIPEX_L2TP_VER | PIPEX_L2TP_FLAG_LENGTH; 18928876230bShsuenaga l2tp->length = htons(plen); 18938876230bShsuenaga l2tp->tunnel_id = htons(session->proto.l2tp.peer_tunnel_id); 18948876230bShsuenaga l2tp->session_id = htons(session->peer_session_id); 18958876230bShsuenaga if (pipex_session_is_l2tp_data_sequencing_on(session)) { 18968876230bShsuenaga seq = (struct pipex_l2tp_seq_header *)(l2tp + 1); 18978876230bShsuenaga l2tp->flagsver |= PIPEX_L2TP_FLAG_SEQUENCE; 1898707c5f41Smvs 1899707c5f41Smvs mtx_enter(&session->pxs_mtx); 19008876230bShsuenaga seq->ns = htons(session->proto.l2tp.ns_nxt); 19018876230bShsuenaga session->proto.l2tp.ns_nxt++; 19028876230bShsuenaga session->proto.l2tp.ns_gap++; 19038876230bShsuenaga session->proto.l2tp.nr_acked = session->proto.l2tp.nr_nxt - 1; 19048876230bShsuenaga seq->nr = htons(session->proto.l2tp.nr_acked); 1905707c5f41Smvs mtx_leave(&session->pxs_mtx); 19068876230bShsuenaga } 1907faf7e06fSmpi l2tp->flagsver = htons(l2tp->flagsver); 19088876230bShsuenaga 19098876230bShsuenaga plen += sizeof(struct udphdr); 19108876230bShsuenaga udp = (struct udphdr *)(mtod(m0, caddr_t) + hlen); 19118876230bShsuenaga udp->uh_sport = session->local.sin6.sin6_port; 19128876230bShsuenaga udp->uh_dport = session->peer.sin6.sin6_port; 19138876230bShsuenaga udp->uh_ulen = htons(plen); 191411d6b1faShenning udp->uh_sum = 0; 19158876230bShsuenaga 191611d6b1faShenning m0->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT; 1917002baba0Smvs m0->m_pkthdr.ph_ifidx = session->ifindex; 191885d4897fSmarkus #if NPF > 0 191985d4897fSmarkus pf_pkt_addr_changed(m0); 192085d4897fSmarkus #endif 19218876230bShsuenaga switch (session->peer.sin6.sin6_family) { 19228876230bShsuenaga case AF_INET: 19238876230bShsuenaga ip = mtod(m0, struct ip *); 19248876230bShsuenaga ip->ip_p = IPPROTO_UDP; 19258876230bShsuenaga ip->ip_src = session->local.sin4.sin_addr; 19268876230bShsuenaga ip->ip_dst = session->peer.sin4.sin_addr; 19278876230bShsuenaga ip->ip_len = htons(hlen + plen); 19288876230bShsuenaga ip->ip_ttl = MAXTTL; 19298876230bShsuenaga ip->ip_tos = 0; 19305cd62ff4Syasuoka ip->ip_off = 0; 19318876230bShsuenaga 1932707c5f41Smvs mtx_enter(&session->pxs_mtx); 1933a1c674a4Syasuoka if (session->proto.l2tp.ipsecflowinfo > 0) { 1934a1c674a4Syasuoka if ((mtag = m_tag_get(PACKET_TAG_IPSEC_FLOWINFO, 1935707c5f41Smvs sizeof(u_int32_t), M_NOWAIT)) == NULL) { 1936707c5f41Smvs mtx_leave(&session->pxs_mtx); 1937a1c674a4Syasuoka goto drop; 1938707c5f41Smvs } 1939707c5f41Smvs 1940a1c674a4Syasuoka *(u_int32_t *)(mtag + 1) = 1941a1c674a4Syasuoka session->proto.l2tp.ipsecflowinfo; 1942a1c674a4Syasuoka m_tag_prepend(m0, mtag); 1943a1c674a4Syasuoka } 1944707c5f41Smvs mtx_leave(&session->pxs_mtx); 1945a1c674a4Syasuoka 19460315bf92Sdlg ip_send(m0); 19478876230bShsuenaga break; 19488876230bShsuenaga #ifdef INET6 19498876230bShsuenaga case AF_INET6: 19508876230bShsuenaga ip6 = mtod(m0, struct ip6_hdr *); 19518876230bShsuenaga 19528876230bShsuenaga ip6->ip6_flow = 0; 19538876230bShsuenaga ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 19548876230bShsuenaga ip6->ip6_vfc |= IPV6_VERSION; 19558876230bShsuenaga ip6->ip6_nxt = IPPROTO_UDP; 19568876230bShsuenaga ip6->ip6_src = session->local.sin6.sin6_addr; 1957952c6363Sbluhm in6_embedscope(&ip6->ip6_dst, &session->peer.sin6, NULL, NULL); 19588876230bShsuenaga /* ip6->ip6_plen will be filled in ip6_output. */ 19598876230bShsuenaga 19600315bf92Sdlg ip6_send(m0); 19618876230bShsuenaga break; 19628876230bShsuenaga #endif 19638876230bShsuenaga } 1964967e9010Sdlg udpstat_inc(udps_opackets); 19658876230bShsuenaga 19668876230bShsuenaga if (datalen > 0) { /* network layer only */ 19678876230bShsuenaga /* countup statistics */ 1968df21f681Smvs counters_pkt(session->stat_counters, pxc_opackets, 1969df21f681Smvs pxc_obytes, datalen); 19708876230bShsuenaga } 19718876230bShsuenaga 19728876230bShsuenaga return; 19738876230bShsuenaga drop: 1974a1c674a4Syasuoka m_freem(m0); 1975df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 19768876230bShsuenaga } 19778876230bShsuenaga 19788876230bShsuenaga struct pipex_session * 1979*f168c03cSyasuoka pipex_l2tp_lookup_session(struct mbuf *m0, int off, struct sockaddr *sasrc) 19808876230bShsuenaga { 19818876230bShsuenaga struct pipex_session *session; 19828876230bShsuenaga uint16_t flags, session_id, ver; 19838876230bShsuenaga u_char *cp, buf[PIPEX_L2TP_MINLEN]; 1984*f168c03cSyasuoka int srcmatch = 0; 19858876230bShsuenaga 19868876230bShsuenaga if (m0->m_pkthdr.len < off + PIPEX_L2TP_MINLEN) { 19878876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, 19888876230bShsuenaga "<%s> packet length is too short", __func__)); 19898876230bShsuenaga goto not_ours; 19908876230bShsuenaga } 19918876230bShsuenaga 19928876230bShsuenaga /* get first 16bits of L2TP */ 19938876230bShsuenaga m_copydata(m0, off, sizeof(buf), buf); 19948876230bShsuenaga cp = buf; 19958876230bShsuenaga GETSHORT(flags, cp); 19968876230bShsuenaga ver = flags & PIPEX_L2TP_VER_MASK; 19978876230bShsuenaga 19988876230bShsuenaga /* l2tp version must be '2' */ 19998876230bShsuenaga if (ver != PIPEX_L2TP_VER) { 20008876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, 20018876230bShsuenaga "<%s> l2tp header wrong version %u.", __func__, ver)); 20028876230bShsuenaga goto not_ours; 20038876230bShsuenaga } 20048876230bShsuenaga if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0) 20058876230bShsuenaga goto not_ours; 20068876230bShsuenaga 20078876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_LENGTH) 20088876230bShsuenaga cp += 2; /* skip length field */ 20098876230bShsuenaga cp += 2; /* skip tunnel-id field */ 20108876230bShsuenaga GETSHORT(session_id, cp); /* get session-id field */ 20118876230bShsuenaga 20128876230bShsuenaga /* lookup pipex session table */ 20138876230bShsuenaga session = pipex_lookup_by_session_id(PIPEX_PROTO_L2TP, session_id); 20148876230bShsuenaga if (session == NULL) { 20158876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, 20168876230bShsuenaga "<%s> session not found (id=%d)", __func__, session_id)); 20178876230bShsuenaga goto not_ours; 20188876230bShsuenaga } 2019*f168c03cSyasuoka switch (sasrc->sa_family) { 2020*f168c03cSyasuoka case AF_INET: 2021*f168c03cSyasuoka if (session->peer.sa.sa_family == AF_INET && 2022*f168c03cSyasuoka session->peer.sin4.sin_addr.s_addr == 2023*f168c03cSyasuoka ((struct sockaddr_in *)sasrc)->sin_addr.s_addr) 2024*f168c03cSyasuoka srcmatch = 1; 2025*f168c03cSyasuoka break; 2026*f168c03cSyasuoka #ifdef INET6 2027*f168c03cSyasuoka case AF_INET6: 2028*f168c03cSyasuoka if (session->peer.sa.sa_family == AF_INET6 && 2029*f168c03cSyasuoka IN6_ARE_ADDR_EQUAL(&session->peer.sin6.sin6_addr, 2030*f168c03cSyasuoka &((struct sockaddr_in6 *)sasrc)->sin6_addr)) 2031*f168c03cSyasuoka srcmatch = 1; 2032*f168c03cSyasuoka break; 20338876230bShsuenaga #endif 2034*f168c03cSyasuoka } 2035*f168c03cSyasuoka if (!srcmatch) { 2036*f168c03cSyasuoka PIPEX_DBG((NULL, LOG_DEBUG, 2037*f168c03cSyasuoka "<%s> the source address of the session is not matched", 2038*f168c03cSyasuoka __func__)); 2039*f168c03cSyasuoka goto not_ours; 2040*f168c03cSyasuoka } 20418876230bShsuenaga 20428876230bShsuenaga return (session); 20438876230bShsuenaga 20448876230bShsuenaga not_ours: 20458876230bShsuenaga return (NULL); 20468876230bShsuenaga } 20478876230bShsuenaga 20488876230bShsuenaga struct mbuf * 2049a5018c90Syasuoka pipex_l2tp_input(struct mbuf *m0, int off0, struct pipex_session *session, 2050a5018c90Syasuoka uint32_t ipsecflowinfo) 20518876230bShsuenaga { 20528876230bShsuenaga struct pipex_l2tp_session *l2tp_session; 2053707c5f41Smvs int length = 0, offset = 0, hlen, nseq; 2054707c5f41Smvs u_char *cp, *nsp = NULL, *nrp = NULL; 20558876230bShsuenaga uint16_t flags, ns = 0, nr = 0; 2056e405d423Syasuoka int rewind = 0; 20578876230bShsuenaga 2058707c5f41Smvs mtx_enter(&session->pxs_mtx); 2059f6e6f8e7Smvs 20608876230bShsuenaga l2tp_session = &session->proto.l2tp; 2061b985d824Syasuoka if (l2tp_session->ipsecflowinfo > 0 && 2062b985d824Syasuoka l2tp_session->ipsecflowinfo != ipsecflowinfo) { 2063207bd73aSyasuoka pipex_session_log(session, LOG_DEBUG, 2064207bd73aSyasuoka "received message is %s", 2065207bd73aSyasuoka (ipsecflowinfo != 0)? "from invalid ipsec flow" : 2066207bd73aSyasuoka "without ipsec"); 2067207bd73aSyasuoka goto drop; 2068207bd73aSyasuoka } 20698876230bShsuenaga 20705c7fed39Sdlg m_copydata(m0, off0, sizeof(flags), &flags); 20718876230bShsuenaga 20728876230bShsuenaga flags = ntohs(flags) & PIPEX_L2TP_FLAG_MASK; 20738876230bShsuenaga KASSERT((flags & PIPEX_L2TP_FLAG_TYPE) == 0); 20748876230bShsuenaga 20758876230bShsuenaga hlen = 2; /* flags and version fields */ 20768876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_LENGTH) /* length */ 20778876230bShsuenaga hlen += 2; 20788876230bShsuenaga hlen += 4; /* tunnel-id and session-id */ 20798876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_SEQUENCE) /* ns and nr */ 20808876230bShsuenaga hlen += 4; 20818876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_OFFSET) /* offset */ 20828876230bShsuenaga hlen += 2; 20838876230bShsuenaga 20848876230bShsuenaga PIPEX_PULLUP(m0, off0 + hlen); 20858876230bShsuenaga if (m0 == NULL) 20868876230bShsuenaga goto drop; 20878876230bShsuenaga 20888876230bShsuenaga cp = mtod(m0, u_char *) + off0; 20898876230bShsuenaga cp += 2; /* flags and version */ 20908876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_LENGTH) 20918876230bShsuenaga GETSHORT(length, cp); 20928876230bShsuenaga else 20938876230bShsuenaga length = m0->m_pkthdr.len - off0; 20948876230bShsuenaga cp += 4; /* skip tunnel-id and session-id field */ 20958876230bShsuenaga 20968876230bShsuenaga /* pullup for seek sequences in header */ 20978876230bShsuenaga nseq = 0; 20988876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_SEQUENCE) { 20998876230bShsuenaga nsp = cp; 21008876230bShsuenaga GETSHORT(ns, cp); 21018876230bShsuenaga nrp = cp; 21028876230bShsuenaga GETSHORT(nr, cp); 21038876230bShsuenaga 21048876230bShsuenaga nr++; 21058876230bShsuenaga if (SEQ16_GT(nr, l2tp_session->ns_una) && 21068876230bShsuenaga SEQ16_LE(nr, l2tp_session->ns_nxt)) 21078876230bShsuenaga /* update 'ns_una' only if the ns is in valid range */ 21088876230bShsuenaga l2tp_session->ns_una = nr; 2109e405d423Syasuoka if (SEQ16_LT(ns, l2tp_session->nr_nxt)) { 2110e405d423Syasuoka rewind = 1; 2111e405d423Syasuoka if (SEQ16_LT(ns, 2112e405d423Syasuoka l2tp_session->nr_nxt - PIPEX_REWIND_LIMIT)) 21138876230bShsuenaga goto out_seq; 2114e405d423Syasuoka } 21158876230bShsuenaga 21168876230bShsuenaga ns++; 21178876230bShsuenaga nseq = SEQ16_SUB(ns, l2tp_session->nr_nxt); 2118e405d423Syasuoka if (!rewind) 21198876230bShsuenaga l2tp_session->nr_nxt = ns; 21208876230bShsuenaga } 21218876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_OFFSET) 21228876230bShsuenaga GETSHORT(offset, cp); 21238876230bShsuenaga 2124d5af01deSyasuoka length -= hlen + offset; 2125d5af01deSyasuoka hlen += off0 + offset; 2126707c5f41Smvs 2127707c5f41Smvs /* 2128707c5f41Smvs * The following pipex_common_input() will release `pxs_mtx' 2129707c5f41Smvs * deep within if the packet will be consumed. In the error 2130707c5f41Smvs * path lock will be held all the time. So increment `nr_gap' 2131707c5f41Smvs * here, and on the error path back it out, no atomicity will 2132707c5f41Smvs * be lost in all cases. 2133707c5f41Smvs */ 2134e405d423Syasuoka if (!rewind) 21358876230bShsuenaga session->proto.l2tp.nr_gap += nseq; 2136707c5f41Smvs m0 = pipex_common_input(session, m0, hlen, length, 1); 2137707c5f41Smvs if (m0 == NULL) { 2138707c5f41Smvs /* 2139707c5f41Smvs * pipex_common_input() releases lock if the 2140707c5f41Smvs * packet was consumed. 2141707c5f41Smvs */ 2142d5af01deSyasuoka return (NULL); 21438876230bShsuenaga } 21448876230bShsuenaga 2145e405d423Syasuoka if (rewind) 2146e405d423Syasuoka goto out_seq; 2147707c5f41Smvs else { 2148707c5f41Smvs /* The packet is not ours, backout `nr_gap'. */ 2149707c5f41Smvs session->proto.l2tp.nr_gap -= nseq; 2150707c5f41Smvs } 2151e405d423Syasuoka 21528876230bShsuenaga /* 21538876230bShsuenaga * overwrite sequence numbers to adjust a gap between pipex and 21548876230bShsuenaga * userland. 21558876230bShsuenaga */ 21568876230bShsuenaga if (flags & PIPEX_L2TP_FLAG_SEQUENCE) { 21578876230bShsuenaga --ns; --nr; /* revert original values */ 21588876230bShsuenaga ns -= l2tp_session->nr_gap; 21598876230bShsuenaga PUTSHORT(ns, nsp); 21608876230bShsuenaga 21618876230bShsuenaga if (l2tp_session->ns_nxt == l2tp_session->ns_una) { 21628876230bShsuenaga nr -= l2tp_session->ns_gap; 21638876230bShsuenaga l2tp_session->ul_ns_una = nr; 21648876230bShsuenaga } else { 21658876230bShsuenaga /* 21668876230bShsuenaga * There are sending packets they are not acked. 21678876230bShsuenaga * In this situation, (ack - snd_gap) may points 21688876230bShsuenaga * before sending window of userland. So we don't 21698876230bShsuenaga * update the ack number. 21708876230bShsuenaga */ 21718876230bShsuenaga nr = l2tp_session->ul_ns_una; 21728876230bShsuenaga } 21738876230bShsuenaga PUTSHORT(nr, nrp); 21748876230bShsuenaga } 21758876230bShsuenaga 2176707c5f41Smvs mtx_leave(&session->pxs_mtx); 2177707c5f41Smvs 21788876230bShsuenaga return (m0); 21798876230bShsuenaga out_seq: 21808876230bShsuenaga pipex_session_log(session, LOG_DEBUG, 21818876230bShsuenaga "Received bad data packet: out of sequence: seq=%u(%u-) " 21828876230bShsuenaga "ack=%u(%u-%u)", ns, l2tp_session->nr_nxt, nr, l2tp_session->ns_una, 21838876230bShsuenaga l2tp_session->ns_nxt); 21848876230bShsuenaga /* FALLTHROUGH */ 21858876230bShsuenaga drop: 2186707c5f41Smvs mtx_leave(&session->pxs_mtx); 2187707c5f41Smvs 21888876230bShsuenaga m_freem(m0); 2189df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 21908876230bShsuenaga 21918876230bShsuenaga return (NULL); 21928876230bShsuenaga } 21938876230bShsuenaga 21948876230bShsuenaga struct pipex_session * 21958876230bShsuenaga pipex_l2tp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst) 21968876230bShsuenaga { 21979c794535Sbluhm struct sockaddr_in sin; 21988876230bShsuenaga 21999c794535Sbluhm memset(&sin, 0, sizeof(sin)); 22009c794535Sbluhm sin.sin_len = sizeof(sin); 22019c794535Sbluhm sin.sin_family = AF_INET; 22029c794535Sbluhm sin.sin_addr = dst; 22038876230bShsuenaga 22049c794535Sbluhm return pipex_l2tp_userland_lookup_session(m0, sintosa(&sin)); 22058876230bShsuenaga } 22068876230bShsuenaga 22078876230bShsuenaga #ifdef INET6 22088876230bShsuenaga struct pipex_session * 22098876230bShsuenaga pipex_l2tp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst) 22108876230bShsuenaga { 22118876230bShsuenaga struct sockaddr_in6 sin6; 22128876230bShsuenaga 22139c794535Sbluhm memset(&sin6, 0, sizeof(sin6)); 22149c794535Sbluhm sin6.sin6_len = sizeof(sin6); 22158876230bShsuenaga sin6.sin6_family = AF_INET6; 22166c8dd0e9Sclaudio in6_recoverscope(&sin6, &dst); 22178876230bShsuenaga 22189c794535Sbluhm return pipex_l2tp_userland_lookup_session(m0, sin6tosa(&sin6)); 22198876230bShsuenaga } 22208876230bShsuenaga #endif 22218876230bShsuenaga 22227b163281Syasuoka struct pipex_session * 22238876230bShsuenaga pipex_l2tp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa) 22248876230bShsuenaga { 22258876230bShsuenaga struct pipex_l2tp_header l2tp; 22268876230bShsuenaga struct pipex_hash_head *list; 22278876230bShsuenaga struct pipex_session *session; 22288876230bShsuenaga uint16_t session_id, tunnel_id, flags; 22298876230bShsuenaga 22307b163281Syasuoka if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) 22317b163281Syasuoka return (NULL); 22327b163281Syasuoka 22338876230bShsuenaga /* pullup */ 22348876230bShsuenaga if (m0->m_pkthdr.len < sizeof(l2tp)) { 22358876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, 22368876230bShsuenaga "<%s> packet length is too short", __func__)); 22378876230bShsuenaga return (NULL); 22388876230bShsuenaga } 22398876230bShsuenaga 22408876230bShsuenaga /* get flags */ 22415c7fed39Sdlg m_copydata(m0, 0, sizeof(l2tp), &l2tp); 22428876230bShsuenaga flags = ntohs(l2tp.flagsver); 22438876230bShsuenaga 22448876230bShsuenaga /* l2tp version must be '2' */ 22458876230bShsuenaga if ((flags & PIPEX_L2TP_VER_MASK) != PIPEX_L2TP_VER) { 22468876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, 22478876230bShsuenaga "<%s> l2tp header wrong version.", __func__)); 22488876230bShsuenaga return (NULL); 22498876230bShsuenaga } 22508876230bShsuenaga /* We need L2TP data messages only */ 22518876230bShsuenaga if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0) 22528876230bShsuenaga return (NULL); 22538876230bShsuenaga /* No need to hook packets that don't have the sequence field */ 22548876230bShsuenaga if ((flags & PIPEX_L2TP_FLAG_SEQUENCE) == 0) 22558876230bShsuenaga return (NULL); 22568876230bShsuenaga 22578876230bShsuenaga session_id = ntohs(l2tp.session_id); 22588876230bShsuenaga tunnel_id = ntohs(l2tp.tunnel_id); 22598876230bShsuenaga 2260f6e6f8e7Smvs mtx_enter(&pipex_list_mtx); 2261f6e6f8e7Smvs 22628876230bShsuenaga list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa)); 22638876230bShsuenaga LIST_FOREACH(session, list, peer_addr_chain) { 22649c681c75Sbluhm if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0) 22658876230bShsuenaga continue; 22668876230bShsuenaga if (session->proto.l2tp.peer_tunnel_id != tunnel_id) 22678876230bShsuenaga continue; 22688876230bShsuenaga if (session->peer_session_id == session_id) 22698876230bShsuenaga break; 22708876230bShsuenaga } 2271f6e6f8e7Smvs 2272f6e6f8e7Smvs if (session != NULL) 2273f6e6f8e7Smvs refcnt_take(&session->pxs_refcnt); 2274f6e6f8e7Smvs 2275f6e6f8e7Smvs mtx_leave(&pipex_list_mtx); 2276f6e6f8e7Smvs 22778876230bShsuenaga #ifdef PIPEX_DEBUG 22788876230bShsuenaga if (session == NULL) { 22798876230bShsuenaga PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found " 22808876230bShsuenaga "(tunnel_id=%d, session_id=%d)", __func__, 22818876230bShsuenaga tunnel_id, session_id)); 22828876230bShsuenaga } 22838876230bShsuenaga #endif 22848876230bShsuenaga 22858876230bShsuenaga return (session); 22868876230bShsuenaga } 22878876230bShsuenaga 22888876230bShsuenaga struct mbuf * 22898876230bShsuenaga pipex_l2tp_userland_output(struct mbuf *m0, struct pipex_session *session) 22908876230bShsuenaga { 22918876230bShsuenaga struct pipex_l2tp_header *l2tp; 22928876230bShsuenaga struct pipex_l2tp_seq_header *seq; 22938876230bShsuenaga uint16_t ns, nr; 22948876230bShsuenaga 22958876230bShsuenaga /* check length */ 22968876230bShsuenaga PIPEX_PULLUP(m0, sizeof(struct pipex_l2tp_header) + 22978876230bShsuenaga sizeof(struct pipex_l2tp_seq_header)); 22988876230bShsuenaga if (m0 == NULL) 22998876230bShsuenaga return (NULL); 23008876230bShsuenaga 23018876230bShsuenaga l2tp = mtod(m0, struct pipex_l2tp_header *); 23028876230bShsuenaga KASSERT(ntohs(l2tp->flagsver) & PIPEX_L2TP_FLAG_SEQUENCE); 23038876230bShsuenaga 23048876230bShsuenaga /* 23058876230bShsuenaga * overwrite sequence numbers to adjust a gap between pipex and 23068876230bShsuenaga * userland. 23078876230bShsuenaga */ 23088876230bShsuenaga seq = (struct pipex_l2tp_seq_header *)(l2tp + 1); 23098876230bShsuenaga ns = ntohs(seq->ns); 23108876230bShsuenaga nr = ntohs(seq->nr); 23118876230bShsuenaga 2312707c5f41Smvs mtx_enter(&session->pxs_mtx); 2313707c5f41Smvs 23148876230bShsuenaga ns += session->proto.l2tp.ns_gap; 23158876230bShsuenaga seq->ns = htons(ns); 23168876230bShsuenaga session->proto.l2tp.ns_nxt++; 23178876230bShsuenaga 23188876230bShsuenaga nr += session->proto.l2tp.nr_gap; 23198876230bShsuenaga seq->nr = htons(nr); 23208876230bShsuenaga if (SEQ16_GT(nr, session->proto.l2tp.nr_acked)) 23218876230bShsuenaga session->proto.l2tp.nr_acked = nr; 23228876230bShsuenaga 2323707c5f41Smvs mtx_leave(&session->pxs_mtx); 2324707c5f41Smvs 23258876230bShsuenaga return (m0); 23268876230bShsuenaga } 23278876230bShsuenaga #endif /* PIPEX_L2TP */ 23288876230bShsuenaga 2329a0ade7e9Syasuoka #ifdef PIPEX_MPPE 2330a0ade7e9Syasuoka /********************************************************************** 2331a0ade7e9Syasuoka * MPPE 2332a0ade7e9Syasuoka ***********************************************************************/ 2333a0ade7e9Syasuoka #define PIPEX_COHERENCY_CNT_MASK 0x0fff 2334f7863625Smillert 2335f7863625Smillert static inline int 2336f7863625Smillert pipex_mppe_setkey(struct pipex_mppe *mppe) 2337f7863625Smillert { 2338f7863625Smillert rc4_keysetup(&mppe->rc4ctx, mppe->session_key, mppe->keylen); 2339f7863625Smillert 2340f7863625Smillert return (0); 2341f7863625Smillert } 2342f7863625Smillert 2343f7863625Smillert static inline int 2344f7863625Smillert pipex_mppe_setoldkey(struct pipex_mppe *mppe, uint16_t coher_cnt) 2345f7863625Smillert { 2346f7863625Smillert KASSERT(mppe->old_session_keys != NULL); 2347f7863625Smillert 2348f7863625Smillert rc4_keysetup(&mppe->rc4ctx, 2349f7863625Smillert mppe->old_session_keys[coher_cnt & PIPEX_MPPE_OLDKEYMASK], 2350f7863625Smillert mppe->keylen); 2351f7863625Smillert 2352f7863625Smillert return (0); 2353f7863625Smillert } 2354f7863625Smillert 2355f7863625Smillert static inline void 2356f7863625Smillert pipex_mppe_crypt(struct pipex_mppe *mppe, int len, u_char *indata, 2357f7863625Smillert u_char *outdata) 2358f7863625Smillert { 2359f7863625Smillert rc4_crypt(&mppe->rc4ctx, indata, outdata, len); 2360f7863625Smillert } 2361f7863625Smillert 2362c02a3381Smvs void 2363e405d423Syasuoka pipex_mppe_init(struct pipex_mppe *mppe, int stateless, int keylenbits, 2364e405d423Syasuoka u_char *master_key, int has_oldkey) 2365a0ade7e9Syasuoka { 2366e405d423Syasuoka memset(mppe, 0, sizeof(struct pipex_mppe)); 23672960d3c8Smvs mtx_init(&mppe->pxm_mtx, IPL_SOFTNET); 2368e405d423Syasuoka if (stateless) 23691935ef3dSmvs mppe->flags |= PIPEX_MPPE_STATELESS; 2370e405d423Syasuoka if (has_oldkey) 2371e405d423Syasuoka mppe->old_session_keys = 2372edccd885Suebayasi pool_get(&mppe_key_pool, PR_WAITOK); 2373e405d423Syasuoka else 2374e405d423Syasuoka mppe->old_session_keys = NULL; 2375e405d423Syasuoka memcpy(mppe->master_key, master_key, sizeof(mppe->master_key)); 2376a0ade7e9Syasuoka 2377e405d423Syasuoka mppe->keylenbits = keylenbits; 2378e405d423Syasuoka switch (keylenbits) { 2379a0ade7e9Syasuoka case 40: 2380a0ade7e9Syasuoka case 56: 2381a0ade7e9Syasuoka mppe->keylen = 8; 2382a0ade7e9Syasuoka break; 2383a0ade7e9Syasuoka case 128: 2384a0ade7e9Syasuoka mppe->keylen = 16; 2385a0ade7e9Syasuoka break; 2386a0ade7e9Syasuoka } 2387a0ade7e9Syasuoka 238863d4736dSyasuoka GetNewKeyFromSHA(mppe->master_key, mppe->master_key, mppe->keylen, 238963d4736dSyasuoka mppe->session_key); 2390a0ade7e9Syasuoka pipex_mppe_reduce_key(mppe); 2391e405d423Syasuoka pipex_mppe_setkey(mppe); 2392e405d423Syasuoka } 2393e405d423Syasuoka 2394e405d423Syasuoka void 2395e405d423Syasuoka pipex_session_init_mppe_recv(struct pipex_session *session, int stateless, 2396e405d423Syasuoka int keylenbits, u_char *master_key) 2397e405d423Syasuoka { 2398e405d423Syasuoka pipex_mppe_init(&session->mppe_recv, stateless, keylenbits, 2399e405d423Syasuoka master_key, stateless); 2400e405d423Syasuoka session->ppp_flags |= PIPEX_PPP_MPPE_ACCEPTED; 2401e405d423Syasuoka } 2402e405d423Syasuoka 2403e405d423Syasuoka void 2404e405d423Syasuoka pipex_session_init_mppe_send(struct pipex_session *session, int stateless, 2405e405d423Syasuoka int keylenbits, u_char *master_key) 2406e405d423Syasuoka { 2407e405d423Syasuoka pipex_mppe_init(&session->mppe_send, stateless, keylenbits, 2408e405d423Syasuoka master_key, 0); 2409e405d423Syasuoka session->ppp_flags |= PIPEX_PPP_MPPE_ENABLED; 2410a0ade7e9Syasuoka } 2411a0ade7e9Syasuoka 2412a0ade7e9Syasuoka #include <crypto/sha1.h> 2413a0ade7e9Syasuoka 2414a0ade7e9Syasuoka static u_char SHAPad1[] = { 2415a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2416a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2417a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2418a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2419a0ade7e9Syasuoka 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2420a0ade7e9Syasuoka }, SHAPad2[] = { 2421a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 2422a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 2423a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 2424a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 2425a0ade7e9Syasuoka 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 2426a0ade7e9Syasuoka }; 2427a0ade7e9Syasuoka 2428c02a3381Smvs void 242963d4736dSyasuoka GetNewKeyFromSHA(u_char *StartKey, u_char *SessionKey, int SessionKeyLength, 243063d4736dSyasuoka u_char *InterimKey) 2431a0ade7e9Syasuoka { 2432a0ade7e9Syasuoka u_char Digest[20]; 243363d4736dSyasuoka SHA1_CTX Context; 2434a0ade7e9Syasuoka 243563d4736dSyasuoka SHA1Init(&Context); 243663d4736dSyasuoka SHA1Update(&Context, StartKey, SessionKeyLength); 243763d4736dSyasuoka SHA1Update(&Context, SHAPad1, 40); 243863d4736dSyasuoka SHA1Update(&Context, SessionKey, SessionKeyLength); 243963d4736dSyasuoka SHA1Update(&Context, SHAPad2, 40); 244063d4736dSyasuoka SHA1Final(Digest, &Context); 2441a0ade7e9Syasuoka 244263d4736dSyasuoka memcpy(InterimKey, Digest, SessionKeyLength); 2443a0ade7e9Syasuoka } 2444a0ade7e9Syasuoka 2445c02a3381Smvs void 2446a0ade7e9Syasuoka pipex_mppe_reduce_key(struct pipex_mppe *mppe) 2447a0ade7e9Syasuoka { 2448a0ade7e9Syasuoka switch (mppe->keylenbits) { 2449a0ade7e9Syasuoka case 40: 24507704014bSmestre mppe->session_key[0] = 0xd1; 2451a0ade7e9Syasuoka mppe->session_key[1] = 0x26; 2452a0ade7e9Syasuoka mppe->session_key[2] = 0x9e; 24537704014bSmestre break; 2454a0ade7e9Syasuoka case 56: 2455a0ade7e9Syasuoka mppe->session_key[0] = 0xd1; 24567704014bSmestre break; 2457a0ade7e9Syasuoka } 2458a0ade7e9Syasuoka } 2459a0ade7e9Syasuoka 2460c02a3381Smvs void 2461a0ade7e9Syasuoka mppe_key_change(struct pipex_mppe *mppe) 2462a0ade7e9Syasuoka { 2463a0ade7e9Syasuoka u_char interim[16]; 2464e405d423Syasuoka struct rc4_ctx keychg; 2465a0ade7e9Syasuoka 2466a0ade7e9Syasuoka memset(&keychg, 0, sizeof(keychg)); 2467a0ade7e9Syasuoka 246863d4736dSyasuoka GetNewKeyFromSHA(mppe->master_key, mppe->session_key, mppe->keylen, 246963d4736dSyasuoka interim); 2470a0ade7e9Syasuoka 2471e405d423Syasuoka rc4_keysetup(&keychg, interim, mppe->keylen); 2472e405d423Syasuoka rc4_crypt(&keychg, interim, mppe->session_key, mppe->keylen); 247363d4736dSyasuoka 2474a0ade7e9Syasuoka pipex_mppe_reduce_key(mppe); 2475e405d423Syasuoka 2476e405d423Syasuoka if (mppe->old_session_keys) { 2477e405d423Syasuoka int idx = mppe->coher_cnt & PIPEX_MPPE_OLDKEYMASK; 2478e405d423Syasuoka memcpy(mppe->old_session_keys[idx], 2479e405d423Syasuoka mppe->session_key, PIPEX_MPPE_KEYLEN); 2480e405d423Syasuoka } 2481a0ade7e9Syasuoka } 2482a0ade7e9Syasuoka 2483707c5f41Smvs struct mbuf * 2484a0ade7e9Syasuoka pipex_mppe_input(struct mbuf *m0, struct pipex_session *session) 2485a0ade7e9Syasuoka { 2486a0ade7e9Syasuoka int pktloss, encrypt, flushed, m, n, len; 2487a0ade7e9Syasuoka struct pipex_mppe *mppe; 2488a0ade7e9Syasuoka uint16_t coher_cnt; 2489a0ade7e9Syasuoka struct mbuf *m1; 2490a0ade7e9Syasuoka u_char *cp; 2491e405d423Syasuoka int rewind = 0; 2492a0ade7e9Syasuoka 2493a0ade7e9Syasuoka /* pullup */ 2494a0ade7e9Syasuoka PIPEX_PULLUP(m0, sizeof(coher_cnt)); 2495a0ade7e9Syasuoka if (m0 == NULL) 2496a0ade7e9Syasuoka goto drop; 2497a0ade7e9Syasuoka 2498a0ade7e9Syasuoka mppe = &session->mppe_recv; 2499a0ade7e9Syasuoka /* get header information */ 2500a0ade7e9Syasuoka cp = mtod(m0, u_char *); 2501a0ade7e9Syasuoka GETSHORT(coher_cnt, cp); 2502a0ade7e9Syasuoka flushed = ((coher_cnt & 0x8000) != 0) ? 1 : 0; 2503a0ade7e9Syasuoka encrypt = ((coher_cnt & 0x1000) != 0) ? 1 : 0; 2504a0ade7e9Syasuoka coher_cnt &= PIPEX_COHERENCY_CNT_MASK; 2505a0ade7e9Syasuoka pktloss = 0; 2506a0ade7e9Syasuoka 25072960d3c8Smvs mtx_enter(&mppe->pxm_mtx); 25082960d3c8Smvs 2509a0ade7e9Syasuoka PIPEX_MPPE_DBG((session, LOG_DEBUG, "in coher_cnt=%03x %s%s", 2510a0ade7e9Syasuoka mppe->coher_cnt, (flushed) ? "[flushed]" : "", 2511a0ade7e9Syasuoka (encrypt) ? "[encrypt]" : "")); 2512a0ade7e9Syasuoka 2513a0ade7e9Syasuoka if (encrypt == 0) { 25142960d3c8Smvs mtx_leave(&mppe->pxm_mtx); 2515a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG, 2516a0ade7e9Syasuoka "Received unexpected MPPE packet.(no ecrypt)"); 2517a0ade7e9Syasuoka goto drop; 2518a0ade7e9Syasuoka } 2519a0ade7e9Syasuoka 2520a0ade7e9Syasuoka /* adjust mbuf */ 2521a0ade7e9Syasuoka m_adj(m0, sizeof(coher_cnt)); 2522a0ade7e9Syasuoka 2523a0ade7e9Syasuoka /* 2524a0ade7e9Syasuoka * L2TP data session may be used without sequencing, PPP frames may 2525a0ade7e9Syasuoka * arrive in disorder. The 'coherency counter' of MPPE detects such 2526a0ade7e9Syasuoka * situations, but we cannot distinguish between 'disorder' and 2527a0ade7e9Syasuoka * 'packet loss' exactly. 2528a0ade7e9Syasuoka * 2529a0ade7e9Syasuoka * When 'coherency counter' detects lost packets greater than 2530a0ade7e9Syasuoka * (4096 - 256), we treat as 'disorder' otherwise treat as 2531a0ade7e9Syasuoka * 'packet loss'. 2532a0ade7e9Syasuoka */ 2533a0ade7e9Syasuoka { 2534a0ade7e9Syasuoka int coher_cnt0; 2535a0ade7e9Syasuoka 2536a0ade7e9Syasuoka coher_cnt0 = coher_cnt; 2537a0ade7e9Syasuoka if (coher_cnt < mppe->coher_cnt) 2538a0ade7e9Syasuoka coher_cnt0 += 0x1000; 2539a0ade7e9Syasuoka if (coher_cnt0 - mppe->coher_cnt > 0x0f00) { 25401935ef3dSmvs if ((mppe->flags & PIPEX_MPPE_STATELESS) == 0 || 2541e405d423Syasuoka coher_cnt0 - mppe->coher_cnt 2542e405d423Syasuoka <= 0x1000 - PIPEX_MPPE_NOLDKEY) { 2543a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG, 2544a0ade7e9Syasuoka "Workaround the out-of-sequence PPP framing problem: " 2545a0ade7e9Syasuoka "%d => %d", mppe->coher_cnt, coher_cnt); 25462960d3c8Smvs mtx_leave(&mppe->pxm_mtx); 2547a0ade7e9Syasuoka goto drop; 2548a0ade7e9Syasuoka } 2549e405d423Syasuoka rewind = 1; 2550a0ade7e9Syasuoka } 2551e405d423Syasuoka } 2552e405d423Syasuoka 25531935ef3dSmvs if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) { 2554e405d423Syasuoka if (!rewind) { 2555a0ade7e9Syasuoka mppe_key_change(mppe); 2556a0ade7e9Syasuoka while (mppe->coher_cnt != coher_cnt) { 2557a0ade7e9Syasuoka mppe->coher_cnt++; 2558a0ade7e9Syasuoka mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK; 2559e405d423Syasuoka mppe_key_change(mppe); 2560a0ade7e9Syasuoka pktloss++; 2561a0ade7e9Syasuoka } 2562e405d423Syasuoka } 2563e405d423Syasuoka pipex_mppe_setoldkey(mppe, coher_cnt); 2564a0ade7e9Syasuoka } else { 2565a0ade7e9Syasuoka if (flushed) { 2566a0ade7e9Syasuoka if (coher_cnt < mppe->coher_cnt) { 2567a0ade7e9Syasuoka coher_cnt += 0x1000; 2568a0ade7e9Syasuoka } 2569a0ade7e9Syasuoka pktloss += coher_cnt - mppe->coher_cnt; 2570a0ade7e9Syasuoka m = mppe->coher_cnt / 256; 2571a0ade7e9Syasuoka n = coher_cnt / 256; 2572a0ade7e9Syasuoka while (m++ < n) 2573a0ade7e9Syasuoka mppe_key_change(mppe); 2574a0ade7e9Syasuoka 2575a0ade7e9Syasuoka coher_cnt &= PIPEX_COHERENCY_CNT_MASK; 2576a0ade7e9Syasuoka mppe->coher_cnt = coher_cnt; 2577a0ade7e9Syasuoka } else if (mppe->coher_cnt != coher_cnt) { 25782960d3c8Smvs int ccp_id; 25792960d3c8Smvs 25802960d3c8Smvs mtx_leave(&mppe->pxm_mtx); 25812960d3c8Smvs 2582a0ade7e9Syasuoka /* Send CCP ResetReq */ 2583a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetReq")); 25842960d3c8Smvs 25852960d3c8Smvs mtx_enter(&session->pxs_mtx); 25862960d3c8Smvs ccp_id = session->ccp_id; 25872960d3c8Smvs session->ccp_id++; 25882960d3c8Smvs mtx_leave(&session->pxs_mtx); 25892960d3c8Smvs 25902960d3c8Smvs pipex_ccp_output(session, CCP_RESETREQ, ccp_id); 2591a0ade7e9Syasuoka goto drop; 2592a0ade7e9Syasuoka } 2593a0ade7e9Syasuoka if ((coher_cnt & 0xff) == 0xff) { 2594a0ade7e9Syasuoka mppe_key_change(mppe); 2595a0ade7e9Syasuoka flushed = 1; 2596a0ade7e9Syasuoka } 2597e405d423Syasuoka if (flushed) 2598e405d423Syasuoka pipex_mppe_setkey(mppe); 2599a0ade7e9Syasuoka } 2600e405d423Syasuoka 2601a0ade7e9Syasuoka if (pktloss > 1000) { 2602a0ade7e9Syasuoka pipex_session_log(session, LOG_DEBUG, 2603a0ade7e9Syasuoka "%d packets loss.", pktloss); 2604a0ade7e9Syasuoka } 2605a0ade7e9Syasuoka 2606a0ade7e9Syasuoka /* decrypt ppp payload */ 2607a0ade7e9Syasuoka for (m1 = m0; m1; m1 = m1->m_next) { 2608a0ade7e9Syasuoka cp = mtod(m1, u_char *); 2609a0ade7e9Syasuoka len = m1->m_len; 2610e405d423Syasuoka pipex_mppe_crypt(mppe, len, cp, cp); 2611a0ade7e9Syasuoka } 2612a0ade7e9Syasuoka 2613e405d423Syasuoka if (!rewind) { 2614a0ade7e9Syasuoka /* update coher_cnt */ 2615a0ade7e9Syasuoka mppe->coher_cnt++; 2616a0ade7e9Syasuoka mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK; 2617e405d423Syasuoka } 26182960d3c8Smvs 26192960d3c8Smvs mtx_leave(&mppe->pxm_mtx); 26202960d3c8Smvs 2621363bb3c1Syasuoka if (m0->m_pkthdr.len < PIPEX_PPPMINLEN) 2622363bb3c1Syasuoka goto drop; 2623a0ade7e9Syasuoka 2624707c5f41Smvs return (m0); 2625a0ade7e9Syasuoka drop: 2626a0ade7e9Syasuoka m_freem(m0); 2627707c5f41Smvs return (NULL); 2628a0ade7e9Syasuoka } 2629a0ade7e9Syasuoka 2630707c5f41Smvs struct mbuf * 263137f64355Syasuoka pipex_mppe_output(struct mbuf *m0, struct pipex_session *session, 263237f64355Syasuoka uint16_t protocol) 2633a0ade7e9Syasuoka { 2634a0ade7e9Syasuoka int encrypt, flushed, len; 263537f64355Syasuoka struct mppe_header { 2636a0ade7e9Syasuoka uint16_t coher_cnt; 263737f64355Syasuoka uint16_t protocol; 263837f64355Syasuoka } __packed *hdr; 2639a0ade7e9Syasuoka u_char *cp; 2640a0ade7e9Syasuoka struct pipex_mppe *mppe; 2641a0ade7e9Syasuoka struct mbuf *m; 2642a0ade7e9Syasuoka 2643a0ade7e9Syasuoka mppe = &session->mppe_send; 2644a0ade7e9Syasuoka 2645a0ade7e9Syasuoka /* 2646a0ade7e9Syasuoka * create a deep-copy if the mbuf has a shared mbuf cluster. 2647678831beSjsg * this is required to handle cases of tcp retransmission. 2648a0ade7e9Syasuoka */ 2649a0ade7e9Syasuoka for (m = m0; m != NULL; m = m->m_next) { 2650a0ade7e9Syasuoka if (M_READONLY(m)) { 26517bb226caSdlg m = m_dup_pkt(m0, max_linkhdr, M_NOWAIT); 2652c31e5583Sdlg m_freem(m0); 2653a0ade7e9Syasuoka if (m == NULL) 2654707c5f41Smvs return (NULL); 2655a0ade7e9Syasuoka m0 = m; 2656a0ade7e9Syasuoka break; 2657a0ade7e9Syasuoka } 2658a0ade7e9Syasuoka } 2659cb76d290Syasuoka /* prepend mppe header */ 2660cb76d290Syasuoka M_PREPEND(m0, sizeof(struct mppe_header), M_NOWAIT); 2661cb76d290Syasuoka if (m0 == NULL) 2662707c5f41Smvs return (NULL); 2663cb76d290Syasuoka hdr = mtod(m0, struct mppe_header *); 2664cb76d290Syasuoka hdr->protocol = protocol; 2665a0ade7e9Syasuoka 2666a0ade7e9Syasuoka /* check coherency counter */ 2667a0ade7e9Syasuoka flushed = 0; 2668a0ade7e9Syasuoka encrypt = 1; 2669a0ade7e9Syasuoka 26702960d3c8Smvs mtx_enter(&mppe->pxm_mtx); 26712960d3c8Smvs 26721935ef3dSmvs if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) { 2673a0ade7e9Syasuoka flushed = 1; 2674a0ade7e9Syasuoka mppe_key_change(mppe); 2675a0ade7e9Syasuoka } else { 2676a0ade7e9Syasuoka if ((mppe->coher_cnt % 0x100) == 0xff) { 2677a0ade7e9Syasuoka flushed = 1; 2678a0ade7e9Syasuoka mppe_key_change(mppe); 26791935ef3dSmvs } else if ((mppe->flags & PIPEX_MPPE_RESETREQ) != 0) { 2680a0ade7e9Syasuoka flushed = 1; 26811935ef3dSmvs mppe->flags &= ~PIPEX_MPPE_RESETREQ; 2682a0ade7e9Syasuoka } 2683a0ade7e9Syasuoka } 2684a0ade7e9Syasuoka 2685a0ade7e9Syasuoka if (flushed) 2686e405d423Syasuoka pipex_mppe_setkey(mppe); 2687a0ade7e9Syasuoka 2688a0ade7e9Syasuoka PIPEX_MPPE_DBG((session, LOG_DEBUG, "out coher_cnt=%03x %s%s", 2689a0ade7e9Syasuoka mppe->coher_cnt, (flushed) ? "[flushed]" : "", 2690a0ade7e9Syasuoka (encrypt) ? "[encrypt]" : "")); 2691a0ade7e9Syasuoka 2692a0ade7e9Syasuoka /* setup header information */ 269337f64355Syasuoka hdr->coher_cnt = (mppe->coher_cnt++) & PIPEX_COHERENCY_CNT_MASK; 269437f64355Syasuoka hdr->coher_cnt &= PIPEX_COHERENCY_CNT_MASK; 2695a0ade7e9Syasuoka if (flushed) 269637f64355Syasuoka hdr->coher_cnt |= 0x8000; 2697a0ade7e9Syasuoka if (encrypt) 269837f64355Syasuoka hdr->coher_cnt |= 0x1000; 2699a0ade7e9Syasuoka 2700faf7e06fSmpi hdr->protocol = htons(hdr->protocol); 2701faf7e06fSmpi hdr->coher_cnt = htons(hdr->coher_cnt); 2702a0ade7e9Syasuoka 2703a0ade7e9Syasuoka /* encrypt chain */ 270437f64355Syasuoka for (m = m0; m; m = m->m_next) { 2705a0ade7e9Syasuoka cp = mtod(m, u_char *); 2706a0ade7e9Syasuoka len = m->m_len; 270737f64355Syasuoka if (m == m0 && len > offsetof(struct mppe_header, protocol)) { 270837f64355Syasuoka len -= offsetof(struct mppe_header, protocol); 270937f64355Syasuoka cp += offsetof(struct mppe_header, protocol); 271037f64355Syasuoka } 2711e405d423Syasuoka pipex_mppe_crypt(mppe, len, cp, cp); 2712a0ade7e9Syasuoka } 2713a0ade7e9Syasuoka 27142960d3c8Smvs mtx_leave(&mppe->pxm_mtx); 27152960d3c8Smvs 2716707c5f41Smvs return (m0); 2717a0ade7e9Syasuoka } 2718a0ade7e9Syasuoka 2719c02a3381Smvs void 2720a0ade7e9Syasuoka pipex_ccp_input(struct mbuf *m0, struct pipex_session *session) 2721a0ade7e9Syasuoka { 2722a0ade7e9Syasuoka u_char *cp; 2723a0ade7e9Syasuoka int code, id, len; 2724a0ade7e9Syasuoka 2725a0ade7e9Syasuoka if (m0->m_pkthdr.len < PPP_HDRLEN) 2726a0ade7e9Syasuoka goto drop; 2727a0ade7e9Syasuoka if ((m0 = m_pullup(m0, PPP_HDRLEN)) == NULL) 2728a0ade7e9Syasuoka goto drop; 2729a0ade7e9Syasuoka 2730a0ade7e9Syasuoka cp = mtod(m0, u_char *); 2731a0ade7e9Syasuoka GETCHAR(code, cp); 2732a0ade7e9Syasuoka GETCHAR(id, cp); 2733a0ade7e9Syasuoka GETSHORT(len, cp); 2734a0ade7e9Syasuoka 2735a0ade7e9Syasuoka switch (code) { 2736a0ade7e9Syasuoka case CCP_RESETREQ: 2737a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetReq")); 27382960d3c8Smvs mtx_enter(&session->mppe_send.pxm_mtx); 27391935ef3dSmvs session->mppe_send.flags |= PIPEX_MPPE_RESETREQ; 27402960d3c8Smvs mtx_leave(&session->mppe_send.pxm_mtx); 2741a0ade7e9Syasuoka #ifndef PIPEX_NO_CCP_RESETACK 2742a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetAck")); 2743a0ade7e9Syasuoka pipex_ccp_output(session, CCP_RESETACK, id); 2744a0ade7e9Syasuoka #endif 2745a0ade7e9Syasuoka /* ignore error */ 2746a0ade7e9Syasuoka break; 2747a0ade7e9Syasuoka case CCP_RESETACK: 2748a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetAck")); 2749a0ade7e9Syasuoka break; 2750a0ade7e9Syasuoka default: 2751a0ade7e9Syasuoka PIPEX_DBG((session, LOG_DEBUG, "CCP Recv code=%d", code)); 2752a0ade7e9Syasuoka goto drop; 2753a0ade7e9Syasuoka } 2754a0ade7e9Syasuoka m_freem(m0); 2755a0ade7e9Syasuoka 2756a0ade7e9Syasuoka return; 2757a0ade7e9Syasuoka drop: 2758a0ade7e9Syasuoka m_freem(m0); 2759df21f681Smvs counters_inc(session->stat_counters, pxc_ierrors); 2760a0ade7e9Syasuoka } 2761a0ade7e9Syasuoka 2762c02a3381Smvs int 2763a0ade7e9Syasuoka pipex_ccp_output(struct pipex_session *session, int code, int id) 2764a0ade7e9Syasuoka { 2765a0ade7e9Syasuoka u_char *cp; 2766a0ade7e9Syasuoka struct mbuf *m; 2767a0ade7e9Syasuoka 2768a0ade7e9Syasuoka MGETHDR(m, M_DONTWAIT, MT_DATA); 2769a0ade7e9Syasuoka if (m == NULL) { 2770df21f681Smvs counters_inc(session->stat_counters, pxc_oerrors); 277163d4736dSyasuoka return (1); 2772a0ade7e9Syasuoka } 2773a0ade7e9Syasuoka m->m_pkthdr.len = m->m_len = 4; 2774a0ade7e9Syasuoka cp = mtod(m, u_char *); 2775a0ade7e9Syasuoka PUTCHAR(code, cp); 2776a0ade7e9Syasuoka PUTCHAR(id, cp); 2777a0ade7e9Syasuoka PUTSHORT(4, cp); 2778a0ade7e9Syasuoka 2779a0ade7e9Syasuoka pipex_ppp_output(m, session, PPP_CCP); 2780a0ade7e9Syasuoka 278163d4736dSyasuoka return (0); 2782a0ade7e9Syasuoka } 2783a0ade7e9Syasuoka #endif 2784a0ade7e9Syasuoka /*********************************************************************** 2785678831beSjsg * Miscellaneous functions 2786a0ade7e9Syasuoka ***********************************************************************/ 2787a0ade7e9Syasuoka /* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */ 2788a0ade7e9Syasuoka /* 2789a0ade7e9Syasuoka * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org> 2790a0ade7e9Syasuoka * All rights reserved. 2791a0ade7e9Syasuoka * 2792a0ade7e9Syasuoka * Redistribution and use in source and binary forms, with or without 2793a0ade7e9Syasuoka * modification, are permitted provided that the following conditions 2794a0ade7e9Syasuoka * are met: 2795a0ade7e9Syasuoka * 1. Redistributions of source code must retain the above copyright 2796a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer. 2797a0ade7e9Syasuoka * 2. Redistributions in binary form must reproduce the above copyright 2798a0ade7e9Syasuoka * notice, this list of conditions and the following disclaimer in the 2799a0ade7e9Syasuoka * documentation and/or other materials provided with the distribution. 2800a0ade7e9Syasuoka * 2801a0ade7e9Syasuoka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2802a0ade7e9Syasuoka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2803a0ade7e9Syasuoka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2804a0ade7e9Syasuoka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2805a0ade7e9Syasuoka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2806a0ade7e9Syasuoka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2807a0ade7e9Syasuoka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2808a0ade7e9Syasuoka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2809a0ade7e9Syasuoka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2810a0ade7e9Syasuoka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2811a0ade7e9Syasuoka * SUCH DAMAGE. 2812a0ade7e9Syasuoka * 2813a0ade7e9Syasuoka * $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $ 2814a0ade7e9Syasuoka */ 2815a0ade7e9Syasuoka #define TCP_OPTLEN_IN_SEGMENT 12 /* timestamp option and padding */ 2816a0ade7e9Syasuoka #define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \ 2817a0ade7e9Syasuoka TCP_OPTLEN_IN_SEGMENT) 2818a0ade7e9Syasuoka /* 2819a0ade7e9Syasuoka * The following macro is used to update an internet checksum. "acc" is a 2820a0ade7e9Syasuoka * 32-bit accumulation of all the changes to the checksum (adding in old 2821a0ade7e9Syasuoka * 16-bit words and subtracting out new words), and "cksum" is the checksum 2822a0ade7e9Syasuoka * value to be updated. 2823a0ade7e9Syasuoka */ 2824a0ade7e9Syasuoka #define ADJUST_CHECKSUM(acc, cksum) { \ 2825a0ade7e9Syasuoka acc += cksum; \ 2826a0ade7e9Syasuoka if (acc < 0) { \ 2827a0ade7e9Syasuoka acc = -acc; \ 2828a0ade7e9Syasuoka acc = (acc >> 16) + (acc & 0xffff); \ 2829a0ade7e9Syasuoka acc += acc >> 16; \ 2830a0ade7e9Syasuoka cksum = (u_short) ~acc; \ 2831a0ade7e9Syasuoka } else { \ 2832a0ade7e9Syasuoka acc = (acc >> 16) + (acc & 0xffff); \ 2833a0ade7e9Syasuoka acc += acc >> 16; \ 2834a0ade7e9Syasuoka cksum = (u_short) acc; \ 2835a0ade7e9Syasuoka } \ 2836a0ade7e9Syasuoka } 2837a0ade7e9Syasuoka 2838a0ade7e9Syasuoka /* 2839a0ade7e9Syasuoka * Rewrite max-segment-size TCP option to avoid PMTU blackhole issues. 2840a0ade7e9Syasuoka * The mtu parameter should be the MTU bottleneck (as far as we know) 2841a0ade7e9Syasuoka * on the link between the source and the destination. 2842a0ade7e9Syasuoka */ 2843c02a3381Smvs struct mbuf * 2844a0ade7e9Syasuoka adjust_tcp_mss(struct mbuf *m0, int mtu) 2845a0ade7e9Syasuoka { 2846a0ade7e9Syasuoka int opt, optlen, acc, mss, maxmss, lpktp; 2847a0ade7e9Syasuoka struct ip *pip; 2848a0ade7e9Syasuoka struct tcphdr *th; 2849a0ade7e9Syasuoka u_char *pktp, *mssp; 2850a0ade7e9Syasuoka u_int16_t ip_off; 2851a0ade7e9Syasuoka 2852a0ade7e9Syasuoka lpktp = sizeof(struct ip) + sizeof(struct tcphdr) + PIPEX_TCP_OPTLEN; 2853a0ade7e9Syasuoka lpktp = MIN(lpktp, m0->m_pkthdr.len); 2854a0ade7e9Syasuoka 2855a0ade7e9Syasuoka PIPEX_PULLUP(m0, lpktp); 2856a0ade7e9Syasuoka if (m0 == NULL) 2857a0ade7e9Syasuoka goto drop; 2858a0ade7e9Syasuoka 2859a0ade7e9Syasuoka pktp = mtod(m0, char *); 2860a0ade7e9Syasuoka pip = (struct ip *)pktp; 2861a0ade7e9Syasuoka ip_off = ntohs(pip->ip_off); 2862a0ade7e9Syasuoka 2863a0ade7e9Syasuoka /* Non TCP or fragmented packet must not have a MSS option */ 2864a0ade7e9Syasuoka if (pip->ip_p != IPPROTO_TCP || 2865a0ade7e9Syasuoka (ip_off & IP_MF) != 0 || (ip_off & IP_OFFMASK) != 0) 2866a0ade7e9Syasuoka goto handled; 2867a0ade7e9Syasuoka 2868a0ade7e9Syasuoka pktp += pip->ip_hl << 2; 2869a0ade7e9Syasuoka lpktp -= pip->ip_hl << 2; 2870a0ade7e9Syasuoka 2871a0ade7e9Syasuoka /* packet is broken */ 2872a0ade7e9Syasuoka if (sizeof(struct tcphdr) > lpktp) 2873a0ade7e9Syasuoka goto drop; 2874a0ade7e9Syasuoka th = (struct tcphdr *)pktp; 2875a0ade7e9Syasuoka 2876a0ade7e9Syasuoka /* 2877a0ade7e9Syasuoka * As RFC 973, a MSS field must only be sent in the initial 2878a0ade7e9Syasuoka * connection request(it must be with SYN). 2879a0ade7e9Syasuoka */ 2880a0ade7e9Syasuoka if ((th->th_flags & TH_SYN) == 0) 2881a0ade7e9Syasuoka goto handled; 2882a0ade7e9Syasuoka 2883e8c0e2e5Syasuoka lpktp = MIN(th->th_off << 4, lpktp); 2884e8c0e2e5Syasuoka 2885a0ade7e9Syasuoka pktp += sizeof(struct tcphdr); 2886a0ade7e9Syasuoka lpktp -= sizeof(struct tcphdr); 2887a0ade7e9Syasuoka while (lpktp >= TCPOLEN_MAXSEG) { 2888a0ade7e9Syasuoka GETCHAR(opt, pktp); 2889a0ade7e9Syasuoka switch (opt) { 2890a0ade7e9Syasuoka case TCPOPT_MAXSEG: 2891a0ade7e9Syasuoka GETCHAR(optlen, pktp); 2892a0ade7e9Syasuoka mssp = pktp; /* mss place holder */ 2893a0ade7e9Syasuoka GETSHORT(mss, pktp); 2894a0ade7e9Syasuoka maxmss = MAXMSS(mtu); 2895a0ade7e9Syasuoka if (mss > maxmss) { 2896a0ade7e9Syasuoka PIPEX_DBG((NULL, LOG_DEBUG, 2897a0ade7e9Syasuoka "change tcp-mss %d => %d", mss, maxmss)); 2898a0ade7e9Syasuoka PUTSHORT(maxmss, mssp); 2899a0ade7e9Syasuoka acc = htons(mss); 2900a0ade7e9Syasuoka acc -= htons(maxmss); 2901a0ade7e9Syasuoka ADJUST_CHECKSUM(acc, th->th_sum); 2902a0ade7e9Syasuoka } 2903a0ade7e9Syasuoka goto handled; 2904a0ade7e9Syasuoka /* NOTREACHED */ 2905a0ade7e9Syasuoka case TCPOPT_EOL: 2906a0ade7e9Syasuoka goto handled; 2907a0ade7e9Syasuoka /* NOTREACHED */ 2908a0ade7e9Syasuoka case TCPOPT_NOP: 2909a0ade7e9Syasuoka lpktp--; 2910a0ade7e9Syasuoka break; 2911a0ade7e9Syasuoka default: 2912a0ade7e9Syasuoka GETCHAR(optlen, pktp); 2913e8c0e2e5Syasuoka if (optlen < 2) /* packet is broken */ 2914e8c0e2e5Syasuoka goto drop; 2915e8c0e2e5Syasuoka pktp += optlen - 2; 2916a0ade7e9Syasuoka lpktp -= optlen; 2917a0ade7e9Syasuoka break; 2918a0ade7e9Syasuoka } 2919a0ade7e9Syasuoka } 2920a0ade7e9Syasuoka 2921a0ade7e9Syasuoka handled: 292263d4736dSyasuoka return (m0); 2923a0ade7e9Syasuoka 2924a0ade7e9Syasuoka drop: 2925a0ade7e9Syasuoka m_freem(m0); 292663d4736dSyasuoka return (NULL); 2927a0ade7e9Syasuoka } 2928a0ade7e9Syasuoka 2929a0ade7e9Syasuoka /* 2930a0ade7e9Syasuoka * Check whether a packet should reset idle timer 2931a0ade7e9Syasuoka * Returns 1 to don't reset timer (i.e. the packet is "idle" packet) 2932a0ade7e9Syasuoka */ 2933c02a3381Smvs struct mbuf * 2934a0ade7e9Syasuoka ip_is_idle_packet(struct mbuf *m0, int *ris_idle) 2935a0ade7e9Syasuoka { 2936a0ade7e9Syasuoka u_int16_t ip_off; 2937a0ade7e9Syasuoka const struct udphdr *uh; 2938a0ade7e9Syasuoka struct ip *pip; 2939a0ade7e9Syasuoka int len; 2940a0ade7e9Syasuoka 2941a0ade7e9Syasuoka /* pullup ip header */ 2942a0ade7e9Syasuoka len = sizeof(struct ip); 2943a0ade7e9Syasuoka PIPEX_PULLUP(m0, len); 2944a0ade7e9Syasuoka if (m0 == NULL) 2945a0ade7e9Syasuoka goto error; 2946a0ade7e9Syasuoka pip = mtod(m0, struct ip *); 2947a0ade7e9Syasuoka 2948a0ade7e9Syasuoka /* 2949a0ade7e9Syasuoka * the packet which fragmentations was not the idle packet. 2950a0ade7e9Syasuoka */ 2951a0ade7e9Syasuoka ip_off = ntohs(pip->ip_off); 2952a0ade7e9Syasuoka if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0)) 2953a0ade7e9Syasuoka goto is_active; 2954a0ade7e9Syasuoka 2955a0ade7e9Syasuoka switch (pip->ip_p) { 2956a0ade7e9Syasuoka case IPPROTO_IGMP: 2957a0ade7e9Syasuoka goto is_active; 2958a0ade7e9Syasuoka case IPPROTO_ICMP: 2959a0ade7e9Syasuoka len = pip->ip_hl * 4 + 8; 2960a0ade7e9Syasuoka PIPEX_PULLUP(m0, len); 2961a0ade7e9Syasuoka if (m0 == NULL) 2962a0ade7e9Syasuoka goto error; 2963581bf0faSyasuoka pip = mtod(m0, struct ip *); 2964a0ade7e9Syasuoka 2965a0ade7e9Syasuoka switch (((unsigned char *) pip)[pip->ip_hl * 4]) { 2966a0ade7e9Syasuoka case 0: /* Echo Reply */ 2967a0ade7e9Syasuoka case 8: /* Echo Request */ 2968a0ade7e9Syasuoka goto is_active; 2969a0ade7e9Syasuoka default: 2970a0ade7e9Syasuoka goto is_idle; 2971a0ade7e9Syasuoka } 2972a0ade7e9Syasuoka 2973a0ade7e9Syasuoka case IPPROTO_UDP: 2974a0ade7e9Syasuoka case IPPROTO_TCP: 2975a0ade7e9Syasuoka len = pip->ip_hl * 4 + sizeof(struct udphdr); 2976a0ade7e9Syasuoka PIPEX_PULLUP(m0, len); 2977a0ade7e9Syasuoka if (m0 == NULL) 2978a0ade7e9Syasuoka goto error; 2979581bf0faSyasuoka pip = mtod(m0, struct ip *); 2980581bf0faSyasuoka uh = (struct udphdr *)(mtod(m0, caddr_t) + pip->ip_hl * 4); 2981a0ade7e9Syasuoka 2982a0ade7e9Syasuoka switch (ntohs(uh->uh_sport)) { 2983a0ade7e9Syasuoka case 53: /* DOMAIN */ 2984a0ade7e9Syasuoka case 67: /* BOOTPS */ 2985a0ade7e9Syasuoka case 68: /* BOOTPC */ 2986a0ade7e9Syasuoka case 123: /* NTP */ 2987a0ade7e9Syasuoka case 137: /* NETBIOS-NS */ 2988a0ade7e9Syasuoka case 520: /* RIP */ 2989a0ade7e9Syasuoka goto is_idle; 2990a0ade7e9Syasuoka } 2991a0ade7e9Syasuoka switch (ntohs(uh->uh_dport)) { 2992a0ade7e9Syasuoka case 53: /* DOMAIN */ 2993a0ade7e9Syasuoka case 67: /* BOOTPS */ 2994a0ade7e9Syasuoka case 68: /* BOOTPC */ 2995a0ade7e9Syasuoka case 123: /* NTP */ 2996a0ade7e9Syasuoka case 137: /* NETBIOS-NS */ 2997a0ade7e9Syasuoka case 520: /* RIP */ 2998a0ade7e9Syasuoka goto is_idle; 2999a0ade7e9Syasuoka } 3000a0ade7e9Syasuoka goto is_active; 3001a0ade7e9Syasuoka default: 3002a0ade7e9Syasuoka goto is_active; 3003a0ade7e9Syasuoka } 3004a0ade7e9Syasuoka 3005a0ade7e9Syasuoka is_active: 3006a0ade7e9Syasuoka *ris_idle = 0; 300763d4736dSyasuoka return (m0); 3008a0ade7e9Syasuoka 3009a0ade7e9Syasuoka is_idle: 3010a0ade7e9Syasuoka *ris_idle = 1; 301163d4736dSyasuoka return (m0); 3012a0ade7e9Syasuoka 3013a0ade7e9Syasuoka error: 301463d4736dSyasuoka return (NULL); 3015a0ade7e9Syasuoka } 3016a0ade7e9Syasuoka 3017c02a3381Smvs void 3018a0ade7e9Syasuoka pipex_session_log(struct pipex_session *session, int prio, const char *fmt, ...) 3019a0ade7e9Syasuoka { 3020a0ade7e9Syasuoka char logbuf[1024]; 3021a0ade7e9Syasuoka va_list ap; 3022a0ade7e9Syasuoka 3023a0ade7e9Syasuoka logpri(prio); 3024a0ade7e9Syasuoka if (session != NULL) { 3025002baba0Smvs struct ifnet *ifp; 3026002baba0Smvs 3027002baba0Smvs ifp = if_get(session->ifindex); 3028a0ade7e9Syasuoka addlog("pipex: ppp=%d iface=%s protocol=%s id=%d ", 3029002baba0Smvs session->ppp_id, 3030002baba0Smvs ifp? ifp->if_xname : "Unknown", 3031a0ade7e9Syasuoka (session->protocol == PIPEX_PROTO_PPPOE)? "PPPoE" : 30328876230bShsuenaga (session->protocol == PIPEX_PROTO_PPTP)? "PPTP" : 30338876230bShsuenaga (session->protocol == PIPEX_PROTO_L2TP) ? "L2TP" : 30348876230bShsuenaga "Unknown", session->session_id); 3035002baba0Smvs if_put(ifp); 3036a0ade7e9Syasuoka } else 3037a0ade7e9Syasuoka addlog("pipex: "); 3038a0ade7e9Syasuoka 3039a0ade7e9Syasuoka va_start(ap, fmt); 3040a0ade7e9Syasuoka vsnprintf(logbuf, sizeof(logbuf), fmt, ap); 3041a0ade7e9Syasuoka va_end(ap); 3042a0ade7e9Syasuoka addlog("%s\n", logbuf); 3043a0ade7e9Syasuoka } 30448876230bShsuenaga 3045c02a3381Smvs uint32_t 30468876230bShsuenaga pipex_sockaddr_hash_key(struct sockaddr *sa) 30478876230bShsuenaga { 30488876230bShsuenaga switch (sa->sa_family) { 30498876230bShsuenaga case AF_INET: 30503e303383Sbluhm return ntohl(satosin(sa)->sin_addr.s_addr); 30518876230bShsuenaga case AF_INET6: 30523e303383Sbluhm return ntohl(satosin6(sa)->sin6_addr.s6_addr32[3]); 30538876230bShsuenaga } 30548876230bShsuenaga panic("pipex_sockaddr_hash_key: unknown address family"); 3055503a8a27Syasuoka return (0); 30568876230bShsuenaga } 30578876230bShsuenaga 30588876230bShsuenaga /* 30598876230bShsuenaga * Compare struct sockaddr_in{,6} with the address only. 30608876230bShsuenaga * The port number is not covered. 30618876230bShsuenaga */ 3062c02a3381Smvs int 30638876230bShsuenaga pipex_sockaddr_compar_addr(struct sockaddr *a, struct sockaddr *b) 30648876230bShsuenaga { 30658876230bShsuenaga int cmp; 30668876230bShsuenaga 30678876230bShsuenaga cmp = b->sa_family - a->sa_family; 30688876230bShsuenaga if (cmp != 0) 30698876230bShsuenaga return cmp; 30708876230bShsuenaga switch (a->sa_family) { 30718876230bShsuenaga case AF_INET: 30723e303383Sbluhm return (satosin(b)->sin_addr.s_addr - 30733e303383Sbluhm satosin(a)->sin_addr.s_addr); 30748876230bShsuenaga case AF_INET6: 30753e303383Sbluhm cmp = (satosin6(b)->sin6_scope_id - satosin6(a)->sin6_scope_id); 30768876230bShsuenaga if (cmp != 0) 30778876230bShsuenaga return cmp; 30783e303383Sbluhm return (memcmp(&satosin6(a)->sin6_addr, 30793e303383Sbluhm &satosin6(b)->sin6_addr, 30803e303383Sbluhm sizeof(struct in6_addr))); 30818876230bShsuenaga } 30828876230bShsuenaga panic("pipex_sockaddr_compar_addr: unknown address family"); 3083e405d423Syasuoka 3084e405d423Syasuoka return (-1); 3085e405d423Syasuoka } 3086e405d423Syasuoka 308790b03482Syasuoka int 308890b03482Syasuoka pipex_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, 308990b03482Syasuoka size_t newlen) 309090b03482Syasuoka { 309190b03482Syasuoka switch (name[0]) { 309290b03482Syasuoka case PIPEXCTL_ENABLE: 30939326fce4Syasuoka if (namelen != 1) 30949326fce4Syasuoka return (ENOTDIR); 30959e5eed67Sgnezdo return (sysctl_int_bounded(oldp, oldlenp, newp, newlen, 30969e5eed67Sgnezdo &pipex_enable, 0, 1)); 309790b03482Syasuoka default: 309890b03482Syasuoka return (ENOPROTOOPT); 309990b03482Syasuoka } 310090b03482Syasuoka /* NOTREACHED */ 310190b03482Syasuoka } 3102