diff options
author | Cody Peter Mello <cody.mello@joyent.com> | 2017-12-06 01:28:51 +0000 |
---|---|---|
committer | Cody Peter Mello <cody.mello@joyent.com> | 2018-06-08 18:42:56 +0000 |
commit | 12a82dd4a058eb86f2b6b075bde38b01424f8f30 (patch) | |
tree | f144354889bae1e4320a5eb915d1014d854246ae /usr/src | |
parent | c6b0ac12851403af18c06800770e65c0314956fb (diff) | |
download | illumos-joyent-12a82dd4a058eb86f2b6b075bde38b01424f8f30.tar.gz |
OS-4683 Using the allowed-ips property prevents using dynamic addresses
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/lib/libdladm/common/libdladm.c | 40 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/libdladm.h | 3 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/libdladm_impl.h | 2 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/linkprop.c | 59 | ||||
-rw-r--r-- | usr/src/man/man1m/dladm.1m | 18 | ||||
-rw-r--r-- | usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh | 45 | ||||
-rw-r--r-- | usr/src/uts/common/inet/ip.h | 19 | ||||
-rw-r--r-- | usr/src/uts/common/inet/ip/ip.c | 84 | ||||
-rw-r--r-- | usr/src/uts/common/inet/ip/ip_if.c | 159 | ||||
-rw-r--r-- | usr/src/uts/common/io/dld/dld_proto.c | 36 | ||||
-rw-r--r-- | usr/src/uts/common/io/mac/mac_protect.c | 91 | ||||
-rw-r--r-- | usr/src/uts/common/sys/dld.h | 6 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mac_client_priv.h | 3 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mac_flow.h | 7 |
14 files changed, 424 insertions, 148 deletions
diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c index 211a775d89..1616885309 100644 --- a/usr/src/lib/libdladm/common/libdladm.c +++ b/usr/src/lib/libdladm/common/libdladm.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, Joyent, Inc. + * Copyright 2017, Joyent, Inc. */ #include <unistd.h> @@ -99,6 +99,18 @@ static link_protect_t link_protect_types[] = { }; #define LPTYPES (sizeof (link_protect_types) / sizeof (link_protect_t)) +typedef struct { + uint32_t ld_type; + char *ld_name; +} link_dynamic_t; + +static link_dynamic_t link_dynamic_types[] = { + { MPT_DYN_DHCPV4, "dhcpv4" }, + { MPT_DYN_DHCPV6, "dhcpv6" }, + { MPT_DYN_SLAAC, "slaac" }, +}; +#define DYNTYPES (sizeof (link_dynamic_types) / sizeof (link_dynamic_t)) + dladm_status_t dladm_open(dladm_handle_t *handle) { @@ -951,6 +963,28 @@ dladm_protect2str(uint32_t ptype, char *buf) return (buf); } + +/* + * Convert dynamic address method value to a string. + */ +const char * +dladm_dynamic2str(uint32_t dtype, char *buf, size_t size) +{ + const char *s = "--"; + link_dynamic_t *ld; + int i; + + for (i = 0; i < DYNTYPES; i++) { + ld = &link_dynamic_types[i]; + if (ld->ld_type == dtype) { + s = ld->ld_name; + break; + } + } + (void) snprintf(buf, size, "%s", dgettext(TEXT_DOMAIN, s)); + return (buf); +} + /* * Convert an IPv4 address to/from a string. */ @@ -1092,8 +1126,8 @@ fail: * is allocated here but should be freed by the caller. */ dladm_status_t -dladm_strs2range(char **prop_val, uint_t val_cnt, mac_propval_type_t type, - mac_propval_range_t **range) +dladm_strs2range(char **prop_val, uint_t val_cnt, + mac_propval_type_t type, mac_propval_range_t **range) { int i; char *endp; diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h index e5da4e3b44..8285517a95 100644 --- a/usr/src/lib/libdladm/common/libdladm.h +++ b/usr/src/lib/libdladm/common/libdladm.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015, Joyent, Inc. + * Copyright 2017, Joyent, Inc. */ #ifndef _LIBDLADM_H @@ -265,6 +265,7 @@ extern dladm_status_t dladm_str2pri(char *, mac_priority_level_t *); extern const char *dladm_pri2str(mac_priority_level_t, char *); extern dladm_status_t dladm_str2protect(char *, uint32_t *); extern const char *dladm_protect2str(uint32_t, char *); +extern const char *dladm_dynamic2str(uint32_t, char *, size_t); extern dladm_status_t dladm_str2ipv4addr(char *, void *); extern const char *dladm_ipv4addr2str(void *, char *); extern dladm_status_t dladm_str2ipv6addr(char *, void *); diff --git a/usr/src/lib/libdladm/common/libdladm_impl.h b/usr/src/lib/libdladm/common/libdladm_impl.h index 08981ff65f..285cb27aad 100644 --- a/usr/src/lib/libdladm/common/libdladm_impl.h +++ b/usr/src/lib/libdladm/common/libdladm_impl.h @@ -147,7 +147,7 @@ extern dladm_status_t dladm_flow_proplist_extract(dladm_arg_list_t *, * by the pd_check function. */ typedef dladm_status_t rp_extractf_t(val_desc_t *, uint_t, void *); -extern rp_extractf_t extract_priority, extract_cpus, +extern rp_extractf_t extract_dynamic_methods, extract_priority, extract_cpus, extract_protection, extract_allowallcids, extract_pool, extract_allowedips, extract_allowedcids, extract_maxbw, extract_rxrings, extract_txrings; diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c index 643c0968ea..0afd4d8bce 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -155,7 +155,7 @@ static pd_getf_t get_zone, get_autopush, get_rate_mod, get_rate, get_txrings, get_cntavail, get_secondary_macs, get_allowallcids, get_allowedips, get_allowedcids, get_pool, get_rings_range, get_linkmode_prop, - get_promisc_filtered; + get_promisc_filtered, get_dynamic_methods; static pd_setf_t set_zone, set_rate, set_powermode, set_radio, set_public_prop, set_resource, set_stp_prop, @@ -451,6 +451,13 @@ static val_desc_t link_protect_vals[] = { { "dhcp-nospoof", MPT_DHCPNOSPOOF }, }; +static val_desc_t link_dynamic_method_vals[] = { + { "dhcpv4", MPT_DYN_DHCPV4 }, + { "dhcpv6", MPT_DYN_DHCPV6 }, + { "slaac", MPT_DYN_SLAAC }, + { "addrconf", (MPT_DYN_SLAAC | MPT_DYN_DHCPV6) }, +}; + static val_desc_t dladm_bool_vals[] = { { "false", MPT_FALSE }, { "true", MPT_TRUE }, @@ -797,6 +804,11 @@ static prop_desc_t prop_table[] = { set_resource, NULL, get_protection, check_prop, 0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, + { "dynamic-methods", { "--", RESET_VAL }, + link_dynamic_method_vals, VALCNT(link_dynamic_method_vals), + set_resource, NULL, get_dynamic_methods, check_prop, 0, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, + { "promisc-filtered", { "on", 1 }, link_promisc_filtered_vals, VALCNT(link_promisc_filtered_vals), set_promisc_filtered, NULL, get_promisc_filtered, check_prop, 0, @@ -864,6 +876,7 @@ static resource_prop_t rsrc_prop_table[] = { {"pool", extract_pool}, {"pool-effective", extract_pool}, {"protection", extract_protection}, + {"dynamic-methods", extract_dynamic_methods}, {"allowed-ips", extract_allowedips}, {"allowed-dhcp-cids", extract_allowedcids}, {"allow-all-dhcp-cids", extract_allowallcids}, @@ -2929,6 +2942,50 @@ dladm_str2cid(char *buf, mac_dhcpcid_t *cid) /* ARGSUSED */ static dladm_status_t +get_dynamic_methods(dladm_handle_t handle, prop_desc_t *pdp, + datalink_id_t linkid, char **prop_val, uint_t *val_cnt, + datalink_media_t media, uint_t flags, uint_t *perm_flags) +{ + mac_resource_props_t mrp; + mac_protect_t *p; + dladm_status_t status; + uint32_t i, cnt = 0, setbits[32]; + + status = i_dladm_get_public_prop(handle, linkid, "resource", flags, + perm_flags, &mrp, sizeof (mrp)); + if (status != DLADM_STATUS_OK) + return (status); + + p = &mrp.mrp_protect; + dladm_find_setbits32(p->mp_dynamic, setbits, &cnt); + if (cnt > *val_cnt) + return (DLADM_STATUS_BADVALCNT); + + for (i = 0; i < cnt; i++) + (void) dladm_dynamic2str( + setbits[i], prop_val[i], DLADM_STRSIZE); + + *val_cnt = cnt; + return (DLADM_STATUS_OK); +} + +dladm_status_t +extract_dynamic_methods(val_desc_t *vdp, uint_t cnt, void *arg) +{ + mac_resource_props_t *mrp = arg; + uint32_t methods = 0; + int i; + + for (i = 0; i < cnt; i++) + methods |= (uint32_t)vdp[i].vd_val; + + mrp->mrp_protect.mp_dynamic = methods; + mrp->mrp_mask |= MRP_PROTECT; + return (DLADM_STATUS_OK); +} + +/* ARGSUSED */ +static dladm_status_t get_allowallcids(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid, char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags, uint_t *perm_flags) diff --git a/usr/src/man/man1m/dladm.1m b/usr/src/man/man1m/dladm.1m index 0519cd307f..7a29e88ad7 100644 --- a/usr/src/man/man1m/dladm.1m +++ b/usr/src/man/man1m/dladm.1m @@ -4991,6 +4991,24 @@ is not bound to any specific processor or processor set. .sp .ne 2 .na +\fB\fBdynamic-methods\fR\fR +.ad +.sp .6 +.RS 4n +When using IP spoofing protection (see \fBprotection\fR), addresses can be +learned dynamically by monitoring certain network traffic, like DHCP +transactions or IPv6 Stateless Address Autoconfiguration (SLAAC). By default, +all learning methods are permitted, but if \fBallowed-ips\fR contains any +addresses, then all methods are disabled, and any packets sent from addresses +previously learned will be dropped. This property allows selecting which ones +are re-enabled, where valid options are \fBdhcpv4\fR, \fBdhcpv6\fR, and +\fBslaac\fR. \fBaddrconf\fR is available as an alias for enabling both +\fBdhcpv6\fR and \fBslaac\fR. +.RE + +.sp +.ne 2 +.na \fB\fBlearn_limit\fR\fR .ad .sp .6 diff --git a/usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh b/usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh new file mode 100644 index 0000000000..4ed64a92b4 --- /dev/null +++ b/usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh @@ -0,0 +1,45 @@ +#!/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2017 Joyent, Inc. +# + +source ./common.ksh + +property="dynamic-methods" + +setup + +# All valid values on their own +epass slaac +epass dhcpv4 +epass dhcpv6 +epass addrconf + +# Combinations of values +epass slaac,dhcpv4 +epass slaac,dhcpv6 +epass dhcpv4,dhcpv6 +epass dhcpv4,addrconf +epass dhcpv4,dhcpv6,slaac + +# Illegal values +efail dhcpv8 +efail slaac,dhcpv8 +efail slack +efail ipv6 +efail dhcp +efail dhcpv + +cleanup +printf "TEST PASS: $ai_arg0\n" diff --git a/usr/src/uts/common/inet/ip.h b/usr/src/uts/common/inet/ip.h index cc8c489c8c..456d778cc7 100644 --- a/usr/src/uts/common/inet/ip.h +++ b/usr/src/uts/common/inet/ip.h @@ -22,7 +22,7 @@ /* * Copyright (c) 1990 Mentat Inc. * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2017, Joyent, Inc. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. * Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved. */ @@ -1415,6 +1415,7 @@ typedef union ill_g_head_u { #define ILL_CAPAB_DLD 0x20 /* DLD capabilities */ #define ILL_CAPAB_DLD_POLL 0x40 /* Polling */ #define ILL_CAPAB_DLD_DIRECT 0x80 /* Direct function call */ +#define ILL_CAPAB_DLD_IPCHECK 0x100 /* Check if IPs are permitted */ /* * Per-ill Hardware Checksumming capbilities. @@ -1728,6 +1729,8 @@ typedef struct ill_s { * Capabilities related fields. */ uint_t ill_dlpi_capab_state; /* State of capability query, IDCS_* */ + kcondvar_t ill_dlpi_capab_cv; /* CV for broadcasting state changes */ + kmutex_t ill_dlpi_capab_lock; /* Lock for accessing above Cond Var */ uint_t ill_capab_pending_cnt; uint64_t ill_capabilities; /* Enabled capabilities, ILL_CAPAB_* */ ill_hcksum_capab_t *ill_hcksum_capab; /* H/W cksumming capabilities */ @@ -1769,6 +1772,10 @@ typedef struct ill_s { * Used to save errors that occur during plumbing */ uint_t ill_ifname_pending_err; + /* + * Used to save errors that occur during binding + */ + uint_t ill_dl_bind_err; avl_node_t ill_avl_byppa; /* avl node based on ppa */ list_t ill_nce; /* pointer to nce_s list */ uint_t ill_refcnt; /* active refcnt by threads */ @@ -1934,6 +1941,7 @@ typedef struct ill_s { * ill_nd_lla_len ipsq + down ill only when ill is up * ill_phys_addr_pend ipsq + down ill only when ill is up * ill_ifname_pending_err ipsq ipsq + * ill_dl_bind_err ipsq ipsq * ill_avl_byppa ipsq, ill_g_lock write once * * ill_fastpath_list ill_lock ill_lock @@ -3575,6 +3583,8 @@ typedef void (*ip_flow_enable_t)(void *, ip_mac_tx_cookie_t); typedef void *(*ip_dld_callb_t)(void *, ip_flow_enable_t, void *); typedef boolean_t (*ip_dld_fctl_t)(void *, ip_mac_tx_cookie_t); +typedef boolean_t (*ip_mac_ipcheck_t)(void *, boolean_t, + in6_addr_t *); typedef int (*ip_capab_func_t)(void *, uint_t, void *, uint_t); @@ -3627,6 +3637,12 @@ typedef struct ill_dld_direct_s { /* DLD provided driver Tx */ void *idd_tx_fctl_dh; /* mac_client_handle */ } ill_dld_direct_t; +/* IP - DLD direct function call to check if an IP is allowed */ +typedef struct ill_dld_ipcheck_s { + ip_mac_ipcheck_t idi_allowed_df; + void *idi_allowed_dh; +} ill_dld_ipcheck_t; + /* IP - DLD polling capability */ typedef struct ill_dld_poll_s { ill_rx_ring_t idp_ring_tbl[ILL_MAX_RINGS]; @@ -3638,6 +3654,7 @@ struct ill_dld_capab_s { void *idc_capab_dh; /* dld_str_t *dsp */ ill_dld_direct_t idc_direct; ill_dld_poll_t idc_poll; + ill_dld_ipcheck_t idc_ipcheck; }; /* diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c index aef71717b3..d673a92387 100644 --- a/usr/src/uts/common/inet/ip/ip.c +++ b/usr/src/uts/common/inet/ip/ip.c @@ -4129,6 +4129,8 @@ ip_modclose(ill_t *ill) rw_destroy(&ill->ill_mcast_lock); mutex_destroy(&ill->ill_mcast_serializer); list_destroy(&ill->ill_nce); + cv_destroy(&ill->ill_dlpi_capab_cv); + mutex_destroy(&ill->ill_dlpi_capab_lock); /* * Now we are done with the module close pieces that @@ -8200,7 +8202,6 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg) 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; @@ -8300,7 +8301,8 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg) ill->ill_state_flags &= ~ILL_DOWN_IN_PROGRESS; mutex_exit(&ill->ill_lock); /* - * Something went wrong with the bind. We presumably + * Something went wrong with the bind. If this was the + * result of a DL_NOTE_REPLUMB, then we presumably * have an IOCTL hanging out waiting for completion. * Find it, take down the interface that was coming * up, and complete the IOCTL with the error noted. @@ -8317,6 +8319,15 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg) (void) ipif_down(ipif, NULL, NULL); /* error is set below the switch */ + } else { + /* + * There's no pending IOCTL, so the bind was + * most likely started by ill_dl_up(). We save + * the error and let it take care of responding + * to the IOCTL. + */ + ill->ill_dl_bind_err = dlea->dl_unix_errno ? + dlea->dl_unix_errno : ENXIO; } break; case DL_ENABMULTI_REQ: @@ -8440,55 +8451,7 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg) DTRACE_PROBE1(ip__rput__dlpi__bind__ack, ill_t *, ill); ill_nic_event_dispatch(ill, 0, NE_UP, NULL, 0); - /* - * Now bring up the resolver; when that is complete, we'll - * create IREs. Note that we intentionally mirror what - * ipif_up() would have done, because we got here by way of - * ill_dl_up(), which stopped ipif_up()'s processing. - */ - if (ill->ill_isv6) { - /* - * v6 interfaces. - * Unlike ARP which has to do another bind - * and attach, once we get here we are - * done with NDP - */ - (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) { - /* - * ARP and other v4 external resolvers. - * Leave the pending mblk intact so that - * the ioctl completes in ip_rput(). - */ - 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 { - /* The conn has started closing */ - err = EINTR; - } - } else { - /* - * This one is complete. Reply to pending ioctl. - */ - (void) ipif_resolver_up(ipif, Res_act_initial); - err = ipif_up_done(ipif); - } - - if ((err == 0) && (ill->ill_up_ipifs)) { + if (ill->ill_up_ipifs) { err = ill_up_ipifs(ill, q, mp1); if (err == EINPROGRESS) { freemsg(mp); @@ -8496,25 +8459,6 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg) } } - /* - * If we have a moved ipif to bring up, and everything has - * succeeded to this point, bring it up on the IPMP ill. - * Otherwise, leave it down -- the admin can try to bring it - * up by hand if need be. - */ - 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: { diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c index b88dcae2d1..c4b60daa68 100644 --- a/usr/src/uts/common/inet/ip/ip_if.c +++ b/usr/src/uts/common/inet/ip/ip_if.c @@ -174,7 +174,7 @@ static ipif_t *ipif_lookup_on_name_async(char *name, size_t namelen, static int ill_alloc_ppa(ill_if_t *, ill_t *); static void ill_delete_interface_type(ill_if_t *); -static int ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q); +static int ill_dl_up(ill_t *ill, ipif_t *ipif); static void ill_dl_down(ill_t *ill); static void ill_down(ill_t *ill); static void ill_down_ipifs(ill_t *, boolean_t); @@ -1380,6 +1380,18 @@ ill_capability_probe(ill_t *ill) ill->ill_dlpi_capab_state = IDCS_PROBE_SENT; } +static void +ill_capability_wait(ill_t *ill) +{ + while (ill->ill_capab_pending_cnt != 0) { + mutex_enter(&ill->ill_dlpi_capab_lock); + ipsq_exit(ill->ill_phyint->phyint_ipsq); + cv_wait(&ill->ill_dlpi_capab_cv, &ill->ill_dlpi_capab_lock); + mutex_exit(&ill->ill_dlpi_capab_lock); + VERIFY(ipsq_enter(ill, B_FALSE, CUR_OP) == B_TRUE); + } +} + void ill_capability_reset(ill_t *ill, boolean_t reneg) { @@ -1390,6 +1402,8 @@ ill_capability_reset(ill_t *ill, boolean_t reneg) ill->ill_dlpi_capab_state = reneg ? IDCS_RENEG : IDCS_RESET_SENT; + ASSERT(ill->ill_capab_reset_mp != NULL); + ill_capability_send(ill, ill->ill_capab_reset_mp); ill->ill_capab_reset_mp = NULL; /* @@ -2108,6 +2122,49 @@ ill_capability_lso_enable(ill_t *ill) } } +/* + * Check whether or not mac will prevent us from sending with a given IP + * address. This requires having the IPCHECK capability, which we should + * always be able to successfully negotiate, but if it's somehow missing + * then we just permit the caller to use the address, since mac does the + * actual enforcement and ip is just performing a courtesy check to help + * prevent users from unwittingly setting and attempting to use blocked + * addresses. + */ +static boolean_t +ill_ipcheck_addr(ill_t *ill, in6_addr_t *v6addr) +{ + if ((ill->ill_capabilities & ILL_CAPAB_DLD_IPCHECK) == 0) + return (B_TRUE); + + ill_dld_ipcheck_t *idi = &ill->ill_dld_capab->idc_ipcheck; + ip_mac_ipcheck_t ipcheck = idi->idi_allowed_df; + return (ipcheck(idi->idi_allowed_dh, ill->ill_isv6, v6addr)); +} + +static void +ill_capability_ipcheck_enable(ill_t *ill) +{ + ill_dld_capab_t *idc = ill->ill_dld_capab; + ill_dld_ipcheck_t *idi = &idc->idc_ipcheck; + dld_capab_ipcheck_t spoof; + int rc; + + ASSERT(IAM_WRITER_ILL(ill)); + + bzero(&spoof, sizeof (spoof)); + if ((rc = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_IPCHECK, + &spoof, DLD_ENABLE)) == 0) { + idi->idi_allowed_df = (ip_mac_ipcheck_t)spoof.ipc_allowed_df; + idi->idi_allowed_dh = spoof.ipc_allowed_dh; + ill->ill_capabilities |= ILL_CAPAB_DLD_IPCHECK; + } else { + cmn_err(CE_WARN, "warning: could not enable IPCHECK " + "capability, rc = %d\n", rc); + DTRACE_PROBE2(ipcheck__off, (ill_t *), ill, (int), rc); + } +} + static void ill_capability_dld_enable(ill_t *ill) { @@ -2115,15 +2172,15 @@ ill_capability_dld_enable(ill_t *ill) ASSERT(IAM_WRITER_ILL(ill)); - if (ill->ill_isv6) - return; - ill_mac_perim_enter(ill, &mph); if (!ill->ill_isv6) { ill_capability_direct_enable(ill); ill_capability_poll_enable(ill); ill_capability_lso_enable(ill); } + + ill_capability_ipcheck_enable(ill); + ill->ill_capabilities |= ILL_CAPAB_DLD; ill_mac_perim_exit(ill, mph); } @@ -2188,6 +2245,15 @@ ill_capability_dld_disable(ill_t *ill) NULL, DLD_DISABLE); } + if ((ill->ill_capabilities & ILL_CAPAB_DLD_IPCHECK) != 0) { + ASSERT(ill->ill_dld_capab->idc_ipcheck.idi_allowed_df != NULL); + ASSERT(ill->ill_dld_capab->idc_ipcheck.idi_allowed_dh != NULL); + + ill->ill_capabilities &= ~ILL_CAPAB_DLD_IPCHECK; + (void) idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_IPCHECK, + NULL, DLD_DISABLE); + } + ill->ill_capabilities &= ~ILL_CAPAB_DLD; ill_mac_perim_exit(ill, mph); } @@ -3429,6 +3495,9 @@ ill_init_common(ill_t *ill, queue_t *q, boolean_t isv6, boolean_t is_loopback, ill->ill_max_buf = ND_MAX_Q; ill->ill_refcnt = 0; + cv_init(&ill->ill_dlpi_capab_cv, NULL, NULL, NULL); + mutex_init(&ill->ill_dlpi_capab_lock, NULL, MUTEX_DEFAULT, NULL); + return (0); } @@ -9676,7 +9745,6 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, in6_addr_t v6addr; boolean_t need_up = B_FALSE; ill_t *ill; - int i; ip1dbg(("ip_sioctl_addr(%s:%u %p)\n", ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif)); @@ -9751,20 +9819,9 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, IN6_IPADDR_TO_V4MAPPED(addr, &v6addr); } - /* - * verify that the address being configured is permitted by the - * ill_allowed_ips[] for the interface. - */ - if (ill->ill_allowed_ips_cnt > 0) { - for (i = 0; i < ill->ill_allowed_ips_cnt; i++) { - if (IN6_ARE_ADDR_EQUAL(&ill->ill_allowed_ips[i], - &v6addr)) - break; - } - if (i == ill->ill_allowed_ips_cnt) { - pr_addr_dbg("!allowed addr %s\n", AF_INET6, &v6addr); - return (EPERM); - } + /* verify that the address being configured is permitted by mac */ + if (!ill_ipcheck_addr(ill, &v6addr)) { + return (EPERM); } /* * Even if there is no change we redo things just to rerun @@ -12704,6 +12761,12 @@ ill_dl_down(ill_t *ill) } ill->ill_unbind_mp = NULL; + + mutex_enter(&ill->ill_lock); + ill->ill_dl_up = 0; + ill_nic_event_dispatch(ill, 0, NE_DOWN, NULL, 0); + mutex_exit(&ill->ill_lock); + if (mp != NULL) { ip1dbg(("ill_dl_down: %s (%u) for %s\n", dl_primstr(*(int *)mp->b_rptr), *(int *)mp->b_rptr, @@ -12726,11 +12789,10 @@ ill_dl_down(ill_t *ill) ill_capability_dld_disable(ill); ill_capability_reset(ill, B_FALSE); ill_dlpi_send(ill, mp); + + /* Wait for the capability reset to finish */ + ill_capability_wait(ill); } - mutex_enter(&ill->ill_lock); - ill->ill_dl_up = 0; - ill_nic_event_dispatch(ill, 0, NE_DOWN, NULL, 0); - mutex_exit(&ill->ill_lock); } void @@ -12859,6 +12921,10 @@ ill_capability_done(ill_t *ill) if (ill->ill_capab_pending_cnt == 0 && ill->ill_dlpi_capab_state == IDCS_OK) ill_capability_reset_alloc(ill); + + mutex_enter(&ill->ill_dlpi_capab_lock); + cv_broadcast(&ill->ill_dlpi_capab_cv); + mutex_exit(&ill->ill_dlpi_capab_lock); } /* @@ -14480,7 +14546,14 @@ ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp) * address/netmask etc cause a down/up dance, but * does not cause an unbind (DL_UNBIND) with the driver */ - return (ill_dl_up(ill, ipif, mp, q)); + if ((err = ill_dl_up(ill, ipif)) != 0) { + return (err); + } + } + + /* Reject bringing up interfaces with unusable IP addresses */ + if (!ill_ipcheck_addr(ill, &ipif->ipif_v6lcl_addr)) { + return (EPERM); } /* @@ -14593,24 +14666,22 @@ ill_delete_ires(ill_t *ill) /* * Perform a bind for the physical device. - * When the routine returns EINPROGRESS then mp has been consumed and - * the ioctl will be acked from ip_rput_dlpi. - * Allocate an unbind message and save it until ipif_down. + * + * When the routine returns successfully then dlpi has been bound and + * capabilities negotiated. An unbind message will have been allocated + * for later use in ipif_down. */ static int -ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) +ill_dl_up(ill_t *ill, ipif_t *ipif) { mblk_t *bind_mp = NULL; mblk_t *unbind_mp = NULL; - conn_t *connp; - boolean_t success; int err; DTRACE_PROBE2(ill__downup, char *, "ill_dl_up", ill_t *, ill); ip1dbg(("ill_dl_up(%s)\n", ill->ill_name)); ASSERT(IAM_WRITER_ILL(ill)); - ASSERT(mp != NULL); /* * Make sure we have an IRE_MULTICAST in case we immediately @@ -14645,19 +14716,6 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) if (unbind_mp == NULL) goto bad; } - /* - * Record state needed to complete this operation when the - * DL_BIND_ACK shows up. Also remember the pre-allocated mblks. - */ - connp = CONN_Q(q) ? Q_TO_CONN(q) : NULL; - ASSERT(connp != NULL || !CONN_Q(q)); - GRAB_CONN_LOCK(q); - mutex_enter(&ipif->ipif_ill->ill_lock); - success = ipsq_pending_mp_add(connp, ipif, q, mp, 0); - mutex_exit(&ipif->ipif_ill->ill_lock); - RELEASE_CONN_LOCK(q); - if (!success) - goto bad; /* * Save the unbind message for ill_dl_down(); it will be consumed when @@ -14669,6 +14727,13 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) ill_dlpi_send(ill, bind_mp); /* Send down link-layer capabilities probe if not already done. */ ill_capability_probe(ill); + /* Wait for DLPI to be bound and the capability probe to finish */ + ill_capability_wait(ill); + + /* DLPI failed to bind. Return the saved error */ + if (!ill->ill_dl_up) { + return (ill->ill_dl_bind_err); + } /* * Sysid used to rely on the fact that netboots set domainname @@ -14686,11 +14751,7 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) cmn_err(CE_WARN, "no cached dhcp response"); } - /* - * This operation will complete in ip_rput_dlpi with either - * a DL_BIND_ACK or DL_ERROR_ACK. - */ - return (EINPROGRESS); + return (0); bad: ip1dbg(("ill_dl_up(%s) FAILED\n", ill->ill_name)); diff --git a/usr/src/uts/common/io/dld/dld_proto.c b/usr/src/uts/common/io/dld/dld_proto.c index 1ee00681fc..b09d4db0f5 100644 --- a/usr/src/uts/common/io/dld/dld_proto.c +++ b/usr/src/uts/common/io/dld/dld_proto.c @@ -1375,6 +1375,9 @@ dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags) ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); + if (dsp->ds_sap == ETHERTYPE_IPV6) + return (ENOTSUP); + switch (flags) { case DLD_ENABLE: dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf, @@ -1484,6 +1487,9 @@ dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags) ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); + if (dsp->ds_sap == ETHERTYPE_IPV6) + return (ENOTSUP); + switch (flags) { case DLD_ENABLE: return (dld_capab_poll_enable(dsp, poll)); @@ -1494,12 +1500,34 @@ dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags) } static int +dld_capab_ipcheck(dld_str_t *dsp, void *data, uint_t flags) +{ + dld_capab_ipcheck_t *ipc = data; + + ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); + + switch (flags) { + case DLD_ENABLE: + ipc->ipc_allowed_df = (uintptr_t)mac_protect_check_addr; + ipc->ipc_allowed_dh = dsp->ds_mch; + return (0); + case DLD_DISABLE: + return (0); + } + + return (ENOTSUP); +} + +static int dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags) { dld_capab_lso_t *lso = data; ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); + if (dsp->ds_sap == ETHERTYPE_IPV6) + return (ENOTSUP); + switch (flags) { case DLD_ENABLE: { mac_capab_lso_t mac_lso; @@ -1545,7 +1573,7 @@ dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags) * completes. So we limit the check to DLD_ENABLE case. */ if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) && - ((dsp->ds_sap != ETHERTYPE_IP || + (((dsp->ds_sap != ETHERTYPE_IP && dsp->ds_sap != ETHERTYPE_IPV6) || !check_mod_above(dsp->ds_rq, "ip")) && !check_mod_above(dsp->ds_rq, "vnd"))) { return (ENOTSUP); @@ -1568,6 +1596,10 @@ dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags) err = dld_capab_lso(dsp, data, flags); break; + case DLD_CAPAB_IPCHECK: + err = dld_capab_ipcheck(dsp, data, flags); + break; + default: err = ENOTSUP; break; @@ -1634,7 +1666,7 @@ proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) * native media type so we know that there are no transformations that * would have to happen to the mac header that it receives. */ - if ((dsp->ds_sap == ETHERTYPE_IP && + if (((dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) && check_mod_above(dsp->ds_rq, "ip")) || (check_mod_above(dsp->ds_rq, "vnd") && dsp->ds_mip->mi_media == dsp->ds_mip->mi_nativemedia)) { diff --git a/usr/src/uts/common/io/mac/mac_protect.c b/usr/src/uts/common/io/mac/mac_protect.c index fafb54cef9..ee493bbca1 100644 --- a/usr/src/uts/common/io/mac/mac_protect.c +++ b/usr/src/uts/common/io/mac/mac_protect.c @@ -209,7 +209,7 @@ typedef struct slaac_addr { } slaac_addr_t; static void start_txn_cleanup_timer(mac_client_impl_t *); -static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t); +static boolean_t dynamic_method_set(mac_protect_t *, uint32_t); #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++ @@ -580,8 +580,7 @@ intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end) if (get_dhcpv4_info(ipha, end, &dh4) != 0) return (B_TRUE); - /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ - if (allowed_ips_set(mrp, IPV4_VERSION)) + if (!dynamic_method_set(&mrp->mrp_protect, MPT_DYN_DHCPV4)) return (B_FALSE); if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || @@ -1310,8 +1309,7 @@ intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end) if (get_dhcpv6_info(ip6h, end, &dh6) != 0) return (B_TRUE); - /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ - if (allowed_ips_set(mrp, IPV6_VERSION)) + if (!dynamic_method_set(&mrp->mrp_protect, MPT_DYN_DHCPV6)) return (B_FALSE); /* @@ -1517,6 +1515,10 @@ intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end, { struct nd_opt_hdr *opt; int len, optlen; + mac_protect_t *protect = &MCIP_RESOURCE_PROPS(mcip)->mrp_protect; + + if (!dynamic_method_set(protect, MPT_DYN_SLAAC)) + return; if (ip6h->ip6_hlim != 255) { DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim); @@ -1755,6 +1757,7 @@ ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect, if (*addr == INADDR_ANY) return (B_TRUE); + /* If any specific addresses or subnets are allowed, check them */ for (i = 0; i < protect->mp_ipaddrcnt; i++) { mac_ipaddr_t *v4addr = &protect->mp_ipaddrs[i]; @@ -1775,14 +1778,19 @@ ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect, return (B_TRUE); } } - return (protect->mp_ipaddrcnt == 0 ? - check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE); + + if (dynamic_method_set(protect, MPT_DYN_DHCPV4)) { + return (check_dhcpv4_dyn_ip(mcip, *addr)); + } + + return (B_FALSE); } static boolean_t ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, in6_addr_t *addr) { + boolean_t slaac_enabled, dhcpv6_enabled; uint_t i; /* @@ -1793,7 +1801,7 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr))) return (B_TRUE); - + /* If any specific addresses or subnets are allowed, check them */ for (i = 0; i < protect->mp_ipaddrcnt; i++) { mac_ipaddr_t *v6addr = &protect->mp_ipaddrs[i]; @@ -1804,12 +1812,15 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, return (B_TRUE); } - if (protect->mp_ipaddrcnt == 0) { - return (check_slaac_ip(mcip, addr) || - check_dhcpv6_dyn_ip(mcip, addr)); - } else { - return (B_FALSE); - } + slaac_enabled = dynamic_method_set(protect, MPT_DYN_SLAAC); + if (slaac_enabled && check_slaac_ip(mcip, addr)) + return (B_TRUE); + + dhcpv6_enabled = dynamic_method_set(protect, MPT_DYN_DHCPV6); + if (dhcpv6_enabled && check_dhcpv6_dyn_ip(mcip, addr)) + return (B_TRUE); + + return (B_FALSE); } /* @@ -2577,6 +2588,11 @@ mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr) } else if (np->mp_allcids != 0) { cp->mp_allcids = MPT_TRUE; } + if (np->mp_dynamic == MPT_RESET) { + cp->mp_dynamic = 0; + } else if (np->mp_dynamic != 0) { + cp->mp_dynamic = np->mp_dynamic; + } } void @@ -2620,15 +2636,50 @@ mac_protect_fini(mac_client_impl_t *mcip) } static boolean_t -allowed_ips_set(mac_resource_props_t *mrp, uint32_t af) +dynamic_method_set(mac_protect_t *mpt, uint32_t method) { - int i; + if (mpt->mp_dynamic != 0) { + return ((mpt->mp_dynamic & method) != 0); + } else { + return (mpt->mp_ipaddrcnt == 0); + } +} - for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) { - if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af) - return (B_TRUE); +boolean_t +mac_protect_check_addr(mac_client_handle_t mch, boolean_t isv6, + in6_addr_t *v6addr) +{ + mac_perim_handle_t perim; + mac_client_impl_t *mcip = (mac_client_impl_t *)mch; + mac_handle_t mh = (mac_handle_t)mcip->mci_mip; + + mac_perim_enter_by_mh(mh, &perim); + + mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); + mac_protect_t *p; + boolean_t allowed; + + ASSERT(mrp != NULL); + + p = &mrp->mrp_protect; + + /* If mac protection/ipnospoof isn't enabled, return true */ + if ((mrp->mrp_mask & MRP_PROTECT) == 0 || + (p->mp_types & MPT_IPNOSPOOF) == 0) { + allowed = B_TRUE; + goto done; } - return (B_FALSE); + + if (isv6) { + allowed = ipnospoof_check_v6(mcip, p, v6addr); + } else { + in_addr_t *v4addr = &V4_PART_OF_V6((*v6addr)); + allowed = ipnospoof_check_v4(mcip, p, v4addr); + } + +done: + mac_perim_exit(perim); + return (allowed); } mac_protect_t * diff --git a/usr/src/uts/common/sys/dld.h b/usr/src/uts/common/sys/dld.h index 158a802c4a..26d32ab34b 100644 --- a/usr/src/uts/common/sys/dld.h +++ b/usr/src/uts/common/sys/dld.h @@ -358,6 +358,7 @@ typedef struct dld_ioc_led { #define DLD_CAPAB_POLL 0x00000002 #define DLD_CAPAB_PERIM 0x00000003 #define DLD_CAPAB_LSO 0x00000004 +#define DLD_CAPAB_IPCHECK 0x00000005 #define DLD_ENABLE 0x00000001 #define DLD_DISABLE 0x00000002 @@ -414,6 +415,11 @@ typedef struct dld_capab_direct_s { uint_t di_flags; } dld_capab_direct_t; +typedef struct dld_capab_ipcheck_s { + uintptr_t ipc_allowed_df; + void *ipc_allowed_dh; +} dld_capab_ipcheck_t; + /* * Polling/softring capability. */ diff --git a/usr/src/uts/common/sys/mac_client_priv.h b/usr/src/uts/common/sys/mac_client_priv.h index 01cb27644c..97b3fd685a 100644 --- a/usr/src/uts/common/sys/mac_client_priv.h +++ b/usr/src/uts/common/sys/mac_client_priv.h @@ -58,6 +58,9 @@ extern const mac_info_t *mac_info(mac_handle_t); extern boolean_t mac_info_get(const char *, mac_info_t *); extern boolean_t mac_promisc_get(mac_handle_t); +extern boolean_t mac_protect_check_addr(mac_client_handle_t, boolean_t, + in6_addr_t *); + extern int mac_start(mac_handle_t); extern void mac_stop(mac_handle_t); diff --git a/usr/src/uts/common/sys/mac_flow.h b/usr/src/uts/common/sys/mac_flow.h index d269d747c2..d37752ec23 100644 --- a/usr/src/uts/common/sys/mac_flow.h +++ b/usr/src/uts/common/sys/mac_flow.h @@ -158,6 +158,12 @@ typedef enum { #define MPT_FALSE 0x00000000 #define MPT_TRUE 0x00000001 +/* Dynamic address detection types */ +#define MPT_DYN_DHCPV4 0x00000001 +#define MPT_DYN_DHCPV6 0x00000002 +#define MPT_DYN_SLAAC 0x00000004 +#define MPT_DYN_ALL 0x00000007 + typedef struct mac_ipaddr_s { uint32_t ip_version; in6_addr_t ip_addr; @@ -183,6 +189,7 @@ typedef struct mac_protect_s { uint32_t mp_cidcnt; /* Count of allowed DHCP CIDs */ mac_dhcpcid_t mp_cids[MPT_MAXCID]; /* Allowed DHCP CIDs */ uint32_t mp_allcids; /* Whether to allow all CIDs through */ + uint32_t mp_dynamic; /* Enabled dynamic address methods */ } mac_protect_t; /* The default priority for links */ |