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; }
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; }
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); }