more lint
[unix-history] / usr / src / sys / netinet / tcp_usrreq.c
index c0a06db..d91552b 100644 (file)
-/* tcp_usrreq.c 1.3 81/10/18 */
-#include "../h/param.h"
-#include "../bbnnet/net.h"
-#include "../bbnnet/tcp.h"
-#include "../bbnnet/ip.h"
-#include "../bbnnet/imp.h"
-#include "../bbnnet/ucb.h"
-#include "../bbnnet/fsm.h"
-#include "../bbnnet/tcp_pred.h"
-
-lis_cls(wp)             /* passive open (1) */
-struct work *wp;
-{
-       
-COUNT(LIS_CLS);
-       t_open(wp->w_tcb, PASSIVE);
-
-       return(LISTEN);
-}
+/* tcp_usrreq.c 1.29 81/11/16 */
 
 
-sys_cls(wp)             /* active open (6) */
-register struct work *wp;
+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/mbuf.h"
+#include "../h/socket.h"
+#include "../h/socketvar.h"
+#include "../h/protosw.h"
+#include "../net/inet.h"
+#include "../net/inet_host.h"
+#include "../net/inet_pcb.h"
+#include "../net/inet_systm.h"
+#include "../net/imp.h"
+#include "../net/ip.h"
+#include "../net/ip_var.h"
+#include "../net/tcp.h"
+#define TCPFSTAB
+#ifdef TCPDEBUG
+#define TCPSTATES
+#endif
+#include "../net/tcp_fsm.h"
+#include "../net/tcp_var.h"
+#include "/usr/include/errno.h"
+
+/*
+ * Tcp initialization
+ */
+tcp_init()
 {
 
 {
 
-COUNT(SYS_CLS);
-       t_open(wp->w_tcb, ACTIVE);
-       send_ctl(wp->w_tcb);            /* send SYN */
-
-       return(SYN_SENT);
+       tcp_iss = 1;            /* wrong */
+       tcb.inp_next = tcb.inp_prev = &tcb;
 }
 
 }
 
-cls_opn(wp)             /* close request before receiving foreign SYN (10) */
-struct work *wp;                           
+/*
+ * Tcp finite state machine entries for timer and user generated
+ * requests.  These routines raise the ipl to that of the network
+ * to prevent reentry.  In particluar, this requires that the software
+ * clock interrupt have lower priority than the network so that
+ * we can enter the network from timeout routines without improperly
+ * nesting the interrupt stack.
+ */
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+tcp_slowtimo()
 {
 {
-
-COUNT(CLS_OPN);
-       t_close(wp->w_tcb, UCLOSED);
-
-       return(CLOSED);
+       register struct inpcb *ip;
+       register struct tcpcb *tp;
+       int s = splnet();
+       register short *tmp;
+       register int i;
+COUNT(TCP_TIMEO);
+
+       /*
+        * Search through tcb's and update active timers.
+        */
+       for (ip = tcb.inp_next; ip != &tcb; ip = ip->inp_next) {
+               tp = intotcpcb(ip);
+               tmp = &tp->t_init;
+               for (i = 0; i < TNTIMERS; i++) {
+                       if (*tmp && --*tmp == 0)
+                               (void) tcp_usrreq(tp->t_inpcb->inp_socket,
+                                   PRU_SLOWTIMO, (struct mbuf *)0,
+                                   (caddr_t)i);
+                       tmp++;
+               }
+               tp->t_xmt++;
+       }
+       tcp_iss += ISSINCR/2;           /* increment iss */
+       splx(s);
 }
 
 }
 
-cl2_clw(wp)             /* close request after receiving foreign FIN (13) */
-struct work *wp;
+/*
+ * Cancel all timers for tcp tp.
+ */
+tcp_tcancel(tp)
+       struct tcpcb *tp;
 {
 {
-       register struct tcb *tp;
-
-COUNT(CL2_CLW);
-       tp = wp->w_tcb;
-
-       tp->snd_fin = TRUE;             /* send our own FIN */
-       send_ctl(tp);                   
-       tp->usr_closed = TRUE;
+       register short *tmp = &tp->t_init;
+       register int i;
 
 
-       return(CLOSING2);
+       for (i = 0; i < TNTIMERS; i++)
+               *tmp++ = 0;
 }
 
 }
 
