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); }
int findmtu6(pcs *pc, ip6 *src) { int i; for (i = 0; i < POOL_SIZE; i++) { if (time_tick - pc->ip6mtu[i].timeout > POOL_TIMEOUT) continue; if (IP6EQ(src, &pc->ip6mtu[i].ip)) return pc->ip6mtu[i].mtu; } return pc->mtu; }
struct in_multi * v6_lookup_mcast(ip6_addr * ipp, NET netp) { struct in_multi * imp; for (imp = netp->mc_list; imp; imp = imp->inm_next) { if(IP6EQ(&imp->ip6addr, ipp)) return imp; } return NULL; /* addr not found in mcast list */ }
void save_mtu6(pcs *pc, struct packet *m) { icmp6hdr *icmp; ip6hdr *ip = NULL, *ip0 = NULL; int i, n; ip = (ip6hdr *)(m->data + sizeof(ethdr)); if ((ip->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) return; n = ip6ehdr(ip, m->len - sizeof(ethdr), IPPROTO_ICMPV6); if (n == 0) return; icmp = (icmp6hdr *)((char *)ip + n); if (icmp->type != ICMP6_PACKET_TOO_BIG) return; ip0 = (ip6hdr *)(m->data + sizeof(ethdr) + sizeof(ip6hdr) + sizeof(icmp6hdr)); for (i = 0, n = -1; i < POOL_SIZE; i++) { if (IP6EQ(&ip0->dst, &pc->ip6mtu[i].ip)) { pc->ip6mtu[i].mtu = ntohl(icmp->icmp6_mtu); pc->ip6mtu[i].timeout = time_tick; return; } if ((n < 0) && (time_tick - pc->ip6mtu[i].timeout > POOL_TIMEOUT)) n = i; } if (n >= 0) { pc->ip6mtu[n].mtu = ntohl(icmp->icmp6_mtu); pc->ip6mtu[n].timeout = time_tick; memcpy(pc->ip6mtu[n].ip.addr8, ip0->dst.addr8, 16); } }
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 */ }
int save_nb_adv(pcs *pc, struct packet *m) { ethdr *eh; ip6hdr *ip; ndhdr *nshdr; ndopt *nsopt; int i; eh = (ethdr *)(m->data); if (eh->type != htons(ETHERTYPE_IPV6)) return -1; if (memcmp(eh->dst, pc->ip4.mac, ETH_ALEN)) return -1; ip = (ip6hdr *)(eh + 1); if ((!IP6EQ(&(pc->ip6.ip), &(ip->dst)) && !IP6EQ(&(pc->link6.ip), &(ip->dst))) || IP6EQ(&ip->dst, &ip->src)) return -1; nshdr = (ndhdr *)(ip + 1); nsopt = (ndopt *)(nshdr + 1); if (nshdr->hdr.type != ND_NEIGHBOR_ADVERT) return -1; /* shoule check sum field * ... */ /* not Target Link-Layer Address */ if (nsopt->type != 2) return -1; i = 0; while (i < POOL_SIZE) { if (IP6EQ(&pc->ipmac6[i].ip, &ip->src) && (time_tick - pc->ipmac6[i].timeout <= 120)) break; if (pc->ipmac6[i].timeout == 0 || (time_tick - pc->ipmac6[i].timeout > 120)) { memcpy(pc->ipmac6[i].mac, nsopt->mac, ETH_ALEN); memcpy(pc->ipmac6[i].ip.addr8, ip->src.addr8, sizeof(ip->src.addr8)); pc->ipmac6[i].timeout = time_tick; pc->ipmac6[i].cidr = 128; break; } i++; } if (i == POOL_SIZE) { i = 0; memcpy(pc->ipmac6[i].mac, nsopt->mac, ETH_ALEN); memcpy(pc->ipmac6[i].ip.addr8, ip->src.addr8, 16); pc->ipmac6[i].timeout = time_tick; } return i; }
int response6(struct packet *m, sesscb *sesscb) { ethdr *eh; ip6hdr *ip; eh = (ethdr *)(m->data); ip = (ip6hdr *)(eh + 1); if (ip->ip6_nxt == IPPROTO_ICMPV6) { icmp6hdr *icmp = (icmp6hdr *)(ip + 1); if (icmp->type == ICMP6_DST_UNREACH || icmp->type == ICMP6_TIME_EXCEEDED || icmp->type == ICMP6_DST_UNREACH_NOPORT || icmp->type == ICMP6_PACKET_TOO_BIG) { if (icmp->type == ICMP6_PACKET_TOO_BIG) sesscb->mtu = ntohl(icmp->icmp6_mtu); sesscb->icmptype = icmp->type; sesscb->icmpcode = icmp->code; sesscb->rttl = ip->ip6_hlim; memcpy(sesscb->rdip6.addr8, ip->src.addr8, 16); return IPPROTO_ICMPV6; } } if (!IP6EQ(&(sesscb->dip6), &(ip->src))) return 0; if (ip->ip6_nxt == IPPROTO_ICMPV6 && sesscb->proto == IPPROTO_ICMPV6) { icmp6hdr *icmp = (icmp6hdr *)(ip + 1); sesscb->icmptype = icmp->type; sesscb->icmpcode = icmp->code; sesscb->rttl = ip->ip6_hlim; memcpy(sesscb->rdip6.addr8, ip->src.addr8, 16); if (ntohs(icmp->icmp6_seq) == sesscb->sn) { return IPPROTO_ICMPV6; } return 0; } if (ip->ip6_nxt == IPPROTO_UDP) { udphdr *ui = (udphdr *)(ip + 1); char *data = ((char*)(ui + 1)); if (memcmp(data, eh->dst, 6) == 0) { sesscb->rttl = ip->ip6_hlim; return IPPROTO_UDP; } return 0; } if (ip->ip6_nxt == IPPROTO_TCP) { struct tcphdr *th = (struct tcphdr *)(ip + 1); char *data = ((char*)(th + 1)); sesscb->rseq = ntohl(th->th_seq); sesscb->rack = ntohl(th->th_ack); sesscb->rflags = th->th_flags; sesscb->rttl = ip->ip6_hlim; sesscb->rdsize = ntohs(ip->ip6_plen) - sizeof(iphdr) - (th->th_off << 2); sesscb->data = NULL; /* try to get MSS from options */ if (sesscb->flags == TH_SYN && sesscb->rdsize > 0 && sesscb->rflags == (TH_SYN | TH_ACK)) { int i = 0; while (data[i] == 0x1 && i < sesscb->rdsize) i++; for (;i < sesscb->rdsize;) { if (data[i] == TCPOPT_MAXSEG && data[i + 1] == TCPOLEN_MAXSEG) { sesscb->rmss = (data[i + 2] << 8) + data[i + 3]; break; } i += data[i + 1]; } } else { sesscb->data = ((char*)(ip + 1)) + (th->th_off << 2); } return IPPROTO_TCP; } return 0; }
struct inpcb * ip6_pcblookup(struct inpcb *head, ip6_addr *faddr, unshort xfport, ip6_addr *laddr, unshort xlport, int flags) { struct inpcb *inp; struct inpcb *match = NULL; unshort fport = xfport; unshort lport = xlport; int matchwild = 3; int wildcard; for (inp = head->inp_next; inp != head; inp = inp->inp_next) { if (inp->inp_lport != lport) continue; /* Skip non IPv6 sockets */ if (inp->inp_socket->so_domain != AF_INET6) continue; /* optimize this to catch exact matches ASAP */ if ((inp->inp_fport == fport) && (IP6EQ(&inp->ip6_laddr, laddr)) && (IP6EQ(&inp->ip6_faddr, faddr))) { match = inp; goto ipl6_cacheit; } wildcard = 0; if (IP6EQ(&inp->ip6_laddr, &in6addr_any) == FALSE) { if (IP6EQ(laddr, &in6addr_any)) wildcard++; else if (IP6EQ(&inp->ip6_laddr, laddr) == FALSE) continue; } else { if (IP6EQ(laddr, &in6addr_any) == FALSE) wildcard++; } if (IP6EQ(&inp->ip6_faddr, &in6addr_any) == FALSE) { if (IP6EQ(faddr, &in6addr_any)) wildcard++; else if ((IP6EQ(&inp->ip6_faddr, faddr) == FALSE) || (inp->inp_fport != fport)) { continue; } } else { if (IP6EQ(faddr, &in6addr_any) == FALSE) wildcard++; } if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) continue; if (wildcard < matchwild) { match = inp; matchwild = wildcard; if (matchwild == 0) break; } /* for the specific case of * inp->ip6_laddr == in6addr_any && * inp->ip6_faddr == in6addr_any && * fport == lport * * This covers the case in multi-homed nodes, * in which SYNs can come in from any of the IFs * with local link addresses and only the * port number to ID the accept or the * waiting UDP. */ if ( (IP6EQ(&inp->ip6_laddr, &in6addr_any) == TRUE) && (IP6EQ(&inp->ip6_faddr, &in6addr_any) == TRUE) && (fport == lport) ) return (inp); } if (match == NULL) return match; ipl6_cacheit: if (head->inp_next == match) /* got cache hit? */ { inpcb_cachehits++; } else { inpcb_cachemiss++; /* "cache" the match to be first checked next time. */ match->inp_next->inp_prev = match->inp_prev; /*unlink match */ match->inp_prev->inp_next = match->inp_next; /* relink match as head->inp_next */ match->inp_next = head->inp_next; head->inp_next = match; match->inp_prev = head; match->inp_next->inp_prev = match; } return (match); }
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); }
/* ping host , char *cmdstr*/ int run_ping6(int argc, char **argv) { pcs *pc = &vpc[pcid]; struct in6_addr ipaddr; struct packet *m = NULL; int i; char *p; char proto_seq[16]; int count = 5; printf("\n"); i = 2; for (i = 2; i < argc; i++) { if (!strcmp(argv[i], "-c")) { if ((i + 1) < argc && digitstring(argv[i + 1])) count = atoi(argv[i + 1]); break; } } if (vinet_pton6(AF_INET6, argv[1], &ipaddr) != 1) { printf("Invalid address: %s\n", argv[1]); return 0; } memcpy(pc->mscb.dip6.addr8, ipaddr.s6_addr, 16); if (pc->mscb.dip6.addr16[0] != IPV6_ADDR_INT16_ULL) memcpy(pc->mscb.sip6.addr8, pc->ip6.ip.addr8, 16); else memcpy(pc->mscb.sip6.addr8, pc->link6.ip.addr8, 16); /* ping self, discard options */ if (IP6EQ(&pc->mscb.sip6, &pc->mscb.dip6)) { i = 1; while (i < 6) { printf("%s icmp_seq=%d ttl=%d time=0.001 ms\n", argv[1], i++, pc->mscb.ttl); delay_ms(1); } return 1; } /* find destination */ p = (char*)nbDiscovery(pc, &pc->mscb.dip6); if (p == NULL) { printf("host (%s) not reachable\n", argv[1]); return 0; } memcpy(pc->mscb.dmac, p, 6); if (pc->mscb.proto == IPPROTO_ICMP) { pc->mscb.proto = IPPROTO_ICMPV6; strcpy(proto_seq, "icmp6_seq"); } else if (pc->mscb.proto == IPPROTO_TCP) { strcpy(proto_seq, "tcp6_seq"); } else if (pc->mscb.proto == IPPROTO_UDP) { strcpy(proto_seq, "udp6_seq"); } if (pc->mscb.proto == IPPROTO_TCP && pc->mscb.flags == 0) { i = 0; while ((i++ < count || count == -1) && !ctrl_c) { struct timeval ts0, ts; u_int usec; int k; int dsize = pc->mscb.dsize; int traveltime = 1; if (i > 1) delay_ms(pc->mscb.waittime); /* clear the input queue */ while ((m = deq(&pc->iq)) != NULL); /* connect the remote */ gettimeofday(&(ts), (void*)0); k = tcp_open(IPV6_VERSION); /* restore data size */ pc->mscb.dsize = dsize; gettimeofday(&(ts0), (void*)0); usec = (ts0.tv_sec - ts.tv_sec) * 1000000 + ts0.tv_usec - ts.tv_usec; if (k == 0) { printf("Connect %d@%s timeout\n", pc->mscb.dport, argv[1]); continue; } else if (k == 2) { char buf[INET6_ADDRSTRLEN + 1]; memset(buf, 0, sizeof(buf)); vinet_ntop6(AF_INET6, &pc->mscb.rdip6, buf, INET6_ADDRSTRLEN + 1); printf("*%s %s=%d ttl=%d time=%.3f ms", buf, proto_seq, i++, pc->mscb.rttl, usec / 1000.0); printf(" (ICMP type:%d, code:%d, %s)\n", pc->mscb.icmptype, pc->mscb.icmpcode, icmpTypeCode2String(6, pc->mscb.icmptype, pc->mscb.icmpcode)); continue; } else if (k == 3) { printf("Connect %d@%s RST returned\n", pc->mscb.dport, argv[1]); continue; } printf("Connect %d@%s seq=%d ttl=%d time=%.3f ms\n", pc->mscb.dport, argv[1], i, pc->mscb.rttl, usec / 1000.0); traveltime = 0.6 * usec / 1000; /* send data */ delay_ms(traveltime); gettimeofday(&(ts), (void*)0); k = tcp_send(IPV6_VERSION); if (k == 0) { printf("SendData %d@%s timeout\n", pc->mscb.dport, argv[1]); continue; } gettimeofday(&(ts0), (void*)0); usec = (ts0.tv_sec - ts.tv_sec) * 1000000 + ts0.tv_usec - ts.tv_usec; printf("SendData %d@%s seq=%d ttl=%d time=%.3f ms\n", pc->mscb.dport, argv[1], i, pc->mscb.rttl, usec / 1000.0); /* close */ if (k != 2) delay_ms(traveltime); gettimeofday(&(ts), (void*)0); k = tcp_close(IPV6_VERSION); if (k == 0) { printf("Close %d@%s timeout\n", pc->mscb.dport, argv[1]); continue; } gettimeofday(&(ts0), (void*)0); usec = (ts0.tv_sec - ts.tv_sec) * 1000000 + ts0.tv_usec - ts.tv_usec; printf("Close %d@%s seq=%d ttl=%d time=%.3f ms\n", pc->mscb.dport, argv[1], i, pc->mscb.rttl, usec / 1000.0); } } else { i = 1; while ((i <= count || count == -1) && !ctrl_c) { struct packet *p; struct timeval tv; u_int usec; int respok = 0; pc->mscb.sn = i; pc->mscb.timeout = time_tick; m = packet6(&pc->mscb); if (m == NULL) { printf("out of memory\n"); return false; } gettimeofday(&(tv), (void*)0); enq(&pc->oq, m); while (!timeout(tv, pc->mscb.waittime) && !ctrl_c) { delay_ms(1); respok = 0; while ((p = deq(&pc->iq)) != NULL && !respok && !timeout(tv, pc->mscb.waittime) && !ctrl_c) { pc->mscb.icmptype = pc->mscb.icmpcode = 0; respok = response6(p, &pc->mscb); usec = (p->ts.tv_sec - tv.tv_sec) * 1000000 + p->ts.tv_usec - tv.tv_usec; del_pkt(p); if (respok == 0) continue; tv.tv_sec = 0; if ((pc->mscb.proto == IPPROTO_ICMPV6 && pc->mscb.icmptype == ICMP6_ECHO_REPLY) || (pc->mscb.proto == IPPROTO_UDP && respok == IPPROTO_UDP)|| (pc->mscb.proto == IPPROTO_TCP && respok == IPPROTO_TCP)) { printf("%s %s=%d ttl=%d time=%.3f ms\n", argv[1], proto_seq, i++, pc->mscb.rttl, usec / 1000.0); break; } if (respok == IPPROTO_ICMPV6) { char buf[INET6_ADDRSTRLEN + 1]; memset(buf, 0, sizeof(buf)); vinet_ntop6(AF_INET6, &pc->mscb.rdip6, buf, INET6_ADDRSTRLEN + 1); printf("*%s %s=%d ttl=%d time=%.3f ms", buf, proto_seq, i++, pc->mscb.rttl, usec / 1000.0); printf(" (ICMP type:%d, code:%d, %s)\n", pc->mscb.icmptype, pc->mscb.icmpcode, icmpTypeCode2String(6, pc->mscb.icmptype, pc->mscb.icmpcode)); break; } } } if (!respok && !ctrl_c) printf("%s %s=%d timeout\n", argv[1], proto_seq, i++); } } return 1; }
int run_tracert6(int argc, char **argv) { int i, j; struct packet *m; pcs *pc = &vpc[pcid]; u_char *dmac; int ok = 0; struct in6_addr ipaddr; ip6 ip; int pktnum = 3; int count = 99; printf("\n"); if (argc < 2) { printf("incompleted command.\n"); return 0; } i = 2; for (i = 2; i < argc; i++) { if (!strcmp(argv[i], "-c")) { if ((i + 1) < argc && digitstring(argv[i + 1])) count = atoi(argv[i + 1]); break; } } if (count == 99 && digitstring(argv[argc - 1])) count = atoi(argv[argc - 1]); if (optind < argc && digitstring(argv[optind])) count = atoi(argv[optind]); if (count < 1 || count > 64) count = 64; if (vinet_pton6(AF_INET6, argv[1], &ipaddr) != 1) { printf("Invalid address: %s\n", argv[1]); return 0; } memcpy(pc->mscb.dip6.addr8, ipaddr.s6_addr, 16); if (pc->mscb.dip6.addr16[0] != IPV6_ADDR_INT16_ULL) memcpy(pc->mscb.sip6.addr8, pc->ip6.ip.addr8, 16); else memcpy(pc->mscb.sip6.addr8, pc->link6.ip.addr8, 16); dmac = nbDiscovery(pc, &ip); if (dmac == NULL) { printf("host (%s) not reachable\n", argv[1]); return 0; } memcpy(pc->mscb.dmac, dmac, 6); printf("trace to %s, %d hops max\n", argv[1], count); /* send the udp packets */ i = 1; while (i <= count && !ctrl_c) { struct packet *p; struct timeval tv; u_int usec; int k; printf("%2d ", i); for (j = 0; j < pktnum && !ctrl_c; j++) { pc->mscb.ttl = i; m = packet6(&pc->mscb); if (m == NULL) { printf("out of memory\n"); return 0; } gettimeofday(&(tv), (void*)0); enq(&pc->oq, m); k = 0; while (!timeout(tv, pc->mscb.waittime) && !ctrl_c) { delay_ms(1); ok = 0; while ((p = deq(&pc->iq)) != NULL && !ok && !timeout(tv, pc->mscb.waittime) && !ctrl_c) { ok = response6(p, &pc->mscb); usec = (p->ts.tv_sec - tv.tv_sec) * 1000000 + p->ts.tv_usec - tv.tv_usec; del_pkt(p); if (pc->mscb.icmptype == ICMP6_TIME_EXCEEDED || IP6EQ(&(pc->mscb.dip6), &(pc->mscb.rdip6))) { if (j == 0) { char buf[128]; memcpy(ipaddr.s6_addr, pc->mscb.rdip6.addr8, 16); memset(buf, 0, 128); vinet_ntop6(AF_INET6, &ipaddr, buf, INET6_ADDRSTRLEN + 1); printf("%s ", buf); } printf(" %.3f ms", usec / 1000.0);fflush(stdout); tv.tv_sec = 0; break; } else if (pc->mscb.icmptype == ICMP6_DST_UNREACH || pc->mscb.icmptype == ICMP6_DST_UNREACH_NOPORT) { if (j == 0) { char buf[128]; memcpy(ipaddr.s6_addr, pc->mscb.rdip6.addr8, 16); memset(buf, 0, 128); vinet_ntop6(AF_INET6, &ipaddr, buf, INET6_ADDRSTRLEN + 1); printf("*%s %.3f ms (ICMP type:%d, code:%d, %s)\n", buf, usec / 1000.0, pc->mscb.icmptype, pc->mscb.icmpcode, icmpTypeCode2String(6, pc->mscb.icmptype, pc->mscb.icmpcode)); } tv.tv_sec = 0; return 1; } } k++; } if (!ok && !ctrl_c) printf(" *"); } printf("\n"); i++; if (pc->mscb.icmptype == ICMP6_DST_UNREACH) break; } return 1; }
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); }