From owner-svn-src-projects@freebsd.org Thu Nov 24 09:39:01 2016 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id A318BC50DA0 for ; Thu, 24 Nov 2016 09:39:01 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 7DAE277; Thu, 24 Nov 2016 09:39:01 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uAO9d0Kv052664; Thu, 24 Nov 2016 09:39:00 GMT (envelope-from ae@FreeBSD.org) Received: (from ae@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uAO9d00N052663; Thu, 24 Nov 2016 09:39:00 GMT (envelope-from ae@FreeBSD.org) Message-Id: <201611240939.uAO9d00N052663@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ae set sender to ae@FreeBSD.org using -f From: "Andrey V. Elsukov" Date: Thu, 24 Nov 2016 09:39:00 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r309091 - projects/ipsec/sys/netipsec X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 24 Nov 2016 09:39:01 -0000 Author: ae Date: Thu Nov 24 09:39:00 2016 New Revision: 309091 URL: https://svnweb.freebsd.org/changeset/base/309091 Log: Rework outbound IPsec processing. Add ipsec4_allocsa() and ipsec6_allocsa() functions that are used to lookup for a SA corresponding to security policy. Modified: projects/ipsec/sys/netipsec/ipsec_output.c Modified: projects/ipsec/sys/netipsec/ipsec_output.c ============================================================================== --- projects/ipsec/sys/netipsec/ipsec_output.c Thu Nov 24 09:00:51 2016 (r309090) +++ projects/ipsec/sys/netipsec/ipsec_output.c Thu Nov 24 09:39:00 2016 (r309091) @@ -88,6 +88,201 @@ #include #endif +#define IPSEC_OSTAT_INC(proto, name) do { \ + if ((proto) == IPPROTO_ESP) \ + ESPSTAT_INC(esps_##name); \ + else if ((proto) == IPPROTO_AH)\ + AHSTAT_INC(ahs_##name); \ + else \ + IPCOMPSTAT_INC(ipcomps_##name); \ +} while (0) + +#ifdef INET +static struct secasvar * +ipsec4_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error) +{ + struct secasindex *saidx, tmpsaidx; + struct ipsecrequest *isr; + struct sockaddr_in *sin; + struct secasvar *sav; + struct ip *ip; + + /* + * Check system global policy controls. + */ +next: + isr = sp->req[*pidx]; + if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || + (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || + (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { + DPRINTF(("%s: IPsec outbound packet dropped due" + " to policy (check your sysctls)\n", __func__)); + IPSEC_OSTAT_INC(isr->saidx.proto, pdrops); + *error = EHOSTUNREACH; + return (NULL); + } + /* + * Craft SA index to search for proper SA. Note that + * we only initialize unspecified SA peers for transport + * mode; for tunnel mode they must already be filled in. + */ + if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { + saidx = &tmpsaidx; + *saidx = isr->saidx; + ip = mtod(m, struct ip *); + if (saidx->src.sa.sa_len == 0) { + sin = &saidx->src.sin; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + sin->sin_addr = ip->ip_src; + } + if (saidx->dst.sa.sa_len == 0) { + sin = &saidx->dst.sin; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + sin->sin_addr = ip->ip_dst; + } + } else + saidx = &sp->req[*pidx]->saidx; + /* + * Lookup SA and validate it. + */ + sav = key_allocsa_policy(sp, saidx, error); + if (sav == NULL) { + IPSECSTAT_INC(ips_out_nosa); + if (*error != 0) + return (NULL); + if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) { + /* + * We have no SA and policy that doesn't require + * this IPsec transform, thus we can continue w/o + * IPsec processing, i.e. return EJUSTRETURN. + * But first check if there is some bundled transform. + */ + if (sp->tcount > (*pidx)) { + (*pidx)++; + goto next; + } + *error = EJUSTRETURN; + } + return (NULL); + } + /* + * Sanity check the SA content for the caller + * before they invoke the xform output method. + */ + if (sav->tdb_xform == NULL) { + DPRINTF(("%s: no transform for SA\n", __func__)); + IPSEC_OSTAT_INC(isr->saidx.proto, noxform); + key_freesav(&sav); + *error = EHOSTUNREACH; + return (NULL); + } + return (sav); +} +#endif + +#ifdef INET6 +static struct secasvar * +ipsec6_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error) +{ + struct secasindex *saidx, tmpsaidx; + struct ipsecrequest *isr; + struct sockaddr_in6 *sin6; + struct secasvar *sav; + struct ip6_hdr *ip6; + + /* + * Check system global policy controls. + */ +next: + isr = sp->req[*pidx]; + if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || + (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || + (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { + DPRINTF(("%s: IPsec outbound packet dropped due" + " to policy (check your sysctls)\n", __func__)); + IPSEC_OSTAT_INC(isr->saidx.proto, pdrops); + *error = EHOSTUNREACH; + return (NULL); + } + /* + * Craft SA index to search for proper SA. Note that + * we only fillin unspecified SA peers for transport + * mode; for tunnel mode they must already be filled in. + */ + if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { + saidx = &tmpsaidx; + *saidx = isr->saidx; + ip6 = mtod(m, struct ip6_hdr *); + if (saidx->src.sin6.sin6_len == 0) { + sin6 = (struct sockaddr_in6 *)&saidx->src; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + sin6->sin6_addr = ip6->ip6_src; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = + ntohs(ip6->ip6_src.s6_addr16[1]); + } + } + if (saidx->dst.sin6.sin6_len == 0) { + sin6 = (struct sockaddr_in6 *)&saidx->dst; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + sin6->sin6_addr = ip6->ip6_dst; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = + ntohs(ip6->ip6_dst.s6_addr16[1]); + } + } + } else + saidx = &sp->req[*pidx]->saidx; + /* + * Lookup SA and validate it. + */ + sav = key_allocsa_policy(sp, saidx, error); + if (sav == NULL) { + IPSEC6STAT_INC(ips_out_nosa); + if (*error != 0) + return (NULL); + if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) { + /* + * We have no SA and policy that doesn't require + * this IPsec transform, thus we can continue w/o + * IPsec processing, i.e. return EJUSTRETURN. + * But first check if there is some bundled transform. + */ + if (sp->tcount > (*pidx)) { + (*pidx)++; + goto next; + } + *error = EJUSTRETURN; + } + return (NULL); + } + /* + * Sanity check the SA content for the caller + * before they invoke the xform output method. + */ + if (sav->tdb_xform == NULL) { + DPRINTF(("%s: no transform for SA\n", __func__)); + IPSEC_OSTAT_INC(isr->saidx.proto, noxform); + key_freesav(&sav); + *error = EHOSTUNREACH; + return (NULL); + } + return (sav); +} +#endif /* INET6 */ + int ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) {