-cls_rwt(wp)             /* rcv request after foreign close (20) */
-struct work *wp;
+/*
+ * Process a TCP user request for tcp tb.  If this is a send request
+ * then m is the mbuf chain of send data.  If this is a timer expiration
+ * (called from the software clock routine), then timertype tells which timer.
+ */
+tcp_usrreq(so, req, m, addr)
+       struct socket *so;
+       int req;
+       struct mbuf *m;
+       caddr_t addr;
 {
 {
-       register struct tcb *tp;
-
-COUNT(CLS_RWT);
-       tp = wp->w_tcb;
+       register struct inpcb *inp = sotoinpcb(so);
+       register struct tcpcb *tp;
+       int s = splnet();
+       register int nstate;
+#ifdef TCPDEBUG
+       struct tcp_debug tdb;
+#endif
+       int error = 0;
+COUNT(TCP_USRREQ);
+
+       /*
+        * Make sure attached.  If not,
+        * only PRU_ATTACH is valid.
+        */
+#ifdef TCPDEBUG
+       tdb.td_tod = 0;
+#endif
+       if (inp == 0) {
+               if (req != PRU_ATTACH) {
+                       splx(s);
+                       return (EINVAL);
+               }
+       } else {
+               tp = intotcpcb(inp);
+               nstate = tp->t_state;
+#ifdef KPROF
+               tcp_acounts[nstate][req]++;
+#endif
+#ifdef TCPDEBUG
+               if (((tp->t_socket->so_options & SO_DEBUG) || tcpconsdebug)) {
+                       tdb_setup(tp, (struct tcpiphdr *)0, req, &tdb);
+                       tdb.td_tim = timertype;
+               }
+#endif
+               tp->tc_flags &= ~TC_NET_KEEP;
+       }
 
 
-       present_data(tp);       /* present any remaining data */
+       switch (req) {
 
 
-       if (rcv_empty(tp)) {
-               t_close(tp, UCLOSED);
-               return(CLOSED);
-       } else
-               return(RCV_WAIT);
+       case PRU_ATTACH:
+               if (tp) {
+                       error = EISCONN;
+                       break;
+               }
+               tcp_attach(so);
+               tp = sototcpcb(so);
+               if (so->so_options & SO_ACCEPTCONN) {
+                       inp->inp_lhost = in_hostalloc(&n_lhost);        /*XXX*/
+                       inp->inp_lport = in_pcbgenport(&tcb);
+                       nstate = LISTEN;
+               } else
+                       nstate = CLOSED;
+               break;
 
 
-}
+       case PRU_DETACH:
+               tcp_detach(tp);
+               break;
 
 
-fw1_syr(wp)             /* close request on synced connection (24,25) */
-struct work *wp;
-{
-       register struct tcb *tp;
+       case PRU_CONNECT:
+               if (tp->t_state != 0 && tp->t_state != CLOSED)
+                       goto bad;
+               inp->inp_fhost = in_hosteval((struct inaddr *)addr, &error);
+               if (inp->inp_fhost == 0)
+                       break;
+               (void) tcp_sndctl(tp);
+               nstate = SYN_SENT;
+               soisconnecting(so);
+               break;
 
 
-COUNT(FW1_SYR);
-       tp = wp->w_tcb;
+       case PRU_DISCONNECT:
+               if ((tp->tc_flags & TC_FIN_RCVD) == 0)
+                       goto abort;
+               if (nstate < ESTAB)
+                       tcp_disconnect(tp);
+               else {
+                       tp->tc_flags |= TC_SND_FIN;
+                       (void) tcp_sndctl(tp);
+                       tp->tc_flags |= TC_USR_CLOSED;
+                       soisdisconnecting(so);
+               }
+               break;
 
 
-       tp->snd_fin = TRUE;                     /* send FIN */
-       send_ctl(tp);
-       tp->usr_closed = TRUE;
+       case PRU_FLUSH:
+               error = EOPNOTSUPP;
+               break;
 
 
-       return(FIN_W1);
-}
+       case PRU_SHUTDOWN:
+               switch (nstate) {
+
+               case LISTEN:
+               case SYN_SENT:
+                       nstate = CLOSED;
+                       break;
+
+               case SYN_RCVD:
+               case L_SYN_RCVD:
+               case ESTAB:     
+               case CLOSE_WAIT:
+                       tp->tc_flags |= TC_SND_FIN;
+                       (void) tcp_sndctl(tp);
+                       tp->tc_flags |= TC_USR_CLOSED;
+                       nstate = nstate != CLOSE_WAIT ? FIN_W1 : LAST_ACK;
+                       break;
+                       
+               case FIN_W1:
+               case FIN_W2:
+               case TIME_WAIT:
+               case CLOSING:
+               case LAST_ACK:
+               case RCV_WAIT:
+                       break;
+
+               default:
+                       goto bad;
+               }
+               break;
 
 
-sss_snd(wp)             /* send request on open connection (40,41) */
-struct work *wp;
-{
-       register struct tcb *tp;
-       register struct mbuf *m, *n;
-       register struct ucb *up;
-       register off;
-       sequence last;
+       case PRU_RCVD:
+               if (nstate < ESTAB || nstate == CLOSED)
+                       goto bad;
+               tcp_sndwin(tp);
+               if ((tp->tc_flags&TC_FIN_RCVD) &&
+                   (tp->tc_flags&TC_USR_CLOSED) == 0 &&
+                   rcv_empty(tp))
+                       error = ESHUTDOWN;
+               if (nstate == RCV_WAIT && rcv_empty(tp))
+                       nstate = CLOSED;
+               break;
 
 
-COUNT(SSS_SND);
-       tp = wp->w_tcb;
-       up = tp->t_ucb;
+       case PRU_SEND:
+               switch (nstate) {
+
+               case ESTAB:
+               case CLOSE_WAIT:
+                       tcp_usrsend(tp, m);
+                       break;
+               
+               default:
+                       if (nstate < ESTAB)
+                               goto bad;
+                       m_freem(m);
+                       error = ENOTCONN;
+                       break;
+               }
+               break;
 
 
-       last = tp->snd_off;
+abort:
+       case PRU_ABORT:
+               tcp_abort(tp);
+               nstate = CLOSED;
+               break;
 
 
-       /* count number of mbufs in send data */
+       case PRU_CONTROL:
+               error = EOPNOTSUPP;
+               break;
 
 
-       for (m = n = (struct mbuf *)wp->w_dat; m != NULL; m = m->m_next) {
-               up->uc_ssize++;
-               last += m->m_len;
-       }
+       case PRU_SLOWTIMO:
+               switch (nstate) {
 
 
-       /* find end of send buffer and append data */
+               case 0:
+               case CLOSED:
+               case LISTEN:
+                       goto bad;
 
 
-       if ((m = up->uc_sbuf) != NULL) {        /* something in send buffer */
-               while (m->m_next != NULL) {             /* find the end */
-                       m = m->m_next;
-                       last += m->m_len;
-               }
-               last += m->m_len;
-
-               /* if there's room in old buffer for new data, consolidate */
-
-               off = m->m_off + m->m_len;
-               while (n != NULL && (MSIZE - off) >= n->m_len) {
-                       bcopy((caddr_t)((int)n + n->m_off), 
-                             (caddr_t)((int)m + off), n->m_len);
-                       m->m_len += n->m_len;
-                       off += n->m_len;
-                       up->uc_ssize--;
-                       n = m_free(n);
+               default:
+                       nstate = tcp_timers(tp, (int)addr);
                }
                }
-               m->m_next = n;
-
-       } else                                  /* nothing in send buffer */
-               up->uc_sbuf = n;
+               break;
 
 
-       if (up->uc_flags & UEOL) {               /* set EOL */
-               tp->snd_end = last;
+       default:
+               panic("tcp_usrreq");
+       bad:
+               printf("tcp: bad state: tcb=%x state=%d input=%d\n",
+                   tp, tp->t_state, req);
+               nstate = EFAILEC;
+               break;
        }
        }
+#ifdef TCPDEBUG
+       if (tdb.td_tod)
+               tdb_stuff(&tdb, nstate);
+#endif
+       switch (nstate) {
+
+       case CLOSED:
+       case SAME:
+               break;
 
 
-       if (up->uc_flags & UURG) {              /* urgent data */
-               tp->snd_urp = last+1;
-               tp->snd_urg = TRUE;
-       } 
-
-       send(tp);
+       case EFAILEC:
+               if (m)
+                       m_freem(dtom(m));
+               break;
 
 
-       return(SAME);
+       default:
+               tp->t_state = nstate;
+               break;
+       }
+       splx(s);
+       return (error);
 }
 
 }
 
