Annotation of src/sys/netinet6/in6_l2tp.c, Revision 1.10
1.10 ! knakahar 1: /* $NetBSD: in6_l2tp.c,v 1.9 2017/12/15 04:58:31 knakahara Exp $ */
1.1 knakahar 2:
3: /*
4: * Copyright (c) 2017 Internet Initiative Japan Inc.
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26: * POSSIBILITY OF SUCH DAMAGE.
27: */
28:
29: #include <sys/cdefs.h>
1.10 ! knakahar 30: __KERNEL_RCSID(0, "$NetBSD: in6_l2tp.c,v 1.9 2017/12/15 04:58:31 knakahara Exp $");
1.1 knakahar 31:
32: #ifdef _KERNEL_OPT
33: #include "opt_l2tp.h"
34: #endif
35:
36: #include <sys/param.h>
37: #include <sys/systm.h>
38: #include <sys/socket.h>
39: #include <sys/sockio.h>
40: #include <sys/mbuf.h>
41: #include <sys/errno.h>
42: #include <sys/ioctl.h>
43: #include <sys/syslog.h>
44: #include <sys/kernel.h>
45:
46: #include <net/if.h>
47: #include <net/route.h>
48: #include <net/if_ether.h>
49:
50: #include <netinet/in.h>
51: #include <netinet/in_systm.h>
52: #include <netinet/ip.h>
53: #include <netinet/ip_var.h>
54: #include <netinet/ip_private.h>
55: #include <netinet/in_l2tp.h>
56: #include <netinet/in_var.h>
57: #include <netinet/ip_encap.h>
58:
59: #include <netinet/ip6.h>
60: #include <netinet6/ip6_var.h>
61: #include <netinet6/in6_l2tp.h>
62:
63: #ifdef ALTQ
64: #include <altq/altq.h>
65: #endif
66:
67: /* TODO: IP_TCPMSS support */
68: #undef IP_TCPMSS
69: #ifdef IP_TCPMSS
70: #include <netinet/ip_tcpmss.h>
71: #endif
72:
73: #include <net/if_l2tp.h>
74:
75: #define L2TP_HLIM6 64
76: int ip6_l2tp_hlim = L2TP_HLIM6;
77:
1.7 knakahar 78: static int in6_l2tp_input(struct mbuf **, int *, int, void *);
1.1 knakahar 79:
80: static const struct encapsw in6_l2tp_encapsw = {
81: .encapsw6 = {
82: .pr_input = in6_l2tp_input,
83: .pr_ctlinput = NULL,
84: }
85: };
86:
87: static int in6_l2tp_match(struct mbuf *, int, int, void *);
88:
89: int
90: in6_l2tp_output(struct l2tp_variant *var, struct mbuf *m)
91: {
92: struct rtentry *rt;
93: struct l2tp_ro *lro;
94: struct l2tp_softc *sc;
95: struct ifnet *ifp;
96: struct sockaddr_in6 *sin6_src = satosin6(var->lv_psrc);
97: struct sockaddr_in6 *sin6_dst = satosin6(var->lv_pdst);
98: struct ip6_hdr ip6hdr; /* capsule IP header, host byte ordered */
99: int error;
100: uint32_t sess_id;
101:
102: KASSERT(var != NULL);
103: KASSERT(l2tp_heldref_variant(var));
104: KASSERT(sin6_src != NULL && sin6_dst != NULL);
105: KASSERT(sin6_src->sin6_family == AF_INET6
106: && sin6_dst->sin6_family == AF_INET6);
107:
108: sc = var->lv_softc;
109: ifp = &sc->l2tp_ec.ec_if;
110: error = l2tp_check_nesting(ifp, m);
1.6 knakahar 111: if (error) {
112: m_freem(m);
1.1 knakahar 113: goto looped;
1.6 knakahar 114: }
1.1 knakahar 115:
116: #ifdef NOTYET
117: /* TODO: support ALTQ for innner frame */
118: #ifdef ALTQ
119: ALTQ_SAVE_PAYLOAD(m, AF_ETHER);
120: #endif
121: #endif
122:
123: memset(&ip6hdr, 0, sizeof(ip6hdr));
124: ip6hdr.ip6_src = sin6_src->sin6_addr;
125: /* bidirectional configured tunnel mode */
126: if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
127: ip6hdr.ip6_dst = sin6_dst->sin6_addr;
128: else {
129: m_freem(m);
130: if ((ifp->if_flags & IFF_DEBUG) != 0)
131: log(LOG_DEBUG, "%s: ENETUNREACH\n", __func__);
132: return ENETUNREACH;
133: }
134: /* unlike IPv4, IP version must be filled by caller of ip6_output() */
135: ip6hdr.ip6_vfc = 0x60;
136: ip6hdr.ip6_nxt = IPPROTO_L2TP;
137: ip6hdr.ip6_hlim = ip6_l2tp_hlim;
138: /* outer IP payload length */
139: ip6hdr.ip6_plen = 0;
140: /* session-id length */
141: ip6hdr.ip6_plen += sizeof(uint32_t);
142: if (var->lv_use_cookie == L2TP_COOKIE_ON) {
143: /* cookie length */
144: ip6hdr.ip6_plen += var->lv_peer_cookie_len;
145: }
146:
147: /* TODO: IP_TCPMSS support */
148: #ifdef IP_TCPMSS
149: m = l2tp_tcpmss_clamp(ifp, m);
150: if (m == NULL)
151: return EINVAL;
152: #endif
153:
154: /*
155: * payload length
156: * NOTE: Payload length may be changed in ip_tcpmss().
157: * Typical case is missing of TCP mss option in original
158: * TCP header.
159: */
160: ip6hdr.ip6_plen += m->m_pkthdr.len;
161: HTONS(ip6hdr.ip6_plen);
162:
163: if (var->lv_use_cookie == L2TP_COOKIE_ON) {
164: /* prepend session cookie */
165: uint32_t cookie_32;
166: uint64_t cookie_64;
167: M_PREPEND(m, var->lv_peer_cookie_len, M_DONTWAIT);
168: if (m && m->m_len < var->lv_peer_cookie_len)
169: m = m_pullup(m, var->lv_peer_cookie_len);
170: if (m == NULL)
171: return ENOBUFS;
172: if (var->lv_peer_cookie_len == 4) {
173: cookie_32 = htonl((uint32_t)var->lv_peer_cookie);
174: memcpy(mtod(m, void *), &cookie_32,
175: sizeof(uint32_t));
176: } else {
177: cookie_64 = htobe64(var->lv_peer_cookie);
178: memcpy(mtod(m, void *), &cookie_64,
179: sizeof(uint64_t));
180: }
181: }
182:
183: /* prepend session-ID */
184: sess_id = htonl(var->lv_peer_sess_id);
185: M_PREPEND(m, sizeof(uint32_t), M_DONTWAIT);
186: if (m && m->m_len < sizeof(uint32_t))
187: m = m_pullup(m, sizeof(uint32_t));
188: if (m == NULL)
189: return ENOBUFS;
190: memcpy(mtod(m, uint32_t *), &sess_id, sizeof(uint32_t));
191:
192: /* prepend new IP header */
193: M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
194: if (IP_HDR_ALIGNED_P(mtod(m, void *)) == 0) {
195: if (m)
196: m = m_copyup(m, sizeof(struct ip), 0);
197: } else {
198: if (m && m->m_len < sizeof(struct ip6_hdr))
199: m = m_pullup(m, sizeof(struct ip6_hdr));
200: }
201: if (m == NULL)
202: return ENOBUFS;
203: memcpy(mtod(m, struct ip6_hdr *), &ip6hdr, sizeof(struct ip6_hdr));
204:
205: lro = percpu_getref(sc->l2tp_ro_percpu);
206: mutex_enter(&lro->lr_lock);
207: if ((rt = rtcache_lookup(&lro->lr_ro, var->lv_pdst)) == NULL) {
208: mutex_exit(&lro->lr_lock);
209: percpu_putref(sc->l2tp_ro_percpu);
210: m_freem(m);
211: return ENETUNREACH;
212: }
213:
214: /* If the route constitutes infinite encapsulation, punt. */
215: if (rt->rt_ifp == ifp) {
216: rtcache_unref(rt, &lro->lr_ro);
217: rtcache_free(&lro->lr_ro);
218: mutex_exit(&lro->lr_lock);
219: percpu_putref(sc->l2tp_ro_percpu);
220: m_freem(m);
221: return ENETUNREACH; /* XXX */
222: }
223: rtcache_unref(rt, &lro->lr_ro);
224:
225: /*
226: * To avoid inappropriate rewrite of checksum,
227: * clear csum flags.
228: */
229: m->m_pkthdr.csum_flags = 0;
230:
231: error = ip6_output(m, 0, &lro->lr_ro, 0, NULL, NULL, NULL);
232: mutex_exit(&lro->lr_lock);
233: percpu_putref(sc->l2tp_ro_percpu);
234: return(error);
235:
236: looped:
237: if (error)
238: ifp->if_oerrors++;
239:
240: return error;
241: }
242:
243: static int
1.7 knakahar 244: in6_l2tp_input(struct mbuf **mp, int *offp, int proto, void *eparg __unused)
1.1 knakahar 245: {
246: struct mbuf *m = *mp;
247: int off = *offp;
248:
249: struct ifnet *l2tpp = NULL;
250: struct l2tp_softc *sc;
251: struct l2tp_variant *var;
252: uint32_t sess_id;
253: uint32_t cookie_32;
254: uint64_t cookie_64;
255: struct psref psref;
256:
257: if (m->m_len < off + sizeof(uint32_t)) {
258: m = m_pullup(m, off + sizeof(uint32_t));
259: if (!m) {
260: /* if payload length < 4 octets */
261: return IPPROTO_DONE;
262: }
263: *mp = m;
264: }
265:
266: /* get L2TP session ID */
267: m_copydata(m, off, sizeof(uint32_t), (void *)&sess_id);
268: NTOHL(sess_id);
269: #ifdef L2TP_DEBUG
270: log(LOG_DEBUG, "%s: sess_id = %" PRIu32 "\n", __func__, sess_id);
271: #endif
272: if (sess_id == 0) {
273: /*
274: * L2TPv3 control packet received.
275: * userland daemon(l2tpd?) should process.
276: */
277: return rip6_input(mp, offp, proto);
278: }
279:
280: var = l2tp_lookup_session_ref(sess_id, &psref);
281: if (var == NULL) {
282: m_freem(m);
283: IP_STATINC(IP_STAT_NOL2TP);
284: return IPPROTO_DONE;
285: } else {
286: sc = var->lv_softc;
287: l2tpp = &(sc->l2tp_ec.ec_if);
288:
289: if (l2tpp == NULL || (l2tpp->if_flags & IFF_UP) == 0) {
290: #ifdef L2TP_DEBUG
291: if (l2tpp == NULL)
292: log(LOG_DEBUG, "%s: l2tpp is NULL\n", __func__);
293: else
294: log(LOG_DEBUG, "%s: l2tpp is down\n", __func__);
295: #endif
296: m_freem(m);
297: IP_STATINC(IP_STAT_NOL2TP);
298: goto out;
299: }
300: /* other CPU do l2tp_delete_tunnel */
301: if (var->lv_psrc == NULL || var->lv_pdst == NULL) {
302: m_freem(m);
303: ip_statinc(IP_STAT_NOL2TP);
304: goto out;
305: }
306: }
307:
308: if (var->lv_state != L2TP_STATE_UP) {
309: m_freem(m);
310: goto out;
311: }
312: m_adj(m, off + sizeof(uint32_t));
313:
314: if (var->lv_use_cookie == L2TP_COOKIE_ON) {
315: if (var->lv_my_cookie_len == 4) {
316: m_copydata(m, 0, sizeof(uint32_t), (void *)&cookie_32);
317: NTOHL(cookie_32);
318: if (cookie_32 != var->lv_my_cookie) {
319: m_freem(m);
320: goto out;
321: }
322: m_adj(m, sizeof(uint32_t));
323: } else {
324: m_copydata(m, 0, sizeof(uint64_t), (void *)&cookie_64);
325: BE64TOH(cookie_64);
326: if (cookie_64 != var->lv_my_cookie) {
327: m_freem(m);
328: goto out;
329: }
330: m_adj(m, sizeof(uint64_t));
331: }
332: }
333:
334: /* TODO: IP_TCPMSS support */
335: #ifdef IP_TCPMSS
336: m = l2tp_tcpmss_clamp(l2tpp, m);
337: if (m == NULL)
338: goto out;
339: #endif
340: l2tp_input(m, l2tpp);
341:
342: out:
343: l2tp_putref_variant(var, &psref);
344: return IPPROTO_DONE;
345: }
346:
347: /*
348: * This function is used by encap6_lookup() to decide priority of the encaptab.
349: * This priority is compared to the match length between mbuf's source/destination
350: * IPv6 address pair and encaptab's one.
351: * l2tp(4) does not use address pairs to search matched encaptab, so this
352: * function must return the length bigger than or equals to IPv6 address pair to
353: * avoid wrong encaptab.
354: */
355: static int
356: in6_l2tp_match(struct mbuf *m, int off, int proto, void *arg)
357: {
358: struct l2tp_variant *var = arg;
359: uint32_t sess_id;
360:
361: KASSERT(proto == IPPROTO_L2TP);
362:
1.9 knakahar 363: if (m->m_len < off + sizeof(uint32_t)) {
1.10 ! knakahar 364: /* if payload length < 4 octets */
! 365: if(!m_ensure_contig(&m, off + sizeof(uint32_t)))
1.9 knakahar 366: return 0;
1.10 ! knakahar 367: }
1.1 knakahar 368:
369: /* get L2TP session ID */
370: m_copydata(m, off, sizeof(uint32_t), (void *)&sess_id);
371: NTOHL(sess_id);
372: if (sess_id == 0) {
373: /*
374: * L2TPv3 control packet received.
375: * userland daemon(l2tpd?) should process.
376: */
377: return 128 * 2;
378: } else if (sess_id == var->lv_my_sess_id)
379: return 128 * 2;
380: else
381: return 0;
382: }
383:
384: int
385: in6_l2tp_attach(struct l2tp_variant *var)
386: {
387:
388: var->lv_encap_cookie = encap_attach_func(AF_INET6, IPPROTO_L2TP,
389: in6_l2tp_match, &in6_l2tp_encapsw, var);
390: if (var->lv_encap_cookie == NULL)
391: return EEXIST;
392:
393: return 0;
394: }
395:
396: int
397: in6_l2tp_detach(struct l2tp_variant *var)
398: {
399: int error;
400:
401: error = encap_detach(var->lv_encap_cookie);
402: if (error == 0)
403: var->lv_encap_cookie = NULL;
404:
405: return error;
406: }
CVSweb <webmaster@jp.NetBSD.org>