Пример #1
0
int
tcp_rcv(PACKET pkt)     /* NOTE: pkt has nb_prot pointing to IP header */
{
   struct mbuf *  m_in;
   struct ip * bip;  /* IP header, berkeley version */
   struct tcphdr *   tcpp;
   unshort  len;  /* scratch length holder */

   /* For TCP, the netport IP layer is modified to set nb_prot to the 
    * start of the IP header (not TCP). We need to do some further
    * mods which the BSD code expects:
    */
   bip = (struct ip *)pkt->nb_prot;    /* get ip header */
   len = ntohs(bip->ip_len);  /* get length in local endian */

   /* verify checksum of received packet */

   tcpp = (struct tcphdr *)ip_data(bip);
   if (tcp_cksum(bip) != tcpp->th_sum)
   {
      TCP_MIB_INC(tcpInErrs);    /* keep MIB stats */
      tcpstat.tcps_rcvbadsum++;  /* keep BSD stats */
      LOCK_NET_RESOURCE(FREEQ_RESID);
      pk_free(pkt);  /* punt packet */
      UNLOCK_NET_RESOURCE(FREEQ_RESID);
      return ENP_BAD_HEADER;
   }

   m_in = m_getnbuf(MT_RXDATA, 0);
   if (!m_in){
      LOCK_NET_RESOURCE(FREEQ_RESID);
      pk_free(pkt);
      UNLOCK_NET_RESOURCE(FREEQ_RESID);
      return ENP_RESOURCE;  
   }

   IN_PROFILER(PF_TCP, PF_ENTRY);      /* measure time in TCP */

   /* subtract IP header length from total IP packet length */
   len -= ((unshort)(bip->ip_ver_ihl & 0x0f) << 2);
   bip->ip_len = len;   /* put TCP length in struct for TCP code to use */

   /* set mbuf to point to start of IP header (not TCP) */
   m_in->pkt = pkt;
   m_in->m_data = pkt->nb_prot;
   m_in->m_len = pkt->nb_plen;
   m_in->m_base = pkt->nb_buff;     /* ??? */
   m_in->m_memsz = pkt->nb_blen;    /* ??? */

   tcp_input(m_in, pkt->net);

   IN_PROFILER(PF_TCP, PF_EXIT);      /* measure time in TCP */

   return 0;
}
Пример #2
0
void
tcp_respond(struct tcpcb    *tp,
            struct tcpiphdr *ti,
            tcp_seq          ack,
            tcp_seq          seq,
            int              flags,
            struct mbuf     *ti_mbuf)
{
   int              tlen;       /* tcp data len - 0 or 1 */
   int              domain;     /* AF_INET or AF_INET6 */
   int              win = 0;    /* window to use in sent packet */
   struct mbuf     *m;          /* mbuf to send */
   struct tcpiphdr *tmp_thdr;   /* scratch */

   if (tp)
      win = (int)sbspace(&tp->t_inpcb->inp_socket->so_rcv);

