NMIP6Config * nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex) { NMIP6ManagerPrivate *priv; NMIP6Device *device; NMIP6Config *config; struct rtnl_addr *rtnladdr; struct nl_addr *nladdr; struct in6_addr *addr; NMIP6Address *ip6addr; struct rtnl_route *rtnlroute; struct nl_addr *nldest, *nlgateway; const struct in6_addr *dest, *gateway; uint32_t metric; NMIP6Route *ip6route; int i; g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL); g_return_val_if_fail (ifindex > 0, NULL); priv = NM_IP6_MANAGER_GET_PRIVATE (manager); device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); if (!device) { nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex); return NULL; } config = nm_ip6_config_new (); if (!config) { nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.", device->iface); return NULL; } /* Make sure we refill the route and address caches, otherwise we won't get * up-to-date information here since the netlink route/addr change messages * may be lagging a bit. */ nl_cache_refill (priv->nlh, priv->route_cache); nl_cache_refill (priv->nlh, priv->addr_cache); /* Add routes */ for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) { /* Make sure it's an IPv6 route for this device */ if (rtnl_route_get_oif (rtnlroute) != device->ifindex) continue; if (rtnl_route_get_family (rtnlroute) != AF_INET6) continue; /* And ignore cache/cloned routes as they aren't part of the interface's * permanent routing configuration. */ if (rtnl_route_get_flags (rtnlroute) & RTM_F_CLONED) continue; nldest = rtnl_route_get_dst (rtnlroute); if (!nldest || nl_addr_get_family (nldest) != AF_INET6) continue; dest = nl_addr_get_binary_addr (nldest); nlgateway = rtnl_route_get_gateway (rtnlroute); if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6) continue; gateway = nl_addr_get_binary_addr (nlgateway); if (rtnl_route_get_dst_len (rtnlroute) == 0) { /* Default gateway route; cache the router's address for later */ if (!nm_ip6_config_get_gateway (config)) nm_ip6_config_set_gateway (config, gateway); continue; } /* Also ignore link-local routes where the destination and gateway are * the same, which apparently get added by the kernel but return -EINVAL * when we try to add them via netlink. */ if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway)) continue; ip6route = nm_ip6_route_new (); nm_ip6_route_set_dest (ip6route, dest); nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute)); nm_ip6_route_set_next_hop (ip6route, gateway); rtnl_route_get_metric(rtnlroute, 1, &metric); if (metric != UINT_MAX) nm_ip6_route_set_metric (ip6route, metric); nm_ip6_config_take_route (config, ip6route); } /* Add addresses */ for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) { if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) continue; nladdr = rtnl_addr_get_local (rtnladdr); if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) continue; addr = nl_addr_get_binary_addr (nladdr); ip6addr = nm_ip6_address_new (); nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr)); nm_ip6_address_set_address (ip6addr, addr); nm_ip6_config_take_address (config, ip6addr); gateway = nm_ip6_config_get_gateway (config); if (gateway) nm_ip6_address_set_gateway (ip6addr, gateway); } /* Add DNS servers */ if (device->rdnss_servers) { NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data); for (i = 0; i < device->rdnss_servers->len; i++) nm_ip6_config_add_nameserver (config, &rdnss[i].addr); } /* Add DNS domains */ if (device->dnssl_domains) { NMIP6DNSSL *dnssl = (NMIP6DNSSL *)(device->dnssl_domains->data); for (i = 0; i < device->dnssl_domains->len; i++) nm_ip6_config_add_domain (config, dnssl[i].domain); } return config; }
/* * The fact that this routines gets called is a first hint that * the current forwarder/nameserver is down at this point in time. * Actually, it may just be slow, be overloaded, or the network may * be congested. In any way, from our point of view it is slow or * down and thus we may gain by trying a configured backup forwarder. * * This routines marks the forwarder with the given address (if any) * with a `minus' point. If a forwarder has gathered `enough' minus points * it will be marked down, such that it will not be used for a while. * * Note that the current forwarder (T.current_fwd) is never marked down! */ void fwd_mark (struct sockaddr *sa, int up) { char *fn = "fwd_mark"; char astr[MAX_DNAME]; char bstr[MAX_DNAME]; G_List *gl; Fwd *fwd; syslog (LOG_DEBUG, "%s: start()", fn); if (!T.Fwd_list || !T.current_fwd) return; fwd = NULL; for (gl = T.Fwd_list->next; gl->list_data; gl = gl->next) { fwd = (Fwd *)gl->list_data; if (sa->sa_family != fwd->sa->sa_family) continue; #ifdef USE_INET6 if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sina, *sinb; sina = (struct sockaddr_in6 *) (fwd->sa); sinb = (struct sockaddr_in6 *) sa; if (IN6_ARE_ADDR_EQUAL(&sina->sin6_addr, &sinb->sin6_addr) && sina->sin6_port == sinb->sin6_port) { fwd->ticks += up; break; } } #endif #ifdef USE_INET4 if (sa->sa_family == AF_INET) { struct sockaddr_in *sina, *sinb; sina = (struct sockaddr_in *) (fwd->sa); sinb = (struct sockaddr_in *) sa; if (sina->sin_addr.s_addr == sinb->sin_addr.s_addr && sina->sin_port == sinb->sin_port) { fwd->ticks += up; break; } } #endif } if (!fwd) return; if (fwd->ticks < 0) fwd->ticks = 0; if (gl->list_data) syslog (LOG_DEBUG, "Mark forwarder with %d: %s ", fwd->ticks, sprint_inet(sa, astr)); if (fwd->ticks < FORWARDER_DEATH_MARK) return; else fwd->went_down_at = time(NULL); if (((Fwd *)(T.current_fwd->list_data))->went_down_at) { G_List *new_fwd; Fwd *fwd_tmp; /* * we marked current forwarder down, so * select new next valid forwarder */ new_fwd = T.current_fwd->next; fwd_tmp = (Fwd *)new_fwd->list_data; while (fwd_tmp) { if (fwd_tmp->sa && !fwd_tmp->went_down_at) break; new_fwd = new_fwd->next; fwd_tmp = (Fwd *)new_fwd->list_data; } if (!fwd_tmp || !fwd_tmp->sa) { /* * we didn't find a next valid forwarder, game over! * Note that we do not mark current forwarder down * (the current one never is) nor do we change it. * * Actually, we mark all forwarders up again! No use * discriminating between things that seem to behave * the same ;) */ new_fwd = T.Fwd_list->next; fwd_tmp = (Fwd *)new_fwd->list_data; while (fwd_tmp) { /* mark 'em `up' again */ fwd_tmp->ticks = 0; fwd_tmp->went_down_at = 0; new_fwd = new_fwd->next; fwd_tmp = (Fwd *)new_fwd->list_data; } return; } syslog (LOG_NOTICE, "Disabling forwarder %s (next %s)", sprint_inet(((Fwd *)T.current_fwd->list_data)->sa, astr), sprint_inet(fwd_tmp->sa, bstr)); T.current_fwd = new_fwd; } }
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_copym(m, 0, M_COPYALL, M_NOWAIT)) != 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); }
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); }
/** Delete the IPsec SA for disconnection */ static void l2tp_ctrl_purge_ipsec_sa(l2tp_ctrl *_this) { int is_natt, proto; struct sockaddr_storage peer, sock; hash_link *hl; #ifdef USE_LIBSOCKUTIL struct in_ipsec_sa_cookie *ipsec_sa_cookie; #endif l2tp_ctrl *anot; /* * Search another tunnel that uses the same IPsec SA * by lineer. */ for (hl = hash_first(_this->l2tpd->ctrl_map); hl != NULL; hl = hash_next(_this->l2tpd->ctrl_map)) { anot = hl->item; if (anot == _this) continue; if (_this->peer.ss_family != anot->peer.ss_family) continue; if (_this->peer.ss_family == AF_INET) { if (SIN(&_this->peer)->sin_addr.s_addr != SIN(&anot->peer)->sin_addr.s_addr) continue; } else if (_this->peer.ss_family == AF_INET6) { if (!IN6_ARE_ADDR_EQUAL( &(SIN6(&_this->peer)->sin6_addr), &(SIN6(&anot->peer)->sin6_addr))) continue; } #ifdef USE_LIBSOCKUTIL if (_this->sa_cookie != NULL && anot->sa_cookie != NULL) { /* Both tunnels belong the same NAT box. */ if (memcmp(_this->sa_cookie, anot->sa_cookie, sizeof(struct in_ipsec_sa_cookie)) != 0) /* Different hosts behind the NAT box. */ continue; /* The SA is shared by another tunnels by one host. */ return; /* don't purge the sa */ } else if (_this->sa_cookie != NULL || anot->sa_cookie != NULL) /* Only one is behind the NAT */ continue; #endif return; /* don't purge the sa */ } #if defined(USE_LIBSOCKUTIL) && defined(IP_IPSEC_SA_COOKIE) is_natt = (_this->sa_cookie != NULL)? 1 : 0; #else is_natt = 0; #endif proto = 0; memcpy(&peer, &_this->peer, _this->peer.ss_len); memcpy(&sock, &_this->sock, _this->sock.ss_len); if (!is_natt) SIN(&peer)->sin_port = SIN(&sock)->sin_port = 0; #if defined(USE_LIBSOCKUTIL) && defined(IP_IPSEC_SA_COOKIE) else { ipsec_sa_cookie = _this->sa_cookie; SIN(&peer)->sin_port = ipsec_sa_cookie->remote_port; SIN(&sock)->sin_port = ipsec_sa_cookie->local_port; #if 1 /* * XXX: As RFC 2367, protocol sould be specified if the port * XXX: number is non-zero. */ proto = 0; #else proto = IPPROTO_UDP; #endif } #endif if (ipsec_util_purge_transport_sa((struct sockaddr *)&peer, (struct sockaddr *)&sock, proto, IPSEC_UTIL_DIRECTION_BOTH) != 0) l2tp_ctrl_log(_this, LOG_NOTICE, "failed to purge IPSec SA"); }
/* * Input a Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) */ void nd6_ns_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa = NULL; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int tlladdr; int rflag; union nd_opts ndopts; struct sockaddr_dl proxydl; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0; if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif) rflag = 0; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { ICMP6STAT_INC(icp6s_tooshort); return; } #endif ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (in6_setscope(&taddr6, ifp, NULL) != 0) goto bad; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp))); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be a solicited node multicast address. */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && /* don't check ifindex portion */ daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); goto bad; } } else if (!V_nd6_onlink_ns_rfc4861) { struct sockaddr_in6 src_sa6; /* * According to recent IETF discussions, it is not a good idea * to accept a NS from an address which would not be deemed * to be a neighbor otherwise. This point is expected to be * clarified in future revisions of the specification. */ bzero(&src_sa6, sizeof(src_sa6)); src_sa6.sin6_family = AF_INET6; src_sa6.sin6_len = sizeof(src_sa6); src_sa6.sin6_addr = saddr6; if (nd6_is_addr_neighbor(&src_sa6, ifp) == 0) { nd6log((LOG_INFO, "nd6_ns_input: " "NS packet from non-neighbor\n")); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n")); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is unicast/anycast MUST NOT add * NS IP dst is solicited-node multicast MUST add * * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface, * (2) Unicast address for which I'm offering proxy service, or * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ if (ifp->if_carp) ifa = (*carp_iamatch6_p)(ifp, &taddr6); else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ if (ifa == NULL) { struct sockaddr_dl rt_gateway; struct rt_addrinfo info; struct sockaddr_in6 dst6; bzero(&dst6, sizeof(dst6)); dst6.sin6_len = sizeof(struct sockaddr_in6); dst6.sin6_family = AF_INET6; dst6.sin6_addr = taddr6; bzero(&rt_gateway, sizeof(rt_gateway)); rt_gateway.sdl_len = sizeof(rt_gateway); bzero(&info, sizeof(info)); info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&rt_gateway; if (rib_lookup_info(ifp->if_fib, (struct sockaddr *)&dst6, 0, 0, &info) == 0) { if ((info.rti_flags & RTF_ANNOUNCE) != 0 && rt_gateway.sdl_family == AF_LINK) { /* * proxy NDP for single entry */ proxydl = *SDL(&rt_gateway); ifa = (struct ifaddr *)in6ifa_ifpforlinklocal( ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); if (ifa) proxy = 1; } } } if (ifa == NULL) { /* * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(ip6bufs, &taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(ip6bufs, &saddr6))); goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicate address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce); goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { struct in6_addr in6_all; in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; nd6_na_output_fib(ifp, &in6_all, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output_fib(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | rflag | ND_NA_FLAG_SOLICITED, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); freeit: if (ifa != NULL) ifa_free(ifa); m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(ip6bufs, &saddr6))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(ip6bufs, &daddr6))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(ip6bufs, &taddr6))); ICMP6STAT_INC(icp6s_badns); if (ifa != NULL) ifa_free(ifa); m_freem(m); }
static int inetport(struct Listener* listener) { struct irc_sockaddr lsin; int fd; int opt = 1; /* * At first, open a new socket */ fd = comm_open(DEF_FAM, SOCK_STREAM, 0, "Listener socket"); #ifdef IPV6 if (!IN6_ARE_ADDR_EQUAL((struct in6_addr *)&listener->addr, &in6addr_any)) { #else if (INADDR_ANY != listener->addr.sins.sin.s_addr) { #endif inetntop(DEF_FAM, &IN_ADDR(listener->addr), listener->vhost, HOSTLEN); listener->name = listener->vhost; } if (fd == -1) { report_error(L_ALL, "opening listener socket %s:%s", get_listener_name(listener), errno); return 0; } else if ((HARD_FDLIMIT - 10) < fd) { report_error(L_ALL, "no more connections left for listener %s:%s", get_listener_name(listener), errno); fd_close(fd); return 0; } /* * XXX - we don't want to do all this crap for a listener * set_sock_opts(listener); */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &opt, sizeof(opt))) { report_error(L_ALL, "setting SO_REUSEADDR for listener %s:%s", get_listener_name(listener), errno); fd_close(fd); return 0; } /* * Bind a port to listen for new connections if port is non-null, * else assume it is already open and try get something from it. */ memset(&lsin, 0, sizeof(struct irc_sockaddr)); S_FAM(lsin) = DEF_FAM; copy_s_addr(S_ADDR(lsin), IN_ADDR(listener->addr)); S_PORT(lsin) = htons(listener->port); if (bind(fd, (struct sockaddr*) &SOCKADDR(lsin), sizeof(struct irc_sockaddr))) { report_error(L_ALL, "binding listener socket %s:%s", get_listener_name(listener), errno); fd_close(fd); return 0; } if (listen(fd, HYBRID_SOMAXCONN)) { report_error(L_ALL, "listen failed for %s:%s", get_listener_name(listener), errno); fd_close(fd); return 0; } /* * XXX - this should always work, performance will suck if it doesn't */ if (!set_non_blocking(fd)) report_error(L_ALL, NONB_ERROR_MSG, get_listener_name(listener), errno); listener->fd = fd; /* Listen completion events are READ events .. */ accept_connection(fd, listener); return 1; } static struct Listener* find_listener(int port, struct irc_inaddr *addr) { struct Listener* listener = NULL; struct Listener* last_closed = NULL; for (listener = ListenerPollList; listener; listener = listener->next) { if ( (port == listener->port) && (!memcmp(&PIN_ADDR(addr), &IN_ADDR(listener->addr), sizeof(struct irc_inaddr)))) { /* Try to return an open listener, otherwise reuse a closed one */ if (listener->fd == -1) last_closed = listener; else return listener; } } return last_closed; } /* * add_listener- create a new listener * port - the port number to listen on * vhost_ip - if non-null must contain a valid IP address string in * the format "255.255.255.255" */ void add_listener(int port, const char* vhost_ip) { struct Listener* listener; struct irc_inaddr vaddr; /* * if no port in conf line, don't bother */ if (port == 0) return; #ifdef IPV6 copy_s_addr(IN_ADDR(vaddr), &in6addr_any); #else copy_s_addr(IN_ADDR(vaddr), INADDR_ANY); #endif if (vhost_ip) { if(inetpton(DEF_FAM, vhost_ip, &IN_ADDR(vaddr)) <= 0) return; } if ((listener = find_listener(port, &vaddr))) { if (listener->fd > -1) return; } else { listener = make_listener(port, &vaddr); listener->next = ListenerPollList; ListenerPollList = listener; } listener->fd = -1; if (inetport(listener)) listener->active = 1; else close_listener(listener); } /* * close_listener - close a single listener */ void close_listener(struct Listener* listener) { assert(listener != NULL); if(listener == NULL) return; if (listener->fd >= 0) { fd_close(listener->fd); listener->fd = -1; } listener->active = 0; if (listener->ref_count) return; free_listener(listener); }
static int map_fix_local_rlocs(struct locator_chain * lcptr) { int error = EINVAL; struct sockaddr_in *rloc_inet = NULL; struct sockaddr_in6 *rloc_inet6 = NULL; struct in_ifaddr *ia = NULL; struct in6_ifaddr *ia6 = NULL; while ( lcptr ) { /* Scan the chain checking if the RLOC is the address * of a local interface. */ switch (lcptr->rloc.rloc_addr->ss_family) { case AF_INET: rloc_inet = (struct sockaddr_in *) lcptr->rloc.rloc_addr; INADDR_TO_IFADDR(rloc_inet->sin_addr, ia); /* * If the address matches, set RLOCF_LIF * flag and MTU. */ if ((ia != NULL) && (IA_SIN(ia)->sin_addr.s_addr == rloc_inet->sin_addr.s_addr)) { lcptr->rloc.rloc_metrix.rlocmtx.flags |= RLOCF_LIF; lcptr->rloc.rloc_metrix.rlocmtx.mtu = (ia->ia_ifp)->if_mtu; error = 0; }; break; case AF_INET6: rloc_inet6 = (struct sockaddr_in6 *) lcptr->rloc.rloc_addr; ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(rloc_inet6)); /* * If the address matches, set RLOCF_LIF * flag and MTU. */ if ((ia6 != NULL) && (IN6_ARE_ADDR_EQUAL(&ia6->ia_addr.sin6_addr, &rloc_inet6->sin6_addr))) { lcptr->rloc.rloc_metrix.rlocmtx.flags |= RLOCF_LIF; lcptr->rloc.rloc_metrix.rlocmtx.mtu = (ia6->ia_ifp)->if_mtu; error = 0; }; break; }; lcptr = lcptr->next; }; #ifdef LISP_DEBUG if (error) { DEBUGLISP("[MAP_FIX_LOCAL_RLOC] No local IF RLOCs Provided for local mapping! \n"); }; #endif /* LISP_DEBUG */ return (error); } /* map_fix_local_rloc() */
static status_e failed_service(struct service *sp, connection_s *cp, access_e result) { struct service_config *scp = SVC_CONF( sp ) ; if ( result != AC_OK ) { bool_int report_failure = TRUE ; /* * Try to avoid reporting multiple times a failed attempt to access * a datagram-based service from a bad address. We do this because * the clients of such services usually send multiple datagrams * before reporting a timeout (we have no way of telling them that * their request has been denied). */ if ( result == AC_ADDRESS && SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) { if( SC_IPV4( scp ) ) { struct sockaddr_in *sinp = SAIN(CONN_ADDRESS( cp )) ; struct sockaddr_in *last = SAIN(SVC_LAST_DGRAM_ADDR(sp)) ; time_t current_time ; if (sinp == NULL ) return FAILED; if ( last == NULL ) { last = SAIN( calloc( 1, sizeof(union xsockaddr) ) ); SVC_LAST_DGRAM_ADDR(sp) = (union xsockaddr *)last; } (void) time( ¤t_time ) ; if ( sinp->sin_addr.s_addr == last->sin_addr.s_addr && sinp->sin_port == last->sin_port ) { if( current_time - SVC_LAST_DGRAM_TIME(sp) <= DGRAM_IGNORE_TIME ) report_failure = FALSE ; else SVC_LAST_DGRAM_TIME(sp) = current_time ; } else { memcpy(SVC_LAST_DGRAM_ADDR(sp), sinp,sizeof(struct sockaddr_in)); SVC_LAST_DGRAM_TIME(sp) = current_time ; } } else if( SC_IPV6( scp ) ) { struct sockaddr_in6 *sinp = SAIN6(CONN_ADDRESS( cp )) ; struct sockaddr_in6 *last = SAIN6(SVC_LAST_DGRAM_ADDR(sp)) ; time_t current_time ; if (sinp == NULL ) return FAILED; if( last == NULL ) { last = SAIN6(calloc( 1, sizeof(union xsockaddr) ) ); SVC_LAST_DGRAM_ADDR( sp ) = (union xsockaddr *)last; } (void) time( ¤t_time ) ; if ( IN6_ARE_ADDR_EQUAL(&(sinp->sin6_addr), &(last->sin6_addr)) && sinp->sin6_port == last->sin6_port ) { if((current_time - SVC_LAST_DGRAM_TIME(sp)) <= DGRAM_IGNORE_TIME) report_failure = FALSE ; else SVC_LAST_DGRAM_TIME(sp) = current_time ; } else { memcpy(SVC_LAST_DGRAM_ADDR(sp),sinp,sizeof(struct sockaddr_in6)); SVC_LAST_DGRAM_TIME(sp) = current_time ; } } } if ( report_failure ) svc_log_failure( sp, cp, result ) ; banner_fail(sp, cp); return( FAILED ) ; } return( OK ); }
/* * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. * If necessary, this function lookups the routing table and returns * an entry to the caller for later use. */ int in6_selectsrc(struct in6_addr **in6src, struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, struct in6_addr *laddr, u_int rtableid) { struct ifnet *ifp = NULL; struct in6_addr *dst; struct in6_ifaddr *ia6 = NULL; struct in6_pktinfo *pi = NULL; int error; dst = &dstsock->sin6_addr; /* * If the source address is explicitly specified by the caller, * check if the requested source address is indeed a unicast address * assigned to the node, and can be used as the packet's source * address. If everything is okay, use the address as source. */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { struct sockaddr_in6 sa6; /* get the outgoing interface */ error = in6_selectif(dstsock, opts, mopts, ro, &ifp, rtableid); if (error) return (error); bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); sa6.sin6_addr = pi->ipi6_addr; if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr)) sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); if_put(ifp); /* put reference from in6_selectif */ ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&sa6), rtableid)); if (ia6 == NULL || (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) return (EADDRNOTAVAIL); pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */ *in6src = &pi->ipi6_addr; return (0); } /* * If the source address is not specified but the socket(if any) * is already bound, use the bound address. */ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) { *in6src = laddr; return (0); } /* * If the caller doesn't specify the source address but * the outgoing interface, use an address associated with * the interface. */ if (pi && pi->ipi6_ifindex) { ifp = if_get(pi->ipi6_ifindex); if (ifp == NULL) return (ENXIO); /* XXX: better error? */ ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } /* * If the destination address is a link-local unicast address or * a link/interface-local multicast address, and if the outgoing * interface is specified by the sin6_scope_id filed, use an address * associated with the interface. * XXX: We're now trying to define more specific semantics of * sin6_scope_id field, so this part will be rewritten in * the near future. */ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) || IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) { ifp = if_get(dstsock->sin6_scope_id); if (ifp == NULL) return (ENXIO); /* XXX: better error? */ ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } /* * If the destination address is a multicast address and * the outgoing interface for the address is specified * by the caller, use an address associated with the interface. * Even if the outgoing interface is not specified, we also * choose a loopback interface as the outgoing interface. */ if (IN6_IS_ADDR_MULTICAST(dst)) { ifp = mopts ? if_get(mopts->im6o_ifidx) : NULL; if (!ifp && dstsock->sin6_scope_id) ifp = if_get(htons(dstsock->sin6_scope_id)); if (ifp) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } } /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ if (ro) { if (!rtisvalid(ro->ro_rt) || (ro->ro_tableid != rtableid) || !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) { rtfree(ro->ro_rt); ro->ro_rt = NULL; } if (ro->ro_rt == NULL) { struct sockaddr_in6 *sa6; /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); ro->ro_tableid = rtableid; sa6 = &ro->ro_dst; sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(struct sockaddr_in6); sa6->sin6_addr = *dst; sa6->sin6_scope_id = dstsock->sin6_scope_id; ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), RT_RESOLVE, ro->ro_tableid); } /* * in_pcbconnect() checks out IFF_LOOPBACK to skip using * the address. But we don't know why it does so. * It is necessary to ensure the scope even for lo0 * so doesn't check out IFF_LOOPBACK. */ if (ro->ro_rt) { ifp = if_get(ro->ro_rt->rt_ifidx); if (ifp != NULL) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); } if (ia6 == NULL) /* xxx scope error ?*/ ia6 = ifatoia6(ro->ro_rt->rt_ifa); } if (ia6 == NULL) return (EHOSTUNREACH); /* no route */ *in6src = &ia6->ia_addr.sin6_addr; return (0); } return (EADDRNOTAVAIL); }
/* * Re-Read the configuration file. * Return 1 on success, 0 on failure. * In case of failure, the old configuration will be unchanged (although the cache may not) and * **errstr will refer to a newly allocated string containing an error message. */ int reload_config_file(const char *nm, char **errstr) { globparm_t global_new; servparm_array servers_new; global_new=global; global_new.cache_dir=NULL; global_new.pidfile=NULL; global_new.scheme_file=NULL; global_new.deleg_only_zones=NULL; global_new.onquery=0; servers_new=NULL; if(read_config_file(nm,&global_new,&servers_new,0,errstr)) { if(global_new.cache_dir && strcmp(global_new.cache_dir,global.cache_dir)) { *errstr=strdup("Cannot reload config file: the specified cache_dir directory has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.pidfile && (!global.pidfile || strcmp(global_new.pidfile,global.pidfile))) { *errstr=strdup("Cannot reload config file: the specified pid_file has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.scheme_file && strcmp(global_new.scheme_file,global.scheme_file)) { *errstr=strdup("Cannot reload config file: the specified scheme_file has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.port!=global.port) { *errstr=strdup("Cannot reload config file: the specified server_port has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(!ADDR_EQUIV(&global_new.a,&global.a)) { *errstr=strdup("Cannot reload config file: the specified interface address (server_ip) has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } #ifdef ENABLE_IPV6 if(!IN6_ARE_ADDR_EQUAL(&global_new.ipv4_6_prefix,&global.ipv4_6_prefix)) { *errstr=strdup("Cannot reload config file: the specified ipv4_6_prefix has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } #endif if(strcmp(global_new.run_as,global.run_as)) { *errstr=strdup("Cannot reload config file: the specified run_as id has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.daemon!=global.daemon) { *errstr=strdup("Cannot reload config file: the daemon option has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.debug!=global.debug) { *errstr=strdup("Cannot reload config file: the debug option has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.stat_pipe!=global.stat_pipe) { *errstr=strdup("Cannot reload config file: the status_ctl option has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.notcp!=global.notcp) { *errstr=strdup("Cannot reload config file: the tcp_server option has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.strict_suid!=global.strict_suid) { *errstr=strdup("Cannot reload config file: the strict_setuid option has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(global_new.ctl_perms!=global.ctl_perms) { *errstr=strdup("Cannot reload config file: the specified ctl_perms has changed.\n" "Try restarting pdnsd instead."); goto cleanup_return; } if(ping_isocket==-1 #ifdef ENABLE_IPV6 && ping6_isocket==-1 #endif ) { int i,n=DA_NEL(servers_new); for (i=0;i<n;++i) { if (DA_INDEX(servers_new,i).uptest==C_PING) { if(asprintf(errstr,"Cannot reload config file: the ping socket is not initialized" " and the new config contains uptest=ping in server section %i.\n" "Try restarting pdnsd instead.",i)<0) *errstr=NULL; goto cleanup_return; } } } /* we need exclusive access to the server data to make the changes */ /* Wait at most 60 seconds to obtain a lock. */ if(!exclusive_lock_server_data(60)) { *errstr=strdup("Cannot reload config file: Timed out while waiting for access to config data."); goto cleanup_return; } free(global_new.cache_dir); global_new.cache_dir=global.cache_dir; free(global_new.pidfile); global_new.pidfile=global.pidfile; free(global_new.scheme_file); global_new.scheme_file=global.scheme_file; free_zones(global.deleg_only_zones); global=global_new; free_server_data(servers); servers=servers_new; /* schedule a retest to check which servers are up, and free the lock. */ exclusive_unlock_server_data(1); return 1; } cleanup_return: free(global_new.cache_dir); free(global_new.pidfile); free(global_new.scheme_file); free_zones(global_new.deleg_only_zones); free_server_data(servers_new); return 0; }
/** * Adds a specified route to the routingtable. * If the route already exists, the existing route * is updated... */ int insertRoute(struct in6_addr *group, struct in6_addr *src, struct sockaddr_in6 *from) { struct Config *conf = getCommonConfig(); struct RouteTable *croute, *tmpRoute; //int result = 1; /* int i; printf("insertRoute called\n"); for (i = 0; i < 16; i ++) printf("%02x ", ((char*)group)[i] & 255 ); printf("\n"); for (i = 0; i < 16; i ++) printf("%02x ", ((char*)src)[i] & 255 ); printf("\n"); */ // Sanitycheck the group adress... if ( !IN6_IS_ADDR_MULTICAST(group) ) { atlog( LOG_DEBUG, 0, "Not multicast group"); return 0; } // Santiycheck the VIF index... if(from->sin6_scope_id >= MAX_MC_VIFS) { atlog(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.", from->sin6_scope_id,MAX_MC_VIFS); return 0; } // Try to find an existing route for this group... croute = findRoute(group, src); if(croute==NULL) { struct RouteTable* newroute; /* IF_DEBUG atlog(LOG_DEBUG, 0, "No existing route for %s. Create new.", inetFmt(group, s1)); */ // Create and initialize the new route table entry.. newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable)); // Insert the route desc and clear all pointers... newroute->group = *group; newroute->originAddr = *src; newroute->nextroute = NULL; newroute->prevroute = NULL; // The group is not joined initially. newroute->upstrState = ROUTESTATE_NOTJOINED; // The route is not active yet, so the age is unimportant. newroute->ageValue = conf->robustnessValue; newroute->ageActivity = 0; BIT_ZERO(newroute->ageVifBits); // Initially we assume no listeners. // Set the listener flag... BIT_ZERO(newroute->vifBits); // Initially no listeners... if(from->sin6_scope_id >= 0) { BIT_SET(newroute->vifBits, from->sin6_scope_id); } // Check if there is a table already.... if(routing_table == NULL) { // No location set, so insert in on the table top. routing_table = newroute; IF_DEBUG atlog(LOG_DEBUG, 0, "No routes in table. Insert at beginning."); } else { IF_DEBUG atlog(LOG_DEBUG, 0, "Found existing routes. Find insert location."); // Check if the route could be inserted at the beginning... if( memcmp(&routing_table->group, group, sizeof(struct in6_addr)) > 0 ) { //IF_DEBUG atlog(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1)); // Insert at beginning... newroute->nextroute = routing_table; newroute->prevroute = NULL; routing_table = newroute; // If the route has a next node, the previous pointer must be updated. if(newroute->nextroute != NULL) { newroute->nextroute->prevroute = newroute; } } else { // Find the location which is closest to the route. for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) { // Find insert position. if( memcmp( &(croute->nextroute->group), group, sizeof(struct in6_addr) ) > 0 ) break; } //IF_DEBUG atlog(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1)); // Insert after current... newroute->nextroute = croute->nextroute; newroute->prevroute = croute; if(croute->nextroute != NULL) { croute->nextroute->prevroute = newroute; } croute->nextroute = newroute; } } // Set the new route as the current... croute = newroute; if( !IN6_ARE_ADDR_EQUAL( &croute->originAddr, &allzero_addr) ) { // Update route in kernel... if( !internUpdateKernelRoute(croute, 1) ) { atlog(LOG_WARNING, 0, "The insertion into Kernel failed."); return 0; } } } else if(from->sin6_scope_id >= 0) { if( !IN6_ARE_ADDR_EQUAL( &croute->originAddr, &allzero_addr) ) { // The route exists already, so just update it. BIT_SET(croute->vifBits, from->sin6_scope_id); // Register the VIF activity for the aging routine BIT_SET(croute->ageVifBits, from->sin6_scope_id); } else { tmpRoute = findNextRoute(group, NULL); while (tmpRoute != NULL) { // The route exists already, so just update it. BIT_SET(tmpRoute->vifBits, from->sin6_scope_id); // Register the VIF activity for the aging routine BIT_SET(tmpRoute->ageVifBits, from->sin6_scope_id); if (tmpRoute->nextroute) tmpRoute = findNextRoute(group, tmpRoute->nextroute); else break; } } } // Send join message upstream, if the route has no joined flag... if(croute->upstrState != ROUTESTATE_JOINED) { // Send Join request upstream sendJoinLeaveUpstream(croute, 1); } IF_DEBUG atlogRouteTable("Insert Route"); return 1; }
/* * Do what we need to do when inserting a route. */ static struct radix_node * in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, struct radix_node *treenodes) { struct rtentry *rt = (struct rtentry *)treenodes; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt); struct radix_node *ret; lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED); RT_LOCK_ASSERT_HELD(rt); /* * If this is a dynamic route (which is created via Redirect) and * we already have the maximum acceptable number of such route entries, * reject creating a new one. We could initiate garbage collection to * make available space right now, but the benefit would probably not * be worth the cleaning overhead; we only have to endure a slightly * suboptimal path even without the redirecbted route. */ if ((rt->rt_flags & RTF_DYNAMIC) != 0 && ip6_maxdynroutes >= 0 && in6dynroutes >= ip6_maxdynroutes) return (NULL); /* * For IPv6, all unicast non-host routes are automatically cloning. */ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) rt->rt_flags |= RTF_MULTICAST; if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) { rt->rt_flags |= RTF_PRCLONING; } /* * A little bit of help for both IPv6 output and input: * For local addresses, we make sure that RTF_LOCAL is set, * with the thought that this might one day be used to speed up * ip_input(). * * We also mark routes to multicast addresses as such, because * it's easy to do and might be useful (but this is much more * dubious since it's so easy to inspect the address). (This * is done above.) * * XXX * should elaborate the code. */ if (rt->rt_flags & RTF_HOST) { if (IN6_ARE_ADDR_EQUAL(&satosin6(rt->rt_ifa->ifa_addr) ->sin6_addr, &sin6->sin6_addr)) { rt->rt_flags |= RTF_LOCAL; } } if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) && rt->rt_ifp) rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; ret = rn_addroute(v_arg, n_arg, head, treenodes); if (ret == NULL && (rt->rt_flags & RTF_HOST)) { struct rtentry *rt2; /* * We are trying to add a host route, but can't. * Find out if it is because of an * ARP entry and delete it if so. */ rt2 = rtalloc1_locked((struct sockaddr *)sin6, 0, RTF_CLONING | RTF_PRCLONING); if (rt2) { RT_LOCK(rt2); if ((rt2->rt_flags & RTF_LLINFO) && (rt2->rt_flags & RTF_HOST) && rt2->rt_gateway != NULL && rt2->rt_gateway->sa_family == AF_LINK) { /* * Safe to drop rt_lock and use rt_key, * rt_gateway, since holding rnh_lock here * prevents another thread from calling * rt_setgate() on this route. */ RT_UNLOCK(rt2); (void) rtrequest_locked(RTM_DELETE, rt_key(rt2), rt2->rt_gateway, rt_mask(rt2), rt2->rt_flags, 0); ret = rn_addroute(v_arg, n_arg, head, treenodes); } else { RT_UNLOCK(rt2); } rtfree_locked(rt2); } } else if (ret == NULL && (rt->rt_flags & RTF_CLONING)) { struct rtentry *rt2; /* * We are trying to add a net route, but can't. * The following case should be allowed, so we'll make a * special check for this: * Two IPv6 addresses with the same prefix is assigned * to a single interrface. * # ifconfig if0 inet6 3ffe:0501::1 prefix 64 alias (*1) * # ifconfig if0 inet6 3ffe:0501::2 prefix 64 alias (*2) * In this case, (*1) and (*2) want to add the same * net route entry, 3ffe:0501:: -> if0. * This case should not raise an error. */ rt2 = rtalloc1_locked((struct sockaddr *)sin6, 0, RTF_CLONING | RTF_PRCLONING); if (rt2) { RT_LOCK(rt2); if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY)) == RTF_CLONING && rt2->rt_gateway && rt2->rt_gateway->sa_family == AF_LINK && rt2->rt_ifp == rt->rt_ifp) { ret = rt2->rt_nodes; } RT_UNLOCK(rt2); rtfree_locked(rt2); } } if (ret != NULL && (rt->rt_flags & RTF_DYNAMIC) != 0) in6dynroutes++; return ret; }
static gboolean process_nduseropt_rdnss (NMIP6Device *device, struct nd_opt_hdr *opt) { size_t opt_len; struct nd_opt_rdnss *rdnss_opt; time_t now = time (NULL); struct in6_addr *addr; GArray *new_servers; NMIP6RDNSS server, *cur_server; gboolean changed = FALSE; guint i; opt_len = opt->nd_opt_len; if (opt_len < 3 || (opt_len & 1) == 0) return FALSE; rdnss_opt = (struct nd_opt_rdnss *) opt; new_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS)); /* Pad the DNS server expiry somewhat to give a bit of slack in cases * where one RA gets lost or something (which can happen on unreliable * links like WiFi where certain types of frames are not retransmitted). * Note that 0 has special meaning and is therefore not adjusted. */ server.expires = ntohl (rdnss_opt->nd_opt_rdnss_lifetime); if (server.expires > 0) if (server.expires < 7200) server.expires = 7200; server.expires += now; for (addr = (struct in6_addr *) (rdnss_opt + 1); opt_len >= 2; addr++, opt_len -= 2) { char buf[INET6_ADDRSTRLEN + 1]; if (!inet_ntop (AF_INET6, addr, buf, sizeof (buf))) { nm_log_warn (LOGD_IP6, "(%s): received invalid RA-provided nameserver", device->iface); continue; } /* Update the cached timeout if we already saw this server */ for (i = 0; i < device->rdnss_servers->len; i++) { cur_server = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i)); if (!IN6_ARE_ADDR_EQUAL (addr, &cur_server->addr)) continue; cur_server->expires = server.expires; if (server.expires > 0) { nm_log_dbg (LOGD_IP6, "(%s): refreshing RA-provided nameserver %s (expires in %ld seconds)", device->iface, buf, server.expires - now); break; } nm_log_dbg (LOGD_IP6, "(%s): removing RA-provided nameserver %s on router request", device->iface, buf); g_array_remove_index (device->rdnss_servers, i); changed = TRUE; break; } if (server.expires == 0) continue; if (i < device->rdnss_servers->len) continue; nm_log_dbg (LOGD_IP6, "(%s): found RA-provided nameserver %s (expires in %ld seconds)", device->iface, buf, server.expires - now); server.addr = *addr; g_array_append_val (new_servers, server); } /* New servers must be added in the order they are listed in the * RA option and before any existing servers. * * Note: This is the place to remove servers if we want to cap the * number of resolvers. The RFC states that the one to expire * first of the existing servers should be removed. */ if (new_servers->len) { g_array_prepend_vals (device->rdnss_servers, new_servers->data, new_servers->len); changed = TRUE; } g_array_free (new_servers, TRUE); /* Timeouts may have changed even if IPs didn't */ set_rdnss_timeout (device); return changed; }
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, *rcvifp, *origifp; /* maybe unnecessary */ u_int32_t inzone, outzone, len; struct in6_addr src_in6, dst_in6; uint64_t curtime = net_uptime(); #if IPSEC struct secpolicy *sp = NULL; #endif unsigned int ifscope = IFSCOPE_NONE; #if PF struct pf_mtag *pf_mtag; #endif /* PF */ /* * In the prefix proxying case, the route to the proxied node normally * gets created by nd6_prproxy_ns_output(), as part of forwarding a * NS (NUD/AR) packet to the proxied node. In the event that such * packet did not arrive in time before the correct route gets created, * ip6_input() would have performed a rtalloc() which most likely will * create the wrong cloned route; this route points back to the same * interface as the inbound interface, since the parent non-scoped * prefix route points there. Therefore we check if that is the case * and perform the necessary fixup to get the correct route installed. */ if (!srcrt && nd6_prproxy && (rt = ip6forward_rt->ro_rt) != NULL && (rt->rt_flags & RTF_PROXY)) { nd6_proxy_find_fwdroute(m->m_pkthdr.rcvif, ip6forward_rt); if ((rt = ip6forward_rt->ro_rt) != NULL) ifscope = rt->rt_ifp->if_index; } #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); ROUTE_RELEASE(ip6forward_rt); 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. * 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 < curtime) { ip6_log_time = curtime; 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_flag(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0, 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) { ROUTE_RELEASE(&state.ro); return (NULL); /* packet is gone - sent over IPv4 */ } m = state.m; ROUTE_RELEASE(&state.ro); 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); } } #endif /* IPSEC */ skip_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); } VERIFY(rt == NULL || rt == ip6forward_rt->ro_rt); if (!srcrt) { /* * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (ROUTE_UNUSABLE(ip6forward_rt)) { if (rt != NULL) { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); } ROUTE_RELEASE(ip6forward_rt); /* 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 (ROUTE_UNUSABLE(ip6forward_rt) || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) { if (rt != NULL) { /* Release extra ref */ RT_REMREF_LOCKED(rt); RT_UNLOCK(rt); } ROUTE_RELEASE(ip6forward_rt); 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 < curtime) { ip6_log_time = curtime; 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 = rcvifp = m->m_pkthdr.rcvif; } else if (nd6_prproxy) { /* * In the prefix proxying case, we need to inform nd6_output() * about the inbound interface, so that any subsequent NS * packets generated by nd6_prproxy_ns_output() will not be * sent back to that same interface. */ origifp = rcvifp = m->m_pkthdr.rcvif; } else { rcvifp = m->m_pkthdr.rcvif; 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.pkt_flags & PKTF_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 */ /* Mark this packet as being forwarded from another interface */ m->m_pkthdr.pkt_flags |= PKTF_FORWARDED; len = m_pktlen(m); error = nd6_output(ifp, origifp, m, dst, rt, NULL); if (error) { in6_ifstat_inc(ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; } else { /* * Increment stats on the source interface; the ones * for destination interface has been taken care of * during output above by virtue of PKTF_FORWARDED. */ rcvifp->if_fpackets++; rcvifp->if_fbytes += len; 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); }
/* * Input an Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) */ void nd6_ns_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ifnet *cmpifp; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa = NULL; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int tlladdr; union nd_opts ndopts; struct sockaddr_dl *proxydl = NULL; /* * Collapse interfaces to the bridge for comparison and * mac (llinfo) purposes. */ cmpifp = ifp; if (ifp->if_bridge) cmpifp = ifp->if_bridge; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { icmp6stat.icp6s_tooshort++; return; } #endif ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be solicited node multicast address. */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && /* don't check ifindex portion */ daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); goto bad; } } else if (!nd6_onlink_ns_rfc4861) { /* * Make sure the source address is from a neighbor's address. * * XXX probably only need to check cmpifp. */ if (in6ifa_ifplocaladdr(cmpifp, &saddr6) == NULL && in6ifa_ifplocaladdr(ifp, &saddr6) == NULL) { nd6log((LOG_INFO, "nd6_ns_input: " "NS packet from non-neighbor\n")); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) taddr6.s6_addr16[1] = htons(ifp->if_index); icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n")); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is unicast/anycast MUST NOT add * NS IP dst is solicited-node multicast MUST add * * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ #if 0 /* too much! */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) tlladdr = 0; else #endif if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface. * (2) Unicast or anycast address for which I'm offering proxy * service. * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ #ifdef CARP if (ifp->if_carp) ifa = carp_iamatch6(ifp->if_carp, &taddr6); if (!ifa) ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); #else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); #endif /* * (2) Check proxying. Requires ip6_forwarding to be turned on. * * If the packet is anycast the target route must be on a * different interface because the anycast will get anything * on the current interface. * * If the packet is unicast the target route may be on the * same interface. If the gateway is a (typically manually * configured) link address we can directly offer it. * XXX for now we don't do this but instead offer ours and * presumably relay. * * WARNING! Since this is a subnet proxy the interface proxying * the ND6 must be in promiscuous mode or it will not see the * solicited multicast requests for various hosts being proxied. * * WARNING! Since this is a subnet proxy we have to treat bridge * interfaces as being the bridge itself so we do not proxy-nd6 * between bridge interfaces (which are effectively switched). * * (In the specific-host-proxy case via RTF_ANNOUNCE, which is * a bitch to configure, a specific multicast route is already * added for that host <-- NOT RECOMMENDED). */ if (!ifa && ip6_forwarding) { struct rtentry *rt; struct sockaddr_in6 tsin6; struct ifnet *rtifp; bzero(&tsin6, sizeof tsin6); tsin6.sin6_len = sizeof(struct sockaddr_in6); tsin6.sin6_family = AF_INET6; tsin6.sin6_addr = taddr6; rt = rtpurelookup((struct sockaddr *)&tsin6); rtifp = rt ? rt->rt_ifp : NULL; if (rtifp && rtifp->if_bridge) rtifp = rtifp->if_bridge; if (rt != NULL && (cmpifp != rtifp || (cmpifp == rtifp && (m->m_flags & M_MCAST) == 0)) ) { ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(cmpifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); nd6log((LOG_INFO, "nd6_ns_input: nd6 proxy %s(%s)<-%s ifa %p\n", if_name(cmpifp), if_name(ifp), if_name(rtifp), ifa)); if (ifa) { proxy = 1; /* * Manual link address on same interface * w/announce flag will proxy-arp using * target mac, else our mac is used. */ if (cmpifp == rtifp && (rt->rt_flags & RTF_ANNOUNCE) && rt->rt_gateway->sa_family == AF_LINK) { proxydl = SDL(rt->rt_gateway); } } } if (rt != NULL) --rt->rt_refcnt; } if (ifa == NULL) { /* * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; if (lladdr && ((cmpifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(&taddr6), cmpifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6))); goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicated address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { saddr6 = kin6addr_linklocal_allnodes; saddr6.s6_addr16[1] = htons(cmpifp->if_index); nd6_na_output(cmpifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), tlladdr, (struct sockaddr *)proxydl); goto freeit; } nd6_cache_lladdr(cmpifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, tlladdr, (struct sockaddr *)proxydl); freeit: m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); icmp6stat.icp6s_badns++; m_freem(m); }
/* * Walk the SCTP global list and refrele the ire for this ipif * This is called when an address goes down, so that we release any reference * to the ire associated with this address. Additionally, for any SCTP if * this was the only/last address in its source list, we don't kill the * assoc., if there is no address added subsequently, or if this does not * come up, then the assoc. will die a natural death (i.e. timeout). */ void sctp_ire_cache_flush(ipif_t *ipif) { sctp_t *sctp; sctp_t *sctp_prev = NULL; sctp_faddr_t *fp; conn_t *connp; ire_t *ire; sctp = gsctp; mutex_enter(&sctp_g_lock); while (sctp != NULL) { mutex_enter(&sctp->sctp_reflock); if (sctp->sctp_condemned) { mutex_exit(&sctp->sctp_reflock); sctp = list_next(&sctp_g_list, sctp); continue; } sctp->sctp_refcnt++; mutex_exit(&sctp->sctp_reflock); mutex_exit(&sctp_g_lock); if (sctp_prev != NULL) SCTP_REFRELE(sctp_prev); RUN_SCTP(sctp); connp = sctp->sctp_connp; mutex_enter(&connp->conn_lock); ire = connp->conn_ire_cache; if (ire != NULL && ire->ire_ipif == ipif) { connp->conn_ire_cache = NULL; mutex_exit(&connp->conn_lock); IRE_REFRELE_NOTR(ire); } else { mutex_exit(&connp->conn_lock); } /* check for ires cached in faddr */ for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->next) { /* * If this ipif is being used as the source address * we need to update it as well, else we will end * up using the dead source address. */ ire = fp->ire; if (ire != NULL && ire->ire_ipif == ipif) { fp->ire = NULL; IRE_REFRELE_NOTR(ire); } /* * This may result in setting the fp as unreachable, * i.e. if all the source addresses are down. In * that case the assoc. would timeout. */ if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr, &fp->saddr)) { sctp_set_saddr(sctp, fp); if (fp == sctp->sctp_current && fp->state != SCTP_FADDRS_UNREACH) { sctp_set_faddr_current(sctp, fp); } } } WAKE_SCTP(sctp); sctp_prev = sctp; mutex_enter(&sctp_g_lock); sctp = list_next(&sctp_g_list, sctp); } mutex_exit(&sctp_g_lock); if (sctp_prev != NULL) SCTP_REFRELE(sctp_prev); }
static gboolean v6_v6_equal (const struct sockaddr_in6 *a, const struct sockaddr_in6 *b) { return IN6_ARE_ADDR_EQUAL (&a->sin6_addr, &b->sin6_addr); }
/***************************************************************************** * processNS * Takes a received Neighbor Solicitation and handles it. Main logic is: * - bit of extra validation. * - determine who it's asking about. * - see if that matches the prefix we are looking after. * - if it does, send a Neighbor Advertisement. * * For more, see the inline comments - There's a lot going on here. * * Inputs: * char *msg * The received NS. * int len * The length of the received (candidate) NS. * *** This has already been sanity checked back in the callers *** * * Outputs: * Potentially, sends the Neighbor Advertisement. * * Return: * void * */ void processNS( int ifIndex, unsigned char *msg, unsigned int len) { // String representations of the various addresses char targetaddr_str[INET6_ADDRSTRLEN]; char prefixaddr_str[INET6_ADDRSTRLEN]; char srcaddr_str[INET6_ADDRSTRLEN]; char dstaddr_str[INET6_ADDRSTRLEN]; // Offsets into the received packet struct ip6_hdr *ip6h = (struct ip6_hdr *)(msg + ETH_HLEN); struct icmp6_hdr *icmph = (struct icmp6_hdr *)(msg + ETH_HLEN + sizeof( struct ip6_hdr)); struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)(msg + ETH_HLEN + sizeof( struct ip6_hdr)); // For the interfaceIdx struct in6_addr prefixaddr = interfaces[ifIndex].prefix; int prefixaddrlen = interfaces[ifIndex].prefixLen; unsigned char *linkAddr = interfaces[ifIndex].linkAddr; int interfaceIdx = interfaces[ifIndex].index; // Extracted from the received packet struct in6_addr *srcaddr; struct in6_addr *dstaddr; struct in6_addr *targetaddr; unsigned int multicastNS; // For outgoing NA struct in6_addr srcLinkAddr = IN6ADDR_ANY_INIT; struct in6_pktinfo *pkt_info; struct sockaddr_in6 sockaddr; unsigned char nabuff[MAX_PKT_BUFF]; struct nd_neighbor_advert *nad; size_t iovlen=0; struct iovec iov; struct cmsghdr *cmsg; char __attribute__((aligned(8))) chdr[CMSG_SPACE(sizeof(struct in6_pktinfo))]; struct msghdr mhdr; ssize_t err; struct nd_opt_hdr *opthdr; void *optdata; // Validate ICMP packet type, to ensure filter was correct // In theory not required, as the filter CAN'T be wrong...! if ( icmph->icmp6_type == ND_NEIGHBOR_SOLICIT ) { flog(LOG_DEBUG2, "Confirmed packet as icmp6 Neighbor Solicitation."); srcaddr = &ip6h->ip6_src; dstaddr = &ip6h->ip6_dst; if (debug) { print_addr(srcaddr, srcaddr_str); print_addr(dstaddr, dstaddr_str); flog( LOG_DEBUG, "src addr = %s", srcaddr_str); flog( LOG_DEBUG, "dst addr = %s", dstaddr_str); } } else { flog(LOG_ERR, "Received impossible packet... filter failed. Oooops."); return; } // Bug 27 - Handle DAD NS as per RFC4862, 5.4.3 if ( IN6_IS_ADDR_UNSPECIFIED(srcaddr) ) { flog(LOG_DEBUG, "Unspecified src addr - DAD activity. Ignoring NS."); return; } // Based upon the dstaddr, record if this was a unicast or multicast NS. // If unicast, we'll use that later when we decide whether to add the // target link-layer option to any outgoing NA. if ( IN6_IS_ADDR_MULTICAST(dstaddr) ) { // This was a multicast NS flog(LOG_DEBUG2, "Multicast NS"); multicastNS = 1; }else { // This was a unicast NS flog(LOG_DEBUG2, "Unicast NS"); multicastNS=0; } // Within the NS, who are they looking for? targetaddr = (struct in6_addr *)&(ns->nd_ns_target); if (debug || listLog) { print_addr16(targetaddr, targetaddr_str); print_addr16(&prefixaddr, prefixaddr_str); flog(LOG_DEBUG, "NS target addr: %s", targetaddr_str); flog(LOG_DEBUG, "Local prefix: %s", prefixaddr_str); } // If tgt-addr == dst-addr then ignore this, as the automatic mechanisms // will reply themselves - we don't need to. if ( nsIgnoreLocal && IN6_ARE_ADDR_EQUAL(targetaddr, dstaddr) ) { flog(LOG_DEBUG, "tgt==dst - Ignore."); return; } // Check for black or white listing compliance switch (listType) { case NOLIST: flog(LOG_DEBUG2, "Neither white nor black listing in operation."); break; case BLACKLIST: // See if the address matches an expression if((compareExpression(targetaddr) == 1)) { flog(LISTLOGGING, "NS for blacklisted EXPR address: %s", targetaddr_str); return; // Abandon } // If active and tgt is in the list, bail. if ( tfind( (void *)targetaddr, &lRoot, tCompare) ) { flog(LISTLOGGING, "NS for blacklisted specific addr: %s", targetaddr_str); return; //Abandon } break; case WHITELIST: // See if the address matches an expression if((compareExpression(targetaddr) == 1)) { flog(LISTLOGGING, "NS for whitelisted EXPR: %s", targetaddr_str); break; // Don't check further - we got a hit. } // If active and tgt is NOT in the list (and didn't match an expr above), bail. if ( tfind( (void *)targetaddr, &lRoot, tCompare) ) { flog(LISTLOGGING, "NS for specific addr whitelisted: %s", targetaddr_str); break; } else { // We have whitelisting in operation but failed to match either type. // Log it if required. flog(LOG_DEBUG, "No whitelist match for: %s", targetaddr_str); return; } break; } // Does it match our configured prefix that we're interested in? if (! addr6match( targetaddr, &prefixaddr, prefixaddrlen) ) { flog(LOG_DEBUG, "Target/:prefix - Ignore NS."); return; } else { flog(LOG_DEBUG, "Target:prefix - Build NA response."); // If configured, log target to list if (collectTargets) { flog(LOG_DEBUG, "Store target to list."); storeTarget( targetaddr ); } // Start building up the header for the packet memset(( void *)&sockaddr, 0, sizeof(struct sockaddr_in6)); sockaddr.sin6_family = AF_INET6; sockaddr.sin6_port = htons(IPPROTO_ICMPV6); // Set the destination of the NA memcpy(&sockaddr.sin6_addr, srcaddr, sizeof(struct in6_addr)); // Set up the NA itself memset( nabuff, 0, sizeof(nabuff) ); nad = (struct nd_neighbor_advert *)nabuff; nad->nd_na_type = ND_NEIGHBOR_ADVERT; nad->nd_na_code = 0; nad->nd_na_cksum = 0; if (naRouter) { nad->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED | ND_NA_FLAG_ROUTER; } else { nad->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED; } memcpy(&(nad->nd_na_target), targetaddr, sizeof(struct in6_addr) ); if (multicastNS || naLinkOptFlag) { // If the NS that came in was to a multicast address // or if we have forced the option for all packets anyway // then add a target link-layer option to the outgoing NA. // Per rfc, we must add dest link-addr option for NSs that came // to the multicast group addr. opthdr = (struct nd_opt_hdr *)&nabuff[sizeof(struct nd_neighbor_advert)] ; opthdr->nd_opt_type = ND_OPT_TARGET_LINKADDR; opthdr->nd_opt_len = 1; // Units of 8-octets optdata = (unsigned char *) (opthdr + 1); memcpy( optdata, linkAddr, 6); // Build the io vector iovlen = sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + ETH_ALEN; iov.iov_len = iovlen; iov.iov_base = (caddr_t) nabuff; } else { // The NS was unicast AND the config option was unset. // Build the io vector iovlen = sizeof(struct nd_neighbor_advert); iov.iov_len = iovlen; iov.iov_base = (caddr_t) nabuff; } // Build the cmsg memset(chdr, 0, sizeof(chdr) ); cmsg = (struct cmsghdr *) chdr; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo) ); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); // Set src (sending) addr and outgoing i/f for the datagram memcpy(&pkt_info->ipi6_addr, &srcLinkAddr, sizeof(struct in6_addr) ); pkt_info->ipi6_ifindex = interfaceIdx; // Build the mhdr memset(&mhdr, 0, sizeof(mhdr) ); mhdr.msg_name = (caddr_t)&sockaddr; mhdr.msg_namelen = sizeof(sockaddr); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = (void *) cmsg; mhdr.msg_controllen = sizeof(chdr); flog(LOG_DEBUG2, "Outbound message built"); err = sendmsg( interfaces[ifIndex].icmpSock, &mhdr, 0); if (err < 0) flog(LOG_ERR, "sendmsg returned with error %d = %s", errno, strerror(errno)); else flog(LOG_DEBUG2, "sendmsg completed OK"); } }
/* * Loop over a tdb chain, taking into consideration protocol tunneling. The * fourth argument is set if the first encapsulation header is already in * place. */ int ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready) { struct timeval tv; int i, off, error; struct mbuf *mp; #ifdef INET6 struct ip6_ext ip6e; int nxt; int dstopt = 0; #endif #ifdef INET int setdf = 0; struct ip *ip; #endif /* INET */ #ifdef INET6 struct ip6_hdr *ip6; #endif /* INET6 */ /* Check that the transform is allowed by the administrator. */ if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) || (tdb->tdb_sproto == IPPROTO_AH && !ah_enable) || (tdb->tdb_sproto == IPPROTO_IPCOMP && !ipcomp_enable)) { DPRINTF(("ipsp_process_packet(): IPsec outbound packet " "dropped due to policy (check your sysctls)\n")); m_freem(m); return EHOSTUNREACH; } /* Sanity check. */ if (!tdb->tdb_xform) { DPRINTF(("ipsp_process_packet(): uninitialized TDB\n")); m_freem(m); return EHOSTUNREACH; } /* Check if the SPI is invalid. */ if (tdb->tdb_flags & TDBF_INVALID) { DPRINTF(("ipsp_process_packet(): attempt to use invalid " "SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto)); m_freem(m); return ENXIO; } /* Check that the network protocol is supported */ switch (tdb->tdb_dst.sa.sa_family) { #ifdef INET case AF_INET: break; #endif /* INET */ #ifdef INET6 case AF_INET6: break; #endif /* INET6 */ default: DPRINTF(("ipsp_process_packet(): attempt to use " "SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family)); m_freem(m); return ENXIO; } /* * Register first use if applicable, setup relevant expiration timer. */ if (tdb->tdb_first_use == 0) { tdb->tdb_first_use = time_second; tv.tv_usec = 0; tv.tv_sec = tdb->tdb_first_use + tdb->tdb_exp_first_use; if (tdb->tdb_flags & TDBF_FIRSTUSE) timeout_add(&tdb->tdb_first_tmo, hzto(&tv)); tv.tv_sec = tdb->tdb_first_use + tdb->tdb_soft_first_use; if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) timeout_add(&tdb->tdb_sfirst_tmo, hzto(&tv)); } /* * Check for tunneling if we don't have the first header in place. * When doing Ethernet-over-IP, we are handed an already-encapsulated * frame, so we don't need to re-encapsulate. */ if (tunalready == 0) { /* * If the target protocol family is different, we know we'll be * doing tunneling. */ if (af == tdb->tdb_dst.sa.sa_family) { #ifdef INET if (af == AF_INET) i = sizeof(struct ip); #endif /* INET */ #ifdef INET6 if (af == AF_INET6) i = sizeof(struct ip6_hdr); #endif /* INET6 */ /* Bring the network header in the first mbuf. */ if (m->m_len < i) { if ((m = m_pullup(m, i)) == NULL) return ENOBUFS; } #ifdef INET if (af == AF_INET) { ip = mtod(m, struct ip *); /* * This is not a bridge packet, remember if we * had IP_DF. */ setdf = ip->ip_off & htons(IP_DF); } #endif /* INET */ #ifdef INET6 if (af == AF_INET6) ip6 = mtod(m, struct ip6_hdr *); #endif /* INET6 */ } /* Do the appropriate encapsulation, if necessary. */ if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */ (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */ #ifdef INET ((tdb->tdb_dst.sa.sa_family == AF_INET) && (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) && (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || #endif /* INET */ #ifdef INET6 ((tdb->tdb_dst.sa.sa_family == AF_INET6) && (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) && (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, &ip6->ip6_dst))) || #endif /* INET6 */ 0) { #ifdef INET /* Fix IPv4 header checksum and length. */ if (af == AF_INET) { if (m->m_len < sizeof(struct ip)) if ((m = m_pullup(m, sizeof(struct ip))) == NULL) return ENOBUFS; ip = mtod(m, struct ip *); ip->ip_len = htons(m->m_pkthdr.len); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, ip->ip_hl << 2); } #endif /* INET */ #ifdef INET6 /* Fix IPv6 header payload length. */ if (af == AF_INET6) { if (m->m_len < sizeof(struct ip6_hdr)) if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) return ENOBUFS; if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { /* No jumbogram support. */ m_freem(m); return ENXIO; /*?*/ } ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); } #endif /* INET6 */ /* Encapsulate -- the last two arguments are unused. */ error = ipip_output(m, tdb, &mp, 0, 0); if ((mp == NULL) && (!error)) error = EFAULT; if (error) { if (mp) { m_freem(mp); mp = NULL; } return error; } m = mp; mp = NULL; #ifdef INET if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) { if (m->m_len < sizeof(struct ip)) if ((m = m_pullup(m, sizeof(struct ip))) == NULL) return ENOBUFS; ip = mtod(m, struct ip *); ip->ip_off |= htons(IP_DF); } #endif /* Remember that we appended a tunnel header. */ tdb->tdb_flags |= TDBF_USEDTUNNEL; } /* We may be done with this TDB */ if (tdb->tdb_xform->xf_type == XF_IP4) return ipsp_process_done(m, tdb); } else {
/* * Pass some notification to all connections of a protocol * associated with address dst. The local address and/or port numbers * may be specified to limit the search. The "usual action" will be * taken, depending on the ctlinput cmd. The caller must filter any * cmds that are uninteresting (e.g., no error in the map). * Call the protocol specific routine (if any) to report * any errors for each matching socket. * * Also perform input-side security policy check * once PCB to be notified has been located. * * Must be called at splnet. */ int in6_pcbnotify(struct inpcbtable *head, struct sockaddr *dst, uint fport_arg, struct sockaddr *src, uint lport_arg, int cmd, void *cmdarg, void (*notify)(struct inpcb *, int)) { struct inpcb *inp, *ninp; u_short fport = fport_arg, lport = lport_arg; struct sockaddr_in6 sa6_src, *sa6_dst; int errno, nmatch = 0; u_int32_t flowinfo; if ((unsigned)cmd >= PRC_NCMDS || dst->sa_family != AF_INET6) return (0); sa6_dst = (struct sockaddr_in6 *)dst; if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr)) return (0); if (IN6_IS_ADDR_V4MAPPED(&sa6_dst->sin6_addr)) { #ifdef DIAGNOSTIC printf("Huh? Thought in6_pcbnotify() never got " "called with mapped!\n"); #endif return (0); } /* * note that src can be NULL when we get notify by local fragmentation. */ sa6_src = (src == NULL) ? sa6_any : *(struct sockaddr_in6 *)src; flowinfo = sa6_src.sin6_flowinfo; /* * Redirects go to all references to the destination, * and use in_rtchange to invalidate the route cache. * Dead host indications: also use in_rtchange to invalidate * the cache, and deliver the error to all the sockets. * Otherwise, if we have knowledge of the local port and address, * deliver only to that socket. */ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; sa6_src.sin6_addr = in6addr_any; if (cmd != PRC_HOSTDEAD) notify = in_rtchange; } errno = inet6ctlerrmap[cmd]; for (inp = CIRCLEQ_FIRST(&head->inpt_queue); inp != CIRCLEQ_END(&head->inpt_queue); inp = ninp) { ninp = CIRCLEQ_NEXT(inp, inp_queue); if ((inp->inp_flags & INP_IPV6) == 0) continue; /* * Under the following condition, notify of redirects * to the pcb, without making address matches against inpcb. * - redirect notification is arrived. * - the inpcb is unconnected. * - the inpcb is caching !RTF_HOST routing entry. * - the ICMPv6 notification is from the gateway cached in the * inpcb. i.e. ICMPv6 notification is from nexthop gateway * the inpcb used very recently. * * This is to improve interaction between netbsd/openbsd * redirect handling code, and inpcb route cache code. * without the clause, !RTF_HOST routing entry (which carries * gateway used by inpcb right before the ICMPv6 redirect) * will be cached forever in unconnected inpcb. * * There still is a question regarding to what is TRT: * - On bsdi/freebsd, RTF_HOST (cloned) routing entry will be * generated on packet output. inpcb will always cache * RTF_HOST routing entry so there's no need for the clause * (ICMPv6 redirect will update RTF_HOST routing entry, * and inpcb is caching it already). * However, bsdi/freebsd are vulnerable to local DoS attacks * due to the cloned routing entries. * - Specwise, "destination cache" is mentioned in RFC2461. * Jinmei says that it implies bsdi/freebsd behavior, itojun * is not really convinced. * - Having hiwat/lowat on # of cloned host route (redirect/ * pmtud) may be a good idea. netbsd/openbsd has it. see * icmp6_mtudisc_update(). */ if ((PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) && IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) && inp->inp_route.ro_rt && !(inp->inp_route.ro_rt->rt_flags & RTF_HOST)) { struct sockaddr_in6 *dst6; dst6 = (struct sockaddr_in6 *)&inp->inp_route.ro_dst; if (IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &sa6_dst->sin6_addr)) goto do_notify; } /* * Detect if we should notify the error. If no source and * destination ports are specified, but non-zero flowinfo and * local address match, notify the error. This is the case * when the error is delivered with an encrypted buffer * by ESP. Otherwise, just compare addresses and ports * as usual. */ if (lport == 0 && fport == 0 && flowinfo && inp->inp_socket != NULL && flowinfo == (inp->inp_flowinfo & IPV6_FLOWLABEL_MASK) && IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &sa6_src.sin6_addr)) goto do_notify; else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &sa6_dst->sin6_addr) || inp->inp_socket == 0 || (lport && inp->inp_lport != lport) || (!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) && !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &sa6_src.sin6_addr)) || (fport && inp->inp_fport != fport)) { continue; } do_notify: nmatch++; if (notify) (*notify)(inp, errno); } return (nmatch); }
int __inline__ ip6_addr_match(struct in6_addr *addr) { return IN6_ARE_ADDR_EQUAL(addr, &if_ip6_addr); }
static int inetport(struct Listener *listener) { int fd; int opt = 1; /* * At first, open a new socket */ fd = comm_socket(listener->addr.ss_family, SOCK_STREAM, 0, "Listener socket"); #ifdef IPV6 if(listener->addr.ss_family == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &listener->addr; if(!IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &in6addr_any)) { inetntop(AF_INET6, &in6->sin6_addr, listener->vhost, sizeof(listener->vhost)); listener->name = listener->vhost; } } else #endif { struct sockaddr_in *in = (struct sockaddr_in *) &listener->addr; if(in->sin_addr.s_addr != INADDR_ANY) { inetntop(AF_INET, &in->sin_addr, listener->vhost, sizeof(listener->vhost)); listener->name = listener->vhost; } } if(fd == -1) { report_error("opening listener socket %s:%s", get_listener_name(listener), get_listener_name(listener), errno); return 0; } else if((maxconnections - 10) < fd) { report_error("no more connections left for listener %s:%s", get_listener_name(listener), get_listener_name(listener), errno); comm_close(fd); return 0; } /* * XXX - we don't want to do all this crap for a listener * set_sock_opts(listener); */ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt))) { report_error("setting SO_REUSEADDR for listener %s:%s", get_listener_name(listener), get_listener_name(listener), errno); comm_close(fd); return 0; } /* * Bind a port to listen for new connections if port is non-null, * else assume it is already open and try get something from it. */ if(bind(fd, (struct sockaddr *) &listener->addr, GET_SS_LEN(listener->addr))) { report_error("binding listener socket %s:%s", get_listener_name(listener), get_listener_name(listener), errno); comm_close(fd); return 0; } if(listen(fd, RATBOX_SOMAXCONN)) { report_error("listen failed for %s:%s", get_listener_name(listener), get_listener_name(listener), errno); comm_close(fd); return 0; } listener->fd = fd; /* Listen completion events are READ events .. */ accept_connection(fd, listener); return 1; }
static void handle_ip_packet(struct ip* iptr, int hw_dir) { int direction = 0; /* incoming */ history_type* ht; union { history_type **ht_pp; void **void_pp; } u_ht = { &ht }; addr_pair ap; unsigned int len = 0; struct in6_addr scribdst; /* Scratch pad. */ struct in6_addr scribsrc; /* Scratch pad. */ /* Reinterpret packet type. */ struct ip6_hdr* ip6tr = (struct ip6_hdr *) iptr; memset(&ap, '\0', sizeof(ap)); tick(0); if( (IP_V(iptr) ==4 && options.netfilter == 0) || (IP_V(iptr) == 6 && options.netfilter6 == 0) ) { /* * Net filter is off, so assign direction based on MAC address */ if(hw_dir == 1) { /* Packet leaving this interface. */ assign_addr_pair(&ap, iptr, 0); direction = 1; } else if(hw_dir == 0) { /* Packet incoming */ assign_addr_pair(&ap, iptr, 1); direction = 0; } /* Packet direction is not given away by h/ware layer. Try IP * layer */ else if((IP_V(iptr) == 4) && have_ip_addr && ip_addr_match(iptr->ip_src)) { /* outgoing */ assign_addr_pair(&ap, iptr, 0); direction = 1; } else if((IP_V(iptr) == 4) && have_ip_addr && ip_addr_match(iptr->ip_dst)) { /* incoming */ assign_addr_pair(&ap, iptr, 1); direction = 0; } else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_src)) { /* outgoing */ assign_addr_pair(&ap, iptr, 0); direction = 1; } else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_dst)) { /* incoming */ assign_addr_pair(&ap, iptr, 1); direction = 0; } else if (IP_V(iptr) == 4 && IN_MULTICAST(iptr->ip_dst.s_addr)) { assign_addr_pair(&ap, iptr, 1); direction = 0; } else if (IP_V(iptr) == 6 && IN6_IS_ADDR_MULTICAST(&ip6tr->ip6_dst)) { assign_addr_pair(&ap, iptr, 1); direction = 0; } /* * Cannot determine direction from hardware or IP levels. Therefore * assume that it was a packet between two other machines, assign * source and dest arbitrarily (by numerical value) and account as * incoming. */ else if (options.promiscuous_but_choosy) { return; /* junk it */ } else if((IP_V(iptr) == 4) && (iptr->ip_src.s_addr < iptr->ip_dst.s_addr)) { assign_addr_pair(&ap, iptr, 1); direction = 0; } else if(IP_V(iptr) == 4) { assign_addr_pair(&ap, iptr, 0); direction = 0; } /* Drop other uncertain packages. */ else return; } if(IP_V(iptr) == 4 && options.netfilter != 0) { /* * Net filter on, assign direction according to netmask */ if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) { /* out of network */ assign_addr_pair(&ap, iptr, 0); direction = 1; } else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) { /* into network */ assign_addr_pair(&ap, iptr, 1); direction = 0; } else { /* drop packet */ return ; } } if(IP_V(iptr) == 6 && options.netfilter6 != 0) { /* * Net filter IPv6 active. */ int j; //else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_dst)) { /* First reduce the participating addresses using the netfilter prefix. * We need scratch pads to do this. */ for (j=0; j < 16; ++j) { scribdst.s6_addr[j] = ip6tr->ip6_dst.s6_addr[j] & options.netfilter6mask.s6_addr[j]; scribsrc.s6_addr[j] = ip6tr->ip6_src.s6_addr[j] & options.netfilter6mask.s6_addr[j]; } /* Now look for any hits. */ //if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) { if (IN6_ARE_ADDR_EQUAL(&scribsrc, &options.netfilter6net) && ! IN6_ARE_ADDR_EQUAL(&scribdst, &options.netfilter6net)) { /* out of network */ assign_addr_pair(&ap, iptr, 0); direction = 1; } //else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) { else if (! IN6_ARE_ADDR_EQUAL(&scribsrc, &options.netfilter6net) && IN6_ARE_ADDR_EQUAL(&scribdst, &options.netfilter6net)) { /* into network */ assign_addr_pair(&ap, iptr, 1); direction = 0; } else { /* drop packet */ return ; } } #if 1 /* Test if link-local IPv6 packets should be dropped. */ if( IP_V(iptr) == 6 && !options.link_local && (IN6_IS_ADDR_LINKLOCAL(&ip6tr->ip6_dst) || IN6_IS_ADDR_LINKLOCAL(&ip6tr->ip6_src)) ) return; #endif /* Do address resolving. */ switch (IP_V(iptr)) { case 4: ap.protocol = iptr->ip_p; /* Add the addresses to be resolved */ /* The IPv4 address is embedded in a in6_addr structure, * so it need be copied, and delivered to resolve(). */ memset(&scribdst, '\0', sizeof(scribdst)); memcpy(&scribdst, &iptr->ip_dst, sizeof(struct in_addr)); resolve(ap.af, &scribdst, NULL, 0); memset(&scribsrc, '\0', sizeof(scribsrc)); memcpy(&scribsrc, &iptr->ip_src, sizeof(struct in_addr)); resolve(ap.af, &scribsrc, NULL, 0); break; case 6: ap.protocol = ip6tr->ip6_nxt; /* Add the addresses to be resolved */ resolve(ap.af, &ip6tr->ip6_dst, NULL, 0); resolve(ap.af, &ip6tr->ip6_src, NULL, 0); default: break; } if(hash_find(history, &ap, u_ht.void_pp) == HASH_STATUS_KEY_NOT_FOUND) { ht = history_create(); hash_insert(history, &ap, ht); } /* Do accounting. */ switch (IP_V(iptr)) { case 4: len = ntohs(iptr->ip_len); break; case 6: len = ntohs(ip6tr->ip6_plen) + 40; default: break; } /* Update record */ ht->last_write = history_pos; if( ((IP_V(iptr) == 4) && (iptr->ip_src.s_addr == ap.src.s_addr)) || ((IP_V(iptr) == 6) && !memcmp(&ip6tr->ip6_src, &ap.src6, sizeof(ap.src6))) ) { ht->sent[history_pos] += len; ht->total_sent += len; } else { ht->recv[history_pos] += len; ht->total_recv += len; } if(direction == 0) { /* incoming */ history_totals.recv[history_pos] += len; history_totals.total_recv += len; } else { history_totals.sent[history_pos] += len; history_totals.total_sent += len; } }
/* Receive packet */ void l2tp_ctrl_input(l2tpd *_this, int listener_index, struct sockaddr *peer, struct sockaddr *sock, void *nat_t_ctx, u_char *pkt, int pktlen) { int i, len, offsiz, reqlen, is_ctrl; uint16_t mestype; struct l2tp_avp *avp, *avp0; l2tp_ctrl *ctrl; l2tp_call *call; char buf[L2TP_AVP_MAXSIZ], errmsg[256]; time_t curr_time; u_char *pkt0; struct l2tp_header hdr; char hbuf[NI_MAXHOST + NI_MAXSERV + 16]; ctrl = NULL; curr_time = get_monosec(); pkt0 = pkt; L2TP_CTRL_ASSERT(peer->sa_family == sock->sa_family); L2TP_CTRL_ASSERT(peer->sa_family == AF_INET || peer->sa_family == AF_INET6) /* * Parse L2TP Header */ memset(&hdr, 0, sizeof(hdr)); if (pktlen < 2) { snprintf(errmsg, sizeof(errmsg), "a short packet. " "length=%d", pktlen); goto bad_packet; } memcpy(&hdr, pkt, 2); pkt += 2; if (hdr.ver != L2TP_HEADER_VERSION_RFC2661) { /* XXX: only RFC2661 is supported */ snprintf(errmsg, sizeof(errmsg), "Unsupported version at header = %d", hdr.ver); goto bad_packet; } is_ctrl = (hdr.t != 0)? 1 : 0; /* calc required length */ reqlen = 6; /* for Flags, Tunnel-Id, Session-Id field */ if (hdr.l) reqlen += 2; /* for Length field (opt) */ if (hdr.s) reqlen += 4; /* for Ns, Nr field (opt) */ if (hdr.o) reqlen += 2; /* for Offset Size field (opt) */ if (reqlen > pktlen) { snprintf(errmsg, sizeof(errmsg), "a short packet. length=%d", pktlen); goto bad_packet; } if (hdr.l != 0) { GETSHORT(hdr.length, pkt); if (hdr.length > pktlen) { snprintf(errmsg, sizeof(errmsg), "Actual packet size is smaller than the length " "field %d < %d", pktlen, hdr.length); goto bad_packet; } pktlen = hdr.length; /* remove trailing trash */ } GETSHORT(hdr.tunnel_id, pkt); GETSHORT(hdr.session_id, pkt); if (hdr.s != 0) { GETSHORT(hdr.ns, pkt); GETSHORT(hdr.nr, pkt); } if (hdr.o != 0) { GETSHORT(offsiz, pkt); if (pktlen < offsiz) { snprintf(errmsg, sizeof(errmsg), "offset field is bigger than remaining packet " "length %d > %d", offsiz, pktlen); goto bad_packet; } pkt += offsiz; } L2TP_CTRL_ASSERT(pkt - pkt0 == reqlen); pktlen -= (pkt - pkt0); /* cut down the length of header */ ctrl = NULL; memset(buf, 0, sizeof(buf)); mestype = 0; avp = NULL; if (is_ctrl) { avp0 = (struct l2tp_avp *)buf; avp = avp_find_message_type_avp(avp0, pkt, pktlen); if (avp != NULL) mestype = avp->attr_value[0] << 8 | avp->attr_value[1]; } ctrl = l2tpd_get_ctrl(_this, hdr.tunnel_id); if (ctrl == NULL) { /* new control */ if (!is_ctrl) { snprintf(errmsg, sizeof(errmsg), "bad data message: tunnelId=%d is not " "found.", hdr.tunnel_id); goto bad_packet; } if (mestype != L2TP_AVP_MESSAGE_TYPE_SCCRQ) { snprintf(errmsg, sizeof(errmsg), "bad control message: tunnelId=%d is not " "found. mestype=%s", hdr.tunnel_id, avp_mes_type_string(mestype)); goto bad_packet; } if ((ctrl = l2tp_ctrl_create()) == NULL) { l2tp_ctrl_log(ctrl, LOG_ERR, "l2tp_ctrl_create() failed: %m"); goto fail; } if (l2tp_ctrl_init(ctrl, _this, peer, sock, nat_t_ctx) != 0) { l2tp_ctrl_log(ctrl, LOG_ERR, "l2tp_ctrl_start() failed: %m"); goto fail; } ctrl->listener_index = listener_index; l2tp_ctrl_reload(ctrl); } else { /* * treat as an error if src address and port is not * match. (because it is potentially DoS attach) */ int notmatch = 0; if (ctrl->peer.ss_family != peer->sa_family) notmatch = 1; else if (peer->sa_family == AF_INET) { if (SIN(peer)->sin_addr.s_addr != SIN(&ctrl->peer)->sin_addr.s_addr || SIN(peer)->sin_port != SIN(&ctrl->peer)->sin_port) notmatch = 1; } else if (peer->sa_family == AF_INET6) { if (!IN6_ARE_ADDR_EQUAL(&(SIN6(peer)->sin6_addr), &(SIN6(&ctrl->peer)->sin6_addr)) || SIN6(peer)->sin6_port != SIN6(&ctrl->peer)->sin6_port) notmatch = 1; } if (notmatch) { snprintf(errmsg, sizeof(errmsg), "tunnelId=%u is already assigned for %s", hdr.tunnel_id, addrport_tostring( (struct sockaddr *)&ctrl->peer, ctrl->peer.ss_len, hbuf, sizeof(hbuf))); goto bad_packet; } } ctrl->last_rcv = curr_time; call = NULL; if (hdr.session_id != 0) { /* search l2tp_call by Session ID */ /* linear search is enough for this purpose */ len = slist_length(&ctrl->call_list); for (i = 0; i < len; i++) { call = slist_get(&ctrl->call_list, i); if (call->session_id == hdr.session_id) break; call = NULL; } } if (!is_ctrl) { int delayed = 0; /* L2TP data */ if (ctrl->state != L2TP_CTRL_STATE_ESTABLISHED) { l2tp_ctrl_log(ctrl, LOG_WARNING, "Received Data packet in '%s'", l2tp_ctrl_state_string(ctrl)); goto fail; } if (call == NULL) { l2tp_ctrl_log(ctrl, LOG_WARNING, "Received a data packet but it has no call. " "session_id=%u", hdr.session_id); goto fail; } L2TP_CTRL_DBG((ctrl, DEBUG_LEVEL_2, "call=%u RECV ns=%u nr=%u snd_nxt=%u rcv_nxt=%u len=%d", call->id, hdr.ns, hdr.nr, call->snd_nxt, call->rcv_nxt, pktlen)); if (call->state != L2TP_CALL_STATE_ESTABLISHED){ l2tp_ctrl_log(ctrl, LOG_WARNING, "Received a data packet but call is not " "established"); goto fail; } if (hdr.s != 0) { if (SEQ_LT(hdr.ns, call->rcv_nxt)) { if (SEQ_LT(hdr.ns, call->rcv_nxt - L2TP_CALL_DELAY_LIMIT)) { /* sequence number seems to be delayed */ /* XXX: need to log? */ L2TP_CTRL_DBG((ctrl, LOG_DEBUG, "receive a out of sequence " "data packet: %u < %u.", hdr.ns, call->rcv_nxt)); return; } delayed = 1; } else { call->rcv_nxt = hdr.ns + 1; } } l2tp_call_ppp_input(call, pkt, pktlen, delayed); return; } if (hdr.s != 0) { L2TP_CTRL_DBG((ctrl, DEBUG_LEVEL_2, "RECV %s ns=%u nr=%u snd_nxt=%u snd_una=%u rcv_nxt=%u " "len=%d", (is_ctrl)? "C" : "", hdr.ns, hdr.nr, ctrl->snd_nxt, ctrl->snd_una, ctrl->rcv_nxt, pktlen)); if (pktlen <= 0) l2tp_ctrl_log(ctrl, LOG_INFO, "RecvZLB"); if (SEQ_GT(hdr.nr, ctrl->snd_una)) { if (hdr.nr == ctrl->snd_nxt || SEQ_LT(hdr.nr, ctrl->snd_nxt)) ctrl->snd_una = hdr.nr; else { l2tp_ctrl_log(ctrl, LOG_INFO, "Received message has bad Nr field: " "%u < %u.", hdr.ns, ctrl->snd_nxt); /* XXX Drop with ZLB? */ goto fail; } } if (l2tp_ctrl_txwin_size(ctrl) <= 0) { /* no waiting ack */ if (ctrl->hello_wait_ack != 0) { /* * Reset Hello state, as an ack for the Hello * is recived. */ ctrl->hello_wait_ack = 0; ctrl->hello_io_time = curr_time; } switch (ctrl->state) { case L2TP_CTRL_STATE_CLEANUP_WAIT: l2tp_ctrl_stop(ctrl, 0); return; } } if (hdr.ns != ctrl->rcv_nxt) { /* there are remaining packet */ if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) { /* resend or sent ZLB */ l2tp_ctrl_send_ZLB(ctrl); } #ifdef L2TP_CTRL_DEBUG if (pktlen != 0) { /* not ZLB */ L2TP_CTRL_DBG((ctrl, LOG_DEBUG, "receive out of sequence %u must be %u. " "mestype=%s", hdr.ns, ctrl->rcv_nxt, avp_mes_type_string(mestype))); } #endif return; } if (pktlen <= 0) return; /* ZLB */ if (l2tp_ctrl_txwin_is_full(ctrl)) { L2TP_CTRL_DBG((ctrl, LOG_DEBUG, "Received message cannot be handled. " "Transmission window is full.")); l2tp_ctrl_send_ZLB(ctrl); return; } ctrl->rcv_nxt++; if (avp == NULL) { l2tpd_log(_this, LOG_WARNING, "bad control message: no message-type AVP."); goto fail; } } /* * state machine (RFC2661 pp. 56-57) */ switch (ctrl->state) { case L2TP_CTRL_STATE_IDLE: switch (mestype) { case L2TP_AVP_MESSAGE_TYPE_SCCRQ: if (l2tp_ctrl_recv_SCCRQ(ctrl, pkt, pktlen, _this, peer) == 0) { /* acceptable */ l2tp_ctrl_send_SCCRP(ctrl); ctrl->state = L2TP_CTRL_STATE_WAIT_CTL_CONN; return; } /* * in case un-acceptable, it was already processed * at l2tcp_ctrl_recv_SCCRQ */ return; case L2TP_AVP_MESSAGE_TYPE_SCCRP: /* * RFC specifies that sent of StopCCN in the state, * However as this implementation only support Passive * open, this packet will not received. */ /* FALLTHROUGH */ case L2TP_AVP_MESSAGE_TYPE_SCCCN: default: break; } goto fsm_fail; case L2TP_CTRL_STATE_WAIT_CTL_CONN: /* Wait-Ctl-Conn */ switch (mestype) { case L2TP_AVP_MESSAGE_TYPE_SCCCN: l2tp_ctrl_log(ctrl, LOG_INFO, "RecvSCCN"); if (l2tp_ctrl_send_ZLB(ctrl) == 0) { ctrl->state = L2TP_CTRL_STATE_ESTABLISHED; } return; case L2TP_AVP_MESSAGE_TYPE_StopCCN: goto receive_stop_ccn; case L2TP_AVP_MESSAGE_TYPE_SCCRQ: case L2TP_AVP_MESSAGE_TYPE_SCCRP: default: break; } break; /* fsm_fail */ case L2TP_CTRL_STATE_ESTABLISHED: /* Established */ switch (mestype) { case L2TP_AVP_MESSAGE_TYPE_SCCCN: case L2TP_AVP_MESSAGE_TYPE_SCCRQ: case L2TP_AVP_MESSAGE_TYPE_SCCRP: break; receive_stop_ccn: case L2TP_AVP_MESSAGE_TYPE_StopCCN: if (l2tp_ctrl_recv_StopCCN(ctrl, pkt, pktlen) == 0) { if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) l2tp_ctrl_send_ZLB(ctrl); l2tp_ctrl_stop(ctrl, 0); return; } l2tp_ctrl_log(ctrl, LOG_ERR, "Received bad StopCCN"); l2tp_ctrl_send_ZLB(ctrl); l2tp_ctrl_stop(ctrl, 0); return; case L2TP_AVP_MESSAGE_TYPE_HELLO: if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) l2tp_ctrl_send_ZLB(ctrl); return; case L2TP_AVP_MESSAGE_TYPE_CDN: case L2TP_AVP_MESSAGE_TYPE_ICRP: case L2TP_AVP_MESSAGE_TYPE_ICCN: if (call == NULL) { l2tp_ctrl_log(ctrl, LOG_INFO, "Unknown call message: %s", avp_mes_type_string(mestype)); goto fail; } /* FALLTHROUGH */ case L2TP_AVP_MESSAGE_TYPE_ICRQ: l2tp_call_recv_packet(ctrl, call, mestype, pkt, pktlen); return; default: break; } break; /* fsm_fail */ case L2TP_CTRL_STATE_CLEANUP_WAIT: if (mestype == L2TP_AVP_MESSAGE_TYPE_StopCCN) { /* * We left ESTABLISHED state, but the peer sent StopCCN. */ goto receive_stop_ccn; } break; /* fsm_fail */ } fsm_fail: /* state machine error */ l2tp_ctrl_log(ctrl, LOG_WARNING, "Received %s in '%s' state", avp_mes_type_string(mestype), l2tp_ctrl_state_string(ctrl)); l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_FSM_ERROR); return; fail: if (ctrl != NULL && mestype != 0) { l2tp_ctrl_log(ctrl, LOG_WARNING, "Received %s in '%s' state", avp_mes_type_string(mestype), l2tp_ctrl_state_string(ctrl)); l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_GENERAL_ERROR); } return; bad_packet: l2tpd_log(_this, LOG_INFO, "Received from=%s: %s", addrport_tostring(peer, peer->sa_len, hbuf, sizeof(hbuf)), errmsg); return; }
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; }
int main(int argc, char *argv[]) { int ch, hold, packlen; u_char *packet; char *target; struct addrinfo hints, *ai; int gai; struct sockaddr_in6 firsthop; int socket_errno = 0; struct icmp6_filter filter; int err; #ifdef __linux__ int csum_offset, sz_opt; #endif static uint32_t scope_id = 0; #ifdef ANDROID android_check_security(); #endif limit_capabilities(); #ifdef USE_IDN setlocale(LC_ALL, ""); #endif icmp_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); if (icmp_sock < 0) { enable_capability_raw(); icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); socket_errno = errno; disable_capability_raw(); using_ping_socket = 0; } source.sin6_family = AF_INET6; memset(&firsthop, 0, sizeof(firsthop)); firsthop.sin6_family = AF_INET6; preload = 1; while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:N:")) != EOF) { switch(ch) { case 'F': flowlabel = hextoui(optarg); if (errno || (flowlabel & ~IPV6_FLOWINFO_FLOWLABEL)) { fprintf(stderr, "ping: Invalid flowinfo %s\n", optarg); exit(2); } options |= F_FLOWINFO; break; case 'Q': tclass = hextoui(optarg); if (errno || (tclass & ~0xff)) { fprintf(stderr, "ping: Invalid tclass %s\n", optarg); exit(2); } options |= F_TCLASS; break; case 'I': if (strchr(optarg, ':')) { char *p, *addr = strdup(optarg); if (!addr) { fprintf(stderr, "ping: out of memory\n"); exit(2); } p = strchr(addr, SCOPE_DELIMITER); if (p) { *p = '\0'; device = optarg + (p - addr) + 1; } if (inet_pton(AF_INET6, addr, (char*)&source.sin6_addr) <= 0) { fprintf(stderr, "ping: invalid source address %s\n", optarg); exit(2); } options |= F_STRICTSOURCE; free(addr); } else { device = optarg; } break; case 'M': if (strcmp(optarg, "do") == 0) pmtudisc = IPV6_PMTUDISC_DO; else if (strcmp(optarg, "dont") == 0) pmtudisc = IPV6_PMTUDISC_DONT; else if (strcmp(optarg, "want") == 0) pmtudisc = IPV6_PMTUDISC_WANT; else { fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n"); exit(2); } break; case 'V': printf("ping6 utility, iputils-%s\n", SNAPSHOT); exit(0); case 'N': if (using_ping_socket) { fprintf(stderr, "ping: -N requires raw socket permissions\n"); exit(2); } if (niquery_option_handler(optarg) < 0) { usage(); break; } break; COMMON_OPTIONS common_options(ch); break; default: usage(); } } argc -= optind; argv += optind; #ifdef ENABLE_PING6_RTHDR while (argc > 1) { struct in6_addr *addr; if (srcrt == NULL) { int space; fprintf(stderr, "ping6: Warning: " "Source routing is deprecated by RFC5095.\n"); #ifdef ENABLE_PING6_RTHDR_RFC3542 space = inet6_rth_space(IPV6_RTHDR_TYPE_0, argc - 1); #else space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1); #endif if (space == 0) { fprintf(stderr, "srcrt_space failed\n"); exit(2); } #ifdef ENABLE_PING6_RTHDR_RFC3542 if (cmsglen + CMSG_SPACE(space) > sizeof(cmsgbuf)) { fprintf(stderr, "no room for options\n"); exit(2); } #else if (space + cmsglen > sizeof(cmsgbuf)) { fprintf(stderr, "no room for options\n"); exit(2); } #endif srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen); #ifdef ENABLE_PING6_RTHDR_RFC3542 memset(srcrt, 0, CMSG_SPACE(0)); srcrt->cmsg_len = CMSG_LEN(space); srcrt->cmsg_level = IPPROTO_IPV6; srcrt->cmsg_type = IPV6_RTHDR; inet6_rth_init(CMSG_DATA(srcrt), space, IPV6_RTHDR_TYPE_0, argc - 1); cmsglen += CMSG_SPACE(space); #else cmsglen += CMSG_ALIGN(space); inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0); #endif } target = *argv; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; #ifdef USE_IDN hints.ai_flags = AI_IDN; #endif gai = getaddrinfo(target, NULL, &hints, &ai); if (gai) { fprintf(stderr, "unknown host\n"); exit(2); } addr = &((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr; #ifdef ENABLE_PING6_RTHDR_RFC3542 inet6_rth_add(CMSG_DATA(srcrt), addr); #else inet6_srcrt_add(srcrt, addr); #endif if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { memcpy(&firsthop.sin6_addr, addr, 16); #ifdef HAVE_SIN6_SCOPEID firsthop.sin6_scope_id = ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_scope_id; /* Verify scope_id is the same as previous nodes */ if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) { fprintf(stderr, "scope discrepancy among the nodes\n"); exit(2); } else if (!scope_id) { scope_id = firsthop.sin6_scope_id; } #endif } freeaddrinfo(ai); argv++; argc--; } #endif if (niquery_is_enabled()) { niquery_init_nonce(); if (!niquery_is_subject_valid()) { ni_subject = &whereto.sin6_addr; ni_subject_len = sizeof(whereto.sin6_addr); ni_subject_type = NI_SUBJ_IPV6; } } if (argc > 1) { #ifndef ENABLE_PING6_RTHDR fprintf(stderr, "ping6: Source routing is deprecated by RFC5095.\n"); #endif usage(); } else if (argc == 1) { target = *argv; } else { if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) usage(); target = ni_group; } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; #ifdef USE_IDN hints.ai_flags = AI_IDN; #endif gai = getaddrinfo(target, NULL, &hints, &ai); if (gai) { fprintf(stderr, "unknown host\n"); exit(2); } memcpy(&whereto, ai->ai_addr, sizeof(whereto)); whereto.sin6_port = htons(IPPROTO_ICMPV6); if (memchr(target, ':', strlen(target))) options |= F_NUMERIC; freeaddrinfo(ai); if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); #ifdef HAVE_SIN6_SCOPEID firsthop.sin6_scope_id = whereto.sin6_scope_id; /* Verify scope_id is the same as intermediate nodes */ if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) { fprintf(stderr, "scope discrepancy among the nodes\n"); exit(2); } else if (!scope_id) { scope_id = firsthop.sin6_scope_id; } #endif } hostname = target; if (IN6_IS_ADDR_UNSPECIFIED(&source.sin6_addr)) { socklen_t alen; int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); if (probe_fd < 0) { perror("socket"); exit(2); } if (device) { #if defined(IPV6_RECVPKTINFO) || defined(HAVE_SIN6_SCOPEID) unsigned int iface = if_name2index(device); #endif #ifdef IPV6_RECVPKTINFO struct in6_pktinfo ipi; memset(&ipi, 0, sizeof(ipi)); ipi.ipi6_ifindex = iface; #endif #ifdef HAVE_SIN6_SCOPEID if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) firsthop.sin6_scope_id = iface; #endif enable_capability_raw(); if ( #ifdef IPV6_RECVPKTINFO setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof(ipi)) == -1 && #endif setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) { perror("setsockopt(SO_BINDTODEVICE)"); exit(2); } disable_capability_raw(); } firsthop.sin6_port = htons(1025); if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) { perror("connect"); exit(2); } alen = sizeof(source); if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) { perror("getsockname"); exit(2); } source.sin6_port = 0; close(probe_fd); #ifndef WITHOUT_IFADDRS if (device) { struct ifaddrs *ifa0, *ifa; if (getifaddrs(&ifa0)) { perror("getifaddrs"); exit(2); } for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) continue; if (!strncmp(ifa->ifa_name, device, sizeof(device) - 1) && IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, &source.sin6_addr)) break; } if (!ifa) fprintf(stderr, "ping6: Warning: source address might be selected on device other than %s.\n", device); freeifaddrs(ifa0); } #endif } #ifdef HAVE_SIN6_SCOPEID else if (device && (IN6_IS_ADDR_LINKLOCAL(&source.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&source.sin6_addr))) source.sin6_scope_id = if_name2index(device); #endif if (icmp_sock < 0) { errno = socket_errno; perror("ping: icmp open socket"); exit(2); } if (device) { struct cmsghdr *cmsg; struct in6_pktinfo *ipi; cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen); cmsglen += CMSG_SPACE(sizeof(*ipi)); cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); cmsg->cmsg_level = SOL_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg); memset(ipi, 0, sizeof(*ipi)); ipi->ipi6_ifindex = if_name2index(device); } if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) { if (uid) { if (interval < 1000) { fprintf(stderr, "ping: multicast ping with too short interval.\n"); exit(2); } if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) { fprintf(stderr, "ping: multicast ping does not fragment.\n"); exit(2); } } if (pmtudisc < 0) pmtudisc = IPV6_PMTUDISC_DO; } if (pmtudisc >= 0) { if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) { perror("ping: IPV6_MTU_DISCOVER"); exit(2); } } if ((options&F_STRICTSOURCE) && bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) { perror("ping: bind icmp socket"); exit(2); } if (datalen >= sizeof(struct timeval) && (ni_query < 0)) { /* can we time transfer */ timing = 1; } packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */ if (!(packet = (u_char *)malloc((u_int)packlen))) { fprintf(stderr, "ping: out of memory.\n"); exit(2); } working_recverr = 1; hold = 1; if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) { fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n"); working_recverr = 0; } /* Estimate memory eaten by single packet. It is rough estimate. * Actually, for small datalen's it depends on kernel side a lot. */ hold = datalen+8; hold += ((hold+511)/512)*(40+16+64+160); sock_setbufs(icmp_sock, hold); if (!using_ping_socket) { #ifdef __linux__ csum_offset = 2; sz_opt = sizeof(int); err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt); if (err < 0) { /* checksum should be enabled by default and setting * this option might fail anyway. */ fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed" " - try to continue."); } #endif /* * select icmp echo reply as icmp type to receive */ ICMP6_FILTER_SETBLOCKALL(&filter); if (!working_recverr) { ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter); ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter); ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter); ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter); } if (niquery_is_enabled()) ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter); else ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); err = setsockopt(icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(struct icmp6_filter)); if (err < 0) { perror("setsockopt(ICMP6_FILTER)"); exit(2); } } if (options & F_NOLOOP) { int loop = 0; if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) == -1) { perror ("can't disable multicast loopback"); exit(2); } } if (options & F_TTL) { if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) == -1) { perror ("can't set multicast hop limit"); exit(2); } if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) == -1) { perror ("can't set unicast hop limit"); exit(2); } } if (1) { int on = 1; if ( #ifdef IPV6_RECVHOPLIMIT setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) == -1 && setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)) == -1 #else setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) == -1 #endif ){ perror ("can't receive hop limit"); exit(2); } } if (options & F_TCLASS) { #ifdef IPV6_TCLASS if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)) == -1) { perror ("setsockopt(IPV6_TCLASS)"); exit(2); } #else fprintf(stderr, "Traffic class is not supported.\n"); #endif } if (options&F_FLOWINFO) { #ifdef IPV6_FLOWINFO_SEND int on = 1; #endif #ifdef IPV6_FLOWLABEL_MGR char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen]; struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; int freq_len = sizeof(*freq); #ifdef ENABLE_PING6_RTHDR if (srcrt) freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len; #endif memset(freq, 0, sizeof(*freq)); freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL); freq->flr_action = IPV6_FL_A_GET; freq->flr_flags = IPV6_FL_F_CREATE; freq->flr_share = IPV6_FL_S_EXCL; memcpy(&freq->flr_dst, &whereto.sin6_addr, 16); #ifdef ENABLE_PING6_RTHDR if (srcrt) memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len); #endif if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) == -1) { perror ("can't set flowlabel"); exit(2); } flowlabel = freq->flr_label; #ifdef ENABLE_PING6_RTHDR if (srcrt) { cmsglen = (char*)srcrt - (char*)cmsgbuf; srcrt = NULL; } #endif #else fprintf(stderr, "Flow labels are not supported.\n"); exit(2); #endif #ifdef IPV6_FLOWINFO_SEND whereto.sin6_flowinfo = flowlabel; if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &on, sizeof(on)) == -1) { perror ("can't send flowinfo"); exit(2); } #else fprintf(stderr, "Flowinfo is not supported.\n"); exit(2); #endif } printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr)); if (flowlabel) printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel)); if (device || (options&F_STRICTSOURCE)) { printf("from %s %s: ", pr_addr_n(&source.sin6_addr), device ? : ""); }
/* * Add an mfc entry */ static int add_m6fc(struct mf6cctl *mfccp) { struct mf6c *rt; u_long hash; struct rtdetq *rte; u_short nstl; int s; MF6CFIND(mfccp->mf6cc_origin.sin6_addr, mfccp->mf6cc_mcastgrp.sin6_addr, rt); /* If an entry already exists, just update the fields */ if (rt) { #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG,"add_m6fc update o %s g %s p %x\n", ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent); #endif s = splsoftnet(); rt->mf6c_parent = mfccp->mf6cc_parent; rt->mf6c_ifset = mfccp->mf6cc_ifset; splx(s); return 0; } /* * Find the entry for which the upcall was made and update */ s = splsoftnet(); hash = MF6CHASH(mfccp->mf6cc_origin.sin6_addr, mfccp->mf6cc_mcastgrp.sin6_addr); for (rt = mf6ctable[hash], nstl = 0; rt; rt = rt->mf6c_next) { if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr, &mfccp->mf6cc_origin.sin6_addr) && IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr, &mfccp->mf6cc_mcastgrp.sin6_addr) && (rt->mf6c_stall != NULL)) { if (nstl++) log(LOG_ERR, "add_m6fc: %s o %s g %s p %x dbx %p\n", "multiple kernel entries", ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent, rt->mf6c_stall); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG, "add_m6fc o %s g %s p %x dbg %p\n", ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent, rt->mf6c_stall); #endif rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; rt->mf6c_parent = mfccp->mf6cc_parent; rt->mf6c_ifset = mfccp->mf6cc_ifset; /* initialize pkt counters per src-grp */ rt->mf6c_pkt_cnt = 0; rt->mf6c_byte_cnt = 0; rt->mf6c_wrong_if = 0; rt->mf6c_expire = 0; /* Don't clean this guy up */ n6expire[hash]--; /* free packets Qed at the end of this entry */ for (rte = rt->mf6c_stall; rte != NULL; ) { struct rtdetq *n = rte->next; if (rte->ifp) { ip6_mdq(rte->m, rte->ifp, rt); } m_freem(rte->m); #ifdef UPCALL_TIMING collate(&(rte->t)); #endif /* UPCALL_TIMING */ free(rte, M_MRTABLE); rte = n; } rt->mf6c_stall = NULL; } } /* * It is possible that an entry is being inserted without an upcall */ if (nstl == 0) { #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG, "add_mfc no upcall h %ld o %s g %s p %x\n", hash, ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent); #endif for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) { if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr, &mfccp->mf6cc_origin.sin6_addr)&& IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr, &mfccp->mf6cc_mcastgrp.sin6_addr)) { rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; rt->mf6c_parent = mfccp->mf6cc_parent; rt->mf6c_ifset = mfccp->mf6cc_ifset; /* initialize pkt counters per src-grp */ rt->mf6c_pkt_cnt = 0; rt->mf6c_byte_cnt = 0; rt->mf6c_wrong_if = 0; if (rt->mf6c_expire) n6expire[hash]--; rt->mf6c_expire = 0; } } if (rt == NULL) { /* no upcall, so make a new entry */ rt = (struct mf6c *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT); if (rt == NULL) { splx(s); return ENOBUFS; } /* insert new entry at head of hash chain */ rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; rt->mf6c_parent = mfccp->mf6cc_parent; rt->mf6c_ifset = mfccp->mf6cc_ifset; /* initialize pkt counters per src-grp */ rt->mf6c_pkt_cnt = 0; rt->mf6c_byte_cnt = 0; rt->mf6c_wrong_if = 0; rt->mf6c_expire = 0; rt->mf6c_stall = NULL; /* link into table */ rt->mf6c_next = mf6ctable[hash]; mf6ctable[hash] = rt; } } splx(s); return 0; }
static void ip6_input(netmsg_t msg) { struct mbuf *m = msg->packet.nm_packet; struct ip6_hdr *ip6; int off = sizeof(struct ip6_hdr), nest; u_int32_t plen; u_int32_t rtalert = ~0; int nxt, ours = 0, rh_present = 0; struct ifnet *deliverifp = NULL; struct in6_addr odst; int srcrt = 0; #ifdef IPSEC /* * should the inner packet be considered authentic? * see comment in ah4_input(). */ if (m) { m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; } #endif /* * make sure we don't have onion peering information into m_aux. */ ip6_delaux(m); /* * mbuf statistics */ if (m->m_flags & M_EXT) { if (m->m_next) ip6stat.ip6s_mext2m++; else ip6stat.ip6s_mext1++; } else { #define M2MMAX NELEM(ip6stat.ip6s_m2m) if (m->m_next) { if (m->m_flags & M_LOOP) { ip6stat.ip6s_m2m[loif[0].if_index]++; /* XXX */ } else if (m->m_pkthdr.rcvif->if_index < M2MMAX) ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; else ip6stat.ip6s_m2m[0]++; } else ip6stat.ip6s_m1++; #undef M2MMAX } in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); ip6stat.ip6s_total++; #ifndef PULLDOWN_TEST /* * L2 bridge code and some other code can return mbuf chain * that does not conform to KAME requirement. too bad. * XXX: fails to join if interface MTU > MCLBYTES. jumbogram? */ if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) { struct mbuf *n; n = m_getb(m->m_pkthdr.len, MB_DONTWAIT, MT_HEADER, M_PKTHDR); if (n == NULL) goto bad; M_MOVE_PKTHDR(n, m); m_copydata(m, 0, n->m_pkthdr.len, mtod(n, caddr_t)); n->m_len = n->m_pkthdr.len; m_freem(m); m = n; } IP6_EXTHDR_CHECK_VOIDRET(m, 0, sizeof(struct ip6_hdr)); #endif if (m->m_len < sizeof(struct ip6_hdr)) { struct ifnet *inifp; inifp = m->m_pkthdr.rcvif; if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { ip6stat.ip6s_toosmall++; in6_ifstat_inc(inifp, ifs6_in_hdrerr); goto bad2; } } ip6 = mtod(m, struct ip6_hdr *); if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { ip6stat.ip6s_badvers++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); goto bad; } /* * Run through list of hooks for input packets. * * NB: Beware of the destination address changing * (e.g. by NAT rewriting). When this happens, * tell ip6_forward to do the right thing. */ if (pfil_has_hooks(&inet6_pfil_hook)) { odst = ip6->ip6_dst; if (pfil_run_hooks(&inet6_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN)) { goto bad2; } if (m == NULL) /* consumed by filter */ goto bad2; ip6 = mtod(m, struct ip6_hdr *); srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); }
/* * 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; #ifdef IN6_IFSTAT_STRICT struct in6_ifaddr *ia; #endif 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; #ifdef IN6_IFSTAT_STRICT /* find the destination interface of the packet. */ if ((ia = ip6_getdstifaddr(m)) != NULL) { dstifp = ia->ia_ifp; ifa_free(&ia->ia_ifa); } #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); 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; } V_ip6stat.ip6s_fragments++; in6_ifstat_inc(dstifp, ifs6_reass_reqd); /* offset now points to data portion */ offset += sizeof(struct ip6_frag); /* * XXX-BZ RFC XXXX (draft-gont-6man-ipv6-atomic-fragments) * 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. */ V_ip6stat.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; }