summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorprovos <provos@openbsd.org>2001-06-24 18:22:47 +0000
committerprovos <provos@openbsd.org>2001-06-24 18:22:47 +0000
commit6d7c3229ae5671e2ea9170f95fc99ae4a8e80f7c (patch)
treef968a59d029b09752c8295e7b27298933a1ce917
parentgrammar (diff)
downloadwireguard-openbsd-6d7c3229ae5671e2ea9170f95fc99ae4a8e80f7c.tar.xz
wireguard-openbsd-6d7c3229ae5671e2ea9170f95fc99ae4a8e80f7c.zip
path mtu discovery for ipsec. on receiving a need fragment icmp match
against active tdb and store the ipsec header size corrected mtu
-rw-r--r--sys/netinet/in_proto.c6
-rw-r--r--sys/netinet/ip_ipsp.h10
-rw-r--r--sys/netinet/ipsec_input.c90
-rw-r--r--sys/netinet/ipsec_output.c108
4 files changed, 208 insertions, 6 deletions
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index 58d0e3b088d..874fe34f4e3 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_proto.c,v 1.29 2001/06/08 03:53:45 angelos Exp $ */
+/* $OpenBSD: in_proto.c,v 1.30 2001/06/24 18:22:47 provos Exp $ */
/* $NetBSD: in_proto.c,v 1.14 1996/02/18 18:58:32 christos Exp $ */
/*
@@ -271,12 +271,12 @@ struct protosw inetsw[] = {
#endif /* NSIP */
#ifdef IPSEC
{ SOCK_RAW, &inetdomain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
- ah4_input, rip_output, 0, rip_ctloutput,
+ ah4_input, rip_output, ah4_ctlinput, rip_ctloutput,
rip_usrreq,
0, 0, 0, 0, ah_sysctl
},
{ SOCK_RAW, &inetdomain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
- esp4_input, rip_output, 0, rip_ctloutput,
+ esp4_input, rip_output, esp4_ctlinput, rip_ctloutput,
rip_usrreq,
0, 0, 0, 0, esp_sysctl
},
diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h
index dfde8679508..aab6998d3b8 100644
--- a/sys/netinet/ip_ipsp.h
+++ b/sys/netinet/ip_ipsp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.h,v 1.104 2001/06/24 18:15:38 provos Exp $ */
+/* $OpenBSD: ip_ipsp.h,v 1.105 2001/06/24 18:22:47 provos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -287,6 +287,7 @@ struct tdb /* tunnel descriptor block */
#define TDBF_NOREPLAY 0x02000 /* No replay counter present */
#define TDBF_RANDOMPADDING 0x04000 /* Random data in the ESP padding */
#define TDBF_SKIPCRYPTO 0x08000 /* Skip actual crypto processing */
+#define TDBF_USEDTUNNEL 0x10000 /* Appended a tunnel header in past */
u_int32_t tdb_flags; /* Flags related to this TDB */
@@ -347,6 +348,9 @@ struct tdb /* tunnel descriptor block */
struct ipsec_ref *tdb_local_auth; /* Local authentication material */
struct ipsec_ref *tdb_remote_auth; /* Remote authentication material */
+ u_int32_t tdb_mtu; /* MTU at this point in the chain */
+ u_int64_t tdb_mtutimeout; /* When to ignore this entry */
+
TAILQ_HEAD(tdb_inp_head_in, inpcb) tdb_inp_in;
TAILQ_HEAD(tdb_inp_head_out, inpcb) tdb_inp_out;
TAILQ_HEAD(tdb_policy_head, ipsec_policy) tdb_policy_head;
@@ -543,6 +547,7 @@ extern int ah_massage_headers(struct mbuf **, int, int, int, int);
#ifdef INET
extern void ah4_input __P((struct mbuf *, ...));
extern int ah4_input_cb __P((struct mbuf *, ...));
+extern void *ah4_ctlinput __P((int, struct sockaddr *, void *));
#endif /* INET */
#ifdef INET6
@@ -563,6 +568,7 @@ extern int esp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
#ifdef INET
extern void esp4_input __P((struct mbuf *, ...));
extern int esp4_input_cb __P((struct mbuf *, ...));
+extern void *esp4_ctlinput __P((int, struct sockaddr *, void *));
#endif /* INET */
#ifdef INET6
@@ -614,5 +620,7 @@ extern void ipsp_skipcrypto_unmark(struct tdb_ident *);
extern void ipsp_skipcrypto_mark(struct tdb_ident *);
extern struct m_tag *ipsp_parse_headers(struct mbuf *, int, u_int8_t);
extern int ipsp_ref_match(struct ipsec_ref *, struct ipsec_ref *);
+extern ssize_t ipsec_hdrsz(struct tdb *);
+extern void ipsec_adjust_mtu(struct mbuf *, u_int32_t);
#endif /* _KERNEL */
#endif /* _NETINET_IPSP_H_ */
diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c
index c570ddf0e33..451f1a7c5e3 100644
--- a/sys/netinet/ipsec_input.c
+++ b/sys/netinet/ipsec_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec_input.c,v 1.45 2001/06/23 16:15:56 fgsch Exp $ */
+/* $OpenBSD: ipsec_input.c,v 1.46 2001/06/24 18:22:47 provos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -37,6 +37,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/protosw.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
@@ -49,7 +50,9 @@
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/ip_var.h>
#include <netinet/in_var.h>
+#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
@@ -71,6 +74,7 @@
#include "bpfilter.h"
int ipsec_common_input(struct mbuf *, int, int, int, int);
+void *ipsec_common_ctlinput(int, struct sockaddr *, void *, int);
#ifdef ENCDEBUG
#define DPRINTF(x) if (encdebug) printf x
@@ -643,6 +647,17 @@ ah4_input_cb(struct mbuf *m, ...)
return 0;
}
+
+void *
+ah4_ctlinput(int cmd, struct sockaddr *sa, void *v)
+{
+ if (sa->sa_family != AF_INET ||
+ sa->sa_len != sizeof(struct sockaddr_in))
+ return (NULL);
+
+ return (ipsec_common_ctlinput(cmd, sa, v, IPPROTO_AH));
+}
+
/* IPv4 ESP wrapper */
void
esp4_input(struct mbuf *m, ...)
@@ -686,6 +701,79 @@ esp4_input_cb(struct mbuf *m, ...)
return 0;
}
+
+void *
+ipsec_common_ctlinput(int cmd, struct sockaddr *sa, void *v, int proto)
+{
+ extern u_int ip_mtudisc_timeout;
+ struct ip *ip = v;
+ int s;
+
+ if (cmd == PRC_MSGSIZE && ip && ip->ip_v == 4) {
+ struct tdb *tdbp;
+ struct sockaddr_in dst;
+ struct icmp *icp;
+ int hlen = ip->ip_hl << 2;
+ u_int32_t spi, mtu;
+ ssize_t adjust;
+
+ /* Find the right MTU */
+ icp = (struct icmp *)((caddr_t) ip -
+ offsetof(struct icmp, icmp_ip));
+ mtu = ntohs(icp->icmp_nextmtu);
+
+ /* Ignore the packet, if we do not receive a MTU
+ * or the MTU is too small to be acceptable.
+ */
+ if (mtu < 296)
+ return (NULL);
+
+ bzero(&dst, sizeof(struct sockaddr_in));
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = ip->ip_dst.s_addr;
+
+ bcopy((caddr_t)ip + hlen, &spi, sizeof(u_int32_t));
+
+ s = spltdb();
+ tdbp = gettdb(spi, (union sockaddr_union *)&dst, proto);
+ if (tdbp == NULL || tdbp->tdb_flags & TDBF_INVALID) {
+ splx(s);
+ return (NULL);
+ }
+
+ /* Walk the chain backswards to the first tdb */
+ for (; tdbp; tdbp = tdbp->tdb_inext) {
+ if (tdbp->tdb_flags & TDBF_INVALID ||
+ (adjust = ipsec_hdrsz(tdbp)) == -1) {
+ splx(s);
+ return (NULL);
+ }
+
+ mtu -= adjust;
+
+ /* Store adjusted MTU in tdb */
+ tdbp->tdb_mtu = mtu;
+ tdbp->tdb_mtutimeout = time.tv_sec +
+ ip_mtudisc_timeout;
+ }
+ splx(s);
+
+ return (NULL);
+ }
+
+ return (NULL);
+}
+
+void *
+esp4_ctlinput(int cmd, struct sockaddr *sa, void *v)
+{
+ if (sa->sa_family != AF_INET ||
+ sa->sa_len != sizeof(struct sockaddr_in))
+ return (NULL);
+
+ return (ipsec_common_ctlinput(cmd, sa, v, IPPROTO_ESP));
+}
#endif /* INET */
#ifdef INET6
diff --git a/sys/netinet/ipsec_output.c b/sys/netinet/ipsec_output.c
index e2b996989ed..6eeed29999b 100644
--- a/sys/netinet/ipsec_output.c
+++ b/sys/netinet/ipsec_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec_output.c,v 1.14 2001/06/08 03:13:15 angelos Exp $ */
+/* $OpenBSD: ipsec_output.c,v 1.15 2001/06/24 18:22:47 provos Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
@@ -48,6 +48,7 @@
#include <netinet/ip_ipsp.h>
#include <netinet/ip_ah.h>
#include <netinet/ip_esp.h>
+#include <crypto/xform.h>
#ifdef ENCDEBUG
#define DPRINTF(x) if (encdebug) printf x
@@ -67,6 +68,7 @@ ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
struct mbuf *mp;
#ifdef INET
+ int setdf = 0;
struct ip *ip;
#endif /* INET */
#ifdef INET6
@@ -159,6 +161,8 @@ ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
#ifdef INET
ip = mtod(m, struct ip *);
+ /* This is not a bridge packet, remember if we had IP_DF */
+ setdf = ntohs(ip->ip_off) & IP_DF;
#endif /* INET */
#ifdef INET6
@@ -233,6 +237,22 @@ ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
m = mp;
mp = NULL;
+
+#ifdef INET
+ if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) {
+ ip = mtod(m, struct ip *);
+ if (m->m_len < sizeof(struct ip))
+ if ((m = m_pullup(m, sizeof(struct ip))) == NULL)
+ return ENOBUFS;
+
+ NTOHS(ip->ip_off);
+ ip->ip_off |= IP_DF;
+ HTONS(ip->ip_off);
+ }
+
+ /* Remember that we appended a tunnel header */
+ tdb->tdb_flags |= TDBF_USEDTUNNEL;
+#endif
}
/* We may be done with this TDB */
@@ -392,3 +412,89 @@ ipsp_process_done(struct mbuf *m, struct tdb *tdb)
/* Not reached */
return EINVAL;
}
+
+ssize_t
+ipsec_hdrsz(struct tdb *tdbp)
+{
+ ssize_t adjust;
+
+ switch (tdbp->tdb_sproto) {
+ case IPPROTO_ESP:
+ if (tdbp->tdb_encalgxform == NULL)
+ return (-1);
+
+ /* Header length */
+ if (tdbp->tdb_flags & TDBF_NOREPLAY)
+ adjust = sizeof(u_int32_t) + tdbp->tdb_ivlen;
+ else
+ adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
+ /* Authenticator */
+ if (tdbp->tdb_authalgxform != NULL)
+ adjust += AH_HMAC_HASHLEN;
+ /* Padding */
+ adjust += tdbp->tdb_encalgxform->blocksize;
+ break;
+
+ case IPPROTO_AH:
+ if (tdbp->tdb_authalgxform == NULL)
+ return (-1);
+
+ if (!(tdbp->tdb_flags & TDBF_NOREPLAY))
+ adjust = AH_FLENGTH + sizeof(u_int32_t);
+ else
+ adjust = AH_FLENGTH;
+ 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) {
+#ifdef INET
+ case AF_INET:
+ adjust += sizeof(struct ip);
+ break;
+#endif /* INET */
+#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;
+ int s;
+
+ s = spltdb();
+
+ 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->spi, &tdbi->dst, tdbi->proto);
+ if (tdbp == NULL)
+ break;
+
+ if ((adjust = ipsec_hdrsz(tdbp)) == -1)
+ break;
+
+ mtu -= adjust;
+ tdbp->tdb_mtu = mtu;
+ tdbp->tdb_mtutimeout = time.tv_sec + ip_mtudisc_timeout;
+ }
+
+ splx(s);
+}