   /* Figure out of we can recycle the passed buffer or if we need a 
    * new one. Construct the easy parts of the the TCP and IP headers.
    */
   if (flags == 0)   /* sending keepalive from timer */
   {
      /* no flags == need a new buffer */
      m = m_getwithdata (MT_HEADER, HDRSLEN);
      if (m == NULL)
         return;
      tlen = 1;   /* Keepalives have one byte of data */
      m->m_len = TCPIPHDRSZ + tlen;
      /*
       * Copy template contents into the mbuf and set ti to point
       * to the header structure in the mbuf.
       */
      tmp_thdr = (struct tcpiphdr *)((char *)m->m_data + sizeof(struct ip)
                                             - sizeof(struct ipovly));
      if ((char *)tmp_thdr < m->pkt->nb_buff)
      {
         panic("tcp_respond- packet ptr underflow\n");
      }
      MEMCPY(tmp_thdr, ti, sizeof(struct tcpiphdr));
      ti = tmp_thdr;
      flags = TH_ACK;
      domain = tp->t_inpcb->inp_socket->so_domain;
   }
   else        /* Flag was passed (e.g. reset); recycle passed mbuf */
   {
      m = ti_mbuf;   /*dtom(ti);*/
      if (m->pkt->type == IPTP)   /* IPv4 packet */
         domain = AF_INET;
      else
         domain = AF_INET6;

      M_FREEM(m->m_next);
      m->m_next = 0;
      tlen = 0;         /* NO data */
      m->m_len = TCPIPHDRSZ;
      xchg(ti->ti_dport, ti->ti_sport, u_short);
      if (m->pkt->type == IPTP)
         xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long);
      if (flags & TH_RST)  /* count resets in MIB */
         TCP_MIB_INC(tcpOutRsts);   /* keep MIB stats */
   }

   /* finish constructing the TCP header */
   ti->ti_seq = htonl(seq);
   ti->ti_ack = htonl(ack);
   ti->ti_t.th_doff = 0x50;      /* NetPort: init data offset bits */
   ti->ti_flags = (u_char)flags;
   ti->ti_win = htons((u_short)win);
   ti->ti_urp = 0;
   ti->ti_t.th_sum = 0;

   /* Finish constructing IP header and send, based on IP type in use */
   switch(domain)
   {
#ifdef IP_V4
      case AF_INET:
      {
         struct ip *pip;

         pip = (struct ip *)((char *)ti + sizeof(struct ipovly)
                                     - sizeof(struct ip));

         m->pkt->nb_tlen = m->pkt->nb_plen = pip->ip_len = (unshort)(TCPIPHDRSZ + tlen);
         
         /* If our system's max. MAC header size is geater than the size 
          * of the MAC header in the received packet then we need to 
          * adjust the IP header offset to allow for this. Since the packets 
          * are only headers they should always fit.
          */
         if (pip >= (struct ip *)(m->pkt->nb_buff + MaxLnh))
         {
            /* headers will fit, just set pointer */
            m->m_data = m->pkt->nb_prot = (char *)pip; 
         }
         else     /* MAC may not fit, adjust pointer and move headers back */
         {
            m->m_data = m->pkt->nb_prot = m->pkt->nb_buff + MaxLnh;  /* new ptr */
            MEMMOVE(m->m_data, pip, TCPIPHDRSZ);  /* move back tcp/ip headers */
         }
#ifdef DOS_SYN        
         if (!tp)
         {
            /* In the case of a SYN DOS attack, many RST|ACK replies
             * have no tp structure and need to be freed.
             */
            M_FREEM(m);
         }
         else
#endif
         {
            struct ip_socopts *sopts;

            int ret;
            
            if (tp && tp->t_inpcb && tp->t_inpcb->inp_socket)
            {
               sopts = tp->t_inpcb->inp_socket->so_optsPack;
            }
            else
               sopts = (struct ip_socopts *)NULL;
            ret = ip_output(m, sopts);
         }
         break;
      }
#endif   /* IP_V4 */
#ifdef IP_V6
      case AF_INET6:
      {
         struct ipv6 *  pip6;
         struct mbuf *  ip_m;     /* IP header's mbuf */

         /* Get mbuf space for the IP header. mbuf m shold contain the
          * TCP header somewhere, so set m_dsata to that and try to prepend 
          * an IPv6 header.
          */
         m->m_data = (char *)&ti->ti_t;    /* TCP header */
         m->m_len = sizeof(struct tcphdr);
         ip_m = mbuf_prepend(m, sizeof(struct ipv6));
         if (!ip_m)
         {
            m_free(m);
            return;
         }
         pip6 = (struct ipv6 *)ip_m->m_data;

         /* we have to find the IPv6 addresses. If a packet was passed
          * then get them form that, otherwise get them from the passed tp.
          * we should always have one or the other.
          */
         if (ti_mbuf)
         {
            ip6_addr     tmp;
            struct ipv6 *inpip = ti_mbuf->pkt->ip6_hdr;

            /* pip6 and inpip may be the same, so swap the IP addresses
             * through a tmp variable.
             */
            IP6CPY(&tmp, &inpip->ip_src);
            IP6CPY(&pip6->ip_src, &inpip->ip_dest);
            IP6CPY(&pip6->ip_dest, &tmp);
         }
         else if (tp)
         {
            struct inpcb *inp = tp->t_inpcb;

            IP6CPY(&pip6->ip_src, &inp->ip6_laddr);
            IP6CPY(&pip6->ip_dest, &inp->ip6_faddr);
         }
         else
         {
            dtrap();
            break;
         }
         /* best effort send */
         /* send down to glue layer to IPv6 */
         /* and don't forget the so_optsPack */
 #ifdef DOS_SYN        
         if (!tp)
         {
            /* In the case of a SYN DOS attack, many RST|ACK replies
             * have no tp structure and need to be freed.
             */
            M_FREEM(m);
         }
         else
#endif  /* DOS_SYN  */
         {
            struct ip_socopts *sopts;
            int ret;
            
            if (tp && tp->t_inpcb && tp->t_inpcb->inp_socket)
               sopts = tp->t_inpcb->inp_socket->so_optsPack;
            else
               sopts = (struct ip_socopts *)NULL;

            ret = tcp6_send(tp, ip_m, &ti->ti_t, 
                            sizeof(struct ipv6) + sizeof(struct tcphdr) + tlen,
                            sopts);
         }
         break;
      }
#endif   /* IP_V6 */
      default:
         dtrap();
         break;
   }
   return;
}
Пример #3
0
int
tcp_usrreq(struct socket * so, 
   struct mbuf *  m,
   struct mbuf *  nam)
{
   struct inpcb * inp;
   struct tcpcb * tp;
   int   error =  0;
   int   req;

#ifdef DO_TCPTRACE
   int   ostate;
#endif

