static int ip6_findroute(struct nhop6_basic *pnh, const struct sockaddr_in6 *dst, struct mbuf *m) { if (fib6_lookup_nh_basic(M_GETFIB(m), &dst->sin6_addr, dst->sin6_scope_id, 0, dst->sin6_flowinfo, pnh) != 0) { IP6STAT_INC(ip6s_noroute); IP6STAT_INC(ip6s_cantforward); icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); return (EHOSTUNREACH); } if (pnh->nh_flags & NHF_BLACKHOLE) { IP6STAT_INC(ip6s_cantforward); m_freem(m); return (EHOSTUNREACH); } if (pnh->nh_flags & NHF_REJECT) { IP6STAT_INC(ip6s_cantforward); icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_REJECT, 0); return (EHOSTUNREACH); } return (0); }
int route6_input(struct mbuf **mp, int *offp, int proto) { #pragma unused(proto) struct ip6_hdr *ip6; struct mbuf *m = *mp; struct ip6_rthdr *rh; int off = *offp, rhlen; #ifdef notyet struct ip6aux *ip6a; ip6a = ip6_findaux(m); if (ip6a) { /* XXX reject home-address option before rthdr */ if (ip6a->ip6a_flags & IP6A_SWAP) { ip6stat.ip6s_badoptions++; m_freem(m); return IPPROTO_DONE; } } #endif /* notyet */ #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*rh), return IPPROTO_DONE); /* Expect 32-bit aligned data pointer on strict-align platforms */ MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); ip6 = mtod(m, struct ip6_hdr *); rh = (struct ip6_rthdr *)((caddr_t)ip6 + off); #else /* Expect 32-bit aligned data pointer on strict-align platforms */ MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); ip6 = mtod(m, struct ip6_hdr *); IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); if (rh == NULL) { ip6stat.ip6s_tooshort++; return (IPPROTO_DONE); } #endif switch (rh->ip6r_type) { default: /* unknown routing type */ if (rh->ip6r_segleft == 0) { rhlen = (rh->ip6r_len + 1) << 3; break; /* Final dst. Just ignore the header. */ } ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&rh->ip6r_type - (caddr_t)ip6); return (IPPROTO_DONE); } *offp += rhlen; return (rh->ip6r_nxt); }
int route6_input(struct mbuf **mp, int *offp, int proto) { struct ip6_hdr *ip6; struct mbuf *m = *mp; struct ip6_rthdr *rh; int off = *offp, rhlen; #ifdef __notyet__ struct ip6aux *ip6a; ip6a = ip6_findaux(m); if (ip6a) { /* XXX reject home-address option before rthdr */ if (ip6a->ip6a_flags & IP6A_SWAP) { IP6STAT_INC(ip6s_badoptions); m_freem(m); return IPPROTO_DONE; } } #endif #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE); ip6 = mtod(m, struct ip6_hdr *); rh = (struct ip6_rthdr *)((caddr_t)ip6 + off); #else ip6 = mtod(m, struct ip6_hdr *); IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); if (rh == NULL) { IP6STAT_INC(ip6s_tooshort); return IPPROTO_DONE; } #endif /* * While this switch may look gratuitous, leave it in * in favour of RH2 implementations, etc. */ switch (rh->ip6r_type) { default: /* Unknown routing header type. */ if (rh->ip6r_segleft == 0) { rhlen = (rh->ip6r_len + 1) << 3; break; /* Final dst. Just ignore the header. */ } IP6STAT_INC(ip6s_badoptions); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&rh->ip6r_type - (caddr_t)ip6); return (IPPROTO_DONE); } *offp += rhlen; return (rh->ip6r_nxt); }
/* * Send any deferred ICMP param problem error messages; caller must not be * holding ip6qlock and is expected to have saved the per-packet parameter * value via frag6_save_context(). */ static void frag6_icmp6_paramprob_error(struct fq6_head *diq6) { lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED); if (!MBUFQ_EMPTY(diq6)) { struct mbuf *merr, *merr_tmp; int param; MBUFQ_FOREACH_SAFE(merr, diq6, merr_tmp) { MBUFQ_REMOVE(diq6, merr); MBUFQ_NEXT(merr) = NULL; param = frag6_restore_context(merr); frag6_scrub_context(merr); icmp6_error(merr, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, param); } }
/** * Processing of IPv6 packets. * * @param eth Ethernet header * @param packet Packet data * * @return 0 for success * @return 1 for failure */ int ipv6(struct s_ethernet *eth, char *packet) { struct s_ipv6 *ip; char *payload; /* load data into structures */ ip = (struct s_ipv6 *) packet; payload = packet + sizeof(struct s_ipv6); /* test if this packet belongs to us */ if (memcmp(&wrapsix_ipv6_prefix, &ip->ip_dest, 12) != 0 && memcmp(&ndp_multicast_addr, &ip->ip_dest, 13) != 0) { return 1; } /* check and decrease hop limit */ if (ip->hop_limit <= 1) { /* deny this error for error ICMP messages */ if (ip->next_header != IPPROTO_ICMPV6 || payload[0] & 0x80) { /* code 0 = hl exceeded in transmit */ icmp6_error(eth->src, ip->ip_src, ICMPV6_TIME_EXCEEDED, 0, (unsigned char *) packet, htons(ip->len) + sizeof(struct s_ipv6)); } return 1; } else { ip->hop_limit--; } switch (ip->next_header) { case IPPROTO_TCP: log_debug("IPv6 Protocol: TCP"); return tcp_ipv6(eth, ip, payload); case IPPROTO_UDP: log_debug("IPv6 Protocol: UDP"); return udp_ipv6(eth, ip, payload); case IPPROTO_ICMPV6: log_debug("IPv6 Protocol: ICMP"); return icmp_ipv6(eth, ip, payload); default: log_debug("IPv6 Protocol: unknown [%d/0x%x]", ip->next_header, ip->next_header); return 1; } }
ER_UINT ip6_unknown_opt (T_NET_BUF *input, uint8_t *opt) { switch (IP6OPT_TYPE(*opt)) { case IP6OPT_TYPE_SKIP: /* 無視する。 */ return *(opt + 1); break; case IP6OPT_TYPE_ICMP: /* 破棄して、マルチキャストでなければ ICMP を返送する。 */ if (IN6_IS_ADDR_MULTICAST(&GET_IP6_HDR(input)->dst)) return IP6_OPT_RET_ERR; /* no break; */ case IP6OPT_TYPE_FORCEICMP: /* 破棄して、ICMP を返送する。 */ icmp6_error(input, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, (opt - input->buf) - IF_HDR_SIZE); return IP6_OPT_RET_REL; break; /*case IP6OPT_TYPE_DISCARD: データグラムを破棄する。 */ } return IP6_OPT_RET_ERR; }
int route6_input(struct mbuf **mp, int *offp, int proto) { struct ip6_hdr *ip6; struct mbuf *m = *mp; struct ip6_rthdr *rh; int off = *offp, rhlen; ip6 = mtod(m, struct ip6_hdr *); IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); if (rh == NULL) { ip6stat.ip6s_tooshort++; return IPPROTO_DONE; } switch (rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: /* * RFC 5095 specifies to handle routing header type 0 * the same way as an unrecognised routing type. */ /* FALLTHROUGH */ default: /* unknown routing type */ if (rh->ip6r_segleft == 0) { rhlen = (rh->ip6r_len + 1) << 3; break; /* Final dst. Just ignore the header. */ } ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&rh->ip6r_type - (caddr_t)ip6); return (IPPROTO_DONE); } *offp += rhlen; return (rh->ip6r_nxt); }
/* * Fragment input */ int frag6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp, *t; struct ip6_hdr *ip6; struct ip6_frag *ip6f; struct ip6q *q6; struct ip6asfrag *af6, *ip6af, *af6dwn; struct in6_ifaddr *ia; int offset = *offp, nxt, i, next; int first_frag = 0; int fragoff, frgpartlen; /* must be larger than u_int16_t */ struct ifnet *dstifp; u_int8_t ecn, ecn0; #if 0 char ip6buf[INET6_ADDRSTRLEN]; #endif ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); #else IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); if (ip6f == NULL) return (IPPROTO_DONE); #endif dstifp = NULL; /* find the destination interface of the packet. */ ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); if (ia != NULL) { dstifp = ia->ia_ifp; ifa_free(&ia->ia_ifa); } /* jumbo payload can't contain a fragment header */ if (ip6->ip6_plen == 0) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } /* * check whether fragment packet's fragment length is * multiple of 8 octets. * sizeof(struct ip6_frag) == 8 * sizeof(struct ip6_hdr) = 40 */ if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offsetof(struct ip6_hdr, ip6_plen)); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } IP6STAT_INC(ip6s_fragments); in6_ifstat_inc(dstifp, ifs6_reass_reqd); /* offset now points to data portion */ offset += sizeof(struct ip6_frag); /* * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0) * upfront, unrelated to any reassembly. Just skip the fragment header. */ if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) { /* XXX-BZ we want dedicated counters for this. */ IP6STAT_INC(ip6s_reassembled); in6_ifstat_inc(dstifp, ifs6_reass_ok); *offp = offset; return (ip6f->ip6f_nxt); } IP6Q_LOCK(); /* * Enforce upper bound on number of fragments. * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ if (V_ip6_maxfrags < 0) ; else if (V_frag6_nfrags >= (u_int)V_ip6_maxfrags) goto dropfrag; for (q6 = V_ip6q.ip6q_next; q6 != &V_ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst) #ifdef MAC && mac_ip6q_match(m, q6) #endif ) break; if (q6 == &V_ip6q) { /* * the first fragment to arrive, create a reassembly queue. */ first_frag = 1; /* * Enforce upper bound on number of fragmented packets * for which we attempt reassembly; * If maxfragpackets is 0, never accept fragments. * If maxfragpackets is -1, accept all fragments without * limitation. */ if (V_ip6_maxfragpackets < 0) ; else if (V_frag6_nfragpackets >= (u_int)V_ip6_maxfragpackets) goto dropfrag; V_frag6_nfragpackets++; q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, M_NOWAIT); if (q6 == NULL) goto dropfrag; bzero(q6, sizeof(*q6)); #ifdef MAC if (mac_ip6q_init(q6, M_NOWAIT) != 0) { free(q6, M_FTABLE); goto dropfrag; } mac_ip6q_create(m, q6); #endif frag6_insque(q6, &V_ip6q); /* ip6q_nxt will be filled afterwards, from 1st fragment */ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; #ifdef notyet q6->ip6q_nxtp = (u_char *)nxtp; #endif q6->ip6q_ident = ip6f->ip6f_ident; q6->ip6q_ttl = IPV6_FRAGTTL; q6->ip6q_src = ip6->ip6_src; q6->ip6q_dst = ip6->ip6_dst; q6->ip6q_ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ q6->ip6q_nfrag = 0; }
static int ip6_fw_chk(struct ip6_hdr **pip6, struct ifnet *oif, struct mbuf **m) { struct ip6_fw_chain *chain; struct ip6_fw *rule = NULL; struct ip6_hdr *ip6 = *pip6; struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short offset = 0; int off = sizeof(struct ip6_hdr), nxt = ip6->ip6_nxt; u_short src_port, dst_port; #ifdef IP6FW_DIVERT_RESTART u_int16_t skipto = 0; #else u_int16_t ignport = 0; #endif /* * Go down the chain, looking for enlightment * #ifdef IP6FW_DIVERT_RESTART * If we've been asked to start at a given rule immediatly, do so. * #endif */ chain = LIST_FIRST(&ip6_fw_chain); #ifdef IP6FW_DIVERT_RESTART if (skipto) { if (skipto >= 65535) goto dropit; while (chain && (chain->rule->fw_number <= skipto)) { chain = LIST_NEXT(chain, chain); } if (! chain) goto dropit; } #endif /* IP6FW_DIVERT_RESTART */ for (; chain; chain = LIST_NEXT(chain, chain)) { struct ip6_fw *const f = chain->rule; if (oif) { /* Check direction outbound */ if (!(f->fw_flg & IPV6_FW_F_OUT)) continue; } else { /* Check direction inbound */ if (!(f->fw_flg & IPV6_FW_F_IN)) continue; } #define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\ (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \ (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \ (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \ (((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3])) /* If src-addr doesn't match, not this rule. */ if (((f->fw_flg & IPV6_FW_F_INVSRC) != 0) ^ (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_src,&f->fw_smsk,&f->fw_src))) continue; /* If dest-addr doesn't match, not this rule. */ if (((f->fw_flg & IPV6_FW_F_INVDST) != 0) ^ (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_dst,&f->fw_dmsk,&f->fw_dst))) continue; #undef IN6_ARE_ADDR_MASKEQUAL /* Interface check */ if ((f->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { struct ifnet *const iface = oif ? oif : rif; /* Backwards compatibility hack for "via" */ if (!iface || !iface_match(iface, &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME)) continue; } else { /* Check receive interface */ if ((f->fw_flg & IPV6_FW_F_IIFACE) && (!rif || !iface_match(rif, &f->fw_in_if, f->fw_flg & IPV6_FW_F_IIFNAME))) continue; /* Check outgoing interface */ if ((f->fw_flg & IPV6_FW_F_OIFACE) && (!oif || !iface_match(oif, &f->fw_out_if, f->fw_flg & IPV6_FW_F_OIFNAME))) continue; } /* Check IP options */ if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset)) continue; /* Fragments */ if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset) continue; /* Check protocol; if wildcard, match */ if (f->fw_prot == IPPROTO_IPV6) goto got_match; /* If different, don't match */ if (nxt != f->fw_prot) continue; #define PULLUP_TO(len) do { \ if ((*m)->m_len < (len) \ && (*m = m_pullup(*m, (len))) == 0) { \ goto dropit; \ } \ *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \ } while (/*CONSTCOND*/ 0) /* Protocol specific checks */ switch (nxt) { case IPPROTO_TCP: { struct tcphdr *tcp6; if (offset == 1) { /* cf. RFC 1858 */ PULLUP_TO(off + 4); /* XXX ? */ goto bogusfrag; } if (offset != 0) { /* * TCP flags and ports aren't available in this * packet -- if this rule specified either one, * we consider the rule a non-match. */ if (f->fw_nports != 0 || f->fw_tcpf != f->fw_tcpnf) continue; break; } PULLUP_TO(off + 14); tcp6 = (struct tcphdr *) ((caddr_t)ip6 + off); if (((f->fw_tcpf != f->fw_tcpnf) || (f->fw_ipflg & IPV6_FW_IF_TCPEST)) && !tcp6flg_match(tcp6, f)) continue; src_port = ntohs(tcp6->th_sport); dst_port = ntohs(tcp6->th_dport); goto check_ports; } case IPPROTO_UDP: { struct udphdr *udp; if (offset != 0) { /* * Port specification is unavailable -- if this * rule specifies a port, we consider the rule * a non-match. */ if (f->fw_nports != 0) continue; break; } PULLUP_TO(off + 4); udp = (struct udphdr *) ((caddr_t)ip6 + off); src_port = ntohs(udp->uh_sport); dst_port = ntohs(udp->uh_dport); check_ports: if (!port_match6(&f->fw_pts[0], IPV6_FW_GETNSRCP(f), src_port, f->fw_flg & IPV6_FW_F_SRNG)) continue; if (!port_match6(&f->fw_pts[IPV6_FW_GETNSRCP(f)], IPV6_FW_GETNDSTP(f), dst_port, f->fw_flg & IPV6_FW_F_DRNG)) continue; break; } case IPPROTO_ICMPV6: { struct icmp6_hdr *icmp; if (offset != 0) /* Type isn't valid */ break; PULLUP_TO(off + 2); icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off); if (!icmp6type_match(icmp, f)) continue; break; } #undef PULLUP_TO bogusfrag: if (fw6_verbose) ip6fw_report(NULL, ip6, rif, oif, off, nxt); goto dropit; } got_match: #ifndef IP6FW_DIVERT_RESTART /* Ignore divert/tee rule if socket port is "ignport" */ switch (f->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_DIVERT: case IPV6_FW_F_TEE: if (f->fw_divert_port == ignport) continue; /* ignore this rule */ break; } #endif /* IP6FW_DIVERT_RESTART */ /* Update statistics */ f->fw_pcnt += 1; f->fw_bcnt += ntohs(ip6->ip6_plen); #ifdef __FreeBSD__ f->timestamp = time_second; #else f->timestamp = time.tv_sec; #endif /* Log to console if desired */ if ((f->fw_flg & IPV6_FW_F_PRN) && fw6_verbose) ip6fw_report(f, ip6, rif, oif, off, nxt); /* Take appropriate action */ switch (f->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_ACCEPT: return (0); case IPV6_FW_F_COUNT: continue; case IPV6_FW_F_DIVERT: return (f->fw_divert_port); case IPV6_FW_F_TEE: /* * XXX someday tee packet here, but beware that you * can't use m_copym() or m_copypacket() because * the divert input routine modifies the mbuf * (and these routines only increment reference * counts in the case of mbuf clusters), so need * to write custom routine. */ continue; case IPV6_FW_F_SKIPTO: #ifdef DIAGNOSTIC while (chain->chain.le_next && chain->chain.le_next->rule->fw_number < f->fw_skipto_rule) #else while (chain->chain.le_next->rule->fw_number < f->fw_skipto_rule) #endif chain = chain->chain.le_next; continue; } /* Deny/reject this packet using this rule */ rule = f; break; } #ifdef DIAGNOSTIC /* Rule 65535 should always be there and should always match */ if (!chain) panic("ip6_fw: chain"); #endif /* * At this point, we're going to drop the packet. * Send a reject notice if all of the following are true: * * - The packet matched a reject rule * - The packet is not an ICMP packet, or is an ICMP query packet * - The packet is not a multicast or broadcast packet */ if ((rule->fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT && (nxt != IPPROTO_ICMPV6 || is_icmp6_query(ip6, off)) && !((*m)->m_flags & (M_BCAST|M_MCAST)) && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { switch (rule->fw_reject_code) { case IPV6_FW_REJECT_RST: #if 1 /* not tested */ { struct tcphdr *const tcp = (struct tcphdr *) ((caddr_t)ip6 + off); struct { struct ip6_hdr ip6; struct tcphdr th; } ti; tcp_seq ack, seq; int flags; if (offset != 0 || (tcp->th_flags & TH_RST)) break; ti.ip6 = *ip6; ti.th = *tcp; ti.th.th_seq = ntohl(ti.th.th_seq); ti.th.th_ack = ntohl(ti.th.th_ack); ti.ip6.ip6_nxt = IPPROTO_TCP; if (ti.th.th_flags & TH_ACK) { ack = 0; seq = ti.th.th_ack; flags = TH_RST; } else { ack = ti.th.th_seq; if (((*m)->m_flags & M_PKTHDR) != 0) { ack += (*m)->m_pkthdr.len - off - (ti.th.th_off << 2); } else if (ip6->ip6_plen) { ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) - off - (ti.th.th_off << 2); } else { m_freem(*m); *m = 0; break; } seq = 0; flags = TH_RST|TH_ACK; } bcopy(&ti, ip6, sizeof(ti)); #ifdef __NetBSD__ tcp_respond(NULL, NULL, *m, (struct tcphdr *)(ip6 + 1), ack, seq, flags); #elif defined(__FreeBSD__) tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), *m, ack, seq, flags); #else m_freem(*m); #endif *m = NULL; break; } #endif default: /* Send an ICMP unreachable using code */ if (oif) (*m)->m_pkthdr.rcvif = oif; icmp6_error(*m, ICMP6_DST_UNREACH, rule->fw_reject_code, 0); *m = NULL; break; } } dropit: /* * Finally, drop the packet. */ if (*m) { m_freem(*m); *m = NULL; } return (0); }
struct mbuf * ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error, type = 0, code = 0; boolean_t proxy = FALSE; struct mbuf *mcopy = NULL; struct ifnet *ifp, *origifp; /* maybe unnecessary */ u_int32_t inzone, outzone; struct in6_addr src_in6, dst_in6; #if IPSEC struct secpolicy *sp = NULL; #endif struct timeval timenow; unsigned int ifscope = IFSCOPE_NONE; #if PF struct pf_mtag *pf_mtag; #endif /* PF */ getmicrotime(&timenow); #if PF pf_mtag = pf_find_mtag(m); if (pf_mtag != NULL && pf_mtag->pftag_rtableid != IFSCOPE_NONE) ifscope = pf_mtag->pftag_rtableid; /* * If the caller provides a route which is on a different interface * than the one specified for scoped forwarding, discard the route * and do a lookup below. */ if (ifscope != IFSCOPE_NONE && (rt = ip6forward_rt->ro_rt) != NULL) { RT_LOCK(rt); if (rt->rt_ifp->if_index != ifscope) { RT_UNLOCK(rt); rtfree(rt); rt = ip6forward_rt->ro_rt = NULL; } else { RT_UNLOCK(rt); } } #endif /* PF */ #if IPSEC /* * Check AH/ESP integrity. */ /* * Don't increment ip6s_cantforward because this is the check * before forwarding packet actually. */ if (ipsec_bypass == 0) { if (ipsec6_in_reject(m, NULL)) { IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); m_freem(m); return (NULL); } } #endif /*IPSEC*/ /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < timenow.tv_sec) { ip6_log_time = timenow.tv_sec; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } m_freem(m); return (NULL); } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return (NULL); } /* * See if the destination is a proxied address, and if so pretend * that it's for us. This is mostly to handle NUD probes against * the proxied addresses. We filter for ICMPv6 here and will let * icmp6_input handle the rest. */ if (!srcrt && nd6_prproxy) { VERIFY(!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)); proxy = nd6_prproxy_isours(m, ip6, ip6forward_rt, ifscope); /* * Don't update hop limit while proxying; RFC 4389 4.1. * Also skip IPsec forwarding path processing as this * packet is not to be forwarded. */ if (proxy) goto skip_ipsec; } ip6->ip6_hlim -= IPV6_HLIMDEC; /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #if IPSEC if (ipsec_bypass != 0) goto skip_ipsec; /* get a security policy for this packet */ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &error); if (sp == NULL) { IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: case IPSEC_POLICY_GENERATE: /* * This packet is just discarded. */ IPSEC_STAT_INCREMENT(ipsec6stat.out_polvio); ip6stat.ip6s_cantforward++; key_freesp(sp, KEY_SADB_UNLOCKED); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ key_freesp(sp, KEY_SADB_UNLOCKED); goto skip_ipsec; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; key_freesp(sp, KEY_SADB_UNLOCKED); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); } /* do IPsec */ break; case IPSEC_POLICY_ENTRUST: default: /* should be panic ?? */ printf("ip6_forward: Invalid policy found. %d\n", sp->policy); key_freesp(sp, KEY_SADB_UNLOCKED); goto skip_ipsec; } { struct ipsec_output_state state; /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers * on inner IPv6 packet (since they are now encapsulated). * * IPv6 [ESP|AH] IPv6 [extension headers] payload */ bzero(&state, sizeof(state)); state.m = m; state.dst = NULL; /* update at ipsec6_output_tunnel() */ error = ipsec6_output_tunnel(&state, sp, 0); key_freesp(sp, KEY_SADB_UNLOCKED); if (state.tunneled == 4) return (NULL); /* packet is gone - sent over IPv4 */ m = state.m; if (state.ro.ro_rt) { rtfree(state.ro.ro_rt); state.ro.ro_rt = NULL; } if (error) { /* mbuf is already reclaimed in ipsec6_output_tunnel. */ switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip6_output (ipsec): error code %d\n", error); /* fall through */ case ENOENT: /* don't show these error codes to the user */ break; } ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return (NULL); } } skip_ipsec: #endif /* IPSEC */ dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst; if ((rt = ip6forward_rt->ro_rt) != NULL) { RT_LOCK(rt); /* Take an extra ref for ourselves */ RT_ADDREF_LOCKED(rt); } if (!srcrt) { /* * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (rt == NULL || !(rt->rt_flags & RTF_UP) || rt->generation_id != route_generation) { if (rt != NULL) { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); rtfree(rt); ip6forward_rt->ro_rt = NULL; } /* this probably fails but give it a try again */ rtalloc_scoped_ign((struct route *)ip6forward_rt, RTF_PRCLONING, ifscope); if ((rt = ip6forward_rt->ro_rt) != NULL) { RT_LOCK(rt); /* Take an extra ref for ourselves */ RT_ADDREF_LOCKED(rt); } } if (rt == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); m_freem(m); return (NULL); } RT_LOCK_ASSERT_HELD(rt); } else if (rt == NULL || !(rt->rt_flags & RTF_UP) || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) || rt->generation_id != route_generation) { if (rt != NULL) { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); rtfree(rt); ip6forward_rt->ro_rt = NULL; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; rtalloc_scoped_ign((struct route *)ip6forward_rt, RTF_PRCLONING, ifscope); if ((rt = ip6forward_rt->ro_rt) == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); m_freem(m); return (NULL); } RT_LOCK(rt); /* Take an extra ref for ourselves */ RT_ADDREF_LOCKED(rt); } /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address) unless we are proxying (source address is link local * for NUDs.) We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1] */ src_in6 = ip6->ip6_src; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) { /* XXX: this should not happen */ ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; m_freem(m); return (NULL); } if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; m_freem(m); return (NULL); } if (inzone != outzone && !proxy) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (ip6_log_time + ip6_log_interval < timenow.tv_sec) { ip6_log_time = timenow.tv_sec; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); } m_freem(m); return (NULL); } /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; m_freem(m); return (NULL); } if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { uint32_t mtu; #if IPSEC struct secpolicy *sp2; int ipsecerror; size_t ipsechdrsiz; #endif mtu = rt->rt_ifp->if_mtu; #if IPSEC /* * When we do IPsec tunnel ingress, we need to play * with the link value (decrement IPsec header size * from mtu value). The code is much simpler than v4 * case, as we have the outgoing interface for * encapsulated packet as "rt->rt_ifp". */ sp2 = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &ipsecerror); if (sp2) { ipsechdrsiz = ipsec6_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND, NULL); if (ipsechdrsiz < mtu) mtu -= ipsechdrsiz; key_freesp(sp2, KEY_SADB_UNLOCKED); } /* * if mtu becomes less than minimum MTU, * tell minimum MTU (and I'll need to fragment it). */ if (mtu < IPV6_MMTU) mtu = IPV6_MMTU; #endif /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } else { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); } m_freem(m); return (NULL); } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)(void *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (!proxy && ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { /* * If the incoming interface is equal to the outgoing * one, and the link attached to the interface is * point-to-point, then it will be highly probable * that a routing loop occurs. Thus, we immediately * drop the packet and send an ICMPv6 error message. * * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ RT_REMREF_LOCKED(rt); /* Release extra ref */ RT_UNLOCK(rt); icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); return (NULL); } type = ND_REDIRECT; } #if IPFW2 /* * Check with the firewall... */ if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; ifp = rt->rt_ifp; /* Drop the lock but retain the extra ref */ RT_UNLOCK(rt); /* If ipfw says divert, we have to just drop packet */ if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) { m_freem(m); goto freecopy; } if (!m) { goto freecopy; } /* We still have the extra ref on rt */ RT_LOCK(rt); } #endif /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); ifp = rt->rt_ifp; /* Drop the lock but retain the extra ref */ RT_UNLOCK(rt); /* * If this is to be processed locally, let ip6_input have it. */ if (proxy) { VERIFY(m->m_pkthdr.aux_flags & MAUXF_PROXY_DST); /* Release extra ref */ RT_REMREF(rt); if (mcopy != NULL) m_freem(mcopy); return (m); } #if PF /* Invoke outbound packet filter */ error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE, NULL); if (error != 0 || m == NULL) { if (m != NULL) { panic("%s: unexpected packet %p\n", __func__, m); /* NOTREACHED */ } /* Already freed by callee */ goto senderr; } ip6 = mtod(m, struct ip6_hdr *); #endif /* PF */ error = nd6_output(ifp, origifp, m, dst, rt, NULL); if (error) { in6_ifstat_inc(ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; } else { ip6stat.ip6s_forward++; in6_ifstat_inc(ifp, ifs6_out_forward); if (type) ip6stat.ip6s_redirectsent++; else { if (mcopy) { goto freecopy; } } } #if PF senderr: #endif /* PF */ if (mcopy == NULL) { /* Release extra ref */ RT_REMREF(rt); return (NULL); } switch (error) { case 0: #if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); /* Release extra ref */ RT_REMREF(rt); return (NULL); } #endif goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); /* Release extra ref */ RT_REMREF(rt); return (NULL); freecopy: m_freem(mcopy); /* Release extra ref */ RT_REMREF(rt); return (NULL); }
/* * Fragment input * NOTE: this function is called with the inet6_domain_mutex held from ip6_input. * inet6_domain_mutex is protecting he frag6 queue manipulation. */ int frag6_input(struct mbuf **mp, int *offp, int proto) { #pragma unused(proto) struct mbuf *m = *mp, *t; struct ip6_hdr *ip6; struct ip6_frag *ip6f; struct ip6q *q6; struct ip6asfrag *af6, *ip6af, *af6dwn; int offset = *offp, nxt, i, next; int first_frag = 0; int fragoff, frgpartlen; /* must be larger than u_int16_t */ struct ifnet *dstifp; struct ifaddr *ifa = NULL; u_int8_t ecn, ecn0; #ifdef IN6_IFSTAT_STRICT struct route_in6 ro; struct sockaddr_in6 *dst; #endif /* Expect 32-bit aligned data pointer on strict-align platforms */ MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), return IPPROTO_DONE); ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); #else IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); if (ip6f == NULL) return IPPROTO_DONE; #endif dstifp = NULL; #ifdef IN6_IFSTAT_STRICT /* find the destination interface of the packet. */ bzero(&ro, sizeof (ro)); dst = (struct sockaddr_in6 *)&ro.ro_dst; dst->sin6_family = AF_INET6; dst->sin6_len = sizeof (struct sockaddr_in6); dst->sin6_addr = ip6->ip6_dst; rtalloc((struct route *)&ro); if (ro.ro_rt != NULL) { RT_LOCK(ro.ro_rt); if ((ifa = ro.ro_rt->rt_ifa) != NULL) { IFA_ADDREF(ifa); dstifp = ((struct in6_ifaddr *)ro.ro_rt->rt_ifa)->ia_ifp; } RT_UNLOCK(ro.ro_rt); rtfree(ro.ro_rt); ro.ro_rt = NULL; } #else /* we are violating the spec, this is not the destination interface */ if ((m->m_flags & M_PKTHDR) != 0) dstifp = m->m_pkthdr.rcvif; #endif /* jumbo payload can't contain a fragment header */ if (ip6->ip6_plen == 0) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); in6_ifstat_inc(dstifp, ifs6_reass_fail); if (ifa != NULL) IFA_REMREF(ifa); return IPPROTO_DONE; } /* * check whether fragment packet's fragment length is * multiple of 8 octets. * sizeof(struct ip6_frag) == 8 * sizeof(struct ip6_hdr) = 40 */ if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offsetof(struct ip6_hdr, ip6_plen)); in6_ifstat_inc(dstifp, ifs6_reass_fail); if (ifa != NULL) IFA_REMREF(ifa); return IPPROTO_DONE; } ip6stat.ip6s_fragments++; in6_ifstat_inc(dstifp, ifs6_reass_reqd); /* offset now points to data portion */ offset += sizeof(struct ip6_frag); frag6_doing_reass = 1; /* * Enforce upper bound on number of fragments. * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ if (ip6_maxfrags < 0) ; else if (frag6_nfrags >= (u_int)ip6_maxfrags) goto dropfrag; for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst)) break; if (q6 == &ip6q) { /* * the first fragment to arrive, create a reassembly queue. */ first_frag = 1; /* * Enforce upper bound on number of fragmented packets * for which we attempt reassembly; * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ if (ip6_maxfragpackets < 0) ; else if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) goto dropfrag; frag6_nfragpackets++; q6 = (struct ip6q *)_MALLOC(sizeof(struct ip6q), M_FTABLE, M_DONTWAIT); if (q6 == NULL) goto dropfrag; bzero(q6, sizeof(*q6)); frag6_insque(q6, &ip6q); /* ip6q_nxt will be filled afterwards, from 1st fragment */ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; #ifdef notyet q6->ip6q_nxtp = (u_char *)nxtp; #endif q6->ip6q_ident = ip6f->ip6f_ident; q6->ip6q_ttl = IPV6_FRAGTTL; q6->ip6q_src = ip6->ip6_src; q6->ip6q_dst = ip6->ip6_dst; q6->ip6q_ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ q6->ip6q_nfrag = 0; }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error = 0, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ #ifdef IPSEC u_int8_t sproto = 0; struct m_tag *mtag; union sockaddr_union sdst; struct tdb_ident *tdbi; u_int32_t sspi; struct tdb *tdb; int s; #if NPF > 0 struct ifnet *encif; #endif #endif /* IPSEC */ u_int rtableid = 0; /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, m->m_pkthdr.rcvif->if_xname); } m_freem(m); return; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; #if NPF > 0 reroute: #endif #ifdef IPSEC if (!ipsec_in_use) goto done_spd; s = splnet(); /* * Check if there was an outgoing SA bound to the flow * from a transport protocol. */ /* Do we have any pending SAs to apply ? */ mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL); if (mtag != NULL) { #ifdef DIAGNOSTIC if (mtag->m_tag_len != sizeof (struct tdb_ident)) panic("ip6_forward: tag of length %d (should be %d", mtag->m_tag_len, sizeof (struct tdb_ident)); #endif tdbi = (struct tdb_ident *)(mtag + 1); tdb = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst, tdbi->proto); if (tdb == NULL) error = -EINVAL; m_tag_delete(m, mtag); } else tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr), &error, IPSP_DIRECTION_OUT, NULL, NULL); if (tdb == NULL) { splx(s); if (error == 0) { /* * No IPsec processing required, we'll just send the * packet out. */ sproto = 0; /* Fall through to routing/multicast handling */ } else { /* * -EINVAL is used to indicate that the packet should * be silently dropped, typically because we've asked * key management for an SA. */ if (error == -EINVAL) /* Should silently drop packet */ error = 0; goto freecopy; } } else { /* Loop detection */ for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) { if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE && mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED) continue; tdbi = (struct tdb_ident *)(mtag + 1); if (tdbi->spi == tdb->tdb_spi && tdbi->proto == tdb->tdb_sproto && tdbi->rdomain == tdb->tdb_rdomain && !bcmp(&tdbi->dst, &tdb->tdb_dst, sizeof(union sockaddr_union))) { splx(s); sproto = 0; /* mark as no-IPsec-needed */ goto done_spd; } } /* We need to do IPsec */ bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst)); sspi = tdb->tdb_spi; sproto = tdb->tdb_sproto; splx(s); } /* Fall through to the routing/multicast handling code */ done_spd: #endif /* IPSEC */ #if NPF > 0 rtableid = m->m_pkthdr.rdomain; #endif /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); dst = &ip6_forward_rt.ro_dst; if (!srcrt) { /* * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (ip6_forward_rt.ro_rt == 0 || (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 || ip6_forward_rt.ro_tableid != rtableid) { if (ip6_forward_rt.ro_rt) { RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } /* this probably fails but give it a try again */ ip6_forward_rt.ro_tableid = rtableid; rtalloc_mpath((struct route *)&ip6_forward_rt, &ip6->ip6_src.s6_addr32[0]); } if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } else if (ip6_forward_rt.ro_rt == 0 || (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) || ip6_forward_rt.ro_tableid != rtableid) { if (ip6_forward_rt.ro_rt) { RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; ip6_forward_rt.ro_tableid = rtableid; rtalloc_mpath((struct route *)&ip6_forward_rt, &ip6->ip6_src.s6_addr32[0]); if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } rt = ip6_forward_rt.ro_rt; /* * Scope check: if a packet can't be delivered to its destination * for the reason that the destination is beyond the scope of the * source address, discard the packet and return an icmp6 destination * unreachable error with Code 2 (beyond scope of source address). * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] */ if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, m->m_pkthdr.rcvif->if_xname, rt->rt_ifp->if_xname); } if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); goto freert; } #ifdef IPSEC /* * Check if the packet needs encapsulation. * ipsp_process_packet will never come back to here. * XXX ipsp_process_packet() calls ip6_output(), and there'll be no * PMTU notification. is it okay? */ if (sproto != 0) { s = splnet(); tdb = gettdb(rtable_l2(m->m_pkthdr.rdomain), sspi, &sdst, sproto); if (tdb == NULL) { splx(s); error = EHOSTUNREACH; m_freem(m); goto senderr; /*XXX*/ } #if NPF > 0 if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL || pf_test6(PF_FWD, encif, &m, NULL) != PF_PASS) { splx(s); error = EHOSTUNREACH; m_freem(m); goto senderr; } if (m == NULL) { splx(s); goto senderr; } ip6 = mtod(m, struct ip6_hdr *); /* * PF_TAG_REROUTE handling or not... * Packet is entering IPsec so the routing is * already overruled by the IPsec policy. * Until now the change was not reconsidered. * What's the behaviour? */ #endif m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ /* Callee frees mbuf */ error = ipsp_process_packet(m, tdb, AF_INET6, 0); splx(s); m_freem(mcopy); goto freert; }
int route6_input(struct mbuf **mp, int *offp, int proto) { struct ip6_hdr *ip6; struct mbuf *m = *mp; struct ip6_rthdr *rh; int off = *offp, rhlen; ip6 = mtod(m, struct ip6_hdr *); IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); if (rh == NULL) { IP6_STATINC(IP6_STAT_TOOSHORT); return IPPROTO_DONE; } switch (rh->ip6r_type) { #if 0 /* * See http://www.secdev.org/conf/IPv6_RH_security-csw07.pdf * for why IPV6_RTHDR_TYPE_0 is banned here. * * We return ICMPv6 parameter problem so that innocent people * (not an attacker) would notice about the use of IPV6_RTHDR_TYPE_0. * Since there's no amplification, and ICMPv6 error will be rate- * controlled, it shouldn't cause any problem. * If you are concerned about this, you may want to use the following * code fragment: * * case IPV6_RTHDR_TYPE_0: * m_freem(m); * return (IPPROTO_DONE); */ case IPV6_RTHDR_TYPE_0: rhlen = (rh->ip6r_len + 1) << 3; /* * note on option length: * maximum rhlen: 2048 * max mbuf m_pulldown can handle: MCLBYTES == usually 2048 * so, here we are assuming that m_pulldown can handle * rhlen == 2048 case. this may not be a good thing to * assume - we may want to avoid pulling it up altogether. */ IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen); if (rh == NULL) { IP6_STATINC(IP6_STAT_TOOSHORT); return IPPROTO_DONE; } if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) return (IPPROTO_DONE); break; #endif default: /* unknown routing type */ if (rh->ip6r_segleft == 0) { rhlen = (rh->ip6r_len + 1) << 3; break; /* Final dst. Just ignore the header. */ } IP6_STATINC(IP6_STAT_BADOPTIONS); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (char *)&rh->ip6r_type - (char *)ip6); return (IPPROTO_DONE); } *offp += rhlen; return (rh->ip6r_nxt); }
/* * Forward a packet. If some error occurs return the sender * an icmp packet. Note we can't always generate a meaningful * icmp message because icmp doesn't have a large enough repertoire * of codes and types. * * If not forwarding, just drop the packet. This could be confusing * if ipforwarding was zero but some routing protocol was advancing * us as a gateway to somewhere. However, we must let the routing * protocol deal with that. * */ void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst = NULL; struct rtentry *rt = NULL; struct route_in6 rin6; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ u_int32_t inzone, outzone; struct in6_addr src_in6, dst_in6, odst; struct m_tag *fwd_tag; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on the ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { IP6STAT_INC(ip6s_cantforward); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (V_ip6_log_time + V_ip6_log_interval < time_uptime) { V_ip6_log_time = time_uptime; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } m_freem(m); return; } if ( #ifdef IPSTEALTH V_ip6stealth == 0 && #endif ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN), M_NOWAIT); #ifdef IPSTEALTH if (V_ip6stealth == 0) #endif ip6->ip6_hlim -= IPV6_HLIMDEC; #if defined(IPSEC) || defined(IPSEC_SUPPORT) if (IPSEC_ENABLED(ipv6)) { if ((error = IPSEC_FORWARD(ipv6, m)) != 0) { /* mbuf consumed by IPsec */ m_freem(mcopy); if (error != EINPROGRESS) IP6STAT_INC(ip6s_cantforward); return; } /* No IPsec processing required */ } #endif again: bzero(&rin6, sizeof(struct route_in6)); dst = (struct sockaddr_in6 *)&rin6.ro_dst; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; again2: rin6.ro_rt = in6_rtalloc1((struct sockaddr *)dst, 0, 0, M_GETFIB(m)); rt = rin6.ro_rt; if (rin6.ro_rt != NULL) RT_UNLOCK(rin6.ro_rt); else { IP6STAT_INC(ip6s_noroute); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } goto bad; } /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address). We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1] */ src_in6 = ip6->ip6_src; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) { /* XXX: this should not happen */ IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); goto bad; } if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) { IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); goto bad; } if (inzone != outzone) { IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (V_ip6_log_time + V_ip6_log_interval < time_uptime) { V_ip6_log_time = time_uptime; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); goto bad; } /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); goto bad; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (V_ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { /* * If the incoming interface is equal to the outgoing * one, and the link attached to the interface is * point-to-point, then it will be highly probable * that a routing loop occurs. Thus, we immediately * drop the packet and send an ICMPv6 error message. * * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); goto bad; } type = ND_REDIRECT; } /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); /* Jump over all PFIL processing if hooks are not active. */ if (!PFIL_HOOKED(&V_inet6_pfil_hook)) goto pass; odst = ip6->ip6_dst; /* Run through list of hooks for output packets. */ error = pfil_run_hooks(&V_inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT, NULL); if (error != 0 || m == NULL) goto freecopy; /* consumed by filter */ ip6 = mtod(m, struct ip6_hdr *); /* See if destination IP address was changed by packet filter. */ if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { m->m_flags |= M_SKIP_FIREWALL; /* If destination is now ourself drop to ip6_input(). */ if (in6_localip(&ip6->ip6_dst)) m->m_flags |= M_FASTFWD_OURS; else { RTFREE(rt); goto again; /* Redo the routing table lookup. */ } } /* See if local, if yes, send it to netisr. */ if (m->m_flags & M_FASTFWD_OURS) { if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif error = netisr_queue(NETISR_IPV6, m); goto out; } /* Or forward to some other address? */ if ((m->m_flags & M_IP6_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { dst = (struct sockaddr_in6 *)&rin6.ro_dst; bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6)); m->m_flags |= M_SKIP_FIREWALL; m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag); RTFREE(rt); goto again2; } pass: /* See if the size was changed by the packet filter. */ if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, IN6_LINKMTU(rt->rt_ifp)); goto bad; } error = nd6_output_ifp(rt->rt_ifp, origifp, m, dst, NULL); if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); IP6STAT_INC(ip6s_cantforward); } else { IP6STAT_INC(ip6s_forward); in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); if (type) IP6STAT_INC(ip6s_redirectsent); else { if (mcopy) goto freecopy; } } if (mcopy == NULL) goto out; switch (error) { case 0: if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); goto out; } goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); goto out; freecopy: m_freem(mcopy); goto out; bad: m_freem(m); out: if (rt != NULL) RTFREE(rt); }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ #ifdef IPSEC struct secpolicy *sp = NULL; #endif #ifdef IPSEC /* * Check AH/ESP integrity. */ /* * Don't increment ip6s_cantforward because this is the check * before forwarding packet actually. */ if (ipsec6_in_reject(m, NULL)) { #if !defined(FAST_IPSEC) ipsec6stat.in_polvio++; #endif m_freem(m); return; } #endif /* IPSEC */ /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST | M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } m_freem(m); return; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #ifdef IPSEC /* get a security policy for this packet */ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &error); if (sp == NULL) { ipsec6stat.out_inval++; ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: /* * This packet is just discarded. */ ipsec6stat.out_polvio++; ip6stat.ip6s_cantforward++; key_freesp(sp); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ key_freesp(sp); goto skip_ipsec; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ kprintf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; key_freesp(sp); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; } /* do IPsec */ break; case IPSEC_POLICY_ENTRUST: default: /* should be panic ?? */ kprintf("ip6_forward: Invalid policy found. %d\n", sp->policy); key_freesp(sp); goto skip_ipsec; } { struct ipsec_output_state state; /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers * on inner IPv6 packet (since they are now encapsulated). * * IPv6 [ESP|AH] IPv6 [extension headers] payload */ bzero(&state, sizeof(state)); state.m = m; state.ro = NULL; /* update at ipsec6_output_tunnel() */ state.dst = NULL; /* update at ipsec6_output_tunnel() */ error = ipsec6_output_tunnel(&state, sp, 0); m = state.m; key_freesp(sp); if (error) { /* mbuf is already reclaimed in ipsec6_output_tunnel. */ switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: kprintf("ip6_output (ipsec): error code %d\n", error); /* fall through */ case ENOENT: /* don't show these error codes to the user */ break; } ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; } } skip_ipsec: #endif /* IPSEC */ dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; if (!srcrt) { /* * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (ip6_forward_rt.ro_rt == NULL || !(ip6_forward_rt.ro_rt->rt_flags & RTF_UP)) { if (ip6_forward_rt.ro_rt) { RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } /* this probably fails but give it a try again */ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); } if (ip6_forward_rt.ro_rt == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } else if ((rt = ip6_forward_rt.ro_rt) == NULL || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) { if (ip6_forward_rt.ro_rt) { RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); if (ip6_forward_rt.ro_rt == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } rt = ip6_forward_rt.ro_rt; /* * Scope check: if a packet can't be delivered to its destination * for the reason that the destination is beyond the scope of the * source address, discard the packet and return an icmp6 destination * unreachable error with Code 2 (beyond scope of source address). * [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1] */ if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { u_long mtu; #ifdef IPSEC struct secpolicy *sp; int ipsecerror; size_t ipsechdrsiz; #endif mtu = rt->rt_ifp->if_mtu; #ifdef IPSEC /* * When we do IPsec tunnel ingress, we need to play * with if_mtu value (decrement IPsec header size * from mtu value). The code is much simpler than v4 * case, as we have the outgoing interface for * encapsulated packet as "rt->rt_ifp". */ sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &ipsecerror); if (sp) { ipsechdrsiz = ipsec6_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND, NULL); if (ipsechdrsiz < mtu) mtu -= ipsechdrsiz; } /* * if mtu becomes less than minimum MTU, * tell minimum MTU (and I'll need to fragment it). */ if (mtu < IPV6_MMTU) mtu = IPV6_MMTU; #endif icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } m_freem(m); return; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && (rt->rt_flags & (RTF_DYNAMIC | RTF_MODIFIED)) == 0) { if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) { /* * If the incoming interface is equal to the outgoing * one, and the link attached to the interface is * point-to-point, then it will be highly probable * that a routing loop occurs. Thus, we immediately * drop the packet and send an ICMPv6 error message. * * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); return; } type = ND_REDIRECT; } /* * Check with the firewall... */ if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; /* If ipfw says divert, we have to just drop packet */ if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) { m_freem(m); goto freecopy; } if (!m) goto freecopy; } /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if (rt->rt_ifp->if_flags & IFF_LOOPBACK) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE | RTF_REJECT)) == 0) #endif { kprintf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); /* * Run through list of hooks for output packets. */ if (pfil_has_hooks(&inet6_pfil_hook)) { error = pfil_run_hooks(&inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT); if (error != 0) goto senderr; if (m == NULL) goto freecopy; ip6 = mtod(m, struct ip6_hdr *); }
struct mbuf* ip6_tryforward(struct mbuf *m) { struct sockaddr_in6 dst; struct nhop6_basic nh; struct m_tag *fwd_tag; struct ip6_hdr *ip6; struct ifnet *rcvif; uint32_t plen; int error; /* * Fallback conditions to ip6_input for slow path processing. */ ip6 = mtod(m, struct ip6_hdr *); if (ip6->ip6_nxt == IPPROTO_HOPOPTS || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) || IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) || in6_localip(&ip6->ip6_dst)) return (m); /* * Check that the amount of data in the buffers * is as at least much as the IPv6 header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ rcvif = m->m_pkthdr.rcvif; plen = ntohs(ip6->ip6_plen); if (plen == 0) { /* * Jumbograms must have hop-by-hop header and go via * slow path. */ IP6STAT_INC(ip6s_badoptions); goto dropin; } if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { IP6STAT_INC(ip6s_tooshort); in6_ifstat_inc(rcvif, ifs6_in_truncated); goto dropin; } if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { if (m->m_len == m->m_pkthdr.len) { m->m_len = sizeof(struct ip6_hdr) + plen; m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; } else m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len); } /* * Hop limit. */ #ifdef IPSTEALTH if (!V_ip6stealth) #endif if (ip6->ip6_hlim <= IPV6_HLIMDEC) { icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); m = NULL; goto dropin; } bzero(&dst, sizeof(dst)); dst.sin6_family = AF_INET6; dst.sin6_len = sizeof(dst); dst.sin6_addr = ip6->ip6_dst; /* * Incoming packet firewall processing. */ if (!PFIL_HOOKED(&V_inet6_pfil_hook)) goto passin; if (pfil_run_hooks(&V_inet6_pfil_hook, &m, rcvif, PFIL_IN, NULL) != 0 || m == NULL) goto dropin; /* * If packet filter sets the M_FASTFWD_OURS flag, this means * that new destination or next hop is our local address. * So, we can just go back to ip6_input. * XXX: should we decrement ip6_hlim in such case? * * Also it can forward packet to another destination, e.g. * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf. */ if (m->m_flags & M_FASTFWD_OURS) return (m); ip6 = mtod(m, struct ip6_hdr *); if ((m->m_flags & M_IP6_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { /* * Now we will find route to forwarded by pfil destination. */ bcopy((fwd_tag + 1), &dst, sizeof(dst)); m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag); } else { /* Update dst since pfil could change it */ dst.sin6_addr = ip6->ip6_dst; } passin: /* * Find route to destination. */ if (ip6_findroute(&nh, &dst, m) != 0) { m = NULL; in6_ifstat_inc(rcvif, ifs6_in_noroute); goto dropin; } /* * We used slow path processing for packets with scoped addresses. * So, scope checks aren't needed here. */ if (m->m_pkthdr.len > nh.nh_mtu) { in6_ifstat_inc(nh.nh_ifp, ifs6_in_toobig); icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu); m = NULL; goto dropout; } /* * Outgoing packet firewall processing. */ if (!PFIL_HOOKED(&V_inet6_pfil_hook)) goto passout; if (pfil_run_hooks(&V_inet6_pfil_hook, &m, nh.nh_ifp, PFIL_OUT, NULL) != 0 || m == NULL) goto dropout; /* * If packet filter sets the M_FASTFWD_OURS flag, this means * that new destination or next hop is our local address. * So, we can just go back to ip6_input. * * Also it can forward packet to another destination, e.g. * M_IP6_NEXTHOP flag is set and fwd_tag is attached to mbuf. */ if (m->m_flags & M_FASTFWD_OURS) { /* * XXX: we did one hop and should decrement hop limit. But * now we are the destination and just don't pay attention. */ return (m); } /* * Again. A packet filter could change the destination address. */ ip6 = mtod(m, struct ip6_hdr *); if (m->m_flags & M_IP6_NEXTHOP) fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); else fwd_tag = NULL; if (fwd_tag != NULL || !IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) { if (fwd_tag != NULL) { bcopy((fwd_tag + 1), &dst, sizeof(dst)); m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag); } else dst.sin6_addr = ip6->ip6_dst; /* * Redo route lookup with new destination address */ if (ip6_findroute(&nh, &dst, m) != 0) { m = NULL; goto dropout; } } passout: #ifdef IPSTEALTH if (!V_ip6stealth) #endif { ip6->ip6_hlim -= IPV6_HLIMDEC; } m_clrprotoflags(m); /* Avoid confusing lower layers. */ IP_PROBE(send, NULL, NULL, ip6, nh.nh_ifp, NULL, ip6); /* * XXX: we need to use destination address with embedded scope * zone id, because LLTABLE uses such form of addresses for lookup. */ dst.sin6_addr = nh.nh_addr; if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6_addr)) dst.sin6_addr.s6_addr16[1] = htons(nh.nh_ifp->if_index & 0xffff); error = (*nh.nh_ifp->if_output)(nh.nh_ifp, m, (struct sockaddr *)&dst, NULL); if (error != 0) { in6_ifstat_inc(nh.nh_ifp, ifs6_out_discard); IP6STAT_INC(ip6s_cantforward); } else { in6_ifstat_inc(nh.nh_ifp, ifs6_out_forward); IP6STAT_INC(ip6s_forward); } return (NULL); dropin: in6_ifstat_inc(rcvif, ifs6_in_discard); goto drop; dropout: in6_ifstat_inc(nh.nh_ifp, ifs6_out_discard); drop: if (m != NULL) m_freem(m); return (NULL); }
int udp6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp; struct ip6_hdr *ip6; struct udphdr *uh; struct inpcb *in6p; struct mbuf *opts = NULL; int off = *offp; int plen, ulen; struct sockaddr_in6 udp_in6; struct socket *so; struct inpcbinfo *pcbinfo = &udbinfo[0]; IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); ip6 = mtod(m, struct ip6_hdr *); if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { /* XXX send icmp6 host/port unreach? */ m_freem(m); return IPPROTO_DONE; } udp_stat.udps_ipackets++; plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); uh = (struct udphdr *)((caddr_t)ip6 + off); ulen = ntohs((u_short)uh->uh_ulen); if (plen != ulen) { udp_stat.udps_badlen++; goto bad; } /* * Checksum extended UDP header and data. */ if (uh->uh_sum == 0) udp_stat.udps_nosum++; else if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) { udp_stat.udps_badsum++; goto bad; } if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct inpcb *last, *marker; /* * Deliver a multicast datagram to all sockets * for which the local and remote addresses and ports match * those of the incoming datagram. This allows more than * one process to receive multicasts on the same port. * (This really ought to be done for unicast datagrams as * well, but that would cause problems with existing * applications that open both address-specific sockets and * a wildcard socket listening to the same port -- they would * end up receiving duplicates of every unicast datagram. * Those applications open the multiple sockets to overcome an * inadequacy of the UDP socket interface, but for backwards * compatibility we avoid the problem here rather than * fixing the interface. Maybe 4.5BSD will remedy this?) */ /* * In a case that laddr should be set to the link-local * address (this happens in RIPng), the multicast address * specified in the received packet does not match with * laddr. To cure this situation, the matching is relaxed * if the receiving interface is the same as one specified * in the socket and if the destination multicast address * matches one of the multicast groups specified in the socket. */ /* * Construct sockaddr format source address. */ init_sin6(&udp_in6, m); /* general init */ udp_in6.sin6_port = uh->uh_sport; /* * KAME note: traditionally we dropped udpiphdr from mbuf here. * We need udphdr for IPsec processing so we do that later. */ /* * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ last = NULL; marker = in_pcbmarker(mycpuid); GET_PCBINFO_TOKEN(pcbinfo); LIST_INSERT_HEAD(&pcbinfo->pcblisthead, marker, inp_list); while ((in6p = LIST_NEXT(marker, inp_list)) != NULL) { LIST_REMOVE(marker, inp_list); LIST_INSERT_AFTER(in6p, marker, inp_list); if (in6p->inp_flags & INP_PLACEMARKER) continue; if (!INP_ISIPV6(in6p)) continue; if (in6p->in6p_lport != uh->uh_dport) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) { if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst) && !in6_mcmatch(in6p, &ip6->ip6_dst, m->m_pkthdr.rcvif)) continue; } if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src) || in6p->in6p_fport != uh->uh_sport) continue; } if (last != NULL) { struct mbuf *n; #ifdef IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject_so(m, last->inp_socket)) ipsec6stat.in_polvio++; /* do not inject data into pcb */ else #endif /* IPSEC */ #ifdef FAST_IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject(m, last)) ; else #endif /* FAST_IPSEC */ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { /* * KAME NOTE: do not * m_copy(m, offset, ...) above. * ssb_appendaddr() expects M_PKTHDR, * and m_copy() will copy M_PKTHDR * only if offset is 0. */ so = last->in6p_socket; if ((last->in6p_flags & IN6P_CONTROLOPTS) || (so->so_options & SO_TIMESTAMP)) { ip6_savecontrol(last, &opts, ip6, n); } m_adj(n, off + sizeof(struct udphdr)); lwkt_gettoken(&so->so_rcv.ssb_token); if (ssb_appendaddr(&so->so_rcv, (struct sockaddr *)&udp_in6, n, opts) == 0) { m_freem(n); if (opts) m_freem(opts); udp_stat.udps_fullsock++; } else { sorwakeup(so); } lwkt_reltoken(&so->so_rcv.ssb_token); opts = NULL; } } last = in6p; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids searching * through all pcbs in the common case of a non-shared * port. It assumes that an application will never * clear these options after setting them. */ if ((last->in6p_socket->so_options & (SO_REUSEPORT | SO_REUSEADDR)) == 0) break; } LIST_REMOVE(marker, inp_list); REL_PCBINFO_TOKEN(pcbinfo); if (last == NULL) { /* * No matching pcb found; discard datagram. * (No need to send an ICMP Port Unreachable * for a broadcast or multicast datgram.) */ udp_stat.udps_noport++; udp_stat.udps_noportmcast++; goto bad; } #ifdef IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject_so(m, last->inp_socket)) { ipsec6stat.in_polvio++; goto bad; } #endif /* IPSEC */ #ifdef FAST_IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject(m, last)) { goto bad; } #endif /* FAST_IPSEC */ if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, m); m_adj(m, off + sizeof(struct udphdr)); so = last->in6p_socket; lwkt_gettoken(&so->so_rcv.ssb_token); if (ssb_appendaddr(&so->so_rcv, (struct sockaddr *)&udp_in6, m, opts) == 0) { udp_stat.udps_fullsock++; lwkt_reltoken(&so->so_rcv.ssb_token); goto bad; } sorwakeup(so); lwkt_reltoken(&so->so_rcv.ssb_token); return IPPROTO_DONE; } /* * Locate pcb for datagram. */ in6p = in6_pcblookup_hash(pcbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, 1, m->m_pkthdr.rcvif); if (in6p == NULL) { if (log_in_vain) { char buf[INET6_ADDRSTRLEN]; strcpy(buf, ip6_sprintf(&ip6->ip6_dst)); log(LOG_INFO, "Connection attempt to UDP [%s]:%d from [%s]:%d\n", buf, ntohs(uh->uh_dport), ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport)); } udp_stat.udps_noport++; if (m->m_flags & M_MCAST) { kprintf("UDP6: M_MCAST is set in a unicast packet.\n"); udp_stat.udps_noportmcast++; goto bad; } icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); return IPPROTO_DONE; } #ifdef IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject_so(m, in6p->in6p_socket)) { ipsec6stat.in_polvio++; goto bad; } #endif /* IPSEC */ #ifdef FAST_IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject(m, in6p)) { goto bad; } #endif /* FAST_IPSEC */ /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ init_sin6(&udp_in6, m); /* general init */ udp_in6.sin6_port = uh->uh_sport; if (in6p->in6p_flags & IN6P_CONTROLOPTS || in6p->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(in6p, &opts, ip6, m); m_adj(m, off + sizeof(struct udphdr)); so = in6p->in6p_socket; lwkt_gettoken(&so->so_rcv.ssb_token); if (ssb_appendaddr(&so->so_rcv, (struct sockaddr *)&udp_in6, m, opts) == 0) { udp_stat.udps_fullsock++; lwkt_reltoken(&so->so_rcv.ssb_token); goto bad; } sorwakeup(so); lwkt_reltoken(&so->so_rcv.ssb_token); return IPPROTO_DONE; bad: if (m) m_freem(m); if (opts) m_freem(opts); return IPPROTO_DONE; }
/* * Forward a packet. If some error occurs return the sender * an icmp packet. Note we can't always generate a meaningful * icmp message because icmp doesn't have a large enough repertoire * of codes and types. * * If not forwarding, just drop the packet. This could be confusing * if ipforwarding was zero but some routing protocol was advancing * us as a gateway to somewhere. However, we must let the routing * protocol deal with that. * */ void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst = NULL; struct rtentry *rt = NULL; struct route_in6 rin6; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ u_int32_t inzone, outzone; struct in6_addr src_in6, dst_in6, odst; #ifdef IPSEC struct secpolicy *sp = NULL; #endif #ifdef SCTP int sw_csum; #endif struct m_tag *fwd_tag; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; #ifdef IPSEC /* * Check AH/ESP integrity. */ /* * Don't increment ip6s_cantforward because this is the check * before forwarding packet actually. */ if (ipsec6_in_reject(m, NULL)) { IPSEC6STAT_INC(ips_in_polvio); m_freem(m); return; } #endif /* IPSEC */ /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on the ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { IP6STAT_INC(ip6s_cantforward); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (V_ip6_log_time + V_ip6_log_interval < time_uptime) { V_ip6_log_time = time_uptime; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } m_freem(m); return; } #ifdef IPSTEALTH if (!V_ip6stealth) { #endif if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; #ifdef IPSTEALTH } #endif /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #ifdef IPSEC /* get a security policy for this packet */ sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &error); if (sp == NULL) { IPSEC6STAT_INC(ips_out_inval); IP6STAT_INC(ip6s_cantforward); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: /* * This packet is just discarded. */ IPSEC6STAT_INC(ips_out_polvio); IP6STAT_INC(ip6s_cantforward); KEY_FREESP(&sp); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ KEY_FREESP(&sp); goto skip_ipsec; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); IP6STAT_INC(ip6s_cantforward); KEY_FREESP(&sp); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } m_freem(m); return; } /* do IPsec */ break; case IPSEC_POLICY_ENTRUST: default: /* should be panic ?? */ printf("ip6_forward: Invalid policy found. %d\n", sp->policy); KEY_FREESP(&sp); goto skip_ipsec; } { struct ipsecrequest *isr = NULL; /* * when the kernel forwards a packet, it is not proper to apply * IPsec transport mode to the packet is not proper. this check * avoid from this. * at present, if there is even a transport mode SA request in the * security policy, the kernel does not apply IPsec to the packet. * this check is not enough because the following case is valid. * ipsec esp/tunnel/xxx-xxx/require esp/transport//require; */ for (isr = sp->req; isr; isr = isr->next) { if (isr->saidx.mode == IPSEC_MODE_ANY) goto doipsectunnel; if (isr->saidx.mode == IPSEC_MODE_TUNNEL) goto doipsectunnel; } /* * if there's no need for tunnel mode IPsec, skip. */ if (!isr) goto skip_ipsec; doipsectunnel: /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers * on inner IPv6 packet (since they are now encapsulated). * * IPv6 [ESP|AH] IPv6 [extension headers] payload */ /* * If we need to encapsulate the packet, do it here * ipsec6_proces_packet will send the packet using ip6_output */ error = ipsec6_process_packet(m, sp->req); KEY_FREESP(&sp); if (error == EJUSTRETURN) { /* * We had a SP with a level of 'use' and no SA. We * will just continue to process the packet without * IPsec processing. */ error = 0; goto skip_ipsec; } if (error) { /* mbuf is already reclaimed in ipsec6_process_packet. */ switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip6_output (ipsec): error code %d\n", error); /* FALLTHROUGH */ case ENOENT: /* don't show these error codes to the user */ break; } IP6STAT_INC(ip6s_cantforward); if (mcopy) { #if 0 /* XXX: what icmp ? */ #else m_freem(mcopy); #endif } return; } else { /* * In the FAST IPSec case we have already * re-injected the packet and it has been freed * by the ipsec_done() function. So, just clean * up after ourselves. */ m = NULL; goto freecopy; } } skip_ipsec: #endif again: bzero(&rin6, sizeof(struct route_in6)); dst = (struct sockaddr_in6 *)&rin6.ro_dst; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; again2: rin6.ro_rt = in6_rtalloc1((struct sockaddr *)dst, 0, 0, M_GETFIB(m)); if (rin6.ro_rt != NULL) RT_UNLOCK(rin6.ro_rt); else { IP6STAT_INC(ip6s_noroute); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } goto bad; } rt = rin6.ro_rt; /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address). We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-04.txt, Section 3.1] */ src_in6 = ip6->ip6_src; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) { /* XXX: this should not happen */ IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); goto bad; } if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) { IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); goto bad; } if (inzone != outzone) { IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (V_ip6_log_time + V_ip6_log_interval < time_uptime) { V_ip6_log_time = time_uptime; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); goto bad; } /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { IP6STAT_INC(ip6s_cantforward); IP6STAT_INC(ip6s_badscope); goto bad; } if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { u_long mtu; #ifdef IPSEC struct secpolicy *sp; int ipsecerror; size_t ipsechdrsiz; #endif /* IPSEC */ mtu = IN6_LINKMTU(rt->rt_ifp); #ifdef IPSEC /* * When we do IPsec tunnel ingress, we need to play * with the link value (decrement IPsec header size * from mtu value). The code is much simpler than v4 * case, as we have the outgoing interface for * encapsulated packet as "rt->rt_ifp". */ sp = ipsec_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &ipsecerror); if (sp) { ipsechdrsiz = ipsec_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND, NULL); if (ipsechdrsiz < mtu) mtu -= ipsechdrsiz; } /* * if mtu becomes less than minimum MTU, * tell minimum MTU (and I'll need to fragment it). */ if (mtu < IPV6_MMTU) mtu = IPV6_MMTU; #endif /* IPSEC */ icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } goto bad; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (V_ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { /* * If the incoming interface is equal to the outgoing * one, and the link attached to the interface is * point-to-point, then it will be highly probable * that a routing loop occurs. Thus, we immediately * drop the packet and send an ICMPv6 error message. * * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); goto bad; } type = ND_REDIRECT; } /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); /* Jump over all PFIL processing if hooks are not active. */ if (!PFIL_HOOKED(&V_inet6_pfil_hook)) goto pass; odst = ip6->ip6_dst; /* Run through list of hooks for output packets. */ error = pfil_run_hooks(&V_inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT, NULL); if (error != 0 || m == NULL) goto freecopy; /* consumed by filter */ ip6 = mtod(m, struct ip6_hdr *); /* See if destination IP address was changed by packet filter. */ if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { m->m_flags |= M_SKIP_FIREWALL; /* If destination is now ourself drop to ip6_input(). */ if (in6_localip(&ip6->ip6_dst)) { m->m_flags |= M_FASTFWD_OURS; if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif error = netisr_queue(NETISR_IPV6, m); goto out; } else goto again; /* Redo the routing table lookup. */ } /* See if local, if yes, send it to netisr. */ if (m->m_flags & M_FASTFWD_OURS) { if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif error = netisr_queue(NETISR_IPV6, m); goto out; } /* Or forward to some other address? */ if ((m->m_flags & M_IP6_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { dst = (struct sockaddr_in6 *)&rin6.ro_dst; bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6)); m->m_flags |= M_SKIP_FIREWALL; m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag); goto again2; } pass: error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); IP6STAT_INC(ip6s_cantforward); } else { IP6STAT_INC(ip6s_forward); in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); if (type) IP6STAT_INC(ip6s_redirectsent); else { if (mcopy) goto freecopy; } } if (mcopy == NULL) goto out; switch (error) { case 0: if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); goto out; } goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); goto out; freecopy: m_freem(mcopy); goto out; bad: m_freem(m); out: if (rt != NULL) RTFREE(rt); }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); const struct sockaddr_in6 *dst; struct rtentry *rt; int error = 0, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ uint32_t inzone, outzone; struct in6_addr src_in6, dst_in6; #ifdef IPSEC int needipsec = 0; struct secpolicy *sp = NULL; #endif /* * Clear any in-bound checksum flags for this packet. */ m->m_pkthdr.csum_flags = 0; /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, NULL, ((m->m_flags & (M_BCAST|M_MCAST)) != 0) ? "bcast/mcast" : IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ? "mcast/dst" : "unspec/src"); m_freem(m); return; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #ifdef IPSEC if (ipsec_used) { /* Check the security policy (SP) for the packet */ sp = ipsec6_check_policy(m, NULL, 0, &needipsec, &error); if (error != 0) { /* * Hack: -EINVAL is used to signal that a packet * should be silently discarded. This is typically * because we asked key management for an SA and * it was delayed (e.g. kicked up to IKE). */ if (error == -EINVAL) error = 0; goto freecopy; } } #endif /* IPSEC */ if (srcrt) { union { struct sockaddr dst; struct sockaddr_in6 dst6; } u; sockaddr_in6_init(&u.dst6, &ip6->ip6_dst, 0, 0, 0); if ((rt = rtcache_lookup(&ip6_forward_rt, &u.dst)) == NULL) { IP6_STATINC(IP6_STAT_NOROUTE); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } else if ((rt = rtcache_validate(&ip6_forward_rt)) == NULL && (rt = rtcache_update(&ip6_forward_rt, 1)) == NULL) { /* * rtcache_getdst(ip6_forward_rt)->sin6_addr was equal to * ip6->ip6_dst */ IP6_STATINC(IP6_STAT_NOROUTE); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } dst = satocsin6(rtcache_getdst(&ip6_forward_rt)); /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address). We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-07, Section 3.1] */ src_in6 = ip6->ip6_src; inzone = outzone = ~0; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone) != 0 || in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone) != 0 || inzone != outzone) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, rt->rt_ifp, "src[%s] inzone %d outzone %d", in6_getscopename(&ip6->ip6_src), inzone, outzone); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } #ifdef IPSEC /* * If we need to encapsulate the packet, do it here * ipsec6_proces_packet will send the packet using ip6_output */ if (needipsec) { int s = splsoftnet(); error = ipsec6_process_packet(m, sp->req); splx(s); if (mcopy) goto freecopy; } #endif /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; inzone = outzone = ~0; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, rt->rt_ifp, "dst[%s] inzone %d outzone %d", in6_getscopename(&ip6->ip6_dst), inzone, outzone); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { u_long mtu; mtu = IN6_LINKMTU(rt->rt_ifp); icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } m_freem(m); return; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && ip6_sendredirects && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) && nd6_is_addr_neighbor( satocsin6(rtcache_getdst(&ip6_forward_rt)), rt->rt_ifp)) { /* * If the incoming interface is equal to the outgoing * one, the link attached to the interface is * point-to-point, and the IPv6 destination is * regarded as on-link on the link, then it will be * highly probable that the destination address does * not exist on the link and that the packet is going * to loop. Thus, we immediately drop the packet and * send an ICMPv6 error message. * For other routing loops, we dare to let the packet * go to the loop, so that a remote diagnosing host * can detect the loop by traceroute. * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); return; } type = ND_REDIRECT; } /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); /* * Run through list of hooks for output packets. */ if ((error = pfil_run_hooks(inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT)) != 0) goto senderr; if (m == NULL) goto freecopy; ip6 = mtod(m, struct ip6_hdr *); error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); IP6_STATINC(IP6_STAT_CANTFORWARD); } else { IP6_STATINC(IP6_STAT_FORWARD); in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); if (type) IP6_STATINC(IP6_STAT_REDIRECTSENT); else { #ifdef GATEWAY if (m->m_flags & M_CANFASTFWD) ip6flow_create(&ip6_forward_rt, m); #endif if (mcopy) goto freecopy; } } senderr: if (mcopy == NULL) return; switch (error) { case 0: if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); return; freecopy: m_freem(mcopy); return; }
void ip6_input (T_NET_BUF *input) { T_IP6_HDR *ip6h; T_IN6_IFADDR *ia6; T_IFNET *ifp; ER_UINT noff; uint_t next, nest, plen, offp, nextp; uint_t (*func)(T_NET_BUF **, uint_t *, uint_t *); NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_OCTETS], input->len - IF_HDR_SIZE); NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_PACKETS], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInReceives, 1); /* IP ヘッダの長さをチェックする。*/ if (input->len < IF_IP6_HDR_SIZE) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_SHORT], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1); goto buf_rel; } ip6h = GET_IP6_HDR(input); /* バージョンをチェックする。*/ if (IP6_VCF_V(ntohl(ip6h->vcf)) != IPV6_VERSION) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_VER], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1); goto buf_rel; } /* * 次のデータグラムは破棄する。 * * ・始点アドレスがマルチキャスト * ・終点アドレスが未定義 * * ネットワークインタフェースがループバックでないとき、 * 次のデータグラムも破棄する。 * * ・始点アドレスがループバック * ・終点アドレスがループバック * * 悪意のあるユーザが TCP/UDP スタックの混乱や * セキュリティチェックをバイパスするため、IPv4 * マップアドレスを悪用することを防ぐため * 以下のデータグラムは破棄する。 * * ・始点アドレスが ::ffff:127.0.0.1 * ・終点アドレスが ::ffff:127.0.0.1 */ if (IN6_IS_ADDR_MULTICAST(&ip6h->src) || IN6_IS_ADDR_UNSPECIFIED(&ip6h->dst)) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1); goto buf_rel; } if (IN6_IS_ADDR_V4MAPPED(&ip6h->src) || IN6_IS_ADDR_V4MAPPED(&ip6h->dst)) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1); goto buf_rel; } #ifdef SUPPORT_LOOP #else /* of #ifdef SUPPORT_LOOP */ if (IN6_IS_ADDR_LOOPBACK(&ip6h->src) || IN6_IS_ADDR_LOOPBACK(&ip6h->dst)) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1); goto buf_rel; } #endif /* of #ifdef SUPPORT_LOOP */ ifp = IF_GET_IFNET(); if (IN6_IS_ADDR_MULTICAST(&ip6h->dst)) { /* 宛先がマルチキャストの場合の処理 */ NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInMcastPkts, 1); if (!in6_lookup_multi(ifp, &ip6h->dst)) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1); goto buf_rel; } } else { if ((ia6 = in6_lookup_ifaddr(ifp, &ip6h->dst)) == NULL) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1); goto buf_rel; } /* アドレスが未解決の場合はデータグラムを破棄する。*/ if (IFA6_IS_NOTREADY(ia6)) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1); goto buf_rel; } } /* オフセットを設定する。*/ offp = GET_IP6_NEXT_HDR(input) - input->buf; /* ペイロード長を取り出す。*/ plen = ntohs(ip6h->plen); /* 次ヘッダの位置を初期化する。*/ nextp = offsetof(T_IP6_HDR, next); /* * 中継点 (Hop-by-Hop) オプションヘッダのチェック */ if (ip6h->next == IPPROTO_HOPOPTS) { /* 中継点ヘッダの長さをチェックする。*/ if (plen < sizeof(T_IP6_HBH_HDR)) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_SHORT], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1); goto buf_rel; } if ((noff = ip6_hopopts_input(input, &offp, &nextp)) < 0) { if (noff == IP6_OPT_RET_ERR) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1); goto buf_rel; } else if (noff == IP6_OPT_RET_REL) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1); return; } } /* * ペイロード長が 0 で、中継点オプションがあるときは、 * 巨大ペイロード・オプションが含まれなければならない。 * このとき、巨大ペイロード・オプションにある * ペイロード長をチェックする必要があるが、本実装では、 * 巨大ペイロード・オプションを未知のオプションとして * 扱うので、何もしない。 */ next = ((T_IP6_HBH_HDR *)(ip6h + 1))->next; } else next = ip6h->next; /* IPv6 データグラム長をチェックする。*/ if (input->len - IF_IP6_HDR_SIZE < plen) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_SHORT], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInTruncatedPkts, 1); goto buf_rel; } else if (input->len > IF_IP6_HDR_SIZE + plen) input->len = IF_IP6_HDR_SIZE + plen; /* 上位プロトコル処理関数を呼び出す。*/ nest = 0; while (next != IPPROTO_DONE) { if (++ nest > IP6_HDR_NEST_LIMIT) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PROTO], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInUnknownProtos, 1); goto buf_rel; } else if ((func = get_upper_proto(next)) == NULL) { NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PROTO], 1); NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1); NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInUnknownProtos, 1); /* icmp6_error でネットワークバッファを開放する。*/ icmp6_error(input, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXT_HEADER, nextp); return; } else { next = (*func)(&input, &offp, &nextp); } } return; buf_rel: NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1); syscall(rel_net_buf(input)); }
int udp6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp; struct ifnet *ifp; struct ip6_hdr *ip6; struct udphdr *uh; struct inpcb *inp; struct inpcbinfo *pcbinfo; struct udpcb *up; int off = *offp; int cscov_partial; int plen, ulen; struct sockaddr_in6 fromsa; struct m_tag *fwd_tag; uint16_t uh_sum; uint8_t nxt; ifp = m->m_pkthdr.rcvif; ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); ip6 = mtod(m, struct ip6_hdr *); uh = (struct udphdr *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(uh, struct udphdr *, m, off, sizeof(*uh)); if (!uh) return (IPPROTO_DONE); #endif UDPSTAT_INC(udps_ipackets); /* * Destination port of 0 is illegal, based on RFC768. */ if (uh->uh_dport == 0) goto badunlocked; plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); ulen = ntohs((u_short)uh->uh_ulen); nxt = proto; cscov_partial = (nxt == IPPROTO_UDPLITE) ? 1 : 0; if (nxt == IPPROTO_UDPLITE) { /* Zero means checksum over the complete packet. */ if (ulen == 0) ulen = plen; if (ulen == plen) cscov_partial = 0; if ((ulen < sizeof(struct udphdr)) || (ulen > plen)) { /* XXX: What is the right UDPLite MIB counter? */ goto badunlocked; } if (uh->uh_sum == 0) { /* XXX: What is the right UDPLite MIB counter? */ goto badunlocked; } } else { if ((ulen < sizeof(struct udphdr)) || (plen != ulen)) { UDPSTAT_INC(udps_badlen); goto badunlocked; } if (uh->uh_sum == 0) { UDPSTAT_INC(udps_nosum); goto badunlocked; } } if ((m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) && !cscov_partial) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) uh_sum = m->m_pkthdr.csum_data; else uh_sum = in6_cksum_pseudo(ip6, ulen, nxt, m->m_pkthdr.csum_data); uh_sum ^= 0xffff; } else uh_sum = in6_cksum_partial(m, nxt, off, plen, ulen); if (uh_sum != 0) { UDPSTAT_INC(udps_badsum); goto badunlocked; } /* * Construct sockaddr format source address. */ init_sin6(&fromsa, m); fromsa.sin6_port = uh->uh_sport; pcbinfo = udp_get_inpcbinfo(nxt); if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct inpcb *last; struct inpcbhead *pcblist; struct ip6_moptions *imo; INP_INFO_RLOCK(pcbinfo); /* * In the event that laddr should be set to the link-local * address (this happens in RIPng), the multicast address * specified in the received packet will not match laddr. To * handle this situation, matching is relaxed if the * receiving interface is the same as one specified in the * socket and if the destination multicast address matches * one of the multicast groups specified in the socket. */ /* * KAME note: traditionally we dropped udpiphdr from mbuf * here. We need udphdr for IPsec processing so we do that * later. */ pcblist = udp_get_pcblist(nxt); last = NULL; LIST_FOREACH(inp, pcblist, inp_list) { if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (inp->inp_lport != uh->uh_dport) continue; if (inp->inp_fport != 0 && inp->inp_fport != uh->uh_sport) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &ip6->ip6_dst)) continue; } if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &ip6->ip6_src) || inp->inp_fport != uh->uh_sport) continue; } /* * XXXRW: Because we weren't holding either the inpcb * or the hash lock when we checked for a match * before, we should probably recheck now that the * inpcb lock is (supposed to be) held. */ /* * Handle socket delivery policy for any-source * and source-specific multicast. [RFC3678] */ imo = inp->in6p_moptions; if (imo && IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct sockaddr_in6 mcaddr; int blocked; INP_RLOCK(inp); bzero(&mcaddr, sizeof(struct sockaddr_in6)); mcaddr.sin6_len = sizeof(struct sockaddr_in6); mcaddr.sin6_family = AF_INET6; mcaddr.sin6_addr = ip6->ip6_dst; blocked = im6o_mc_filter(imo, ifp, (struct sockaddr *)&mcaddr, (struct sockaddr *)&fromsa); if (blocked != MCAST_PASS) { if (blocked == MCAST_NOTGMEMBER) IP6STAT_INC(ip6s_notmember); if (blocked == MCAST_NOTSMEMBER || blocked == MCAST_MUTED) UDPSTAT_INC(udps_filtermcast); INP_RUNLOCK(inp); /* XXX */ continue; } INP_RUNLOCK(inp); } if (last != NULL) { struct mbuf *n; if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { INP_RLOCK(last); UDP_PROBE(receive, NULL, last, ip6, last, uh); if (udp6_append(last, n, off, &fromsa)) goto inp_lost; INP_RUNLOCK(last); } } last = inp; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids * searching through all pcbs in the common case of a * non-shared port. It assumes that an application * will never clear these options after setting them. */ if ((last->inp_socket->so_options & (SO_REUSEPORT|SO_REUSEADDR)) == 0) break; } if (last == NULL) { /* * No matching pcb found; discard datagram. (No need * to send an ICMP Port Unreachable for a broadcast * or multicast datgram.) */ UDPSTAT_INC(udps_noport); UDPSTAT_INC(udps_noportmcast); goto badheadlocked; } INP_RLOCK(last); INP_INFO_RUNLOCK(pcbinfo); UDP_PROBE(receive, NULL, last, ip6, last, uh); if (udp6_append(last, m, off, &fromsa) == 0) INP_RUNLOCK(last); inp_lost: return (IPPROTO_DONE); } /* * Locate pcb for datagram. */ /* * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. */ if ((m->m_flags & M_IP6_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { struct sockaddr_in6 *next_hop6; next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1); /* * Transparently forwarded. Pretend to be the destination. * Already got one like this? */ inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); if (!inp) { /* * It's new. Try to find the ambushing socket. * Because we've rewritten the destination address, * any hardware-generated hash is ignored. */ inp = in6_pcblookup(pcbinfo, &ip6->ip6_src, uh->uh_sport, &next_hop6->sin6_addr, next_hop6->sin6_port ? htons(next_hop6->sin6_port) : uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif); } /* Remove the tag from the packet. We don't need it anymore. */ m_tag_delete(m, fwd_tag); m->m_flags &= ~M_IP6_NEXTHOP; } else inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); if (inp == NULL) { if (udp_log_in_vain) { char ip6bufs[INET6_ADDRSTRLEN]; char ip6bufd[INET6_ADDRSTRLEN]; log(LOG_INFO, "Connection attempt to UDP [%s]:%d from [%s]:%d\n", ip6_sprintf(ip6bufd, &ip6->ip6_dst), ntohs(uh->uh_dport), ip6_sprintf(ip6bufs, &ip6->ip6_src), ntohs(uh->uh_sport)); } UDPSTAT_INC(udps_noport); if (m->m_flags & M_MCAST) { printf("UDP6: M_MCAST is set in a unicast packet.\n"); UDPSTAT_INC(udps_noportmcast); goto badunlocked; } if (V_udp_blackhole) goto badunlocked; if (badport_bandlim(BANDLIM_ICMP6_UNREACH) < 0) goto badunlocked; icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); return (IPPROTO_DONE); } INP_RLOCK_ASSERT(inp); up = intoudpcb(inp); if (cscov_partial) { if (up->u_rxcslen == 0 || up->u_rxcslen > ulen) { INP_RUNLOCK(inp); m_freem(m); return (IPPROTO_DONE); } } UDP_PROBE(receive, NULL, inp, ip6, inp, uh); if (udp6_append(inp, m, off, &fromsa) == 0) INP_RUNLOCK(inp); return (IPPROTO_DONE); badheadlocked: INP_INFO_RUNLOCK(pcbinfo); badunlocked: if (m) m_freem(m); return (IPPROTO_DONE); }
void nd6_timer (void) { T_IFNET *ifp = IF_GET_IFNET(); T_LLINFO_ND6 *ln; SYSTIM now; int_t ix; /* 近隣キャッシュの処理 */ syscall(wai_sem(SEM_ND6_CACHE)); syscall(get_tim(&now)); for (ix = NUM_ND6_CACHE_ENTRY; ix -- > 0; ) { ln = &nd6_cache[ix]; if (ln->state >= ND6_LLINFO_NO_STATE && (int32_t)(ln->expire - now) < 0) { /* ln->expire < now */ switch (ln->state) { case ND6_LLINFO_INCOMPLETE: /* 未解決 */ if (ln->asked < ND6_MAX_MCAST_QUERY) { /* * マルチキャストアドレス要請の再送回数以下の場合の処理 */ ln->asked ++; ln->expire = now + ND6_RETRANS_TIME; /* 近隣探索送信間隔 */ nd6_ns_output(ifp, &in6_addr_unspecified, &ln->addr, ln, false); } else { if (ln->hold != NULL) { /* * 最終的には、保留されているネットワークバッファが * あれば、ICMP6 ERROR を自分自身に返したあと、 * ネットワークバッファを開放する。 * しかし、現在、icmp6_error の実装が不完全のため、 * ネットワークバッファの開放のみ行う。 */ #if 0 /* 保留 */ icmp6_error(ln->hold, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); #endif /* #if 0 */ syscall(rel_net_buf(ln->hold)); ln->hold = NULL; } nd6_free(ln); memset((void*)ln, 0, sizeof(*ln)); } break; case ND6_LLINFO_REACHABLE: /* 到達可能 */ if (ln->expire) { ln->expire = now + ND6_GCOLLECTION_TIME; ln->state = ND6_LLINFO_STALE; } break; case ND6_LLINFO_STALE: /* 到達可能性は未確認 */ if (ln->expire) { if (ln->hold != NULL) { syscall(rel_net_buf(ln->hold)); ln->hold = NULL; } nd6_free(ln); memset((void*)ln, 0, sizeof(*ln)); } break; case ND6_LLINFO_DELAY: /* 到達可能性の確認待機 */ ln->asked = 1; ln->expire = now + ND6_RETRANS_TIME; /* 近隣探索送信間隔 */ ln->state = ND6_LLINFO_PROBE; nd6_ns_output(ifp, &ln->addr, &ln->addr, ln, false); break; case ND6_LLINFO_PROBE: /* 到達可能性を確認中 */ if (ln->asked < ND6_MAX_UCAST_QUERY) { /* * ユニキャストアドレス要請の再送回数以下の場合の処理 */ ln->asked ++; ln->expire += ND6_RETRANS_TIME; /* 近隣探索送信間隔 */ nd6_ns_output(ifp, &ln->addr, &ln->addr, ln, false); } else { if (ln->hold != NULL) { syscall(rel_net_buf(ln->hold)); ln->hold = NULL; } nd6_free(ln); memset((void*)ln, 0, sizeof(*ln)); } break; } } } syscall(sig_sem(SEM_ND6_CACHE)); /* ディフォルトルータ・リストの処理 */ nd6_defrtrlist_timer(); /* プレフィックス・リストの処理 */ nd6_prelist_timer(); /* ネットワークインタフェースのアドレスの処理 */ in6_ifaddr_timer(ifp); /* * 到達可能状態の有効時間の更新 */ if ((int32_t)(nd6_recalc_reachtm_interval - now) < 0) { /* nd6_recalc_reachtm_interval < now */ nd6_recalc_reachtm_interval = now + ND6_RECALC_REACHTM_INTERVAL; nd6_reachable_time = ND6_CALC_REACH_TIME(nd6_base_reachable_time); } timeout((callout_func)nd6_timer, NULL, ND6_TIMER_TMO); }
/* * Type0 routing header processing * * RFC2292 backward compatibility warning: no support for strict/loose bitmap, * as it was dropped between RFC1883 and RFC2460. */ static int ip6_rthdr0(struct mbuf *m, struct ip6_hdr *ip6, struct ip6_rthdr0 *rh0) { int addrs, index; struct in6_addr *nextaddr, tmpaddr; const struct ip6aux *ip6a; if (rh0->ip6r0_segleft == 0) return (0); if (rh0->ip6r0_len % 2 #ifdef COMPAT_RFC1883 || rh0->ip6r0_len > 46 #endif ) { /* * Type 0 routing header can't contain more than 23 addresses. * RFC 2462: this limitation was removed since strict/loose * bitmap field was deleted. */ IP6_STATINC(IP6_STAT_BADOPTIONS); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (char *)&rh0->ip6r0_len - (char *)ip6); return (-1); } if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) { IP6_STATINC(IP6_STAT_BADOPTIONS); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (char *)&rh0->ip6r0_segleft - (char *)ip6); return (-1); } index = addrs - rh0->ip6r0_segleft; rh0->ip6r0_segleft--; nextaddr = ((struct in6_addr *)(rh0 + 1)) + index; /* * reject invalid addresses. be proactive about malicious use of * IPv4 mapped/compat address. * XXX need more checks? */ if (IN6_IS_ADDR_MULTICAST(nextaddr) || IN6_IS_ADDR_UNSPECIFIED(nextaddr) || IN6_IS_ADDR_V4MAPPED(nextaddr) || IN6_IS_ADDR_V4COMPAT(nextaddr)) { p6stat[IP6_STAT_BADOPTIONS]++; goto bad; } if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) || IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) || IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { IP6_STATINC(IP6_STAT_BADOPTIONS); goto bad; } /* * Determine the scope zone of the next hop, based on the interface * of the current hop. [RFC4007, Section 9] * Then disambiguate the scope zone for the next hop (if necessary). */ if ((ip6a = ip6_getdstifaddr(m)) == NULL) goto bad; if (in6_setzoneid(nextaddr, ip6a->ip6a_scope_id) != 0) { IP6_STATINC(IP6_STAT_BADSCOPE); goto bad; } /* * Swap the IPv6 destination address and nextaddr. Forward the packet. */ tmpaddr = *nextaddr; *nextaddr = ip6->ip6_dst; in6_clearscope(nextaddr); /* XXX */ ip6->ip6_dst = tmpaddr; #ifdef COMPAT_RFC1883 if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8)))) ip6_forward(m, IPV6_SRCRT_NEIGHBOR); else ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR); #else ip6_forward(m, 1); #endif return (-1); /* m would be freed in ip6_forward() */ bad: m_freem(m); return (-1); }