-sss_rcv(wp)             /* rcv request on open connection (42) */
-struct work *wp;
+tcp_attach(so)
+       struct socket *so;
 {
 {
-       register struct tcb *tp;
-
-COUNT(SSS_RCV);
-       tp = wp->w_tcb;
-
-       send_ctl(tp);                   /* send new window */
-       present_data(tp);
-
-       return(SAME);
+       register struct tcpcb *tp = sototcpcb(so);
+COUNT(TCP_ATTACH);
+
+       /*
+        * Make empty reassembly queue.
+        */
+       tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp;
+
+       /*
+        * Initialize sequence numbers and round trip retransmit timer.
+        */
+       tp->t_xmtime = T_REXMT;
+       tp->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi = tp->snd_una =
+           tp->iss = tcp_iss;
+       tp->snd_off = tp->iss + 1;
+       tcp_iss += (ISSINCR >> 1) + 1;
 }
 
 }
 
-cls_nsy(wp)                  /* abort request on unsynced connection (44) */
-struct work *wp;
+tcp_detach(tp)
+       struct tcpcb *tp;
 {
 {
+COUNT(TCP_DETACH);
 
 
-COUNT(CLS_NSY);
-       t_close(wp->w_tcb, UABORT);
-
-       return(CLOSED);
+       in_pcbfree(tp->t_inpcb);
+       (void) m_free(dtom(tp));
 }
 
 }
 
