/*
 * Shared implementation to inject a packet to or from an interface
 * Return value:
 *   0: successful
 *  -1: memory allocation failed
 *   1: other errors
 */
static int
ip_inject_impl(inject_t style, net_inject_t *packet, boolean_t isv6,
    ip_stack_t *ipst)
{
	ddi_taskq_t *tq = NULL;
	void (* func)(void *);
	injection_t *inject;
	mblk_t *mp;

	ASSERT(packet != NULL);
	ASSERT(packet->ni_packet != NULL);
	ASSERT(packet->ni_packet->b_datap->db_type == M_DATA);

	switch (style) {
	case NI_QUEUE_IN:
		inject = kmem_alloc(sizeof (*inject), KM_NOSLEEP);
		if (inject == NULL)
			return (-1);
		inject->inj_data = *packet;
		inject->inj_isv6 = isv6;
		/*
		 * deliver up into the kernel, immitating its reception by a
		 * network interface, add to list and schedule timeout
		 */
		func = ip_ni_queue_in_func;
		tq = eventq_queue_in;
		break;

	case NI_QUEUE_OUT:
		inject = kmem_alloc(sizeof (*inject), KM_NOSLEEP);
		if (inject == NULL)
			return (-1);
		inject->inj_data = *packet;
		inject->inj_isv6 = isv6;
		/*
		 * deliver out of the kernel, as if it were being sent via a
		 * raw socket so that IPFilter will see it again, add to list
		 * and schedule timeout
		 */
		func = ip_ni_queue_out_func;
		tq = eventq_queue_out;
		break;

	case NI_DIRECT_OUT: {
		struct sockaddr *sock;

		mp = packet->ni_packet;

		sock = (struct sockaddr *)&packet->ni_addr;
		/*
		 * ipfil_sendpkt was provided by surya to ease the
		 * problems associated with sending out a packet.
		 */
		switch (ipfil_sendpkt(sock, mp, packet->ni_physical,
		    netstackid_to_zoneid(
		    ipst->ips_netstack->netstack_stackid))) {
		case 0 :
		case EINPROGRESS:
			return (0);
		case ECOMM :
		case ENONET :
			return (1);
		default :
			return (1);
		}
		/* NOTREACHED */
	}
	default:
		freemsg(packet->ni_packet);
		return (1);
	}

	ASSERT(tq != NULL);

	inject->inj_ptr = ipst;
	if (ddi_taskq_dispatch(tq, func, (void *)inject,
	    DDI_SLEEP) == DDI_FAILURE) {
		ip2dbg(("ip_inject:  ddi_taskq_dispatch failed\n"));
		freemsg(packet->ni_packet);
		return (1);
	}
	return (0);
}
zoneid_t
netstack_get_zoneid(netstack_t *ns)
{
	return (netstackid_to_zoneid(ns->netstack_stackid));
}
/*
 * Find the interface used for traffic to an address.
 * For lint reasons, next/next6/sin/sin6 are all declared and assigned
 * a value at the top.  The alternative would end up with two bunches
 * of assignments, with each bunch setting half to NULL.
 */
static phy_if_t
ip_routeto_impl(struct sockaddr *address, struct sockaddr *nexthop,
    ip_stack_t *ipst)
{
	struct sockaddr_in6 *next6 = (struct sockaddr_in6 *)nexthop;
	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)address;
	struct sockaddr_in *next = (struct sockaddr_in *)nexthop;
	struct sockaddr_in *sin = (struct sockaddr_in *)address;
	ire_t *ire;
	ire_t *nexthop_ire;
	phy_if_t phy_if;
	zoneid_t zoneid;

	zoneid = netstackid_to_zoneid(ipst->ips_netstack->netstack_stackid);

	if (address->sa_family == AF_INET6) {
		ire = ire_route_recursive_v6(&sin6->sin6_addr, 0, NULL,
		    zoneid, NULL, MATCH_IRE_DSTONLY, B_TRUE, 0, ipst, NULL,
		    NULL, NULL);
	} else {
		ire = ire_route_recursive_v4(sin->sin_addr.s_addr, 0, NULL,
		    zoneid, NULL, MATCH_IRE_DSTONLY, B_TRUE, 0, ipst, NULL,
		    NULL, NULL);
	}
	ASSERT(ire != NULL);
	/*
	 * For some destinations, we have routes that are dead ends, so
	 * return to indicate that no physical interface can be used to
	 * reach the destination.
	 */
	if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
		ire_refrele(ire);
		return (NULL);
	}

	nexthop_ire = ire_nexthop(ire);
	if (nexthop_ire == NULL) {
		ire_refrele(ire);
		return (0);
	}
	if (nexthop_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
		ire_refrele(nexthop_ire);
		ire_refrele(ire);
		return (0);
	}

	ASSERT(nexthop_ire->ire_ill != NULL);

	if (nexthop != NULL) {
		if (address->sa_family == AF_INET6) {
			next6->sin6_addr = nexthop_ire->ire_addr_v6;
		} else {
			next->sin_addr.s_addr = nexthop_ire->ire_addr;
		}
	}

	phy_if = (phy_if_t)nexthop_ire->ire_ill->ill_phyint->phyint_ifindex;
	ire_refrele(ire);
	ire_refrele(nexthop_ire);

	return (phy_if);
}