ipsec: Add support for PMTUD for IPv6 tunnels
Discard and send ICMPv6 Packet Too Big to sender when we try to encapsulate and forward a packet which total length exceeds the PMTU. Logic is based on the IPv4 implementation. Common code was moved to a separate function. Differential revision: https://reviews.freebsd.org/D31771 Obtained from: Semihalf Sponsored by: Stormshield
This commit is contained in:
parent
b4220bf387
commit
9dfc8606eb
@ -74,6 +74,7 @@ int ipsec6_output(struct mbuf *, struct inpcb *);
|
|||||||
int ipsec6_capability(struct mbuf *, u_int);
|
int ipsec6_capability(struct mbuf *, u_int);
|
||||||
int ipsec6_common_input_cb(struct mbuf *, struct secasvar *, int, int);
|
int ipsec6_common_input_cb(struct mbuf *, struct secasvar *, int, int);
|
||||||
int ipsec6_ctlinput(int, struct sockaddr *, void *);
|
int ipsec6_ctlinput(int, struct sockaddr *, void *);
|
||||||
|
int ipsec6_check_pmtu(struct mbuf *, struct secpolicy *, int);
|
||||||
int ipsec6_process_packet(struct mbuf *, struct secpolicy *, struct inpcb *);
|
int ipsec6_process_packet(struct mbuf *, struct secpolicy *, struct inpcb *);
|
||||||
|
|
||||||
int ip6_ipsec_filtertunnel(struct mbuf *);
|
int ip6_ipsec_filtertunnel(struct mbuf *);
|
||||||
|
@ -106,6 +106,7 @@
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx);
|
static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx);
|
||||||
|
static size_t ipsec_get_pmtu(struct secasvar *sav);
|
||||||
|
|
||||||
#ifdef INET
|
#ifdef INET
|
||||||
static struct secasvar *
|
static struct secasvar *
|
||||||
@ -297,8 +298,6 @@ ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp,
|
|||||||
int
|
int
|
||||||
ipsec4_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
|
ipsec4_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
|
||||||
{
|
{
|
||||||
union sockaddr_union *dst;
|
|
||||||
struct in_conninfo inc;
|
|
||||||
struct secasvar *sav;
|
struct secasvar *sav;
|
||||||
struct ip *ip;
|
struct ip *ip;
|
||||||
size_t hlen, pmtu;
|
size_t hlen, pmtu;
|
||||||
@ -333,44 +332,15 @@ ipsec4_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
dst = &sav->sah->saidx.dst;
|
pmtu = ipsec_get_pmtu(sav);
|
||||||
memset(&inc, 0, sizeof(inc));
|
if (pmtu == 0) {
|
||||||
switch (dst->sa.sa_family) {
|
|
||||||
case AF_INET:
|
|
||||||
inc.inc_faddr = satosin(&dst->sa)->sin_addr;
|
|
||||||
break;
|
|
||||||
#ifdef INET6
|
|
||||||
case AF_INET6:
|
|
||||||
inc.inc6_faddr = satosin6(&dst->sa)->sin6_addr;
|
|
||||||
inc.inc_flags |= INC_ISIPV6;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
key_freesav(&sav);
|
key_freesav(&sav);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
key_freesav(&sav);
|
|
||||||
pmtu = tcp_hc_getmtu(&inc);
|
|
||||||
/* No entry in hostcache. Use link MTU instead. */
|
|
||||||
if (pmtu == 0) {
|
|
||||||
switch (dst->sa.sa_family) {
|
|
||||||
case AF_INET:
|
|
||||||
pmtu = tcp_maxmtu(&inc, NULL);
|
|
||||||
break;
|
|
||||||
#ifdef INET6
|
|
||||||
case AF_INET6:
|
|
||||||
pmtu = tcp_maxmtu6(&inc, NULL);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (pmtu == 0)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
tcp_hc_updatemtu(&inc, pmtu);
|
|
||||||
}
|
|
||||||
|
|
||||||
hlen = ipsec_hdrsiz_internal(sp);
|
hlen = ipsec_hdrsiz_internal(sp);
|
||||||
|
key_freesav(&sav);
|
||||||
|
|
||||||
if (m_length(m, NULL) + hlen > pmtu) {
|
if (m_length(m, NULL) + hlen > pmtu) {
|
||||||
/*
|
/*
|
||||||
* If we're forwarding generate ICMP message here,
|
* If we're forwarding generate ICMP message here,
|
||||||
@ -720,6 +690,72 @@ ipsec6_process_packet(struct mbuf *m, struct secpolicy *sp,
|
|||||||
return (ipsec6_perform_request(m, sp, inp, 0));
|
return (ipsec6_perform_request(m, sp, inp, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IPv6 implementation is based on IPv4 implementation.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ipsec6_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
|
||||||
|
{
|
||||||
|
struct secasvar *sav;
|
||||||
|
size_t hlen, pmtu;
|
||||||
|
uint32_t idx;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* According to RFC8200 L3 fragmentation is supposed to be done only on
|
||||||
|
* locally generated packets. During L3 forwarding packets that are too
|
||||||
|
* big are always supposed to be dropped, with an ICMPv6 packet being
|
||||||
|
* sent back.
|
||||||
|
*/
|
||||||
|
if (!forwarding)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
idx = sp->tcount - 1;
|
||||||
|
sav = ipsec6_allocsa(m, sp, &idx, &error);
|
||||||
|
if (sav == NULL) {
|
||||||
|
key_freesp(&sp);
|
||||||
|
/*
|
||||||
|
* No matching SA was found and SADB_ACQUIRE message was generated.
|
||||||
|
* Since we have matched a SP to this packet drop it silently.
|
||||||
|
*/
|
||||||
|
if (error == 0)
|
||||||
|
error = EINPROGRESS;
|
||||||
|
if (error != EJUSTRETURN)
|
||||||
|
m_freem(m);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
pmtu = ipsec_get_pmtu(sav);
|
||||||
|
if (pmtu == 0) {
|
||||||
|
key_freesav(&sav);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hlen = ipsec_hdrsiz_internal(sp);
|
||||||
|
key_freesav(&sav);
|
||||||
|
|
||||||
|
if (m_length(m, NULL) + hlen > pmtu) {
|
||||||
|
/*
|
||||||
|
* If we're forwarding generate ICMPv6 message here,
|
||||||
|
* so that it contains pmtu substracted by header size.
|
||||||
|
* Set error to EINPROGRESS, in order for the frame
|
||||||
|
* to be dropped silently.
|
||||||
|
*/
|
||||||
|
if (forwarding) {
|
||||||
|
if (pmtu > hlen)
|
||||||
|
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, pmtu - hlen);
|
||||||
|
else
|
||||||
|
m_freem(m);
|
||||||
|
|
||||||
|
key_freesp(&sp);
|
||||||
|
return (EINPROGRESS); /* Pretend that we consumed it. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
|
ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
|
||||||
{
|
{
|
||||||
@ -766,6 +802,15 @@ ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error = ipsec6_check_pmtu(m, sp, forwarding);
|
||||||
|
if (error != 0) {
|
||||||
|
if (error == EJUSTRETURN)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
/* NB: callee frees mbuf and releases reference to SP */
|
/* NB: callee frees mbuf and releases reference to SP */
|
||||||
error = ipsec6_process_packet(m, sp, inp);
|
error = ipsec6_process_packet(m, sp, inp);
|
||||||
if (error == EJUSTRETURN) {
|
if (error == EJUSTRETURN) {
|
||||||
@ -1001,6 +1046,59 @@ ipsec_prepend(struct mbuf *m, int len, int how)
|
|||||||
return (n);
|
return (n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
ipsec_get_pmtu(struct secasvar *sav)
|
||||||
|
{
|
||||||
|
union sockaddr_union *dst;
|
||||||
|
struct in_conninfo inc;
|
||||||
|
size_t pmtu;
|
||||||
|
|
||||||
|
dst = &sav->sah->saidx.dst;
|
||||||
|
memset(&inc, 0, sizeof(inc));
|
||||||
|
|
||||||
|
switch (dst->sa.sa_family) {
|
||||||
|
#ifdef INET
|
||||||
|
case AF_INET:
|
||||||
|
inc.inc_faddr = satosin(&dst->sa)->sin_addr;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef INET6
|
||||||
|
case AF_INET6:
|
||||||
|
inc.inc6_faddr = satosin6(&dst->sa)->sin6_addr;
|
||||||
|
inc.inc_flags |= INC_ISIPV6;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pmtu = tcp_hc_getmtu(&inc);
|
||||||
|
if (pmtu != 0)
|
||||||
|
return (pmtu);
|
||||||
|
|
||||||
|
/* No entry in hostcache. Assume that PMTU is equal to link's MTU */
|
||||||
|
switch (dst->sa.sa_family) {
|
||||||
|
#ifdef INET
|
||||||
|
case AF_INET:
|
||||||
|
pmtu = tcp_maxmtu(&inc, NULL);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef INET6
|
||||||
|
case AF_INET6:
|
||||||
|
pmtu = tcp_maxmtu6(&inc, NULL);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (pmtu == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
tcp_hc_updatemtu(&inc, pmtu);
|
||||||
|
|
||||||
|
return (pmtu);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
|
ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user