tun.c revision 91785ffff883655a89eb843ed89bcd24d717e320
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Tunnel driver
* Implements the logic for IP (IPv4 or IPv6) encapsulation
* within IP (IPv4 or IPv6)
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ethernet.h>
#include <sys/netstack.h>
#include <sys/isa_defs.h>
#include <netinet/igmp_var.h>
#include <inet/ipsec_impl.h>
#include <inet/ipsec_impl.h>
static void tun_bufcall_handler(void *);
mblk_t *);
mblk_t *);
static void tun_sendsdusize(queue_t *);
static void tun_timeout_handler(void *);
static void tun_send_ire_req(queue_t *);
static void tun_rem_ppa_list(tun_t *);
static void tun_rem_tun_byaddr_list(tun_t *);
static int tun_send_bind_req(queue_t *);
static int tun_stat_kstat_update(kstat_t *, int);
static void tun_stack_fini(netstackid_t, void *);
/* module's defined constants, globals and data structures */
#define IP "ip"
#define IP6 "ip6"
#define TUN_DEBUG
#define TUN_LINK_EXTRA_OFF 32
#define IPV6V4_DEF_TTL 60
#define IPV6V4_DEF_ENCAP 60
#define TUN_WHO_BUF 60
#ifdef TUN_DEBUG
/* levels of debugging verbosity */
/*
* Global variable storing debugging level for all tunnels. By default
* all crucial messages will be printed. Value can be masked to exclusively
* print certain debug levels and not others.
*/
#else
#define tun0dbg(a) /* */
#define tun1dbg(a) /* */
#define tun2dbg(a) /* */
#define tun3dbg(a) /* */
#endif /* TUN_DEBUG */
/* canned DL_INFO_ACK - adjusted based on tunnel type */
dl_info_ack_t infoack = {
DL_INFO_ACK, /* dl_primitive */
4196, /* dl_max_sdu */
0, /* dl_min_sdu */
0, /* dl_addr_length */
DL_IPV4, /* dl_mac_type */
0, /* dl_reserved */
DL_UNATTACHED, /* dl_current_state */
0, /* dl_sap_length */
DL_CLDLS, /* dl_service_mode */
0, /* dl_qos_length */
0, /* dl_qos_offset */
0, /* dl_qos_range_length */
0, /* dl_qos_range_offset */
DL_STYLE2, /* dl_provider_style */
0, /* dl_addr_offset */
DL_VERSION_2, /* dl_version */
0, /* dl_brdcast_addr_length */
0, /* dl_brdcst_addr_offset */
0 /* dl_grow */
};
/*
* canned DL_BIND_ACK - IP doesn't use any of this info.
*/
dl_bind_ack_t bindack = {
DL_BIND_ACK, /* dl_primitive */
0, /* dl_sap */
0, /* dl_addr_length */
0, /* dl_addr_offset */
0, /* dl_max_conind */
0 /* dl_xidtest_flg */
};
/*
* Canned IPv6 destination options header containing Tunnel
* Encapsulation Limit option.
*/
static struct tun_encap_limit tun_limit_init_upper_v4 = {
{ IPPROTO_ENCAP, 0 },
1,
IPV6_DEFAULT_ENCAPLIMIT, /* filled in with actual value later */
1,
0
};
static struct tun_encap_limit tun_limit_init_upper_v6 = {
{ IPPROTO_IPV6, 0 },
1,
IPV6_DEFAULT_ENCAPLIMIT, /* filled in with actual value later */
1,
0
};
static void tun_add_byaddr(tun_t *);
netstack_t *);
/* streams linkages */
static struct module_info info = {
TUN_MODID, /* module id number */
TUN_NAME, /* module name */
1, /* min packet size accepted */
INFPSZ, /* max packet size accepted */
65536, /* hi-water mark */
1024 /* lo-water mark */
};
tun_open, /* open procedure */
tun_close, /* close procedure */
NULL, /* for future use */
&info, /* module information structure */
NULL /* module statistics structure */
};
NULL,
NULL,
NULL,
&info,
};
&tunrinit, /* read side queue init */
&tunwinit, /* write side queue init */
NULL, /* mux read side init */
NULL /* mux write side init */
};
static struct fmodsw tun_fmodsw = {
&tuninfo,
};
static struct modlstrmod modlstrmod = {
"configured tunneling module",
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int rc;
/*
* We want to be informed each time a stack is created or
* destroyed in the kernel, so we can maintain the
* set of tun_stack_t's.
*/
if (rc != 0)
return (rc);
}
int
_fini(void)
{
int error;
if (error == 0)
return (error);
}
int
{
}
/*
* this module is meant to be pushed on an instance of IP and
* have an instance of IP pushed on top of it.
*/
/* ARGSUSED */
int
{
netstack_t *ns;
/* re-open of an already open instance */
return (0);
}
return (EINVAL);
}
tun1dbg(("tun_open\n"));
/*
* For exclusive stacks we set the zoneid to zero
* to make IP operate as if in the global zone.
*/
else
return (ENOMEM);
}
/* allocate per-instance structure */
/*
* Based on the lower version of IP, initialize stuff that
* won't change
*/
/*
* The tunnel MTU is recalculated when we know more
* about the tunnel destination.
*/
ipha->ipha_type_of_service = 0;
} else {
return (ENXIO);
}
/*
* If this is the automatic tunneling module, atun, verify that the
* lower protocol is IPv4 and set TUN_AUTOMATIC. Since we don't do
* automatic tunneling over IPv6, trying to run over IPv6 is an error,
* so free memory and return an error.
*/
} else {
/* Error. */
return (ENXIO);
}
/*
* Set 6to4 flag if this is the 6to4tun module and make
* the same checks mentioned above.
*/
} else {
/* Error. */
return (ENXIO);
}
}
qprocson(q);
return (0);
}
/* ARGSUSED */
int
{
netstack_t *ns;
/* Cancel outstanding qtimeouts() or qbufcalls() */
qprocsoff(q);
/* NOTE: tun_rem_ppa_list() may unlink tun_itp from its AVL tree. */
/* In brackets because of ITP_REFRELE's brackets. */
}
/* remove tun_t from global list */
/* free per-instance struct */
return (0);
}
/*
* Cancel bufcall and timer requests
* Don't need to hold lock. protected by perimeter
*/
static void
{
if (evs->ev_rbufcid != 0) {
evs->ev_rbufcid = 0;
}
if (evs->ev_wbufcid != 0) {
evs->ev_wbufcid = 0;
}
if (evs->ev_rtimoutid != 0) {
evs->ev_rtimoutid = 0;
}
if (evs->ev_wtimoutid != 0) {
evs->ev_wtimoutid = 0;
}
}
/*
* Called by bufcall() when memory becomes available
* Don't need to hold lock. protected by perimeter
*/
static void
tun_bufcall_handler(void *arg)
{
evs->ev_rbufcid = 0;
} else {
evs->ev_wbufcid = 0;
}
enableok(q);
qenable(q);
}
/*
* Called by timeout (if we couldn't do a bufcall)
* Don't need to hold lock. protected by perimeter
*/
static void
tun_timeout_handler(void *arg)
{
evs->ev_rtimoutid = 0;
} else {
evs->ev_wtimoutid = 0;
}
enableok(q);
qenable(q);
}
/*
* This routine is called when a message buffer can not
* be allocated. M_PCPROT message are converted to M_PROTO, but
* other than that, the mblk passed in must not be a high
* priority message (putting a hight priority message back on
* the queue is a bad idea)
* Side effect: the queue is disabled
* (timeout or bufcall handler will re-enable the queue)
* tun_cancel_rec_evs() must be called in close to cancel all
* outstanding requests.
*/
static void
{
/*
* To avoid re-enabling the queue, change the high priority
* M_PCPROTO message to a M_PROTO before putting it on the queue
*/
/*
* Make sure there is at most one outstanding request per queue.
*/
return;
} else {
return;
}
noenable(q);
/*
* locking is needed here because this routine may be called
* with two puts() running
*/
else
} else {
else
}
}
/*
* tun_realloc_mblk(q, mp, size, orig_mp, copy)
*
* q - pointer to a queue_t, must not be NULL
* mp - pointer to an mblk to copy, can be NULL
* size - Number of bytes being (re)allocated
* orig_mp - pointer to the original mblk_t which will be passed to
* tun_recover if the memory (re)allocation fails. This is done
* so that the message can be rescheduled on the queue.
* orig_mp must be NULL if the original mblk_t is a high priority
* message of type other then M_PCPROTO.
* copy - a boolean to specify wheater the contents of mp should be copied
* into the new mblk_t returned by this function.
*
* note: this routine will adjust the b_rptr and b_wptr of the
* mblk. Returns an mblk able to hold the requested size or
* NULL if allocation failed. If copy is true, original
* contents, if any, will be copied to new mblk
*/
static mblk_t *
{
/*
* If we are passed in an mblk.. check to make sure that
* it is big enough and we are the only users of the mblk
* If not, then try and allocate one
*/
/* allocate at least as much as we had -- don't shrink */
} else {
}
/*
* Reschedule the mblk via bufcall or timeout
* if orig_mp is non-NULL
*/
}
tun1dbg(("tun_realloc_mblk: couldn't allocate" \
" dl_ok_ack mblk\n"));
return (NULL);
}
if (copy)
}
} else {
if (copy)
}
}
return (mp);
}
/* send a DL_OK_ACK back upstream */
static void
{
return;
}
}
/*
* Send a DL_NOTIFY_IND message with DL_NOTE_SDU_SIZE up to notify IP of a
* link MTU change.
*/
static void
{
return;
return;
}
notify->dl_addr_length = 0;
notify->dl_addr_offset = 0;
/*
* We send this notification to the upper IP instance who is using
* us as a device.
*/
}
/* send a DL_ERROR_ACK back upstream */
static void
{
return;
}
}
/*
* Free all messages in an mblk chain and optionally collect
* byte-counter stats. Caller responsible for per-packet stats
*/
static void
{
}
}
/*
* Send all messages in a chain of mblk chains and optionally collect
* byte-counter stats. Caller responsible for per-packet stats, and insuring
* mp is always non-NULL.
*
* This is a macro so we can save stack. Assume the caller function
* has local-variable "nmp" as a placeholder. Define two versions, one with
* byte-counting stats and one without.
*/
do { \
\
do { \
\
/*
* Macro that not only checks tun_itp, but also sees if one got loaded
* via ipsecconf(1m)/PF_POLICY behind our backs. Note the sleazy update of
* (tun)->tun_itp_gen so we don't lose races with other possible updates via
* PF_POLICY.
*/
!= NULL)))
/*
* Search tuns_byaddr_list for occurrence of tun_t with matching
* inner addresses. This function does not take into account
* prefixes. Possibly we could generalize this function in the
* future with V6_MASK_EQ() and pass in an all 1's prefix for IP
* address matches.
* Returns NULL on no match.
* This function is not directly called - it's assigned into itp_get_byaddr().
*/
static ipsec_tun_pol_t *
{
} else {
}
/*
*/
/*
* walk through list of tun_t looking for a match of
* inner addresses. Addresses are inserted with
* IN6_IPADDR_TO_V4MAPPED(), so v6 matching works for
* all cases.
*/
tun1dbg(("itp_get_byaddr: No IPsec policy on "
"matching tun_t instance %p/%s\n",
continue;
}
tun1dbg(("itp_get_byaddr: Found matching tun_t %p with "
"IPsec policy\n", (void *)tun_list));
tun1dbg(("itp_get_byaddr: Found itp %p \n",
(void *)itp));
return (itp);
}
}
/* didn't find one, return zilch */
tun1dbg(("itp_get_byaddr: No matching tunnel instances with policy\n"));
return (NULL);
}
/*
* Search tuns_byaddr_list for occurrence of tun_t, same upper and lower stream,
* and same type (6to4 vs automatic vs configured)
* If none is found, insert this tun entry.
*/
static void
{
/*
*/
/*
* walk through list of tun_t looking for a match of
* ppa, same upper and lower stream and same tunnel type
* (automatic or configured).
* There shouldn't be all that many tunnels, so a sequential
* search of the bucket should be fine.
*/
TUN_UPPER_MASK)) == mask) &&
tun_type)) {
tun1dbg(("tun_add_byaddr: tun 0x%p Found ppa %d " \
(void *)tun_list));
tun1dbg(("tun_add_byaddr: Nothing to do."));
return;
}
}
/* didn't find one, throw it in the global list */
}
/*
* Search tuns_ppa_list for occurrence of tun_ppa, same lower stream,
* and same type (6to4 vs automatic vs configured)
* If none is found, insert this tun entry and create a new kstat for
* the entry.
* This is needed so that multiple tunnels with the same interface
* same kstats. (they share the same tun_stat and kstat)
* Don't need to hold tun_lock if we are coming is as qwriter()
*/
static tun_stats_t *
tun_add_stat(queue_t *q)
{
/*
*/
/*
* walk through list of tun_stats looking for a match of
* ppa, same lower stream and same tunnel type (automatic
* or configured
* There shouldn't be all that many tunnels, so a sequential
* search should be fine
* XXX - this may change if tunnels get ever get created on the fly
*/
tun1dbg(("tun_add_stat: tun 0x%p Found ppa %d " \
(void *)tun_list));
/*
* add this tunnel instance to head of list
* of tunnels referencing this kstat structure
*/
/*
* Check for IPsec tunnel policy pointer, if it hasn't
* been set already. If we call get_tunnel_policy()
* and return NULL, there's none configured.
*/
atp->tun_netstack);
}
return (tun_list);
}
}
/* didn't find one, allocate a new one */
NULL);
(void *)tun_stat));
} else {
}
return (tun_stat);
}
/*
* remove tun from tuns_byaddr_list
* called either holding tun_lock or in perimeter
*/
static void
{
/*
* remove tunnel instance from list of tun_t
*/
}
}
/*
* remove tun from tuns_ppa_list
* called either holding tun_lock or in perimeter
*/
static void
{
return;
/*
* If this is the last instance, delete the tun_stat AND unlink the
* ipsec_tun_pol_t from the AVL tree.
*/
tun1dbg(("tun_rem_ppa_list: tun 0x%p Last ref ppa %d tun_stat" \
(void *)tun_stat));
break;
}
}
return;
}
tun1dbg(("tun_rem_ppa_list: tun 0x%p Removing ref ppa %d tun_stat " \
/*
* remove tunnel instance from list of tunnels referencing
* this kstat. List should be short, so we just search
* sequentially
*/
break;
}
}
}
/*
* handle all non-unitdata DLPI requests from above
* called as qwriter()
*/
static void
{
t_uscalar_t dl_errno = 0;
switch (prim) {
case DL_INFO_REQ: {
tun1dbg(("tun_wput_dlpi_other: got DL_INFO_REQ\n"));
return;
}
/* send DL_INFO_ACK back up */
/* dl_mac_type is set to DL_IPV4 by default. */
/*
* We set the address length to non-zero so that
* automatic tunnels will not have multicast or
* point to point set.
* Someday IPv6 needs to support multicast over automatic
* tunnels
* 6to4 tunnels should behave the same as automatic tunnels
*/
/*
* set length to size of ip address so that
* ip_newroute will generate dl_unitdata_req for
* us with gateway or dest filed in. (i.e.
* might as well have ip do something useful)
*/
} else {
dinfo->dl_addr_length = 0;
}
return;
}
case DL_ATTACH_REQ: {
tun1dbg(("tun_wput_dlpi_other: got DL_ATTACH_REQ\n"));
return;
}
tun0dbg(("tun_wput_dlpi_other: "
"DL_ATTACH_REQ state not DL_UNATTACHED (0x%x)\n",
break;
}
/*
* get (possibly shared) kstat structure
*/
if (tun_add_stat(q) == NULL) {
break;
}
return;
}
case DL_DETACH_REQ:
tun1dbg(("tun_wput_dlpi_other: got DL_DETACH_REQ\n"));
return;
}
tun0dbg(("tun_wput_dlpi_other: " \
"DL_DETACH_REQ state not DL_UNBOUND (0x%x)\n",
break;
}
/*
* don't need to hold tun_lock
* since this is really a single thread operation
* for this instance
*/
tun1dbg(("tun_wput_dlpi_other: deleting kstat"));
}
return;
case DL_BIND_REQ: {
t_uscalar_t dl_sap = 0;
tun1dbg(("tun_wput_dlpi_other: got DL_BIND_REQ\n"));
tun0dbg(("tun_wput_dlpi_other: " \
"DL_BIND_REQ state not DL_UNBOUND (0x%x)\n",
break;
}
tun0dbg(("tun_wput_dlpi_other: " \
"DL_BIND_REQ upper TUN_U_V6 (0x%x)\n",
break;
}
tun0dbg(("tun_wput_dlpi_other: " \
"DL_BIND_REQ for IPv4 atun (0x%x)\n",
break;
}
tun0dbg(("tun_wput_dlpi_other: " \
"DL_BIND_REQ for 6to4 tunnel (0x%x)\n",
break;
}
} else {
/* Adjust headers. */
if (atp->tun_encap_lim >= 0) {
atp->tun_telopt =
} else {
}
}
} else if (dl_sap == IP6_DL_SAP) {
tun0dbg(("tun_wput_dlpi_other: "
"DL_BIND_REQ upper TUN_U_V4 (0x%x)\n",
break;
}
} else {
if (atp->tun_encap_lim >= 0) {
atp->tun_telopt =
} else {
}
}
} else {
break;
}
/*
* Send DL_BIND_ACK, which is the same size as the
* request, so we can re-use the mblk.
*/
return;
}
case DL_UNBIND_REQ:
tun1dbg(("tun_wput_dlpi_other: got DL_UNBIND_REQ\n"));
return;
}
tun0dbg(("tun_wput_dlpi_other: " \
"DL_UNBIND_REQ state not DL_IDLE (0x%x)\n",
break;
}
/* Send a DL_OK_ACK. */
return;
case DL_PHYS_ADDR_REQ: {
tun1dbg(("tun_wput_dlpi_other: got DL_PHYS_ADDR_REQ\n"));
return;
}
/*
* dl_addr_length must match info ack
*/
tun0dbg(("tun_wput_dlpi_other: " \
"DL_PHYS_ADDR_REQ for IPv4 atun\n"));
break;
} else {
}
tun0dbg(("tun_wput_dlpi_other: " \
"DL_PHYS_ADDR_REQ for 6to4 tunnel\n"));
break;
} else {
}
} else {
dpa->dl_addr_length = 0;
}
dpa->dl_addr_offset = 0;
return;
}
case DL_SUBS_BIND_REQ:
case DL_ENABMULTI_REQ:
case DL_DISABMULTI_REQ:
case DL_PROMISCON_REQ:
case DL_PROMISCOFF_REQ:
case DL_AGGR_REQ:
case DL_UNAGGR_REQ:
case DL_UDQOS_REQ:
case DL_CONNECT_REQ:
case DL_TOKEN_REQ:
case DL_DISCONNECT_REQ:
case DL_RESET_REQ:
case DL_DATA_ACK_REQ:
case DL_REPLY_REQ:
case DL_REPLY_UPDATE_REQ:
case DL_XID_REQ:
case DL_TEST_REQ:
case DL_SET_PHYS_ADDR_REQ:
case DL_GET_STATISTICS_REQ:
case DL_CAPABILITY_REQ:
case DL_CONTROL_REQ:
/* unsupported command */
break;
default:
/* unknown command */
tun0dbg(("tun_wput_dlpi_other: unknown DLPI message type: " \
"%d\n", prim));
dl_err = DL_BADPRIM;
}
}
/*
* handle all DLPI requests from above
*/
static int
{
int error = 0;
switch (prim) {
case DL_UNITDATA_REQ:
break;
}
if (!canputnext(q)) {
return (ENOMEM); /* to get service proc to stop */
}
/* we don't use any of the data in the DLPI header */
break;
}
case TUN_U_V4:
tun_wdata_v4(q, mp1);
break;
case TUN_U_V6:
tun_wdata_v6(q, mp1);
break;
default:
break;
}
break;
case DL_NOTIFY_REQ: {
break;
}
tun_sendsdusize(q);
break;
}
default:
break;
}
return (error);
}
/*
* set the tunnel parameters
* called as qwriter
*/
static void
{
int uerr = 0;
/* don't allow changes after dl_bind_req */
goto nak;
}
goto nak;
}
goto nak;
}
(size != sizeof (struct old_iftun_req))) {
goto nak;
}
goto nak;
}
/*
* Check version number for parsing the security settings.
*/
goto nak;
}
/*
* already.
*/
} else {
}
}
(TUN_AUTOMATIC | TUN_U_V4)) ||
goto nak;
}
case AF_INET:
goto nak;
}
goto nak;
}
break;
case AF_INET6:
goto nak;
}
goto nak;
}
break;
default:
uerr = EAFNOSUPPORT;
goto nak;
}
/*
* If I reach here, then I didn't bail, the src address
* was good.
*/
}
goto nak;
}
goto nak;
}
goto nak;
}
/* Remove from previous hash bucket */
goto nak;
}
goto nak;
}
/* Remove from previous hash bucket */
} else {
uerr = EAFNOSUPPORT;
goto nak;
}
/*
* If I reach here, then I didn't bail, the dst address
* was good.
*/
/* tun_faddr changed, move to proper hash bucket */
}
/* Check bounds. */
goto nak;
}
/* XXX do we really need this flag */
} else {
}
}
/* Bounds checking. */
goto nak;
}
if (ta->ifta_encap_lim >= 0) {
} else {
case TUN_U_V4:
break;
case TUN_U_V6:
break;
default:
/* This shouldn't happen! */
break;
}
}
}
/*
* If we passed in IFTUN_COMPLEX_SECURITY, do not do anything. This
* allows us to let dumb ifconfig(1m)-like apps reflect what they see
* without a penalty.
*/
/* Can't set security properties for automatic tunnels. */
goto nak;
}
/*
* The version number checked out, so just cast
* ifta_secinfo to an ipsr.
*/
if (ipsec_loaded(ipss)) {
} else {
if (ipsec_failed(ipss)) {
goto nak;
}
/* Otherwise, try again later and load IPsec. */
return;
}
if (uerr != 0)
goto nak;
}
/*
* _AND_ tsrc is turned on _AND_ the tunnel either has tdst turned on
* or is an automatic tunnel.
*/
uerr = tun_send_bind_req(q);
if (uerr == 0) {
/* qreply() done by T_BIND_ACK processing */
return;
} else {
goto nak;
}
}
return;
nak:
}
static boolean_t
{
return (B_FALSE);
return (rc);
}
/*
* Processes SIOCs to setup a tunnel and IOCs to configure tunnel module.
* M_IOCDATA->M_COPY->DATA or M_IOCTL->DATA
*/
static int
{
int reterr = 0;
int uerr = 0;
char buf[INET6_ADDRSTRLEN];
case OSIOCSTUNPARAM:
case SIOCSTUNPARAM:
return (0);
case OSIOCGTUNPARAM:
case SIOCGTUNPARAM:
goto nak;
}
goto nak;
}
(size != sizeof (struct old_iftun_req))) {
goto nak;
}
/*
* don't need to hold any locks. Can only be
* changed by qwriter
*/
ta->ifta_flags = 0;
/*
* Unlike tun_sparam(), the version number for security
* parameters is ignored, since we're filling it in!
*/
/* in case we are pushed under something unsupported */
case TUN_U_V4:
break;
case TUN_U_V6:
break;
default:
ta->ifta_upper = 0;
break;
}
/*
* Copy in security information.
*
* If we revise IFTUN_VERSION, this will become revision-
* dependent.
*/
(atp->tun_policy_index >=
/*
* Convert 0.0.0.0/0, 0::0/0 tree entry to
* ipsec_req_t.
*/
/* Reality check for empty polhead. */
if (ipsr->ipsr_ah_req != 0 ||
ipsr->ipsr_esp_req != 0)
} else {
ta->ifta_flags |=
}
}
/* Copy in hop limit. */
}
/* Copy in encapsulation limit. */
}
}
/* lower must be IPv4 or IPv6, otherwise open fails */
} else {
}
} else {
}
} else {
} else {
}
} else {
}
}
break;
case SIOCS6TO4TUNRRADDR: {
/* check to make sure this is not a TRANSPARENT ioctl */
goto nak;
}
/* skip over iocblk to M_DATA */
goto nak;
}
goto nak;
}
/*
* Value read MUST equal either:
* 1) a valid unicast IPv4 Address
* 2) INADDR_ANY
*
* (1) enables 6to4 Relay Router communication support on
* this system and denotes the IPv4 destination address used
* for sending to 6to4 Relay Routers.
* (2) disables 6to4 Relay Router communication support on
* this system.
*
* Any other value results in a NAK.
*/
tun1dbg(("tun_ioctl: 6to4 Relay Router = %s\n",
sizeof (buf))));
} else {
tun1dbg(("tun_ioctl: Invalid 6to4 Relay Router " \
"address (%s)\n",
sizeof (buf))));
goto nak;
}
break;
}
case SIOCG6TO4TUNRRADDR:
/* skip over iocblk to M_DATA */
goto nak;
}
goto nak;
}
break;
case DL_IOC_HDR_INFO:
if (uerr != 0)
goto nak;
break;
case SIOCSLIFNAME:
/*
* Intercept SIOCSLIFNAME and attach the name to my
* tunnel_instance. For extra paranoia, if my name is not ""
* (as it would be at tun_t initialization), don't change
* anything.
*
* For now, this is the only way to tie tunnel names (as
* used in IPsec Tunnel Policy (ITP) instances) to actual
* tunnel instances. In practice, SIOCSLIFNAME is only
* used by ifconfig(1m) to change the ill name to something
* ifconfig can handle.
*/
ns);
/*
* It really doesn't matter if we return
* NULL or not. If we get the itp pointer,
* we're in good shape.
*/
} else {
tun0dbg(("SIOCSLIFNAME: new is %s, old is %s"
" - not changing\n",
}
}
break;
default:
/*
* We are module that thinks it's a driver so nak anything we
* don't understand
*/
goto nak;
}
return (reterr);
nak:
return (reterr);
}
/*
* mp contains the M_IOCTL DL_IOC_HDR_INFO message
* allocate mblk for fast path.
* XXX - fix IP so that db_base and rptr can be different
*/
static int
{
int error;
int hdrlen;
return (EINVAL);
if (error != 0)
return (error);
return (EINVAL);
case TUN_L_V4:
return (ENOMEM);
}
break;
case TUN_L_V6:
if (atp->tun_encap_lim >= 0) {
}
return (ENOMEM);
}
break;
default:
return (EPFNOSUPPORT);
}
return (0);
}
/*
* write side service procedure
*/
void
{
/* out of memory or canputnext failed */
break;
}
/*
* If we called qwriter, then the only way we
* can tell if we ran out of memory is to check if
* any events have been scheduled
*/
break;
}
}
}
/* write side put procedure */
void
{
/* note: q_first is 'protected' by perimeter */
} else {
}
}
/*
* called from write side put or service procedure to process
* messages
*/
static int
{
int error = 0;
case M_DATA:
break;
case M_PROTO:
case M_PCPROTO:
/* its a DLPI message */
break;
case M_IOCDATA:
case M_IOCTL:
/* Data to be copied out arrives from ip as M_IOCDATA */
break;
/* we are a module pretending to be a driver.. turn around flush */
case M_FLUSH:
}
break;
/*
* we are a module pretending to be a driver.. so just free message
* we don't understand
*/
default: {
char buf[TUN_WHO_BUF];
tun0dbg(("tun_wproc: %s got unknown mblk type %d\n",
break;
}
}
return (error);
}
/*
* handle fast path M_DATA message
*/
static int
{
int error = 0;
sizeof (ip6_t)) :
sizeof (ipha_t)) : 1));
if (!canputnext(q)) {
return (ENOMEM); /* get service procedure to stop */
}
int iph_hdr_length;
/*
* get rid of fastpath header. let tun_wdata*
* fill in real thing
*/
atp->tun_extra_offset));
tun0dbg(("tun_wproc_mdata: message too " \
"short for IPv6 header\n"));
return (0);
}
}
tun_wdata_v6(q, mp);
return (error);
}
case TUN_U_V4:
break;
case TUN_U_V6:
break;
default:
}
return (error);
}
/*
* Because a TUNSPARAM ioctl()'s requirement to only set IPsec policy for a
* given upper instance (IPv4-over-IP* or IPv6-over-IP*), have a special
* AF-specific flusher. This way, setting one upper instance doesn't sabotage
* the other. Don't bother with the hash-chained policy heads - they won't be
* filled in in TUNSPARAM cases.
*/
static void
{
int dir;
}
}
}
/*
* Set and insert the actual simple policies.
*/
static boolean_t
{
if (ulp_vector & TUN_U_V4) {
/* v4 inbound */
return (B_FALSE);
/* v4 outbound */
return (B_FALSE);
}
if (ulp_vector & TUN_U_V6) {
/* v6 inbound */
return (B_FALSE);
/* v6 outbound */
return (B_FALSE);
}
return (B_TRUE);
}
/*
* For the old-fashioned tunnel-ioctl method of setting tunnel security
* properties. In the new world, set this to be a low-priority 0.0.0.0/0
* match.
*/
static int
{
int rc = 0;
("tun_set_sec_simple: adjusting tunnel security the old way."));
/* Can't specify self-encap on a tunnel!!! */
return (EINVAL);
/*
* If it's a "clear-all" entry, unset the security flags and
* resume normal cleartext (or inherit-from-global) policy.
*/
if (clear_all) {
atp->tun_policy_index = 0;
goto bail; /* No need to allocate! */
}
/* NOTE: "rc" set by create_tunnel_policy(). */
goto bail;
}
/* Allocate the actvec now, before holding itp or polhead locks. */
goto bail;
}
/*
* stuff for spdsock operations.
*
* Mutex because we need to write to the polhead AND flags atomically.
* Other threads will acquire the polhead lock as a reader if the
* (unprotected) flag is set.
*/
/*
* Oops, we lost a race. Let's get out of here.
*/
goto mutex_bail;
}
if (old_policy) {
/*
* We have to be more subtle here than we would
* in the spdosock code-paths, due to backward compatibility.
*/
if (rc != 0) {
/* inactive has already been cleared. */
goto mutex_bail;
}
} else {
/* Else assume itp->itp_policy is already flushed. */
}
if (clear_all) {
/* We've already cleared out the polhead. We are now done. */
goto recover_bail;
}
/*
* Adjust MTU and make sure the DL side knows what's up.
*/
/*
* <sigh> There has to be a better way, but for now, send an
* IRE_DB_REQ again. We will resynch from scratch, but have
* the tun_ipsec_overhead taken into account.
*/
/* Copy ipsec_req_t for subsequent SIOGTUNPARAM ops. */
} else {
}
} else {
}
}
if (old_policy) {
/* Recover policy in in active polhead. */
}
/* Clear policy in inactive polhead. */
bail:
return (rc);
}
/*
* Send an IRE_DB_REQ_TYPE to the lower module to obtain an IRE for the
* tunnel destination. If the tunnel has no destination, then request an
* IRE for the source instead.
*/
static void
{
char addrstr[INET6_ADDRSTRLEN];
NULL) {
tun0dbg(("tun_send_ire_req: couldn't allocate mblk\n"));
return;
}
/*
* For tunnels without destinations, we request the source
* ire so that we can account for IPsec policy in our MTU
* calculation.
*/
} else {
}
tun1dbg(("tun_send_ire_req: requesting ire for %s",
INET6_ADDRSTRLEN))));
}
/*
* Given the path MTU to the tunnel destination, calculate tunnel's link
* mtu. For configured tunnels, we update the tunnel's link MTU and notify
* the upper instance of IP of the change so that the IP interface's MTU
* can be updated. If the tunnel is a 6to4 or automatic tunnel, just
* return the effective MTU of the tunnel without updating it. We don't
* update the link MTU of 6to4 or automatic tunnels because they tunnel to
* multiple destinations all with potentially differing path MTU's.
*/
static uint32_t
{
/*
* If the pmtu provided came from an ICMP error being passed up
* from below, then the pmtu argument has already been adjusted
* by the IPsec overhead.
*/
if (newmtu < IP_MIN_MTU)
newmtu = IP_MIN_MTU;
} else {
if (atp->tun_encap_lim > 0)
if (newmtu < IPV6_MIN_MTU)
}
}
if (sendsdusize)
tun_sendsdusize(q);
}
return (newmtu);
}
/*
* Process TPI messages responses comming up the read side
*/
/* ARGSUSED */
int
{
switch (prim) {
case T_BIND_ACK:
tun1dbg(("tun_rput_tpi: got a T_BIND_ACK\n"));
/*
* XXX This first assert may fail if this path gets re-
* executed because of tun_recover() being invoked.
*/
/*
* If we have an IRE in mp->b_cont, use it to help compute
* atp->tun_extra_offset, tun_ipsec_overhead, and the link
* MTU of configured tunnels.
*/
/*
* Take advice from lower-layer if it is bigger than
* what we have cached now. We do manage per-tunnel
* policy, but there may be global overhead to account
* for.
*/
(void) tun_update_link_mtu(q,
}
}
/*
* Automatic and 6to4 tunnels only require source to be set
* Configured tunnels require both
*/
}
/*
* Ack the ioctl
*/
break;
case T_ERROR_ACK: {
switch (terr->ERROR_prim) {
case T_BIND_REQ: {
}
}
}
default:
break;
case TSYSERR:
break;
case TBADADDR:
break;
}
return (0);
}
default: {
char buf[TUN_WHO_BUF];
tun0dbg(("tun_rput_tpi: %s got an unkown TPI Error " \
"message: %d\n",
break;
}
}
break;
}
case T_OK_ACK:
break;
/* act like a stream head and eat all up comming tpi messages */
default: {
char buf[TUN_WHO_BUF];
tun0dbg(("tun_rput_tpi: %s got an unkown TPI message: %d\n",
break;
}
}
return (0);
}
/*
* handle tunnel over IPv6
*/
static int
{
char buf[TUN_WHO_BUF];
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
int pullup_len;
/* need at least an IPv6 header. */
/* Handle ip6i_t case. */
/*
* Assume sizeof (ip6i_t) == sizeof(ip6_t), can't
* use ASSERT because of lint warnings.
*/
}
}
/*
* If the Next Header field is not IPPROTO_ENCAP or IPPROTO_IPV6, there
* are IPv6 options present that we need to parse in order to figure
* out whether we have an encapsulated IPv4 or IPv6 packet here.
*/
/* Tunnel packet with options!!! */
} else {
/* Otherwise, pretend it's IP + ESP. */
}
} else {
}
/*
* NOTE: The "+ 4" is for the upper-layer protocol information
* (ports) so we can enforce policy.
*/
goto drop;
}
}
/* Shave off the outer header(s). */
if (inner_v4) {
/* IPv4 in IPv6 */
atp->tun_netstack)) {
goto drop;
}
/* mp has changed, reset appropriate pointers */
/* Outer hdrlen is already shaved off */
}
/*
* Remember - ipsec_tun_inbound() may return a whole chain
* of packets if there was per-port policy on the ITP and
* we got a fragmented packet.
*/
} else {
}
} else {
/* IPv6 in IPv6 */
goto drop;
}
/* mp has changed, reset appropriate pointers */
/* v6src should still be a valid and relevant ptr */
}
/*
* Remember - ipsec_tun_inbound() may return a whole chain
* of packets if there was per-port policy on the ITP and
* we got a fragmented packet.
*/
} else {
}
/*
* Configured Tunnel packet source should match our
* destination
* Lower IP should ensure that this is true
*/
tun0dbg(("tun_rdata_v6: %s src (%s) != tun_faddr " \
sizeof (buf1)),
sizeof (buf2))));
goto drop;
}
}
return (0);
drop:
return (0);
}
/*
* Handle tunnels over IPv4
* XXX - we don't do any locking here. The worst that
* can happen is we drop the packet or don't record stats quite right
* what's the worst that can happen if the header stuff changes?
*/
static int
{
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
char buf[TUN_WHO_BUF];
int pullup_len;
/* need at least an IP header */
/* check IP version number */
/*
* NOTE: The "+ 4" is for the upper-layer protocol headers
* so we can enforce policy.
*/
goto drop;
}
}
/* Shave off the IPv4 header. */
if (inner_v4) {
/* IPv4 in IPv4 */
/* NOTE: ipsec_tun_inbound() always frees ipsec_mp. */
goto drop;
}
/* mp has changed, reset appropriate pointers */
/* Outer hdrlen is already shaved off */
}
/*
* Remember - ipsec_tun_inbound() may return a whole chain
* of packets if there was per-port policy on the ITP and
* we got a fragmented packet.
*/
} else {
}
} else {
/* IPv6 in IPv4 */
/* NOTE: ipsec_tun_inbound() always frees ipsec_mp. */
goto drop;
}
/* mp has changed, reset appropriate pointers */
/*
* v6src and v4dst should still be
* valid and relevant pointers
*/
}
/*
* Remember - ipsec_tun_inbound() may return a whole chain
* of packets if there was per-port policy on the ITP and
* we got a fragmented packet.
*/
} else {
}
/* Is this an automatic tunnel ? */
/*
* make sure IPv4 destination makes sense
*/
tun0dbg(("tun_rdata_v4: %s tun: invalid IPv4" \
" dest (%s)\n",
}
goto drop;
}
/*
* send packet up as DL_UNITDATA_IND so that it won't
* be forwarded
*/
tun0dbg(("tun_rdata_v4: allocb failed\n"));
goto drop;
}
/*
* create dl_unitdata_ind with group address set so
* we don't forward
*/
sizeof (dl_unitdata_ind_t);
dludindp->dl_dest_addr_length = 0;
dludindp->dl_dest_addr_offset = 0;
dludindp->dl_src_addr_length = 0;
dludindp->dl_src_addr_offset = 0;
/* Is this a 6to4 tunnel ? */
/*
* Make sure IPv6 destination is a 6to4 address.
* ip_rput_data_v6 will ensure that 6to4 prefix
* of IPv6 destination and the prefix assigned to
* the interface, on which this packet was received,
* match.
*/
tun0dbg(("tun_rdata_v4: %s tun: invalid " \
sizeof (buf1))));
goto drop;
}
/*
* make sure IPv4 destination makes sense
*/
tun0dbg(("tun_rdata_v4: %s tun: invalid " \
sizeof (buf1))));
}
goto drop;
}
/*
* 6to4 router security considerations state that
* the V4ADDR portion of the IPv6 destination
* MUST be equal to the IPv4 destination.
*/
tun0dbg(("tun_rdata_v4: %s tun: V4ADDR " \
"portion of 6to4 IPv6 dest (%s) does not" \
}
goto drop;
}
/*
* check to see if the source was another 6to4 router
*/
/*
* 6to4 router security considerations state
* that the V4ADDR portion of the IPv6 source
* MUST be equal to the IPv4 source, when
* the source machine is another 6to4 router
*/
tun0dbg(("tun_rdata_v4: %s tun: " \
"V4ADDR portion of 6to4 IPv6 src" \
" (%s) does not equal IPv4 src " \
"(%s)\n",
}
goto drop;
}
/*
* IPv6 source is, possibly, a "Native"
* (ie non-6to4) IPv6 host. IPv4 source is,
* possibly, a 6to4 Relay Router.
*/
} else {
/*
* Check if tun module support 6to4 Relay
* Router is disabled or enabled.
* tuns_relay_rtr_addr_v4 will equal INADDR_ANY
* if support is disabled. Otherwise, it will
* equal a valid, routable, IPv4 address;
* denoting that the packet will be accepted.
*
* There is no standard trust mechanism for
* 6to4 Relay Routers, thus communication
* support is disabled by default for
* security reasons.
*/
if (tuns->tuns_relay_rtr_addr_v4 ==
INADDR_ANY) {
tun1dbg(("tun_rdata_v4: "
"%s tuns_relay_rtr_addr_v4 = %s, "
"dropping packet from IPv4 src "
sizeof (buf2))));
}
goto drop;
}
}
/*
* this might happen if we are in the middle of
* re-binding
*/
} else if (!IN6_ARE_ADDR_EQUAL(&v4mapped_src,
/*
* Configured Tunnel packet source should match our
* destination
* Lower IP should ensure that this is true
*/
tun0dbg(("tun_rdata_v4: %s src (%s) != tun_faddr " \
/* XXX - should this be per-frag? */
goto drop;
}
}
return (0);
drop:
return (0);
}
static void
{
int hdr_length;
case TUN_U_V6:
break;
case TUN_U_V4:
break;
default:
ASSERT(0);
}
}
/*
* icmp from lower IPv4
* Process ICMP messages from IPv4. Pass them to the appropriate
* lower processing function.
*/
static void
{
case TUN_U_V6:
break;
case TUN_U_V4:
break;
default:
ASSERT(0);
}
}
/*
* Process ICMP message from IPv4 encapsulating an IPv4 packet.
* If this message contains path mtu information, cut out the
* encapsulation from the icmp data. If there is still useful
* information in the icmp data pass it upstream (packaged correctly for
* the upper layer IP)
*/
static void
{
int outer_hlen;
int inner_hlen;
int hlen;
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
/*
* The packet looks like this :
*
* [IPv4(0)][ICMPv4][IPv4(1)][IPv4(2)][ULP]
*
* We want most of this in one piece. But if the ULP is ICMP, we
* need to see whether it is an ICMP error or not. We should not
* send icmp errors in response to icmp errors. "outer_ipha" points
* to IP header (1), "inner_ipha" points to IP header (2). Inbound
* Fortunately, ipsec_tun_inbound() can determine if this is an ICMP
* message or not.
*
* The caller already pulled up the entire message, or should have!
*/
return;
}
return;
}
switch (inner_icmph->icmph_type) {
case ICMP_DEST_UNREACHABLE:
case ICMP_SOURCE_QUENCH:
case ICMP_TIME_EXCEEDED:
case ICMP_PARAM_PROBLEM:
case ICMP_REDIRECT:
return;
default :
break;
}
}
/*
* NOTE: icmp_inbound() in IP already checked global policy on the
* outermost header. If we got here, IP thought it was okay for
* us to receive it. We now have to use inner policy to see if
* we want to percolate it up (like conn_t's are checked).
*
* Use -outer_hlen to indicate this is an ICMP packet. And
* ipsec_tun_inbound() always frees ipsec_mp.
*/
/* Callee did all of the freeing */
return;
}
/* New packet will contain all of old packet */
switch (type) {
case ICMP_DEST_UNREACHABLE:
switch (code) {
case ICMP_FRAGMENTATION_NEEDED: {
tun0dbg(("icmp_ricmp_err_v4_v4: invalid " \
"icmp mtu\n"));
return;
}
}
return;
}
/*
* XXX may need way to throttle messages
* XXX should we do this for automatic or
* just configured tunnels ?
*/
"%s.%s%d: Protocol unreachable. "
"Misconfigured tunnel? source %s"
" destination %s\n",
/* FALLTHRU */
case ICMP_NET_UNREACHABLE:
case ICMP_HOST_UNREACHABLE:
case ICMP_DEST_NET_UNKNOWN:
case ICMP_DEST_HOST_UNKNOWN:
case ICMP_SRC_HOST_ISOLATED:
case ICMP_SOURCE_ROUTE_FAILED:
/* XXX HOST or NET unreachable? */
break;
break;
default:
return;
}
break;
case ICMP_TIME_EXCEEDED:
break;
case ICMP_PARAM_PROBLEM:
tun0dbg(("icmp_ricmp_err_v4_v4: ICMP_PARAM_PROBLEM " \
"too short\n"));
return;
}
sizeof (icmph_t));
break;
default:
return;
}
}
}
/*
* Process ICMP message from IPv6 encapsulating an IPv4 packet
* If this message contains path mtu information, cut out the
* encapsulation from the icmp data. If there is still useful
* information in the icmp data pass it upstream (packaged correctly for
* the upper layer IP)
*/
static void
{
int outer_hlen;
/*
* The packet looks like this:
*
* [IPv6(0)][ICMPv6][IPv6(1)][IPv4][ULP]
*
* "ip6" points to the IPv6 header labelled (1).
*/
/*
* NOTE: icmp_inbound() in IP already checked global policy on the
* outermost header. If we got here, IP thought it was okay for
* us to receive it. We now have to use inner policy to see if
* we want to percolate it up (like conn_t's are checked).
*
* Use -outer_hlen to indicate this is an ICMP packet. And
* ipsec_tun_inbound() always frees ipsec_mp.
*/
/* Callee did all of the freeing */
return;
/* new packet will contain all of old packet */
/*
* Fill in "icmp" data structure for passing to tun_icmp_message_v4().
*/
switch (type) {
case ICMP6_PARAM_PROB:
/*
* If the ICMPv6 error points to a valid Tunnel
* Encapsulation Limit option and the limit value is
* 0, then fall through and send a host unreachable
* message. Otherwise, break.
*/
} else {
break; /* out of switch */
}
/*
* We have a potential match. Parse the header into
* options.
*/
offset += 2;
hdrp += 2;
/*
* hdrp[2] is the tunnel encapsulation limit
* value.
*/
(hdrp[2] == 0)) {
/* Found it. */
}
}
}
return;
}
/*FALLTHRU*/
case ICMP6_TIME_EXCEEDED:
case ICMP6_DST_UNREACH:
break;
case ICMP6_PACKET_TOO_BIG: {
/*
* RFC 2473 says we should only forward this on to the IPv4
* original source if the IPv4 header has the DF bit set.
*/
/*
* NOTE - htons() because ICMP (for IPv4) uses a
* uint16_t here.
*/
icmp.icmph_du_zero = 0;
}
break;
}
default:
return;
}
}
}
/*
* Process ICMP message from IPv6 encapsulating an IPv6 packet
* If this message contains path mtu information, cut out the
* encapsulation from the icmp data. If there is still useful
* information in the icmp data pass it upstream (packaged correctly for
* the upper layer IP). Otherwise, drop the message.
*/
static void
{
int outer_hlen;
/*
* The packet looks like this :
*
* [IPv6(0)][ICMPv4][IPv6(1)][IPv6(2)][ULP]
*
* "ip6" points to the IPv6 header labelled (1), and inner_ip6 points
* to IPv6 header (2).
*/
/*
* NOTE: icmp_inbound() in IP already checked global policy on the
* outermost header. If we got here, IP thought it was okay for
* us to receive it. We now have to use inner policy to see if
* we want to percolate it up (like conn_t's are checked).
*
* Use -outer_hlen to indicate this is an ICMP packet. And
* ipsec_tun_inbound() always frees ipsec_mp.
*/
/* Callee did all of the freeing */
return;
/* new packet will contain all of old packet */
/*
* Fill in "icmp" data structure for passing to tun_icmp_message_v6().
*/
switch (type) {
case ICMP6_PARAM_PROB:
/*
* If the ICMPv6 error points to a valid Tunnel
* Encapsulation Limit option and the limit value is
* 0, then fall through and send a host unreachable
* message. Otherwise, break.
*/
} else {
break; /* out of switch */
}
/*
* We have a potential match. Parse the header into
* options.
*/
offset += 2;
hdrp += 2;
/*
* hdrp[2] is the tunnel encapsulation limit
* value.
*/
(hdrp[2] == 0)) {
/* Found it. */
}
}
}
return; /* case */
}
/*FALLTHRU*/
case ICMP6_TIME_EXCEEDED:
case ICMP6_DST_UNREACH:
break;
case ICMP6_PACKET_TOO_BIG: {
/*
* RFC 2473 says we should forward this on to the IPv6 original
* source only if the original packet size is larger than the
* IPv6 minimum link MTU.
*/
icmp.icmp6_code = 0;
}
break;
}
default:
return;
}
B_FALSE) {
}
}
/*
* Process ICMP message from IPv4 encapsulating an IPv6 packet
* If this message contains path mtu information, cut out the
* encapsulation from the icmp data. If there is still useful
* information in the icmp data pass it upstream (packaged correctly for
* the upper layer IP)
*/
static void
{
int outer_hlen;
int hlen;
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
/*
* The case here is pretty easy when compared to IPv4 in IPv4
* encapsulation.
*
* The packet looks like this :
*
* [IPv4(0)][ICMPv4][IPv4(1)][IPv6][ULP]
*
* We want most of this in one piece. But if the ULP is ICMPv6, we
* need to see whether it is an ICMPv6 error or not. We should not
* send icmp errors in response to icmp errors. "outer_ipha" points to
* IP header (1). "ip6h" is obvious. To see whether ULP is ICMPv6 or
* not, we need to call ip_hdr_length_nexthdr_v6 function which
* expects everything to be pulled up. Fortunately, the caller
* should've done all of the pulling up.
*/
/*
* icmp_inbound has pulled up the message until the
* outer IP header excluding any IP options.
*/
return;
}
/*
* Do not send ICMPv6 error in reply to ICMPv6 error.
*/
return;
}
if (*nexthdrp == IPPROTO_ICMPV6) {
return;
}
}
/*
* NOTE: icmp_inbound() in IP already checked global policy on the
* outermost header. If we got here, IP thought it was okay for
* us to receive it. We now have to use inner policy to see if
* we want to percolate it up (like conn_t's are checked).
*
* Use -outer_hlen to indicate this is an ICMP packet. And
* ipsec_tun_inbound() always frees ipsec_mp.
*/
/* Callee did all of the freeing */
return;
/* New packet will contain all of old packet */
switch (type) {
case ICMP_DEST_UNREACHABLE:
switch (code) {
case ICMP_FRAGMENTATION_NEEDED: {
tun0dbg(("icmp_ricmp_err_v6_v4: invalid " \
"icmp mtu\n"));
return;
}
}
return;
}
case ICMP_PROTOCOL_UNREACHABLE: {
/*
* XXX may need way to throttle messages
* XXX should we do this for automatic or
* just configured tunnels ?
*/
"%s.%s%d: Protocol unreachable. "
"Misconfigured tunnel? source %s"
" destination %s\n",
icmp6.icmp6_data32[0] = 0;
break;
}
case ICMP_PORT_UNREACHABLE:
icmp6.icmp6_data32[0] = 0;
break;
case ICMP_NET_UNREACHABLE:
case ICMP_HOST_UNREACHABLE:
case ICMP_DEST_NET_UNKNOWN:
case ICMP_DEST_HOST_UNKNOWN:
case ICMP_SRC_HOST_ISOLATED:
icmp6.icmp6_data32[0] = 0;
break;
icmp6.icmp6_data32[0] = 0;
break;
case ICMP_SOURCE_ROUTE_FAILED:
icmp6.icmp6_data32[0] = 0;
break;
default:
return;
}
break;
case ICMP_TIME_EXCEEDED:
icmp6.icmp6_data32[0] = 0;
break;
case ICMP_PARAM_PROBLEM:
tun0dbg(("icmp_ricmp_err_v6_v4: ICMP_PARAM_PROBLEM " \
"too short\n"));
return;
}
break;
default:
return;
}
}
}
/*
* Rewhack the packet for the upper IP.
*/
static boolean_t
{
tun2dbg(("tun_icmp_too_big_v4: mtu %u src %08x dst %08x len %d\n",
ipha->ipha_length));
icmp.icmph_du_zero = 0;
}
/*
* Send an ICMP6_PACKET_TOO_BIG message
*/
static boolean_t
{
icmp6.icmp6_code = 0;
}
/*
* Send an icmp message up an IPv4 stream. Take the data in mp,
* and prepend a new set of IPv4 + ICMP set of headers. Use the ipha and
* icmp pointers to help construct the aforementioned new headers.
*/
static boolean_t
{
return (B_FALSE);
}
nipha->ipha_type_of_service = 0;
nipha->ipha_ident = 0;
nipha->ipha_hdr_checksum = 0;
}
nicmp->icmph_checksum = 0;
/* let ip know we are an icmp message */
return (B_TRUE);
}
/*
* Send an icmp message up an IPv6 stream.
*/
static boolean_t
{
return (B_FALSE);
}
/* copy of ipv6 header into icmp6 message */
/* add in the rest of the packet if any */
}
nicmp6->icmp6_cksum = 0;
/* let ip know we are an icmp message */
return (B_TRUE);
}
/*
* Read side service routine.
*/
void
{
break;
}
/*
* If we called qwriter, then the only way we
* can tell if we ran out of memory is to check if
* any events have been scheduled
*/
break;
}
}
}
/*
* Read side put procedure
*/
void
{
/* note: q_first is 'protected' by perimeter */
} else {
}
}
static int
{
char buf[TUN_WHO_BUF];
int error = 0;
/*
* If it's an IPSEC_IN w/o any security properties, start treating
* it like a cleartext packet.
*/
}
return (error); /* pre-set to 0 */
}
if (!canputnext(q)) {
tun1dbg(("tun_rdata: flow controlled\n"));
goto bail;
}
tun0dbg(("tun_rproc: %s no lower version\n",
goto bail;
}
bail:
if (error) {
/* only record non flow control problems */
tun0dbg(("tun_rproc: %s error encounterd %d\n",
}
}
return (error);
}
/*
* Process read side messages
*/
static int
{
int error = 0;
char buf[TUN_WHO_BUF];
ipsec_in_t *ii;
/* no lock needed, won't ever change */
case M_DATA:
break;
case M_PROTO:
case M_PCPROTO:
/* its a TPI message */
break;
case M_CTL:
/* its either an IPsec-protect packet... */
lvers);
break; /* Out of switch. */
} else {
/*
* ICMP message protected by IPsec.
* Split out IPSEC_IN and pass it up separately.
*/
}
} else {
}
/* ... or an ICMP error message from IP */
if (!canputnext(q)) {
break;
}
/* Pull everything up into mp. */
break;
}
} else {
}
break;
case M_FLUSH:
}
/* we're pretending to be a stream head */
} else {
}
break;
case IRE_DB_TYPE: {
ip1dbg(("tun_rproc: received IRE_DB_TYPE."));
tun1dbg(("tun_rproc: received IRE_DB_TYPE, "
/*
* Take advice from lower-layer if it is bigger than what we
* have cached now. We do manage per-tunnel policy, but
* there may be global overhead to account for.
*/
B_FALSE);
}
break;
}
default:
tun0dbg(("tun_rproc: %s got unknown mblk type %d\n",
break;
}
return (error);
}
/*
* Handle Upper IPv4
*/
static void
{
/*
* increment mib counters and pass message off to ip
* note: we must always increment packet counters, but
* only increment byte counter if we actually send packet
*/
} else {
}
return;
}
case TUN_L_V4:
/*
* Watch out! There is potential for an infinite loop.
* If IP sent a packet with destination address equal
* to the tunnel's destination address, we'll hit
* an infinite routing loop, where the packet will keep
* going through here.
*
* In the long term, perhaps IP should be somewhat
* intelligent about this. Until then, nip this in
* the bud.
*/
tun0dbg(("tun_wdata: inner dst == tunnel dst.\n"));
atp->tun_OutErrors++;
return;
}
/* room for IPv4 header? */
/* no */
BPRI_HI);
return;
}
} else {
/* yes */
}
/*
* copy template header into packet IPv4 header
*/
+ sizeof (ipha_t));
/*
* copy the tos from inner header. We mask off
* ECN bits (bits 6 and 7) because there is currently no
* tunnel-tunnel communication to determine if
* both sides support ECN, so we opt for the safe
* choice: don't copy the ECN bits when doing encapsulation.
*/
break;
case TUN_L_V6:
/* room for IPv6 header? */
if (encap_limit >= 0) {
}
/* no */
BPRI_HI);
return;
}
} else {
/* yes */
}
/*
* copy template header into packet IPv6 header
*/
sizeof (ip6_t));
break;
default:
/* LINTED */
ASSERT(0 && "not supported");
return;
}
/*
* Request the destination ire regularly in case Path MTU has
* increased.
*/
if (TUN_IRE_TOO_OLD(atp))
tun_send_ire_req(q);
return;
}
/*
* put M_DATA fastpath upper IPv4
* Assumes canput is possible
*/
static int
{
return (0); /* silently fail */
}
}
sizeof (ipha_t));
/*
* copy the tos from inner header. We mask off
* ECN bits (bits 6 and 7) because there is currently no
* tunnel-tunnel communication to determine if
* both sides support ECN, so we opt for the safe
* choice: don't copy the ECN bits when doing encapsulation.
*/
/*
* Infinite loop check. See the TUN_L_V4 case in
* tun_wdata_v4() for details.
*/
("tun_wputnext_v4: inner dst == tunnel dst.\n"));
atp->tun_OutErrors++;
return (EINVAL);
}
/* XXX The code should be more general */
}
return (0); /* silently fail */
}
}
} else {
/* XXX can't get here yet - force assert */
return (EINVAL);
}
/* XXX Do I hit this, given I have this check earlier? */
/*
* Watch out! There is potential for an infinite loop.
* If IP sent a packet with destination address equal
* to the tunnel's destination address, we'll hit
* an infinite routing loop, where the packet will keep
* going through here.
*
* In the long term, perhaps IP should be somewhat
* intelligent about this. Until then, nip this in
* the bud.
*/
tun0dbg(("tun_wputnext_v4: inner dst == tunnel dst.\n"));
atp->tun_OutErrors++;
return (EINVAL);
}
/*
* increment mib counters and pass message off to ip
* note: we must always increment packet counters, but
* only increment byte counter if we actually send packet
*/
} else {
}
return (EINVAL);
}
return (0);
/*
* Request the destination ire regularly in case Path MTU has
* increased.
*/
if (TUN_IRE_TOO_OLD(atp))
tun_send_ire_req(q);
return (0);
}
/*
* put M_DATA fastpath upper IPv6
* Assumes canput is possible
*/
static int
{
struct ip6_opt_tunnel *encap_opt;
int encap_limit = 0;
/*
* fastpath reserves a bit more then we can use.
* get rid of hardware bits.. ip below us will fill it in
*/
return (0); /* silently fail */
}
}
/*
* if we are less than the minimum IPv6 mtu size, then
* allow IPv4 to fragment the packet
*/
} else {
}
return (0); /* silently fail */
}
}
/*
* Watch out! There is potential for an infinite loop.
* If IP sent a packet with destination address equal
* to the tunnel's destination address, we'll hit
* an infinite routing loop, where the packet will keep
* going through here.
*
* In the long term, perhaps IP should be somewhat
* intelligent about this. Until then, nip this in
* the bud.
*/
("tun_wputnext_v6: inner dst == tunnel dst.\n"));
atp->tun_OutErrors++;
return (EINVAL);
}
if (encap_limit >= 0) {
encap_opt = (struct ip6_opt_tunnel *)
((char *)outer_ip6 +
sizeof (ip6_t) +
sizeof (struct ip6_dest));
}
} else {
/* mp already freed by tun_limit_value_v6 */
return (0); /* silently fail */
}
}
} else {
/* XXX can't get here yet - force assert */
return (EINVAL);
}
/*
* increment mib counters and pass message off to ip
* note: we must always increment packet counters, but
* only increment byte counter if we actually send packet
*/
} else {
}
return (EINVAL);
}
/*
* Request the destination ire regularly in case Path MTU has
* increased, but only for configured tunnels.
*/
tun_send_ire_req(q);
atp->tun_netstack);
return (0);
return (0);
}
/*
* Determine whether we need to add a Tunnel Encapsulation Limit option and
* what it's value should be. There are two reasons to add a TEL option:
* 1. The tunnel data structure specifies it by a greater-than-zero
* tun_encap_lim member.
* 2. The data being encapsulated is an IPv6 packet that contains a TEL
* option. RFC 2473 says if the value is 1, return an ICMP parameter
* problem error report, else decrement the value and use it for a TEL
* option to be inserted in the encapsulating IPv6 packet.
*
* Return values:
* B_TRUE: Has a limit, use the value in *limitp.
* B_FALSE: Problem with limit, i.e. it was zero.
*/
static boolean_t
{
int limit = 0;
int optlen;
/*
* If tunnel has a non-negative limit, use it, but allow it to be
* overridden by tunnel encapsulation limit option in original packet
* (mp).
*/
/* Check mp for tunnel encapsulation limit destination option. */
while (optlen > 0) {
/*
* XXX maybe we should send an ICMP parameter
* problem in this case instead.
*/
/*
* RFC 2473 says send an ICMP parameter problem
* if the limit is 0, send an ICMP parameter
* problem error and return B_FALSE.
*/
if (limit == 0) {
icmp6.icmp6_code = 0;
(void) tun_icmp_message_v6(q, ip6h,
return (B_FALSE);
}
--limit;
break;
}
}
}
return (B_TRUE);
}
/*
* Handle Upper IPv6 write side data
* Note: all lower tunnels must have a source
* This routine assumes that a canput has already been done on the
* stream.
*/
static void
{
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
char buf[TUN_WHO_BUF];
int encap_limit = 0;
struct ip6_opt_tunnel *encap_opt;
/*
* increment mib counters and pass message off to ip
* note: we must always increment packet counters, but
* only increment byte counter if we actually send packet
*/
} else {
}
goto drop;
}
/* check version */
case TUN_L_V4:
/* room for IPv4 header? */
/* no */
BPRI_HI);
goto drop;
}
} else {
/* yes */
}
/*
* copy template header into packet IPv4 header
* for configured tunnels everything should be
* in template.
* Automatic tunnels need the dest set from
* incoming ipv6 packet
*/
/* XXX don't support tun_laddr of 0 */
/* Is this an automatic tunnel ? */
/*
* Process packets for automatic tunneling
*/
/*
* destination address must be compatible address
* and cannot be multicast
*/
("tun_wdata_v6: %s dest is not IPv4: %s\n",
goto drop;
}
tun0dbg(("tun_wdata_v6: %s Multicast dst not" \
goto drop;
}
/* Is this a 6to4 tunnel ? */
/*
* make sure IPv6 source is a 6to4 address.
*/
tun0dbg(("tun_wdata_v6: %s tun: invalid " \
goto drop;
}
/*
* As per RFC 3056, the IPv4 source MUST be set to the
* V4ADDR portion of the IPv6 source.
*/
/*
* As per RFC 3056, the IPv4 destination MUST be set to
* either:
* - the V4ADDR portion of the IPv6 destination, if the
* destination is a 6to4 address.
* - the well known 6to4 Relay Router anycast address
* (192.88.99.1, defined in RFC 3068), if IPv6
* destination is a native IPv6 address.
* - a unicast address of a 6to4 relay router set by
* the administrator.
*
* This implementation will drop packets with native
* IPv6 destinations if 6to4 Relay Router communication
* support is disabled. This support is checked
* by examining tuns_relay_rtr_addr_v4; INADDR_ANY
* denotes
* support is disabled; a valid, routable IPv4 addr
* denotes support is enabled. Support is disabled
* by default, because there is no standard trust
* mechanism for communicating with 6to4 Relay Routers.
*/
/* destination is a 6to4 router */
&in_v4addr);
} else {
/*
* destination is a native IPv6 address
*/
if (tuns->tuns_relay_rtr_addr_v4 ==
INADDR_ANY) {
/*
* 6to4 Relay Router communication
* support is disabled.
*/
tun1dbg(("tun_wdata_v6: "
"%s tuns_relay_rtr_addr_v4 = %s, "
"dropping packet with IPv6 dst "
goto drop;
}
/*
* 6to4 Relay Router communication support
* is enabled. Set IPv4 destination to
* address of configured Relay Router
* (this addr may equal the well-known
* 6to4 Relay Router anycast address,
* defined in RFC 3068)
*/
}
}
/*
* If IPv4 mtu is less than the minimum IPv6 mtu size, then
* allow IPv4 to fragment the packet.
* This works because if our IPv6 length is less than
* min IPv6 mtu, IPv4 might have to fragment anyway
* and we really can't handle an message too big icmp
* error. If the packet is greater them min IPv6 mtu,
* then a message too big icmp error will cause the
* IPv6 to shrink its packets
*/
} else {
}
tun3dbg(("tun_wdata_v6: %s sending IPv4 packet src %s dest " \
break;
case TUN_L_V6:
/* room for IPv6 header? */
/*
* Calculate tunnel encapsulation limit. < 0 means error, 0
* means don't include a TEL option, and > 0 means use this
* value as the limit. Right here, just update the header
* length to take the extra TEL destination option into
* account, or send an ICMP parameter problem and return.
*/
if (encap_limit >= 0)
} else
return; /* mp freed by tun_limit_value_v6 */
/* no */
BPRI_HI);
return;
}
} else {
/* yes */
}
if (encap_limit >= 0) {
encap_opt = (struct ip6_opt_tunnel *)
sizeof (struct ip6_dest));
}
/* Is this a 6to4 or automatic tunnel ? */
goto drop;
}
hdrlen);
break;
default:
/* LINTED */
ASSERT(0 && "not supported");
goto drop;
}
/*
* Request the destination ire regularly in case Path MTU has
* increased, but only for configured tunnels.
*/
tun_send_ire_req(q);
/* send the packet down the transport stream to IP */
atp->tun_netstack);
return;
return;
drop:
}
/*
* T_BIND to lower stream.
*/
static int
{
struct T_bind_req *tbr;
int err = 0;
char *cp;
return (EINVAL);
}
size = sizeof (ipa_conn_x_t);
} else {
}
} else {
return (EINVAL);
}
} else { /* lower is V6 */
size = sizeof (ipa6_conn_x_t);
} else {
}
} else {
return (EINVAL);
}
}
/* allocate an mblk */
tun0dbg(("tun_send_bind_req: couldn't allocate mblk\n"));
return (ENOMEM);
}
tun0dbg(("tun_send_bind_req: couldn't allocate mblk\n"));
return (ENOMEM);
}
tbr->CONIND_number = 0;
/*
* Send a T_BIND_REQ down to IP to bind to IPPROTO_IPV6
* or IPPROTO_ENCAP.
*/
/* Source is always required */
} else {
/*
* We used to use ipa_conn_t here, but discovered that
* IP insisted that the tunnel destination address be
* reachable, i.e. have a route. This causes problems
* in a number of cases. ipa_conn_x_t was invented to
* allow verifying destination reachability to be
* controlled. We choose not to verify destination
* reachability. All we really want is to register to
* receive packets for the tunnel, and don't care at
* this point whether the tunnel destination is
* reachable.
*/
goto error;
}
}
else
} else {
sizeof (in6_addr_t));
} else {
sizeof (in6_addr_t));
sizeof (in6_addr_t));
}
else
}
/*
* Since we're requesting ire information for the destination
* along with this T_BIND_REQ, stamp the tunnel's tun_ire_lastreq
* with the current time.
*/
return (0);
return (err);
}
/*
* Update kstats
*/
static int
{
return (EIO);
/* Initialize kstat, but only the first one */
if (rw == KSTAT_WRITE) {
return (EACCES);
}
/*
* MIB II kstat variables
*/
return (0);
}
/*
* update kstats.. fist zero them all out, then
* walk through all the interfaces that share kstat and
* add in the new stats
*/
}
return (0);
}
/*
* Initialize kstats
*/
static void
{
char buf[32];
char *mod_buf;
/*
* create kstat name based on lower ip and ppa
*/
mod_buf = "ip";
} else {
mod_buf = "ip6";
}
return;
}
}
/*
* Debug routine to print out tunnel name
*/
static char *
{
char ppa_buf[20];
return ("tun_who: no buf");
} else {
}
"<unknown af>");
return (buf);
}
/*
* Initialize the tunnel stack instance.
*/
/*ARGSUSED*/
static void *
{
return (tuns);
}
/*
* Free the tunnel stack instance.
*/
/*ARGSUSED*/
static void
{
int i;
for (i = 0; i < TUN_PPA_SZ; i++) {
}
for (i = 0; i < TUN_T_SZ; i++) {
}
}