1*c01d8fb5Sandvar /* $NetBSD: ipsecif.c,v 1.22 2023/09/01 11:23:39 andvar Exp $ */
24ab3af3eSknakahara
34ab3af3eSknakahara /*
44ab3af3eSknakahara * Copyright (c) 2017 Internet Initiative Japan Inc.
54ab3af3eSknakahara * All rights reserved.
64ab3af3eSknakahara *
74ab3af3eSknakahara * Redistribution and use in source and binary forms, with or without
84ab3af3eSknakahara * modification, are permitted provided that the following conditions
94ab3af3eSknakahara * are met:
104ab3af3eSknakahara * 1. Redistributions of source code must retain the above copyright
114ab3af3eSknakahara * notice, this list of conditions and the following disclaimer.
124ab3af3eSknakahara * 2. Redistributions in binary form must reproduce the above copyright
134ab3af3eSknakahara * notice, this list of conditions and the following disclaimer in the
144ab3af3eSknakahara * documentation and/or other materials provided with the distribution.
154ab3af3eSknakahara *
164ab3af3eSknakahara * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
174ab3af3eSknakahara * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
184ab3af3eSknakahara * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
194ab3af3eSknakahara * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
204ab3af3eSknakahara * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
214ab3af3eSknakahara * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224ab3af3eSknakahara * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
234ab3af3eSknakahara * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244ab3af3eSknakahara * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
254ab3af3eSknakahara * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
264ab3af3eSknakahara * POSSIBILITY OF SUCH DAMAGE.
274ab3af3eSknakahara */
284ab3af3eSknakahara
294ab3af3eSknakahara #include <sys/cdefs.h>
30*c01d8fb5Sandvar __KERNEL_RCSID(0, "$NetBSD: ipsecif.c,v 1.22 2023/09/01 11:23:39 andvar Exp $");
314ab3af3eSknakahara
324ab3af3eSknakahara #ifdef _KERNEL_OPT
334ab3af3eSknakahara #include "opt_inet.h"
344ab3af3eSknakahara #include "opt_ipsec.h"
354ab3af3eSknakahara #endif
364ab3af3eSknakahara
374ab3af3eSknakahara #include <sys/param.h>
384ab3af3eSknakahara #include <sys/systm.h>
394ab3af3eSknakahara #include <sys/socket.h>
404ab3af3eSknakahara #include <sys/sockio.h>
414ab3af3eSknakahara #include <sys/mbuf.h>
424ab3af3eSknakahara #include <sys/errno.h>
434ab3af3eSknakahara #include <sys/ioctl.h>
444ab3af3eSknakahara #include <sys/syslog.h>
454ab3af3eSknakahara #include <sys/kernel.h>
464ab3af3eSknakahara
474ab3af3eSknakahara #include <net/if.h>
484ab3af3eSknakahara #include <net/route.h>
494ab3af3eSknakahara
504ab3af3eSknakahara #include <netinet/in.h>
514ab3af3eSknakahara #include <netinet/in_systm.h>
524ab3af3eSknakahara #include <netinet/ip.h>
534ab3af3eSknakahara #include <netinet/ip_var.h>
544ab3af3eSknakahara #include <netinet/in_var.h>
554ab3af3eSknakahara #include <netinet/ip_encap.h>
564ab3af3eSknakahara #include <netinet/ip_ecn.h>
574ab3af3eSknakahara #include <netinet/ip_private.h>
584ab3af3eSknakahara #include <netinet/udp.h>
594ab3af3eSknakahara
604ab3af3eSknakahara #ifdef INET6
614ab3af3eSknakahara #include <netinet/ip6.h>
624ab3af3eSknakahara #include <netinet6/ip6_var.h>
634ab3af3eSknakahara #include <netinet6/ip6_private.h>
644ab3af3eSknakahara #include <netinet6/in6_var.h>
654ab3af3eSknakahara #include <netinet6/ip6protosw.h> /* for struct ip6ctlparam */
664ab3af3eSknakahara #include <netinet/ip_ecn.h>
674ab3af3eSknakahara #endif
684ab3af3eSknakahara
694ab3af3eSknakahara #include <netipsec/key.h>
704ab3af3eSknakahara #include <netipsec/ipsecif.h>
714ab3af3eSknakahara
724ab3af3eSknakahara #include <net/if_ipsec.h>
734ab3af3eSknakahara
7458c56020Sknakahara static int ipsecif_set_natt_ports(struct ipsec_variant *, struct mbuf *);
754ab3af3eSknakahara static void ipsecif4_input(struct mbuf *, int, int, void *);
764ab3af3eSknakahara static int ipsecif4_output(struct ipsec_variant *, int, struct mbuf *);
774ab3af3eSknakahara static int ipsecif4_filter4(const struct ip *, struct ipsec_variant *,
784ab3af3eSknakahara struct ifnet *);
794ab3af3eSknakahara
804ab3af3eSknakahara #ifdef INET6
814ab3af3eSknakahara static int ipsecif6_input(struct mbuf **, int *, int, void *);
824ab3af3eSknakahara static int ipsecif6_output(struct ipsec_variant *, int, struct mbuf *);
834ab3af3eSknakahara static int ipsecif6_filter6(const struct ip6_hdr *, struct ipsec_variant *,
844ab3af3eSknakahara struct ifnet *);
854ab3af3eSknakahara #endif
864ab3af3eSknakahara
874ab3af3eSknakahara static int ip_ipsec_ttl = IPSEC_TTL;
884ab3af3eSknakahara static int ip_ipsec_copy_tos = 0;
894ab3af3eSknakahara #ifdef INET6
9093a28c82Sknakahara int ip6_ipsec_hlim = IPSEC_HLIM;
9193a28c82Sknakahara int ip6_ipsec_pmtu = 0;
924ab3af3eSknakahara static int ip6_ipsec_copy_tos = 0;
934ab3af3eSknakahara #endif
944ab3af3eSknakahara
9585839668Smaxv static const struct encapsw ipsecif4_encapsw = {
964ab3af3eSknakahara .encapsw4 = {
974ab3af3eSknakahara .pr_input = ipsecif4_input,
984ab3af3eSknakahara .pr_ctlinput = NULL,
994ab3af3eSknakahara }
1004ab3af3eSknakahara };
1014ab3af3eSknakahara
1024ab3af3eSknakahara #ifdef INET6
1034ab3af3eSknakahara static const struct encapsw ipsecif6_encapsw;
1044ab3af3eSknakahara #endif
1054ab3af3eSknakahara
10658c56020Sknakahara static int
ipsecif_set_natt_ports(struct ipsec_variant * var,struct mbuf * m)10758c56020Sknakahara ipsecif_set_natt_ports(struct ipsec_variant *var, struct mbuf *m)
10858c56020Sknakahara {
10958c56020Sknakahara
11058c56020Sknakahara KASSERT(if_ipsec_heldref_variant(var));
11158c56020Sknakahara
11258c56020Sknakahara if (var->iv_sport || var->iv_dport) {
11358c56020Sknakahara struct m_tag *mtag;
11458c56020Sknakahara
11558c56020Sknakahara mtag = m_tag_get(PACKET_TAG_IPSEC_NAT_T_PORTS,
11658c56020Sknakahara sizeof(uint16_t) + sizeof(uint16_t), M_DONTWAIT);
11758c56020Sknakahara if (mtag) {
11858c56020Sknakahara uint16_t *natt_port;
11958c56020Sknakahara
12058c56020Sknakahara natt_port = (uint16_t *)(mtag + 1);
12158c56020Sknakahara natt_port[0] = var->iv_dport;
12258c56020Sknakahara natt_port[1] = var->iv_sport;
12358c56020Sknakahara m_tag_prepend(m, mtag);
12458c56020Sknakahara } else {
12558c56020Sknakahara return ENOBUFS;
12658c56020Sknakahara }
12758c56020Sknakahara }
12858c56020Sknakahara
12958c56020Sknakahara return 0;
13058c56020Sknakahara }
13158c56020Sknakahara
1324ab3af3eSknakahara static struct mbuf *
ipsecif4_prepend_hdr(struct ipsec_variant * var,struct mbuf * m,uint8_t proto,uint8_t tos)1334ab3af3eSknakahara ipsecif4_prepend_hdr(struct ipsec_variant *var, struct mbuf *m,
1344ab3af3eSknakahara uint8_t proto, uint8_t tos)
1354ab3af3eSknakahara {
1364ab3af3eSknakahara struct ip *ip;
1374ab3af3eSknakahara struct sockaddr_in *src, *dst;
1384ab3af3eSknakahara
1394ab3af3eSknakahara src = satosin(var->iv_psrc);
1404ab3af3eSknakahara dst = satosin(var->iv_pdst);
1414ab3af3eSknakahara
1424ab3af3eSknakahara if (in_nullhost(src->sin_addr) || in_nullhost(src->sin_addr) ||
1434ab3af3eSknakahara src->sin_addr.s_addr == INADDR_BROADCAST ||
1444ab3af3eSknakahara dst->sin_addr.s_addr == INADDR_BROADCAST) {
1454ab3af3eSknakahara m_freem(m);
1464ab3af3eSknakahara return NULL;
1474ab3af3eSknakahara }
1484ab3af3eSknakahara m->m_flags &= ~M_BCAST;
1494ab3af3eSknakahara
1504ab3af3eSknakahara if (IN_MULTICAST(src->sin_addr.s_addr) ||
1514ab3af3eSknakahara IN_MULTICAST(dst->sin_addr.s_addr)) {
1524ab3af3eSknakahara m_freem(m);
1534ab3af3eSknakahara return NULL;
1544ab3af3eSknakahara }
1554ab3af3eSknakahara
1564ab3af3eSknakahara M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
1574ab3af3eSknakahara if (m && M_UNWRITABLE(m, sizeof(struct ip)))
1584ab3af3eSknakahara m = m_pullup(m, sizeof(struct ip));
1594ab3af3eSknakahara if (m == NULL)
1604ab3af3eSknakahara return NULL;
1614ab3af3eSknakahara
1624ab3af3eSknakahara ip = mtod(m, struct ip *);
1634ab3af3eSknakahara ip->ip_v = IPVERSION;
1644ab3af3eSknakahara ip->ip_off = htons(0);
1657c378391Sknakahara if (m->m_pkthdr.len < IP_MINFRAGSIZE)
1664ab3af3eSknakahara ip->ip_id = 0;
1677c378391Sknakahara else
1687c378391Sknakahara ip->ip_id = ip_newid(NULL);
1694ab3af3eSknakahara ip->ip_hl = sizeof(*ip) >> 2;
1704ab3af3eSknakahara if (ip_ipsec_copy_tos)
1714ab3af3eSknakahara ip->ip_tos = tos;
1724ab3af3eSknakahara else
1734ab3af3eSknakahara ip->ip_tos = 0;
1744ab3af3eSknakahara ip->ip_sum = 0;
1754ab3af3eSknakahara ip->ip_src = src->sin_addr;
1764ab3af3eSknakahara ip->ip_dst = dst->sin_addr;
1774ab3af3eSknakahara ip->ip_p = proto;
1784ab3af3eSknakahara ip->ip_ttl = ip_ipsec_ttl;
1794ab3af3eSknakahara ip->ip_len = htons(m->m_pkthdr.len);
1804ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
1814ab3af3eSknakahara struct ifnet *ifp = &var->iv_softc->ipsec_if;
1824ab3af3eSknakahara if (ifp->if_flags & IFF_ECN)
1834ab3af3eSknakahara ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos);
1844ab3af3eSknakahara else
1854ab3af3eSknakahara ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos);
1864ab3af3eSknakahara #endif
1874ab3af3eSknakahara
1884ab3af3eSknakahara return m;
1894ab3af3eSknakahara }
1904ab3af3eSknakahara
1914ab3af3eSknakahara static int
ipsecif4_needfrag(struct mbuf * m,struct ipsecrequest * isr)1924ab3af3eSknakahara ipsecif4_needfrag(struct mbuf *m, struct ipsecrequest *isr)
1934ab3af3eSknakahara {
1944ab3af3eSknakahara struct ip ip0;
1954ab3af3eSknakahara struct ip *ip;
1964ab3af3eSknakahara int mtu;
1974ab3af3eSknakahara struct secasvar *sav;
1984ab3af3eSknakahara
1994ab3af3eSknakahara sav = key_lookup_sa_bysaidx(&isr->saidx);
2004ab3af3eSknakahara if (sav == NULL)
2014ab3af3eSknakahara return 0;
2024ab3af3eSknakahara
203f645db7aSmaxv if (!(sav->natt_type & UDP_ENCAP_ESPINUDP)) {
2044ab3af3eSknakahara mtu = 0;
2054ab3af3eSknakahara goto out;
2064ab3af3eSknakahara }
2074ab3af3eSknakahara
2084ab3af3eSknakahara if (m->m_len < sizeof(struct ip)) {
2094ab3af3eSknakahara m_copydata(m, 0, sizeof(ip0), &ip0);
2104ab3af3eSknakahara ip = &ip0;
2114ab3af3eSknakahara } else {
2124ab3af3eSknakahara ip = mtod(m, struct ip *);
2134ab3af3eSknakahara }
2144ab3af3eSknakahara mtu = sav->esp_frag;
2154ab3af3eSknakahara if (ntohs(ip->ip_len) <= mtu)
2164ab3af3eSknakahara mtu = 0;
2174ab3af3eSknakahara
2184ab3af3eSknakahara out:
2194ab3af3eSknakahara KEY_SA_UNREF(&sav);
2204ab3af3eSknakahara return mtu;
2214ab3af3eSknakahara }
2224ab3af3eSknakahara
2234ab3af3eSknakahara static struct mbuf *
ipsecif4_flowinfo(struct mbuf * m,int family,int * proto0,u_int8_t * tos0)2244ab3af3eSknakahara ipsecif4_flowinfo(struct mbuf *m, int family, int *proto0, u_int8_t *tos0)
2254ab3af3eSknakahara {
2264ab3af3eSknakahara const struct ip *ip;
2274ab3af3eSknakahara int proto;
2284ab3af3eSknakahara int tos;
2294ab3af3eSknakahara
2304ab3af3eSknakahara KASSERT(proto0 != NULL);
2314ab3af3eSknakahara KASSERT(tos0 != NULL);
2324ab3af3eSknakahara
2334ab3af3eSknakahara switch (family) {
2344ab3af3eSknakahara case AF_INET:
2354ab3af3eSknakahara proto = IPPROTO_IPV4;
2364ab3af3eSknakahara if (m->m_len < sizeof(*ip)) {
2374ab3af3eSknakahara m = m_pullup(m, sizeof(*ip));
23816a6b570Smaxv if (m == NULL) {
2394ab3af3eSknakahara *tos0 = 0;
2404ab3af3eSknakahara *proto0 = 0;
2414ab3af3eSknakahara return NULL;
2424ab3af3eSknakahara }
2434ab3af3eSknakahara }
2444ab3af3eSknakahara ip = mtod(m, const struct ip *);
2454ab3af3eSknakahara tos = ip->ip_tos;
246*c01d8fb5Sandvar /* TODO: support ALTQ for inner packet */
2474ab3af3eSknakahara break;
2484ab3af3eSknakahara #ifdef INET6
2494ab3af3eSknakahara case AF_INET6: {
2504ab3af3eSknakahara const struct ip6_hdr *ip6;
2514ab3af3eSknakahara proto = IPPROTO_IPV6;
2524ab3af3eSknakahara if (m->m_len < sizeof(*ip6)) {
2534ab3af3eSknakahara m = m_pullup(m, sizeof(*ip6));
25416a6b570Smaxv if (m == NULL) {
2554ab3af3eSknakahara *tos0 = 0;
2564ab3af3eSknakahara *proto0 = 0;
2574ab3af3eSknakahara return NULL;
2584ab3af3eSknakahara }
2594ab3af3eSknakahara }
2604ab3af3eSknakahara ip6 = mtod(m, const struct ip6_hdr *);
2614ab3af3eSknakahara tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
262*c01d8fb5Sandvar /* TODO: support ALTQ for inner packet */
2634ab3af3eSknakahara break;
2644ab3af3eSknakahara }
2654ab3af3eSknakahara #endif /* INET6 */
2664ab3af3eSknakahara default:
2674ab3af3eSknakahara *tos0 = 0;
2684ab3af3eSknakahara *proto0 = 0;
2694ab3af3eSknakahara return NULL;
2704ab3af3eSknakahara }
2714ab3af3eSknakahara
2724ab3af3eSknakahara *proto0 = proto;
2734ab3af3eSknakahara *tos0 = tos;
2744ab3af3eSknakahara return m;
2754ab3af3eSknakahara }
2764ab3af3eSknakahara
2774ab3af3eSknakahara static int
ipsecif4_fragout(struct ipsec_variant * var,int family,struct mbuf * m,int mtu)2784ab3af3eSknakahara ipsecif4_fragout(struct ipsec_variant *var, int family, struct mbuf *m, int mtu)
2794ab3af3eSknakahara {
2804ab3af3eSknakahara struct ifnet *ifp = &var->iv_softc->ipsec_if;
2814ab3af3eSknakahara struct mbuf *next;
2824ab3af3eSknakahara struct m_tag *mtag;
2834ab3af3eSknakahara int error;
2844ab3af3eSknakahara
2854ab3af3eSknakahara KASSERT(if_ipsec_heldref_variant(var));
2864ab3af3eSknakahara
2875c987100Smaxv mtag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS);
2884ab3af3eSknakahara if (mtag)
2894ab3af3eSknakahara m_tag_delete(m, mtag);
2904ab3af3eSknakahara
291400c1914Sknakahara /* consider new IP header prepended in ipsecif4_output() */
292400c1914Sknakahara if (mtu <= sizeof(struct ip)) {
293400c1914Sknakahara m_freem(m);
294400c1914Sknakahara return ENETUNREACH;
295400c1914Sknakahara }
296400c1914Sknakahara m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
297400c1914Sknakahara error = ip_fragment(m, ifp, mtu - sizeof(struct ip));
2984ab3af3eSknakahara if (error)
2994ab3af3eSknakahara return error;
3004ab3af3eSknakahara
3014ab3af3eSknakahara for (error = 0; m; m = next) {
3024ab3af3eSknakahara next = m->m_nextpkt;
3034fda12c6Sknakahara m->m_nextpkt = NULL;
3044ab3af3eSknakahara if (error) {
3054ab3af3eSknakahara m_freem(m);
3064ab3af3eSknakahara continue;
3074ab3af3eSknakahara }
3084ab3af3eSknakahara
3094ab3af3eSknakahara error = ipsecif4_output(var, family, m);
3104ab3af3eSknakahara }
3114ab3af3eSknakahara if (error == 0)
3124ab3af3eSknakahara IP_STATINC(IP_STAT_FRAGMENTED);
3134ab3af3eSknakahara
3144ab3af3eSknakahara return error;
3154ab3af3eSknakahara }
3164ab3af3eSknakahara
3174ab3af3eSknakahara int
ipsecif4_encap_func(struct mbuf * m,struct ip * ip,struct ipsec_variant * var)3184ab3af3eSknakahara ipsecif4_encap_func(struct mbuf *m, struct ip *ip, struct ipsec_variant *var)
3194ab3af3eSknakahara {
3204ab3af3eSknakahara struct m_tag *mtag;
3214ab3af3eSknakahara struct sockaddr_in *src, *dst;
3224ab3af3eSknakahara u_int16_t src_port = 0;
3234ab3af3eSknakahara u_int16_t dst_port = 0;
3244ab3af3eSknakahara
3254ab3af3eSknakahara KASSERT(var != NULL);
3264ab3af3eSknakahara
3274ab3af3eSknakahara src = satosin(var->iv_psrc);
3284ab3af3eSknakahara dst = satosin(var->iv_pdst);
3295c987100Smaxv mtag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS);
3304ab3af3eSknakahara if (mtag) {
3314ab3af3eSknakahara u_int16_t *ports;
3324ab3af3eSknakahara
3334ab3af3eSknakahara ports = (u_int16_t *)(mtag + 1);
3344ab3af3eSknakahara src_port = ports[0];
3354ab3af3eSknakahara dst_port = ports[1];
3364ab3af3eSknakahara }
3374ab3af3eSknakahara
3384ab3af3eSknakahara /* address match */
3394ab3af3eSknakahara if (src->sin_addr.s_addr != ip->ip_dst.s_addr ||
3404ab3af3eSknakahara dst->sin_addr.s_addr != ip->ip_src.s_addr)
3414ab3af3eSknakahara return 0;
3424ab3af3eSknakahara
3434ab3af3eSknakahara /* UDP encap? */
3444ab3af3eSknakahara if (mtag == NULL && var->iv_sport == 0 && var->iv_dport == 0)
3454ab3af3eSknakahara goto match;
3464ab3af3eSknakahara
3474ab3af3eSknakahara /* port match */
3484ab3af3eSknakahara if (src_port != var->iv_dport ||
3494ab3af3eSknakahara dst_port != var->iv_sport) {
3504ab3af3eSknakahara #ifdef DEBUG
3514ab3af3eSknakahara printf("%s: port mismatch: pkt(%u, %u), if(%u, %u)\n",
3524ab3af3eSknakahara __func__, ntohs(src_port), ntohs(dst_port),
3534ab3af3eSknakahara ntohs(var->iv_sport), ntohs(var->iv_dport));
3544ab3af3eSknakahara #endif
3554ab3af3eSknakahara return 0;
3564ab3af3eSknakahara }
3574ab3af3eSknakahara
3584ab3af3eSknakahara match:
3594ab3af3eSknakahara /*
3604ab3af3eSknakahara * hide NAT-T information from encapsulated traffics.
3614ab3af3eSknakahara * they don't know about IPsec.
3624ab3af3eSknakahara */
3634ab3af3eSknakahara if (mtag)
3644ab3af3eSknakahara m_tag_delete(m, mtag);
3654ab3af3eSknakahara return sizeof(src->sin_addr) + sizeof(dst->sin_addr);
3664ab3af3eSknakahara }
3674ab3af3eSknakahara
3684ab3af3eSknakahara static int
ipsecif4_output(struct ipsec_variant * var,int family,struct mbuf * m)3694ab3af3eSknakahara ipsecif4_output(struct ipsec_variant *var, int family, struct mbuf *m)
3704ab3af3eSknakahara {
3714ab3af3eSknakahara struct secpolicy *sp = NULL;
3724ab3af3eSknakahara u_int8_t tos;
3734ab3af3eSknakahara int proto;
3744ab3af3eSknakahara int error;
3754ab3af3eSknakahara int mtu;
3764ab3af3eSknakahara u_long sa_mtu = 0;
3774ab3af3eSknakahara
3784ab3af3eSknakahara KASSERT(if_ipsec_heldref_variant(var));
3794ab3af3eSknakahara KASSERT(if_ipsec_variant_is_configured(var));
3804ab3af3eSknakahara KASSERT(var->iv_psrc->sa_family == AF_INET);
3814ab3af3eSknakahara KASSERT(var->iv_pdst->sa_family == AF_INET);
3824ab3af3eSknakahara
383840a0bbcSknakahara switch (family) {
384840a0bbcSknakahara case AF_INET:
3854ab3af3eSknakahara sp = IV_SP_OUT(var);
386840a0bbcSknakahara break;
387840a0bbcSknakahara case AF_INET6:
388840a0bbcSknakahara sp = IV_SP_OUT6(var);
389840a0bbcSknakahara break;
390840a0bbcSknakahara default:
391840a0bbcSknakahara m_freem(m);
392840a0bbcSknakahara return EAFNOSUPPORT;
393840a0bbcSknakahara }
3944ab3af3eSknakahara KASSERT(sp != NULL);
3954ab3af3eSknakahara /*
3964ab3af3eSknakahara * The SPs in ipsec_variant are prevented from freed by
3974ab3af3eSknakahara * ipsec_variant->iv_psref. So, KEY_SP_REF() is unnecessary here.
39813a5442fSknakahara *
39913a5442fSknakahara * However, lastused should be updated.
4004ab3af3eSknakahara */
40113a5442fSknakahara key_sp_touch(sp);
4024ab3af3eSknakahara
4034ab3af3eSknakahara KASSERT(sp->policy != IPSEC_POLICY_NONE);
4044ab3af3eSknakahara KASSERT(sp->policy != IPSEC_POLICY_ENTRUST);
4054ab3af3eSknakahara KASSERT(sp->policy != IPSEC_POLICY_BYPASS);
4064ab3af3eSknakahara if (sp->policy != IPSEC_POLICY_IPSEC) {
4074ab3af3eSknakahara m_freem(m);
408d2e69c99Sknakahara error = ENETUNREACH;
409d2e69c99Sknakahara goto done;
4104ab3af3eSknakahara }
4114ab3af3eSknakahara
4124ab3af3eSknakahara /* get flowinfo */
4134ab3af3eSknakahara m = ipsecif4_flowinfo(m, family, &proto, &tos);
4144ab3af3eSknakahara if (m == NULL) {
4154ab3af3eSknakahara error = ENETUNREACH;
4164ab3af3eSknakahara goto done;
4174ab3af3eSknakahara }
4184ab3af3eSknakahara
4194ab3af3eSknakahara /* prepend new IP header */
4204ab3af3eSknakahara m = ipsecif4_prepend_hdr(var, m, proto, tos);
4214ab3af3eSknakahara if (m == NULL) {
4224ab3af3eSknakahara error = ENETUNREACH;
4234ab3af3eSknakahara goto done;
4244ab3af3eSknakahara }
4254ab3af3eSknakahara
4264ab3af3eSknakahara /*
4274ab3af3eSknakahara * Normal netipsec's NAT-T fragmentation is done in ip_output().
4284ab3af3eSknakahara * See "natt_frag" processing.
4294ab3af3eSknakahara * However, ipsec(4) interface's one is not done in the same way,
4304ab3af3eSknakahara * so we must do NAT-T fragmentation by own code.
4314ab3af3eSknakahara */
4324ab3af3eSknakahara /* NAT-T ESP fragmentation */
4334ab3af3eSknakahara mtu = ipsecif4_needfrag(m, sp->req);
4344ab3af3eSknakahara if (mtu > 0)
4354ab3af3eSknakahara return ipsecif4_fragout(var, family, m, mtu);
4364ab3af3eSknakahara
43758c56020Sknakahara /* set NAT-T ports */
43858c56020Sknakahara error = ipsecif_set_natt_ports(var, m);
43958c56020Sknakahara if (error) {
44058c56020Sknakahara m_freem(m);
44158c56020Sknakahara goto done;
44258c56020Sknakahara }
44358c56020Sknakahara
4444ab3af3eSknakahara /* IPsec output */
4454ab3af3eSknakahara IP_STATINC(IP_STAT_LOCALOUT);
4464ab3af3eSknakahara error = ipsec4_process_packet(m, sp->req, &sa_mtu);
4474ab3af3eSknakahara if (error == ENOENT)
4484ab3af3eSknakahara error = 0;
4494ab3af3eSknakahara /*
4504ab3af3eSknakahara * frangmentation is already done in ipsecif4_fragout(),
4514ab3af3eSknakahara * so ipsec4_process_packet() must not do fragmentation here.
4524ab3af3eSknakahara */
453400c1914Sknakahara KASSERT(sa_mtu == 0);
4544ab3af3eSknakahara
4554ab3af3eSknakahara done:
4564ab3af3eSknakahara return error;
4574ab3af3eSknakahara }
4584ab3af3eSknakahara
4594ab3af3eSknakahara #ifdef INET6
460d83c566fSknakahara int
ipsecif6_encap_func(struct mbuf * m,struct ip6_hdr * ip6,struct ipsec_variant * var)461d83c566fSknakahara ipsecif6_encap_func(struct mbuf *m, struct ip6_hdr *ip6, struct ipsec_variant *var)
462d83c566fSknakahara {
463d83c566fSknakahara struct m_tag *mtag;
464d83c566fSknakahara struct sockaddr_in6 *src, *dst;
465d83c566fSknakahara u_int16_t src_port = 0;
466d83c566fSknakahara u_int16_t dst_port = 0;
467d83c566fSknakahara
468d83c566fSknakahara KASSERT(var != NULL);
469d83c566fSknakahara
470d83c566fSknakahara src = satosin6(var->iv_psrc);
471d83c566fSknakahara dst = satosin6(var->iv_pdst);
4725c987100Smaxv mtag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS);
473d83c566fSknakahara if (mtag) {
474d83c566fSknakahara u_int16_t *ports;
475d83c566fSknakahara
476d83c566fSknakahara ports = (u_int16_t *)(mtag + 1);
477d83c566fSknakahara src_port = ports[0];
478d83c566fSknakahara dst_port = ports[1];
479d83c566fSknakahara }
480d83c566fSknakahara
481d83c566fSknakahara /* address match */
482d83c566fSknakahara if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
483d83c566fSknakahara !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src))
484d83c566fSknakahara return 0;
485d83c566fSknakahara
486d83c566fSknakahara /* UDP encap? */
487d83c566fSknakahara if (mtag == NULL && var->iv_sport == 0 && var->iv_dport == 0)
488d83c566fSknakahara goto match;
489d83c566fSknakahara
490d83c566fSknakahara /* port match */
491d83c566fSknakahara if (src_port != var->iv_dport ||
492d83c566fSknakahara dst_port != var->iv_sport) {
493d83c566fSknakahara #ifdef DEBUG
494d83c566fSknakahara printf("%s: port mismatch: pkt(%u, %u), if(%u, %u)\n",
495d83c566fSknakahara __func__, ntohs(src_port), ntohs(dst_port),
496d83c566fSknakahara ntohs(var->iv_sport), ntohs(var->iv_dport));
497d83c566fSknakahara #endif
498d83c566fSknakahara return 0;
499d83c566fSknakahara }
500d83c566fSknakahara
501d83c566fSknakahara match:
502d83c566fSknakahara /*
503d83c566fSknakahara * hide NAT-T information from encapsulated traffics.
504d83c566fSknakahara * they don't know about IPsec.
505d83c566fSknakahara */
506d83c566fSknakahara if (mtag)
507d83c566fSknakahara m_tag_delete(m, mtag);
508d83c566fSknakahara return sizeof(src->sin6_addr) + sizeof(dst->sin6_addr);
509d83c566fSknakahara }
510d83c566fSknakahara
5114ab3af3eSknakahara static int
ipsecif6_output(struct ipsec_variant * var,int family,struct mbuf * m)5124ab3af3eSknakahara ipsecif6_output(struct ipsec_variant *var, int family, struct mbuf *m)
5134ab3af3eSknakahara {
5144ab3af3eSknakahara struct ifnet *ifp = &var->iv_softc->ipsec_if;
5154ab3af3eSknakahara struct ipsec_softc *sc = ifp->if_softc;
5162da350beSknakahara struct route *ro_pc;
5172da350beSknakahara kmutex_t *lock_pc;
5184ab3af3eSknakahara struct rtentry *rt;
5194ab3af3eSknakahara struct sockaddr_in6 *sin6_src;
5204ab3af3eSknakahara struct sockaddr_in6 *sin6_dst;
5214ab3af3eSknakahara struct ip6_hdr *ip6;
52293a28c82Sknakahara int proto, error, flags;
5234ab3af3eSknakahara u_int8_t itos, otos;
5244ab3af3eSknakahara union {
5254ab3af3eSknakahara struct sockaddr dst;
5264ab3af3eSknakahara struct sockaddr_in6 dst6;
5274ab3af3eSknakahara } u;
5284ab3af3eSknakahara
5294ab3af3eSknakahara KASSERT(if_ipsec_heldref_variant(var));
5304ab3af3eSknakahara KASSERT(if_ipsec_variant_is_configured(var));
5314ab3af3eSknakahara
5324ab3af3eSknakahara sin6_src = satosin6(var->iv_psrc);
5334ab3af3eSknakahara sin6_dst = satosin6(var->iv_pdst);
5344ab3af3eSknakahara
5354ab3af3eSknakahara KASSERT(sin6_src->sin6_family == AF_INET6);
5364ab3af3eSknakahara KASSERT(sin6_dst->sin6_family == AF_INET6);
5374ab3af3eSknakahara
5384ab3af3eSknakahara switch (family) {
5394ab3af3eSknakahara #ifdef INET
5404ab3af3eSknakahara case AF_INET:
5414ab3af3eSknakahara {
5424ab3af3eSknakahara struct ip *ip;
5434ab3af3eSknakahara
5444ab3af3eSknakahara proto = IPPROTO_IPV4;
5454ab3af3eSknakahara if (m->m_len < sizeof(*ip)) {
5464ab3af3eSknakahara m = m_pullup(m, sizeof(*ip));
54716a6b570Smaxv if (m == NULL)
5484ab3af3eSknakahara return ENOBUFS;
5494ab3af3eSknakahara }
5504ab3af3eSknakahara ip = mtod(m, struct ip *);
5514ab3af3eSknakahara itos = ip->ip_tos;
552*c01d8fb5Sandvar /* TODO: support ALTQ for inner packet */
5534ab3af3eSknakahara break;
5544ab3af3eSknakahara }
5554ab3af3eSknakahara #endif /* INET */
5564ab3af3eSknakahara case AF_INET6:
5574ab3af3eSknakahara {
5584ab3af3eSknakahara struct ip6_hdr *xip6;
5594ab3af3eSknakahara proto = IPPROTO_IPV6;
5604ab3af3eSknakahara if (m->m_len < sizeof(*xip6)) {
5614ab3af3eSknakahara m = m_pullup(m, sizeof(*xip6));
56216a6b570Smaxv if (m == NULL)
5634ab3af3eSknakahara return ENOBUFS;
5644ab3af3eSknakahara }
5654ab3af3eSknakahara xip6 = mtod(m, struct ip6_hdr *);
5664ab3af3eSknakahara itos = (ntohl(xip6->ip6_flow) >> 20) & 0xff;
567*c01d8fb5Sandvar /* TODO: support ALTQ for inner packet */
5684ab3af3eSknakahara break;
5694ab3af3eSknakahara }
5704ab3af3eSknakahara default:
5714ab3af3eSknakahara m_freem(m);
5724ab3af3eSknakahara return EAFNOSUPPORT;
5734ab3af3eSknakahara }
5744ab3af3eSknakahara
5754ab3af3eSknakahara /* prepend new IP header */
5764ab3af3eSknakahara M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
5774ab3af3eSknakahara if (m && M_UNWRITABLE(m, sizeof(struct ip6_hdr)))
5784ab3af3eSknakahara m = m_pullup(m, sizeof(struct ip6_hdr));
5794ab3af3eSknakahara if (m == NULL)
5804ab3af3eSknakahara return ENOBUFS;
5814ab3af3eSknakahara
5824ab3af3eSknakahara ip6 = mtod(m, struct ip6_hdr *);
5834ab3af3eSknakahara ip6->ip6_flow = 0;
5844ab3af3eSknakahara ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
5854ab3af3eSknakahara ip6->ip6_vfc |= IPV6_VERSION;
586e7acdb68Sknakahara #if 0 /* ip6->ip6_plen will be filled by ip6_output */
587e7acdb68Sknakahara ip6->ip6_plen = htons((u_short)m->m_pkthdr.len - sizeof(*ip6));
588e7acdb68Sknakahara #endif
5894ab3af3eSknakahara ip6->ip6_nxt = proto;
5904ab3af3eSknakahara ip6->ip6_hlim = ip6_ipsec_hlim;
5914ab3af3eSknakahara ip6->ip6_src = sin6_src->sin6_addr;
5924ab3af3eSknakahara /* bidirectional configured tunnel mode */
5934ab3af3eSknakahara if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) {
5944ab3af3eSknakahara ip6->ip6_dst = sin6_dst->sin6_addr;
5954ab3af3eSknakahara } else {
5964ab3af3eSknakahara m_freem(m);
5974ab3af3eSknakahara return ENETUNREACH;
5984ab3af3eSknakahara }
5994ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
60075255032Sknakahara if (!ip6_ipsec_copy_tos)
60175255032Sknakahara otos = 0;
60275255032Sknakahara
6034ab3af3eSknakahara if (ifp->if_flags & IFF_ECN)
6044ab3af3eSknakahara ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
6054ab3af3eSknakahara else
6064ab3af3eSknakahara ip_ecn_ingress(ECN_NOCARE, &otos, &itos);
6074ab3af3eSknakahara #else
6084ab3af3eSknakahara if (ip6_ipsec_copy_tos)
6094ab3af3eSknakahara otos = itos;
6104ab3af3eSknakahara else
6114ab3af3eSknakahara otos = 0;
6124ab3af3eSknakahara #endif
6134ab3af3eSknakahara ip6->ip6_flow &= ~ntohl(0xff00000);
6144ab3af3eSknakahara ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
6154ab3af3eSknakahara
6164ab3af3eSknakahara sockaddr_in6_init(&u.dst6, &sin6_dst->sin6_addr, 0, 0, 0);
6174ab3af3eSknakahara
6182da350beSknakahara if_tunnel_get_ro(sc->ipsec_ro_percpu, &ro_pc, &lock_pc);
6192da350beSknakahara if ((rt = rtcache_lookup(ro_pc, &u.dst)) == NULL) {
6202da350beSknakahara if_tunnel_put_ro(sc->ipsec_ro_percpu, lock_pc);
6214ab3af3eSknakahara m_freem(m);
6224ab3af3eSknakahara return ENETUNREACH;
6234ab3af3eSknakahara }
6244ab3af3eSknakahara
6254ab3af3eSknakahara if (rt->rt_ifp == ifp) {
6262da350beSknakahara rtcache_unref(rt, ro_pc);
6272da350beSknakahara rtcache_free(ro_pc);
6282da350beSknakahara if_tunnel_put_ro(sc->ipsec_ro_percpu, lock_pc);
6294ab3af3eSknakahara m_freem(m);
6304ab3af3eSknakahara return ENETUNREACH;
6314ab3af3eSknakahara }
6322da350beSknakahara rtcache_unref(rt, ro_pc);
6334ab3af3eSknakahara
63458c56020Sknakahara /* set NAT-T ports */
63558c56020Sknakahara error = ipsecif_set_natt_ports(var, m);
63658c56020Sknakahara if (error) {
63758c56020Sknakahara m_freem(m);
63858c56020Sknakahara goto out;
63958c56020Sknakahara }
64058c56020Sknakahara
6414ab3af3eSknakahara /*
64293a28c82Sknakahara * - IPSEC_PMTU_MINMTU
64393a28c82Sknakahara * Force fragmentation to minimum MTU to avoid path MTU discovery
64493a28c82Sknakahara * - IPSEC_PMTU_OUTERMTU
64593a28c82Sknakahara * Trust outer MTU is large enough to send all packets
64693a28c82Sknakahara *
64793a28c82Sknakahara * It is too painful to ask for resend of inner packet, to achieve
6484ab3af3eSknakahara * path MTU discovery for encapsulated packets.
64993a28c82Sknakahara *
65093a28c82Sknakahara * See RFC4459.
6514ab3af3eSknakahara */
65293a28c82Sknakahara if (sc->ipsec_pmtu == IPSEC_PMTU_SYSDEFAULT) {
65393a28c82Sknakahara switch (ip6_ipsec_pmtu) {
65493a28c82Sknakahara case IPSEC_PMTU_MINMTU:
65593a28c82Sknakahara flags = IPV6_MINMTU;
65693a28c82Sknakahara break;
65793a28c82Sknakahara case IPSEC_PMTU_OUTERMTU:
65893a28c82Sknakahara flags = 0;
65993a28c82Sknakahara break;
66093a28c82Sknakahara default:
66193a28c82Sknakahara #ifdef DEBUG
66293a28c82Sknakahara log(LOG_DEBUG, "%s: ignore unexpected ip6_ipsec_pmtu %d\n",
66393a28c82Sknakahara __func__, ip6_ipsec_pmtu);
66493a28c82Sknakahara #endif
66593a28c82Sknakahara flags = IPV6_MINMTU;
66693a28c82Sknakahara break;
66793a28c82Sknakahara }
66893a28c82Sknakahara } else {
66993a28c82Sknakahara switch (sc->ipsec_pmtu) {
67093a28c82Sknakahara case IPSEC_PMTU_MINMTU:
67193a28c82Sknakahara flags = IPV6_MINMTU;
67293a28c82Sknakahara break;
67393a28c82Sknakahara case IPSEC_PMTU_OUTERMTU:
67493a28c82Sknakahara flags = 0;
67593a28c82Sknakahara break;
67693a28c82Sknakahara default:
67793a28c82Sknakahara #ifdef DEBUG
67893a28c82Sknakahara log(LOG_DEBUG, "%s: ignore unexpected ipsec_pmtu of %s %d\n",
67993a28c82Sknakahara __func__, ifp->if_xname, sc->ipsec_pmtu);
68093a28c82Sknakahara #endif
68193a28c82Sknakahara flags = IPV6_MINMTU;
68293a28c82Sknakahara break;
68393a28c82Sknakahara }
68493a28c82Sknakahara }
68593a28c82Sknakahara error = ip6_output(m, 0, ro_pc, flags, 0, NULL, NULL);
68658c56020Sknakahara
68758c56020Sknakahara out:
6884ab3af3eSknakahara if (error)
6892da350beSknakahara rtcache_free(ro_pc);
6902da350beSknakahara if_tunnel_put_ro(sc->ipsec_ro_percpu, lock_pc);
6914ab3af3eSknakahara
6924ab3af3eSknakahara return error;
6934ab3af3eSknakahara }
6944ab3af3eSknakahara #endif /* INET6 */
6954ab3af3eSknakahara
6964ab3af3eSknakahara static void
ipsecif4_input(struct mbuf * m,int off,int proto,void * eparg)6974ab3af3eSknakahara ipsecif4_input(struct mbuf *m, int off, int proto, void *eparg)
6984ab3af3eSknakahara {
6994ab3af3eSknakahara struct ifnet *ipsecp;
7004ab3af3eSknakahara struct ipsec_softc *sc = eparg;
7014ab3af3eSknakahara struct ipsec_variant *var;
7024ab3af3eSknakahara const struct ip *ip;
7034ab3af3eSknakahara int af;
7044ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
7054ab3af3eSknakahara u_int8_t otos;
7064ab3af3eSknakahara #endif
7074ab3af3eSknakahara struct psref psref_rcvif;
7084ab3af3eSknakahara struct psref psref_var;
7094ab3af3eSknakahara struct ifnet *rcvif;
7104ab3af3eSknakahara
7114ab3af3eSknakahara KASSERT(sc != NULL);
7124ab3af3eSknakahara
7134ab3af3eSknakahara ipsecp = &sc->ipsec_if;
7144ab3af3eSknakahara if ((ipsecp->if_flags & IFF_UP) == 0) {
7154ab3af3eSknakahara m_freem(m);
7164ab3af3eSknakahara ip_statinc(IP_STAT_NOIPSEC);
7174ab3af3eSknakahara return;
7184ab3af3eSknakahara }
7194ab3af3eSknakahara
7204ab3af3eSknakahara var = if_ipsec_getref_variant(sc, &psref_var);
7214ab3af3eSknakahara if (if_ipsec_variant_is_unconfigured(var)) {
7224ab3af3eSknakahara if_ipsec_putref_variant(var, &psref_var);
7234ab3af3eSknakahara m_freem(m);
7244ab3af3eSknakahara ip_statinc(IP_STAT_NOIPSEC);
7254ab3af3eSknakahara return;
7264ab3af3eSknakahara }
7274ab3af3eSknakahara
7284ab3af3eSknakahara ip = mtod(m, const struct ip *);
7294ab3af3eSknakahara
7304ab3af3eSknakahara rcvif = m_get_rcvif_psref(m, &psref_rcvif);
7314ab3af3eSknakahara if (rcvif == NULL || !ipsecif4_filter4(ip, var, rcvif)) {
7324ab3af3eSknakahara m_put_rcvif_psref(rcvif, &psref_rcvif);
7334ab3af3eSknakahara if_ipsec_putref_variant(var, &psref_var);
7344ab3af3eSknakahara m_freem(m);
7354ab3af3eSknakahara ip_statinc(IP_STAT_NOIPSEC);
7364ab3af3eSknakahara return;
7374ab3af3eSknakahara }
7384ab3af3eSknakahara m_put_rcvif_psref(rcvif, &psref_rcvif);
7394ab3af3eSknakahara if_ipsec_putref_variant(var, &psref_var);
7404ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
7414ab3af3eSknakahara otos = ip->ip_tos;
7424ab3af3eSknakahara #endif
7434ab3af3eSknakahara m_adj(m, off);
7444ab3af3eSknakahara
7454ab3af3eSknakahara switch (proto) {
7464ab3af3eSknakahara case IPPROTO_IPV4:
7474ab3af3eSknakahara {
7484ab3af3eSknakahara struct ip *xip;
7494ab3af3eSknakahara af = AF_INET;
7504ab3af3eSknakahara if (M_UNWRITABLE(m, sizeof(*xip))) {
7514ab3af3eSknakahara m = m_pullup(m, sizeof(*xip));
75216a6b570Smaxv if (m == NULL)
7534ab3af3eSknakahara return;
7544ab3af3eSknakahara }
7554ab3af3eSknakahara xip = mtod(m, struct ip *);
7564ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
7574ab3af3eSknakahara if (ipsecp->if_flags & IFF_ECN)
7584ab3af3eSknakahara ip_ecn_egress(ECN_ALLOWED, &otos, &xip->ip_tos);
7594ab3af3eSknakahara else
7604ab3af3eSknakahara ip_ecn_egress(ECN_NOCARE, &otos, &xip->ip_tos);
7614ab3af3eSknakahara #endif
7624ab3af3eSknakahara break;
7634ab3af3eSknakahara }
7644ab3af3eSknakahara #ifdef INET6
7654ab3af3eSknakahara case IPPROTO_IPV6:
7664ab3af3eSknakahara {
7674ab3af3eSknakahara struct ip6_hdr *ip6;
7684ab3af3eSknakahara u_int8_t itos;
7694ab3af3eSknakahara af = AF_INET6;
7704ab3af3eSknakahara if (M_UNWRITABLE(m, sizeof(*ip6))) {
7714ab3af3eSknakahara m = m_pullup(m, sizeof(*ip6));
77216a6b570Smaxv if (m == NULL)
7734ab3af3eSknakahara return;
7744ab3af3eSknakahara }
7754ab3af3eSknakahara ip6 = mtod(m, struct ip6_hdr *);
7764ab3af3eSknakahara itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
7774ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
7784ab3af3eSknakahara if (ipsecp->if_flags & IFF_ECN)
7794ab3af3eSknakahara ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
7804ab3af3eSknakahara else
7814ab3af3eSknakahara ip_ecn_egress(ECN_NOCARE, &otos, &itos);
7824ab3af3eSknakahara #endif
7834ab3af3eSknakahara ip6->ip6_flow &= ~htonl(0xff << 20);
7844ab3af3eSknakahara ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
7854ab3af3eSknakahara break;
7864ab3af3eSknakahara }
7874ab3af3eSknakahara #endif /* INET6 */
7884ab3af3eSknakahara default:
7894ab3af3eSknakahara ip_statinc(IP_STAT_NOIPSEC);
7904ab3af3eSknakahara m_freem(m);
7914ab3af3eSknakahara return;
7924ab3af3eSknakahara }
7934ab3af3eSknakahara if_ipsec_input(m, af, ipsecp);
7944ab3af3eSknakahara
7954ab3af3eSknakahara return;
7964ab3af3eSknakahara }
7974ab3af3eSknakahara
7984ab3af3eSknakahara /*
799ad2fa80cSmsaitoh * validate and filter the packet
8004ab3af3eSknakahara */
8014ab3af3eSknakahara static int
ipsecif4_filter4(const struct ip * ip,struct ipsec_variant * var,struct ifnet * ifp)8024ab3af3eSknakahara ipsecif4_filter4(const struct ip *ip, struct ipsec_variant *var,
8034ab3af3eSknakahara struct ifnet *ifp)
8044ab3af3eSknakahara {
8054ab3af3eSknakahara struct sockaddr_in *src, *dst;
8064ab3af3eSknakahara
8074ab3af3eSknakahara src = satosin(var->iv_psrc);
8084ab3af3eSknakahara dst = satosin(var->iv_pdst);
8094ab3af3eSknakahara
8104ab3af3eSknakahara return in_tunnel_validate(ip, src->sin_addr, dst->sin_addr);
8114ab3af3eSknakahara }
8124ab3af3eSknakahara
8134ab3af3eSknakahara #ifdef INET6
8144ab3af3eSknakahara static int
ipsecif6_input(struct mbuf ** mp,int * offp,int proto,void * eparg)8154ab3af3eSknakahara ipsecif6_input(struct mbuf **mp, int *offp, int proto, void *eparg)
8164ab3af3eSknakahara {
8174ab3af3eSknakahara struct mbuf *m = *mp;
8184ab3af3eSknakahara struct ifnet *ipsecp;
8194ab3af3eSknakahara struct ipsec_softc *sc = eparg;
8204ab3af3eSknakahara struct ipsec_variant *var;
8214ab3af3eSknakahara struct ip6_hdr *ip6;
8224ab3af3eSknakahara int af = 0;
8234ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
8244ab3af3eSknakahara u_int32_t otos;
8254ab3af3eSknakahara #endif
8264ab3af3eSknakahara struct psref psref_rcvif;
8274ab3af3eSknakahara struct psref psref_var;
8284ab3af3eSknakahara struct ifnet *rcvif;
8294ab3af3eSknakahara
8304ab3af3eSknakahara KASSERT(eparg != NULL);
8314ab3af3eSknakahara
8324ab3af3eSknakahara ipsecp = &sc->ipsec_if;
8334ab3af3eSknakahara if ((ipsecp->if_flags & IFF_UP) == 0) {
8344ab3af3eSknakahara m_freem(m);
8354ab3af3eSknakahara IP6_STATINC(IP6_STAT_NOIPSEC);
8364ab3af3eSknakahara return IPPROTO_DONE;
8374ab3af3eSknakahara }
8384ab3af3eSknakahara
8394ab3af3eSknakahara var = if_ipsec_getref_variant(sc, &psref_var);
8404ab3af3eSknakahara if (if_ipsec_variant_is_unconfigured(var)) {
8414ab3af3eSknakahara if_ipsec_putref_variant(var, &psref_var);
8424ab3af3eSknakahara m_freem(m);
8434ab3af3eSknakahara IP6_STATINC(IP6_STAT_NOIPSEC);
8444ab3af3eSknakahara return IPPROTO_DONE;
8454ab3af3eSknakahara }
8464ab3af3eSknakahara
8474ab3af3eSknakahara ip6 = mtod(m, struct ip6_hdr *);
8484ab3af3eSknakahara
8494ab3af3eSknakahara rcvif = m_get_rcvif_psref(m, &psref_rcvif);
8504ab3af3eSknakahara if (rcvif == NULL || !ipsecif6_filter6(ip6, var, rcvif)) {
8514ab3af3eSknakahara m_put_rcvif_psref(rcvif, &psref_rcvif);
8524ab3af3eSknakahara if_ipsec_putref_variant(var, &psref_var);
8534ab3af3eSknakahara m_freem(m);
8544ab3af3eSknakahara IP6_STATINC(IP6_STAT_NOIPSEC);
8554ab3af3eSknakahara return IPPROTO_DONE;
8564ab3af3eSknakahara }
8574ab3af3eSknakahara m_put_rcvif_psref(rcvif, &psref_rcvif);
8584ab3af3eSknakahara if_ipsec_putref_variant(var, &psref_var);
8594ab3af3eSknakahara
8604ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
8614ab3af3eSknakahara otos = ip6->ip6_flow;
8624ab3af3eSknakahara #endif
8634ab3af3eSknakahara m_adj(m, *offp);
8644ab3af3eSknakahara
8654ab3af3eSknakahara switch (proto) {
8664ab3af3eSknakahara #ifdef INET
8674ab3af3eSknakahara case IPPROTO_IPV4:
8684ab3af3eSknakahara {
8694ab3af3eSknakahara af = AF_INET;
8704ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
8714ab3af3eSknakahara struct ip *ip;
8724ab3af3eSknakahara u_int8_t otos8;
8734ab3af3eSknakahara otos8 = (ntohl(otos) >> 20) & 0xff;
8744ab3af3eSknakahara
8754ab3af3eSknakahara if (M_UNWRITABLE(m, sizeof(*ip))) {
8764ab3af3eSknakahara m = m_pullup(m, sizeof(*ip));
87716a6b570Smaxv if (m == NULL)
8784ab3af3eSknakahara return IPPROTO_DONE;
8794ab3af3eSknakahara }
8804ab3af3eSknakahara ip = mtod(m, struct ip *);
8814ab3af3eSknakahara if (ipsecp->if_flags & IFF_ECN)
8824ab3af3eSknakahara ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
8834ab3af3eSknakahara else
8844ab3af3eSknakahara ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
8854ab3af3eSknakahara #endif
8864ab3af3eSknakahara break;
8874ab3af3eSknakahara }
8884ab3af3eSknakahara #endif /* INET */
8894ab3af3eSknakahara case IPPROTO_IPV6:
8904ab3af3eSknakahara {
8914ab3af3eSknakahara af = AF_INET6;
8924ab3af3eSknakahara #ifndef IPSEC_TX_TOS_CLEAR
8934ab3af3eSknakahara struct ip6_hdr *xip6;
8944ab3af3eSknakahara
8954ab3af3eSknakahara if (M_UNWRITABLE(m, sizeof(*xip6))) {
8964ab3af3eSknakahara m = m_pullup(m, sizeof(*xip6));
89716a6b570Smaxv if (m == NULL)
8984ab3af3eSknakahara return IPPROTO_DONE;
8994ab3af3eSknakahara }
9004ab3af3eSknakahara xip6 = mtod(m, struct ip6_hdr *);
9014ab3af3eSknakahara if (ipsecp->if_flags & IFF_ECN)
9024ab3af3eSknakahara ip6_ecn_egress(ECN_ALLOWED, &otos, &xip6->ip6_flow);
9034ab3af3eSknakahara else
9044ab3af3eSknakahara ip6_ecn_egress(ECN_NOCARE, &otos, &xip6->ip6_flow);
9054ab3af3eSknakahara break;
9064ab3af3eSknakahara #endif
9074ab3af3eSknakahara }
9084ab3af3eSknakahara default:
9094ab3af3eSknakahara IP6_STATINC(IP6_STAT_NOIPSEC);
9104ab3af3eSknakahara m_freem(m);
9114ab3af3eSknakahara return IPPROTO_DONE;
9124ab3af3eSknakahara }
9134ab3af3eSknakahara
9144ab3af3eSknakahara if_ipsec_input(m, af, ipsecp);
9154ab3af3eSknakahara return IPPROTO_DONE;
9164ab3af3eSknakahara }
9174ab3af3eSknakahara
9184ab3af3eSknakahara /*
9194ab3af3eSknakahara * validate and filter the packet.
9204ab3af3eSknakahara */
9214ab3af3eSknakahara static int
ipsecif6_filter6(const struct ip6_hdr * ip6,struct ipsec_variant * var,struct ifnet * ifp)9224ab3af3eSknakahara ipsecif6_filter6(const struct ip6_hdr *ip6, struct ipsec_variant *var,
9234ab3af3eSknakahara struct ifnet *ifp)
9244ab3af3eSknakahara {
9254ab3af3eSknakahara struct sockaddr_in6 *src, *dst;
9264ab3af3eSknakahara
9274ab3af3eSknakahara src = satosin6(var->iv_psrc);
9284ab3af3eSknakahara dst = satosin6(var->iv_pdst);
9294ab3af3eSknakahara
9304ab3af3eSknakahara return in6_tunnel_validate(ip6, &src->sin6_addr, &dst->sin6_addr);
9314ab3af3eSknakahara }
9324ab3af3eSknakahara #endif /* INET6 */
9334ab3af3eSknakahara
9344ab3af3eSknakahara int
ipsecif4_attach(struct ipsec_variant * var)9354ab3af3eSknakahara ipsecif4_attach(struct ipsec_variant *var)
9364ab3af3eSknakahara {
9374ab3af3eSknakahara struct ipsec_softc *sc = var->iv_softc;
9384ab3af3eSknakahara
9394ab3af3eSknakahara KASSERT(if_ipsec_variant_is_configured(var));
9404ab3af3eSknakahara
9414ab3af3eSknakahara if (var->iv_encap_cookie4 != NULL)
9424ab3af3eSknakahara return EALREADY;
94345ca136dSknakahara
94445ca136dSknakahara var->iv_encap_cookie4 = encap_attach_addr(AF_INET, -1,
94545ca136dSknakahara var->iv_psrc, var->iv_pdst, if_ipsec_encap_func, &ipsecif4_encapsw,
94645ca136dSknakahara sc);
9474ab3af3eSknakahara if (var->iv_encap_cookie4 == NULL)
9484ab3af3eSknakahara return EEXIST;
9494ab3af3eSknakahara
9504ab3af3eSknakahara var->iv_output = ipsecif4_output;
9514ab3af3eSknakahara return 0;
9524ab3af3eSknakahara }
9534ab3af3eSknakahara
9544ab3af3eSknakahara int
ipsecif4_detach(struct ipsec_variant * var)9554ab3af3eSknakahara ipsecif4_detach(struct ipsec_variant *var)
9564ab3af3eSknakahara {
9574ab3af3eSknakahara int error;
9584ab3af3eSknakahara
9594ab3af3eSknakahara if (var->iv_encap_cookie4 == NULL)
9604ab3af3eSknakahara return 0;
9614ab3af3eSknakahara
9624ab3af3eSknakahara var->iv_output = NULL;
9634ab3af3eSknakahara error = encap_detach(var->iv_encap_cookie4);
9644ab3af3eSknakahara if (error == 0)
9654ab3af3eSknakahara var->iv_encap_cookie4 = NULL;
9664ab3af3eSknakahara
9674ab3af3eSknakahara return error;
9684ab3af3eSknakahara }
9694ab3af3eSknakahara
9704ab3af3eSknakahara #ifdef INET6
9714ab3af3eSknakahara int
ipsecif6_attach(struct ipsec_variant * var)9724ab3af3eSknakahara ipsecif6_attach(struct ipsec_variant *var)
9734ab3af3eSknakahara {
9744ab3af3eSknakahara struct ipsec_softc *sc = var->iv_softc;
9754ab3af3eSknakahara
9764ab3af3eSknakahara KASSERT(if_ipsec_variant_is_configured(var));
9774ab3af3eSknakahara KASSERT(var->iv_encap_cookie6 == NULL);
9784ab3af3eSknakahara
97945ca136dSknakahara var->iv_encap_cookie6 = encap_attach_addr(AF_INET6, -1,
98045ca136dSknakahara var->iv_psrc, var->iv_pdst, if_ipsec_encap_func, &ipsecif6_encapsw,
98145ca136dSknakahara sc);
9824ab3af3eSknakahara if (var->iv_encap_cookie6 == NULL)
9834ab3af3eSknakahara return EEXIST;
9844ab3af3eSknakahara
9854ab3af3eSknakahara var->iv_output = ipsecif6_output;
9864ab3af3eSknakahara return 0;
9874ab3af3eSknakahara }
9884ab3af3eSknakahara
9894ab3af3eSknakahara int
ipsecif6_detach(struct ipsec_variant * var)9904ab3af3eSknakahara ipsecif6_detach(struct ipsec_variant *var)
9914ab3af3eSknakahara {
9924ab3af3eSknakahara struct ipsec_softc *sc = var->iv_softc;
9934ab3af3eSknakahara int error;
9944ab3af3eSknakahara
9954ab3af3eSknakahara KASSERT(var->iv_encap_cookie6 != NULL);
9964ab3af3eSknakahara
9972da350beSknakahara if_tunnel_ro_percpu_rtcache_free(sc->ipsec_ro_percpu);
9984ab3af3eSknakahara
9994ab3af3eSknakahara var->iv_output = NULL;
10004ab3af3eSknakahara error = encap_detach(var->iv_encap_cookie6);
10014ab3af3eSknakahara if (error == 0)
10024ab3af3eSknakahara var->iv_encap_cookie6 = NULL;
10034ab3af3eSknakahara return error;
10044ab3af3eSknakahara }
10054ab3af3eSknakahara
10064ab3af3eSknakahara void *
ipsecif6_ctlinput(int cmd,const struct sockaddr * sa,void * d,void * eparg)10074ab3af3eSknakahara ipsecif6_ctlinput(int cmd, const struct sockaddr *sa, void *d, void *eparg)
10084ab3af3eSknakahara {
10094ab3af3eSknakahara struct ipsec_softc *sc = eparg;
10104ab3af3eSknakahara struct ip6ctlparam *ip6cp = NULL;
10114ab3af3eSknakahara struct ip6_hdr *ip6;
10124ab3af3eSknakahara const struct sockaddr_in6 *dst6;
10132da350beSknakahara struct route *ro_pc;
10142da350beSknakahara kmutex_t *lock_pc;
10154ab3af3eSknakahara
10164ab3af3eSknakahara if (sa->sa_family != AF_INET6 ||
10174ab3af3eSknakahara sa->sa_len != sizeof(struct sockaddr_in6))
10184ab3af3eSknakahara return NULL;
10194ab3af3eSknakahara
10204ab3af3eSknakahara if ((unsigned)cmd >= PRC_NCMDS)
10214ab3af3eSknakahara return NULL;
10224ab3af3eSknakahara if (cmd == PRC_HOSTDEAD)
10234ab3af3eSknakahara d = NULL;
10244ab3af3eSknakahara else if (inet6ctlerrmap[cmd] == 0)
10254ab3af3eSknakahara return NULL;
10264ab3af3eSknakahara
10274ab3af3eSknakahara /* if the parameter is from icmp6, decode it. */
10284ab3af3eSknakahara if (d != NULL) {
10294ab3af3eSknakahara ip6cp = (struct ip6ctlparam *)d;
10304ab3af3eSknakahara ip6 = ip6cp->ip6c_ip6;
10314ab3af3eSknakahara } else {
10324ab3af3eSknakahara ip6 = NULL;
10334ab3af3eSknakahara }
10344ab3af3eSknakahara
10354ab3af3eSknakahara if (!ip6)
10364ab3af3eSknakahara return NULL;
10374ab3af3eSknakahara
10382da350beSknakahara if_tunnel_get_ro(sc->ipsec_ro_percpu, &ro_pc, &lock_pc);
10392da350beSknakahara dst6 = satocsin6(rtcache_getdst(ro_pc));
10404ab3af3eSknakahara /* XXX scope */
10414ab3af3eSknakahara if (dst6 == NULL)
10424ab3af3eSknakahara ;
10434ab3af3eSknakahara else if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst6->sin6_addr))
10444ab3af3eSknakahara /* flush route cache */
10452da350beSknakahara rtcache_free(ro_pc);
10464ab3af3eSknakahara
10472da350beSknakahara if_tunnel_put_ro(sc->ipsec_ro_percpu, lock_pc);
10484ab3af3eSknakahara
10494ab3af3eSknakahara return NULL;
10504ab3af3eSknakahara }
10514ab3af3eSknakahara
10524ab3af3eSknakahara ENCAP_PR_WRAP_CTLINPUT(ipsecif6_ctlinput)
10534ab3af3eSknakahara #define ipsecif6_ctlinput ipsecif6_ctlinput_wrapper
10544ab3af3eSknakahara
10554ab3af3eSknakahara static const struct encapsw ipsecif6_encapsw = {
10564ab3af3eSknakahara .encapsw6 = {
10574ab3af3eSknakahara .pr_input = ipsecif6_input,
10584ab3af3eSknakahara .pr_ctlinput = ipsecif6_ctlinput,
10594ab3af3eSknakahara }
10604ab3af3eSknakahara };
10614ab3af3eSknakahara #endif /* INET6 */
1062