diff options
author | 2001-06-24 18:22:47 +0000 | |
---|---|---|
committer | 2001-06-24 18:22:47 +0000 | |
commit | 6d7c3229ae5671e2ea9170f95fc99ae4a8e80f7c (patch) | |
tree | f968a59d029b09752c8295e7b27298933a1ce917 | |
parent | grammar (diff) | |
download | wireguard-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.c | 6 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 10 | ||||
-rw-r--r-- | sys/netinet/ipsec_input.c | 90 | ||||
-rw-r--r-- | sys/netinet/ipsec_output.c | 108 |
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); +} |