#include <sys/types.h>
#include <sys/stream.h>
#include <sys/dlpi.h>
#include <sys/stropts.h>
#include <sys/sysmacros.h>
#include <sys/strsubr.h>
#include <sys/strlog.h>
#include <sys/strsun.h>
#include <sys/zone.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/xti_inet.h>
#include <sys/ddi.h>
#include <sys/suntpi.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/kobj.h>
#include <sys/modctl.h>
#include <sys/atomic.h>
#include <sys/policy.h>
#include <sys/priv.h>
#include <sys/taskq.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/sdt.h>
#include <sys/socket.h>
#include <sys/vtrace.h>
#include <sys/isa_defs.h>
#include <sys/mac.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/route.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/mib2.h>
#include <inet/nd.h>
#include <inet/arp.h>
#include <inet/snmpcom.h>
#include <inet/optcom.h>
#include <inet/kstatcom.h>
#include <netinet/igmp_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/sctp.h>
#include <inet/ip.h>
#include <inet/ip_impl.h>
#include <inet/ip6.h>
#include <inet/ip6_asp.h>
#include <inet/tcp.h>
#include <inet/tcp_impl.h>
#include <inet/ip_multi.h>
#include <inet/ip_if.h>
#include <inet/ip_ire.h>
#include <inet/ip_ftable.h>
#include <inet/ip_rts.h>
#include <inet/ip_ndp.h>
#include <inet/ip_listutils.h>
#include <netinet/igmp.h>
#include <netinet/ip_mroute.h>
#include <inet/ipp_common.h>
#include <inet/cc.h>
#include <net/pfkeyv2.h>
#include <inet/sadb.h>
#include <inet/ipsec_impl.h>
#include <inet/iptun/iptun_impl.h>
#include <inet/ipdrop.h>
#include <inet/ip_netinfo.h>
#include <inet/ilb_ip.h>
#include <sys/ethernet.h>
#include <net/if_types.h>
#include <sys/cpuvar.h>
#include <ipp/ipp.h>
#include <ipp/ipp_impl.h>
#include <ipp/ipgpc/ipgpc.h>
#include <sys/pattr.h>
#include <inet/ipclassifier.h>
#include <inet/sctp_ip.h>
#include <inet/sctp/sctp_impl.h>
#include <inet/udp_impl.h>
#include <inet/rawip_impl.h>
#include <inet/rts_impl.h>
#include <sys/tsol/label.h>
#include <sys/tsol/tnet.h>
#include <sys/squeue_impl.h>
#include <inet/ip_arp.h>
#include <sys/clock_impl.h>
int ip_squeue_enter = IP_SQUEUE_ENTER;
int ip_squeue_flag;
int ip_poll_normal_ms = 100;
int ip_poll_normal_ticks = 0;
int ip_modclose_ackwait_ms = 3000;
uint_t ip_thread_data;
krwlock_t ip_thread_rwlock;
list_t ip_thread_list;
struct listptr_s {
mblk_t *lp_head;
mblk_t *lp_tail;
};
typedef struct listptr_s listptr_t;
typedef struct iproutedata_s {
uint_t ird_idx;
uint_t ird_flags;
listptr_t ird_route;
listptr_t ird_netmedia;
listptr_t ird_attrs;
} iproutedata_t;
#define IRD_REPORT_ALL 0x01
int (*cl_inet_isclusterwide)(netstackid_t stack_id, uint8_t protocol,
sa_family_t addr_family, uint8_t *laddrp, void *args) = NULL;
uint32_t (*cl_inet_ipident)(netstackid_t stack_id, uint8_t protocol,
sa_family_t addr_family, uint8_t *laddrp, uint8_t *faddrp,
void *args) = NULL;
void (*cl_inet_getspi)(netstackid_t, uint8_t, uint8_t *, size_t,
void *) = NULL;
int (*cl_inet_checkspi)(netstackid_t, uint8_t, uint32_t, void *) = NULL;
void (*cl_inet_deletespi)(netstackid_t, uint8_t, uint32_t, void *) = NULL;
void (*cl_inet_idlesa)(netstackid_t, uint8_t, uint32_t, sa_family_t,
in6_addr_t, in6_addr_t, void *) = NULL;
boolean_t ip_squeue_fanout = 0;
uint_t ip_max_frag_dups = 10;
static int ip_open(queue_t *q, dev_t *devp, int flag, int sflag,
cred_t *credp, boolean_t isv6);
static mblk_t *ip_xmit_attach_llhdr(mblk_t *, nce_t *);
static boolean_t icmp_inbound_verify_v4(mblk_t *, icmph_t *, ip_recv_attr_t *);
static void icmp_inbound_too_big_v4(icmph_t *, ip_recv_attr_t *);
static void icmp_inbound_error_fanout_v4(mblk_t *, icmph_t *,
ip_recv_attr_t *);
static void icmp_options_update(ipha_t *);
static void icmp_param_problem(mblk_t *, uint8_t, ip_recv_attr_t *);
static void icmp_pkt(mblk_t *, void *, size_t, ip_recv_attr_t *);
static mblk_t *icmp_pkt_err_ok(mblk_t *, ip_recv_attr_t *);
static void icmp_redirect_v4(mblk_t *mp, ipha_t *, icmph_t *,
ip_recv_attr_t *);
static void icmp_send_redirect(mblk_t *, ipaddr_t, ip_recv_attr_t *);
static void icmp_send_reply_v4(mblk_t *, ipha_t *, icmph_t *,
ip_recv_attr_t *);
mblk_t *ip_dlpi_alloc(size_t, t_uscalar_t);
char *ip_dot_addr(ipaddr_t, char *);
mblk_t *ip_carve_mp(mblk_t **, ssize_t);
static char *ip_dot_saddr(uchar_t *, char *);
static int ip_lrput(queue_t *, mblk_t *);
ipaddr_t ip_net_mask(ipaddr_t);
char *ip_nv_lookup(nv_t *, int);
int ip_rput(queue_t *, mblk_t *);
static void ip_rput_dlpi_writer(ipsq_t *dummy_sq, queue_t *q, mblk_t *mp,
void *dummy_arg);
int ip_snmp_get(queue_t *, mblk_t *, int, boolean_t);
static mblk_t *ip_snmp_get_mib2_ip(queue_t *, mblk_t *,
mib2_ipIfStatsEntry_t *, ip_stack_t *, boolean_t);
static mblk_t *ip_snmp_get_mib2_ip_traffic_stats(queue_t *, mblk_t *,
ip_stack_t *, boolean_t);
static mblk_t *ip_snmp_get_mib2_ip6(queue_t *, mblk_t *, ip_stack_t *,
boolean_t);
static mblk_t *ip_snmp_get_mib2_icmp(queue_t *, mblk_t *, ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_icmp6(queue_t *, mblk_t *, ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_igmp(queue_t *, mblk_t *, ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_multi(queue_t *, mblk_t *, ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_ip_addr(queue_t *, mblk_t *,
ip_stack_t *ipst, boolean_t);
static mblk_t *ip_snmp_get_mib2_ip6_addr(queue_t *, mblk_t *,
ip_stack_t *ipst, boolean_t);
static mblk_t *ip_snmp_get_mib2_ip_group_src(queue_t *, mblk_t *,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_ip6_group_src(queue_t *, mblk_t *,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_ip_group_mem(queue_t *, mblk_t *,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_ip6_group_mem(queue_t *, mblk_t *,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_virt_multi(queue_t *, mblk_t *,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_multi_rtable(queue_t *, mblk_t *,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_ip_route_media(queue_t *, mblk_t *, int,
ip_stack_t *ipst);
static mblk_t *ip_snmp_get_mib2_ip6_route_media(queue_t *, mblk_t *, int,
ip_stack_t *ipst);
static void ip_snmp_get2_v4(ire_t *, iproutedata_t *);
static void ip_snmp_get2_v6_route(ire_t *, iproutedata_t *);
static void ip_snmp_get2_v4_media(ncec_t *, void *);
static void ip_snmp_get2_v6_media(ncec_t *, void *);
int ip_snmp_set(queue_t *, int, int, uchar_t *, int);
static mblk_t *ip_fragment_copyhdr(uchar_t *, int, int, ip_stack_t *,
mblk_t *);
static void conn_drain_init(ip_stack_t *);
static void conn_drain_fini(ip_stack_t *);
static void conn_drain(conn_t *connp, boolean_t closing);
static void conn_walk_drain(ip_stack_t *, idl_tx_list_t *);
static void conn_walk_sctp(pfv_t, void *, zoneid_t, netstack_t *);
static void *ip_stack_init(netstackid_t stackid, netstack_t *ns);
static void ip_stack_shutdown(netstackid_t stackid, void *arg);
static void ip_stack_fini(netstackid_t stackid, void *arg);
static int ip_multirt_apply_membership(int (*fn)(conn_t *, boolean_t,
const in6_addr_t *, ipaddr_t, uint_t, mcast_record_t, const in6_addr_t *),
ire_t *, conn_t *, boolean_t, const in6_addr_t *, mcast_record_t,
const in6_addr_t *);
static int ip_squeue_switch(int);
static void *ip_kstat_init(netstackid_t, ip_stack_t *);
static void ip_kstat_fini(netstackid_t, kstat_t *);
static int ip_kstat_update(kstat_t *kp, int rw);
static void *icmp_kstat_init(netstackid_t);
static void icmp_kstat_fini(netstackid_t, kstat_t *);
static int icmp_kstat_update(kstat_t *kp, int rw);
static void *ip_kstat2_init(netstackid_t, ip_stat_t *);
static void ip_kstat2_fini(netstackid_t, kstat_t *);
static void ipobs_init(ip_stack_t *);
static void ipobs_fini(ip_stack_t *);
static int ip_tp_cpu_update(cpu_setup_t, int, void *);
ipaddr_t ip_g_all_ones = IP_HOST_MASK;
static long ip_rput_pullups;
int dohwcksum = 1;
vmem_t *ip_minor_arena_sa;
vmem_t *ip_minor_arena_la;
int ip_debug;
int ip_cgtp_filter_rev = CGTP_FILTER_REV;
extern mod_prop_info_t ip_propinfo_tbl[];
extern int ip_propinfo_count;
ip_ioctl_cmd_t ip_ndx_ioctl_table[] = {
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCADDRT, sizeof (struct rtentry), IPI_PRIV,
MISC_CMD, ip_siocaddrt, NULL },
{ SIOCDELRT, sizeof (struct rtentry), IPI_PRIV,
MISC_CMD, ip_siocdelrt, NULL },
{ SIOCSIFADDR, sizeof (struct ifreq), IPI_PRIV | IPI_WR,
IF_CMD, ip_sioctl_addr, ip_sioctl_addr_restart },
{ SIOCGIFADDR, sizeof (struct ifreq), IPI_GET_CMD,
IF_CMD, ip_sioctl_get_addr, NULL },
{ SIOCSIFDSTADDR, sizeof (struct ifreq), IPI_PRIV | IPI_WR,
IF_CMD, ip_sioctl_dstaddr, ip_sioctl_dstaddr_restart },
{ SIOCGIFDSTADDR, sizeof (struct ifreq),
IPI_GET_CMD, IF_CMD, ip_sioctl_get_dstaddr, NULL },
{ SIOCSIFFLAGS, sizeof (struct ifreq),
IPI_PRIV | IPI_WR,
IF_CMD, ip_sioctl_flags, ip_sioctl_flags_restart },
{ SIOCGIFFLAGS, sizeof (struct ifreq),
IPI_MODOK | IPI_GET_CMD,
IF_CMD, ip_sioctl_get_flags, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ O_SIOCGIFCONF, 0, IPI_GET_CMD,
MISC_CMD, ip_sioctl_get_ifconf, NULL },
{ SIOCSIFMTU, sizeof (struct ifreq), IPI_PRIV | IPI_WR,
IF_CMD, ip_sioctl_mtu, NULL },
{ SIOCGIFMTU, sizeof (struct ifreq), IPI_GET_CMD,
IF_CMD, ip_sioctl_get_mtu, NULL },
{ SIOCGIFBRDADDR, sizeof (struct ifreq),
IPI_GET_CMD, IF_CMD, ip_sioctl_get_brdaddr, NULL },
{ SIOCSIFBRDADDR, sizeof (struct ifreq), IPI_PRIV | IPI_WR,
IF_CMD, ip_sioctl_brdaddr, NULL },
{ SIOCGIFNETMASK, sizeof (struct ifreq),
IPI_GET_CMD, IF_CMD, ip_sioctl_get_netmask, NULL },
{ SIOCSIFNETMASK, sizeof (struct ifreq), IPI_PRIV | IPI_WR,
IF_CMD, ip_sioctl_netmask, ip_sioctl_netmask_restart },
{ SIOCGIFMETRIC, sizeof (struct ifreq),
IPI_GET_CMD, IF_CMD, ip_sioctl_get_metric, NULL },
{ SIOCSIFMETRIC, sizeof (struct ifreq), IPI_PRIV,
IF_CMD, ip_sioctl_metric, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCSARP, sizeof (struct arpreq), IPI_PRIV | IPI_WR,
ARP_CMD, ip_sioctl_arp, NULL },
{ SIOCGARP, sizeof (struct arpreq), IPI_GET_CMD,
ARP_CMD, ip_sioctl_arp, NULL },
{ SIOCDARP, sizeof (struct arpreq), IPI_PRIV | IPI_WR,
ARP_CMD, ip_sioctl_arp, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IF_UNITSEL, sizeof (int), IPI_PRIV | IPI_WR | IPI_MODOK,
MISC_CMD, if_unitsel, if_unitsel_restart },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCSIFNAME, sizeof (struct ifreq),
IPI_PRIV | IPI_WR | IPI_MODOK,
IF_CMD, ip_sioctl_sifname, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCGIFNUM, sizeof (int), IPI_GET_CMD,
MISC_CMD, ip_sioctl_get_ifnum, NULL },
{ SIOCGIFMUXID, sizeof (struct ifreq), IPI_GET_CMD,
IF_CMD, ip_sioctl_get_muxid, NULL },
{ SIOCSIFMUXID, sizeof (struct ifreq),
IPI_PRIV | IPI_WR, IF_CMD, ip_sioctl_muxid, NULL },
{ SIOCGIFINDEX, sizeof (struct ifreq), IPI_GET_CMD,
IF_CMD, ip_sioctl_get_lifindex, NULL },
{ SIOCSIFINDEX, sizeof (struct ifreq),
IPI_PRIV | IPI_WR, IF_CMD, ip_sioctl_slifindex, NULL },
{ SIOCGIFCONF, 0, IPI_GET_CMD,
MISC_CMD, ip_sioctl_get_ifconf, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCLIFREMOVEIF, sizeof (struct lifreq),
IPI_PRIV | IPI_WR, LIF_CMD, ip_sioctl_removeif,
ip_sioctl_removeif_restart },
{ SIOCLIFADDIF, sizeof (struct lifreq),
IPI_GET_CMD | IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_addif, NULL },
#define SIOCLIFADDR_NDX 112
{ SIOCSLIFADDR, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_addr, ip_sioctl_addr_restart },
{ SIOCGLIFADDR, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_addr, NULL },
{ SIOCSLIFDSTADDR, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_dstaddr, ip_sioctl_dstaddr_restart },
{ SIOCGLIFDSTADDR, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_dstaddr, NULL },
{ SIOCSLIFFLAGS, sizeof (struct lifreq),
IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_flags, ip_sioctl_flags_restart },
{ SIOCGLIFFLAGS, sizeof (struct lifreq),
IPI_GET_CMD | IPI_MODOK,
LIF_CMD, ip_sioctl_get_flags, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ O_SIOCGLIFCONF, 0, IPI_GET_CMD, MISC_CMD,
ip_sioctl_get_lifconf, NULL },
{ SIOCSLIFMTU, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_mtu, NULL },
{ SIOCGLIFMTU, sizeof (struct lifreq), IPI_GET_CMD,
LIF_CMD, ip_sioctl_get_mtu, NULL },
{ SIOCGLIFBRDADDR, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_brdaddr, NULL },
{ SIOCSLIFBRDADDR, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_brdaddr, NULL },
{ SIOCGLIFNETMASK, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_netmask, NULL },
{ SIOCSLIFNETMASK, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_netmask, ip_sioctl_netmask_restart },
{ SIOCGLIFMETRIC, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_metric, NULL },
{ SIOCSLIFMETRIC, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_metric, NULL },
{ SIOCSLIFNAME, sizeof (struct lifreq),
IPI_PRIV | IPI_WR | IPI_MODOK,
LIF_CMD, ip_sioctl_slifname,
ip_sioctl_slifname_restart },
{ SIOCGLIFNUM, sizeof (struct lifnum), IPI_GET_CMD,
MISC_CMD, ip_sioctl_get_lifnum, NULL },
{ SIOCGLIFMUXID, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_muxid, NULL },
{ SIOCSLIFMUXID, sizeof (struct lifreq),
IPI_PRIV | IPI_WR, LIF_CMD, ip_sioctl_muxid, NULL },
{ SIOCGLIFINDEX, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_lifindex, 0 },
{ SIOCSLIFINDEX, sizeof (struct lifreq),
IPI_PRIV | IPI_WR, LIF_CMD, ip_sioctl_slifindex, 0 },
{ SIOCSLIFTOKEN, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_token, NULL },
{ SIOCGLIFTOKEN, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_token, NULL },
{ SIOCSLIFSUBNET, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_subnet, ip_sioctl_subnet_restart },
{ SIOCGLIFSUBNET, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_subnet, NULL },
{ SIOCSLIFLNKINFO, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_lnkinfo, NULL },
{ SIOCGLIFLNKINFO, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_lnkinfo, NULL },
{ SIOCLIFDELND, sizeof (struct lifreq), IPI_PRIV,
LIF_CMD, ip_siocdelndp_v6, NULL },
{ SIOCLIFGETND, sizeof (struct lifreq), IPI_GET_CMD,
LIF_CMD, ip_siocqueryndp_v6, NULL },
{ SIOCLIFSETND, sizeof (struct lifreq), IPI_PRIV,
LIF_CMD, ip_siocsetndp_v6, NULL },
{ SIOCTMYADDR, sizeof (struct sioc_addrreq), IPI_GET_CMD,
MISC_CMD, ip_sioctl_tmyaddr, NULL },
{ SIOCTONLINK, sizeof (struct sioc_addrreq), IPI_GET_CMD,
MISC_CMD, ip_sioctl_tonlink, NULL },
{ SIOCTMYSITE, sizeof (struct sioc_addrreq), 0,
MISC_CMD, ip_sioctl_tmysite, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCGLIFBINDING, sizeof (struct lifreq), IPI_GET_CMD,
LIF_CMD, ip_sioctl_get_binding, NULL },
{ SIOCSLIFGROUPNAME, sizeof (struct lifreq),
IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_groupname, ip_sioctl_groupname },
{ SIOCGLIFGROUPNAME, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_groupname, NULL },
{ SIOCGLIFGROUPINFO, sizeof (lifgroupinfo_t),
IPI_GET_CMD, MISC_CMD, ip_sioctl_groupinfo, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCGIP6ADDRPOLICY, 0, IPI_NULL_BCONT,
MISC_CMD, NULL, NULL },
{ SIOCSIP6ADDRPOLICY, 0, IPI_PRIV | IPI_NULL_BCONT,
MISC_CMD, NULL, NULL },
{ SIOCGDSTINFO, 0, IPI_GET_CMD, MISC_CMD, NULL, NULL },
{ SIOCGLIFCONF, 0, IPI_GET_CMD, MISC_CMD,
ip_sioctl_get_lifconf, NULL },
{ SIOCSXARP, sizeof (struct xarpreq), IPI_PRIV | IPI_WR,
XARP_CMD, ip_sioctl_arp, NULL },
{ SIOCGXARP, sizeof (struct xarpreq), IPI_GET_CMD,
XARP_CMD, ip_sioctl_arp, NULL },
{ SIOCDXARP, sizeof (struct xarpreq), IPI_PRIV | IPI_WR,
XARP_CMD, ip_sioctl_arp, NULL },
{ IPI_DONTCARE , 0, 0, 0, NULL, NULL },
{ SIOCGLIFZONE, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_lifzone, NULL },
{ SIOCSLIFZONE, sizeof (struct lifreq),
IPI_PRIV | IPI_WR, LIF_CMD, ip_sioctl_slifzone,
ip_sioctl_slifzone_restart },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ SIOCGLIFUSESRC, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD,
ip_sioctl_get_lifusesrc, 0 },
{ SIOCSLIFUSESRC, sizeof (struct lifreq),
IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_slifusesrc,
NULL },
{ SIOCGLIFSRCOF, 0, IPI_GET_CMD, MISC_CMD,
ip_sioctl_get_lifsrcof, NULL },
{ SIOCGMSFILTER, sizeof (struct group_filter), IPI_GET_CMD,
MSFILT_CMD, ip_sioctl_msfilter, NULL },
{ SIOCSMSFILTER, sizeof (struct group_filter), 0,
MSFILT_CMD, ip_sioctl_msfilter, NULL },
{ SIOCGIPMSFILTER, sizeof (struct ip_msfilter), IPI_GET_CMD,
MSFILT_CMD, ip_sioctl_msfilter, NULL },
{ SIOCSIPMSFILTER, sizeof (struct ip_msfilter), 0,
MSFILT_CMD, ip_sioctl_msfilter, NULL },
{ IPI_DONTCARE, 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE , 0, 0, 0, NULL, NULL },
{ IPI_DONTCARE , 0, 0, 0, NULL, NULL },
{ SIOCGIFHWADDR, sizeof (struct ifreq), IPI_GET_CMD,
IF_CMD, ip_sioctl_get_ifhwaddr, NULL },
{ IPI_DONTCARE , 0, 0, 0, NULL, NULL },
{ SIOCILB, 0, IPI_PRIV | IPI_GET_CMD, MISC_CMD,
ip_sioctl_ilb_cmd, NULL },
{ SIOCGETPROP, 0, IPI_GET_CMD, 0, NULL, NULL },
{ SIOCSETPROP, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL},
{ SIOCGLIFDADSTATE, sizeof (struct lifreq),
IPI_GET_CMD, LIF_CMD, ip_sioctl_get_dadstate, NULL },
{ SIOCSLIFPREFIX, sizeof (struct lifreq), IPI_PRIV | IPI_WR,
LIF_CMD, ip_sioctl_prefix, ip_sioctl_prefix_restart },
{ SIOCGLIFHWADDR, sizeof (struct lifreq), IPI_GET_CMD,
LIF_CMD, ip_sioctl_get_lifhwaddr, NULL }
};
int ip_ndx_ioctl_count = sizeof (ip_ndx_ioctl_table) / sizeof (ip_ioctl_cmd_t);
ip_ioctl_cmd_t ip_misc_ioctl_table[] = {
{ I_LINK, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL },
{ I_UNLINK, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL },
{ I_PLINK, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL },
{ I_PUNLINK, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL },
{ ND_GET, 0, 0, 0, NULL, NULL },
{ ND_SET, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL },
{ IP_IOCTL, 0, 0, 0, NULL, NULL },
{ SIOCGETVIFCNT, sizeof (struct sioc_vif_req), IPI_GET_CMD,
MISC_CMD, mrt_ioctl},
{ SIOCGETSGCNT, sizeof (struct sioc_sg_req), IPI_GET_CMD,
MISC_CMD, mrt_ioctl},
{ SIOCGETLSGCNT, sizeof (struct sioc_lsg_req), IPI_GET_CMD,
MISC_CMD, mrt_ioctl}
};
int ip_misc_ioctl_count =
sizeof (ip_misc_ioctl_table) / sizeof (ip_ioctl_cmd_t);
int conn_drain_nthreads;
extern uint32_t ip_ire_max_bucket_cnt, ip6_ire_max_bucket_cnt;
extern uint32_t ip_ire_min_bucket_cnt, ip6_ire_min_bucket_cnt;
extern uint32_t ip_ire_mem_ratio, ip_ire_cpu_ratio;
static nv_t ire_nv_arr[] = {
{ IRE_BROADCAST, "BROADCAST" },
{ IRE_LOCAL, "LOCAL" },
{ IRE_LOOPBACK, "LOOPBACK" },
{ IRE_DEFAULT, "DEFAULT" },
{ IRE_PREFIX, "PREFIX" },
{ IRE_IF_NORESOLVER, "IF_NORESOL" },
{ IRE_IF_RESOLVER, "IF_RESOLV" },
{ IRE_IF_CLONE, "IF_CLONE" },
{ IRE_HOST, "HOST" },
{ IRE_MULTICAST, "MULTICAST" },
{ IRE_NOROUTE, "NOROUTE" },
{ 0 }
};
nv_t *ire_nv_tbl = ire_nv_arr;
static ipha_t icmp_ipha = {
IP_SIMPLE_HDR_VERSION, 0, 0, 0, 0, 0, IPPROTO_ICMP
};
struct module_info ip_mod_info = {
IP_MOD_ID, IP_MOD_NAME, IP_MOD_MINPSZ, IP_MOD_MAXPSZ, IP_MOD_HIWAT,
IP_MOD_LOWAT
};
static struct qinit iprinitv4 = {
ip_rput, NULL, ip_openv4, ip_close, NULL, &ip_mod_info
};
struct qinit iprinitv6 = {
ip_rput_v6, NULL, ip_openv6, ip_close, NULL, &ip_mod_info
};
static struct qinit ipwinit = {
ip_wput_nondata, ip_wsrv, NULL, NULL, NULL, &ip_mod_info
};
static struct qinit iplrinit = {
ip_lrput, NULL, ip_openv4, ip_close, NULL, &ip_mod_info
};
static struct qinit iplwinit = {
ip_lwput, NULL, NULL, NULL, NULL, &ip_mod_info
};
struct streamtab ipinfov4 = {
&iprinitv4, &ipwinit, &iplrinit, &iplwinit
};
struct streamtab ipinfov6 = {
&iprinitv6, &ipwinit, &iplrinit, &iplwinit
};
#ifdef DEBUG
boolean_t skip_sctp_cksum = B_FALSE;
#endif
void
icmp_frag_needed(mblk_t *mp, int mtu, ip_recv_attr_t *ira)
{
icmph_t icmph;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
mp = icmp_pkt_err_ok(mp, ira);
if (mp == NULL)
return;
bzero(&icmph, sizeof (icmph_t));
icmph.icmph_type = ICMP_DEST_UNREACHABLE;
icmph.icmph_code = ICMP_FRAGMENTATION_NEEDED;
icmph.icmph_du_mtu = htons((uint16_t)mtu);
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutFragNeeded);
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDestUnreachs);
icmp_pkt(mp, &icmph, sizeof (icmph_t), ira);
}
mblk_t *
icmp_inbound_v4(mblk_t *mp, ip_recv_attr_t *ira)
{
icmph_t *icmph;
ipha_t *ipha;
int ip_hdr_length;
boolean_t interested;
ipif_t *ipif;
uint32_t ts;
uint32_t *tsp;
timestruc_t now;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
zoneid_t zoneid = ira->ira_zoneid;
int len_needed;
mblk_t *mp_ret = NULL;
ipha = (ipha_t *)mp->b_rptr;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInMsgs);
ip_hdr_length = ira->ira_ip_hdr_length;
if ((mp->b_wptr - mp->b_rptr) < (ip_hdr_length + ICMPH_SIZE)) {
if (ira->ira_pktlen < (ip_hdr_length + ICMPH_SIZE)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
freemsg(mp);
return (NULL);
}
ipha = ip_pullup(mp, ip_hdr_length + ICMPH_SIZE, ira);
if (ipha == NULL) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpInErrors);
freemsg(mp);
return (NULL);
}
}
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
ip2dbg(("icmp_inbound_v4: type %d code %d\n", icmph->icmph_type,
icmph->icmph_code));
interested = B_FALSE;
switch (icmph->icmph_type) {
case ICMP_ECHO_REPLY:
BUMP_MIB(&ipst->ips_icmp_mib, icmpInEchoReps);
break;
case ICMP_DEST_UNREACHABLE:
if (icmph->icmph_code == ICMP_FRAGMENTATION_NEEDED)
BUMP_MIB(&ipst->ips_icmp_mib, icmpInFragNeeded);
interested = B_TRUE;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInDestUnreachs);
break;
case ICMP_SOURCE_QUENCH:
interested = B_TRUE;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInSrcQuenchs);
break;
case ICMP_REDIRECT:
if (!ipst->ips_ip_ignore_redirect)
interested = B_TRUE;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInRedirects);
break;
case ICMP_ECHO_REQUEST:
if (ira->ira_flags & IRAF_MULTICAST) {
interested = ipst->ips_ip_g_resp_to_echo_mcast;
} else if (ira->ira_flags & IRAF_BROADCAST) {
interested = ipst->ips_ip_g_resp_to_echo_bcast;
} else {
interested = B_TRUE;
}
BUMP_MIB(&ipst->ips_icmp_mib, icmpInEchos);
if (!interested) {
freemsg(mp);
return (NULL);
}
if (mp->b_datap->db_ref > 1) {
mblk_t *mp1;
mp1 = copymsg(mp);
freemsg(mp);
if (!mp1) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDrops);
return (NULL);
}
mp = mp1;
ipha = (ipha_t *)mp->b_rptr;
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
}
icmph->icmph_type = ICMP_ECHO_REPLY;
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutEchoReps);
icmp_send_reply_v4(mp, ipha, icmph, ira);
return (NULL);
case ICMP_ROUTER_ADVERTISEMENT:
case ICMP_ROUTER_SOLICITATION:
break;
case ICMP_TIME_EXCEEDED:
interested = B_TRUE;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInTimeExcds);
break;
case ICMP_PARAM_PROBLEM:
interested = B_TRUE;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInParmProbs);
break;
case ICMP_TIME_STAMP_REQUEST:
if (ipst->ips_ip_g_resp_to_timestamp) {
if (ira->ira_flags & IRAF_MULTIBROADCAST)
interested =
ipst->ips_ip_g_resp_to_timestamp_bcast;
else
interested = B_TRUE;
}
if (!interested) {
freemsg(mp);
return (NULL);
}
len_needed = ip_hdr_length + ICMPH_SIZE +
3 * sizeof (uint32_t);
if (mp->b_wptr - mp->b_rptr < len_needed) {
ipha = ip_pullup(mp, len_needed, ira);
if (ipha == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - ip_pullup",
mp, ill);
freemsg(mp);
return (NULL);
}
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
}
BUMP_MIB(&ipst->ips_icmp_mib, icmpInTimestamps);
if (mp->b_datap->db_ref > 1) {
mblk_t *mp1;
mp1 = copymsg(mp);
freemsg(mp);
if (!mp1) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDrops);
return (NULL);
}
mp = mp1;
ipha = (ipha_t *)mp->b_rptr;
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
}
icmph->icmph_type = ICMP_TIME_STAMP_REPLY;
tsp = (uint32_t *)&icmph[1];
tsp++;
gethrestime(&now);
ts = (now.tv_sec % (24 * 60 * 60)) * 1000 +
NSEC2MSEC(now.tv_nsec);
*tsp++ = htonl(ts);
*tsp++ = htonl(ts);
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutTimestampReps);
icmp_send_reply_v4(mp, ipha, icmph, ira);
return (NULL);
case ICMP_TIME_STAMP_REPLY:
BUMP_MIB(&ipst->ips_icmp_mib, icmpInTimestampReps);
break;
case ICMP_INFO_REQUEST:
case ICMP_INFO_REPLY:
break;
case ICMP_ADDRESS_MASK_REQUEST:
if (ira->ira_flags & IRAF_MULTIBROADCAST) {
interested =
ipst->ips_ip_respond_to_address_mask_broadcast;
} else {
interested = B_TRUE;
}
if (!interested) {
freemsg(mp);
return (NULL);
}
len_needed = ip_hdr_length + ICMPH_SIZE + IP_ADDR_LEN;
if (mp->b_wptr - mp->b_rptr < len_needed) {
ipha = ip_pullup(mp, len_needed, ira);
if (ipha == NULL) {
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp,
ill);
freemsg(mp);
return (NULL);
}
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
}
BUMP_MIB(&ipst->ips_icmp_mib, icmpInAddrMasks);
if (mp->b_datap->db_ref > 1) {
mblk_t *mp1;
mp1 = copymsg(mp);
freemsg(mp);
if (!mp1) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDrops);
return (NULL);
}
mp = mp1;
ipha = (ipha_t *)mp->b_rptr;
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
}
ipif = ipif_lookup_addr(ipha->ipha_dst, ill, zoneid, ipst);
if (ipif == NULL) {
ipif = ipif_lookup_remote(ill, ipha->ipha_src, zoneid);
if (ipif == NULL) {
freemsg(mp);
return (NULL);
}
}
icmph->icmph_type = ICMP_ADDRESS_MASK_REPLY;
bcopy(&ipif->ipif_net_mask, &icmph[1], IP_ADDR_LEN);
ipif_refrele(ipif);
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutAddrMaskReps);
icmp_send_reply_v4(mp, ipha, icmph, ira);
return (NULL);
case ICMP_ADDRESS_MASK_REPLY:
BUMP_MIB(&ipst->ips_icmp_mib, icmpInAddrMaskReps);
break;
default:
interested = B_TRUE;
BUMP_MIB(&ipst->ips_icmp_mib, icmpInUnknowns);
break;
}
if (ipst->ips_ipcl_proto_fanout_v4[IPPROTO_ICMP].connf_head != NULL) {
if (!interested) {
return (mp);
}
mp_ret = copymsg(mp);
if (mp_ret == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
}
} else if (!interested) {
freemsg(mp);
return (NULL);
}
if (mp->b_cont != NULL) {
if (ip_pullup(mp, -1, ira) == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - ip_pullup",
mp, ill);
freemsg(mp);
return (mp_ret);
}
}
if (mp->b_datap->db_ref > 1) {
mblk_t *mp1;
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
freemsg(mp);
return (mp_ret);
}
freemsg(mp);
mp = mp1;
}
ipha = (ipha_t *)mp->b_rptr;
icmph = (icmph_t *)&mp->b_rptr[ip_hdr_length];
if (!icmp_inbound_verify_v4(mp, icmph, ira)) {
freemsg(mp);
return (mp_ret);
}
switch (icmph->icmph_type) {
case ICMP_REDIRECT:
icmp_redirect_v4(mp, ipha, icmph, ira);
break;
case ICMP_DEST_UNREACHABLE:
if (icmph->icmph_code == ICMP_FRAGMENTATION_NEEDED) {
icmp_inbound_too_big_v4(icmph, ira);
}
default:
icmp_inbound_error_fanout_v4(mp, icmph, ira);
break;
}
return (mp_ret);
}
static void
icmp_send_reply_v4(mblk_t *mp, ipha_t *ipha, icmph_t *icmph,
ip_recv_attr_t *ira)
{
uint_t ip_hdr_length = ira->ira_ip_hdr_length;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ip_xmit_attr_t ixas;
icmph->icmph_checksum = 0;
icmph->icmph_checksum = IP_CSUM(mp, ip_hdr_length, 0);
ipha->ipha_ttl = ipst->ips_ip_def_ttl;
{
ipaddr_t tmp;
tmp = ipha->ipha_src;
ipha->ipha_src = ipha->ipha_dst;
ipha->ipha_dst = tmp;
}
ipha->ipha_ident = 0;
if (!IS_SIMPLE_IPH(ipha))
icmp_options_update(ipha);
bzero(&ixas, sizeof (ixas));
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
ixas.ixa_zoneid = ira->ira_zoneid;
ixas.ixa_cred = kcred;
ixas.ixa_cpid = NOPID;
ixas.ixa_tsl = ira->ira_tsl;
ixas.ixa_ifindex = 0;
ixas.ixa_ipst = ipst;
ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) {
ixas.ixa_flags |= IXAF_NO_IPSEC;
} else {
if (!ipsec_in_to_out(ira, &ixas, mp, ipha, NULL)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
return;
}
}
if (ira->ira_flags & IRAF_MULTIBROADCAST) {
ipha->ipha_src = INADDR_ANY;
ixas.ixa_flags |= IXAF_SET_SOURCE;
}
if (ipst->ips_ipv4_icmp_return_pmtu) {
ixas.ixa_flags |= IXAF_PMTU_DISCOVERY;
ipha->ipha_fragment_offset_and_flags |= IPH_DF_HTONS;
}
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutMsgs);
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
}
static boolean_t
icmp_inbound_verify_v4(mblk_t *mp, icmph_t *icmph, ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
int hdr_length;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
conn_t *connp;
ipha_t *ipha;
ipha = (ipha_t *)&icmph[1];
if ((uchar_t *)ipha + IP_SIMPLE_HDR_LENGTH > mp->b_wptr)
goto truncated;
hdr_length = IPH_HDR_LENGTH(ipha);
if ((IPH_HDR_VERSION(ipha) != IPV4_VERSION))
goto discard_pkt;
if (hdr_length < sizeof (ipha_t))
goto truncated;
if ((uchar_t *)ipha + hdr_length > mp->b_wptr)
goto truncated;
if (icmph->icmph_type == ICMP_REDIRECT)
return (B_TRUE);
switch (ipha->ipha_protocol) {
case IPPROTO_UDP:
if ((uchar_t *)ipha + hdr_length + ICMP_MIN_TP_HDR_LEN >
mp->b_wptr)
goto truncated;
break;
case IPPROTO_TCP: {
tcpha_t *tcpha;
if ((uchar_t *)ipha + hdr_length + ICMP_MIN_TP_HDR_LEN >
mp->b_wptr)
goto truncated;
tcpha = (tcpha_t *)((uchar_t *)ipha + hdr_length);
connp = ipcl_tcp_lookup_reversed_ipv4(ipha, tcpha, TCPS_LISTEN,
ipst);
if (connp == NULL)
goto discard_pkt;
if ((connp->conn_verifyicmp != NULL) &&
!connp->conn_verifyicmp(connp, tcpha, icmph, NULL, ira)) {
CONN_DEC_REF(connp);
goto discard_pkt;
}
CONN_DEC_REF(connp);
break;
}
case IPPROTO_SCTP:
if ((uchar_t *)ipha + hdr_length + ICMP_MIN_TP_HDR_LEN >
mp->b_wptr)
goto truncated;
break;
case IPPROTO_ESP:
case IPPROTO_AH:
break;
case IPPROTO_ENCAP:
if ((uchar_t *)ipha + hdr_length + sizeof (ipha_t) >
mp->b_wptr)
goto truncated;
break;
default:
break;
}
return (B_TRUE);
discard_pkt:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
return (B_FALSE);
truncated:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
return (B_FALSE);
}
static int icmp_frag_size_table[] =
{ 32000, 17914, 8166, 4352, 2002, 1496, 1006, 508, 296, 68 };
static void
icmp_inbound_too_big_v4(icmph_t *icmph, ip_recv_attr_t *ira)
{
dce_t *dce;
int old_mtu;
int mtu, orig_mtu;
ipaddr_t dst;
boolean_t disable_pmtud;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
uint_t hdr_length;
ipha_t *ipha;
ipha = (ipha_t *)&icmph[1];
ASSERT(icmph->icmph_type == ICMP_DEST_UNREACHABLE &&
icmph->icmph_code == ICMP_FRAGMENTATION_NEEDED);
ASSERT(ill != NULL);
hdr_length = IPH_HDR_LENGTH(ipha);
dst = ip_get_dst(ipha);
dce = dce_lookup_and_add_v4(dst, ipst);
if (dce == NULL) {
ip1dbg(("icmp_inbound_too_big_v4: no dce for 0x%x\n",
ntohl(dst)));
return;
}
mtu = ntohs(icmph->icmph_du_mtu);
orig_mtu = mtu;
disable_pmtud = B_FALSE;
mutex_enter(&dce->dce_lock);
if (dce->dce_flags & DCEF_PMTU)
old_mtu = dce->dce_pmtu;
else
old_mtu = ill->ill_mtu;
if (icmph->icmph_du_zero != 0 || mtu < ipst->ips_ip_pmtu_min) {
uint32_t length;
int i;
length = ntohs(ipha->ipha_length);
DTRACE_PROBE2(ip4__pmtu__guess, dce_t *, dce,
uint32_t, length);
if (old_mtu <= length &&
old_mtu >= length - hdr_length) {
ip1dbg(("Wrong mtu: sent %d, dce %d\n",
length, old_mtu));
length -= hdr_length;
}
for (i = 0; i < A_CNT(icmp_frag_size_table); i++) {
if (length > icmp_frag_size_table[i])
break;
}
if (i == A_CNT(icmp_frag_size_table)) {
ip1dbg(("Too big for packet size %d\n",
length));
disable_pmtud = B_TRUE;
mtu = ipst->ips_ip_pmtu_min;
} else {
mtu = icmp_frag_size_table[i];
ip1dbg(("Calculated mtu %d, packet size %d, "
"before %d\n", mtu, length, old_mtu));
if (mtu < ipst->ips_ip_pmtu_min) {
mtu = ipst->ips_ip_pmtu_min;
disable_pmtud = B_TRUE;
}
}
}
if (disable_pmtud)
dce->dce_flags |= DCEF_TOO_SMALL_PMTU;
else
dce->dce_flags &= ~DCEF_TOO_SMALL_PMTU;
dce->dce_pmtu = MIN(old_mtu, mtu);
icmph->icmph_du_zero = 0;
icmph->icmph_du_mtu = htons((uint16_t)dce->dce_pmtu);
DTRACE_PROBE4(ip4__pmtu__change, icmph_t *, icmph, dce_t *,
dce, int, orig_mtu, int, mtu);
dce->dce_flags |= DCEF_PMTU;
dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
mutex_exit(&dce->dce_lock);
dce_increment_generation(dce);
dce_refrele(dce);
}
static mblk_t *
icmp_inbound_self_encap_error_v4(mblk_t *mp, ipha_t *ipha, ipha_t *in_ipha)
{
int length;
ASSERT(mp->b_datap->db_type == M_DATA);
ASSERT(mp->b_cont == NULL);
length = msgdsize(mp) - ((uchar_t *)in_ipha - mp->b_rptr);
bcopy((uchar_t *)in_ipha, (uchar_t *)ipha, length);
mp->b_wptr -= (uchar_t *)in_ipha - (uchar_t *)ipha;
return (mp);
}
static void
icmp_inbound_error_fanout_v4(mblk_t *mp, icmph_t *icmph, ip_recv_attr_t *ira)
{
uint16_t *up;
uint32_t ports;
ipha_t ripha;
ipha_t *ipha;
uint_t hdr_length;
tcpha_t *tcpha;
conn_t *connp;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
ill_t *rill = ira->ira_rill;
ipha = (ipha_t *)&icmph[1];
ASSERT((uchar_t *)&ipha[1] <= mp->b_wptr);
ASSERT(mp->b_cont == NULL);
hdr_length = IPH_HDR_LENGTH(ipha);
ira->ira_protocol = ipha->ipha_protocol;
ripha.ipha_src = ipha->ipha_dst;
ripha.ipha_dst = ipha->ipha_src;
ripha.ipha_protocol = ipha->ipha_protocol;
ripha.ipha_version_and_hdr_length = ipha->ipha_version_and_hdr_length;
ip2dbg(("icmp_inbound_error_v4: proto %d %x to %x: %d/%d\n",
ripha.ipha_protocol, ntohl(ipha->ipha_src),
ntohl(ipha->ipha_dst),
icmph->icmph_type, icmph->icmph_code));
switch (ipha->ipha_protocol) {
case IPPROTO_UDP:
up = (uint16_t *)((uchar_t *)ipha + hdr_length);
ip2dbg(("icmp_inbound_error_v4: UDP ports %d to %d\n",
ntohs(up[0]), ntohs(up[1])));
ira->ira_flags |= IRAF_ICMP_ERROR;
ip_fanout_udp_multi_v4(mp, &ripha, up[0], up[1], ira);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
case IPPROTO_TCP:
tcpha = (tcpha_t *)((uchar_t *)ipha + hdr_length);
connp = ipcl_tcp_lookup_reversed_ipv4(ipha, tcpha, TCPS_LISTEN,
ipst);
if (connp == NULL)
goto discard_pkt;
if (connp->conn_min_ttl != 0 &&
connp->conn_min_ttl > ira->ira_ttl) {
CONN_DEC_REF(connp);
goto discard_pkt;
}
if (CONN_INBOUND_POLICY_PRESENT(connp, ipss) ||
(ira->ira_flags & IRAF_IPSEC_SECURE)) {
mp = ipsec_check_inbound_policy(mp, connp,
ipha, NULL, ira);
if (mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
CONN_DEC_REF(connp);
return;
}
}
ira->ira_flags |= IRAF_ICMP_ERROR;
ira->ira_ill = ira->ira_rill = NULL;
if (IPCL_IS_TCP(connp)) {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp,
connp->conn_recvicmp, connp, ira, SQ_FILL,
SQTAG_TCP_INPUT_ICMP_ERR);
} else {
(connp->conn_recv)(connp, mp, NULL, ira);
CONN_DEC_REF(connp);
}
ira->ira_ill = ill;
ira->ira_rill = rill;
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
case IPPROTO_SCTP:
up = (uint16_t *)((uchar_t *)ipha + hdr_length);
((uint16_t *)&ports)[0] = up[1];
((uint16_t *)&ports)[1] = up[0];
ira->ira_flags |= IRAF_ICMP_ERROR;
ip_fanout_sctp(mp, &ripha, NULL, ports, ira);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
case IPPROTO_ESP:
case IPPROTO_AH:
if (!ipsec_loaded(ipss)) {
ip_proto_not_sup(mp, ira);
return;
}
if (ipha->ipha_protocol == IPPROTO_ESP)
mp = ipsecesp_icmp_error(mp, ira);
else
mp = ipsecah_icmp_error(mp, ira);
if (mp == NULL)
return;
if (mp->b_cont != NULL) {
if (!pullupmsg(mp, -1))
goto discard_pkt;
}
if (mp->b_wptr - mp->b_rptr < IP_SIMPLE_HDR_LENGTH)
goto truncated;
ipha = (ipha_t *)mp->b_rptr;
hdr_length = IPH_HDR_LENGTH(ipha);
icmph = (icmph_t *)&mp->b_rptr[hdr_length];
if (!icmp_inbound_verify_v4(mp, icmph, ira)) {
freemsg(mp);
return;
}
icmp_inbound_error_fanout_v4(mp, icmph, ira);
return;
case IPPROTO_ENCAP: {
ipha_t *in_ipha;
ASSERT(hdr_length >= sizeof (ipha_t));
in_ipha = (ipha_t *)((uchar_t *)ipha + hdr_length);
if ((IPH_HDR_VERSION(in_ipha) != IPV4_VERSION)) {
goto discard_pkt;
}
if (IPH_HDR_LENGTH(in_ipha) < sizeof (ipha_t)) {
goto discard_pkt;
}
if (in_ipha->ipha_src == ipha->ipha_src &&
in_ipha->ipha_dst == ipha->ipha_dst) {
mp = icmp_inbound_self_encap_error_v4(mp, ipha,
in_ipha);
if (mp == NULL)
goto discard_pkt;
if (mp->b_cont != NULL) {
if (!pullupmsg(mp, -1))
goto discard_pkt;
}
if (mp->b_wptr - mp->b_rptr < IP_SIMPLE_HDR_LENGTH)
goto truncated;
ipha = (ipha_t *)mp->b_rptr;
hdr_length = IPH_HDR_LENGTH(ipha);
icmph = (icmph_t *)&mp->b_rptr[hdr_length];
if (!icmp_inbound_verify_v4(mp, icmph, ira)) {
freemsg(mp);
return;
}
if (ipha->ipha_protocol == IPPROTO_ENCAP) {
goto discard_pkt;
}
icmp_inbound_error_fanout_v4(mp, icmph, ira);
return;
}
}
case IPPROTO_IPV6:
if ((connp = ipcl_iptun_classify_v4(&ripha.ipha_src,
&ripha.ipha_dst, ipst)) != NULL) {
ira->ira_flags |= IRAF_ICMP_ERROR;
connp->conn_recvicmp(connp, mp, NULL, ira);
CONN_DEC_REF(connp);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
}
default:
ira->ira_flags |= IRAF_ICMP_ERROR;
ip_fanout_proto_v4(mp, &ripha, ira);
ira->ira_flags &= ~IRAF_ICMP_ERROR;
return;
}
discard_pkt:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip1dbg(("icmp_inbound_error_fanout_v4: drop pkt\n"));
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return;
truncated:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
freemsg(mp);
}
uint8_t
ipoptp_first(ipoptp_t *optp, ipha_t *ipha)
{
uint32_t totallen;
totallen = ipha->ipha_version_and_hdr_length -
(uint8_t)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS);
totallen <<= 2;
optp->ipoptp_next = (uint8_t *)(&ipha[1]);
optp->ipoptp_end = optp->ipoptp_next + totallen;
optp->ipoptp_flags = 0;
return (ipoptp_next(optp));
}
uint8_t
ipoptp_first2(ipoptp_t *optp, uint32_t totallen, uint8_t *opt)
{
optp->ipoptp_next = opt;
optp->ipoptp_end = optp->ipoptp_next + totallen;
optp->ipoptp_flags = 0;
return (ipoptp_next(optp));
}
uint8_t
ipoptp_next(ipoptp_t *optp)
{
uint8_t *end = optp->ipoptp_end;
uint8_t *cur = optp->ipoptp_next;
uint8_t opt, len, pointer;
ASSERT(cur <= end);
if (cur == end)
return (IPOPT_EOL);
opt = cur[IPOPT_OPTVAL];
while (opt == IPOPT_NOP) {
cur++;
if (cur == end)
return (IPOPT_EOL);
opt = cur[IPOPT_OPTVAL];
}
if (opt == IPOPT_EOL)
return (IPOPT_EOL);
if ((cur + 1) >= end) {
optp->ipoptp_flags |= IPOPTP_ERROR;
return (IPOPT_EOL);
}
len = cur[IPOPT_OLEN];
if (len < 2) {
optp->ipoptp_flags |= IPOPTP_ERROR;
return (IPOPT_EOL);
}
optp->ipoptp_cur = cur;
optp->ipoptp_len = len;
optp->ipoptp_next = cur + len;
if (cur + len > end) {
optp->ipoptp_flags |= IPOPTP_ERROR;
return (IPOPT_EOL);
}
pointer = IPOPT_EOL;
switch (opt) {
case IPOPT_RR:
case IPOPT_TS:
case IPOPT_LSRR:
case IPOPT_SSRR:
if (len <= IPOPT_OFFSET) {
optp->ipoptp_flags |= IPOPTP_ERROR;
return (opt);
}
pointer = cur[IPOPT_OFFSET];
if (pointer - 1 > len) {
optp->ipoptp_flags |= IPOPTP_ERROR;
return (opt);
}
break;
}
switch (opt) {
case IPOPT_RR:
case IPOPT_SSRR:
case IPOPT_LSRR:
if (pointer < IPOPT_MINOFF_SR)
optp->ipoptp_flags |= IPOPTP_ERROR;
break;
case IPOPT_TS:
if (pointer < IPOPT_MINOFF_IT)
optp->ipoptp_flags |= IPOPTP_ERROR;
ASSERT(len > IPOPT_POS_OV_FLG);
break;
}
return (opt);
}
int
ip_opt_get_user(conn_t *connp, uchar_t *buf)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
uint32_t len = 0;
uchar_t *buf1 = buf;
uint32_t totallen;
ipaddr_t dst;
ip_pkt_t *ipp = &connp->conn_xmit_ipp;
if (!(ipp->ipp_fields & IPPF_IPV4_OPTIONS))
return (0);
totallen = ipp->ipp_ipv4_options_len;
if (totallen & 0x3)
return (0);
buf += IP_ADDR_LEN;
len += IP_ADDR_LEN;
bzero(buf1, IP_ADDR_LEN);
dst = connp->conn_faddr_v4;
for (optval = ipoptp_first2(&opts, totallen, ipp->ipp_ipv4_options);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
int off;
opt = opts.ipoptp_cur;
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
break;
}
optlen = opts.ipoptp_len;
switch (optval) {
case IPOPT_SSRR:
case IPOPT_LSRR:
buf[IPOPT_OPTVAL] = optval;
buf[IPOPT_OLEN] = optlen;
buf[IPOPT_OFFSET] = optlen;
off = optlen - IP_ADDR_LEN;
if (off < 0) {
break;
}
if (dst == INADDR_ANY)
bcopy(opt + off, buf1, IP_ADDR_LEN);
off -= IP_ADDR_LEN;
while (off > 0) {
bcopy(opt + off,
buf + off + IP_ADDR_LEN,
IP_ADDR_LEN);
off -= IP_ADDR_LEN;
}
bcopy(&dst, buf + off + IP_ADDR_LEN,
IP_ADDR_LEN);
buf += optlen;
len += optlen;
break;
default:
bcopy(opt, buf, optlen);
buf += optlen;
len += optlen;
break;
}
}
while (len & 0x3) {
*buf++ = IPOPT_EOL;
len++;
}
return (len);
}
static void
icmp_options_update(ipha_t *ipha)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
ipaddr_t src;
ipaddr_t dst;
ip2dbg(("icmp_options_update\n"));
src = ipha->ipha_src;
dst = ipha->ipha_dst;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
ASSERT((opts.ipoptp_flags & IPOPTP_ERROR) == 0);
opt = opts.ipoptp_cur;
ip2dbg(("icmp_options_update: opt %d, len %d\n",
optval, opts.ipoptp_len));
switch (optval) {
int off1, off2;
case IPOPT_SSRR:
case IPOPT_LSRR:
off1 = IPOPT_MINOFF_SR - 1;
off2 = opt[IPOPT_OFFSET] - IP_ADDR_LEN - 1;
if (off2 < 0) {
ip1dbg((
"icmp_options_update: bad src route\n"));
break;
}
bcopy((char *)opt + off2, &dst, IP_ADDR_LEN);
bcopy(&ipha->ipha_dst, (char *)opt + off2, IP_ADDR_LEN);
bcopy(&dst, &ipha->ipha_dst, IP_ADDR_LEN);
off2 -= IP_ADDR_LEN;
while (off1 < off2) {
bcopy((char *)opt + off1, &src, IP_ADDR_LEN);
bcopy((char *)opt + off2, (char *)opt + off1,
IP_ADDR_LEN);
bcopy(&src, (char *)opt + off2, IP_ADDR_LEN);
off1 += IP_ADDR_LEN;
off2 -= IP_ADDR_LEN;
}
opt[IPOPT_OFFSET] = IPOPT_MINOFF_SR;
break;
}
}
}
static void
icmp_redirect_v4(mblk_t *mp, ipha_t *ipha, icmph_t *icmph, ip_recv_attr_t *ira)
{
ire_t *ire, *nire;
ire_t *prev_ire;
ipaddr_t src, dst, gateway;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ipha_t *inner_ipha;
inner_ipha = (ipha_t *)&icmph[1];
src = ipha->ipha_src;
dst = inner_ipha->ipha_dst;
gateway = icmph->icmph_rd_gateway;
ire = ire_ftable_lookup_v4(gateway, 0, 0, IRE_ONLINK, NULL,
ALL_ZONES, NULL, MATCH_IRE_TYPE, 0, ipst, NULL);
prev_ire = ire_ftable_lookup_v4(dst, 0, 0, 0, NULL, ALL_ZONES,
NULL, MATCH_IRE_DSTONLY, 0, ipst, NULL);
if (prev_ire == NULL || ire == NULL ||
(prev_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) ||
(prev_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) ||
!(ire->ire_type & IRE_IF_ALL) ||
prev_ire->ire_gateway_addr != src) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpInBadRedirects);
ip_drop_input("icmpInBadRedirects - ire", mp, ira->ira_ill);
freemsg(mp);
if (ire != NULL)
ire_refrele(ire);
if (prev_ire != NULL)
ire_refrele(prev_ire);
return;
}
ire_refrele(prev_ire);
ire_refrele(ire);
switch (icmph->icmph_code) {
case 0:
case 1:
case 2:
case 3:
break;
default:
BUMP_MIB(&ipst->ips_icmp_mib, icmpInBadRedirects);
ip_drop_input("icmpInBadRedirects - code", mp, ira->ira_ill);
freemsg(mp);
return;
}
ire = ire_create(
(uchar_t *)&dst,
(uchar_t *)&ip_g_all_ones,
(uchar_t *)&gateway,
IRE_HOST,
NULL,
ALL_ZONES,
(RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST),
NULL,
ipst);
if (ire == NULL) {
freemsg(mp);
return;
}
nire = ire_add(ire);
if (nire != NULL && nire != ire) {
ASSERT(nire->ire_identical_ref > 1);
ire_delete(nire);
ire_refrele(nire);
nire = NULL;
}
ire = nire;
if (ire != NULL) {
ire_refrele(ire);
ip_rts_change(RTM_REDIRECT, dst, gateway, IP_HOST_MASK, 0, src,
(RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 0,
(RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_AUTHOR), ipst);
}
prev_ire = ire_ftable_lookup_v4(dst, 0, src, IRE_HOST, NULL,
ALL_ZONES, NULL, (MATCH_IRE_GW | MATCH_IRE_TYPE), 0, ipst, NULL);
if (prev_ire != NULL) {
if (prev_ire ->ire_flags & RTF_DYNAMIC)
ire_delete(prev_ire);
ire_refrele(prev_ire);
}
freemsg(mp);
}
static void
icmp_param_problem(mblk_t *mp, uint8_t ptr, ip_recv_attr_t *ira)
{
icmph_t icmph;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
mp = icmp_pkt_err_ok(mp, ira);
if (mp == NULL)
return;
bzero(&icmph, sizeof (icmph_t));
icmph.icmph_type = ICMP_PARAM_PROBLEM;
icmph.icmph_pp_ptr = ptr;
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutParmProbs);
icmp_pkt(mp, &icmph, sizeof (icmph_t), ira);
}
static void
icmp_pkt(mblk_t *mp, void *stuff, size_t len, ip_recv_attr_t *ira)
{
ipaddr_t dst;
icmph_t *icmph;
ipha_t *ipha;
uint_t len_needed;
size_t msg_len;
mblk_t *mp1;
ipaddr_t src;
ire_t *ire;
ip_xmit_attr_t ixas;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ipha = (ipha_t *)mp->b_rptr;
bzero(&ixas, sizeof (ixas));
ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
ixas.ixa_zoneid = ira->ira_zoneid;
ixas.ixa_ifindex = 0;
ixas.ixa_ipst = ipst;
ixas.ixa_cred = kcred;
ixas.ixa_cpid = NOPID;
ixas.ixa_tsl = ira->ira_tsl;
ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
if (ira->ira_flags & IRAF_IPSEC_SECURE) {
if (!ipsec_in_to_out(ira, &ixas, mp, ipha, NULL)) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
return;
}
} else {
ixas.ixa_flags |= IXAF_NO_IPSEC;
}
dst = ipha->ipha_src;
ire = ire_ftable_lookup_v4(ipha->ipha_dst, 0, 0,
(IRE_LOCAL|IRE_LOOPBACK), NULL, ira->ira_zoneid, NULL,
MATCH_IRE_TYPE|MATCH_IRE_ZONEONLY, 0, ipst, NULL);
if (ire != NULL) {
ire_refrele(ire);
src = ipha->ipha_dst;
} else {
src = INADDR_ANY;
ixas.ixa_flags |= IXAF_SET_SOURCE;
}
len_needed = IPH_HDR_LENGTH(ipha);
if (ipha->ipha_protocol == IPPROTO_ENCAP ||
ipha->ipha_protocol == IPPROTO_IPV6) {
ip6_t *inner_ip6h = (ip6_t *)((uchar_t *)ipha + len_needed);
if (!pullupmsg(mp, -1)) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, NULL);
freemsg(mp);
return;
}
ipha = (ipha_t *)mp->b_rptr;
if (ipha->ipha_protocol == IPPROTO_ENCAP) {
if (IPH_HDR_VERSION(inner_ip6h) == IPV4_VERSION) {
len_needed +=
IPH_HDR_LENGTH(((uchar_t *)inner_ip6h));
} else {
len_needed = sizeof (ipha_t);
}
} else {
ASSERT(ipha->ipha_protocol == IPPROTO_IPV6);
len_needed += ip_hdr_length_v6(mp, inner_ip6h);
}
}
len_needed += ipst->ips_ip_icmp_return;
msg_len = msgdsize(mp);
if (msg_len > len_needed) {
(void) adjmsg(mp, len_needed - msg_len);
msg_len = len_needed;
}
mp1 = allocb(sizeof (icmp_ipha) + len, BPRI_MED);
if (mp1 == NULL) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutErrors);
freemsg(mp);
return;
}
mp1->b_cont = mp;
mp = mp1;
ixas.ixa_flags |= IXAF_TRUSTED_ICMP;
ipha = (ipha_t *)mp->b_rptr;
mp1->b_wptr = (uchar_t *)ipha + (sizeof (icmp_ipha) + len);
*ipha = icmp_ipha;
ipha->ipha_src = src;
ipha->ipha_dst = dst;
ipha->ipha_ttl = ipst->ips_ip_def_ttl;
msg_len += sizeof (icmp_ipha) + len;
if (msg_len > IP_MAXPACKET) {
(void) adjmsg(mp, IP_MAXPACKET - msg_len);
msg_len = IP_MAXPACKET;
}
ipha->ipha_length = htons((uint16_t)msg_len);
icmph = (icmph_t *)&ipha[1];
bcopy(stuff, icmph, len);
icmph->icmph_checksum = 0;
icmph->icmph_checksum = IP_CSUM(mp, (int32_t)sizeof (ipha_t), 0);
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutMsgs);
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
}
boolean_t
icmp_err_rate_limit(ip_stack_t *ipst)
{
clock_t now = TICK_TO_MSEC(ddi_get_lbolt());
uint_t refilled;
uint_t err_interval = ipst->ips_ip_icmp_err_interval;
if (err_interval == 0)
return (B_FALSE);
if (ipst->ips_icmp_pkt_err_last > now) {
ipst->ips_icmp_pkt_err_last = 0;
ipst->ips_icmp_pkt_err_sent = 0;
}
if (ipst->ips_icmp_pkt_err_sent != 0) {
refilled = (now - ipst->ips_icmp_pkt_err_last)/err_interval;
if (refilled > ipst->ips_icmp_pkt_err_sent) {
ipst->ips_icmp_pkt_err_sent = 0;
} else {
ipst->ips_icmp_pkt_err_sent -= refilled;
ipst->ips_icmp_pkt_err_last += refilled * err_interval;
}
}
if (ipst->ips_icmp_pkt_err_sent == 0) {
ipst->ips_icmp_pkt_err_last = now;
}
if (ipst->ips_icmp_pkt_err_sent < ipst->ips_ip_icmp_err_burst) {
ipst->ips_icmp_pkt_err_sent++;
ip1dbg(("icmp_err_rate_limit: %d sent in burst\n",
ipst->ips_icmp_pkt_err_sent));
return (B_FALSE);
}
ip1dbg(("icmp_err_rate_limit: dropped\n"));
return (B_TRUE);
}
static mblk_t *
icmp_pkt_err_ok(mblk_t *mp, ip_recv_attr_t *ira)
{
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
icmph_t *icmph;
ipha_t *ipha;
uint_t len_needed;
if (!mp)
return (NULL);
ipha = (ipha_t *)mp->b_rptr;
if (ip_csum_hdr(ipha)) {
BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsInCksumErrs);
ip_drop_input("ipIfStatsInCksumErrs", mp, NULL);
freemsg(mp);
return (NULL);
}
if (ip_type_v4(ipha->ipha_dst, ipst) == IRE_BROADCAST ||
ip_type_v4(ipha->ipha_src, ipst) == IRE_BROADCAST ||
CLASSD(ipha->ipha_dst) ||
CLASSD(ipha->ipha_src) ||
(ntohs(ipha->ipha_fragment_offset_and_flags) & IPH_OFFSET)) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDrops);
freemsg(mp);
return (NULL);
}
if (ipha->ipha_protocol == IPPROTO_ICMP) {
len_needed = IPH_HDR_LENGTH(ipha) + ICMPH_SIZE;
if (mp->b_wptr - mp->b_rptr < len_needed) {
if (!pullupmsg(mp, len_needed)) {
BUMP_MIB(&ipst->ips_icmp_mib, icmpInErrors);
freemsg(mp);
return (NULL);
}
ipha = (ipha_t *)mp->b_rptr;
}
icmph = (icmph_t *)
(&((char *)ipha)[IPH_HDR_LENGTH(ipha)]);
switch (icmph->icmph_type) {
case ICMP_DEST_UNREACHABLE:
case ICMP_SOURCE_QUENCH:
case ICMP_TIME_EXCEEDED:
case ICMP_PARAM_PROBLEM:
case ICMP_REDIRECT:
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDrops);
freemsg(mp);
return (NULL);
default:
break;
}
}
if (is_system_labeled() && !tsol_can_reply_error(mp, ira)) {
ip2dbg(("icmp_pkt_err_ok: can't respond to packet\n"));
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDrops);
freemsg(mp);
return (NULL);
}
if (icmp_err_rate_limit(ipst)) {
freemsg(mp);
return (NULL);
}
return (mp);
}
void
ip_send_potential_redirect_v4(mblk_t *mp, ipha_t *ipha, ire_t *ire,
ip_recv_attr_t *ira)
{
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ipaddr_t src, nhop;
mblk_t *mp1;
ire_t *nhop_ire;
if ((ire->ire_type & IRE_ONLINK) ||
ip_source_routed(ipha, ipst))
return;
nhop_ire = ire_nexthop(ire);
if (nhop_ire == NULL)
return;
nhop = nhop_ire->ire_addr;
if (nhop_ire->ire_type & IRE_IF_CLONE) {
ire_t *ire2;
mutex_enter(&nhop_ire->ire_lock);
ire2 = nhop_ire->ire_dep_parent;
if (ire2 != NULL)
ire_refhold(ire2);
mutex_exit(&nhop_ire->ire_lock);
ire_refrele(nhop_ire);
nhop_ire = ire2;
}
if (nhop_ire == NULL)
return;
ASSERT(!(nhop_ire->ire_type & IRE_IF_CLONE));
src = ipha->ipha_src;
if ((src & nhop_ire->ire_mask) == (nhop & nhop_ire->ire_mask)) {
mp1 = copymsg(mp);
if (mp1 != NULL) {
icmp_send_redirect(mp1, nhop, ira);
}
}
ire_refrele(nhop_ire);
}
static void
icmp_send_redirect(mblk_t *mp, ipaddr_t gateway, ip_recv_attr_t *ira)
{
icmph_t icmph;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
mp = icmp_pkt_err_ok(mp, ira);
if (mp == NULL)
return;
bzero(&icmph, sizeof (icmph_t));
icmph.icmph_type = ICMP_REDIRECT;
icmph.icmph_code = 1;
icmph.icmph_rd_gateway = gateway;
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutRedirects);
icmp_pkt(mp, &icmph, sizeof (icmph_t), ira);
}
void
icmp_time_exceeded(mblk_t *mp, uint8_t code, ip_recv_attr_t *ira)
{
icmph_t icmph;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
mp = icmp_pkt_err_ok(mp, ira);
if (mp == NULL)
return;
bzero(&icmph, sizeof (icmph_t));
icmph.icmph_type = ICMP_TIME_EXCEEDED;
icmph.icmph_code = code;
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutTimeExcds);
icmp_pkt(mp, &icmph, sizeof (icmph_t), ira);
}
void
icmp_unreachable(mblk_t *mp, uint8_t code, ip_recv_attr_t *ira)
{
icmph_t icmph;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
mp = icmp_pkt_err_ok(mp, ira);
if (mp == NULL)
return;
bzero(&icmph, sizeof (icmph_t));
icmph.icmph_type = ICMP_DEST_UNREACHABLE;
icmph.icmph_code = code;
BUMP_MIB(&ipst->ips_icmp_mib, icmpOutDestUnreachs);
icmp_pkt(mp, &icmph, sizeof (icmph_t), ira);
}
boolean_t
ip_ipsec_policy_inherit(conn_t *connp, conn_t *lconnp, ip_recv_attr_t *ira)
{
ASSERT(lconnp->conn_policy != NULL);
ASSERT(connp->conn_policy == NULL);
IPPH_REFHOLD(lconnp->conn_policy);
connp->conn_policy = lconnp->conn_policy;
if (ira->ira_ipsec_action != NULL) {
if (connp->conn_latch == NULL) {
connp->conn_latch = iplatch_create();
if (connp->conn_latch == NULL)
return (B_FALSE);
}
ipsec_latch_inbound(connp, ira);
}
return (B_TRUE);
}
ip_laddr_t
ip_laddr_verify_v4(ipaddr_t src_addr, zoneid_t zoneid,
ip_stack_t *ipst, boolean_t allow_mcbc)
{
ire_t *src_ire;
ASSERT(src_addr != INADDR_ANY);
src_ire = ire_ftable_lookup_v4(src_addr, 0, 0, 0,
NULL, zoneid, NULL, MATCH_IRE_ZONEONLY, 0, ipst, NULL);
if (src_ire != NULL && (src_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK))) {
ire_refrele(src_ire);
return (IPVL_UNICAST_UP);
} else if (src_ire != NULL && src_ire->ire_type & IRE_BROADCAST) {
ire_refrele(src_ire);
if (allow_mcbc)
return (IPVL_BCAST);
else
return (IPVL_BAD);
} else if (CLASSD(src_addr)) {
if (src_ire != NULL)
ire_refrele(src_ire);
if (allow_mcbc)
return (IPVL_MCAST);
else
return (IPVL_BAD);
} else {
ipif_t *ipif;
if (src_ire != NULL)
ire_refrele(src_ire);
ipif = ipif_lookup_addr(src_addr, NULL, zoneid, ipst);
if (ipif == NULL)
return (IPVL_BAD);
if (ipif->ipif_flags & (IPIF_NOLOCAL | IPIF_ANYCAST)) {
ipif_refrele(ipif);
return (IPVL_BAD);
}
ipif_refrele(ipif);
return (IPVL_UNICAST_DOWN);
}
}
int
ip_laddr_fanout_insert(conn_t *connp)
{
int error;
connp->conn_policy_cached = B_FALSE;
error = ipcl_bind_insert(connp);
if (error != 0) {
if (connp->conn_anon_port) {
(void) tsol_mlp_anon(crgetzone(connp->conn_cred),
connp->conn_mlp_type, connp->conn_proto,
ntohs(connp->conn_lport), B_FALSE);
}
connp->conn_mlp_type = mlptSingle;
}
return (error);
}
int
ip_set_destination_v4(ipaddr_t *src_addrp, ipaddr_t dst_addr, ipaddr_t firsthop,
ip_xmit_attr_t *ixa, iulp_t *uinfo, uint32_t flags, uint_t mac_mode)
{
ire_t *ire = NULL;
int error = 0;
ipaddr_t setsrc;
zoneid_t zoneid = ixa->ixa_zoneid;
ip_stack_t *ipst = ixa->ixa_ipst;
dce_t *dce;
uint_t pmtu;
uint_t generation;
nce_t *nce;
ill_t *ill = NULL;
boolean_t multirt = B_FALSE;
ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
ASSERT(dst_addr != INADDR_ANY);
if (is_system_labeled()) {
ts_label_t *tsl = NULL;
error = tsol_check_dest(ixa->ixa_tsl, &dst_addr, IPV4_VERSION,
mac_mode, (flags & IPDF_ZONE_IS_GLOBAL) != 0, &tsl);
if (error != 0)
return (error);
if (tsl != NULL) {
ip_xmit_attr_replace_tsl(ixa, tsl);
}
}
setsrc = INADDR_ANY;
ire = ip_select_route_v4(firsthop, *src_addrp, ixa,
&generation, &setsrc, &error, &multirt);
ASSERT(ire != NULL);
if (error != 0)
goto bad_addr;
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
ASSERT(generation == IRE_GENERATION_VERIFY);
if (flags & IPDF_VERIFY_DST) {
if (!(ire->ire_type & IRE_HOST))
error = ENETUNREACH;
else
error = EHOSTUNREACH;
}
}
if ((ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST)) &&
!(flags & IPDF_ALLOW_MCBC)) {
ire_refrele(ire);
ire = ire_reject(ipst, B_FALSE);
generation = IRE_GENERATION_VERIFY;
error = ENETUNREACH;
}
if (ixa->ixa_ire != NULL)
ire_refrele_notr(ixa->ixa_ire);
#ifdef DEBUG
ire_refhold_notr(ire);
ire_refrele(ire);
#endif
ixa->ixa_ire = ire;
ixa->ixa_ire_generation = generation;
if (flags & IPDF_UNIQUE_DCE) {
dce = dce_lookup_and_add_v4(dst_addr, ipst);
if (dce != NULL)
generation = dce->dce_generation;
else
dce = dce_lookup_v4(dst_addr, ipst, &generation);
} else {
dce = dce_lookup_v4(dst_addr, ipst, &generation);
}
ASSERT(dce != NULL);
if (ixa->ixa_dce != NULL)
dce_refrele_notr(ixa->ixa_dce);
#ifdef DEBUG
dce_refhold_notr(dce);
dce_refrele(dce);
#endif
ixa->ixa_dce = dce;
ixa->ixa_dce_generation = generation;
if (multirt) {
ixa->ixa_postfragfn = ip_postfrag_multirt_v4;
ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST;
} else {
ixa->ixa_postfragfn = ire->ire_postfragfn;
ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST;
}
if (!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
nce = ire_to_nce(ire, firsthop, NULL);
if (nce == NULL) {
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
} else {
if (ixa->ixa_nce != NULL)
nce_refrele(ixa->ixa_nce);
ixa->ixa_nce = nce;
}
}
if (*src_addrp == htonl(INADDR_LOOPBACK)) {
if ((ire->ire_type & IRE_LOCAL) && ire->ire_zoneid != zoneid) {
ire = NULL;
error = EADDRNOTAVAIL;
goto bad_addr;
}
if (!(ire->ire_type & (IRE_LOOPBACK|IRE_LOCAL|IRE_MULTICAST))) {
ire = NULL;
error = EADDRNOTAVAIL;
goto bad_addr;
}
}
if (ire->ire_type & IRE_BROADCAST) {
if (flags & IPDF_SELECT_SRC)
ixa->ixa_flags |= IXAF_SET_SOURCE;
else
ixa->ixa_flags &= ~IXAF_SET_SOURCE;
}
if (flags & IPDF_SELECT_SRC) {
ipaddr_t src_addr;
ill = ire_nexthop_ill(ire);
if (ill == NULL) {
src_addr = htonl(INADDR_LOOPBACK);
generation = SRC_GENERATION_VERIFY;
} else {
error = ip_select_source_v4(ill, setsrc, dst_addr,
ixa->ixa_multicast_ifaddr, zoneid,
ipst, &src_addr, &generation, NULL);
if (error != 0) {
ire = NULL;
goto bad_addr;
}
}
if ((src_addr == htonl(INADDR_LOOPBACK)) &&
!(ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK|IRE_MULTICAST)) &&
!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
ire = NULL;
error = EADDRNOTAVAIL;
goto bad_addr;
}
*src_addrp = src_addr;
ixa->ixa_src_generation = generation;
}
nce = ixa->ixa_nce;
if (nce != NULL && nce->nce_is_condemned) {
nce_refrele(nce);
ixa->ixa_nce = NULL;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
if (ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST))
ixa->ixa_flags &= ~IXAF_PMTU_DISCOVERY;
pmtu = ip_get_pmtu(ixa);
ixa->ixa_fragsize = pmtu;
if (ixa->ixa_flags & IXAF_VERIFY_PMTU)
ixa->ixa_pmtu = pmtu;
if (uinfo != NULL) {
bzero(uinfo, sizeof (*uinfo));
if (dce->dce_flags & DCEF_UINFO)
*uinfo = dce->dce_uinfo;
rts_merge_metrics(uinfo, &ire->ire_metrics);
if (uinfo->iulp_mtu == 0 || uinfo->iulp_mtu > pmtu)
uinfo->iulp_mtu = pmtu;
uinfo->iulp_localnet = (ire->ire_type & IRE_ONLINK) != 0;
uinfo->iulp_loopback = (ire->ire_type & IRE_LOOPBACK) != 0;
uinfo->iulp_local = (ire->ire_type & IRE_LOCAL) != 0;
}
if (ill != NULL)
ill_refrele(ill);
return (error);
bad_addr:
if (ire != NULL)
ire_refrele(ire);
if (ill != NULL)
ill_refrele(ill);
nce = ixa->ixa_nce;
if (nce != NULL && nce->nce_is_condemned) {
nce_refrele(nce);
ixa->ixa_nce = NULL;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
return (error);
}
uint_t
ip_get_base_mtu(ill_t *ill, ire_t *ire)
{
uint_t mtu;
uint_t iremtu = ire->ire_metrics.iulp_mtu;
if (ire->ire_type & (IRE_MULTICAST|IRE_BROADCAST))
mtu = ill->ill_mc_mtu;
else
mtu = ill->ill_mtu;
if (iremtu != 0 && iremtu < mtu)
mtu = iremtu;
return (mtu);
}
uint_t
ip_get_pmtu(ip_xmit_attr_t *ixa)
{
ip_stack_t *ipst = ixa->ixa_ipst;
dce_t *dce;
nce_t *nce;
ire_t *ire;
uint_t pmtu;
ire = ixa->ixa_ire;
dce = ixa->ixa_dce;
nce = ixa->ixa_nce;
if (!ipst->ips_ip_path_mtu_discovery)
ixa->ixa_flags &= ~IXAF_PMTU_DISCOVERY;
pmtu = IP_MAXPACKET;
if (ixa->ixa_flags & (IXAF_PMTU_DISCOVERY | IXAF_DONTFRAG)) {
ixa->ixa_flags |= IXAF_PMTU_IPV4_DF;
} else {
ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF;
if (!(ixa->ixa_flags & IXAF_IS_IPV4))
pmtu = IPV6_MIN_MTU;
}
if ((dce->dce_flags & DCEF_PMTU) &&
TICK_TO_SEC(ddi_get_lbolt64()) - dce->dce_last_change_time >
ipst->ips_ip_pathmtu_interval) {
mutex_enter(&dce->dce_lock);
dce->dce_flags &= ~(DCEF_PMTU|DCEF_TOO_SMALL_PMTU);
dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
mutex_exit(&dce->dce_lock);
dce_increment_generation(dce);
}
if (ire->ire_metrics.iulp_mtu != 0 &&
ire->ire_metrics.iulp_mtu < pmtu)
pmtu = ire->ire_metrics.iulp_mtu;
if (ixa->ixa_flags & IXAF_PMTU_DISCOVERY) {
if (dce->dce_flags & DCEF_PMTU) {
if (dce->dce_pmtu < pmtu)
pmtu = dce->dce_pmtu;
if (dce->dce_flags & DCEF_TOO_SMALL_PMTU) {
ixa->ixa_flags |= IXAF_PMTU_TOO_SMALL;
ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF;
} else {
ixa->ixa_flags &= ~IXAF_PMTU_TOO_SMALL;
ixa->ixa_flags |= IXAF_PMTU_IPV4_DF;
}
} else {
ixa->ixa_flags &= ~IXAF_PMTU_TOO_SMALL;
ixa->ixa_flags |= IXAF_PMTU_IPV4_DF;
}
}
if (ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) {
uint_t loopback_mtu;
loopback_mtu = (ire->ire_ipversion == IPV6_VERSION) ?
ip_loopback_mtu_v6plus : ip_loopback_mtuplus;
if (loopback_mtu < pmtu)
pmtu = loopback_mtu;
} else if (nce != NULL) {
if (ire->ire_type & (IRE_MULTICAST|IRE_BROADCAST)) {
if (nce->nce_common->ncec_ill->ill_mc_mtu < pmtu)
pmtu = nce->nce_common->ncec_ill->ill_mc_mtu;
if (nce->nce_common->ncec_ill != nce->nce_ill &&
nce->nce_ill->ill_mc_mtu < pmtu) {
pmtu = nce->nce_ill->ill_mc_mtu;
}
} else {
if (nce->nce_common->ncec_ill->ill_mtu < pmtu)
pmtu = nce->nce_common->ncec_ill->ill_mtu;
if (nce->nce_common->ncec_ill != nce->nce_ill &&
nce->nce_ill->ill_mtu < pmtu) {
pmtu = nce->nce_ill->ill_mtu;
}
}
}
if (!(ixa->ixa_flags & IXAF_IS_IPV4)) {
if (ixa->ixa_flags & IXAF_USE_MIN_MTU) {
switch (ixa->ixa_use_min_mtu) {
case IPV6_USE_MIN_MTU_MULTICAST:
if (ire->ire_type & IRE_MULTICAST)
pmtu = IPV6_MIN_MTU;
break;
case IPV6_USE_MIN_MTU_ALWAYS:
pmtu = IPV6_MIN_MTU;
break;
case IPV6_USE_MIN_MTU_NEVER:
break;
}
} else {
if (ire->ire_type & IRE_MULTICAST)
pmtu = IPV6_MIN_MTU;
}
}
if (!(ixa->ixa_flags & IXAF_IS_IPV4)) {
if ((ire->ire_flags & RTF_MULTIRT) ||
(ixa->ixa_flags & IXAF_MULTIRT_MULTICAST)) {
pmtu -= sizeof (ip6_frag_t);
ixa->ixa_flags |= IXAF_IPV6_ADD_FRAGHDR;
}
}
return (pmtu);
}
mblk_t *
ip_carve_mp(mblk_t **mpp, ssize_t len)
{
mblk_t *mp0;
mblk_t *mp1;
mblk_t *mp2;
if (!len || !mpp || !(mp0 = *mpp))
return (NULL);
if (mp0->b_wptr - mp0->b_rptr > len) {
mp1 = dupb(mp0);
if (mp1) {
mp1->b_wptr = mp1->b_rptr + len;
mp0->b_rptr = mp1->b_wptr;
if (!OK_32PTR(mp0->b_rptr)) {
if (!pullupmsg(mp0, -1)) {
freemsg(mp0);
freemsg(mp1);
*mpp = NULL;
return (NULL);
}
}
}
return (mp1);
}
len -= mp0->b_wptr - mp0->b_rptr;
for (mp2 = mp1 = mp0; (mp2 = mp2->b_cont) != 0 && len; mp1 = mp2) {
if (mp2->b_wptr - mp2->b_rptr > len) {
mp1->b_cont = dupb(mp2);
mp1 = mp1->b_cont;
if (!mp1) {
freemsg(mp0);
freemsg(mp2);
*mpp = NULL;
return (NULL);
}
mp1->b_wptr = mp1->b_rptr + len;
mp2->b_rptr = mp1->b_wptr;
if (!OK_32PTR(mp2->b_rptr)) {
if (!pullupmsg(mp2, -1)) {
freemsg(mp0);
freemsg(mp2);
*mpp = NULL;
return (NULL);
}
}
*mpp = mp2;
return (mp0);
}
len -= mp2->b_wptr - mp2->b_rptr;
}
if (len) {
freemsg(mp0);
*mpp = NULL;
return (NULL);
}
mp1->b_cont = NULL;
*mpp = mp2;
return (mp0);
}
int
ip_modclose(ill_t *ill)
{
boolean_t success;
ipsq_t *ipsq;
ipif_t *ipif;
queue_t *q = ill->ill_rq;
ip_stack_t *ipst = ill->ill_ipst;
int i;
arl_ill_common_t *ai = ill->ill_common;
success = ipsq_enter(ill, B_FALSE, NEW_OP);
ASSERT(success);
ipsq = ill->ill_phyint->phyint_ipsq;
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;
}
cv_broadcast(&ill->ill_cv);
mutex_exit(&ill->ill_lock);
ill_dlpi_send_deferred(ill);
(void) untimeout(ill->ill_frag_timer_id);
(void) ill_frag_timeout(ill, 0);
ill_delete(ill);
mutex_enter(&ill->ill_lock);
while (!ill_is_freeable(ill))
cv_wait(&ill->ill_cv, &ill->ill_lock);
while (ill->ill_waiters)
cv_wait(&ill->ill_cv, &ill->ill_lock);
mutex_exit(&ill->ill_lock);
netstack_hold(ipst->ips_netstack);
ill_delete_tail(ill);
arp_unbind_complete(ill);
ASSERT(ill->ill_ipst == NULL);
ip1dbg(("ip_wsrv: walking\n"));
for (i = 0; i < TX_FANOUT_SIZE; i++) {
conn_walk_drain(ipst, &ipst->ips_idl_tx_list[i]);
}
if (ai != NULL) {
ASSERT(!ill->ill_isv6);
mutex_enter(&ai->ai_lock);
ai->ai_ill = NULL;
if (ai->ai_arl == NULL) {
mutex_destroy(&ai->ai_lock);
kmem_free(ai, sizeof (*ai));
} else {
cv_signal(&ai->ai_ill_unplumb_done);
mutex_exit(&ai->ai_lock);
}
}
mutex_enter(&ipst->ips_ip_mi_lock);
mi_close_unlink(&ipst->ips_ip_g_head, (IDP)ill);
mutex_exit(&ipst->ips_ip_mi_lock);
if (ill->ill_credp != NULL)
crfree(ill->ill_credp);
mutex_destroy(&ill->ill_saved_ire_lock);
mutex_destroy(&ill->ill_lock);
rw_destroy(&ill->ill_mcast_lock);
mutex_destroy(&ill->ill_mcast_serializer);
list_destroy(&ill->ill_nce);
netstack_rele(ipst->ips_netstack);
mi_close_free((IDP)ill);
q->q_ptr = WR(q)->q_ptr = NULL;
ipsq_exit(ipsq);
return (0);
}
void
ip_quiesce_conn(conn_t *connp)
{
boolean_t drain_cleanup_reqd = B_FALSE;
boolean_t conn_ioctl_cleanup_reqd = B_FALSE;
boolean_t ilg_cleanup_reqd = B_FALSE;
ip_stack_t *ipst;
ASSERT(!IPCL_IS_TCP(connp));
ipst = connp->conn_netstack->netstack_ip;
mutex_enter(&connp->conn_lock);
ASSERT(!(connp->conn_state_flags & CONN_QUIESCED));
connp->conn_state_flags |= CONN_CLOSING;
if (connp->conn_idl != NULL)
drain_cleanup_reqd = B_TRUE;
if (connp->conn_oper_pending_ill != NULL)
conn_ioctl_cleanup_reqd = B_TRUE;
if (connp->conn_dhcpinit_ill != NULL) {
ASSERT(connp->conn_dhcpinit_ill->ill_dhcpinit != 0);
atomic_dec_32(&connp->conn_dhcpinit_ill->ill_dhcpinit);
ill_set_inputfn(connp->conn_dhcpinit_ill);
connp->conn_dhcpinit_ill = NULL;
}
if (connp->conn_ilg != NULL)
ilg_cleanup_reqd = B_TRUE;
mutex_exit(&connp->conn_lock);
if (conn_ioctl_cleanup_reqd)
conn_ioctl_cleanup(connp);
if (is_system_labeled() && connp->conn_anon_port) {
(void) tsol_mlp_anon(crgetzone(connp->conn_cred),
connp->conn_mlp_type, connp->conn_proto,
ntohs(connp->conn_lport), B_FALSE);
connp->conn_anon_port = 0;
}
connp->conn_mlp_type = mlptSingle;
ipcl_hash_remove(connp);
if (drain_cleanup_reqd && connp->conn_idl != NULL) {
idl_t *idl = connp->conn_idl;
mutex_enter(&idl->idl_lock);
conn_drain(connp, B_TRUE);
mutex_exit(&idl->idl_lock);
}
if (connp == ipst->ips_ip_g_mrouter)
(void) ip_mrouter_done(ipst);
if (ilg_cleanup_reqd)
ilg_delete_all(connp);
mutex_enter(&connp->conn_lock);
connp->conn_state_flags |= CONN_CONDEMNED;
while (connp->conn_ref != 1)
cv_wait(&connp->conn_cv, &connp->conn_lock);
connp->conn_state_flags |= CONN_QUIESCED;
mutex_exit(&connp->conn_lock);
}
int
ip_close(queue_t *q, int flags, cred_t *credp __unused)
{
conn_t *connp;
if (WR(q)->q_next != NULL) {
return (ip_modclose((ill_t *)q->q_ptr));
}
connp = q->q_ptr;
ip_quiesce_conn(connp);
qprocsoff(q);
ASSERT(connp->conn_ref == 1);
inet_minor_free(connp->conn_minor_arena, connp->conn_dev);
connp->conn_ref--;
ipcl_conn_destroy(connp);
q->q_ptr = WR(q)->q_ptr = NULL;
return (0);
}
static void
ip_conn_input(void *arg1, mblk_t *mp, void *arg2, ip_recv_attr_t *ira)
{
conn_t *connp = (conn_t *)arg1;
putnext(connp->conn_rq, mp);
}
static void
ip_conn_input_icmp(void *arg1, mblk_t *mp, void *arg2, ip_recv_attr_t *ira)
{
freemsg(mp);
}
void
ip_ddi_destroy(void)
{
mutex_enter(&cpu_lock);
unregister_cpu_setup_func(ip_tp_cpu_update, NULL);
mutex_exit(&cpu_lock);
tnet_fini();
icmp_ddi_g_destroy();
rts_ddi_g_destroy();
udp_ddi_g_destroy();
sctp_ddi_g_destroy();
tcp_ddi_g_destroy();
ilb_ddi_g_destroy();
dce_g_destroy();
ipsec_policy_g_destroy();
ipcl_g_destroy();
ip_net_g_destroy();
ip_ire_g_fini();
inet_minor_destroy(ip_minor_arena_sa);
#if defined(_LP64)
inet_minor_destroy(ip_minor_arena_la);
#endif
#ifdef DEBUG
list_destroy(&ip_thread_list);
rw_destroy(&ip_thread_rwlock);
tsd_destroy(&ip_thread_data);
#endif
netstack_unregister(NS_IP);
}
static void
ip_stack_shutdown(netstackid_t stackid, void *arg)
{
ip_stack_t *ipst = (ip_stack_t *)arg;
kt_did_t ktid;
#ifdef NS_DEBUG
printf("ip_stack_shutdown(%p, stack %d)\n", (void *)ipst, stackid);
#endif
ip_interface_cleanup(ipst);
ipv4_hook_shutdown(ipst);
ipv6_hook_shutdown(ipst);
arp_hook_shutdown(ipst);
mutex_enter(&ipst->ips_capab_taskq_lock);
ktid = ipst->ips_capab_taskq_thread->t_did;
ipst->ips_capab_taskq_quit = B_TRUE;
cv_signal(&ipst->ips_capab_taskq_cv);
mutex_exit(&ipst->ips_capab_taskq_lock);
thread_join(ktid);
}
static void
ip_stack_fini(netstackid_t stackid, void *arg)
{
ip_stack_t *ipst = (ip_stack_t *)arg;
int ret;
#ifdef NS_DEBUG
printf("ip_stack_fini(%p, stack %d)\n", (void *)ipst, stackid);
#endif
ipobs_fini(ipst);
ipv4_hook_destroy(ipst);
ipv6_hook_destroy(ipst);
arp_hook_destroy(ipst);
ip_net_destroy(ipst);
ipmp_destroy(ipst);
ip_kstat_fini(stackid, ipst->ips_ip_mibkp);
ipst->ips_ip_mibkp = NULL;
icmp_kstat_fini(stackid, ipst->ips_icmp_mibkp);
ipst->ips_icmp_mibkp = NULL;
ip_kstat2_fini(stackid, ipst->ips_ip_kstat);
ipst->ips_ip_kstat = NULL;
bzero(&ipst->ips_ip_statistics, sizeof (ipst->ips_ip_statistics));
ip6_kstat_fini(stackid, ipst->ips_ip6_kstat);
ipst->ips_ip6_kstat = NULL;
bzero(&ipst->ips_ip6_statistics, sizeof (ipst->ips_ip6_statistics));
kmem_free(ipst->ips_propinfo_tbl,
ip_propinfo_count * sizeof (mod_prop_info_t));
ipst->ips_propinfo_tbl = NULL;
dce_stack_destroy(ipst);
ip_mrouter_stack_destroy(ipst);
mutex_enter(&ipst->ips_igmp_timer_lock);
ipst->ips_igmp_timer_quiesce = B_TRUE;
mutex_exit(&ipst->ips_igmp_timer_lock);
mutex_enter(&ipst->ips_mld_timer_lock);
ipst->ips_mld_timer_quiesce = B_TRUE;
mutex_exit(&ipst->ips_mld_timer_lock);
mutex_enter(&ipst->ips_igmp_slowtimeout_lock);
ipst->ips_igmp_slowtimeout_quiesce = B_TRUE;
mutex_exit(&ipst->ips_igmp_slowtimeout_lock);
mutex_enter(&ipst->ips_mld_slowtimeout_lock);
ipst->ips_mld_slowtimeout_quiesce = B_TRUE;
mutex_exit(&ipst->ips_mld_slowtimeout_lock);
ret = untimeout(ipst->ips_igmp_timeout_id);
if (ret == -1) {
ASSERT(ipst->ips_igmp_timeout_id == 0);
} else {
ASSERT(ipst->ips_igmp_timeout_id != 0);
ipst->ips_igmp_timeout_id = 0;
}
ret = untimeout(ipst->ips_igmp_slowtimeout_id);
if (ret == -1) {
ASSERT(ipst->ips_igmp_slowtimeout_id == 0);
} else {
ASSERT(ipst->ips_igmp_slowtimeout_id != 0);
ipst->ips_igmp_slowtimeout_id = 0;
}
ret = untimeout(ipst->ips_mld_timeout_id);
if (ret == -1) {
ASSERT(ipst->ips_mld_timeout_id == 0);
} else {
ASSERT(ipst->ips_mld_timeout_id != 0);
ipst->ips_mld_timeout_id = 0;
}
ret = untimeout(ipst->ips_mld_slowtimeout_id);
if (ret == -1) {
ASSERT(ipst->ips_mld_slowtimeout_id == 0);
} else {
ASSERT(ipst->ips_mld_slowtimeout_id != 0);
ipst->ips_mld_slowtimeout_id = 0;
}
ip_ire_fini(ipst);
ip6_asp_free(ipst);
conn_drain_fini(ipst);
ipcl_destroy(ipst);
mutex_destroy(&ipst->ips_ndp4->ndp_g_lock);
mutex_destroy(&ipst->ips_ndp6->ndp_g_lock);
kmem_free(ipst->ips_ndp4, sizeof (ndp_g_t));
ipst->ips_ndp4 = NULL;
kmem_free(ipst->ips_ndp6, sizeof (ndp_g_t));
ipst->ips_ndp6 = NULL;
if (ipst->ips_loopback_ksp != NULL) {
kstat_delete_netstack(ipst->ips_loopback_ksp, stackid);
ipst->ips_loopback_ksp = NULL;
}
mutex_destroy(&ipst->ips_capab_taskq_lock);
cv_destroy(&ipst->ips_capab_taskq_cv);
rw_destroy(&ipst->ips_srcid_lock);
mutex_destroy(&ipst->ips_ip_mi_lock);
rw_destroy(&ipst->ips_ill_g_usesrc_lock);
mutex_destroy(&ipst->ips_igmp_timer_lock);
mutex_destroy(&ipst->ips_mld_timer_lock);
mutex_destroy(&ipst->ips_igmp_slowtimeout_lock);
mutex_destroy(&ipst->ips_mld_slowtimeout_lock);
mutex_destroy(&ipst->ips_ip_addr_avail_lock);
rw_destroy(&ipst->ips_ill_g_lock);
kmem_free(ipst->ips_phyint_g_list, sizeof (phyint_list_t));
ipst->ips_phyint_g_list = NULL;
kmem_free(ipst->ips_ill_g_heads, sizeof (ill_g_head_t) * MAX_G_HEADS);
ipst->ips_ill_g_heads = NULL;
ldi_ident_release(ipst->ips_ldi_ident);
kmem_free(ipst, sizeof (*ipst));
}
static void
ip_thread_exit(void *phash)
{
th_hash_t *thh = phash;
rw_enter(&ip_thread_rwlock, RW_WRITER);
list_remove(&ip_thread_list, thh);
rw_exit(&ip_thread_rwlock);
mod_hash_destroy_hash(thh->thh_hash);
kmem_free(thh, sizeof (*thh));
}
void
ip_ddi_init(void)
{
ip_squeue_flag = ip_squeue_switch(ip_squeue_enter);
ip_minor_arena_la = NULL;
ip_minor_arena_sa = NULL;
#if defined(_LP64)
if ((ip_minor_arena_sa = inet_minor_create("ip_minor_arena_sa",
INET_MIN_DEV + 2, MAXMIN32, KM_SLEEP)) == NULL) {
cmn_err(CE_PANIC,
"ip_ddi_init: ip_minor_arena_sa creation failed\n");
}
if ((ip_minor_arena_la = inet_minor_create("ip_minor_arena_la",
MAXMIN32 + 1, MAXMIN64, KM_SLEEP)) == NULL) {
cmn_err(CE_PANIC,
"ip_ddi_init: ip_minor_arena_la creation failed\n");
}
#else
if ((ip_minor_arena_sa = inet_minor_create("ip_minor_arena_sa",
INET_MIN_DEV + 2, MAXMIN, KM_SLEEP)) == NULL) {
cmn_err(CE_PANIC,
"ip_ddi_init: ip_minor_arena_sa creation failed\n");
}
#endif
ip_poll_normal_ticks = MSEC_TO_TICK_ROUNDUP(ip_poll_normal_ms);
ipcl_g_init();
ip_ire_g_init();
ip_net_g_init();
#ifdef DEBUG
tsd_create(&ip_thread_data, ip_thread_exit);
rw_init(&ip_thread_rwlock, NULL, RW_DEFAULT, NULL);
list_create(&ip_thread_list, sizeof (th_hash_t),
offsetof(th_hash_t, thh_link));
#endif
ipsec_policy_g_init();
tcp_ddi_g_init();
sctp_ddi_g_init();
dce_g_init();
netstack_register(NS_IP, ip_stack_init, ip_stack_shutdown,
ip_stack_fini);
tnet_init();
udp_ddi_g_init();
rts_ddi_g_init();
icmp_ddi_g_init();
ilb_ddi_g_init();
mutex_enter(&cpu_lock);
register_cpu_setup_func(ip_tp_cpu_update, NULL);
mutex_exit(&cpu_lock);
}
static void *
ip_stack_init(netstackid_t stackid, netstack_t *ns)
{
ip_stack_t *ipst;
size_t arrsz;
major_t major;
#ifdef NS_DEBUG
printf("ip_stack_init(stack %d)\n", stackid);
#endif
ipst = (ip_stack_t *)kmem_zalloc(sizeof (*ipst), KM_SLEEP);
ipst->ips_netstack = ns;
ipst->ips_ill_g_heads = kmem_zalloc(sizeof (ill_g_head_t) * MAX_G_HEADS,
KM_SLEEP);
ipst->ips_phyint_g_list = kmem_zalloc(sizeof (phyint_list_t),
KM_SLEEP);
ipst->ips_ndp4 = kmem_zalloc(sizeof (ndp_g_t), KM_SLEEP);
ipst->ips_ndp6 = kmem_zalloc(sizeof (ndp_g_t), KM_SLEEP);
mutex_init(&ipst->ips_ndp4->ndp_g_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipst->ips_ndp6->ndp_g_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipst->ips_igmp_timer_lock, NULL, MUTEX_DEFAULT, NULL);
ipst->ips_igmp_deferred_next = INFINITY;
mutex_init(&ipst->ips_mld_timer_lock, NULL, MUTEX_DEFAULT, NULL);
ipst->ips_mld_deferred_next = INFINITY;
mutex_init(&ipst->ips_igmp_slowtimeout_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipst->ips_mld_slowtimeout_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipst->ips_ip_mi_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ipst->ips_ip_addr_avail_lock, NULL, MUTEX_DEFAULT, NULL);
rw_init(&ipst->ips_ill_g_lock, NULL, RW_DEFAULT, NULL);
rw_init(&ipst->ips_ill_g_usesrc_lock, NULL, RW_DEFAULT, NULL);
ipcl_init(ipst);
ip_ire_init(ipst);
ip6_asp_init(ipst);
ipif_init(ipst);
conn_drain_init(ipst);
ip_mrouter_stack_init(ipst);
dce_stack_init(ipst);
ipst->ips_ip_multirt_log_interval = 1000;
ipst->ips_ill_index = 1;
ipst->ips_saved_ip_forwarding = -1;
ipst->ips_reg_vif_num = ALL_VIFS;
arrsz = ip_propinfo_count * sizeof (mod_prop_info_t);
ipst->ips_propinfo_tbl = (mod_prop_info_t *)kmem_alloc(arrsz, KM_SLEEP);
bcopy(ip_propinfo_tbl, ipst->ips_propinfo_tbl, arrsz);
ipst->ips_ip_mibkp = ip_kstat_init(stackid, ipst);
ipst->ips_icmp_mibkp = icmp_kstat_init(stackid);
ipst->ips_ip_kstat = ip_kstat2_init(stackid, &ipst->ips_ip_statistics);
ipst->ips_ip6_kstat =
ip6_kstat_init(stackid, &ipst->ips_ip6_statistics);
ipst->ips_ip_src_id = 1;
rw_init(&ipst->ips_srcid_lock, NULL, RW_DEFAULT, NULL);
ipst->ips_src_generation = SRC_GENERATION_INITIAL;
ip_net_init(ipst, ns);
ipv4_hook_init(ipst);
ipv6_hook_init(ipst);
arp_hook_init(ipst);
ipmp_init(ipst);
ipobs_init(ipst);
mutex_init(&ipst->ips_capab_taskq_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&ipst->ips_capab_taskq_cv, NULL, CV_DEFAULT, NULL);
ipst->ips_capab_taskq_thread = thread_create(NULL, 0,
ill_taskq_dispatch, ipst, 0, &p0, TS_RUN, minclsyspri);
major = mod_name_to_major(INET_NAME);
(void) ldi_ident_from_major(major, &ipst->ips_ldi_ident);
return (ipst);
}
mblk_t *
ip_dlpi_alloc(size_t len, t_uscalar_t prim)
{
mblk_t *mp;
mp = allocb(len, BPRI_MED);
if (!mp)
return (NULL);
if (prim == DL_INFO_REQ) {
mp->b_datap->db_type = M_PCPROTO;
} else {
mp->b_datap->db_type = M_PROTO;
}
mp->b_wptr = mp->b_rptr + len;
bzero(mp->b_rptr, len);
((dl_unitdata_req_t *)mp->b_rptr)->dl_primitive = prim;
return (mp);
}
mblk_t *
ip_dlnotify_alloc(uint_t notification, uint_t data)
{
dl_notify_ind_t *notifyp;
mblk_t *mp;
if ((mp = ip_dlpi_alloc(DL_NOTIFY_IND_SIZE, DL_NOTIFY_IND)) == NULL)
return (NULL);
notifyp = (dl_notify_ind_t *)mp->b_rptr;
notifyp->dl_notification = notification;
notifyp->dl_data = data;
return (mp);
}
mblk_t *
ip_dlnotify_alloc2(uint_t notification, uint_t data1, uint_t data2)
{
dl_notify_ind_t *notifyp;
mblk_t *mp;
if ((mp = ip_dlpi_alloc(DL_NOTIFY_IND_SIZE, DL_NOTIFY_IND)) == NULL)
return (NULL);
notifyp = (dl_notify_ind_t *)mp->b_rptr;
notifyp->dl_notification = notification;
notifyp->dl_data1 = data1;
notifyp->dl_data2 = data2;
return (mp);
}
char *
ip_dot_addr(ipaddr_t addr, char *buf)
{
uint8_t *ap = (uint8_t *)&addr;
(void) mi_sprintf(buf, "%03d.%03d.%03d.%03d",
ap[0] & 0xFF, ap[1] & 0xFF, ap[2] & 0xFF, ap[3] & 0xFF);
return (buf);
}
const char *
mac_colon_addr(const uint8_t *addr, size_t alen, char *buf, size_t buflen)
{
char *bp;
if (alen == 0 || buflen < 4)
return ("?");
bp = buf;
for (;;) {
if ((alen == 2 && buflen < 6) || (alen > 2 && buflen < 7)) {
(void) strcpy(bp, "...");
break;
}
(void) sprintf(bp, "%02x", *addr++);
bp += 2;
if (--alen == 0)
break;
*bp++ = ':';
buflen -= 3;
}
return (buf);
}
void
ip_fanout_send_icmp_v4(mblk_t *mp, uint_t icmp_type, uint_t icmp_code,
ip_recv_attr_t *ira)
{
ipha_t *ipha;
boolean_t secure;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
netstack_t *ns = ipst->ips_netstack;
ipsec_stack_t *ipss = ns->netstack_ipsec;
secure = ira->ira_flags & IRAF_IPSEC_SECURE;
ipha = (ipha_t *)mp->b_rptr;
if (secure || ipss->ipsec_inbound_v4_policy_present) {
mp = ipsec_check_global_policy(mp, NULL, ipha, NULL, ira, ns);
if (mp == NULL)
return;
}
if (ira->ira_protocol == IPPROTO_ICMP ||
ira->ira_protocol == IPPROTO_IGMP) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ip_fanout_send_icmp_v4", mp, ill);
freemsg(mp);
return;
}
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
switch (icmp_type) {
case ICMP_DEST_UNREACHABLE:
switch (icmp_code) {
case ICMP_PROTOCOL_UNREACHABLE:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInUnknownProtos);
ip_drop_input("ipIfStatsInUnknownProtos", mp, ill);
break;
case ICMP_PORT_UNREACHABLE:
BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts);
ip_drop_input("ipIfStatsNoPorts", mp, ill);
break;
}
icmp_unreachable(mp, icmp_code, ira);
break;
default:
#ifdef DEBUG
panic("ip_fanout_send_icmp_v4: wrong type");
#else
freemsg(mp);
break;
#endif
}
}
void
ip_proto_not_sup(mblk_t *mp, ip_recv_attr_t *ira)
{
ipha_t *ipha;
ipha = (ipha_t *)mp->b_rptr;
if (ira->ira_flags & IRAF_IS_IPV4) {
ASSERT(IPH_HDR_VERSION(ipha) == IP_VERSION);
ip_fanout_send_icmp_v4(mp, ICMP_DEST_UNREACHABLE,
ICMP_PROTOCOL_UNREACHABLE, ira);
} else {
ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
ip_fanout_send_icmp_v6(mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_NEXTHEADER, ira);
}
}
void
ip_fanout_proto_conn(conn_t *connp, mblk_t *mp, ipha_t *ipha, ip6_t *ip6h,
ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
boolean_t secure;
uint_t protocol = ira->ira_protocol;
iaflags_t iraflags = ira->ira_flags;
queue_t *rq;
secure = iraflags & IRAF_IPSEC_SECURE;
rq = connp->conn_rq;
if (IPCL_IS_NONSTR(connp) ? connp->conn_flow_cntrld : !canputnext(rq)) {
switch (protocol) {
case IPPROTO_ICMPV6:
BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInOverflows);
break;
case IPPROTO_ICMP:
BUMP_MIB(&ipst->ips_icmp_mib, icmpInOverflows);
break;
default:
BUMP_MIB(ill->ill_ip_mib, rawipIfStatsInOverflows);
break;
}
freemsg(mp);
return;
}
ASSERT(!(IPCL_IS_IPTUN(connp)));
if (connp->conn_min_ttl != 0 && connp->conn_min_ttl > ira->ira_ttl) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return;
}
if (((iraflags & IRAF_IS_IPV4) ?
CONN_INBOUND_POLICY_PRESENT(connp, ipss) :
CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss)) ||
secure) {
mp = ipsec_check_inbound_policy(mp, connp, ipha,
ip6h, ira);
if (mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
return;
}
}
if (iraflags & IRAF_ICMP_ERROR) {
(connp->conn_recvicmp)(connp, mp, NULL, ira);
} else {
ill_t *rill = ira->ira_rill;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInDelivers);
ira->ira_ill = ira->ira_rill = NULL;
(connp->conn_recv)(connp, mp, NULL, ira);
ira->ira_ill = ill;
ira->ira_rill = rill;
}
}
void
ip_fanout_proto_v4(mblk_t *mp, ipha_t *ipha, ip_recv_attr_t *ira)
{
mblk_t *mp1;
ipaddr_t laddr;
conn_t *connp, *first_connp, *next_connp;
connf_t *connfp;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
laddr = ipha->ipha_dst;
connfp = &ipst->ips_ipcl_proto_fanout_v4[ira->ira_protocol];
mutex_enter(&connfp->connf_lock);
connp = connfp->connf_head;
for (connp = connfp->connf_head; connp != NULL;
connp = connp->conn_next) {
if (IPCL_PROTO_MATCH(connp, ira, ipha) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV4_VERSION, ira, connp))) {
break;
}
}
if (connp == NULL) {
mutex_exit(&connfp->connf_lock);
ip_fanout_send_icmp_v4(mp, ICMP_DEST_UNREACHABLE,
ICMP_PROTOCOL_UNREACHABLE, ira);
return;
}
ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL);
CONN_INC_REF(connp);
first_connp = connp;
connp = connp->conn_next;
for (;;) {
while (connp != NULL) {
if (IPCL_PROTO_MATCH(connp, ira, ipha) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV4_VERSION,
ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
connp = first_connp;
break;
}
if (((mp1 = dupmsg(mp)) == NULL) &&
((mp1 = copymsg(mp)) == NULL)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
connp = first_connp;
break;
}
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
ip_fanout_proto_conn(connp, mp1, (ipha_t *)mp1->b_rptr, NULL,
ira);
mutex_enter(&connfp->connf_lock);
next_connp = connp->conn_next;
CONN_DEC_REF(connp);
connp = next_connp;
}
mutex_exit(&connfp->connf_lock);
ip_fanout_proto_conn(connp, mp, ipha, NULL, ira);
CONN_DEC_REF(connp);
}
mblk_t *
zero_spi_check(mblk_t *mp, ip_recv_attr_t *ira)
{
int shift, plen, iph_len;
ipha_t *ipha;
udpha_t *udpha;
uint32_t *spi;
uint32_t esp_ports;
uint8_t *orptr;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
ipha = (ipha_t *)mp->b_rptr;
iph_len = ira->ira_ip_hdr_length;
plen = ira->ira_pktlen;
if (plen - iph_len - sizeof (udpha_t) < sizeof (uint32_t)) {
ip_drop_packet(mp, B_TRUE, ira->ira_ill,
DROPPER(ipss, ipds_esp_nat_t_ka), &ipss->ipsec_dropper);
return (NULL);
}
if (MBLKL(mp) < iph_len + sizeof (udpha_t) + sizeof (*spi)) {
if (!pullupmsg(mp, -1)) {
ip_drop_packet(mp, B_TRUE, ira->ira_ill,
DROPPER(ipss, ipds_esp_nomem),
&ipss->ipsec_dropper);
return (NULL);
}
ipha = (ipha_t *)mp->b_rptr;
}
spi = (uint32_t *)(mp->b_rptr + iph_len + sizeof (udpha_t));
if (*spi == 0) {
shift = sizeof (uint32_t);
} else {
ipha->ipha_protocol = IPPROTO_ESP;
shift = sizeof (udpha_t);
}
ira->ira_pktlen = (plen - shift);
ipha->ipha_length = htons(ira->ira_pktlen);
ipha->ipha_hdr_checksum = 0;
orptr = mp->b_rptr;
mp->b_rptr += shift;
udpha = (udpha_t *)(orptr + iph_len);
if (*spi == 0) {
ASSERT((uint8_t *)ipha == orptr);
udpha->uha_length = htons(plen - shift - iph_len);
iph_len += sizeof (udpha_t);
esp_ports = 0;
} else {
esp_ports = *((uint32_t *)udpha);
ASSERT(esp_ports != 0);
}
ovbcopy(orptr, orptr + shift, iph_len);
if (esp_ports != 0) {
ipha = (ipha_t *)(orptr + shift);
ira->ira_flags |= IRAF_ESP_UDP_PORTS;
ira->ira_esp_udp_ports = esp_ports;
ip_fanout_v4(mp, ipha, ira);
return (NULL);
}
return (mp);
}
void
ip_fanout_udp_conn(conn_t *connp, mblk_t *mp, ipha_t *ipha, ip6_t *ip6h,
ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
boolean_t secure;
iaflags_t iraflags = ira->ira_flags;
secure = iraflags & IRAF_IPSEC_SECURE;
if (connp->conn_min_ttl != 0 && connp->conn_min_ttl > ira->ira_ttl) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return;
}
if (IPCL_IS_NONSTR(connp) ? connp->conn_flow_cntrld :
!canputnext(connp->conn_rq)) {
BUMP_MIB(ill->ill_ip_mib, udpIfStatsInOverflows);
freemsg(mp);
return;
}
if (((iraflags & IRAF_IS_IPV4) ?
CONN_INBOUND_POLICY_PRESENT(connp, ipss) :
CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss)) ||
secure) {
mp = ipsec_check_inbound_policy(mp, connp, ipha,
ip6h, ira);
if (mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
return;
}
}
if (ira->ira_flags & IRAF_ICMP_ERROR) {
(connp->conn_recvicmp)(connp, mp, NULL, ira);
} else {
ill_t *rill = ira->ira_rill;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInDelivers);
ira->ira_ill = ira->ira_rill = NULL;
(connp->conn_recv)(connp, mp, NULL, ira);
ira->ira_ill = ill;
ira->ira_rill = rill;
}
}
void
ip_fanout_udp_multi_v4(mblk_t *mp, ipha_t *ipha, uint16_t lport, uint16_t fport,
ip_recv_attr_t *ira)
{
ipaddr_t laddr;
in6_addr_t v6faddr;
conn_t *connp;
connf_t *connfp;
ipaddr_t faddr;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ASSERT(ira->ira_flags & (IRAF_MULTIBROADCAST|IRAF_ICMP_ERROR));
laddr = ipha->ipha_dst;
faddr = ipha->ipha_src;
connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)];
mutex_enter(&connfp->connf_lock);
connp = connfp->connf_head;
while (connp != NULL) {
if ((IPCL_UDP_MATCH(connp, lport, laddr, fport, faddr)) &&
conn_wantpacket(connp, ira, ipha) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV4_VERSION, ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL)
goto notfound;
CONN_INC_REF(connp);
if (connp->conn_reuseaddr) {
conn_t *first_connp = connp;
conn_t *next_connp;
mblk_t *mp1;
connp = connp->conn_next;
for (;;) {
while (connp != NULL) {
if (IPCL_UDP_MATCH(connp, lport, laddr,
fport, faddr) &&
conn_wantpacket(connp, ira, ipha) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV4_VERSION,
ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
connp = first_connp;
break;
}
if (((mp1 = dupmsg(mp)) == NULL) &&
((mp1 = copymsg(mp)) == NULL)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
connp = first_connp;
break;
}
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
IP_STAT(ipst, ip_udp_fanmb);
ip_fanout_udp_conn(connp, mp1, (ipha_t *)mp1->b_rptr,
NULL, ira);
mutex_enter(&connfp->connf_lock);
next_connp = connp->conn_next;
CONN_DEC_REF(connp);
connp = next_connp;
}
}
mutex_exit(&connfp->connf_lock);
IP_STAT(ipst, ip_udp_fanmb);
ip_fanout_udp_conn(connp, mp, ipha, NULL, ira);
CONN_DEC_REF(connp);
return;
notfound:
mutex_exit(&connfp->connf_lock);
IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &v6faddr);
connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)];
mutex_enter(&connfp->connf_lock);
connp = connfp->connf_head;
while (connp != NULL) {
if (IPCL_UDP_MATCH_V6(connp, lport, ipv6_all_zeros,
fport, v6faddr) &&
conn_wantpacket(connp, ira, ipha) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV4_VERSION, ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
mutex_exit(&connfp->connf_lock);
if (ipst->ips_ipcl_proto_fanout_v4[IPPROTO_UDP].connf_head !=
NULL) {
ASSERT(ira->ira_protocol == IPPROTO_UDP);
ip_fanout_proto_v4(mp, ipha, ira);
} else {
BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts);
freemsg(mp);
}
return;
}
CONN_INC_REF(connp);
ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL);
if (connp->conn_reuseaddr) {
conn_t *first_connp = connp;
conn_t *next_connp;
mblk_t *mp1;
connp = connp->conn_next;
for (;;) {
while (connp != NULL) {
if (IPCL_UDP_MATCH_V6(connp, lport,
ipv6_all_zeros, fport, v6faddr) &&
conn_wantpacket(connp, ira, ipha) &&
(!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
tsol_receive_local(mp, &laddr, IPV4_VERSION,
ira, connp)))
break;
connp = connp->conn_next;
}
if (connp == NULL) {
connp = first_connp;
break;
}
if (((mp1 = dupmsg(mp)) == NULL) &&
((mp1 = copymsg(mp)) == NULL)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
connp = first_connp;
break;
}
CONN_INC_REF(connp);
mutex_exit(&connfp->connf_lock);
IP_STAT(ipst, ip_udp_fanmb);
ip_fanout_udp_conn(connp, mp1, (ipha_t *)mp1->b_rptr,
NULL, ira);
mutex_enter(&connfp->connf_lock);
next_connp = connp->conn_next;
CONN_DEC_REF(connp);
connp = next_connp;
}
}
mutex_exit(&connfp->connf_lock);
IP_STAT(ipst, ip_udp_fanmb);
ip_fanout_udp_conn(connp, mp, ipha, NULL, ira);
CONN_DEC_REF(connp);
}
int
ip_find_hdr_v4(ipha_t *ipha, ip_pkt_t *ipp, boolean_t allocate)
{
uchar_t *opt;
uint32_t totallen;
uint32_t optval;
uint32_t optlen;
ipp->ipp_fields |= IPPF_HOPLIMIT | IPPF_TCLASS | IPPF_ADDR;
ipp->ipp_hoplimit = ipha->ipha_ttl;
ipp->ipp_type_of_service = ipha->ipha_type_of_service;
IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &ipp->ipp_addr);
totallen = ipha->ipha_version_and_hdr_length -
(uint8_t)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS);
if (totallen == 0) {
if (!allocate)
return (0);
if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
kmem_free(ipp->ipp_ipv4_options,
ipp->ipp_ipv4_options_len);
ipp->ipp_ipv4_options = NULL;
ipp->ipp_ipv4_options_len = 0;
ipp->ipp_fields &= ~IPPF_IPV4_OPTIONS;
}
if (ipp->ipp_fields & IPPF_LABEL_V4) {
kmem_free(ipp->ipp_label_v4, ipp->ipp_label_len_v4);
ipp->ipp_label_v4 = NULL;
ipp->ipp_label_len_v4 = 0;
ipp->ipp_fields &= ~IPPF_LABEL_V4;
}
return (0);
}
totallen <<= 2;
opt = (uchar_t *)&ipha[1];
if (!is_system_labeled()) {
copyall:
if (!allocate) {
if (totallen != 0) {
ipp->ipp_ipv4_options = opt;
ipp->ipp_ipv4_options_len = totallen;
ipp->ipp_fields |= IPPF_IPV4_OPTIONS;
}
return (0);
}
if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
if (totallen == ipp->ipp_ipv4_options_len) {
bcopy(opt, ipp->ipp_ipv4_options, totallen);
return (0);
}
kmem_free(ipp->ipp_ipv4_options,
ipp->ipp_ipv4_options_len);
ipp->ipp_ipv4_options = NULL;
ipp->ipp_ipv4_options_len = 0;
ipp->ipp_fields &= ~IPPF_IPV4_OPTIONS;
}
if (totallen == 0)
return (0);
ipp->ipp_ipv4_options = kmem_alloc(totallen, KM_NOSLEEP);
if (ipp->ipp_ipv4_options == NULL)
return (ENOMEM);
ipp->ipp_ipv4_options_len = totallen;
ipp->ipp_fields |= IPPF_IPV4_OPTIONS;
bcopy(opt, ipp->ipp_ipv4_options, totallen);
return (0);
}
if (allocate && (ipp->ipp_fields & IPPF_LABEL_V4)) {
kmem_free(ipp->ipp_label_v4, ipp->ipp_label_len_v4);
ipp->ipp_label_v4 = NULL;
ipp->ipp_label_len_v4 = 0;
ipp->ipp_fields &= ~IPPF_LABEL_V4;
}
while (totallen != 0) {
switch (optval = opt[IPOPT_OPTVAL]) {
case IPOPT_EOL:
return (0);
case IPOPT_NOP:
optlen = 1;
break;
default:
if (totallen <= IPOPT_OLEN)
return (EINVAL);
optlen = opt[IPOPT_OLEN];
if (optlen < 2)
return (EINVAL);
}
if (optlen > totallen)
return (EINVAL);
switch (optval) {
case IPOPT_COMSEC:
if (!allocate) {
ipp->ipp_label_v4 = opt;
ipp->ipp_label_len_v4 = optlen;
ipp->ipp_fields |= IPPF_LABEL_V4;
} else {
ipp->ipp_label_v4 = kmem_alloc(optlen,
KM_NOSLEEP);
if (ipp->ipp_label_v4 == NULL)
return (ENOMEM);
ipp->ipp_label_len_v4 = optlen;
ipp->ipp_fields |= IPPF_LABEL_V4;
bcopy(opt, ipp->ipp_label_v4, optlen);
}
totallen -= optlen;
opt += optlen;
while ((totallen & 3) != 0 && opt[0] == IPOPT_NOP) {
totallen--;
opt++;
}
goto copyall;
}
totallen -= optlen;
opt += optlen;
}
totallen = ipha->ipha_version_and_hdr_length -
(uint8_t)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS);
totallen <<= 2;
opt = (uchar_t *)&ipha[1];
goto copyall;
}
uint_t
ip_type_v4(ipaddr_t addr, ip_stack_t *ipst)
{
ire_t *ire;
uint_t result;
ire = ire_ftable_lookup_simple_v4(addr, 0, ipst, NULL);
ASSERT(ire != NULL);
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))
result = IRE_NOROUTE;
else
result = ire->ire_type;
ire_refrele(ire);
return (result);
}
uint_t
ip_type_v6(const in6_addr_t *addr, ip_stack_t *ipst)
{
ire_t *ire;
uint_t result;
ire = ire_ftable_lookup_simple_v6(addr, 0, ipst, NULL);
ASSERT(ire != NULL);
if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))
result = IRE_NOROUTE;
else
result = ire->ire_type;
ire_refrele(ire);
return (result);
}
static int
ip_lrput(queue_t *q, mblk_t *mp)
{
switch (mp->b_datap->db_type) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
*mp->b_rptr &= ~FLUSHR;
qreply(q, mp);
return (0);
}
break;
}
freemsg(mp);
return (0);
}
int
ip_lwput(queue_t *q, mblk_t *mp)
{
freemsg(mp);
return (0);
}
ipaddr_t
ip_massage_options(ipha_t *ipha, netstack_t *ns)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
int i;
ip_stack_t *ipst = ns->netstack_ip;
ip2dbg(("ip_massage_options\n"));
dst = ipha->ipha_dst;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
opt = opts.ipoptp_cur;
switch (optval) {
uint8_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg(("ip_massage_options: bad src route\n"));
break;
}
optlen = opts.ipoptp_len;
off = opt[IPOPT_OFFSET];
off--;
redo_srr:
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg(("ip_massage_options: end of SR\n"));
break;
}
bcopy((char *)opt + off, &dst, IP_ADDR_LEN);
ip1dbg(("ip_massage_options: next hop 0x%x\n",
ntohl(dst)));
if (ip_type_v4(dst, ipst) == IRE_LOCAL) {
off += IP_ADDR_LEN;
goto redo_srr;
}
if (dst == htonl(INADDR_LOOPBACK)) {
ip1dbg(("ip_massage_options: loopback addr in "
"source route!\n"));
break;
}
ipha->ipha_dst = dst;
off = ((optlen - IP_ADDR_LEN - 3) & ~(IP_ADDR_LEN-1)) +
3;
bcopy(&opt[off], &dst, IP_ADDR_LEN);
ip1dbg(("ip_massage_options: last hop 0x%x\n",
ntohl(dst)));
opt[IP_ADDR_LEN] = opt[0];
opt[IP_ADDR_LEN+1] = opt[IPOPT_OLEN] - IP_ADDR_LEN;
opt[IP_ADDR_LEN+2] = opt[IPOPT_OFFSET];
for (i = 0; i < IP_ADDR_LEN; i++)
opt[i] = IPOPT_NOP;
break;
}
}
return (dst);
}
ipaddr_t
ip_net_mask(ipaddr_t addr)
{
uchar_t *up = (uchar_t *)&addr;
ipaddr_t mask = 0;
uchar_t *maskp = (uchar_t *)&mask;
#if defined(__x86)
#define TOTALLY_BRAIN_DAMAGED_C_COMPILER
#endif
#ifdef TOTALLY_BRAIN_DAMAGED_C_COMPILER
maskp[0] = maskp[1] = maskp[2] = maskp[3] = 0;
#endif
if (CLASSD(addr)) {
maskp[0] = 0xF0;
return (mask);
}
if (CLASSE(addr))
return (0xffffffffU);
if (addr == 0)
return (0);
maskp[0] = 0xFF;
if ((up[0] & 0x80) == 0)
return (mask);
maskp[1] = 0xFF;
if ((up[0] & 0xC0) == 0x80)
return (mask);
maskp[2] = 0xFF;
if ((up[0] & 0xE0) == 0xC0)
return (mask);
return ((ipaddr_t)0);
}
char *
ip_nv_lookup(nv_t *nv, int value)
{
if (!nv)
return (NULL);
for (; nv->nv_name; nv++) {
if (nv->nv_value == value)
return (nv->nv_name);
}
return ("unknown");
}
static int
ip_wait_for_info_ack(ill_t *ill)
{
int err;
mutex_enter(&ill->ill_lock);
while (ill->ill_state_flags & ILL_LL_SUBNET_PENDING) {
err = cv_wait_sig(&ill->ill_cv, &ill->ill_lock);
if (err == 0) {
mutex_exit(&ill->ill_lock);
return (EINTR);
}
}
mutex_exit(&ill->ill_lock);
return (ill->ill_error);
}
static int
ip_modopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
ill_t *ill;
int err;
zoneid_t zoneid;
netstack_t *ns;
ip_stack_t *ipst;
if (secpolicy_net_rawaccess(credp) != 0)
return (EPERM);
ns = netstack_find_by_cred(credp);
ASSERT(ns != NULL);
ipst = ns->netstack_ip;
ASSERT(ipst != NULL);
if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
zoneid = GLOBAL_ZONEID;
else
zoneid = crgetzoneid(credp);
ill = (ill_t *)mi_open_alloc_sleep(sizeof (ill_t));
q->q_ptr = WR(q)->q_ptr = ill;
ill->ill_ipst = ipst;
ill->ill_zoneid = zoneid;
err = ill_init(q, ill);
if (err != 0) {
mi_free(ill);
netstack_rele(ipst->ips_netstack);
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
return (err);
}
ipsq_exit(ill->ill_phyint->phyint_ipsq);
err = ip_wait_for_info_ack(ill);
if (err == 0)
ill->ill_credp = credp;
else
goto fail;
crhold(credp);
mutex_enter(&ipst->ips_ip_mi_lock);
err = mi_open_link(&ipst->ips_ip_g_head, (IDP)q->q_ptr, devp, flag,
sflag, credp);
mutex_exit(&ipst->ips_ip_mi_lock);
fail:
if (err) {
(void) ip_close(q, 0, credp);
return (err);
}
return (0);
}
int
ip_openv4(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
return (ip_open(q, devp, flag, sflag, credp, B_FALSE));
}
int
ip_openv6(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
return (ip_open(q, devp, flag, sflag, credp, B_TRUE));
}
int
ip_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp,
boolean_t isv6)
{
conn_t *connp;
major_t maj;
zoneid_t zoneid;
netstack_t *ns;
ip_stack_t *ipst;
if (q->q_ptr != NULL)
return (0);
if (sflag & MODOPEN) {
return (ip_modopen(q, devp, flag, sflag, credp));
}
if ((flag & ~(FKLYR)) == IP_HELPER_STR) {
return (ip_helper_stream_setup(q, devp, flag, sflag,
credp, isv6));
}
ns = netstack_find_by_cred(credp);
ASSERT(ns != NULL);
ipst = ns->netstack_ip;
ASSERT(ipst != NULL);
if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
zoneid = GLOBAL_ZONEID;
else
zoneid = crgetzoneid(credp);
connp = ipcl_conn_create(IPCL_IPCCONN, KM_SLEEP, ipst->ips_netstack);
netstack_rele(ipst->ips_netstack);
connp->conn_ixa->ixa_flags |= IXAF_MULTICAST_LOOP | IXAF_SET_ULP_CKSUM;
connp->conn_ixa->ixa_zoneid = zoneid;
connp->conn_zoneid = zoneid;
connp->conn_rq = q;
q->q_ptr = WR(q)->q_ptr = connp;
if (isv6) {
connp->conn_family = AF_INET6;
connp->conn_ipversion = IPV6_VERSION;
connp->conn_ixa->ixa_flags &= ~IXAF_IS_IPV4;
connp->conn_ixa->ixa_src_preferences = IPV6_PREFER_SRC_DEFAULT;
} else {
connp->conn_family = AF_INET;
connp->conn_ipversion = IPV4_VERSION;
connp->conn_ixa->ixa_flags |= IXAF_IS_IPV4;
}
if ((ip_minor_arena_la != NULL) && (flag & SO_SOCKSTR) &&
((connp->conn_dev = inet_minor_alloc(ip_minor_arena_la)) != 0)) {
connp->conn_minor_arena = ip_minor_arena_la;
} else {
if ((connp->conn_dev =
inet_minor_alloc(ip_minor_arena_sa)) == 0) {
q->q_ptr = WR(q)->q_ptr = NULL;
CONN_DEC_REF(connp);
return (EBUSY);
}
connp->conn_minor_arena = ip_minor_arena_sa;
}
maj = getemajor(*devp);
*devp = makedevice(maj, (minor_t)connp->conn_dev);
connp->conn_cred = credp;
connp->conn_cpid = curproc->p_pid;
ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED));
connp->conn_ixa->ixa_cred = connp->conn_cred;
connp->conn_ixa->ixa_cpid = connp->conn_cpid;
if (is_system_labeled())
connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred);
connp->conn_recv = ip_conn_input;
connp->conn_recvicmp = ip_conn_input_icmp;
crhold(connp->conn_cred);
if (getpflags(NET_MAC_AWARE, credp) != 0)
connp->conn_mac_mode = CONN_MAC_AWARE;
connp->conn_zone_is_global = (crgetzoneid(credp) == GLOBAL_ZONEID);
connp->conn_rq = q;
connp->conn_wq = WR(q);
connp->conn_ixa->ixa_flags |= IXAF_MULTICAST_LOOP;
ASSERT(connp->conn_ref == 1);
mutex_enter(&connp->conn_lock);
connp->conn_state_flags &= ~CONN_INCIPIENT;
mutex_exit(&connp->conn_lock);
qprocson(q);
return (0);
}
int
ipsec_set_req(cred_t *cr, conn_t *connp, ipsec_req_t *req)
{
uint_t ah_req = 0;
uint_t esp_req = 0;
uint_t se_req = 0;
ipsec_act_t *actp = NULL;
uint_t nact;
ipsec_policy_head_t *ph;
boolean_t is_pol_reset, is_pol_inserted = B_FALSE;
int error = 0;
netstack_t *ns = connp->conn_netstack;
ip_stack_t *ipst = ns->netstack_ip;
ipsec_stack_t *ipss = ns->netstack_ipsec;
#define REQ_MASK (IPSEC_PREF_REQUIRED|IPSEC_PREF_NEVER)
if (req == NULL)
return (EINVAL);
ah_req = req->ipsr_ah_req;
esp_req = req->ipsr_esp_req;
se_req = req->ipsr_self_encap_req;
if (se_req != 0 && esp_req == 0 && ah_req == 0)
return (EINVAL);
is_pol_reset = ((ah_req & REQ_MASK) == 0 &&
(esp_req & REQ_MASK) == 0 &&
(se_req & REQ_MASK) == 0);
if (!is_pol_reset) {
mutex_enter(&ipss->ipsec_loader_lock);
if (ipss->ipsec_loader_state != IPSEC_LOADER_SUCCEEDED) {
mutex_exit(&ipss->ipsec_loader_lock);
return (EPROTONOSUPPORT);
}
mutex_exit(&ipss->ipsec_loader_lock);
if ((ah_req & ~(REQ_MASK|IPSEC_PREF_UNIQUE)) != 0 ||
(esp_req & ~(REQ_MASK|IPSEC_PREF_UNIQUE)) != 0 ||
(se_req & ~(REQ_MASK|IPSEC_PREF_UNIQUE)) != 0) {
return (EINVAL);
}
if (((ah_req & IPSEC_PREF_NEVER) ||
(esp_req & IPSEC_PREF_NEVER) ||
(se_req & IPSEC_PREF_NEVER)) &&
secpolicy_ip_config(cr, B_FALSE) != 0) {
return (EPERM);
}
if (((ah_req & REQ_MASK) == REQ_MASK) ||
((esp_req & REQ_MASK) == REQ_MASK) ||
((se_req & REQ_MASK) == REQ_MASK)) {
return (EINVAL);
}
}
ASSERT(MUTEX_HELD(&connp->conn_lock));
if (connp->conn_policy_cached) {
return (EINVAL);
}
if (is_pol_reset) {
if (connp->conn_policy != NULL) {
IPPH_REFRELE(connp->conn_policy, ipst->ips_netstack);
connp->conn_policy = NULL;
}
connp->conn_in_enforce_policy = B_FALSE;
connp->conn_out_enforce_policy = B_FALSE;
return (0);
}
ph = connp->conn_policy = ipsec_polhead_split(connp->conn_policy,
ipst->ips_netstack);
if (ph == NULL)
goto enomem;
ipsec_actvec_from_req(req, &actp, &nact, ipst->ips_netstack);
if (actp == NULL)
goto enomem;
if (!ipsec_polhead_insert(ph, actp, nact, IPSEC_AF_V4,
IPSEC_TYPE_INBOUND, ns))
goto enomem;
is_pol_inserted = B_TRUE;
if (!ipsec_polhead_insert(ph, actp, nact, IPSEC_AF_V4,
IPSEC_TYPE_OUTBOUND, ns))
goto enomem;
if (connp->conn_family == AF_INET6) {
if (!ipsec_polhead_insert(ph, actp, nact, IPSEC_AF_V6,
IPSEC_TYPE_INBOUND, ns))
goto enomem;
if (!ipsec_polhead_insert(ph, actp, nact, IPSEC_AF_V6,
IPSEC_TYPE_OUTBOUND, ns))
goto enomem;
}
ipsec_actvec_free(actp, nact);
if ((ah_req & REQ_MASK) != 0 ||
(esp_req & REQ_MASK) != 0 ||
(se_req & REQ_MASK) != 0) {
connp->conn_in_enforce_policy = B_TRUE;
connp->conn_out_enforce_policy = B_TRUE;
}
return (error);
#undef REQ_MASK
enomem:
if (actp != NULL)
ipsec_actvec_free(actp, nact);
if (is_pol_inserted)
ipsec_polhead_flush(ph, ns);
return (ENOMEM);
}
int
ip_opt_set_multicast_group(conn_t *connp, t_scalar_t name,
uchar_t *invalp, boolean_t inet6, boolean_t checkonly)
{
int *i1 = (int *)invalp;
int error = 0;
ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
struct ip_mreq *v4_mreqp;
struct ipv6_mreq *v6_mreqp;
struct group_req *greqp;
ire_t *ire;
boolean_t done = B_FALSE;
ipaddr_t ifaddr;
in6_addr_t v6group;
uint_t ifindex;
boolean_t mcast_opt = B_TRUE;
mcast_record_t fmode;
int (*optfn)(conn_t *, boolean_t, const in6_addr_t *,
ipaddr_t, uint_t, mcast_record_t, const in6_addr_t *);
switch (name) {
case IP_ADD_MEMBERSHIP:
case IPV6_JOIN_GROUP:
mcast_opt = B_FALSE;
case MCAST_JOIN_GROUP:
fmode = MODE_IS_EXCLUDE;
optfn = ip_opt_add_group;
break;
case IP_DROP_MEMBERSHIP:
case IPV6_LEAVE_GROUP:
mcast_opt = B_FALSE;
case MCAST_LEAVE_GROUP:
fmode = MODE_IS_INCLUDE;
optfn = ip_opt_delete_group;
break;
default:
fmode = MODE_IS_INCLUDE;
optfn = NULL;
ASSERT(0);
}
if (mcast_opt) {
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
greqp = (struct group_req *)i1;
if (greqp->gr_group.ss_family == AF_INET) {
sin = (struct sockaddr_in *)&(greqp->gr_group);
IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &v6group);
} else {
if (!inet6)
return (EINVAL);
sin6 = (struct sockaddr_in6 *)&(greqp->gr_group);
v6group = sin6->sin6_addr;
}
ifaddr = INADDR_ANY;
ifindex = greqp->gr_interface;
} else if (inet6) {
v6_mreqp = (struct ipv6_mreq *)i1;
v6group = v6_mreqp->ipv6mr_multiaddr;
ifaddr = INADDR_ANY;
ifindex = v6_mreqp->ipv6mr_interface;
} else {
v4_mreqp = (struct ip_mreq *)i1;
IN6_INADDR_TO_V4MAPPED(&v4_mreqp->imr_multiaddr, &v6group);
ifaddr = (ipaddr_t)v4_mreqp->imr_interface.s_addr;
ifindex = 0;
}
if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
ipaddr_t group;
IN6_V4MAPPED_TO_IPADDR(&v6group, group);
ire = ire_ftable_lookup_v4(group, IP_HOST_MASK, 0,
IRE_HOST | IRE_INTERFACE, NULL, ALL_ZONES, NULL,
MATCH_IRE_MASK | MATCH_IRE_TYPE, 0, ipst, NULL);
} else {
ire = ire_ftable_lookup_v6(&v6group, &ipv6_all_ones, 0,
IRE_HOST | IRE_INTERFACE, NULL, ALL_ZONES, NULL,
MATCH_IRE_MASK | MATCH_IRE_TYPE, 0, ipst, NULL);
}
if (ire != NULL) {
if (ire->ire_flags & RTF_MULTIRT) {
error = ip_multirt_apply_membership(optfn, ire, connp,
checkonly, &v6group, fmode, &ipv6_all_zeros);
done = B_TRUE;
}
ire_refrele(ire);
}
if (!done) {
error = optfn(connp, checkonly, &v6group, ifaddr, ifindex,
fmode, &ipv6_all_zeros);
}
return (error);
}
int
ip_opt_set_multicast_sources(conn_t *connp, t_scalar_t name,
uchar_t *invalp, boolean_t inet6, boolean_t checkonly)
{
int *i1 = (int *)invalp;
int error = 0;
ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
struct ip_mreq_source *imreqp;
struct group_source_req *gsreqp;
in6_addr_t v6group, v6src;
uint32_t ifindex;
ipaddr_t ifaddr;
boolean_t mcast_opt = B_TRUE;
mcast_record_t fmode;
ire_t *ire;
boolean_t done = B_FALSE;
int (*optfn)(conn_t *, boolean_t, const in6_addr_t *,
ipaddr_t, uint_t, mcast_record_t, const in6_addr_t *);
switch (name) {
case IP_BLOCK_SOURCE:
mcast_opt = B_FALSE;
case MCAST_BLOCK_SOURCE:
fmode = MODE_IS_EXCLUDE;
optfn = ip_opt_add_group;
break;
case IP_UNBLOCK_SOURCE:
mcast_opt = B_FALSE;
case MCAST_UNBLOCK_SOURCE:
fmode = MODE_IS_EXCLUDE;
optfn = ip_opt_delete_group;
break;
case IP_ADD_SOURCE_MEMBERSHIP:
mcast_opt = B_FALSE;
case MCAST_JOIN_SOURCE_GROUP:
fmode = MODE_IS_INCLUDE;
optfn = ip_opt_add_group;
break;
case IP_DROP_SOURCE_MEMBERSHIP:
mcast_opt = B_FALSE;
case MCAST_LEAVE_SOURCE_GROUP:
fmode = MODE_IS_INCLUDE;
optfn = ip_opt_delete_group;
break;
default:
optfn = NULL;
fmode = 0;
ASSERT(0);
}
if (mcast_opt) {
gsreqp = (struct group_source_req *)i1;
ifindex = gsreqp->gsr_interface;
if (gsreqp->gsr_group.ss_family == AF_INET) {
struct sockaddr_in *s;
s = (struct sockaddr_in *)&gsreqp->gsr_group;
IN6_INADDR_TO_V4MAPPED(&s->sin_addr, &v6group);
s = (struct sockaddr_in *)&gsreqp->gsr_source;
IN6_INADDR_TO_V4MAPPED(&s->sin_addr, &v6src);
} else {
struct sockaddr_in6 *s6;
if (!inet6)
return (EINVAL);
s6 = (struct sockaddr_in6 *)&gsreqp->gsr_group;
v6group = s6->sin6_addr;
s6 = (struct sockaddr_in6 *)&gsreqp->gsr_source;
v6src = s6->sin6_addr;
}
ifaddr = INADDR_ANY;
} else {
imreqp = (struct ip_mreq_source *)i1;
IN6_INADDR_TO_V4MAPPED(&imreqp->imr_multiaddr, &v6group);
IN6_INADDR_TO_V4MAPPED(&imreqp->imr_sourceaddr, &v6src);
ifaddr = (ipaddr_t)imreqp->imr_interface.s_addr;
ifindex = 0;
}
if (IN6_IS_ADDR_V4MAPPED_ANY(&v6src))
v6src = ipv6_all_zeros;
if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
ipaddr_t group;
IN6_V4MAPPED_TO_IPADDR(&v6group, group);
ire = ire_ftable_lookup_v4(group, IP_HOST_MASK, 0,
IRE_HOST | IRE_INTERFACE, NULL, ALL_ZONES, NULL,
MATCH_IRE_MASK | MATCH_IRE_TYPE, 0, ipst, NULL);
} else {
ire = ire_ftable_lookup_v6(&v6group, &ipv6_all_ones, 0,
IRE_HOST | IRE_INTERFACE, NULL, ALL_ZONES, NULL,
MATCH_IRE_MASK | MATCH_IRE_TYPE, 0, ipst, NULL);
}
if (ire != NULL) {
if (ire->ire_flags & RTF_MULTIRT) {
error = ip_multirt_apply_membership(optfn, ire, connp,
checkonly, &v6group, fmode, &v6src);
done = B_TRUE;
}
ire_refrele(ire);
}
if (!done) {
error = optfn(connp, checkonly, &v6group, ifaddr, ifindex,
fmode, &v6src);
}
return (error);
}
int
ip_fill_mtuinfo(conn_t *connp, ip_xmit_attr_t *ixa, struct ip6_mtuinfo *mtuinfo)
{
uint32_t pmtu = IP_MAXPACKET;
uint_t scopeid;
if (IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6))
return (-1);
if (ixa->ixa_ire != NULL)
pmtu = ip_get_pmtu(ixa);
if (ixa->ixa_flags & IXAF_SCOPEID_SET)
scopeid = ixa->ixa_scopeid;
else
scopeid = 0;
bzero(mtuinfo, sizeof (*mtuinfo));
mtuinfo->ip6m_addr.sin6_family = AF_INET6;
mtuinfo->ip6m_addr.sin6_port = connp->conn_fport;
mtuinfo->ip6m_addr.sin6_addr = connp->conn_faddr_v6;
mtuinfo->ip6m_addr.sin6_scope_id = scopeid;
mtuinfo->ip6m_mtu = pmtu;
return (sizeof (struct ip6_mtuinfo));
}
void
ip_ire_rebind_walker(ire_t *ire, void *notused)
{
if (!ire->ire_unbound || ire->ire_ill != NULL)
return;
ire_rebind(ire);
ire_delete(ire);
}
void
ip_ire_unbind_walker(ire_t *ire, void *notused)
{
ire_t *new_ire;
if (!ire->ire_unbound || ire->ire_ill == NULL)
return;
if (ire->ire_ipversion == IPV6_VERSION) {
new_ire = ire_create_v6(&ire->ire_addr_v6, &ire->ire_mask_v6,
&ire->ire_gateway_addr_v6, ire->ire_type, NULL,
ire->ire_zoneid, ire->ire_flags, NULL, ire->ire_ipst);
} else {
new_ire = ire_create((uchar_t *)&ire->ire_addr,
(uchar_t *)&ire->ire_mask,
(uchar_t *)&ire->ire_gateway_addr, ire->ire_type, NULL,
ire->ire_zoneid, ire->ire_flags, NULL, ire->ire_ipst);
}
if (new_ire == NULL)
return;
new_ire->ire_unbound = B_TRUE;
ire_delete(ire);
new_ire = ire_add(new_ire);
if (new_ire != NULL)
ire_refrele(new_ire);
}
void
conn_ire_revalidate(conn_t *connp, void *arg)
{
boolean_t isv6 = (boolean_t)arg;
if ((isv6 && connp->conn_ipversion != IPV6_VERSION) ||
(!isv6 && connp->conn_ipversion != IPV4_VERSION))
return;
connp->conn_ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
int
ip_reassemble(mblk_t *mp, ipf_t *ipf, uint_t start, boolean_t more, ill_t *ill,
size_t msg_len)
{
uint_t end;
mblk_t *next_mp;
mblk_t *mp1;
uint_t offset;
boolean_t incr_dups = B_TRUE;
boolean_t offset_zero_seen = B_FALSE;
boolean_t pkt_boundary_checked = B_FALSE;
ASSERT(start != 0 || ipf->ipf_nf_hdr_len != 0);
ipf->ipf_count += msg_len;
if (ipf->ipf_end) {
offset = 0;
for (mp1 = ipf->ipf_mp->b_cont; mp1; mp1 = mp1->b_cont) {
IP_REASS_SET_START(mp1, offset);
if (offset == 0) {
ASSERT(ipf->ipf_nf_hdr_len != 0);
offset = -ipf->ipf_nf_hdr_len;
}
offset += mp1->b_wptr - mp1->b_rptr;
IP_REASS_SET_END(mp1, offset);
}
ipf->ipf_hole_cnt = 1;
ipf->ipf_end = 0;
}
do {
end = start + (mp->b_wptr - mp->b_rptr);
if (start == 0 && !offset_zero_seen) {
ASSERT(ipf->ipf_nf_hdr_len != 0);
end -= ipf->ipf_nf_hdr_len;
offset_zero_seen = B_TRUE;
}
next_mp = mp->b_cont;
if (start == end && start != 0 && ipf->ipf_nf_hdr_len != 0) {
IP_REASS_SET_START(mp, 0);
IP_REASS_SET_END(mp, 0);
if (ipf->ipf_mp->b_cont == mp)
ipf->ipf_mp->b_cont = next_mp;
freeb(mp);
continue;
}
mp->b_cont = NULL;
IP_REASS_SET_START(mp, start);
IP_REASS_SET_END(mp, end);
if (!ipf->ipf_tail_mp) {
ipf->ipf_tail_mp = mp;
ipf->ipf_mp->b_cont = mp;
if (start == 0 || !more) {
ipf->ipf_hole_cnt = 1;
if (next_mp)
ipf->ipf_hole_cnt++;
} else
ipf->ipf_hole_cnt = 2;
continue;
} else if (ipf->ipf_last_frag_seen && !more &&
!pkt_boundary_checked) {
if (start + msgdsize(mp) !=
IP_REASS_END(ipf->ipf_tail_mp)) {
return (IP_REASS_FAILED);
}
pkt_boundary_checked = B_TRUE;
}
offset = IP_REASS_END(ipf->ipf_tail_mp);
if (start >= offset) {
if (ipf->ipf_last_frag_seen) {
return (IP_REASS_FAILED);
}
ipf->ipf_tail_mp->b_cont = mp;
ipf->ipf_tail_mp = mp;
if (more) {
if (start != offset)
ipf->ipf_hole_cnt++;
} else if (start == offset && next_mp == NULL)
ipf->ipf_hole_cnt--;
continue;
}
mp1 = ipf->ipf_mp->b_cont;
offset = IP_REASS_START(mp1);
if (start < offset) {
if (start == 0) {
if (end >= offset) {
ipf->ipf_hole_cnt--;
}
} else if (end < offset) {
ipf->ipf_hole_cnt++;
}
mp->b_cont = mp1;
while (end > offset) {
if (end < IP_REASS_END(mp1)) {
mp->b_wptr -= end - offset;
IP_REASS_SET_END(mp, offset);
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsReasmPartDups);
break;
}
if ((mp1->b_cont &&
IP_REASS_END(mp1) !=
IP_REASS_START(mp1->b_cont) &&
end >= IP_REASS_START(mp1->b_cont)) ||
(!ipf->ipf_last_frag_seen && !more)) {
ipf->ipf_hole_cnt--;
}
if ((mp->b_cont = mp1->b_cont) == NULL) {
ipf->ipf_tail_mp = mp;
}
IP_REASS_SET_START(mp1, 0);
IP_REASS_SET_END(mp1, 0);
ipf->ipf_count -= mp1->b_datap->db_lim -
mp1->b_datap->db_base;
freeb(mp1);
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsReasmPartDups);
mp1 = mp->b_cont;
if (!mp1)
break;
offset = IP_REASS_START(mp1);
}
ipf->ipf_mp->b_cont = mp;
continue;
}
for (; mp1; mp1 = mp1->b_cont) {
offset = IP_REASS_END(mp1);
if (start < offset) {
if (end <= offset) {
IP_REASS_SET_START(mp, 0);
IP_REASS_SET_END(mp, 0);
ipf->ipf_count -= mp->b_datap->db_lim -
mp->b_datap->db_base;
if (incr_dups) {
ipf->ipf_num_dups++;
incr_dups = B_FALSE;
}
freeb(mp);
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsReasmDuplicates);
break;
}
IP_REASS_SET_START(mp, offset);
mp->b_rptr += offset - start;
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsReasmPartDups);
start = offset;
if (!mp1->b_cont) {
mp1->b_cont = mp;
ipf->ipf_tail_mp = mp;
if (!more) {
ipf->ipf_hole_cnt--;
}
break;
}
}
if (start >= IP_REASS_START(mp1->b_cont))
continue;
if (start > offset)
ipf->ipf_hole_cnt++;
mp->b_cont = mp1->b_cont;
mp1->b_cont = mp;
mp1 = mp->b_cont;
offset = IP_REASS_START(mp1);
if (end >= offset) {
ipf->ipf_hole_cnt--;
while (end > offset) {
if (end < IP_REASS_END(mp1)) {
mp->b_wptr -= end - offset;
IP_REASS_SET_END(mp, offset);
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsReasmPartDups);
break;
}
if ((mp1->b_cont &&
IP_REASS_END(mp1)
!= IP_REASS_START(mp1->b_cont) &&
end >=
IP_REASS_START(mp1->b_cont)) ||
(!ipf->ipf_last_frag_seen &&
!more)) {
ipf->ipf_hole_cnt--;
}
if ((mp->b_cont = mp1->b_cont) ==
NULL) {
ipf->ipf_tail_mp = mp;
}
IP_REASS_SET_START(mp1, 0);
IP_REASS_SET_END(mp1, 0);
ipf->ipf_count -=
mp1->b_datap->db_lim -
mp1->b_datap->db_base;
freeb(mp1);
BUMP_MIB(ill->ill_ip_mib,
ipIfStatsReasmPartDups);
mp1 = mp->b_cont;
if (!mp1)
break;
offset = IP_REASS_START(mp1);
}
}
break;
}
} while (start = end, mp = next_mp);
if (!more)
ipf->ipf_last_frag_seen = B_TRUE;
if (ipf->ipf_hole_cnt)
return (IP_REASS_PARTIAL);
for (mp1 = ipf->ipf_mp->b_cont; mp1; mp1 = mp1->b_cont) {
IP_REASS_SET_START(mp1, 0);
IP_REASS_SET_END(mp1, 0);
}
return (IP_REASS_COMPLETE);
}
mblk_t *
ip_input_fragment(mblk_t *mp, ipha_t *ipha, ip_recv_attr_t *ira)
{
uint32_t frag_offset_flags;
mblk_t *t_mp;
ipaddr_t dst;
uint8_t proto = ipha->ipha_protocol;
uint32_t sum_val;
uint16_t sum_flags;
ipf_t *ipf;
ipf_t **ipfp;
ipfb_t *ipfb;
uint16_t ident;
uint32_t offset;
ipaddr_t src;
uint_t hdr_length;
uint32_t end;
mblk_t *mp1;
mblk_t *tail_mp;
size_t count;
size_t msg_len;
uint8_t ecn_info = 0;
uint32_t packet_size;
boolean_t pruned = B_FALSE;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
if (ipst->ips_ip_reass_queue_bytes == 0) {
freemsg(mp);
return (NULL);
}
if ((frag_offset_flags = ntohs(ipha->ipha_fragment_offset_and_flags) &
(IPH_MF | IPH_OFFSET)) == 0)
return (mp);
ASSERT(ira->ira_rill != NULL);
if (proto == IPPROTO_UDP && dohwcksum &&
ILL_HCKSUM_CAPABLE(ira->ira_rill) &&
(DB_CKSUMFLAGS(mp) & (HCK_FULLCKSUM | HCK_PARTIALCKSUM))) {
mblk_t *mp1 = mp->b_cont;
int32_t len;
sum_val = (uint32_t)DB_CKSUM16(mp);
sum_flags = DB_CKSUMFLAGS(mp);
offset = ((uchar_t *)ipha + IPH_HDR_LENGTH(ipha)) - mp->b_rptr;
if ((sum_flags & HCK_PARTIALCKSUM) &&
(mp1 == NULL || mp1->b_cont == NULL) &&
offset >= DB_CKSUMSTART(mp) &&
((len = offset - DB_CKSUMSTART(mp)) & 1) == 0) {
uint32_t adj;
IP_ADJCKSUM_PARTIAL(mp->b_rptr + DB_CKSUMSTART(mp),
mp, mp1, len, adj);
if (adj >= sum_val)
sum_val = ~(adj - sum_val) & 0xFFFF;
else
sum_val -= adj;
}
} else {
sum_val = 0;
sum_flags = 0;
}
DB_CKSUMFLAGS(mp) = 0;
ident = ipha->ipha_ident;
offset = (frag_offset_flags << 3) & 0xFFFF;
src = ipha->ipha_src;
dst = ipha->ipha_dst;
hdr_length = IPH_HDR_LENGTH(ipha);
end = ntohs(ipha->ipha_length) - hdr_length;
if (end == 0) {
freemsg(mp);
return (NULL);
}
ecn_info = (ipha->ipha_type_of_service & 0x3);
if (offset != 0) {
mp->b_rptr += hdr_length;
end += offset;
}
if (mp->b_datap->db_ref > 2)
msg_len = 0;
else
msg_len = MBLKSIZE(mp);
tail_mp = mp;
while (tail_mp->b_cont != NULL) {
tail_mp = tail_mp->b_cont;
if (tail_mp->b_datap->db_ref <= 2)
msg_len += MBLKSIZE(tail_mp);
}
if ((msg_len + sizeof (*ipf) + ill->ill_frag_count) >=
ipst->ips_ip_reass_queue_bytes) {
DTRACE_PROBE3(ip_reass_queue_bytes, uint_t, msg_len,
uint_t, ill->ill_frag_count,
uint_t, ipst->ips_ip_reass_queue_bytes);
ill_frag_prune(ill,
(ipst->ips_ip_reass_queue_bytes < msg_len) ? 0 :
(ipst->ips_ip_reass_queue_bytes - msg_len));
pruned = B_TRUE;
}
ipfb = &ill->ill_frag_hash_tbl[ILL_FRAG_HASH(src, ident)];
mutex_enter(&ipfb->ipfb_lock);
ipfp = &ipfb->ipfb_ipf;
for (;;) {
ipf = ipfp[0];
if (ipf != NULL) {
if (ipf->ipf_ident == ident &&
ipf->ipf_src == src &&
ipf->ipf_dst == dst &&
ipf->ipf_protocol == proto) {
if (ipf->ipf_num_dups > ip_max_frag_dups) {
ill_frag_free_pkts(ill, ipfb, ipf, 1);
freemsg(mp);
mutex_exit(&ipfb->ipfb_lock);
return (NULL);
}
break;
}
ipfp = &ipf->ipf_hash_next;
continue;
}
if (pruned && offset != 0) {
mutex_exit(&ipfb->ipfb_lock);
freemsg(mp);
return (NULL);
}
if (ipfb->ipfb_frag_pkts >= MAX_FRAG_PKTS(ipst)) {
ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf, 1);
}
mp1 = allocb(sizeof (*ipf), BPRI_MED);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
reass_done:
mutex_exit(&ipfb->ipfb_lock);
return (NULL);
}
BUMP_MIB(ill->ill_ip_mib, ipIfStatsReasmReqds);
mp1->b_cont = mp;
ipf = (ipf_t *)mp1->b_rptr;
ipf->ipf_mp = mp1;
ipf->ipf_ptphn = ipfp;
ipfp[0] = ipf;
ipf->ipf_hash_next = NULL;
ipf->ipf_ident = ident;
ipf->ipf_protocol = proto;
ipf->ipf_src = src;
ipf->ipf_dst = dst;
ipf->ipf_nf_hdr_len = 0;
ipf->ipf_timestamp = gethrestime_sec();
ipf->ipf_gen = ill->ill_ipf_gen++;
ipf->ipf_count = MBLKSIZE(mp1);
ipf->ipf_last_frag_seen = B_FALSE;
ipf->ipf_ecn = ecn_info;
ipf->ipf_num_dups = 0;
ipfb->ipfb_frag_pkts++;
ipf->ipf_checksum = 0;
ipf->ipf_checksum_flags = 0;
if (sum_flags != 0) {
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
ipf->ipf_checksum = sum_val;
ipf->ipf_checksum_flags = sum_flags;
}
if (offset == 0) {
ipf->ipf_count += msg_len;
ipf->ipf_tail_mp = tail_mp;
ipf->ipf_end = end;
ipf->ipf_nf_hdr_len = hdr_length;
} else {
ipf->ipf_tail_mp = NULL;
ipf->ipf_end = 0;
ipf->ipf_checksum_flags = 0;
(void) ip_reassemble(mp, ipf,
(frag_offset_flags & IPH_OFFSET) << 3,
(frag_offset_flags & IPH_MF), ill, msg_len);
}
ipfb->ipfb_count += ipf->ipf_count;
ASSERT(ipfb->ipfb_count > 0);
atomic_add_32(&ill->ill_frag_count, ipf->ipf_count);
mutex_enter(&ill->ill_lock);
ill_frag_timer_start(ill);
mutex_exit(&ill->ill_lock);
goto reass_done;
}
if (sum_flags != 0 && sum_flags == ipf->ipf_checksum_flags) {
sum_val += ipf->ipf_checksum;
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
ipf->ipf_checksum = sum_val;
} else if (ipf->ipf_checksum_flags != 0) {
ipf->ipf_checksum_flags = 0;
}
if (ecn_info != IPH_ECN_NECT && ipf->ipf_ecn != IPH_ECN_NECT) {
if (ecn_info == IPH_ECN_CE)
ipf->ipf_ecn = IPH_ECN_CE;
} else {
ipf->ipf_ecn = IPH_ECN_NECT;
}
if (offset && ipf->ipf_end == offset) {
ipf->ipf_tail_mp->b_cont = mp;
ipf->ipf_count += msg_len;
ipfb->ipfb_count += msg_len;
ASSERT(ipfb->ipfb_count > 0);
atomic_add_32(&ill->ill_frag_count, msg_len);
if (frag_offset_flags & IPH_MF) {
ipf->ipf_end = end;
ipf->ipf_tail_mp = tail_mp;
goto reass_done;
}
} else {
int ret;
if (offset == 0)
ipf->ipf_nf_hdr_len = hdr_length;
count = ipf->ipf_count;
ret = ip_reassemble(mp, ipf,
(frag_offset_flags & IPH_OFFSET) << 3,
(frag_offset_flags & IPH_MF), ill, msg_len);
count = ipf->ipf_count - count;
if (count) {
ipfb->ipfb_count += count;
ASSERT(ipfb->ipfb_count > 0);
atomic_add_32(&ill->ill_frag_count, count);
}
if (ret == IP_REASS_PARTIAL) {
goto reass_done;
} else if (ret == IP_REASS_FAILED) {
ill_frag_free_pkts(ill, ipfb, ipf, 1);
for (t_mp = mp; t_mp != NULL; t_mp = t_mp->b_cont) {
IP_REASS_SET_START(t_mp, 0);
IP_REASS_SET_END(t_mp, 0);
}
freemsg(mp);
goto reass_done;
}
}
ecn_info = ipf->ipf_ecn;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsReasmOKs);
ipfp = ipf->ipf_ptphn;
if ((sum_flags = ipf->ipf_checksum_flags) != 0)
sum_val = ipf->ipf_checksum;
else
sum_val = 0;
mp1 = ipf->ipf_mp;
count = ipf->ipf_count;
ipf = ipf->ipf_hash_next;
if (ipf != NULL)
ipf->ipf_ptphn = ipfp;
ipfp[0] = ipf;
atomic_add_32(&ill->ill_frag_count, -count);
ASSERT(ipfb->ipfb_count >= count);
ipfb->ipfb_count -= count;
ipfb->ipfb_frag_pkts--;
mutex_exit(&ipfb->ipfb_lock);
mp = mp1->b_cont;
freeb(mp1);
packet_size = (uint32_t)msgdsize(mp);
if (packet_size > IP_MAXPACKET) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("Reassembled packet too large", mp, ill);
freemsg(mp);
return (NULL);
}
if (DB_REF(mp) > 1) {
mblk_t *mp2 = copymsg(mp);
if (mp2 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return (NULL);
}
freemsg(mp);
mp = mp2;
}
ipha = (ipha_t *)mp->b_rptr;
ipha->ipha_length = htons((uint16_t)packet_size);
ipha->ipha_fragment_offset_and_flags = 0;
ipha->ipha_type_of_service &= 0xFC;
ipha->ipha_type_of_service |= ecn_info;
ira->ira_pktlen = packet_size;
ira->ira_ip_hdr_length = IPH_HDR_LENGTH(ipha);
DB_CKSUM16(mp) = (uint16_t)sum_val;
DB_CKSUMFLAGS(mp) = sum_flags;
DB_CKSUMSTART(mp) = ira->ira_ip_hdr_length;
return (mp);
}
void *
ip_pullup(mblk_t *mp, ssize_t len, ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
if (ip_rput_pullups++ == 0) {
(void) mi_strlog(ill->ill_rq, 1, SL_ERROR|SL_TRACE,
"ip_pullup: %s forced us to "
" pullup pkt, hdr len %ld, hdr addr %p",
ill->ill_name, len, (void *)mp->b_rptr);
}
if (!(ira->ira_flags & IRAF_L2SRC_SET))
ip_setl2src(mp, ira, ira->ira_rill);
ASSERT(ira->ira_flags & IRAF_L2SRC_SET);
if (!pullupmsg(mp, len))
return (NULL);
else
return (mp->b_rptr);
}
void
ip_setl2src(mblk_t *mp, ip_recv_attr_t *ira, ill_t *ill)
{
const uchar_t *addr;
int alen;
if (ira->ira_flags & IRAF_L2SRC_SET)
return;
ASSERT(ill != NULL);
alen = ill->ill_phys_addr_length;
ASSERT(alen <= sizeof (ira->ira_l2src));
if (ira->ira_mhip != NULL &&
(addr = ira->ira_mhip->mhi_saddr) != NULL) {
bcopy(addr, ira->ira_l2src, alen);
} else if ((ira->ira_flags & IRAF_L2SRC_LOOPBACK) &&
(addr = ill->ill_phys_addr) != NULL) {
bcopy(addr, ira->ira_l2src, alen);
} else {
bzero(ira->ira_l2src, alen);
}
ira->ira_flags |= IRAF_L2SRC_SET;
}
mblk_t *
ip_check_and_align_header(mblk_t *mp, uint_t min_size, ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ssize_t len;
len = MBLKL(mp);
if (!OK_32PTR(mp->b_rptr))
IP_STAT(ill->ill_ipst, ip_notaligned);
else
IP_STAT(ill->ill_ipst, ip_recv_pullup);
if (len < 0) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
freemsg(mp);
return (NULL);
}
if (len == 0) {
mblk_t *mp1 = mp->b_cont;
if (!(ira->ira_flags & IRAF_L2SRC_SET))
ip_setl2src(mp, ira, ira->ira_rill);
ASSERT(ira->ira_flags & IRAF_L2SRC_SET);
freeb(mp);
mp = mp1;
if (mp == NULL)
return (NULL);
if (OK_32PTR(mp->b_rptr) && MBLKL(mp) >= min_size)
return (mp);
}
if (ip_pullup(mp, min_size, ira) == NULL) {
if (msgdsize(mp) < min_size) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
} else {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
}
freemsg(mp);
return (NULL);
}
return (mp);
}
mblk_t *
ip_check_length(mblk_t *mp, uchar_t *rptr, ssize_t len, uint_t pkt_len,
uint_t min_size, ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
if (mp->b_cont == NULL) {
if (pkt_len < min_size) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
freemsg(mp);
return (NULL);
}
if (len < 0) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
freemsg(mp);
return (NULL);
}
mp->b_wptr = rptr + pkt_len;
} else if ((len += msgdsize(mp->b_cont)) != 0) {
ASSERT(pkt_len >= min_size);
if (pkt_len < min_size) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
freemsg(mp);
return (NULL);
}
if (len < 0) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
freemsg(mp);
return (NULL);
}
(void) adjmsg(mp, -len);
DB_CKSUMFLAGS(mp) = 0;
IP_STAT(ill->ill_ipst, ip_multimblk);
}
return (mp);
}
mblk_t *
ip_check_optlen(mblk_t *mp, ipha_t *ipha, uint_t opt_len, uint_t pkt_len,
ip_recv_attr_t *ira)
{
ill_t *ill = ira->ira_ill;
ssize_t len;
if (IPH_HDR_VERSION(ipha) != IPV4_VERSION) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInWrongIPVersion);
ip_drop_input("IPvN packet on IPv4 ill", mp, ill);
freemsg(mp);
return (NULL);
}
if (opt_len > (15 - IP_SIMPLE_HDR_LENGTH_IN_WORDS)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
freemsg(mp);
return (NULL);
}
len = ((size_t)opt_len + IP_SIMPLE_HDR_LENGTH_IN_WORDS) << 2;
if (len > (mp->b_wptr - mp->b_rptr)) {
if (len > pkt_len) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
freemsg(mp);
return (NULL);
}
if (ip_pullup(mp, len, ira) == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return (NULL);
}
}
return (mp);
}
ire_t *
ip_check_multihome(void *addr, ire_t *ire, ill_t *ill)
{
ire_t *new_ire;
ill_t *ire_ill;
uint_t ifindex;
ip_stack_t *ipst = ill->ill_ipst;
boolean_t strict_check = B_FALSE;
ASSERT(ire->ire_ill != NULL);
if (IS_IN_SAME_ILLGRP(ill, ire->ire_ill))
return (ire);
if (ire->ire_ipversion == IPV4_VERSION) {
if (ipst->ips_ip_strict_dst_multihoming)
strict_check = B_TRUE;
new_ire = ire_ftable_lookup_v4(*((ipaddr_t *)addr), 0, 0,
IRE_LOCAL, ill, ALL_ZONES, NULL,
(MATCH_IRE_TYPE|MATCH_IRE_ILL), 0, ipst, NULL);
} else {
ASSERT(!IN6_IS_ADDR_MULTICAST((in6_addr_t *)addr));
if (ipst->ips_ipv6_strict_dst_multihoming)
strict_check = B_TRUE;
new_ire = ire_ftable_lookup_v6((in6_addr_t *)addr, NULL, NULL,
IRE_LOCAL, ill, ALL_ZONES, NULL,
(MATCH_IRE_TYPE|MATCH_IRE_ILL), 0, ipst, NULL);
}
if (new_ire != NULL) {
if (new_ire != ire)
return (new_ire);
ire_refrele(new_ire);
return (ire);
}
ASSERT(ire->ire_ill != NULL);
ire_ill = ire->ire_ill;
ifindex = ill->ill_usesrc_ifindex;
if (ifindex != 0 && ifindex == ire_ill->ill_phyint->phyint_ifindex) {
return (ire);
}
if (!(strict_check))
return (ire);
if ((ill->ill_flags & ire->ire_ill->ill_flags & ILLF_ROUTER) != 0) {
return (ire);
}
return (NULL);
}
void
ip_dlur_to_mhi(ill_t *ill, mblk_t *mb, struct mac_header_info_s *mhip)
{
dl_unitdata_ind_t *ind = (dl_unitdata_ind_t *)mb->b_rptr;
mblk_t *bmp;
uint_t extra_offset;
bzero(mhip, sizeof (struct mac_header_info_s));
mhip->mhi_dsttype = MAC_ADDRTYPE_UNICAST;
if (ill->ill_sap_length < 0)
extra_offset = 0;
else
extra_offset = ill->ill_sap_length;
mhip->mhi_daddr = (uchar_t *)ind + ind->dl_dest_addr_offset +
extra_offset;
mhip->mhi_saddr = (uchar_t *)ind + ind->dl_src_addr_offset +
extra_offset;
if (!ind->dl_group_address)
return;
mhip->mhi_dsttype = MAC_ADDRTYPE_MULTICAST;
if (ind->dl_dest_addr_offset > sizeof (*ind) &&
ind->dl_dest_addr_offset + ind->dl_dest_addr_length < MBLKL(mb) &&
(bmp = ill->ill_bcast_mp) != NULL) {
dl_unitdata_req_t *dlur;
uint8_t *bphys_addr;
dlur = (dl_unitdata_req_t *)bmp->b_rptr;
bphys_addr = (uchar_t *)dlur + dlur->dl_dest_addr_offset +
extra_offset;
if (bcmp(mhip->mhi_daddr, bphys_addr,
ind->dl_dest_addr_length) == 0)
mhip->mhi_dsttype = MAC_ADDRTYPE_BROADCAST;
}
}
void
ip_mdata_to_mhi(ill_t *ill, mblk_t *mp, struct mac_header_info_s *mhip)
{
mblk_t *bmp;
struct ether_header *pether;
bzero(mhip, sizeof (struct mac_header_info_s));
mhip->mhi_dsttype = MAC_ADDRTYPE_UNICAST;
pether = (struct ether_header *)((char *)mp->b_rptr
- sizeof (struct ether_header));
if (ill->ill_type != IFT_ETHER)
return;
retry:
if ((uchar_t *)pether < mp->b_datap->db_base)
return;
if (ill->ill_isv6) {
if (pether->ether_type != htons(ETHERTYPE_IPV6)) {
pether = (struct ether_header *)((char *)pether - 4);
goto retry;
}
} else {
if (pether->ether_type != htons(ETHERTYPE_IP)) {
pether = (struct ether_header *)((char *)pether - 4);
goto retry;
}
}
mhip->mhi_daddr = (uchar_t *)&pether->ether_dhost;
mhip->mhi_saddr = (uchar_t *)&pether->ether_shost;
if (!(mhip->mhi_daddr[0] & 0x01))
return;
mhip->mhi_dsttype = MAC_ADDRTYPE_MULTICAST;
if ((bmp = ill->ill_bcast_mp) != NULL) {
dl_unitdata_req_t *dlur;
uint8_t *bphys_addr;
uint_t addrlen;
dlur = (dl_unitdata_req_t *)bmp->b_rptr;
addrlen = dlur->dl_dest_addr_length;
if (ill->ill_sap_length < 0) {
bphys_addr = (uchar_t *)dlur +
dlur->dl_dest_addr_offset;
addrlen += ill->ill_sap_length;
} else {
bphys_addr = (uchar_t *)dlur +
dlur->dl_dest_addr_offset +
ill->ill_sap_length;
addrlen -= ill->ill_sap_length;
}
if (bcmp(mhip->mhi_daddr, bphys_addr, addrlen) == 0)
mhip->mhi_dsttype = MAC_ADDRTYPE_BROADCAST;
}
}
void
ip_rput_notdata(ill_t *ill, mblk_t *mp)
{
mblk_t *first_mp;
struct iocblk *iocp;
struct mac_header_info_s mhi;
switch (DB_TYPE(mp)) {
case M_PROTO:
case M_PCPROTO: {
if (((dl_unitdata_ind_t *)mp->b_rptr)->dl_primitive !=
DL_UNITDATA_IND) {
ip_rput_dlpi(ill, mp);
return;
}
first_mp = mp;
mp = first_mp->b_cont;
first_mp->b_cont = NULL;
if (mp == NULL) {
freeb(first_mp);
return;
}
ip_dlur_to_mhi(ill, first_mp, &mhi);
if (ill->ill_isv6)
ip_input_v6(ill, NULL, mp, &mhi);
else
ip_input(ill, NULL, mp, &mhi);
freeb(first_mp);
return;
}
case M_IOCACK:
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case DL_IOC_HDR_INFO:
ill_fastpath_ack(ill, mp);
return;
default:
putnext(ill->ill_rq, mp);
return;
}
case M_ERROR:
case M_HANGUP:
mutex_enter(&ill->ill_lock);
if (ill->ill_state_flags & ILL_CONDEMNED) {
mutex_exit(&ill->ill_lock);
freemsg(mp);
return;
}
ill_refhold_locked(ill);
mutex_exit(&ill->ill_lock);
qwriter_ip(ill, ill->ill_rq, mp, ip_rput_other, CUR_OP,
B_FALSE);
return;
case M_CTL:
putnext(ill->ill_rq, mp);
return;
case M_IOCNAK:
ip1dbg(("got iocnak "));
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case DL_IOC_HDR_INFO:
ip_rput_other(NULL, ill->ill_rq, mp, NULL);
return;
default:
break;
}
default:
putnext(ill->ill_rq, mp);
return;
}
}
int
ip_rput(queue_t *q, mblk_t *mp)
{
ill_t *ill;
union DL_primitives *dl;
ill = (ill_t *)q->q_ptr;
if (ill->ill_state_flags & (ILL_CONDEMNED | ILL_LL_SUBNET_PENDING)) {
dl = (union DL_primitives *)mp->b_rptr;
if (DB_TYPE(mp) != M_PCPROTO ||
dl->dl_primitive == DL_UNITDATA_IND) {
inet_freemsg(mp);
return (0);
}
}
if (DB_TYPE(mp) == M_DATA) {
struct mac_header_info_s mhi;
ip_mdata_to_mhi(ill, mp, &mhi);
ip_input(ill, NULL, mp, &mhi);
} else {
ip_rput_notdata(ill, mp);
}
return (0);
}
mblk_t *
ip_fix_dbref(mblk_t *mp, ip_recv_attr_t *ira)
{
mblk_t *mp1;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
IP_STAT(ipst, ip_db_ref);
if (!(ira->ira_flags & IRAF_L2SRC_SET))
ip_setl2src(mp, ira, ira->ira_rill);
mp1 = copymsg(mp);
if (mp1 == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return (NULL);
}
if (DB_CKSUMFLAGS(mp) != 0) {
DB_CKSUMFLAGS(mp1) = DB_CKSUMFLAGS(mp);
DB_CKSUMSTART(mp1) = DB_CKSUMSTART(mp);
DB_CKSUMSTUFF(mp1) = DB_CKSUMSTUFF(mp);
DB_CKSUMEND(mp1) = DB_CKSUMEND(mp);
DB_CKSUM16(mp1) = DB_CKSUM16(mp);
}
freemsg(mp);
return (mp1);
}
static void
ip_dlpi_error(ill_t *ill, t_uscalar_t prim, t_uscalar_t dl_err,
t_uscalar_t err)
{
if (dl_err == DL_SYSERR) {
(void) mi_strlog(ill->ill_rq, 1, SL_CONSOLE|SL_ERROR|SL_TRACE,
"%s: %s failed: DL_SYSERR (errno %u)\n",
ill->ill_name, dl_primstr(prim), err);
return;
}
(void) mi_strlog(ill->ill_rq, 1, SL_CONSOLE|SL_ERROR|SL_TRACE,
"%s: %s failed: %s\n", ill->ill_name, dl_primstr(prim),
dl_errstr(dl_err));
}
void
ip_rput_dlpi(ill_t *ill, mblk_t *mp)
{
dl_ok_ack_t *dloa = (dl_ok_ack_t *)mp->b_rptr;
dl_error_ack_t *dlea = (dl_error_ack_t *)dloa;
queue_t *q = ill->ill_rq;
t_uscalar_t prim = dloa->dl_primitive;
t_uscalar_t reqprim = DL_PRIM_INVAL;
DTRACE_PROBE3(ill__dlpi, char *, "ip_rput_dlpi",
char *, dl_primstr(prim), ill_t *, ill);
ip1dbg(("ip_rput_dlpi"));
switch (prim) {
case DL_ERROR_ACK:
reqprim = dlea->dl_error_primitive;
ip2dbg(("ip_rput_dlpi(%s): DL_ERROR_ACK for %s (0x%x): %s "
"(0x%x), unix %u\n", ill->ill_name, dl_primstr(reqprim),
reqprim, dl_errstr(dlea->dl_errno), dlea->dl_errno,
dlea->dl_unix_errno));
break;
case DL_OK_ACK:
reqprim = dloa->dl_correct_primitive;
break;
case DL_INFO_ACK:
reqprim = DL_INFO_REQ;
break;
case DL_BIND_ACK:
reqprim = DL_BIND_REQ;
break;
case DL_PHYS_ADDR_ACK:
reqprim = DL_PHYS_ADDR_REQ;
break;
case DL_NOTIFY_ACK:
reqprim = DL_NOTIFY_REQ;
break;
case DL_CAPABILITY_ACK:
reqprim = DL_CAPABILITY_REQ;
break;
}
if (prim != DL_NOTIFY_IND) {
if (reqprim == DL_PRIM_INVAL ||
!ill_dlpi_pending(ill, reqprim)) {
freemsg(mp);
return;
}
ip1dbg(("ip_rput: received %s for %s\n", dl_primstr(prim),
dl_primstr(reqprim)));
}
switch (reqprim) {
case DL_UNBIND_REQ:
mutex_enter(&ill->ill_lock);
ill->ill_state_flags &= ~ILL_DL_UNBIND_IN_PROGRESS;
cv_signal(&ill->ill_cv);
mutex_exit(&ill->ill_lock);
break;
case DL_ENABMULTI_REQ:
if (prim == DL_OK_ACK) {
if (ill->ill_dlpi_multicast_state == IDS_INPROGRESS)
ill->ill_dlpi_multicast_state = IDS_OK;
}
break;
}
ill_refhold(ill);
if (prim == DL_NOTIFY_IND)
qwriter_ip(ill, q, mp, ip_rput_dlpi_writer, NEW_OP, B_FALSE);
else
qwriter_ip(ill, q, mp, ip_rput_dlpi_writer, CUR_OP, B_FALSE);
}
static void
ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
{
dl_ok_ack_t *dloa = (dl_ok_ack_t *)mp->b_rptr;
dl_error_ack_t *dlea = (dl_error_ack_t *)dloa;
int err = 0;
ill_t *ill = (ill_t *)q->q_ptr;
ipif_t *ipif = NULL;
mblk_t *mp1 = NULL;
conn_t *connp = NULL;
t_uscalar_t paddrreq;
mblk_t *mp_hw;
boolean_t success;
boolean_t ioctl_aborted = B_FALSE;
boolean_t log = B_TRUE;
DTRACE_PROBE3(ill__dlpi, char *, "ip_rput_dlpi_writer",
char *, dl_primstr(dloa->dl_primitive), ill_t *, ill);
ip1dbg(("ip_rput_dlpi_writer .."));
ASSERT(ipsq->ipsq_xop == ill->ill_phyint->phyint_ipsq->ipsq_xop);
ASSERT(IAM_WRITER_ILL(ill));
ipif = ipsq->ipsq_xop->ipx_pending_ipif;
if (ipif != NULL && ipif->ipif_ill != ill)
ioctl_aborted = B_TRUE;
switch (dloa->dl_primitive) {
case DL_ERROR_ACK:
ip1dbg(("ip_rput_dlpi_writer: got DL_ERROR_ACK for %s\n",
dl_primstr(dlea->dl_error_primitive)));
DTRACE_PROBE3(ill__dlpi, char *, "ip_rput_dlpi_writer error",
char *, dl_primstr(dlea->dl_error_primitive),
ill_t *, ill);
switch (dlea->dl_error_primitive) {
case DL_DISABMULTI_REQ:
ill_dlpi_done(ill, dlea->dl_error_primitive);
break;
case DL_PROMISCON_REQ:
case DL_PROMISCOFF_REQ:
case DL_UNBIND_REQ:
case DL_ATTACH_REQ:
case DL_INFO_REQ:
ill_dlpi_done(ill, dlea->dl_error_primitive);
break;
case DL_NOTIFY_REQ:
ill_dlpi_done(ill, DL_NOTIFY_REQ);
log = B_FALSE;
break;
case DL_PHYS_ADDR_REQ:
paddrreq = ill->ill_phys_addr_pend;
ill_dlpi_done(ill, DL_PHYS_ADDR_REQ);
if (paddrreq == DL_IPV6_TOKEN) {
ill->ill_token_length = 0;
log = B_FALSE;
break;
} else if (paddrreq == DL_IPV6_LINK_LAYER_ADDR) {
ill->ill_nd_lla_len = 0;
log = B_FALSE;
break;
}
if (!ill->ill_ifname_pending)
break;
ill->ill_ifname_pending = 0;
if (!ioctl_aborted)
mp1 = ipsq_pending_mp_get(ipsq, &connp);
if (mp1 != NULL) {
ASSERT(connp == NULL);
q = ill->ill_wq;
}
break;
case DL_BIND_REQ:
ill_dlpi_done(ill, DL_BIND_REQ);
if (ill->ill_ifname_pending)
break;
mutex_enter(&ill->ill_lock);
ill->ill_state_flags &= ~ILL_DOWN_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
if (!ioctl_aborted)
mp1 = ipsq_pending_mp_get(ipsq, &connp);
if (mp1 != NULL) {
if (connp != NULL)
q = CONNP_TO_WQ(connp);
(void) ipif_down(ipif, NULL, NULL);
}
break;
case DL_ENABMULTI_REQ:
ill_dlpi_done(ill, DL_ENABMULTI_REQ);
if (ill->ill_dlpi_multicast_state == IDS_INPROGRESS)
ill->ill_dlpi_multicast_state = IDS_FAILED;
if (ill->ill_dlpi_multicast_state == IDS_FAILED) {
printf("ip: joining multicasts failed (%d)"
" on %s - will use link layer "
"broadcasts for multicast\n",
dlea->dl_errno, ill->ill_name);
mutex_enter(&ill->ill_phyint->phyint_lock);
ill->ill_phyint->phyint_flags |=
PHYI_MULTI_BCAST;
mutex_exit(&ill->ill_phyint->phyint_lock);
}
freemsg(mp);
return;
case DL_CAPABILITY_REQ:
ip1dbg(("ip_rput_dlpi_writer: got DL_ERROR_ACK for "
"DL_CAPABILITY REQ\n"));
if (ill->ill_dlpi_capab_state == IDCS_PROBE_SENT)
ill->ill_dlpi_capab_state = IDCS_FAILED;
ill_capability_done(ill);
freemsg(mp);
return;
}
if (mp1 != NULL) {
if (ill->ill_ifname_pending_err != 0) {
err = ill->ill_ifname_pending_err;
ill->ill_ifname_pending_err = 0;
} else {
err = dlea->dl_unix_errno ?
dlea->dl_unix_errno : ENXIO;
}
} else if (log && ill->ill_ifname_pending &&
ill->ill_ifname_pending_err == 0) {
ill->ill_ifname_pending_err = dlea->dl_unix_errno ?
dlea->dl_unix_errno : ENXIO;
}
if (log)
ip_dlpi_error(ill, dlea->dl_error_primitive,
dlea->dl_errno, dlea->dl_unix_errno);
break;
case DL_CAPABILITY_ACK:
ill_capability_ack(ill, mp);
mp = NULL;
break;
case DL_INFO_ACK:
ill_dlpi_done(ill, DL_INFO_REQ);
ip_ll_subnet_defaults(ill, mp);
ASSERT(!MUTEX_HELD(&ill->ill_phyint->phyint_ipsq->ipsq_lock));
return;
case DL_BIND_ACK:
ill_dlpi_done(ill, DL_BIND_REQ);
if (ill->ill_ifname_pending) {
DTRACE_PROBE2(ip__rput__dlpi__ifname__pending,
ill_t *, ill, mblk_t *, mp);
break;
}
mutex_enter(&ill->ill_lock);
ill->ill_dl_up = 1;
ill->ill_state_flags &= ~ILL_DOWN_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
if (!ioctl_aborted)
mp1 = ipsq_pending_mp_get(ipsq, &connp);
if (mp1 == NULL) {
DTRACE_PROBE1(ip__rput__dlpi__no__mblk, ill_t *, ill);
break;
}
if (connp != NULL)
q = CONNP_TO_WQ(connp);
ip1dbg(("ip_rput_dlpi: bind_ack %s\n", ill->ill_name));
DTRACE_PROBE1(ip__rput__dlpi__bind__ack, ill_t *, ill);
ill_nic_event_dispatch(ill, 0, NE_UP, NULL, 0);
if (ill->ill_isv6) {
(void) ipif_resolver_up(ipif, Res_act_initial);
if ((err = ipif_ndp_up(ipif, B_TRUE)) == 0)
err = ipif_up_done_v6(ipif);
} else if (ill->ill_net_type == IRE_IF_RESOLVER) {
if (connp != NULL)
mutex_enter(&connp->conn_lock);
mutex_enter(&ill->ill_lock);
success = ipsq_pending_mp_add(connp, ipif, q, mp1, 0);
mutex_exit(&ill->ill_lock);
if (connp != NULL)
mutex_exit(&connp->conn_lock);
if (success) {
err = ipif_resolver_up(ipif, Res_act_initial);
if (err == EINPROGRESS) {
freemsg(mp);
return;
}
mp1 = ipsq_pending_mp_get(ipsq, &connp);
} else {
err = EINTR;
}
} else {
(void) ipif_resolver_up(ipif, Res_act_initial);
err = ipif_up_done(ipif);
}
if ((err == 0) && (ill->ill_up_ipifs)) {
err = ill_up_ipifs(ill, q, mp1);
if (err == EINPROGRESS) {
freemsg(mp);
return;
}
}
if (ill->ill_move_ipif != NULL) {
if (err != 0) {
ill->ill_move_ipif = NULL;
} else {
ipif = ill->ill_move_ipif;
ill->ill_move_ipif = NULL;
err = ipif_up(ipif, q, mp1);
if (err == EINPROGRESS) {
freemsg(mp);
return;
}
}
}
break;
case DL_NOTIFY_IND: {
dl_notify_ind_t *notify = (dl_notify_ind_t *)mp->b_rptr;
uint_t orig_mtu, orig_mc_mtu;
switch (notify->dl_notification) {
case DL_NOTE_PHYS_ADDR:
err = ill_set_phys_addr(ill, mp);
break;
case DL_NOTE_REPLUMB:
err = ill_replumb(ill, mp);
return;
case DL_NOTE_FASTPATH_FLUSH:
nce_flush(ill, B_FALSE);
break;
case DL_NOTE_SDU_SIZE:
case DL_NOTE_SDU_SIZE2:
mutex_enter(&ill->ill_lock);
orig_mtu = ill->ill_mtu;
orig_mc_mtu = ill->ill_mc_mtu;
switch (notify->dl_notification) {
case DL_NOTE_SDU_SIZE:
ill->ill_current_frag =
(uint_t)notify->dl_data;
ill->ill_mc_mtu = (uint_t)notify->dl_data;
break;
case DL_NOTE_SDU_SIZE2:
ill->ill_current_frag =
(uint_t)notify->dl_data1;
ill->ill_mc_mtu = (uint_t)notify->dl_data2;
break;
}
if (ill->ill_current_frag > ill->ill_max_frag)
ill->ill_max_frag = ill->ill_current_frag;
if (!(ill->ill_flags & ILLF_FIXEDMTU)) {
ill->ill_mtu = ill->ill_current_frag;
if (ill->ill_user_mtu != 0 &&
ill->ill_user_mtu < ill->ill_mtu)
ill->ill_mtu = ill->ill_user_mtu;
if (ill->ill_user_mtu != 0 &&
ill->ill_user_mtu < ill->ill_mc_mtu)
ill->ill_mc_mtu = ill->ill_user_mtu;
if (ill->ill_isv6) {
if (ill->ill_mtu < IPV6_MIN_MTU)
ill->ill_mtu = IPV6_MIN_MTU;
if (ill->ill_mc_mtu < IPV6_MIN_MTU)
ill->ill_mc_mtu = IPV6_MIN_MTU;
} else {
if (ill->ill_mtu < IP_MIN_MTU)
ill->ill_mtu = IP_MIN_MTU;
if (ill->ill_mc_mtu < IP_MIN_MTU)
ill->ill_mc_mtu = IP_MIN_MTU;
}
} else if (ill->ill_mc_mtu > ill->ill_mtu) {
ill->ill_mc_mtu = ill->ill_mtu;
}
mutex_exit(&ill->ill_lock);
if (orig_mtu != ill->ill_mtu ||
orig_mc_mtu != ill->ill_mc_mtu) {
dce_increment_all_generations(ill->ill_isv6,
ill->ill_ipst);
}
if (IS_UNDER_IPMP(ill))
ipmp_illgrp_refresh_mtu(ill->ill_grp);
break;
case DL_NOTE_LINK_UP:
case DL_NOTE_LINK_DOWN: {
phyint_t *phyint = ill->ill_phyint;
uint64_t new_phyint_flags;
boolean_t changed = B_FALSE;
boolean_t went_up;
went_up = notify->dl_notification == DL_NOTE_LINK_UP;
mutex_enter(&phyint->phyint_lock);
new_phyint_flags = went_up ?
phyint->phyint_flags | PHYI_RUNNING :
phyint->phyint_flags & ~PHYI_RUNNING;
if (IS_IPMP(ill)) {
new_phyint_flags = went_up ?
new_phyint_flags & ~PHYI_FAILED :
new_phyint_flags | PHYI_FAILED;
}
if (new_phyint_flags != phyint->phyint_flags) {
phyint->phyint_flags = new_phyint_flags;
changed = B_TRUE;
}
mutex_exit(&phyint->phyint_lock);
if (changed) {
ill_restart_dad(phyint->phyint_illv4, went_up);
ill_restart_dad(phyint->phyint_illv6, went_up);
}
break;
}
case DL_NOTE_PROMISC_ON_PHYS: {
phyint_t *phyint = ill->ill_phyint;
mutex_enter(&phyint->phyint_lock);
phyint->phyint_flags |= PHYI_PROMISC;
mutex_exit(&phyint->phyint_lock);
break;
}
case DL_NOTE_PROMISC_OFF_PHYS: {
phyint_t *phyint = ill->ill_phyint;
mutex_enter(&phyint->phyint_lock);
phyint->phyint_flags &= ~PHYI_PROMISC;
mutex_exit(&phyint->phyint_lock);
break;
}
case DL_NOTE_CAPAB_RENEG:
ipsq_current_start(ipsq, ill->ill_ipif, 0);
ill_capability_reset(ill, B_TRUE);
ipsq_current_finish(ipsq);
break;
case DL_NOTE_ALLOWED_IPS:
ill_set_allowed_ips(ill, mp);
break;
default:
ip0dbg(("ip_rput_dlpi_writer: unknown notification "
"type 0x%x for DL_NOTIFY_IND\n",
notify->dl_notification));
break;
}
break;
}
case DL_NOTIFY_ACK: {
dl_notify_ack_t *noteack = (dl_notify_ack_t *)mp->b_rptr;
if (noteack->dl_notifications & DL_NOTE_LINK_UP)
ill->ill_note_link = 1;
ill_dlpi_done(ill, DL_NOTIFY_REQ);
break;
}
case DL_PHYS_ADDR_ACK: {
uint_t paddrlen, paddroff;
uint8_t *addr;
paddrreq = ill->ill_phys_addr_pend;
paddrlen = ((dl_phys_addr_ack_t *)mp->b_rptr)->dl_addr_length;
paddroff = ((dl_phys_addr_ack_t *)mp->b_rptr)->dl_addr_offset;
addr = mp->b_rptr + paddroff;
ill_dlpi_done(ill, DL_PHYS_ADDR_REQ);
if (paddrreq == DL_IPV6_TOKEN) {
bcopy(addr, &ill->ill_token.s6_addr32[2], paddrlen);
ill->ill_token_length = paddrlen;
break;
} else if (paddrreq == DL_IPV6_LINK_LAYER_ADDR) {
ASSERT(ill->ill_nd_lla_mp == NULL);
ill_set_ndmp(ill, mp, paddroff, paddrlen);
mp = NULL;
break;
} else if (paddrreq == DL_CURR_DEST_ADDR) {
ASSERT(ill->ill_dest_addr_mp == NULL);
ill->ill_dest_addr_mp = mp;
ill->ill_dest_addr = addr;
mp = NULL;
if (ill->ill_isv6) {
ill_setdesttoken(ill);
ipif_setdestlinklocal(ill->ill_ipif);
}
break;
}
ASSERT(paddrreq == DL_CURR_PHYS_ADDR);
ASSERT(ill->ill_phys_addr_mp == NULL);
if (!ill->ill_ifname_pending)
break;
ill->ill_ifname_pending = 0;
if (!ioctl_aborted)
mp1 = ipsq_pending_mp_get(ipsq, &connp);
if (mp1 != NULL) {
ASSERT(connp == NULL);
q = ill->ill_wq;
}
if (ill->ill_ifname_pending_err != 0) {
err = ill->ill_ifname_pending_err;
ill->ill_ifname_pending_err = 0;
break;
}
ill->ill_phys_addr_mp = mp;
ill->ill_phys_addr = (paddrlen == 0 ? NULL : addr);
mp = NULL;
if (paddrlen == 0 || ill->ill_phys_addr_length == 0) {
ill->ill_phys_addr = NULL;
} else if (paddrlen != ill->ill_phys_addr_length) {
ip0dbg(("DL_PHYS_ADDR_ACK: got addrlen %d, expected %d",
paddrlen, ill->ill_phys_addr_length));
err = EINVAL;
break;
}
if (ill->ill_nd_lla_mp == NULL) {
if ((mp_hw = copyb(ill->ill_phys_addr_mp)) == NULL) {
err = ENOMEM;
break;
}
ill_set_ndmp(ill, mp_hw, paddroff, paddrlen);
}
if (ill->ill_isv6) {
ill_setdefaulttoken(ill);
ipif_setlinklocal(ill->ill_ipif);
}
break;
}
case DL_OK_ACK:
ip2dbg(("DL_OK_ACK %s (0x%x)\n",
dl_primstr((int)dloa->dl_correct_primitive),
dloa->dl_correct_primitive));
DTRACE_PROBE3(ill__dlpi, char *, "ip_rput_dlpi_writer ok",
char *, dl_primstr(dloa->dl_correct_primitive),
ill_t *, ill);
switch (dloa->dl_correct_primitive) {
case DL_ENABMULTI_REQ:
case DL_DISABMULTI_REQ:
ill_dlpi_done(ill, dloa->dl_correct_primitive);
break;
case DL_PROMISCON_REQ:
case DL_PROMISCOFF_REQ:
case DL_UNBIND_REQ:
case DL_ATTACH_REQ:
ill_dlpi_done(ill, dloa->dl_correct_primitive);
break;
}
break;
default:
break;
}
freemsg(mp);
if (mp1 == NULL)
return;
ASSERT(err != EINPROGRESS);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_rput_dlpi_writer finish",
int, ipsq->ipsq_xop->ipx_current_ioctl, ill_t *, ill,
ipif_t *, NULL);
switch (ipsq->ipsq_xop->ipx_current_ioctl) {
case 0:
ipsq_current_finish(ipsq);
break;
case SIOCSLIFNAME:
case IF_UNITSEL: {
ill_t *ill_other = ILL_OTHER(ill);
if (err == 0 && ill_other != NULL && IS_UNDER_IPMP(ill_other)) {
ipmp_grp_t *grp = ill->ill_phyint->phyint_grp;
ipmp_illgrp_t *illg;
illg = ill->ill_isv6 ? grp->gr_v6 : grp->gr_v4;
if (illg == NULL)
ipmp_phyint_leave_grp(ill->ill_phyint);
else
ipmp_ill_join_illgrp(ill, illg);
}
if (ipsq->ipsq_xop->ipx_current_ioctl == IF_UNITSEL)
ip_ioctl_finish(q, mp1, err, NO_COPYOUT, ipsq);
else
ip_ioctl_finish(q, mp1, err, COPYOUT, ipsq);
break;
}
case SIOCLIFADDIF:
ip_ioctl_finish(q, mp1, err, COPYOUT, ipsq);
break;
default:
ip_ioctl_finish(q, mp1, err, NO_COPYOUT, ipsq);
break;
}
}
void
ip_rput_other(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
{
ill_t *ill = q->q_ptr;
struct iocblk *iocp;
ip1dbg(("ip_rput_other "));
if (ipsq != NULL) {
ASSERT(IAM_WRITER_IPSQ(ipsq));
ASSERT(ipsq->ipsq_xop ==
ill->ill_phyint->phyint_ipsq->ipsq_xop);
}
switch (mp->b_datap->db_type) {
case M_ERROR:
case M_HANGUP:
ASSERT(ipsq != NULL);
if (mp->b_rptr < mp->b_wptr)
ill->ill_error = (int)(*mp->b_rptr & 0xFF);
if (ill->ill_error == 0)
ill->ill_error = ENXIO;
if (!ill_down_start(q, mp))
return;
ipif_all_down_tail(ipsq, q, mp, NULL);
break;
case M_IOCNAK: {
iocp = (struct iocblk *)mp->b_rptr;
ASSERT(iocp->ioc_cmd == DL_IOC_HDR_INFO);
mutex_enter(&ill->ill_lock);
if (ill->ill_dlpi_fastpath_state == IDS_INPROGRESS) {
ill->ill_dlpi_fastpath_state = IDS_FAILED;
mutex_exit(&ill->ill_lock);
ip1dbg(("ip_rput: DLPI fastpath off on interface %s\n",
ill->ill_name));
} else {
mutex_exit(&ill->ill_lock);
}
freemsg(mp);
break;
}
default:
ASSERT(0);
break;
}
}
boolean_t
ip_forward_options(mblk_t *mp, ipha_t *ipha, ill_t *dst_ill,
ip_recv_attr_t *ira)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
ipaddr_t ifaddr;
uint32_t ts;
timestruc_t now;
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ip2dbg(("ip_forward_options\n"));
dst = ipha->ipha_dst;
opt = NULL;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
ASSERT((opts.ipoptp_flags & IPOPTP_ERROR) == 0);
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ip2dbg(("ip_forward_options: opt %d, len %d\n",
optval, opts.ipoptp_len));
switch (optval) {
uint32_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
if (!ipst->ips_ip_forward_src_routed) {
BUMP_MIB(dst_ill->ill_ip_mib,
ipIfStatsForwProhibits);
ip_drop_input("ICMP_SOURCE_ROUTE_FAILED",
mp, dst_ill);
icmp_unreachable(mp, ICMP_SOURCE_ROUTE_FAILED,
ira);
return (B_FALSE);
}
if (ip_type_v4(dst, ipst) != IRE_LOCAL) {
break;
}
off = opt[IPOPT_OFFSET];
off--;
redo_srr:
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg((
"ip_forward_options: end of SR\n"));
break;
}
ASSERT(dst_ill != NULL);
if (ip_select_source_v4(dst_ill, INADDR_ANY, dst,
INADDR_ANY, ALL_ZONES, ipst, &ifaddr, NULL,
NULL) != 0) {
ifaddr = INADDR_ANY;
}
bcopy((char *)opt + off, &dst, IP_ADDR_LEN);
bcopy(&ifaddr, (char *)opt + off, IP_ADDR_LEN);
ip1dbg(("ip_forward_options: next hop 0x%x\n",
ntohl(dst)));
if (ip_type_v4(dst, ipst) == IRE_LOCAL) {
off += IP_ADDR_LEN;
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
goto redo_srr;
}
ipha->ipha_dst = dst;
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
break;
case IPOPT_RR:
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg((
"ip_forward_options: end of RR\n"));
break;
}
ASSERT(dst_ill != NULL);
if (ip_select_source_v4(dst_ill, INADDR_ANY, dst,
INADDR_ANY, ALL_ZONES, ipst, &ifaddr, NULL,
NULL) != 0) {
ifaddr = INADDR_ANY;
}
bcopy(&ifaddr, (char *)opt + off, IP_ADDR_LEN);
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
break;
case IPOPT_TS:
off = 0;
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_TSONLY:
off = IPOPT_TS_TIMELEN;
break;
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
off = opt[IPOPT_OFFSET] - 1;
bcopy((char *)opt + off, &dst, IP_ADDR_LEN);
if (ip_type_v4(dst, ipst) != IRE_LOCAL) {
break;
}
case IPOPT_TS_TSANDADDR:
off = IP_ADDR_LEN + IPOPT_TS_TIMELEN;
break;
default:
cmn_err(CE_PANIC, "ip_forward_options: "
"unknown IT - bug in ip_input_options?\n");
}
if (opt[IPOPT_OFFSET] - 1 + off > optlen) {
off = (opt[IPOPT_POS_OV_FLG] >> 4) + 1;
opt[IPOPT_POS_OV_FLG] =
(uint8_t)((opt[IPOPT_POS_OV_FLG] & 0x0F) |
(off << 4));
break;
}
off = opt[IPOPT_OFFSET] - 1;
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
case IPOPT_TS_TSANDADDR:
ASSERT(dst_ill != NULL);
if (ip_select_source_v4(dst_ill, INADDR_ANY,
dst, INADDR_ANY, ALL_ZONES, ipst, &ifaddr,
NULL, NULL) != 0) {
ifaddr = INADDR_ANY;
}
bcopy(&ifaddr, (char *)opt + off, IP_ADDR_LEN);
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
case IPOPT_TS_TSONLY:
off = opt[IPOPT_OFFSET] - 1;
gethrestime(&now);
ts = (now.tv_sec % (24 * 60 * 60)) * 1000 +
NSEC2MSEC(now.tv_nsec);
bcopy(&ts, (char *)opt + off, IPOPT_TS_TIMELEN);
opt[IPOPT_OFFSET] += IPOPT_TS_TIMELEN;
break;
}
break;
}
}
return (B_TRUE);
}
void
ill_frag_timer(void *arg)
{
ill_t *ill = (ill_t *)arg;
boolean_t frag_pending;
ip_stack_t *ipst = ill->ill_ipst;
time_t timeout;
mutex_enter(&ill->ill_lock);
ASSERT(!ill->ill_fragtimer_executing);
if (ill->ill_state_flags & ILL_CONDEMNED) {
ill->ill_frag_timer_id = 0;
mutex_exit(&ill->ill_lock);
return;
}
ill->ill_fragtimer_executing = 1;
mutex_exit(&ill->ill_lock);
timeout = (ill->ill_isv6 ? ipst->ips_ipv6_reassembly_timeout :
ipst->ips_ip_reassembly_timeout);
frag_pending = ill_frag_timeout(ill, timeout);
mutex_enter(&ill->ill_lock);
ill->ill_fragtimer_executing = 0;
ill->ill_frag_timer_id = 0;
if (frag_pending || ill->ill_fragtimer_needrestart)
ill_frag_timer_start(ill);
mutex_exit(&ill->ill_lock);
}
void
ill_frag_timer_start(ill_t *ill)
{
ip_stack_t *ipst = ill->ill_ipst;
clock_t timeo_ms;
ASSERT(MUTEX_HELD(&ill->ill_lock));
if (ill->ill_state_flags & ILL_CONDEMNED)
return;
if (ill->ill_fragtimer_executing) {
ill->ill_fragtimer_needrestart = 1;
return;
}
if (ill->ill_frag_timer_id == 0) {
timeo_ms = (ill->ill_isv6 ? ipst->ips_ipv6_reassembly_timeout :
ipst->ips_ip_reassembly_timeout) * SECONDS;
ill->ill_frag_timer_id = timeout(ill_frag_timer, ill,
MSEC_TO_TICK(timeo_ms >> 1));
ill->ill_fragtimer_needrestart = 0;
}
}
boolean_t
ip_input_local_options(mblk_t *mp, ipha_t *ipha, ip_recv_attr_t *ira)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
ipaddr_t ifaddr;
uint32_t ts;
timestruc_t now;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ip2dbg(("ip_input_local_options\n"));
opt = NULL;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
ASSERT((opts.ipoptp_flags & IPOPTP_ERROR) == 0);
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ip2dbg(("ip_input_local_options: opt %d, len %d\n",
optval, optlen));
switch (optval) {
uint32_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg(("ip_input_local_options: end of SR\n"));
break;
}
ip1dbg(("ip_input_local_options: not end of SR\n"));
if (optval == IPOPT_SSRR) {
goto bad_src_route;
}
opt[IPOPT_OLEN] = (uint8_t)off;
while (off < optlen) {
opt[off++] = IPOPT_NOP;
}
break;
case IPOPT_RR:
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg((
"ip_input_local_options: end of RR\n"));
break;
}
if (ip_select_source_v4(ill, INADDR_ANY, ipha->ipha_dst,
INADDR_ANY, ALL_ZONES, ipst, &ifaddr, NULL,
NULL) != 0) {
ifaddr = INADDR_ANY;
}
bcopy(&ifaddr, (char *)opt + off, IP_ADDR_LEN);
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
break;
case IPOPT_TS:
off = 0;
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_TSONLY:
off = IPOPT_TS_TIMELEN;
break;
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
off = opt[IPOPT_OFFSET] - 1;
bcopy((char *)opt + off, &dst, IP_ADDR_LEN);
if (ip_type_v4(dst, ipst) != IRE_LOCAL) {
break;
}
case IPOPT_TS_TSANDADDR:
off = IP_ADDR_LEN + IPOPT_TS_TIMELEN;
break;
default:
cmn_err(CE_PANIC, "ip_input_local_options: "
"unknown IT - bug in ip_input_options?\n");
}
if (opt[IPOPT_OFFSET] - 1 + off > optlen) {
off = (opt[IPOPT_POS_OV_FLG] >> 4) + 1;
opt[IPOPT_POS_OV_FLG] =
(uint8_t)((opt[IPOPT_POS_OV_FLG] & 0x0F) |
(off << 4));
break;
}
off = opt[IPOPT_OFFSET] - 1;
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
case IPOPT_TS_TSANDADDR:
if (ip_select_source_v4(ill, INADDR_ANY,
ipha->ipha_dst, INADDR_ANY, ALL_ZONES, ipst,
&ifaddr, NULL, NULL) != 0) {
ifaddr = INADDR_ANY;
}
bcopy(&ifaddr, (char *)opt + off, IP_ADDR_LEN);
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
case IPOPT_TS_TSONLY:
off = opt[IPOPT_OFFSET] - 1;
gethrestime(&now);
ts = (now.tv_sec % (24 * 60 * 60)) * 1000 +
NSEC2MSEC(now.tv_nsec);
bcopy(&ts, (char *)opt + off, IPOPT_TS_TIMELEN);
opt[IPOPT_OFFSET] += IPOPT_TS_TIMELEN;
break;
}
break;
}
}
return (B_TRUE);
bad_src_route:
DB_CKSUMFLAGS(mp) = 0;
ip_drop_input("ICMP_SOURCE_ROUTE_FAILED", mp, ill);
icmp_unreachable(mp, ICMP_SOURCE_ROUTE_FAILED, ira);
return (B_FALSE);
}
ipaddr_t
ip_input_options(ipha_t *ipha, ipaddr_t dst, mblk_t *mp,
ip_recv_attr_t *ira, int *errorp)
{
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
intptr_t code = 0;
ire_t *ire;
ip2dbg(("ip_input_options\n"));
opt = NULL;
*errorp = 0;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ip2dbg(("ip_input_options: opt %d, len %d\n",
optval, optlen));
switch (optval) {
uint32_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
if (ip_type_v4(dst, ipst) != IRE_LOCAL) {
if (optval == IPOPT_SSRR) {
ip1dbg(("ip_input_options: not next"
" strict source route 0x%x\n",
ntohl(dst)));
code = (char *)&ipha->ipha_dst -
(char *)ipha;
goto param_prob;
}
ip2dbg(("ip_input_options: "
"not next source route 0x%x\n",
ntohl(dst)));
break;
}
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg((
"ip_input_options: bad option offset\n"));
code = (char *)&opt[IPOPT_OLEN] -
(char *)ipha;
goto param_prob;
}
off = opt[IPOPT_OFFSET];
off--;
redo_srr:
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg(("ip_input_options: end of SR\n"));
break;
}
bcopy((char *)opt + off, &dst, IP_ADDR_LEN);
ip1dbg(("ip_input_options: next hop 0x%x\n",
ntohl(dst)));
if (ip_type_v4(dst, ipst) == IRE_LOCAL) {
off += IP_ADDR_LEN;
goto redo_srr;
}
if (dst == htonl(INADDR_LOOPBACK)) {
ip1dbg(("ip_input_options: loopback addr in "
"source route!\n"));
goto bad_src_route;
}
if (optval == IPOPT_SSRR) {
ire = ire_ftable_lookup_v4(dst, 0, 0,
IRE_INTERFACE, NULL, ALL_ZONES,
ira->ira_tsl,
MATCH_IRE_TYPE | MATCH_IRE_SECATTR, 0, ipst,
NULL);
if (ire == NULL) {
ip1dbg(("ip_input_options: SSRR not "
"directly reachable: 0x%x\n",
ntohl(dst)));
goto bad_src_route;
}
ire_refrele(ire);
}
break;
case IPOPT_RR:
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg((
"ip_input_options: bad option offset\n"));
code = (char *)&opt[IPOPT_OLEN] -
(char *)ipha;
goto param_prob;
}
break;
case IPOPT_TS:
code = (char *)&opt[IPOPT_OLEN] - (char *)ipha;
if (optlen < IPOPT_MINLEN_IT) {
goto param_prob;
}
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg((
"ip_input_options: bad option offset\n"));
code = (char *)&opt[IPOPT_OFFSET] -
(char *)ipha;
goto param_prob;
}
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_TSONLY:
off = IPOPT_TS_TIMELEN;
break;
case IPOPT_TS_TSANDADDR:
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
off = IP_ADDR_LEN + IPOPT_TS_TIMELEN;
break;
default:
code = (char *)&opt[IPOPT_POS_OV_FLG] -
(char *)ipha;
goto param_prob;
}
if (opt[IPOPT_OFFSET] - 1 + off > optlen &&
(opt[IPOPT_POS_OV_FLG] & 0xF0) == 0xF0) {
goto param_prob;
}
break;
}
}
if ((opts.ipoptp_flags & IPOPTP_ERROR) == 0) {
return (dst);
}
ip1dbg(("ip_input_options: error processing IP options."));
code = (char *)&opt[IPOPT_OFFSET] - (char *)ipha;
param_prob:
DB_CKSUMFLAGS(mp) = 0;
ip_drop_input("ICMP_PARAM_PROBLEM", mp, ira->ira_ill);
icmp_param_problem(mp, (uint8_t)code, ira);
*errorp = -1;
return (dst);
bad_src_route:
DB_CKSUMFLAGS(mp) = 0;
ip_drop_input("ICMP_SOURCE_ROUTE_FAILED", mp, ira->ira_ill);
icmp_unreachable(mp, ICMP_SOURCE_ROUTE_FAILED, ira);
*errorp = -1;
return (dst);
}
int
ip_snmp_get(queue_t *q, mblk_t *mpctl, int level, boolean_t legacy_req)
{
ip_stack_t *ipst;
sctp_stack_t *sctps;
if (q->q_next != NULL) {
ipst = ILLQ_TO_IPST(q);
} else {
ipst = CONNQ_TO_IPST(q);
}
ASSERT(ipst != NULL);
sctps = ipst->ips_netstack->netstack_sctp;
if (mpctl == NULL || mpctl->b_cont == NULL) {
return (0);
}
if (!(level == MIB2_TCP || level == MIB2_UDP)) {
if ((mpctl = icmp_snmp_get(q, mpctl)) == NULL) {
return (1);
}
}
if (level != MIB2_TCP) {
if ((mpctl = udp_snmp_get(q, mpctl, legacy_req)) == NULL) {
return (1);
}
if (level == MIB2_UDP) {
goto done;
}
}
if (level != MIB2_UDP) {
if ((mpctl = tcp_snmp_get(q, mpctl, legacy_req)) == NULL) {
return (1);
}
if (level == MIB2_TCP) {
goto done;
}
}
if ((mpctl = ip_snmp_get_mib2_ip_traffic_stats(q, mpctl,
ipst, legacy_req)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip6(q, mpctl, ipst,
legacy_req)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_icmp(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_icmp6(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_igmp(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_multi(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip_addr(q, mpctl, ipst,
legacy_req)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip6_addr(q, mpctl, ipst,
legacy_req)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip_group_mem(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip6_group_mem(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip_group_src(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip6_group_src(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_virt_multi(q, mpctl, ipst)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_multi_rtable(q, mpctl, ipst)) == NULL) {
return (1);
}
mpctl = ip_snmp_get_mib2_ip_route_media(q, mpctl, level, ipst);
if (mpctl == NULL)
return (1);
mpctl = ip_snmp_get_mib2_ip6_route_media(q, mpctl, level, ipst);
if (mpctl == NULL)
return (1);
if ((mpctl = sctp_snmp_get_mib2(q, mpctl, sctps)) == NULL) {
return (1);
}
if ((mpctl = ip_snmp_get_mib2_ip_dce(q, mpctl, ipst)) == NULL) {
return (1);
}
done:
freemsg(mpctl);
return (1);
}
static mblk_t *
ip_snmp_get_mib2_ip(queue_t *q, mblk_t *mpctl, mib2_ipIfStatsEntry_t *ipmib,
ip_stack_t *ipst, boolean_t legacy_req)
{
mib2_ip_t old_ip_mib;
struct opthdr *optp;
mblk_t *mp2ctl;
mib2_ipAddrEntry_t mae;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = 0;
SET_MIB(old_ip_mib.ipForwarding,
(WE_ARE_FORWARDING(ipst) ? 1 : 2));
SET_MIB(old_ip_mib.ipDefaultTTL,
(uint32_t)ipst->ips_ip_def_ttl);
SET_MIB(old_ip_mib.ipReasmTimeout,
ipst->ips_ip_reassembly_timeout);
SET_MIB(old_ip_mib.ipAddrEntrySize,
(legacy_req) ? LEGACY_MIB_SIZE(&mae, mib2_ipAddrEntry_t) :
sizeof (mib2_ipAddrEntry_t));
SET_MIB(old_ip_mib.ipRouteEntrySize,
sizeof (mib2_ipRouteEntry_t));
SET_MIB(old_ip_mib.ipNetToMediaEntrySize,
sizeof (mib2_ipNetToMediaEntry_t));
SET_MIB(old_ip_mib.ipMemberEntrySize, sizeof (ip_member_t));
SET_MIB(old_ip_mib.ipGroupSourceEntrySize, sizeof (ip_grpsrc_t));
SET_MIB(old_ip_mib.ipRouteAttributeSize,
sizeof (mib2_ipAttributeEntry_t));
SET_MIB(old_ip_mib.transportMLPSize, sizeof (mib2_transportMLPEntry_t));
SET_MIB(old_ip_mib.ipDestEntrySize, sizeof (dest_cache_entry_t));
SET_MIB(old_ip_mib.ipInReceives,
(uint32_t)ipmib->ipIfStatsHCInReceives);
SET_MIB(old_ip_mib.ipInHdrErrors, ipmib->ipIfStatsInHdrErrors);
SET_MIB(old_ip_mib.ipInAddrErrors, ipmib->ipIfStatsInAddrErrors);
SET_MIB(old_ip_mib.ipForwDatagrams,
(uint32_t)ipmib->ipIfStatsHCOutForwDatagrams);
SET_MIB(old_ip_mib.ipInUnknownProtos,
ipmib->ipIfStatsInUnknownProtos);
SET_MIB(old_ip_mib.ipInDiscards, ipmib->ipIfStatsInDiscards);
SET_MIB(old_ip_mib.ipInDelivers,
(uint32_t)ipmib->ipIfStatsHCInDelivers);
SET_MIB(old_ip_mib.ipOutRequests,
(uint32_t)ipmib->ipIfStatsHCOutRequests);
SET_MIB(old_ip_mib.ipOutDiscards, ipmib->ipIfStatsOutDiscards);
SET_MIB(old_ip_mib.ipOutNoRoutes, ipmib->ipIfStatsOutNoRoutes);
SET_MIB(old_ip_mib.ipReasmReqds, ipmib->ipIfStatsReasmReqds);
SET_MIB(old_ip_mib.ipReasmOKs, ipmib->ipIfStatsReasmOKs);
SET_MIB(old_ip_mib.ipReasmFails, ipmib->ipIfStatsReasmFails);
SET_MIB(old_ip_mib.ipFragOKs, ipmib->ipIfStatsOutFragOKs);
SET_MIB(old_ip_mib.ipFragFails, ipmib->ipIfStatsOutFragFails);
SET_MIB(old_ip_mib.ipFragCreates, ipmib->ipIfStatsOutFragCreates);
SET_MIB(old_ip_mib.ipRoutingDiscards, 0);
SET_MIB(old_ip_mib.tcpInErrs, ipmib->tcpIfStatsInErrs);
SET_MIB(old_ip_mib.udpNoPorts, ipmib->udpIfStatsNoPorts);
SET_MIB(old_ip_mib.ipInCksumErrs, ipmib->ipIfStatsInCksumErrs);
SET_MIB(old_ip_mib.ipReasmDuplicates,
ipmib->ipIfStatsReasmDuplicates);
SET_MIB(old_ip_mib.ipReasmPartDups, ipmib->ipIfStatsReasmPartDups);
SET_MIB(old_ip_mib.ipForwProhibits, ipmib->ipIfStatsForwProhibits);
SET_MIB(old_ip_mib.udpInCksumErrs, ipmib->udpIfStatsInCksumErrs);
SET_MIB(old_ip_mib.udpInOverflows, ipmib->udpIfStatsInOverflows);
SET_MIB(old_ip_mib.rawipInOverflows,
ipmib->rawipIfStatsInOverflows);
SET_MIB(old_ip_mib.ipsecInSucceeded, ipmib->ipsecIfStatsInSucceeded);
SET_MIB(old_ip_mib.ipsecInFailed, ipmib->ipsecIfStatsInFailed);
SET_MIB(old_ip_mib.ipInIPv6, ipmib->ipIfStatsInWrongIPVersion);
SET_MIB(old_ip_mib.ipOutIPv6, ipmib->ipIfStatsOutWrongIPVersion);
SET_MIB(old_ip_mib.ipOutSwitchIPv6,
ipmib->ipIfStatsOutSwitchIPVersion);
if (!snmp_append_data(mpctl->b_cont, (char *)&old_ip_mib,
(int)sizeof (old_ip_mib))) {
ip1dbg(("ip_snmp_get_mib2_ip: failed to allocate %u bytes\n",
(uint_t)sizeof (old_ip_mib)));
}
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_ip: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip_traffic_stats(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst,
boolean_t legacy_req)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ill_walk_context_t ctx;
mblk_t *mp_tail = NULL;
mib2_ipIfStatsEntry_t global_ip_mib;
mib2_ipAddrEntry_t mae;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = MIB2_IP_TRAFFIC_STATS;
ipst->ips_ip_mib.ipIfStatsIPVersion = MIB2_INETADDRESSTYPE_ipv4;
ipst->ips_ip_mib.ipIfStatsIfIndex =
MIB2_UNKNOWN_INTERFACE;
SET_MIB(ipst->ips_ip_mib.ipIfStatsForwarding,
(ipst->ips_ip_forwarding ? 1 : 2));
SET_MIB(ipst->ips_ip_mib.ipIfStatsDefaultTTL,
(uint32_t)ipst->ips_ip_def_ttl);
SET_MIB(ipst->ips_ip_mib.ipIfStatsEntrySize,
sizeof (mib2_ipIfStatsEntry_t));
SET_MIB(ipst->ips_ip_mib.ipIfStatsAddrEntrySize,
sizeof (mib2_ipAddrEntry_t));
SET_MIB(ipst->ips_ip_mib.ipIfStatsRouteEntrySize,
sizeof (mib2_ipRouteEntry_t));
SET_MIB(ipst->ips_ip_mib.ipIfStatsNetToMediaEntrySize,
sizeof (mib2_ipNetToMediaEntry_t));
SET_MIB(ipst->ips_ip_mib.ipIfStatsMemberEntrySize,
sizeof (ip_member_t));
SET_MIB(ipst->ips_ip_mib.ipIfStatsGroupSourceEntrySize,
sizeof (ip_grpsrc_t));
bcopy(&ipst->ips_ip_mib, &global_ip_mib, sizeof (global_ip_mib));
if (legacy_req) {
SET_MIB(global_ip_mib.ipIfStatsAddrEntrySize,
LEGACY_MIB_SIZE(&mae, mib2_ipAddrEntry_t));
}
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&global_ip_mib, (int)sizeof (global_ip_mib))) {
ip1dbg(("ip_snmp_get_mib2_ip_traffic_stats: "
"failed to allocate %u bytes\n",
(uint_t)sizeof (global_ip_mib)));
}
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->ill_ip_mib->ipIfStatsIfIndex =
ill->ill_phyint->phyint_ifindex;
SET_MIB(ill->ill_ip_mib->ipIfStatsForwarding,
(ipst->ips_ip_forwarding ? 1 : 2));
SET_MIB(ill->ill_ip_mib->ipIfStatsDefaultTTL,
(uint32_t)ipst->ips_ip_def_ttl);
ip_mib2_add_ip_stats(&global_ip_mib, ill->ill_ip_mib);
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)ill->ill_ip_mib,
(int)sizeof (*ill->ill_ip_mib))) {
ip1dbg(("ip_snmp_get_mib2_ip_traffic_stats: "
"failed to allocate %u bytes\n",
(uint_t)sizeof (*ill->ill_ip_mib)));
}
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_ip_traffic_stats: "
"level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
if (mp2ctl == NULL)
return (NULL);
return (ip_snmp_get_mib2_ip(q, mp2ctl, &global_ip_mib, ipst,
legacy_req));
}
static mblk_t *
ip_snmp_get_mib2_icmp(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_ICMP;
optp->name = 0;
if (!snmp_append_data(mpctl->b_cont, (char *)&ipst->ips_icmp_mib,
(int)sizeof (ipst->ips_icmp_mib))) {
ip1dbg(("ip_snmp_get_mib2_icmp: failed to allocate %u bytes\n",
(uint_t)sizeof (ipst->ips_icmp_mib)));
}
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_icmp: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_igmp(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = EXPER_IGMP;
optp->name = 0;
if (!snmp_append_data(mpctl->b_cont, (char *)&ipst->ips_igmpstat,
(int)sizeof (ipst->ips_igmpstat))) {
ip1dbg(("ip_snmp_get_mib2_igmp: failed to allocate %u bytes\n",
(uint_t)sizeof (ipst->ips_igmpstat)));
}
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_igmp: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_multi(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = EXPER_DVMRP;
optp->name = 0;
if (!ip_mroute_stats(mpctl->b_cont, ipst)) {
ip0dbg(("ip_mroute_stats: failed\n"));
}
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_multi: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip_addr(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst,
boolean_t legacy_req)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mblk_t *mp_tail = NULL;
ill_t *ill;
ipif_t *ipif;
uint_t bitval;
mib2_ipAddrEntry_t mae;
size_t mae_size;
zoneid_t zoneid;
ill_walk_context_t ctx;
mp2ctl = copymsg(mpctl);
mae_size = (legacy_req) ? LEGACY_MIB_SIZE(&mae, mib2_ipAddrEntry_t) :
sizeof (mib2_ipAddrEntry_t);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = MIB2_IP_ADDR;
zoneid = Q_TO_CONN(q)->conn_zoneid;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
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->ipif_zoneid != zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
mae.ipAdEntInfo.ae_ibcnt = ipif->ipif_ib_pkt_count;
if (ipif->ipif_ire_local != NULL) {
mae.ipAdEntInfo.ae_ibcnt +=
ipif->ipif_ire_local->ire_ib_pkt_count;
}
mae.ipAdEntInfo.ae_obcnt = 0;
mae.ipAdEntInfo.ae_focnt = 0;
ipif_get_name(ipif, mae.ipAdEntIfIndex.o_bytes,
OCTET_LENGTH);
mae.ipAdEntIfIndex.o_length =
mi_strlen(mae.ipAdEntIfIndex.o_bytes);
mae.ipAdEntAddr = ipif->ipif_lcl_addr;
mae.ipAdEntNetMask = ipif->ipif_net_mask;
mae.ipAdEntInfo.ae_subnet = ipif->ipif_subnet;
mae.ipAdEntInfo.ae_subnet_len =
ip_mask_to_plen(ipif->ipif_net_mask);
mae.ipAdEntInfo.ae_src_addr = ipif->ipif_lcl_addr;
for (bitval = 1;
bitval &&
!(bitval & ipif->ipif_brd_addr);
bitval <<= 1)
noop;
mae.ipAdEntBcastAddr = bitval;
mae.ipAdEntReasmMaxSize = IP_MAXPACKET;
mae.ipAdEntInfo.ae_mtu = ipif->ipif_ill->ill_mtu;
mae.ipAdEntInfo.ae_metric = ipif->ipif_ill->ill_metric;
mae.ipAdEntInfo.ae_broadcast_addr =
ipif->ipif_brd_addr;
mae.ipAdEntInfo.ae_pp_dst_addr =
ipif->ipif_pp_dst_addr;
mae.ipAdEntInfo.ae_flags = ipif->ipif_flags |
ill->ill_flags | ill->ill_phyint->phyint_flags;
mae.ipAdEntRetransmitTime =
ill->ill_reachable_retrans_time;
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&mae, (int)mae_size)) {
ip1dbg(("ip_snmp_get_mib2_ip_addr: failed to "
"allocate %u bytes\n", (uint_t)mae_size));
}
}
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_ip_addr: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip6_addr(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst,
boolean_t legacy_req)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mblk_t *mp_tail = NULL;
ill_t *ill;
ipif_t *ipif;
mib2_ipv6AddrEntry_t mae6;
size_t mae6_size;
zoneid_t zoneid;
ill_walk_context_t ctx;
mp2ctl = copymsg(mpctl);
mae6_size = (legacy_req) ?
LEGACY_MIB_SIZE(&mae6, mib2_ipv6AddrEntry_t) :
sizeof (mib2_ipv6AddrEntry_t);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = MIB2_IP6_ADDR;
zoneid = Q_TO_CONN(q)->conn_zoneid;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V6(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (ipif->ipif_zoneid != zoneid &&
ipif->ipif_zoneid != ALL_ZONES)
continue;
mae6.ipv6AddrInfo.ae_ibcnt = ipif->ipif_ib_pkt_count;
if (ipif->ipif_ire_local != NULL) {
mae6.ipv6AddrInfo.ae_ibcnt +=
ipif->ipif_ire_local->ire_ib_pkt_count;
}
mae6.ipv6AddrInfo.ae_obcnt = 0;
mae6.ipv6AddrInfo.ae_focnt = 0;
ipif_get_name(ipif, mae6.ipv6AddrIfIndex.o_bytes,
OCTET_LENGTH);
mae6.ipv6AddrIfIndex.o_length =
mi_strlen(mae6.ipv6AddrIfIndex.o_bytes);
mae6.ipv6AddrAddress = ipif->ipif_v6lcl_addr;
mae6.ipv6AddrPfxLength =
ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
mae6.ipv6AddrInfo.ae_subnet = ipif->ipif_v6subnet;
mae6.ipv6AddrInfo.ae_subnet_len =
mae6.ipv6AddrPfxLength;
mae6.ipv6AddrInfo.ae_src_addr = ipif->ipif_v6lcl_addr;
if (ipif->ipif_flags & IPIF_ADDRCONF)
mae6.ipv6AddrType = 1;
else
mae6.ipv6AddrType = 2;
if (ipif->ipif_flags & IPIF_ANYCAST)
mae6.ipv6AddrAnycastFlag = 1;
else
mae6.ipv6AddrAnycastFlag = 2;
if (ipif->ipif_flags & IPIF_NOLOCAL)
mae6.ipv6AddrStatus = 3;
else if (ipif->ipif_flags & IPIF_DEPRECATED)
mae6.ipv6AddrStatus = 2;
else
mae6.ipv6AddrStatus = 1;
mae6.ipv6AddrInfo.ae_mtu = ipif->ipif_ill->ill_mtu;
mae6.ipv6AddrInfo.ae_metric =
ipif->ipif_ill->ill_metric;
mae6.ipv6AddrInfo.ae_pp_dst_addr =
ipif->ipif_v6pp_dst_addr;
mae6.ipv6AddrInfo.ae_flags = ipif->ipif_flags |
ill->ill_flags | ill->ill_phyint->phyint_flags;
mae6.ipv6AddrReasmMaxSize = IP_MAXPACKET;
mae6.ipv6AddrIdentifier = ill->ill_token;
mae6.ipv6AddrIdentifierLen = ill->ill_token_length;
mae6.ipv6AddrReachableTime = ill->ill_reachable_time;
mae6.ipv6AddrRetransmitTime =
ill->ill_reachable_retrans_time;
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&mae6, (int)mae6_size)) {
ip1dbg(("ip_snmp_get_mib2_ip6_addr: failed to "
"allocate %u bytes\n",
(uint_t)mae6_size));
}
}
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_ip6_addr: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip_group_mem(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ipif_t *ipif;
ilm_t *ilm;
ip_member_t ipm;
mblk_t *mp_tail = NULL;
ill_walk_context_t ctx;
zoneid_t zoneid;
mp2ctl = copymsg(mpctl);
zoneid = Q_TO_CONN(q)->conn_zoneid;
optp = (struct opthdr *)&mpctl->b_rptr[
sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = EXPER_IP_GROUP_MEMBERSHIP;
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 (!ill_check_and_refhold(ill))
continue;
rw_exit(&ipst->ips_ill_g_lock);
rw_enter(&ill->ill_mcast_lock, RW_READER);
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
if (ilm->ilm_zoneid != zoneid &&
ilm->ilm_zoneid != ALL_ZONES)
continue;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (!IPIF_IS_CONDEMNED(ipif) &&
ipif->ipif_lcl_addr == ilm->ilm_ifaddr &&
ilm->ilm_ifaddr != INADDR_ANY)
break;
}
if (ipif != NULL) {
ipif_get_name(ipif,
ipm.ipGroupMemberIfIndex.o_bytes,
OCTET_LENGTH);
} else {
ill_get_name(ill,
ipm.ipGroupMemberIfIndex.o_bytes,
OCTET_LENGTH);
}
ipm.ipGroupMemberIfIndex.o_length =
mi_strlen(ipm.ipGroupMemberIfIndex.o_bytes);
ipm.ipGroupMemberAddress = ilm->ilm_addr;
ipm.ipGroupMemberRefCnt = ilm->ilm_refcnt;
ipm.ipGroupMemberFilterMode = ilm->ilm_fmode;
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&ipm, (int)sizeof (ipm))) {
ip1dbg(("ip_snmp_get_mib2_ip_group: "
"failed to allocate %u bytes\n",
(uint_t)sizeof (ipm)));
}
}
rw_exit(&ill->ill_mcast_lock);
ill_refrele(ill);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip6_group_mem(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ilm_t *ilm;
ipv6_member_t ipm6;
mblk_t *mp_tail = NULL;
ill_walk_context_t ctx;
zoneid_t zoneid;
mp2ctl = copymsg(mpctl);
zoneid = Q_TO_CONN(q)->conn_zoneid;
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = EXPER_IP6_GROUP_MEMBERSHIP;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V6(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (!ill_check_and_refhold(ill))
continue;
rw_exit(&ipst->ips_ill_g_lock);
rw_enter(&ill->ill_mcast_lock, RW_READER);
ipm6.ipv6GroupMemberIfIndex = ill->ill_phyint->phyint_ifindex;
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
if (ilm->ilm_zoneid != zoneid &&
ilm->ilm_zoneid != ALL_ZONES)
continue;
ipm6.ipv6GroupMemberAddress = ilm->ilm_v6addr;
ipm6.ipv6GroupMemberRefCnt = ilm->ilm_refcnt;
ipm6.ipv6GroupMemberFilterMode = ilm->ilm_fmode;
if (!snmp_append_data2(mpctl->b_cont,
&mp_tail,
(char *)&ipm6, (int)sizeof (ipm6))) {
ip1dbg(("ip_snmp_get_mib2_ip6_group: "
"failed to allocate %u bytes\n",
(uint_t)sizeof (ipm6)));
}
}
rw_exit(&ill->ill_mcast_lock);
ill_refrele(ill);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip_group_src(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ipif_t *ipif;
ilm_t *ilm;
ip_grpsrc_t ips;
mblk_t *mp_tail = NULL;
ill_walk_context_t ctx;
zoneid_t zoneid;
int i;
slist_t *sl;
mp2ctl = copymsg(mpctl);
zoneid = Q_TO_CONN(q)->conn_zoneid;
optp = (struct opthdr *)&mpctl->b_rptr[
sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = EXPER_IP_GROUP_SOURCES;
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 (!ill_check_and_refhold(ill))
continue;
rw_exit(&ipst->ips_ill_g_lock);
rw_enter(&ill->ill_mcast_lock, RW_READER);
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
sl = ilm->ilm_filter;
if (ilm->ilm_zoneid != zoneid &&
ilm->ilm_zoneid != ALL_ZONES)
continue;
if (SLIST_IS_EMPTY(sl))
continue;
for (ipif = ill->ill_ipif; ipif != NULL;
ipif = ipif->ipif_next) {
if (!IPIF_IS_CONDEMNED(ipif) &&
ipif->ipif_lcl_addr == ilm->ilm_ifaddr &&
ilm->ilm_ifaddr != INADDR_ANY)
break;
}
if (ipif != NULL) {
ipif_get_name(ipif,
ips.ipGroupSourceIfIndex.o_bytes,
OCTET_LENGTH);
} else {
ill_get_name(ill,
ips.ipGroupSourceIfIndex.o_bytes,
OCTET_LENGTH);
}
ips.ipGroupSourceIfIndex.o_length =
mi_strlen(ips.ipGroupSourceIfIndex.o_bytes);
ips.ipGroupSourceGroup = ilm->ilm_addr;
for (i = 0; i < sl->sl_numsrc; i++) {
if (!IN6_IS_ADDR_V4MAPPED(&sl->sl_addr[i]))
continue;
IN6_V4MAPPED_TO_IPADDR(&sl->sl_addr[i],
ips.ipGroupSourceAddress);
if (snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&ips, (int)sizeof (ips)) == 0) {
ip1dbg(("ip_snmp_get_mib2_ip_group_src:"
" failed to allocate %u bytes\n",
(uint_t)sizeof (ips)));
}
}
}
rw_exit(&ill->ill_mcast_lock);
ill_refrele(ill);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip6_group_src(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ilm_t *ilm;
ipv6_grpsrc_t ips6;
mblk_t *mp_tail = NULL;
ill_walk_context_t ctx;
zoneid_t zoneid;
int i;
slist_t *sl;
mp2ctl = copymsg(mpctl);
zoneid = Q_TO_CONN(q)->conn_zoneid;
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = EXPER_IP6_GROUP_SOURCES;
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V6(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
if (!ill_check_and_refhold(ill))
continue;
rw_exit(&ipst->ips_ill_g_lock);
rw_enter(&ill->ill_mcast_lock, RW_READER);
ips6.ipv6GroupSourceIfIndex = ill->ill_phyint->phyint_ifindex;
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
sl = ilm->ilm_filter;
if (ilm->ilm_zoneid != zoneid &&
ilm->ilm_zoneid != ALL_ZONES)
continue;
if (SLIST_IS_EMPTY(sl))
continue;
ips6.ipv6GroupSourceGroup = ilm->ilm_v6addr;
for (i = 0; i < sl->sl_numsrc; i++) {
ips6.ipv6GroupSourceAddress = sl->sl_addr[i];
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&ips6, (int)sizeof (ips6))) {
ip1dbg(("ip_snmp_get_mib2_ip6_"
"group_src: failed to allocate "
"%u bytes\n",
(uint_t)sizeof (ips6)));
}
}
}
rw_exit(&ill->ill_mcast_lock);
ill_refrele(ill);
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_virt_multi(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = EXPER_DVMRP;
optp->name = EXPER_DVMRP_VIF;
if (!ip_mroute_vif(mpctl->b_cont, ipst)) {
ip0dbg(("ip_mroute_vif: failed\n"));
}
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_virt_multi: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_multi_rtable(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = EXPER_DVMRP;
optp->name = EXPER_DVMRP_MRT;
if (!ip_mroute_mrt(mpctl->b_cont, ipst)) {
ip0dbg(("ip_mroute_mrt: failed\n"));
}
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_multi_rtable: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip_route_media(queue_t *q, mblk_t *mpctl, int level,
ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mblk_t *mp3ctl;
mblk_t *mp4ctl;
iproutedata_t ird;
zoneid_t zoneid;
mp2ctl = copymsg(mpctl);
mp3ctl = copymsg(mpctl);
mp4ctl = copymsg(mpctl);
if (mp3ctl == NULL || mp4ctl == NULL) {
freemsg(mp4ctl);
freemsg(mp3ctl);
freemsg(mp2ctl);
freemsg(mpctl);
return (NULL);
}
bzero(&ird, sizeof (ird));
ird.ird_route.lp_head = mpctl->b_cont;
ird.ird_netmedia.lp_head = mp3ctl->b_cont;
ird.ird_attrs.lp_head = mp4ctl->b_cont;
if (level == EXPER_IP_AND_ALL_IRES)
ird.ird_flags |= IRD_REPORT_ALL;
zoneid = Q_TO_CONN(q)->conn_zoneid;
ire_walk_v4(ip_snmp_get2_v4, &ird, zoneid, ipst);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = MIB2_IP_ROUTE;
optp->len = msgdsize(ird.ird_route.lp_head);
ip3dbg(("ip_snmp_get_mib2_ip_route_media: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
ncec_walk(NULL, ip_snmp_get2_v4_media, &ird, ipst);
optp = (struct opthdr *)&mp3ctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = MIB2_IP_MEDIA;
optp->len = msgdsize(ird.ird_netmedia.lp_head);
ip3dbg(("ip_snmp_get_mib2_ip_route_media: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mp3ctl);
optp = (struct opthdr *)&mp4ctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP;
optp->name = EXPER_IP_RTATTR;
optp->len = msgdsize(ird.ird_attrs.lp_head);
ip3dbg(("ip_snmp_get_mib2_ip_route_media: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
if (optp->len == 0)
freemsg(mp4ctl);
else
qreply(q, mp4ctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip6_route_media(queue_t *q, mblk_t *mpctl, int level,
ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
mblk_t *mp3ctl;
mblk_t *mp4ctl;
iproutedata_t ird;
zoneid_t zoneid;
mp2ctl = copymsg(mpctl);
mp3ctl = copymsg(mpctl);
mp4ctl = copymsg(mpctl);
if (mp3ctl == NULL || mp4ctl == NULL) {
freemsg(mp4ctl);
freemsg(mp3ctl);
freemsg(mp2ctl);
freemsg(mpctl);
return (NULL);
}
bzero(&ird, sizeof (ird));
ird.ird_route.lp_head = mpctl->b_cont;
ird.ird_netmedia.lp_head = mp3ctl->b_cont;
ird.ird_attrs.lp_head = mp4ctl->b_cont;
if (level == EXPER_IP_AND_ALL_IRES)
ird.ird_flags |= IRD_REPORT_ALL;
zoneid = Q_TO_CONN(q)->conn_zoneid;
ire_walk_v6(ip_snmp_get2_v6_route, &ird, zoneid, ipst);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = MIB2_IP6_ROUTE;
optp->len = msgdsize(ird.ird_route.lp_head);
ip3dbg(("ip_snmp_get_mib2_ip6_route_media: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
ncec_walk(NULL, ip_snmp_get2_v6_media, &ird, ipst);
optp = (struct opthdr *)&mp3ctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = MIB2_IP6_MEDIA;
optp->len = msgdsize(ird.ird_netmedia.lp_head);
ip3dbg(("ip_snmp_get_mib2_ip6_route_media: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mp3ctl);
optp = (struct opthdr *)&mp4ctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = EXPER_IP_RTATTR;
optp->len = msgdsize(ird.ird_attrs.lp_head);
ip3dbg(("ip_snmp_get_mib2_ip6_route_media: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
if (optp->len == 0)
freemsg(mp4ctl);
else
qreply(q, mp4ctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_ip6(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst,
boolean_t legacy_req)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ill_walk_context_t ctx;
mblk_t *mp_tail = NULL;
mib2_ipv6AddrEntry_t mae6;
mib2_ipIfStatsEntry_t *ise;
size_t ise_size, iae_size;
mp2ctl = copymsg(mpctl);
if (legacy_req) {
ise_size = LEGACY_MIB_SIZE(&ipst->ips_ip6_mib,
mib2_ipIfStatsEntry_t);
iae_size = LEGACY_MIB_SIZE(&mae6, mib2_ipv6AddrEntry_t);
} else {
ise_size = sizeof (mib2_ipIfStatsEntry_t);
iae_size = sizeof (mib2_ipv6AddrEntry_t);
}
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_IP6;
optp->name = 0;
ipst->ips_ip6_mib.ipIfStatsIPVersion = MIB2_INETADDRESSTYPE_ipv6;
ipst->ips_ip6_mib.ipIfStatsIfIndex =
MIB2_UNKNOWN_INTERFACE;
SET_MIB(ipst->ips_ip6_mib.ipIfStatsForwarding,
ipst->ips_ipv6_forwarding ? 1 : 2);
SET_MIB(ipst->ips_ip6_mib.ipIfStatsDefaultHopLimit,
ipst->ips_ipv6_def_hops);
SET_MIB(ipst->ips_ip6_mib.ipIfStatsEntrySize,
sizeof (mib2_ipIfStatsEntry_t));
SET_MIB(ipst->ips_ip6_mib.ipIfStatsAddrEntrySize,
sizeof (mib2_ipv6AddrEntry_t));
SET_MIB(ipst->ips_ip6_mib.ipIfStatsRouteEntrySize,
sizeof (mib2_ipv6RouteEntry_t));
SET_MIB(ipst->ips_ip6_mib.ipIfStatsNetToMediaEntrySize,
sizeof (mib2_ipv6NetToMediaEntry_t));
SET_MIB(ipst->ips_ip6_mib.ipIfStatsMemberEntrySize,
sizeof (ipv6_member_t));
SET_MIB(ipst->ips_ip6_mib.ipIfStatsGroupSourceEntrySize,
sizeof (ipv6_grpsrc_t));
SYNC32_MIB(&ipst->ips_ip6_mib, ipIfStatsInReceives,
ipIfStatsHCInReceives);
SYNC32_MIB(&ipst->ips_ip6_mib, ipIfStatsInDelivers,
ipIfStatsHCInDelivers);
SYNC32_MIB(&ipst->ips_ip6_mib, ipIfStatsOutRequests,
ipIfStatsHCOutRequests);
SYNC32_MIB(&ipst->ips_ip6_mib, ipIfStatsOutForwDatagrams,
ipIfStatsHCOutForwDatagrams);
SYNC32_MIB(&ipst->ips_ip6_mib, ipIfStatsOutMcastPkts,
ipIfStatsHCOutMcastPkts);
SYNC32_MIB(&ipst->ips_ip6_mib, ipIfStatsInMcastPkts,
ipIfStatsHCInMcastPkts);
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&ipst->ips_ip6_mib, (int)ise_size)) {
ip1dbg(("ip_snmp_get_mib2_ip6: failed to allocate %u bytes\n",
(uint_t)ise_size));
} else if (legacy_req) {
ise =
(mib2_ipIfStatsEntry_t *)(mp_tail->b_wptr - (int)ise_size);
SET_MIB(ise->ipIfStatsEntrySize, ise_size);
SET_MIB(ise->ipIfStatsAddrEntrySize, iae_size);
}
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V6(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
ill->ill_ip_mib->ipIfStatsIfIndex =
ill->ill_phyint->phyint_ifindex;
SET_MIB(ill->ill_ip_mib->ipIfStatsForwarding,
ipst->ips_ipv6_forwarding ? 1 : 2);
SET_MIB(ill->ill_ip_mib->ipIfStatsDefaultHopLimit,
ill->ill_max_hops);
SYNC32_MIB(ill->ill_ip_mib, ipIfStatsInReceives,
ipIfStatsHCInReceives);
SYNC32_MIB(ill->ill_ip_mib, ipIfStatsInDelivers,
ipIfStatsHCInDelivers);
SYNC32_MIB(ill->ill_ip_mib, ipIfStatsOutRequests,
ipIfStatsHCOutRequests);
SYNC32_MIB(ill->ill_ip_mib, ipIfStatsOutForwDatagrams,
ipIfStatsHCOutForwDatagrams);
SYNC32_MIB(ill->ill_ip_mib, ipIfStatsOutMcastPkts,
ipIfStatsHCOutMcastPkts);
SYNC32_MIB(ill->ill_ip_mib, ipIfStatsInMcastPkts,
ipIfStatsHCInMcastPkts);
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)ill->ill_ip_mib, (int)ise_size)) {
ip1dbg(("ip_snmp_get_mib2_ip6: failed to allocate "
"%u bytes\n", (uint_t)ise_size));
} else if (legacy_req) {
ise = (mib2_ipIfStatsEntry_t *)(mp_tail->b_wptr -
(int)ise_size);
SET_MIB(ise->ipIfStatsEntrySize, ise_size);
SET_MIB(ise->ipIfStatsAddrEntrySize, iae_size);
}
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_ip6: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static mblk_t *
ip_snmp_get_mib2_icmp6(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
{
struct opthdr *optp;
mblk_t *mp2ctl;
ill_t *ill;
ill_walk_context_t ctx;
mblk_t *mp_tail = NULL;
mp2ctl = copymsg(mpctl);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_ICMP6;
optp->name = 0;
ipst->ips_icmp6_mib.ipv6IfIcmpIfIndex =
MIB2_UNKNOWN_INTERFACE;
ipst->ips_icmp6_mib.ipv6IfIcmpEntrySize =
sizeof (mib2_ipv6IfIcmpEntry_t);
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)&ipst->ips_icmp6_mib,
(int)sizeof (ipst->ips_icmp6_mib))) {
ip1dbg(("ip_snmp_get_mib2_icmp6: failed to allocate %u bytes\n",
(uint_t)sizeof (ipst->ips_icmp6_mib)));
}
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V6(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill)) {
ill->ill_icmp6_mib->ipv6IfIcmpIfIndex =
ill->ill_phyint->phyint_ifindex;
if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
(char *)ill->ill_icmp6_mib,
(int)sizeof (*ill->ill_icmp6_mib))) {
ip1dbg(("ip_snmp_get_mib2_icmp6: failed to allocate "
"%u bytes\n",
(uint_t)sizeof (*ill->ill_icmp6_mib)));
}
}
rw_exit(&ipst->ips_ill_g_lock);
optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
ip3dbg(("ip_snmp_get_mib2_icmp6: level %d, name %d, len %d\n",
(int)optp->level, (int)optp->name, (int)optp->len));
qreply(q, mpctl);
return (mp2ctl);
}
static void
ip_snmp_get2_v4(ire_t *ire, iproutedata_t *ird)
{
ill_t *ill;
mib2_ipRouteEntry_t *re;
mib2_ipAttributeEntry_t iaes;
tsol_ire_gw_secattr_t *attrp;
tsol_gc_t *gc = NULL;
tsol_gcgrp_t *gcgrp = NULL;
ip_stack_t *ipst = ire->ire_ipst;
ASSERT(ire->ire_ipversion == IPV4_VERSION);
if (!(ird->ird_flags & IRD_REPORT_ALL)) {
if (ire->ire_testhidden)
return;
if (ire->ire_type & IRE_IF_CLONE)
return;
}
if ((re = kmem_zalloc(sizeof (*re), KM_NOSLEEP)) == NULL)
return;
if ((attrp = ire->ire_gw_secattr) != NULL) {
mutex_enter(&attrp->igsa_lock);
if ((gc = attrp->igsa_gc) != NULL) {
gcgrp = gc->gc_grp;
ASSERT(gcgrp != NULL);
rw_enter(&gcgrp->gcgrp_rwlock, RW_READER);
}
mutex_exit(&attrp->igsa_lock);
}
re->ipRouteDest = ire->ire_addr;
ill = ire->ire_ill;
re->ipRouteIfIndex.o_length = 0;
if (ill != NULL) {
ill_get_name(ill, re->ipRouteIfIndex.o_bytes, OCTET_LENGTH);
re->ipRouteIfIndex.o_length =
mi_strlen(re->ipRouteIfIndex.o_bytes);
}
re->ipRouteMetric1 = -1;
re->ipRouteMetric2 = -1;
re->ipRouteMetric3 = -1;
re->ipRouteMetric4 = -1;
re->ipRouteNextHop = ire->ire_gateway_addr;
if (ire->ire_flags & (RTF_REJECT | RTF_BLACKHOLE))
re->ipRouteType = 2;
else if (ire->ire_type & IRE_ONLINK)
re->ipRouteType = 3;
else
re->ipRouteType = 4;
re->ipRouteProto = -1;
re->ipRouteAge = gethrestime_sec() - ire->ire_create_time;
re->ipRouteMask = ire->ire_mask;
re->ipRouteMetric5 = -1;
re->ipRouteInfo.re_max_frag = ire->ire_metrics.iulp_mtu;
if (ire->ire_ill != NULL && re->ipRouteInfo.re_max_frag == 0)
re->ipRouteInfo.re_max_frag = ire->ire_ill->ill_mtu;
re->ipRouteInfo.re_frag_flag = 0;
re->ipRouteInfo.re_rtt = 0;
re->ipRouteInfo.re_src_addr = 0;
re->ipRouteInfo.re_ref = ire->ire_refcnt;
re->ipRouteInfo.re_obpkt = ire->ire_ob_pkt_count;
re->ipRouteInfo.re_ibpkt = ire->ire_ib_pkt_count;
re->ipRouteInfo.re_flags = ire->ire_flags;
if (ire->ire_type & IRE_INTERFACE) {
ire_t *child;
rw_enter(&ipst->ips_ire_dep_lock, RW_READER);
child = ire->ire_dep_children;
while (child != NULL) {
re->ipRouteInfo.re_obpkt += child->ire_ob_pkt_count;
re->ipRouteInfo.re_ibpkt += child->ire_ib_pkt_count;
child = child->ire_dep_sib_next;
}
rw_exit(&ipst->ips_ire_dep_lock);
}
if (ire->ire_flags & RTF_DYNAMIC) {
re->ipRouteInfo.re_ire_type = IRE_HOST_REDIRECT;
} else {
re->ipRouteInfo.re_ire_type = ire->ire_type;
}
if (!snmp_append_data2(ird->ird_route.lp_head, &ird->ird_route.lp_tail,
(char *)re, (int)sizeof (*re))) {
ip1dbg(("ip_snmp_get2_v4: failed to allocate %u bytes\n",
(uint_t)sizeof (*re)));
}
if (gc != NULL) {
iaes.iae_routeidx = ird->ird_idx;
iaes.iae_doi = gc->gc_db->gcdb_doi;
iaes.iae_slrange = gc->gc_db->gcdb_slrange;
if (!snmp_append_data2(ird->ird_attrs.lp_head,
&ird->ird_attrs.lp_tail, (char *)&iaes, sizeof (iaes))) {
ip1dbg(("ip_snmp_get2_v4: failed to allocate %u "
"bytes\n", (uint_t)sizeof (iaes)));
}
}
ird->ird_idx++;
kmem_free(re, sizeof (*re));
if (gcgrp != NULL)
rw_exit(&gcgrp->gcgrp_rwlock);
}
static void
ip_snmp_get2_v6_route(ire_t *ire, iproutedata_t *ird)
{
ill_t *ill;
mib2_ipv6RouteEntry_t *re;
mib2_ipAttributeEntry_t iaes;
tsol_ire_gw_secattr_t *attrp;
tsol_gc_t *gc = NULL;
tsol_gcgrp_t *gcgrp = NULL;
ip_stack_t *ipst = ire->ire_ipst;
ASSERT(ire->ire_ipversion == IPV6_VERSION);
if (!(ird->ird_flags & IRD_REPORT_ALL)) {
if (ire->ire_testhidden)
return;
if (ire->ire_type & IRE_IF_CLONE)
return;
}
if ((re = kmem_zalloc(sizeof (*re), KM_NOSLEEP)) == NULL)
return;
if ((attrp = ire->ire_gw_secattr) != NULL) {
mutex_enter(&attrp->igsa_lock);
if ((gc = attrp->igsa_gc) != NULL) {
gcgrp = gc->gc_grp;
ASSERT(gcgrp != NULL);
rw_enter(&gcgrp->gcgrp_rwlock, RW_READER);
}
mutex_exit(&attrp->igsa_lock);
}
re->ipv6RouteDest = ire->ire_addr_v6;
re->ipv6RoutePfxLength = ip_mask_to_plen_v6(&ire->ire_mask_v6);
re->ipv6RouteIndex = 0;
re->ipv6RouteIfIndex.o_length = 0;
ill = ire->ire_ill;
if (ill != NULL) {
ill_get_name(ill, re->ipv6RouteIfIndex.o_bytes, OCTET_LENGTH);
re->ipv6RouteIfIndex.o_length =
mi_strlen(re->ipv6RouteIfIndex.o_bytes);
}
ASSERT(!(ire->ire_type & IRE_BROADCAST));
mutex_enter(&ire->ire_lock);
re->ipv6RouteNextHop = ire->ire_gateway_addr_v6;
mutex_exit(&ire->ire_lock);
if (ire->ire_flags & (RTF_REJECT | RTF_BLACKHOLE))
re->ipv6RouteType = 2;
else if (ire->ire_type & IRE_ONLINK)
re->ipv6RouteType = 3;
else
re->ipv6RouteType = 4;
re->ipv6RouteProtocol = -1;
re->ipv6RoutePolicy = 0;
re->ipv6RouteAge = gethrestime_sec() - ire->ire_create_time;
re->ipv6RouteNextHopRDI = 0;
re->ipv6RouteWeight = 0;
re->ipv6RouteMetric = 0;
re->ipv6RouteInfo.re_max_frag = ire->ire_metrics.iulp_mtu;
if (ire->ire_ill != NULL && re->ipv6RouteInfo.re_max_frag == 0)
re->ipv6RouteInfo.re_max_frag = ire->ire_ill->ill_mtu;
re->ipv6RouteInfo.re_frag_flag = 0;
re->ipv6RouteInfo.re_rtt = 0;
re->ipv6RouteInfo.re_src_addr = ipv6_all_zeros;
re->ipv6RouteInfo.re_obpkt = ire->ire_ob_pkt_count;
re->ipv6RouteInfo.re_ibpkt = ire->ire_ib_pkt_count;
re->ipv6RouteInfo.re_ref = ire->ire_refcnt;
re->ipv6RouteInfo.re_flags = ire->ire_flags;
if (ire->ire_type & IRE_INTERFACE) {
ire_t *child;
rw_enter(&ipst->ips_ire_dep_lock, RW_READER);
child = ire->ire_dep_children;
while (child != NULL) {
re->ipv6RouteInfo.re_obpkt += child->ire_ob_pkt_count;
re->ipv6RouteInfo.re_ibpkt += child->ire_ib_pkt_count;
child = child->ire_dep_sib_next;
}
rw_exit(&ipst->ips_ire_dep_lock);
}
if (ire->ire_flags & RTF_DYNAMIC) {
re->ipv6RouteInfo.re_ire_type = IRE_HOST_REDIRECT;
} else {
re->ipv6RouteInfo.re_ire_type = ire->ire_type;
}
if (!snmp_append_data2(ird->ird_route.lp_head, &ird->ird_route.lp_tail,
(char *)re, (int)sizeof (*re))) {
ip1dbg(("ip_snmp_get2_v6: failed to allocate %u bytes\n",
(uint_t)sizeof (*re)));
}
if (gc != NULL) {
iaes.iae_routeidx = ird->ird_idx;
iaes.iae_doi = gc->gc_db->gcdb_doi;
iaes.iae_slrange = gc->gc_db->gcdb_slrange;
if (!snmp_append_data2(ird->ird_attrs.lp_head,
&ird->ird_attrs.lp_tail, (char *)&iaes, sizeof (iaes))) {
ip1dbg(("ip_snmp_get2_v6: failed to allocate %u "
"bytes\n", (uint_t)sizeof (iaes)));
}
}
ird->ird_idx++;
kmem_free(re, sizeof (*re));
if (gcgrp != NULL)
rw_exit(&gcgrp->gcgrp_rwlock);
}
static void
ip_snmp_get2_v6_media(ncec_t *ncec, void *ptr)
{
iproutedata_t *ird = ptr;
ill_t *ill;
mib2_ipv6NetToMediaEntry_t ntme;
ill = ncec->ncec_ill;
if (ill->ill_isv6 == B_FALSE || ill->ill_net_type == IRE_LOOPBACK)
return;
ntme.ipv6NetToMediaIfIndex = ill->ill_phyint->phyint_ifindex;
ntme.ipv6NetToMediaNetAddress = ncec->ncec_addr;
ntme.ipv6NetToMediaPhysAddress.o_length = ill->ill_phys_addr_length;
if (ncec->ncec_lladdr != NULL) {
bcopy(ncec->ncec_lladdr, ntme.ipv6NetToMediaPhysAddress.o_bytes,
ntme.ipv6NetToMediaPhysAddress.o_length);
}
ntme.ipv6NetToMediaState = ncec->ncec_state;
ntme.ipv6NetToMediaLastUpdated = 0;
if (NCE_MYADDR(ncec)) {
ntme.ipv6NetToMediaType = 4;
} else if (ncec->ncec_flags & NCE_F_PUBLISH) {
ntme.ipv6NetToMediaType = 1;
} else if (ncec->ncec_flags & NCE_F_STATIC) {
ntme.ipv6NetToMediaType = 3;
} else if (ncec->ncec_flags & (NCE_F_MCAST|NCE_F_BCAST)) {
ntme.ipv6NetToMediaType = 1;
} else {
ntme.ipv6NetToMediaType = 2;
}
if (!snmp_append_data2(ird->ird_netmedia.lp_head,
&ird->ird_netmedia.lp_tail, (char *)&ntme, sizeof (ntme))) {
ip1dbg(("ip_snmp_get2_v6_media: failed to allocate %u bytes\n",
(uint_t)sizeof (ntme)));
}
}
int
nce2ace(ncec_t *ncec)
{
int flags = 0;
if (NCE_ISREACHABLE(ncec))
flags |= ACE_F_RESOLVED;
if (ncec->ncec_flags & NCE_F_AUTHORITY)
flags |= ACE_F_AUTHORITY;
if (ncec->ncec_flags & NCE_F_PUBLISH)
flags |= ACE_F_PUBLISH;
if ((ncec->ncec_flags & NCE_F_NONUD) != 0)
flags |= ACE_F_PERMANENT;
if (NCE_MYADDR(ncec))
flags |= (ACE_F_MYADDR | ACE_F_AUTHORITY);
if (ncec->ncec_flags & NCE_F_UNVERIFIED)
flags |= ACE_F_UNVERIFIED;
if (ncec->ncec_flags & NCE_F_AUTHORITY)
flags |= ACE_F_AUTHORITY;
if (ncec->ncec_flags & NCE_F_DELAYED)
flags |= ACE_F_DELAYED;
return (flags);
}
static void
ip_snmp_get2_v4_media(ncec_t *ncec, void *ptr)
{
iproutedata_t *ird = ptr;
ill_t *ill;
mib2_ipNetToMediaEntry_t ntme;
const char *name = "unknown";
ipaddr_t ncec_addr;
ill = ncec->ncec_ill;
if (ill->ill_isv6 || (ncec->ncec_flags & NCE_F_BCAST) ||
ill->ill_net_type == IRE_LOOPBACK)
return;
name = ill->ill_name;
if (NCE_MYADDR(ncec)) {
ntme.ipNetToMediaType = 4;
} else if (ncec->ncec_flags & (NCE_F_MCAST|NCE_F_BCAST|NCE_F_PUBLISH)) {
ntme.ipNetToMediaType = 1;
} else {
ntme.ipNetToMediaType = 3;
}
ntme.ipNetToMediaIfIndex.o_length = MIN(OCTET_LENGTH, strlen(name));
bcopy(name, ntme.ipNetToMediaIfIndex.o_bytes,
ntme.ipNetToMediaIfIndex.o_length);
IN6_V4MAPPED_TO_IPADDR(&ncec->ncec_addr, ncec_addr);
bcopy(&ncec_addr, &ntme.ipNetToMediaNetAddress, sizeof (ncec_addr));
ntme.ipNetToMediaInfo.ntm_mask.o_length = sizeof (ipaddr_t);
ncec_addr = INADDR_BROADCAST;
bcopy(&ncec_addr, ntme.ipNetToMediaInfo.ntm_mask.o_bytes,
sizeof (ncec_addr));
ntme.ipNetToMediaInfo.ntm_flags = nce2ace(ncec);
ntme.ipNetToMediaPhysAddress.o_length =
MIN(OCTET_LENGTH, ill->ill_phys_addr_length);
if (!NCE_ISREACHABLE(ncec))
ntme.ipNetToMediaPhysAddress.o_length = 0;
else {
if (ncec->ncec_lladdr != NULL) {
bcopy(ncec->ncec_lladdr,
ntme.ipNetToMediaPhysAddress.o_bytes,
ntme.ipNetToMediaPhysAddress.o_length);
}
}
if (!snmp_append_data2(ird->ird_netmedia.lp_head,
&ird->ird_netmedia.lp_tail, (char *)&ntme, sizeof (ntme))) {
ip1dbg(("ip_snmp_get2_v4_media: failed to allocate %u bytes\n",
(uint_t)sizeof (ntme)));
}
}
int
ip_snmp_set(queue_t *q, int level, int name, uchar_t *ptr, int len)
{
switch (level) {
case MIB2_IP:
case MIB2_ICMP:
switch (name) {
default:
break;
}
return (1);
default:
return (1);
}
}
void
ip_mib2_add_ip_stats(mib2_ipIfStatsEntry_t *o1, mib2_ipIfStatsEntry_t *o2)
{
UPDATE_MIB(o1, ipIfStatsInHdrErrors, o2->ipIfStatsInHdrErrors);
UPDATE_MIB(o1, ipIfStatsInTooBigErrors, o2->ipIfStatsInTooBigErrors);
UPDATE_MIB(o1, ipIfStatsInNoRoutes, o2->ipIfStatsInNoRoutes);
UPDATE_MIB(o1, ipIfStatsInAddrErrors, o2->ipIfStatsInAddrErrors);
UPDATE_MIB(o1, ipIfStatsInUnknownProtos, o2->ipIfStatsInUnknownProtos);
UPDATE_MIB(o1, ipIfStatsInTruncatedPkts, o2->ipIfStatsInTruncatedPkts);
UPDATE_MIB(o1, ipIfStatsInDiscards, o2->ipIfStatsInDiscards);
UPDATE_MIB(o1, ipIfStatsOutDiscards, o2->ipIfStatsOutDiscards);
UPDATE_MIB(o1, ipIfStatsOutFragOKs, o2->ipIfStatsOutFragOKs);
UPDATE_MIB(o1, ipIfStatsOutFragFails, o2->ipIfStatsOutFragFails);
UPDATE_MIB(o1, ipIfStatsOutFragCreates, o2->ipIfStatsOutFragCreates);
UPDATE_MIB(o1, ipIfStatsReasmReqds, o2->ipIfStatsReasmReqds);
UPDATE_MIB(o1, ipIfStatsReasmOKs, o2->ipIfStatsReasmOKs);
UPDATE_MIB(o1, ipIfStatsReasmFails, o2->ipIfStatsReasmFails);
UPDATE_MIB(o1, ipIfStatsOutNoRoutes, o2->ipIfStatsOutNoRoutes);
UPDATE_MIB(o1, ipIfStatsReasmDuplicates, o2->ipIfStatsReasmDuplicates);
UPDATE_MIB(o1, ipIfStatsReasmPartDups, o2->ipIfStatsReasmPartDups);
UPDATE_MIB(o1, ipIfStatsForwProhibits, o2->ipIfStatsForwProhibits);
UPDATE_MIB(o1, udpInCksumErrs, o2->udpInCksumErrs);
UPDATE_MIB(o1, udpInOverflows, o2->udpInOverflows);
UPDATE_MIB(o1, rawipInOverflows, o2->rawipInOverflows);
UPDATE_MIB(o1, ipIfStatsInWrongIPVersion,
o2->ipIfStatsInWrongIPVersion);
UPDATE_MIB(o1, ipIfStatsOutWrongIPVersion,
o2->ipIfStatsInWrongIPVersion);
UPDATE_MIB(o1, ipIfStatsOutSwitchIPVersion,
o2->ipIfStatsOutSwitchIPVersion);
UPDATE_MIB(o1, ipIfStatsHCInReceives, o2->ipIfStatsHCInReceives);
UPDATE_MIB(o1, ipIfStatsHCInOctets, o2->ipIfStatsHCInOctets);
UPDATE_MIB(o1, ipIfStatsHCInForwDatagrams,
o2->ipIfStatsHCInForwDatagrams);
UPDATE_MIB(o1, ipIfStatsHCInDelivers, o2->ipIfStatsHCInDelivers);
UPDATE_MIB(o1, ipIfStatsHCOutRequests, o2->ipIfStatsHCOutRequests);
UPDATE_MIB(o1, ipIfStatsHCOutForwDatagrams,
o2->ipIfStatsHCOutForwDatagrams);
UPDATE_MIB(o1, ipIfStatsOutFragReqds, o2->ipIfStatsOutFragReqds);
UPDATE_MIB(o1, ipIfStatsHCOutTransmits, o2->ipIfStatsHCOutTransmits);
UPDATE_MIB(o1, ipIfStatsHCOutOctets, o2->ipIfStatsHCOutOctets);
UPDATE_MIB(o1, ipIfStatsHCInMcastPkts, o2->ipIfStatsHCInMcastPkts);
UPDATE_MIB(o1, ipIfStatsHCInMcastOctets, o2->ipIfStatsHCInMcastOctets);
UPDATE_MIB(o1, ipIfStatsHCOutMcastPkts, o2->ipIfStatsHCOutMcastPkts);
UPDATE_MIB(o1, ipIfStatsHCOutMcastOctets,
o2->ipIfStatsHCOutMcastOctets);
UPDATE_MIB(o1, ipIfStatsHCInBcastPkts, o2->ipIfStatsHCInBcastPkts);
UPDATE_MIB(o1, ipIfStatsHCOutBcastPkts, o2->ipIfStatsHCOutBcastPkts);
UPDATE_MIB(o1, ipsecInSucceeded, o2->ipsecInSucceeded);
UPDATE_MIB(o1, ipsecInFailed, o2->ipsecInFailed);
UPDATE_MIB(o1, ipInCksumErrs, o2->ipInCksumErrs);
UPDATE_MIB(o1, tcpInErrs, o2->tcpInErrs);
UPDATE_MIB(o1, udpNoPorts, o2->udpNoPorts);
}
void
ip_mib2_add_icmp6_stats(mib2_ipv6IfIcmpEntry_t *o1, mib2_ipv6IfIcmpEntry_t *o2)
{
UPDATE_MIB(o1, ipv6IfIcmpInMsgs, o2->ipv6IfIcmpInMsgs);
UPDATE_MIB(o1, ipv6IfIcmpInErrors, o2->ipv6IfIcmpInErrors);
UPDATE_MIB(o1, ipv6IfIcmpInDestUnreachs, o2->ipv6IfIcmpInDestUnreachs);
UPDATE_MIB(o1, ipv6IfIcmpInAdminProhibs, o2->ipv6IfIcmpInAdminProhibs);
UPDATE_MIB(o1, ipv6IfIcmpInTimeExcds, o2->ipv6IfIcmpInTimeExcds);
UPDATE_MIB(o1, ipv6IfIcmpInParmProblems, o2->ipv6IfIcmpInParmProblems);
UPDATE_MIB(o1, ipv6IfIcmpInPktTooBigs, o2->ipv6IfIcmpInPktTooBigs);
UPDATE_MIB(o1, ipv6IfIcmpInEchos, o2->ipv6IfIcmpInEchos);
UPDATE_MIB(o1, ipv6IfIcmpInEchoReplies, o2->ipv6IfIcmpInEchoReplies);
UPDATE_MIB(o1, ipv6IfIcmpInRouterSolicits,
o2->ipv6IfIcmpInRouterSolicits);
UPDATE_MIB(o1, ipv6IfIcmpInRouterAdvertisements,
o2->ipv6IfIcmpInRouterAdvertisements);
UPDATE_MIB(o1, ipv6IfIcmpInNeighborSolicits,
o2->ipv6IfIcmpInNeighborSolicits);
UPDATE_MIB(o1, ipv6IfIcmpInNeighborAdvertisements,
o2->ipv6IfIcmpInNeighborAdvertisements);
UPDATE_MIB(o1, ipv6IfIcmpInRedirects, o2->ipv6IfIcmpInRedirects);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembQueries,
o2->ipv6IfIcmpInGroupMembQueries);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembResponses,
o2->ipv6IfIcmpInGroupMembResponses);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembReductions,
o2->ipv6IfIcmpInGroupMembReductions);
UPDATE_MIB(o1, ipv6IfIcmpOutMsgs, o2->ipv6IfIcmpOutMsgs);
UPDATE_MIB(o1, ipv6IfIcmpOutErrors, o2->ipv6IfIcmpOutErrors);
UPDATE_MIB(o1, ipv6IfIcmpOutDestUnreachs,
o2->ipv6IfIcmpOutDestUnreachs);
UPDATE_MIB(o1, ipv6IfIcmpOutAdminProhibs,
o2->ipv6IfIcmpOutAdminProhibs);
UPDATE_MIB(o1, ipv6IfIcmpOutTimeExcds, o2->ipv6IfIcmpOutTimeExcds);
UPDATE_MIB(o1, ipv6IfIcmpOutParmProblems,
o2->ipv6IfIcmpOutParmProblems);
UPDATE_MIB(o1, ipv6IfIcmpOutPktTooBigs, o2->ipv6IfIcmpOutPktTooBigs);
UPDATE_MIB(o1, ipv6IfIcmpOutEchos, o2->ipv6IfIcmpOutEchos);
UPDATE_MIB(o1, ipv6IfIcmpOutEchoReplies, o2->ipv6IfIcmpOutEchoReplies);
UPDATE_MIB(o1, ipv6IfIcmpOutRouterSolicits,
o2->ipv6IfIcmpOutRouterSolicits);
UPDATE_MIB(o1, ipv6IfIcmpOutRouterAdvertisements,
o2->ipv6IfIcmpOutRouterAdvertisements);
UPDATE_MIB(o1, ipv6IfIcmpOutNeighborSolicits,
o2->ipv6IfIcmpOutNeighborSolicits);
UPDATE_MIB(o1, ipv6IfIcmpOutNeighborAdvertisements,
o2->ipv6IfIcmpOutNeighborAdvertisements);
UPDATE_MIB(o1, ipv6IfIcmpOutRedirects, o2->ipv6IfIcmpOutRedirects);
UPDATE_MIB(o1, ipv6IfIcmpOutGroupMembQueries,
o2->ipv6IfIcmpOutGroupMembQueries);
UPDATE_MIB(o1, ipv6IfIcmpOutGroupMembResponses,
o2->ipv6IfIcmpOutGroupMembResponses);
UPDATE_MIB(o1, ipv6IfIcmpOutGroupMembReductions,
o2->ipv6IfIcmpOutGroupMembReductions);
UPDATE_MIB(o1, ipv6IfIcmpInOverflows, o2->ipv6IfIcmpInOverflows);
UPDATE_MIB(o1, ipv6IfIcmpBadHoplimit, o2->ipv6IfIcmpBadHoplimit);
UPDATE_MIB(o1, ipv6IfIcmpInBadNeighborAdvertisements,
o2->ipv6IfIcmpInBadNeighborAdvertisements);
UPDATE_MIB(o1, ipv6IfIcmpInBadNeighborSolicitations,
o2->ipv6IfIcmpInBadNeighborSolicitations);
UPDATE_MIB(o1, ipv6IfIcmpInBadRedirects, o2->ipv6IfIcmpInBadRedirects);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembTotal,
o2->ipv6IfIcmpInGroupMembTotal);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembBadQueries,
o2->ipv6IfIcmpInGroupMembBadQueries);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembBadReports,
o2->ipv6IfIcmpInGroupMembBadReports);
UPDATE_MIB(o1, ipv6IfIcmpInGroupMembOurReports,
o2->ipv6IfIcmpInGroupMembOurReports);
}
boolean_t
ip_source_routed(ipha_t *ipha, ip_stack_t *ipst)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
if (IS_SIMPLE_IPH(ipha)) {
ip2dbg(("not source routed\n"));
return (B_FALSE);
}
dst = ipha->ipha_dst;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
ASSERT((opts.ipoptp_flags & IPOPTP_ERROR) == 0);
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ip2dbg(("ip_source_routed: opt %d, len %d\n",
optval, optlen));
switch (optval) {
uint32_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
if (ip_type_v4(dst, ipst) != IRE_LOCAL) {
ip2dbg(("ip_source_routed: not next"
" source route 0x%x\n",
ntohl(dst)));
return (B_FALSE);
}
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg(("ip_source_routed: end of SR\n"));
return (B_FALSE);
}
return (B_TRUE);
}
}
ip2dbg(("not source routed\n"));
return (B_FALSE);
}
void
ip_unbind(conn_t *connp)
{
ASSERT(!MUTEX_HELD(&connp->conn_lock));
if (is_system_labeled() && connp->conn_anon_port) {
(void) tsol_mlp_anon(crgetzone(connp->conn_cred),
connp->conn_mlp_type, connp->conn_proto,
ntohs(connp->conn_lport), B_FALSE);
connp->conn_anon_port = 0;
}
connp->conn_mlp_type = mlptSingle;
ipcl_hash_remove(connp);
}
int
conn_ipsec_length(conn_t *connp)
{
ipsec_latch_t *ipl;
ipl = connp->conn_latch;
if (ipl == NULL)
return (0);
if (connp->conn_ixa->ixa_ipsec_policy == NULL)
return (0);
return (connp->conn_ixa->ixa_ipsec_policy->ipsp_act->ipa_ovhd);
}
int
ipsec_out_extra_length(ip_xmit_attr_t *ixa)
{
ipsec_action_t *a;
if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE))
return (0);
a = ixa->ixa_ipsec_action;
if (a == NULL) {
ASSERT(ixa->ixa_ipsec_policy != NULL);
a = ixa->ixa_ipsec_policy->ipsp_act;
}
ASSERT(a != NULL);
return (a->ipa_ovhd);
}
ipaddr_t
ip_get_dst(ipha_t *ipha)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
uint32_t off;
dst = ipha->ipha_dst;
if (IS_SIMPLE_IPH(ipha))
return (dst);
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ASSERT((opts.ipoptp_flags & IPOPTP_ERROR) == 0);
switch (optval) {
case IPOPT_SSRR:
case IPOPT_LSRR:
off = opt[IPOPT_OFFSET];
if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) {
off = optlen - IP_ADDR_LEN;
bcopy(&opt[off], &dst, IP_ADDR_LEN);
}
return (dst);
default:
break;
}
}
return (dst);
}
int
ip_fragment_v4(mblk_t *mp_orig, nce_t *nce, iaflags_t ixaflags,
uint_t pkt_len, uint32_t max_frag, uint32_t xmit_hint, zoneid_t szone,
zoneid_t nolzid, pfirepostfrag_t postfragfn, uintptr_t *ixa_cookie)
{
int i1;
int hdr_len;
mblk_t *hdr_mp;
ipha_t *ipha;
int ip_data_end;
int len;
mblk_t *mp = mp_orig;
int offset;
ill_t *ill = nce->nce_ill;
ip_stack_t *ipst = ill->ill_ipst;
mblk_t *carve_mp;
uint32_t frag_flag;
uint_t priority = mp->b_band;
int error = 0;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragReqds);
if (pkt_len != msgdsize(mp)) {
ip0dbg(("Packet length mismatch: %d, %ld\n",
pkt_len, msgdsize(mp)));
freemsg(mp);
return (EINVAL);
}
if (max_frag == 0) {
ip1dbg(("ip_fragment_v4: max_frag is zero. Dropping packet\n"));
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: zero max_frag", mp, ill);
freemsg(mp);
return (EINVAL);
}
ASSERT(MBLKL(mp) >= sizeof (ipha_t));
ipha = (ipha_t *)mp->b_rptr;
ASSERT(ntohs(ipha->ipha_length) == pkt_len);
frag_flag = ntohs(ipha->ipha_fragment_offset_and_flags) & IPH_DF;
offset = ntohs(ipha->ipha_fragment_offset_and_flags) & IPH_OFFSET;
if (((max_frag - ntohs(ipha->ipha_length)) & ~7) < 8) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: bad starting offset", mp, ill);
freemsg(mp);
return (EINVAL);
}
hdr_len = IPH_HDR_LENGTH(ipha);
ipha->ipha_hdr_checksum = 0;
len = (max_frag - hdr_len) & ~7;
hdr_mp = ip_fragment_copyhdr((uchar_t *)ipha, hdr_len, offset, ipst,
mp);
if (hdr_mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: no hdr_mp", mp, ill);
freemsg(mp);
return (ENOBUFS);
}
i1 = offset | IPH_MF | frag_flag;
ipha->ipha_fragment_offset_and_flags = htons((uint16_t)i1);
offset <<= 3;
ip_data_end = offset + ntohs(ipha->ipha_length) - hdr_len;
i1 = len + hdr_len;
ASSERT(i1 <= IP_MAXPACKET);
ipha->ipha_length = htons((uint16_t)i1);
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
if (!(mp = ip_carve_mp(&mp_orig, i1))) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: could not carve mp", mp_orig, ill);
freeb(hdr_mp);
freemsg(mp_orig);
return (ENOBUFS);
}
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates);
error = postfragfn(mp, nce, ixaflags, i1, xmit_hint, szone, nolzid,
ixa_cookie);
if (error != 0 && error != EWOULDBLOCK) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: postfragfn failed", mp_orig, ill);
freeb(hdr_mp);
freemsg(mp_orig);
return (error);
}
ixaflags &= ~IXAF_REACH_CONF;
offset += len;
hdr_len = IPH_HDR_LENGTH(hdr_mp->b_rptr);
for (;;) {
uint16_t offset_and_flags;
uint16_t ip_len;
if (ip_data_end - offset > len) {
if (!(carve_mp = ip_carve_mp(&mp_orig, len))) {
mp = NULL;
break;
}
if (carve_mp->b_datap->db_ref == 1 &&
hdr_mp->b_wptr - hdr_mp->b_rptr <
carve_mp->b_rptr - carve_mp->b_datap->db_base) {
carve_mp->b_rptr -= hdr_mp->b_wptr -
hdr_mp->b_rptr;
bcopy(hdr_mp->b_rptr, carve_mp->b_rptr,
hdr_mp->b_wptr - hdr_mp->b_rptr);
mp = carve_mp;
} else {
if (!(mp = copyb(hdr_mp))) {
freemsg(carve_mp);
break;
}
mp->b_band = priority;
mp->b_cont = carve_mp;
}
ipha = (ipha_t *)mp->b_rptr;
offset_and_flags = IPH_MF;
} else {
len = ip_data_end - offset;
if (!(carve_mp = ip_carve_mp(&mp_orig, len))) {
mp = NULL;
break;
}
if (carve_mp->b_datap->db_ref == 1 &&
hdr_mp->b_wptr - hdr_mp->b_rptr <
carve_mp->b_rptr - carve_mp->b_datap->db_base) {
carve_mp->b_rptr -= hdr_mp->b_wptr -
hdr_mp->b_rptr;
bcopy(hdr_mp->b_rptr, carve_mp->b_rptr,
hdr_mp->b_wptr - hdr_mp->b_rptr);
mp = carve_mp;
freeb(hdr_mp);
hdr_mp = mp;
} else {
mp = hdr_mp;
mp->b_band = priority;
mp->b_cont = carve_mp;
}
ipha = (ipha_t *)mp->b_rptr;
offset_and_flags =
ntohs(ipha->ipha_fragment_offset_and_flags) &
IPH_MF;
}
offset_and_flags |= (uint16_t)(offset >> 3);
offset_and_flags |= (uint16_t)frag_flag;
ipha->ipha_fragment_offset_and_flags = htons(offset_and_flags);
ip_len = (uint16_t)(len + hdr_len);
ipha->ipha_length = htons(ip_len);
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates);
error = postfragfn(mp, nce, ixaflags, ip_len, xmit_hint, szone,
nolzid, ixa_cookie);
if (mp == hdr_mp) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragOKs);
return (error);
}
if (error != 0 && error != EWOULDBLOCK) {
DTRACE_PROBE2(ip__xmit__frag__fail, ill_t *, ill,
mblk_t *, hdr_mp);
break;
}
offset += len;
}
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
ip_drop_output("FragFails: loop ended", NULL, ill);
if (mp != hdr_mp)
freeb(hdr_mp);
if (mp != mp_orig)
freemsg(mp_orig);
return (error);
}
static mblk_t *
ip_fragment_copyhdr(uchar_t *rptr, int hdr_len, int offset, ip_stack_t *ipst,
mblk_t *src)
{
mblk_t *mp;
uchar_t *up;
mp = allocb_tmpl(ipst->ips_ip_wroff_extra + hdr_len, src);
if (!mp)
return (mp);
mp->b_rptr += ipst->ips_ip_wroff_extra;
if (hdr_len == IP_SIMPLE_HDR_LENGTH || offset != 0) {
bcopy(rptr, mp->b_rptr, hdr_len);
mp->b_wptr += hdr_len + ipst->ips_ip_wroff_extra;
return (mp);
}
up = mp->b_rptr;
bcopy(rptr, up, IP_SIMPLE_HDR_LENGTH);
up += IP_SIMPLE_HDR_LENGTH;
rptr += IP_SIMPLE_HDR_LENGTH;
hdr_len -= IP_SIMPLE_HDR_LENGTH;
while (hdr_len > 0) {
uint32_t optval;
uint32_t optlen;
optval = *rptr;
if (optval == IPOPT_EOL)
break;
if (optval == IPOPT_NOP)
optlen = 1;
else
optlen = rptr[1];
if (optval & IPOPT_COPY) {
bcopy(rptr, up, optlen);
up += optlen;
}
rptr += optlen;
hdr_len -= optlen;
}
for (hdr_len = up - (mp->b_rptr + IP_SIMPLE_HDR_LENGTH);
hdr_len & 0x3; hdr_len++)
*up++ = IPOPT_EOL;
mp->b_wptr = up;
mp->b_rptr[0] = (uint8_t)((IP_VERSION << 4) | ((up - mp->b_rptr) >> 2));
return (mp);
}
void
ip_output_local_options(ipha_t *ipha, ip_stack_t *ipst)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
uint32_t ts;
timestruc_t now;
uint32_t off = 0;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ASSERT((opts.ipoptp_flags & IPOPTP_ERROR) == 0);
switch (optval) {
case IPOPT_SSRR:
case IPOPT_LSRR:
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
break;
}
if (optval == IPOPT_SSRR) {
return;
}
opt[IPOPT_OLEN] = (uint8_t)off;
while (off < optlen) {
opt[off++] = IPOPT_NOP;
}
break;
case IPOPT_RR:
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
ip1dbg((
"ip_output_local_options: end of RR\n"));
break;
}
dst = htonl(INADDR_LOOPBACK);
bcopy(&dst, (char *)opt + off, IP_ADDR_LEN);
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
break;
case IPOPT_TS:
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_TSONLY:
off = IPOPT_TS_TIMELEN;
break;
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
off = opt[IPOPT_OFFSET] - 1;
bcopy((char *)opt + off, &dst, IP_ADDR_LEN);
if (ip_type_v4(dst, ipst) != IRE_LOCAL) {
break;
}
case IPOPT_TS_TSANDADDR:
off = IP_ADDR_LEN + IPOPT_TS_TIMELEN;
break;
default:
cmn_err(CE_PANIC, "ip_output_local_options: "
"unknown IT - bug in ip_output_options?\n");
}
if (opt[IPOPT_OFFSET] - 1 + off > optlen) {
off = (opt[IPOPT_POS_OV_FLG] >> 4) + 1;
opt[IPOPT_POS_OV_FLG] = (uint8_t)
(opt[IPOPT_POS_OV_FLG] & 0x0F) |
(off << 4);
break;
}
off = opt[IPOPT_OFFSET] - 1;
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
case IPOPT_TS_TSANDADDR:
dst = htonl(INADDR_LOOPBACK);
bcopy(&dst, (char *)opt + off, IP_ADDR_LEN);
opt[IPOPT_OFFSET] += IP_ADDR_LEN;
case IPOPT_TS_TSONLY:
off = opt[IPOPT_OFFSET] - 1;
gethrestime(&now);
ts = (now.tv_sec % (24 * 60 * 60)) * 1000 +
NSEC2MSEC(now.tv_nsec);
bcopy(&ts, (char *)opt + off, IPOPT_TS_TIMELEN);
opt[IPOPT_OFFSET] += IPOPT_TS_TIMELEN;
break;
}
break;
}
}
}
static mblk_t *
ip_xmit_attach_llhdr(mblk_t *mp, nce_t *nce)
{
uint_t hlen;
mblk_t *mp1;
uint_t priority;
uchar_t *rptr;
rptr = mp->b_rptr;
ASSERT(DB_TYPE(mp) == M_DATA);
priority = mp->b_band;
ASSERT(nce != NULL);
if ((mp1 = nce->nce_fp_mp) != NULL) {
hlen = MBLKL(mp1);
if (hlen != 0 && (rptr - mp->b_datap->db_base) >= hlen) {
rptr -= hlen;
bcopy(mp1->b_rptr, rptr, hlen);
mp->b_rptr = rptr;
return (mp);
}
mp1 = copyb(mp1);
if (mp1 == NULL) {
ill_t *ill = nce->nce_ill;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
freemsg(mp);
return (NULL);
}
mp1->b_band = priority;
mp1->b_cont = mp;
DB_CKSUMSTART(mp1) = DB_CKSUMSTART(mp);
DB_CKSUMSTUFF(mp1) = DB_CKSUMSTUFF(mp);
DB_CKSUMEND(mp1) = DB_CKSUMEND(mp);
DB_CKSUMFLAGS(mp1) = DB_CKSUMFLAGS(mp);
DB_LSOMSS(mp1) = DB_LSOMSS(mp);
DTRACE_PROBE1(ip__xmit__copyb, (mblk_t *), mp1);
return (mp1);
}
mp1 = copyb(nce->nce_dlur_mp);
if (mp1 == NULL) {
ill_t *ill = nce->nce_ill;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
freemsg(mp);
return (NULL);
}
mp1->b_cont = mp;
if (priority != 0) {
mp1->b_band = priority;
((dl_unitdata_req_t *)(mp1->b_rptr))->dl_priority.dl_max =
priority;
}
return (mp1);
}
int
ip_output_post_ipsec(mblk_t *mp, ip_xmit_attr_t *ixa)
{
iaflags_t ixaflags = ixa->ixa_flags;
uint_t pktlen;
if (ixaflags & IXAF_IS_IPV4) {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
pktlen = ntohs(ipha->ipha_length);
} else {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
ASSERT(IPH_HDR_VERSION(mp->b_rptr) == IPV6_VERSION);
pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
}
if (ixa->ixa_ipsec_esp_sa != NULL) {
IPSA_REFRELE(ixa->ixa_ipsec_esp_sa);
ixa->ixa_ipsec_esp_sa = NULL;
}
if (ixa->ixa_ipsec_ah_sa != NULL) {
IPSA_REFRELE(ixa->ixa_ipsec_ah_sa);
ixa->ixa_ipsec_ah_sa = NULL;
}
if ((ixa->ixa_flags & IXAF_IPV6_ADD_FRAGHDR) ||
pktlen > ixa->ixa_fragsize) {
if (ixaflags & IXAF_IS_IPV4) {
ASSERT(!(ixa->ixa_flags & IXAF_IPV6_ADD_FRAGHDR));
return (ip_fragment_v4(mp, ixa->ixa_nce, ixa->ixa_flags,
pktlen, ixa->ixa_fragsize,
ixa->ixa_xmit_hint, ixa->ixa_zoneid,
ixa->ixa_no_loop_zoneid, ixa->ixa_postfragfn,
&ixa->ixa_cookie));
} else {
mp = ip_fraghdr_add_v6(mp, ixa->ixa_ident, ixa);
if (mp == NULL) {
return (ENOMEM);
}
pktlen += sizeof (ip6_frag_t);
if (pktlen > ixa->ixa_fragsize) {
return (ip_fragment_v6(mp, ixa->ixa_nce,
ixa->ixa_flags, pktlen,
ixa->ixa_fragsize, ixa->ixa_xmit_hint,
ixa->ixa_zoneid, ixa->ixa_no_loop_zoneid,
ixa->ixa_postfragfn, &ixa->ixa_cookie));
}
}
}
return ((ixa->ixa_postfragfn)(mp, ixa->ixa_nce, ixa->ixa_flags,
pktlen, ixa->ixa_xmit_hint, ixa->ixa_zoneid,
ixa->ixa_no_loop_zoneid, NULL));
}
void
ip_input_post_ipsec(mblk_t *mp, ip_recv_attr_t *ira)
{
iaflags_t iraflags = ira->ira_flags;
if (iraflags & IRAF_IS_IPV4) {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
ira->ira_pktlen = ntohs(ipha->ipha_length);
ira->ira_ip_hdr_length = IPH_HDR_LENGTH(ipha);
ira->ira_protocol = ipha->ipha_protocol;
ip_fanout_v4(mp, ipha, ira);
} else {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
uint8_t *nexthdrp;
ASSERT(IPH_HDR_VERSION(mp->b_rptr) == IPV6_VERSION);
ira->ira_pktlen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &ira->ira_ip_hdr_length,
&nexthdrp)) {
BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ira->ira_ill);
freemsg(mp);
return;
}
ira->ira_protocol = *nexthdrp;
ip_fanout_v6(mp, ip6h, ira);
}
}
static boolean_t
ipsec_out_select_sa(mblk_t *mp, ip_xmit_attr_t *ixa)
{
boolean_t need_ah_acquire = B_FALSE, need_esp_acquire = B_FALSE;
ipsec_policy_t *pp;
ipsec_action_t *ap;
ASSERT(ixa->ixa_flags & IXAF_IPSEC_SECURE);
ASSERT((ixa->ixa_ipsec_policy != NULL) ||
(ixa->ixa_ipsec_action != NULL));
ap = ixa->ixa_ipsec_action;
if (ap == NULL) {
pp = ixa->ixa_ipsec_policy;
ASSERT(pp != NULL);
ap = pp->ipsp_act;
ASSERT(ap != NULL);
}
if (ap->ipa_want_esp) {
if (ixa->ixa_ipsec_esp_sa == NULL) {
need_esp_acquire = !ipsec_outbound_sa(mp, ixa,
IPPROTO_ESP);
}
ASSERT(need_esp_acquire || ixa->ixa_ipsec_esp_sa != NULL);
}
if (ap->ipa_want_ah) {
if (ixa->ixa_ipsec_ah_sa == NULL) {
need_ah_acquire = !ipsec_outbound_sa(mp, ixa,
IPPROTO_AH);
}
ASSERT(need_ah_acquire || ixa->ixa_ipsec_ah_sa != NULL);
if (ap->ipa_want_esp && need_ah_acquire)
need_esp_acquire = B_TRUE;
}
if (need_ah_acquire || need_esp_acquire) {
if (ixa->ixa_ipsec_ah_sa != NULL) {
IPSA_REFRELE(ixa->ixa_ipsec_ah_sa);
ixa->ixa_ipsec_ah_sa = NULL;
}
if (ixa->ixa_ipsec_esp_sa != NULL) {
IPSA_REFRELE(ixa->ixa_ipsec_esp_sa);
ixa->ixa_ipsec_esp_sa = NULL;
}
sadb_acquire(mp, ixa, need_ah_acquire, need_esp_acquire);
return (B_FALSE);
}
return (B_TRUE);
}
int
ipsec_out_process(mblk_t *mp, ip_xmit_attr_t *ixa)
{
ill_t *ill = ixa->ixa_nce->nce_ill;
ip_stack_t *ipst = ixa->ixa_ipst;
ipsec_stack_t *ipss;
ipsec_policy_t *pp;
ipsec_action_t *ap;
ASSERT(ixa->ixa_flags & IXAF_IPSEC_SECURE);
ASSERT((ixa->ixa_ipsec_policy != NULL) ||
(ixa->ixa_ipsec_action != NULL));
ipss = ipst->ips_netstack->netstack_ipsec;
if (!ipsec_loaded(ipss)) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_packet(mp, B_TRUE, ill,
DROPPER(ipss, ipds_ip_ipsec_not_loaded),
&ipss->ipsec_dropper);
return (ENOTSUP);
}
ap = ixa->ixa_ipsec_action;
if (ap == NULL) {
pp = ixa->ixa_ipsec_policy;
ASSERT(pp != NULL);
ap = pp->ipsp_act;
ASSERT(ap != NULL);
}
switch (ap->ipa_act.ipa_type) {
case IPSEC_ACT_DISCARD:
case IPSEC_ACT_REJECT:
ip_drop_packet(mp, B_FALSE, ill,
DROPPER(ipss, ipds_spd_explicit), &ipss->ipsec_spd_dropper);
return (EHOSTUNREACH);
case IPSEC_ACT_BYPASS:
return (ip_output_post_ipsec(mp, ixa));
}
if ((ixa->ixa_flags & IXAF_IS_IPV4) && ap->ipa_want_se) {
ipha_t *oipha, *iipha;
mblk_t *outer_mp, *inner_mp;
if ((outer_mp = allocb(sizeof (ipha_t), BPRI_HI)) == NULL) {
(void) mi_strlog(ill->ill_rq, 0,
SL_ERROR|SL_TRACE|SL_CONSOLE,
"ipsec_out_process: "
"Self-Encapsulation failed: Out of memory\n");
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards", mp, ill);
freemsg(mp);
return (ENOBUFS);
}
inner_mp = mp;
ASSERT(inner_mp->b_datap->db_type == M_DATA);
oipha = (ipha_t *)outer_mp->b_rptr;
iipha = (ipha_t *)inner_mp->b_rptr;
*oipha = *iipha;
outer_mp->b_wptr += sizeof (ipha_t);
oipha->ipha_length = htons(ntohs(iipha->ipha_length) +
sizeof (ipha_t));
oipha->ipha_protocol = IPPROTO_ENCAP;
oipha->ipha_version_and_hdr_length =
IP_SIMPLE_HDR_VERSION;
oipha->ipha_hdr_checksum = 0;
oipha->ipha_hdr_checksum = ip_csum_hdr(oipha);
outer_mp->b_cont = inner_mp;
mp = outer_mp;
ixa->ixa_flags |= IXAF_IPSEC_TUNNEL;
}
if (((ap->ipa_want_ah && (ixa->ixa_ipsec_ah_sa == NULL)) ||
(ap->ipa_want_esp && (ixa->ixa_ipsec_esp_sa == NULL))) &&
!ipsec_out_select_sa(mp, ixa))
return (0);
if (ap->ipa_want_esp) {
ASSERT(ixa->ixa_ipsec_esp_sa != NULL);
mp = ixa->ixa_ipsec_esp_sa->ipsa_output_func(mp, ixa);
if (mp == NULL) {
return (0);
}
}
if (ap->ipa_want_ah) {
ASSERT(ixa->ixa_ipsec_ah_sa != NULL);
mp = ixa->ixa_ipsec_ah_sa->ipsa_output_func(mp, ixa);
if (mp == NULL) {
return (0);
}
}
return (ip_output_post_ipsec(mp, ixa));
}
void
ip_reprocess_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
{
struct iocblk *iocp;
mblk_t *mp1;
ip_ioctl_cmd_t *ipip;
int err;
sin_t *sin;
struct lifreq *lifr;
struct ifreq *ifr;
iocp = (struct iocblk *)mp->b_rptr;
ASSERT(ipsq != NULL);
mp1 = mp->b_cont->b_cont;
ipip = ip_sioctl_lookup(iocp->ioc_cmd);
if (ipip->ipi_cmd == SIOCSLIFNAME || ipip->ipi_cmd == IF_UNITSEL) {
ill_t *ill = q->q_ptr;
ipsq_current_start(ipsq, ill->ill_ipif, ipip->ipi_cmd);
}
ASSERT(ipsq->ipsq_xop->ipx_current_ipif != NULL);
if (ipip->ipi_cmd_type == IF_CMD) {
ifr = (struct ifreq *)mp1->b_rptr;
sin = (sin_t *)&ifr->ifr_addr;
} else if (ipip->ipi_cmd_type == LIF_CMD) {
lifr = (struct lifreq *)mp1->b_rptr;
sin = (sin_t *)&lifr->lifr_addr;
} else {
sin = NULL;
}
err = (*ipip->ipi_func_restart)(ipsq->ipsq_xop->ipx_current_ipif, sin,
q, mp, ipip, mp1->b_rptr);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_reprocess_ioctl finish",
int, ipip->ipi_cmd,
ill_t *, ipsq->ipsq_xop->ipx_current_ipif->ipif_ill,
ipif_t *, ipsq->ipsq_xop->ipx_current_ipif);
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), ipsq);
}
void
ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
{
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
ip_ioctl_cmd_t *ipip = arg;
ip_extract_func_t *extract_funcp;
cmd_info_t ci;
int err;
boolean_t entered_ipsq = B_FALSE;
ip3dbg(("ip_process_ioctl: ioctl %X\n", iocp->ioc_cmd));
if (ipip == NULL)
ipip = ip_sioctl_lookup(iocp->ioc_cmd);
if (ipip->ipi_cmd == SIOCLIFADDIF) {
err = ip_sioctl_addif(NULL, NULL, q, mp, NULL, NULL);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_process_ioctl finish",
int, ipip->ipi_cmd, ill_t *, NULL, ipif_t *, NULL);
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), NULL);
return;
}
ci.ci_ipif = NULL;
extract_funcp = NULL;
switch (ipip->ipi_cmd_type) {
case MISC_CMD:
case MSFILT_CMD:
if (ipip->ipi_cmd == IF_UNITSEL) {
ci.ci_ipif = ((ill_t *)q->q_ptr)->ill_ipif;
ipif_refhold(ci.ci_ipif);
}
err = 0;
ci.ci_sin = NULL;
ci.ci_sin6 = NULL;
ci.ci_lifr = NULL;
extract_funcp = NULL;
break;
case IF_CMD:
case LIF_CMD:
extract_funcp = ip_extract_lifreq;
break;
case ARP_CMD:
case XARP_CMD:
extract_funcp = ip_extract_arpreq;
break;
default:
ASSERT(0);
}
if (extract_funcp != NULL) {
err = (*extract_funcp)(q, mp, ipip, &ci);
if (err != 0) {
DTRACE_PROBE4(ipif__ioctl,
char *, "ip_process_ioctl finish err",
int, ipip->ipi_cmd, ill_t *, NULL, ipif_t *, NULL);
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), NULL);
return;
}
ASSERT(ci.ci_ipif != NULL);
}
if (!(ipip->ipi_flags & IPI_WR)) {
err = (*ipip->ipi_func)(ci.ci_ipif, ci.ci_sin, q, mp, ipip,
ci.ci_lifr);
if (ci.ci_ipif != NULL) {
DTRACE_PROBE4(ipif__ioctl,
char *, "ip_process_ioctl finish RD",
int, ipip->ipi_cmd, ill_t *, ci.ci_ipif->ipif_ill,
ipif_t *, ci.ci_ipif);
ipif_refrele(ci.ci_ipif);
} else {
DTRACE_PROBE4(ipif__ioctl,
char *, "ip_process_ioctl finish RD",
int, ipip->ipi_cmd, ill_t *, NULL, ipif_t *, NULL);
}
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), NULL);
return;
}
ASSERT(ci.ci_ipif != NULL);
ASSERT(ipsq == NULL || IAM_WRITER_IPSQ(ipsq));
if (ipsq == NULL) {
ipsq = ipsq_try_enter(ci.ci_ipif, NULL, q, mp, ip_process_ioctl,
NEW_OP, B_TRUE);
if (ipsq == NULL) {
ipif_refrele(ci.ci_ipif);
return;
}
entered_ipsq = B_TRUE;
}
ipif_refrele(ci.ci_ipif);
ipsq_current_start(ipsq, ci.ci_ipif, ipip->ipi_cmd);
err = (*ipip->ipi_func)(ci.ci_ipif, ci.ci_sin, q, mp, ipip, ci.ci_lifr);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_process_ioctl finish WR",
int, ipip->ipi_cmd,
ill_t *, ci.ci_ipif == NULL ? NULL : ci.ci_ipif->ipif_ill,
ipif_t *, ci.ci_ipif);
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), ipsq);
if (entered_ipsq)
ipsq_exit(ipsq);
}
void
ip_ioctl_finish(queue_t *q, mblk_t *mp, int err, int mode, ipsq_t *ipsq)
{
conn_t *connp = NULL;
if (err == EINPROGRESS)
return;
if (CONN_Q(q)) {
connp = Q_TO_CONN(q);
ASSERT(connp->conn_ref >= 2);
}
switch (mode) {
case COPYOUT:
if (err == 0)
mi_copyout(q, mp);
else
mi_copy_done(q, mp, err);
break;
case NO_COPYOUT:
mi_copy_done(q, mp, err);
break;
default:
ASSERT(mode == CONN_CLOSE);
break;
}
if (connp != NULL) {
CONN_DEC_IOCTLREF(connp);
CONN_OPER_PENDING_DONE(connp);
}
if (ipsq != NULL)
ipsq_current_finish(ipsq);
}
int
ip_wput_nondata(queue_t *q, mblk_t *mp)
{
mblk_t *mp1;
struct iocblk *iocp;
ip_ioctl_cmd_t *ipip;
conn_t *connp;
cred_t *cr;
char *proto_str;
if (CONN_Q(q))
connp = Q_TO_CONN(q);
else
connp = NULL;
iocp = NULL;
switch (DB_TYPE(mp)) {
case M_IOCTL:
ip_sioctl_copyin_setup(q, mp);
return (0);
case M_IOCDATA:
iocp = (struct iocblk *)mp->b_rptr;
ipip = ip_sioctl_lookup(iocp->ioc_cmd);
if (ipip == NULL) {
if (q->q_next == NULL) {
goto nak;
} else {
putnext(q, mp);
}
return (0);
}
if ((q->q_next != NULL) && !(ipip->ipi_flags & IPI_MODOK)) {
goto nak;
}
if (mi_copy_state(q, mp, NULL) == -1) {
return (0);
}
if (MI_COPY_DIRECTION(mp) == MI_COPY_IN) {
if (!(mp1 = mp->b_cont) || !(mp1 = mp1->b_cont)) {
mi_copy_done(q, mp, EPROTO);
return (0);
}
if (ipip->ipi_cmd_type == MSFILT_CMD &&
MI_COPY_COUNT(mp) == 1) {
if (ip_copyin_msfilter(q, mp) == 0)
return (0);
}
if (connp != NULL) {
CONN_INC_REF(connp);
CONN_INC_IOCTLREF(connp);
} else {
if (!(ipip->ipi_flags & IPI_MODOK)) {
mi_copy_done(q, mp, EINVAL);
return (0);
}
}
ip_process_ioctl(NULL, q, mp, ipip);
} else {
mi_copyout(q, mp);
}
return (0);
case M_IOCNAK:
(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
"ip_wput_nondata: unexpected M_IOCNAK, ioc_cmd 0x%x",
((struct iocblk *)mp->b_rptr)->ioc_cmd);
freemsg(mp);
return (0);
case M_IOCACK:
goto nak;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq(q, FLUSHALL);
if (q->q_next) {
putnext(q, mp);
return (0);
}
if (*mp->b_rptr & FLUSHR) {
*mp->b_rptr &= ~FLUSHW;
qreply(q, mp);
return (0);
}
freemsg(mp);
return (0);
case M_CTL:
break;
case M_PROTO:
case M_PCPROTO:
switch (((union T_primitives *)mp->b_rptr)->type) {
case T_SVR4_OPTMGMT_REQ:
ip2dbg(("ip_wput_nondata: T_SVR4_OPTMGMT_REQ "
"flags %x\n",
((struct T_optmgmt_req *)mp->b_rptr)->MGMT_flags));
if (connp == NULL) {
proto_str = "T_SVR4_OPTMGMT_REQ";
goto protonak;
}
cr = msg_getcred(mp, NULL);
ASSERT(cr != NULL);
if (cr == NULL) {
mp = mi_tpi_err_ack_alloc(mp, TSYSERR, EINVAL);
if (mp != NULL)
qreply(q, mp);
return (0);
}
if (!snmpcom_req(q, mp, ip_snmp_set, ip_snmp_get, cr)) {
proto_str = "Bad SNMPCOM request?";
goto protonak;
}
return (0);
default:
ip1dbg(("ip_wput_nondata: dropping M_PROTO prim %u\n",
(int)*(uint_t *)mp->b_rptr));
freemsg(mp);
return (0);
}
default:
break;
}
if (q->q_next) {
putnext(q, mp);
} else
freemsg(mp);
return (0);
nak:
iocp->ioc_error = EINVAL;
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_count = 0;
qreply(q, mp);
return (0);
protonak:
cmn_err(CE_NOTE, "IP doesn't process %s as a module", proto_str);
if ((mp = mi_tpi_err_ack_alloc(mp, TPROTO, EINVAL)) != NULL)
qreply(q, mp);
return (0);
}
int
ip_output_options(mblk_t *mp, ipha_t *ipha, ip_xmit_attr_t *ixa, ill_t *ill)
{
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
ipaddr_t dst;
intptr_t code = 0;
ire_t *ire;
ip_stack_t *ipst = ixa->ixa_ipst;
ip_recv_attr_t iras;
ip2dbg(("ip_output_options\n"));
opt = NULL;
dst = ipha->ipha_dst;
for (optval = ipoptp_first(&opts, ipha);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
opt = opts.ipoptp_cur;
optlen = opts.ipoptp_len;
ip2dbg(("ip_output_options: opt %d, len %d\n",
optval, optlen));
switch (optval) {
uint32_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg((
"ip_output_options: bad option offset\n"));
code = (char *)&opt[IPOPT_OLEN] -
(char *)ipha;
goto param_prob;
}
off = opt[IPOPT_OFFSET];
ip1dbg(("ip_output_options: next hop 0x%x\n",
ntohl(dst)));
if (optval == IPOPT_SSRR) {
ire = ire_ftable_lookup_v4(dst, 0, 0,
IRE_INTERFACE, NULL, ALL_ZONES,
ixa->ixa_tsl,
MATCH_IRE_TYPE | MATCH_IRE_SECATTR, 0, ipst,
NULL);
if (ire == NULL) {
ip1dbg(("ip_output_options: SSRR not"
" directly reachable: 0x%x\n",
ntohl(dst)));
goto bad_src_route;
}
ire_refrele(ire);
}
break;
case IPOPT_RR:
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg((
"ip_output_options: bad option offset\n"));
code = (char *)&opt[IPOPT_OLEN] -
(char *)ipha;
goto param_prob;
}
break;
case IPOPT_TS:
code = (char *)&opt[IPOPT_OLEN] - (char *)ipha;
if (optlen < IPOPT_MINLEN_IT) {
goto param_prob;
}
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
ip1dbg((
"ip_output_options: bad option offset\n"));
code = (char *)&opt[IPOPT_OFFSET] -
(char *)ipha;
goto param_prob;
}
switch (opt[IPOPT_POS_OV_FLG] & 0x0F) {
case IPOPT_TS_TSONLY:
off = IPOPT_TS_TIMELEN;
break;
case IPOPT_TS_TSANDADDR:
case IPOPT_TS_PRESPEC:
case IPOPT_TS_PRESPEC_RFC791:
off = IP_ADDR_LEN + IPOPT_TS_TIMELEN;
break;
default:
code = (char *)&opt[IPOPT_POS_OV_FLG] -
(char *)ipha;
goto param_prob;
}
if (opt[IPOPT_OFFSET] - 1 + off > optlen &&
(opt[IPOPT_POS_OV_FLG] & 0xF0) == 0xF0) {
goto param_prob;
}
break;
}
}
if ((opts.ipoptp_flags & IPOPTP_ERROR) == 0)
return (0);
ip1dbg(("ip_output_options: error processing IP options."));
code = (char *)&opt[IPOPT_OFFSET] - (char *)ipha;
param_prob:
bzero(&iras, sizeof (iras));
iras.ira_ill = iras.ira_rill = ill;
iras.ira_ruifindex = ill->ill_phyint->phyint_ifindex;
iras.ira_rifindex = iras.ira_ruifindex;
iras.ira_flags = IRAF_IS_IPV4;
ip_drop_output("ip_output_options", mp, ill);
icmp_param_problem(mp, (uint8_t)code, &iras);
ASSERT(!(iras.ira_flags & IRAF_IPSEC_SECURE));
return (-1);
bad_src_route:
bzero(&iras, sizeof (iras));
iras.ira_ill = iras.ira_rill = ill;
iras.ira_ruifindex = ill->ill_phyint->phyint_ifindex;
iras.ira_rifindex = iras.ira_ruifindex;
iras.ira_flags = IRAF_IS_IPV4;
ip_drop_input("ICMP_SOURCE_ROUTE_FAILED", mp, ill);
icmp_unreachable(mp, ICMP_SOURCE_ROUTE_FAILED, &iras);
ASSERT(!(iras.ira_flags & IRAF_IPSEC_SECURE));
return (-1);
}
#define CONN_MAXDRAINCNT 64
static void
conn_drain_init(ip_stack_t *ipst)
{
int i, j;
idl_tx_list_t *itl_tx;
ipst->ips_conn_drain_list_cnt = conn_drain_nthreads;
if ((ipst->ips_conn_drain_list_cnt == 0) ||
(ipst->ips_conn_drain_list_cnt > CONN_MAXDRAINCNT)) {
if (boot_max_ncpus != -1)
ipst->ips_conn_drain_list_cnt = MIN(boot_max_ncpus, 8);
else
ipst->ips_conn_drain_list_cnt = MIN(max_ncpus, 8);
}
ipst->ips_idl_tx_list =
kmem_zalloc(TX_FANOUT_SIZE * sizeof (idl_tx_list_t), KM_SLEEP);
for (i = 0; i < TX_FANOUT_SIZE; i++) {
itl_tx = &ipst->ips_idl_tx_list[i];
itl_tx->txl_drain_list =
kmem_zalloc(ipst->ips_conn_drain_list_cnt *
sizeof (idl_t), KM_SLEEP);
mutex_init(&itl_tx->txl_lock, NULL, MUTEX_DEFAULT, NULL);
for (j = 0; j < ipst->ips_conn_drain_list_cnt; j++) {
mutex_init(&itl_tx->txl_drain_list[j].idl_lock, NULL,
MUTEX_DEFAULT, NULL);
itl_tx->txl_drain_list[j].idl_itl = itl_tx;
}
}
}
static void
conn_drain_fini(ip_stack_t *ipst)
{
int i;
idl_tx_list_t *itl_tx;
for (i = 0; i < TX_FANOUT_SIZE; i++) {
itl_tx = &ipst->ips_idl_tx_list[i];
kmem_free(itl_tx->txl_drain_list,
ipst->ips_conn_drain_list_cnt * sizeof (idl_t));
}
kmem_free(ipst->ips_idl_tx_list,
TX_FANOUT_SIZE * sizeof (idl_tx_list_t));
ipst->ips_idl_tx_list = NULL;
}
void
conn_drain_insert(conn_t *connp, idl_tx_list_t *tx_list)
{
idl_t *idl = tx_list->txl_drain_list;
uint_t index;
ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
mutex_enter(&connp->conn_lock);
if (connp->conn_state_flags & CONN_CLOSING) {
mutex_exit(&connp->conn_lock);
return;
} else if (connp->conn_idl == NULL) {
index = tx_list->txl_drain_index;
ASSERT(index < ipst->ips_conn_drain_list_cnt);
connp->conn_idl = &tx_list->txl_drain_list[index];
index++;
if (index == ipst->ips_conn_drain_list_cnt)
index = 0;
tx_list->txl_drain_index = index;
} else {
ASSERT(connp->conn_idl->idl_itl == tx_list);
}
mutex_exit(&connp->conn_lock);
idl = connp->conn_idl;
mutex_enter(&idl->idl_lock);
if ((connp->conn_drain_prev != NULL) ||
(connp->conn_state_flags & CONN_CLOSING)) {
mutex_exit(&idl->idl_lock);
return;
}
if (idl->idl_conn == NULL) {
idl->idl_conn = connp;
connp->conn_drain_next = connp;
connp->conn_drain_prev = connp;
} else {
conn_t *head = idl->idl_conn;
connp->conn_drain_next = head;
connp->conn_drain_prev = head->conn_drain_prev;
head->conn_drain_prev->conn_drain_next = connp;
head->conn_drain_prev = connp;
}
conn_setqfull(connp, NULL);
mutex_exit(&idl->idl_lock);
}
static void
conn_drain_remove(conn_t *connp)
{
idl_t *idl = connp->conn_idl;
if (idl != NULL) {
if (connp->conn_drain_next == connp) {
ASSERT(connp->conn_drain_prev == connp);
idl->idl_conn = NULL;
} else {
connp->conn_drain_prev->conn_drain_next =
connp->conn_drain_next;
connp->conn_drain_next->conn_drain_prev =
connp->conn_drain_prev;
if (idl->idl_conn == connp)
idl->idl_conn = connp->conn_drain_next;
}
connp->conn_idl = NULL;
}
connp->conn_drain_next = NULL;
connp->conn_drain_prev = NULL;
conn_clrqfull(connp, NULL);
if (!IPCL_IS_NONSTR(connp))
enableok(connp->conn_wq);
}
static void
conn_drain(conn_t *connp, boolean_t closing)
{
idl_t *idl;
conn_t *next_connp;
ASSERT(!closing || connp == NULL || connp->conn_idl != NULL);
if (connp == NULL || connp->conn_idl == NULL ||
connp->conn_drain_prev == NULL) {
return;
}
idl = connp->conn_idl;
ASSERT(MUTEX_HELD(&idl->idl_lock));
if (!closing) {
next_connp = connp->conn_drain_next;
while (next_connp != connp) {
conn_t *delconnp = next_connp;
next_connp = next_connp->conn_drain_next;
conn_drain_remove(delconnp);
}
ASSERT(connp->conn_drain_next == idl->idl_conn);
}
conn_drain_remove(connp);
}
int
ip_wsrv(queue_t *q)
{
ill_t *ill;
ill = (ill_t *)q->q_ptr;
if (ill->ill_state_flags == 0) {
ip_stack_t *ipst = ill->ill_ipst;
ip1dbg(("ip_wsrv: walking\n"));
conn_walk_drain(ipst, &ipst->ips_idl_tx_list[0]);
enableok(ill->ill_wq);
}
return (0);
}
void
ill_flow_enable(void *arg, ip_mac_tx_cookie_t cookie)
{
ill_t *ill = (ill_t *)arg;
ip_stack_t *ipst = ill->ill_ipst;
idl_tx_list_t *idl_txl;
idl_txl = &ipst->ips_idl_tx_list[IDLHASHINDEX(cookie)];
mutex_enter(&idl_txl->txl_lock);
conn_walk_drain(ipst, idl_txl);
mutex_exit(&idl_txl->txl_lock);
}
static void
conn_walk_drain(ip_stack_t *ipst, idl_tx_list_t *tx_list)
{
int i;
idl_t *idl;
IP_STAT(ipst, ip_conn_walk_drain);
for (i = 0; i < ipst->ips_conn_drain_list_cnt; i++) {
idl = &tx_list->txl_drain_list[i];
mutex_enter(&idl->idl_lock);
conn_drain(idl->idl_conn, B_FALSE);
mutex_exit(&idl->idl_lock);
}
}
boolean_t
conn_wantpacket(conn_t *connp, ip_recv_attr_t *ira, ipha_t *ipha)
{
ill_t *ill = ira->ira_rill;
zoneid_t zoneid = ira->ira_zoneid;
uint_t in_ifindex;
ipaddr_t dst, src;
dst = ipha->ipha_dst;
src = ipha->ipha_src;
in_ifindex = connp->conn_incoming_ifindex;
if (in_ifindex != 0 && in_ifindex != ill->ill_phyint->phyint_ifindex) {
if (!IS_UNDER_IPMP(ill))
return (B_FALSE);
if (in_ifindex != ipmp_ill_get_ipmp_ifindex(ill))
return (B_FALSE);
}
if (!IPCL_ZONE_MATCH(connp, zoneid))
return (B_FALSE);
if (!(ira->ira_flags & IRAF_MULTICAST))
return (B_TRUE);
if (connp->conn_multi_router) {
return (B_TRUE);
}
if (ipha->ipha_protocol == IPPROTO_PIM ||
ipha->ipha_protocol == IPPROTO_RSVP)
return (B_TRUE);
return (conn_hasmembers_ill_withsrc_v4(connp, dst, src, ira->ira_ill));
}
void
conn_setqfull(conn_t *connp, boolean_t *flow_stopped)
{
if (IPCL_IS_NONSTR(connp)) {
(*connp->conn_upcalls->su_txq_full)
(connp->conn_upper_handle, B_TRUE);
if (flow_stopped != NULL)
*flow_stopped = B_TRUE;
} else {
queue_t *q = connp->conn_wq;
ASSERT(q != NULL);
if (!(q->q_flag & QFULL)) {
mutex_enter(QLOCK(q));
if (!(q->q_flag & QFULL)) {
q->q_flag |= QFULL;
if (flow_stopped != NULL)
*flow_stopped = B_TRUE;
mutex_exit(QLOCK(q));
} else {
mutex_exit(QLOCK(q));
}
}
}
}
void
conn_clrqfull(conn_t *connp, boolean_t *flow_stopped)
{
if (IPCL_IS_NONSTR(connp)) {
(*connp->conn_upcalls->su_txq_full)
(connp->conn_upper_handle, B_FALSE);
if (flow_stopped != NULL)
*flow_stopped = B_FALSE;
} else {
queue_t *q = connp->conn_wq;
ASSERT(q != NULL);
if (q->q_flag & QFULL) {
mutex_enter(QLOCK(q));
if (q->q_flag & QFULL) {
q->q_flag &= ~QFULL;
if (flow_stopped != NULL)
*flow_stopped = B_FALSE;
mutex_exit(QLOCK(q));
if (q->q_flag & QWANTW)
qbackenable(q, 0);
} else {
mutex_exit(QLOCK(q));
}
}
}
mutex_enter(&connp->conn_lock);
connp->conn_blocked = B_FALSE;
mutex_exit(&connp->conn_lock);
}
int
ip_total_hdrs_len_v4(const ip_pkt_t *ipp)
{
int len;
len = IP_SIMPLE_HDR_LENGTH;
if (ipp->ipp_fields & IPPF_LABEL_V4) {
ASSERT(ipp->ipp_label_len_v4 != 0);
len += (ipp->ipp_label_len_v4 + 3) & ~3;
}
if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
ASSERT(ipp->ipp_ipv4_options_len != 0);
ASSERT((ipp->ipp_ipv4_options_len & 3) == 0);
len += ipp->ipp_ipv4_options_len;
}
return (len);
}
void
ip_build_hdrs_v4(uchar_t *buf, uint_t buf_len, const ip_pkt_t *ipp,
uint8_t protocol)
{
ipha_t *ipha = (ipha_t *)buf;
uint8_t *cp;
ipha->ipha_type_of_service = ipp->ipp_type_of_service;
ipha->ipha_length = 0;
ipha->ipha_ident = 0;
ipha->ipha_fragment_offset_and_flags = 0;
ipha->ipha_ttl = ipp->ipp_unicast_hops;
ipha->ipha_protocol = protocol;
ipha->ipha_hdr_checksum = 0;
if ((ipp->ipp_fields & IPPF_ADDR) &&
IN6_IS_ADDR_V4MAPPED(&ipp->ipp_addr))
ipha->ipha_src = ipp->ipp_addr_v4;
cp = (uint8_t *)&ipha[1];
if (ipp->ipp_fields & IPPF_LABEL_V4) {
ASSERT(ipp->ipp_label_len_v4 != 0);
bcopy(ipp->ipp_label_v4, cp, ipp->ipp_label_len_v4);
cp += ipp->ipp_label_len_v4;
while ((uintptr_t)cp & 0x3) {
*cp++ = IPOPT_NOP;
}
}
if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
ASSERT(ipp->ipp_ipv4_options_len != 0);
ASSERT((ipp->ipp_ipv4_options_len & 3) == 0);
bcopy(ipp->ipp_ipv4_options, cp, ipp->ipp_ipv4_options_len);
cp += ipp->ipp_ipv4_options_len;
}
ipha->ipha_version_and_hdr_length =
(uint8_t)((IP_VERSION << 4) + buf_len / 4);
ASSERT((int)(cp - buf) == buf_len);
}
static int
ip_priv_alloc(void **bufp)
{
void *buf;
if ((buf = kmem_alloc(sizeof (ip_priv_t), KM_NOSLEEP)) == NULL)
return (ENOMEM);
*bufp = buf;
return (0);
}
void
ip_priv_free(void *buf)
{
ASSERT(buf != NULL);
kmem_free(buf, sizeof (ip_priv_t));
}
mblk_t *
ip_process(ip_proc_t proc, mblk_t *mp, ill_t *rill, ill_t *ill)
{
ip_priv_t *priv;
ipp_action_id_t aid;
int rc = 0;
ipp_packet_t *pp;
if ((aid = ipp_action_lookup(IPGPC_CLASSIFY)) == IPP_ACTION_INVAL) {
return (mp);
}
ASSERT(mp != NULL);
rc = ipp_packet_alloc(&pp, "ip", aid);
if (rc != 0)
goto drop;
rc = ip_priv_alloc((void **)&priv);
if (rc != 0) {
ipp_packet_free(pp);
goto drop;
}
priv->proc = proc;
priv->ill_index = ill_get_upper_ifindex(rill);
ipp_packet_set_private(pp, priv, ip_priv_free);
ipp_packet_set_data(pp, mp);
rc = ipp_packet_process(&pp);
if (pp != NULL) {
mp = ipp_packet_get_data(pp);
ipp_packet_free(pp);
if (rc != 0)
goto drop;
return (mp);
} else {
mp = NULL;
}
drop:
if (proc == IPP_LOCAL_IN || proc == IPP_FWD_IN) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ip_process", mp, ill);
} else {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ip_process", mp, ill);
}
freemsg(mp);
return (NULL);
}
static int
ip_multirt_apply_membership(int (*fn)(conn_t *, boolean_t,
const in6_addr_t *, ipaddr_t, uint_t, mcast_record_t, const in6_addr_t *),
ire_t *ire, conn_t *connp, boolean_t checkonly, const in6_addr_t *v6group,
mcast_record_t fmode, const in6_addr_t *v6src)
{
ire_t *ire_gw;
irb_t *irb;
int ifindex;
int error = 0;
int result;
ip_stack_t *ipst = ire->ire_ipst;
ipaddr_t group;
boolean_t isv6;
int match_flags;
if (IN6_IS_ADDR_V4MAPPED(v6group)) {
IN6_V4MAPPED_TO_IPADDR(v6group, group);
isv6 = B_FALSE;
} else {
isv6 = B_TRUE;
}
irb = ire->ire_bucket;
ASSERT(irb != NULL);
result = 0;
irb_refhold(irb);
for (; ire != NULL; ire = ire->ire_next) {
if ((ire->ire_flags & RTF_MULTIRT) == 0)
continue;
match_flags = MATCH_IRE_TYPE;
if (ire->ire_ill != NULL)
match_flags |= MATCH_IRE_ILL;
if (isv6) {
if (!IN6_ARE_ADDR_EQUAL(&ire->ire_addr_v6, v6group))
continue;
ire_gw = ire_ftable_lookup_v6(&ire->ire_gateway_addr_v6,
0, 0, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL,
match_flags, 0, ipst, NULL);
} else {
if (ire->ire_addr != group)
continue;
ire_gw = ire_ftable_lookup_v4(ire->ire_gateway_addr,
0, 0, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL,
match_flags, 0, ipst, NULL);
}
if (ire_gw == NULL)
continue;
if (ire_gw->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
ire_refrele(ire_gw);
continue;
}
ASSERT(ire_gw->ire_ill != NULL);
ifindex = ire_gw->ire_ill->ill_phyint->phyint_ifindex;
error = fn(connp, checkonly, v6group, INADDR_ANY, ifindex,
fmode, v6src);
if (error == 0)
result = CGTP_MCAST_SUCCESS;
ire_refrele(ire_gw);
}
irb_refrele(irb);
return (result == CGTP_MCAST_SUCCESS ? 0 : error);
}
int
ip_cgtp_filter_supported(void)
{
return (ip_cgtp_filter_rev);
}
int
ip_cgtp_filter_register(netstackid_t stackid, cgtp_filter_ops_t *ops)
{
netstack_t *ns;
ip_stack_t *ipst;
if (ops->cfo_filter_rev != CGTP_FILTER_REV)
return (ENOTSUP);
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (EINVAL);
ipst = ns->netstack_ip;
ASSERT(ipst != NULL);
if (ipst->ips_ip_cgtp_filter_ops != NULL) {
netstack_rele(ns);
return (EALREADY);
}
ipst->ips_ip_cgtp_filter_ops = ops;
ill_set_inputfn_all(ipst);
netstack_rele(ns);
return (0);
}
int
ip_cgtp_filter_unregister(netstackid_t stackid)
{
netstack_t *ns;
ip_stack_t *ipst;
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (EINVAL);
ipst = ns->netstack_ip;
ASSERT(ipst != NULL);
if (ipst->ips_ip_cgtp_filter) {
netstack_rele(ns);
return (EBUSY);
}
if (ipst->ips_ip_cgtp_filter_ops == NULL) {
netstack_rele(ns);
return (ENXIO);
}
ipst->ips_ip_cgtp_filter_ops = NULL;
ill_set_inputfn_all(ipst);
netstack_rele(ns);
return (0);
}
int
ip_cgtp_filter_is_registered(netstackid_t stackid)
{
netstack_t *ns;
ip_stack_t *ipst;
int ret;
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (0);
ipst = ns->netstack_ip;
ASSERT(ipst != NULL);
if (ipst->ips_ip_cgtp_filter_ops != NULL)
ret = 1;
else
ret = 0;
netstack_rele(ns);
return (ret);
}
static int
ip_squeue_switch(int val)
{
int rval;
switch (val) {
case IP_SQUEUE_ENTER_NODRAIN:
rval = SQ_NODRAIN;
break;
case IP_SQUEUE_ENTER:
rval = SQ_PROCESS;
break;
case IP_SQUEUE_FILL:
default:
rval = SQ_FILL;
break;
}
return (rval);
}
static void *
ip_kstat2_init(netstackid_t stackid, ip_stat_t *ip_statisticsp)
{
kstat_t *ksp;
ip_stat_t template = {
{ "ip_udp_fannorm", KSTAT_DATA_UINT64 },
{ "ip_udp_fanmb", KSTAT_DATA_UINT64 },
{ "ip_recv_pullup", KSTAT_DATA_UINT64 },
{ "ip_db_ref", KSTAT_DATA_UINT64 },
{ "ip_notaligned", KSTAT_DATA_UINT64 },
{ "ip_multimblk", KSTAT_DATA_UINT64 },
{ "ip_opt", KSTAT_DATA_UINT64 },
{ "ipsec_proto_ahesp", KSTAT_DATA_UINT64 },
{ "ip_conn_flputbq", KSTAT_DATA_UINT64 },
{ "ip_conn_walk_drain", KSTAT_DATA_UINT64 },
{ "ip_out_sw_cksum", KSTAT_DATA_UINT64 },
{ "ip_out_sw_cksum_bytes", KSTAT_DATA_UINT64 },
{ "ip_in_sw_cksum", KSTAT_DATA_UINT64 },
{ "ip_ire_reclaim_calls", KSTAT_DATA_UINT64 },
{ "ip_ire_reclaim_deleted", KSTAT_DATA_UINT64 },
{ "ip_nce_reclaim_calls", KSTAT_DATA_UINT64 },
{ "ip_nce_reclaim_deleted", KSTAT_DATA_UINT64 },
{ "ip_nce_mcast_reclaim_calls", KSTAT_DATA_UINT64 },
{ "ip_nce_mcast_reclaim_deleted", KSTAT_DATA_UINT64 },
{ "ip_nce_mcast_reclaim_tqfail", KSTAT_DATA_UINT64 },
{ "ip_dce_reclaim_calls", KSTAT_DATA_UINT64 },
{ "ip_dce_reclaim_deleted", KSTAT_DATA_UINT64 },
{ "ip_tcp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip_tcp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip_tcp_in_sw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip_udp_in_full_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip_udp_in_part_hw_cksum_err", KSTAT_DATA_UINT64 },
{ "ip_udp_in_sw_cksum_err", KSTAT_DATA_UINT64 },
{ "conn_in_recvdstaddr", KSTAT_DATA_UINT64 },
{ "conn_in_recvopts", KSTAT_DATA_UINT64 },
{ "conn_in_recvif", KSTAT_DATA_UINT64 },
{ "conn_in_recvslla", KSTAT_DATA_UINT64 },
{ "conn_in_recvucred", KSTAT_DATA_UINT64 },
{ "conn_in_recvttl", KSTAT_DATA_UINT64 },
{ "conn_in_recvtos", KSTAT_DATA_UINT64 },
{ "conn_in_recvhopopts", KSTAT_DATA_UINT64 },
{ "conn_in_recvhoplimit", KSTAT_DATA_UINT64 },
{ "conn_in_recvdstopts", KSTAT_DATA_UINT64 },
{ "conn_in_recvrthdrdstopts", KSTAT_DATA_UINT64 },
{ "conn_in_recvrthdr", KSTAT_DATA_UINT64 },
{ "conn_in_recvpktinfo", KSTAT_DATA_UINT64 },
{ "conn_in_recvtclass", KSTAT_DATA_UINT64 },
{ "conn_in_timestamp", KSTAT_DATA_UINT64 },
};
ksp = kstat_create_netstack("ip", 0, "ipstat", "net",
KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL, stackid);
if (ksp == NULL)
return (NULL);
bcopy(&template, ip_statisticsp, sizeof (template));
ksp->ks_data = (void *)ip_statisticsp;
ksp->ks_private = (void *)(uintptr_t)stackid;
kstat_install(ksp);
return (ksp);
}
static void
ip_kstat2_fini(netstackid_t stackid, kstat_t *ksp)
{
if (ksp != NULL) {
ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
kstat_delete_netstack(ksp, stackid);
}
}
static void *
ip_kstat_init(netstackid_t stackid, ip_stack_t *ipst)
{
kstat_t *ksp;
ip_named_kstat_t template = {
{ "forwarding", KSTAT_DATA_UINT32, 0 },
{ "defaultTTL", KSTAT_DATA_UINT32, 0 },
{ "inReceives", KSTAT_DATA_UINT64, 0 },
{ "inHdrErrors", KSTAT_DATA_UINT32, 0 },
{ "inAddrErrors", KSTAT_DATA_UINT32, 0 },
{ "forwDatagrams", KSTAT_DATA_UINT64, 0 },
{ "inUnknownProtos", KSTAT_DATA_UINT32, 0 },
{ "inDiscards", KSTAT_DATA_UINT32, 0 },
{ "inDelivers", KSTAT_DATA_UINT64, 0 },
{ "outRequests", KSTAT_DATA_UINT64, 0 },
{ "outDiscards", KSTAT_DATA_UINT32, 0 },
{ "outNoRoutes", KSTAT_DATA_UINT32, 0 },
{ "reasmTimeout", KSTAT_DATA_UINT32, 0 },
{ "reasmReqds", KSTAT_DATA_UINT32, 0 },
{ "reasmOKs", KSTAT_DATA_UINT32, 0 },
{ "reasmFails", KSTAT_DATA_UINT32, 0 },
{ "fragOKs", KSTAT_DATA_UINT32, 0 },
{ "fragFails", KSTAT_DATA_UINT32, 0 },
{ "fragCreates", KSTAT_DATA_UINT32, 0 },
{ "addrEntrySize", KSTAT_DATA_INT32, 0 },
{ "routeEntrySize", KSTAT_DATA_INT32, 0 },
{ "netToMediaEntrySize", KSTAT_DATA_INT32, 0 },
{ "routingDiscards", KSTAT_DATA_UINT32, 0 },
{ "inErrs", KSTAT_DATA_UINT32, 0 },
{ "noPorts", KSTAT_DATA_UINT32, 0 },
{ "inCksumErrs", KSTAT_DATA_UINT32, 0 },
{ "reasmDuplicates", KSTAT_DATA_UINT32, 0 },
{ "reasmPartDups", KSTAT_DATA_UINT32, 0 },
{ "forwProhibits", KSTAT_DATA_UINT32, 0 },
{ "udpInCksumErrs", KSTAT_DATA_UINT32, 0 },
{ "udpInOverflows", KSTAT_DATA_UINT32, 0 },
{ "rawipInOverflows", KSTAT_DATA_UINT32, 0 },
{ "ipsecInSucceeded", KSTAT_DATA_UINT32, 0 },
{ "ipsecInFailed", KSTAT_DATA_INT32, 0 },
{ "memberEntrySize", KSTAT_DATA_INT32, 0 },
{ "inIPv6", KSTAT_DATA_UINT32, 0 },
{ "outIPv6", KSTAT_DATA_UINT32, 0 },
{ "outSwitchIPv6", KSTAT_DATA_UINT32, 0 },
};
ksp = kstat_create_netstack("ip", 0, "ip", "mib2", KSTAT_TYPE_NAMED,
NUM_OF_FIELDS(ip_named_kstat_t), 0, stackid);
if (ksp == NULL || ksp->ks_data == NULL)
return (NULL);
template.forwarding.value.ui32 = WE_ARE_FORWARDING(ipst) ? 1:2;
template.defaultTTL.value.ui32 = (uint32_t)ipst->ips_ip_def_ttl;
template.reasmTimeout.value.ui32 = ipst->ips_ip_reassembly_timeout;
template.addrEntrySize.value.i32 = sizeof (mib2_ipAddrEntry_t);
template.routeEntrySize.value.i32 = sizeof (mib2_ipRouteEntry_t);
template.netToMediaEntrySize.value.i32 =
sizeof (mib2_ipNetToMediaEntry_t);
template.memberEntrySize.value.i32 = sizeof (ipv6_member_t);
bcopy(&template, ksp->ks_data, sizeof (template));
ksp->ks_update = ip_kstat_update;
ksp->ks_private = (void *)(uintptr_t)stackid;
kstat_install(ksp);
return (ksp);
}
static void
ip_kstat_fini(netstackid_t stackid, kstat_t *ksp)
{
if (ksp != NULL) {
ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
kstat_delete_netstack(ksp, stackid);
}
}
static int
ip_kstat_update(kstat_t *kp, int rw)
{
ip_named_kstat_t *ipkp;
mib2_ipIfStatsEntry_t ipmib;
ill_walk_context_t ctx;
ill_t *ill;
netstackid_t stackid = (zoneid_t)(uintptr_t)kp->ks_private;
netstack_t *ns;
ip_stack_t *ipst;
if (kp->ks_data == NULL)
return (EIO);
if (rw == KSTAT_WRITE)
return (EACCES);
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (-1);
ipst = ns->netstack_ip;
if (ipst == NULL) {
netstack_rele(ns);
return (-1);
}
ipkp = (ip_named_kstat_t *)kp->ks_data;
bcopy(&ipst->ips_ip_mib, &ipmib, sizeof (ipmib));
rw_enter(&ipst->ips_ill_g_lock, RW_READER);
ill = ILL_START_WALK_V4(&ctx, ipst);
for (; ill != NULL; ill = ill_next(&ctx, ill))
ip_mib2_add_ip_stats(&ipmib, ill->ill_ip_mib);
rw_exit(&ipst->ips_ill_g_lock);
ipkp->forwarding.value.ui32 = ipmib.ipIfStatsForwarding;
ipkp->defaultTTL.value.ui32 = ipmib.ipIfStatsDefaultTTL;
ipkp->inReceives.value.ui64 = ipmib.ipIfStatsHCInReceives;
ipkp->inHdrErrors.value.ui32 = ipmib.ipIfStatsInHdrErrors;
ipkp->inAddrErrors.value.ui32 = ipmib.ipIfStatsInAddrErrors;
ipkp->forwDatagrams.value.ui64 = ipmib.ipIfStatsHCOutForwDatagrams;
ipkp->inUnknownProtos.value.ui32 = ipmib.ipIfStatsInUnknownProtos;
ipkp->inDiscards.value.ui32 = ipmib.ipIfStatsInDiscards;
ipkp->inDelivers.value.ui64 = ipmib.ipIfStatsHCInDelivers;
ipkp->outRequests.value.ui64 = ipmib.ipIfStatsHCOutRequests;
ipkp->outDiscards.value.ui32 = ipmib.ipIfStatsOutDiscards;
ipkp->outNoRoutes.value.ui32 = ipmib.ipIfStatsOutNoRoutes;
ipkp->reasmTimeout.value.ui32 = ipst->ips_ip_reassembly_timeout;
ipkp->reasmReqds.value.ui32 = ipmib.ipIfStatsReasmReqds;
ipkp->reasmOKs.value.ui32 = ipmib.ipIfStatsReasmOKs;
ipkp->reasmFails.value.ui32 = ipmib.ipIfStatsReasmFails;
ipkp->fragOKs.value.ui32 = ipmib.ipIfStatsOutFragOKs;
ipkp->fragFails.value.ui32 = ipmib.ipIfStatsOutFragFails;
ipkp->fragCreates.value.ui32 = ipmib.ipIfStatsOutFragCreates;
ipkp->routingDiscards.value.ui32 = 0;
ipkp->inErrs.value.ui32 = ipmib.tcpIfStatsInErrs;
ipkp->noPorts.value.ui32 = ipmib.udpIfStatsNoPorts;
ipkp->inCksumErrs.value.ui32 = ipmib.ipIfStatsInCksumErrs;
ipkp->reasmDuplicates.value.ui32 = ipmib.ipIfStatsReasmDuplicates;
ipkp->reasmPartDups.value.ui32 = ipmib.ipIfStatsReasmPartDups;
ipkp->forwProhibits.value.ui32 = ipmib.ipIfStatsForwProhibits;
ipkp->udpInCksumErrs.value.ui32 = ipmib.udpIfStatsInCksumErrs;
ipkp->udpInOverflows.value.ui32 = ipmib.udpIfStatsInOverflows;
ipkp->rawipInOverflows.value.ui32 = ipmib.rawipIfStatsInOverflows;
ipkp->ipsecInSucceeded.value.ui32 = ipmib.ipsecIfStatsInSucceeded;
ipkp->ipsecInFailed.value.i32 = ipmib.ipsecIfStatsInFailed;
ipkp->inIPv6.value.ui32 = ipmib.ipIfStatsInWrongIPVersion;
ipkp->outIPv6.value.ui32 = ipmib.ipIfStatsOutWrongIPVersion;
ipkp->outSwitchIPv6.value.ui32 = ipmib.ipIfStatsOutSwitchIPVersion;
netstack_rele(ns);
return (0);
}
static void *
icmp_kstat_init(netstackid_t stackid)
{
kstat_t *ksp;
icmp_named_kstat_t template = {
{ "inMsgs", KSTAT_DATA_UINT32 },
{ "inErrors", KSTAT_DATA_UINT32 },
{ "inDestUnreachs", KSTAT_DATA_UINT32 },
{ "inTimeExcds", KSTAT_DATA_UINT32 },
{ "inParmProbs", KSTAT_DATA_UINT32 },
{ "inSrcQuenchs", KSTAT_DATA_UINT32 },
{ "inRedirects", KSTAT_DATA_UINT32 },
{ "inEchos", KSTAT_DATA_UINT32 },
{ "inEchoReps", KSTAT_DATA_UINT32 },
{ "inTimestamps", KSTAT_DATA_UINT32 },
{ "inTimestampReps", KSTAT_DATA_UINT32 },
{ "inAddrMasks", KSTAT_DATA_UINT32 },
{ "inAddrMaskReps", KSTAT_DATA_UINT32 },
{ "outMsgs", KSTAT_DATA_UINT32 },
{ "outErrors", KSTAT_DATA_UINT32 },
{ "outDestUnreachs", KSTAT_DATA_UINT32 },
{ "outTimeExcds", KSTAT_DATA_UINT32 },
{ "outParmProbs", KSTAT_DATA_UINT32 },
{ "outSrcQuenchs", KSTAT_DATA_UINT32 },
{ "outRedirects", KSTAT_DATA_UINT32 },
{ "outEchos", KSTAT_DATA_UINT32 },
{ "outEchoReps", KSTAT_DATA_UINT32 },
{ "outTimestamps", KSTAT_DATA_UINT32 },
{ "outTimestampReps", KSTAT_DATA_UINT32 },
{ "outAddrMasks", KSTAT_DATA_UINT32 },
{ "outAddrMaskReps", KSTAT_DATA_UINT32 },
{ "inChksumErrs", KSTAT_DATA_UINT32 },
{ "inUnknowns", KSTAT_DATA_UINT32 },
{ "inFragNeeded", KSTAT_DATA_UINT32 },
{ "outFragNeeded", KSTAT_DATA_UINT32 },
{ "outDrops", KSTAT_DATA_UINT32 },
{ "inOverFlows", KSTAT_DATA_UINT32 },
{ "inBadRedirects", KSTAT_DATA_UINT32 },
};
ksp = kstat_create_netstack("ip", 0, "icmp", "mib2", KSTAT_TYPE_NAMED,
NUM_OF_FIELDS(icmp_named_kstat_t), 0, stackid);
if (ksp == NULL || ksp->ks_data == NULL)
return (NULL);
bcopy(&template, ksp->ks_data, sizeof (template));
ksp->ks_update = icmp_kstat_update;
ksp->ks_private = (void *)(uintptr_t)stackid;
kstat_install(ksp);
return (ksp);
}
static void
icmp_kstat_fini(netstackid_t stackid, kstat_t *ksp)
{
if (ksp != NULL) {
ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
kstat_delete_netstack(ksp, stackid);
}
}
static int
icmp_kstat_update(kstat_t *kp, int rw)
{
icmp_named_kstat_t *icmpkp;
netstackid_t stackid = (zoneid_t)(uintptr_t)kp->ks_private;
netstack_t *ns;
ip_stack_t *ipst;
if (kp->ks_data == NULL)
return (EIO);
if (rw == KSTAT_WRITE)
return (EACCES);
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (-1);
ipst = ns->netstack_ip;
if (ipst == NULL) {
netstack_rele(ns);
return (-1);
}
icmpkp = (icmp_named_kstat_t *)kp->ks_data;
icmpkp->inMsgs.value.ui32 = ipst->ips_icmp_mib.icmpInMsgs;
icmpkp->inErrors.value.ui32 = ipst->ips_icmp_mib.icmpInErrors;
icmpkp->inDestUnreachs.value.ui32 =
ipst->ips_icmp_mib.icmpInDestUnreachs;
icmpkp->inTimeExcds.value.ui32 = ipst->ips_icmp_mib.icmpInTimeExcds;
icmpkp->inParmProbs.value.ui32 = ipst->ips_icmp_mib.icmpInParmProbs;
icmpkp->inSrcQuenchs.value.ui32 = ipst->ips_icmp_mib.icmpInSrcQuenchs;
icmpkp->inRedirects.value.ui32 = ipst->ips_icmp_mib.icmpInRedirects;
icmpkp->inEchos.value.ui32 = ipst->ips_icmp_mib.icmpInEchos;
icmpkp->inEchoReps.value.ui32 = ipst->ips_icmp_mib.icmpInEchoReps;
icmpkp->inTimestamps.value.ui32 = ipst->ips_icmp_mib.icmpInTimestamps;
icmpkp->inTimestampReps.value.ui32 =
ipst->ips_icmp_mib.icmpInTimestampReps;
icmpkp->inAddrMasks.value.ui32 = ipst->ips_icmp_mib.icmpInAddrMasks;
icmpkp->inAddrMaskReps.value.ui32 =
ipst->ips_icmp_mib.icmpInAddrMaskReps;
icmpkp->outMsgs.value.ui32 = ipst->ips_icmp_mib.icmpOutMsgs;
icmpkp->outErrors.value.ui32 = ipst->ips_icmp_mib.icmpOutErrors;
icmpkp->outDestUnreachs.value.ui32 =
ipst->ips_icmp_mib.icmpOutDestUnreachs;
icmpkp->outTimeExcds.value.ui32 = ipst->ips_icmp_mib.icmpOutTimeExcds;
icmpkp->outParmProbs.value.ui32 = ipst->ips_icmp_mib.icmpOutParmProbs;
icmpkp->outSrcQuenchs.value.ui32 =
ipst->ips_icmp_mib.icmpOutSrcQuenchs;
icmpkp->outRedirects.value.ui32 = ipst->ips_icmp_mib.icmpOutRedirects;
icmpkp->outEchos.value.ui32 = ipst->ips_icmp_mib.icmpOutEchos;
icmpkp->outEchoReps.value.ui32 = ipst->ips_icmp_mib.icmpOutEchoReps;
icmpkp->outTimestamps.value.ui32 =
ipst->ips_icmp_mib.icmpOutTimestamps;
icmpkp->outTimestampReps.value.ui32 =
ipst->ips_icmp_mib.icmpOutTimestampReps;
icmpkp->outAddrMasks.value.ui32 =
ipst->ips_icmp_mib.icmpOutAddrMasks;
icmpkp->outAddrMaskReps.value.ui32 =
ipst->ips_icmp_mib.icmpOutAddrMaskReps;
icmpkp->inCksumErrs.value.ui32 = ipst->ips_icmp_mib.icmpInCksumErrs;
icmpkp->inUnknowns.value.ui32 = ipst->ips_icmp_mib.icmpInUnknowns;
icmpkp->inFragNeeded.value.ui32 = ipst->ips_icmp_mib.icmpInFragNeeded;
icmpkp->outFragNeeded.value.ui32 =
ipst->ips_icmp_mib.icmpOutFragNeeded;
icmpkp->outDrops.value.ui32 = ipst->ips_icmp_mib.icmpOutDrops;
icmpkp->inOverflows.value.ui32 = ipst->ips_icmp_mib.icmpInOverflows;
icmpkp->inBadRedirects.value.ui32 =
ipst->ips_icmp_mib.icmpInBadRedirects;
netstack_rele(ns);
return (0);
}
void
ip_fanout_sctp_raw(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h, uint32_t ports,
ip_recv_attr_t *ira)
{
conn_t *connp;
queue_t *rq;
boolean_t secure;
ill_t *ill = ira->ira_ill;
ip_stack_t *ipst = ill->ill_ipst;
ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec;
sctp_stack_t *sctps = ipst->ips_netstack->netstack_sctp;
iaflags_t iraflags = ira->ira_flags;
ill_t *rill = ira->ira_rill;
secure = iraflags & IRAF_IPSEC_SECURE;
connp = ipcl_classify_raw(mp, IPPROTO_SCTP, ports, ipha, ip6h,
ira, ipst);
if (connp == NULL) {
if (iraflags & IRAF_SCTP_CSUM_ERR) {
SCTPS_BUMP_MIB(sctps, sctpChecksumError);
freemsg(mp);
return;
}
ira->ira_ill = ira->ira_rill = NULL;
sctp_ootb_input(mp, ira, ipst);
ira->ira_ill = ill;
ira->ira_rill = rill;
return;
}
if (connp->conn_min_ttl != 0 && connp->conn_min_ttl > ira->ira_ttl) {
CONN_DEC_REF(connp);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
freemsg(mp);
return;
}
rq = connp->conn_rq;
if (IPCL_IS_NONSTR(connp) ? connp->conn_flow_cntrld : !canputnext(rq)) {
CONN_DEC_REF(connp);
BUMP_MIB(ill->ill_ip_mib, rawipIfStatsInOverflows);
freemsg(mp);
return;
}
if (((iraflags & IRAF_IS_IPV4) ?
CONN_INBOUND_POLICY_PRESENT(connp, ipss) :
CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss)) ||
secure) {
mp = ipsec_check_inbound_policy(mp, connp, ipha,
ip6h, ira);
if (mp == NULL) {
BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
ip_drop_input("ipIfStatsInDiscards", mp, ill);
CONN_DEC_REF(connp);
return;
}
}
if (iraflags & IRAF_ICMP_ERROR) {
(connp->conn_recvicmp)(connp, mp, NULL, ira);
} else {
ill_t *rill = ira->ira_rill;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInDelivers);
ira->ira_ill = ira->ira_rill = NULL;
(connp->conn_recv)(connp, mp, NULL, ira);
ira->ira_ill = ill;
ira->ira_rill = rill;
}
CONN_DEC_REF(connp);
}
static void
ip_xmit_flowctl_drop(ill_t *ill, mblk_t *mp, boolean_t is_fp_mp, int fp_mp_len)
{
int len = (mp->b_wptr - mp->b_rptr);
mblk_t *ip_mp;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
if (is_fp_mp || len != fp_mp_len) {
if (len > fp_mp_len) {
mp->b_rptr += fp_mp_len;
} else {
ip_mp = mp->b_cont;
freeb(mp);
mp = ip_mp;
mp->b_rptr += (fp_mp_len - len);
}
} else {
ip_mp = mp->b_cont;
freeb(mp);
mp = ip_mp;
}
ip_drop_output("ipIfStatsOutDiscards - flow ctl", mp, ill);
freemsg(mp);
}
int
ip_xmit(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, uint_t pkt_len,
uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid, uintptr_t *ixacookie)
{
queue_t *wq;
ill_t *ill = nce->nce_ill;
ip_stack_t *ipst = ill->ill_ipst;
uint64_t delta;
boolean_t isv6 = ill->ill_isv6;
boolean_t fp_mp;
ncec_t *ncec = nce->nce_common;
int64_t now = LBOLT_FASTPATH64;
boolean_t is_probe;
DTRACE_PROBE1(ip__xmit, nce_t *, nce);
ASSERT(mp != NULL);
ASSERT(mp->b_datap->db_type == M_DATA);
ASSERT3U(pkt_len, ==, msgdsize(mp));
if (ixaflags & IXAF_NO_TRACE)
goto sendit;
if (ixaflags & IXAF_IS_IPV4) {
ipha_t *ipha = (ipha_t *)mp->b_rptr;
ASSERT(!isv6);
ASSERT3U(pkt_len, ==,
ntohs(((ipha_t *)mp->b_rptr)->ipha_length));
if (HOOKS4_INTERESTED_PHYSICAL_OUT(ipst) &&
!(ixaflags & IXAF_NO_PFHOOK)) {
int error;
FW_HOOKS(ipst->ips_ip4_physical_out_event,
ipst->ips_ipv4firewall_physical_out,
NULL, ill, ipha, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip4__physical__out__end,
mblk_t *, mp);
if (mp == NULL)
return (error);
pkt_len = msgdsize(mp);
}
if (ipst->ips_ip4_observe.he_interested) {
szone = IP_REAL_ZONEID(szone, ipst);
ipobs_hook(mp, IPOBS_HOOK_OUTBOUND, szone, ALL_ZONES,
ill, ipst);
}
DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL,
void_ip_t *, ipha, __dtrace_ipsr_ill_t *, ill,
ipha_t *, ipha, ip6_t *, NULL, int, 0);
} else {
ip6_t *ip6h = (ip6_t *)mp->b_rptr;
ASSERT(isv6);
ASSERT(pkt_len ==
ntohs(((ip6_t *)mp->b_rptr)->ip6_plen) + IPV6_HDR_LEN);
if (HOOKS6_INTERESTED_PHYSICAL_OUT(ipst) &&
!(ixaflags & IXAF_NO_PFHOOK)) {
int error;
FW_HOOKS6(ipst->ips_ip6_physical_out_event,
ipst->ips_ipv6firewall_physical_out,
NULL, ill, ip6h, mp, mp, 0, ipst, error);
DTRACE_PROBE1(ip6__physical__out__end,
mblk_t *, mp);
if (mp == NULL)
return (error);
pkt_len = msgdsize(mp);
}
if (ipst->ips_ip6_observe.he_interested) {
szone = IP_REAL_ZONEID(szone, ipst);
ipobs_hook(mp, IPOBS_HOOK_OUTBOUND, szone, ALL_ZONES,
ill, ipst);
}
DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL,
void_ip_t *, ip6h, __dtrace_ipsr_ill_t *, ill,
ipha_t *, NULL, ip6_t *, ip6h, int, 0);
}
sendit:
switch (ncec->ncec_state) {
case ND_REACHABLE:
case ND_STALE:
case ND_DELAY:
case ND_PROBE:
mp = ip_xmit_attach_llhdr(mp, nce);
if (mp == NULL) {
return (ENOBUFS);
}
fp_mp = (mp->b_datap->db_type == M_DATA);
if (fp_mp &&
(ill->ill_capabilities & ILL_CAPAB_DLD_DIRECT)) {
ill_dld_direct_t *idd;
idd = &ill->ill_dld_capab->idc_direct;
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutTransmits);
UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCOutOctets,
pkt_len);
if (ixaflags & IXAF_NO_DEV_FLOW_CTL) {
(void) idd->idd_tx_df(idd->idd_tx_dh, mp,
(uintptr_t)xmit_hint, IP_DROP_ON_NO_DESC);
} else {
uintptr_t cookie;
if ((cookie = idd->idd_tx_df(idd->idd_tx_dh,
mp, (uintptr_t)xmit_hint, 0)) != 0) {
if (ixacookie != NULL)
*ixacookie = cookie;
return (EWOULDBLOCK);
}
}
} else {
wq = ill->ill_wq;
if (!(ixaflags & IXAF_NO_DEV_FLOW_CTL) &&
!canputnext(wq)) {
if (ixacookie != NULL)
*ixacookie = 0;
ip_xmit_flowctl_drop(ill, mp, fp_mp,
nce->nce_fp_mp != NULL ?
MBLKL(nce->nce_fp_mp) : 0);
return (EWOULDBLOCK);
}
BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutTransmits);
UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCOutOctets,
pkt_len);
putnext(wq, mp);
}
if (ncec->ncec_flags & NCE_F_NONUD)
return (0);
ASSERT(ncec->ncec_state != ND_INCOMPLETE);
if (ixaflags & IXAF_REACH_CONF) {
timeout_id_t tid;
ncec->ncec_last = TICK_TO_MSEC(now);
if (ncec->ncec_state != ND_REACHABLE) {
mutex_enter(&ncec->ncec_lock);
ncec->ncec_state = ND_REACHABLE;
tid = ncec->ncec_timeout_id;
ncec->ncec_timeout_id = 0;
mutex_exit(&ncec->ncec_lock);
(void) untimeout(tid);
if (ip_debug > 2) {
pr_addr_dbg("ip_xmit: state"
" for %s changed to"
" REACHABLE\n", AF_INET6,
&ncec->ncec_addr);
}
}
return (0);
}
delta = TICK_TO_MSEC(now) - ncec->ncec_last;
ip1dbg(("ip_xmit: delta = %" PRId64
" ill_reachable_time = %d \n", delta,
ill->ill_reachable_time));
if (delta > (uint64_t)ill->ill_reachable_time) {
mutex_enter(&ncec->ncec_lock);
switch (ncec->ncec_state) {
case ND_REACHABLE:
ASSERT((ncec->ncec_flags & NCE_F_NONUD) == 0);
case ND_STALE:
ncec->ncec_state = ND_DELAY;
mutex_exit(&ncec->ncec_lock);
nce_restart_timer(ncec,
ipst->ips_delay_first_probe_time);
if (ip_debug > 3) {
pr_addr_dbg("ip_xmit: state"
" for %s changed to"
" DELAY\n", AF_INET6,
&ncec->ncec_addr);
}
break;
case ND_DELAY:
case ND_PROBE:
mutex_exit(&ncec->ncec_lock);
break;
case ND_UNREACHABLE:
mutex_exit(&ncec->ncec_lock);
break;
default:
ASSERT(0);
mutex_exit(&ncec->ncec_lock);
}
}
return (0);
case ND_INCOMPLETE:
is_probe = ipmp_packet_is_probe(mp, nce->nce_ill);
mutex_enter(&ncec->ncec_lock);
if (NCE_ISREACHABLE(ncec)) {
mutex_exit(&ncec->ncec_lock);
goto sendit;
}
nce_queue_mp(ncec, mp, is_probe);
mutex_exit(&ncec->ncec_lock);
DTRACE_PROBE2(ip__xmit__incomplete,
(ncec_t *), ncec, (mblk_t *), mp);
return (0);
case ND_INITIAL:
is_probe = ipmp_packet_is_probe(mp, nce->nce_ill);
mutex_enter(&ncec->ncec_lock);
if (NCE_ISREACHABLE(ncec)) {
mutex_exit(&ncec->ncec_lock);
goto sendit;
}
nce_queue_mp(ncec, mp, is_probe);
if (ncec->ncec_state == ND_INITIAL) {
ncec->ncec_state = ND_INCOMPLETE;
mutex_exit(&ncec->ncec_lock);
ip_ndp_resolve(ncec);
} else {
mutex_exit(&ncec->ncec_lock);
}
return (0);
case ND_UNREACHABLE:
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - ND_UNREACHABLE",
mp, ill);
freemsg(mp);
return (0);
default:
ASSERT(0);
BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
ip_drop_output("ipIfStatsOutDiscards - ND_other",
mp, ill);
freemsg(mp);
return (ENETUNREACH);
}
}
boolean_t
ip_cmpbuf(const void *abuf, uint_t alen, boolean_t b_valid, const void *bbuf,
uint_t blen)
{
if (!b_valid)
blen = 0;
if (alen != blen)
return (B_TRUE);
if (alen == 0)
return (B_FALSE);
return (bcmp(abuf, bbuf, alen));
}
boolean_t
ip_allocbuf(void **dstp, uint_t *dstlenp, boolean_t src_valid,
const void *src, uint_t srclen)
{
void *dst;
if (!src_valid)
srclen = 0;
ASSERT(*dstlenp == 0);
if (src != NULL && srclen != 0) {
dst = mi_alloc(srclen, BPRI_MED);
if (dst == NULL)
return (B_FALSE);
} else {
dst = NULL;
}
if (*dstp != NULL)
mi_free(*dstp);
*dstp = dst;
*dstlenp = dst == NULL ? 0 : srclen;
return (B_TRUE);
}
void
ip_savebuf(void **dstp, uint_t *dstlenp, boolean_t src_valid,
const void *src, uint_t srclen)
{
if (!src_valid)
srclen = 0;
ASSERT(*dstlenp == srclen);
if (src != NULL && srclen != 0)
bcopy(src, *dstp, srclen);
}
void
ip_pkt_free(ip_pkt_t *ipp)
{
uint_t fields = ipp->ipp_fields;
if (fields & IPPF_HOPOPTS) {
kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen);
ipp->ipp_hopopts = NULL;
ipp->ipp_hopoptslen = 0;
}
if (fields & IPPF_RTHDRDSTOPTS) {
kmem_free(ipp->ipp_rthdrdstopts, ipp->ipp_rthdrdstoptslen);
ipp->ipp_rthdrdstopts = NULL;
ipp->ipp_rthdrdstoptslen = 0;
}
if (fields & IPPF_DSTOPTS) {
kmem_free(ipp->ipp_dstopts, ipp->ipp_dstoptslen);
ipp->ipp_dstopts = NULL;
ipp->ipp_dstoptslen = 0;
}
if (fields & IPPF_RTHDR) {
kmem_free(ipp->ipp_rthdr, ipp->ipp_rthdrlen);
ipp->ipp_rthdr = NULL;
ipp->ipp_rthdrlen = 0;
}
if (fields & IPPF_IPV4_OPTIONS) {
kmem_free(ipp->ipp_ipv4_options, ipp->ipp_ipv4_options_len);
ipp->ipp_ipv4_options = NULL;
ipp->ipp_ipv4_options_len = 0;
}
if (fields & IPPF_LABEL_V4) {
kmem_free(ipp->ipp_label_v4, ipp->ipp_label_len_v4);
ipp->ipp_label_v4 = NULL;
ipp->ipp_label_len_v4 = 0;
}
if (fields & IPPF_LABEL_V6) {
kmem_free(ipp->ipp_label_v6, ipp->ipp_label_len_v6);
ipp->ipp_label_v6 = NULL;
ipp->ipp_label_len_v6 = 0;
}
ipp->ipp_fields &= ~(IPPF_HOPOPTS | IPPF_RTHDRDSTOPTS | IPPF_DSTOPTS |
IPPF_RTHDR | IPPF_IPV4_OPTIONS | IPPF_LABEL_V4 | IPPF_LABEL_V6);
}
int
ip_pkt_copy(ip_pkt_t *src, ip_pkt_t *dst, int kmflag)
{
uint_t fields = src->ipp_fields;
dst->ipp_fields = fields &
~(IPPF_HOPOPTS | IPPF_RTHDRDSTOPTS | IPPF_DSTOPTS |
IPPF_RTHDR | IPPF_IPV4_OPTIONS | IPPF_LABEL_V4 | IPPF_LABEL_V6);
dst->ipp_addr = src->ipp_addr;
dst->ipp_unicast_hops = src->ipp_unicast_hops;
dst->ipp_hoplimit = src->ipp_hoplimit;
dst->ipp_tclass = src->ipp_tclass;
dst->ipp_type_of_service = src->ipp_type_of_service;
if (!(fields & (IPPF_HOPOPTS | IPPF_RTHDRDSTOPTS | IPPF_DSTOPTS |
IPPF_RTHDR | IPPF_IPV4_OPTIONS | IPPF_LABEL_V4 | IPPF_LABEL_V6)))
return (0);
if (fields & IPPF_HOPOPTS) {
dst->ipp_hopopts = kmem_alloc(src->ipp_hopoptslen, kmflag);
if (dst->ipp_hopopts == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_HOPOPTS;
bcopy(src->ipp_hopopts, dst->ipp_hopopts,
src->ipp_hopoptslen);
dst->ipp_hopoptslen = src->ipp_hopoptslen;
}
if (fields & IPPF_RTHDRDSTOPTS) {
dst->ipp_rthdrdstopts = kmem_alloc(src->ipp_rthdrdstoptslen,
kmflag);
if (dst->ipp_rthdrdstopts == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_RTHDRDSTOPTS;
bcopy(src->ipp_rthdrdstopts, dst->ipp_rthdrdstopts,
src->ipp_rthdrdstoptslen);
dst->ipp_rthdrdstoptslen = src->ipp_rthdrdstoptslen;
}
if (fields & IPPF_DSTOPTS) {
dst->ipp_dstopts = kmem_alloc(src->ipp_dstoptslen, kmflag);
if (dst->ipp_dstopts == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_DSTOPTS;
bcopy(src->ipp_dstopts, dst->ipp_dstopts,
src->ipp_dstoptslen);
dst->ipp_dstoptslen = src->ipp_dstoptslen;
}
if (fields & IPPF_RTHDR) {
dst->ipp_rthdr = kmem_alloc(src->ipp_rthdrlen, kmflag);
if (dst->ipp_rthdr == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_RTHDR;
bcopy(src->ipp_rthdr, dst->ipp_rthdr,
src->ipp_rthdrlen);
dst->ipp_rthdrlen = src->ipp_rthdrlen;
}
if (fields & IPPF_IPV4_OPTIONS) {
dst->ipp_ipv4_options = kmem_alloc(src->ipp_ipv4_options_len,
kmflag);
if (dst->ipp_ipv4_options == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_IPV4_OPTIONS;
bcopy(src->ipp_ipv4_options, dst->ipp_ipv4_options,
src->ipp_ipv4_options_len);
dst->ipp_ipv4_options_len = src->ipp_ipv4_options_len;
}
if (fields & IPPF_LABEL_V4) {
dst->ipp_label_v4 = kmem_alloc(src->ipp_label_len_v4, kmflag);
if (dst->ipp_label_v4 == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_LABEL_V4;
bcopy(src->ipp_label_v4, dst->ipp_label_v4,
src->ipp_label_len_v4);
dst->ipp_label_len_v4 = src->ipp_label_len_v4;
}
if (fields & IPPF_LABEL_V6) {
dst->ipp_label_v6 = kmem_alloc(src->ipp_label_len_v6, kmflag);
if (dst->ipp_label_v6 == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_LABEL_V6;
bcopy(src->ipp_label_v6, dst->ipp_label_v6,
src->ipp_label_len_v6);
dst->ipp_label_len_v6 = src->ipp_label_len_v6;
}
if (fields & IPPF_FRAGHDR) {
dst->ipp_fraghdr = kmem_alloc(src->ipp_fraghdrlen, kmflag);
if (dst->ipp_fraghdr == NULL) {
ip_pkt_free(dst);
return (ENOMEM);
}
dst->ipp_fields |= IPPF_FRAGHDR;
bcopy(src->ipp_fraghdr, dst->ipp_fraghdr,
src->ipp_fraghdrlen);
dst->ipp_fraghdrlen = src->ipp_fraghdrlen;
}
return (0);
}
ipaddr_t
ip_pkt_source_route_v4(const ip_pkt_t *ipp)
{
ipaddr_t nexthop = INADDR_ANY;
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint8_t optlen;
uint32_t totallen;
if (!(ipp->ipp_fields & IPPF_IPV4_OPTIONS))
return (INADDR_ANY);
totallen = ipp->ipp_ipv4_options_len;
if (totallen & 0x3)
return (INADDR_ANY);
for (optval = ipoptp_first2(&opts, totallen, ipp->ipp_ipv4_options);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
opt = opts.ipoptp_cur;
switch (optval) {
uint8_t off;
case IPOPT_SSRR:
case IPOPT_LSRR:
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
break;
}
optlen = opts.ipoptp_len;
off = opt[IPOPT_OFFSET];
off--;
if (optlen < IP_ADDR_LEN ||
off > optlen - IP_ADDR_LEN) {
break;
}
bcopy((char *)opt + off, &nexthop, IP_ADDR_LEN);
if (nexthop == htonl(INADDR_LOOPBACK)) {
nexthop = INADDR_ANY;
break;
}
break;
}
}
return (nexthop);
}
void
ip_pkt_source_route_reverse_v4(ip_pkt_t *ipp)
{
ipaddr_t tmp;
ipoptp_t opts;
uchar_t *opt;
uint8_t optval;
uint32_t totallen;
if (!(ipp->ipp_fields & IPPF_IPV4_OPTIONS))
return;
totallen = ipp->ipp_ipv4_options_len;
if (totallen & 0x3)
return;
for (optval = ipoptp_first2(&opts, totallen, ipp->ipp_ipv4_options);
optval != IPOPT_EOL;
optval = ipoptp_next(&opts)) {
uint8_t off1, off2;
opt = opts.ipoptp_cur;
switch (optval) {
case IPOPT_SSRR:
case IPOPT_LSRR:
if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
break;
}
off1 = IPOPT_MINOFF_SR - 1;
off2 = opt[IPOPT_OFFSET] - IP_ADDR_LEN - 1;
while (off2 > off1) {
bcopy(opt + off2, &tmp, IP_ADDR_LEN);
bcopy(opt + off1, opt + off2, IP_ADDR_LEN);
bcopy(&tmp, opt + off2, IP_ADDR_LEN);
off2 -= IP_ADDR_LEN;
off1 += IP_ADDR_LEN;
}
opt[IPOPT_OFFSET] = IPOPT_MINOFF_SR;
break;
}
}
}
in6_addr_t *
ip_pkt_source_route_v6(const ip_pkt_t *ipp)
{
in6_addr_t *nexthop = NULL;
ip6_rthdr0_t *rthdr;
if (!(ipp->ipp_fields & IPPF_RTHDR))
return (NULL);
rthdr = (ip6_rthdr0_t *)ipp->ipp_rthdr;
if (rthdr->ip6r0_segleft == 0)
return (NULL);
nexthop = (in6_addr_t *)((char *)rthdr + sizeof (*rthdr));
return (nexthop);
}
zoneid_t
ip_get_zoneid_v4(ipaddr_t addr, mblk_t *mp, ip_recv_attr_t *ira,
zoneid_t lookup_zoneid)
{
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ire_t *ire;
int ire_flags = MATCH_IRE_TYPE;
zoneid_t zoneid = ALL_ZONES;
if (is_system_labeled() && !tsol_can_accept_raw(mp, ira, B_FALSE))
return (ALL_ZONES);
if (lookup_zoneid != ALL_ZONES)
ire_flags |= MATCH_IRE_ZONEONLY;
ire = ire_ftable_lookup_v4(addr, 0, 0, IRE_LOCAL | IRE_LOOPBACK,
NULL, lookup_zoneid, NULL, ire_flags, 0, ipst, NULL);
if (ire != NULL) {
zoneid = IP_REAL_ZONEID(ire->ire_zoneid, ipst);
ire_refrele(ire);
}
return (zoneid);
}
zoneid_t
ip_get_zoneid_v6(in6_addr_t *addr, mblk_t *mp, const ill_t *ill,
ip_recv_attr_t *ira, zoneid_t lookup_zoneid)
{
ip_stack_t *ipst = ira->ira_ill->ill_ipst;
ire_t *ire;
int ire_flags = MATCH_IRE_TYPE;
zoneid_t zoneid = ALL_ZONES;
if (is_system_labeled() && !tsol_can_accept_raw(mp, ira, B_FALSE))
return (ALL_ZONES);
if (IN6_IS_ADDR_LINKLOCAL(addr))
ire_flags |= MATCH_IRE_ILL;
if (lookup_zoneid != ALL_ZONES)
ire_flags |= MATCH_IRE_ZONEONLY;
ire = ire_ftable_lookup_v6(addr, NULL, NULL, IRE_LOCAL | IRE_LOOPBACK,
ill, lookup_zoneid, NULL, ire_flags, 0, ipst, NULL);
if (ire != NULL) {
zoneid = IP_REAL_ZONEID(ire->ire_zoneid, ipst);
ire_refrele(ire);
}
return (zoneid);
}
static void
ipobs_init(ip_stack_t *ipst)
{
netid_t id;
id = net_getnetidbynetstackid(ipst->ips_netstack->netstack_stackid);
ipst->ips_ip4_observe_pr = net_protocol_lookup(id, NHF_INET);
VERIFY(ipst->ips_ip4_observe_pr != NULL);
ipst->ips_ip6_observe_pr = net_protocol_lookup(id, NHF_INET6);
VERIFY(ipst->ips_ip6_observe_pr != NULL);
}
static void
ipobs_fini(ip_stack_t *ipst)
{
VERIFY(net_protocol_release(ipst->ips_ip4_observe_pr) == 0);
VERIFY(net_protocol_release(ipst->ips_ip6_observe_pr) == 0);
}
void
ipobs_hook(mblk_t *mp, int htype, zoneid_t zsrc, zoneid_t zdst,
const ill_t *ill, ip_stack_t *ipst)
{
hook_pkt_observe_t *hdr;
uint64_t grifindex;
mblk_t *imp;
imp = allocb(sizeof (*hdr), BPRI_HI);
if (imp == NULL)
return;
hdr = (hook_pkt_observe_t *)imp->b_rptr;
imp->b_wptr = imp->b_rptr + sizeof (dl_ipnetinfo_t);
imp->b_cont = mp;
ASSERT(DB_TYPE(mp) == M_DATA);
if (IS_UNDER_IPMP(ill))
grifindex = ipmp_ill_get_ipmp_ifindex(ill);
else
grifindex = 0;
hdr->hpo_version = 1;
hdr->hpo_htype = htons(htype);
hdr->hpo_pktlen = htonl((ulong_t)msgdsize(mp));
hdr->hpo_ifindex = htonl(ill->ill_phyint->phyint_ifindex);
hdr->hpo_grifindex = htonl(grifindex);
hdr->hpo_zsrc = htonl(zsrc);
hdr->hpo_zdst = htonl(zdst);
hdr->hpo_pkt = imp;
hdr->hpo_ctx = ipst->ips_netstack;
if (ill->ill_isv6) {
hdr->hpo_family = AF_INET6;
(void) hook_run(ipst->ips_ipv6_net_data->netd_hooks,
ipst->ips_ipv6observing, (hook_data_t)hdr);
} else {
hdr->hpo_family = AF_INET;
(void) hook_run(ipst->ips_ipv4_net_data->netd_hooks,
ipst->ips_ipv4observing, (hook_data_t)hdr);
}
imp->b_cont = NULL;
freemsg(imp);
}
boolean_t
ipif_lookup_testaddr_v4(ill_t *ill, const in_addr_t *v4srcp, ipif_t **ipifp)
{
ipif_t *ipif;
ipif = ipif_lookup_addr_exact(*v4srcp, ill, ill->ill_ipst);
if (ipif != NULL) {
if (ipifp != NULL)
*ipifp = ipif;
else
ipif_refrele(ipif);
return (B_TRUE);
}
ip1dbg(("ipif_lookup_testaddr_v4: cannot find ipif for src %x\n",
*v4srcp));
return (B_FALSE);
}
static int
ip_tp_cpu_update(cpu_setup_t what, int id, void *arg)
{
processorid_t cpu_seqid;
netstack_handle_t nh;
netstack_t *ns;
ASSERT(MUTEX_HELD(&cpu_lock));
switch (what) {
case CPU_CONFIG:
case CPU_ON:
case CPU_INIT:
case CPU_CPUPART_IN:
cpu_seqid = cpu[id]->cpu_seqid;
netstack_next_init(&nh);
while ((ns = netstack_next(&nh)) != NULL) {
tcp_stack_cpu_add(ns->netstack_tcp, cpu_seqid);
sctp_stack_cpu_add(ns->netstack_sctp, cpu_seqid);
udp_stack_cpu_add(ns->netstack_udp, cpu_seqid);
netstack_rele(ns);
}
netstack_next_fini(&nh);
break;
case CPU_UNCONFIG:
case CPU_OFF:
case CPU_CPUPART_OUT:
break;
default:
break;
}
return (0);
}