-cls_syn(wp)             /* abort request on synced connection (45) */
-struct work *wp;
+tcp_disconnect(tp)
+       register struct tcpcb *tp;
 {
 {
-       register struct tcb *tp;
-
-COUNT(CLS_SYN);
-       tp = wp->w_tcb;
-
-       tp->snd_rst = TRUE;            /* send reset */
-       send_null(tp);
-       t_close(tp, UABORT);
-
-       return(CLOSED);
+       register struct tcpiphdr *t;
+
+       tcp_tcancel(tp);
+       t = tp->seg_next;
+       for (; t != (struct tcpiphdr *)tp; t = (struct tcpiphdr *)t->ti_next)
+               m_freem(dtom(t));
+       tcp_drainunack(tp);
+       if (tp->t_template) {
+               (void) m_free(dtom(tp->t_template));
+               tp->t_template = 0;
+       }
+       in_pcbfree(tp->t_inpcb);
 }
 
 }
 
-cls_act(wp)             /* net closing open connection (47) */
-struct work *wp;
+tcp_abort(tp)
+       register struct tcpcb *tp;
 {
 
 {
 
-COUNT(CLS_ACT);
-       t_close(wp->w_tcb, UNETDWN);
+       switch (tp->t_state) {
 
 
-       return(CLOSED);
+       case SYN_RCVD:
+       case ESTAB:
+       case FIN_W1:
+       case FIN_W2:
+       case CLOSE_WAIT:
+               tp->tc_flags |= TC_SND_RST;
+               tcp_sndnull(tp);
+       }
+       soisdisconnected(tp->t_inpcb->inp_socket);
 }
 
 }
 
-cls_err(wp)             /* invalid user request in closing states */
-struct work *wp;
+/*
+/*###366 [cc] warning: struct/union or struct/union pointer required %%%*/
+/*###366 [cc] member of structure or union required %%%*/
+/*###366 [cc] tp_inpcb undefined %%%*/
+ * Send data queue headed by m0 into the protocol.
+ */
+tcp_usrsend(tp, m0)
+       register struct tcpcb *tp;
+       struct mbuf *m0;
 {
 {
-COUNT(CLS_ERR);
-       to_user(wp->w_tcb->t_ucb, UCLSERR);
-
-       return(SAME);
+       register struct socket *so = tp->t_inpcb->inp_socket;
+COUNT(TCP_USRSEND);
+
+       sbappend(&so->so_snd, m0);
+       if (tp->t_options & TO_EOL)
+               tp->snd_end = tp->snd_off + so->so_snd.sb_cc;
+       if (tp->t_options & TO_URG) {
+               tp->snd_urp = tp->snd_off + so->so_snd.sb_cc + 1;
+               tp->tc_flags |= TC_SND_URG;
+       }
+       (void) tcp_send(tp);
 }
 
 }
 