   req = so->so_req;    /* get request from socket struct */
   inp = sotoinpcb(so);
   /*
    * When a TCP is attached to a socket, then there will be
    * a (struct inpcb) pointed at by the socket, and this
    * structure will point at a subsidary (struct tcpcb).
    */
   if (inp == 0 && req != PRU_ATTACH) 
   {
      return (EINVAL);
   }

   if (inp)
      tp = intotcpcb(inp);
   else  /* inp and tp not set, make sure this is OK: */
   { 
      if (req == PRU_ATTACH)
         tp = NULL;  /* stifle compiler warnings about using unassigned tp*/
      else
      {
         dtrap(); /* programming error? */
         return EINVAL;
      }
   }

   switch (req) 
   {
   /*
    * TCP attaches to socket via PRU_ATTACH, reserving space,
    * and an internet control block.
    */
   case PRU_ATTACH:
      if (inp) 
      {
         error = EISCONN;
         break;
      }
      error = tcp_attach(so);
      if (error)
         break;
      if ((so->so_options & SO_LINGER) && so->so_linger == 0)
         so->so_linger = TCP_LINGERTIME;
#ifdef   DO_TCPTRACE
      SETTP(tp, sototcpcb(so));
#endif
      break;

   /*
    * PRU_DETACH detaches the TCP protocol from the socket.
    * If the protocol state is non-embryonic, then can't
    * do this directly: have to initiate a PRU_DISCONNECT,
    * which may finish later; embryonic TCB's can just
    * be discarded here.
    */
   case PRU_DETACH:
      if (tp->t_state > TCPS_LISTEN)
         SETTP(tp, tcp_disconnect(tp));
      else
         SETTP(tp, tcp_close(tp));
      break;

   /*
    * Give the socket an address.
    */
   case PRU_BIND:

      /* bind is quite different for IPv4 and v6, so we use two 
       * seperate pcbbind routines. so_domain was checked for 
       * validity way up in t_bind()
       */
#ifdef IP_V4
      if(inp->inp_socket->so_domain == AF_INET)
      {
         error = in_pcbbind(inp, nam);
         break;
      }
#endif /* IP_V4 */
#ifdef IP_V6
      if(inp->inp_socket->so_domain == AF_INET6)
      {
         error = ip6_pcbbind(inp, nam);
         break;
      }
#endif /* IP_V6 */
      dtrap();    /* not v4 or v6? */
      error = EINVAL;
      break;
   /*
    * Prepare to accept connections.
    */
   case PRU_LISTEN:
      if (inp->inp_lport == 0)
         error = in_pcbbind(inp, (struct mbuf *)0);
      if (error == 0)
         tp->t_state = TCPS_LISTEN;
      break;

   /*
    * Initiate connection to peer.
    * Create a template for use in transmissions on this connection.
    * Enter SYN_SENT state, and mark socket as connecting.
    * Start keep-alive timer, and seed output sequence space.
    * Send initial segment on connection.
    */
   case PRU_CONNECT:
      if (inp->inp_lport == 0) 
      {

#ifdef IP_V4
#ifndef IP_V6  /* v4 only */
      error = in_pcbbind(inp, (struct mbuf *)0);
#else    /* dual mode */
      if(so->so_domain == AF_INET)
         error = in_pcbbind(inp, (struct mbuf *)0);
      else
         error = ip6_pcbbind(inp, (struct mbuf *)0);
#endif   /* end dual mode code */
#else    /* no v4, v6 only */
      error = ip6_pcbbind(inp, (struct mbuf *)0);
#endif   /* end v6 only */

         if (error)
            break;
      }

#ifdef IP_V4
#ifndef IP_V6  /* v4 only */
      error = in_pcbconnect(inp, nam);
#else    /* dual mode */
      if(so->so_domain == AF_INET)
         error = in_pcbconnect(inp, nam);
      else
         error = ip6_pcbconnect(inp, nam);
#endif   /* end dual mode code */
#else    /* no v4, v6 only */
      error = ip6_pcbconnect(inp, nam);
#endif   /* end v6 only */

      if (error)
         break;
      tp->t_template = tcp_template(tp);
      if (tp->t_template == 0) 
      {

#ifdef IP_V4
#ifndef IP_V6  /* v4 only */
         in_pcbdisconnect(inp);
#else    /* dual mode */
         if(so->so_domain == AF_INET)
            in_pcbdisconnect(inp);
         else
            ip6_pcbdisconnect(inp);
#endif   /* end dual mode code */
#else    /* no v4, v6 only */
         ip6_pcbdisconnect(inp);
#endif   /* end v6 only */

         error = ENOBUFS;
         break;
      }

      soisconnecting(so);
      tcpstat.tcps_connattempt++;
      tp->t_state = TCPS_SYN_SENT;
      tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
      tp->iss = tcp_iss; 
      tcp_iss += (tcp_seq)(TCP_ISSINCR/2);
      tcp_sendseqinit(tp);
      error = tcp_output(tp);
      if (!error)
         TCP_MIB_INC(tcpActiveOpens);     /* keep MIB stats */
      break;

   /*
    * Create a TCP connection between two sockets.
    */
   case PRU_CONNECT2:
      error = EOPNOTSUPP;
      break;

   /*
    * Initiate disconnect from peer.
    * If connection never passed embryonic stage, just drop;
    * else if don't need to let data drain, then can just drop anyways,
    * else have to begin TCP shutdown process: mark socket disconnecting,
    * drain unread data, state switch to reflect user close, and
    * send segment (e.g. FIN) to peer.  Socket will be really disconnected
    * when peer sends FIN and acks ours.
    *
    * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
    */
   case PRU_DISCONNECT:
      SETTP(tp, tcp_disconnect(tp));
      break;

   /*
    * Accept a connection.  Essentially all the work is
    * done at higher levels; just return the address
    * of the peer, storing through addr.
    */
   case PRU_ACCEPT: 
   {
         struct sockaddr_in * sin   =  mtod(nam,   struct sockaddr_in *);
#ifdef IP_V6
         struct sockaddr_in6 * sin6 = mtod(nam,   struct sockaddr_in6 *);
#endif

#ifdef IP_V6
         if (so->so_domain == AF_INET6)
         {
            nam->m_len = sizeof (struct sockaddr_in6);
            sin6->sin6_port = inp->inp_fport;
            sin6->sin6_family = AF_INET6;
            IP6CPY(&sin6->sin6_addr, &inp->ip6_faddr);
         }
#endif

#ifdef IP_V4
         if (so->so_domain == AF_INET)
         {
            nam->m_len = sizeof (struct sockaddr_in);
            sin->sin_family = AF_INET;
            sin->sin_port = inp->inp_fport;
            sin->sin_addr = inp->inp_faddr;
         }
#endif
         if ( !(so->so_domain == AF_INET) &&
              !(so->so_domain == AF_INET6)
             )
         {
            dprintf("*** PRU_ACCEPT bad domain = %d\n", so->so_domain);
            dtrap();
         } 
         TCP_MIB_INC(tcpPassiveOpens);    /* keep MIB stats */
         break;
      }

   /*
    * Mark the connection as being incapable of further output.
    */
   case PRU_SHUTDOWN:
      socantsendmore(so);
      tp = tcp_usrclosed(tp);
      if (tp)
         error = tcp_output(tp);
      break;

   /*
    * After a receive, possibly send window update to peer.
    */
   case PRU_RCVD:
      (void) tcp_output(tp);
      break;

   /*
    * Do a send by putting data in output queue and updating urgent
    * marker if URG set.  Possibly send more data.
    */
   case PRU_SEND:
      if (so->so_pcb == NULL)
      {                    /* Return EPIPE error if socket is not connected */
         error = EPIPE;
         break;
      }
      sbappend(&so->so_snd, m);
      error = tcp_output(tp);
      if (error == ENOBUFS)
         sbdropend(&so->so_snd,m);  /* Remove data from socket buffer */
      break;

   /*
    * Abort the TCP.
    */
   case PRU_ABORT:
      SETTP(tp, tcp_drop(tp, ECONNABORTED));
      break;

   case PRU_SENSE:
      /*      ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat; */
      dtrap();    /* does this ever happen? */
      return (0);

   case PRU_RCVOOB:
      if ((so->so_oobmark == 0 &&
          (so->so_state & SS_RCVATMARK) == 0) ||
#ifdef SO_OOBINLINE
       so->so_options & SO_OOBINLINE ||
#endif
       tp->t_oobflags & TCPOOB_HADDATA) 
       {
         error = EINVAL;
         break;
      }
      if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) 
      {
         error = EWOULDBLOCK;
         break;
      }
      m->m_len = 1;
      *mtod(m, char *) = tp->t_iobc;
      if ((MBUF2LONG(nam) & MSG_PEEK) == 0)
         tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
      break;

