summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandre <andre@FreeBSD.org>2005-04-21 14:29:34 +0000
committerandre <andre@FreeBSD.org>2005-04-21 14:29:34 +0000
commit4f8537607278c0fbb4d30ab96c2fac657d5fdeaf (patch)
treef9fb2ed28a25065dedd8eb4f8192b59cb2728403
parent510a2f302a52427fe35f48836697a480c0caeef2 (diff)
downloadFreeBSD-src-4f8537607278c0fbb4d30ab96c2fac657d5fdeaf.zip
FreeBSD-src-4f8537607278c0fbb4d30ab96c2fac657d5fdeaf.tar.gz
Move Path MTU discovery ICMP processing from icmp_input() to
tcp_ctlinput() and subject it to active tcpcb and sequence number checking. Previously any ICMP unreachable/needfrag message would cause an update to the TCP hostcache. Now only ICMP PMTU messages belonging to an active TCP session with the correct src/dst/port and sequence number will update the hostcache and complete the path MTU discovery process. Note that we don't entirely implement the recommended counter measures of Section 7.2 of the paper. However we close down the possible degradation vector from trivially easy to really complex and resource intensive. In addition we have limited the smallest acceptable MTU with net.inet.tcp.minmss sysctl for some time already, further reducing the effect of any degradation due to an attack. Security: draft-gont-tcpm-icmp-attacks-03.txt Section 7.2 MFC after: 3 days
-rw-r--r--sys/netinet/ip_icmp.c46
-rw-r--r--sys/netinet/ip_icmp.h1
-rw-r--r--sys/netinet/tcp_subr.c43
-rw-r--r--sys/netinet/tcp_timewait.c43
4 files changed, 74 insertions, 59 deletions
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c
index 4547886..c57b684 100644
--- a/sys/netinet/ip_icmp.c
+++ b/sys/netinet/ip_icmp.c
@@ -125,7 +125,6 @@ int icmpprintfs = 0;
static void icmp_reflect(struct mbuf *);
static void icmp_send(struct mbuf *, struct mbuf *);
-static int ip_next_mtu(int, int);
extern struct protosw inetsw[];
@@ -407,49 +406,6 @@ icmp_input(m, off)
printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
#endif
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
-
- /*
- * MTU discovery:
- * If we got a needfrag and there is a host route to the
- * original destination, and the MTU is not locked, then
- * set the MTU in the route to the suggested new value
- * (if given) and then notify as usual. The ULPs will
- * notice that the MTU has changed and adapt accordingly.
- * If no new MTU was suggested, then we guess a new one
- * less than the current value. If the new MTU is
- * unreasonably small (defined by sysctl tcp_minmss), then
- * we don't update the MTU value.
- *
- * XXX: All this should be done in tcp_mtudisc() because
- * the way we do it now, everyone can send us bogus ICMP
- * MSGSIZE packets for any destination. By doing this far
- * higher in the chain we have a matching tcp connection.
- * Thus spoofing is much harder. However there is no easy
- * non-hackish way to pass the new MTU up to tcp_mtudisc().
- * Also see next XXX regarding IPv4 AH TCP.
- */
- if (code == PRC_MSGSIZE) {
- int mtu;
- struct in_conninfo inc;
-
- bzero(&inc, sizeof(inc));
- inc.inc_flags = 0; /* IPv4 */
- inc.inc_faddr = icmpsrc.sin_addr;
-
- mtu = ntohs(icp->icmp_nextmtu);
- if (!mtu)
- mtu = ip_next_mtu(mtu, 1);
-
- if (mtu >= max(296, (tcp_minmss +
- sizeof(struct tcpiphdr))))
- tcp_hc_updatemtu(&inc, mtu);
-
-#ifdef DEBUG_MTUDISC
- printf("MTU for %s reduced to %d\n",
- inet_ntoa(icmpsrc.sin_addr), mtu);
-#endif
- }
-
/*
* XXX if the packet contains [IPv4 AH TCP], we can't make a
* notification to TCP layer.
@@ -831,7 +787,7 @@ iptime()
* given current value MTU. If DIR is less than zero, a larger plateau
* is returned; otherwise, a smaller value is returned.
*/
-static int
+int
ip_next_mtu(mtu, dir)
int mtu;
int dir;
diff --git a/sys/netinet/ip_icmp.h b/sys/netinet/ip_icmp.h
index 9d58529..6acff63 100644
--- a/sys/netinet/ip_icmp.h
+++ b/sys/netinet/ip_icmp.h
@@ -193,6 +193,7 @@ struct icmp {
#ifdef _KERNEL
void icmp_error(struct mbuf *, int, int, n_long, struct ifnet *);
void icmp_input(struct mbuf *, int);
+int ip_next_mtu(int, int);
#endif
#endif
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 02e753a..474aa52 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -76,6 +76,7 @@
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#endif
+#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
@@ -1110,8 +1111,10 @@ tcp_ctlinput(cmd, sa, vip)
struct inpcb *inp;
struct tcpcb *tp;
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify;
- tcp_seq icmp_seq;
- int s;
+ struct icmp *icp;
+ struct in_conninfo inc;
+ tcp_seq icmp_tcp_seq;
+ int mtu, s;
faddr = ((struct sockaddr_in *)sa)->sin_addr;
if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)
@@ -1143,6 +1146,8 @@ tcp_ctlinput(cmd, sa, vip)
return;
if (ip != NULL) {
s = splnet();
+ icp = (struct icmp *)((caddr_t)ip
+ - offsetof(struct icmp, icmp_ip));
th = (struct tcphdr *)((caddr_t)ip
+ (ip->ip_hl << 2));
INP_INFO_WLOCK(&tcbinfo);
@@ -1151,17 +1156,41 @@ tcp_ctlinput(cmd, sa, vip)
if (inp != NULL) {
INP_LOCK(inp);
if (inp->inp_socket != NULL) {
- icmp_seq = htonl(th->th_seq);
+ icmp_tcp_seq = htonl(th->th_seq);
tp = intotcpcb(inp);
- if (SEQ_GEQ(icmp_seq, tp->snd_una) &&
- SEQ_LT(icmp_seq, tp->snd_max))
+ if (SEQ_GEQ(icmp_tcp_seq, tp->snd_una) &&
+ SEQ_LT(icmp_tcp_seq, tp->snd_max)) {
+ if (cmd == PRC_MSGSIZE) {
+ /*
+ * MTU discovery:
+ * If we got a needfrag set the MTU
+ * in the route to the suggested new
+ * value (if given) and then notify.
+ * If no new MTU was suggested, then
+ * we guess a new one less than the
+ * current value.
+ * If the new MTU is unreasonably
+ * small (defined by sysctl tcp_minmss),
+ * then we up the MTU value to minimum.
+ */
+ bzero(&inc, sizeof(inc));
+ inc.inc_flags = 0; /* IPv4 */
+ inc.inc_faddr = faddr;
+
+ mtu = ntohs(icp->icmp_nextmtu);
+ if (!mtu)
+ mtu = ip_next_mtu(mtu, 1);
+ if (mtu >= max(296, (tcp_minmss
+ + sizeof(struct tcpiphdr))))
+ tcp_hc_updatemtu(&inc, mtu);
+ }
+
inp = (*notify)(inp, inetctlerrmap[cmd]);
+ }
}
if (inp != NULL)
INP_UNLOCK(inp);
} else {
- struct in_conninfo inc;
-
inc.inc_fport = th->th_dport;
inc.inc_lport = th->th_sport;
inc.inc_faddr = faddr;
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
index 02e753a..474aa52 100644
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -76,6 +76,7 @@
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#endif
+#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
@@ -1110,8 +1111,10 @@ tcp_ctlinput(cmd, sa, vip)
struct inpcb *inp;
struct tcpcb *tp;
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify;
- tcp_seq icmp_seq;
- int s;
+ struct icmp *icp;
+ struct in_conninfo inc;
+ tcp_seq icmp_tcp_seq;
+ int mtu, s;
faddr = ((struct sockaddr_in *)sa)->sin_addr;
if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)
@@ -1143,6 +1146,8 @@ tcp_ctlinput(cmd, sa, vip)
return;
if (ip != NULL) {
s = splnet();
+ icp = (struct icmp *)((caddr_t)ip
+ - offsetof(struct icmp, icmp_ip));
th = (struct tcphdr *)((caddr_t)ip
+ (ip->ip_hl << 2));
INP_INFO_WLOCK(&tcbinfo);
@@ -1151,17 +1156,41 @@ tcp_ctlinput(cmd, sa, vip)
if (inp != NULL) {
INP_LOCK(inp);
if (inp->inp_socket != NULL) {
- icmp_seq = htonl(th->th_seq);
+ icmp_tcp_seq = htonl(th->th_seq);
tp = intotcpcb(inp);
- if (SEQ_GEQ(icmp_seq, tp->snd_una) &&
- SEQ_LT(icmp_seq, tp->snd_max))
+ if (SEQ_GEQ(icmp_tcp_seq, tp->snd_una) &&
+ SEQ_LT(icmp_tcp_seq, tp->snd_max)) {
+ if (cmd == PRC_MSGSIZE) {
+ /*
+ * MTU discovery:
+ * If we got a needfrag set the MTU
+ * in the route to the suggested new
+ * value (if given) and then notify.
+ * If no new MTU was suggested, then
+ * we guess a new one less than the
+ * current value.
+ * If the new MTU is unreasonably
+ * small (defined by sysctl tcp_minmss),
+ * then we up the MTU value to minimum.
+ */
+ bzero(&inc, sizeof(inc));
+ inc.inc_flags = 0; /* IPv4 */
+ inc.inc_faddr = faddr;
+
+ mtu = ntohs(icp->icmp_nextmtu);
+ if (!mtu)
+ mtu = ip_next_mtu(mtu, 1);
+ if (mtu >= max(296, (tcp_minmss
+ + sizeof(struct tcpiphdr))))
+ tcp_hc_updatemtu(&inc, mtu);
+ }
+
inp = (*notify)(inp, inetctlerrmap[cmd]);
+ }
}
if (inp != NULL)
INP_UNLOCK(inp);
} else {
- struct in_conninfo inc;
-
inc.inc_fport = th->th_dport;
inc.inc_lport = th->th_sport;
inc.inc_faddr = faddr;
OpenPOWER on IntegriCloud