int ip6_pcbbind( struct inpcb *inp, struct mbuf *nam) { struct socket *so = inp->inp_socket; struct inpcb *head = inp->inp_head; struct sockaddr_in6 *sin; u_short lport = 0; /* require that we not be bound already */ if ((inp->inp_lport) || (!IN6_IS_ADDR_UNSPECIFIED(&inp->ip6_laddr))) { return (0); } if (nam) { sin = mtod(nam, struct sockaddr_in6 *); if (IP6EQ(&sin->sin6_addr, &in6addr_any) == FALSE) /* not a wildcard? */ { /* make sure caller gave us a valid local address */ if (ip6_lookup(&sin->sin6_addr, NULL) == NULL) return (EADDRNOTAVAIL); } lport = sin->sin6_port; if (lport != 0) { int wild = 0; if ((so->so_options & SO_REUSEADDR) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) { wild = INPLOOKUP_WILDCARD; } if (ip6_pcblookup(head, NULL, 0, &sin->sin6_addr, lport, wild)) { return (EADDRINUSE); } } IP6CPY(&inp->ip6_laddr, &sin->sin6_addr); } /* if local port is unspecified, then find an unused value for it. */ if (lport == 0) { do { if ((head->inp_lport++ < IPPORT_RESERVED) || (head->inp_lport > IPPORT_USERRESERVED)) { head->inp_lport = IPPORT_RESERVED; } lport = htons(head->inp_lport); } while (ip6_pcblookup(head, NULL, 0, &inp->ip6_laddr, lport, 0)); } inp->inp_lport = lport; return (0); }
void ip6_setpeeraddr(struct inpcb *inp, struct mbuf *nam) { struct sockaddr_in6 *sin; nam->m_len = sizeof(*sin); sin = mtod(nam, struct sockaddr_in6 *); MEMSET(sin, 0, sizeof (*sin)); sin->sin6_family = AF_INET6; sin->sin6_port = inp->inp_fport; IP6CPY(&sin->sin6_addr, &inp->ip6_faddr); }
int tcp_output(struct tcpcb * tp) { struct socket * so = tp->t_inpcb->inp_socket; int len; long win; int off, flags, error; struct mbuf * m; struct tcpiphdr * ti; unsigned optlen = 0; int idle, sendalot; struct mbuf * sendm; /* mbuf which contains data to send */ struct mbuf * tcp_mbuf; /* mbuf containing TCP header */ int bufoff; /* offset of data in sendm->m_data */ #ifdef TCP_SACK int sack_resend; int sack_hole = 0; /* next sack hole to fill */ if(tp->t_flags & TF_SACKREPLY) { /* we are resending based on a received SACK header */ sack_resend = TRUE; tp->t_flags &= ~TF_SACKREPLY; /* clear flag */ } else sack_resend = FALSE; #endif /* TCP_SACK */ /* * Determine length of data that should be transmitted, * and flags that will be used. * If there is some data or critical controls (SYN, RST) * to send, then transmit; otherwise, investigate further. */ idle = (tp->snd_max == tp->snd_una); again: sendalot = 0; off = (int)(tp->snd_nxt - tp->snd_una); win = (long)tp->snd_wnd; /* set basic send window */ if (win > (long)tp->snd_cwnd) /* see if we need congestion control */ { win = (int)(tp->snd_cwnd & ~(ALIGN_TYPE-1)); /* keep data aligned */ } /* * If in persist timeout with window of 0, send 1 byte. * Otherwise, if window is small but nonzero * and timer expired, we will send what we can * and go to transmit state. */ if (tp->t_force) { if (win == 0) win = 1; else { tp->t_timer[TCPT_PERSIST] = 0; tp->t_rxtshift = 0; } } #ifdef TCP_SACK /* See if we need to adjust the offset for a sack resend */ if(sack_resend) { off = (int)(tp->sack_hole_start[sack_hole] - tp->snd_una); /* if this hole's already been acked then punt and move to next hole */ if(off < 0) { /* clear out the acked hole */ tp->sack_hole_start[sack_hole] = tp->sack_hole_end[sack_hole] = 0; /* see if we're done with SACK hole list (2 tests) */ if(++sack_hole >= SACK_BLOCKS) return 0; if(tp->sack_hole_start[sack_hole] == tp->sack_hole_end[sack_hole]) return 0; goto again; } tp->snd_nxt = tp->sack_hole_start[sack_hole]; len = (int)(tp->sack_hole_end[sack_hole] - tp->sack_hole_start[sack_hole]); len = (int)MIN(len, (int)win); } else #endif /* TCP_SACK */ { /* set length of packets which are not sack resends */ len = (int)MIN(so->so_snd.sb_cc, (unsigned)win) - off; } flags = tcp_outflags[tp->t_state]; /* See if we need to build TCP options field. This test should be fast. */ #if (defined(TCP_TIMESTAMP) | defined(TCP_SACK)) if((flags & TH_SYN) || /* !!!??? (so->so_options & SO_TIMESTAMP) || */ (tp->t_flags & TF_SACKNOW) ) { optlen = bld_options(tp, &tcp_optionbuf[optlen], flags, so); } #else /* If other options not defined this build then don't bother to call bld_options() except * on SYN packets */ if(flags & TH_SYN) { optlen = bld_options(tp, &tcp_optionbuf[optlen], flags, so); } #endif if (len < 0) { /* * If FIN has been sent but not acked, * but we haven't been called to retransmit, * len will be -1. Otherwise, window shrank * after we sent into it. If window shrank to 0, * cancel pending retransmit and pull snd_nxt * back to (closed) window. We will enter persist * state below. If the window didn't close completely, * just wait for an ACK. */ len = 0; if (win == 0) { tp->t_timer[TCPT_REXMT] = 0; tp->snd_nxt = tp->snd_una; } } if (len > (int)tp->t_maxseg) { len = tp->t_maxseg; sendalot = 1; } #ifdef IP_V4 #ifdef IP_PMTU { int pmtu = tp->t_inpcb->inp_pmtu - 40; if (len > pmtu) { len = pmtu - 40; sendalot = 1; } } #endif /* IP_PMTU */ /* We don't need a pmtu test for IPv6. V6 code limits t_maxseg to * the Path MTU, so the test above the v4 ifdef above covers us. */ #endif /* IP_V4 */ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc)) flags &= ~TH_FIN; win = (long)(sbspace(&so->so_rcv)); /* * If our state indicates that FIN should be sent * and we have not yet done so, or we're retransmitting the FIN, * then we need to send. */ if ((flags & TH_FIN) && (so->so_snd.sb_cc == 0) && ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) { goto send; } /* * Send if we owe peer an ACK. */ if (tp->t_flags & TF_ACKNOW) goto send; if (flags & (TH_SYN|TH_RST)) goto send; if (SEQ_GT(tp->snd_up, tp->snd_una)) goto send; /* * Sender silly window avoidance. If connection is idle * and can send all data, a maximum segment, * at least a maximum default-size segment do it, * or are forced, do it; otherwise don't bother. * If peer's buffer is tiny, then send * when window is at least half open. * If retransmitting (possibly after persist timer forced us * to send into a small window), then must resend. */ if (len) { if (len == (int)tp->t_maxseg) goto send; if ((idle || tp->t_flags & TF_NODELAY) && len + off >= (int)so->so_snd.sb_cc) { goto send; } if (tp->t_force) goto send; if (len >= (int)(tp->max_sndwnd / 2)) goto send; if (SEQ_LT(tp->snd_nxt, tp->snd_max)) goto send; } /* * Compare available window to amount of window * known to peer (as advertised window less * next expected input). If the difference is at least two * max size segments or at least 35% of the maximum possible * window, then want to send a window update to peer. */ if (win > 0) { int adv = (int)win - (int)(tp->rcv_adv - tp->rcv_nxt); if (so->so_rcv.sb_cc == 0 && adv >= (int)(tp->t_maxseg * 2)) goto send; if (100 * (u_int)adv / so->so_rcv.sb_hiwat >= 35) goto send; } /* * TCP window updates are not reliable, rather a polling protocol * using ``persist'' packets is used to insure receipt of window * updates. The three ``states'' for the output side are: * idle not doing retransmits or persists * persisting to move a small or zero window * (re)transmitting and thereby not persisting * * tp->t_timer[TCPT_PERSIST] * is set when we are in persist state. * tp->t_force * is set when we are called to send a persist packet. * tp->t_timer[TCPT_REXMT] * is set when we are retransmitting * The output side is idle when both timers are zero. * * If send window is too small, there is data to transmit, and no * retransmit or persist is pending, then go to persist state. * If nothing happens soon, send when timer expires: * if window is nonzero, transmit what we can, * otherwise force out a byte. */ if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 && tp->t_timer[TCPT_PERSIST] == 0) { tp->t_rxtshift = 0; tcp_setpersist(tp); } /* * No reason to send a segment, just return. */ return (0); send: ENTER_CRIT_SECTION(tp); /* Limit send length to the current buffer so as to * avoid doing the "mbuf shuffle" in m_copy(). */ bufoff = off; sendm = so->so_snd.sb_mb; if (len) { /* find mbuf containing data to send (at "off") */ while (sendm) /* loop through socket send list */ { bufoff -= sendm->m_len; if (bufoff < 0) /* if off is in this buffer, break */ break; sendm = sendm->m_next; } if (!sendm) { dtrap(); /* shouldn't happen */ } bufoff += sendm->m_len; /* index to next data to send in msend */ /* if socket has multiple unsent mbufs, set flag for send to loop */ if ((sendm->m_next) && (len > (int)sendm->m_len)) { flags &= ~TH_FIN; /* don't FIN on segment prior to last */ sendalot = 1; /* set to send more segments */ } if((flags & TH_FIN) && (so->so_snd.sb_cc > (unsigned)len)) { /* This can happen on slow links (PPP) which retry the last * segment - the one with the FIN bit attached to data. */ flags &= ~TH_FIN; /* don't FIN on segment prior to last */ } /* only send the rest of msend */ len = min(len, (int)sendm->m_len); /* if we're not sending starting at sendm->m_data (in which * case bufoff != 0), then we will copy the data; else we would * write IP/TCP headers over sent but un-ack'ed data in sendm. * Similarly, if sendm->m_data is not aligned with respect to * sendm->m_base and ALIGN_TYPE, we will copy the data to * ensure that it (and the then-prepended IP/TCP headers) will * be aligned according to ALIGN_TYPE. */ if ((bufoff != 0) || /* data not front aligned in send mbuf? */ (((sendm->m_data - sendm->m_base) & (ALIGN_TYPE - 1)) != 0)) { len = min(len, (int)(sendm->m_len - bufoff)); /* limit len again */ /* One more test - if this data is not aligned with the front * of the m_data buffer then we can't use it in place, else we * might write the IP/TCP header over data that has not yet * been acked. In this case we must make sure our send * fits into a little buffer and send what we can. */ if ((len > (int)(lilbufsiz - HDRSLEN)) && /* length is bigger the small buffer? */ (bigfreeq.q_len < 2)) /* and we are low on big buffers */ { len = lilbufsiz - HDRSLEN; } } } /* if send data is sufficiently aligned in packet, prepend TCP/IP header * in the space provided. */ if (len && (bufoff == 0) && (sendm->pkt->inuse == 1) && (((sendm->m_data - sendm->m_base) & (ALIGN_TYPE - 1)) == 0) && (optlen == 0)) { /* get an empty mbuf to "clone" the data */ m = m_getnbuf(MT_TXDATA, 0); if (!m) { EXIT_CRIT_SECTION(tp); return (ENOBUFS); } m->pkt = sendm->pkt; /* copy packet location in new mbuf */ m->pkt->inuse++; /* bump packet's use count */ m->m_base = sendm->m_base; /* clone mbuf members */ m->m_memsz = sendm->m_memsz; m->m_len = len + TCPIPHDRSZ; /* adjust clone for header */ m->m_data = sendm->m_data - TCPIPHDRSZ; } else /* either no data or data is not front aligned in mbuf */ { /* Grab a header mbuf, attaching a copy of data to be * transmitted, and initialize the header from * the template for sends on this connection. */ m = m_getwithdata (MT_HEADER, IFNETHDR_SIZE + TCPIPHDRSZ); if (m ==(struct mbuf *)NULL) { EXIT_CRIT_SECTION(tp); return ENOBUFS; } m->m_len = TCPIPHDRSZ; m->m_data += IFNETHDR_SIZE;/* Move this to sizeof tcpip hdr leave*/ /* 14 bytes for ethernet header */ if (len) /* attach any data to send */ { m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len); if (m->m_next == 0) { m_freem(m); EXIT_CRIT_SECTION(tp); return ENOBUFS; } } } EXIT_CRIT_SECTION(tp); if (len) { if (tp->t_force && len == 1) tcpstat.tcps_sndprobe++; else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) { tcpstat.tcps_sndrexmitpack++; tcpstat.tcps_sndrexmitbyte += len; #ifdef TCP_SACK if(sack_resend) tcpstat.tcps_sackresend++; #endif } else { tcpstat.tcps_sndpack++; tcpstat.tcps_sndbyte += len; } } else if (tp->t_flags & TF_ACKNOW) { tcpstat.tcps_sndacks++; } else if (flags & (TH_SYN|TH_FIN|TH_RST)) tcpstat.tcps_sndctrl++; else if (SEQ_GT(tp->snd_up, tp->snd_una)) tcpstat.tcps_sndurg++; else tcpstat.tcps_sndwinup++; ti = (struct tcpiphdr *)(m->m_data+sizeof(struct ip)-sizeof(struct ipovly)); if ((char *)ti < m->pkt->nb_buff) { panic("tcp_out- packet ptr underflow\n"); } tcp_mbuf = m; /* flag TCP header mbuf */ #ifdef IP_V6 /* Dual mode code */ if(so->so_domain == AF_INET6) { m = mbuf_prepend(m, sizeof(struct ipv6)); if(m == NULL) { /* this can happen when we run out of mbufs or pkt buffers * That is, mfreeq is empty or (lilfreeq, bigfreeq) are empty. * One solution is to find out which one is getting full and * then increase them. */ dtrap(); /* This is really rare... */ m_freem(tcp_mbuf); /* Free TCP/data chain */ return ENOBUFS; } /* strip overlay from front of TCP header */ tcp_mbuf->m_data += sizeof(struct ipovly); tcp_mbuf->m_len -= sizeof(struct ipovly); } #endif /* end IP_V6 */ if (tp->t_template == 0) panic("tcp_output"); MEMCPY((char*)ti, (char*)tp->t_template, sizeof(struct tcpiphdr)); /* * Fill in fields, remembering maximum advertised * window for use in delaying messages about window sizes. * If resending a FIN, be sure not to use a new sequence number. */ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && tp->snd_nxt == tp->snd_max) { tp->snd_nxt--; } ti->ti_seq = htonl(tp->snd_nxt); ti->ti_ack = htonl(tp->rcv_nxt); /* * If we're sending a SYN, check the IP address of the interface * that we will (likely) use to send the IP datagram -- if it's * changed from what is in the template (as it might if this is * a retransmission, and the original SYN caused PPP to start * bringing the interface up, and PPP has got a new IP address * via IPCP), update the template and the inpcb with the new * address. */ if (flags & TH_SYN) { struct inpcb * inp; inp = (struct inpcb *)so->so_pcb; switch(so->so_domain) { #ifdef IP_V4 case AF_INET: { ip_addr src; #ifdef INCLUDE_PPP if(((flags & TH_ACK) == 0) && /* SYN only, not SYN/ACK */ (inp->ifp) && /* Make sure we have iface */ (inp->ifp->mib.ifType == PPP)) /* only PPP type */ { dtrap(); /* remove after confirmed to work in PPP */ src = ip_mymach(ti->ti_dst.s_addr); if (src != ti->ti_src.s_addr) { ti->ti_src.s_addr = src; tp->t_template->ti_src.s_addr = src; tp->t_inpcb->inp_laddr.s_addr = src; } } #endif /* INCLUDE_PPP */ /* If this is a SYN (not a SYN/ACK) then set the pmtu */ if((flags & TH_ACK) == 0) { #ifdef IP_PMTU inp->inp_pmtu = pmtucache_get(inp->inp_faddr.s_addr); #else /* not compiled for pathmtu, guess based on iface */ { NET ifp; /* find iface for route. Pass "src" as nexthop return */ ifp = iproute(ti->ti_dst.s_addr, &src); if(ifp) inp->inp_pmtu = ifp->n_mtu - (ifp->n_lnh + 40); else inp->inp_pmtu = 580; /* Ugh. */ } #endif /* IP_PMTU */ } break; } #endif /* IP_V4 */ #ifdef IP_V6 case AF_INET6: { struct ip6_inaddr * local; local = ip6_myaddr(&tp->t_inpcb->ip6_faddr, inp->ifp); /* If we got a local address & it's not the one in the pcb, then * we assume it changed at the iface and fix it in the pcb. Unlike * v4, we don't have an IP header yet, not do we have a template * to worry about. */ if((local) && (!IP6EQ(&local->addr, &tp->t_inpcb->ip6_laddr))) { IP6CPY(&tp->t_inpcb->ip6_laddr, &local->addr); } /* If this is a SYN (not a SYN/ACK) then set the pmtu */ if((flags & TH_ACK) == 0) { inp->inp_pmtu = ip6_pmtulookup(&inp->ip6_laddr, inp->ifp); } break; } #endif /* IP_V6 */ default: dtrap(); /* bad domain setting */ } } /* fill in options if any are set */ if (optlen) { struct mbuf * mopt; mopt = m_getwithdata(MT_TXDATA, MAXOPTLEN); if (mopt == NULL) { m_freem(m); return (ENOBUFS); } /* insert options mbuf after after tmp_mbuf */ mopt->m_next = tcp_mbuf->m_next; tcp_mbuf->m_next = mopt; /* extend options to aligned address */ while(optlen & 0x03) tcp_optionbuf[optlen++] = TCPOPT_EOL; MEMCPY(mtod(mopt, char *), tcp_optionbuf, optlen); mopt->m_len = optlen; /* use portable macro to set tcp data offset bits */ SET_TH_OFF(ti->ti_t, ((sizeof (struct tcphdr) + optlen) >> 2)); } ti->ti_flags = (u_char)flags; /* * Calculate receive window. Don't shrink window, * but avoid silly window syndrome. */ if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg) win = 0; if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) win = (long)(tp->rcv_adv - tp->rcv_nxt); /* do check for Iniche buffer limits -JB- */ if (bigfreeq.q_len == 0) /* If queue length is 0, set window to 0 */ { win = 0; } else if(win > (((long)bigfreeq.q_len - 1) * (long)bigbufsiz)) { win = ((long)bigfreeq.q_len - 1) * bigbufsiz; } #ifdef TCP_WIN_SCALE if(tp->t_flags & TF_WINSCALE) { ti->ti_win = htons((u_short)(win >> tp->rcv_wind_scale)); /* apply scale */ }
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); }
void netmain_init(void) { int e; int i; char * msg; #ifdef IP_V6 ip6_addr host; #endif printf("\n%s\n", name); printf("Copyright 1997-2006 by InterNiche Technologies. All rights reserved. \n"); #ifndef SUPERLOOP /* call this to do pre-task setup including intialization of port_prep */ msg = pre_task_setup(); if (msg) panic(msg); #endif #ifdef INCLUDE_NVPARMS /* system uses InterNiche NV system */ e = get_nv_params(); /* get flash parameters into data structs */ if (e) { printf("fatal error (%d) reading NV parameters.\n", e); panic("nv"); } /* set static iface IP info up from stored parameters. These may be overwritten from command line parms or DHCP later. */ for (i = 0; i < STATIC_NETS; i++) { netstatic[i].n_ipaddr = inet_nvparms.ifs[i].ipaddr; netstatic[i].snmask = inet_nvparms.ifs[i].subnet; netstatic[i].n_defgw = inet_nvparms.ifs[i].gateway; #ifdef IP_MULTICAST /* Create a dummy entry for the Ethernet interface mcastlist */ /* If this entry is set to NULL, multicast is not supported */ /* on this interface */ netstatic[i].n_mcastlist = mcastlist; #endif /* IP_MULTICAST */ #ifdef IP_V6 IP6CPY(&host, &inet_nvparms.ifs[i].ipv6addr); if ( (host.addr[0] == 0xFE) && (host.addr[1] == 0xC0)) { netstatic[i].v6addrs[IPA_SITE] = ip6_mkaddr(&netstatic[i], IPA_SITE, &host); } else if ( (host.addr[0] == 0xFE) && (host.addr[1] == 0x80) ) { printf ("[IPV6 init]error : bad IPV6 address\n"); } else if (host.addr[0] != 0) { netstatic[i].v6addrs[IPA_GLOBAL] = ip6_mkaddr(&netstatic[i], IPA_GLOBAL, &host ); } #endif } #ifdef DNS_CLIENT /* set DNS client's server list from nvparms information */ MEMCPY(dns_servers, inet_nvparms.dns_servers, sizeof(dns_servers)); #ifdef DNS_CLIENT_UPDT MEMCPY(soa_mname, inet_nvparms.dns_zone_name, sizeof(soa_mname)); #endif /* DNS_CLIENT_UPDT */ #endif /* DNS_CLIENT */ #ifdef USE_COMPORT comportcfg.comport = comport_nvparms.comport; comportcfg.LineProtocol = comport_nvparms.LineProtocol; #endif /* USE_COMPORT */ #endif /* INCLUDE_NVPARMS */ #ifndef INCLUDE_NVPARMS #ifdef USE_COMPORT comportcfg.comport = 0x01; comportcfg.LineProtocol = PPP; /* Default to PPP */ #endif /* USE_COMPORT */ #endif /* INCLUDE_NVPARMS */ msg = ip_startup(); if (msg) { printf("inet startup error: %s\n", msg); panic("IP"); } #if defined(MEMDEV_SIZE) && defined(VFS_FILES) init_memdev(); /* init the mem and null test devices */ #endif #ifdef IP_MULTICAST #ifdef INCLUDE_TCP /* call the IP multicast test program */ u_mctest_init(); #endif #endif /* clear debugging flags. Port can optionally turn them * back on in post_task_setup(); * NDEBUG = UPCTRACE | IPTRACE | TPTRACE ; */ NDEBUG = 0; /* print IP address of the first interface - for user's benefit */ printf("IP address of %s : %s\n" , ((NET)(netlist.q_head))->name, print_ipad(((NET)(netlist.q_head))->n_ipaddr)); #ifndef SUPERLOOP /* call this per-target routine after basic tasks & net are up */ msg = post_task_setup(); if (msg) panic(msg); #endif #ifdef PING_APP ping_init(); #endif /* PING_APP */ #ifdef RAWIPTEST raw_test_init(); #endif /* RAWIPTEST */ #if defined(TFTP_CLIENT) || defined(TFTP_SERVER) tftp_init(); #endif /* TFTP */ #ifdef TESTMENU install_menu(testmenu); #endif /* TESTMENU */ #ifdef USE_AUTOIP Upnp_init(); /* start Auto IP before DHCP client */ #endif /* USE_AUTOIP */ #ifdef DHCP_CLIENT if( POWERUP_CONFIG_DHCP_ENABLED ) dhc_setup(); /* kick off any DHCP clients */ #endif /* DHCP_CLIENT */ #ifdef DHCP_SERVER #ifdef INCLUDE_NVPARMS if(dhserve_nvparms.ServeDHCP) #endif { e = dhcp_init(); if(e) { dprintf("Error %d starting DHCP server.\n",e); } else { exit_hook(dhcpsrv_cleanup); dprintf("Started DHCP server\n"); } } #endif /* DHCP_SERVER */ #ifdef IN_MENUS printf(prompt); #endif #ifdef UDPSTEST e=udp_echo_init(); if ( e == SUCCESS ) { exit_hook(udp_echo_cleanup); } else dprintf("Error %d starting UDP Echo server.\n",e); #endif #ifdef RIP_SUPPORT e=rip_init(); if ( e == SUCCESS ) { exit_hook(rip_cleanup); } else dprintf("Error %d starting RIP server.\n",e); #endif #ifdef INICHE_SYSLOG e =syslog_init(); if (e == SUCCESS) exit_hook(closelog); else dprintf("Error %d initializing syslog client.\n",e); #endif #ifdef FTP_CLIENT fc_callback=ftpc_callback; #endif /* The following initializations take place when SUPERLOOP is enabled. * Otherwise they would be done in the respective task. */ #ifdef SUPERLOOP #ifdef INCLUDE_SNMP e = snmp_init(); if (e == SUCCESS) exit_hook(snmp_cleanup); else dprintf("Error %d initializing SNMP agent.\n",e); #endif /* INCLUDE_SNMP */ #ifdef WEBPORT e = http_init(); /* start up http server */ if (e) dprintf("Error %d starting HTTP server.\n",e); #endif /* WEBPORT */ #ifdef FTP_SERVER e = ftps_init(); if ( e == SUCCESS ) { exit_hook(ftps_cleanup); } else dprintf("Error %d starting FTP server.\n",e); #endif /* FTP_SERVER */ #ifdef TELNET_SVR e=tel_init(); if ( e == SUCCESS ) { exit_hook(tel_cleanup); } else dprintf("Error %d starting TELNET server.\n",e); #endif #ifdef TCP_ECHOTEST e=tcp_echo_init(); if ( e == SUCCESS ) { exit_hook(tcp_echo_cleanup); } else dprintf("Error %d starting TCP Echo server.\n",e); #endif #ifdef TCP_CIPHERTEST e=tcp_cipher_init(); if ( e == SUCCESS ) { exit_hook(tcp_cipher_cleanup); } else dprintf("Error %d starting TCP cipher server.\n",e); #endif #ifdef USE_CRYPTOENG e = ce_init(); if(e != 0) { dprintf("ce_init() failed\n"); panic("prep_modules"); } #endif #ifdef SMTP_ALERTS smtp_init (); #endif #endif /* SUPERLOOP */ USE_ARG(e); /* Avoid compiler warnings */ USE_ARG(i); } /* end of netmain_init() */
int ip6_pcbconnect(struct inpcb *inp, struct mbuf *nam) { struct sockaddr_in6 *sin; int err; sin = mtod(nam, struct sockaddr_in6 *); if (sin->sin6_family != AF_INET6) return (EAFNOSUPPORT); if (sin->sin6_port == 0) return (EADDRNOTAVAIL); /* If the destination address is unspecified or multicast, * then blow it off. */ if ((IP6EQ(&sin->sin6_addr, &in6addr_any)) || /* wildcard? */ (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))) { return (EADDRNOTAVAIL); } if (!inp->ifp) { #ifdef IP6_ROUTING if (inp->inp_socket && inp->inp_socket->so_optsPack) { #ifdef MULTI_HOMED if (inp->inp_socket->so_optsPack->ip_scopeid == 0) { gio_printf(NULL, "*** ip6_pcbconnect, no IPv6 IF.\n"); return ENP_LOGIC; } #endif inp->ifp = nets[inp->inp_socket->so_optsPack->ip_scopeid - 1]; } else { /* try to find an IPv6 IF */ inp->ifp = ip6_findIF(); if (inp->ifp == NULL) { gio_printf(NULL, "*** ip6_pcbconnect, no IPv6 IF.\n"); return ENP_LOGIC; } } #else /* just use nd0 */ inp->ifp = nets[0]; #endif } inp->inp_flags |= INPF_ROUTESET; /* Make sure this is not a duplicate of an existing connection */ if (ip6_pcblookup(inp->inp_head, &sin->sin6_addr, sin->sin6_port, &inp->ip6_laddr, inp->inp_lport, 0)) { return (EADDRINUSE); } if (IP6EQ(&inp->ip6_laddr, &in6addr_any)) { if ((err = ip6_pcbbind(inp, (struct mbuf *)NULL)) != 0) { dprintf("*** ip6_pcbconnect - ip6_pcbbind err = %d", err); panic("***"); } } IP6CPY(&inp->ip6_faddr, &sin->sin6_addr); inp->inp_fport = sin->sin6_port; return (0); }
struct in_multi * in_addmulti(ip_addr *ap, struct net *netp, int addrtype) { struct in_multi *inm = (struct in_multi *)NULL; int error; /* check for good addr. */ if ((ap == (ip_addr *)NULL) || (*ap == 0)) return ((struct in_multi *)NULL); ENTER_CRIT_SECTION(netp); /* See if address already in list. */ #ifdef IP_V6 if(addrtype == 6) inm = v6_lookup_mcast((ip6_addr*)ap, netp); #endif #ifdef IP_V4 if(addrtype != 6) inm = lookup_mcast(*ap, netp); #endif if (inm != (struct in_multi *)NULL) { /* Found it; just increment the reference count. */ ++inm->inm_refcount; } else { /* * New address; allocate a new multicast record * and link it into the interface's multicast list. */ inm = (struct in_multi *)INM_ALLOC(sizeof(*inm)); if (inm == (struct in_multi *)NULL) { EXIT_CRIT_SECTION(netp); return ((struct in_multi *)NULL); } #ifdef IP_V6 if(addrtype == 6) IP6CPY(&inm->ip6addr, (struct in6_addr *)ap); #endif #ifdef IP_V4 if(addrtype != 6) inm->inm_addr = *ap; #endif inm->inm_netp = netp; inm->inm_refcount = 1; inm->inm_next = netp->mc_list; netp->mc_list = inm; /* * If net has a multicast address registration routine then ask * the network driver to update its multicast reception * filter appropriately for the new address. */ if(netp->n_mcastlist) error = netp->n_mcastlist(inm); else error = 0; #if defined (IGMP_V1) || defined (IGMP_V2) /* * Let IGMP know that we have joined a new IP multicast group. */ if (inm->inm_addr) igmp_joingroup(inm); #endif } EXIT_CRIT_SECTION(netp); USE_ARG(error); return (inm); }
HTCPCONN tcp_ipv6_server_init(unsigned short p_usPort, void * arg ) { gFunctions = (T_PFN_functions *)arg; SOCKTYPE sock = t_socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (sock == INVALID_SOCKET) { C_NLOG_ERR("Iniche:tcp_ipv6_server_init: couldn't create ip6 socket"); return NULL; } //add sock in array int i = 0; int isSett = 0; for(;i < MAX_SOCK_NO; i++) { if(g_sockets[i].sock == INVALID_SOCKET) { g_sockets[i].sock = sock; g_sockets[i].indxToListen = -1; isSett = 1; break; } } if(isSett == 0) { t_socketclose(sock); C_NLOG_ERR("Iniche:tcp_ipv6_server_init: couldn't create ip6 socket."); return NULL; } struct sockaddr_in6 tcpsin6; int addrlen = sizeof(tcpsin6); IP6CPY(&tcpsin6.sin6_addr, &in6addr_any); tcpsin6.sin6_port = htons(p_usPort); tcpsin6.sin6_family = AF_INET6; int e = t_bind(g_sockets[i].sock, (struct sockaddr *)&tcpsin6, addrlen); if (e != 0) { e = t_errno(g_sockets[i].sock); t_socketclose(g_sockets[i].sock); g_sockets[i].sock = INVALID_SOCKET; C_NLOG_ERR("Iniche:tcp_ipv6_server_init: error %d binding tcp listen on port %d",e, p_usPort); return NULL; } int iTmp = 1; e = t_setsockopt(g_sockets[i].sock, SOL_SOCKET, SO_NONBLOCK, (void *)&iTmp, sizeof(iTmp)); if (e == SOCKET_ERROR) { e = t_errno(g_sockets[i].sock); t_socketclose(g_sockets[i].sock); g_sockets[i].sock = INVALID_SOCKET; C_NLOG_ERR("Iniche:tcp_ipv6_server_init: t_setsockopt() SO_NONBLOCK failed, Err: %d", e); return NULL; } e = t_listen(g_sockets[i].sock,MAX_SOCK_NO); if (e != 0) { e = t_errno(g_sockets[i].sock); t_socketclose(g_sockets[i].sock); g_sockets[i].sock = INVALID_SOCKET; C_NLOG_ERR("Iniche:tcp_ipv6_server_init: error %d tcp can't listen on port %d",e, p_usPort); return NULL; } C_NLOG_INFO( "Iniche:tcp_ipv6_server_init: TCP server initialized successfully." ); return (HTCPCONN)(g_sockets+i); }
int ping6_start(GIO *gio, ip6_addr *addr, int scopeID, long count, int dataLen, char *data, int delay, int *live) { struct ping6 *ping; NET ifp; int i; *live = 0; /* init to bad */ /* sanity check parameters */ if ((addr == NULL) || (count < 1)) { gio_printf(gio, "*** ping6 ENP_PARAM.\n"); return (0); } /* alloc ping */ ping = (struct ping6 *)PING6_ALLOC(sizeof(struct ping6)); if (!ping) { gio_printf(gio, "*** ping6 ENP_NOMEM.\n"); return (0); } if ((ifp = nets[scopeID - 1]) == (NET)NULL) { gio_printf(gio, "*** ping6 bad scope id.\n"); return (0); } /* MCAST? */ if (addr->addr[0] != 0xff) { /* no */ ping->net = ifp; IP6CPY(&ping->nexthop, addr); /* is dest GLOBAL? */ if (IN6_IS_ADDR_GLOBAL(addr) && (ifp->v6addrs[IPA_GLOBAL] != 0)) IP6CPY(&ping->lhost, &(ifp->v6addrs[IPA_GLOBAL]->addr)); else IP6CPY(&ping->lhost, &(ifp->v6addrs[IPA_LINK]->addr)); } else { /* yes - set src addr. */ if (!ifp || (ifp->v6addrs[IPA_LINK] == 0)) { gio_printf(gio, "*** ping6 ENP_NO_ROUTE.\n"); return (0); } IP6CPY(&ping->lhost, &ifp->v6addrs[IPA_LINK]->addr); ping->net = ifp; } IP6CPY(&ping->fhost, addr); ping->count = count; ping->gio = gio; ping->ping6_interval = delay; ping->wait_time = delay + GIVE_UP_INTERVAL; ping->length = dataLen; ping->data = data; ping->sess_id = htons(ping6ids); ping6ids++; ping->net = ifp; ping->scopeID = scopeID; *live = 1; ping->last = CTICKS; LOCK_NET_RESOURCE(NET_RESID); /* protect list */ ping->next = ping6q; /* enque in master ping list */ ping6q = ping; UNLOCK_NET_RESOURCE(NET_RESID); /* set the ping callback function */ ping6_callback = ping6_recv; /* go do first send */ if ((i = ping6_send(ping)) != 0) { /* nope */ gio_printf(gio, "*** ping6_send error = %d.\n", i); } return(0); }
int ping6_send(struct ping6 *ping) { PACKET pkt; struct icmp6req *pinghdr; char addrbuf[40]; /* for printf()ing */ int plen = sizeof(struct icmp6req) + ping->length; int err = 0; int sendflags; int bytesleft; int offset; /* try for a pkt chain */ LOCK_NET_RESOURCE(FREEQ_RESID); PK_ALLOC(pkt, plen + MaxLnh + sizeof(struct ipv6)); UNLOCK_NET_RESOURCE(FREEQ_RESID); if (pkt == NULL) { ping->count = 0; /* mark session for deletion */ return (ENP_NOBUFFER); } pkt->flags = 0; /* prepare for cb_read */ pkt->nb_prot = pkt->nb_buff + MaxLnh + sizeof(struct ipv6); /* got chain? */ if (pkt->pk_next == NULL) pkt->nb_plen = plen; /* no */ else pkt->nb_plen = pkt->nb_blen - MaxLnh - sizeof(struct ipv6); /* yes */ /* Advance to point where we write the data */ offset = sizeof(struct icmp6req); pkt->nb_tlen = offset; bytesleft = ping->length; while (bytesleft > PINGSTRSIZE) { err = cb_read(pkt, offset, (uint8_t *)pingdata6, PINGSTRSIZE); if (err < 0) break; offset += err; bytesleft -= err; } if (bytesleft && (err >= 0)) err = cb_read(pkt, offset, (uint8_t *)pingdata6, bytesleft); /* read in data - user or standard? */ /* got err? */ if (err <= 0) { LOCK_NET_RESOURCE(FREEQ_RESID); PK_FREE(pkt); UNLOCK_NET_RESOURCE(FREEQ_RESID); ping->count = 0; /* mark session for deletion */ return (ENP_NOBUFFER); } #ifdef IP6_ROUTING /* Put scopeID in pkt */ pkt->soxopts = npalloc(sizeof(struct ip_socopts)); if (pkt->soxopts == NULL) { LOCK_NET_RESOURCE(FREEQ_RESID); pk_free(pkt); UNLOCK_NET_RESOURCE(FREEQ_RESID); ping->count = 0; /* mark session for deletion */ return (ENP_NOBUFFER); } pkt->soxopts->ip_scopeid = ping->scopeID; #endif pinghdr = (struct icmp6req *)pkt->nb_prot; pinghdr->code = 0; pinghdr->type = ICMP6_ECHOREQ; pinghdr->id = ping->sess_id; pinghdr->sequence = (htons((unshort)ping->sent)); ping->sent++; pkt->net = ping->net; pkt->type = htons(0x86dd); /* multicast ping? */ if (ping->fhost.addr[0] == 0xFF) { pkt->flags |= PKF_MCAST; /* send mac multicast */ sendflags = 0; /* no routing */ } else { pkt->flags &= ~PKF_MCAST; /* send mac unicast */ /* see if we can skip the routing step */ if(pkt->net && (!IP6EQ(&ping->nexthop, &ip6unspecified))) { pkt->nexthop = &ping->nexthop; /* set next hop */ sendflags = IP6F_NOROUTE; } else sendflags = IP6F_ALL; } /* is the scope global? */ if ( (ping->fhost.addr[0] == 0xFF) && ((ping->fhost.addr[1] & 0xF) == 0xE) ) { /* yup - it's a global ping */ pkt->flags |= PKF_IPV6_GBL_PING; /* global ping */ } /* loopback? */ if (IN6_IS_ADDR_LOOPBACK((struct in6_addr *)&(ping->fhost.addr))) IP6CPY((struct in6_addr *)&(ping->lhost.addr), (struct in6_addr *)&(ping->fhost.addr)); /* both are loopback */ /* put prot at IPv6 hdr */ pkt->nb_prot -= sizeof(struct ipv6); pkt->nb_plen += sizeof(struct ipv6); pkt->nb_tlen += sizeof(struct ipv6); /* set time for next ping */ ping->nextping = TIME_ADD(CTICKS, ping->ping6_interval); err = icmp6_send(pkt, &ping->lhost, &ping->fhost, sendflags); pkt->net->icmp6_ifmib.OutEchos++; if (err < 0) { /* Don't record gio error, since we're going to kill this * ping session anyway. */ gio_printf(ping->gio, "error %d sending ping; sess %d, seq:%d\n", err, ntohs(ping->sess_id), ping->sent); ping->count = 0; /* mark session for deletion by timer */ } else if ((err == 1) || (err == 0)) { err = gio_printf(ping->gio, "Sent ping; sess: %d, Seq: %d to %s\n", ntohs(ping->sess_id), ping->sent, print_ip6(&ping->fhost, addrbuf)); } return (0); }
STATIC int ping_debug(CLI_CTX ctx) { GIO *gio = ctx->gio; uint32_t count = 1; /* Default count */ char *data = NULL; int datalen; int ret; u_long ipadd = 0; struct cli_addr *cliaddr = NULL; #ifdef IP_V6 ip6_addr fhost6; /* v6 target to ping */ int scopeID = 1; /* Default scope ID */ int prefixLen = 64; int *ping6Live = (int *)NULL; #endif /* IP_V6 */ int delay = TPS; /* Default delay */ int set_datalen = 0; if (CLI_HELP(ctx)) return (0); if ((!CLI_DEFINED(ctx, 'a') && !CLI_DEFINED(ctx, 'h')) || (CLI_DEFINED(ctx, 'a') && CLI_DEFINED(ctx, 'h'))) { gio_printf(gio, "One of: -a <addr> or -h <name> is required\n"); return (CLI_ERR_PARAM); } if (CLI_DEFINED(ctx, 'a')) { cliaddr = (struct cli_addr *)CLI_VALUE(ctx, 'a'); if ((cliaddr->type != CLI_IPV4) && (cliaddr->type != CLI_IPV6)) return (CLI_ERR_PARAM); #ifdef IP_V4 if (cliaddr->type == CLI_IPV4) ipadd = *((ip_addr *)&cliaddr->addr[0]); /* IPv4 address? */ #endif /* IP_V4 */ #ifdef IP_V6 if (cliaddr->type == CLI_IPV6) { IP6CPY(&fhost6, ((ip6_addr *)&cliaddr->addr[0])); /* IPv6 address? */ scopeID = cliaddr->scopeID; if (cliaddr->prefixLen != 0) prefixLen = cliaddr->prefixLen; } #endif /* IP_V6 */ } else { char *hdata = (char *)CLI_VALUE(ctx, 'h'); ip_addr fhost4 = 0L; #ifdef IP_V6 ret = in_reshost(hdata, &fhost4, &fhost6, RH_VERBOSE | RH_BLOCK); #else ret = in_reshost(hdata, &fhost4, NULL, RH_VERBOSE | RH_BLOCK); #endif /* IP_V6 or not */ if (ret != 0) { gio_printf(gio, "Unable to resolve ping host \"%s\"\n", hdata); return (CLI_ERR_PARAM); } if (fhost4 != 0L) ipadd = (u_long)fhost4; } /* length? */ if (CLI_DEFINED(ctx, 'l')) { datalen = (int)CLI_VALUE(ctx, 'l'); set_datalen = 1; if (datalen < 0) { gio_printf(gio, "length < 0\n"); return (CLI_ERR_PARAM); } } /* delay between pings? */ if (CLI_DEFINED(ctx, 't')) { delay = (int)CLI_VALUE(ctx, 't'); if (delay <= 0) { gio_printf(gio, "delay <= 0\n"); return (CLI_ERR_PARAM); } } /* Number of pings */ if (CLI_DEFINED(ctx, 'n')) { count = (int)CLI_VALUE(ctx, 'n'); if (count < 1) { gio_printf(gio, "ping count <= 0\n"); return (CLI_ERR_PARAM); } } if (count > 5) gio_printf(gio, "Type 'k' (Kill) for early termination\n"); #ifdef IP_V6 /* ipadd is now used as a flag to indicate IPv4 or not */ if (ipadd == 0) { char wrkBuf[40]; if (!set_datalen) datalen = strlen(pingdata6); /* Default length */ ping6Live = &ping6LiveSig; gio_printf(gio, "ping IPv6 addr: %s\n", print_ip6(&fhost6, wrkBuf)); ping6_start(gio, &fhost6, scopeID, count, datalen, data, delay, ping6Live); /* bad start? */ if (ping6LiveSig == 0) { gio_printf(gio, "Bad ping6 initialization\n"); return (0); /* yes */ } } #endif /* IP_V6 */ #ifdef IP_V4 /* ipadd is now used as a flag to indicate IPv4 or not */ if (ipadd != 0) { ping4ID++; /* bump to next ping ID */ if (!set_datalen) datalen = strlen(pingdata); /* Default length */ #ifndef OS_PREEMPTIVE start_ping_req(gio, ipadd, count, datalen, data, delay); #else msgp = (struct pingtask_msg *) PING_ALLOC (sizeof (struct pingtask_msg)); if (!msgp) { ++ping_err.alloc_fail; return (PING_ST_ALLOC_ERR); } /* send message for PING client task */ msgp->type = PING_CNTRL_START_PING; msgp->ipadd = (u_long) ipadd; msgp->times = count; msgp->gio = gio; msgp->data = data; msgp->length = dataLen; msgp->delay = delay; LOCK_NET_RESOURCE (PINGQ_RESID); putq(&pingRcvq, (q_elt)msgp); UNLOCK_NET_RESOURCE (PINGQ_RESID); TK_SIGNAL(to_diagcheck, NULL); #endif /* OS_PREEMPTIVE */ } #endif /* IP_V4 */ /* wait for console input or ping end */ for (;;) { int ret; char buff[3]; int flags; buff[0] = NUL; /* Check if ping is still running */ #if defined(IP_V4) && defined(PING_APP) if ((ipadd != 0) && !ping_search(gio)) break; /* No */ #endif /* IP_V4 && PING_APP */ #ifdef IP_V6 if ((ipadd == 0) && !ping6q) break; /* No */ #endif /* IP_V6 */ /* read a char - set GIO non-blocking read */ flags = gio->flags; gio->flags &= ~GIO_F_BIN; ret = gio_in(gio, buff, 1); gio->flags = flags; /* Console routine puts char in buff[0]; telnet's puts it in ret */ if ((ret > 0) && ((buff[0] == 'k') || (ret == 'k'))) { #if defined(IP_V4) && defined(PING_APP) if (ipadd != 0) { PING_INFO p; if ((p = ping_search(gio)) != NULL) ping_cmd_end(p); break; /* already done */ } else #endif /* IP_V4 && PING_APP */ #ifdef IP_V6 ping6_done(ping6q); #endif /* IP_V6 */ gio_printf(gio, "Ping canceled by console\n"); break; } TK_YIELD(); } return (0); }