   case PRU_SENDOOB:
      if (so->so_pcb == NULL)
      {                    /* Return EPIPE error if socket is not connected */
         error = EPIPE;
         break;
      }
      if (sbspace(&so->so_snd) == 0) 
      {
         m_freem(m);
         error = ENOBUFS;
         break;
      }
      /*
       * According to RFC961 (Assigned Protocols),
       * the urgent pointer points to the last octet
       * of urgent data.  We continue, however,
       * to consider it to indicate the first octet
       * of data past the urgent section.
       * Otherwise, snd_up should be one lower.
       */
      sbappend(&so->so_snd, m);
      tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
      tp->t_force = 1;
      error = tcp_output(tp);
      if (error == ENOBUFS)
         sbdropend(&so->so_snd,m);  /* Remove data from socket buffer */
      tp->t_force = 0;
      break;

   case PRU_SOCKADDR:

   /* sockaddr and peeraddr have to switch based on IP type */
#ifdef IP_V4
#ifndef IP_V6  /* v4 only */
      in_setsockaddr(inp, nam);
#else /* dual mode */
      if(so->so_domain == AF_INET6)
         ip6_setsockaddr(inp, nam);
      else
         in_setsockaddr(inp, nam);
#endif   /* dual mode */
#else    /* IP_V6 */
         ip6_setsockaddr(inp, nam);
#endif
      break;         

   case PRU_PEERADDR:
#ifdef IP_V4
#ifndef IP_V6  /* v4 only */
      in_setpeeraddr(inp, nam);
#else /* dual mode */
      if(so->so_domain == AF_INET6)
         ip6_setpeeraddr(inp, nam);
      else
         in_setpeeraddr(inp, nam);
#endif   /* dual mode */
#else    /* IP_V6 */
         ip6_setpeeraddr(inp, nam);
#endif
      break;

   case PRU_SLOWTIMO:
      SETTP(tp, tcp_timers(tp, (int)MBUF2LONG(nam)));
#ifdef DO_TCPTRACE
      req |= (long)nam << 8;        /* for debug's sake */
#endif
      break;

      default:
      panic("tcp_usrreq");
   }
#ifdef DO_TCPTRACE
   if (tp && (so->so_options & SO_DEBUG))
      tcp_trace("usrreq: state: %d, tcpcb: %x, req: %d",
    ostate, tp, req);
#endif
   return (error);
}