On Wed, Dec 20, 2000 at 03:51:18PM +0100, Jesper Skriver wrote:
> On Wed, Dec 20, 2000 at 02:46:21AM -0800, Don Lewis wrote:
> > } +         /*
> > } +          * If tcp_sequence is set, then skip sessions where
> > } +          * the sequence number is not one of a unacknowledged
> > } +          * packet.
> > } +          */
> > } +         if ((tcp_sequence) && (tcp_seq_vs_sess(inp, tcp_sequence) == 0)) {
> > }                   inp = inp->inp_list.le_next;
> > }                   continue;
> > 
> > We should pass in an extra flag to indicate if tcp_sequence is valid, since
> > it can legally be zero.
> 
> Ack, will do.

Attached new diff (relative to -current) with this fix.

I think this should be committed, afterwards I'll look at the suggestions
for improvements.

/Jesper

-- 
Jesper Skriver, jesper(at)skriver(dot)dk  -  CCIE #5456
Work:    Network manager @ AS3292 (Tele Danmark DataNetworks)
Private: Geek            @ 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.
diff -ru src/sys/netinet.old/in_pcb.c src/sys/netinet/in_pcb.c
--- src/sys/netinet.old/in_pcb.c        Sun Dec 17 18:57:24 2000
+++ src/sys/netinet/in_pcb.c    Wed Dec 20 18:28:35 2000
@@ -665,15 +665,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;
@@ -714,6 +719,15 @@
                    (lport && inp->inp_lport != lport) ||
                    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
                    (fport && inp->inp_fport != fport)) {
+                       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 ((tcp_seq_check == 1) && (tcp_seq_vs_sess(inp, tcp_sequence) == 0)) 
+{
                        inp = inp->inp_list.le_next;
                        continue;
                }
diff -ru src/sys/netinet.old/in_pcb.h src/sys/netinet/in_pcb.h
--- src/sys/netinet.old/in_pcb.h        Sun Dec 17 18:57:24 2000
+++ src/sys/netinet/in_pcb.h    Wed Dec 20 17:33:34 2000
@@ -290,7 +290,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_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 -ru src/sys/netinet.old/tcp_subr.c src/sys/netinet/tcp_subr.c
--- src/sys/netinet.old/tcp_subr.c      Sun Dec 17 18:57:24 2000
+++ src/sys/netinet/tcp_subr.c  Wed Dec 20 18:34:36 2000
@@ -139,9 +139,20 @@
  * 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 @@
        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 @@
        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 @@
 #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 @@
 
 /*
  * 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 @@
        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 -ru src/sys/netinet.old/tcp_var.h src/sys/netinet/tcp_var.h
--- src/sys/netinet.old/tcp_var.h       Sun Dec 17 18:57:24 2000
+++ src/sys/netinet/tcp_var.h   Tue Dec 19 20:16:54 2000
@@ -392,6 +392,7 @@
 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 -ru src/sys/netinet.old/udp_usrreq.c src/sys/netinet/udp_usrreq.c
--- src/sys/netinet.old/udp_usrreq.c    Sun Dec 17 18:57:24 2000
+++ src/sys/netinet/udp_usrreq.c        Wed Dec 20 17:33:00 2000
@@ -512,9 +512,9 @@
        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

Reply via email to