-timers(wp)              /* timer processor (14,17,34,35,36,37,38) */
-struct work *wp;
+/*
+ * TCP timer went off processing.
+ */
+tcp_timers(tp, timertype)
+       register struct tcpcb *tp;
+       int timertype;
 {
 {
-       register struct tcb *tp;
-       register type;
 
 
-COUNT(TIMERS);
-       tp = wp->w_tcb;
-       type = wp->w_stype;
-
-       switch (type) {
-
-       case TINIT:             /* initialization timer */
-
-               if (!tp->syn_acked) {           /* haven't got ACK of our SYN (35) */
-
-                       t_close(tp, UINTIMO);
-                       return(CLOSED);
+COUNT(TCP_TIMERS);
+       switch (timertype) {
+
+       case TFINACK:           /* fin-ack timer */
+               switch (tp->t_state) {
+
+               case TIME_WAIT:
+                       /*
+                        * We can be sure our ACK of foreign FIN was rcvd,
+                        * and can close if no data left for user.
+                        */
+                       if (rcv_empty(tp)) {
+                               tcp_disconnect(tp);
+                               return (CLOSED);
+                       }
+                       return (RCV_WAIT);                      /* 17 */
+
+               case CLOSING:
+                       tp->tc_flags |= TC_WAITED_2_ML;
+                       return (SAME);
+
+               default:
+                       return (SAME);
                }
                }
-               break;
-
-       case TFINACK:           /* fin-ack timer */   
-
-               if (tp->t_state == TIME_WAIT) {
-
-                       /* can be sure our ACK of for FIN was rcvd,
-                          can close if no data left for user */
-
-                       if (rcv_empty(tp)) {            /* 14 */
-                               t_close(tp, UCLOSED);
-                               return(CLOSED);
-                       } else                          /* 17 */
-                               return(RCV_WAIT);
-
-               } else if (tp->t_state == CLOSING1)     /* 37 */
-
-                       /* safe to close */
 
 
-                       tp->waited_2_ml = TRUE;
-
-               break;
-
-       case TREXMT:            /* retransmission timer */
-
-               /* set up for a retransmission, increase rexmt time
-                  in case of multiple retransmissions. */
+       case TREXMT:            /* retransmission timer */
+               if (tp->t_rexmt_val > tp->snd_una) {            /* 34 */
+                       /*
+                        * Set so for a retransmission, increase rexmt time
+                        * in case of multiple retransmissions.
+                        */
+                       tp->snd_nxt = tp->snd_una;
+                       tp->tc_flags |= TC_REXMT;
+                       tp->t_xmtime = tp->t_xmtime << 1;
+                       if (tp->t_xmtime > T_REMAX)
+                               tp->t_xmtime = T_REMAX;
+                       (void) tcp_send(tp);
+               }
+               return (SAME);
+
+       case TREXMTTL:          /* retransmit too long */
+               if (tp->t_rtl_val > tp->snd_una)                /* 36 */
+                       tcp_error(tp, EIO);             /* URXTIMO !?! */
+               /*
+                * If user has already closed, abort the connection.
+                */
+               if (tp->tc_flags & TC_USR_CLOSED) {
+                       tcp_abort(tp);
+                       return (CLOSED);
+               }
+               return (SAME);
+
+       case TPERSIST:          /* persist timer */
+               /*
+                * Force a byte send through closed window.
+                */
+               tp->tc_flags |= TC_FORCE_ONE;
+               (void) tcp_send(tp);
+               return (SAME);
+       }
+       panic("tcp_timers");
+       /*NOTREACHED*/
+}
 
 
-               if (tp->t_rexmt_val > tp->snd_una) {   /* 34 */
-                       tp->snd_nxt = tp->snd_una;
-                       tp->rexmt = TRUE;
-                       tp->t_xmtime = tp->t_xmtime << 1;                  
-                       if (tp->t_xmtime > T_REMAX)
-                               tp->t_xmtime = T_REMAX;
-                       send(tp);
-               }
-               break;
+/*ARGSUSED*/
+tcp_sense(m)
+       struct mbuf *m;
+{
 
 
-       case TREXMTTL:          /* retransmit too long */
+       return (EOPNOTSUPP);
+}
 
 
-               /* tell user */
+tcp_error(tp, errno)
+       struct tcpcb *tp;
+       int errno;
+{
+       struct socket *so = tp->t_inpcb->inp_socket;
+COUNT(TCP_ERROR);
 
 
-               if (tp->t_rtl_val > tp->snd_una)        /* 36 */
-                       to_user(tp->t_ucb, URXTIMO);
+       so->so_error = errno;
+       sorwakeup(so);
+       sowwakeup(so);
+}
 
 
-               /* if user has already closed, abort the connection */
+#ifdef TCPDEBUG
+/*
+ * TCP debugging utility subroutines.
+ * THE NAMES OF THE FIELDS USED BY THESE ROUTINES ARE STUPID.
+ */
+tdb_setup(tp, n, input, tdp)
+       struct tcpcb *tp;
+       register struct tcpiphdr *n;
+       int input;
+       register struct tcp_debug *tdp;
+{
 
 
-               if (tp->usr_closed) {
-                       t_close(tp, URXTIMO);
-                       return(CLOSED);
-               }
-               break;
+COUNT(TDB_SETUP);
+       tdp->td_tod = time;
+       tdp->td_tcb = tp;
+       tdp->td_old = tp->t_state;
+       tdp->td_inp = input;
+       tdp->td_tim = 0;
+       tdp->td_new = -1;
+       if (n) {
+               tdp->td_sno = n->ti_seq;
+               tdp->td_ano = n->ti_ackno;
+               tdp->td_wno = n->t_win;
+               tdp->td_lno = n->ti_len;
+               tdp->td_flg = n->ti_flags;
+       } else
+               tdp->td_sno = tdp->td_ano = tdp->td_wno = tdp->td_lno =
+                   tdp->td_flg = 0;
+}
 
 
-       case TPERSIST:          /* persist timer */
+tdb_stuff(tdp, nstate)
+       struct tcp_debug *tdp;
+       int nstate;
+{
+COUNT(TDB_STUFF);
 
 
-               /* force a byte send through closed window */
+       tdp->td_new = nstate;
+       tcp_debug[tdbx++ % TDBSIZE] = *tdp;
+       if (tcpconsdebug & 2)
+               tcp_prt(tdp);
+}
 
 
-               tp->force_one = TRUE;                   /* 38 */
-               send(tp);
-               break;
+tcp_prt(tdp)
+       register struct tcp_debug *tdp;
+{
+COUNT(TCP_PRT);
+
+       printf("%x ", ((int)tdp->td_tcb)&0xffffff);
+       if (tdp->td_inp == INSEND) {
+               printf("SEND #%x", tdp->td_sno);
+               tdp->td_lno = ntohs(tdp->td_lno);
+               tdp->td_wno = ntohs(tdp->td_wno);
+       } else {
+               if (tdp->td_inp == INRECV)
+                       printf("RCV #%x ", tdp->td_sno);
+               printf("%s.%s",
+                   tcpstates[tdp->td_old], tcpinputs[tdp->td_inp]);
+               if (tdp->td_inp == ISTIMER)
+                       printf("(%s)", tcptimers[tdp->td_tim]);
+               printf(" -> %s",
+                   tcpstates[(tdp->td_new > 0) ? tdp->td_new : tdp->td_old]);
+               if (tdp->td_new == -1)
+                       printf(" (FAILED)");
        }
        }
-
-       return(SAME);
+       /* GROSS... DEPENDS ON SIGN EXTENSION OF CHARACTERS */
+       if (tdp->td_lno)
+               printf(" len=%d", tdp->td_lno);
+       if (tdp->td_wno)
+               printf(" win=%d", tdp->td_wno);
+       if (tdp->td_flg & TH_FIN) printf(" FIN");
+       if (tdp->td_flg & TH_SYN) printf(" SYN");
+       if (tdp->td_flg & TH_RST) printf(" RST");
+       if (tdp->td_flg & TH_EOL) printf(" EOL");
+       if (tdp->td_flg & TH_ACK)  printf(" ACK %x", tdp->td_ano);
+       if (tdp->td_flg & TH_URG) printf(" URG");
+       printf("\n");
 }
 }
+#endif