1*481d3881Srin /* $NetBSD: ipsec_output.c,v 1.87 2024/07/05 04:31:54 rin Exp $ */
2fdbf515aSthorpej
379002044Smaxv /*
4fdbf515aSthorpej * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
5fdbf515aSthorpej * All rights reserved.
6fdbf515aSthorpej *
7fdbf515aSthorpej * Redistribution and use in source and binary forms, with or without
8fdbf515aSthorpej * modification, are permitted provided that the following conditions
9fdbf515aSthorpej * are met:
10fdbf515aSthorpej * 1. Redistributions of source code must retain the above copyright
11fdbf515aSthorpej * notice, this list of conditions and the following disclaimer.
12fdbf515aSthorpej * 2. Redistributions in binary form must reproduce the above copyright
13fdbf515aSthorpej * notice, this list of conditions and the following disclaimer in the
14fdbf515aSthorpej * documentation and/or other materials provided with the distribution.
15fdbf515aSthorpej *
16fdbf515aSthorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17fdbf515aSthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18fdbf515aSthorpej * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19fdbf515aSthorpej * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20fdbf515aSthorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21fdbf515aSthorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22fdbf515aSthorpej * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23fdbf515aSthorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24fdbf515aSthorpej * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25fdbf515aSthorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26fdbf515aSthorpej * SUCH DAMAGE.
27fdbf515aSthorpej *
28e2c8a664Smaxv * $FreeBSD: sys/netipsec/ipsec_output.c,v 1.3.2.2 2003/03/28 20:32:53 sam Exp $
29fdbf515aSthorpej */
3074029031Sjonathan
3174029031Sjonathan #include <sys/cdefs.h>
32*481d3881Srin __KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.87 2024/07/05 04:31:54 rin Exp $");
3374029031Sjonathan
3480d40a78Sozaki-r #if defined(_KERNEL_OPT)
3574029031Sjonathan #include "opt_inet.h"
36e1c9808fSozaki-r #include "opt_net_mpsafe.h"
3780d40a78Sozaki-r #endif
3874029031Sjonathan
3974029031Sjonathan #include <sys/param.h>
4074029031Sjonathan #include <sys/systm.h>
4174029031Sjonathan #include <sys/mbuf.h>
4274029031Sjonathan #include <sys/domain.h>
4374029031Sjonathan #include <sys/protosw.h>
4474029031Sjonathan #include <sys/socket.h>
4574029031Sjonathan #include <sys/errno.h>
4674029031Sjonathan #include <sys/syslog.h>
4774029031Sjonathan
4874029031Sjonathan #include <net/if.h>
4974029031Sjonathan #include <net/route.h>
5074029031Sjonathan
5174029031Sjonathan #include <netinet/in.h>
5274029031Sjonathan #include <netinet/in_systm.h>
5374029031Sjonathan #include <netinet/ip.h>
5474029031Sjonathan #include <netinet/ip_var.h>
5574029031Sjonathan #include <netinet/in_var.h>
5674029031Sjonathan #include <netinet/ip_ecn.h>
5774029031Sjonathan
5874029031Sjonathan #include <netinet/ip6.h>
5974029031Sjonathan #ifdef INET6
6074029031Sjonathan #include <netinet6/ip6_var.h>
6174029031Sjonathan #endif
6274029031Sjonathan #include <netinet/in_pcb.h>
6374029031Sjonathan #ifdef INET6
6474029031Sjonathan #include <netinet/icmp6.h>
6574029031Sjonathan #endif
664ddfe916Sdegroote #include <netinet/udp.h>
6774029031Sjonathan
6874029031Sjonathan #include <netipsec/ipsec.h>
6985b3ba5bSjonathan #include <netipsec/ipsec_var.h>
70caf49ea5Sthorpej #include <netipsec/ipsec_private.h>
7174029031Sjonathan #ifdef INET6
7274029031Sjonathan #include <netipsec/ipsec6.h>
7374029031Sjonathan #endif
7474029031Sjonathan #include <netipsec/ah_var.h>
7574029031Sjonathan #include <netipsec/esp_var.h>
7674029031Sjonathan #include <netipsec/ipcomp_var.h>
7774029031Sjonathan
7874029031Sjonathan #include <netipsec/xform.h>
7974029031Sjonathan
809355900eStls #include <netipsec/key.h>
819355900eStls #include <netipsec/keydb.h>
829355900eStls #include <netipsec/key_debug.h>
8374029031Sjonathan
8464e64168Sozaki-r static percpu_t *ipsec_rtcache_percpu __cacheline_aligned;
8561e79ba3Sdegroote
8661e79ba3Sdegroote /*
8761e79ba3Sdegroote * Add a IPSEC_OUT_DONE tag to mark that we have finished the ipsec processing
8861e79ba3Sdegroote * It will be used by ip{,6}_output to check if we have already or not
8961e79ba3Sdegroote * processed this packet.
9061e79ba3Sdegroote */
9161e79ba3Sdegroote static int
ipsec_register_done(struct mbuf * m,int * error)9261e79ba3Sdegroote ipsec_register_done(struct mbuf *m, int *error)
9361e79ba3Sdegroote {
9461e79ba3Sdegroote struct m_tag *mtag;
9561e79ba3Sdegroote
9661e79ba3Sdegroote mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 0, M_NOWAIT);
9761e79ba3Sdegroote if (mtag == NULL) {
98290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "could not get packet tag\n");
9961e79ba3Sdegroote *error = ENOMEM;
10061e79ba3Sdegroote return -1;
10161e79ba3Sdegroote }
10261e79ba3Sdegroote
10361e79ba3Sdegroote m_tag_prepend(m, mtag);
10461e79ba3Sdegroote return 0;
10561e79ba3Sdegroote }
10661e79ba3Sdegroote
10755718e80Sdegroote static int
ipsec_reinject_ipstack(struct mbuf * m,int af,int flags)108c535599fSknakahara ipsec_reinject_ipstack(struct mbuf *m, int af, int flags)
10955718e80Sdegroote {
110f26d4ea4Sozaki-r int rv = -1;
11164e64168Sozaki-r struct route *ro;
11255718e80Sdegroote
113f26d4ea4Sozaki-r KASSERT(af == AF_INET || af == AF_INET6);
114f26d4ea4Sozaki-r
115cead3b88Sozaki-r KERNEL_LOCK_UNLESS_NET_MPSAFE();
116e524fb36Sozaki-r ro = rtcache_percpu_getref(ipsec_rtcache_percpu);
117f26d4ea4Sozaki-r switch (af) {
118f26d4ea4Sozaki-r #ifdef INET
119f26d4ea4Sozaki-r case AF_INET:
12064e64168Sozaki-r rv = ip_output(m, NULL, ro, IP_RAWOUTPUT|IP_NOIPNEWID,
1217f3d4048Splunky NULL, NULL);
122f26d4ea4Sozaki-r break;
123e1c9808fSozaki-r #endif
12455718e80Sdegroote #ifdef INET6
12555718e80Sdegroote case AF_INET6:
12655718e80Sdegroote /*
12755718e80Sdegroote * We don't need massage, IPv6 header fields are always in
12855718e80Sdegroote * net endian.
12955718e80Sdegroote */
130c535599fSknakahara rv = ip6_output(m, NULL, ro, flags, NULL, NULL, NULL);
131f26d4ea4Sozaki-r break;
132f26d4ea4Sozaki-r #endif
133f26d4ea4Sozaki-r }
134e524fb36Sozaki-r rtcache_percpu_putref(ipsec_rtcache_percpu);
135cead3b88Sozaki-r KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
13655718e80Sdegroote
137f26d4ea4Sozaki-r return rv;
13855718e80Sdegroote }
13955718e80Sdegroote
14074029031Sjonathan int
ipsec_process_done(struct mbuf * m,const struct ipsecrequest * isr,struct secasvar * sav,int flags)14183c2b87aSozaki-r ipsec_process_done(struct mbuf *m, const struct ipsecrequest *isr,
142c535599fSknakahara struct secasvar *sav, int flags)
14374029031Sjonathan {
14474029031Sjonathan struct secasindex *saidx;
14574029031Sjonathan int error;
1464ddfe916Sdegroote #ifdef INET
1474ddfe916Sdegroote struct ip *ip;
14879002044Smaxv #endif
1494ddfe916Sdegroote #ifdef INET6
1504ddfe916Sdegroote struct ip6_hdr *ip6;
15179002044Smaxv #endif
1524ddfe916Sdegroote struct mbuf *mo;
1534ddfe916Sdegroote struct udphdr *udp = NULL;
154890dda53Sknakahara int hlen, roff, iphlen;
15574029031Sjonathan
1562620e166Sozaki-r KASSERT(m != NULL);
1572620e166Sozaki-r KASSERT(isr != NULL);
1582620e166Sozaki-r KASSERT(sav != NULL);
15974029031Sjonathan
16074029031Sjonathan saidx = &sav->sah->saidx;
1614ddfe916Sdegroote
1624ddfe916Sdegroote if (sav->natt_type != 0) {
1634ddfe916Sdegroote hlen = sizeof(struct udphdr);
1644ddfe916Sdegroote
165890dda53Sknakahara switch (saidx->dst.sa.sa_family) {
166890dda53Sknakahara #ifdef INET
167890dda53Sknakahara case AF_INET:
168890dda53Sknakahara ip = mtod(m, struct ip *);
1694ddfe916Sdegroote mo = m_makespace(m, sizeof(struct ip), hlen, &roff);
170890dda53Sknakahara iphlen = ip->ip_hl << 2;
171890dda53Sknakahara break;
172890dda53Sknakahara #endif
173890dda53Sknakahara #ifdef INET6
174890dda53Sknakahara case AF_INET6:
175890dda53Sknakahara ip6 = mtod(m, struct ip6_hdr *);
176890dda53Sknakahara mo = m_makespace(m, sizeof(struct ip6_hdr), hlen, &roff);
177890dda53Sknakahara iphlen = sizeof(*ip6);
178890dda53Sknakahara break;
179890dda53Sknakahara #endif
180890dda53Sknakahara default:
181890dda53Sknakahara IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
182890dda53Sknakahara saidx->dst.sa.sa_family);
183890dda53Sknakahara error = ENXIO;
184890dda53Sknakahara goto bad;
185890dda53Sknakahara }
186890dda53Sknakahara
1874ddfe916Sdegroote if (mo == NULL) {
188dd8c81f5Sryo char buf[IPSEC_ADDRSTRLEN];
189290dc492Sozaki-r IPSECLOG(LOG_DEBUG,
190290dc492Sozaki-r "failed to inject %u byte UDP for SA %s/%08lx\n",
191dd8c81f5Sryo hlen, ipsec_address(&saidx->dst, buf, sizeof(buf)),
192290dc492Sozaki-r (u_long)ntohl(sav->spi));
1934ddfe916Sdegroote error = ENOBUFS;
1944ddfe916Sdegroote goto bad;
1954ddfe916Sdegroote }
1964ddfe916Sdegroote
1974ddfe916Sdegroote udp = (struct udphdr *)(mtod(mo, char *) + roff);
1984ddfe916Sdegroote udp->uh_sport = key_portfromsaddr(&saidx->src);
1994ddfe916Sdegroote udp->uh_dport = key_portfromsaddr(&saidx->dst);
2004ddfe916Sdegroote udp->uh_sum = 0;
201890dda53Sknakahara udp->uh_ulen = htons(m->m_pkthdr.len - iphlen);
2024ddfe916Sdegroote }
2034ddfe916Sdegroote
2041e45b2f1Smaxv /*
2051e45b2f1Smaxv * Fix the header length, for AH processing.
2061e45b2f1Smaxv */
20774029031Sjonathan switch (saidx->dst.sa.sa_family) {
20874029031Sjonathan #ifdef INET
20974029031Sjonathan case AF_INET:
2104ddfe916Sdegroote ip = mtod(m, struct ip *);
2114ddfe916Sdegroote ip->ip_len = htons(m->m_pkthdr.len);
212890dda53Sknakahara /* IPv4 packet does not have to be set UDP checksum. */
2134ddfe916Sdegroote if (sav->natt_type != 0)
2144ddfe916Sdegroote ip->ip_p = IPPROTO_UDP;
21574029031Sjonathan break;
21679002044Smaxv #endif
21774029031Sjonathan #ifdef INET6
21874029031Sjonathan case AF_INET6:
21974029031Sjonathan if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
22074029031Sjonathan error = ENXIO;
22174029031Sjonathan goto bad;
22274029031Sjonathan }
22374029031Sjonathan if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
22474029031Sjonathan /* No jumbogram support. */
22574029031Sjonathan error = ENXIO; /*?*/
22674029031Sjonathan goto bad;
22774029031Sjonathan }
2284ddfe916Sdegroote ip6 = mtod(m, struct ip6_hdr *);
2294ddfe916Sdegroote ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
230890dda53Sknakahara /* IPv6 packet should be set UDP checksum. */
231890dda53Sknakahara if (sav->natt_type != 0) {
2324ddfe916Sdegroote ip6->ip6_nxt = IPPROTO_UDP;
233890dda53Sknakahara ipsec6_udp_cksum(m);
234890dda53Sknakahara }
23574029031Sjonathan break;
23679002044Smaxv #endif
23774029031Sjonathan default:
238290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
239290dc492Sozaki-r saidx->dst.sa.sa_family);
24074029031Sjonathan error = ENXIO;
24174029031Sjonathan goto bad;
24274029031Sjonathan }
24374029031Sjonathan
244e790ebf1Sdrochner key_sa_recordxfer(sav, m);
245e790ebf1Sdrochner
24674029031Sjonathan /*
24774029031Sjonathan * If there's another (bundled) SA to apply, do so.
24874029031Sjonathan * Note that this puts a burden on the kernel stack size.
24974029031Sjonathan * If this is a problem we'll need to introduce a queue
25074029031Sjonathan * to set the packet on so we can unwind the stack before
25174029031Sjonathan * doing further processing.
25274029031Sjonathan */
25374029031Sjonathan if (isr->next) {
254caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_OUT_BUNDLESA);
255e2211411Sdegroote switch (saidx->dst.sa.sa_family) {
256e2211411Sdegroote #ifdef INET
257e2211411Sdegroote case AF_INET:
258ea86b0a9Sozaki-r return ipsec4_process_packet(m, isr->next, NULL);
25979002044Smaxv #endif
260e2211411Sdegroote #ifdef INET6
261e2211411Sdegroote case AF_INET6:
262c535599fSknakahara return ipsec6_process_packet(m, isr->next, flags);
26379002044Smaxv #endif
264e2211411Sdegroote default:
265290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
266290dc492Sozaki-r saidx->dst.sa.sa_family);
267e2211411Sdegroote error = ENXIO;
268e2211411Sdegroote goto bad;
269e2211411Sdegroote }
27074029031Sjonathan }
27174029031Sjonathan
27274029031Sjonathan /*
2731e45b2f1Smaxv * We're done with IPsec processing, mark the packet as processed,
2741e45b2f1Smaxv * and transmit it using the appropriate network protocol
2751e45b2f1Smaxv * (IPv4/IPv6).
27674029031Sjonathan */
27761e79ba3Sdegroote
27861e79ba3Sdegroote if (ipsec_register_done(m, &error) < 0)
27961e79ba3Sdegroote goto bad;
28061e79ba3Sdegroote
281c535599fSknakahara return ipsec_reinject_ipstack(m, saidx->dst.sa.sa_family, flags);
28279002044Smaxv
28374029031Sjonathan bad:
28474029031Sjonathan m_freem(m);
28579002044Smaxv return error;
28674029031Sjonathan }
28774029031Sjonathan
288289fe532Sozaki-r static void
ipsec_fill_saidx_bymbuf(struct secasindex * saidx,const struct mbuf * m,const int af)289289fe532Sozaki-r ipsec_fill_saidx_bymbuf(struct secasindex *saidx, const struct mbuf *m,
290289fe532Sozaki-r const int af)
29174029031Sjonathan {
29258c56020Sknakahara struct m_tag *mtag;
29358c56020Sknakahara u_int16_t natt_src = IPSEC_PORT_ANY;
29458c56020Sknakahara u_int16_t natt_dst = IPSEC_PORT_ANY;
29558c56020Sknakahara
29658c56020Sknakahara /*
29758c56020Sknakahara * For NAT-T enabled ipsecif(4), set NAT-T port numbers
29858c56020Sknakahara * even if the saidx uses transport mode.
29958c56020Sknakahara *
30058c56020Sknakahara * See also ipsecif[46]_output().
30158c56020Sknakahara */
30258c56020Sknakahara mtag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS);
30358c56020Sknakahara if (mtag) {
30458c56020Sknakahara u_int16_t *natt_ports;
30558c56020Sknakahara
30658c56020Sknakahara natt_ports = (u_int16_t *)(mtag + 1);
30758c56020Sknakahara natt_src = natt_ports[1];
30858c56020Sknakahara natt_dst = natt_ports[0];
30958c56020Sknakahara }
310caf49ea5Sthorpej
31174029031Sjonathan if (af == AF_INET) {
31274029031Sjonathan struct sockaddr_in *sin;
31374029031Sjonathan struct ip *ip = mtod(m, struct ip *);
31474029031Sjonathan
31574029031Sjonathan if (saidx->src.sa.sa_len == 0) {
31674029031Sjonathan sin = &saidx->src.sin;
31774029031Sjonathan sin->sin_len = sizeof(*sin);
31874029031Sjonathan sin->sin_family = AF_INET;
31958c56020Sknakahara sin->sin_port = natt_src;
32074029031Sjonathan sin->sin_addr = ip->ip_src;
32174029031Sjonathan }
32274029031Sjonathan if (saidx->dst.sa.sa_len == 0) {
32374029031Sjonathan sin = &saidx->dst.sin;
32474029031Sjonathan sin->sin_len = sizeof(*sin);
32574029031Sjonathan sin->sin_family = AF_INET;
32658c56020Sknakahara sin->sin_port = natt_dst;
32774029031Sjonathan sin->sin_addr = ip->ip_dst;
32874029031Sjonathan }
32974029031Sjonathan } else {
33074029031Sjonathan struct sockaddr_in6 *sin6;
33174029031Sjonathan struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
33274029031Sjonathan
33374029031Sjonathan if (saidx->src.sin6.sin6_len == 0) {
33474029031Sjonathan sin6 = (struct sockaddr_in6 *)&saidx->src;
33574029031Sjonathan sin6->sin6_len = sizeof(*sin6);
33674029031Sjonathan sin6->sin6_family = AF_INET6;
33758c56020Sknakahara sin6->sin6_port = natt_src;
33874029031Sjonathan sin6->sin6_addr = ip6->ip6_src;
33974029031Sjonathan if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
34074029031Sjonathan /* fix scope id for comparing SPD */
34174029031Sjonathan sin6->sin6_addr.s6_addr16[1] = 0;
34274029031Sjonathan sin6->sin6_scope_id =
34374029031Sjonathan ntohs(ip6->ip6_src.s6_addr16[1]);
34474029031Sjonathan }
34574029031Sjonathan }
34674029031Sjonathan if (saidx->dst.sin6.sin6_len == 0) {
34774029031Sjonathan sin6 = (struct sockaddr_in6 *)&saidx->dst;
34874029031Sjonathan sin6->sin6_len = sizeof(*sin6);
34974029031Sjonathan sin6->sin6_family = AF_INET6;
35058c56020Sknakahara sin6->sin6_port = natt_dst;
35174029031Sjonathan sin6->sin6_addr = ip6->ip6_dst;
35274029031Sjonathan if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
35374029031Sjonathan /* fix scope id for comparing SPD */
35474029031Sjonathan sin6->sin6_addr.s6_addr16[1] = 0;
35574029031Sjonathan sin6->sin6_scope_id =
35674029031Sjonathan ntohs(ip6->ip6_dst.s6_addr16[1]);
35774029031Sjonathan }
35874029031Sjonathan }
35974029031Sjonathan }
36074029031Sjonathan }
36174029031Sjonathan
36213270c39Sozaki-r struct secasvar *
ipsec_lookup_sa(const struct ipsecrequest * isr,const struct mbuf * m)36313270c39Sozaki-r ipsec_lookup_sa(const struct ipsecrequest *isr, const struct mbuf *m)
36413270c39Sozaki-r {
36513270c39Sozaki-r struct secasindex saidx;
36613270c39Sozaki-r
36713270c39Sozaki-r saidx = isr->saidx;
36813270c39Sozaki-r if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
36913270c39Sozaki-r /* Fillin unspecified SA peers only for transport mode */
37013270c39Sozaki-r ipsec_fill_saidx_bymbuf(&saidx, m, isr->saidx.dst.sa.sa_family);
37113270c39Sozaki-r }
37213270c39Sozaki-r
37313270c39Sozaki-r return key_lookup_sa_bysaidx(&saidx);
37413270c39Sozaki-r }
37513270c39Sozaki-r
37674029031Sjonathan /*
377289fe532Sozaki-r * ipsec_nextisr can return :
378289fe532Sozaki-r * - isr == NULL and error != 0 => something is bad : the packet must be
379289fe532Sozaki-r * discarded
380289fe532Sozaki-r * - isr == NULL and error == 0 => no more rules to apply, ipsec processing
381289fe532Sozaki-r * is done, reinject it in ip stack
382289fe532Sozaki-r * - isr != NULL (error == 0) => we need to apply one rule to the packet
383289fe532Sozaki-r */
38483c2b87aSozaki-r static const struct ipsecrequest *
ipsec_nextisr(struct mbuf * m,const struct ipsecrequest * isr,int af,int * error,struct secasvar ** ret)38579002044Smaxv ipsec_nextisr(struct mbuf *m, const struct ipsecrequest *isr, int af,
38679002044Smaxv int *error, struct secasvar **ret)
387289fe532Sozaki-r {
388289fe532Sozaki-r #define IPSEC_OSTAT(type) \
389289fe532Sozaki-r do { \
390289fe532Sozaki-r switch (isr->saidx.proto) { \
391289fe532Sozaki-r case IPPROTO_ESP: \
392289fe532Sozaki-r ESP_STATINC(ESP_STAT_ ## type); \
393289fe532Sozaki-r break; \
394289fe532Sozaki-r case IPPROTO_AH: \
395289fe532Sozaki-r AH_STATINC(AH_STAT_ ## type); \
396289fe532Sozaki-r break; \
397289fe532Sozaki-r default: \
398289fe532Sozaki-r IPCOMP_STATINC(IPCOMP_STAT_ ## type); \
399289fe532Sozaki-r break; \
400289fe532Sozaki-r } \
401289fe532Sozaki-r } while (/*CONSTCOND*/0)
402289fe532Sozaki-r
403289fe532Sozaki-r struct secasvar *sav = NULL;
4047f4c0fa0Sozaki-r struct secasindex saidx;
405289fe532Sozaki-r
406289fe532Sozaki-r KASSERTMSG(af == AF_INET || af == AF_INET6,
407289fe532Sozaki-r "invalid address family %u", af);
408289fe532Sozaki-r again:
409289fe532Sozaki-r /*
410289fe532Sozaki-r * Craft SA index to search for proper SA. Note that
411289fe532Sozaki-r * we only fillin unspecified SA peers for transport
412289fe532Sozaki-r * mode; for tunnel mode they must already be filled in.
413289fe532Sozaki-r */
4147f4c0fa0Sozaki-r saidx = isr->saidx;
415289fe532Sozaki-r if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
416289fe532Sozaki-r /* Fillin unspecified SA peers only for transport mode */
4177f4c0fa0Sozaki-r ipsec_fill_saidx_bymbuf(&saidx, m, af);
418289fe532Sozaki-r }
419289fe532Sozaki-r
420289fe532Sozaki-r /*
42174029031Sjonathan * Lookup SA and validate it.
42274029031Sjonathan */
4237f4c0fa0Sozaki-r *error = key_checkrequest(isr, &saidx, &sav);
42474029031Sjonathan if (*error != 0) {
42574029031Sjonathan /*
42674029031Sjonathan * IPsec processing is required, but no SA found.
42774029031Sjonathan * I assume that key_acquire() had been called
42874029031Sjonathan * to get/establish the SA. Here I discard
42974029031Sjonathan * this packet because it is responsibility for
43074029031Sjonathan * upper layer to retransmit the packet.
43174029031Sjonathan */
432caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_OUT_NOSA);
43374029031Sjonathan goto bad;
43474029031Sjonathan }
43555718e80Sdegroote /* sav may be NULL here if we have an USE rule */
43655718e80Sdegroote if (sav == NULL) {
4372620e166Sozaki-r KASSERTMSG(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE,
4382620e166Sozaki-r "no SA found, but required; level %u",
4392620e166Sozaki-r ipsec_get_reqlevel(isr));
44074029031Sjonathan isr = isr->next;
44155718e80Sdegroote /*
4421e45b2f1Smaxv * No more rules to apply, return NULL isr and no error.
4431e45b2f1Smaxv * It can happen when the last rules are USE rules.
44479002044Smaxv */
44574029031Sjonathan if (isr == NULL) {
44638b8f795Sozaki-r *ret = NULL;
44755718e80Sdegroote *error = 0;
44874029031Sjonathan return isr;
44974029031Sjonathan }
45074029031Sjonathan goto again;
45174029031Sjonathan }
45274029031Sjonathan
45374029031Sjonathan /*
45474029031Sjonathan * Check system global policy controls.
45574029031Sjonathan */
45674029031Sjonathan if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) ||
45774029031Sjonathan (isr->saidx.proto == IPPROTO_AH && !ah_enable) ||
45874029031Sjonathan (isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) {
459290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "IPsec outbound packet dropped due"
460290dc492Sozaki-r " to policy (check your sysctls)\n");
461fc624c4aSozaki-r IPSEC_OSTAT(PDROPS);
46274029031Sjonathan *error = EHOSTUNREACH;
4638be5cabcSozaki-r KEY_SA_UNREF(&sav);
46474029031Sjonathan goto bad;
46574029031Sjonathan }
46674029031Sjonathan
46774029031Sjonathan /*
46874029031Sjonathan * Sanity check the SA contents for the caller
46974029031Sjonathan * before they invoke the xform output method.
47074029031Sjonathan */
471765c1e87Sozaki-r KASSERT(sav->tdb_xform != NULL);
47238b8f795Sozaki-r *ret = sav;
47374029031Sjonathan return isr;
47479002044Smaxv
47574029031Sjonathan bad:
4762620e166Sozaki-r KASSERTMSG(*error != 0, "error return w/ no error code");
47774029031Sjonathan return NULL;
47874029031Sjonathan #undef IPSEC_OSTAT
47974029031Sjonathan }
48074029031Sjonathan
48174029031Sjonathan #ifdef INET
48274029031Sjonathan /*
48374029031Sjonathan * IPsec output logic for IPv4.
48474029031Sjonathan */
48574029031Sjonathan int
ipsec4_process_packet(struct mbuf * m,const struct ipsecrequest * isr,u_long * mtu)48683c2b87aSozaki-r ipsec4_process_packet(struct mbuf *m, const struct ipsecrequest *isr,
487ea86b0a9Sozaki-r u_long *mtu)
48874029031Sjonathan {
48938b8f795Sozaki-r struct secasvar *sav = NULL;
49074029031Sjonathan struct ip *ip;
491986909fbSozaki-r int error, i, off;
4929ceb805eSozaki-r union sockaddr_union *dst;
4939ceb805eSozaki-r int setdf;
49474029031Sjonathan
4952620e166Sozaki-r KASSERT(m != NULL);
4966c5d24edSmaxv KASSERT(m->m_nextpkt == NULL);
4972620e166Sozaki-r KASSERT(isr != NULL);
49874029031Sjonathan
49938b8f795Sozaki-r isr = ipsec_nextisr(m, isr, AF_INET, &error, &sav);
50055718e80Sdegroote if (isr == NULL) {
50155718e80Sdegroote if (error != 0) {
50274029031Sjonathan goto bad;
50355718e80Sdegroote } else {
50455718e80Sdegroote if (ipsec_register_done(m, &error) < 0)
50555718e80Sdegroote goto bad;
50655718e80Sdegroote
507c535599fSknakahara return ipsec_reinject_ipstack(m, AF_INET, 0);
50855718e80Sdegroote }
50955718e80Sdegroote }
51038b8f795Sozaki-r KASSERT(sav != NULL);
51179002044Smaxv
512052a8431Smaxv if (m->m_len < sizeof(struct ip) &&
513052a8431Smaxv (m = m_pullup(m, sizeof(struct ip))) == NULL) {
514052a8431Smaxv error = ENOBUFS;
515052a8431Smaxv goto unrefsav;
516052a8431Smaxv }
517052a8431Smaxv
518ea86b0a9Sozaki-r /*
519ea86b0a9Sozaki-r * Check if we need to handle NAT-T fragmentation.
520ea86b0a9Sozaki-r */
521ea86b0a9Sozaki-r if (isr == isr->sp->req) { /* Check only if called from ipsec4_output */
522ea86b0a9Sozaki-r KASSERT(mtu != NULL);
523ea86b0a9Sozaki-r ip = mtod(m, struct ip *);
524f645db7aSmaxv if (!(sav->natt_type & UDP_ENCAP_ESPINUDP)) {
525ea86b0a9Sozaki-r goto noneed;
526ea86b0a9Sozaki-r }
527ea86b0a9Sozaki-r if (ntohs(ip->ip_len) <= sav->esp_frag)
528ea86b0a9Sozaki-r goto noneed;
529ea86b0a9Sozaki-r *mtu = sav->esp_frag;
5308be5cabcSozaki-r KEY_SA_UNREF(&sav);
531ea86b0a9Sozaki-r return 0;
532ea86b0a9Sozaki-r }
533ea86b0a9Sozaki-r noneed:
5349ceb805eSozaki-r dst = &sav->sah->saidx.dst;
53574029031Sjonathan
53674029031Sjonathan /*
53774029031Sjonathan * Collect IP_DF state from the outer header.
53874029031Sjonathan */
53974029031Sjonathan if (dst->sa.sa_family == AF_INET) {
54074029031Sjonathan ip = mtod(m, struct ip *);
54174029031Sjonathan /* Honor system-wide control of how to handle IP_DF */
54274029031Sjonathan switch (ip4_ipsec_dfbit) {
54374029031Sjonathan case 0: /* clear in outer header */
54474029031Sjonathan case 1: /* set in outer header */
54574029031Sjonathan setdf = ip4_ipsec_dfbit;
54674029031Sjonathan break;
54774029031Sjonathan default: /* propagate to outer header */
5482ea4c766Sjonathan setdf = ip->ip_off;
5492ea4c766Sjonathan setdf = ntohs(setdf);
5502ea4c766Sjonathan setdf = htons(setdf & IP_DF);
55174029031Sjonathan break;
55274029031Sjonathan }
55374029031Sjonathan } else {
55474029031Sjonathan ip = NULL; /* keep compiler happy */
55574029031Sjonathan setdf = 0;
55674029031Sjonathan }
55779002044Smaxv
55874029031Sjonathan /* Do the appropriate encapsulation, if necessary */
55974029031Sjonathan if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
56074029031Sjonathan dst->sa.sa_family != AF_INET || /* PF mismatch */
56174029031Sjonathan (dst->sa.sa_family == AF_INET && /* Proxy */
56274029031Sjonathan dst->sin.sin_addr.s_addr != INADDR_ANY &&
56374029031Sjonathan dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
56474029031Sjonathan struct mbuf *mp;
56574029031Sjonathan
56674029031Sjonathan /* Fix IPv4 header checksum and length */
56774029031Sjonathan ip = mtod(m, struct ip *);
56874029031Sjonathan ip->ip_len = htons(m->m_pkthdr.len);
56974029031Sjonathan ip->ip_sum = 0;
57074029031Sjonathan ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
57174029031Sjonathan
57274029031Sjonathan /* Encapsulate the packet */
57320668b06Smaxv error = ipip_output(m, sav, &mp);
57474029031Sjonathan if (mp == NULL && !error) {
57574029031Sjonathan /* Should never happen. */
576290dc492Sozaki-r IPSECLOG(LOG_DEBUG,
577290dc492Sozaki-r "ipip_output returns no mbuf and no error!");
57874029031Sjonathan error = EFAULT;
57974029031Sjonathan }
58074029031Sjonathan if (error) {
581f8d2d740Sscw if (mp) {
582f8d2d740Sscw /* XXX: Should never happen! */
58374029031Sjonathan m_freem(mp);
584f8d2d740Sscw }
585f8d2d740Sscw m = NULL; /* ipip_output() already freed it */
58638b8f795Sozaki-r goto unrefsav;
58774029031Sjonathan }
58874029031Sjonathan m = mp, mp = NULL;
58979002044Smaxv
59074029031Sjonathan /*
59174029031Sjonathan * ipip_output clears IP_DF in the new header. If
59274029031Sjonathan * we need to propagate IP_DF from the outer header,
59374029031Sjonathan * then we have to do it here.
59474029031Sjonathan *
59574029031Sjonathan * XXX shouldn't assume what ipip_output does.
59674029031Sjonathan */
59774029031Sjonathan if (dst->sa.sa_family == AF_INET && setdf) {
59874029031Sjonathan if (m->m_len < sizeof(struct ip) &&
59974029031Sjonathan (m = m_pullup(m, sizeof(struct ip))) == NULL) {
60074029031Sjonathan error = ENOBUFS;
60138b8f795Sozaki-r goto unrefsav;
60274029031Sjonathan }
60374029031Sjonathan ip = mtod(m, struct ip *);
604ef67739aSozaki-r ip->ip_off |= htons(IP_DF);
60574029031Sjonathan }
60674029031Sjonathan }
60774029031Sjonathan
60874029031Sjonathan /*
60974029031Sjonathan * Dispatch to the appropriate IPsec transform logic. The
61074029031Sjonathan * packet will be returned for transmission after crypto
61174029031Sjonathan * processing, etc. are completed. For encapsulation we
61274029031Sjonathan * bypass this call because of the explicit call done above
61374029031Sjonathan * (necessary to deal with IP_DF handling for IPv4).
61474029031Sjonathan *
61575d2abaeSandvar * NB: m & sav are ``passed to caller'' who's responsible for
61674029031Sjonathan * for reclaiming their resources.
61774029031Sjonathan */
61874029031Sjonathan if (sav->tdb_xform->xf_type != XF_IP4) {
619a46f4db6Sdrochner if (dst->sa.sa_family == AF_INET) {
62074029031Sjonathan ip = mtod(m, struct ip *);
62174029031Sjonathan i = ip->ip_hl << 2;
62274029031Sjonathan off = offsetof(struct ip, ip_p);
623a46f4db6Sdrochner } else {
624a46f4db6Sdrochner i = sizeof(struct ip6_hdr);
625a46f4db6Sdrochner off = offsetof(struct ip6_hdr, ip6_nxt);
626a46f4db6Sdrochner }
627c535599fSknakahara error = (*sav->tdb_xform->xf_output)(m, isr, sav, i, off, 0);
62874029031Sjonathan } else {
629c535599fSknakahara error = ipsec_process_done(m, isr, sav, 0);
63074029031Sjonathan }
6318be5cabcSozaki-r KEY_SA_UNREF(&sav);
63274029031Sjonathan return error;
63379002044Smaxv
63438b8f795Sozaki-r unrefsav:
6358be5cabcSozaki-r KEY_SA_UNREF(&sav);
63674029031Sjonathan bad:
63774029031Sjonathan m_freem(m);
63874029031Sjonathan return error;
63974029031Sjonathan }
64074029031Sjonathan #endif
64174029031Sjonathan
64274029031Sjonathan #ifdef INET6
643a7d0cc88Smaxv static int
compute_ipsec_pos(struct mbuf * m,int * i,int * off)644cf21c579Sdrochner compute_ipsec_pos(struct mbuf *m, int *i, int *off)
645cf21c579Sdrochner {
646cf21c579Sdrochner struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
647cf21c579Sdrochner struct ip6_ext ip6e;
648cf21c579Sdrochner int dstopt = 0;
64979002044Smaxv int nxt;
650cf21c579Sdrochner
651cf21c579Sdrochner *i = sizeof(struct ip6_hdr);
652cf21c579Sdrochner *off = offsetof(struct ip6_hdr, ip6_nxt);
653cf21c579Sdrochner nxt = ip6->ip6_nxt;
654cf21c579Sdrochner
655cf21c579Sdrochner /*
656cf21c579Sdrochner * chase mbuf chain to find the appropriate place to
657cf21c579Sdrochner * put AH/ESP/IPcomp header.
658cf21c579Sdrochner * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
659cf21c579Sdrochner */
660a7d0cc88Smaxv while (1) {
661cf21c579Sdrochner switch (nxt) {
662cf21c579Sdrochner case IPPROTO_AH:
663cf21c579Sdrochner case IPPROTO_ESP:
664cf21c579Sdrochner case IPPROTO_IPCOMP:
665cf21c579Sdrochner /*
66656fc5b5dSmaxv * We should not skip security header added
667cf21c579Sdrochner * beforehand.
668cf21c579Sdrochner */
669a7d0cc88Smaxv return 0;
670cf21c579Sdrochner
671cf21c579Sdrochner case IPPROTO_HOPOPTS:
672cf21c579Sdrochner case IPPROTO_DSTOPTS:
673cf21c579Sdrochner case IPPROTO_ROUTING:
67456fc5b5dSmaxv if (*i + sizeof(ip6e) > m->m_pkthdr.len) {
67556fc5b5dSmaxv return EINVAL;
67656fc5b5dSmaxv }
67756fc5b5dSmaxv
678cf21c579Sdrochner /*
67956fc5b5dSmaxv * If we see 2nd destination option header,
680cf21c579Sdrochner * we should stop there.
681cf21c579Sdrochner */
682cf21c579Sdrochner if (nxt == IPPROTO_DSTOPTS && dstopt)
683a7d0cc88Smaxv return 0;
684cf21c579Sdrochner
685cf21c579Sdrochner if (nxt == IPPROTO_DSTOPTS) {
686cf21c579Sdrochner /*
68756fc5b5dSmaxv * Seen 1st or 2nd destination option.
688cf21c579Sdrochner * next time we see one, it must be 2nd.
689cf21c579Sdrochner */
690cf21c579Sdrochner dstopt = 1;
691cf21c579Sdrochner } else if (nxt == IPPROTO_ROUTING) {
692cf21c579Sdrochner /*
69356fc5b5dSmaxv * If we see destination option next
694cf21c579Sdrochner * time, it must be dest2.
695cf21c579Sdrochner */
696cf21c579Sdrochner dstopt = 2;
697cf21c579Sdrochner }
698cf21c579Sdrochner
699cf21c579Sdrochner /* skip this header */
700cf21c579Sdrochner m_copydata(m, *i, sizeof(ip6e), &ip6e);
701cf21c579Sdrochner nxt = ip6e.ip6e_nxt;
702cf21c579Sdrochner *off = *i + offsetof(struct ip6_ext, ip6e_nxt);
703cf21c579Sdrochner *i += (ip6e.ip6e_len + 1) << 3;
70456fc5b5dSmaxv if (*i > m->m_pkthdr.len) {
70556fc5b5dSmaxv return EINVAL;
70656fc5b5dSmaxv }
707cf21c579Sdrochner break;
708cf21c579Sdrochner default:
709a7d0cc88Smaxv return 0;
710cf21c579Sdrochner }
711a7d0cc88Smaxv }
712a7d0cc88Smaxv
713a7d0cc88Smaxv return 0;
714cf21c579Sdrochner }
715cf21c579Sdrochner
716813bea3eSdrochner static int
in6_sa_equal_addrwithscope(const struct sockaddr_in6 * sa,const struct in6_addr * ia)71779002044Smaxv in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa,
71879002044Smaxv const struct in6_addr *ia)
719813bea3eSdrochner {
720813bea3eSdrochner struct in6_addr ia2;
721813bea3eSdrochner
722813bea3eSdrochner memcpy(&ia2, &sa->sin6_addr, sizeof(ia2));
723813bea3eSdrochner if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr))
724813bea3eSdrochner ia2.s6_addr16[1] = htons(sa->sin6_scope_id);
725813bea3eSdrochner
726813bea3eSdrochner return IN6_ARE_ADDR_EQUAL(ia, &ia2);
727813bea3eSdrochner }
728813bea3eSdrochner
72974029031Sjonathan int
ipsec6_process_packet(struct mbuf * m,const struct ipsecrequest * isr,int flags)730c535599fSknakahara ipsec6_process_packet(struct mbuf *m, const struct ipsecrequest *isr, int flags)
73174029031Sjonathan {
73238b8f795Sozaki-r struct secasvar *sav = NULL;
733e2211411Sdegroote struct ip6_hdr *ip6;
734986909fbSozaki-r int error, i, off;
735e4ef78d8Sdrochner union sockaddr_union *dst;
73674029031Sjonathan
7372620e166Sozaki-r KASSERT(m != NULL);
7386c5d24edSmaxv KASSERT(m->m_nextpkt == NULL);
7392620e166Sozaki-r KASSERT(isr != NULL);
74074029031Sjonathan
74138b8f795Sozaki-r isr = ipsec_nextisr(m, isr, AF_INET6, &error, &sav);
74274029031Sjonathan if (isr == NULL) {
74355718e80Sdegroote if (error != 0) {
7447038ebdfSdrochner /* XXX Should we send a notification ? */
74574029031Sjonathan goto bad;
74655718e80Sdegroote } else {
74755718e80Sdegroote if (ipsec_register_done(m, &error) < 0)
74855718e80Sdegroote goto bad;
74955718e80Sdegroote
750c535599fSknakahara return ipsec_reinject_ipstack(m, AF_INET6, flags);
75155718e80Sdegroote }
75274029031Sjonathan }
75374029031Sjonathan
75438b8f795Sozaki-r KASSERT(sav != NULL);
755e4ef78d8Sdrochner dst = &sav->sah->saidx.dst;
756e2211411Sdegroote
757589f503dSmaxv if (m->m_len < sizeof(struct ip6_hdr)) {
758589f503dSmaxv if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL) {
759589f503dSmaxv error = ENOBUFS;
760589f503dSmaxv goto unrefsav;
761589f503dSmaxv }
762589f503dSmaxv }
763589f503dSmaxv ip6 = mtod(m, struct ip6_hdr *);
76474029031Sjonathan
765e2211411Sdegroote /* Do the appropriate encapsulation, if necessary */
766e2211411Sdegroote if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
76779002044Smaxv dst->sa.sa_family != AF_INET6 || /* AF mismatch */
768e2211411Sdegroote ((dst->sa.sa_family == AF_INET6) &&
769e2211411Sdegroote (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
77079002044Smaxv (!in6_sa_equal_addrwithscope(&dst->sin6, &ip6->ip6_dst)))) {
771e2211411Sdegroote struct mbuf *mp;
7727038ebdfSdrochner
773e2211411Sdegroote if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
774e2211411Sdegroote /* No jumbogram support. */
77574f0dfc5Sozaki-r error = ENXIO; /*XXX*/
77638b8f795Sozaki-r goto unrefsav;
77774029031Sjonathan }
7787038ebdfSdrochner
779589f503dSmaxv /* Fix IPv6 header payload length. */
780e2211411Sdegroote ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
78174029031Sjonathan
782e2211411Sdegroote /* Encapsulate the packet */
78320668b06Smaxv error = ipip_output(m, sav, &mp);
784e2211411Sdegroote if (mp == NULL && !error) {
785e2211411Sdegroote /* Should never happen. */
786290dc492Sozaki-r IPSECLOG(LOG_DEBUG,
787290dc492Sozaki-r "ipip_output returns no mbuf and no error!");
788e2211411Sdegroote error = EFAULT;
78974029031Sjonathan }
79074029031Sjonathan
79174029031Sjonathan if (error) {
792e2211411Sdegroote if (mp) {
793e2211411Sdegroote /* XXX: Should never happen! */
794e2211411Sdegroote m_freem(mp);
795e2211411Sdegroote }
796e2211411Sdegroote m = NULL; /* ipip_output() already freed it */
79738b8f795Sozaki-r goto unrefsav;
79874029031Sjonathan }
79974029031Sjonathan
800e2211411Sdegroote m = mp;
801e2211411Sdegroote mp = NULL;
802eb04733cSjoerg }
80374029031Sjonathan
804e4ef78d8Sdrochner if (dst->sa.sa_family == AF_INET) {
805e4ef78d8Sdrochner struct ip *ip;
806e4ef78d8Sdrochner ip = mtod(m, struct ip *);
807e4ef78d8Sdrochner i = ip->ip_hl << 2;
808e4ef78d8Sdrochner off = offsetof(struct ip, ip_p);
809e4ef78d8Sdrochner } else {
810a7d0cc88Smaxv error = compute_ipsec_pos(m, &i, &off);
811a7d0cc88Smaxv if (error)
812a7d0cc88Smaxv goto unrefsav;
81374029031Sjonathan }
814c535599fSknakahara error = (*sav->tdb_xform->xf_output)(m, isr, sav, i, off, flags);
8158be5cabcSozaki-r KEY_SA_UNREF(&sav);
816e2211411Sdegroote return error;
81779002044Smaxv
81838b8f795Sozaki-r unrefsav:
8198be5cabcSozaki-r KEY_SA_UNREF(&sav);
82074029031Sjonathan bad:
82174029031Sjonathan m_freem(m);
82274029031Sjonathan return error;
82374029031Sjonathan }
82474029031Sjonathan #endif /* INET6 */
82564e64168Sozaki-r
82664e64168Sozaki-r void
ipsec_output_init(void)82764e64168Sozaki-r ipsec_output_init(void)
82864e64168Sozaki-r {
82964e64168Sozaki-r
830e524fb36Sozaki-r ipsec_rtcache_percpu = rtcache_percpu_alloc();
83164e64168Sozaki-r }
832