diff options
author | phk <phk@FreeBSD.org> | 2000-12-24 10:57:21 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2000-12-24 10:57:21 +0000 |
commit | 6bfb7240b822195a74d4fa5a8268f2143dc0102e (patch) | |
tree | 405d81d62b19cc1cbb3560b679a7e9ebbb48cf2e /sys/netinet | |
parent | f57db1fdc0f7f85d46f2d4044f81ba307bd16a5d (diff) | |
download | FreeBSD-src-6bfb7240b822195a74d4fa5a8268f2143dc0102e.zip FreeBSD-src-6bfb7240b822195a74d4fa5a8268f2143dc0102e.tar.gz |
Update the "icmp_admin_prohib_like_rst" code to check the tcp-window and
to be configurable with respect to acting only in SYN or in all TCP states.
PR: 23665
Submitted by: Jesper Skriver <jesper@skriver.dk>
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/in_pcb.c | 20 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 3 | ||||
-rw-r--r-- | sys/netinet/tcp_subr.c | 69 | ||||
-rw-r--r-- | sys/netinet/tcp_timewait.c | 69 | ||||
-rw-r--r-- | sys/netinet/tcp_var.h | 1 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 4 |
6 files changed, 144 insertions, 22 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index fd0f248..3d40b9f 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -665,15 +665,20 @@ in_setpeeraddr(so, nam) * cmds that are uninteresting (e.g., no error in the map). * Call the protocol specific routine (if any) to report * any errors for each matching socket. + * + * If tcp_seq_check != 0 it also checks if tcp_sequence is + * a valid TCP sequence number for the session. */ void -in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) +in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify, tcp_sequence, tcp_seq_check) struct inpcbhead *head; struct sockaddr *dst; u_int fport_arg, lport_arg; struct in_addr laddr; int cmd; void (*notify) __P((struct inpcb *, int)); + u_int32_t tcp_sequence; + int tcp_seq_check; { register struct inpcb *inp, *oinp; struct in_addr faddr; @@ -717,6 +722,19 @@ in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) inp = inp->inp_list.le_next; continue; } + /* + * If tcp_seq_check is set, then skip sessions where + * the sequence number is not one of a unacknowledged + * packet. + * + * If it doesn't match, we break the loop, as only a + * single session can match on src/dst ip addresses + * and TCP port numbers. + */ + if ((tcp_seq_check == 1) && (tcp_seq_vs_sess(inp, tcp_sequence) == 0)) { + inp = inp->inp_list.le_next; + break; + } oinp = inp; inp = inp->inp_list.le_next; if (notify) diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 08733ad..08b1627 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -290,7 +290,8 @@ struct inpcb * struct in_addr, u_int, struct in_addr, u_int, int, struct ifnet *)); void in_pcbnotify __P((struct inpcbhead *, struct sockaddr *, - u_int, struct in_addr, u_int, int, void (*)(struct inpcb *, int))); + u_int, struct in_addr, u_int, int, void (*)(struct inpcb *, int), + u_int32_t, int)); void in_pcbrehash __P((struct inpcb *)); int in_setpeeraddr __P((struct socket *so, struct sockaddr **nam)); int in_setsockaddr __P((struct socket *so, struct sockaddr **nam)); diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index fe00373..5b1db4f 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -139,9 +139,20 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD, * as required by rfc1122 section 3.2.2.1 */ -static int icmp_admin_prohib_like_rst = 0; +static int icmp_admin_prohib_like_rst = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_admin_prohib_like_rst, CTLFLAG_RW, - &icmp_admin_prohib_like_rst, 0, "Treat ICMP administratively prohibited messages like TCP RST, rfc1122 section 3.2.2.1"); + &icmp_admin_prohib_like_rst, 0, + "Treat ICMP administratively prohibited messages like TCP RST, rfc1122 section 3.2.2.1"); + +/* + * When icmp_admin_prohib_like_rst is enabled, only act on + * sessions in SYN-SENT state + */ + +static int icmp_like_rst_syn_sent_only = 1; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_like_rst_syn_sent_only, CTLFLAG_RW, + &icmp_like_rst_syn_sent_only, 0, + "When icmp_admin_prohib_like_rst is enabled, only act on sessions in SYN-SENT state"); static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); @@ -967,12 +978,23 @@ tcp_ctlinput(cmd, sa, vip) register struct ip *ip = vip; register struct tcphdr *th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; + tcp_seq tcp_sequence = 0; + int tcp_seq_check = 0; if (cmd == PRC_QUENCH) notify = tcp_quench; - else if ((icmp_admin_prohib_like_rst == 1) && (cmd == PRC_UNREACH_PORT) && (ip)) + else if ((icmp_admin_prohib_like_rst == 1) && (cmd == PRC_UNREACH_PORT) && + (ip) && ((IP_VHL_HL(ip->ip_vhl) << 2) == sizeof(struct ip))) { + /* + * Only go here if the length of the IP header in the ICMP packet + * is 20 bytes, that is it doesn't have options, if it does have + * options, we will not have the first 8 bytes of the TCP header, + * and thus we cannot match against TCP source/destination port + * numbers and TCP sequence number. + */ + tcp_seq_check = 1; notify = tcp_drop_syn_sent; - else if (cmd == PRC_MSGSIZE) + } else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)) @@ -980,10 +1002,12 @@ tcp_ctlinput(cmd, sa, vip) if (ip) { th = (struct tcphdr *)((caddr_t)ip + (IP_VHL_HL(ip->ip_vhl) << 2)); + if (tcp_seq_check == 1) + tcp_sequence = ntohl(th->th_seq); in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport, - cmd, notify); + cmd, notify, tcp_sequence, tcp_seq_check); } else - in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify); + in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify, 0, 0); } #ifdef INET6 @@ -1070,6 +1094,30 @@ tcp6_ctlinput(cmd, sa, d) #endif /* INET6 */ /* + * Check if the supplied TCP sequence number is a sequence number + * for a sent but unacknowledged packet on the given TCP session. + */ +int +tcp_seq_vs_sess(inp, tcp_sequence) + struct inpcb *inp; + tcp_seq tcp_sequence; +{ + struct tcpcb *tp = intotcpcb(inp); + /* + * If the sequence number is less than that of the last + * unacknowledged packet, or greater than that of the + * last sent, the given sequence number is not that + * of a sent but unacknowledged packet for this session. + */ + if (SEQ_LT(tcp_sequence, tp->snd_una) || + SEQ_GT(tcp_sequence, tp->snd_max)) { + return(0); + } else { + return(1); + } +} + +/* * When a source quench is received, close congestion window * to one segment. We will gradually open it again as we proceed. */ @@ -1086,7 +1134,9 @@ tcp_quench(inp, errno) /* * When a ICMP unreachable is recieved, drop the - * TCP connection, but only if in SYN_SENT + * TCP connection, depending on the sysctl + * icmp_like_rst_syn_sent_only, it only drops + * the session if it's in SYN-SENT state */ void tcp_drop_syn_sent(inp, errno) @@ -1094,8 +1144,9 @@ tcp_drop_syn_sent(inp, errno) int errno; { struct tcpcb *tp = intotcpcb(inp); - if((tp) && (tp->t_state == TCPS_SYN_SENT)) - tcp_drop(tp, errno); + if((tp) && ((icmp_like_rst_syn_sent_only == 0) || + (tp->t_state == TCPS_SYN_SENT))) + tcp_drop(tp, errno); } /* diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index fe00373..5b1db4f 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -139,9 +139,20 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD, * as required by rfc1122 section 3.2.2.1 */ -static int icmp_admin_prohib_like_rst = 0; +static int icmp_admin_prohib_like_rst = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_admin_prohib_like_rst, CTLFLAG_RW, - &icmp_admin_prohib_like_rst, 0, "Treat ICMP administratively prohibited messages like TCP RST, rfc1122 section 3.2.2.1"); + &icmp_admin_prohib_like_rst, 0, + "Treat ICMP administratively prohibited messages like TCP RST, rfc1122 section 3.2.2.1"); + +/* + * When icmp_admin_prohib_like_rst is enabled, only act on + * sessions in SYN-SENT state + */ + +static int icmp_like_rst_syn_sent_only = 1; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_like_rst_syn_sent_only, CTLFLAG_RW, + &icmp_like_rst_syn_sent_only, 0, + "When icmp_admin_prohib_like_rst is enabled, only act on sessions in SYN-SENT state"); static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); @@ -967,12 +978,23 @@ tcp_ctlinput(cmd, sa, vip) register struct ip *ip = vip; register struct tcphdr *th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; + tcp_seq tcp_sequence = 0; + int tcp_seq_check = 0; if (cmd == PRC_QUENCH) notify = tcp_quench; - else if ((icmp_admin_prohib_like_rst == 1) && (cmd == PRC_UNREACH_PORT) && (ip)) + else if ((icmp_admin_prohib_like_rst == 1) && (cmd == PRC_UNREACH_PORT) && + (ip) && ((IP_VHL_HL(ip->ip_vhl) << 2) == sizeof(struct ip))) { + /* + * Only go here if the length of the IP header in the ICMP packet + * is 20 bytes, that is it doesn't have options, if it does have + * options, we will not have the first 8 bytes of the TCP header, + * and thus we cannot match against TCP source/destination port + * numbers and TCP sequence number. + */ + tcp_seq_check = 1; notify = tcp_drop_syn_sent; - else if (cmd == PRC_MSGSIZE) + } else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)) @@ -980,10 +1002,12 @@ tcp_ctlinput(cmd, sa, vip) if (ip) { th = (struct tcphdr *)((caddr_t)ip + (IP_VHL_HL(ip->ip_vhl) << 2)); + if (tcp_seq_check == 1) + tcp_sequence = ntohl(th->th_seq); in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport, - cmd, notify); + cmd, notify, tcp_sequence, tcp_seq_check); } else - in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify); + in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify, 0, 0); } #ifdef INET6 @@ -1070,6 +1094,30 @@ tcp6_ctlinput(cmd, sa, d) #endif /* INET6 */ /* + * Check if the supplied TCP sequence number is a sequence number + * for a sent but unacknowledged packet on the given TCP session. + */ +int +tcp_seq_vs_sess(inp, tcp_sequence) + struct inpcb *inp; + tcp_seq tcp_sequence; +{ + struct tcpcb *tp = intotcpcb(inp); + /* + * If the sequence number is less than that of the last + * unacknowledged packet, or greater than that of the + * last sent, the given sequence number is not that + * of a sent but unacknowledged packet for this session. + */ + if (SEQ_LT(tcp_sequence, tp->snd_una) || + SEQ_GT(tcp_sequence, tp->snd_max)) { + return(0); + } else { + return(1); + } +} + +/* * When a source quench is received, close congestion window * to one segment. We will gradually open it again as we proceed. */ @@ -1086,7 +1134,9 @@ tcp_quench(inp, errno) /* * When a ICMP unreachable is recieved, drop the - * TCP connection, but only if in SYN_SENT + * TCP connection, depending on the sysctl + * icmp_like_rst_syn_sent_only, it only drops + * the session if it's in SYN-SENT state */ void tcp_drop_syn_sent(inp, errno) @@ -1094,8 +1144,9 @@ tcp_drop_syn_sent(inp, errno) int errno; { struct tcpcb *tp = intotcpcb(inp); - if((tp) && (tp->t_state == TCPS_SYN_SENT)) - tcp_drop(tp, errno); + if((tp) && ((icmp_like_rst_syn_sent_only == 0) || + (tp->t_state == TCPS_SYN_SENT))) + tcp_drop(tp, errno); } /* diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index e361caf..dc69d3e 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -392,6 +392,7 @@ void tcp_mtudisc __P((struct inpcb *, int)); struct tcpcb * tcp_newtcpcb __P((struct inpcb *)); int tcp_output __P((struct tcpcb *)); +int tcp_seq_vs_sess __P((struct inpcb *, tcp_seq)); void tcp_quench __P((struct inpcb *, int)); void tcp_respond __P((struct tcpcb *, void *, struct tcphdr *, struct mbuf *, tcp_seq, tcp_seq, int)); diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 651e166..5ddcecc 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -512,9 +512,9 @@ udp_ctlinput(cmd, sa, vip) if (ip) { uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, - cmd, udp_notify); + cmd, udp_notify, 0, 0); } else - in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); + in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify, 0, 0); } static int |