#include <sys/types.h>
#include <sys/stream.h>
#include <sys/dlpi.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/strsubr.h>
#include <sys/strlog.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/kstat.h>
#include <sys/debug.h>
#include <sys/zone.h>
#include <sys/sunldi.h>
#include <sys/file.h>
#include <sys/bitmap.h>
#include <sys/cpuvar.h>
#include <sys/time.h>
#include <sys/ctype.h>
#include <sys/kmem.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/isa_defs.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/igmp_var.h>
#include <sys/policy.h>
#include <sys/ethernet.h>
#include <sys/callb.h>
#include <sys/md5.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/nd.h>
#include <inet/tunables.h>
#include <inet/arp.h>
#include <inet/ip_arp.h>
#include <inet/mib2.h>
#include <inet/ip.h>
#include <inet/ip6.h>
#include <inet/ip6_asp.h>
#include <inet/tcp.h>
#include <inet/ip_multi.h>
#include <inet/ip_ire.h>
#include <inet/ip_ftable.h>
#include <inet/ip_rts.h>
#include <inet/ip_ndp.h>
#include <inet/ip_if.h>
#include <inet/ip_impl.h>
#include <inet/sctp_ip.h>
#include <inet/ip_netinfo.h>
#include <inet/ilb_ip.h>
#include <netinet/igmp.h>
#include <inet/ip_listutils.h>
#include <inet/ipclassifier.h>
#include <sys/mac_client.h>
#include <sys/dld.h>
#include <sys/mac_flow.h>
#include <sys/systeminfo.h>
#include <sys/bootconf.h>
#include <sys/tsol/tndb.h>
#include <sys/tsol/tnet.h>
#include <inet/rawip_impl.h>
#include <inet/udp_impl.h>
#define IPIF_SEPARATOR_CHAR ':'
typedef struct ipft_s {
int ipft_cmd;
pfi_t ipft_pfi;
int ipft_min_size;
int ipft_flags;
} ipft_t;
#define IPFT_F_NO_REPLY 0x1
#define IPFT_F_SELF_REPLY 0x2
static int nd_ill_forward_get(queue_t *, mblk_t *, caddr_t, cred_t *);
static int nd_ill_forward_set(queue_t *q, mblk_t *mp,
char *value, caddr_t cp, cred_t *ioc_cr);
static boolean_t ill_is_quiescent(ill_t *);
static boolean_t ip_addr_ok_v4(ipaddr_t addr, ipaddr_t subnet_mask);
static ip_m_t *ip_m_lookup(t_uscalar_t mac_type);
static int ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q,
mblk_t *mp, boolean_t need_up);
static int ip_sioctl_dstaddr_tail(ipif_t *ipif, sin_t *sin, queue_t *q,
mblk_t *mp, boolean_t need_up);
static int ip_sioctl_slifzone_tail(ipif_t *ipif, zoneid_t zoneid,
queue_t *q, mblk_t *mp, boolean_t need_up);
static int ip_sioctl_flags_tail(ipif_t *ipif, uint64_t flags, queue_t *q,
mblk_t *mp);
static int ip_sioctl_netmask_tail(ipif_t *ipif, sin_t *sin, queue_t *q,
mblk_t *mp);
static int ip_sioctl_subnet_tail(ipif_t *ipif, in6_addr_t, in6_addr_t,
queue_t *q, mblk_t *mp, boolean_t need_up);
static int ip_sioctl_plink_ipmod(ipsq_t *ipsq, queue_t *q, mblk_t *mp,
int ioccmd, struct linkblk *li);
static ipaddr_t ip_subnet_mask(ipaddr_t addr, ipif_t **, ip_stack_t *);
static void ip_wput_ioctl(queue_t *q, mblk_t *mp);
static void ipsq_flush(ill_t *ill);
static int ip_sioctl_token_tail(ipif_t *ipif, sin6_t *sin6, int addrlen,
queue_t *q, mblk_t *mp, boolean_t need_up);
static void ipsq_delete(ipsq_t *);
static ipif_t *ipif_allocate(ill_t *ill, int id, uint_t ire_type,
boolean_t initialize, boolean_t insert, int *errorp);
static ire_t **ipif_create_bcast_ires(ipif_t *ipif, ire_t **irep);
static void ipif_delete_bcast_ires(ipif_t *ipif);
static int ipif_add_ires_v4(ipif_t *, boolean_t);
static boolean_t ipif_comp_multi(ipif_t *old_ipif, ipif_t *new_ipif,
boolean_t isv6);
static int ipif_logical_down(ipif_t *ipif, queue_t *q, mblk_t *mp);
static void ipif_free(ipif_t *ipif);
static void ipif_free_tail(ipif_t *ipif);
static void ipif_set_default(ipif_t *ipif);
static int ipif_set_values(queue_t *q, mblk_t *mp,
char *interf_name, uint_t *ppa);
static int ipif_set_values_tail(ill_t *ill, ipif_t *ipif, mblk_t *mp,
queue_t *q);
static ipif_t *ipif_lookup_on_name(char *name, size_t namelen,
boolean_t do_alloc, boolean_t *exists, boolean_t isv6, zoneid_t zoneid,
ip_stack_t *);
static ipif_t *ipif_lookup_on_name_async(char *name, size_t namelen,
boolean_t isv6, zoneid_t zoneid, queue_t *q, mblk_t *mp, ipsq_func_t func,
int *error, ip_stack_t *);
static int ill_alloc_ppa(ill_if_t *, ill_t *);
static void ill_delete_interface_type(ill_if_t *);
static int ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q);
static void ill_dl_down(ill_t *ill);
static void ill_down(ill_t *ill);
static void ill_down_ipifs(ill_t *, boolean_t);
static void ill_free_mib(ill_t *ill);
static void ill_glist_delete(ill_t *);
static void ill_phyint_reinit(ill_t *ill);
static void ill_set_nce_router_flags(ill_t *, boolean_t);
static void ill_set_phys_addr_tail(ipsq_t *, queue_t *, mblk_t *, void *);
static void ill_replumb_tail(ipsq_t *, queue_t *, mblk_t *, void *);
static ip_v6intfid_func_t ip_ether_v6intfid, ip_ib_v6intfid;
static ip_v6intfid_func_t ip_ipv4_v6intfid, ip_ipv6_v6intfid;
static ip_v6intfid_func_t ip_ipmp_v6intfid, ip_nodef_v6intfid;
static ip_v6intfid_func_t ip_ipv4_v6destintfid, ip_ipv6_v6destintfid;
static ip_v4mapinfo_func_t ip_ether_v4_mapping;
static ip_v6mapinfo_func_t ip_ether_v6_mapping;
static ip_v4mapinfo_func_t ip_ib_v4_mapping;
static ip_v6mapinfo_func_t ip_ib_v6_mapping;
static ip_v4mapinfo_func_t ip_mbcast_mapping;
static void ip_cgtp_bcast_add(ire_t *, ip_stack_t *);
static void ip_cgtp_bcast_delete(ire_t *, ip_stack_t *);
static void phyint_free(phyint_t *);
static void ill_capability_dispatch(ill_t *, mblk_t *, dl_capability_sub_t *);
static void ill_capability_id_ack(ill_t *, mblk_t *, dl_capability_sub_t *);
static void ill_capability_vrrp_ack(ill_t *, mblk_t *, dl_capability_sub_t *);
static void ill_capability_hcksum_ack(ill_t *, mblk_t *, dl_capability_sub_t *);
static void ill_capability_hcksum_reset_fill(ill_t *, mblk_t *);
static void ill_capability_zerocopy_ack(ill_t *, mblk_t *,
dl_capability_sub_t *);
static void ill_capability_zerocopy_reset_fill(ill_t *, mblk_t *);
static void ill_capability_dld_reset_fill(ill_t *, mblk_t *);
static void ill_capability_dld_ack(ill_t *, mblk_t *,
dl_capability_sub_t *);
static void ill_capability_dld_enable(ill_t *);
static void ill_capability_ack_thr(void *);
static void ill_capability_lso_enable(ill_t *);
static ill_t *ill_prev_usesrc(ill_t *);
static int ill_relink_usesrc_ills(ill_t *, ill_t *, uint_t);
static void ill_disband_usesrc_group(ill_t *);
static void ip_sioctl_garp_reply(mblk_t *, ill_t *, void *, int);
#ifdef DEBUG
static void ill_trace_cleanup(const ill_t *);
static void ipif_trace_cleanup(const ipif_t *);
#endif
static void ill_dlpi_clear_deferred(ill_t *ill);
static void phyint_flags_init(phyint_t *, t_uscalar_t);
int ip_min_frag_prune_time = 0;
static ipft_t ip_ioctl_ftbl[] = {
{ IP_IOC_IRE_DELETE, ip_ire_delete, sizeof (ipid_t), 0 },
{ IP_IOC_IRE_DELETE_NO_REPLY, ip_ire_delete, sizeof (ipid_t),
IPFT_F_NO_REPLY },
{ IP_IOC_RTS_REQUEST, ip_rts_request, 0, IPFT_F_SELF_REPLY },
{ 0 }
};
static ipha_t icmp_ipha = {
IP_SIMPLE_HDR_VERSION, 0, 0, 0, 0, 0, IPPROTO_ICMP
};
static uchar_t ip_six_byte_all_ones[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static ip_m_t ip_m_tbl[] = {
{ DL_ETHER, IFT_ETHER, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ether_v4_mapping, ip_ether_v6_mapping, ip_ether_v6intfid,
ip_nodef_v6intfid },
{ DL_CSMACD, IFT_ISO88023, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ether_v4_mapping, ip_ether_v6_mapping, ip_nodef_v6intfid,
ip_nodef_v6intfid },
{ DL_TPB, IFT_ISO88024, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ether_v4_mapping, ip_ether_v6_mapping, ip_nodef_v6intfid,
ip_nodef_v6intfid },
{ DL_TPR, IFT_ISO88025, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ether_v4_mapping, ip_ether_v6_mapping, ip_nodef_v6intfid,
ip_nodef_v6intfid },
{ DL_FDDI, IFT_FDDI, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ether_v4_mapping, ip_ether_v6_mapping, ip_ether_v6intfid,
ip_nodef_v6intfid },
{ DL_IB, IFT_IB, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ib_v4_mapping, ip_ib_v6_mapping, ip_ib_v6intfid,
ip_nodef_v6intfid },
{ DL_IPV4, IFT_IPV4, IPPROTO_ENCAP, IPPROTO_IPV6,
ip_mbcast_mapping, ip_mbcast_mapping, ip_ipv4_v6intfid,
ip_ipv4_v6destintfid },
{ DL_IPV6, IFT_IPV6, IPPROTO_ENCAP, IPPROTO_IPV6,
ip_mbcast_mapping, ip_mbcast_mapping, ip_ipv6_v6intfid,
ip_ipv6_v6destintfid },
{ DL_6TO4, IFT_6TO4, IPPROTO_ENCAP, IPPROTO_IPV6,
ip_mbcast_mapping, ip_mbcast_mapping, ip_ipv4_v6intfid,
ip_nodef_v6intfid },
{ SUNW_DL_VNI, IFT_OTHER, ETHERTYPE_IP, ETHERTYPE_IPV6,
NULL, NULL, ip_nodef_v6intfid, ip_nodef_v6intfid },
{ SUNW_DL_IPMP, IFT_OTHER, ETHERTYPE_IP, ETHERTYPE_IPV6,
NULL, NULL, ip_ipmp_v6intfid, ip_nodef_v6intfid },
{ DL_OTHER, IFT_OTHER, ETHERTYPE_IP, ETHERTYPE_IPV6,
ip_ether_v4_mapping, ip_ether_v6_mapping, ip_nodef_v6intfid,
ip_nodef_v6intfid }
};
char ipif_loopback_name[] = "lo0";
sin6_t sin6_null;
sin_t sin_null;
static ipif_t ipif_zero;
uint_t ill_no_arena = 12;
static boolean_t
ill_allocate_mibs(ill_t *ill)
{
if (ill->ill_ip_mib != NULL) {
if (ill->ill_isv6)
ASSERT(ill->ill_icmp6_mib != NULL);
return (B_TRUE);
}
ill->ill_ip_mib = kmem_zalloc(sizeof (*ill->ill_ip_mib),
KM_NOSLEEP);
if (ill->ill_ip_mib == NULL) {
return (B_FALSE);
}
SET_MIB(ill->ill_ip_mib->ipIfStatsEntrySize,
sizeof (mib2_ipIfStatsEntry_t));
if (ill->ill_isv6) {
ill->ill_ip_mib->ipIfStatsIPVersion = MIB2_INETADDRESSTYPE_ipv6;
SET_MIB(ill->ill_ip_mib->ipIfStatsAddrEntrySize,
sizeof (mib2_ipv6AddrEntry_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsRouteEntrySize,
sizeof (mib2_ipv6RouteEntry_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsNetToMediaEntrySize,
sizeof (mib2_ipv6NetToMediaEntry_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsMemberEntrySize,
sizeof (ipv6_member_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsGroupSourceEntrySize,
sizeof (ipv6_grpsrc_t));
} else {
ill->ill_ip_mib->ipIfStatsIPVersion = MIB2_INETADDRESSTYPE_ipv4;
SET_MIB(ill->ill_ip_mib->ipIfStatsAddrEntrySize,
sizeof (mib2_ipAddrEntry_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsRouteEntrySize,
sizeof (mib2_ipRouteEntry_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsNetToMediaEntrySize,
sizeof (mib2_ipNetToMediaEntry_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsMemberEntrySize,
sizeof (ip_member_t));
SET_MIB(ill->ill_ip_mib->ipIfStatsGroupSourceEntrySize,
sizeof (ip_grpsrc_t));
return (B_TRUE);
}
ill->ill_icmp6_mib = kmem_zalloc(sizeof (*ill->ill_icmp6_mib),
KM_NOSLEEP);
if (ill->ill_icmp6_mib == NULL) {
kmem_free(ill->ill_ip_mib, sizeof (*ill->ill_ip_mib));
ill->ill_ip_mib = NULL;
return (B_FALSE);
}
ill->ill_icmp6_mib->ipv6IfIcmpEntrySize =
sizeof (mib2_ipv6IfIcmpEntry_t);
return (B_TRUE);
}
void
ill_delete(ill_t *ill)
{
ipif_t *ipif;
ill_t *prev_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsq_flush(ill);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next)
ipif_free(ipif);
nce_flush(ill, B_TRUE);
reset_mrt_ill(ill);
update_conn_ill(ill, ipst);
ip_purge_allmulti(ill);
if (IS_UNDER_IPMP(ill))
ipmp_ill_leave_illgrp(ill);
ill_down(ill);
sctp_update_ill(ill, SCTP_ILL_REMOVE);
ipcl_walk(conn_ixa_cleanup, (void *)B_TRUE, ipst);
if (ill->ill_isv6)
dce_cleanup(ill->ill_phyint->phyint_ifindex, ipst);
rw_enter(&ipst->ips_ill_g_usesrc_lock, RW_WRITER);
if (ill->ill_usesrc_grp_next != NULL) {
if (ill->ill_usesrc_ifindex == 0) {
ill_disband_usesrc_group(ill);
} else {
prev_ill = ill_prev_usesrc(ill);
prev_ill->ill_usesrc_grp_next =
ill->ill_usesrc_grp_next;
}
}
rw_exit(&ipst->ips_ill_g_usesrc_lock);
}
static void
ipif_non_duplicate(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
mutex_enter(&ill->ill_lock);
if (ipif->ipif_flags & IPIF_DUPLICATE) {
ipif->ipif_flags &= ~IPIF_DUPLICATE;
ASSERT(ill->ill_ipif_dup_count > 0);
ill->ill_ipif_dup_count--;
}
mutex_exit(&ill->ill_lock);
}
void
ill_delete_tail(ill_t *ill)
{
mblk_t **mpp;
ipif_t *ipif;
ip_stack_t *ipst = ill->ill_ipst;
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
ipif_non_duplicate(ipif);
(void) ipif_down_tail(ipif);
}
ASSERT(ill->ill_ipif_dup_count == 0);
mutex_enter(&ill->ill_lock);
while (ill->ill_state_flags & ILL_DL_UNBIND_IN_PROGRESS)
cv_wait(&ill->ill_cv, &ill->ill_lock);
mutex_exit(&ill->ill_lock);
ASSERT(!(ill->ill_capabilities &
(ILL_CAPAB_DLD | ILL_CAPAB_DLD_POLL | ILL_CAPAB_DLD_DIRECT)));
if (ill->ill_net_type != IRE_LOOPBACK)
qprocsoff(ill->ill_rq);
ipsq_flush(ill);
if (ill->ill_hcksum_capab != NULL) {
kmem_free(ill->ill_hcksum_capab, sizeof (ill_hcksum_capab_t));
ill->ill_hcksum_capab = NULL;
}
if (ill->ill_zerocopy_capab != NULL) {
kmem_free(ill->ill_zerocopy_capab,
sizeof (ill_zerocopy_capab_t));
ill->ill_zerocopy_capab = NULL;
}
if (ill->ill_lso_capab != NULL) {
kmem_free(ill->ill_lso_capab, sizeof (ill_lso_capab_t));
ill->ill_lso_capab = NULL;
}
if (ill->ill_dld_capab != NULL) {
kmem_free(ill->ill_dld_capab, sizeof (ill_dld_capab_t));
ill->ill_dld_capab = NULL;
}
if (ill->ill_allowed_ips != NULL) {
ASSERT(ill->ill_allowed_ips_cnt > 0);
kmem_free(ill->ill_allowed_ips,
ill->ill_allowed_ips_cnt * sizeof (in6_addr_t));
ill->ill_allowed_ips = NULL;
ill->ill_allowed_ips_cnt = 0;
}
while (ill->ill_ipif != NULL)
ipif_free_tail(ill->ill_ipif);
if (IS_IPMP(ill)) {
ipmp_illgrp_destroy(ill->ill_grp);
ill->ill_grp = NULL;
}
if (ill->ill_mphysaddr_list != NULL) {
multiphysaddr_t *mpa, *tmpa;
mpa = ill->ill_mphysaddr_list;
ill->ill_mphysaddr_list = NULL;
while (mpa) {
tmpa = mpa->mpa_next;
kmem_free(mpa, sizeof (*mpa));
mpa = tmpa;
}
}
(void) ill_glist_delete(ill);
if (ill->ill_frag_ptr != NULL) {
uint_t count;
for (count = 0; count < ILL_FRAG_HASH_TBL_COUNT; count++) {
mutex_destroy(&ill->ill_frag_hash_tbl[count].ipfb_lock);
}
mi_free(ill->ill_frag_ptr);
ill->ill_frag_ptr = NULL;
ill->ill_frag_hash_tbl = NULL;
}
freemsg(ill->ill_nd_lla_mp);
mpp = &ill->ill_first_mp_to_free;
do {
while (mpp[0]) {
mblk_t *mp;
mblk_t *mp1;
mp = mpp[0];
mpp[0] = mp->b_next;
for (mp1 = mp; mp1 != NULL; mp1 = mp1->b_cont) {
mp1->b_next = NULL;
mp1->b_prev = NULL;
}
freemsg(mp);
}
} while (mpp++ != &ill->ill_last_mp_to_free);
ill_free_mib(ill);
#ifdef DEBUG
ill_trace_cleanup(ill);
#endif
ire_increment_multicast_generation(ipst, ill->ill_isv6);
netstack_rele(ill->ill_ipst->ips_netstack);
ill->ill_ipst = NULL;
}
static void
ill_free_mib(ill_t *ill)
{
ip_stack_t *ipst = ill->ill_ipst;
if (ill->ill_ip_mib != NULL) {
if (ill->ill_isv6) {
ip_mib2_add_ip_stats(&ipst->ips_ip6_mib,
ill->ill_ip_mib);
} else {
ip_mib2_add_ip_stats(&ipst->ips_ip_mib,
ill->ill_ip_mib);
}
kmem_free(ill->ill_ip_mib, sizeof (*ill->ill_ip_mib));
ill->ill_ip_mib = NULL;
}
if (ill->ill_icmp6_mib != NULL) {
ip_mib2_add_icmp6_stats(&ipst->ips_icmp6_mib,
ill->ill_icmp6_mib);
kmem_free(ill->ill_icmp6_mib, sizeof (*ill->ill_icmp6_mib));
ill->ill_icmp6_mib = NULL;
}
}
static void
ill_dlur_copy_address(uchar_t *phys_src, uint_t phys_length,
t_scalar_t sap_src, t_scalar_t sap_length, uchar_t *dst)
{
uint16_t sap_addr = (uint16_t)sap_src;
if (sap_length == 0) {
if (phys_src == NULL)
bzero(dst, phys_length);
else
bcopy(phys_src, dst, phys_length);
} else if (sap_length < 0) {
if (phys_src == NULL)
bzero(dst, phys_length);
else
bcopy(phys_src, dst, phys_length);
bcopy(&sap_addr, (char *)dst + phys_length, sizeof (sap_addr));
} else {
bcopy(&sap_addr, dst, sizeof (sap_addr));
if (phys_src == NULL)
bzero((char *)dst + sap_length, phys_length);
else
bcopy(phys_src, (char *)dst + sap_length, phys_length);
}
}
mblk_t *
ill_dlur_gen(uchar_t *addr, uint_t addr_length, t_uscalar_t sap,
t_scalar_t sap_length)
{
dl_unitdata_req_t *dlur;
mblk_t *mp;
t_scalar_t abs_sap_length;
abs_sap_length = ABS(sap_length);
mp = ip_dlpi_alloc(sizeof (*dlur) + addr_length + abs_sap_length,
DL_UNITDATA_REQ);
if (mp == NULL)
return (NULL);
dlur = (dl_unitdata_req_t *)mp->b_rptr;
if (addr_length == 8)
addr_length = 6;
dlur->dl_dest_addr_length = addr_length + abs_sap_length;
dlur->dl_dest_addr_offset = sizeof (*dlur);
dlur->dl_priority.dl_min = 0;
dlur->dl_priority.dl_max = 0;
ill_dlur_copy_address(addr, addr_length, sap, sap_length,
(uchar_t *)&dlur[1]);
return (mp);
}
boolean_t
ipsq_pending_mp_add(conn_t *connp, ipif_t *ipif, queue_t *q, mblk_t *add_mp,
int waitfor)
{
ipxop_t *ipx = ipif->ipif_ill->ill_phyint->phyint_ipsq->ipsq_xop;
ASSERT(IAM_WRITER_IPIF(ipif));
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
ASSERT((add_mp->b_next == NULL) && (add_mp->b_prev == NULL));
ASSERT(ipx->ipx_pending_mp == NULL);
ASSERT(ipx->ipx_current_ipif != NULL);
ASSERT((DB_TYPE(add_mp) == M_IOCDATA) || (DB_TYPE(add_mp) == M_ERROR) ||
(DB_TYPE(add_mp) == M_HANGUP) || (DB_TYPE(add_mp) == M_PROTO) ||
(DB_TYPE(add_mp) == M_PCPROTO));
if (connp != NULL) {
ASSERT(MUTEX_HELD(&connp->conn_lock));
if (connp->conn_state_flags & CONN_CLOSING)
return (B_FALSE);
}
mutex_enter(&ipx->ipx_lock);
ipx->ipx_pending_ipif = ipif;
add_mp->b_next = NULL;
add_mp->b_queue = q;
ipx->ipx_pending_mp = add_mp;
ipx->ipx_waitfor = waitfor;
mutex_exit(&ipx->ipx_lock);
if (connp != NULL)
connp->conn_oper_pending_ill = ipif->ipif_ill;
return (B_TRUE);
}
mblk_t *
ipsq_pending_mp_get(ipsq_t *ipsq, conn_t **connpp)
{
mblk_t *curr = NULL;
ipxop_t *ipx = ipsq->ipsq_xop;
*connpp = NULL;
mutex_enter(&ipx->ipx_lock);
if (ipx->ipx_pending_mp == NULL) {
mutex_exit(&ipx->ipx_lock);
return (NULL);
}
curr = ipx->ipx_pending_mp;
ASSERT(curr->b_next == NULL);
ipx->ipx_pending_ipif = NULL;
ipx->ipx_pending_mp = NULL;
ipx->ipx_waitfor = 0;
mutex_exit(&ipx->ipx_lock);
if (CONN_Q(curr->b_queue)) {
*connpp = Q_TO_CONN(curr->b_queue);
} else {
*connpp = NULL;
}
curr->b_next = NULL;
curr->b_prev = NULL;
return (curr);
}
boolean_t
ipsq_pending_mp_cleanup(ill_t *ill, conn_t *connp)
{
mblk_t *mp;
ipxop_t *ipx;
queue_t *q;
ipif_t *ipif;
int cmd;
ASSERT(IAM_WRITER_ILL(ill));
ipx = ill->ill_phyint->phyint_ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
mp = ipx->ipx_pending_mp;
if (connp != NULL) {
if (mp == NULL || mp->b_queue != CONNP_TO_WQ(connp)) {
mutex_exit(&ipx->ipx_lock);
return (B_FALSE);
}
} else {
if (mp == NULL && ill->ill_error == 0) {
mutex_exit(&ipx->ipx_lock);
return (B_FALSE);
}
}
ipx->ipx_pending_mp = NULL;
ipif = ipx->ipx_pending_ipif;
ipx->ipx_pending_ipif = NULL;
ipx->ipx_waitfor = 0;
ipx->ipx_current_ipif = NULL;
cmd = ipx->ipx_current_ioctl;
ipx->ipx_current_ioctl = 0;
ipx->ipx_current_done = B_TRUE;
mutex_exit(&ipx->ipx_lock);
if (mp == NULL)
return (B_FALSE);
q = mp->b_queue;
mp->b_next = NULL;
mp->b_prev = NULL;
mp->b_queue = NULL;
if (DB_TYPE(mp) == M_IOCTL || DB_TYPE(mp) == M_IOCDATA) {
DTRACE_PROBE4(ipif__ioctl,
char *, "ipsq_pending_mp_cleanup",
int, cmd, ill_t *, ipif == NULL ? NULL : ipif->ipif_ill,
ipif_t *, ipif);
if (connp == NULL) {
ip_ioctl_finish(q, mp, ENXIO, NO_COPYOUT, NULL);
} else {
ip_ioctl_finish(q, mp, ENXIO, CONN_CLOSE, NULL);
mutex_enter(&ipif->ipif_ill->ill_lock);
ipif->ipif_state_flags &= ~IPIF_CHANGING;
mutex_exit(&ipif->ipif_ill->ill_lock);
}
} else {
inet_freemsg(mp);
}
return (B_TRUE);
}
static void
ipsq_xopq_mp_cleanup(ill_t *ill, conn_t *connp)
{
ipsq_t *ipsq;
mblk_t *prev;
mblk_t *curr;
mblk_t *next;
queue_t *wq, *rq = NULL;
mblk_t *tmp_list = NULL;
ASSERT(IAM_WRITER_ILL(ill));
if (connp != NULL)
wq = CONNP_TO_WQ(connp);
else
wq = ill->ill_wq;
if (wq != NULL)
rq = RD(wq);
ipsq = ill->ill_phyint->phyint_ipsq;
mutex_enter(&ipsq->ipsq_lock);
for (prev = NULL, curr = ipsq->ipsq_xopq_mphead; curr != NULL;
curr = next) {
next = curr->b_next;
if (connp == NULL ||
(curr->b_queue == wq || curr->b_queue == rq)) {
if (prev != NULL) {
prev->b_next = curr->b_next;
} else {
ASSERT(ipsq->ipsq_xopq_mphead == curr);
ipsq->ipsq_xopq_mphead = curr->b_next;
}
if (ipsq->ipsq_xopq_mptail == curr)
ipsq->ipsq_xopq_mptail = prev;
curr->b_next = tmp_list;
tmp_list = curr;
} else {
prev = curr;
}
}
mutex_exit(&ipsq->ipsq_lock);
while (tmp_list != NULL) {
curr = tmp_list;
tmp_list = curr->b_next;
curr->b_next = NULL;
curr->b_prev = NULL;
wq = curr->b_queue;
curr->b_queue = NULL;
if (DB_TYPE(curr) == M_IOCTL || DB_TYPE(curr) == M_IOCDATA) {
DTRACE_PROBE4(ipif__ioctl,
char *, "ipsq_xopq_mp_cleanup",
int, 0, ill_t *, NULL, ipif_t *, NULL);
ip_ioctl_finish(wq, curr, ENXIO, connp != NULL ?
CONN_CLOSE : NO_COPYOUT, NULL);
} else {
inet_freemsg(curr);
}
}
}
void
conn_ioctl_cleanup(conn_t *connp)
{
ipsq_t *ipsq;
ill_t *ill;
boolean_t refheld;
mutex_enter(&connp->conn_lock);
ill = connp->conn_oper_pending_ill;
if (ill == NULL) {
mutex_exit(&connp->conn_lock);
return;
}
refheld = ill_waiter_inc(ill);
mutex_exit(&connp->conn_lock);
if (refheld) {
if (ipsq_enter(ill, B_TRUE, NEW_OP)) {
ill_waiter_dcr(ill);
if (!ipsq_pending_mp_cleanup(ill, connp))
ipsq_xopq_mp_cleanup(ill, connp);
ipsq = ill->ill_phyint->phyint_ipsq;
ipsq_exit(ipsq);
return;
}
}
mutex_enter(&connp->conn_lock);
while (connp->conn_oper_pending_ill != NULL)
cv_wait(&connp->conn_refcv, &connp->conn_lock);
mutex_exit(&connp->conn_lock);
if (refheld)
ill_waiter_dcr(ill);
}
static void
conn_cleanup_ill(conn_t *connp, caddr_t arg)
{
ill_t *ill = (ill_t *)arg;
mutex_enter(&connp->conn_lock);
if (connp->conn_dhcpinit_ill == ill) {
connp->conn_dhcpinit_ill = NULL;
ASSERT(ill->ill_dhcpinit != 0);
atomic_dec_32(&ill->ill_dhcpinit);
ill_set_inputfn(ill);
}
mutex_exit(&connp->conn_lock);
}
static int
ill_down_ipifs_tail(ill_t *ill)
{
ipif_t *ipif;
int err;
ASSERT(IAM_WRITER_ILL(ill));
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
ipif_non_duplicate(ipif);
if ((err = ipif_down_tail(ipif)) != 0)
return (err);
}
return (0);
}
void
ipif_all_down_tail(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
{
ASSERT(IAM_WRITER_IPSQ(ipsq));
(void) ill_down_ipifs_tail(q->q_ptr);
freemsg(mp);
ipsq_current_finish(ipsq);
}
boolean_t
ill_down_start(queue_t *q, mblk_t *mp)
{
ill_t *ill = q->q_ptr;
ipif_t *ipif;
ASSERT(IAM_WRITER_ILL(ill));
(void) ipsq_pending_mp_cleanup(ill, NULL);
ill_dlpi_clear_deferred(ill);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next)
(void) ipif_down(ipif, NULL, NULL);
ill_down(ill);
ipcl_walk(conn_ixa_cleanup, (void *)B_TRUE, ill->ill_ipst);
if (ill->ill_isv6)
dce_cleanup(ill->ill_phyint->phyint_ifindex, ill->ill_ipst);
ipsq_current_start(ill->ill_phyint->phyint_ipsq, ill->ill_ipif, 0);
mutex_enter(&ill->ill_lock);
if (!ill_is_quiescent(ill)) {
(void) ipsq_pending_mp_add(NULL, ill->ill_ipif, ill->ill_rq,
mp, ILL_DOWN);
mutex_exit(&ill->ill_lock);
return (B_FALSE);
}
mutex_exit(&ill->ill_lock);
return (B_TRUE);
}
static void
ill_down(ill_t *ill)
{
mblk_t *mp;
ip_stack_t *ipst = ill->ill_ipst;
ill_delete_ires(ill);
ire_walk_ill(0, 0, ill_downi, ill, ill);
ipcl_walk(conn_cleanup_ill, (caddr_t)ill, ipst);
mutex_enter(&ill->ill_saved_ire_lock);
mp = ill->ill_saved_ire_mp;
ill->ill_saved_ire_mp = NULL;
ill->ill_saved_ire_cnt = 0;
mutex_exit(&ill->ill_saved_ire_lock);
freemsg(mp);
}
void
ill_downi(ire_t *ire, char *ill_arg)
{
ill_t *ill = (ill_t *)ill_arg;
nce_t *nce;
mutex_enter(&ire->ire_lock);
nce = ire->ire_nce_cache;
if (nce != NULL && nce->nce_ill == ill)
ire->ire_nce_cache = NULL;
else
nce = NULL;
mutex_exit(&ire->ire_lock);
if (nce != NULL)
nce_refrele(nce);
if (ire->ire_ill == ill) {
ASSERT(ire->ire_bucket->irb_refcnt > 0);
ire_delete(ire);
if (ire->ire_unbound)
ire_rebind(ire);
}
}
void
ill_downi_if_clone(ire_t *ire, char *ill_arg)
{
ill_t *ill = (ill_t *)ill_arg;
ASSERT(ire->ire_type & IRE_IF_CLONE);
if (ire->ire_ill == ill)
ire_delete(ire);
}
void
ill_fastpath_ack(ill_t *ill, mblk_t *mp)
{
mblk_t *mp1 = mp;
mutex_enter(&ill->ill_lock);
if (ill->ill_dlpi_fastpath_state == IDS_INPROGRESS)
ill->ill_dlpi_fastpath_state = IDS_OK;
mutex_exit(&ill->ill_lock);
mp = mp->b_cont;
freeb(mp1);
if (mp == NULL)
return;
if (mp->b_cont != NULL)
nce_fastpath_update(ill, mp);
else
ip0dbg(("ill_fastpath_ack: no b_cont\n"));
freemsg(mp);
}
int
ill_fastpath_probe(ill_t *ill, mblk_t *dlur_mp)
{
struct iocblk *ioc;
mblk_t *mp;
if (dlur_mp == NULL)
return (EINVAL);
mutex_enter(&ill->ill_lock);
switch (ill->ill_dlpi_fastpath_state) {
case IDS_FAILED:
mutex_exit(&ill->ill_lock);
return (ENOTSUP);
case IDS_UNKNOWN:
ill->ill_dlpi_fastpath_state = IDS_INPROGRESS;
break;
default:
break;
}
mutex_exit(&ill->ill_lock);
if ((mp = mkiocb(DL_IOC_HDR_INFO)) == NULL)
return (EAGAIN);
mp->b_cont = copyb(dlur_mp);
if (mp->b_cont == NULL) {
freeb(mp);
return (EAGAIN);
}
ioc = (struct iocblk *)mp->b_rptr;
ioc->ioc_count = msgdsize(mp->b_cont);
DTRACE_PROBE3(ill__dlpi, char *, "ill_fastpath_probe",
char *, "DL_IOC_HDR_INFO", ill_t *, ill);
putnext(ill->ill_wq, mp);
return (0);
}
void
ill_capability_probe(ill_t *ill)
{
mblk_t *mp;
ASSERT(IAM_WRITER_ILL(ill));
if (ill->ill_dlpi_capab_state != IDCS_UNKNOWN &&
ill->ill_dlpi_capab_state != IDCS_FAILED)
return;
if (ill->ill_capab_reset_mp != NULL) {
freemsg(ill->ill_capab_reset_mp);
ill->ill_capab_reset_mp = NULL;
}
ip1dbg(("ill_capability_probe: starting capability negotiation\n"));
mp = ip_dlpi_alloc(sizeof (dl_capability_req_t), DL_CAPABILITY_REQ);
if (mp == NULL)
return;
ill_capability_send(ill, mp);
ill->ill_dlpi_capab_state = IDCS_PROBE_SENT;
}
void
ill_capability_reset(ill_t *ill, boolean_t reneg)
{
ASSERT(IAM_WRITER_ILL(ill));
if (ill->ill_dlpi_capab_state != IDCS_OK)
return;
ill->ill_dlpi_capab_state = reneg ? IDCS_RENEG : IDCS_RESET_SENT;
ill_capability_send(ill, ill->ill_capab_reset_mp);
ill->ill_capab_reset_mp = NULL;
ill->ill_capabilities &= ~(ILL_CAPAB_HCKSUM | ILL_CAPAB_ZEROCOPY);
}
static void
ill_capability_reset_alloc(ill_t *ill)
{
mblk_t *mp;
size_t size = 0;
int err;
dl_capability_req_t *capb;
ASSERT(IAM_WRITER_ILL(ill));
ASSERT(ill->ill_capab_reset_mp == NULL);
if (ILL_HCKSUM_CAPABLE(ill)) {
size += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_hcksum_t);
}
if (ill->ill_capabilities & ILL_CAPAB_ZEROCOPY) {
size += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_zerocopy_t);
}
if (ill->ill_capabilities & ILL_CAPAB_DLD) {
size += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_dld_t);
}
mp = allocb_wait(size + sizeof (dl_capability_req_t), BPRI_MED,
STR_NOSIG, &err);
mp->b_datap->db_type = M_PROTO;
bzero(mp->b_rptr, size + sizeof (dl_capability_req_t));
capb = (dl_capability_req_t *)mp->b_rptr;
capb->dl_primitive = DL_CAPABILITY_REQ;
capb->dl_sub_offset = sizeof (dl_capability_req_t);
capb->dl_sub_length = size;
mp->b_wptr += sizeof (dl_capability_req_t);
ill_capability_hcksum_reset_fill(ill, mp);
ill_capability_zerocopy_reset_fill(ill, mp);
ill_capability_dld_reset_fill(ill, mp);
ill->ill_capab_reset_mp = mp;
}
static void
ill_capability_id_ack(ill_t *ill, mblk_t *mp, dl_capability_sub_t *outers)
{
dl_capab_id_t *id_ic;
uint_t sub_dl_cap = outers->dl_cap;
dl_capability_sub_t *inners;
uint8_t *capend;
ASSERT(sub_dl_cap == DL_CAPAB_ID_WRAPPER);
capend = (uint8_t *)(outers + 1) + outers->dl_length;
if (capend > mp->b_wptr) {
cmn_err(CE_WARN, "ill_capability_id_ack: "
"malformed sub-capability too long for mblk");
return;
}
id_ic = (dl_capab_id_t *)(outers + 1);
inners = &id_ic->id_subcap;
if (outers->dl_length < sizeof (*id_ic) ||
inners->dl_length > (outers->dl_length - sizeof (*inners))) {
cmn_err(CE_WARN, "ill_capability_id_ack: malformed "
"encapsulated capab type %d too long for mblk",
inners->dl_cap);
return;
}
if (!dlcapabcheckqid(&id_ic->id_mid, ill->ill_lmod_rq)) {
ip1dbg(("ill_capability_id_ack: mid token for capab type %d "
"isn't as expected; pass-thru module(s) detected, "
"discarding capability\n", inners->dl_cap));
return;
}
ill_capability_dispatch(ill, mp, inners);
}
static void
ill_capability_dld_reset_fill(ill_t *ill, mblk_t *mp)
{
dl_capability_sub_t *dl_subcap;
if (!(ill->ill_capabilities & ILL_CAPAB_DLD))
return;
dl_subcap = (dl_capability_sub_t *)mp->b_wptr;
dl_subcap->dl_cap = DL_CAPAB_DLD;
dl_subcap->dl_length = sizeof (dl_capab_dld_t);
mp->b_wptr += sizeof (dl_capability_sub_t) + sizeof (dl_capab_dld_t);
}
static void
ill_capability_dispatch(ill_t *ill, mblk_t *mp, dl_capability_sub_t *subp)
{
if (!ill->ill_dl_up) {
if (subp->dl_cap == DL_CAPAB_VRRP)
ill_capability_vrrp_ack(ill, mp, subp);
return;
}
switch (subp->dl_cap) {
case DL_CAPAB_HCKSUM:
ill_capability_hcksum_ack(ill, mp, subp);
break;
case DL_CAPAB_ZEROCOPY:
ill_capability_zerocopy_ack(ill, mp, subp);
break;
case DL_CAPAB_DLD:
ill_capability_dld_ack(ill, mp, subp);
break;
case DL_CAPAB_VRRP:
break;
default:
ip1dbg(("ill_capability_dispatch: unknown capab type %d\n",
subp->dl_cap));
}
}
static void
ill_capability_vrrp_ack(ill_t *ill, mblk_t *mp, dl_capability_sub_t *isub)
{
dl_capab_vrrp_t *vrrp;
uint_t sub_dl_cap = isub->dl_cap;
uint8_t *capend;
ASSERT(IAM_WRITER_ILL(ill));
ASSERT(sub_dl_cap == DL_CAPAB_VRRP);
capend = (uint8_t *)(isub + 1) + isub->dl_length;
if (capend > mp->b_wptr) {
cmn_err(CE_WARN, "ill_capability_vrrp_ack: "
"malformed sub-capability too long for mblk");
return;
}
vrrp = (dl_capab_vrrp_t *)(isub + 1);
if ((vrrp->vrrp_af == AF_INET6 && ill->ill_isv6) ||
(vrrp->vrrp_af == AF_INET && !ill->ill_isv6)) {
ill->ill_flags |= ILLF_VRRP;
}
}
static void
ill_capability_hcksum_ack(ill_t *ill, mblk_t *mp, dl_capability_sub_t *isub)
{
dl_capability_req_t *ocap;
dl_capab_hcksum_t *ihck, *ohck;
ill_hcksum_capab_t **ill_hcksum;
mblk_t *nmp = NULL;
uint_t sub_dl_cap = isub->dl_cap;
uint8_t *capend;
ASSERT(sub_dl_cap == DL_CAPAB_HCKSUM);
ill_hcksum = (ill_hcksum_capab_t **)&ill->ill_hcksum_capab;
capend = (uint8_t *)(isub + 1) + isub->dl_length;
if (capend > mp->b_wptr) {
cmn_err(CE_WARN, "ill_capability_hcksum_ack: "
"malformed sub-capability too long for mblk");
return;
}
ihck = (dl_capab_hcksum_t *)(isub + 1);
if (ihck->hcksum_version != HCKSUM_VERSION_1) {
cmn_err(CE_CONT, "ill_capability_hcksum_ack: "
"unsupported hardware checksum "
"sub-capability (version %d, expected %d)",
ihck->hcksum_version, HCKSUM_VERSION_1);
return;
}
if (!dlcapabcheckqid(&ihck->hcksum_mid, ill->ill_lmod_rq)) {
ip1dbg(("ill_capability_hcksum_ack: mid token for hardware "
"checksum capability isn't as expected; pass-thru "
"module(s) detected, discarding capability\n"));
return;
}
#define CURR_HCKSUM_CAPAB \
(HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 | \
HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)
if ((ihck->hcksum_txflags & HCKSUM_ENABLE) &&
(ihck->hcksum_txflags & CURR_HCKSUM_CAPAB)) {
if (*ill_hcksum == NULL) {
*ill_hcksum = kmem_zalloc(sizeof (ill_hcksum_capab_t),
KM_NOSLEEP);
if (*ill_hcksum == NULL) {
cmn_err(CE_WARN, "ill_capability_hcksum_ack: "
"could not enable hcksum version %d "
"for %s (ENOMEM)\n", HCKSUM_CURRENT_VERSION,
ill->ill_name);
return;
}
}
(*ill_hcksum)->ill_hcksum_version = ihck->hcksum_version;
(*ill_hcksum)->ill_hcksum_txflags = ihck->hcksum_txflags;
ill->ill_capabilities |= ILL_CAPAB_HCKSUM;
ip1dbg(("ill_capability_hcksum_ack: interface %s "
"has enabled hardware checksumming\n ",
ill->ill_name));
} else if (ihck->hcksum_txflags & CURR_HCKSUM_CAPAB) {
uint_t size;
uchar_t *rptr;
size = sizeof (dl_capability_req_t) +
sizeof (dl_capability_sub_t) + isub->dl_length;
if ((nmp = ip_dlpi_alloc(size, DL_CAPABILITY_REQ)) == NULL) {
cmn_err(CE_WARN, "ill_capability_hcksum_ack: "
"could not enable hardware cksum for %s (ENOMEM)\n",
ill->ill_name);
return;
}
rptr = nmp->b_rptr;
ocap = (dl_capability_req_t *)nmp->b_rptr;
ocap->dl_sub_offset =
sizeof (dl_capability_req_t);
ocap->dl_sub_length =
sizeof (dl_capability_sub_t) +
isub->dl_length;
nmp->b_rptr += sizeof (dl_capability_req_t);
bcopy(isub, nmp->b_rptr, sizeof (*isub));
nmp->b_rptr += sizeof (*isub);
ohck = (dl_capab_hcksum_t *)nmp->b_rptr;
bcopy(ihck, ohck, sizeof (*ihck));
nmp->b_rptr = rptr;
ASSERT(nmp->b_wptr == (nmp->b_rptr + size));
ohck->hcksum_txflags &= CURR_HCKSUM_CAPAB;
ohck->hcksum_txflags |= HCKSUM_ENABLE;
ill_capability_send(ill, nmp);
} else {
ip1dbg(("ill_capability_hcksum_ack: interface %s has "
"advertised %x hardware checksum capability flags\n",
ill->ill_name, ihck->hcksum_txflags));
}
}
static void
ill_capability_hcksum_reset_fill(ill_t *ill, mblk_t *mp)
{
dl_capab_hcksum_t *hck_subcap;
dl_capability_sub_t *dl_subcap;
if (!ILL_HCKSUM_CAPABLE(ill))
return;
ASSERT(ill->ill_hcksum_capab != NULL);
dl_subcap = (dl_capability_sub_t *)mp->b_wptr;
dl_subcap->dl_cap = DL_CAPAB_HCKSUM;
dl_subcap->dl_length = sizeof (*hck_subcap);
hck_subcap = (dl_capab_hcksum_t *)(dl_subcap + 1);
hck_subcap->hcksum_version = ill->ill_hcksum_capab->ill_hcksum_version;
hck_subcap->hcksum_txflags = 0;
mp->b_wptr += sizeof (*dl_subcap) + sizeof (*hck_subcap);
}
static void
ill_capability_zerocopy_ack(ill_t *ill, mblk_t *mp, dl_capability_sub_t *isub)
{
mblk_t *nmp = NULL;
dl_capability_req_t *oc;
dl_capab_zerocopy_t *zc_ic, *zc_oc;
ill_zerocopy_capab_t **ill_zerocopy_capab;
uint_t sub_dl_cap = isub->dl_cap;
uint8_t *capend;
ASSERT(sub_dl_cap == DL_CAPAB_ZEROCOPY);
ill_zerocopy_capab = (ill_zerocopy_capab_t **)&ill->ill_zerocopy_capab;
capend = (uint8_t *)(isub + 1) + isub->dl_length;
if (capend > mp->b_wptr) {
cmn_err(CE_WARN, "ill_capability_zerocopy_ack: "
"malformed sub-capability too long for mblk");
return;
}
zc_ic = (dl_capab_zerocopy_t *)(isub + 1);
if (zc_ic->zerocopy_version != ZEROCOPY_VERSION_1) {
cmn_err(CE_CONT, "ill_capability_zerocopy_ack: "
"unsupported ZEROCOPY sub-capability (version %d, "
"expected %d)", zc_ic->zerocopy_version,
ZEROCOPY_VERSION_1);
return;
}
if (!dlcapabcheckqid(&zc_ic->zerocopy_mid, ill->ill_lmod_rq)) {
ip1dbg(("ill_capability_zerocopy_ack: mid token for zerocopy "
"capability isn't as expected; pass-thru module(s) "
"detected, discarding capability\n"));
return;
}
if ((zc_ic->zerocopy_flags & DL_CAPAB_VMSAFE_MEM) != 0) {
if (*ill_zerocopy_capab == NULL) {
*ill_zerocopy_capab =
kmem_zalloc(sizeof (ill_zerocopy_capab_t),
KM_NOSLEEP);
if (*ill_zerocopy_capab == NULL) {
cmn_err(CE_WARN, "ill_capability_zerocopy_ack: "
"could not enable Zero-copy version %d "
"for %s (ENOMEM)\n", ZEROCOPY_VERSION_1,
ill->ill_name);
return;
}
}
ip1dbg(("ill_capability_zerocopy_ack: interface %s "
"supports Zero-copy version %d\n", ill->ill_name,
ZEROCOPY_VERSION_1));
(*ill_zerocopy_capab)->ill_zerocopy_version =
zc_ic->zerocopy_version;
(*ill_zerocopy_capab)->ill_zerocopy_flags =
zc_ic->zerocopy_flags;
ill->ill_capabilities |= ILL_CAPAB_ZEROCOPY;
} else {
uint_t size;
uchar_t *rptr;
size = sizeof (dl_capability_req_t) +
sizeof (dl_capability_sub_t) +
sizeof (dl_capab_zerocopy_t);
if ((nmp = ip_dlpi_alloc(size, DL_CAPABILITY_REQ)) == NULL) {
cmn_err(CE_WARN, "ill_capability_zerocopy_ack: "
"could not enable zerocopy for %s (ENOMEM)\n",
ill->ill_name);
return;
}
rptr = nmp->b_rptr;
oc = (dl_capability_req_t *)rptr;
oc->dl_sub_offset = sizeof (dl_capability_req_t);
oc->dl_sub_length = sizeof (dl_capability_sub_t) +
sizeof (dl_capab_zerocopy_t);
rptr += sizeof (dl_capability_req_t);
bcopy(isub, rptr, sizeof (*isub));
rptr += sizeof (*isub);
zc_oc = (dl_capab_zerocopy_t *)rptr;
*zc_oc = *zc_ic;
ip1dbg(("ill_capability_zerocopy_ack: asking interface %s "
"to enable zero-copy version %d\n", ill->ill_name,
ZEROCOPY_VERSION_1));
zc_oc->zerocopy_flags |= DL_CAPAB_VMSAFE_MEM;
ill_capability_send(ill, nmp);
}
}
static void
ill_capability_zerocopy_reset_fill(ill_t *ill, mblk_t *mp)
{
dl_capab_zerocopy_t *zerocopy_subcap;
dl_capability_sub_t *dl_subcap;
if (!(ill->ill_capabilities & ILL_CAPAB_ZEROCOPY))
return;
ASSERT(ill->ill_zerocopy_capab != NULL);
dl_subcap = (dl_capability_sub_t *)mp->b_wptr;
dl_subcap->dl_cap = DL_CAPAB_ZEROCOPY;
dl_subcap->dl_length = sizeof (*zerocopy_subcap);
zerocopy_subcap = (dl_capab_zerocopy_t *)(dl_subcap + 1);
zerocopy_subcap->zerocopy_version =
ill->ill_zerocopy_capab->ill_zerocopy_version;
zerocopy_subcap->zerocopy_flags = 0;
mp->b_wptr += sizeof (*dl_subcap) + sizeof (*zerocopy_subcap);
}
static void
ill_capability_dld_ack(ill_t *ill, mblk_t *mp, dl_capability_sub_t *isub)
{
dl_capab_dld_t *dld_ic, dld;
uint_t sub_dl_cap = isub->dl_cap;
uint8_t *capend;
ill_dld_capab_t *idc;
ASSERT(IAM_WRITER_ILL(ill));
ASSERT(sub_dl_cap == DL_CAPAB_DLD);
capend = (uint8_t *)(isub + 1) + isub->dl_length;
if (capend > mp->b_wptr) {
cmn_err(CE_WARN, "ill_capability_dld_ack: "
"malformed sub-capability too long for mblk");
return;
}
dld_ic = (dl_capab_dld_t *)(isub + 1);
if (dld_ic->dld_version != DLD_CURRENT_VERSION) {
cmn_err(CE_CONT, "ill_capability_dld_ack: "
"unsupported DLD sub-capability (version %d, "
"expected %d)", dld_ic->dld_version,
DLD_CURRENT_VERSION);
return;
}
if (!dlcapabcheckqid(&dld_ic->dld_mid, ill->ill_lmod_rq)) {
ip1dbg(("ill_capability_dld_ack: mid token for dld "
"capability isn't as expected; pass-thru module(s) "
"detected, discarding capability\n"));
return;
}
bcopy(dld_ic, &dld, sizeof (dl_capab_dld_t));
if ((idc = ill->ill_dld_capab) == NULL) {
idc = kmem_zalloc(sizeof (ill_dld_capab_t), KM_NOSLEEP);
if (idc == NULL) {
cmn_err(CE_WARN, "ill_capability_dld_ack: "
"could not enable DLD version %d "
"for %s (ENOMEM)\n", DLD_CURRENT_VERSION,
ill->ill_name);
return;
}
ill->ill_dld_capab = idc;
}
idc->idc_capab_df = (ip_capab_func_t)dld.dld_capab;
idc->idc_capab_dh = (void *)dld.dld_capab_handle;
ip1dbg(("ill_capability_dld_ack: interface %s "
"supports DLD version %d\n", ill->ill_name, DLD_CURRENT_VERSION));
ill_capability_dld_enable(ill);
}
static void
ill_mac_perim_enter(ill_t *ill, mac_perim_handle_t *mphp)
{
ill_dld_capab_t *idc = ill->ill_dld_capab;
int err;
err = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_PERIM, mphp,
DLD_ENABLE);
ASSERT(err == 0);
}
static void
ill_mac_perim_exit(ill_t *ill, mac_perim_handle_t mph)
{
ill_dld_capab_t *idc = ill->ill_dld_capab;
int err;
err = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_PERIM, mph,
DLD_DISABLE);
ASSERT(err == 0);
}
boolean_t
ill_mac_perim_held(ill_t *ill)
{
ill_dld_capab_t *idc = ill->ill_dld_capab;
return (idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_PERIM, NULL,
DLD_QUERY));
}
static void
ill_capability_direct_enable(ill_t *ill)
{
ill_dld_capab_t *idc = ill->ill_dld_capab;
ill_dld_direct_t *idd = &idc->idc_direct;
dld_capab_direct_t direct;
int rc;
ASSERT(IAM_WRITER_ILL(ill));
bzero(&direct, sizeof (direct));
if (ill->ill_isv6) {
direct.di_rx_cf = (uintptr_t)ip_input_v6;
} else {
direct.di_rx_cf = (uintptr_t)ip_input;
}
direct.di_rx_ch = ill;
rc = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_DIRECT, &direct,
DLD_ENABLE);
if (rc == 0) {
idd->idd_tx_df = (ip_dld_tx_t)direct.di_tx_df;
idd->idd_tx_dh = direct.di_tx_dh;
idd->idd_tx_cb_df = (ip_dld_callb_t)direct.di_tx_cb_df;
idd->idd_tx_cb_dh = direct.di_tx_cb_dh;
idd->idd_tx_fctl_df = (ip_dld_fctl_t)direct.di_tx_fctl_df;
idd->idd_tx_fctl_dh = direct.di_tx_fctl_dh;
ASSERT(idd->idd_tx_cb_df != NULL);
ASSERT(idd->idd_tx_fctl_df != NULL);
ASSERT(idd->idd_tx_df != NULL);
ill->ill_flownotify_mh = idd->idd_tx_cb_df(idd->idd_tx_cb_dh,
ill_flow_enable, ill);
ill->ill_capabilities |= ILL_CAPAB_DLD_DIRECT;
DTRACE_PROBE1(direct_on, (ill_t *), ill);
} else {
cmn_err(CE_WARN, "warning: could not enable DIRECT "
"capability, rc = %d\n", rc);
DTRACE_PROBE2(direct_off, (ill_t *), ill, (int), rc);
}
}
static void
ill_capability_poll_enable(ill_t *ill)
{
ill_dld_capab_t *idc = ill->ill_dld_capab;
dld_capab_poll_t poll;
int rc;
ASSERT(IAM_WRITER_ILL(ill));
bzero(&poll, sizeof (poll));
poll.poll_ring_add_cf = (uintptr_t)ip_squeue_add_ring;
poll.poll_ring_remove_cf = (uintptr_t)ip_squeue_clean_ring;
poll.poll_ring_quiesce_cf = (uintptr_t)ip_squeue_quiesce_ring;
poll.poll_ring_restart_cf = (uintptr_t)ip_squeue_restart_ring;
poll.poll_ring_bind_cf = (uintptr_t)ip_squeue_bind_ring;
poll.poll_ring_ch = ill;
rc = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_POLL, &poll,
DLD_ENABLE);
if (rc == 0) {
ill->ill_capabilities |= ILL_CAPAB_DLD_POLL;
DTRACE_PROBE1(poll_on, (ill_t *), ill);
} else {
ip1dbg(("warning: could not enable POLL "
"capability, rc = %d\n", rc));
DTRACE_PROBE2(poll_off, (ill_t *), ill, (int), rc);
}
}
static void
ill_capability_lso_enable(ill_t *ill)
{
ill_dld_capab_t *idc = ill->ill_dld_capab;
dld_capab_lso_t lso;
int rc;
ASSERT(IAM_WRITER_ILL(ill));
if (ill->ill_lso_capab == NULL) {
ill->ill_lso_capab = kmem_zalloc(sizeof (ill_lso_capab_t),
KM_NOSLEEP);
if (ill->ill_lso_capab == NULL) {
cmn_err(CE_WARN, "ill_capability_lso_enable: "
"could not enable LSO for %s (ENOMEM)\n",
ill->ill_name);
return;
}
}
bzero(&lso, sizeof (lso));
if ((rc = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_LSO, &lso,
DLD_ENABLE)) == 0) {
ill->ill_lso_capab->ill_lso_flags = lso.lso_flags;
ill->ill_lso_capab->ill_lso_max_tcpv4 = lso.lso_max_tcpv4;
ill->ill_lso_capab->ill_lso_max_tcpv6 = lso.lso_max_tcpv6;
ill->ill_capabilities |= ILL_CAPAB_LSO;
ip1dbg(("ill_capability_lso_enable: interface %s "
"has enabled LSO\n ", ill->ill_name));
} else {
kmem_free(ill->ill_lso_capab, sizeof (ill_lso_capab_t));
ill->ill_lso_capab = NULL;
DTRACE_PROBE2(lso_off, (ill_t *), ill, (int), rc);
}
}
static void
ill_capability_dld_enable(ill_t *ill)
{
mac_perim_handle_t mph;
ASSERT(IAM_WRITER_ILL(ill));
ill_mac_perim_enter(ill, &mph);
ill_capability_direct_enable(ill);
ill_capability_poll_enable(ill);
ill_capability_lso_enable(ill);
ill->ill_capabilities |= ILL_CAPAB_DLD;
ill_mac_perim_exit(ill, mph);
}
static void
ill_capability_dld_disable(ill_t *ill)
{
ill_dld_capab_t *idc;
ill_dld_direct_t *idd;
mac_perim_handle_t mph;
ASSERT(IAM_WRITER_ILL(ill));
if (!(ill->ill_capabilities & ILL_CAPAB_DLD))
return;
ill_mac_perim_enter(ill, &mph);
idc = ill->ill_dld_capab;
if ((ill->ill_capabilities & ILL_CAPAB_DLD_DIRECT) != 0) {
mutex_enter(&ill->ill_lock);
ill->ill_capabilities &= ~ILL_CAPAB_DLD_DIRECT;
mutex_exit(&ill->ill_lock);
if (ill->ill_flownotify_mh != NULL) {
idd = &idc->idc_direct;
idd->idd_tx_cb_df(idd->idd_tx_cb_dh, NULL,
ill->ill_flownotify_mh);
ill->ill_flownotify_mh = NULL;
}
(void) idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_DIRECT,
NULL, DLD_DISABLE);
}
if ((ill->ill_capabilities & ILL_CAPAB_DLD_POLL) != 0) {
ill->ill_capabilities &= ~ILL_CAPAB_DLD_POLL;
ip_squeue_clean_all(ill);
(void) idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_POLL,
NULL, DLD_DISABLE);
}
if ((ill->ill_capabilities & ILL_CAPAB_LSO) != 0) {
ASSERT(ill->ill_lso_capab != NULL);
ill->ill_capabilities &= ~ILL_CAPAB_LSO;
(void) idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_LSO,
NULL, DLD_DISABLE);
}
ill->ill_capabilities &= ~ILL_CAPAB_DLD;
ill_mac_perim_exit(ill, mph);
}
void
ill_taskq_dispatch(ip_stack_t *ipst)
{
callb_cpr_t cprinfo;
char name[64];
mblk_t *mp;
(void) snprintf(name, sizeof (name), "ill_taskq_dispatch_%d",
ipst->ips_netstack->netstack_stackid);
CALLB_CPR_INIT(&cprinfo, &ipst->ips_capab_taskq_lock, callb_generic_cpr,
name);
mutex_enter(&ipst->ips_capab_taskq_lock);
for (;;) {
mp = ipst->ips_capab_taskq_head;
while (mp != NULL) {
ipst->ips_capab_taskq_head = mp->b_next;
if (ipst->ips_capab_taskq_head == NULL)
ipst->ips_capab_taskq_tail = NULL;
mutex_exit(&ipst->ips_capab_taskq_lock);
mp->b_next = NULL;
VERIFY(taskq_dispatch(system_taskq,
ill_capability_ack_thr, mp, TQ_SLEEP) !=
TASKQID_INVALID);
mutex_enter(&ipst->ips_capab_taskq_lock);
mp = ipst->ips_capab_taskq_head;
}
if (ipst->ips_capab_taskq_quit)
break;
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&ipst->ips_capab_taskq_cv, &ipst->ips_capab_taskq_lock);
CALLB_CPR_SAFE_END(&cprinfo, &ipst->ips_capab_taskq_lock);
}
VERIFY(ipst->ips_capab_taskq_head == NULL);
VERIFY(ipst->ips_capab_taskq_tail == NULL);
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
}
static void
ill_capability_ack_thr(void *arg)
{
mblk_t *mp = arg;
dl_capability_ack_t *capp;
dl_capability_sub_t *subp, *endp;
ill_t *ill;
boolean_t reneg;
ill = (ill_t *)mp->b_prev;
mp->b_prev = NULL;
VERIFY(ipsq_enter(ill, B_FALSE, CUR_OP) == B_TRUE);
if (ill->ill_dlpi_capab_state == IDCS_RESET_SENT ||
ill->ill_dlpi_capab_state == IDCS_RENEG) {
reneg = ill->ill_dlpi_capab_state == IDCS_RENEG;
ill_capability_dld_disable(ill);
ill->ill_dlpi_capab_state = IDCS_UNKNOWN;
if (reneg)
ill_capability_probe(ill);
goto done;
}
if (ill->ill_dlpi_capab_state == IDCS_PROBE_SENT)
ill->ill_dlpi_capab_state = IDCS_OK;
capp = (dl_capability_ack_t *)mp->b_rptr;
if (capp->dl_sub_length == 0) {
goto done;
}
if ((sizeof (*capp) + capp->dl_sub_length) > MBLKL(mp)) {
ip0dbg(("ill_capability_ack: bad DL_CAPABILITY_ACK, "
"invalid dl_sub_length (%d)\n", capp->dl_sub_length));
goto done;
}
#define SC(base, offset) (dl_capability_sub_t *)(((uchar_t *)(base))+(offset))
for (subp = SC(capp, capp->dl_sub_offset),
endp = SC(subp, capp->dl_sub_length - sizeof (*subp));
subp <= endp;
subp = SC(subp, sizeof (dl_capability_sub_t) + subp->dl_length)) {
switch (subp->dl_cap) {
case DL_CAPAB_ID_WRAPPER:
ill_capability_id_ack(ill, mp, subp);
break;
default:
ill_capability_dispatch(ill, mp, subp);
break;
}
}
#undef SC
done:
inet_freemsg(mp);
ill_capability_done(ill);
ipsq_exit(ill->ill_phyint->phyint_ipsq);
}
void
ill_capability_ack(ill_t *ill, mblk_t *mp)
{
ip_stack_t *ipst = ill->ill_ipst;
mp->b_prev = (mblk_t *)ill;
ASSERT(mp->b_next == NULL);
if (taskq_dispatch(system_taskq, ill_capability_ack_thr, mp,
TQ_NOSLEEP) != TASKQID_INVALID)
return;
mutex_enter(&ipst->ips_capab_taskq_lock);
if (ipst->ips_capab_taskq_head == NULL) {
ASSERT(ipst->ips_capab_taskq_tail == NULL);
ipst->ips_capab_taskq_head = mp;
} else {
ipst->ips_capab_taskq_tail->b_next = mp;
}
ipst->ips_capab_taskq_tail = mp;
cv_signal(&ipst->ips_capab_taskq_cv);
mutex_exit(&ipst->ips_capab_taskq_lock);
}
time_t
ill_frag_timeout(ill_t *ill, time_t dead_interval)
{
ipfb_t *ipfb;
ipfb_t *endp;
ipf_t *ipf;
ipf_t *ipfnext;
mblk_t *mp;
time_t current_time = gethrestime_sec();
time_t next_timeout = 0;
uint32_t hdr_length;
mblk_t *send_icmp_head;
mblk_t *send_icmp_head_v6;
ip_stack_t *ipst = ill->ill_ipst;
ip_recv_attr_t iras;
bzero(&iras, sizeof (iras));
iras.ira_flags = 0;
iras.ira_ill = iras.ira_rill = ill;
iras.ira_ruifindex = ill->ill_phyint->phyint_ifindex;
iras.ira_rifindex = iras.ira_ruifindex;
ipfb = ill->ill_frag_hash_tbl;
if (ipfb == NULL)
return (B_FALSE);
endp = &ipfb[ILL_FRAG_HASH_TBL_COUNT];
for (; ipfb < endp; ipfb++) {
send_icmp_head = NULL;
send_icmp_head_v6 = NULL;
mutex_enter(&ipfb->ipfb_lock);
while ((ipf = ipfb->ipfb_ipf) != 0) {
time_t frag_time = current_time - ipf->ipf_timestamp;
time_t frag_timeout;
if (frag_time < dead_interval) {
frag_timeout = dead_interval - frag_time;
if (next_timeout == 0 ||
frag_timeout < next_timeout) {
next_timeout = frag_timeout;
}
break;
}
hdr_length = ipf->ipf_nf_hdr_len;
ipfnext = ipf->ipf_hash_next;
if (ipfnext)
ipfnext->ipf_ptphn = ipf->ipf_ptphn;
*ipf->ipf_ptphn = ipfnext;
mp = ipf->ipf_mp->b_cont;
for (; mp; mp = mp->b_cont) {
IP_REASS_SET_START(mp, 0);
IP_REASS_SET_END(mp, 0);
}
mp = ipf->ipf_mp->b_cont;
atomic_add_32(&ill->ill_frag_count, -ipf->ipf_count);
ASSERT(ipfb->ipfb_count >= ipf->ipf_count);
ipfb->ipfb_count -= ipf->ipf_count;
ASSERT(ipfb->ipfb_frag_pkts > 0);
ipfb->ipfb_frag_pkts--;
if (ill->ill_isv6) {
if (hdr_length != 0) {
mp->b_next = send_icmp_head_v6;
send_icmp_head_v6 = mp;
} else {
freemsg(mp);
}
} else {
if (hdr_length != 0) {
mp->b_next = send_icmp_head;
send_icmp_head = mp;
} else {
freemsg(mp);
}
}
BUMP_MIB(ill->ill_ip_mib, ipIfStatsReasmFails);
ip_drop_input("ipIfStatsReasmFails", ipf->ipf_mp, ill);
freeb(ipf->ipf_mp);
}
mutex_exit(&ipfb->ipfb_lock);
while (send_icmp_head_v6 != NULL) {
ip6_t *ip6h;
mp = send_icmp_head_v6;
send_icmp_head_v6 = send_icmp_head_v6->b_next;
mp->b_next = NULL;
ip6h = (ip6_t *)mp->b_rptr;
iras.ira_flags = 0;
iras.ira_zoneid =
ipif_lookup_addr_zoneid_v6(&ip6h->ip6_dst,
ill, ipst);
ip_drop_input("ICMP_TIME_EXCEEDED reass", mp, ill);
icmp_time_exceeded_v6(mp,
ICMP_REASSEMBLY_TIME_EXCEEDED, B_FALSE,
&iras);
ASSERT(!(iras.ira_flags & IRAF_IPSEC_SECURE));
}
while (send_icmp_head != NULL) {
ipaddr_t dst;
mp = send_icmp_head;
send_icmp_head = send_icmp_head->b_next;
mp->b_next = NULL;
dst = ((ipha_t *)mp->b_rptr)->ipha_dst;
iras.ira_flags = IRAF_IS_IPV4;
iras.ira_zoneid = ipif_lookup_addr_zoneid(dst,
ill, ipst);
ip_drop_input("ICMP_TIME_EXCEEDED reass", mp, ill);
icmp_time_exceeded(mp,
ICMP_REASSEMBLY_TIME_EXCEEDED, &iras);
ASSERT(!(iras.ira_flags & IRAF_IPSEC_SECURE));
}
}
return (next_timeout);
}
void
ill_frag_prune(ill_t *ill, uint_t max_count)
{
ipfb_t *ipfb;
ipf_t *ipf;
size_t count;
clock_t now;
mutex_enter(&ill->ill_lock);
now = ddi_get_lbolt();
if (TICK_TO_MSEC(now - ill->ill_last_frag_clean_time) <=
(ip_min_frag_prune_time != 0 ?
ip_min_frag_prune_time : msec_per_tick)) {
ill->ill_frag_free_num_pkts++;
} else {
ill->ill_frag_free_num_pkts = 0;
}
ill->ill_last_frag_clean_time = now;
mutex_exit(&ill->ill_lock);
if (ill->ill_frag_free_num_pkts != 0) {
int ix;
for (ix = 0; ix < ILL_FRAG_HASH_TBL_COUNT; ix++) {
ipfb = &ill->ill_frag_hash_tbl[ix];
mutex_enter(&ipfb->ipfb_lock);
if (ipfb->ipfb_ipf != NULL) {
ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf,
ill->ill_frag_free_num_pkts);
}
mutex_exit(&ipfb->ipfb_lock);
}
}
while (ill->ill_frag_count > max_count) {
int ix;
ipfb_t *oipfb = NULL;
uint_t oldest = UINT_MAX;
count = 0;
for (ix = 0; ix < ILL_FRAG_HASH_TBL_COUNT; ix++) {
ipfb = &ill->ill_frag_hash_tbl[ix];
mutex_enter(&ipfb->ipfb_lock);
ipf = ipfb->ipfb_ipf;
if (ipf != NULL && ipf->ipf_gen < oldest) {
oldest = ipf->ipf_gen;
oipfb = ipfb;
}
count += ipfb->ipfb_count;
mutex_exit(&ipfb->ipfb_lock);
}
if (oipfb == NULL)
break;
if (count <= max_count)
return;
mutex_enter(&oipfb->ipfb_lock);
ipf = oipfb->ipfb_ipf;
if (ipf != NULL) {
ill_frag_free_pkts(ill, oipfb, ipf, 1);
}
mutex_exit(&oipfb->ipfb_lock);
}
}
void
ill_frag_free_pkts(ill_t *ill, ipfb_t *ipfb, ipf_t *ipf, int free_cnt)
{
size_t count;
mblk_t *mp;
mblk_t *tmp;
ipf_t **ipfp = ipf->ipf_ptphn;
ASSERT(MUTEX_HELD(&ipfb->ipfb_lock));
ASSERT(ipfp != NULL);
ASSERT(ipf != NULL);
while (ipf != NULL && free_cnt-- > 0) {
count = ipf->ipf_count;
mp = ipf->ipf_mp;
ipf = ipf->ipf_hash_next;
for (tmp = mp; tmp; tmp = tmp->b_cont) {
IP_REASS_SET_START(tmp, 0);
IP_REASS_SET_END(tmp, 0);
}
atomic_add_32(&ill->ill_frag_count, -count);
ASSERT(ipfb->ipfb_count >= count);
ipfb->ipfb_count -= count;
ASSERT(ipfb->ipfb_frag_pkts > 0);
ipfb->ipfb_frag_pkts--;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsReasmFails);
ip_drop_input("ipIfStatsReasmFails", mp, ill);
freemsg(mp);
}
if (ipf)
ipf->ipf_ptphn = ipfp;
ipfp[0] = ipf;
}
static void
ill_forward_set_on_ill(ill_t *ill, boolean_t enable)
{
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(IAM_WRITER_ILL(ill) || RW_READ_HELD(&ipst->ips_ill_g_lock));
ip1dbg(("ill_forward_set: %s %s forwarding on %s",
(enable ? "Enabling" : "Disabling"),
(ill->ill_isv6 ? "IPv6" : "IPv4"), ill->ill_name));
mutex_enter(&ill->ill_lock);
if (enable)
ill->ill_flags |= ILLF_ROUTER;
else
ill->ill_flags &= ~ILLF_ROUTER;
mutex_exit(&ill->ill_lock);
if (ill->ill_isv6)
ill_set_nce_router_flags(ill, enable);
if (ill->ill_ipif != NULL)
ip_rts_ifmsg(ill->ill_ipif, RTSQ_DEFAULT);
}
int
ill_forward_set(ill_t *ill, boolean_t enable)
{
ipmp_illgrp_t *illg;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(IAM_WRITER_ILL(ill) || RW_READ_HELD(&ipst->ips_ill_g_lock));
if ((enable && (ill->ill_flags & ILLF_ROUTER)) ||
(!enable && !(ill->ill_flags & ILLF_ROUTER)))
return (0);
if (IS_LOOPBACK(ill))
return (EINVAL);
if (enable && ill->ill_allowed_ips_cnt > 0)
return (EPERM);
if (IS_IPMP(ill) || IS_UNDER_IPMP(ill)) {
illg = ill->ill_grp;
ill = list_head(&illg->ig_if);
for (; ill != NULL; ill = list_next(&illg->ig_if, ill))
ill_forward_set_on_ill(ill, enable);
ill_forward_set_on_ill(ipmp_illgrp_ipmp_ill(illg), enable);
return (0);
}
ill_forward_set_on_ill(ill, enable);
return (0);
}
static void
ill_set_nce_router_flags(ill_t *ill, boolean_t enable)
{
ipif_t *ipif;
ncec_t *ncec;
nce_t *nce;
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
nce = nce_lookup_v6(ill, &ipif->ipif_v6lcl_addr);
if (nce != NULL) {
ncec = nce->nce_common;
mutex_enter(&ncec->ncec_lock);
if (enable)
ncec->ncec_flags |= NCE_F_ISROUTER;
else
ncec->ncec_flags &= ~NCE_F_ISROUTER;
mutex_exit(&ncec->ncec_lock);
nce_refrele(nce);
}
}
}
ill_t *
ill_first(int start_list, int end_list, ill_walk_context_t *ctx,
ip_stack_t *ipst)
{
ill_if_t *ifp;
ill_t *ill;
avl_tree_t *avl_tree;
ASSERT(RW_LOCK_HELD(&ipst->ips_ill_g_lock));
ASSERT(end_list <= MAX_G_HEADS && start_list >= 0);
if (end_list != MAX_G_HEADS) {
ctx->ctx_current_list = start_list;
ctx->ctx_last_list = end_list;
} else {
ctx->ctx_last_list = MAX_G_HEADS - 1;
ctx->ctx_current_list = 0;
}
while (ctx->ctx_current_list <= ctx->ctx_last_list) {
ifp = IP_VX_ILL_G_LIST(ctx->ctx_current_list, ipst);
if (ifp != (ill_if_t *)
&IP_VX_ILL_G_LIST(ctx->ctx_current_list, ipst)) {
avl_tree = &ifp->illif_avl_by_ppa;
ill = avl_first(avl_tree);
ASSERT(ill != NULL);
return (ill);
}
ctx->ctx_current_list++;
}
return (NULL);
}
ill_t *
ill_next(ill_walk_context_t *ctx, ill_t *lastill)
{
ill_if_t *ifp;
ill_t *ill;
ip_stack_t *ipst = lastill->ill_ipst;
ASSERT(lastill->ill_ifptr != (ill_if_t *)
&IP_VX_ILL_G_LIST(ctx->ctx_current_list, ipst));
if ((ill = avl_walk(&lastill->ill_ifptr->illif_avl_by_ppa, lastill,
AVL_AFTER)) != NULL) {
return (ill);
}
ifp = lastill->ill_ifptr->illif_next;
while (ifp ==
(ill_if_t *)&IP_VX_ILL_G_LIST(ctx->ctx_current_list, ipst)) {
if (++ctx->ctx_current_list > ctx->ctx_last_list)
return (NULL);
ifp = IP_VX_ILL_G_LIST(ctx->ctx_current_list, ipst);
}
return (avl_first(&ifp->illif_avl_by_ppa));
}
static char *
ill_get_ppa_ptr(char *name)
{
int namelen = strlen(name);
int end_ndx = namelen - 1;
int ppa_ndx, i;
if (namelen == 0 || !isalpha(name[0]) || !isdigit(name[end_ndx]))
return (NULL);
for (ppa_ndx = end_ndx; ppa_ndx > 0; ppa_ndx--)
if (!isdigit(name[ppa_ndx - 1]))
break;
if (name[ppa_ndx] == '0' && ppa_ndx < end_ndx)
return (NULL);
for (i = 1; i < ppa_ndx; i++) {
if (!isalpha(name[i]) && !isdigit(name[i]) &&
name[i] != '.' && name[i] != '_') {
return (NULL);
}
}
return (name + ppa_ndx);
}
static ill_t *
ill_find_by_name(char *name, boolean_t isv6, ip_stack_t *ipst)
{
char *ppa_ptr = NULL;
int len;
uint_t ppa;
ill_t *ill = NULL;
ill_if_t *ifp;
int list;
if (isv6)
list = IP_V6_G_HEAD;
else
list = IP_V4_G_HEAD;
if ((ppa_ptr = ill_get_ppa_ptr(name)) == NULL) {
return (NULL);
}
len = ppa_ptr - name + 1;
ppa = stoi(&ppa_ptr);
ifp = IP_VX_ILL_G_LIST(list, ipst);
while (ifp != (ill_if_t *)&IP_VX_ILL_G_LIST(list, ipst)) {
if ((ifp->illif_name_len == len) &&
bcmp(ifp->illif_name, name, len - 1) == 0) {
break;
} else {
ifp = ifp->illif_next;
}
}
if (ifp == (ill_if_t *)&IP_VX_ILL_G_LIST(list, ipst)) {
return (NULL);
}
ill = avl_find(&ifp->illif_avl_by_ppa, (void *) &ppa, NULL);
if (ill != NULL) {
mutex_enter(&ill->ill_lock);
if (ILL_CAN_LOOKUP(ill)) {
ill_refhold_locked(ill);
mutex_exit(&ill->ill_lock);
return (ill);
}
mutex_exit(&ill->ill_lock);
}
return (NULL);
}
static int
ill_compare_ppa(const void *ppa_ptr, const void *ill_ptr)
{
uint_t ppa;
uint_t ill_ppa;
ASSERT(ppa_ptr != NULL && ill_ptr != NULL);
ppa = *((uint_t *)ppa_ptr);
ill_ppa = ((const ill_t *)ill_ptr)->ill_ppa;
if (ill_ppa < ppa)
return (1);
if (ill_ppa > ppa)
return (-1);
return (0);
}
static void
ill_delete_interface_type(ill_if_t *interface)
{
ASSERT(interface != NULL);
ASSERT(avl_numnodes(&interface->illif_avl_by_ppa) == 0);
avl_destroy(&interface->illif_avl_by_ppa);
if (interface->illif_ppa_arena != NULL)
vmem_destroy(interface->illif_ppa_arena);
remque(interface);
mi_free(interface);
}
static void
ill_glist_delete(ill_t *ill)
{
ip_stack_t *ipst;
phyint_t *phyi;
if (ill == NULL)
return;
ipst = ill->ill_ipst;
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
if (ill->ill_ifptr != NULL) {
avl_remove(&ill->ill_ifptr->illif_avl_by_ppa, ill);
if (ill->ill_ifptr->illif_ppa_arena != NULL) {
vmem_free(ill->ill_ifptr->illif_ppa_arena,
(void *)(uintptr_t)(ill->ill_ppa+1), 1);
}
if (avl_numnodes(&ill->ill_ifptr->illif_avl_by_ppa) == 0) {
ill_delete_interface_type(ill->ill_ifptr);
}
ill->ill_ifptr = NULL;
ill->ill_name_length = 0;
ill->ill_name[0] = '\0';
ill->ill_ppa = UINT_MAX;
}
ill_nic_event_dispatch(ill, 0, NE_UNPLUMB, ill->ill_name,
ill->ill_name_length);
ASSERT(ill->ill_phyint != NULL);
phyi = ill->ill_phyint;
ill->ill_phyint = NULL;
if (ill->ill_flags & ILLF_IPV6)
phyi->phyint_illv6 = NULL;
else
phyi->phyint_illv4 = NULL;
if (phyi->phyint_illv4 != NULL || phyi->phyint_illv6 != NULL) {
rw_exit(&ipst->ips_ill_g_lock);
return;
}
if (phyi->phyint_ifindex > 0) {
avl_remove(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
phyi);
avl_remove(&ipst->ips_phyint_g_list->phyint_list_avl_by_name,
phyi);
}
rw_exit(&ipst->ips_ill_g_lock);
phyint_free(phyi);
}
static int
ill_alloc_ppa(ill_if_t *ifp, ill_t *ill)
{
ill_t *tmp_ill;
uint_t start, end;
int ppa;
if (ifp->illif_ppa_arena == NULL &&
(avl_numnodes(&ifp->illif_avl_by_ppa) + 1 > ill_no_arena)) {
ifp->illif_ppa_arena = vmem_create(ifp->illif_name,
(void *)1, UINT_MAX - 1, 1, NULL, NULL,
NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
for (tmp_ill = avl_first(&ifp->illif_avl_by_ppa);
tmp_ill != NULL; tmp_ill = avl_walk(&ifp->illif_avl_by_ppa,
tmp_ill, AVL_AFTER)) {
ppa = (int)(uintptr_t)vmem_xalloc(ifp->illif_ppa_arena,
1,
1,
0,
0,
(void *)((uintptr_t)tmp_ill->ill_ppa + 1),
(void *)((uintptr_t)tmp_ill->ill_ppa + 2),
VM_NOSLEEP|VM_FIRSTFIT);
if (ppa == 0) {
ip1dbg(("ill_alloc_ppa: ppa allocation"
" failed while switching"));
vmem_destroy(ifp->illif_ppa_arena);
ifp->illif_ppa_arena = NULL;
break;
}
}
}
if (ifp->illif_ppa_arena != NULL) {
if (ill->ill_ppa == UINT_MAX) {
ppa = (int)(uintptr_t)vmem_alloc(ifp->illif_ppa_arena,
1, VM_NOSLEEP|VM_FIRSTFIT);
if (ppa == 0)
return (EAGAIN);
ill->ill_ppa = --ppa;
} else {
ppa = (int)(uintptr_t)vmem_xalloc(ifp->illif_ppa_arena,
1,
1,
0,
0,
(void *)(uintptr_t)(ill->ill_ppa + 1),
(void *)(uintptr_t)(ill->ill_ppa + 2),
VM_NOSLEEP|VM_FIRSTFIT);
if (ppa == 0)
return (EEXIST);
}
return (0);
}
if (ill->ill_ppa == UINT_MAX) {
end = UINT_MAX - 1;
start = 0;
} else {
end = start = ill->ill_ppa;
}
tmp_ill = avl_find(&ifp->illif_avl_by_ppa, (void *)&start, NULL);
while (tmp_ill != NULL && tmp_ill->ill_ppa == start) {
if (start++ >= end) {
if (ill->ill_ppa == UINT_MAX)
return (EAGAIN);
else
return (EEXIST);
}
tmp_ill = avl_walk(&ifp->illif_avl_by_ppa, tmp_ill, AVL_AFTER);
}
ill->ill_ppa = start;
return (0);
}
static int
ill_glist_insert(ill_t *ill, char *name, boolean_t isv6)
{
ill_if_t *ill_interface;
avl_index_t where = 0;
int error;
int name_length;
int index;
boolean_t check_length = B_FALSE;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(RW_WRITE_HELD(&ipst->ips_ill_g_lock));
name_length = mi_strlen(name) + 1;
if (isv6)
index = IP_V6_G_HEAD;
else
index = IP_V4_G_HEAD;
ill_interface = IP_VX_ILL_G_LIST(index, ipst);
while (ill_interface != (ill_if_t *)&IP_VX_ILL_G_LIST(index, ipst)) {
if ((ill_interface->illif_name_len == name_length) &&
(strcmp(ill_interface->illif_name, name) == 0)) {
break;
}
ill_interface = ill_interface->illif_next;
}
if (ill_interface == (ill_if_t *)&IP_VX_ILL_G_LIST(index, ipst)) {
ill_g_head_t ghead;
ill_interface = (ill_if_t *)mi_zalloc(sizeof (ill_if_t));
if (ill_interface == NULL) {
return (ENOMEM);
}
(void) strcpy(ill_interface->illif_name, name);
ill_interface->illif_name_len = name_length;
avl_create(&ill_interface->illif_avl_by_ppa,
ill_compare_ppa, sizeof (ill_t),
offsetof(struct ill_s, ill_avl_byppa));
ghead = ipst->ips_ill_g_heads[index];
insque(ill_interface, ghead.ill_g_list_tail);
}
if (ill->ill_ppa == UINT_MAX)
check_length = B_TRUE;
error = ill_alloc_ppa(ill_interface, ill);
if (error != 0) {
if (avl_numnodes(&ill_interface->illif_avl_by_ppa) == 0)
ill_delete_interface_type(ill->ill_ifptr);
return (error);
}
if (check_length) {
char buf[sizeof (uint_t) * 3];
numtos(ill->ill_ppa, buf);
if ((mi_strlen(name) + mi_strlen(buf) + 1) > LIFNAMSIZ) {
if (ill_interface->illif_ppa_arena != NULL) {
vmem_free(ill_interface->illif_ppa_arena,
(void *)(uintptr_t)(ill->ill_ppa+1), 1);
}
if (avl_numnodes(&ill_interface->illif_avl_by_ppa) == 0)
ill_delete_interface_type(ill->ill_ifptr);
return (EINVAL);
}
}
(void) sprintf(ill->ill_name, "%s%u", name, ill->ill_ppa);
ill->ill_name_length = mi_strlen(ill->ill_name) + 1;
(void) avl_find(&ill_interface->illif_avl_by_ppa, &ill->ill_ppa,
&where);
ill->ill_ifptr = ill_interface;
avl_insert(&ill_interface->illif_avl_by_ppa, ill, where);
ill_phyint_reinit(ill);
return (0);
}
static boolean_t
ipsq_init(ill_t *ill, boolean_t enter)
{
ipsq_t *ipsq;
ipxop_t *ipx;
if ((ipsq = kmem_zalloc(sizeof (ipsq_t), KM_NOSLEEP)) == NULL)
return (B_FALSE);
ill->ill_phyint->phyint_ipsq = ipsq;
ipx = ipsq->ipsq_xop = &ipsq->ipsq_ownxop;
ipx->ipx_ipsq = ipsq;
ipsq->ipsq_next = ipsq;
ipsq->ipsq_phyint = ill->ill_phyint;
mutex_init(&ipsq->ipsq_lock, NULL, MUTEX_DEFAULT, 0);
mutex_init(&ipx->ipx_lock, NULL, MUTEX_DEFAULT, 0);
ipsq->ipsq_ipst = ill->ill_ipst;
if (enter) {
ipx->ipx_writer = curthread;
ipx->ipx_forced = B_FALSE;
ipx->ipx_reentry_cnt = 1;
#ifdef DEBUG
ipx->ipx_depth = getpcstack(ipx->ipx_stack, IPX_STACK_DEPTH);
#endif
}
return (B_TRUE);
}
static int
ill_init_common(ill_t *ill, queue_t *q, boolean_t isv6, boolean_t is_loopback,
boolean_t ipsq_enter)
{
int count;
uchar_t *frag_ptr;
mutex_init(&ill->ill_lock, NULL, MUTEX_DEFAULT, 0);
mutex_init(&ill->ill_saved_ire_lock, NULL, MUTEX_DEFAULT, NULL);
ill->ill_saved_ire_cnt = 0;
if (is_loopback) {
ill->ill_max_frag = isv6 ? ip_loopback_mtu_v6plus :
ip_loopback_mtuplus;
ill->ill_net_type = IRE_LOOPBACK;
} else {
ill->ill_rq = q;
ill->ill_wq = WR(q);
ill->ill_ppa = UINT_MAX;
}
ill->ill_isv6 = isv6;
frag_ptr = (uchar_t *)mi_zalloc(ILL_FRAG_HASH_TBL_SIZE + 2 * LIFNAMSIZ);
if (frag_ptr == NULL)
return (ENOMEM);
ill->ill_frag_ptr = frag_ptr;
ill->ill_frag_free_num_pkts = 0;
ill->ill_last_frag_clean_time = 0;
ill->ill_frag_hash_tbl = (ipfb_t *)frag_ptr;
ill->ill_name = (char *)(frag_ptr + ILL_FRAG_HASH_TBL_SIZE);
for (count = 0; count < ILL_FRAG_HASH_TBL_COUNT; count++) {
mutex_init(&ill->ill_frag_hash_tbl[count].ipfb_lock,
NULL, MUTEX_DEFAULT, NULL);
}
ill->ill_phyint = (phyint_t *)mi_zalloc(sizeof (phyint_t));
if (ill->ill_phyint == NULL) {
mi_free(frag_ptr);
return (ENOMEM);
}
mutex_init(&ill->ill_phyint->phyint_lock, NULL, MUTEX_DEFAULT, 0);
if (isv6) {
ill->ill_phyint->phyint_illv6 = ill;
} else {
ill->ill_phyint->phyint_illv4 = ill;
}
if (is_loopback) {
phyint_flags_init(ill->ill_phyint, DL_LOOP);
}
list_create(&ill->ill_nce, sizeof (nce_t), offsetof(nce_t, nce_node));
ill_set_inputfn(ill);
if (!ipsq_init(ill, ipsq_enter)) {
mi_free(frag_ptr);
mi_free(ill->ill_phyint);
return (ENOMEM);
}
ill->ill_frag_count = 0;
ill->ill_ipf_gen = 0;
rw_init(&ill->ill_mcast_lock, NULL, RW_DEFAULT, NULL);
mutex_init(&ill->ill_mcast_serializer, NULL, MUTEX_DEFAULT, NULL);
ill->ill_global_timer = INFINITY;
ill->ill_mcast_v1_time = ill->ill_mcast_v2_time = 0;
ill->ill_mcast_v1_tset = ill->ill_mcast_v2_tset = 0;
ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
ill->ill_mcast_qi = MCAST_DEF_QUERY_INTERVAL;
ill->ill_reachable_time = ND_REACHABLE_TIME;
ill->ill_xmit_count = ND_MAX_MULTICAST_SOLICIT;
ill->ill_max_buf = ND_MAX_Q;
ill->ill_refcnt = 0;
return (0);
}
int
ill_init(queue_t *q, ill_t *ill)
{
int ret;
dl_info_req_t *dlir;
mblk_t *info_mp;
info_mp = allocb(MAX(sizeof (dl_info_req_t), sizeof (dl_info_ack_t)),
BPRI_HI);
if (info_mp == NULL)
return (ENOMEM);
if ((ret = ill_init_common(ill, q, B_FALSE, B_FALSE, B_TRUE)) != 0) {
freemsg(info_mp);
return (ret);
}
ill->ill_state_flags |= ILL_LL_SUBNET_PENDING;
info_mp->b_datap->db_type = M_PCPROTO;
dlir = (dl_info_req_t *)info_mp->b_rptr;
info_mp->b_wptr = (uchar_t *)&dlir[1];
dlir->dl_primitive = DL_INFO_REQ;
ill->ill_dlpi_pending = DL_PRIM_INVAL;
qprocson(q);
ill_dlpi_send(ill, info_mp);
return (0);
}
int
ill_dls_info(struct sockaddr_dl *sdl, const ill_t *ill)
{
size_t len;
sdl->sdl_family = AF_LINK;
sdl->sdl_index = ill_get_upper_ifindex(ill);
sdl->sdl_type = ill->ill_type;
ill_get_name(ill, sdl->sdl_data, sizeof (sdl->sdl_data));
len = strlen(sdl->sdl_data);
ASSERT(len < 256);
sdl->sdl_nlen = (uchar_t)len;
sdl->sdl_alen = ill->ill_phys_addr_length;
sdl->sdl_slen = 0;
if (ill->ill_phys_addr_length != 0 && ill->ill_phys_addr != NULL)
bcopy(ill->ill_phys_addr, &sdl->sdl_data[len], sdl->sdl_alen);
return (sizeof (struct sockaddr_dl));
}
static int
ill_xarp_info(struct sockaddr_dl *sdl, ill_t *ill)
{
sdl->sdl_family = AF_LINK;
sdl->sdl_index = ill->ill_phyint->phyint_ifindex;
sdl->sdl_type = ill->ill_type;
ill_get_name(ill, sdl->sdl_data, sizeof (sdl->sdl_data));
sdl->sdl_nlen = (uchar_t)mi_strlen(sdl->sdl_data);
sdl->sdl_alen = ill->ill_phys_addr_length;
sdl->sdl_slen = 0;
return (sdl->sdl_nlen);
}
static int
loopback_kstat_update(kstat_t *ksp, int rw)
{
kstat_named_t *kn;
netstackid_t stackid;
netstack_t *ns;
ip_stack_t *ipst;
if (ksp == NULL || ksp->ks_data == NULL)
return (EIO);
if (rw == KSTAT_WRITE)
return (EACCES);
kn = KSTAT_NAMED_PTR(ksp);
stackid = (zoneid_t)(uintptr_t)ksp->ks_private;
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (-1);
ipst = ns->netstack_ip;
if (ipst == NULL) {
netstack_rele(ns);
return (-1);
}
kn[0].value.ui32 = ipst->ips_loopback_packets;
kn[1].value.ui32 = ipst->ips_loopback_packets;
netstack_rele(ns);
return (0);
}
static boolean_t
phyint_exists(uint_t index, ip_stack_t *ipst)
{
ASSERT(index != 0);
ASSERT(RW_LOCK_HELD(&ipst->ips_ill_g_lock));
return (avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
&index, NULL) != NULL);
}
boolean_t
ip_assign_ifindex(uint_t *indexp, ip_stack_t *ipst)
{
uint_t loops;
if (!ipst->ips_ill_index_wrap) {
*indexp = ipst->ips_ill_index++;
if (ipst->ips_ill_index > IF_INDEX_MAX) {
ipst->ips_ill_index_wrap = B_TRUE;
}
return (B_TRUE);
}
if (ipst->ips_ill_index > IF_INDEX_MAX)
ipst->ips_ill_index = 1;
for (loops = IF_INDEX_MAX; loops > 0; loops--) {
if (!phyint_exists(ipst->ips_ill_index, ipst)) {
*indexp = ipst->ips_ill_index;
return (B_TRUE);
}
ipst->ips_ill_index++;
if (ipst->ips_ill_index > IF_INDEX_MAX)
ipst->ips_ill_index = 1;
}
return (B_FALSE);
}
static boolean_t
phyint_assign_ifindex(phyint_t *phyi, ip_stack_t *ipst)
{
ASSERT(phyi->phyint_ifindex == 0);
return (ip_assign_ifindex(&phyi->phyint_ifindex, ipst));
}
static void
phyint_flags_init(phyint_t *phyi, t_uscalar_t mactype)
{
uint64_t flags = 0;
if (mactype == SUNW_DL_IPMP)
flags |= PHYI_FAILED;
else
flags |= PHYI_RUNNING;
switch (mactype) {
case SUNW_DL_VNI:
flags |= PHYI_VIRTUAL;
break;
case SUNW_DL_IPMP:
flags |= PHYI_IPMP;
break;
case DL_LOOP:
flags |= (PHYI_LOOPBACK | PHYI_VIRTUAL);
break;
}
mutex_enter(&phyi->phyint_lock);
phyi->phyint_flags |= flags;
mutex_exit(&phyi->phyint_lock);
}
ill_t *
ill_lookup_on_name(char *name, boolean_t do_alloc, boolean_t isv6,
boolean_t *did_alloc, ip_stack_t *ipst)
{
ill_t *ill;
ipif_t *ipif;
ipsq_t *ipsq;
kstat_named_t *kn;
boolean_t isloopback;
in6_addr_t ov6addr;
isloopback = mi_strcmp(name, ipif_loopback_name) == 0;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ill_find_by_name(name, isv6, ipst);
rw_exit(&ipst->ips_ill_g_lock);
if (ill != NULL)
return (ill);
if (!isloopback || !do_alloc)
return (NULL);
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
ill = ill_find_by_name(name, isv6, ipst);
if (ill != NULL) {
rw_exit(&ipst->ips_ill_g_lock);
return (ill);
}
ill = (ill_t *)(mi_alloc(sizeof (ill_t) +
sizeof (ipif_loopback_name), BPRI_MED));
if (ill == NULL)
goto done;
bzero(ill, sizeof (*ill));
ill->ill_ipst = ipst;
netstack_hold(ipst->ips_netstack);
ill->ill_zoneid = GLOBAL_ZONEID;
if (ill_init_common(ill, NULL, isv6, B_TRUE, B_FALSE) != 0)
goto done;
if (!ill_allocate_mibs(ill))
goto done;
ill->ill_current_frag = ill->ill_max_frag;
ill->ill_mtu = ill->ill_max_frag;
ill->ill_mc_mtu = ill->ill_mtu;
ill->ill_name = (char *)ill + sizeof (*ill);
(void) strcpy(ill->ill_name, ipif_loopback_name);
ill->ill_name_length = sizeof (ipif_loopback_name);
ill->ill_dlpi_pending = DL_PRIM_INVAL;
ipif = ipif_allocate(ill, 0L, IRE_LOOPBACK, B_TRUE, B_TRUE, NULL);
if (ipif == NULL)
goto done;
ill->ill_flags = ILLF_MULTICAST;
ov6addr = ipif->ipif_v6lcl_addr;
if (!isv6) {
ipaddr_t inaddr_loopback = htonl(INADDR_LOOPBACK);
IN6_IPADDR_TO_V4MAPPED(inaddr_loopback, &ipif->ipif_v6lcl_addr);
V4MASK_TO_V6(htonl(IN_CLASSA_NET), ipif->ipif_v6net_mask);
V6_MASK_COPY(ipif->ipif_v6lcl_addr, ipif->ipif_v6net_mask,
ipif->ipif_v6subnet);
ill->ill_flags |= ILLF_IPV4;
} else {
ipif->ipif_v6lcl_addr = ipv6_loopback;
ipif->ipif_v6net_mask = ipv6_all_ones;
V6_MASK_COPY(ipif->ipif_v6lcl_addr, ipif->ipif_v6net_mask,
ipif->ipif_v6subnet);
ill->ill_flags |= ILLF_IPV6;
}
ill_refhold(ill);
ipsq = ill->ill_phyint->phyint_ipsq;
if (ill_glist_insert(ill, "lo", isv6) != 0)
cmn_err(CE_PANIC, "cannot insert loopback interface");
sctp_update_ill(ill, SCTP_ILL_INSERT);
sctp_update_ipif_addr(ipif, ov6addr);
ip_rts_newaddrmsg(RTM_CHGADDR, 0, ipif, RTSQ_DEFAULT);
if (ipsq != ill->ill_phyint->phyint_ipsq)
ipsq_delete(ipsq);
if (ipst->ips_loopback_ksp == NULL) {
ipst->ips_loopback_ksp = kstat_create_netstack("lo", 0,
ipif_loopback_name, "net",
KSTAT_TYPE_NAMED, 2, 0,
ipst->ips_netstack->netstack_stackid);
if (ipst->ips_loopback_ksp != NULL) {
ipst->ips_loopback_ksp->ks_update =
loopback_kstat_update;
kn = KSTAT_NAMED_PTR(ipst->ips_loopback_ksp);
kstat_named_init(&kn[0], "ipackets", KSTAT_DATA_UINT32);
kstat_named_init(&kn[1], "opackets", KSTAT_DATA_UINT32);
ipst->ips_loopback_ksp->ks_private =
(void *)(uintptr_t)ipst->ips_netstack->
netstack_stackid;
kstat_install(ipst->ips_loopback_ksp);
}
}
*did_alloc = B_TRUE;
rw_exit(&ipst->ips_ill_g_lock);
ill_nic_event_dispatch(ill, MAP_IPIF_ID(ill->ill_ipif->ipif_id),
NE_PLUMB, ill->ill_name, ill->ill_name_length);
return (ill);
done:
if (ill != NULL) {
if (ill->ill_phyint != NULL) {
ipsq = ill->ill_phyint->phyint_ipsq;
if (ipsq != NULL) {
ipsq->ipsq_phyint = NULL;
ipsq_delete(ipsq);
}
mi_free(ill->ill_phyint);
}
ill_free_mib(ill);
if (ill->ill_ipst != NULL)
netstack_rele(ill->ill_ipst->ips_netstack);
mi_free(ill);
}
rw_exit(&ipst->ips_ill_g_lock);
return (NULL);
}
ill_t *
ill_lookup_on_ifindex_global_instance(uint_t index, boolean_t isv6)
{
ip_stack_t *ipst;
ill_t *ill;
netstack_t *ns;
ns = netstack_find_by_stackid(GLOBAL_NETSTACKID);
if ((ipst = ns->netstack_ip) == NULL) {
cmn_err(CE_WARN, "No ip_stack_t for zoneid zero!\n");
netstack_rele(ns);
return (NULL);
}
ill = ill_lookup_on_ifindex(index, isv6, ipst);
netstack_rele(ns);
return (ill);
}
ill_t *
ill_lookup_on_ifindex(uint_t index, boolean_t isv6, ip_stack_t *ipst)
{
ill_t *ill;
phyint_t *phyi;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
(void *) &index, NULL);
if (phyi != NULL) {
ill = isv6 ? phyi->phyint_illv6: phyi->phyint_illv4;
if (ill != NULL) {
mutex_enter(&ill->ill_lock);
if (!ILL_IS_CONDEMNED(ill)) {
ill_refhold_locked(ill);
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
return (ill);
}
mutex_exit(&ill->ill_lock);
}
}
rw_exit(&ipst->ips_ill_g_lock);
return (NULL);
}
boolean_t
ip_xmit_ifindex_valid(uint_t ifindex, zoneid_t zoneid, boolean_t isv6,
ip_stack_t *ipst)
{
ill_t *ill;
if (ifindex == 0)
return (B_TRUE);
ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid, isv6, ipst);
if (ill == NULL)
return (B_FALSE);
if (IS_VNI(ill)) {
ill_refrele(ill);
return (B_FALSE);
}
ill_refrele(ill);
return (B_TRUE);
}
uint_t
ill_get_next_ifindex(uint_t index, boolean_t isv6, ip_stack_t *ipst)
{
phyint_t *phyi;
phyint_t *phyi_initial;
uint_t ifindex;
phyi_initial = NULL;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
if (index == 0) {
phyi = avl_first(
&ipst->ips_phyint_g_list->phyint_list_avl_by_index);
} else {
phyi = phyi_initial = avl_find(
&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
(void *) &index, NULL);
}
for (; phyi != NULL;
phyi = avl_walk(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
phyi, AVL_AFTER)) {
if (!((index != 0) && (phyi == phyi_initial))) {
if (isv6) {
if ((phyi->phyint_illv6) &&
ILL_CAN_LOOKUP(phyi->phyint_illv6) &&
(phyi->phyint_illv6->ill_isv6 == 1))
break;
} else {
if ((phyi->phyint_illv4) &&
ILL_CAN_LOOKUP(phyi->phyint_illv4) &&
(phyi->phyint_illv4->ill_isv6 == 0))
break;
}
}
}
rw_exit(&ipst->ips_ill_g_lock);
if (phyi != NULL)
ifindex = phyi->phyint_ifindex;
else
ifindex = 0;
return (ifindex);
}
uint_t
ill_get_ifindex_by_name(char *name, ip_stack_t *ipst)
{
phyint_t *phyi;
avl_index_t where = 0;
uint_t ifindex;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
if ((phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_name,
name, &where)) == NULL) {
rw_exit(&ipst->ips_ill_g_lock);
return (0);
}
ifindex = phyi->phyint_ifindex;
rw_exit(&ipst->ips_ill_g_lock);
return (ifindex);
}
uint_t
ill_get_upper_ifindex(const ill_t *ill)
{
if (IS_UNDER_IPMP(ill))
return (ipmp_ill_get_ipmp_ifindex(ill));
else
return (ill->ill_phyint->phyint_ifindex);
}
void
ill_refhold(ill_t *ill)
{
mutex_enter(&ill->ill_lock);
ill->ill_refcnt++;
ILL_TRACE_REF(ill);
mutex_exit(&ill->ill_lock);
}
void
ill_refhold_locked(ill_t *ill)
{
ASSERT(MUTEX_HELD(&ill->ill_lock));
ill->ill_refcnt++;
ILL_TRACE_REF(ill);
}
boolean_t
ill_check_and_refhold(ill_t *ill)
{
mutex_enter(&ill->ill_lock);
if (!ILL_IS_CONDEMNED(ill)) {
ill_refhold_locked(ill);
mutex_exit(&ill->ill_lock);
return (B_TRUE);
}
mutex_exit(&ill->ill_lock);
return (B_FALSE);
}
void
ill_refrele(ill_t *ill)
{
mutex_enter(&ill->ill_lock);
ASSERT(ill->ill_refcnt != 0);
ill->ill_refcnt--;
ILL_UNTRACE_REF(ill);
if (ill->ill_refcnt != 0) {
mutex_exit(&ill->ill_lock);
return;
}
ipif_ill_refrele_tail(ill);
}
boolean_t
ill_waiter_inc(ill_t *ill)
{
mutex_enter(&ill->ill_lock);
if (ill->ill_state_flags & ILL_CONDEMNED) {
mutex_exit(&ill->ill_lock);
return (B_FALSE);
}
ill->ill_waiters++;
mutex_exit(&ill->ill_lock);
return (B_TRUE);
}
void
ill_waiter_dcr(ill_t *ill)
{
mutex_enter(&ill->ill_lock);
ill->ill_waiters--;
if (ill->ill_waiters == 0)
cv_broadcast(&ill->ill_cv);
mutex_exit(&ill->ill_lock);
}
void
ip_ll_subnet_defaults(ill_t *ill, mblk_t *mp)
{
uchar_t *brdcst_addr;
uint_t brdcst_addr_length, phys_addr_length;
t_scalar_t sap_length;
dl_info_ack_t *dlia;
ip_m_t *ipm;
dl_qos_cl_sel1_t *sel1;
int min_mtu;
ASSERT(IAM_WRITER_ILL(ill));
dlia = (dl_info_ack_t *)mp->b_rptr;
ill->ill_mactype = dlia->dl_mac_type;
ipm = ip_m_lookup(dlia->dl_mac_type);
if (ipm == NULL) {
ipm = ip_m_lookup(DL_OTHER);
ASSERT(ipm != NULL);
}
ill->ill_media = ipm;
if (dlia->dl_version == DL_VERSION_2) {
brdcst_addr_length = dlia->dl_brdcst_addr_length;
brdcst_addr = mi_offset_param(mp, dlia->dl_brdcst_addr_offset,
brdcst_addr_length);
if (brdcst_addr == NULL) {
brdcst_addr_length = 0;
}
sap_length = dlia->dl_sap_length;
phys_addr_length = dlia->dl_addr_length - ABS(sap_length);
ip1dbg(("ip: bcast_len %d, sap_len %d, phys_len %d\n",
brdcst_addr_length, sap_length, phys_addr_length));
} else {
brdcst_addr_length = 6;
brdcst_addr = ip_six_byte_all_ones;
sap_length = -2;
phys_addr_length = brdcst_addr_length;
}
ill->ill_bcast_addr_length = brdcst_addr_length;
ill->ill_phys_addr_length = phys_addr_length;
ill->ill_sap_length = sap_length;
min_mtu = ill->ill_isv6 ? IPV6_MIN_MTU : IP_MIN_MTU;
ill->ill_max_frag = MAX(min_mtu, dlia->dl_max_sdu);
ill->ill_current_frag = ill->ill_max_frag;
ill->ill_mtu = ill->ill_max_frag;
ill->ill_mc_mtu = ill->ill_mtu;
ill->ill_type = ipm->ip_m_type;
if (!ill->ill_dlpi_style_set) {
if (dlia->dl_provider_style == DL_STYLE2)
ill->ill_needs_attach = 1;
phyint_flags_init(ill->ill_phyint, ill->ill_mactype);
(void) ipif_allocate(ill, 0, IRE_LOCAL,
dlia->dl_provider_style != DL_STYLE2, B_TRUE, NULL);
mutex_enter(&ill->ill_lock);
ASSERT(ill->ill_dlpi_style_set == 0);
ill->ill_dlpi_style_set = 1;
ill->ill_state_flags &= ~ILL_LL_SUBNET_PENDING;
cv_broadcast(&ill->ill_cv);
mutex_exit(&ill->ill_lock);
freemsg(mp);
return;
}
ASSERT(ill->ill_ipif != NULL);
ill->ill_sap = (ill->ill_isv6) ? ipm->ip_m_ipv6sap : ipm->ip_m_ipv4sap;
ill->ill_flags &= ~(ILLF_MULTICAST | ILLF_NONUD | ILLF_NOARP);
ill->ill_ipif->ipif_flags &= ~(IPIF_BROADCAST | IPIF_POINTOPOINT);
if (ill->ill_bcast_addr_length == 0 && !IS_IPMP(ill)) {
if (ill->ill_bcast_mp != NULL)
freemsg(ill->ill_bcast_mp);
ill->ill_net_type = IRE_IF_NORESOLVER;
ill->ill_bcast_mp = ill_dlur_gen(NULL,
ill->ill_phys_addr_length,
ill->ill_sap,
ill->ill_sap_length);
if (ill->ill_isv6)
ill->ill_flags |= ILLF_NONUD;
else
ill->ill_flags |= ILLF_NOARP;
if (ill->ill_mactype == SUNW_DL_VNI) {
ill->ill_ipif->ipif_flags |= IPIF_NOXMIT;
} else if (ill->ill_phys_addr_length == 0 ||
ill->ill_mactype == DL_IPV4 ||
ill->ill_mactype == DL_IPV6) {
ill->ill_flags |= ILLF_MULTICAST;
ill->ill_ipif->ipif_flags |= IPIF_POINTOPOINT;
}
} else {
ill->ill_net_type = IRE_IF_RESOLVER;
if (ill->ill_bcast_mp != NULL)
freemsg(ill->ill_bcast_mp);
ill->ill_bcast_mp = ill_dlur_gen(brdcst_addr,
ill->ill_bcast_addr_length, ill->ill_sap,
ill->ill_sap_length);
ill->ill_flags |= ILLF_MULTICAST;
if (!ill->ill_isv6)
ill->ill_ipif->ipif_flags |= IPIF_BROADCAST;
}
if (ill->ill_mactype == SUNW_DL_IPMP)
ASSERT(ill->ill_phyint->phyint_flags & PHYI_IPMP);
ill->ill_flags &= ~ILLF_COS_ENABLED;
sel1 = (dl_qos_cl_sel1_t *)mi_offset_param(mp, dlia->dl_qos_offset,
dlia->dl_qos_length);
if ((sel1 != NULL) && (sel1->dl_qos_type == DL_QOS_CL_SEL1)) {
ill->ill_flags |= ILLF_COS_ENABLED;
}
ill->ill_error = 0;
freemsg(mp);
}
static boolean_t
ip_addr_ok_v4(ipaddr_t addr, ipaddr_t subnet_mask)
{
ipaddr_t net_mask;
if ((net_mask = ip_net_mask(addr)) == 0)
return (B_FALSE);
if (subnet_mask != 0)
net_mask = subnet_mask;
if ((net_mask != ~(ipaddr_t)0) && ((addr == (addr & net_mask)) ||
(addr == (addr | ~net_mask)))) {
return (B_FALSE);
}
if (addr == INADDR_BROADCAST)
return (B_FALSE);
if (CLASSD(addr))
return (B_FALSE);
return (B_TRUE);
}
#define V6_IPIF_LINKLOCAL(p) \
IN6_IS_ADDR_LINKLOCAL(&(p)->ipif_v6lcl_addr)
static boolean_t
ipif_comp_multi(ipif_t *old_ipif, ipif_t *new_ipif, boolean_t isv6)
{
if (IS_LOOPBACK(old_ipif->ipif_ill)) {
if (IS_LOOPBACK(new_ipif->ipif_ill))
return (B_FALSE);
else
return (B_TRUE);
}
if (isv6 && V6_IPIF_LINKLOCAL(old_ipif)) {
if (IS_LOOPBACK(new_ipif->ipif_ill) ||
V6_IPIF_LINKLOCAL(new_ipif)) {
return (B_FALSE);
} else {
return (B_TRUE);
}
}
if (old_ipif->ipif_flags & IPIF_POINTOPOINT) {
if (IS_LOOPBACK(new_ipif->ipif_ill) ||
(isv6 && V6_IPIF_LINKLOCAL(new_ipif)) ||
(new_ipif->ipif_flags & IPIF_POINTOPOINT)) {
return (B_FALSE);
} else {
return (B_TRUE);
}
}
return (B_FALSE);
}
static ipif_t *
ipif_lookup_multicast(ip_stack_t *ipst, zoneid_t zoneid, boolean_t isv6)
{
ill_t *ill;
ill_walk_context_t ctx;
ipif_t *ipif;
ipif_t *saved_ipif = NULL;
ipif_t *dep_ipif = NULL;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
if (isv6)
ill = ILL_START_WALK_V6(&ctx, ipst);
else
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
mutex_enter(&ill->ill_lock);
if (IS_VNI(ill) || IS_UNDER_IPMP(ill) ||
ILL_IS_CONDEMNED(ill) ||
!(ill->ill_flags & ILLF_MULTICAST)) {
mutex_exit(&ill->ill_lock);
continue;
}
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (zoneid != ipif->ipif_zoneid &&
zoneid != ALL_ZONES &&
ipif->ipif_zoneid != ALL_ZONES) {
continue;
}
if (!(ipif->ipif_flags & IPIF_UP) ||
IPIF_IS_CONDEMNED(ipif)) {
continue;
}
if (ipif->ipif_flags & IPIF_DEPRECATED) {
if (dep_ipif == NULL) {
dep_ipif = ipif;
} else if (ipif_comp_multi(dep_ipif, ipif,
isv6)) {
if (dep_ipif->ipif_ill != ill)
ipif_refrele(dep_ipif);
dep_ipif = ipif;
}
continue;
}
if (saved_ipif == NULL) {
saved_ipif = ipif;
} else {
if (ipif_comp_multi(saved_ipif, ipif, isv6)) {
if (saved_ipif->ipif_ill != ill)
ipif_refrele(saved_ipif);
saved_ipif = ipif;
}
}
}
if (saved_ipif != NULL && saved_ipif->ipif_ill == ill)
ipif_refhold_locked(saved_ipif);
if (dep_ipif != NULL && dep_ipif->ipif_ill == ill)
ipif_refhold_locked(dep_ipif);
mutex_exit(&ill->ill_lock);
}
rw_exit(&ipst->ips_ill_g_lock);
if (saved_ipif != NULL) {
if (dep_ipif != NULL) {
if (ipif_comp_multi(saved_ipif, dep_ipif, isv6)) {
ipif_refrele(saved_ipif);
return (dep_ipif);
} else {
ipif_refrele(dep_ipif);
return (saved_ipif);
}
}
return (saved_ipif);
} else {
return (dep_ipif);
}
}
ill_t *
ill_lookup_multicast(ip_stack_t *ipst, zoneid_t zoneid, boolean_t isv6)
{
ipif_t *ipif;
ill_t *ill;
ipif = ipif_lookup_multicast(ipst, zoneid, isv6);
if (ipif == NULL)
return (NULL);
ill = ipif->ipif_ill;
ill_refhold(ill);
ipif_refrele(ipif);
return (ill);
}
ill_t *
ill_lookup_group_v4(ipaddr_t group, zoneid_t zoneid, ip_stack_t *ipst,
boolean_t *multirtp, ipaddr_t *setsrcp)
{
ill_t *ill;
ill = ire_lookup_multi_ill_v4(group, zoneid, ipst, multirtp, setsrcp);
if (ill != NULL)
return (ill);
return (ill_lookup_multicast(ipst, zoneid, B_FALSE));
}
ipif_t *
ipif_lookup_interface(ipaddr_t if_addr, ipaddr_t dst, ip_stack_t *ipst)
{
ipif_t *ipif;
ill_t *ill;
ill_walk_context_t ctx;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if ((ipif->ipif_flags & IPIF_POINTOPOINT) &&
(ipif->ipif_lcl_addr == if_addr) &&
(ipif->ipif_pp_dst_addr == dst)) {
if (!IPIF_IS_CONDEMNED(ipif)) {
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
return (ipif);
}
}
}
mutex_exit(&ill->ill_lock);
}
rw_exit(&ipst->ips_ill_g_lock);
ipif = ipif_lookup_addr(if_addr, NULL, ALL_ZONES, ipst);
ASSERT(ipif == NULL || !ipif->ipif_isv6);
return (ipif);
}
static ipif_t *
ipif_lookup_addr_common(ipaddr_t addr, ill_t *match_ill, uint32_t match_flags,
zoneid_t zoneid, ip_stack_t *ipst)
{
ipif_t *ipif;
ill_t *ill;
boolean_t ptp = B_FALSE;
ill_walk_context_t ctx;
boolean_t match_illgrp = (match_flags & IPIF_MATCH_ILLGRP);
boolean_t no_duplicate = (match_flags & IPIF_MATCH_NONDUP);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
repeat:
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (match_ill != NULL && ill != match_ill &&
(!match_illgrp || !IS_IN_SAME_ILLGRP(ill, match_ill))) {
continue;
}
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (zoneid != ALL_ZONES &&
zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
if (no_duplicate && !(ipif->ipif_flags & IPIF_UP))
continue;
if ((!ptp && (ipif->ipif_lcl_addr == addr) &&
((ipif->ipif_flags & IPIF_UNNUMBERED) == 0)) ||
(ptp && (ipif->ipif_flags & IPIF_POINTOPOINT) &&
(ipif->ipif_pp_dst_addr == addr))) {
if (!IPIF_IS_CONDEMNED(ipif)) {
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
return (ipif);
}
}
}
mutex_exit(&ill->ill_lock);
}
if (ptp) {
rw_exit(&ipst->ips_ill_g_lock);
return (NULL);
}
ptp = B_TRUE;
goto repeat;
}
ipif_t *
ipif_lookup_addr(ipaddr_t addr, ill_t *match_ill, zoneid_t zoneid,
ip_stack_t *ipst)
{
return (ipif_lookup_addr_common(addr, match_ill, IPIF_MATCH_ILLGRP,
zoneid, ipst));
}
ipif_t *
ipif_lookup_addr_nondup(ipaddr_t addr, ill_t *match_ill, zoneid_t zoneid,
ip_stack_t *ipst)
{
return (ipif_lookup_addr_common(addr, match_ill,
(IPIF_MATCH_ILLGRP | IPIF_MATCH_NONDUP),
zoneid, ipst));
}
ipif_t *
ipif_lookup_addr_exact(ipaddr_t addr, ill_t *match_ill, ip_stack_t *ipst)
{
ASSERT(match_ill != NULL);
return (ipif_lookup_addr_common(addr, match_ill, 0, ALL_ZONES,
ipst));
}
zoneid_t
ipif_lookup_addr_zoneid(ipaddr_t addr, ill_t *match_ill, ip_stack_t *ipst)
{
zoneid_t zoneid;
ipif_t *ipif;
ill_t *ill;
boolean_t ptp = B_FALSE;
ill_walk_context_t ctx;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
repeat:
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (match_ill != NULL && ill != match_ill &&
!IS_IN_SAME_ILLGRP(ill, match_ill)) {
continue;
}
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if ((!ptp && (ipif->ipif_lcl_addr == addr) &&
((ipif->ipif_flags & IPIF_UNNUMBERED) == 0)) ||
(ptp && (ipif->ipif_flags & IPIF_POINTOPOINT) &&
(ipif->ipif_pp_dst_addr == addr)) &&
!(ipif->ipif_state_flags & IPIF_CONDEMNED)) {
zoneid = ipif->ipif_zoneid;
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
if (zoneid == ALL_ZONES)
zoneid = GLOBAL_ZONEID;
return (zoneid);
}
}
mutex_exit(&ill->ill_lock);
}
if (ptp) {
rw_exit(&ipst->ips_ill_g_lock);
return (ALL_ZONES);
}
ptp = B_TRUE;
goto repeat;
}
ipif_t *
ipif_lookup_remote(ill_t *ill, ipaddr_t addr, zoneid_t zoneid)
{
ipif_t *ipif;
ASSERT(!ill->ill_isv6);
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (IPIF_IS_CONDEMNED(ipif))
continue;
if (zoneid != ALL_ZONES && zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
if (ipif->ipif_flags & IPIF_POINTOPOINT) {
if ((ipif->ipif_pp_dst_addr == addr) ||
(!(ipif->ipif_flags & IPIF_UNNUMBERED) &&
ipif->ipif_lcl_addr == addr)) {
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
return (ipif);
}
} else if (ipif->ipif_subnet == (addr & ipif->ipif_net_mask)) {
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
return (ipif);
}
}
mutex_exit(&ill->ill_lock);
ipif = ipif_get_next_ipif(NULL, ill);
return (ipif);
}
static boolean_t
ill_is_quiescent(ill_t *ill)
{
ipif_t *ipif;
ASSERT(MUTEX_HELD(&ill->ill_lock));
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (ipif->ipif_refcnt != 0)
return (B_FALSE);
}
if (!ILL_DOWN_OK(ill) || ill->ill_refcnt != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
boolean_t
ill_is_freeable(ill_t *ill)
{
ipif_t *ipif;
ASSERT(MUTEX_HELD(&ill->ill_lock));
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (ipif->ipif_refcnt != 0) {
return (B_FALSE);
}
}
if (!ILL_FREE_OK(ill) || ill->ill_refcnt != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
ipif_is_quiescent(ipif_t *ipif)
{
ill_t *ill;
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
if (ipif->ipif_refcnt != 0)
return (B_FALSE);
ill = ipif->ipif_ill;
if (ill->ill_ipif_up_count != 0 || ill->ill_ipif_dup_count != 0 ||
ill->ill_logical_down) {
return (B_TRUE);
}
if (ill->ill_ire_cnt != 0 || ill->ill_refcnt != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
ipif_is_freeable(ipif_t *ipif)
{
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
ASSERT(ipif->ipif_id != 0);
return (ipif->ipif_refcnt == 0);
}
void
ipif_ill_refrele_tail(ill_t *ill)
{
mblk_t *mp;
conn_t *connp;
ipsq_t *ipsq;
ipxop_t *ipx;
ipif_t *ipif;
dl_notify_ind_t *dlindp;
ASSERT(MUTEX_HELD(&ill->ill_lock));
if ((ill->ill_state_flags & ILL_CONDEMNED) && ill_is_freeable(ill)) {
cv_broadcast(&ill->ill_cv);
}
ipsq = ill->ill_phyint->phyint_ipsq;
mutex_enter(&ipsq->ipsq_lock);
ipx = ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
if (ipx->ipx_waitfor == 0)
goto unlock;
ASSERT(ipx->ipx_pending_mp != NULL && ipx->ipx_pending_ipif != NULL);
ipif = ipx->ipx_pending_ipif;
if (ipif->ipif_ill != ill)
goto unlock;
switch (ipx->ipx_waitfor) {
case IPIF_DOWN:
if (!ipif_is_quiescent(ipif))
goto unlock;
break;
case IPIF_FREE:
if (!ipif_is_freeable(ipif))
goto unlock;
break;
case ILL_DOWN:
if (!ill_is_quiescent(ill))
goto unlock;
break;
case ILL_FREE:
if (!ill_is_freeable(ill))
goto unlock;
break;
default:
cmn_err(CE_PANIC, "ipsq: %p unknown ipx_waitfor %d\n",
(void *)ipsq, ipx->ipx_waitfor);
}
ill_refhold_locked(ill);
mutex_exit(&ipx->ipx_lock);
mp = ipsq_pending_mp_get(ipsq, &connp);
mutex_exit(&ipsq->ipsq_lock);
mutex_exit(&ill->ill_lock);
ASSERT(mp != NULL);
switch (mp->b_datap->db_type) {
case M_PCPROTO:
case M_PROTO:
dlindp = (dl_notify_ind_t *)mp->b_rptr;
ASSERT(dlindp->dl_primitive == DL_NOTIFY_IND);
switch (dlindp->dl_notification) {
case DL_NOTE_PHYS_ADDR:
qwriter_ip(ill, ill->ill_rq, mp,
ill_set_phys_addr_tail, CUR_OP, B_TRUE);
return;
case DL_NOTE_REPLUMB:
qwriter_ip(ill, ill->ill_rq, mp,
ill_replumb_tail, CUR_OP, B_TRUE);
return;
default:
ASSERT(0);
ill_refrele(ill);
}
break;
case M_ERROR:
case M_HANGUP:
qwriter_ip(ill, ill->ill_rq, mp, ipif_all_down_tail, CUR_OP,
B_TRUE);
return;
case M_IOCTL:
case M_IOCDATA:
qwriter_ip(ill, (connp != NULL ? CONNP_TO_WQ(connp) :
ill->ill_wq), mp, ip_reprocess_ioctl, CUR_OP, B_TRUE);
return;
default:
cmn_err(CE_PANIC, "ipif_ill_refrele_tail mp %p "
"db_type %d\n", (void *)mp, mp->b_datap->db_type);
}
return;
unlock:
mutex_exit(&ipsq->ipsq_lock);
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ill->ill_lock);
}
#ifdef DEBUG
static void
th_trace_rrecord(th_trace_t *th_trace)
{
tr_buf_t *tr_buf;
uint_t lastref;
lastref = th_trace->th_trace_lastref;
lastref++;
if (lastref == TR_BUF_MAX)
lastref = 0;
th_trace->th_trace_lastref = lastref;
tr_buf = &th_trace->th_trbuf[lastref];
tr_buf->tr_time = ddi_get_lbolt();
tr_buf->tr_depth = getpcstack(tr_buf->tr_stack, TR_STACK_DEPTH);
}
static void
th_trace_free(void *value)
{
th_trace_t *th_trace = value;
ASSERT(th_trace->th_refcnt == 0);
kmem_free(th_trace, sizeof (*th_trace));
}
static mod_hash_t *
th_trace_gethash(ip_stack_t *ipst)
{
th_hash_t *thh;
if ((thh = tsd_get(ip_thread_data)) == NULL && ipst != NULL) {
mod_hash_t *mh;
char name[256];
size_t objsize, rshift;
int retv;
if ((thh = kmem_alloc(sizeof (*thh), KM_NOSLEEP)) == NULL)
return (NULL);
(void) snprintf(name, sizeof (name), "th_trace_%p",
(void *)curthread);
objsize = MAX(MAX(sizeof (ill_t), sizeof (ipif_t)),
MAX(sizeof (ire_t), sizeof (ncec_t)));
rshift = highbit(objsize);
mh = mod_hash_create_extended(name, 64, mod_hash_null_keydtor,
th_trace_free, mod_hash_byptr, (void *)rshift,
mod_hash_ptrkey_cmp, KM_NOSLEEP);
if (mh == NULL) {
kmem_free(thh, sizeof (*thh));
return (NULL);
}
thh->thh_hash = mh;
thh->thh_ipst = ipst;
rw_enter(&ip_thread_rwlock, RW_WRITER);
list_insert_tail(&ip_thread_list, thh);
rw_exit(&ip_thread_rwlock);
retv = tsd_set(ip_thread_data, thh);
ASSERT(retv == 0);
}
return (thh != NULL ? thh->thh_hash : NULL);
}
boolean_t
th_trace_ref(const void *obj, ip_stack_t *ipst)
{
th_trace_t *th_trace;
mod_hash_t *mh;
mod_hash_val_t val;
if ((mh = th_trace_gethash(ipst)) == NULL)
return (B_FALSE);
if (mod_hash_find(mh, (mod_hash_key_t)obj, &val) == MH_ERR_NOTFOUND) {
th_trace = kmem_zalloc(sizeof (th_trace_t), KM_NOSLEEP);
if (th_trace == NULL)
return (B_FALSE);
th_trace->th_id = curthread;
if (mod_hash_insert(mh, (mod_hash_key_t)obj,
(mod_hash_val_t)th_trace) != 0) {
kmem_free(th_trace, sizeof (th_trace_t));
return (B_FALSE);
}
} else {
th_trace = (th_trace_t *)val;
}
ASSERT(th_trace->th_refcnt >= 0 &&
th_trace->th_refcnt < TR_BUF_MAX - 1);
th_trace->th_refcnt++;
th_trace_rrecord(th_trace);
return (B_TRUE);
}
void
th_trace_unref(const void *obj)
{
int retv;
mod_hash_t *mh;
th_trace_t *th_trace;
mod_hash_val_t val;
mh = th_trace_gethash(NULL);
retv = mod_hash_find(mh, (mod_hash_key_t)obj, &val);
ASSERT(retv == 0);
th_trace = (th_trace_t *)val;
ASSERT(th_trace->th_refcnt > 0);
th_trace->th_refcnt--;
th_trace_rrecord(th_trace);
}
void
th_trace_cleanup(const void *obj, boolean_t trace_disable)
{
th_hash_t *thh;
mod_hash_t *mh;
mod_hash_val_t val;
th_trace_t *th_trace;
int retv;
rw_enter(&ip_thread_rwlock, RW_READER);
for (thh = list_head(&ip_thread_list); thh != NULL;
thh = list_next(&ip_thread_list, thh)) {
if (mod_hash_find(mh = thh->thh_hash, (mod_hash_key_t)obj,
&val) == 0) {
th_trace = (th_trace_t *)val;
if (trace_disable)
th_trace->th_refcnt = 0;
retv = mod_hash_destroy(mh, (mod_hash_key_t)obj);
ASSERT(retv == 0);
}
}
rw_exit(&ip_thread_rwlock);
}
void
ipif_trace_ref(ipif_t *ipif)
{
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
if (ipif->ipif_trace_disable)
return;
if (!th_trace_ref(ipif, ipif->ipif_ill->ill_ipst)) {
ipif->ipif_trace_disable = B_TRUE;
ipif_trace_cleanup(ipif);
}
}
void
ipif_untrace_ref(ipif_t *ipif)
{
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
if (!ipif->ipif_trace_disable)
th_trace_unref(ipif);
}
void
ill_trace_ref(ill_t *ill)
{
ASSERT(MUTEX_HELD(&ill->ill_lock));
if (ill->ill_trace_disable)
return;
if (!th_trace_ref(ill, ill->ill_ipst)) {
ill->ill_trace_disable = B_TRUE;
ill_trace_cleanup(ill);
}
}
void
ill_untrace_ref(ill_t *ill)
{
ASSERT(MUTEX_HELD(&ill->ill_lock));
if (!ill->ill_trace_disable)
th_trace_unref(ill);
}
static void
ipif_trace_cleanup(const ipif_t *ipif)
{
th_trace_cleanup(ipif, ipif->ipif_trace_disable);
}
static void
ill_trace_cleanup(const ill_t *ill)
{
th_trace_cleanup(ill, ill->ill_trace_disable);
}
#endif
void
ipif_refhold_locked(ipif_t *ipif)
{
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
ipif->ipif_refcnt++;
IPIF_TRACE_REF(ipif);
}
void
ipif_refhold(ipif_t *ipif)
{
ill_t *ill;
ill = ipif->ipif_ill;
mutex_enter(&ill->ill_lock);
ipif->ipif_refcnt++;
IPIF_TRACE_REF(ipif);
mutex_exit(&ill->ill_lock);
}
void
ipif_refrele(ipif_t *ipif)
{
ill_t *ill;
ill = ipif->ipif_ill;
mutex_enter(&ill->ill_lock);
ASSERT(ipif->ipif_refcnt != 0);
ipif->ipif_refcnt--;
IPIF_UNTRACE_REF(ipif);
if (ipif->ipif_refcnt != 0) {
mutex_exit(&ill->ill_lock);
return;
}
ipif_ill_refrele_tail(ill);
}
ipif_t *
ipif_get_next_ipif(ipif_t *curr, ill_t *ill)
{
ipif_t *ipif;
mutex_enter(&ill->ill_lock);
for (ipif = (curr == NULL ? ill->ill_ipif : curr->ipif_next);
ipif != NULL; ipif = ipif->ipif_next) {
if (IPIF_IS_CONDEMNED(ipif))
continue;
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
return (ipif);
}
mutex_exit(&ill->ill_lock);
return (NULL);
}
static ip_m_t *
ip_m_lookup(t_uscalar_t mac_type)
{
ip_m_t *ipm;
for (ipm = ip_m_tbl; ipm < A_END(ip_m_tbl); ipm++)
if (ipm->ip_m_mac_type == mac_type)
return (ipm);
return (NULL);
}
void
ip_mcast_mapping(ill_t *ill, uchar_t *addr, uchar_t *hwaddr)
{
ip_m_t *ipm;
if (ill->ill_net_type == IRE_IF_NORESOLVER)
return;
ASSERT(addr != NULL);
ipm = ip_m_lookup(ill->ill_mactype);
if (ipm == NULL ||
(ill->ill_isv6 && ipm->ip_m_v6mapping == NULL) ||
(!ill->ill_isv6 && ipm->ip_m_v4mapping == NULL)) {
ip0dbg(("no mapping for ill %s mactype 0x%x\n",
ill->ill_name, ill->ill_mactype));
return;
}
if (ill->ill_isv6)
(*ipm->ip_m_v6mapping)(ill, addr, hwaddr);
else
(*ipm->ip_m_v4mapping)(ill, addr, hwaddr);
}
static boolean_t
ip_contiguous_mask(uint32_t mask)
{
uint32_t m = mask;
int i;
for (i = 1; i < 32; i++)
m |= (mask << i);
return (m == mask);
}
int
ip_rt_add(ipaddr_t dst_addr, ipaddr_t mask, ipaddr_t gw_addr,
ipaddr_t src_addr, int flags, ill_t *ill, ire_t **ire_arg,
boolean_t ioctl_msg, struct rtsa_s *sp, ip_stack_t *ipst, zoneid_t zoneid)
{
ire_t *ire, *nire;
ire_t *gw_ire = NULL;
ipif_t *ipif = NULL;
uint_t type;
int match_flags = MATCH_IRE_TYPE;
tsol_gc_t *gc = NULL;
tsol_gcgrp_t *gcgrp = NULL;
boolean_t gcgrp_xtraref = B_FALSE;
boolean_t cgtp_broadcast;
boolean_t unbound = B_FALSE;
ip1dbg(("ip_rt_add:"));
if (ire_arg != NULL)
*ire_arg = NULL;
if (!ip_contiguous_mask(ntohl(mask)))
return (ENOTSUP);
if (flags & RTF_HOST)
mask = IP_HOST_MASK;
if (gw_addr == 0)
return (ENETUNREACH);
if (ill != NULL)
ipif = ipif_lookup_addr(gw_addr, ill, ALL_ZONES, ipst);
else
ipif = ipif_lookup_interface(gw_addr, dst_addr, ipst);
if (ipif != NULL) {
if (IS_VNI(ipif->ipif_ill)) {
ipif_refrele(ipif);
return (EINVAL);
}
}
if ((ipif != NULL) && (ipif->ipif_ire_type == IRE_LOOPBACK)) {
flags &= ~RTF_GATEWAY;
if (gw_addr == INADDR_LOOPBACK && dst_addr == INADDR_LOOPBACK &&
mask == IP_HOST_MASK) {
ire = ire_ftable_lookup_v4(dst_addr, 0, 0, IRE_LOOPBACK,
NULL, ALL_ZONES, NULL, MATCH_IRE_TYPE, 0, ipst,
NULL);
if (ire != NULL) {
ire_refrele(ire);
ipif_refrele(ipif);
return (EEXIST);
}
ip1dbg(("ip_rt_add: 0x%p creating IRE 0x%x"
"for 0x%x\n", (void *)ipif,
ipif->ipif_ire_type,
ntohl(ipif->ipif_lcl_addr)));
ire = ire_create(
(uchar_t *)&dst_addr,
(uchar_t *)&mask,
NULL,
ipif->ipif_ire_type,
ipif->ipif_ill,
zoneid,
(ipif->ipif_flags & IPIF_PRIVATE) ? RTF_PRIVATE : 0,
NULL,
ipst);
if (ire == NULL) {
ipif_refrele(ipif);
return (ENOMEM);
}
if ((src_addr != INADDR_ANY) && (flags & RTF_SETSRC))
ire->ire_setsrc_addr = src_addr;
nire = ire_add(ire);
if (nire == NULL) {
ipif_refrele(ipif);
return (ENOMEM);
}
if (nire != ire) {
ASSERT(nire->ire_identical_ref > 1);
ire_delete(nire);
ire_refrele(nire);
ipif_refrele(ipif);
return (EEXIST);
}
ire = nire;
goto save_ire;
}
}
if ((flags & RTF_MULTIRT) && ipif != NULL)
flags &= ~RTF_GATEWAY;
if (!(flags & RTF_GATEWAY)) {
if (sp != NULL) {
ip2dbg(("ip_rt_add: gateway security attributes "
"cannot be set with interface route\n"));
if (ipif != NULL)
ipif_refrele(ipif);
return (EINVAL);
}
if (ipif == NULL)
return (ENETUNREACH);
match_flags |= MATCH_IRE_GW | MATCH_IRE_ILL;
if (ill == NULL) {
ill = ipif->ipif_ill;
} else if (ill != ipif->ipif_ill) {
ipif_refrele(ipif);
return (EINVAL);
}
if (!ioctl_msg)
match_flags |= MATCH_IRE_MASK;
ire = ire_ftable_lookup_v4(dst_addr, mask, gw_addr,
IRE_INTERFACE, ill, ALL_ZONES, NULL, match_flags, 0, ipst,
NULL);
if (ire != NULL) {
ire_refrele(ire);
ipif_refrele(ipif);
return (EEXIST);
}
type = ill->ill_net_type;
if (type == IRE_LOOPBACK) {
type = IRE_IF_NORESOLVER;
flags |= RTF_BLACKHOLE;
}
ire = ire_create(
(uchar_t *)&dst_addr,
(uint8_t *)&mask,
(uint8_t *)&gw_addr,
type,
ill,
zoneid,
flags,
NULL,
ipst);
if (ire == NULL) {
ipif_refrele(ipif);
return (ENOMEM);
}
if ((src_addr != INADDR_ANY) && (flags & RTF_SETSRC))
ire->ire_setsrc_addr = src_addr;
nire = ire_add(ire);
if (nire == NULL) {
ipif_refrele(ipif);
return (ENOMEM);
}
if (nire != ire) {
ire_delete(nire);
ire_refrele(nire);
ipif_refrele(ipif);
return (EEXIST);
}
ire = nire;
goto save_ire;
}
if (ill != NULL)
match_flags |= MATCH_IRE_ILL;
again:
type = IRE_INTERFACE | IRE_LOCAL | IRE_LOOPBACK;
if (flags & RTF_INDIRECT)
type |= IRE_OFFLINK;
gw_ire = ire_ftable_lookup_v4(gw_addr, 0, 0, type, ill,
ALL_ZONES, NULL, match_flags, 0, ipst, NULL);
if (gw_ire == NULL) {
if (!(match_flags & MATCH_IRE_TESTHIDDEN)) {
match_flags |= MATCH_IRE_TESTHIDDEN;
goto again;
}
if (ipif != NULL)
ipif_refrele(ipif);
return (ENETUNREACH);
}
if (gw_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) {
ire_refrele(gw_ire);
if (ipif != NULL)
ipif_refrele(ipif);
return (ENETUNREACH);
}
if (ill == NULL && !(flags & RTF_INDIRECT)) {
unbound = B_TRUE;
if (ipst->ips_ip_strict_src_multihoming > 0)
ill = gw_ire->ire_ill;
}
if (mask == IP_HOST_MASK)
type = IRE_HOST;
else if (mask == 0)
type = IRE_DEFAULT;
else
type = IRE_PREFIX;
ire = ire_ftable_lookup_v4(dst_addr, mask, gw_addr, type, ill,
ALL_ZONES, NULL, match_flags | MATCH_IRE_MASK | MATCH_IRE_GW,
0, ipst, NULL);
if (ire != NULL) {
if (ipif != NULL)
ipif_refrele(ipif);
ire_refrele(gw_ire);
ire_refrele(ire);
return (EEXIST);
}
if (sp != NULL) {
tsol_gcgrp_addr_t ga;
ga.ga_af = AF_INET;
IN6_IPADDR_TO_V4MAPPED(gw_addr, &ga.ga_addr);
gcgrp = gcgrp_lookup(&ga, B_TRUE);
if (gcgrp == NULL) {
if (ipif != NULL)
ipif_refrele(ipif);
ire_refrele(gw_ire);
return (ENOMEM);
}
gc = gc_create(sp, gcgrp, &gcgrp_xtraref);
if (gc == NULL) {
if (ipif != NULL)
ipif_refrele(ipif);
GCGRP_REFRELE(gcgrp);
ire_refrele(gw_ire);
return (ENOMEM);
}
}
ire = ire_create(
(uchar_t *)&dst_addr,
(uchar_t *)&mask,
(uchar_t *)&gw_addr,
(ushort_t)type,
ill,
zoneid,
flags,
gc,
ipst);
if (gcgrp_xtraref)
GCGRP_REFRELE(gcgrp);
if (ire == NULL) {
if (gc != NULL)
GC_REFRELE(gc);
if (ipif != NULL)
ipif_refrele(ipif);
ire_refrele(gw_ire);
return (ENOMEM);
}
cgtp_broadcast = ((flags & RTF_MULTIRT) &&
ip_type_v4(ire->ire_addr, ipst) == IRE_BROADCAST);
if ((src_addr != INADDR_ANY) && (flags & RTF_SETSRC))
ire->ire_setsrc_addr = src_addr;
ire->ire_unbound = unbound;
nire = ire_add(ire);
if (nire == NULL) {
if (ipif != NULL)
ipif_refrele(ipif);
ire_refrele(gw_ire);
return (ENOMEM);
}
if (nire != ire) {
ire_delete(nire);
ire_refrele(nire);
if (ipif != NULL)
ipif_refrele(ipif);
ire_refrele(gw_ire);
return (EEXIST);
}
ire = nire;
if (flags & RTF_MULTIRT) {
if (cgtp_broadcast) {
ip_cgtp_bcast_add(ire, ipst);
goto save_ire;
}
if (ipst->ips_ip_cgtp_filter_ops != NULL &&
!CLASSD(ire->ire_addr)) {
int res;
ipif_t *src_ipif;
src_ipif = ipif_lookup_addr(gw_ire->ire_gateway_addr,
NULL, zoneid, ipst);
if (src_ipif != NULL) {
res = ipst->ips_ip_cgtp_filter_ops->
cfo_add_dest_v4(
ipst->ips_netstack->netstack_stackid,
ire->ire_addr,
ire->ire_gateway_addr,
ire->ire_setsrc_addr,
src_ipif->ipif_lcl_addr);
ipif_refrele(src_ipif);
} else {
res = EADDRNOTAVAIL;
}
if (res != 0) {
if (ipif != NULL)
ipif_refrele(ipif);
ire_refrele(gw_ire);
ire_delete(ire);
ire_refrele(ire);
return (res);
}
}
}
save_ire:
if (gw_ire != NULL) {
ire_refrele(gw_ire);
gw_ire = NULL;
}
if (ill != NULL) {
ill_save_ire(ill, ire);
}
if (ioctl_msg)
ip_rts_rtmsg(RTM_OLDADD, ire, 0, ipst);
if (ire_arg != NULL) {
*ire_arg = ire;
} else {
ire_refrele(ire);
}
if (ipif != NULL)
ipif_refrele(ipif);
return (0);
}
int
ip_rt_delete(ipaddr_t dst_addr, ipaddr_t mask, ipaddr_t gw_addr,
uint_t rtm_addrs, int flags, ill_t *ill, boolean_t ioctl_msg,
ip_stack_t *ipst, zoneid_t zoneid)
{
ire_t *ire = NULL;
ipif_t *ipif;
uint_t type;
uint_t match_flags = MATCH_IRE_TYPE;
int err = 0;
ip1dbg(("ip_rt_delete:"));
if (flags & RTF_HOST) {
mask = IP_HOST_MASK;
match_flags |= MATCH_IRE_MASK;
} else if (rtm_addrs & RTA_NETMASK) {
match_flags |= MATCH_IRE_MASK;
}
ipif = ipif_lookup_interface(gw_addr, dst_addr, ipst);
if (ipif != NULL) {
ill_t *ill_match;
if (ill != NULL)
ill_match = ill;
else
ill_match = ipif->ipif_ill;
match_flags |= MATCH_IRE_ILL;
if (ipif->ipif_ire_type == IRE_LOOPBACK) {
ire = ire_ftable_lookup_v4(dst_addr, mask, 0,
IRE_LOOPBACK, ill_match, ALL_ZONES, NULL,
match_flags, 0, ipst, NULL);
}
if (ire == NULL) {
match_flags |= MATCH_IRE_GW;
ire = ire_ftable_lookup_v4(dst_addr, mask, gw_addr,
IRE_INTERFACE, ill_match, ALL_ZONES, NULL,
match_flags, 0, ipst, NULL);
}
if (ire != NULL && (ire->ire_flags & RTF_KERNEL)) {
ire_refrele(ire);
ire = NULL;
}
match_flags &= ~(MATCH_IRE_GW|MATCH_IRE_ILL);
}
if (ire == NULL) {
match_flags |= MATCH_IRE_GW;
if (ill != NULL)
match_flags |= MATCH_IRE_ILL;
if (mask == IP_HOST_MASK)
type = IRE_HOST;
else if (mask == 0)
type = IRE_DEFAULT;
else
type = IRE_PREFIX;
ire = ire_ftable_lookup_v4(dst_addr, mask, gw_addr, type, ill,
ALL_ZONES, NULL, match_flags, 0, ipst, NULL);
}
if (ipif != NULL) {
ipif_refrele(ipif);
ipif = NULL;
}
if (ire == NULL)
return (ESRCH);
if (ire->ire_flags & RTF_MULTIRT) {
if (ipst->ips_ip_cgtp_filter_ops != NULL) {
err = ipst->ips_ip_cgtp_filter_ops->cfo_del_dest_v4(
ipst->ips_netstack->netstack_stackid,
ire->ire_addr, ire->ire_gateway_addr);
}
ip_cgtp_bcast_delete(ire, ipst);
}
ill = ire->ire_ill;
if (ill != NULL)
ill_remove_saved_ire(ill, ire);
if (ioctl_msg)
ip_rts_rtmsg(RTM_OLDDEL, ire, 0, ipst);
ire_delete(ire);
ire_refrele(ire);
return (err);
}
int
ip_siocaddrt(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_if_req)
{
ipaddr_t dst_addr;
ipaddr_t gw_addr;
ipaddr_t mask;
int error = 0;
mblk_t *mp1;
struct rtentry *rt;
ipif_t *ipif = NULL;
ip_stack_t *ipst;
ASSERT(q->q_next == NULL);
ipst = CONNQ_TO_IPST(q);
ip1dbg(("ip_siocaddrt:"));
mp1 = mp->b_cont->b_cont;
rt = (struct rtentry *)mp1->b_rptr;
dst_addr = ((sin_t *)&rt->rt_dst)->sin_addr.s_addr;
gw_addr = ((sin_t *)&rt->rt_gateway)->sin_addr.s_addr;
if (rt->rt_flags & RTF_HOST) {
mask = IP_HOST_MASK;
} else {
mask = ip_subnet_mask(dst_addr, &ipif, ipst);
}
error = ip_rt_add(dst_addr, mask, gw_addr, 0, rt->rt_flags, NULL, NULL,
B_TRUE, NULL, ipst, ALL_ZONES);
if (ipif != NULL)
ipif_refrele(ipif);
return (error);
}
int
ip_siocdelrt(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_if_req)
{
ipaddr_t dst_addr;
ipaddr_t gw_addr;
ipaddr_t mask;
int error;
mblk_t *mp1;
struct rtentry *rt;
ipif_t *ipif = NULL;
ip_stack_t *ipst;
ASSERT(q->q_next == NULL);
ipst = CONNQ_TO_IPST(q);
ip1dbg(("ip_siocdelrt:"));
mp1 = mp->b_cont->b_cont;
rt = (struct rtentry *)mp1->b_rptr;
dst_addr = ((sin_t *)&rt->rt_dst)->sin_addr.s_addr;
gw_addr = ((sin_t *)&rt->rt_gateway)->sin_addr.s_addr;
if (rt->rt_flags & RTF_HOST) {
mask = IP_HOST_MASK;
} else {
mask = ip_subnet_mask(dst_addr, &ipif, ipst);
}
error = ip_rt_delete(dst_addr, mask, gw_addr,
RTA_DST | RTA_GATEWAY | RTA_NETMASK, rt->rt_flags, NULL, B_TRUE,
ipst, ALL_ZONES);
if (ipif != NULL)
ipif_refrele(ipif);
return (error);
}
void
ipsq_enq(ipsq_t *ipsq, queue_t *q, mblk_t *mp, ipsq_func_t func, int type,
ill_t *pending_ill)
{
conn_t *connp;
ipxop_t *ipx = ipsq->ipsq_xop;
ASSERT(MUTEX_HELD(&ipsq->ipsq_lock));
ASSERT(MUTEX_HELD(&ipx->ipx_lock));
ASSERT(func != NULL);
mp->b_queue = q;
mp->b_prev = (void *)func;
mp->b_next = NULL;
switch (type) {
case CUR_OP:
if (ipx->ipx_mptail != NULL) {
ASSERT(ipx->ipx_mphead != NULL);
ipx->ipx_mptail->b_next = mp;
} else {
ASSERT(ipx->ipx_mphead == NULL);
ipx->ipx_mphead = mp;
}
ipx->ipx_mptail = mp;
break;
case NEW_OP:
if (ipsq->ipsq_xopq_mptail != NULL) {
ASSERT(ipsq->ipsq_xopq_mphead != NULL);
ipsq->ipsq_xopq_mptail->b_next = mp;
} else {
ASSERT(ipsq->ipsq_xopq_mphead == NULL);
ipsq->ipsq_xopq_mphead = mp;
}
ipsq->ipsq_xopq_mptail = mp;
ipx->ipx_ipsq_queued = B_TRUE;
break;
case SWITCH_OP:
ASSERT(ipsq->ipsq_swxop != NULL);
ASSERT(ipsq->ipsq_switch_mp == NULL);
ipsq->ipsq_switch_mp = mp;
ipx->ipx_ipsq_queued = B_TRUE;
break;
default:
cmn_err(CE_PANIC, "ipsq_enq %d type \n", type);
}
if (CONN_Q(q) && pending_ill != NULL) {
connp = Q_TO_CONN(q);
ASSERT(MUTEX_HELD(&connp->conn_lock));
connp->conn_oper_pending_ill = pending_ill;
}
}
static mblk_t *
ipsq_dq(ipsq_t *ipsq)
{
ill_t *illv4, *illv6;
mblk_t *mp;
ipsq_t *xopipsq;
ipsq_t *leftipsq = NULL;
ipxop_t *ipx;
phyint_t *phyi = ipsq->ipsq_phyint;
ip_stack_t *ipst = ipsq->ipsq_ipst;
boolean_t emptied = B_FALSE;
rw_enter(&ipst->ips_ill_g_lock,
ipsq->ipsq_swxop != NULL ? RW_WRITER : RW_READER);
mutex_enter(&ipsq->ipsq_lock);
ipx = ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
if ((mp = ipx->ipx_mphead) != NULL) {
ipx->ipx_mphead = mp->b_next;
if (ipx->ipx_mphead == NULL)
ipx->ipx_mptail = NULL;
mp->b_next = (void *)ipsq;
goto out;
}
if (ipx->ipx_current_ipif != NULL)
goto empty;
if (ipsq->ipsq_swxop != NULL) {
ASSERT(phyi == NULL || !(phyi->phyint_flags & PHYI_IPMP));
ASSERT(RW_WRITE_HELD(&ipst->ips_ill_g_lock));
DTRACE_PROBE1(ipsq__switch, (ipsq_t *), ipsq);
if (ipsq->ipsq_swxop == &ipsq->ipsq_ownxop) {
ASSERT(ipsq->ipsq_xop != &ipsq->ipsq_ownxop);
xopipsq = ipx->ipx_ipsq;
while (xopipsq->ipsq_next != ipsq)
xopipsq = xopipsq->ipsq_next;
xopipsq->ipsq_next = ipsq->ipsq_next;
ipsq->ipsq_next = ipsq;
ipsq->ipsq_xop = ipsq->ipsq_swxop;
ipsq->ipsq_swxop = NULL;
leftipsq = xopipsq;
mutex_exit(&ipx->ipx_lock);
ipx = ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
ASSERT(ipx->ipx_writer == NULL);
ASSERT(ipx->ipx_current_ipif == NULL);
} else {
ASSERT(ipsq->ipsq_xop == &ipsq->ipsq_ownxop);
xopipsq = ipsq->ipsq_swxop->ipx_ipsq;
while (xopipsq->ipsq_next != ipsq->ipsq_swxop->ipx_ipsq)
xopipsq = xopipsq->ipsq_next;
xopipsq->ipsq_next = ipsq;
ipsq->ipsq_next = ipsq->ipsq_swxop->ipx_ipsq;
ipsq->ipsq_xop = ipsq->ipsq_swxop;
ipsq->ipsq_swxop = NULL;
ASSERT(ipx->ipx_writer == curthread);
ipx->ipx_writer = NULL;
VERIFY(--ipx->ipx_reentry_cnt == 0);
ipx->ipx_ipsq_queued = B_FALSE;
mutex_exit(&ipx->ipx_lock);
ipx = ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
if (ipx->ipx_writer != NULL ||
ipx->ipx_current_ipif != NULL) {
goto out;
}
}
ASSERT(ipx->ipx_current_ipif == NULL);
ASSERT(ipx->ipx_mphead == NULL && ipx->ipx_mptail == NULL);
VERIFY(ipx->ipx_reentry_cnt++ == 0);
ipx->ipx_writer = curthread;
ipx->ipx_forced = B_FALSE;
#ifdef DEBUG
ipx->ipx_depth = getpcstack(ipx->ipx_stack, IPX_STACK_DEPTH);
#endif
}
xopipsq = ipsq;
do {
if ((mp = xopipsq->ipsq_switch_mp) != NULL) {
xopipsq->ipsq_switch_mp = NULL;
ASSERT(mp->b_next == NULL);
mp->b_next = (void *)xopipsq;
goto out;
}
if ((mp = xopipsq->ipsq_xopq_mphead) != NULL) {
xopipsq->ipsq_xopq_mphead = mp->b_next;
if (xopipsq->ipsq_xopq_mphead == NULL)
xopipsq->ipsq_xopq_mptail = NULL;
mp->b_next = (void *)xopipsq;
goto out;
}
} while ((xopipsq = xopipsq->ipsq_next) != ipsq);
empty:
ipx->ipx_writer = NULL;
ipx->ipx_forced = B_FALSE;
VERIFY(--ipx->ipx_reentry_cnt == 0);
ipx->ipx_ipsq_queued = B_FALSE;
emptied = B_TRUE;
#ifdef DEBUG
ipx->ipx_depth = 0;
#endif
out:
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
if (emptied) {
xopipsq = ipsq;
do {
if ((phyi = xopipsq->ipsq_phyint) == NULL)
continue;
illv4 = phyi->phyint_illv4;
illv6 = phyi->phyint_illv6;
GRAB_ILL_LOCKS(illv4, illv6);
if (illv4 != NULL)
cv_broadcast(&illv4->ill_cv);
if (illv6 != NULL)
cv_broadcast(&illv6->ill_cv);
RELEASE_ILL_LOCKS(illv4, illv6);
} while ((xopipsq = xopipsq->ipsq_next) != ipsq);
}
rw_exit(&ipst->ips_ill_g_lock);
if (leftipsq != NULL)
ipsq_exit(leftipsq);
return (mp);
}
static boolean_t
ipsq_dlpi_done(ipsq_t *ipsq)
{
ipsq_t *ipsq_start;
phyint_t *phyi;
ill_t *ill;
ASSERT(RW_LOCK_HELD(&ipsq->ipsq_ipst->ips_ill_g_lock));
ipsq_start = ipsq;
do {
ASSERT(ipsq->ipsq_xop->ipx_writer == NULL);
phyi = ipsq->ipsq_phyint;
if (phyi != NULL) {
ill = phyi->phyint_illv4;
if (ill != NULL &&
(ill->ill_dlpi_pending != DL_PRIM_INVAL ||
ill->ill_arl_dlpi_pending))
return (B_FALSE);
ill = phyi->phyint_illv6;
if (ill != NULL &&
ill->ill_dlpi_pending != DL_PRIM_INVAL)
return (B_FALSE);
}
} while ((ipsq = ipsq->ipsq_next) != ipsq_start);
return (B_TRUE);
}
#define ENTER_SQ_WAIT_TICKS 100
boolean_t
ipsq_enter(ill_t *ill, boolean_t force, int type)
{
ipsq_t *ipsq;
ipxop_t *ipx;
boolean_t waited_enough = B_FALSE;
ip_stack_t *ipst = ill->ill_ipst;
for (;;) {
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
mutex_enter(&ill->ill_lock);
if (ill->ill_state_flags & ILL_CONDEMNED) {
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
return (B_FALSE);
}
ipsq = ill->ill_phyint->phyint_ipsq;
mutex_enter(&ipsq->ipsq_lock);
ipx = ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
if (ipx->ipx_writer == NULL && (type == CUR_OP ||
(ipx->ipx_current_ipif == NULL && ipsq_dlpi_done(ipsq)) ||
waited_enough))
break;
rw_exit(&ipst->ips_ill_g_lock);
if (!force || ipx->ipx_writer != NULL) {
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
cv_wait(&ill->ill_cv, &ill->ill_lock);
} else {
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
(void) cv_reltimedwait(&ill->ill_cv,
&ill->ill_lock, ENTER_SQ_WAIT_TICKS, TR_CLOCK_TICK);
waited_enough = B_TRUE;
}
mutex_exit(&ill->ill_lock);
}
ASSERT(ipx->ipx_mphead == NULL && ipx->ipx_mptail == NULL);
ASSERT(ipx->ipx_reentry_cnt == 0);
ipx->ipx_writer = curthread;
ipx->ipx_forced = (ipx->ipx_current_ipif != NULL);
ipx->ipx_reentry_cnt++;
#ifdef DEBUG
ipx->ipx_depth = getpcstack(ipx->ipx_stack, IPX_STACK_DEPTH);
#endif
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
return (B_TRUE);
}
static ipsq_t *
ipsq_try_enter_internal(ill_t *ill, queue_t *q, mblk_t *mp, ipsq_func_t func,
int type, boolean_t reentry_ok)
{
ipsq_t *ipsq;
ipxop_t *ipx;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(RW_LOCK_HELD(&ipst->ips_ill_g_lock));
GRAB_CONN_LOCK(q);
mutex_enter(&ill->ill_lock);
ipsq = ill->ill_phyint->phyint_ipsq;
mutex_enter(&ipsq->ipsq_lock);
ipx = ipsq->ipsq_xop;
mutex_enter(&ipx->ipx_lock);
if ((ipx->ipx_writer == curthread && reentry_ok) ||
(ipx->ipx_writer == NULL && (type == CUR_OP || (type == NEW_OP &&
!ipx->ipx_ipsq_queued && ipx->ipx_current_ipif == NULL &&
ipsq_dlpi_done(ipsq))))) {
ipx->ipx_reentry_cnt++;
ipx->ipx_writer = curthread;
ipx->ipx_forced = B_FALSE;
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
mutex_exit(&ill->ill_lock);
RELEASE_CONN_LOCK(q);
#ifdef DEBUG
ipx->ipx_depth = getpcstack(ipx->ipx_stack, IPX_STACK_DEPTH);
#endif
return (ipsq);
}
if (func != NULL)
ipsq_enq(ipsq, q, mp, func, type, ill);
mutex_exit(&ipx->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
mutex_exit(&ill->ill_lock);
RELEASE_CONN_LOCK(q);
return (NULL);
}
ipsq_t *
ipsq_try_enter(ipif_t *ipif, ill_t *ill, queue_t *q, mblk_t *mp,
ipsq_func_t func, int type, boolean_t reentry_ok)
{
ip_stack_t *ipst;
ipsq_t *ipsq;
ASSERT((ipif != NULL) ^ (ill != NULL));
if (ipif != NULL)
ill = ipif->ipif_ill;
ipst = ill->ill_ipst;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ipsq = ipsq_try_enter_internal(ill, q, mp, func, type, reentry_ok);
rw_exit(&ipst->ips_ill_g_lock);
return (ipsq);
}
void
qwriter_ip(ill_t *ill, queue_t *q, mblk_t *mp, ipsq_func_t func, int type,
boolean_t reentry_ok)
{
ipsq_t *ipsq;
ipsq = ipsq_try_enter(NULL, ill, q, mp, func, type, reentry_ok);
ill_refrele(ill);
if (ipsq != NULL) {
(*func)(ipsq, q, mp, NULL);
ipsq_exit(ipsq);
}
}
void
ipsq_exit(ipsq_t *ipsq)
{
mblk_t *mp;
ipsq_t *mp_ipsq;
queue_t *q;
phyint_t *phyi;
ipsq_func_t func;
ASSERT(IAM_WRITER_IPSQ(ipsq));
ASSERT(ipsq->ipsq_xop->ipx_reentry_cnt >= 1);
if (ipsq->ipsq_xop->ipx_reentry_cnt != 1) {
ipsq->ipsq_xop->ipx_reentry_cnt--;
return;
}
for (;;) {
phyi = ipsq->ipsq_phyint;
mp = ipsq_dq(ipsq);
mp_ipsq = (mp == NULL) ? NULL : (ipsq_t *)mp->b_next;
if (mp_ipsq != ipsq && phyi == NULL) {
ASSERT(ipsq->ipsq_next == ipsq);
ASSERT(ipsq->ipsq_xop == &ipsq->ipsq_ownxop);
ipsq_delete(ipsq);
}
if (mp == NULL)
break;
q = mp->b_queue;
func = (ipsq_func_t)mp->b_prev;
ipsq = mp_ipsq;
mp->b_next = mp->b_prev = NULL;
mp->b_queue = NULL;
(*func)(ipsq, q, mp, NULL);
}
}
void
ill_mcast_timer_start(ip_stack_t *ipst)
{
int next;
mutex_enter(&ipst->ips_igmp_timer_lock);
next = ipst->ips_igmp_deferred_next;
ipst->ips_igmp_deferred_next = INFINITY;
mutex_exit(&ipst->ips_igmp_timer_lock);
if (next != INFINITY)
igmp_start_timers(next, ipst);
mutex_enter(&ipst->ips_mld_timer_lock);
next = ipst->ips_mld_deferred_next;
ipst->ips_mld_deferred_next = INFINITY;
mutex_exit(&ipst->ips_mld_timer_lock);
if (next != INFINITY)
mld_start_timers(next, ipst);
}
void
ipsq_current_start(ipsq_t *ipsq, ipif_t *ipif, int ioccmd)
{
ill_t *ill = ipif->ipif_ill;
ipxop_t *ipx = ipsq->ipsq_xop;
ASSERT(IAM_WRITER_IPSQ(ipsq));
ASSERT(ipx->ipx_current_ipif == NULL);
ASSERT(ipx->ipx_current_ioctl == 0);
ipx->ipx_current_done = B_FALSE;
ipx->ipx_current_ioctl = ioccmd;
mutex_enter(&ipx->ipx_lock);
ipx->ipx_current_ipif = ipif;
mutex_exit(&ipx->ipx_lock);
switch (ioccmd) {
case SIOCLIFREMOVEIF:
break;
case 0:
mutex_enter(&ill->ill_lock);
ipif = ipif->ipif_ill->ill_ipif;
for (; ipif != NULL; ipif = ipif->ipif_next)
ipif->ipif_state_flags |= IPIF_CHANGING;
mutex_exit(&ill->ill_lock);
break;
default:
mutex_enter(&ill->ill_lock);
ipif->ipif_state_flags |= IPIF_CHANGING;
mutex_exit(&ill->ill_lock);
}
}
void
ipsq_current_finish(ipsq_t *ipsq)
{
ipxop_t *ipx = ipsq->ipsq_xop;
t_uscalar_t dlpi_pending = DL_PRIM_INVAL;
ipif_t *ipif = ipx->ipx_current_ipif;
ASSERT(IAM_WRITER_IPSQ(ipsq));
if (ipx->ipx_current_ioctl != SIOCLIFREMOVEIF) {
ill_t *ill = ipif->ipif_ill;
mutex_enter(&ill->ill_lock);
dlpi_pending = ill->ill_dlpi_pending;
if (ipx->ipx_current_ioctl == 0) {
ipif = ill->ill_ipif;
for (; ipif != NULL; ipif = ipif->ipif_next)
ipif->ipif_state_flags &= ~IPIF_CHANGING;
} else {
ipif->ipif_state_flags &= ~IPIF_CHANGING;
}
mutex_exit(&ill->ill_lock);
}
ASSERT(!ipx->ipx_current_done);
ipx->ipx_current_done = B_TRUE;
ipx->ipx_current_ioctl = 0;
if (dlpi_pending == DL_PRIM_INVAL) {
mutex_enter(&ipx->ipx_lock);
ipx->ipx_current_ipif = NULL;
mutex_exit(&ipx->ipx_lock);
}
}
static void
ipsq_flush(ill_t *ill)
{
queue_t *q;
mblk_t *prev;
mblk_t *mp;
mblk_t *mp_next;
ipxop_t *ipx = ill->ill_phyint->phyint_ipsq->ipsq_xop;
ASSERT(IAM_WRITER_ILL(ill));
mutex_enter(&ipx->ipx_lock);
for (prev = NULL, mp = ipx->ipx_mphead; mp != NULL; mp = mp_next) {
mp_next = mp->b_next;
q = mp->b_queue;
if (q == ill->ill_rq || q == ill->ill_wq) {
if (prev == NULL)
ipx->ipx_mphead = mp->b_next;
else
prev->b_next = mp->b_next;
if (ipx->ipx_mptail == mp) {
ASSERT(mp_next == NULL);
ipx->ipx_mptail = prev;
}
inet_freemsg(mp);
} else {
prev = mp;
}
}
mutex_exit(&ipx->ipx_lock);
(void) ipsq_pending_mp_cleanup(ill, NULL);
ipsq_xopq_mp_cleanup(ill, NULL);
}
int
ip_extract_lifreq(queue_t *q, mblk_t *mp, const ip_ioctl_cmd_t *ipip,
cmd_info_t *ci)
{
char *name;
struct ifreq *ifr;
struct lifreq *lifr;
ipif_t *ipif = NULL;
ill_t *ill;
conn_t *connp;
boolean_t isv6;
int err;
mblk_t *mp1;
zoneid_t zoneid;
ip_stack_t *ipst;
if (q->q_next != NULL) {
ill = (ill_t *)q->q_ptr;
isv6 = ill->ill_isv6;
connp = NULL;
zoneid = ALL_ZONES;
ipst = ill->ill_ipst;
} else {
ill = NULL;
connp = Q_TO_CONN(q);
isv6 = (connp->conn_family == AF_INET6);
zoneid = connp->conn_zoneid;
if (zoneid == GLOBAL_ZONEID) {
zoneid = ALL_ZONES;
}
ipst = connp->conn_netstack->netstack_ip;
}
mp1 = mp->b_cont->b_cont;
if (ipip->ipi_cmd_type == IF_CMD) {
ifr = (struct ifreq *)mp1->b_rptr;
ifr->ifr_name[IFNAMSIZ - 1] = '\0';
name = ifr->ifr_name;
ci->ci_sin = (sin_t *)&ifr->ifr_addr;
ci->ci_sin6 = NULL;
ci->ci_lifr = (struct lifreq *)ifr;
} else {
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
lifr = (struct lifreq *)mp1->b_rptr;
lifr->lifr_name[LIFNAMSIZ - 1] = '\0';
name = lifr->lifr_name;
ci->ci_sin = (sin_t *)&lifr->lifr_addr;
ci->ci_sin6 = (sin6_t *)&lifr->lifr_addr;
ci->ci_lifr = lifr;
}
if (ipip->ipi_cmd == SIOCSLIFNAME) {
if (ill == NULL) {
return (ENXIO);
}
ipif = ill->ill_ipif;
ipif_refhold(ipif);
} else {
ipif = ipif_lookup_on_name_async(name, mi_strlen(name),
isv6, zoneid, q, mp, ip_process_ioctl, &err, ipst);
if (ipif == NULL) {
if (err == EINPROGRESS)
return (err);
err = 0;
}
}
if (ipif != NULL && ipif->ipif_isv6 && ipip->ipi_cmd_type == IF_CMD) {
ipif_refrele(ipif);
return (ENXIO);
}
if (ipif == NULL && ill != NULL && ill->ill_ipif != NULL &&
name[0] == '\0') {
ipif = ill->ill_ipif;
ipif_refhold(ipif);
}
if (ipif == NULL)
return (ENXIO);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_extract_lifreq",
int, ipip->ipi_cmd, ill_t *, ipif->ipif_ill, ipif_t *, ipif);
ci->ci_ipif = ipif;
return (0);
}
static uint_t
ip_get_numifs(zoneid_t zoneid, ip_stack_t *ipst)
{
uint_t numifs = 0;
ill_t *ill;
ill_walk_context_t ctx;
ipif_t *ipif;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (IS_UNDER_IPMP(ill))
continue;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (ipif->ipif_zoneid == zoneid ||
ipif->ipif_zoneid == ALL_ZONES)
numifs++;
}
}
rw_exit(&ipst->ips_ill_g_lock);
return (numifs);
}
static uint_t
ip_get_numlifs(int family, int lifn_flags, zoneid_t zoneid, ip_stack_t *ipst)
{
uint_t numifs = 0;
ill_t *ill;
ipif_t *ipif;
ill_walk_context_t ctx;
ip1dbg(("ip_get_numlifs(%d %u %d)\n", family, lifn_flags, (int)zoneid));
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
if (family == AF_INET)
ill = ILL_START_WALK_V4(&ctx, ipst);
else if (family == AF_INET6)
ill = ILL_START_WALK_V6(&ctx, ipst);
else
ill = ILL_START_WALK_ALL(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (IS_UNDER_IPMP(ill) && !(lifn_flags & LIFC_UNDER_IPMP))
continue;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if ((ipif->ipif_flags & IPIF_NOXMIT) &&
!(lifn_flags & LIFC_NOXMIT))
continue;
if ((ipif->ipif_flags & IPIF_TEMPORARY) &&
!(lifn_flags & LIFC_TEMPORARY))
continue;
if (((ipif->ipif_flags &
(IPIF_NOXMIT|IPIF_NOLOCAL|
IPIF_DEPRECATED)) ||
IS_LOOPBACK(ill) ||
!(ipif->ipif_flags & IPIF_UP)) &&
(lifn_flags & LIFC_EXTERNAL_SOURCE))
continue;
if (zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES &&
(zoneid != GLOBAL_ZONEID ||
!(lifn_flags & LIFC_ALLZONES)))
continue;
numifs++;
}
}
rw_exit(&ipst->ips_ill_g_lock);
return (numifs);
}
uint_t
ip_get_lifsrcofnum(ill_t *ill)
{
uint_t numifs = 0;
ill_t *ill_head = ill;
ip_stack_t *ipst = ill->ill_ipst;
rw_enter(&ipst->ips_ill_g_usesrc_lock, RW_READER);
if ((ill->ill_usesrc_ifindex == 0) &&
(ill->ill_usesrc_grp_next != NULL)) {
for (; (ill != NULL) && (ill->ill_usesrc_grp_next != ill_head);
ill = ill->ill_usesrc_grp_next)
numifs++;
}
rw_exit(&ipst->ips_ill_g_usesrc_lock);
return (numifs);
}
int
ip_sioctl_get_ifnum(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q,
mblk_t *mp, ip_ioctl_cmd_t *ipip, void *ifreq)
{
int *nump;
conn_t *connp = Q_TO_CONN(q);
ASSERT(q->q_next == NULL);
nump = (int *)mp->b_cont->b_cont->b_rptr;
*nump = ip_get_numifs(connp->conn_zoneid,
connp->conn_netstack->netstack_ip);
ip1dbg(("ip_sioctl_get_ifnum numifs %d", *nump));
return (0);
}
int
ip_sioctl_get_lifnum(ipif_t *dummy_ipif, sin_t *dummy_sin,
queue_t *q, mblk_t *mp, ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifnum *lifn;
mblk_t *mp1;
conn_t *connp = Q_TO_CONN(q);
ASSERT(q->q_next == NULL);
mp1 = mp->b_cont->b_cont;
lifn = (struct lifnum *)mp1->b_rptr;
switch (lifn->lifn_family) {
case AF_UNSPEC:
case AF_INET:
case AF_INET6:
break;
default:
return (EAFNOSUPPORT);
}
lifn->lifn_count = ip_get_numlifs(lifn->lifn_family, lifn->lifn_flags,
connp->conn_zoneid, connp->conn_netstack->netstack_ip);
ip1dbg(("ip_sioctl_get_lifnum numifs %d", lifn->lifn_count));
return (0);
}
int
ip_sioctl_get_ifconf(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q,
mblk_t *mp, ip_ioctl_cmd_t *ipip, void *ifreq)
{
STRUCT_HANDLE(ifconf, ifc);
mblk_t *mp1;
struct iocblk *iocp;
struct ifreq *ifr;
ill_walk_context_t ctx;
ill_t *ill;
ipif_t *ipif;
struct sockaddr_in *sin;
int32_t ifclen;
zoneid_t zoneid;
ip_stack_t *ipst = CONNQ_TO_IPST(q);
ASSERT(q->q_next == NULL);
ip1dbg(("ip_sioctl_get_ifconf"));
mp1 = mp->b_cont->b_cont;
iocp = (struct iocblk *)mp->b_rptr;
zoneid = Q_TO_CONN(q)->conn_zoneid;
STRUCT_SET_HANDLE(ifc, iocp->ioc_flag, NULL);
if ((mp1->b_wptr - mp1->b_rptr) == STRUCT_SIZE(ifc)) {
int numifs = 0;
size_t ifc_bufsize;
STRUCT_SET_HANDLE(ifc, iocp->ioc_flag,
(struct ifconf *)mp1->b_rptr);
numifs = ip_get_numifs(zoneid, ipst);
ifclen = STRUCT_FGET(ifc, ifc_len);
ifc_bufsize = numifs * sizeof (struct ifreq);
if (ifc_bufsize > ifclen) {
if (iocp->ioc_cmd == O_SIOCGIFCONF) {
return (EINVAL);
} else {
ifc_bufsize = ifclen;
}
}
mp1 = mi_copyout_alloc(q, mp,
STRUCT_FGETP(ifc, ifc_buf), ifc_bufsize, B_FALSE);
if (mp1 == NULL)
return (ENOMEM);
mp1->b_wptr = mp1->b_rptr + ifc_bufsize;
}
bzero(mp1->b_rptr, mp1->b_wptr - mp1->b_rptr);
ifr = (struct ifreq *)mp1->b_rptr;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (IS_UNDER_IPMP(ill))
continue;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
if ((uchar_t *)&ifr[1] > mp1->b_wptr) {
if (iocp->ioc_cmd == O_SIOCGIFCONF) {
rw_exit(&ipst->ips_ill_g_lock);
return (EINVAL);
} else {
goto if_copydone;
}
}
ipif_get_name(ipif, ifr->ifr_name,
sizeof (ifr->ifr_name));
sin = (sin_t *)&ifr->ifr_addr;
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_lcl_addr;
ifr++;
}
}
if_copydone:
rw_exit(&ipst->ips_ill_g_lock);
mp1->b_wptr = (uchar_t *)ifr;
if (STRUCT_BUF(ifc) != NULL) {
STRUCT_FSET(ifc, ifc_len,
(int)((uchar_t *)ifr - mp1->b_rptr));
}
return (0);
}
int
ip_sioctl_get_lifsrcof(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q,
mblk_t *mp, ip_ioctl_cmd_t *ipip, void *ifreq)
{
mblk_t *mp1;
ill_t *ill, *ill_head;
ipif_t *ipif, *orig_ipif;
int numlifs = 0;
size_t lifs_bufsize, lifsmaxlen;
struct lifreq *lifr;
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
uint_t ifindex;
zoneid_t zoneid;
boolean_t isv6 = B_FALSE;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
STRUCT_HANDLE(lifsrcof, lifs);
ip_stack_t *ipst;
ipst = CONNQ_TO_IPST(q);
ASSERT(q->q_next == NULL);
zoneid = Q_TO_CONN(q)->conn_zoneid;
mp1 = mp->b_cont->b_cont;
STRUCT_SET_HANDLE(lifs, iocp->ioc_flag,
(struct lifsrcof *)mp1->b_rptr);
if (MBLKL(mp1) != STRUCT_SIZE(lifs))
return (EINVAL);
ifindex = STRUCT_FGET(lifs, lifs_ifindex);
isv6 = (Q_TO_CONN(q))->conn_family == AF_INET6;
ipif = ipif_lookup_on_ifindex(ifindex, isv6, zoneid, ipst);
if (ipif == NULL) {
ip1dbg(("ip_sioctl_get_lifsrcof: no ipif for ifindex %d\n",
ifindex));
return (ENXIO);
}
numlifs = ip_get_lifsrcofnum(ipif->ipif_ill);
lifs_bufsize = numlifs * sizeof (struct lifreq);
lifsmaxlen = STRUCT_FGET(lifs, lifs_maxlen);
STRUCT_FSET(lifs, lifs_len, lifs_bufsize);
if (lifs_bufsize > lifsmaxlen || lifs_bufsize == 0) {
ipif_refrele(ipif);
return (0);
}
mp1 = mi_copyout_alloc(q, mp,
STRUCT_FGETP(lifs, lifs_buf), lifs_bufsize, B_FALSE);
if (mp1 == NULL) {
ipif_refrele(ipif);
return (ENOMEM);
}
mp1->b_wptr = mp1->b_rptr + lifs_bufsize;
bzero(mp1->b_rptr, lifs_bufsize);
lifr = (struct lifreq *)mp1->b_rptr;
ill = ill_head = ipif->ipif_ill;
orig_ipif = ipif;
rw_enter(&ipst->ips_ill_g_usesrc_lock, RW_READER);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ill->ill_usesrc_grp_next;
for (; (ill != NULL) && (ill != ill_head);
ill = ill->ill_usesrc_grp_next) {
if ((uchar_t *)&lifr[1] > mp1->b_wptr)
break;
ipif = ill->ill_ipif;
ipif_get_name(ipif, lifr->lifr_name, sizeof (lifr->lifr_name));
if (ipif->ipif_isv6) {
sin6 = (sin6_t *)&lifr->lifr_addr;
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ipif->ipif_v6lcl_addr;
lifr->lifr_addrlen = ip_mask_to_plen_v6(
&ipif->ipif_v6net_mask);
} else {
sin = (sin_t *)&lifr->lifr_addr;
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_lcl_addr;
lifr->lifr_addrlen = ip_mask_to_plen(
ipif->ipif_net_mask);
}
lifr++;
}
rw_exit(&ipst->ips_ill_g_lock);
rw_exit(&ipst->ips_ill_g_usesrc_lock);
ipif_refrele(orig_ipif);
mp1->b_wptr = (uchar_t *)lifr;
STRUCT_FSET(lifs, lifs_len, (int)((uchar_t *)lifr - mp1->b_rptr));
return (0);
}
int
ip_sioctl_get_lifconf(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q,
mblk_t *mp, ip_ioctl_cmd_t *ipip, void *ifreq)
{
mblk_t *mp1;
int list;
ill_t *ill;
ipif_t *ipif;
int flags;
int numlifs = 0;
size_t lifc_bufsize;
struct lifreq *lifr;
sa_family_t family;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
ill_walk_context_t ctx;
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
int32_t lifclen;
zoneid_t zoneid;
STRUCT_HANDLE(lifconf, lifc);
ip_stack_t *ipst = CONNQ_TO_IPST(q);
ip1dbg(("ip_sioctl_get_lifconf"));
ASSERT(q->q_next == NULL);
zoneid = Q_TO_CONN(q)->conn_zoneid;
mp1 = mp->b_cont->b_cont;
STRUCT_SET_HANDLE(lifc, iocp->ioc_flag, NULL);
if ((mp1->b_wptr - mp1->b_rptr) != STRUCT_SIZE(lifc))
return (EINVAL);
STRUCT_SET_HANDLE(lifc, iocp->ioc_flag, (struct lifconf *)mp1->b_rptr);
family = STRUCT_FGET(lifc, lifc_family);
flags = STRUCT_FGET(lifc, lifc_flags);
switch (family) {
case AF_UNSPEC:
list = MAX_G_HEADS;
break;
case AF_INET:
list = IP_V4_G_HEAD;
break;
case AF_INET6:
list = IP_V6_G_HEAD;
break;
default:
return (EAFNOSUPPORT);
}
numlifs = ip_get_numlifs(family, flags, zoneid, ipst);
lifc_bufsize = numlifs * sizeof (struct lifreq);
lifclen = STRUCT_FGET(lifc, lifc_len);
if (lifc_bufsize > lifclen) {
if (iocp->ioc_cmd == O_SIOCGLIFCONF)
return (EINVAL);
else
lifc_bufsize = lifclen;
}
mp1 = mi_copyout_alloc(q, mp,
STRUCT_FGETP(lifc, lifc_buf), lifc_bufsize, B_FALSE);
if (mp1 == NULL)
return (ENOMEM);
mp1->b_wptr = mp1->b_rptr + lifc_bufsize;
bzero(mp1->b_rptr, mp1->b_wptr - mp1->b_rptr);
lifr = (struct lifreq *)mp1->b_rptr;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ill_first(list, list, &ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (IS_UNDER_IPMP(ill) && !(flags & LIFC_UNDER_IPMP))
continue;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if ((ipif->ipif_flags & IPIF_NOXMIT) &&
!(flags & LIFC_NOXMIT))
continue;
if ((ipif->ipif_flags & IPIF_TEMPORARY) &&
!(flags & LIFC_TEMPORARY))
continue;
if (((ipif->ipif_flags &
(IPIF_NOXMIT|IPIF_NOLOCAL|
IPIF_DEPRECATED)) ||
IS_LOOPBACK(ill) ||
!(ipif->ipif_flags & IPIF_UP)) &&
(flags & LIFC_EXTERNAL_SOURCE))
continue;
if (zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES &&
(zoneid != GLOBAL_ZONEID ||
!(flags & LIFC_ALLZONES)))
continue;
if ((uchar_t *)&lifr[1] > mp1->b_wptr) {
if (iocp->ioc_cmd == O_SIOCGLIFCONF) {
rw_exit(&ipst->ips_ill_g_lock);
return (EINVAL);
} else {
goto lif_copydone;
}
}
ipif_get_name(ipif, lifr->lifr_name,
sizeof (lifr->lifr_name));
lifr->lifr_type = ill->ill_type;
if (ipif->ipif_isv6) {
sin6 = (sin6_t *)&lifr->lifr_addr;
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr =
ipif->ipif_v6lcl_addr;
lifr->lifr_addrlen =
ip_mask_to_plen_v6(
&ipif->ipif_v6net_mask);
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
sin6->sin6_scope_id =
ill->ill_phyint->phyint_ifindex;
}
} else {
sin = (sin_t *)&lifr->lifr_addr;
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr =
ipif->ipif_lcl_addr;
lifr->lifr_addrlen =
ip_mask_to_plen(
ipif->ipif_net_mask);
}
lifr++;
}
}
lif_copydone:
rw_exit(&ipst->ips_ill_g_lock);
mp1->b_wptr = (uchar_t *)lifr;
if (STRUCT_BUF(lifc) != NULL) {
STRUCT_FSET(lifc, lifc_len,
(int)((uchar_t *)lifr - mp1->b_rptr));
}
return (0);
}
static void
ip_sioctl_ip6addrpolicy(queue_t *q, mblk_t *mp)
{
ip6_asp_t *table;
size_t table_size;
mblk_t *data_mp;
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
ip_stack_t *ipst;
if (q->q_next == NULL)
ipst = CONNQ_TO_IPST(q);
else
ipst = ILLQ_TO_IPST(q);
if (iocp->ioc_count == TRANSPARENT) {
miocnak(q, mp, 0, EINVAL);
return;
}
data_mp = mp->b_cont;
if (data_mp == NULL) {
table = NULL;
table_size = iocp->ioc_count;
} else {
if (MBLKL(data_mp) < iocp->ioc_count) {
mblk_t *new_data_mp;
if ((new_data_mp = msgpullup(data_mp, -1)) ==
NULL) {
miocnak(q, mp, 0, ENOMEM);
return;
}
freemsg(data_mp);
data_mp = new_data_mp;
mp->b_cont = data_mp;
}
table = (ip6_asp_t *)data_mp->b_rptr;
table_size = iocp->ioc_count;
}
switch (iocp->ioc_cmd) {
case SIOCGIP6ADDRPOLICY:
iocp->ioc_rval = ip6_asp_get(table, table_size, ipst);
if (iocp->ioc_rval == -1)
iocp->ioc_error = EINVAL;
#if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4
else if (table != NULL &&
(iocp->ioc_flag & IOC_MODELS) == IOC_ILP32) {
ip6_asp_t *src = table;
ip6_asp32_t *dst = (void *)table;
int count = table_size / sizeof (ip6_asp_t);
int i;
ASSERT(sizeof (*src) > sizeof (*dst));
for (i = 1; i < count; i++)
bcopy(src + i, dst + i, sizeof (*dst));
}
#endif
break;
case SIOCSIP6ADDRPOLICY:
ASSERT(mp->b_prev == NULL);
mp->b_prev = (void *)q;
#if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4
#endif
ip6_asp_replace(mp, table, table_size, B_FALSE, ipst,
iocp->ioc_flag & IOC_MODELS);
return;
}
DB_TYPE(mp) = (iocp->ioc_error == 0) ? M_IOCACK : M_IOCNAK;
qreply(q, mp);
}
static void
ip_sioctl_dstinfo(queue_t *q, mblk_t *mp)
{
mblk_t *data_mp;
struct dstinforeq *dir;
uint8_t *end, *cur;
in6_addr_t *daddr, *saddr;
ipaddr_t v4daddr;
ire_t *ire;
ipaddr_t v4setsrc;
in6_addr_t v6setsrc;
char *slabel, *dlabel;
boolean_t isipv4;
int match_ire;
ill_t *dst_ill;
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
conn_t *connp = Q_TO_CONN(q);
zoneid_t zoneid = IPCL_ZONEID(connp);
ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
uint64_t ipif_flags;
ASSERT(q->q_next == NULL);
data_mp = mp->b_cont;
if (iocp->ioc_count == TRANSPARENT || data_mp == NULL) {
miocnak(q, mp, 0, EINVAL);
return;
}
if (MBLKL(data_mp) < iocp->ioc_count) {
mblk_t *new_data_mp;
if ((new_data_mp = msgpullup(data_mp, -1)) == NULL) {
miocnak(q, mp, 0, ENOMEM);
return;
}
freemsg(data_mp);
data_mp = new_data_mp;
mp->b_cont = data_mp;
}
match_ire = MATCH_IRE_DSTONLY;
for (cur = data_mp->b_rptr, end = data_mp->b_wptr;
end - cur >= sizeof (struct dstinforeq);
cur += sizeof (struct dstinforeq)) {
dir = (struct dstinforeq *)cur;
daddr = &dir->dir_daddr;
saddr = &dir->dir_saddr;
dir->dir_dscope = ip_addr_scope_v6(daddr);
dlabel = ip6_asp_lookup(daddr, &dir->dir_precedence, ipst);
isipv4 = IN6_IS_ADDR_V4MAPPED(daddr);
if (isipv4) {
IN6_V4MAPPED_TO_IPADDR(daddr, v4daddr);
v4setsrc = INADDR_ANY;
ire = ire_route_recursive_v4(v4daddr, 0, NULL, zoneid,
NULL, match_ire, IRR_ALLOCATE, 0, ipst, &v4setsrc,
NULL, NULL);
} else {
v6setsrc = ipv6_all_zeros;
ire = ire_route_recursive_v6(daddr, 0, NULL, zoneid,
NULL, match_ire, IRR_ALLOCATE, 0, ipst, &v6setsrc,
NULL, NULL);
}
ASSERT(ire != NULL);
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
ire_refrele(ire);
dir->dir_dreachable = 0;
continue;
}
dir->dir_dreachable = 1;
dst_ill = ire_nexthop_ill(ire);
if (dst_ill == NULL) {
ire_refrele(ire);
continue;
}
dir->dir_dmactype = dst_ill->ill_mactype;
if (isipv4) {
ipaddr_t v4saddr;
if (ip_select_source_v4(dst_ill, v4setsrc, v4daddr,
connp->conn_ixa->ixa_multicast_ifaddr, zoneid, ipst,
&v4saddr, NULL, &ipif_flags) != 0) {
v4saddr = INADDR_ANY;
ipif_flags = 0;
}
IN6_IPADDR_TO_V4MAPPED(v4saddr, saddr);
} else {
if (ip_select_source_v6(dst_ill, &v6setsrc, daddr,
zoneid, ipst, B_FALSE, IPV6_PREFER_SRC_DEFAULT,
saddr, NULL, &ipif_flags) != 0) {
*saddr = ipv6_all_zeros;
ipif_flags = 0;
}
}
dir->dir_sscope = ip_addr_scope_v6(saddr);
slabel = ip6_asp_lookup(saddr, NULL, ipst);
dir->dir_labelmatch = ip6_asp_labelcmp(dlabel, slabel);
dir->dir_sdeprecated = (ipif_flags & IPIF_DEPRECATED) ? 1 : 0;
ire_refrele(ire);
ill_refrele(dst_ill);
}
miocack(q, mp, iocp->ioc_count, 0);
}
int
ip_sioctl_tmyaddr(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
{
struct sioc_addrreq *sia;
sin_t *sin;
ire_t *ire;
mblk_t *mp1;
zoneid_t zoneid;
ip_stack_t *ipst;
ip1dbg(("ip_sioctl_tmyaddr"));
ASSERT(q->q_next == NULL);
zoneid = Q_TO_CONN(q)->conn_zoneid;
ipst = CONNQ_TO_IPST(q);
mp1 = mp->b_cont->b_cont;
sia = (struct sioc_addrreq *)mp1->b_rptr;
sin = (sin_t *)&sia->sa_addr;
switch (sin->sin_family) {
case AF_INET6: {
sin6_t *sin6 = (sin6_t *)sin;
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
ipaddr_t v4_addr;
IN6_V4MAPPED_TO_IPADDR(&sin6->sin6_addr,
v4_addr);
ire = ire_ftable_lookup_v4(v4_addr, 0, 0,
IRE_LOCAL|IRE_LOOPBACK, NULL, zoneid, NULL,
MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY, 0, ipst, NULL);
} else {
in6_addr_t v6addr;
v6addr = sin6->sin6_addr;
ire = ire_ftable_lookup_v6(&v6addr, 0, 0,
IRE_LOCAL|IRE_LOOPBACK, NULL, zoneid, NULL,
MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY, 0, ipst, NULL);
}
break;
}
case AF_INET: {
ipaddr_t v4addr;
v4addr = sin->sin_addr.s_addr;
ire = ire_ftable_lookup_v4(v4addr, 0, 0,
IRE_LOCAL|IRE_LOOPBACK, NULL, zoneid,
NULL, MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY, 0, ipst, NULL);
break;
}
default:
return (EAFNOSUPPORT);
}
if (ire != NULL) {
sia->sa_res = 1;
ire_refrele(ire);
} else {
sia->sa_res = 0;
}
return (0);
}
int
ip_sioctl_tonlink(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *duymmy_ifreq)
{
struct sioc_addrreq *sia;
sin_t *sin;
mblk_t *mp1;
ire_t *ire = NULL;
zoneid_t zoneid;
ip_stack_t *ipst;
ip1dbg(("ip_sioctl_tonlink"));
ASSERT(q->q_next == NULL);
zoneid = Q_TO_CONN(q)->conn_zoneid;
ipst = CONNQ_TO_IPST(q);
mp1 = mp->b_cont->b_cont;
sia = (struct sioc_addrreq *)mp1->b_rptr;
sin = (sin_t *)&sia->sa_addr;
switch (sin->sin_family) {
case AF_INET6: {
sin6_t *sin6 = (sin6_t *)sin;
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
ipaddr_t v4_addr;
IN6_V4MAPPED_TO_IPADDR(&sin6->sin6_addr,
v4_addr);
if (!CLASSD(v4_addr)) {
ire = ire_ftable_lookup_v4(v4_addr, 0, 0, 0,
NULL, zoneid, NULL, MATCH_IRE_DSTONLY,
0, ipst, NULL);
}
} else {
in6_addr_t v6addr;
v6addr = sin6->sin6_addr;
if (!IN6_IS_ADDR_MULTICAST(&v6addr)) {
ire = ire_ftable_lookup_v6(&v6addr, 0, 0, 0,
NULL, zoneid, NULL, MATCH_IRE_DSTONLY, 0,
ipst, NULL);
}
}
break;
}
case AF_INET: {
ipaddr_t v4addr;
v4addr = sin->sin_addr.s_addr;
if (!CLASSD(v4addr)) {
ire = ire_ftable_lookup_v4(v4addr, 0, 0, 0, NULL,
zoneid, NULL, MATCH_IRE_DSTONLY, 0, ipst, NULL);
}
break;
}
default:
return (EAFNOSUPPORT);
}
sia->sa_res = 0;
if (ire != NULL) {
ASSERT(!(ire->ire_type & IRE_MULTICAST));
if ((ire->ire_type & IRE_ONLINK) &&
!(ire->ire_type & IRE_BROADCAST))
sia->sa_res = 1;
ire_refrele(ire);
}
return (0);
}
int
ip_sioctl_tmysite(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
return (ENXIO);
}
int
ip_sioctl_arp(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
{
int err;
ipaddr_t ipaddr;
struct iocblk *iocp;
conn_t *connp;
struct arpreq *ar;
struct xarpreq *xar;
int arp_flags, flags, alength;
uchar_t *lladdr;
ip_stack_t *ipst;
ill_t *ill = ipif->ipif_ill;
ill_t *proxy_ill = NULL;
ipmp_arpent_t *entp = NULL;
boolean_t proxyarp = B_FALSE;
boolean_t if_arp_ioctl = B_FALSE;
ncec_t *ncec = NULL;
nce_t *nce;
ASSERT(!(q->q_flag & QREADR) && q->q_next == NULL);
connp = Q_TO_CONN(q);
ipst = connp->conn_netstack->netstack_ip;
iocp = (struct iocblk *)mp->b_rptr;
if (ipip->ipi_cmd_type == XARP_CMD) {
xar = (struct xarpreq *)mp->b_cont->b_cont->b_rptr;
ar = NULL;
arp_flags = xar->xarp_flags;
lladdr = (uchar_t *)LLADDR(&xar->xarp_ha);
if_arp_ioctl = (xar->xarp_ha.sdl_nlen != 0);
alength = ill->ill_phys_addr_length;
if (ipip->ipi_cmd == SIOCSXARP) {
if (alength != xar->xarp_ha.sdl_alen ||
(alength + xar->xarp_ha.sdl_nlen >
sizeof (xar->xarp_ha.sdl_data)))
return (EINVAL);
}
} else {
ar = (struct arpreq *)mp->b_cont->b_cont->b_rptr;
xar = NULL;
arp_flags = ar->arp_flags;
lladdr = (uchar_t *)ar->arp_ha.sa_data;
alength = 6;
if ((ipip->ipi_cmd != SIOCDARP) &&
(alength != ill->ill_phys_addr_length)) {
return (EINVAL);
}
}
flags = 0;
if (arp_flags & ATF_AUTHORITY)
flags |= NCE_F_AUTHORITY;
if (arp_flags & ATF_PERM)
flags |= NCE_F_NONUD;
if (arp_flags & ATF_PUBL)
flags |= NCE_F_PUBLISH;
if (IS_UNDER_IPMP(ill)) {
if (ipip->ipi_cmd != SIOCGARP && ipip->ipi_cmd != SIOCGXARP)
return (EPERM);
}
if (IS_IPMP(ill)) {
ipmp_illgrp_t *illg = ill->ill_grp;
switch (ipip->ipi_cmd) {
case SIOCSARP:
case SIOCSXARP:
proxy_ill = ipmp_illgrp_find_ill(illg, lladdr, alength);
if (proxy_ill != NULL) {
proxyarp = B_TRUE;
if (!ipmp_ill_is_active(proxy_ill))
proxy_ill = ipmp_illgrp_next_ill(illg);
if (proxy_ill != NULL)
lladdr = proxy_ill->ill_phys_addr;
}
}
}
ipaddr = sin->sin_addr.s_addr;
nce = nce_lookup_v4(ill, &ipaddr);
if (nce != NULL)
ncec = nce->nce_common;
switch (iocp->ioc_cmd) {
case SIOCDARP:
case SIOCDXARP: {
if (ncec == NULL) {
iocp->ioc_error = ENXIO;
break;
}
if (NCE_MYADDR(ncec)) {
nce_refrele(nce);
return (ENOTSUP);
}
iocp->ioc_error = 0;
ncec_delete(ncec);
break;
}
case SIOCGARP:
case SIOCGXARP:
if (ncec != NULL) {
lladdr = ncec->ncec_lladdr;
flags = ncec->ncec_flags;
iocp->ioc_error = 0;
ip_sioctl_garp_reply(mp, ncec->ncec_ill, lladdr, flags);
} else {
iocp->ioc_error = ENXIO;
}
break;
case SIOCSARP:
case SIOCSXARP:
if (ncec != NULL && NCE_MYADDR(ncec)) {
nce_refrele(nce);
return (ENOTSUP);
}
flags |= NCE_F_STATIC;
if (!if_arp_ioctl) {
ip_nce_lookup_and_update(&ipaddr, NULL, ipst,
lladdr, alength, flags);
} else {
ipif_t *ipif = ipif_get_next_ipif(NULL, ill);
if (ipif != NULL) {
ip_nce_lookup_and_update(&ipaddr, ipif, ipst,
lladdr, alength, flags);
ipif_refrele(ipif);
}
}
if (nce != NULL) {
nce_refrele(nce);
nce = NULL;
}
err = nce_lookup_then_add_v4(ill, lladdr,
ill->ill_phys_addr_length, &ipaddr, flags, ND_UNCHANGED,
&nce);
if (err == EEXIST) {
ncec = nce->nce_common;
mutex_enter(&ncec->ncec_lock);
ncec->ncec_state = ND_REACHABLE;
ncec->ncec_flags = flags;
nce_update(ncec, ND_UNCHANGED, lladdr);
mutex_exit(&ncec->ncec_lock);
err = 0;
}
if (nce != NULL) {
nce_refrele(nce);
nce = NULL;
}
if (IS_IPMP(ill) && err == 0) {
entp = ipmp_illgrp_create_arpent(ill->ill_grp,
proxyarp, ipaddr, lladdr, ill->ill_phys_addr_length,
flags);
if (entp == NULL || (proxyarp && proxy_ill == NULL)) {
iocp->ioc_error = (entp == NULL ? ENOMEM : 0);
break;
}
}
iocp->ioc_error = err;
}
if (nce != NULL) {
nce_refrele(nce);
}
if (entp != NULL)
ipmp_illgrp_mark_arpent(ill->ill_grp, entp);
return (iocp->ioc_error);
}
int
ip_extract_arpreq(queue_t *q, mblk_t *mp, const ip_ioctl_cmd_t *ipip,
cmd_info_t *ci)
{
mblk_t *mp1;
sin_t *sin;
conn_t *connp;
ipif_t *ipif;
ire_t *ire = NULL;
ill_t *ill = NULL;
boolean_t exists;
ip_stack_t *ipst;
struct arpreq *ar;
struct xarpreq *xar;
struct sockaddr_dl *sdl;
ASSERT(!(q->q_flag & QREADR) && q->q_next == NULL);
connp = Q_TO_CONN(q);
if (connp->conn_family == AF_INET6)
return (ENXIO);
ipst = connp->conn_netstack->netstack_ip;
mp1 = mp->b_cont->b_cont;
if (ipip->ipi_cmd_type == XARP_CMD) {
ASSERT(MBLKL(mp1) >= sizeof (struct xarpreq));
xar = (struct xarpreq *)mp1->b_rptr;
sin = (sin_t *)&xar->xarp_pa;
sdl = &xar->xarp_ha;
if (sdl->sdl_family != AF_LINK || sin->sin_family != AF_INET)
return (ENXIO);
if (sdl->sdl_nlen >= LIFNAMSIZ)
return (EINVAL);
} else {
ASSERT(ipip->ipi_cmd_type == ARP_CMD);
ASSERT(MBLKL(mp1) >= sizeof (struct arpreq));
ar = (struct arpreq *)mp1->b_rptr;
sin = (sin_t *)&ar->arp_pa;
}
if (ipip->ipi_cmd_type == XARP_CMD && sdl->sdl_nlen != 0) {
ipif = ipif_lookup_on_name(sdl->sdl_data, sdl->sdl_nlen,
B_FALSE, &exists, B_FALSE, ALL_ZONES, ipst);
if (ipif == NULL)
return (ENXIO);
if (ipif->ipif_id != 0) {
ipif_refrele(ipif);
return (ENXIO);
}
} else {
ipif = ipif_lookup_addr(sin->sin_addr.s_addr, NULL, ALL_ZONES,
ipst);
if (ipif == NULL) {
ire = ire_ftable_lookup_v4(sin->sin_addr.s_addr,
0, 0, IRE_IF_RESOLVER, NULL, ALL_ZONES,
NULL, MATCH_IRE_TYPE, 0, ipst, NULL);
if (ire == NULL || ((ill = ire->ire_ill) == NULL)) {
if (ire != NULL)
ire_refrele(ire);
return (ENXIO);
}
ASSERT(ire != NULL && ill != NULL);
ipif = ill->ill_ipif;
ipif_refhold(ipif);
ire_refrele(ire);
}
}
if (ipif->ipif_ill->ill_net_type != IRE_IF_RESOLVER) {
ipif_refrele(ipif);
return (ENXIO);
}
ci->ci_sin = sin;
ci->ci_ipif = ipif;
return (0);
}
static int
ip_sioctl_plink_ipmp(ill_t *ill, int ioccmd)
{
int err;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(IS_IPMP(ill));
ASSERT(IAM_WRITER_ILL(ill));
switch (ioccmd) {
case I_LINK:
return (ENOTSUP);
case I_PLINK:
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
ipmp_illgrp_link_grp(ill->ill_grp, ill->ill_phyint->phyint_grp);
rw_exit(&ipst->ips_ipmp_lock);
break;
case I_PUNLINK:
if (ill->ill_ipif_up_count + ill->ill_ipif_dup_count > 0)
return (EBUSY);
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
err = ipmp_illgrp_unlink_grp(ill->ill_grp);
rw_exit(&ipst->ips_ipmp_lock);
return (err);
default:
break;
}
return (0);
}
void
ip_sioctl_plink(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
{
mblk_t *mp1;
struct linkblk *li;
int ioccmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
int err = 0;
ASSERT(ioccmd == I_PLINK || ioccmd == I_PUNLINK ||
ioccmd == I_LINK || ioccmd == I_UNLINK);
mp1 = mp->b_cont;
li = (struct linkblk *)mp1->b_rptr;
err = ip_sioctl_plink_ipmod(ipsq, q, mp, ioccmd, li);
if (err == EINPROGRESS)
return;
if (err == 0)
miocack(q, mp, 0, 0);
else
miocnak(q, mp, 0, err);
if (CONN_Q(q)) {
CONN_DEC_IOCTLREF(Q_TO_CONN(q));
CONN_OPER_PENDING_DONE(Q_TO_CONN(q));
}
}
static int
ip_sioctl_plink_ipmod(ipsq_t *ipsq, queue_t *q, mblk_t *mp, int ioccmd,
struct linkblk *li)
{
int err = 0;
ill_t *ill;
queue_t *ipwq, *dwq;
const char *name;
struct qinit *qinfo;
boolean_t islink = (ioccmd == I_PLINK || ioccmd == I_LINK);
boolean_t entered_ipsq = B_FALSE;
boolean_t is_ip = B_FALSE;
arl_t *arl;
for (ipwq = li->l_qbot; ipwq != NULL; ipwq = ipwq->q_next) {
qinfo = ipwq->q_qinfo;
name = qinfo->qi_minfo->mi_idname;
if (name != NULL && strcmp(name, ip_mod_info.mi_idname) == 0 &&
qinfo->qi_putp != ip_lwput && ipwq->q_next != NULL) {
is_ip = B_TRUE;
break;
}
if (name != NULL && strcmp(name, arp_mod_info.mi_idname) == 0 &&
qinfo->qi_putp != ip_lwput && ipwq->q_next != NULL) {
break;
}
}
if (ipwq == NULL)
return (0);
if (!is_ip) {
arl = (arl_t *)ipwq->q_ptr;
ill = arl_to_ill(arl);
if (ill == NULL)
return (0);
} else {
ill = ipwq->q_ptr;
}
ASSERT(ill != NULL);
if (ipsq == NULL) {
ipsq = ipsq_try_enter(NULL, ill, q, mp, ip_sioctl_plink,
NEW_OP, B_FALSE);
if (ipsq == NULL) {
if (!is_ip)
ill_refrele(ill);
return (EINPROGRESS);
}
entered_ipsq = B_TRUE;
}
ASSERT(IAM_WRITER_ILL(ill));
mutex_enter(&ill->ill_lock);
if (!is_ip) {
if (islink && ill->ill_muxid == 0) {
mutex_exit(&ill->ill_lock);
if (entered_ipsq)
ipsq_exit(ipsq);
ill_refrele(ill);
return (EINVAL);
}
}
mutex_exit(&ill->ill_lock);
if (!is_ip) {
arl->arl_muxid = islink ? li->l_index : 0;
ill_refrele(ill);
goto done;
}
if (IS_IPMP(ill) && (err = ip_sioctl_plink_ipmp(ill, ioccmd)) != 0)
goto done;
ill->ill_lmod_rq = NULL;
ill->ill_lmod_cnt = 0;
if (islink && ((dwq = ipwq->q_next) != NULL)) {
ill->ill_lmod_rq = RD(dwq);
for (; dwq != NULL; dwq = dwq->q_next)
ill->ill_lmod_cnt++;
}
ill->ill_muxid = islink ? li->l_index : 0;
ipsq_current_start(ipsq, ill->ill_ipif, ioccmd);
if (ill->ill_ipif_up_count > 0) {
if (islink)
ill_capability_probe(ill);
else
ill_capability_reset(ill, B_FALSE);
}
ipsq_current_finish(ipsq);
done:
if (entered_ipsq)
ipsq_exit(ipsq);
return (err);
}
ip_ioctl_cmd_t *
ip_sioctl_lookup(int ioc_cmd)
{
int index;
ip_ioctl_cmd_t *ipip;
ip_ioctl_cmd_t *ipip_end;
if (ioc_cmd == IPI_DONTCARE)
return (NULL);
index = ioc_cmd & 0xFF;
if (index < ip_ndx_ioctl_count) {
ipip = &ip_ndx_ioctl_table[index];
if (ipip->ipi_cmd == ioc_cmd) {
return (ipip);
}
}
ipip_end = &ip_misc_ioctl_table[ip_misc_ioctl_count];
for (ipip = ip_misc_ioctl_table; ipip < ipip_end; ipip++) {
if (ipip->ipi_cmd == ioc_cmd)
return (ipip);
}
return (NULL);
}
static boolean_t
getset_ioctl_checks(mblk_t *mp)
{
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
mblk_t *mp1 = mp->b_cont;
mod_ioc_prop_t *pioc;
uint_t flags;
uint_t pioc_size;
if (mp1 == NULL || iocp->ioc_count == 0 ||
iocp->ioc_count == TRANSPARENT) {
return (B_FALSE);
}
if (msgdsize(mp1) < iocp->ioc_count) {
if (!pullupmsg(mp1, iocp->ioc_count))
return (B_FALSE);
}
pioc = (mod_ioc_prop_t *)mp1->b_rptr;
pioc_size = sizeof (mod_ioc_prop_t);
if (pioc->mpr_valsize != 0)
pioc_size += pioc->mpr_valsize - 1;
if (iocp->ioc_count != pioc_size)
return (B_FALSE);
flags = pioc->mpr_flags;
if (iocp->ioc_cmd == SIOCSETPROP) {
if ((flags & MOD_PROP_DEFAULT) != MOD_PROP_DEFAULT &&
flags != MOD_PROP_ACTIVE &&
flags != (MOD_PROP_ACTIVE|MOD_PROP_APPEND) &&
flags != (MOD_PROP_ACTIVE|MOD_PROP_REMOVE))
return (B_FALSE);
} else {
ASSERT(iocp->ioc_cmd == SIOCGETPROP);
if ((flags & MOD_PROP_ACTIVE) != MOD_PROP_ACTIVE &&
(flags & MOD_PROP_DEFAULT) != MOD_PROP_DEFAULT &&
(flags & MOD_PROP_POSSIBLE) != MOD_PROP_POSSIBLE &&
(flags & MOD_PROP_PERM) != MOD_PROP_PERM)
return (B_FALSE);
}
return (B_TRUE);
}
static void
ip_sioctl_getsetprop(queue_t *q, mblk_t *mp)
{
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
mblk_t *mp1 = mp->b_cont;
mod_ioc_prop_t *pioc;
mod_prop_info_t *ptbl = NULL, *pinfo = NULL;
ip_stack_t *ipst;
netstack_t *stack;
cred_t *cr;
boolean_t set;
int err;
ASSERT(q->q_next == NULL);
ASSERT(CONN_Q(q));
if (!getset_ioctl_checks(mp)) {
miocnak(q, mp, 0, EINVAL);
return;
}
ipst = CONNQ_TO_IPST(q);
stack = ipst->ips_netstack;
pioc = (mod_ioc_prop_t *)mp1->b_rptr;
switch (pioc->mpr_proto) {
case MOD_PROTO_IP:
case MOD_PROTO_IPV4:
case MOD_PROTO_IPV6:
ptbl = ipst->ips_propinfo_tbl;
break;
case MOD_PROTO_RAWIP:
ptbl = stack->netstack_icmp->is_propinfo_tbl;
break;
case MOD_PROTO_TCP:
ptbl = stack->netstack_tcp->tcps_propinfo_tbl;
break;
case MOD_PROTO_UDP:
ptbl = stack->netstack_udp->us_propinfo_tbl;
break;
case MOD_PROTO_SCTP:
ptbl = stack->netstack_sctp->sctps_propinfo_tbl;
break;
default:
miocnak(q, mp, 0, EINVAL);
return;
}
pinfo = mod_prop_lookup(ptbl, pioc->mpr_name, pioc->mpr_proto);
if (pinfo == NULL) {
miocnak(q, mp, 0, ENOENT);
return;
}
set = (iocp->ioc_cmd == SIOCSETPROP) ? B_TRUE : B_FALSE;
if (set && pinfo->mpi_setf != NULL) {
cr = msg_getcred(mp, NULL);
if (cr == NULL)
cr = iocp->ioc_cr;
err = pinfo->mpi_setf(stack, cr, pinfo, pioc->mpr_ifname,
pioc->mpr_val, pioc->mpr_flags);
} else if (!set && pinfo->mpi_getf != NULL) {
err = pinfo->mpi_getf(stack, pinfo, pioc->mpr_ifname,
pioc->mpr_val, pioc->mpr_valsize, pioc->mpr_flags);
} else {
err = EPERM;
}
if (err != 0) {
miocnak(q, mp, 0, err);
} else {
if (set)
miocack(q, mp, 0, 0);
else
miocack(q, mp, iocp->ioc_count, 0);
}
}
static void
ip_process_legacy_nddprop(queue_t *q, mblk_t *mp)
{
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
mblk_t *mp1 = mp->b_cont;
char *pname, *pval, *buf;
uint_t bufsize, proto;
mod_prop_info_t *pinfo = NULL;
ip_stack_t *ipst;
int err = 0;
ASSERT(CONN_Q(q));
ipst = CONNQ_TO_IPST(q);
if (iocp->ioc_count == 0 || mp1 == NULL) {
miocnak(q, mp, 0, EINVAL);
return;
}
mp1->b_datap->db_lim[-1] = '\0';
pval = buf = pname = (char *)mp1->b_rptr;
bufsize = MBLKL(mp1);
if (strcmp(pname, "ip_forwarding") == 0) {
pname = "forwarding";
proto = MOD_PROTO_IPV4;
} else if (strcmp(pname, "ip6_forwarding") == 0) {
pname = "forwarding";
proto = MOD_PROTO_IPV6;
} else {
miocnak(q, mp, 0, EINVAL);
return;
}
pinfo = mod_prop_lookup(ipst->ips_propinfo_tbl, pname, proto);
switch (iocp->ioc_cmd) {
case ND_GET:
if ((err = pinfo->mpi_getf(ipst->ips_netstack, pinfo, NULL, buf,
bufsize, 0)) == 0) {
miocack(q, mp, iocp->ioc_count, 0);
return;
}
break;
case ND_SET:
while (*pval++)
noop;
if (!*pval || pval >= (char *)mp1->b_wptr) {
err = EINVAL;
} else if ((err = pinfo->mpi_setf(ipst->ips_netstack, NULL,
pinfo, NULL, pval, 0)) == 0) {
miocack(q, mp, 0, 0);
return;
}
break;
default:
err = EINVAL;
break;
}
miocnak(q, mp, 0, err);
}
void
ip_sioctl_copyin_resume(ipsq_t *dummy_ipsq, queue_t *q, mblk_t *mp,
void *dummy_arg)
{
ip_sioctl_copyin_setup(q, mp);
}
void
ip_sioctl_copyin_setup(queue_t *q, mblk_t *mp)
{
int copyin_size;
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
ip_ioctl_cmd_t *ipip;
cred_t *cr;
ip_stack_t *ipst;
if (CONN_Q(q))
ipst = CONNQ_TO_IPST(q);
else
ipst = ILLQ_TO_IPST(q);
ipip = ip_sioctl_lookup(iocp->ioc_cmd);
if (ipip == NULL) {
if (q->q_next == NULL) {
goto nak;
} else {
putnext(q, mp);
return;
}
}
if ((iocp->ioc_cmd == SIOCGDSTINFO ||
iocp->ioc_cmd == SIOCGIP6ADDRPOLICY) && !ip6_asp_can_lookup(ipst)) {
ip6_asp_pending_op(q, mp, ip_sioctl_copyin_resume);
return;
}
if ((q->q_next != NULL) && !(ipip->ipi_flags & IPI_MODOK)) {
goto nak;
}
if (mp->b_cont == NULL && !(ipip->ipi_flags & IPI_NULL_BCONT))
goto nak;
cr = msg_getcred(mp, NULL);
if (cr == NULL)
cr = iocp->ioc_cr;
if ((ipip->ipi_flags & IPI_PRIV) &&
(cr != NULL) && secpolicy_ip_config(cr, B_TRUE) != 0) {
miocnak(q, mp, 0, secpolicy_ip_config(cr, B_FALSE));
return;
}
if (ipip->ipi_copyin_size != 0) {
mi_copyin(q, mp, NULL, ipip->ipi_copyin_size);
return;
}
switch (iocp->ioc_cmd) {
case O_SIOCGIFCONF:
case SIOCGIFCONF:
if (iocp->ioc_count == TRANSPARENT)
copyin_size = SIZEOF_STRUCT(ifconf,
iocp->ioc_flag);
else
copyin_size = iocp->ioc_count;
mi_copyin(q, mp, NULL, copyin_size);
return;
case O_SIOCGLIFCONF:
case SIOCGLIFCONF:
copyin_size = SIZEOF_STRUCT(lifconf, iocp->ioc_flag);
mi_copyin(q, mp, NULL, copyin_size);
return;
case SIOCGLIFSRCOF:
copyin_size = SIZEOF_STRUCT(lifsrcof, iocp->ioc_flag);
mi_copyin(q, mp, NULL, copyin_size);
return;
case SIOCGIP6ADDRPOLICY:
ip_sioctl_ip6addrpolicy(q, mp);
ip6_asp_table_refrele(ipst);
return;
case SIOCSIP6ADDRPOLICY:
ip_sioctl_ip6addrpolicy(q, mp);
return;
case SIOCGDSTINFO:
ip_sioctl_dstinfo(q, mp);
ip6_asp_table_refrele(ipst);
return;
case ND_SET:
case ND_GET:
ip_process_legacy_nddprop(q, mp);
return;
case SIOCSETPROP:
case SIOCGETPROP:
ip_sioctl_getsetprop(q, mp);
return;
case I_PLINK:
case I_PUNLINK:
case I_LINK:
case I_UNLINK:
if (CONN_Q(q)) {
CONN_INC_REF(Q_TO_CONN(q));
CONN_INC_IOCTLREF(Q_TO_CONN(q))
}
ip_sioctl_plink(NULL, q, mp, NULL);
return;
case IP_IOCTL:
ip_wput_ioctl(q, mp);
return;
case SIOCILB:
copyin_size = iocp->ioc_count;
if (copyin_size < sizeof (ilb_cmd_t))
goto nak;
mi_copyin(q, mp, NULL, copyin_size);
return;
default:
cmn_err(CE_WARN, "Unknown ioctl %d/0x%x slipped through.",
iocp->ioc_cmd, iocp->ioc_cmd);
}
nak:
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
iocp->ioc_error = EINVAL;
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_count = 0;
qreply(q, mp);
}
static void
ip_sioctl_garp_reply(mblk_t *mp, ill_t *ill, void *hwaddr, int flags)
{
struct arpreq *ar;
struct xarpreq *xar;
mblk_t *tmp;
struct iocblk *iocp;
int x_arp_ioctl = B_FALSE;
int *flagsp;
char *storage = NULL;
ASSERT(ill != NULL);
iocp = (struct iocblk *)mp->b_rptr;
ASSERT(iocp->ioc_cmd == SIOCGXARP || iocp->ioc_cmd == SIOCGARP);
tmp = (mp->b_cont)->b_cont;
if ((iocp->ioc_cmd == SIOCGXARP) ||
(iocp->ioc_cmd == SIOCSXARP)) {
x_arp_ioctl = B_TRUE;
xar = (struct xarpreq *)tmp->b_rptr;
flagsp = &xar->xarp_flags;
storage = xar->xarp_ha.sdl_data;
} else {
ar = (struct arpreq *)tmp->b_rptr;
flagsp = &ar->arp_flags;
storage = ar->arp_ha.sa_data;
}
if (x_arp_ioctl) {
storage += ill_xarp_info(&xar->xarp_ha, ill);
if ((ill->ill_phys_addr_length + ill->ill_name_length) >
sizeof (xar->xarp_ha.sdl_data)) {
iocp->ioc_error = EINVAL;
return;
}
}
*flagsp = ATF_INUSE;
if ((flags & NCE_F_MYADDR) || (flags & NCE_F_AUTHORITY))
*flagsp |= ATF_AUTHORITY;
if (flags & NCE_F_NONUD)
*flagsp |= ATF_PERM;
if (flags & NCE_F_PUBLISH)
*flagsp |= ATF_PUBL;
if (hwaddr != NULL) {
*flagsp |= ATF_COM;
bcopy((char *)hwaddr, storage, ill->ill_phys_addr_length);
}
}
int
ip_sioctl_addif(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *dummy_ipip, void *dummy_ifreq)
{
mblk_t *mp1;
struct lifreq *lifr;
boolean_t isv6;
boolean_t exists;
char *name;
char *endp;
char *cp;
int namelen;
ipif_t *ipif;
long id;
ipsq_t *ipsq;
ill_t *ill;
sin_t *sin;
int err = 0;
boolean_t found_sep = B_FALSE;
conn_t *connp;
zoneid_t zoneid;
ip_stack_t *ipst = CONNQ_TO_IPST(q);
ASSERT(q->q_next == NULL);
ip1dbg(("ip_sioctl_addif\n"));
mp1 = mp->b_cont->b_cont;
lifr = (struct lifreq *)mp1->b_rptr;
lifr->lifr_name[LIFNAMSIZ - 1] = '\0';
name = lifr->lifr_name;
ASSERT(CONN_Q(q));
connp = Q_TO_CONN(q);
isv6 = (connp->conn_family == AF_INET6);
zoneid = connp->conn_zoneid;
namelen = mi_strlen(name);
if (namelen == 0)
return (EINVAL);
exists = B_FALSE;
if ((namelen + 1 == sizeof (ipif_loopback_name)) &&
(mi_strcmp(name, ipif_loopback_name) == 0)) {
ipif = ipif_lookup_on_name(lifr->lifr_name, namelen, B_TRUE,
&exists, isv6, zoneid, ipst);
if (ipif == NULL) {
return (ENOBUFS);
} else if (!exists) {
ipif_refrele(ipif);
return (0);
} else {
ill = ipif->ipif_ill;
ill_refhold(ill);
ipif_refrele(ipif);
}
} else {
endp = &name[namelen];
for (cp = endp; --cp > name; ) {
if (*cp == IPIF_SEPARATOR_CHAR) {
found_sep = B_TRUE;
if ((strncmp("0", cp+1, 1)) == 0)
return (EINVAL);
if (ddi_strtol(cp+1, &endp, 10, &id) != 0 ||
id <= 0 || *endp != '\0') {
return (EINVAL);
}
*cp = '\0';
break;
}
}
ill = ill_lookup_on_name(name, B_FALSE, isv6, NULL, ipst);
if (found_sep)
*cp = IPIF_SEPARATOR_CHAR;
if (ill == NULL)
return (ENXIO);
}
ipsq = ipsq_try_enter(NULL, ill, q, mp, ip_process_ioctl, NEW_OP,
B_TRUE);
ill_refrele(ill);
if (ipsq == NULL)
return (EINPROGRESS);
ASSERT(IAM_WRITER_ILL(ill));
if (found_sep) {
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (ipif->ipif_id == id) {
err = EEXIST;
goto done;
}
}
}
if ((ipif = ipif_allocate(ill, found_sep ? id : -1, IRE_LOCAL,
B_TRUE, B_TRUE, &err)) == NULL) {
goto done;
}
(void) sprintf(lifr->lifr_name, "%s%c%d", ill->ill_name,
IPIF_SEPARATOR_CHAR, ipif->ipif_id);
ip1dbg(("created %s\n", lifr->lifr_name));
sin = (sin_t *)&lifr->lifr_addr;
if (sin->sin_family != AF_UNSPEC) {
err = ip_sioctl_addr(ipif, sin, q, mp,
&ip_ndx_ioctl_table[SIOCLIFADDR_NDX], lifr);
}
done:
ipsq_exit(ipsq);
return (err);
}
int
ip_sioctl_removeif(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_if_req)
{
conn_t *connp;
ill_t *ill = ipif->ipif_ill;
boolean_t success;
ip_stack_t *ipst;
ipst = CONNQ_TO_IPST(q);
ASSERT(q->q_next == NULL);
ip1dbg(("ip_sioctl_remove_if(%s:%u %p)\n",
ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
connp = Q_TO_CONN(q);
if (ipif->ipif_id == 0 && ill->ill_net_type == IRE_LOOPBACK) {
if (sin->sin_family == AF_UNSPEC &&
(IN6_IS_ADDR_UNSPECIFIED(&((sin6_t *)sin)->sin6_addr))) {
mutex_enter(&ill->ill_lock);
ill->ill_state_flags |= ILL_CONDEMNED;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
ipif->ipif_state_flags |= IPIF_CONDEMNED;
}
mutex_exit(&ill->ill_lock);
ipif = ill->ill_ipif;
ill_delete(ill);
mutex_enter(&connp->conn_lock);
mutex_enter(&ill->ill_lock);
if (ill_is_freeable(ill)) {
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
ill_delete_tail(ill);
mi_free(ill);
return (0);
}
success = ipsq_pending_mp_add(connp, ipif,
CONNP_TO_WQ(connp), mp, ILL_FREE);
mutex_exit(&connp->conn_lock);
mutex_exit(&ill->ill_lock);
if (success)
return (EINPROGRESS);
else
return (EINTR);
}
}
if (ipif->ipif_id == 0) {
ipsq_t *ipsq;
if (ipif->ipif_isv6) {
sin6_t *sin6;
if (sin->sin_family != AF_INET6)
return (EAFNOSUPPORT);
sin6 = (sin6_t *)sin;
ipif = ipif_lookup_addr_exact_v6(&sin6->sin6_addr, ill,
ipst);
} else {
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
ipif = ipif_lookup_addr_exact(sin->sin_addr.s_addr, ill,
ipst);
}
if (ipif == NULL) {
return (EADDRNOTAVAIL);
}
ipsq = ipif->ipif_ill->ill_phyint->phyint_ipsq;
ipsq->ipsq_xop->ipx_current_ipif = ipif;
ipif_refrele(ipif);
}
if (ipif->ipif_id == 0)
return (EBUSY);
mutex_enter(&ill->ill_lock);
ipif->ipif_state_flags |= IPIF_CONDEMNED;
mutex_exit(&ill->ill_lock);
ipif_free(ipif);
mutex_enter(&connp->conn_lock);
mutex_enter(&ill->ill_lock);
if (ipif_is_freeable(ipif)) {
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
ipif_non_duplicate(ipif);
(void) ipif_down_tail(ipif);
ipif_free_tail(ipif);
return (0);
}
success = ipsq_pending_mp_add(connp, ipif, CONNP_TO_WQ(connp), mp,
IPIF_FREE);
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
if (success)
return (EINPROGRESS);
else
return (EINTR);
}
int
ip_sioctl_removeif_restart(ipif_t *ipif, sin_t *dummy_sin, queue_t *q,
mblk_t *mp, ip_ioctl_cmd_t *ipip, void *dummy_if_req)
{
ill_t *ill = ipif->ipif_ill;
ASSERT(IAM_WRITER_IPIF(ipif));
ASSERT(ipif->ipif_state_flags & IPIF_CONDEMNED);
ip1dbg(("ip_sioctl_removeif_restart(%s:%u %p)\n",
ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_id == 0 && ill->ill_net_type == IRE_LOOPBACK) {
ASSERT(ill->ill_state_flags & ILL_CONDEMNED);
ill_delete_tail(ill);
mi_free(ill);
return (0);
}
ipif_non_duplicate(ipif);
(void) ipif_down_tail(ipif);
ipif_free_tail(ipif);
return (0);
}
int
ip_sioctl_prefix(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *dummy_ipip, void *dummy_ifreq)
{
int err;
in6_addr_t v6addr;
sin6_t *sin6;
ill_t *ill;
int i;
ip1dbg(("ip_sioctl_prefix(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
if (!ipif->ipif_isv6)
return (EINVAL);
if (sin->sin_family != AF_INET6)
return (EAFNOSUPPORT);
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
ill = ipif->ipif_ill;
if (IN6_IS_ADDR_UNSPECIFIED(&v6addr) ||
IN6_IS_ADDR_UNSPECIFIED(&ill->ill_token))
return (EADDRNOTAVAIL);
for (i = 0; i < 4; i++)
sin6->sin6_addr.s6_addr32[i] |= ill->ill_token.s6_addr32[i];
err = ip_sioctl_addr(ipif, sin, q, mp,
&ip_ndx_ioctl_table[SIOCLIFADDR_NDX], dummy_ifreq);
return (err);
}
int
ip_sioctl_prefix_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
ip1dbg(("ip_sioctl_prefix_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
return (ip_sioctl_addr_restart(ipif, sin, q, mp, ipip, ifreq));
}
int
ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *dummy_ipip, void *dummy_ifreq)
{
int err = 0;
in6_addr_t v6addr;
boolean_t need_up = B_FALSE;
ill_t *ill;
int i;
ip1dbg(("ip_sioctl_addr(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
ill = ipif->ipif_ill;
if (ipif->ipif_isv6) {
sin6_t *sin6;
phyint_t *phyi;
if (sin->sin_family != AF_INET6)
return (EAFNOSUPPORT);
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
phyi = ill->ill_phyint;
if (ipif->ipif_id == 0 &&
(ill->ill_flags & ILLF_MULTICAST) &&
!(ipif->ipif_flags & (IPIF_POINTOPOINT)) &&
!(phyi->phyint_flags & (PHYI_LOOPBACK)) &&
!IN6_IS_ADDR_LINKLOCAL(&v6addr)) {
if (ill->ill_flags & ILLF_NOLINKLOCAL) {
if (!IN6_IS_ADDR_UNSPECIFIED(&v6addr))
return (EADDRNOTAVAIL);
} else {
return (EADDRNOTAVAIL);
}
}
if ((ipif->ipif_flags & IPIF_UP) &&
IN6_IS_ADDR_UNSPECIFIED(&v6addr) &&
(!(ipif->ipif_flags & IPIF_NOLOCAL) ||
IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6subnet))) {
return (EADDRNOTAVAIL);
}
if (!ip_local_addr_ok_v6(&v6addr, &ipif->ipif_v6net_mask))
return (EADDRNOTAVAIL);
} else {
ipaddr_t addr;
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
addr = sin->sin_addr.s_addr;
if (addr != INADDR_ANY &&
!ip_addr_ok_v4(addr, ipif->ipif_net_mask))
return (EADDRNOTAVAIL);
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
}
if (ill->ill_allowed_ips_cnt > 0) {
for (i = 0; i < ill->ill_allowed_ips_cnt; i++) {
if (IN6_ARE_ADDR_EQUAL(&ill->ill_allowed_ips[i],
&v6addr))
break;
}
if (i == ill->ill_allowed_ips_cnt) {
pr_addr_dbg("!allowed addr %s\n", AF_INET6, &v6addr);
return (EPERM);
}
}
if (ipif->ipif_flags & IPIF_UP) {
err = ipif_logical_down(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
need_up = 1;
}
err = ip_sioctl_addr_tail(ipif, sin, q, mp, need_up);
return (err);
}
int
ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
boolean_t need_up)
{
in6_addr_t v6addr;
in6_addr_t ov6addr;
ipaddr_t addr;
sin6_t *sin6;
int sinlen;
int err = 0;
ill_t *ill = ipif->ipif_ill;
boolean_t need_dl_down;
boolean_t need_arp_down;
struct iocblk *iocp;
iocp = (mp != NULL) ? (struct iocblk *)mp->b_rptr : NULL;
ip1dbg(("ip_sioctl_addr_tail(%s:%u %p)\n",
ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
if (ipif->ipif_recovery_id != 0)
(void) untimeout(ipif->ipif_recovery_id);
ipif->ipif_recovery_id = 0;
if (ipif->ipif_isv6) {
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
sinlen = sizeof (struct sockaddr_in6);
} else {
addr = sin->sin_addr.s_addr;
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
sinlen = sizeof (struct sockaddr_in);
}
mutex_enter(&ill->ill_lock);
ov6addr = ipif->ipif_v6lcl_addr;
ipif->ipif_v6lcl_addr = v6addr;
sctp_update_ipif_addr(ipif, ov6addr);
ipif->ipif_addr_ready = 0;
ip_rts_newaddrmsg(RTM_CHGADDR, 0, ipif, RTSQ_DEFAULT);
need_dl_down = need_arp_down = B_FALSE;
if (ipif->ipif_flags & IPIF_DUPLICATE) {
need_arp_down = !need_up;
ipif->ipif_flags &= ~IPIF_DUPLICATE;
if (--ill->ill_ipif_dup_count == 0 && !need_up &&
ill->ill_ipif_up_count == 0 && ill->ill_dl_up) {
need_dl_down = B_TRUE;
}
}
ipif_set_default(ipif);
if (ipif->ipif_isv6 && ipif->ipif_id == 0) {
if (iocp == NULL || (iocp->ioc_cmd == SIOCSLIFADDR &&
!IN6_IS_ADDR_UNSPECIFIED(&v6addr)))
ill->ill_manual_linklocal = 1;
}
if (iocp != NULL && iocp->ioc_cmd != SIOCLIFADDIF) {
ill_nic_event_dispatch(ill, MAP_IPIF_ID(ipif->ipif_id),
NE_ADDRESS_CHANGE, sin, sinlen);
}
mutex_exit(&ill->ill_lock);
if (need_up) {
err = ipif_up(ipif, q, mp);
} else {
update_conn_ill(NULL, ill->ill_ipst);
}
if (need_dl_down)
ill_dl_down(ill);
if (need_arp_down && !ill->ill_isv6)
(void) ipif_arp_down(ipif);
ire_increment_multicast_generation(ill->ill_ipst, ill->ill_isv6);
return (err);
}
int
ip_sioctl_addr_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
ip1dbg(("ip_sioctl_addr_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
(void) ipif_down_tail(ipif);
return (ip_sioctl_addr_tail(ipif, sin, q, mp, B_TRUE));
}
int
ip_sioctl_get_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
sin6_t *sin6 = (struct sockaddr_in6 *)sin;
struct lifreq *lifr = (struct lifreq *)if_req;
ip1dbg(("ip_sioctl_get_addr(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_isv6) {
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ipif->ipif_v6lcl_addr;
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
sin6->sin6_scope_id =
ipif->ipif_ill->ill_phyint->phyint_ifindex;
}
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
lifr->lifr_addrlen =
ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
} else {
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_lcl_addr;
if (ipip->ipi_cmd_type == LIF_CMD) {
lifr->lifr_addrlen =
ip_mask_to_plen(ipif->ipif_net_mask);
}
}
return (0);
}
int
ip_sioctl_dstaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int err = 0;
in6_addr_t v6addr;
boolean_t need_up = B_FALSE;
ip1dbg(("ip_sioctl_dstaddr(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
if (ipif->ipif_isv6) {
sin6_t *sin6;
if (sin->sin_family != AF_INET6)
return (EAFNOSUPPORT);
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
if (!ip_remote_addr_ok_v6(&v6addr, &ipif->ipif_v6net_mask))
return (EADDRNOTAVAIL);
} else {
ipaddr_t addr;
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
addr = sin->sin_addr.s_addr;
if (addr != INADDR_ANY &&
!ip_addr_ok_v4(addr, ipif->ipif_net_mask)) {
return (EADDRNOTAVAIL);
}
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
}
if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6pp_dst_addr, &v6addr))
return (0);
if (ipif->ipif_flags & IPIF_UP) {
err = ipif_logical_down(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
need_up = B_TRUE;
}
err = ip_sioctl_dstaddr_tail(ipif, sin, q, mp, need_up);
return (err);
}
static int
ip_sioctl_dstaddr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
boolean_t need_up)
{
in6_addr_t v6addr;
ill_t *ill = ipif->ipif_ill;
int err = 0;
boolean_t need_dl_down;
boolean_t need_arp_down;
ip1dbg(("ip_sioctl_dstaddr_tail(%s:%u %p)\n", ill->ill_name,
ipif->ipif_id, (void *)ipif));
if (ipif->ipif_recovery_id != 0)
(void) untimeout(ipif->ipif_recovery_id);
ipif->ipif_recovery_id = 0;
if (ipif->ipif_isv6) {
sin6_t *sin6;
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
} else {
ipaddr_t addr;
addr = sin->sin_addr.s_addr;
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
}
mutex_enter(&ill->ill_lock);
if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0) {
ipif->ipif_flags |= IPIF_POINTOPOINT;
ipif->ipif_flags &= ~IPIF_BROADCAST;
if (ipif->ipif_isv6)
ill->ill_flags |= ILLF_NONUD;
}
need_dl_down = need_arp_down = B_FALSE;
if (ipif->ipif_flags & IPIF_DUPLICATE) {
need_arp_down = !need_up;
ipif->ipif_flags &= ~IPIF_DUPLICATE;
if (--ill->ill_ipif_dup_count == 0 && !need_up &&
ill->ill_ipif_up_count == 0 && ill->ill_dl_up) {
need_dl_down = B_TRUE;
}
}
if (ipif->ipif_isv6 && ipif->ipif_id == 0)
ill->ill_manual_dst_linklocal = 1;
ipif->ipif_v6pp_dst_addr = v6addr;
ipif->ipif_v6subnet = ipif->ipif_v6pp_dst_addr;
mutex_exit(&ill->ill_lock);
if (need_up) {
err = ipif_up(ipif, q, mp);
}
if (need_dl_down)
ill_dl_down(ill);
if (need_arp_down && !ipif->ipif_isv6)
(void) ipif_arp_down(ipif);
return (err);
}
int
ip_sioctl_dstaddr_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
ip1dbg(("ip_sioctl_dstaddr_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
(void) ipif_down_tail(ipif);
return (ip_sioctl_dstaddr_tail(ipif, sin, q, mp, B_TRUE));
}
int
ip_sioctl_get_dstaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
sin6_t *sin6 = (struct sockaddr_in6 *)sin;
ip1dbg(("ip_sioctl_get_dstaddr(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0)
return (EADDRNOTAVAIL);
if (ipif->ipif_isv6) {
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ipif->ipif_v6pp_dst_addr;
} else {
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_pp_dst_addr;
}
return (0);
}
static void
ip_sioctl_flags_onoff(ipif_t *ipif, uint64_t flags, uint64_t *onp,
uint64_t *offp)
{
ill_t *ill = ipif->ipif_ill;
phyint_t *phyi = ill->ill_phyint;
uint64_t cantchange_flags, intf_flags;
uint64_t turn_on, turn_off;
intf_flags = ipif->ipif_flags | ill->ill_flags | phyi->phyint_flags;
cantchange_flags = IFF_CANTCHANGE;
if (IS_IPMP(ill))
cantchange_flags |= IFF_IPMP_CANTCHANGE;
turn_on = (flags ^ intf_flags) & ~cantchange_flags;
turn_off = intf_flags & turn_on;
turn_on ^= turn_off;
*onp = turn_on;
*offp = turn_off;
}
int
ip_sioctl_flags(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
uint64_t turn_on;
uint64_t turn_off;
int err = 0;
phyint_t *phyi;
ill_t *ill;
conn_t *connp;
uint64_t intf_flags;
boolean_t phyint_flags_modified = B_FALSE;
uint64_t flags;
struct ifreq *ifr;
struct lifreq *lifr;
boolean_t set_linklocal = B_FALSE;
ip1dbg(("ip_sioctl_flags(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
ill = ipif->ipif_ill;
phyi = ill->ill_phyint;
if (ipip->ipi_cmd_type == IF_CMD) {
ifr = (struct ifreq *)if_req;
flags = (uint64_t)(ifr->ifr_flags & 0x0000ffff);
} else {
lifr = (struct lifreq *)if_req;
flags = lifr->lifr_flags;
}
intf_flags = ipif->ipif_flags | ill->ill_flags | phyi->phyint_flags;
ASSERT((phyi->phyint_flags & ~(IFF_PHYINT_FLAGS)) == 0);
ASSERT((ill->ill_flags & ~(IFF_PHYINTINST_FLAGS)) == 0);
ASSERT((ipif->ipif_flags & ~(IFF_LOGINT_FLAGS)) == 0);
if (ipip->ipi_cmd == SIOCSIFFLAGS)
flags |= intf_flags & ~0xFFFF;
if (IS_IPMP(ill) && ((flags ^ intf_flags) & IFF_IPMP_INVALID))
return (EINVAL);
ip_sioctl_flags_onoff(ipif, flags, &turn_on, &turn_off);
if ((turn_on|turn_off) == 0)
return (0);
if ((turn_off & (IFF_DEPRECATED|IFF_NOFAILOVER)) == IFF_DEPRECATED &&
(turn_on|intf_flags) & IFF_NOFAILOVER)
return (EINVAL);
if ((connp = Q_TO_CONN(q)) == NULL)
return (EINVAL);
if ((intf_flags & IFF_VRRP) && ((turn_off | turn_on) & IFF_UP)) {
if (!connp->conn_isvrrp)
return (EINVAL);
}
if ((turn_off | turn_on) & IFF_NOACCEPT) {
if (!connp->conn_isvrrp || !(intf_flags & IFF_VRRP))
return (EINVAL);
}
if (turn_on & IFF_NOFAILOVER) {
turn_on |= IFF_DEPRECATED;
flags |= IFF_DEPRECATED;
}
if (IS_UNDER_IPMP(ill)) {
const uint64_t appflags = IFF_DHCPRUNNING | IFF_ADDRCONF;
if ((turn_on & appflags) && !(flags & IFF_NOFAILOVER))
return (EINVAL);
if ((turn_off & IFF_NOFAILOVER) &&
(flags & (appflags | IFF_UP | IFF_DUPLICATE)))
return (EINVAL);
}
if ((turn_on & IFF_TEMPORARY) && !(ipif->ipif_isv6))
return (EINVAL);
if ((turn_off & IFF_NOXMIT) && IS_VNI(ipif->ipif_ill))
return (EINVAL);
if ((turn_on & IFF_ROUTER) && (phyi->phyint_flags & PHYI_LOOPBACK))
return (EINVAL);
if (ipif->ipif_id == 0 && ipif->ipif_isv6 &&
(flags & IFF_UP) && !(flags & (IFF_NOLOCAL|IFF_ANYCAST)) &&
IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr)) {
if (ipif_cant_setlinklocal(ipif))
return (EINVAL);
set_linklocal = B_TRUE;
}
if ((turn_on|turn_off) & IFF_PHYINT_FLAGS)
phyint_flags_modified = B_TRUE;
if (turn_on & PHYI_STANDBY) {
if (ill->ill_usesrc_grp_next != NULL ||
intf_flags & PHYI_INACTIVE)
return (EINVAL);
if (!(flags & PHYI_FAILED)) {
flags |= PHYI_INACTIVE;
turn_on |= PHYI_INACTIVE;
}
}
if (turn_off & PHYI_STANDBY) {
flags &= ~PHYI_INACTIVE;
turn_off |= PHYI_INACTIVE;
}
if ((flags & (PHYI_FAILED | PHYI_INACTIVE)) ==
(PHYI_FAILED | PHYI_INACTIVE))
return (EINVAL);
if ((turn_on | turn_off) & ILLF_ROUTER) {
err = ill_forward_set(ill, ((turn_on & ILLF_ROUTER) != 0));
if (err != 0)
return (err);
}
if (!(ipif->ipif_flags & IPIF_UP) &&
!(turn_on & IPIF_UP)) {
mutex_enter(&ill->ill_lock);
mutex_enter(&ill->ill_phyint->phyint_lock);
ipif->ipif_flags |= (turn_on & IFF_LOGINT_FLAGS);
ipif->ipif_flags &= (~turn_off & IFF_LOGINT_FLAGS);
ill->ill_flags |= (turn_on & IFF_PHYINTINST_FLAGS);
ill->ill_flags &= (~turn_off & IFF_PHYINTINST_FLAGS);
phyi->phyint_flags |= (turn_on & IFF_PHYINT_FLAGS);
phyi->phyint_flags &= (~turn_off & IFF_PHYINT_FLAGS);
mutex_exit(&ill->ill_lock);
mutex_exit(&ill->ill_phyint->phyint_lock);
if ((turn_on|turn_off) &
(PHYI_FAILED | PHYI_INACTIVE | PHYI_OFFLINE)) {
ASSERT(!IS_IPMP(ill));
if (IS_UNDER_IPMP(ill))
ipmp_phyint_refresh_active(phyi);
}
if (phyint_flags_modified) {
if (phyi->phyint_illv4 != NULL) {
ip_rts_ifmsg(phyi->phyint_illv4->
ill_ipif, RTSQ_DEFAULT);
}
if (phyi->phyint_illv6 != NULL) {
ip_rts_ifmsg(phyi->phyint_illv6->
ill_ipif, RTSQ_DEFAULT);
}
}
ire_increment_multicast_generation(ill->ill_ipst,
ill->ill_isv6);
return (0);
} else if (set_linklocal) {
mutex_enter(&ill->ill_lock);
if (set_linklocal)
ipif->ipif_state_flags |= IPIF_SET_LINKLOCAL;
mutex_exit(&ill->ill_lock);
}
if (ipif->ipif_isv6 &&
((IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr) &&
(!(ipif->ipif_flags & IPIF_NOLOCAL) && !(turn_on & IPIF_NOLOCAL) ||
IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6subnet))) ||
((ipif->ipif_flags & IPIF_POINTOPOINT) &&
IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6pp_dst_addr)))) {
return (EINVAL);
}
if (!ipif->ipif_isv6 &&
((ipif->ipif_flags & IPIF_POINTOPOINT) &&
ipif->ipif_pp_dst_addr == INADDR_ANY)) {
return (EINVAL);
}
if ((turn_on|turn_off) &
(IPIF_UP|IPIF_DEPRECATED|IPIF_NOXMIT|IPIF_NOLOCAL|ILLF_NOARP|
ILLF_NONUD|IPIF_PRIVATE|IPIF_ANYCAST|IPIF_PREFERRED|
IPIF_NOFAILOVER)) {
if (((ipif->ipif_flags | turn_on) & IPIF_UP) &&
!(turn_off & IPIF_UP)) {
if (ipif->ipif_flags & IPIF_UP)
ill->ill_logical_down = 1;
turn_on &= ~IPIF_UP;
}
err = ipif_down(ipif, q, mp);
ip1dbg(("ipif_down returns %d err ", err));
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
} else if ((turn_on|turn_off) & ILLF_NOACCEPT) {
ill_down_ipifs(ill, B_TRUE);
mutex_enter(&connp->conn_lock);
mutex_enter(&ill->ill_lock);
if (!ill_is_quiescent(ill)) {
boolean_t success;
success = ipsq_pending_mp_add(connp, ill->ill_ipif,
q, mp, ILL_DOWN);
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
return (success ? EINPROGRESS : EINTR);
}
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
}
return (ip_sioctl_flags_tail(ipif, flags, q, mp));
}
static int
ip_sioctl_flags_tail(ipif_t *ipif, uint64_t flags, queue_t *q, mblk_t *mp)
{
ill_t *ill;
phyint_t *phyi;
uint64_t turn_on, turn_off;
boolean_t phyint_flags_modified = B_FALSE;
int err = 0;
boolean_t set_linklocal = B_FALSE;
ip1dbg(("ip_sioctl_flags_tail(%s:%u)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id));
ASSERT(IAM_WRITER_IPIF(ipif));
ill = ipif->ipif_ill;
phyi = ill->ill_phyint;
ip_sioctl_flags_onoff(ipif, flags, &turn_on, &turn_off);
turn_on &= ~IFF_UP;
turn_off &= ~IFF_UP;
if ((turn_on|turn_off) & IFF_PHYINT_FLAGS)
phyint_flags_modified = B_TRUE;
mutex_enter(&ill->ill_lock);
mutex_enter(&phyi->phyint_lock);
ipif->ipif_flags |= (turn_on & IFF_LOGINT_FLAGS);
ipif->ipif_flags &= (~turn_off & IFF_LOGINT_FLAGS);
ill->ill_flags |= (turn_on & IFF_PHYINTINST_FLAGS);
ill->ill_flags &= (~turn_off & IFF_PHYINTINST_FLAGS);
phyi->phyint_flags |= (turn_on & IFF_PHYINT_FLAGS);
phyi->phyint_flags &= (~turn_off & IFF_PHYINT_FLAGS);
if (ipif->ipif_state_flags & IPIF_SET_LINKLOCAL) {
set_linklocal = B_TRUE;
ipif->ipif_state_flags &= ~IPIF_SET_LINKLOCAL;
}
mutex_exit(&ill->ill_lock);
mutex_exit(&phyi->phyint_lock);
if (set_linklocal)
(void) ipif_setlinklocal(ipif);
if ((turn_on|turn_off) & (PHYI_FAILED | PHYI_INACTIVE | PHYI_OFFLINE)) {
ASSERT(!IS_IPMP(ill));
if (IS_UNDER_IPMP(ill))
ipmp_phyint_refresh_active(phyi);
}
if ((turn_on|turn_off) & ILLF_NOACCEPT) {
err = ill_up_ipifs(ill, q, mp);
} else if ((flags & IFF_UP) && !(ipif->ipif_flags & IPIF_UP)) {
err = ipif_up(ipif, q, mp);
} else {
if (phyint_flags_modified) {
if (phyi->phyint_illv4 != NULL) {
ip_rts_ifmsg(phyi->phyint_illv4->
ill_ipif, RTSQ_DEFAULT);
}
if (phyi->phyint_illv6 != NULL) {
ip_rts_ifmsg(phyi->phyint_illv6->
ill_ipif, RTSQ_DEFAULT);
}
} else {
ip_rts_ifmsg(ipif, RTSQ_DEFAULT);
}
sctp_update_ipif(ipif, SCTP_IPIF_UPDATE);
}
ire_increment_multicast_generation(ill->ill_ipst, ill->ill_isv6);
return (err);
}
int
ip_sioctl_flags_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
uint64_t flags;
struct ifreq *ifr = if_req;
struct lifreq *lifr = if_req;
uint64_t turn_on, turn_off;
ip1dbg(("ip_sioctl_flags_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipip->ipi_cmd_type == IF_CMD) {
flags = (uint16_t)ifr->ifr_flags;
} else {
flags = lifr->lifr_flags;
}
ip_sioctl_flags_onoff(ipif, flags, &turn_on, &turn_off);
if (!((turn_on|turn_off) & ILLF_NOACCEPT))
(void) ipif_down_tail(ipif);
return (ip_sioctl_flags_tail(ipif, flags, q, mp));
}
int
ip_sioctl_get_flags(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ill_t *ill = ipif->ipif_ill;
phyint_t *phyi = ill->ill_phyint;
ip1dbg(("ip_sioctl_get_flags(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT((phyi->phyint_flags & ~(IFF_PHYINT_FLAGS)) == 0);
ASSERT((ill->ill_flags & ~(IFF_PHYINTINST_FLAGS)) == 0);
ASSERT((ipif->ipif_flags & ~(IFF_LOGINT_FLAGS)) == 0);
mutex_enter(&ill->ill_lock);
if (ipip->ipi_cmd_type == IF_CMD) {
struct ifreq *ifr = (struct ifreq *)if_req;
ifr->ifr_flags = ((ipif->ipif_flags |
ill->ill_flags | phyi->phyint_flags) & 0xffff);
} else {
struct lifreq *lifr = (struct lifreq *)if_req;
lifr->lifr_flags = ipif->ipif_flags |
ill->ill_flags | phyi->phyint_flags;
}
mutex_exit(&ill->ill_lock);
return (0);
}
int
ip_sioctl_mtu(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int mtu;
int ip_min_mtu;
struct ifreq *ifr;
struct lifreq *lifr;
ill_t *ill;
ip1dbg(("ip_sioctl_mtu(%s:%u %p)\n", ipif->ipif_ill->ill_name,
ipif->ipif_id, (void *)ipif));
if (ipip->ipi_cmd_type == IF_CMD) {
ifr = (struct ifreq *)if_req;
mtu = ifr->ifr_metric;
} else {
lifr = (struct lifreq *)if_req;
mtu = lifr->lifr_mtu;
}
if (ipif->ipif_id != 0)
return (EINVAL);
ill = ipif->ipif_ill;
if (ipif->ipif_isv6)
ip_min_mtu = IPV6_MIN_MTU;
else
ip_min_mtu = IP_MIN_MTU;
mutex_enter(&ill->ill_lock);
if (mtu > ill->ill_max_frag || mtu < ip_min_mtu) {
mutex_exit(&ill->ill_lock);
return (EINVAL);
}
if (ill->ill_mc_mtu > mtu)
ill->ill_mc_mtu = mtu;
ill->ill_mtu = mtu;
ill->ill_flags |= ILLF_FIXEDMTU;
mutex_exit(&ill->ill_lock);
dce_increment_all_generations(ill->ill_isv6, ill->ill_ipst);
if (IS_UNDER_IPMP(ill))
ipmp_illgrp_refresh_mtu(ill->ill_grp);
sctp_update_ipif(ipif, SCTP_IPIF_UPDATE);
return (0);
}
int
ip_sioctl_get_mtu(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct ifreq *ifr;
struct lifreq *lifr;
ip1dbg(("ip_sioctl_get_mtu(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipip->ipi_cmd_type == IF_CMD) {
ifr = (struct ifreq *)if_req;
ifr->ifr_metric = ipif->ipif_ill->ill_mtu;
} else {
lifr = (struct lifreq *)if_req;
lifr->lifr_mtu = ipif->ipif_ill->ill_mtu;
}
return (0);
}
int
ip_sioctl_brdaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ipaddr_t addr;
ire_t *ire;
ill_t *ill = ipif->ipif_ill;
ip_stack_t *ipst = ill->ill_ipst;
ip1dbg(("ip_sioctl_brdaddr(%s:%u)\n", ill->ill_name,
ipif->ipif_id));
ASSERT(IAM_WRITER_IPIF(ipif));
if (!(ipif->ipif_flags & IPIF_BROADCAST))
return (EADDRNOTAVAIL);
ASSERT(!(ipif->ipif_isv6));
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
addr = sin->sin_addr.s_addr;
if (ipif->ipif_flags & IPIF_UP) {
ire = ire_ftable_lookup_v4(addr, 0, 0, IRE_BROADCAST,
ill, ipif->ipif_zoneid, NULL,
(MATCH_IRE_ILL | MATCH_IRE_TYPE), 0, ipst, NULL);
if (ire == NULL) {
return (EINVAL);
} else {
ire_refrele(ire);
}
}
if (addr != ipif->ipif_brd_addr)
IN6_IPADDR_TO_V4MAPPED(addr, &ipif->ipif_v6brd_addr);
return (0);
}
int
ip_sioctl_get_brdaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ip1dbg(("ip_sioctl_get_brdaddr(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (!(ipif->ipif_flags & IPIF_BROADCAST))
return (EADDRNOTAVAIL);
ASSERT(!ipif->ipif_isv6);
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_brd_addr;
return (0);
}
int
ip_sioctl_netmask(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int err = 0;
in6_addr_t v6mask;
ip1dbg(("ip_sioctl_netmask(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
if (ipif->ipif_isv6) {
sin6_t *sin6;
if (sin->sin_family != AF_INET6)
return (EAFNOSUPPORT);
sin6 = (sin6_t *)sin;
v6mask = sin6->sin6_addr;
} else {
ipaddr_t mask;
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
mask = sin->sin_addr.s_addr;
if (!ip_contiguous_mask(ntohl(mask)))
return (ENOTSUP);
V4MASK_TO_V6(mask, v6mask);
}
if (!(ipif->ipif_flags & IPIF_UP) ||
IN6_ARE_ADDR_EQUAL(&v6mask, &ipif->ipif_v6net_mask) ||
(ipif->ipif_flags & IPIF_POINTOPOINT)) {
ipif->ipif_v6net_mask = v6mask;
if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0) {
V6_MASK_COPY(ipif->ipif_v6lcl_addr,
ipif->ipif_v6net_mask,
ipif->ipif_v6subnet);
}
return (0);
}
err = ipif_logical_down(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
err = ip_sioctl_netmask_tail(ipif, sin, q, mp);
return (err);
}
static int
ip_sioctl_netmask_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp)
{
in6_addr_t v6mask;
int err = 0;
ip1dbg(("ip_sioctl_netmask_tail(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_isv6) {
sin6_t *sin6;
sin6 = (sin6_t *)sin;
v6mask = sin6->sin6_addr;
} else {
ipaddr_t mask;
mask = sin->sin_addr.s_addr;
V4MASK_TO_V6(mask, v6mask);
}
ipif->ipif_v6net_mask = v6mask;
if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0) {
V6_MASK_COPY(ipif->ipif_v6lcl_addr, ipif->ipif_v6net_mask,
ipif->ipif_v6subnet);
}
err = ipif_up(ipif, q, mp);
if (err == 0 || err == EINPROGRESS) {
if (!ipif->ipif_isv6 && ipif->ipif_ill->ill_wq != NULL) {
ipif_mask_reply(ipif);
}
}
return (err);
}
int
ip_sioctl_netmask_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ip1dbg(("ip_sioctl_netmask_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
(void) ipif_down_tail(ipif);
return (ip_sioctl_netmask_tail(ipif, sin, q, mp));
}
int
ip_sioctl_get_netmask(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct lifreq *lifr = (struct lifreq *)if_req;
struct sockaddr_in6 *sin6 = (sin6_t *)sin;
ip1dbg(("ip_sioctl_get_netmask(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_isv6) {
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ipif->ipif_v6net_mask;
lifr->lifr_addrlen =
ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
} else {
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_net_mask;
if (ipip->ipi_cmd_type == LIF_CMD) {
lifr->lifr_addrlen =
ip_mask_to_plen(ipif->ipif_net_mask);
}
}
return (0);
}
int
ip_sioctl_metric(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ip1dbg(("ip_sioctl_metric(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (IS_UNDER_IPMP(ipif->ipif_ill))
return (EINVAL);
if (ipip->ipi_cmd_type == IF_CMD) {
struct ifreq *ifr;
ifr = (struct ifreq *)if_req;
ipif->ipif_ill->ill_metric = ifr->ifr_metric;
} else {
struct lifreq *lifr;
lifr = (struct lifreq *)if_req;
ipif->ipif_ill->ill_metric = lifr->lifr_metric;
}
return (0);
}
int
ip_sioctl_get_metric(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ip1dbg(("ip_sioctl_get_metric(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipip->ipi_cmd_type == IF_CMD) {
struct ifreq *ifr;
ifr = (struct ifreq *)if_req;
ifr->ifr_metric = ipif->ipif_ill->ill_metric;
} else {
struct lifreq *lifr;
lifr = (struct lifreq *)if_req;
lifr->lifr_metric = ipif->ipif_ill->ill_metric;
}
return (0);
}
int
ip_sioctl_muxid(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int arp_muxid;
ip1dbg(("ip_sioctl_muxid(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipip->ipi_cmd_type == IF_CMD) {
struct ifreq *ifr = (struct ifreq *)if_req;
ipif->ipif_ill->ill_muxid = ifr->ifr_ip_muxid;
arp_muxid = ifr->ifr_arp_muxid;
} else {
struct lifreq *lifr = (struct lifreq *)if_req;
ipif->ipif_ill->ill_muxid = lifr->lifr_ip_muxid;
arp_muxid = lifr->lifr_arp_muxid;
}
arl_set_muxid(ipif->ipif_ill, arp_muxid);
return (0);
}
int
ip_sioctl_get_muxid(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int arp_muxid = 0;
ip1dbg(("ip_sioctl_get_muxid(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
arp_muxid = arl_get_muxid(ipif->ipif_ill);
if (ipip->ipi_cmd_type == IF_CMD) {
struct ifreq *ifr = (struct ifreq *)if_req;
ifr->ifr_ip_muxid = ipif->ipif_ill->ill_muxid;
ifr->ifr_arp_muxid = arp_muxid;
} else {
struct lifreq *lifr = (struct lifreq *)if_req;
lifr->lifr_ip_muxid = ipif->ipif_ill->ill_muxid;
lifr->lifr_arp_muxid = arp_muxid;
}
return (0);
}
int
ip_sioctl_subnet(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int err = 0;
in6_addr_t v6addr;
in6_addr_t v6mask;
boolean_t need_up = B_FALSE;
int addrlen;
ip1dbg(("ip_sioctl_subnet(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
addrlen = ((struct lifreq *)if_req)->lifr_addrlen;
if (ipif->ipif_isv6) {
sin6_t *sin6;
if (sin->sin_family != AF_INET6)
return (EAFNOSUPPORT);
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
if (!ip_remote_addr_ok_v6(&v6addr, &ipv6_all_ones))
return (EADDRNOTAVAIL);
} else {
ipaddr_t addr;
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
addr = sin->sin_addr.s_addr;
if (!ip_addr_ok_v4(addr, 0xFFFFFFFF))
return (EADDRNOTAVAIL);
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
addrlen += IPV6_ABITS - IP_ABITS;
}
if (ip_plen_to_mask_v6(addrlen, &v6mask) == NULL)
return (EINVAL);
if (!V6_MASK_EQ(v6addr, v6mask, v6addr))
return (EINVAL);
if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6subnet, &v6addr) &&
IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6net_mask, &v6mask))
return (0);
if (ipif->ipif_flags & IPIF_UP) {
err = ipif_logical_down(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
need_up = B_TRUE;
}
err = ip_sioctl_subnet_tail(ipif, v6addr, v6mask, q, mp, need_up);
return (err);
}
static int
ip_sioctl_subnet_tail(ipif_t *ipif, in6_addr_t v6addr, in6_addr_t v6mask,
queue_t *q, mblk_t *mp, boolean_t need_up)
{
ill_t *ill = ipif->ipif_ill;
int err = 0;
ip1dbg(("ip_sioctl_subnet_tail(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
mutex_enter(&ill->ill_lock);
ipif->ipif_v6net_mask = v6mask;
if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0) {
V6_MASK_COPY(v6addr, ipif->ipif_v6net_mask,
ipif->ipif_v6subnet);
}
mutex_exit(&ill->ill_lock);
if (need_up) {
err = ipif_up(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
}
return (err);
}
int
ip_sioctl_subnet_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
int addrlen;
in6_addr_t v6addr;
in6_addr_t v6mask;
struct lifreq *lifr = (struct lifreq *)if_req;
ip1dbg(("ip_sioctl_subnet_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
(void) ipif_down_tail(ipif);
addrlen = lifr->lifr_addrlen;
if (ipif->ipif_isv6) {
sin6_t *sin6;
sin6 = (sin6_t *)sin;
v6addr = sin6->sin6_addr;
} else {
ipaddr_t addr;
addr = sin->sin_addr.s_addr;
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
addrlen += IPV6_ABITS - IP_ABITS;
}
(void) ip_plen_to_mask_v6(addrlen, &v6mask);
return (ip_sioctl_subnet_tail(ipif, v6addr, v6mask, q, mp, B_TRUE));
}
int
ip_sioctl_get_subnet(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct lifreq *lifr = (struct lifreq *)if_req;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin;
ip1dbg(("ip_sioctl_get_subnet(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
if (ipif->ipif_isv6) {
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ipif->ipif_v6subnet;
lifr->lifr_addrlen =
ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
} else {
*sin = sin_null;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipif->ipif_subnet;
lifr->lifr_addrlen = ip_mask_to_plen(ipif->ipif_net_mask);
}
return (0);
}
int
ip_sioctl_token(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipi, void *if_req)
{
ill_t *ill = ipif->ipif_ill;
int err;
in6_addr_t v6addr;
in6_addr_t v6mask;
boolean_t need_up = B_FALSE;
int i;
sin6_t *sin6 = (sin6_t *)sin;
struct lifreq *lifr = (struct lifreq *)if_req;
int addrlen;
ip1dbg(("ip_sioctl_token(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(IAM_WRITER_IPIF(ipif));
addrlen = lifr->lifr_addrlen;
if (ipif->ipif_id != 0)
return (EINVAL);
if (!ipif->ipif_isv6)
return (EINVAL);
if (addrlen > IPV6_ABITS)
return (EINVAL);
v6addr = sin6->sin6_addr;
if (ip_plen_to_mask_v6(IPV6_ABITS - addrlen, &v6mask) == NULL)
return (EINVAL);
for (i = 0; i < 4; i++) {
v6mask.s6_addr32[i] ^= (uint32_t)0xffffffff;
}
if (V6_MASK_EQ(v6addr, v6mask, ill->ill_token) &&
ill->ill_token_length == addrlen)
return (0);
if (ipif->ipif_flags & IPIF_UP) {
err = ipif_logical_down(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
need_up = B_TRUE;
}
err = ip_sioctl_token_tail(ipif, sin6, addrlen, q, mp, need_up);
return (err);
}
static int
ip_sioctl_token_tail(ipif_t *ipif, sin6_t *sin6, int addrlen, queue_t *q,
mblk_t *mp, boolean_t need_up)
{
in6_addr_t v6addr;
in6_addr_t v6mask;
ill_t *ill = ipif->ipif_ill;
int i;
int err = 0;
ip1dbg(("ip_sioctl_token_tail(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
v6addr = sin6->sin6_addr;
(void) ip_plen_to_mask_v6(IPV6_ABITS - addrlen, &v6mask);
for (i = 0; i < 4; i++)
v6mask.s6_addr32[i] ^= (uint32_t)0xffffffff;
mutex_enter(&ill->ill_lock);
V6_MASK_COPY(v6addr, v6mask, ill->ill_token);
ill->ill_token_length = addrlen;
ill->ill_manual_token = 1;
ipif_setlinklocal(ill->ill_ipif);
mutex_exit(&ill->ill_lock);
if (need_up) {
err = ipif_up(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
}
return (err);
}
int
ip_sioctl_get_token(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipi, void *if_req)
{
ill_t *ill;
sin6_t *sin6 = (sin6_t *)sin;
struct lifreq *lifr = (struct lifreq *)if_req;
ip1dbg(("ip_sioctl_get_token(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_id != 0)
return (EINVAL);
ill = ipif->ipif_ill;
if (!ill->ill_isv6)
return (ENXIO);
*sin6 = sin6_null;
sin6->sin6_family = AF_INET6;
ASSERT(!IN6_IS_ADDR_V4MAPPED(&ill->ill_token));
sin6->sin6_addr = ill->ill_token;
lifr->lifr_addrlen = ill->ill_token_length;
return (0);
}
int
ip_sioctl_lnkinfo(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipi, void *if_req)
{
ill_t *ill = ipif->ipif_ill;
int ip_min_mtu;
struct lifreq *lifr = (struct lifreq *)if_req;
lif_ifinfo_req_t *lir;
ip1dbg(("ip_sioctl_lnkinfo(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
lir = &lifr->lifr_ifinfo;
ASSERT(IAM_WRITER_IPIF(ipif));
if (ipif->ipif_id != 0)
return (EINVAL);
if (ipif->ipif_isv6)
ip_min_mtu = IPV6_MIN_MTU;
else
ip_min_mtu = IP_MIN_MTU;
if (lir->lir_maxmtu != 0 &&
(lir->lir_maxmtu > ill->ill_current_frag ||
lir->lir_maxmtu < ip_min_mtu))
return (EINVAL);
if (lir->lir_reachtime != 0 &&
lir->lir_reachtime > ND_MAX_REACHTIME)
return (EINVAL);
if (lir->lir_reachretrans != 0 &&
lir->lir_reachretrans > ND_MAX_REACHRETRANSTIME)
return (EINVAL);
mutex_enter(&ill->ill_lock);
if (lir->lir_maxmtu != 0)
ill->ill_user_mtu = lir->lir_maxmtu;
if (lir->lir_reachtime != 0)
ill->ill_reachable_time = lir->lir_reachtime;
if (lir->lir_reachretrans != 0)
ill->ill_reachable_retrans_time = lir->lir_reachretrans;
ill->ill_max_hops = lir->lir_maxhops;
ill->ill_max_buf = ND_MAX_Q;
if (!(ill->ill_flags & ILLF_FIXEDMTU) && ill->ill_user_mtu != 0) {
ill->ill_mtu = MIN(ill->ill_current_frag, ill->ill_user_mtu);
ill->ill_mc_mtu = MIN(ill->ill_mc_mtu, ill->ill_user_mtu);
}
mutex_exit(&ill->ill_lock);
if (!(ill->ill_flags & ILLF_FIXEDMTU) && (lir->lir_maxmtu != 0))
dce_increment_all_generations(ill->ill_isv6, ill->ill_ipst);
if (IS_UNDER_IPMP(ill))
ipmp_illgrp_refresh_mtu(ill->ill_grp);
return (0);
}
int
ip_sioctl_get_lnkinfo(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipi, void *if_req)
{
struct lif_ifinfo_req *lir;
ill_t *ill = ipif->ipif_ill;
ip1dbg(("ip_sioctl_get_lnkinfo(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipif->ipif_id != 0)
return (EINVAL);
lir = &((struct lifreq *)if_req)->lifr_ifinfo;
lir->lir_maxhops = ill->ill_max_hops;
lir->lir_reachtime = ill->ill_reachable_time;
lir->lir_reachretrans = ill->ill_reachable_retrans_time;
lir->lir_maxmtu = ill->ill_mtu;
return (0);
}
static ipaddr_t
ip_subnet_mask(ipaddr_t addr, ipif_t **ipifp, ip_stack_t *ipst)
{
ipaddr_t net_mask;
ill_t *ill;
ipif_t *ipif;
ill_walk_context_t ctx;
ipif_t *fallback_ipif = NULL;
net_mask = ip_net_mask(addr);
if (net_mask == 0) {
*ipifp = NULL;
return (0);
}
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (IPIF_IS_CONDEMNED(ipif))
continue;
if (!(ipif->ipif_flags & IPIF_UP))
continue;
if ((ipif->ipif_subnet & net_mask) ==
(addr & net_mask)) {
if (ipif->ipif_flags & IPIF_POINTOPOINT) {
if (fallback_ipif == NULL) {
ipif_refhold_locked(ipif);
fallback_ipif = ipif;
}
continue;
}
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
rw_exit(&ipst->ips_ill_g_lock);
if (fallback_ipif != NULL)
ipif_refrele(fallback_ipif);
*ipifp = ipif;
return (ipif->ipif_net_mask);
}
}
mutex_exit(&ill->ill_lock);
}
rw_exit(&ipst->ips_ill_g_lock);
*ipifp = fallback_ipif;
return ((fallback_ipif != NULL) ?
fallback_ipif->ipif_net_mask : net_mask);
}
static void
ip_wput_ioctl(queue_t *q, mblk_t *mp)
{
IOCP iocp;
ipft_t *ipft;
ipllc_t *ipllc;
mblk_t *mp1;
cred_t *cr;
int error = 0;
conn_t *connp;
ip1dbg(("ip_wput_ioctl"));
iocp = (IOCP)mp->b_rptr;
mp1 = mp->b_cont;
if (mp1 == NULL) {
iocp->ioc_error = EINVAL;
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_count = 0;
qreply(q, mp);
return;
}
iocp->ioc_error = EINVAL;
if (!pullupmsg(mp1, sizeof (ipllc->ipllc_cmd)))
goto done;
ipllc = (ipllc_t *)mp1->b_rptr;
for (ipft = ip_ioctl_ftbl; ipft->ipft_pfi; ipft++) {
if (ipllc->ipllc_cmd == ipft->ipft_cmd)
break;
}
cr = msg_getcred(mp, NULL);
if (cr == NULL)
cr = iocp->ioc_cr;
ASSERT(CONN_Q(q));
connp = Q_TO_CONN(q);
CONN_INC_REF(connp);
CONN_INC_IOCTLREF(connp);
if (ipft->ipft_pfi &&
((mp1->b_wptr - mp1->b_rptr) >= ipft->ipft_min_size ||
pullupmsg(mp1, ipft->ipft_min_size))) {
error = (*ipft->ipft_pfi)(q,
(ipft->ipft_flags & IPFT_F_SELF_REPLY) ? mp : mp1, cr);
}
if (ipft->ipft_flags & IPFT_F_SELF_REPLY) {
return;
}
CONN_DEC_IOCTLREF(connp);
CONN_OPER_PENDING_DONE(connp);
if (ipft->ipft_flags & IPFT_F_NO_REPLY) {
freemsg(mp);
return;
}
iocp->ioc_error = error;
done:
mp->b_datap->db_type = M_IOCACK;
if (iocp->ioc_error)
iocp->ioc_count = 0;
qreply(q, mp);
}
static void
ipif_assign_seqid(ipif_t *ipif)
{
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
ipif->ipif_seqid = atomic_inc_64_nv(&ipst->ips_ipif_g_seqid);
}
static void
ipif_clone(const ipif_t *sipif, ipif_t *dipif)
{
ASSERT(MUTEX_HELD(&sipif->ipif_ill->ill_lock));
ASSERT(MUTEX_HELD(&dipif->ipif_ill->ill_lock));
ASSERT(!(sipif->ipif_flags & (IPIF_UP|IPIF_DUPLICATE)));
ASSERT(!(dipif->ipif_flags & (IPIF_UP|IPIF_DUPLICATE)));
ASSERT(sipif->ipif_ire_type == dipif->ipif_ire_type);
dipif->ipif_flags = sipif->ipif_flags;
dipif->ipif_zoneid = sipif->ipif_zoneid;
dipif->ipif_v6subnet = sipif->ipif_v6subnet;
dipif->ipif_v6lcl_addr = sipif->ipif_v6lcl_addr;
dipif->ipif_v6net_mask = sipif->ipif_v6net_mask;
dipif->ipif_v6brd_addr = sipif->ipif_v6brd_addr;
dipif->ipif_v6pp_dst_addr = sipif->ipif_v6pp_dst_addr;
dipif->ipif_seqid = sipif->ipif_seqid;
dipif->ipif_state_flags = sipif->ipif_state_flags;
}
static void
ipif_transfer(ipif_t *sipif, ipif_t *dipif, ipif_t *virgipif)
{
ipsq_t *ipsq = sipif->ipif_ill->ill_phyint->phyint_ipsq;
ipxop_t *ipx = ipsq->ipsq_xop;
ASSERT(sipif != dipif);
ASSERT(sipif != virgipif);
GRAB_ILL_LOCKS(sipif->ipif_ill, dipif->ipif_ill);
ipif_clone(sipif, dipif);
if (virgipif != NULL) {
ipif_clone(virgipif, sipif);
mi_free(virgipif);
}
RELEASE_ILL_LOCKS(sipif->ipif_ill, dipif->ipif_ill);
if (ipx->ipx_current_ipif == sipif) {
ASSERT(ipx->ipx_pending_ipif == NULL);
mutex_enter(&ipx->ipx_lock);
ipx->ipx_current_ipif = dipif;
mutex_exit(&ipx->ipx_lock);
}
if (virgipif == NULL)
mi_free(sipif);
}
static int
is_lifname_valid(ill_t *ill, unsigned int ipif_id)
{
if (snprintf(NULL, 0, "%s:%d", ill->ill_name, ipif_id) >= LIFNAMSIZ)
return (ENAMETOOLONG);
if (ipif_id >= ill->ill_ipst->ips_ip_addrs_per_if)
return (ERANGE);
return (0);
}
static int
ipif_insert(ipif_t *ipif, boolean_t acquire_g_lock)
{
ill_t *ill;
ipif_t *tipif;
ipif_t **tipifp;
int id, err;
ip_stack_t *ipst;
ASSERT(ipif->ipif_ill->ill_net_type == IRE_LOOPBACK ||
IAM_WRITER_IPIF(ipif));
ill = ipif->ipif_ill;
ASSERT(ill != NULL);
ipst = ill->ill_ipst;
if (acquire_g_lock)
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
mutex_enter(&ill->ill_lock);
id = ipif->ipif_id;
tipifp = &(ill->ill_ipif);
if (id == -1) {
id = 0;
while ((tipif = *tipifp) != NULL) {
ASSERT(tipif->ipif_id >= id);
if (tipif->ipif_id != id)
break;
id++;
tipifp = &(tipif->ipif_next);
}
if ((err = is_lifname_valid(ill, id)) != 0) {
mutex_exit(&ill->ill_lock);
if (acquire_g_lock)
rw_exit(&ipst->ips_ill_g_lock);
return (err);
}
ipif->ipif_id = id;
} else if ((err = is_lifname_valid(ill, id)) == 0) {
while ((tipif = *tipifp) != NULL) {
ASSERT(tipif->ipif_id != id);
if (tipif->ipif_id > id)
break;
tipifp = &(tipif->ipif_next);
}
} else {
mutex_exit(&ill->ill_lock);
if (acquire_g_lock)
rw_exit(&ipst->ips_ill_g_lock);
return (err);
}
ASSERT(tipifp != &(ill->ill_ipif) || id == 0);
ipif->ipif_next = tipif;
*tipifp = ipif;
mutex_exit(&ill->ill_lock);
if (acquire_g_lock)
rw_exit(&ipst->ips_ill_g_lock);
return (0);
}
static void
ipif_remove(ipif_t *ipif)
{
ipif_t **ipifp;
ill_t *ill = ipif->ipif_ill;
ASSERT(RW_WRITE_HELD(&ill->ill_ipst->ips_ill_g_lock));
mutex_enter(&ill->ill_lock);
ipifp = &ill->ill_ipif;
for (; *ipifp != NULL; ipifp = &ipifp[0]->ipif_next) {
if (*ipifp == ipif) {
*ipifp = ipif->ipif_next;
break;
}
}
mutex_exit(&ill->ill_lock);
}
static ipif_t *
ipif_allocate(ill_t *ill, int id, uint_t ire_type, boolean_t initialize,
boolean_t insert, int *errorp)
{
int err;
ipif_t *ipif;
ip_stack_t *ipst = ill->ill_ipst;
ip1dbg(("ipif_allocate(%s:%d ill %p)\n",
ill->ill_name, id, (void *)ill));
ASSERT(ire_type == IRE_LOOPBACK || IAM_WRITER_ILL(ill));
if (errorp != NULL)
*errorp = 0;
if ((ipif = mi_alloc(sizeof (ipif_t), BPRI_MED)) == NULL) {
if (errorp != NULL)
*errorp = ENOMEM;
return (NULL);
}
*ipif = ipif_zero;
ipif->ipif_ill = ill;
ipif->ipif_id = id;
ipif->ipif_zoneid = ill->ill_zoneid;
ipif->ipif_refcnt = 0;
if (insert) {
if ((err = ipif_insert(ipif, ire_type != IRE_LOOPBACK)) != 0) {
mi_free(ipif);
if (errorp != NULL)
*errorp = err;
return (NULL);
}
id = ipif->ipif_id;
ASSERT(id >= 0);
}
if (ill->ill_name[0] != '\0')
ipif_assign_seqid(ipif);
if (id == 0 && IS_IPMP(ill)) {
if (ipmp_illgrp_create(ill) == NULL) {
if (insert) {
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
ipif_remove(ipif);
rw_exit(&ipst->ips_ill_g_lock);
}
mi_free(ipif);
if (errorp != NULL)
*errorp = ENOMEM;
return (NULL);
}
}
mutex_enter(&ill->ill_lock);
ipif->ipif_ire_type = ire_type;
if (ipif->ipif_isv6) {
ill->ill_flags |= ILLF_IPV6;
} else {
ipaddr_t inaddr_any = INADDR_ANY;
ill->ill_flags |= ILLF_IPV4;
IN6_IPADDR_TO_V4MAPPED(inaddr_any,
&ipif->ipif_v6lcl_addr);
IN6_IPADDR_TO_V4MAPPED(inaddr_any,
&ipif->ipif_v6subnet);
IN6_IPADDR_TO_V4MAPPED(inaddr_any,
&ipif->ipif_v6net_mask);
IN6_IPADDR_TO_V4MAPPED(inaddr_any,
&ipif->ipif_v6brd_addr);
IN6_IPADDR_TO_V4MAPPED(inaddr_any,
&ipif->ipif_v6pp_dst_addr);
}
if (!initialize)
goto out;
if (ill->ill_bcast_addr_length != 0 || IS_IPMP(ill)) {
ill->ill_flags |= ILLF_MULTICAST;
if (!ipif->ipif_isv6)
ipif->ipif_flags |= IPIF_BROADCAST;
} else {
if (ill->ill_net_type != IRE_LOOPBACK) {
if (ipif->ipif_isv6)
ill->ill_flags |= ILLF_NONUD;
else
ill->ill_flags |= ILLF_NOARP;
}
if (ill->ill_phys_addr_length == 0) {
if (IS_VNI(ill)) {
ipif->ipif_flags |= IPIF_NOXMIT;
} else {
ill->ill_flags |= ILLF_MULTICAST;
if (ill->ill_net_type != IRE_LOOPBACK)
ipif->ipif_flags |= IPIF_POINTOPOINT;
}
}
}
out:
mutex_exit(&ill->ill_lock);
return (ipif);
}
int
ipif_arp_down(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
int err = 0;
ip1dbg(("ipif_arp_down(%s:%u)\n", ill->ill_name, ipif->ipif_id));
ASSERT(IAM_WRITER_IPIF(ipif));
DTRACE_PROBE3(ipif__downup, char *, "ipif_arp_down",
ill_t *, ill, ipif_t *, ipif);
ipif_nce_down(ipif);
if (ill->ill_ipif_up_count == 0 && ill->ill_ipif_dup_count == 0 &&
!ill->ill_logical_down && ill->ill_net_type == IRE_IF_RESOLVER) {
if (IS_IPMP(ill))
ipmp_illgrp_refresh_arpent(ill->ill_grp);
err = arp_ll_down(ill);
}
return (err);
}
int
ipif_resolver_up(ipif_t *ipif, enum ip_resolver_action res_act)
{
ill_t *ill = ipif->ipif_ill;
int err;
boolean_t was_dup;
ip1dbg(("ipif_resolver_up(%s:%u) flags 0x%x\n",
ill->ill_name, ipif->ipif_id, (uint_t)ipif->ipif_flags));
ASSERT(IAM_WRITER_IPIF(ipif));
was_dup = B_FALSE;
if (res_act == Res_act_initial) {
ipif->ipif_addr_ready = 0;
mutex_enter(&ill->ill_lock);
if (ipif->ipif_flags & IPIF_DUPLICATE) {
ipif->ipif_flags &= ~IPIF_DUPLICATE;
ill->ill_ipif_dup_count--;
was_dup = B_TRUE;
}
mutex_exit(&ill->ill_lock);
}
if (ipif->ipif_recovery_id != 0)
(void) untimeout(ipif->ipif_recovery_id);
ipif->ipif_recovery_id = 0;
if (ill->ill_net_type != IRE_IF_RESOLVER) {
ipif->ipif_addr_ready = 1;
return (0);
}
if (ill->ill_isv6)
return (0);
err = ipif_arp_up(ipif, res_act, was_dup);
return (err);
}
static void
ipif_nce_start_dad(ipif_t *ipif)
{
ncec_t *ncec;
ill_t *ill = ipif->ipif_ill;
boolean_t isv6 = ill->ill_isv6;
if (isv6) {
ncec = ncec_lookup_illgrp_v6(ipif->ipif_ill,
&ipif->ipif_v6lcl_addr);
} else {
ipaddr_t v4addr;
if (ill->ill_net_type != IRE_IF_RESOLVER ||
(ipif->ipif_flags & IPIF_UNNUMBERED) ||
ipif->ipif_lcl_addr == INADDR_ANY) {
ipif_mask_reply(ipif);
ipif_up_notify(ipif);
ipif->ipif_addr_ready = 1;
return;
}
IN6_V4MAPPED_TO_IPADDR(&ipif->ipif_v6lcl_addr, v4addr);
ncec = ncec_lookup_illgrp_v4(ipif->ipif_ill, &v4addr);
}
if (ncec == NULL) {
ip1dbg(("couldn't find ncec for ipif %p leaving !ready\n",
(void *)ipif));
return;
}
if (!nce_restart_dad(ncec)) {
ipif_up_notify(ipif);
ipif->ipif_addr_ready = 1;
}
ncec_refrele(ncec);
}
void
ill_restart_dad(ill_t *ill, boolean_t went_up)
{
ipif_t *ipif;
if (ill == NULL)
return;
if (!ill->ill_isv6 && arp_no_defense) {
ip_rts_ifmsg(ill->ill_ipif, RTSQ_DEFAULT);
return;
}
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (went_up) {
if (ipif->ipif_flags & IPIF_UP) {
ipif_nce_start_dad(ipif);
} else if (ipif->ipif_flags & IPIF_DUPLICATE) {
ipif_do_recovery(ipif);
} else {
if (ipif == ill->ill_ipif) {
ip_rts_ifmsg(ill->ill_ipif,
RTSQ_DEFAULT);
}
}
} else {
ipif->ipif_addr_ready = 0;
}
}
if (!went_up)
ip_rts_ifmsg(ill->ill_ipif, RTSQ_DEFAULT);
}
static void
ipsq_delete(ipsq_t *ipsq)
{
ipxop_t *ipx = ipsq->ipsq_xop;
ipsq->ipsq_ipst = NULL;
ASSERT(ipsq->ipsq_phyint == NULL);
ASSERT(ipsq->ipsq_xop != NULL);
ASSERT(ipsq->ipsq_xopq_mphead == NULL && ipx->ipx_mphead == NULL);
ASSERT(ipx->ipx_pending_mp == NULL);
kmem_free(ipsq, sizeof (ipsq_t));
}
static int
ill_up_ipifs_on_ill(ill_t *ill, queue_t *q, mblk_t *mp)
{
int err = 0;
ipif_t *ipif;
if (ill == NULL)
return (0);
ASSERT(IAM_WRITER_ILL(ill));
ill->ill_up_ipifs = B_TRUE;
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (ipif->ipif_was_up) {
if (!(ipif->ipif_flags & IPIF_UP))
err = ipif_up(ipif, q, mp);
ipif->ipif_was_up = B_FALSE;
if (err != 0) {
ASSERT(err == EINPROGRESS);
return (err);
}
}
}
ill->ill_up_ipifs = B_FALSE;
return (0);
}
int
ill_up_ipifs(ill_t *ill, queue_t *q, mblk_t *mp)
{
int err;
ASSERT(IAM_WRITER_ILL(ill));
if (ill->ill_replumbing) {
ill->ill_replumbing = 0;
if (!ill->ill_isv6)
arp_send_replumb_conf(ill);
}
err = ill_up_ipifs_on_ill(ill->ill_phyint->phyint_illv4, q, mp);
if (err != 0)
return (err);
return (ill_up_ipifs_on_ill(ill->ill_phyint->phyint_illv6, q, mp));
}
static void
ill_down_ipifs(ill_t *ill, boolean_t logical)
{
ipif_t *ipif;
ASSERT(IAM_WRITER_ILL(ill));
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (ipif->ipif_flags & IPIF_UP)
ipif->ipif_was_up = B_TRUE;
if (logical) {
(void) ipif_logical_down(ipif, NULL, NULL);
ipif_non_duplicate(ipif);
(void) ipif_down_tail(ipif);
} else {
(void) ipif_down(ipif, NULL, NULL);
}
}
}
void
ip_update_source_selection(ip_stack_t *ipst)
{
if (atomic_inc_32_nv(&ipst->ips_src_generation) ==
SRC_GENERATION_VERIFY)
atomic_inc_32(&ipst->ips_src_generation);
}
static void
ip_join_illgrps(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy)
{
ill_t *ill = q->q_ptr;
phyint_t *phyi = ill->ill_phyint;
ipmp_grp_t *grp = phyi->phyint_grp;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(!IS_IPMP(ill) && grp != NULL);
ASSERT(IAM_WRITER_IPSQ(ipsq));
if (phyi->phyint_illv4 != NULL) {
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
VERIFY(grp->gr_pendv4-- > 0);
rw_exit(&ipst->ips_ipmp_lock);
ipmp_ill_join_illgrp(phyi->phyint_illv4, grp->gr_v4);
}
if (phyi->phyint_illv6 != NULL) {
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
VERIFY(grp->gr_pendv6-- > 0);
rw_exit(&ipst->ips_ipmp_lock);
ipmp_ill_join_illgrp(phyi->phyint_illv6, grp->gr_v6);
}
freemsg(mp);
}
int
ip_sioctl_groupname(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifreq *lifr = ifreq;
ill_t *ill = ipif->ipif_ill;
ip_stack_t *ipst = ill->ill_ipst;
phyint_t *phyi = ill->ill_phyint;
ipmp_grp_t *grp = phyi->phyint_grp;
mblk_t *ipsq_mp;
int err = 0;
ASSERT(IAM_WRITER_ILL(ill));
if (ipif->ipif_id != 0 || ill->ill_usesrc_grp_next != NULL ||
(phyi->phyint_flags & PHYI_VIRTUAL))
return (EINVAL);
lifr->lifr_groupname[LIFGRNAMSIZ - 1] = '\0';
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
if (grp != NULL && strcmp(grp->gr_name, lifr->lifr_groupname) == 0)
goto unlock;
if (IS_IPMP(ill)) {
err = ipmp_grp_rename(grp, lifr->lifr_groupname);
goto unlock;
}
if (lifr->lifr_groupname[0] != '\0') {
if (IS_UNDER_IPMP(ill)) {
err = EALREADY;
goto unlock;
}
grp = ipmp_grp_lookup(lifr->lifr_groupname, ipst);
if (grp == NULL) {
err = ENOENT;
goto unlock;
}
if ((err = ipmp_grp_vet_phyint(grp, phyi)) != 0)
goto unlock;
if ((ipsq_mp = allocb(0, BPRI_MED)) == NULL) {
err = ENOMEM;
goto unlock;
}
if (phyi->phyint_illv4 != NULL)
grp->gr_pendv4++;
if (phyi->phyint_illv6 != NULL)
grp->gr_pendv6++;
rw_exit(&ipst->ips_ipmp_lock);
ipmp_phyint_join_grp(phyi, grp);
ill_refhold(ill);
qwriter_ip(ill, ill->ill_rq, ipsq_mp, ip_join_illgrps,
SWITCH_OP, B_FALSE);
return (0);
} else {
rw_exit(&ipst->ips_ipmp_lock);
if (IS_UNDER_IPMP(ill))
ipmp_phyint_leave_grp(phyi);
return (0);
}
unlock:
rw_exit(&ipst->ips_ipmp_lock);
return (err);
}
int
ip_sioctl_get_binding(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
ill_t *ill;
struct lifreq *lifr = ifreq;
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
if (!IS_IPMP(ipif->ipif_ill))
return (EINVAL);
rw_enter(&ipst->ips_ipmp_lock, RW_READER);
if ((ill = ipif->ipif_bound_ill) == NULL)
lifr->lifr_binding[0] = '\0';
else
(void) strlcpy(lifr->lifr_binding, ill->ill_name, LIFNAMSIZ);
rw_exit(&ipst->ips_ipmp_lock);
return (0);
}
int
ip_sioctl_get_groupname(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
ipmp_grp_t *grp;
struct lifreq *lifr = ifreq;
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
rw_enter(&ipst->ips_ipmp_lock, RW_READER);
if ((grp = ipif->ipif_ill->ill_phyint->phyint_grp) == NULL)
lifr->lifr_groupname[0] = '\0';
else
(void) strlcpy(lifr->lifr_groupname, grp->gr_name, LIFGRNAMSIZ);
rw_exit(&ipst->ips_ipmp_lock);
return (0);
}
int
ip_sioctl_groupinfo(ipif_t *dummy_ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy)
{
ipmp_grp_t *grp;
lifgroupinfo_t *lifgr;
ip_stack_t *ipst = CONNQ_TO_IPST(q);
lifgr = (lifgroupinfo_t *)mp->b_cont->b_cont->b_rptr;
lifgr->gi_grname[LIFGRNAMSIZ - 1] = '\0';
rw_enter(&ipst->ips_ipmp_lock, RW_READER);
if ((grp = ipmp_grp_lookup(lifgr->gi_grname, ipst)) == NULL) {
rw_exit(&ipst->ips_ipmp_lock);
return (ENOENT);
}
ipmp_grp_info(grp, lifgr);
rw_exit(&ipst->ips_ipmp_lock);
return (0);
}
static void
ill_dl_down(ill_t *ill)
{
DTRACE_PROBE2(ill__downup, char *, "ill_dl_down", ill_t *, ill);
mblk_t *mp = ill->ill_unbind_mp;
ip1dbg(("ill_dl_down(%s)\n", ill->ill_name));
if (!ill->ill_replumbing) {
update_conn_ill(ill, ill->ill_ipst);
} else {
ill_leave_multicast(ill);
}
ill->ill_unbind_mp = NULL;
if (mp != NULL) {
ip1dbg(("ill_dl_down: %s (%u) for %s\n",
dl_primstr(*(int *)mp->b_rptr), *(int *)mp->b_rptr,
ill->ill_name));
mutex_enter(&ill->ill_lock);
ill->ill_state_flags |= ILL_DL_UNBIND_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
if (ill->ill_state_flags & ILL_CONDEMNED)
ill_capability_dld_disable(ill);
ill_capability_reset(ill, B_FALSE);
ill_dlpi_send(ill, mp);
}
mutex_enter(&ill->ill_lock);
ill->ill_dl_up = 0;
ill_nic_event_dispatch(ill, 0, NE_DOWN, NULL, 0);
mutex_exit(&ill->ill_lock);
}
void
ill_dlpi_dispatch(ill_t *ill, mblk_t *mp)
{
union DL_primitives *dlp;
t_uscalar_t prim;
boolean_t waitack = B_FALSE;
ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
dlp = (union DL_primitives *)mp->b_rptr;
prim = dlp->dl_primitive;
ip1dbg(("ill_dlpi_dispatch: sending %s (%u) to %s\n",
dl_primstr(prim), prim, ill->ill_name));
switch (prim) {
case DL_PHYS_ADDR_REQ:
{
dl_phys_addr_req_t *dlpap = (dl_phys_addr_req_t *)mp->b_rptr;
ill->ill_phys_addr_pend = dlpap->dl_addr_type;
break;
}
case DL_BIND_REQ:
mutex_enter(&ill->ill_lock);
ill->ill_state_flags &= ~ILL_DL_UNBIND_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
break;
}
mutex_enter(&ill->ill_lock);
if (!(ill->ill_state_flags & ILL_CONDEMNED) ||
(prim == DL_UNBIND_REQ)) {
ill->ill_dlpi_pending = prim;
waitack = B_TRUE;
}
mutex_exit(&ill->ill_lock);
DTRACE_PROBE3(ill__dlpi, char *, "ill_dlpi_dispatch",
char *, dl_primstr(prim), ill_t *, ill);
putnext(ill->ill_wq, mp);
if (waitack && prim == DL_NOTIFY_CONF)
ill_dlpi_done(ill, prim);
}
static void
ill_dlpi_send_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
{
ill_dlpi_send(q->q_ptr, mp);
}
void
ill_dlpi_send(ill_t *ill, mblk_t *mp)
{
mblk_t **mpp;
ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
if (!IAM_WRITER_ILL(ill)) {
ill_refhold(ill);
qwriter_ip(ill, ill->ill_wq, mp, ill_dlpi_send_writer,
NEW_OP, B_TRUE);
return;
}
mutex_enter(&ill->ill_lock);
if (ill->ill_dlpi_pending != DL_PRIM_INVAL) {
mpp = &ill->ill_dlpi_deferred;
while (*mpp != NULL)
mpp = &((*mpp)->b_next);
ip1dbg(("ill_dlpi_send: deferring request for %s "
"while %s pending\n", ill->ill_name,
dl_primstr(ill->ill_dlpi_pending)));
*mpp = mp;
mutex_exit(&ill->ill_lock);
return;
}
mutex_exit(&ill->ill_lock);
ill_dlpi_dispatch(ill, mp);
}
void
ill_capability_send(ill_t *ill, mblk_t *mp)
{
ill->ill_capab_pending_cnt++;
ill_dlpi_send(ill, mp);
}
void
ill_capability_done(ill_t *ill)
{
ASSERT(ill->ill_capab_pending_cnt != 0);
ill_dlpi_done(ill, DL_CAPABILITY_REQ);
ill->ill_capab_pending_cnt--;
if (ill->ill_capab_pending_cnt == 0 &&
ill->ill_dlpi_capab_state == IDCS_OK)
ill_capability_reset_alloc(ill);
}
void
ill_dlpi_send_deferred(ill_t *ill)
{
mblk_t *mp, *nextmp;
mutex_enter(&ill->ill_lock);
ill->ill_dlpi_pending = DL_PRIM_INVAL;
mp = ill->ill_dlpi_deferred;
ill->ill_dlpi_deferred = NULL;
mutex_exit(&ill->ill_lock);
for (; mp != NULL; mp = nextmp) {
nextmp = mp->b_next;
mp->b_next = NULL;
ill_dlpi_send(ill, mp);
}
}
static void
ill_dlpi_clear_deferred(ill_t *ill)
{
mblk_t *mp, *nextmp;
mutex_enter(&ill->ill_lock);
ill->ill_dlpi_pending = DL_PRIM_INVAL;
mp = ill->ill_dlpi_deferred;
ill->ill_dlpi_deferred = NULL;
mutex_exit(&ill->ill_lock);
for (; mp != NULL; mp = nextmp) {
nextmp = mp->b_next;
inet_freemsg(mp);
}
}
boolean_t
ill_dlpi_pending(ill_t *ill, t_uscalar_t prim)
{
t_uscalar_t pending;
mutex_enter(&ill->ill_lock);
if (ill->ill_dlpi_pending == prim) {
mutex_exit(&ill->ill_lock);
return (B_TRUE);
}
if (ill->ill_state_flags & ILL_CONDEMNED) {
mutex_exit(&ill->ill_lock);
return (B_FALSE);
}
pending = ill->ill_dlpi_pending;
mutex_exit(&ill->ill_lock);
if (pending == DL_PRIM_INVAL) {
(void) mi_strlog(ill->ill_rq, 1, SL_CONSOLE|SL_ERROR|SL_TRACE,
"received unsolicited ack for %s on %s\n",
dl_primstr(prim), ill->ill_name);
} else {
(void) mi_strlog(ill->ill_rq, 1, SL_CONSOLE|SL_ERROR|SL_TRACE,
"received unexpected ack for %s on %s (expecting %s)\n",
dl_primstr(prim), ill->ill_name, dl_primstr(pending));
}
return (B_FALSE);
}
void
ill_dlpi_done(ill_t *ill, t_uscalar_t prim)
{
mblk_t *mp;
ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
ipxop_t *ipx = ipsq->ipsq_xop;
ASSERT(IAM_WRITER_IPSQ(ipsq));
mutex_enter(&ill->ill_lock);
ASSERT(prim != DL_PRIM_INVAL);
ASSERT(ill->ill_dlpi_pending == prim);
ip1dbg(("ill_dlpi_done: %s has completed %s (%u)\n", ill->ill_name,
dl_primstr(ill->ill_dlpi_pending), ill->ill_dlpi_pending));
if ((mp = ill->ill_dlpi_deferred) == NULL) {
ill->ill_dlpi_pending = DL_PRIM_INVAL;
if (ipx->ipx_current_done) {
mutex_enter(&ipx->ipx_lock);
ipx->ipx_current_ipif = NULL;
mutex_exit(&ipx->ipx_lock);
}
cv_signal(&ill->ill_cv);
mutex_exit(&ill->ill_lock);
return;
}
ill->ill_dlpi_deferred = mp->b_next;
mp->b_next = NULL;
mutex_exit(&ill->ill_lock);
ill_dlpi_dispatch(ill, mp);
}
void
ill_dlpi_queue(ill_t *ill, mblk_t *mp)
{
mblk_t **mpp;
ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
mutex_enter(&ill->ill_lock);
mpp = &ill->ill_dlpi_deferred;
while (*mpp != NULL)
mpp = &((*mpp)->b_next);
*mpp = mp;
mutex_exit(&ill->ill_lock);
}
void
ill_dlpi_send_queued(ill_t *ill)
{
mblk_t *mp;
union DL_primitives *dlp;
t_uscalar_t prim;
ill_t *release_ill = NULL;
if (IS_IPMP(ill)) {
release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
if (release_ill == NULL) {
return;
}
ill = release_ill;
}
mutex_enter(&ill->ill_lock);
while ((mp = ill->ill_dlpi_deferred) != NULL) {
if (ill->ill_dlpi_pending != DL_PRIM_INVAL) {
mutex_exit(&ill->ill_lock);
goto done;
}
ill->ill_dlpi_deferred = mp->b_next;
mp->b_next = NULL;
if (!ill->ill_dl_up) {
freemsg(mp);
continue;
}
dlp = (union DL_primitives *)mp->b_rptr;
prim = dlp->dl_primitive;
if (!(ill->ill_state_flags & ILL_CONDEMNED) ||
(prim == DL_UNBIND_REQ)) {
ill->ill_dlpi_pending = prim;
}
mutex_exit(&ill->ill_lock);
DTRACE_PROBE3(ill__dlpi, char *, "ill_dlpi_send_queued",
char *, dl_primstr(prim), ill_t *, ill);
putnext(ill->ill_wq, mp);
mutex_enter(&ill->ill_lock);
}
mutex_exit(&ill->ill_lock);
done:
if (release_ill != NULL)
ill_refrele(release_ill);
}
void
ill_mcast_queue(ill_t *ill, mblk_t *mp)
{
mblk_t **mpp;
ill_t *release_ill = NULL;
ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
if (IS_IPMP(ill)) {
release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
if (release_ill == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - no cast_ill",
mp, ill);
freemsg(mp);
return;
}
ill = release_ill;
}
mutex_enter(&ill->ill_lock);
mpp = &ill->ill_mcast_deferred;
while (*mpp != NULL)
mpp = &((*mpp)->b_next);
*mpp = mp;
mutex_exit(&ill->ill_lock);
if (release_ill != NULL)
ill_refrele(release_ill);
}
void
ill_mcast_send_queued(ill_t *ill)
{
mblk_t *mp;
ip_xmit_attr_t ixas;
ill_t *release_ill = NULL;
if (IS_IPMP(ill)) {
release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
if (release_ill == NULL) {
return;
}
ill = release_ill;
}
bzero(&ixas, sizeof (ixas));
ixas.ixa_zoneid = ALL_ZONES;
ixas.ixa_cred = kcred;
ixas.ixa_cpid = NOPID;
ixas.ixa_tsl = NULL;
ixas.ixa_ifindex = ill->ill_phyint->phyint_ifindex;
ixas.ixa_ipst = ill->ill_ipst;
mutex_enter(&ill->ill_lock);
while ((mp = ill->ill_mcast_deferred) != NULL) {
ill->ill_mcast_deferred = mp->b_next;
mp->b_next = NULL;
if (!ill->ill_dl_up) {
freemsg(mp);
continue;
}
mutex_enter(&ill->ill_phyint->phyint_lock);
if (IS_UNDER_IPMP(ill) && !ipmp_ill_is_active(ill)) {
mutex_exit(&ill->ill_phyint->phyint_lock);
freemsg(mp);
continue;
}
mutex_exit(&ill->ill_phyint->phyint_lock);
mutex_exit(&ill->ill_lock);
if (ill->ill_isv6) {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
ixas.ixa_multicast_ttl = ip6h->ip6_hops;
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
} else {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
ixas.ixa_multicast_ttl = ipha->ipha_ttl;
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
ixas.ixa_flags &= ~IXAF_SET_ULP_CKSUM;
}
ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE;
ixas.ixa_flags |= IXAF_MULTICAST_LOOP | IXAF_SET_SOURCE;
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
mutex_enter(&ill->ill_lock);
}
mutex_exit(&ill->ill_lock);
if (release_ill != NULL)
ill_refrele(release_ill);
}
int
ipif_down(ipif_t *ipif, queue_t *q, mblk_t *mp)
{
ill_t *ill = ipif->ipif_ill;
conn_t *connp;
boolean_t success;
boolean_t ipif_was_up = B_FALSE;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(IAM_WRITER_IPIF(ipif));
ip1dbg(("ipif_down(%s:%u)\n", ill->ill_name, ipif->ipif_id));
DTRACE_PROBE3(ipif__downup, char *, "ipif_down",
ill_t *, ill, ipif_t *, ipif);
if (ipif->ipif_flags & IPIF_UP) {
mutex_enter(&ill->ill_lock);
ipif->ipif_flags &= ~IPIF_UP;
ASSERT(ill->ill_ipif_up_count > 0);
--ill->ill_ipif_up_count;
mutex_exit(&ill->ill_lock);
ipif_was_up = B_TRUE;
sctp_update_ipif(ipif, SCTP_IPIF_DOWN);
ill_nic_event_dispatch(ipif->ipif_ill,
MAP_IPIF_ID(ipif->ipif_id), NE_LIF_DOWN, NULL, 0);
}
if (ill->ill_wq != NULL && !ill->ill_logical_down &&
ill->ill_ipif_up_count == 0 && ill->ill_ipif_dup_count == 0 &&
ill->ill_dl_up) {
ill->ill_state_flags |= ILL_DOWN_IN_PROGRESS;
}
ipif_multicast_down(ipif);
if (ipif_was_up && !IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr) &&
!IN6_IS_ADDR_V4MAPPED_ANY(&ipif->ipif_v6lcl_addr) &&
!(ipif->ipif_flags & IPIF_NOLOCAL)) {
int err;
err = ip_srcid_remove(&ipif->ipif_v6lcl_addr,
ipif->ipif_zoneid, ipst);
if (err != 0) {
ip0dbg(("ipif_down: srcid_remove %d\n", err));
}
}
if (ipif_was_up) {
if (ipif->ipif_isv6)
ipif_delete_ires_v6(ipif);
else
ipif_delete_ires_v4(ipif);
}
if (ipif_was_up && ill->ill_ipif_up_count == 0) {
if (IS_UNDER_IPMP(ill))
ipmp_ill_refresh_active(ill);
}
ipif_nce_down(ipif);
if (ill->ill_ipif_up_count == 0 && ill->ill_ipif_dup_count == 0)
ire_walk_ill(0, 0, ill_downi, ill, ill);
ipcl_walk(conn_ixa_cleanup, (void *)B_TRUE, ipst);
if (mp == NULL) {
ASSERT(q == NULL);
return (0);
}
if (CONN_Q(q)) {
connp = Q_TO_CONN(q);
mutex_enter(&connp->conn_lock);
} else {
connp = NULL;
}
mutex_enter(&ill->ill_lock);
if (ipif_is_quiescent(ipif)) {
mutex_exit(&ill->ill_lock);
if (connp != NULL)
mutex_exit(&connp->conn_lock);
return (0);
}
ip1dbg(("ipif_down: need to wait, adding pending mp %s ill %p",
ill->ill_name, (void *)ill));
success = ipsq_pending_mp_add(connp, ipif, q, mp, IPIF_DOWN);
if (!success) {
ASSERT(connp != NULL);
mutex_exit(&ill->ill_lock);
mutex_exit(&connp->conn_lock);
return (EINTR);
}
mutex_exit(&ill->ill_lock);
if (connp != NULL)
mutex_exit(&connp->conn_lock);
return (EINPROGRESS);
}
int
ipif_down_tail(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
int err = 0;
DTRACE_PROBE3(ipif__downup, char *, "ipif_down_tail",
ill_t *, ill, ipif_t *, ipif);
if (ill->ill_wq != NULL && !ill->ill_logical_down &&
ill->ill_ipif_up_count == 0 && ill->ill_ipif_dup_count == 0 &&
ill->ill_dl_up) {
ill_dl_down(ill);
}
if (!ipif->ipif_isv6)
err = ipif_arp_down(ipif);
ill->ill_logical_down = 0;
ip_rts_ifmsg(ipif, RTSQ_DEFAULT);
ip_rts_newaddrmsg(RTM_DELETE, 0, ipif, RTSQ_DEFAULT);
return (err);
}
static int
ipif_logical_down(ipif_t *ipif, queue_t *q, mblk_t *mp)
{
DTRACE_PROBE3(ipif__downup, char *, "ipif_logical_down",
ill_t *, ipif->ipif_ill, ipif_t *, ipif);
ipif->ipif_ill->ill_logical_down = 1;
return (ipif_down(ipif, q, mp));
}
static void
ipif_free(ipif_t *ipif)
{
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
ASSERT(IAM_WRITER_IPIF(ipif));
if (ipif->ipif_recovery_id != 0)
(void) untimeout(ipif->ipif_recovery_id);
ipif->ipif_recovery_id = 0;
(void) ipif_down(ipif, NULL, NULL);
if (ipif->ipif_recovery_id != 0)
(void) untimeout(ipif->ipif_recovery_id);
ipif->ipif_recovery_id = 0;
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
reset_mrt_vif_ipif(ipif);
if (ipif->ipif_ill->ill_src_ipif == ipif)
ipif->ipif_ill->ill_src_ipif = NULL;
rw_exit(&ipst->ips_ill_g_lock);
}
static void
ipif_free_tail(ipif_t *ipif)
{
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
#ifdef DEBUG
ipif_trace_cleanup(ipif);
#endif
sctp_update_ipif(ipif, SCTP_IPIF_REMOVE);
ip_rts_newaddrmsg(RTM_FREEADDR, 0, ipif, RTSQ_DEFAULT);
ipif_remove(ipif);
rw_exit(&ipst->ips_ill_g_lock);
ASSERT(!(ipif->ipif_flags & (IPIF_UP | IPIF_DUPLICATE)));
ASSERT(ipif->ipif_recovery_id == 0);
ASSERT(ipif->ipif_ire_local == NULL);
ASSERT(ipif->ipif_ire_if == NULL);
mi_free(ipif);
}
void
ipif_get_name(const ipif_t *ipif, char *buf, int len)
{
char lbuf[LIFNAMSIZ];
char *name;
size_t name_len;
buf[0] = '\0';
name = ipif->ipif_ill->ill_name;
name_len = ipif->ipif_ill->ill_name_length;
if (ipif->ipif_id != 0) {
(void) sprintf(lbuf, "%s%c%d", name, IPIF_SEPARATOR_CHAR,
ipif->ipif_id);
name = lbuf;
name_len = mi_strlen(name) + 1;
}
len -= 1;
buf[len] = '\0';
len = MIN(len, name_len);
bcopy(name, buf, len);
}
void
ill_get_name(const ill_t *ill, char *buf, int len)
{
char *name;
size_t name_len;
name = ill->ill_name;
name_len = ill->ill_name_length;
len -= 1;
buf[len] = '\0';
len = MIN(len, name_len);
bcopy(name, buf, len);
}
static ipif_t *
ipif_lookup_on_name(char *name, size_t namelen, boolean_t do_alloc,
boolean_t *exists, boolean_t isv6, zoneid_t zoneid, ip_stack_t *ipst)
{
char *cp;
char *endp;
long id;
ill_t *ill;
ipif_t *ipif;
uint_t ire_type;
boolean_t did_alloc = B_FALSE;
char last;
ASSERT(!do_alloc || zoneid != ALL_ZONES);
if (namelen == 0) {
return (NULL);
}
*exists = B_FALSE;
endp = &name[namelen];
for (cp = endp; --cp > name; ) {
if (*cp == IPIF_SEPARATOR_CHAR)
break;
}
if (*cp == IPIF_SEPARATOR_CHAR) {
if (&cp[2] < endp && cp[1] == '0') {
return (NULL);
}
}
if (cp <= name) {
cp = endp;
}
last = *cp;
*cp = '\0';
ill = ill_lookup_on_name(name, do_alloc, isv6,
&did_alloc, ipst);
*cp = last;
if (ill == NULL)
return (NULL);
id = 0;
if (cp < endp && *endp == '\0') {
cp++;
if (ddi_strtol(cp, NULL, 0, &id) != 0) {
ill_refrele(ill);
return (NULL);
}
}
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (ipif->ipif_id == id) {
if (zoneid != ALL_ZONES &&
zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES) {
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
return (NULL);
}
if (IPIF_CAN_LOOKUP(ipif)) {
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
if (!did_alloc)
*exists = B_TRUE;
ill_refrele(ill);
return (ipif);
}
}
}
if (!do_alloc) {
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
return (NULL);
}
if (ill->ill_net_type == IRE_LOOPBACK && id == 0)
ire_type = IRE_LOOPBACK;
else
ire_type = IRE_LOCAL;
ipif = ipif_allocate(ill, id, ire_type, B_TRUE, B_TRUE, NULL);
if (ipif != NULL)
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
return (ipif);
}
static ipif_t *
ipif_lookup_on_name_async(char *name, size_t namelen, boolean_t isv6,
zoneid_t zoneid, queue_t *q, mblk_t *mp, ipsq_func_t func, int *error,
ip_stack_t *ipst)
{
char *cp;
char *endp;
long id;
ill_t *ill;
ipif_t *ipif;
boolean_t did_alloc = B_FALSE;
ipsq_t *ipsq;
if (error != NULL)
*error = 0;
if (namelen == 0) {
if (error != NULL)
*error = ENXIO;
return (NULL);
}
endp = &name[namelen];
for (cp = endp; --cp > name; ) {
if (*cp == IPIF_SEPARATOR_CHAR)
break;
}
if (*cp == IPIF_SEPARATOR_CHAR) {
if (&cp[2] < endp && cp[1] == '0') {
if (error != NULL)
*error = EINVAL;
return (NULL);
}
}
if (cp <= name) {
cp = endp;
} else {
*cp = '\0';
}
ill = ill_lookup_on_name(name, B_FALSE, isv6, &did_alloc, ipst);
if (cp != endp)
*cp = IPIF_SEPARATOR_CHAR;
if (ill == NULL)
return (NULL);
id = 0;
if (cp < endp && *endp == '\0') {
cp++;
if (ddi_strtol(cp, NULL, 0, &id) != 0) {
ill_refrele(ill);
if (error != NULL)
*error = ENXIO;
return (NULL);
}
}
GRAB_CONN_LOCK(q);
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (ipif->ipif_id == id) {
if (zoneid != ALL_ZONES &&
zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES) {
mutex_exit(&ill->ill_lock);
RELEASE_CONN_LOCK(q);
ill_refrele(ill);
if (error != NULL)
*error = ENXIO;
return (NULL);
}
if (!(IPIF_IS_CHANGING(ipif) ||
IPIF_IS_CONDEMNED(ipif)) ||
IAM_WRITER_IPIF(ipif)) {
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
RELEASE_CONN_LOCK(q);
ill_refrele(ill);
return (ipif);
} else if (q != NULL && !IPIF_IS_CONDEMNED(ipif)) {
ipsq = ill->ill_phyint->phyint_ipsq;
mutex_enter(&ipsq->ipsq_lock);
mutex_enter(&ipsq->ipsq_xop->ipx_lock);
mutex_exit(&ill->ill_lock);
ipsq_enq(ipsq, q, mp, func, NEW_OP, ill);
mutex_exit(&ipsq->ipsq_xop->ipx_lock);
mutex_exit(&ipsq->ipsq_lock);
RELEASE_CONN_LOCK(q);
ill_refrele(ill);
if (error != NULL)
*error = EINPROGRESS;
return (NULL);
}
}
}
RELEASE_CONN_LOCK(q);
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
if (error != NULL)
*error = ENXIO;
return (NULL);
}
void
ipif_mask_reply(ipif_t *ipif)
{
icmph_t *icmph;
ipha_t *ipha;
mblk_t *mp;
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
ip_xmit_attr_t ixas;
#define REPLY_LEN (sizeof (icmp_ipha) + sizeof (icmph_t) + IP_ADDR_LEN)
if (!ipst->ips_ip_respond_to_address_mask_broadcast)
return;
ASSERT(!ipif->ipif_isv6);
ASSERT(ipif->ipif_ill->ill_wq != NULL);
if (ipif->ipif_lcl_addr == INADDR_ANY)
return;
mp = allocb(REPLY_LEN, BPRI_HI);
if (mp == NULL)
return;
mp->b_wptr = mp->b_rptr + REPLY_LEN;
ipha = (ipha_t *)mp->b_rptr;
bzero(ipha, REPLY_LEN);
*ipha = icmp_ipha;
ipha->ipha_ttl = ipst->ips_ip_broadcast_ttl;
ipha->ipha_src = ipif->ipif_lcl_addr;
ipha->ipha_dst = ipif->ipif_brd_addr;
ipha->ipha_length = htons(REPLY_LEN);
ipha->ipha_ident = 0;
icmph = (icmph_t *)&ipha[1];
icmph->icmph_type = ICMP_ADDRESS_MASK_REPLY;
bcopy(&ipif->ipif_net_mask, &icmph[1], IP_ADDR_LEN);
icmph->icmph_checksum = IP_CSUM(mp, sizeof (ipha_t), 0);
bzero(&ixas, sizeof (ixas));
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
ixas.ixa_zoneid = ALL_ZONES;
ixas.ixa_ifindex = 0;
ixas.ixa_ipst = ipst;
ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
#undef REPLY_LEN
}
void
ipif_multicast_up(ipif_t *ipif)
{
int err;
ill_t *ill;
ilm_t *ilm;
ASSERT(IAM_WRITER_IPIF(ipif));
ill = ipif->ipif_ill;
ip1dbg(("ipif_multicast_up\n"));
if (!(ill->ill_flags & ILLF_MULTICAST) ||
ipif->ipif_allhosts_ilm != NULL)
return;
if (ipif->ipif_isv6) {
in6_addr_t v6allmc = ipv6_all_hosts_mcast;
in6_addr_t v6solmc = ipv6_solicited_node_mcast;
v6solmc.s6_addr32[3] |= ipif->ipif_v6lcl_addr.s6_addr32[3];
if (IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr))
return;
ip1dbg(("ipif_multicast_up - addmulti\n"));
if (!IS_UNDER_IPMP(ill)) {
ilm = ip_addmulti(&v6allmc, ill, ipif->ipif_zoneid,
&err);
if (ilm == NULL) {
ASSERT(err != 0);
ip0dbg(("ipif_multicast_up: "
"all_hosts_mcast failed %d\n", err));
return;
}
ipif->ipif_allhosts_ilm = ilm;
}
if (!(ipif->ipif_flags & IPIF_NOLOCAL)) {
ill_t *mcast_ill = NULL;
boolean_t need_refrele;
if (IS_UNDER_IPMP(ill) &&
(mcast_ill = ipmp_ill_hold_ipmp_ill(ill)) != NULL) {
need_refrele = B_TRUE;
} else {
mcast_ill = ill;
need_refrele = B_FALSE;
}
ilm = ip_addmulti(&v6solmc, mcast_ill,
ipif->ipif_zoneid, &err);
if (need_refrele)
ill_refrele(mcast_ill);
if (ilm == NULL) {
ASSERT(err != 0);
ip0dbg(("ipif_multicast_up: solicited MC"
" failed %d\n", err));
if ((ilm = ipif->ipif_allhosts_ilm) != NULL) {
ipif->ipif_allhosts_ilm = NULL;
(void) ip_delmulti(ilm);
}
return;
}
ipif->ipif_solmulti_ilm = ilm;
}
} else {
in6_addr_t v6group;
if (ipif->ipif_lcl_addr == INADDR_ANY || IS_UNDER_IPMP(ill))
return;
ip1dbg(("ipif_multicast_up - addmulti\n"));
IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_ALLHOSTS_GROUP), &v6group);
ilm = ip_addmulti(&v6group, ill, ipif->ipif_zoneid, &err);
if (ilm == NULL) {
ASSERT(err != 0);
ip0dbg(("ipif_multicast_up: failed %d\n", err));
return;
}
ipif->ipif_allhosts_ilm = ilm;
}
}
void
ipif_multicast_down(ipif_t *ipif)
{
ASSERT(IAM_WRITER_IPIF(ipif));
ip1dbg(("ipif_multicast_down\n"));
if (ipif->ipif_allhosts_ilm != NULL) {
(void) ip_delmulti(ipif->ipif_allhosts_ilm);
ipif->ipif_allhosts_ilm = NULL;
}
if (ipif->ipif_solmulti_ilm != NULL) {
(void) ip_delmulti(ipif->ipif_solmulti_ilm);
ipif->ipif_solmulti_ilm = NULL;
}
}
int
ill_recover_saved_ire(ill_t *ill)
{
mblk_t *mp;
ip_stack_t *ipst = ill->ill_ipst;
ip1dbg(("ill_recover_saved_ire(%s)", ill->ill_name));
mutex_enter(&ill->ill_saved_ire_lock);
for (mp = ill->ill_saved_ire_mp; mp != NULL; mp = mp->b_cont) {
ire_t *ire, *nire;
ifrt_t *ifrt;
ifrt = (ifrt_t *)mp->b_rptr;
if (ill->ill_isv6) {
ire = ire_create_v6(
&ifrt->ifrt_v6addr,
&ifrt->ifrt_v6mask,
&ifrt->ifrt_v6gateway_addr,
ifrt->ifrt_type,
ill,
ifrt->ifrt_zoneid,
ifrt->ifrt_flags,
NULL,
ipst);
} else {
ire = ire_create(
(uint8_t *)&ifrt->ifrt_addr,
(uint8_t *)&ifrt->ifrt_mask,
(uint8_t *)&ifrt->ifrt_gateway_addr,
ifrt->ifrt_type,
ill,
ifrt->ifrt_zoneid,
ifrt->ifrt_flags,
NULL,
ipst);
}
if (ire == NULL) {
mutex_exit(&ill->ill_saved_ire_lock);
return (ENOMEM);
}
if (ifrt->ifrt_flags & RTF_SETSRC) {
if (ill->ill_isv6) {
ire->ire_setsrc_addr_v6 =
ifrt->ifrt_v6setsrc_addr;
} else {
ire->ire_setsrc_addr = ifrt->ifrt_setsrc_addr;
}
}
if (ill->ill_net_type == IRE_LOOPBACK)
ire->ire_type = IRE_IF_NORESOLVER;
nire = ire_add(ire);
if (nire == NULL) {
ip1dbg(("ill_recover_saved_ire: FAILED\n"));
} else if (nire != ire) {
ip1dbg(("ill_recover_saved_ire: duplicate ire %p\n",
(void *)nire));
ire_delete(nire);
} else {
ip1dbg(("ill_recover_saved_ire: added ire %p\n",
(void *)nire));
}
if (nire != NULL)
ire_refrele(nire);
}
mutex_exit(&ill->ill_saved_ire_lock);
return (0);
}
static void
ipif_set_default(ipif_t *ipif)
{
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
if (!ipif->ipif_isv6) {
if (!ipif->ipif_net_mask) {
ipaddr_t v4mask;
v4mask = ip_net_mask(ipif->ipif_lcl_addr);
V4MASK_TO_V6(v4mask, ipif->ipif_v6net_mask);
}
if (ipif->ipif_flags & IPIF_POINTOPOINT) {
ipif->ipif_v6subnet = ipif->ipif_v6pp_dst_addr;
} else {
V6_MASK_COPY(ipif->ipif_v6lcl_addr,
ipif->ipif_v6net_mask, ipif->ipif_v6subnet);
}
if (ipif->ipif_flags & IPIF_BROADCAST) {
ipaddr_t v4addr;
v4addr = ipif->ipif_subnet | ~ipif->ipif_net_mask;
IN6_IPADDR_TO_V4MAPPED(v4addr, &ipif->ipif_v6brd_addr);
}
} else {
if (IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6net_mask))
ipif->ipif_v6net_mask = ipv6_all_ones;
if (ipif->ipif_flags & IPIF_POINTOPOINT) {
ipif->ipif_v6subnet = ipif->ipif_v6pp_dst_addr;
} else {
V6_MASK_COPY(ipif->ipif_v6lcl_addr,
ipif->ipif_v6net_mask, ipif->ipif_v6subnet);
}
}
}
int
ip_addr_availability_check(ipif_t *new_ipif)
{
in6_addr_t our_v6addr;
ill_t *ill;
ipif_t *ipif;
ill_walk_context_t ctx;
ip_stack_t *ipst = new_ipif->ipif_ill->ill_ipst;
ASSERT(IAM_WRITER_IPIF(new_ipif));
ASSERT(MUTEX_HELD(&ipst->ips_ip_addr_avail_lock));
ASSERT(RW_READ_HELD(&ipst->ips_ill_g_lock));
new_ipif->ipif_flags &= ~IPIF_UNNUMBERED;
if (IN6_IS_ADDR_UNSPECIFIED(&new_ipif->ipif_v6lcl_addr) ||
IN6_IS_ADDR_V4MAPPED_ANY(&new_ipif->ipif_v6lcl_addr))
return (0);
our_v6addr = new_ipif->ipif_v6lcl_addr;
if (new_ipif->ipif_isv6)
ill = ILL_START_WALK_V6(&ctx, ipst);
else
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if ((ipif == new_ipif) ||
!(ipif->ipif_flags & IPIF_UP) ||
(ipif->ipif_flags & IPIF_UNNUMBERED) ||
!IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr,
&our_v6addr))
continue;
if (new_ipif->ipif_flags & IPIF_POINTOPOINT)
new_ipif->ipif_flags |= IPIF_UNNUMBERED;
else if (ipif->ipif_flags & IPIF_POINTOPOINT)
ipif->ipif_flags |= IPIF_UNNUMBERED;
else if ((IN6_IS_ADDR_LINKLOCAL(&our_v6addr) ||
IN6_IS_ADDR_SITELOCAL(&our_v6addr)) &&
!IS_ON_SAME_LAN(ill, new_ipif->ipif_ill))
continue;
else if (new_ipif->ipif_zoneid != ipif->ipif_zoneid &&
ipif->ipif_zoneid != ALL_ZONES && IS_LOOPBACK(ill))
continue;
else if (new_ipif->ipif_ill == ill)
return (EADDRINUSE);
else
return (EADDRNOTAVAIL);
}
}
return (0);
}
int
ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp)
{
ill_t *ill = ipif->ipif_ill;
boolean_t isv6 = ipif->ipif_isv6;
int err = 0;
boolean_t success;
uint_t ipif_orig_id;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(IAM_WRITER_IPIF(ipif));
ip1dbg(("ipif_up(%s:%u)\n", ill->ill_name, ipif->ipif_id));
DTRACE_PROBE3(ipif__downup, char *, "ipif_up",
ill_t *, ill, ipif_t *, ipif);
if (ipif->ipif_flags & IPIF_UP)
return (EALREADY);
if (IS_UNDER_IPMP(ill) && ipmp_ipif_is_dataaddr(ipif) &&
(!ipif->ipif_isv6 || !V6_IPIF_LINKLOCAL(ipif))) {
ipif_t *stubipif = NULL, *moveipif = NULL;
ill_t *ipmp_ill = ipmp_illgrp_ipmp_ill(ill->ill_grp);
mutex_enter(&ill->ill_lock);
if (!ipif_is_quiescent(ipif)) {
mutex_exit(&ill->ill_lock);
return (EINVAL);
}
mutex_exit(&ill->ill_lock);
if (ipif->ipif_id == 0) {
if ((moveipif = ipif_allocate(ill, 0, IRE_LOCAL, B_TRUE,
B_FALSE, &err)) == NULL) {
return (err);
}
if ((stubipif = ipif_allocate(ill, 0, IRE_LOCAL, B_TRUE,
B_FALSE, &err)) == NULL) {
mi_free(moveipif);
return (err);
}
}
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
if (ipif->ipif_id != 0) {
ipif_remove(ipif);
} else {
ipif_transfer(ipif, moveipif, stubipif);
ipif = moveipif;
}
ipif->ipif_ill = ipmp_ill;
ipif_orig_id = ipif->ipif_id;
if (ipmp_ipif_is_stubaddr(ipmp_ill->ill_ipif)) {
ipif_transfer(ipif, ipmp_ill->ill_ipif, NULL);
ipif = ipmp_ill->ill_ipif;
} else {
ipif->ipif_id = -1;
if ((err = ipif_insert(ipif, B_FALSE)) != 0) {
ipif->ipif_id = ipif_orig_id;
ipif->ipif_ill = ill;
if (ipif_orig_id == 0) {
ipif_transfer(ipif, ill->ill_ipif,
NULL);
} else {
VERIFY(ipif_insert(ipif, B_FALSE) == 0);
}
rw_exit(&ipst->ips_ill_g_lock);
return (err);
}
}
rw_exit(&ipst->ips_ill_g_lock);
sctp_move_ipif(ipif, ill, ipmp_ill);
if (ipif_orig_id == 0) {
ASSERT(ill->ill_move_ipif == NULL);
ill->ill_move_ipif = ipif;
if ((err = ipif_up(ill->ill_ipif, q, mp)) == 0)
ASSERT(ill->ill_move_ipif == NULL);
if (err != EINPROGRESS)
ill->ill_move_ipif = NULL;
return (err);
}
return (ipif_up(ipif, q, mp));
}
if (ill->ill_wq != NULL) {
conn_t *connp = CONN_Q(q) ? Q_TO_CONN(q) : NULL;
ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
if (!ill->ill_dl_up) {
return (ill_dl_up(ill, ipif, mp, q));
}
ASSERT(connp != NULL || !CONN_Q(q));
if (connp != NULL)
mutex_enter(&connp->conn_lock);
mutex_enter(&ill->ill_lock);
success = ipsq_pending_mp_add(connp, ipif, q, mp, 0);
mutex_exit(&ill->ill_lock);
if (connp != NULL)
mutex_exit(&connp->conn_lock);
if (!success)
return (EINTR);
err = ipif_resolver_up(ipif, Res_act_initial);
if (err == EINPROGRESS) {
return (err);
}
if (isv6 && err == 0)
err = ipif_ndp_up(ipif, B_TRUE);
ASSERT(err != EINPROGRESS);
mp = ipsq_pending_mp_get(ipsq, &connp);
ASSERT(mp != NULL);
if (err != 0)
return (err);
} else {
ASSERT(!(ipif->ipif_flags & IPIF_DUPLICATE));
ipif->ipif_addr_ready = 1;
err = ill_add_ires(ill);
if (err != 0)
return (err);
}
err = (isv6 ? ipif_up_done_v6(ipif) : ipif_up_done(ipif));
if (err == 0 && ill->ill_move_ipif != NULL) {
ipif = ill->ill_move_ipif;
ill->ill_move_ipif = NULL;
return (ipif_up(ipif, q, mp));
}
return (err);
}
int
ill_add_ires(ill_t *ill)
{
ire_t *ire;
in6_addr_t dummy6 = {(uint32_t)V6_MCAST, 0, 0, 1};
in_addr_t dummy4 = htonl(INADDR_ALLHOSTS_GROUP);
if (ill->ill_ire_multicast != NULL)
return (0);
if (ill->ill_isv6) {
ire = ire_create_v6(&dummy6, 0, 0, IRE_MULTICAST, ill,
ALL_ZONES, RTF_UP, NULL, ill->ill_ipst);
} else {
ire = ire_create((uchar_t *)&dummy4, 0, 0, IRE_MULTICAST, ill,
ALL_ZONES, RTF_UP, NULL, ill->ill_ipst);
}
if (ire == NULL)
return (ENOMEM);
ill->ill_ire_multicast = ire;
return (0);
}
void
ill_delete_ires(ill_t *ill)
{
if (ill->ill_ire_multicast != NULL) {
ire_make_condemned(ill->ill_ire_multicast);
ire_refrele_notr(ill->ill_ire_multicast);
ill->ill_ire_multicast = NULL;
}
}
static int
ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
{
mblk_t *bind_mp = NULL;
mblk_t *unbind_mp = NULL;
conn_t *connp;
boolean_t success;
int err;
DTRACE_PROBE2(ill__downup, char *, "ill_dl_up", ill_t *, ill);
ip1dbg(("ill_dl_up(%s)\n", ill->ill_name));
ASSERT(IAM_WRITER_ILL(ill));
ASSERT(mp != NULL);
err = ill_add_ires(ill);
if (err != 0)
goto bad;
bind_mp = ip_dlpi_alloc(sizeof (dl_bind_req_t) + sizeof (long),
DL_BIND_REQ);
if (bind_mp == NULL)
goto bad;
((dl_bind_req_t *)bind_mp->b_rptr)->dl_sap = ill->ill_sap;
((dl_bind_req_t *)bind_mp->b_rptr)->dl_service_mode = DL_CLDLS;
if (ill->ill_unbind_mp == NULL) {
unbind_mp = ip_dlpi_alloc(sizeof (dl_unbind_req_t),
DL_UNBIND_REQ);
if (unbind_mp == NULL)
goto bad;
}
connp = CONN_Q(q) ? Q_TO_CONN(q) : NULL;
ASSERT(connp != NULL || !CONN_Q(q));
GRAB_CONN_LOCK(q);
mutex_enter(&ipif->ipif_ill->ill_lock);
success = ipsq_pending_mp_add(connp, ipif, q, mp, 0);
mutex_exit(&ipif->ipif_ill->ill_lock);
RELEASE_CONN_LOCK(q);
if (!success)
goto bad;
if (ill->ill_unbind_mp == NULL)
ill->ill_unbind_mp = unbind_mp;
ill_dlpi_send(ill, bind_mp);
ill_capability_probe(ill);
if ((ipif->ipif_flags & IPIF_DHCPRUNNING) &&
(strcmp(ill->ill_name, dhcifname) == 0) &&
(strlen(srpc_domain) == 0)) {
if (dhcpinit() != 0)
cmn_err(CE_WARN, "no cached dhcp response");
}
return (EINPROGRESS);
bad:
ip1dbg(("ill_dl_up(%s) FAILED\n", ill->ill_name));
freemsg(bind_mp);
freemsg(unbind_mp);
return (ENOMEM);
}
uint_t ip_loopback_mtuplus = IP_LOOPBACK_MTU + IP_SIMPLE_HDR_LENGTH + 20;
int
ipif_up_done(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
int err = 0;
boolean_t loopback = B_FALSE;
boolean_t update_src_selection = B_TRUE;
ipif_t *tmp_ipif;
ip1dbg(("ipif_up_done(%s:%u)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id));
DTRACE_PROBE3(ipif__downup, char *, "ipif_up_done",
ill_t *, ill, ipif_t *, ipif);
if (ipif->ipif_ill->ill_wq == NULL)
loopback = B_TRUE;
ASSERT(!MUTEX_HELD(&ipif->ipif_ill->ill_lock));
for (tmp_ipif = ill->ill_ipif; tmp_ipif;
tmp_ipif = tmp_ipif->ipif_next) {
if (((tmp_ipif->ipif_flags &
(IPIF_NOXMIT|IPIF_ANYCAST|IPIF_NOLOCAL|IPIF_DEPRECATED)) ||
!(tmp_ipif->ipif_flags & IPIF_UP)) ||
(tmp_ipif == ipif))
continue;
update_src_selection = B_FALSE;
break;
}
if (update_src_selection)
ip_update_source_selection(ill->ill_ipst);
if (IS_LOOPBACK(ill) || ill->ill_net_type == IRE_IF_NORESOLVER) {
nce_t *loop_nce = NULL;
uint16_t flags = (NCE_F_MYADDR | NCE_F_AUTHORITY | NCE_F_NONUD);
if (V4_PART_OF_V6(ipif->ipif_v6lcl_addr) ==
htonl(INADDR_LOOPBACK))
ipif->ipif_ire_type = IRE_LOOPBACK;
else
ipif->ipif_ire_type = IRE_LOCAL;
if (ill->ill_net_type != IRE_LOOPBACK)
flags |= NCE_F_PUBLISH;
err = nce_lookup_then_add_v4(ill, NULL,
ill->ill_phys_addr_length, &ipif->ipif_lcl_addr, flags,
ND_REACHABLE, &loop_nce);
if (err == 0 || err == EEXIST) {
ipif->ipif_added_nce = 1;
loop_nce->nce_ipif_cnt++;
nce_refrele(loop_nce);
err = 0;
} else {
ASSERT(loop_nce == NULL);
return (err);
}
}
err = ipif_add_ires_v4(ipif, loopback);
if (err != 0) {
if (err != EADDRINUSE) {
(void) ipif_arp_down(ipif);
} else {
if (IS_IPMP(ill))
ipmp_illgrp_del_ipif(ill->ill_grp, ipif);
err = EADDRNOTAVAIL;
}
return (err);
}
if (ill->ill_ipif_up_count == 1 && !loopback) {
(void) ill_recover_saved_ire(ill);
}
if (ill->ill_need_recover_multicast) {
ill_recover_multicast(ill);
}
if (ill->ill_ipif_up_count == 1) {
if (IS_UNDER_IPMP(ill))
ipmp_ill_refresh_active(ill);
if (IS_IPMP(ill))
ipmp_illgrp_refresh_arpent(ill->ill_grp);
}
ipif_multicast_up(ipif);
if (!loopback && !update_src_selection &&
!(ipif->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST|IPIF_DEPRECATED)))
ip_update_source_selection(ill->ill_ipst);
if (!loopback && ipif->ipif_addr_ready) {
ipif_mask_reply(ipif);
}
update_conn_ill(NULL, ill->ill_ipst);
if (ipif->ipif_addr_ready)
ipif_up_notify(ipif);
return (0);
}
static int
ipif_add_ires_v4(ipif_t *ipif, boolean_t loopback)
{
ill_t *ill = ipif->ipif_ill;
ip_stack_t *ipst = ill->ill_ipst;
ire_t *ire_array[20];
ire_t **irep = ire_array;
ire_t **irep1;
ipaddr_t net_mask = 0;
ipaddr_t subnet_mask, route_mask;
int err;
ire_t *ire_local = NULL;
ire_t *ire_if = NULL;
uchar_t *gw;
if ((ipif->ipif_lcl_addr != INADDR_ANY) &&
!(ipif->ipif_flags & IPIF_NOLOCAL)) {
if (is_system_labeled() &&
ipif->ipif_ire_type != IRE_LOOPBACK &&
!tsol_check_interface_address(ipif))
return (EINVAL);
err = ip_srcid_insert(&ipif->ipif_v6lcl_addr,
ipif->ipif_zoneid, ipst);
if (err != 0) {
ip0dbg(("ipif_add_ires: srcid_insert %d\n", err));
return (err);
}
if (loopback)
gw = (uchar_t *)&ipif->ipif_lcl_addr;
else
gw = NULL;
ire_local = ire_create(
(uchar_t *)&ipif->ipif_lcl_addr,
(uchar_t *)&ip_g_all_ones,
gw,
ipif->ipif_ire_type,
ipif->ipif_ill,
ipif->ipif_zoneid,
((ipif->ipif_flags & IPIF_PRIVATE) ?
RTF_PRIVATE : 0) | RTF_KERNEL,
NULL,
ipst);
ip1dbg(("ipif_add_ires: 0x%p creating IRE %p type 0x%x"
" for 0x%x\n", (void *)ipif, (void *)ire_local,
ipif->ipif_ire_type,
ntohl(ipif->ipif_lcl_addr)));
if (ire_local == NULL) {
ip1dbg(("ipif_up_done: NULL ire_local\n"));
err = ENOMEM;
goto bad;
}
} else {
ip1dbg((
"ipif_add_ires: not creating IRE %d for 0x%x: flags 0x%x\n",
ipif->ipif_ire_type,
ntohl(ipif->ipif_lcl_addr),
(uint_t)ipif->ipif_flags));
}
if ((ipif->ipif_lcl_addr != INADDR_ANY) &&
!(ipif->ipif_flags & IPIF_NOLOCAL)) {
net_mask = ip_net_mask(ipif->ipif_lcl_addr);
} else {
net_mask = htonl(IN_CLASSA_NET);
}
subnet_mask = ipif->ipif_net_mask;
if (subnet_mask == 0) {
subnet_mask = net_mask;
V4MASK_TO_V6(subnet_mask, ipif->ipif_v6net_mask);
V6_MASK_COPY(ipif->ipif_v6lcl_addr, ipif->ipif_v6net_mask,
ipif->ipif_v6subnet);
}
if (!loopback && !(ipif->ipif_flags & IPIF_NOXMIT) &&
ipif->ipif_subnet != INADDR_ANY) {
if (ipif->ipif_flags & IPIF_POINTOPOINT) {
route_mask = IP_HOST_MASK;
} else {
route_mask = subnet_mask;
}
ip1dbg(("ipif_add_ires: ipif 0x%p ill 0x%p "
"creating if IRE ill_net_type 0x%x for 0x%x\n",
(void *)ipif, (void *)ill, ill->ill_net_type,
ntohl(ipif->ipif_subnet)));
ire_if = ire_create(
(uchar_t *)&ipif->ipif_subnet,
(uchar_t *)&route_mask,
(uchar_t *)&ipif->ipif_lcl_addr,
ill->ill_net_type,
ill,
ipif->ipif_zoneid,
((ipif->ipif_flags & IPIF_PRIVATE) ?
RTF_PRIVATE: 0) | RTF_KERNEL,
NULL,
ipst);
if (ire_if == NULL) {
ip1dbg(("ipif_up_done: NULL ire_if\n"));
err = ENOMEM;
goto bad;
}
}
if ((ipif->ipif_flags & IPIF_BROADCAST) &&
!(ipif->ipif_flags & IPIF_NOXMIT))
irep = ipif_create_bcast_ires(ipif, irep);
for (irep1 = irep; irep1 > ire_array; ) {
irep1--;
if (*irep1 == NULL) {
ip1dbg(("ipif_up_done: NULL ire found in ire_array\n"));
err = ENOMEM;
goto bad;
}
}
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
mutex_enter(&ipst->ips_ip_addr_avail_lock);
ipif->ipif_flags |= IPIF_UP;
ill->ill_ipif_up_count++;
err = ip_addr_availability_check(ipif);
mutex_exit(&ipst->ips_ip_addr_avail_lock);
rw_exit(&ipst->ips_ill_g_lock);
if (err != 0) {
ill->ill_ipif_up_count--;
ipif->ipif_flags &= ~IPIF_UP;
goto bad;
}
if (ire_if != NULL) {
ire_if = ire_add(ire_if);
if (ire_if == NULL) {
err = ENOMEM;
goto bad2;
}
#ifdef DEBUG
ire_refhold_notr(ire_if);
ire_refrele(ire_if);
#endif
}
if (ire_local != NULL) {
ire_local = ire_add(ire_local);
if (ire_local == NULL) {
err = ENOMEM;
goto bad2;
}
#ifdef DEBUG
ire_refhold_notr(ire_local);
ire_refrele(ire_local);
#endif
}
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
if (ire_local != NULL)
ipif->ipif_ire_local = ire_local;
if (ire_if != NULL)
ipif->ipif_ire_if = ire_if;
rw_exit(&ipst->ips_ill_g_lock);
ire_local = NULL;
ire_if = NULL;
for (irep1 = irep; irep1 > ire_array; ) {
irep1--;
ASSERT(!MUTEX_HELD(&((*irep1)->ire_ill->ill_lock)));
*irep1 = ire_add(*irep1);
if (*irep1 == NULL) {
err = ENOMEM;
goto bad2;
}
}
for (irep1 = irep; irep1 > ire_array; ) {
irep1--;
if (*irep1 != NULL) {
ire_refrele(*irep1);
*irep1 = NULL;
}
}
if (!loopback) {
if ((ipif->ipif_brd_addr != INADDR_ANY) &&
(ipif->ipif_flags & IPIF_BROADCAST)) {
ire_t *ire;
ire = ire_ftable_lookup_v4(ipif->ipif_brd_addr, 0, 0,
IRE_BROADCAST, ipif->ipif_ill, ALL_ZONES, NULL,
(MATCH_IRE_TYPE | MATCH_IRE_ILL), 0, ipst, NULL);
if (ire == NULL) {
ipif->ipif_v6brd_addr = ipv6_all_zeros;
mutex_enter(&ipif->ipif_ill->ill_lock);
ipif_set_default(ipif);
mutex_exit(&ipif->ipif_ill->ill_lock);
} else {
ire_refrele(ire);
}
}
}
return (0);
bad2:
ill->ill_ipif_up_count--;
ipif->ipif_flags &= ~IPIF_UP;
bad:
ip1dbg(("ipif_add_ires: FAILED \n"));
if (ire_local != NULL)
ire_delete(ire_local);
if (ire_if != NULL)
ire_delete(ire_if);
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
ire_local = ipif->ipif_ire_local;
ipif->ipif_ire_local = NULL;
ire_if = ipif->ipif_ire_if;
ipif->ipif_ire_if = NULL;
rw_exit(&ipst->ips_ill_g_lock);
if (ire_local != NULL) {
ire_delete(ire_local);
ire_refrele_notr(ire_local);
}
if (ire_if != NULL) {
ire_delete(ire_if);
ire_refrele_notr(ire_if);
}
while (irep > ire_array) {
irep--;
if (*irep != NULL) {
ire_delete(*irep);
}
}
(void) ip_srcid_remove(&ipif->ipif_v6lcl_addr, ipif->ipif_zoneid, ipst);
return (err);
}
void
ipif_delete_ires_v4(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
ip_stack_t *ipst = ill->ill_ipst;
ire_t *ire;
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
ire = ipif->ipif_ire_local;
ipif->ipif_ire_local = NULL;
rw_exit(&ipst->ips_ill_g_lock);
if (ire != NULL) {
atomic_add_32(&ipif->ipif_ib_pkt_count, ire->ire_ib_pkt_count);
ire_delete(ire);
ire_refrele_notr(ire);
}
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
ire = ipif->ipif_ire_if;
ipif->ipif_ire_if = NULL;
rw_exit(&ipst->ips_ill_g_lock);
if (ire != NULL) {
ire_delete(ire);
ire_refrele_notr(ire);
}
if ((ipif->ipif_flags & IPIF_BROADCAST) &&
!(ipif->ipif_flags & IPIF_NOXMIT))
ipif_delete_bcast_ires(ipif);
}
boolean_t
ipif_zone_avail(uint_t ifindex, boolean_t isv6, zoneid_t zoneid,
ip_stack_t *ipst)
{
ipif_t *ipif = NULL;
ill_t *uill;
ASSERT(ifindex != 0);
uill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
if (uill == NULL)
return (B_FALSE);
mutex_enter(&uill->ill_lock);
for (ipif = uill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (IPIF_IS_CONDEMNED(ipif))
continue;
if (ipif->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST))
continue;
if (!(ipif->ipif_flags & IPIF_UP))
continue;
if (ipif->ipif_zoneid != zoneid)
continue;
if (isv6 ? IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr) :
ipif->ipif_lcl_addr == INADDR_ANY)
continue;
mutex_exit(&uill->ill_lock);
ill_refrele(uill);
return (B_TRUE);
}
mutex_exit(&uill->ill_lock);
ill_refrele(uill);
return (B_FALSE);
}
ipif_t *
ipif_good_addr(ill_t *ill, zoneid_t zoneid)
{
ipif_t *ipif;
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (IPIF_IS_CONDEMNED(ipif))
continue;
if (ipif->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST))
continue;
if (!(ipif->ipif_flags & IPIF_UP))
continue;
if (ipif->ipif_zoneid != zoneid &&
ipif->ipif_zoneid != ALL_ZONES && zoneid != ALL_ZONES)
continue;
if (ill->ill_isv6 ?
IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr) :
ipif->ipif_lcl_addr == INADDR_ANY)
continue;
ipif_refhold_locked(ipif);
mutex_exit(&ill->ill_lock);
return (ipif);
}
mutex_exit(&ill->ill_lock);
return (NULL);
}
typedef enum {
IPIF_NONE,
IPIF_DIFFNET_DEPRECATED,
IPIF_SAMENET_DEPRECATED,
IPIF_DIFFNET_ALLZONES,
IPIF_SAMENET_ALLZONES,
IPIF_DIFFNET,
IPIF_SAMENET,
IPIF_LOCALADDR
} ipif_type_t;
ipif_t *
ipif_select_source_v4(ill_t *ill, ipaddr_t dst, zoneid_t zoneid,
boolean_t allow_usesrc, boolean_t *notreadyp)
{
ill_t *usill = NULL;
ill_t *ipmp_ill = NULL;
ipif_t *start_ipif, *next_ipif, *ipif, *best_ipif;
ipif_type_t type, best_type;
tsol_tpc_t *src_rhtp, *dst_rhtp;
ip_stack_t *ipst = ill->ill_ipst;
boolean_t samenet;
if (ill->ill_usesrc_ifindex != 0 && allow_usesrc) {
usill = ill_lookup_on_ifindex(ill->ill_usesrc_ifindex,
B_FALSE, ipst);
if (usill != NULL)
ill = usill;
else
return (NULL);
}
if (IS_UNDER_IPMP(ill)) {
if ((ipmp_ill = ipmp_ill_hold_ipmp_ill(ill)) != NULL)
ill = ipmp_ill;
else
return (NULL);
}
dst_rhtp = NULL;
if (is_system_labeled()) {
dst_rhtp = find_tpc(&dst, IPV4_VERSION, B_FALSE);
if (dst_rhtp == NULL)
return (NULL);
if (dst_rhtp->tpc_tp.host_type != UNLABELED) {
TPC_RELE(dst_rhtp);
dst_rhtp = NULL;
}
}
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
retry:
start_ipif = ill->ill_ipif;
if (IS_IPMP(ill) && ill->ill_src_ipif != NULL)
start_ipif = ill->ill_src_ipif;
ipif = start_ipif;
best_ipif = NULL;
best_type = IPIF_NONE;
do {
if ((next_ipif = ipif->ipif_next) == NULL)
next_ipif = ill->ill_ipif;
if (IPIF_IS_CONDEMNED(ipif))
continue;
if (ipif->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST))
continue;
if (ipif->ipif_ill->ill_flags & ILLF_NOACCEPT)
continue;
if (!(ipif->ipif_flags & IPIF_UP))
continue;
if (!ipif->ipif_addr_ready) {
if (notreadyp != NULL)
*notreadyp = B_TRUE;
continue;
}
if (zoneid != ALL_ZONES &&
ipif->ipif_zoneid != zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
if (ipif->ipif_lcl_addr == INADDR_ANY)
continue;
if (dst_rhtp != NULL) {
boolean_t incompat;
src_rhtp = find_tpc(&ipif->ipif_lcl_addr,
IPV4_VERSION, B_FALSE);
if (src_rhtp == NULL)
continue;
incompat = src_rhtp->tpc_tp.host_type != SUN_CIPSO ||
src_rhtp->tpc_tp.tp_doi !=
dst_rhtp->tpc_tp.tp_doi ||
(!_blinrange(&dst_rhtp->tpc_tp.tp_def_label,
&src_rhtp->tpc_tp.tp_sl_range_cipso) &&
!blinlset(&dst_rhtp->tpc_tp.tp_def_label,
src_rhtp->tpc_tp.tp_sl_set_cipso));
TPC_RELE(src_rhtp);
if (incompat)
continue;
}
samenet = ((ipif->ipif_net_mask & dst) == ipif->ipif_subnet);
if (ipif->ipif_lcl_addr == dst) {
type = IPIF_LOCALADDR;
} else if (ipif->ipif_flags & IPIF_DEPRECATED) {
type = samenet ? IPIF_SAMENET_DEPRECATED :
IPIF_DIFFNET_DEPRECATED;
} else if (ipif->ipif_zoneid == ALL_ZONES) {
type = samenet ? IPIF_SAMENET_ALLZONES :
IPIF_DIFFNET_ALLZONES;
} else {
type = samenet ? IPIF_SAMENET : IPIF_DIFFNET;
}
if (type > best_type) {
best_type = type;
best_ipif = ipif;
if (best_type == IPIF_LOCALADDR)
break;
}
} while ((ipif = next_ipif) != start_ipif);
if ((ipif = best_ipif) != NULL) {
mutex_enter(&ipif->ipif_ill->ill_lock);
if (IPIF_IS_CONDEMNED(ipif)) {
mutex_exit(&ipif->ipif_ill->ill_lock);
goto retry;
}
ipif_refhold_locked(ipif);
if (IS_IPMP(ill) && ipif != NULL) {
next_ipif = ipif->ipif_next;
if (next_ipif != NULL && !IPIF_IS_CONDEMNED(next_ipif))
ill->ill_src_ipif = next_ipif;
else
ill->ill_src_ipif = NULL;
}
mutex_exit(&ipif->ipif_ill->ill_lock);
}
rw_exit(&ipst->ips_ill_g_lock);
if (usill != NULL)
ill_refrele(usill);
if (ipmp_ill != NULL)
ill_refrele(ipmp_ill);
if (dst_rhtp != NULL)
TPC_RELE(dst_rhtp);
#ifdef DEBUG
if (ipif == NULL) {
char buf1[INET6_ADDRSTRLEN];
ip1dbg(("ipif_select_source_v4(%s, %s) -> NULL\n",
ill->ill_name,
inet_ntop(AF_INET, &dst, buf1, sizeof (buf1))));
} else {
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
ip1dbg(("ipif_select_source_v4(%s, %s) -> %s\n",
ipif->ipif_ill->ill_name,
inet_ntop(AF_INET, &dst, buf1, sizeof (buf1)),
inet_ntop(AF_INET, &ipif->ipif_lcl_addr,
buf2, sizeof (buf2))));
}
#endif
return (ipif);
}
int
ip_select_source_v4(ill_t *ill, ipaddr_t setsrc, ipaddr_t dst,
ipaddr_t multicast_ifaddr,
zoneid_t zoneid, ip_stack_t *ipst, ipaddr_t *srcp,
uint32_t *generation, uint64_t *flagsp)
{
ipif_t *ipif;
boolean_t notready = B_FALSE;
if (flagsp != NULL)
*flagsp = 0;
if (generation != NULL) {
*generation = ipst->ips_src_generation;
}
if (CLASSD(dst) && multicast_ifaddr != INADDR_ANY) {
*srcp = multicast_ifaddr;
return (0);
}
if (setsrc != INADDR_ANY) {
*srcp = setsrc;
return (0);
}
ipif = ipif_select_source_v4(ill, dst, zoneid, B_TRUE, ¬ready);
if (ipif == NULL) {
if (notready)
return (ENETDOWN);
else
return (EADDRNOTAVAIL);
}
*srcp = ipif->ipif_lcl_addr;
if (flagsp != NULL)
*flagsp = ipif->ipif_flags;
ipif_refrele(ipif);
return (0);
}
int
if_unitsel_restart(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
{
return (ipif_set_values_tail(ipif->ipif_ill, ipif, mp, q));
}
int
if_unitsel(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
{
queue_t *q1 = q;
char *cp;
char interf_name[LIFNAMSIZ];
uint_t ppa = *(uint_t *)mp->b_cont->b_cont->b_rptr;
if (q->q_next == NULL) {
ip1dbg((
"if_unitsel: IF_UNITSEL: no q_next\n"));
return (EINVAL);
}
if (((ill_t *)(q->q_ptr))->ill_name[0] != '\0')
return (EALREADY);
do {
q1 = q1->q_next;
} while (q1->q_next);
cp = q1->q_qinfo->qi_minfo->mi_idname;
(void) sprintf(interf_name, "%s%d", cp, ppa);
return (ipif_set_values(q, mp, interf_name, &ppa));
}
int
ip_sioctl_sifname(ipif_t *dummy_ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
{
return (ENXIO);
}
static ire_t **
ipif_create_bcast_ires(ipif_t *ipif, ire_t **irep)
{
ipaddr_t addr;
ipaddr_t netmask = ip_net_mask(ipif->ipif_lcl_addr);
ipaddr_t subnetmask = ipif->ipif_net_mask;
ill_t *ill = ipif->ipif_ill;
zoneid_t zoneid = ipif->ipif_zoneid;
ip1dbg(("ipif_create_bcast_ires: creating broadcast IREs\n"));
ASSERT(ipif->ipif_flags & IPIF_BROADCAST);
ASSERT(!(ipif->ipif_flags & IPIF_NOXMIT));
if (ipif->ipif_lcl_addr == INADDR_ANY ||
(ipif->ipif_flags & IPIF_NOLOCAL))
netmask = htonl(IN_CLASSA_NET);
irep = ire_create_bcast(ill, 0, zoneid, irep);
irep = ire_create_bcast(ill, INADDR_BROADCAST, zoneid, irep);
if (netmask < subnetmask) {
addr = netmask & ipif->ipif_subnet;
irep = ire_create_bcast(ill, addr, zoneid, irep);
irep = ire_create_bcast(ill, ~netmask | addr, zoneid, irep);
}
if (subnetmask != 0xFFFFFFFF) {
addr = ipif->ipif_subnet;
irep = ire_create_bcast(ill, addr, zoneid, irep);
irep = ire_create_bcast(ill, ~subnetmask | addr, zoneid, irep);
}
return (irep);
}
static void
ipif_delete_bcast_ires(ipif_t *ipif)
{
ipaddr_t addr;
ipaddr_t netmask = ip_net_mask(ipif->ipif_lcl_addr);
ipaddr_t subnetmask = ipif->ipif_net_mask;
ill_t *ill = ipif->ipif_ill;
zoneid_t zoneid = ipif->ipif_zoneid;
ire_t *ire;
ASSERT(ipif->ipif_flags & IPIF_BROADCAST);
ASSERT(!(ipif->ipif_flags & IPIF_NOXMIT));
if (ipif->ipif_lcl_addr == INADDR_ANY ||
(ipif->ipif_flags & IPIF_NOLOCAL))
netmask = htonl(IN_CLASSA_NET);
ire = ire_lookup_bcast(ill, 0, zoneid);
ASSERT(ire != NULL);
ire_delete(ire); ire_refrele(ire);
ire = ire_lookup_bcast(ill, INADDR_BROADCAST, zoneid);
ASSERT(ire != NULL);
ire_delete(ire); ire_refrele(ire);
if (netmask < subnetmask) {
addr = netmask & ipif->ipif_subnet;
ire = ire_lookup_bcast(ill, addr, zoneid);
ASSERT(ire != NULL);
ire_delete(ire); ire_refrele(ire);
ire = ire_lookup_bcast(ill, ~netmask | addr, zoneid);
ASSERT(ire != NULL);
ire_delete(ire); ire_refrele(ire);
}
if (subnetmask != 0xFFFFFFFF) {
addr = ipif->ipif_subnet;
ire = ire_lookup_bcast(ill, addr, zoneid);
ASSERT(ire != NULL);
ire_delete(ire); ire_refrele(ire);
ire = ire_lookup_bcast(ill, ~subnetmask | addr, zoneid);
ASSERT(ire != NULL);
ire_delete(ire); ire_refrele(ire);
}
}
int
ip_sioctl_slifname(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
ill_t *ill = q->q_ptr;
phyint_t *phyi;
ip_stack_t *ipst;
struct lifreq *lifr = if_req;
uint64_t new_flags;
ASSERT(ipif != NULL);
ip1dbg(("ip_sioctl_slifname %s\n", lifr->lifr_name));
if (q->q_next == NULL) {
ip1dbg(("if_sioctl_slifname: SIOCSLIFNAME: no q_next\n"));
return (EINVAL);
}
if (ill != ipif->ipif_ill)
return (EALREADY);
if (ill->ill_name[0] != '\0')
return (EALREADY);
ipst = ill->ill_ipst;
phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_name,
lifr->lifr_name, NULL);
if (phyi != NULL) {
ill_t *ill_mate = phyi->phyint_illv4;
if (ill_mate == NULL)
ill_mate = phyi->phyint_illv6;
ASSERT(ill_mate != NULL);
if (ill_mate->ill_media->ip_m_mac_type !=
ill->ill_media->ip_m_mac_type) {
ip1dbg(("if_sioctl_slifname: SIOCSLIFNAME: attempt to "
"use the same ill name on differing media\n"));
return (EINVAL);
}
}
if ((lifr->lifr_flags & IFF_BROADCAST) &&
((lifr->lifr_flags & IFF_IPV6) ||
(!ill->ill_needs_attach && ill->ill_bcast_addr_length == 0))) {
ip1dbg(("ip_sioctl_slifname: link not broadcast capable "
"or IPv6 i.e., no broadcast \n"));
return (EINVAL);
}
new_flags =
lifr->lifr_flags & (IFF_IPV6|IFF_IPV4|IFF_BROADCAST);
if ((new_flags ^ (IFF_IPV6|IFF_IPV4)) == 0) {
ip1dbg(("ip_sioctl_slifname: flags must be exactly one of "
"IFF_IPV4 or IFF_IPV6\n"));
return (EINVAL);
}
if ((new_flags & IFF_IPV6) != 0) {
ill->ill_flags |= ILLF_IPV6;
ill->ill_flags &= ~ILLF_IPV4;
if (lifr->lifr_flags & IFF_NOLINKLOCAL)
ill->ill_flags |= ILLF_NOLINKLOCAL;
}
if ((new_flags & IFF_BROADCAST) != 0)
ipif->ipif_flags |= IPIF_BROADCAST;
else
ipif->ipif_flags &= ~IPIF_BROADCAST;
if (ill->ill_flags & ILLF_IPV6) {
ill->ill_phyint->phyint_illv6 = ill;
ill->ill_phyint->phyint_illv4 = NULL;
}
return (ipif_set_values(q, mp, lifr->lifr_name, &lifr->lifr_ppa));
}
int
ip_sioctl_slifname_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
return (ipif_set_values_tail(ipif->ipif_ill, ipif, mp, q));
}
ipif_t *
ipif_lookup_on_ifindex(uint_t index, boolean_t isv6, zoneid_t zoneid,
ip_stack_t *ipst)
{
ill_t *ill;
ipif_t *ipif = NULL;
ill = ill_lookup_on_ifindex(index, isv6, ipst);
if (ill != NULL) {
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (!IPIF_IS_CONDEMNED(ipif) && (zoneid == ALL_ZONES ||
zoneid == ipif->ipif_zoneid ||
ipif->ipif_zoneid == ALL_ZONES)) {
ipif_refhold_locked(ipif);
break;
}
}
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
}
return (ipif);
}
int
ip_sioctl_slifindex(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
ill_t *ill;
phyint_t *phyi;
struct ifreq *ifr = (struct ifreq *)ifreq;
struct lifreq *lifr = (struct lifreq *)ifreq;
uint_t old_index, index;
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
avl_index_t where;
if (ipip->ipi_cmd_type == IF_CMD)
index = ifr->ifr_index;
else
index = lifr->lifr_index;
ill = ipif->ipif_ill;
phyi = ill->ill_phyint;
if (ipif->ipif_id != 0 || index == 0 || index > IF_INDEX_MAX) {
return (EINVAL);
}
if (phyi->phyint_ifindex == index)
return (0);
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
if (phyint_exists(index, ipst)) {
rw_exit(&ipst->ips_ill_g_lock);
return (EEXIST);
}
ill_nic_event_dispatch(ill, 0, NE_IFINDEX_CHANGE,
&index, sizeof (index));
old_index = phyi->phyint_ifindex;
phyi->phyint_ifindex = index;
avl_remove(&ipst->ips_phyint_g_list->phyint_list_avl_by_index, phyi);
(void) avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
&index, &where);
avl_insert(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
phyi, where);
rw_exit(&ipst->ips_ill_g_lock);
sctp_ill_reindex(ill, old_index);
ip_rts_ifmsg(ipif, RTSQ_DEFAULT);
if (ILL_OTHER(ill))
ip_rts_ifmsg(ILL_OTHER(ill)->ill_ipif, RTSQ_DEFAULT);
update_conn_ill(NULL, ill->ill_ipst);
return (0);
}
int
ip_sioctl_get_lifindex(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct ifreq *ifr = (struct ifreq *)ifreq;
struct lifreq *lifr = (struct lifreq *)ifreq;
ip1dbg(("ip_sioctl_get_lifindex(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (ipip->ipi_cmd_type == IF_CMD) {
ifr->ifr_index = ipif->ipif_ill->ill_phyint->phyint_ifindex;
} else {
lifr->lifr_index = ipif->ipif_ill->ill_phyint->phyint_ifindex;
}
return (0);
}
int
ip_sioctl_get_lifzone(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifreq *lifr = (struct lifreq *)ifreq;
ip1dbg(("ip_sioctl_get_lifzone(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
lifr->lifr_zoneid = ipif->ipif_zoneid;
return (0);
}
int
ip_sioctl_slifzone(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifreq *lifr = (struct lifreq *)ifreq;
int err = 0;
boolean_t need_up = B_FALSE;
zone_t *zptr;
zone_status_t status;
zoneid_t zoneid;
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
if ((zoneid = lifr->lifr_zoneid) == ALL_ZONES) {
if (!is_system_labeled())
return (ENOTSUP);
zoneid = GLOBAL_ZONEID;
}
if (ipif->ipif_id == 0 && zoneid != GLOBAL_ZONEID)
return (ENOTSUP);
if ((zptr = zone_find_by_id(zoneid)) == NULL)
return (EINVAL);
status = zone_status_get(zptr);
zone_rele(zptr);
if (status != ZONE_IS_READY && status != ZONE_IS_RUNNING)
return (EINVAL);
if (ipif->ipif_flags & IPIF_UP) {
err = ipif_logical_down(ipif, q, mp);
if (err == EINPROGRESS)
return (err);
(void) ipif_down_tail(ipif);
need_up = B_TRUE;
}
err = ip_sioctl_slifzone_tail(ipif, lifr->lifr_zoneid, q, mp, need_up);
return (err);
}
static int
ip_sioctl_slifzone_tail(ipif_t *ipif, zoneid_t zoneid,
queue_t *q, mblk_t *mp, boolean_t need_up)
{
int err = 0;
ip_stack_t *ipst;
ip1dbg(("ip_sioctl_zoneid_tail(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
if (CONN_Q(q))
ipst = CONNQ_TO_IPST(q);
else
ipst = ILLQ_TO_IPST(q);
if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID &&
zoneid != GLOBAL_ZONEID)
return (EINVAL);
ipif->ipif_zoneid = zoneid;
sctp_update_ipif(ipif, SCTP_IPIF_UPDATE);
ire_increment_multicast_generation(ipst, ipif->ipif_ill->ill_isv6);
if (need_up) {
err = ipif_up(ipif, q, mp);
}
return (err);
}
int
ip_sioctl_slifzone_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct lifreq *lifr = (struct lifreq *)if_req;
zoneid_t zoneid;
zone_t *zptr;
zone_status_t status;
ASSERT(ipip->ipi_cmd_type == LIF_CMD);
if ((zoneid = lifr->lifr_zoneid) == ALL_ZONES)
zoneid = GLOBAL_ZONEID;
ip1dbg(("ip_sioctl_slifzone_restart(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
status = ZONE_IS_UNINITIALIZED;
if ((zptr = zone_find_by_id(zoneid)) != NULL) {
status = zone_status_get(zptr);
zone_rele(zptr);
}
if (status != ZONE_IS_READY && status != ZONE_IS_RUNNING) {
if (ipif->ipif_isv6) {
(void) ipif_up_done_v6(ipif);
} else {
(void) ipif_up_done(ipif);
}
return (EINVAL);
}
(void) ipif_down_tail(ipif);
return (ip_sioctl_slifzone_tail(ipif, lifr->lifr_zoneid, q, mp,
B_TRUE));
}
static uint_t
ill_flagaddr_cnt(const ill_t *ill, uint64_t set, uint64_t clear)
{
ipif_t *ipif;
uint_t cnt = 0;
ASSERT(IAM_WRITER_ILL(ill));
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next)
if ((ipif->ipif_flags & set) && !(ipif->ipif_flags & clear))
cnt++;
return (cnt);
}
uint_t
ill_appaddr_cnt(const ill_t *ill)
{
return (ill_flagaddr_cnt(ill, IPIF_DHCPRUNNING | IPIF_ADDRCONF,
IPIF_NOFAILOVER));
}
uint_t
ill_ptpaddr_cnt(const ill_t *ill)
{
return (ill_flagaddr_cnt(ill, IPIF_POINTOPOINT, 0));
}
int
ip_sioctl_get_lifusesrc(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifreq *lifr = ifreq;
ASSERT(q->q_next == NULL);
ASSERT(CONN_Q(q));
ip1dbg(("ip_sioctl_get_lifusesrc(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
lifr->lifr_index = ipif->ipif_ill->ill_usesrc_ifindex;
ip1dbg(("ip_sioctl_get_lifusesrc:lifr_index = %d\n", lifr->lifr_index));
return (0);
}
static ill_t *
ill_prev_usesrc(ill_t *uill)
{
ill_t *ill;
for (ill = uill->ill_usesrc_grp_next;
ASSERT(ill), ill->ill_usesrc_grp_next != uill;
ill = ill->ill_usesrc_grp_next)
;
return (ill);
}
static void
ill_disband_usesrc_group(ill_t *uill)
{
ill_t *next_ill, *tmp_ill;
ip_stack_t *ipst = uill->ill_ipst;
ASSERT(RW_WRITE_HELD(&ipst->ips_ill_g_usesrc_lock));
next_ill = uill->ill_usesrc_grp_next;
do {
ASSERT(next_ill != NULL);
tmp_ill = next_ill->ill_usesrc_grp_next;
ASSERT(tmp_ill != NULL);
next_ill->ill_usesrc_grp_next = NULL;
next_ill->ill_usesrc_ifindex = 0;
next_ill = tmp_ill;
} while (next_ill->ill_usesrc_ifindex != 0);
uill->ill_usesrc_grp_next = NULL;
}
int
ill_relink_usesrc_ills(ill_t *ucill, ill_t *uill, uint_t ifindex)
{
ill_t *ill, *tmp_ill;
ip_stack_t *ipst = ucill->ill_ipst;
ASSERT((ucill != NULL) && (ucill->ill_usesrc_grp_next != NULL) &&
(uill != NULL) && RW_WRITE_HELD(&ipst->ips_ill_g_usesrc_lock));
if ((ucill->ill_usesrc_ifindex == 0) ||
(uill->ill_usesrc_ifindex != 0)) {
return (-1);
}
ill = ill_prev_usesrc(ucill);
ASSERT(ill->ill_usesrc_grp_next != NULL);
if (ill->ill_usesrc_grp_next->ill_usesrc_grp_next == ill) {
ASSERT(ill->ill_usesrc_ifindex == 0);
ill->ill_usesrc_grp_next = NULL;
} else {
ill->ill_usesrc_grp_next = ucill->ill_usesrc_grp_next;
}
if (ifindex == 0) {
ucill->ill_usesrc_ifindex = 0;
ucill->ill_usesrc_grp_next = NULL;
return (0);
}
ucill->ill_usesrc_ifindex = ifindex;
tmp_ill = uill->ill_usesrc_grp_next;
uill->ill_usesrc_grp_next = ucill;
ucill->ill_usesrc_grp_next =
(tmp_ill != NULL) ? tmp_ill : uill;
return (0);
}
int
ip_sioctl_slifusesrc(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifreq *lifr = (struct lifreq *)ifreq;
boolean_t isv6 = B_FALSE, reset_flg = B_FALSE;
ill_t *usesrc_ill, *usesrc_cli_ill = ipif->ipif_ill;
int err = 0, ret;
uint_t ifindex;
ipsq_t *ipsq = NULL;
ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
ASSERT(IAM_WRITER_IPIF(ipif));
ASSERT(q->q_next == NULL);
ASSERT(CONN_Q(q));
isv6 = (Q_TO_CONN(q))->conn_family == AF_INET6;
ifindex = lifr->lifr_index;
if (ifindex == 0) {
if (usesrc_cli_ill->ill_usesrc_grp_next == NULL) {
return (0);
}
ifindex = usesrc_cli_ill->ill_usesrc_ifindex;
reset_flg = B_TRUE;
}
usesrc_ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
if (usesrc_ill == NULL)
return (ENXIO);
if (usesrc_ill == ipif->ipif_ill) {
ill_refrele(usesrc_ill);
return (EINVAL);
}
ipsq = ipsq_try_enter(NULL, usesrc_ill, q, mp, ip_process_ioctl,
NEW_OP, B_TRUE);
if (ipsq == NULL) {
err = EINPROGRESS;
goto done;
}
if (IS_IPMP(usesrc_ill) || IS_UNDER_IPMP(usesrc_ill)) {
err = ENOTSUP;
goto done;
}
if (usesrc_ill->ill_phyint->phyint_flags & PHYI_STANDBY) {
err = ENOTSUP;
goto done;
}
if (IS_USESRC_ILL(usesrc_cli_ill) || IS_USESRC_CLI_ILL(usesrc_ill)) {
err = EINVAL;
goto done;
}
if (!reset_flg && usesrc_cli_ill->ill_usesrc_ifindex == ifindex) {
err = 0;
goto done;
}
ip1dbg(("ip_sioctl_slifusesrc: usesrc_cli_ill %s, usesrc_ill %s,"
" v6 = %d", usesrc_cli_ill->ill_name, usesrc_ill->ill_name,
usesrc_ill->ill_isv6));
rw_enter(&ipst->ips_ill_g_usesrc_lock, RW_WRITER);
if (reset_flg) {
ret = ill_relink_usesrc_ills(usesrc_cli_ill, usesrc_ill, 0);
if (ret != 0) {
err = EINVAL;
}
rw_exit(&ipst->ips_ill_g_usesrc_lock);
goto done;
}
if ((usesrc_ill->ill_usesrc_grp_next == NULL) &&
(usesrc_cli_ill->ill_usesrc_grp_next == NULL)) {
ASSERT(usesrc_ill->ill_usesrc_ifindex == 0);
usesrc_cli_ill->ill_usesrc_ifindex = ifindex;
usesrc_ill->ill_usesrc_grp_next = usesrc_cli_ill;
usesrc_cli_ill->ill_usesrc_grp_next = usesrc_ill;
} else if ((usesrc_ill->ill_usesrc_grp_next != NULL) &&
(usesrc_cli_ill->ill_usesrc_grp_next == NULL)) {
usesrc_cli_ill->ill_usesrc_ifindex = ifindex;
usesrc_cli_ill->ill_usesrc_grp_next =
usesrc_ill->ill_usesrc_grp_next;
usesrc_ill->ill_usesrc_grp_next = usesrc_cli_ill;
} else {
ret = ill_relink_usesrc_ills(usesrc_cli_ill, usesrc_ill,
ifindex);
if (ret != 0)
err = EINVAL;
}
rw_exit(&ipst->ips_ill_g_usesrc_lock);
done:
if (ipsq != NULL)
ipsq_exit(ipsq);
ill_refrele(usesrc_ill);
ip_update_source_selection(ipst);
return (err);
}
int
ip_sioctl_get_dadstate(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct lifreq *lifr = (struct lifreq *)if_req;
ill_t *ill = ipif->ipif_ill;
mutex_enter(&ill->ill_lock);
if ((ipif->ipif_flags & IPIF_UP) && ipif->ipif_addr_ready == 0)
lifr->lifr_dadstate = DAD_IN_PROGRESS;
else
lifr->lifr_dadstate = DAD_DONE;
mutex_exit(&ill->ill_lock);
return (0);
}
static int
ill_phyint_compare_index(const void *index_ptr, const void *phyip)
{
uint_t index;
ASSERT(phyip != NULL && index_ptr != NULL);
index = *((uint_t *)index_ptr);
if (((phyint_t *)phyip)->phyint_ifindex < index)
return (1);
if (((phyint_t *)phyip)->phyint_ifindex > index)
return (-1);
return (0);
}
static int
ill_phyint_compare_name(const void *name_ptr, const void *phyip)
{
ill_t *ill;
int res = 0;
ASSERT(phyip != NULL && name_ptr != NULL);
if (((phyint_t *)phyip)->phyint_illv4)
ill = ((phyint_t *)phyip)->phyint_illv4;
else
ill = ((phyint_t *)phyip)->phyint_illv6;
ASSERT(ill != NULL);
res = strcmp(ill->ill_name, (char *)name_ptr);
if (res > 0)
return (1);
else if (res < 0)
return (-1);
return (0);
}
static void
phyint_free(phyint_t *phyi)
{
ip_stack_t *ipst = PHYINT_TO_IPST(phyi);
ASSERT(phyi->phyint_illv4 == NULL && phyi->phyint_illv6 == NULL);
if ((phyi->phyint_flags & PHYI_IPMP) && (phyi->phyint_grp != NULL)) {
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
ipmp_grp_destroy(phyi->phyint_grp);
phyi->phyint_grp = NULL;
rw_exit(&ipst->ips_ipmp_lock);
}
if (phyi->phyint_grp != NULL)
ipmp_phyint_leave_grp(phyi);
phyi->phyint_ipsq->ipsq_phyint = NULL;
phyi->phyint_name[0] = '\0';
mi_free(phyi);
}
static void
ill_phyint_reinit(ill_t *ill)
{
boolean_t isv6 = ill->ill_isv6;
phyint_t *phyi_old;
phyint_t *phyi;
avl_index_t where = 0;
ill_t *ill_other = NULL;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(RW_WRITE_HELD(&ipst->ips_ill_g_lock));
phyi_old = ill->ill_phyint;
ASSERT(isv6 || (phyi_old->phyint_illv4 == ill &&
phyi_old->phyint_illv6 == NULL));
ASSERT(!isv6 || (phyi_old->phyint_illv6 == ill &&
phyi_old->phyint_illv4 == NULL));
ASSERT(phyi_old->phyint_ifindex == 0);
(void) strlcpy(ill->ill_phyint->phyint_name, ill->ill_name, LIFNAMSIZ);
phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_name,
ill->ill_name, &where);
if (phyi != NULL) {
ill_other = (isv6) ? phyi->phyint_illv4 : phyi->phyint_illv6;
ASSERT(ill_other->ill_phyint != NULL);
ASSERT((isv6 && !ill_other->ill_isv6) ||
(!isv6 && ill_other->ill_isv6));
GRAB_ILL_LOCKS(ill, ill_other);
mutex_enter(&phyi->phyint_lock);
if (isv6) {
ASSERT(phyi->phyint_illv6 == NULL);
phyi->phyint_illv6 = ill;
} else {
ASSERT(phyi->phyint_illv4 == NULL);
phyi->phyint_illv4 = ill;
}
phyi_old->phyint_illv4 = NULL;
phyi_old->phyint_illv6 = NULL;
phyi_old->phyint_ipsq->ipsq_phyint = NULL;
phyi_old->phyint_name[0] = '\0';
mi_free(phyi_old);
} else {
mutex_enter(&ill->ill_lock);
phyi = phyi_old;
mutex_enter(&phyi->phyint_lock);
if (!phyint_assign_ifindex(phyi, ipst))
cmn_err(CE_PANIC, "phyint_assign_ifindex() failed");
avl_insert(&ipst->ips_phyint_g_list->phyint_list_avl_by_name,
(void *)phyi, where);
(void) avl_find(&ipst->ips_phyint_g_list->
phyint_list_avl_by_index,
&phyi->phyint_ifindex, &where);
avl_insert(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
(void *)phyi, where);
}
ill->ill_phyint = phyi;
ill->ill_ip_mib->ipIfStatsIfIndex = ill->ill_phyint->phyint_ifindex;
if (ill->ill_isv6) {
ill->ill_icmp6_mib->ipv6IfIcmpIfIndex =
ill->ill_phyint->phyint_ifindex;
ill->ill_mcast_type = ipst->ips_mld_max_version;
} else {
ill->ill_mcast_type = ipst->ips_igmp_max_version;
}
if (!IS_LOOPBACK(ill)) {
ill_nic_event_dispatch(ill, 0, NE_PLUMB, ill->ill_name,
ill->ill_name_length);
}
RELEASE_ILL_LOCKS(ill, ill_other);
mutex_exit(&phyi->phyint_lock);
}
static void
ip_ifname_notify(ill_t *ill, queue_t *q)
{
mblk_t *mp1, *mp2;
struct iocblk *iocp;
struct lifreq *lifr;
mp1 = mkiocb(SIOCSLIFNAME);
if (mp1 == NULL)
return;
mp2 = allocb(sizeof (struct lifreq), BPRI_HI);
if (mp2 == NULL) {
freeb(mp1);
return;
}
mp1->b_cont = mp2;
iocp = (struct iocblk *)mp1->b_rptr;
iocp->ioc_count = sizeof (struct lifreq);
lifr = (struct lifreq *)mp2->b_rptr;
mp2->b_wptr += sizeof (struct lifreq);
bzero(lifr, sizeof (struct lifreq));
(void) strncpy(lifr->lifr_name, ill->ill_name, LIFNAMSIZ);
lifr->lifr_ppa = ill->ill_ppa;
lifr->lifr_flags = (ill->ill_flags & (ILLF_IPV4|ILLF_IPV6));
DTRACE_PROBE3(ill__dlpi, char *, "ip_ifname_notify",
char *, "SIOCSLIFNAME", ill_t *, ill);
putnext(q, mp1);
}
static int
ipif_set_values_tail(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
{
int err;
ip_stack_t *ipst = ill->ill_ipst;
phyint_t *phyi = ill->ill_phyint;
if (IS_IPMP(ill)) {
rw_enter(&ipst->ips_ipmp_lock, RW_WRITER);
if (phyi->phyint_grp == NULL) {
if (ipmp_grp_lookup(ill->ill_name, ipst) != NULL) {
rw_exit(&ipst->ips_ipmp_lock);
return (EEXIST);
}
phyi->phyint_grp = ipmp_grp_create(ill->ill_name, phyi);
if (phyi->phyint_grp == NULL) {
rw_exit(&ipst->ips_ipmp_lock);
return (ENOMEM);
}
}
rw_exit(&ipst->ips_ipmp_lock);
}
ip_ifname_notify(ill, q);
err = ill_dl_phys(ill, ipif, mp, q);
if (ill->ill_isv6) {
mutex_enter(&ipst->ips_mld_slowtimeout_lock);
if (ipst->ips_mld_slowtimeout_id == 0) {
ipst->ips_mld_slowtimeout_id = timeout(mld_slowtimo,
(void *)ipst,
MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
}
mutex_exit(&ipst->ips_mld_slowtimeout_lock);
} else {
mutex_enter(&ipst->ips_igmp_slowtimeout_lock);
if (ipst->ips_igmp_slowtimeout_id == 0) {
ipst->ips_igmp_slowtimeout_id = timeout(igmp_slowtimo,
(void *)ipst,
MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
}
mutex_exit(&ipst->ips_igmp_slowtimeout_lock);
}
return (err);
}
static int
ipif_set_values(queue_t *q, mblk_t *mp, char *interf_name, uint_t *new_ppa_ptr)
{
ill_t *ill;
ipif_t *ipif;
ipsq_t *ipsq;
char *ppa_ptr;
char *old_ptr;
char old_char;
int error;
ip_stack_t *ipst;
ip1dbg(("ipif_set_values: interface %s\n", interf_name));
ASSERT(q->q_next != NULL);
ASSERT(interf_name != NULL);
ill = (ill_t *)q->q_ptr;
ipst = ill->ill_ipst;
ASSERT(ill->ill_ipst != NULL);
ASSERT(ill->ill_name[0] == '\0');
ASSERT(IAM_WRITER_ILL(ill));
ASSERT((mi_strlen(interf_name) + 1) <= LIFNAMSIZ);
ASSERT(ill->ill_ppa == UINT_MAX);
ill->ill_defend_start = ill->ill_defend_count = 0;
if ((ppa_ptr = ill_get_ppa_ptr(interf_name)) == NULL) {
return (EINVAL);
}
if (*new_ppa_ptr != UINT_MAX) {
old_ptr = ppa_ptr;
if (*new_ppa_ptr == 0)
*new_ppa_ptr = stoi(&old_ptr);
else if (*new_ppa_ptr != (uint_t)stoi(&old_ptr))
return (EINVAL);
}
old_char = ppa_ptr[0];
ppa_ptr[0] = '\0';
ill->ill_ppa = *new_ppa_ptr;
ipif = ill->ill_ipif;
ipif_assign_seqid(ipif);
if (!(ill->ill_flags & (ILLF_IPV4|ILLF_IPV6)))
ill->ill_flags |= ILLF_IPV4;
ASSERT(ipif->ipif_next == NULL);
ASSERT((ipif->ipif_flags & IPIF_UP) == 0);
if (ill->ill_flags & ILLF_IPV6) {
ill->ill_isv6 = B_TRUE;
ill_set_inputfn(ill);
if (ill->ill_rq != NULL) {
ill->ill_rq->q_qinfo = &iprinitv6;
}
ipif->ipif_v6lcl_addr = ipv6_all_zeros;
ipif->ipif_v6subnet = ipv6_all_zeros;
ipif->ipif_v6net_mask = ipv6_all_zeros;
ipif->ipif_v6brd_addr = ipv6_all_zeros;
ipif->ipif_v6pp_dst_addr = ipv6_all_zeros;
ill->ill_reachable_retrans_time = ND_RETRANS_TIMER;
if (ipif->ipif_flags & IPIF_POINTOPOINT ||
!(ill->ill_flags & ILLF_MULTICAST)) {
ill->ill_flags |= ILLF_NONUD;
}
if (ill->ill_flags & ILLF_NOARP) {
ill->ill_flags &= ~ILLF_NOARP;
}
if (ipst->ips_ipv6_forwarding != 0)
ill->ill_flags |= ILLF_ROUTER;
} else if (ill->ill_flags & ILLF_IPV4) {
ill->ill_isv6 = B_FALSE;
ill_set_inputfn(ill);
ill->ill_reachable_retrans_time = ARP_RETRANS_TIMER;
IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &ipif->ipif_v6lcl_addr);
IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &ipif->ipif_v6subnet);
IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &ipif->ipif_v6net_mask);
IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &ipif->ipif_v6brd_addr);
IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &ipif->ipif_v6pp_dst_addr);
if (ipst->ips_ip_forwarding != 0)
ill->ill_flags |= ILLF_ROUTER;
}
ASSERT(ill->ill_phyint != NULL);
if (!ill_allocate_mibs(ill))
return (ENOMEM);
ill->ill_sap = (ill->ill_isv6) ? ill->ill_media->ip_m_ipv6sap :
ill->ill_media->ip_m_ipv4sap;
ill->ill_ifname_pending = 1;
ill->ill_ifname_pending_err = 0;
ill->ill_need_recover_multicast = 1;
ill_refhold(ill);
rw_enter(&ipst->ips_ill_g_lock, RW_WRITER);
if ((error = ill_glist_insert(ill, interf_name,
(ill->ill_flags & ILLF_IPV6) == ILLF_IPV6)) > 0) {
ill->ill_ppa = UINT_MAX;
ill->ill_name[0] = '\0';
ppa_ptr[0] = old_char;
rw_exit(&ipst->ips_ill_g_lock);
ill_refrele(ill);
return (error);
}
ASSERT(ill->ill_name_length <= LIFNAMSIZ);
if (*new_ppa_ptr == UINT_MAX) {
bcopy(ill->ill_name, interf_name, ill->ill_name_length);
*new_ppa_ptr = ill->ill_ppa;
} else {
ppa_ptr[0] = old_char;
}
sctp_update_ill(ill, SCTP_ILL_INSERT);
ipsq = ipsq_try_enter_internal(ill, q, mp, ip_reprocess_ioctl, NEW_OP,
B_TRUE);
rw_exit(&ipst->ips_ill_g_lock);
ill_refrele(ill);
if (ipsq == NULL)
return (EINPROGRESS);
if (ipsq->ipsq_xop->ipx_current_ipif == NULL)
ipsq_current_start(ipsq, ipif, SIOCSLIFNAME);
else
ASSERT(ipsq->ipsq_xop->ipx_current_ipif == ipif);
error = ipif_set_values_tail(ill, ipif, mp, q);
ipsq_exit(ipsq);
if (error != 0 && error != EINPROGRESS) {
ill->ill_isv6 = B_FALSE;
ill_set_inputfn(ill);
}
return (error);
}
void
ipif_init(ip_stack_t *ipst)
{
int i;
for (i = 0; i < MAX_G_HEADS; i++) {
ipst->ips_ill_g_heads[i].ill_g_list_head =
(ill_if_t *)&ipst->ips_ill_g_heads[i];
ipst->ips_ill_g_heads[i].ill_g_list_tail =
(ill_if_t *)&ipst->ips_ill_g_heads[i];
}
avl_create(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
ill_phyint_compare_index,
sizeof (phyint_t),
offsetof(struct phyint, phyint_avl_by_index));
avl_create(&ipst->ips_phyint_g_list->phyint_list_avl_by_name,
ill_phyint_compare_name,
sizeof (phyint_t),
offsetof(struct phyint, phyint_avl_by_name));
}
void
ill_save_ire(ill_t *ill, ire_t *ire)
{
mblk_t *save_mp;
save_mp = allocb(sizeof (ifrt_t), BPRI_MED);
if (save_mp != NULL) {
ifrt_t *ifrt;
save_mp->b_wptr += sizeof (ifrt_t);
ifrt = (ifrt_t *)save_mp->b_rptr;
bzero(ifrt, sizeof (ifrt_t));
ifrt->ifrt_type = ire->ire_type;
if (ire->ire_ipversion == IPV4_VERSION) {
ASSERT(!ill->ill_isv6);
ifrt->ifrt_addr = ire->ire_addr;
ifrt->ifrt_gateway_addr = ire->ire_gateway_addr;
ifrt->ifrt_setsrc_addr = ire->ire_setsrc_addr;
ifrt->ifrt_mask = ire->ire_mask;
} else {
ASSERT(ill->ill_isv6);
ifrt->ifrt_v6addr = ire->ire_addr_v6;
mutex_enter(&ire->ire_lock);
ifrt->ifrt_v6gateway_addr = ire->ire_gateway_addr_v6;
mutex_exit(&ire->ire_lock);
ifrt->ifrt_v6setsrc_addr = ire->ire_setsrc_addr_v6;
ifrt->ifrt_v6mask = ire->ire_mask_v6;
}
ifrt->ifrt_flags = ire->ire_flags;
ifrt->ifrt_zoneid = ire->ire_zoneid;
mutex_enter(&ill->ill_saved_ire_lock);
save_mp->b_cont = ill->ill_saved_ire_mp;
ill->ill_saved_ire_mp = save_mp;
ill->ill_saved_ire_cnt++;
mutex_exit(&ill->ill_saved_ire_lock);
}
}
void
ill_remove_saved_ire(ill_t *ill, ire_t *ire)
{
mblk_t **mpp;
mblk_t *mp;
ifrt_t *ifrt;
mutex_enter(&ill->ill_saved_ire_lock);
for (mpp = &ill->ill_saved_ire_mp; *mpp != NULL;
mpp = &(*mpp)->b_cont) {
in6_addr_t gw_addr_v6;
mp = *mpp;
ifrt = (ifrt_t *)mp->b_rptr;
mutex_enter(&ire->ire_lock);
gw_addr_v6 = ire->ire_gateway_addr_v6;
mutex_exit(&ire->ire_lock);
if (ifrt->ifrt_zoneid != ire->ire_zoneid ||
ifrt->ifrt_type != ire->ire_type)
continue;
if (ill->ill_isv6 ?
(IN6_ARE_ADDR_EQUAL(&ifrt->ifrt_v6addr,
&ire->ire_addr_v6) &&
IN6_ARE_ADDR_EQUAL(&ifrt->ifrt_v6gateway_addr,
&gw_addr_v6) &&
IN6_ARE_ADDR_EQUAL(&ifrt->ifrt_v6mask,
&ire->ire_mask_v6)) :
(ifrt->ifrt_addr == ire->ire_addr &&
ifrt->ifrt_gateway_addr == ire->ire_gateway_addr &&
ifrt->ifrt_mask == ire->ire_mask)) {
*mpp = mp->b_cont;
ill->ill_saved_ire_cnt--;
freeb(mp);
break;
}
}
mutex_exit(&ill->ill_saved_ire_lock);
}
static void
ip_cgtp_bcast_add(ire_t *ire, ip_stack_t *ipst)
{
ire_t *ire_prim;
ASSERT(ire != NULL);
ire_prim = ire_ftable_lookup_v4(ire->ire_gateway_addr, 0, 0,
IRE_BROADCAST, NULL, ALL_ZONES, NULL, MATCH_IRE_TYPE, 0, ipst,
NULL);
if (ire_prim != NULL) {
ill_t *ill_prim;
ire_t *bcast_ire;
ill_prim = ire_prim->ire_ill;
ip2dbg(("ip_cgtp_filter_bcast_add: ire_prim %p, ill_prim %p\n",
(void *)ire_prim, (void *)ill_prim));
bcast_ire = ire_create(
(uchar_t *)&ire->ire_addr,
(uchar_t *)&ip_g_all_ones,
(uchar_t *)&ire->ire_gateway_addr,
IRE_BROADCAST,
ill_prim,
GLOBAL_ZONEID,
ire->ire_flags | RTF_KERNEL,
NULL,
ipst);
if (bcast_ire != NULL) {
if (ire->ire_flags & RTF_SETSRC) {
bcast_ire->ire_setsrc_addr =
ire->ire_setsrc_addr;
}
bcast_ire = ire_add(bcast_ire);
if (bcast_ire != NULL) {
ip2dbg(("ip_cgtp_filter_bcast_add: "
"added bcast_ire %p\n",
(void *)bcast_ire));
ill_save_ire(ill_prim, bcast_ire);
ire_refrele(bcast_ire);
}
}
ire_refrele(ire_prim);
}
}
static void
ip_cgtp_bcast_delete(ire_t *ire, ip_stack_t *ipst)
{
ASSERT(ire != NULL);
if (ip_type_v4(ire->ire_addr, ipst) == IRE_BROADCAST) {
ire_t *ire_prim;
ire_prim = ire_ftable_lookup_v4(ire->ire_gateway_addr, 0, 0,
IRE_BROADCAST, NULL, ALL_ZONES, NULL, MATCH_IRE_TYPE, 0,
ipst, NULL);
if (ire_prim != NULL) {
ill_t *ill_prim;
ire_t *bcast_ire;
ill_prim = ire_prim->ire_ill;
ip2dbg(("ip_cgtp_filter_bcast_delete: "
"ire_prim %p, ill_prim %p\n",
(void *)ire_prim, (void *)ill_prim));
bcast_ire = ire_ftable_lookup_v4(ire->ire_addr, 0,
ire->ire_gateway_addr, IRE_BROADCAST,
ill_prim, ALL_ZONES, NULL,
MATCH_IRE_TYPE | MATCH_IRE_GW | MATCH_IRE_ILL |
MATCH_IRE_MASK, 0, ipst, NULL);
if (bcast_ire != NULL) {
ip2dbg(("ip_cgtp_filter_bcast_delete: "
"looked up bcast_ire %p\n",
(void *)bcast_ire));
ill_remove_saved_ire(bcast_ire->ire_ill,
bcast_ire);
ire_delete(bcast_ire);
ire_refrele(bcast_ire);
}
ire_refrele(ire_prim);
}
}
}
static void
ip_ether_v6intfid(ill_t *ill, in6_addr_t *v6addr)
{
char *addr;
if (ill->ill_phys_addr_length == ETHERADDRL) {
addr = (char *)&v6addr->s6_addr32[2];
bcopy(ill->ill_phys_addr, addr, 3);
addr[0] ^= 0x2;
addr[3] = (char)0xff;
addr[4] = (char)0xfe;
bcopy(ill->ill_phys_addr + 3, addr + 5, 3);
}
}
static void
ip_nodef_v6intfid(ill_t *ill, in6_addr_t *v6addr)
{
}
typedef struct ipmp_ifcookie {
uint32_t ic_hostid;
char ic_ifname[LIFNAMSIZ];
char ic_zonename[ZONENAME_MAX];
} ipmp_ifcookie_t;
static void
ip_ipmp_v6intfid(ill_t *ill, in6_addr_t *v6addr)
{
zone_t *zp;
uint8_t *addr;
uchar_t hash[16];
ulong_t hostid;
MD5_CTX ctx;
ipmp_ifcookie_t ic = { 0 };
ASSERT(IS_IPMP(ill));
(void) ddi_strtoul(hw_serial, NULL, 10, &hostid);
ic.ic_hostid = htonl((uint32_t)hostid);
(void) strlcpy(ic.ic_ifname, ill->ill_name, LIFNAMSIZ);
if ((zp = zone_find_by_id(ill->ill_zoneid)) != NULL) {
(void) strlcpy(ic.ic_zonename, zp->zone_name, ZONENAME_MAX);
zone_rele(zp);
}
MD5Init(&ctx);
MD5Update(&ctx, &ic, sizeof (ic));
MD5Final(hash, &ctx);
addr = &v6addr->s6_addr8[8];
bcopy(hash + 8, addr, sizeof (uint64_t));
addr[0] &= ~0x2;
}
static void
ip_ether_v6_mapping(ill_t *ill, uchar_t *m_ip6addr, uchar_t *m_physaddr)
{
phyint_t *phyi = ill->ill_phyint;
if ((phyi->phyint_flags & PHYI_MULTI_BCAST) != 0 ||
ill->ill_phys_addr_length != ETHERADDRL) {
ip_mbcast_mapping(ill, m_ip6addr, m_physaddr);
return;
}
m_physaddr[0] = 0x33;
m_physaddr[1] = 0x33;
m_physaddr[2] = m_ip6addr[12];
m_physaddr[3] = m_ip6addr[13];
m_physaddr[4] = m_ip6addr[14];
m_physaddr[5] = m_ip6addr[15];
}
static void
ip_ether_v4_mapping(ill_t *ill, uchar_t *m_ipaddr, uchar_t *m_physaddr)
{
phyint_t *phyi = ill->ill_phyint;
if ((phyi->phyint_flags & PHYI_MULTI_BCAST) != 0 ||
ill->ill_phys_addr_length != ETHERADDRL) {
ip_mbcast_mapping(ill, m_ipaddr, m_physaddr);
return;
}
m_physaddr[0] = 0x01;
m_physaddr[1] = 0x00;
m_physaddr[2] = 0x5e;
m_physaddr[3] = m_ipaddr[1] & 0x7f;
m_physaddr[4] = m_ipaddr[2];
m_physaddr[5] = m_ipaddr[3];
}
static void
ip_mbcast_mapping(ill_t *ill, uchar_t *m_ipaddr, uchar_t *m_physaddr)
{
uint8_t *bphys_addr;
dl_unitdata_req_t *dlur;
dlur = (dl_unitdata_req_t *)ill->ill_bcast_mp->b_rptr;
if (ill->ill_sap_length < 0) {
bphys_addr = (uchar_t *)dlur +
dlur->dl_dest_addr_offset;
} else {
bphys_addr = (uchar_t *)dlur +
dlur->dl_dest_addr_offset + ill->ill_sap_length;
}
bcopy(bphys_addr, m_physaddr, ill->ill_phys_addr_length);
}
static void
ip_ib_v6intfid(ill_t *ill, in6_addr_t *v6addr)
{
char *addr;
ASSERT(ill->ill_phys_addr_length == 20);
addr = (char *)&v6addr->s6_addr32[2];
bcopy(ill->ill_phys_addr + 12, addr, 8);
addr[0] |= 2;
}
static void
ip_ib_v4_mapping(ill_t *ill, uchar_t *m_ipaddr, uchar_t *m_physaddr)
{
static uint8_t ipv4_g_phys_ibmulti_addr[] = { 0x00, 0xff, 0xff, 0xff,
0xff, 0x10, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t *bphys_addr;
dl_unitdata_req_t *dlur;
bcopy(ipv4_g_phys_ibmulti_addr, m_physaddr, ill->ill_phys_addr_length);
m_physaddr[16] = m_ipaddr[0] & 0x0f;
m_physaddr[17] = m_ipaddr[1];
m_physaddr[18] = m_ipaddr[2];
m_physaddr[19] = m_ipaddr[3];
dlur = (dl_unitdata_req_t *)ill->ill_bcast_mp->b_rptr;
if (ill->ill_sap_length < 0) {
bphys_addr = (uchar_t *)dlur + dlur->dl_dest_addr_offset;
} else {
bphys_addr = (uchar_t *)dlur + dlur->dl_dest_addr_offset +
ill->ill_sap_length;
}
m_physaddr[5] = bphys_addr[5];
m_physaddr[8] = bphys_addr[8];
m_physaddr[9] = bphys_addr[9];
}
static void
ip_ib_v6_mapping(ill_t *ill, uchar_t *m_ipaddr, uchar_t *m_physaddr)
{
static uint8_t ipv4_g_phys_ibmulti_addr[] = { 0x00, 0xff, 0xff, 0xff,
0xff, 0x10, 0x60, 0x1b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t *bphys_addr;
dl_unitdata_req_t *dlur;
bcopy(ipv4_g_phys_ibmulti_addr, m_physaddr, ill->ill_phys_addr_length);
bcopy(&m_ipaddr[6], &m_physaddr[10], 10);
dlur = (dl_unitdata_req_t *)ill->ill_bcast_mp->b_rptr;
if (ill->ill_sap_length < 0) {
bphys_addr = (uchar_t *)dlur + dlur->dl_dest_addr_offset;
} else {
bphys_addr = (uchar_t *)dlur + dlur->dl_dest_addr_offset +
ill->ill_sap_length;
}
m_physaddr[5] = bphys_addr[5];
m_physaddr[8] = bphys_addr[8];
m_physaddr[9] = bphys_addr[9];
}
static void
ip_ipv4_genv6intfid(ill_t *ill, uint8_t *physaddr, in6_addr_t *v6addr)
{
ASSERT(ill->ill_phys_addr_length == sizeof (ipaddr_t));
v6addr->s6_addr32[2] = 0;
bcopy(physaddr, &v6addr->s6_addr32[3], sizeof (ipaddr_t));
}
static void
ip_ipv6_genv6intfid(ill_t *ill, uint8_t *physaddr, in6_addr_t *v6addr)
{
in6_addr_t *v6lladdr = (in6_addr_t *)physaddr;
ASSERT(ill->ill_phys_addr_length == sizeof (in6_addr_t));
bcopy(&v6lladdr->s6_addr32[2], &v6addr->s6_addr32[2], 8);
}
static void
ip_ipv6_v6intfid(ill_t *ill, in6_addr_t *v6addr)
{
ip_ipv6_genv6intfid(ill, ill->ill_phys_addr, v6addr);
}
static void
ip_ipv6_v6destintfid(ill_t *ill, in6_addr_t *v6addr)
{
ip_ipv6_genv6intfid(ill, ill->ill_dest_addr, v6addr);
}
static void
ip_ipv4_v6intfid(ill_t *ill, in6_addr_t *v6addr)
{
ip_ipv4_genv6intfid(ill, ill->ill_phys_addr, v6addr);
}
static void
ip_ipv4_v6destintfid(ill_t *ill, in6_addr_t *v6addr)
{
ip_ipv4_genv6intfid(ill, ill->ill_dest_addr, v6addr);
}
ill_t *
ill_lookup_on_ifindex_zoneid(uint_t index, zoneid_t zoneid, boolean_t isv6,
ip_stack_t *ipst)
{
ill_t *ill;
ipif_t *ipif;
ill = ill_lookup_on_ifindex(index, isv6, ipst);
if (ill == NULL)
return (NULL);
mutex_enter(&ill->ill_lock);
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (IPIF_IS_CONDEMNED(ipif))
continue;
if (zoneid != ALL_ZONES && ipif->ipif_zoneid != zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
mutex_exit(&ill->ill_lock);
return (ill);
}
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
return (NULL);
}
ipif_t *
ipif_getby_indexes(uint_t ifindex, uint_t lifidx, boolean_t isv6,
ip_stack_t *ipst)
{
ipif_t *ipif;
ill_t *ill;
ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
if (ill == NULL)
return (NULL);
mutex_enter(&ill->ill_lock);
if (ill->ill_state_flags & ILL_CONDEMNED) {
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
return (NULL);
}
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) {
if (!IPIF_CAN_LOOKUP(ipif))
continue;
if (lifidx == ipif->ipif_id) {
ipif_refhold_locked(ipif);
break;
}
}
mutex_exit(&ill->ill_lock);
ill_refrele(ill);
return (ipif);
}
void
ill_set_inputfn(ill_t *ill)
{
ip_stack_t *ipst = ill->ill_ipst;
if (ill->ill_isv6) {
if (is_system_labeled())
ill->ill_inputfn = ill_input_full_v6;
else
ill->ill_inputfn = ill_input_short_v6;
} else {
if (is_system_labeled())
ill->ill_inputfn = ill_input_full_v4;
else if (ill->ill_dhcpinit != 0)
ill->ill_inputfn = ill_input_full_v4;
else if (ipst->ips_ipcl_proto_fanout_v4[IPPROTO_RSVP].connf_head
!= NULL)
ill->ill_inputfn = ill_input_full_v4;
else if (ipst->ips_ip_cgtp_filter &&
ipst->ips_ip_cgtp_filter_ops != NULL)
ill->ill_inputfn = ill_input_full_v4;
else
ill->ill_inputfn = ill_input_short_v4;
}
}
void
ill_set_inputfn_all(ip_stack_t *ipst)
{
ill_walk_context_t ctx;
ill_t *ill;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill))
ill_set_inputfn(ill);
rw_exit(&ipst->ips_ill_g_lock);
}
int
ill_set_phys_addr(ill_t *ill, mblk_t *mp)
{
ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
dl_notify_ind_t *dlindp = (dl_notify_ind_t *)mp->b_rptr;
ASSERT(IAM_WRITER_IPSQ(ipsq));
if (dlindp->dl_data != DL_IPV6_LINK_LAYER_ADDR &&
dlindp->dl_data != DL_CURR_DEST_ADDR &&
dlindp->dl_data != DL_CURR_PHYS_ADDR) {
return (0);
}
if ((mp = copyb(mp)) == NULL || (mp->b_cont = copyb(mp)) == NULL) {
freemsg(mp);
return (ENOMEM);
}
ipsq_current_start(ipsq, ill->ill_ipif, 0);
mutex_enter(&ill->ill_lock);
ill->ill_state_flags |= ILL_DOWN_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
ill_down_ipifs(ill, B_TRUE);
mutex_enter(&ill->ill_lock);
if (!ill_is_quiescent(ill)) {
(void) ipsq_pending_mp_add(NULL, ill->ill_ipif, ill->ill_rq,
mp, ILL_DOWN);
mutex_exit(&ill->ill_lock);
return (EINPROGRESS);
}
mutex_exit(&ill->ill_lock);
ill_set_phys_addr_tail(ipsq, ill->ill_rq, mp, NULL);
return (0);
}
void
ill_set_allowed_ips(ill_t *ill, mblk_t *mp)
{
ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
dl_notify_ind_t *dlip = (dl_notify_ind_t *)mp->b_rptr;
mac_protect_t *mrp;
int i;
ASSERT(IAM_WRITER_IPSQ(ipsq));
mrp = (mac_protect_t *)&dlip[1];
if (mrp->mp_ipaddrcnt == 0) {
kmem_free(ill->ill_allowed_ips,
ill->ill_allowed_ips_cnt * sizeof (in6_addr_t));
ill->ill_allowed_ips_cnt = 0;
ill->ill_allowed_ips = NULL;
mutex_enter(&ill->ill_phyint->phyint_lock);
ill->ill_phyint->phyint_flags &= ~PHYI_L3PROTECT;
mutex_exit(&ill->ill_phyint->phyint_lock);
return;
}
if (ill->ill_allowed_ips != NULL) {
kmem_free(ill->ill_allowed_ips,
ill->ill_allowed_ips_cnt * sizeof (in6_addr_t));
}
ill->ill_allowed_ips_cnt = mrp->mp_ipaddrcnt;
ill->ill_allowed_ips = kmem_alloc(
ill->ill_allowed_ips_cnt * sizeof (in6_addr_t), KM_SLEEP);
for (i = 0; i < mrp->mp_ipaddrcnt; i++)
ill->ill_allowed_ips[i] = mrp->mp_ipaddrs[i].ip_addr;
mutex_enter(&ill->ill_phyint->phyint_lock);
ill->ill_phyint->phyint_flags |= PHYI_L3PROTECT;
mutex_exit(&ill->ill_phyint->phyint_lock);
}
static void
ill_set_phys_addr_tail(ipsq_t *ipsq, queue_t *q, mblk_t *addrmp, void *dummy)
{
ill_t *ill = q->q_ptr;
mblk_t *addrmp2 = unlinkb(addrmp);
dl_notify_ind_t *dlindp = (dl_notify_ind_t *)addrmp->b_rptr;
uint_t addrlen, addroff;
int status;
ASSERT(IAM_WRITER_IPSQ(ipsq));
addroff = dlindp->dl_addr_offset;
addrlen = dlindp->dl_addr_length - ABS(ill->ill_sap_length);
switch (dlindp->dl_data) {
case DL_IPV6_LINK_LAYER_ADDR:
ill_set_ndmp(ill, addrmp, addroff, addrlen);
freemsg(addrmp2);
break;
case DL_CURR_DEST_ADDR:
freemsg(ill->ill_dest_addr_mp);
ill->ill_dest_addr = addrmp->b_rptr + addroff;
ill->ill_dest_addr_mp = addrmp;
if (ill->ill_isv6) {
ill_setdesttoken(ill);
ipif_setdestlinklocal(ill->ill_ipif);
}
freemsg(addrmp2);
break;
case DL_CURR_PHYS_ADDR:
freemsg(ill->ill_phys_addr_mp);
ill->ill_phys_addr = addrmp->b_rptr + addroff;
ill->ill_phys_addr_mp = addrmp;
ill->ill_phys_addr_length = addrlen;
if (ill->ill_isv6)
ill_set_ndmp(ill, addrmp2, addroff, addrlen);
else
freemsg(addrmp2);
if (ill->ill_isv6) {
ill_setdefaulttoken(ill);
ipif_setlinklocal(ill->ill_ipif);
}
break;
default:
ASSERT(0);
}
mutex_enter(&ill->ill_lock);
ill->ill_state_flags &= ~ILL_DOWN_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
status = ill_up_ipifs(ill, q, addrmp);
if (status != EINPROGRESS)
ipsq_current_finish(ipsq);
}
void
ill_set_ndmp(ill_t *ill, mblk_t *ndmp, uint_t addroff, uint_t addrlen)
{
freemsg(ill->ill_nd_lla_mp);
ill->ill_nd_lla = ndmp->b_rptr + addroff;
ill->ill_nd_lla_mp = ndmp;
ill->ill_nd_lla_len = addrlen;
}
int
ill_replumb(ill_t *ill, mblk_t *mp)
{
ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
ASSERT(IAM_WRITER_IPSQ(ipsq));
ipsq_current_start(ipsq, ill->ill_ipif, 0);
ill_down_ipifs(ill, B_FALSE);
mutex_enter(&ill->ill_lock);
if (!ill_is_quiescent(ill)) {
(void) ipsq_pending_mp_add(NULL, ill->ill_ipif, ill->ill_rq,
mp, ILL_DOWN);
mutex_exit(&ill->ill_lock);
return (EINPROGRESS);
}
mutex_exit(&ill->ill_lock);
ill_replumb_tail(ipsq, ill->ill_rq, mp, NULL);
return (0);
}
static void
ill_replumb_tail(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy)
{
ill_t *ill = q->q_ptr;
int err;
conn_t *connp = NULL;
ASSERT(IAM_WRITER_IPSQ(ipsq));
freemsg(ill->ill_replumb_mp);
ill->ill_replumb_mp = copyb(mp);
if (ill->ill_replumb_mp == NULL) {
ipsq_current_finish(ipsq);
return;
}
mutex_enter(&ill->ill_lock);
ill->ill_up_ipifs = ipsq_pending_mp_add(NULL, ill->ill_ipif,
ill->ill_rq, ill->ill_replumb_mp, 0);
mutex_exit(&ill->ill_lock);
if (!ill->ill_up_ipifs) {
ipsq_current_finish(ipsq);
return;
}
ill->ill_replumbing = 1;
err = ill_down_ipifs_tail(ill);
mp = mexchange(NULL, mp, sizeof (dl_notify_conf_t), M_PROTO,
DL_NOTIFY_CONF);
ASSERT(mp != NULL);
((dl_notify_conf_t *)mp->b_rptr)->dl_notification =
DL_NOTE_REPLUMB_DONE;
ill_dlpi_send(ill, mp);
ASSERT(ill->ill_replumb_mp != NULL);
if (err == EINPROGRESS)
return;
else
ill->ill_replumb_mp = ipsq_pending_mp_get(ipsq, &connp);
ASSERT(connp == NULL);
if (err == 0 && ill->ill_replumb_mp != NULL &&
ill_up_ipifs(ill, q, ill->ill_replumb_mp) == EINPROGRESS) {
return;
}
ipsq_current_finish(ipsq);
}
static int
ip_ioctl(ldi_handle_t lh, int cmd, void *buf, uint_t bufsize, cred_t *cr)
{
int rval;
struct strioctl iocb;
iocb.ic_cmd = cmd;
iocb.ic_timout = 15;
iocb.ic_len = bufsize;
iocb.ic_dp = buf;
return (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, cr, &rval));
}
static int
ip_lifconf_ioctl(ldi_handle_t lh, int af, struct lifconf *lifcp,
uint_t *bufsizep, cred_t *cr)
{
int err;
struct lifnum lifn;
bzero(&lifn, sizeof (lifn));
lifn.lifn_family = af;
lifn.lifn_flags = LIFC_UNDER_IPMP;
if ((err = ip_ioctl(lh, SIOCGLIFNUM, &lifn, sizeof (lifn), cr)) != 0)
return (err);
lifn.lifn_count += 4;
bzero(lifcp, sizeof (*lifcp));
lifcp->lifc_flags = LIFC_UNDER_IPMP;
lifcp->lifc_family = af;
lifcp->lifc_len = *bufsizep = lifn.lifn_count * sizeof (struct lifreq);
lifcp->lifc_buf = kmem_zalloc(*bufsizep, KM_SLEEP);
err = ip_ioctl(lh, SIOCGLIFCONF, lifcp, sizeof (*lifcp), cr);
if (err != 0) {
kmem_free(lifcp->lifc_buf, *bufsizep);
return (err);
}
return (0);
}
static void
ip_loopback_removeif(ldi_handle_t lh, boolean_t isv6, cred_t *cr)
{
int err;
struct lifreq lifr;
bzero(&lifr, sizeof (lifr));
(void) strcpy(lifr.lifr_name, ipif_loopback_name);
err = ip_ioctl(lh, SIOCLIFREMOVEIF, &lifr, sizeof (lifr), cr);
if (err != 0 && err != ENXIO) {
ip0dbg(("ip_loopback_removeif: IP%s SIOCLIFREMOVEIF failed: "
"error %d\n", isv6 ? "v6" : "v4", err));
}
}
static void
ip_ipmp_cleanup(ldi_handle_t lh, boolean_t isv6, cred_t *cr)
{
int af = isv6 ? AF_INET6 : AF_INET;
int i, nifs;
int err;
uint_t bufsize;
uint_t lifrsize = sizeof (struct lifreq);
struct lifconf lifc;
struct lifreq *lifrp;
if ((err = ip_lifconf_ioctl(lh, af, &lifc, &bufsize, cr)) != 0) {
cmn_err(CE_WARN, "ip_ipmp_cleanup: cannot get interface list "
"(error %d); any IPMP interfaces cannot be shutdown", err);
return;
}
nifs = lifc.lifc_len / lifrsize;
for (lifrp = lifc.lifc_req, i = 0; i < nifs; i++, lifrp++) {
err = ip_ioctl(lh, SIOCGLIFFLAGS, lifrp, lifrsize, cr);
if (err != 0) {
cmn_err(CE_WARN, "ip_ipmp_cleanup: %s: cannot get "
"flags: error %d", lifrp->lifr_name, err);
continue;
}
if (lifrp->lifr_flags & IFF_IPMP) {
if ((lifrp->lifr_flags & (IFF_UP|IFF_DUPLICATE)) == 0)
continue;
lifrp->lifr_flags &= ~IFF_UP;
err = ip_ioctl(lh, SIOCSLIFFLAGS, lifrp, lifrsize, cr);
if (err != 0) {
cmn_err(CE_WARN, "ip_ipmp_cleanup: %s: cannot "
"bring down (error %d); IPMP interface may "
"not be shutdown", lifrp->lifr_name, err);
}
err = ip_ioctl(lh, SIOCGLIFFLAGS, lifrp, lifrsize, cr);
if (err != 0 || !(lifrp->lifr_flags & IFF_DUPLICATE))
continue;
err = ip_ioctl(lh, SIOCGLIFADDR, lifrp, lifrsize, cr);
if (err != 0 || (err = ip_ioctl(lh, SIOCGLIFADDR,
lifrp, lifrsize, cr)) != 0) {
cmn_err(CE_WARN, "ip_ipmp_cleanup: %s: cannot "
"reset DAD (error %d); IPMP interface may "
"not be shutdown", lifrp->lifr_name, err);
}
continue;
}
if (strchr(lifrp->lifr_name, IPIF_SEPARATOR_CHAR) == 0) {
lifrp->lifr_groupname[0] = '\0';
if ((err = ip_ioctl(lh, SIOCSLIFGROUPNAME, lifrp,
lifrsize, cr)) != 0) {
cmn_err(CE_WARN, "ip_ipmp_cleanup: %s: cannot "
"leave IPMP group (error %d); associated "
"IPMP interface may not be shutdown",
lifrp->lifr_name, err);
continue;
}
}
}
kmem_free(lifc.lifc_buf, bufsize);
}
#define UDPDEV "/devices/pseudo/udp@0:udp"
#define UDP6DEV "/devices/pseudo/udp6@0:udp6"
void
ip_interface_cleanup(ip_stack_t *ipst)
{
ldi_handle_t lh;
ldi_ident_t li;
cred_t *cr;
int err;
int i;
char *devs[] = { UDP6DEV, UDPDEV };
netstackid_t stackid = ipst->ips_netstack->netstack_stackid;
if ((err = ldi_ident_from_major(ddi_name_to_major("ip"), &li)) != 0) {
cmn_err(CE_WARN, "ip_interface_cleanup: cannot get ldi ident:"
" error %d", err);
return;
}
cr = zone_get_kcred(netstackid_to_zoneid(stackid));
ASSERT(cr != NULL);
for (i = 0; i < 2; i++) {
err = ldi_open_by_name(devs[i], FREAD|FWRITE, cr, &lh, li);
if (err != 0) {
cmn_err(CE_WARN, "ip_interface_cleanup: cannot open %s:"
" error %d", devs[i], err);
continue;
}
ip_loopback_removeif(lh, i == 0, cr);
ip_ipmp_cleanup(lh, i == 0, cr);
(void) ldi_close(lh, FREAD|FWRITE, cr);
}
ldi_ident_release(li);
crfree(cr);
}
static const char *
ill_hook_event2str(nic_event_t event)
{
switch (event) {
case NE_PLUMB:
return ("PLUMB");
case NE_UNPLUMB:
return ("UNPLUMB");
case NE_UP:
return ("UP");
case NE_DOWN:
return ("DOWN");
case NE_ADDRESS_CHANGE:
return ("ADDRESS_CHANGE");
case NE_LIF_UP:
return ("LIF_UP");
case NE_LIF_DOWN:
return ("LIF_DOWN");
case NE_IFINDEX_CHANGE:
return ("IFINDEX_CHANGE");
default:
return ("UNKNOWN");
}
}
void
ill_nic_event_dispatch(ill_t *ill, lif_if_t lif, nic_event_t event,
nic_event_data_t data, size_t datalen)
{
ip_stack_t *ipst = ill->ill_ipst;
hook_nic_event_int_t *info;
const char *str = NULL;
if ((info = kmem_alloc(sizeof (*info), KM_NOSLEEP)) == NULL)
goto fail;
info->hnei_event.hne_nic = ill->ill_phyint->phyint_ifindex;
info->hnei_event.hne_lif = lif;
info->hnei_event.hne_event = event;
info->hnei_event.hne_protocol = ill->ill_isv6 ?
ipst->ips_ipv6_net_data : ipst->ips_ipv4_net_data;
info->hnei_event.hne_data = NULL;
info->hnei_event.hne_datalen = 0;
info->hnei_stackid = ipst->ips_netstack->netstack_stackid;
if (data != NULL && datalen != 0) {
info->hnei_event.hne_data = kmem_alloc(datalen, KM_NOSLEEP);
if (info->hnei_event.hne_data == NULL)
goto fail;
bcopy(data, info->hnei_event.hne_data, datalen);
info->hnei_event.hne_datalen = datalen;
}
if (ddi_taskq_dispatch(eventq_queue_nic, ip_ne_queue_func, info,
DDI_NOSLEEP) == DDI_SUCCESS)
return;
fail:
if (info != NULL) {
if (info->hnei_event.hne_data != NULL) {
kmem_free(info->hnei_event.hne_data,
info->hnei_event.hne_datalen);
}
kmem_free(info, sizeof (hook_nic_event_t));
}
str = ill_hook_event2str(event);
ip2dbg(("ill_nic_event_dispatch: could not dispatch %s nic event "
"information for %s (ENOMEM)\n", str, ill->ill_name));
}
static int
ipif_arp_up_done_tail(ipif_t *ipif, enum ip_resolver_action res_act)
{
int err = 0;
const in_addr_t *addr = NULL;
nce_t *nce = NULL;
ill_t *ill = ipif->ipif_ill;
ill_t *bound_ill;
boolean_t added_ipif = B_FALSE;
uint16_t state;
uint16_t flags;
DTRACE_PROBE3(ipif__downup, char *, "ipif_arp_up_done_tail",
ill_t *, ill, ipif_t *, ipif);
if (ipif->ipif_lcl_addr != INADDR_ANY) {
addr = &ipif->ipif_lcl_addr;
}
if ((ipif->ipif_flags & IPIF_UNNUMBERED) || addr == NULL) {
if (res_act != Res_act_initial)
return (EINVAL);
}
if (addr != NULL) {
ipmp_illgrp_t *illg = ill->ill_grp;
if (IS_IPMP(ill)) {
if ((bound_ill = ipmp_ipif_bound_ill(ipif)) == NULL) {
bound_ill = ipmp_illgrp_add_ipif(illg, ipif);
if (bound_ill == NULL) {
ipif->ipif_addr_ready = 1;
return (0);
}
added_ipif = B_TRUE;
}
} else {
bound_ill = ill;
}
flags = (NCE_F_MYADDR | NCE_F_PUBLISH | NCE_F_AUTHORITY |
NCE_F_NONUD);
if (res_act == Res_act_initial || !ipif->ipif_addr_ready) {
state = ND_UNCHANGED;
} else {
state = ND_REACHABLE;
flags |= NCE_F_UNSOL_ADV;
}
retry:
err = nce_lookup_then_add_v4(ill,
bound_ill->ill_phys_addr, bound_ill->ill_phys_addr_length,
addr, flags, state, &nce);
switch (err) {
case 0:
ipif->ipif_added_nce = 1;
nce->nce_ipif_cnt++;
break;
case EEXIST:
ip1dbg(("ipif_arp_up: NCE already exists for %s\n",
ill->ill_name));
if (!NCE_MYADDR(nce->nce_common)) {
ncec_delete(nce->nce_common);
nce_refrele(nce);
nce = NULL;
goto retry;
}
if ((ipif->ipif_flags & IPIF_POINTOPOINT) == 0) {
nce_refrele(nce);
nce = NULL;
ip1dbg(("ipif_arp_up: NCE already exists "
"for %s:%u\n", ill->ill_name,
ipif->ipif_id));
goto arp_up_done;
}
ipif->ipif_addr_ready = 1;
ipif->ipif_added_nce = 1;
nce->nce_ipif_cnt++;
err = 0;
break;
default:
ASSERT(nce == NULL);
goto arp_up_done;
}
if (arp_no_defense) {
if ((ipif->ipif_flags & IPIF_UP) &&
!ipif->ipif_addr_ready)
ipif_up_notify(ipif);
ipif->ipif_addr_ready = 1;
}
} else {
ipif->ipif_addr_ready = 1;
}
if (nce != NULL)
nce_refrele(nce);
arp_up_done:
if (added_ipif && err != 0)
ipmp_illgrp_del_ipif(ill->ill_grp, ipif);
return (err);
}
int
ipif_arp_up(ipif_t *ipif, enum ip_resolver_action res_act, boolean_t was_dup)
{
int err = 0;
ill_t *ill = ipif->ipif_ill;
boolean_t first_interface, wait_for_dlpi = B_FALSE;
DTRACE_PROBE3(ipif__downup, char *, "ipif_arp_up",
ill_t *, ill, ipif_t *, ipif);
first_interface = (ill->ill_ipif_up_count == 0 &&
ill->ill_ipif_dup_count == 0 && !was_dup);
if (res_act == Res_act_initial && first_interface) {
err = arp_ll_up(ill);
if (err != EINPROGRESS && err != 0)
return (err);
if (err == EINPROGRESS)
wait_for_dlpi = B_TRUE;
}
if (!wait_for_dlpi)
(void) ipif_arp_up_done_tail(ipif, res_act);
return (!wait_for_dlpi ? 0 : EINPROGRESS);
}
void
arp_bringup_done(ill_t *ill, int err)
{
mblk_t *mp1;
ipif_t *ipif;
conn_t *connp = NULL;
ipsq_t *ipsq;
queue_t *q;
ip1dbg(("arp_bringup_done(%s)\n", ill->ill_name));
ASSERT(IAM_WRITER_ILL(ill));
ipsq = ill->ill_phyint->phyint_ipsq;
ipif = ipsq->ipsq_xop->ipx_pending_ipif;
mp1 = ipsq_pending_mp_get(ipsq, &connp);
ASSERT(!((mp1 != NULL) ^ (ipif != NULL)));
if (mp1 == NULL)
return;
if (ipsq->ipsq_xop->ipx_current_ioctl != 0) {
ASSERT(connp != NULL);
q = CONNP_TO_WQ(connp);
} else {
ASSERT(connp == NULL);
q = ill->ill_rq;
}
if (err == 0) {
if (ipif->ipif_isv6) {
if ((err = ipif_up_done_v6(ipif)) != 0)
ip0dbg(("arp_bringup_done: init failed\n"));
} else {
err = ipif_arp_up_done_tail(ipif, Res_act_initial);
if (err != 0 ||
(err = ipif_up_done(ipif)) != 0) {
ip0dbg(("arp_bringup_done: "
"init failed err %x\n", err));
(void) ipif_arp_down(ipif);
}
}
} else {
ip0dbg(("arp_bringup_done: DL_BIND_REQ failed\n"));
}
if ((err == 0) && (ill->ill_up_ipifs)) {
err = ill_up_ipifs(ill, q, mp1);
if (err == EINPROGRESS)
return;
}
if (ill->ill_move_ipif != NULL) {
ipif = ill->ill_move_ipif;
ip1dbg(("bringing up ipif %p on ill %s\n", (void *)ipif,
ipif->ipif_ill->ill_name));
ill->ill_move_ipif = NULL;
if (err == 0) {
err = ipif_up(ipif, q, mp1);
if (err == EINPROGRESS)
return;
}
}
ASSERT(err != EINPROGRESS);
if (ipsq->ipsq_xop->ipx_current_ioctl != 0) {
DTRACE_PROBE4(ipif__ioctl, char *, "arp_bringup_done finish",
int, ipsq->ipsq_xop->ipx_current_ioctl,
ill_t *, ill, ipif_t *, ipif);
ip_ioctl_finish(q, mp1, err, NO_COPYOUT, ipsq);
} else {
ipsq_current_finish(ipsq);
}
}
void
arp_replumb_done(ill_t *ill, int err)
{
mblk_t *mp1;
ipif_t *ipif;
conn_t *connp = NULL;
ipsq_t *ipsq;
queue_t *q;
ASSERT(IAM_WRITER_ILL(ill));
ipsq = ill->ill_phyint->phyint_ipsq;
ipif = ipsq->ipsq_xop->ipx_pending_ipif;
mp1 = ipsq_pending_mp_get(ipsq, &connp);
ASSERT(!((mp1 != NULL) ^ (ipif != NULL)));
if (mp1 == NULL) {
ip0dbg(("arp_replumb_done: bringup aborted ioctl %x\n",
ipsq->ipsq_xop->ipx_current_ioctl));
return;
}
if (ipsq->ipsq_xop->ipx_current_ioctl != 0) {
ASSERT(connp != NULL);
q = CONNP_TO_WQ(connp);
} else {
ASSERT(connp == NULL);
q = ill->ill_rq;
}
if ((err == 0) && (ill->ill_up_ipifs)) {
err = ill_up_ipifs(ill, q, mp1);
if (err == EINPROGRESS)
return;
}
ASSERT(err != EINPROGRESS);
if (ipsq->ipsq_xop->ipx_current_ioctl != 0) {
DTRACE_PROBE4(ipif__ioctl, char *,
"arp_replumb_done finish",
int, ipsq->ipsq_xop->ipx_current_ioctl,
ill_t *, ill, ipif_t *, ipif);
ip_ioctl_finish(q, mp1, err, NO_COPYOUT, ipsq);
} else {
ipsq_current_finish(ipsq);
}
}
void
ipif_up_notify(ipif_t *ipif)
{
ip_rts_ifmsg(ipif, RTSQ_DEFAULT);
ip_rts_newaddrmsg(RTM_ADD, 0, ipif, RTSQ_DEFAULT);
sctp_update_ipif(ipif, SCTP_IPIF_UP);
ill_nic_event_dispatch(ipif->ipif_ill, MAP_IPIF_ID(ipif->ipif_id),
NE_LIF_UP, NULL, 0);
}
int
ip_sioctl_ilb_cmd(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *arg)
{
mblk_t *cmd_mp = mp->b_cont->b_cont;
ilb_cmd_t command = *((ilb_cmd_t *)cmd_mp->b_rptr);
int ret = 0;
int i;
size_t size;
ip_stack_t *ipst;
zoneid_t zoneid;
ilb_stack_t *ilbs;
ipst = CONNQ_TO_IPST(q);
ilbs = ipst->ips_netstack->netstack_ilb;
zoneid = Q_TO_CONN(q)->conn_zoneid;
switch (command) {
case ILB_CREATE_RULE: {
ilb_rule_cmd_t *cmd = (ilb_rule_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) != sizeof (ilb_rule_cmd_t)) {
ret = EINVAL;
break;
}
ret = ilb_rule_add(ilbs, zoneid, cmd);
break;
}
case ILB_DESTROY_RULE:
case ILB_ENABLE_RULE:
case ILB_DISABLE_RULE: {
ilb_name_cmd_t *cmd = (ilb_name_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) != sizeof (ilb_name_cmd_t)) {
ret = EINVAL;
break;
}
if (cmd->flags & ILB_RULE_ALLRULES) {
if (command == ILB_DESTROY_RULE) {
ilb_rule_del_all(ilbs, zoneid);
break;
} else if (command == ILB_ENABLE_RULE) {
ilb_rule_enable_all(ilbs, zoneid);
break;
} else if (command == ILB_DISABLE_RULE) {
ilb_rule_disable_all(ilbs, zoneid);
break;
}
} else {
if (command == ILB_DESTROY_RULE) {
ret = ilb_rule_del(ilbs, zoneid, cmd->name);
} else if (command == ILB_ENABLE_RULE) {
ret = ilb_rule_enable(ilbs, zoneid, cmd->name,
NULL);
} else if (command == ILB_DISABLE_RULE) {
ret = ilb_rule_disable(ilbs, zoneid, cmd->name,
NULL);
}
}
break;
}
case ILB_NUM_RULES: {
ilb_num_rules_cmd_t *cmd;
if (MBLKL(cmd_mp) != sizeof (ilb_num_rules_cmd_t)) {
ret = EINVAL;
break;
}
cmd = (ilb_num_rules_cmd_t *)cmd_mp->b_rptr;
ilb_get_num_rules(ilbs, zoneid, &(cmd->num));
break;
}
case ILB_RULE_NAMES: {
ilb_rule_names_cmd_t *cmd;
cmd = (ilb_rule_names_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) < sizeof (ilb_rule_names_cmd_t) ||
cmd->num_names == 0) {
ret = EINVAL;
break;
}
size = cmd->num_names * ILB_RULE_NAMESZ;
if (cmd_mp->b_rptr + offsetof(ilb_rule_names_cmd_t, buf) +
size != cmd_mp->b_wptr) {
ret = EINVAL;
break;
}
ilb_get_rulenames(ilbs, zoneid, &cmd->num_names, cmd->buf);
break;
}
case ILB_NUM_SERVERS: {
ilb_num_servers_cmd_t *cmd;
if (MBLKL(cmd_mp) != sizeof (ilb_num_servers_cmd_t)) {
ret = EINVAL;
break;
}
cmd = (ilb_num_servers_cmd_t *)cmd_mp->b_rptr;
ret = ilb_get_num_servers(ilbs, zoneid, cmd->name,
&(cmd->num));
break;
}
case ILB_LIST_RULE: {
ilb_rule_cmd_t *cmd = (ilb_rule_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) != sizeof (ilb_rule_cmd_t)) {
ret = EINVAL;
break;
}
ret = ilb_rule_list(ilbs, zoneid, cmd);
break;
}
case ILB_LIST_SERVERS: {
ilb_servers_info_cmd_t *cmd;
cmd = (ilb_servers_info_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) < sizeof (ilb_servers_info_cmd_t) ||
cmd->num_servers == 0) {
ret = EINVAL;
break;
}
size = cmd->num_servers * sizeof (ilb_server_info_t);
if (cmd_mp->b_rptr + offsetof(ilb_servers_info_cmd_t, servers) +
size != cmd_mp->b_wptr) {
ret = EINVAL;
break;
}
ret = ilb_get_servers(ilbs, zoneid, cmd->name, cmd->servers,
&cmd->num_servers);
break;
}
case ILB_ADD_SERVERS: {
ilb_servers_info_cmd_t *cmd;
ilb_rule_t *rule;
cmd = (ilb_servers_info_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) < sizeof (ilb_servers_info_cmd_t)) {
ret = EINVAL;
break;
}
size = cmd->num_servers * sizeof (ilb_server_info_t);
if (cmd_mp->b_rptr + offsetof(ilb_servers_info_cmd_t, servers) +
size != cmd_mp->b_wptr) {
ret = EINVAL;
break;
}
rule = ilb_find_rule(ilbs, zoneid, cmd->name, &ret);
if (rule == NULL) {
ASSERT(ret != 0);
break;
}
for (i = 0; i < cmd->num_servers; i++) {
ilb_server_info_t *s;
s = &cmd->servers[i];
s->err = ilb_server_add(ilbs, rule, s);
}
ILB_RULE_REFRELE(rule);
break;
}
case ILB_DEL_SERVERS:
case ILB_ENABLE_SERVERS:
case ILB_DISABLE_SERVERS: {
ilb_servers_cmd_t *cmd;
ilb_rule_t *rule;
int (*f)();
cmd = (ilb_servers_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) < sizeof (ilb_servers_cmd_t)) {
ret = EINVAL;
break;
}
size = cmd->num_servers * sizeof (ilb_server_arg_t);
if (cmd_mp->b_rptr + offsetof(ilb_servers_cmd_t, servers) +
size != cmd_mp->b_wptr) {
ret = EINVAL;
break;
}
if (command == ILB_DEL_SERVERS)
f = ilb_server_del;
else if (command == ILB_ENABLE_SERVERS)
f = ilb_server_enable;
else if (command == ILB_DISABLE_SERVERS)
f = ilb_server_disable;
rule = ilb_find_rule(ilbs, zoneid, cmd->name, &ret);
if (rule == NULL) {
ASSERT(ret != 0);
break;
}
for (i = 0; i < cmd->num_servers; i++) {
ilb_server_arg_t *s;
s = &cmd->servers[i];
s->err = f(ilbs, zoneid, NULL, rule, &s->addr);
}
ILB_RULE_REFRELE(rule);
break;
}
case ILB_LIST_NAT_TABLE: {
ilb_list_nat_cmd_t *cmd;
cmd = (ilb_list_nat_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) < sizeof (ilb_list_nat_cmd_t)) {
ret = EINVAL;
break;
}
size = cmd->num_nat * sizeof (ilb_nat_entry_t);
if (cmd_mp->b_rptr + offsetof(ilb_list_nat_cmd_t, entries) +
size != cmd_mp->b_wptr) {
ret = EINVAL;
break;
}
ret = ilb_list_nat(ilbs, zoneid, cmd->entries, &cmd->num_nat,
&cmd->flags);
break;
}
case ILB_LIST_STICKY_TABLE: {
ilb_list_sticky_cmd_t *cmd;
cmd = (ilb_list_sticky_cmd_t *)cmd_mp->b_rptr;
if (MBLKL(cmd_mp) < sizeof (ilb_list_sticky_cmd_t)) {
ret = EINVAL;
break;
}
size = cmd->num_sticky * sizeof (ilb_sticky_entry_t);
if (cmd_mp->b_rptr + offsetof(ilb_list_sticky_cmd_t, entries) +
size != cmd_mp->b_wptr) {
ret = EINVAL;
break;
}
ret = ilb_list_sticky(ilbs, zoneid, cmd->entries,
&cmd->num_sticky, &cmd->flags);
break;
}
default:
ret = EINVAL;
break;
}
return (ret);
}
void
ipif_nce_down(ipif_t *ipif)
{
ill_t *ill = ipif->ipif_ill;
nce_t *nce;
DTRACE_PROBE3(ipif__downup, char *, "ipif_nce_down",
ill_t *, ill, ipif_t *, ipif);
if (ipif->ipif_added_nce) {
if (ipif->ipif_isv6)
nce = nce_lookup_v6(ill, &ipif->ipif_v6lcl_addr);
else
nce = nce_lookup_v4(ill, &ipif->ipif_lcl_addr);
if (nce != NULL) {
if (--nce->nce_ipif_cnt == 0)
ncec_delete(nce->nce_common);
ipif->ipif_added_nce = 0;
nce_refrele(nce);
} else {
ipif->ipif_added_nce = 0;
}
}
if (IS_IPMP(ill))
ipmp_illgrp_del_ipif(ill->ill_grp, ipif);
if (ill->ill_ipif_up_count == 0) {
ncec_walk(ill, ncec_delete_per_ill, ill, ill->ill_ipst);
if (IS_UNDER_IPMP(ill))
nce_flush(ill, B_TRUE);
}
}
ill_t *
ill_lookup_usesrc(ill_t *usill)
{
ip_stack_t *ipst = usill->ill_ipst;
ill_t *ill;
ASSERT(usill != NULL);
rw_enter(&ipst->ips_ill_g_usesrc_lock, RW_WRITER);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
for (ill = usill->ill_usesrc_grp_next; ill != NULL && ill != usill;
ill = ill->ill_usesrc_grp_next) {
if (!IS_UNDER_IPMP(ill) && (ill->ill_flags & ILLF_MULTICAST) &&
!ILL_IS_CONDEMNED(ill)) {
ill_refhold(ill);
break;
}
}
rw_exit(&ipst->ips_ill_g_lock);
rw_exit(&ipst->ips_ill_g_usesrc_lock);
return (ill);
}
int
ip_sioctl_get_ifhwaddr(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct sockaddr *sock;
struct ifreq *ifr;
mblk_t *mp1;
ill_t *ill;
ASSERT(ipif != NULL);
ill = ipif->ipif_ill;
if (ill->ill_phys_addr == NULL) {
return (EADDRNOTAVAIL);
}
if (ill->ill_phys_addr_length > sizeof (sock->sa_data)) {
return (EPFNOSUPPORT);
}
ip1dbg(("ip_sioctl_get_hwaddr(%s)\n", ill->ill_name));
mp1 = mp->b_cont->b_cont;
ifr = (struct ifreq *)mp1->b_rptr;
sock = &ifr->ifr_addr;
sock->sa_family = arp_hw_type(ill->ill_mactype);
bcopy(ill->ill_phys_addr, &sock->sa_data, ill->ill_phys_addr_length);
return (0);
}
int
ip_sioctl_get_lifhwaddr(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
ip_ioctl_cmd_t *ipip, void *if_req)
{
struct sockaddr_dl *sock;
struct lifreq *lifr;
mblk_t *mp1;
ill_t *ill;
ASSERT(ipif != NULL);
ill = ipif->ipif_ill;
if (ill->ill_phys_addr == NULL) {
return (EADDRNOTAVAIL);
}
if (ill->ill_phys_addr_length > sizeof (sock->sdl_data)) {
return (EPFNOSUPPORT);
}
ip1dbg(("ip_sioctl_get_lifhwaddr(%s)\n", ill->ill_name));
mp1 = mp->b_cont->b_cont;
lifr = (struct lifreq *)mp1->b_rptr;
lifr->lifr_type = ill->ill_type;
sock = (struct sockaddr_dl *)&lifr->lifr_addr;
sock->sdl_family = AF_LINK;
sock->sdl_index = ill->ill_phyint->phyint_ifindex;
sock->sdl_type = ill->ill_mactype;
sock->sdl_nlen = 0;
sock->sdl_slen = 0;
sock->sdl_alen = ill->ill_phys_addr_length;
bcopy(ill->ill_phys_addr, sock->sdl_data, ill->ill_phys_addr_length);
return (0);
}