Date: Sun, 25 Feb 2001 19:01:23 +0100 From: Jesper Skriver <jesper@FreeBSD.org> To: audit@FreeBSD.org, Jonathan Lemon <jlemon@FreeBSD.org> Subject: please review: MFC of "react to ICMP unreachables" Message-ID: <20010225190123.A33539@skriver.dk>
next in thread | raw e-mail | index | archive | help
The below is a combination of what PHK originally committed (based on my PR's), my and jlemon's later extensions. I hope to get this in before 4.3-RELEASE Suggestion to commit message MFC: src/sys/sys/protosw.h: rev 1.32 src/sys/netinet/ip_icmp.c: rev 1.48, 1.52 & 1.53 src/sys/netinet/ip_input.c: rev 1.155 src/sys/netinet/tcp_subr.c: rev 1.95, 1.86, 1.88 & 1.92 src/sys/netinet/tcp_var.h: rev 1.62 & 1.63 src/sys/netinet/udp_usrreq.c: rev 1.80 src/sys/netinet/in_pcb.c: rev 1.70, 1.71 & 1.76 src/sys/netinet/in_pcb.h: rev 1.35 Allow ICMP unreachables which map into PRC_UNREACH_ADMIN_PROHIB to reset TCP connections which are in the SYN_SENT state, if the sequence number in the echoed ICMP reply is correct. This behavior can be controlled by the sysctl net.inet.tcp.icmp_may_rst. Currently, only subtypes 2,3,10,11,12 are treated as such (port, protocol and administrative unreachables). Assocaiate an error code with these resets which is reported to the user application: ENETRESET. /Jesper -- Jesper Skriver, jesper(at)skriver(dot)dk - CCIE #5456 Work: Network manager @ AS3292 (Tele Danmark DataNetworks) Private: FreeBSD committer @ AS2109 (A much smaller network ;-) One Unix to rule them all, One Resolver to find them, One IP to bring them all and in the zone to bind them. Index: src/sys/netinet/in_pcb.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/in_pcb.c,v retrieving revision 1.59.2.6 diff -u -r1.59.2.6 in_pcb.c --- src/sys/netinet/in_pcb.c 2001/02/24 18:36:01 1.59.2.6 +++ src/sys/netinet/in_pcb.c 2001/02/25 16:48:53 @@ -62,6 +62,8 @@ #include <netinet/in_pcb.h> #include <netinet/in_var.h> #include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/tcp_var.h> #ifdef INET6 #include <netinet/ip6.h> #include <netinet6/ip6_var.h> @@ -642,15 +644,20 @@ * 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; @@ -679,6 +686,17 @@ 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)) + break; oinp = inp; inp = inp->inp_list.le_next; if (notify) Index: src/sys/netinet/in_pcb.h =================================================================== RCS file: /home/ncvs/src/sys/netinet/in_pcb.h,v retrieving revision 1.32.2.1 diff -u -r1.32.2.1 in_pcb.h --- src/sys/netinet/in_pcb.h 2001/02/24 18:36:01 1.32.2.1 +++ src/sys/netinet/in_pcb.h 2001/02/25 16:53:39 @@ -291,7 +291,8 @@ 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_pcbnotifyall __P((struct inpcbhead *, struct sockaddr *, int, void (*)(struct inpcb *, int))); void in_pcbrehash __P((struct inpcb *)); Index: src/sys/netinet/ip_icmp.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/ip_icmp.c,v retrieving revision 1.39.2.4 diff -u -r1.39.2.4 ip_icmp.c --- src/sys/netinet/ip_icmp.c 2001/02/24 21:35:18 1.39.2.4 +++ src/sys/netinet/ip_icmp.c 2001/02/25 17:10:29 @@ -324,33 +324,34 @@ switch (code) { case ICMP_UNREACH_NET: case ICMP_UNREACH_HOST: - case ICMP_UNREACH_PROTOCOL: - case ICMP_UNREACH_PORT: case ICMP_UNREACH_SRCFAIL: - code += PRC_UNREACH_NET; + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_TOSNET: + case ICMP_UNREACH_TOSHOST: + case ICMP_UNREACH_HOST_PRECEDENCE: + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + code = PRC_UNREACH_NET; break; case ICMP_UNREACH_NEEDFRAG: code = PRC_MSGSIZE; break; - case ICMP_UNREACH_NET_UNKNOWN: - case ICMP_UNREACH_NET_PROHIB: - case ICMP_UNREACH_TOSNET: - code = PRC_UNREACH_NET; + /* + * RFC 1122, Sections 3.2.2.1 and 4.2.3.9. + * Treat subcodes 2,3 as immediate RST + */ + case ICMP_UNREACH_PROTOCOL: + case ICMP_UNREACH_PORT: + code = PRC_UNREACH_ADMIN_PROHIB; break; - case ICMP_UNREACH_HOST_UNKNOWN: - case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_NET_PROHIB: case ICMP_UNREACH_HOST_PROHIB: - case ICMP_UNREACH_TOSHOST: - code = PRC_UNREACH_HOST; - break; - case ICMP_UNREACH_FILTER_PROHIB: - case ICMP_UNREACH_HOST_PRECEDENCE: - case ICMP_UNREACH_PRECEDENCE_CUTOFF: - code = PRC_UNREACH_PORT; + code = PRC_UNREACH_ADMIN_PROHIB; break; default: Index: src/sys/netinet/ip_input.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v retrieving revision 1.130.2.13 diff -u -r1.130.2.13 ip_input.c --- src/sys/netinet/ip_input.c 2001/02/07 01:03:13 1.130.2.13 +++ src/sys/netinet/ip_input.c 2001/02/25 17:10:29 @@ -1427,7 +1427,7 @@ EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, EMSGSIZE, EHOSTUNREACH, 0, 0, 0, 0, 0, 0, - ENOPROTOOPT + ENOPROTOOPT, ENETRESET }; /* Index: src/sys/netinet/tcp_subr.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/tcp_subr.c,v retrieving revision 1.73.2.7 diff -u -r1.73.2.7 tcp_subr.c --- src/sys/netinet/tcp_subr.c 2001/02/24 18:36:01 1.73.2.7 +++ src/sys/netinet/tcp_subr.c 2001/02/25 17:10:30 @@ -135,6 +135,10 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD, &tcbinfo.ipi_count, 0, "Number of active PCBs"); +static int icmp_may_rst = 1; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_may_rst, CTLFLAG_RW, &icmp_may_rst, 0, + "Certain ICMP unreachable messages may abort connections in SYN_SENT"); + static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); @@ -956,13 +960,18 @@ struct sockaddr *sa; void *vip; { - register struct ip *ip = vip; - register struct tcphdr *th; + struct ip *ip = vip; + 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 (cmd == PRC_MSGSIZE) + else if (icmp_may_rst && cmd == PRC_UNREACH_ADMIN_PROHIB && ip) { + tcp_seq_check = 1; + notify = tcp_drop_syn_sent; + } else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (PRC_IS_REDIRECT(cmd)) { /* @@ -981,8 +990,10 @@ 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_pcbnotifyall(&tcb, sa, cmd, notify); } @@ -1071,6 +1082,30 @@ #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. */ @@ -1083,6 +1118,22 @@ if (tp) tp->snd_cwnd = tp->t_maxseg; +} + +/* + * When a specific ICMP unreachable message is received and the + * connection state is SYN-SENT, drop the connection. This behavior + * is controlled by the icmp_may_rst sysctl. + */ +void +tcp_drop_syn_sent(inp, errno) + struct inpcb *inp; + int errno; +{ + struct tcpcb *tp = intotcpcb(inp); + + if (tp && tp->t_state == TCPS_SYN_SENT) + tcp_drop(tp, errno); } /* Index: src/sys/netinet/tcp_var.h =================================================================== RCS file: /home/ncvs/src/sys/netinet/tcp_var.h,v retrieving revision 1.56.2.2 diff -u -r1.56.2.2 tcp_var.h --- src/sys/netinet/tcp_var.h 2000/08/16 06:14:23 1.56.2.2 +++ src/sys/netinet/tcp_var.h 2001/02/25 17:21:49 @@ -384,10 +384,12 @@ void tcp_input __P((struct mbuf *, int, int)); void tcp_mss __P((struct tcpcb *, int)); int tcp_mssopt __P((struct tcpcb *)); +void tcp_drop_syn_sent __P((struct inpcb *, int)); 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)); Index: src/sys/netinet/udp_usrreq.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/udp_usrreq.c,v retrieving revision 1.64.2.7 diff -u -r1.64.2.7 udp_usrreq.c --- src/sys/netinet/udp_usrreq.c 2001/02/24 21:35:18 1.64.2.7 +++ src/sys/netinet/udp_usrreq.c 2001/02/25 16:52:45 @@ -526,7 +526,7 @@ 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, notify); + cmd, notify, 0, 0); } else in_pcbnotifyall(&udb, sa, cmd, notify); } Index: src/sys/sys/protosw.h =================================================================== RCS file: /home/ncvs/src/sys/sys/protosw.h,v retrieving revision 1.28 diff -u -r1.28 protosw.h --- src/sys/sys/protosw.h 1999/12/29 04:24:45 1.28 +++ src/sys/sys/protosw.h 2001/02/25 16:48:53 @@ -269,8 +269,9 @@ #define PRC_TIMXCEED_INTRANS 18 /* packet lifetime expired in transit */ #define PRC_TIMXCEED_REASS 19 /* lifetime expired on reass q */ #define PRC_PARAMPROB 20 /* header incorrect */ +#define PRC_UNREACH_ADMIN_PROHIB 21 /* packet administrativly prohibited */ -#define PRC_NCMDS 21 +#define PRC_NCMDS 22 #define PRC_IS_REDIRECT(cmd) \ ((cmd) >= PRC_REDIRECT_NET && (cmd) <= PRC_REDIRECT_TOSHOST) @@ -282,7 +283,7 @@ "NET-UNREACH", "HOST-UNREACH", "PROTO-UNREACH", "PORT-UNREACH", "#12", "SRCFAIL-UNREACH", "NET-REDIRECT", "HOST-REDIRECT", "TOSNET-REDIRECT", "TOSHOST-REDIRECT", "TX-INTRANS", "TX-REASS", - "PARAMPROB" + "PARAMPROB", "ADMIN-UNREACH" }; #endif To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-audit" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010225190123.A33539>