646 lines
15 KiB
C
646 lines
15 KiB
C
/* $OpenBSD: ipsec_output.c,v 1.98 2024/02/11 01:27:45 bluhm Exp $ */
|
|
/*
|
|
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
|
|
*
|
|
* Copyright (c) 2000-2001 Angelos D. Keromytis.
|
|
*
|
|
* Permission to use, copy, and modify this software with or without fee
|
|
* is hereby granted, provided that this entire notice is included in
|
|
* all copies of any software which is or includes a copy or
|
|
* modification of this software.
|
|
* You may use this code under the GNU public license if you so wish. Please
|
|
* contribute changes back to the authors under this freer than GPL license
|
|
* so that we may further the use of strong encryption without limitations to
|
|
* all.
|
|
*
|
|
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
|
|
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
|
|
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
|
|
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
|
|
* PURPOSE.
|
|
*/
|
|
|
|
#include "pf.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/timeout.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet6/ip6_var.h>
|
|
|
|
#if NPF > 0
|
|
#include <net/pfvar.h>
|
|
#endif
|
|
|
|
#include <netinet/udp.h>
|
|
#include <netinet/ip_ipip.h>
|
|
#include <netinet/ip_ah.h>
|
|
#include <netinet/ip_esp.h>
|
|
#include <netinet/ip_ipcomp.h>
|
|
|
|
#include <crypto/cryptodev.h>
|
|
#include <crypto/xform.h>
|
|
|
|
#ifdef ENCDEBUG
|
|
#define DPRINTF(fmt, args...) \
|
|
do { \
|
|
if (encdebug) \
|
|
printf("%s: " fmt "\n", __func__, ## args); \
|
|
} while (0)
|
|
#else
|
|
#define DPRINTF(fmt, args...) \
|
|
do { } while (0)
|
|
#endif
|
|
|
|
int udpencap_enable = 1; /* enabled by default */
|
|
int udpencap_port = 4500; /* triggers decapsulation */
|
|
|
|
/*
|
|
* Loop over a tdb chain, taking into consideration protocol tunneling. The
|
|
* fourth argument is set if the first encapsulation header is already in
|
|
* place.
|
|
*/
|
|
int
|
|
ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
|
|
{
|
|
int hlen, off, error;
|
|
#ifdef INET6
|
|
struct ip6_ext ip6e;
|
|
int nxt;
|
|
int dstopt = 0;
|
|
#endif
|
|
|
|
int setdf = 0;
|
|
struct ip *ip;
|
|
#ifdef INET6
|
|
struct ip6_hdr *ip6;
|
|
#endif /* INET6 */
|
|
|
|
#ifdef ENCDEBUG
|
|
char buf[INET6_ADDRSTRLEN];
|
|
#endif
|
|
|
|
/* Check that the transform is allowed by the administrator. */
|
|
if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
|
|
(tdb->tdb_sproto == IPPROTO_AH && !ah_enable) ||
|
|
(tdb->tdb_sproto == IPPROTO_IPCOMP && !ipcomp_enable)) {
|
|
DPRINTF("IPsec outbound packet dropped due to policy "
|
|
"(check your sysctls)");
|
|
error = EHOSTUNREACH;
|
|
goto drop;
|
|
}
|
|
|
|
/* Sanity check. */
|
|
if (!tdb->tdb_xform) {
|
|
DPRINTF("uninitialized TDB");
|
|
error = EHOSTUNREACH;
|
|
goto drop;
|
|
}
|
|
|
|
/* Check if the SPI is invalid. */
|
|
if (tdb->tdb_flags & TDBF_INVALID) {
|
|
DPRINTF("attempt to use invalid SA %s/%08x/%u",
|
|
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
|
|
ntohl(tdb->tdb_spi), tdb->tdb_sproto);
|
|
error = ENXIO;
|
|
goto drop;
|
|
}
|
|
|
|
/* Check that the network protocol is supported */
|
|
switch (tdb->tdb_dst.sa.sa_family) {
|
|
case AF_INET:
|
|
break;
|
|
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
break;
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d",
|
|
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
|
|
ntohl(tdb->tdb_spi), tdb->tdb_sproto,
|
|
tdb->tdb_dst.sa.sa_family);
|
|
error = EPFNOSUPPORT;
|
|
goto drop;
|
|
}
|
|
|
|
/*
|
|
* Register first use if applicable, setup relevant expiration timer.
|
|
*/
|
|
if (tdb->tdb_first_use == 0) {
|
|
tdb->tdb_first_use = gettime();
|
|
if (tdb->tdb_flags & TDBF_FIRSTUSE) {
|
|
if (timeout_add_sec(&tdb->tdb_first_tmo,
|
|
tdb->tdb_exp_first_use))
|
|
tdb_ref(tdb);
|
|
}
|
|
if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
|
|
if (timeout_add_sec(&tdb->tdb_sfirst_tmo,
|
|
tdb->tdb_soft_first_use))
|
|
tdb_ref(tdb);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for tunneling if we don't have the first header in place.
|
|
* When doing Ethernet-over-IP, we are handed an already-encapsulated
|
|
* frame, so we don't need to re-encapsulate.
|
|
*/
|
|
if (tunalready == 0) {
|
|
/*
|
|
* If the target protocol family is different, we know we'll be
|
|
* doing tunneling.
|
|
*/
|
|
if (af == tdb->tdb_dst.sa.sa_family) {
|
|
switch (af) {
|
|
case AF_INET:
|
|
hlen = sizeof(struct ip);
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
hlen = sizeof(struct ip6_hdr);
|
|
break;
|
|
#endif /* INET6 */
|
|
}
|
|
|
|
/* Bring the network header in the first mbuf. */
|
|
if (m->m_len < hlen) {
|
|
if ((m = m_pullup(m, hlen)) == NULL) {
|
|
error = ENOBUFS;
|
|
goto drop;
|
|
}
|
|
}
|
|
|
|
if (af == AF_INET) {
|
|
ip = mtod(m, struct ip *);
|
|
|
|
/*
|
|
* This is not a bridge packet, remember if we
|
|
* had IP_DF.
|
|
*/
|
|
setdf = ip->ip_off & htons(IP_DF);
|
|
}
|
|
|
|
#ifdef INET6
|
|
if (af == AF_INET6)
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
#endif /* INET6 */
|
|
}
|
|
|
|
/* Do the appropriate encapsulation, if necessary. */
|
|
if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */
|
|
(tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */
|
|
(tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */
|
|
((tdb->tdb_dst.sa.sa_family == AF_INET) &&
|
|
(tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
|
|
(tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
|
|
#ifdef INET6
|
|
((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
|
|
(!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) &&
|
|
(!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
|
|
&ip6->ip6_dst))) ||
|
|
#endif /* INET6 */
|
|
0) {
|
|
/* Fix IPv4 header checksum and length. */
|
|
if (af == AF_INET) {
|
|
if (m->m_len < sizeof(struct ip))
|
|
if ((m = m_pullup(m,
|
|
sizeof(struct ip))) == NULL) {
|
|
error = ENOBUFS;
|
|
goto drop;
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
|
ip->ip_len = htons(m->m_pkthdr.len);
|
|
ip->ip_sum = 0;
|
|
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
|
|
}
|
|
|
|
#ifdef INET6
|
|
/* Fix IPv6 header payload length. */
|
|
if (af == AF_INET6) {
|
|
if (m->m_len < sizeof(struct ip6_hdr))
|
|
if ((m = m_pullup(m,
|
|
sizeof(struct ip6_hdr))) == NULL) {
|
|
error = ENOBUFS;
|
|
goto drop;
|
|
}
|
|
|
|
if (m->m_pkthdr.len - sizeof(*ip6) >
|
|
IPV6_MAXPACKET) {
|
|
/* No jumbogram support. */
|
|
error = ENXIO; /*?*/
|
|
goto drop;
|
|
}
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
ip6->ip6_plen = htons(m->m_pkthdr.len
|
|
- sizeof(*ip6));
|
|
}
|
|
#endif /* INET6 */
|
|
|
|
/* Encapsulate -- m may be changed or set to NULL. */
|
|
error = ipip_output(&m, tdb);
|
|
if ((m == NULL) && (!error))
|
|
error = EFAULT;
|
|
if (error)
|
|
goto drop;
|
|
|
|
if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) {
|
|
if (m->m_len < sizeof(struct ip))
|
|
if ((m = m_pullup(m,
|
|
sizeof(struct ip))) == NULL) {
|
|
error = ENOBUFS;
|
|
goto drop;
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
|
ip->ip_off |= htons(IP_DF);
|
|
}
|
|
|
|
/* Remember that we appended a tunnel header. */
|
|
mtx_enter(&tdb->tdb_mtx);
|
|
tdb->tdb_flags |= TDBF_USEDTUNNEL;
|
|
mtx_leave(&tdb->tdb_mtx);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this is just an IP-IP TDB and we're told there's already an
|
|
* encapsulation header or ipip_output() has encapsulated it, move on.
|
|
*/
|
|
if (tdb->tdb_xform->xf_type == XF_IP4)
|
|
return ipsp_process_done(m, tdb);
|
|
|
|
/* Extract some information off the headers. */
|
|
switch (tdb->tdb_dst.sa.sa_family) {
|
|
case AF_INET:
|
|
ip = mtod(m, struct ip *);
|
|
hlen = ip->ip_hl << 2;
|
|
off = offsetof(struct ip, ip_p);
|
|
break;
|
|
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
hlen = sizeof(struct ip6_hdr);
|
|
off = offsetof(struct ip6_hdr, ip6_nxt);
|
|
nxt = ip6->ip6_nxt;
|
|
/*
|
|
* chase mbuf chain to find the appropriate place to
|
|
* put AH/ESP/IPcomp header.
|
|
* IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
|
|
*/
|
|
do {
|
|
switch (nxt) {
|
|
case IPPROTO_AH:
|
|
case IPPROTO_ESP:
|
|
case IPPROTO_IPCOMP:
|
|
/*
|
|
* we should not skip security header added
|
|
* beforehand.
|
|
*/
|
|
goto exitip6loop;
|
|
|
|
case IPPROTO_HOPOPTS:
|
|
case IPPROTO_DSTOPTS:
|
|
case IPPROTO_ROUTING:
|
|
/*
|
|
* if we see 2nd destination option header,
|
|
* we should stop there.
|
|
*/
|
|
if (nxt == IPPROTO_DSTOPTS && dstopt)
|
|
goto exitip6loop;
|
|
|
|
if (nxt == IPPROTO_DSTOPTS) {
|
|
/*
|
|
* seen 1st or 2nd destination option.
|
|
* next time we see one, it must be 2nd.
|
|
*/
|
|
dstopt = 1;
|
|
} else if (nxt == IPPROTO_ROUTING) {
|
|
/*
|
|
* if we see destination option next
|
|
* time, it must be dest2.
|
|
*/
|
|
dstopt = 2;
|
|
}
|
|
if (m->m_pkthdr.len < hlen + sizeof(ip6e)) {
|
|
error = EINVAL;
|
|
goto drop;
|
|
}
|
|
/* skip this header */
|
|
m_copydata(m, hlen, sizeof(ip6e),
|
|
(caddr_t)&ip6e);
|
|
nxt = ip6e.ip6e_nxt;
|
|
off = hlen + offsetof(struct ip6_ext, ip6e_nxt);
|
|
/*
|
|
* we will never see nxt == IPPROTO_AH
|
|
* so it is safe to omit AH case.
|
|
*/
|
|
hlen += (ip6e.ip6e_len + 1) << 3;
|
|
break;
|
|
default:
|
|
goto exitip6loop;
|
|
}
|
|
} while (hlen < m->m_pkthdr.len);
|
|
exitip6loop:
|
|
break;
|
|
#endif /* INET6 */
|
|
default:
|
|
error = EPFNOSUPPORT;
|
|
goto drop;
|
|
}
|
|
|
|
if (m->m_pkthdr.len < hlen) {
|
|
error = EINVAL;
|
|
goto drop;
|
|
}
|
|
|
|
ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len);
|
|
tdbstat_add(tdb, tdb_ouncompbytes, m->m_pkthdr.len);
|
|
|
|
/* Non expansion policy for IPCOMP */
|
|
if (tdb->tdb_sproto == IPPROTO_IPCOMP) {
|
|
if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) {
|
|
/* No need to compress, leave the packet untouched */
|
|
ipcompstat_inc(ipcomps_minlen);
|
|
return ipsp_process_done(m, tdb);
|
|
}
|
|
}
|
|
|
|
/* Invoke the IPsec transform. */
|
|
return (*(tdb->tdb_xform->xf_output))(m, tdb, hlen, off);
|
|
|
|
drop:
|
|
m_freem(m);
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Called by the IPsec output transform callbacks, to transmit the packet
|
|
* or do further processing, as necessary.
|
|
*/
|
|
int
|
|
ipsp_process_done(struct mbuf *m, struct tdb *tdb)
|
|
{
|
|
struct ip *ip;
|
|
#ifdef INET6
|
|
struct ip6_hdr *ip6;
|
|
#endif /* INET6 */
|
|
struct tdb *tdbo;
|
|
struct tdb_ident *tdbi;
|
|
struct m_tag *mtag;
|
|
int roff, error;
|
|
|
|
NET_ASSERT_LOCKED();
|
|
|
|
tdb->tdb_last_used = gettime();
|
|
|
|
if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) {
|
|
struct mbuf *mi;
|
|
struct udphdr *uh;
|
|
int iphlen;
|
|
|
|
if (!udpencap_enable || !udpencap_port) {
|
|
error = ENXIO;
|
|
goto drop;
|
|
}
|
|
|
|
switch (tdb->tdb_dst.sa.sa_family) {
|
|
case AF_INET:
|
|
iphlen = sizeof(struct ip);
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
iphlen = sizeof(struct ip6_hdr);
|
|
break;
|
|
#endif /* INET6 */
|
|
default:
|
|
DPRINTF("unknown protocol family (%d)",
|
|
tdb->tdb_dst.sa.sa_family);
|
|
error = EPFNOSUPPORT;
|
|
goto drop;
|
|
}
|
|
|
|
mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff);
|
|
if (mi == NULL) {
|
|
error = ENOMEM;
|
|
goto drop;
|
|
}
|
|
uh = (struct udphdr *)(mtod(mi, caddr_t) + roff);
|
|
uh->uh_sport = uh->uh_dport = htons(udpencap_port);
|
|
if (tdb->tdb_udpencap_port)
|
|
uh->uh_dport = tdb->tdb_udpencap_port;
|
|
|
|
uh->uh_ulen = htons(m->m_pkthdr.len - iphlen);
|
|
uh->uh_sum = 0;
|
|
#ifdef INET6
|
|
if (tdb->tdb_dst.sa.sa_family == AF_INET6)
|
|
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
|
|
#endif /* INET6 */
|
|
espstat_inc(esps_udpencout);
|
|
}
|
|
|
|
switch (tdb->tdb_dst.sa.sa_family) {
|
|
case AF_INET:
|
|
/* Fix the header length, for AH processing. */
|
|
ip = mtod(m, struct ip *);
|
|
ip->ip_len = htons(m->m_pkthdr.len);
|
|
if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
|
|
ip->ip_p = IPPROTO_UDP;
|
|
break;
|
|
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
/* Fix the header length, for AH processing. */
|
|
if (m->m_pkthdr.len < sizeof(*ip6)) {
|
|
error = ENXIO;
|
|
goto drop;
|
|
}
|
|
if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
|
|
/* No jumbogram support. */
|
|
error = ENXIO;
|
|
goto drop;
|
|
}
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
|
|
if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
|
|
ip6->ip6_nxt = IPPROTO_UDP;
|
|
break;
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
DPRINTF("unknown protocol family (%d)",
|
|
tdb->tdb_dst.sa.sa_family);
|
|
error = EPFNOSUPPORT;
|
|
goto drop;
|
|
}
|
|
|
|
/*
|
|
* Add a record of what we've done or what needs to be done to the
|
|
* packet.
|
|
*/
|
|
mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident),
|
|
M_NOWAIT);
|
|
if (mtag == NULL) {
|
|
DPRINTF("could not allocate packet tag");
|
|
error = ENOMEM;
|
|
goto drop;
|
|
}
|
|
|
|
tdbi = (struct tdb_ident *)(mtag + 1);
|
|
tdbi->dst = tdb->tdb_dst;
|
|
tdbi->proto = tdb->tdb_sproto;
|
|
tdbi->spi = tdb->tdb_spi;
|
|
tdbi->rdomain = tdb->tdb_rdomain;
|
|
|
|
m_tag_prepend(m, mtag);
|
|
|
|
ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdr.len);
|
|
tdbstat_pkt(tdb, tdb_opackets, tdb_obytes, m->m_pkthdr.len);
|
|
|
|
/* If there's another (bundled) TDB to apply, do so. */
|
|
tdbo = tdb_ref(tdb->tdb_onext);
|
|
if (tdbo != NULL) {
|
|
KERNEL_ASSERT_LOCKED();
|
|
error = ipsp_process_packet(m, tdbo,
|
|
tdb->tdb_dst.sa.sa_family, 0);
|
|
tdb_unref(tdbo);
|
|
return error;
|
|
}
|
|
|
|
#if NPF > 0
|
|
/* Add pf tag if requested. */
|
|
pf_tag_packet(m, tdb->tdb_tag, -1);
|
|
pf_pkt_addr_changed(m);
|
|
#endif
|
|
if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
|
|
m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post;
|
|
|
|
/*
|
|
* We're done with IPsec processing, transmit the packet using the
|
|
* appropriate network protocol (IP or IPv6). SPD lookup will be
|
|
* performed again there.
|
|
*/
|
|
switch (tdb->tdb_dst.sa.sa_family) {
|
|
case AF_INET:
|
|
error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0);
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
/*
|
|
* We don't need massage, IPv6 header fields are always in
|
|
* net endian.
|
|
*/
|
|
error = ip6_output(m, NULL, NULL, 0, NULL, NULL);
|
|
break;
|
|
#endif /* INET6 */
|
|
default:
|
|
error = EPFNOSUPPORT;
|
|
break;
|
|
}
|
|
return error;
|
|
|
|
drop:
|
|
m_freem(m);
|
|
return error;
|
|
}
|
|
|
|
ssize_t
|
|
ipsec_hdrsz(struct tdb *tdbp)
|
|
{
|
|
ssize_t adjust;
|
|
|
|
switch (tdbp->tdb_sproto) {
|
|
case IPPROTO_IPIP:
|
|
adjust = 0;
|
|
break;
|
|
|
|
case IPPROTO_ESP:
|
|
if (tdbp->tdb_encalgxform == NULL)
|
|
return (-1);
|
|
|
|
/* Header length */
|
|
adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
|
|
if (tdbp->tdb_flags & TDBF_UDPENCAP)
|
|
adjust += sizeof(struct udphdr);
|
|
/* Authenticator */
|
|
if (tdbp->tdb_authalgxform != NULL)
|
|
adjust += tdbp->tdb_authalgxform->authsize;
|
|
/* Padding */
|
|
adjust += MAX(4, tdbp->tdb_encalgxform->blocksize);
|
|
break;
|
|
|
|
case IPPROTO_AH:
|
|
if (tdbp->tdb_authalgxform == NULL)
|
|
return (-1);
|
|
|
|
adjust = AH_FLENGTH + sizeof(u_int32_t);
|
|
adjust += tdbp->tdb_authalgxform->authsize;
|
|
break;
|
|
|
|
default:
|
|
return (-1);
|
|
}
|
|
|
|
if (!(tdbp->tdb_flags & TDBF_TUNNELING) &&
|
|
!(tdbp->tdb_flags & TDBF_USEDTUNNEL))
|
|
return (adjust);
|
|
|
|
switch (tdbp->tdb_dst.sa.sa_family) {
|
|
case AF_INET:
|
|
adjust += sizeof(struct ip);
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
adjust += sizeof(struct ip6_hdr);
|
|
break;
|
|
#endif /* INET6 */
|
|
}
|
|
|
|
return (adjust);
|
|
}
|
|
|
|
void
|
|
ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
|
|
{
|
|
struct tdb_ident *tdbi;
|
|
struct tdb *tdbp;
|
|
struct m_tag *mtag;
|
|
ssize_t adjust;
|
|
|
|
NET_ASSERT_LOCKED();
|
|
|
|
for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag;
|
|
mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) {
|
|
tdbi = (struct tdb_ident *)(mtag + 1);
|
|
tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
|
|
tdbi->proto);
|
|
if (tdbp == NULL)
|
|
break;
|
|
|
|
if ((adjust = ipsec_hdrsz(tdbp)) == -1) {
|
|
tdb_unref(tdbp);
|
|
break;
|
|
}
|
|
|
|
mtu -= adjust;
|
|
tdbp->tdb_mtu = mtu;
|
|
tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
|
|
DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",
|
|
ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m);
|
|
tdb_unref(tdbp);
|
|
}
|
|
}
|