125ec3e3dSEric Cheng /*
225ec3e3dSEric Cheng * CDDL HEADER START
325ec3e3dSEric Cheng *
425ec3e3dSEric Cheng * The contents of this file are subject to the terms of the
525ec3e3dSEric Cheng * Common Development and Distribution License (the "License").
625ec3e3dSEric Cheng * You may not use this file except in compliance with the License.
725ec3e3dSEric Cheng *
825ec3e3dSEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925ec3e3dSEric Cheng * or http://www.opensolaris.org/os/licensing.
1025ec3e3dSEric Cheng * See the License for the specific language governing permissions
1125ec3e3dSEric Cheng * and limitations under the License.
1225ec3e3dSEric Cheng *
1325ec3e3dSEric Cheng * When distributing Covered Code, include this CDDL HEADER in each
1425ec3e3dSEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525ec3e3dSEric Cheng * If applicable, add the following below this CDDL HEADER, with the
1625ec3e3dSEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying
1725ec3e3dSEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner]
1825ec3e3dSEric Cheng *
1925ec3e3dSEric Cheng * CDDL HEADER END
2025ec3e3dSEric Cheng */
2125ec3e3dSEric Cheng
2225ec3e3dSEric Cheng /*
23550b6e40SSowmini Varadhan * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24146e34b6SCody Peter Mello * Copyright (c) 2015, Joyent, Inc. All rights reserved.
2525ec3e3dSEric Cheng */
264d6a58d3SJosef 'Jeff' Sipek /*
274d6a58d3SJosef 'Jeff' Sipek * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
284d6a58d3SJosef 'Jeff' Sipek */
2925ec3e3dSEric Cheng
30797f979dSCody Peter Mello #include <sys/cmn_err.h>
3125ec3e3dSEric Cheng #include <sys/strsun.h>
3225ec3e3dSEric Cheng #include <sys/sdt.h>
3325ec3e3dSEric Cheng #include <sys/mac.h>
3425ec3e3dSEric Cheng #include <sys/mac_impl.h>
3525ec3e3dSEric Cheng #include <sys/mac_client_impl.h>
3625ec3e3dSEric Cheng #include <sys/mac_client_priv.h>
3725ec3e3dSEric Cheng #include <sys/ethernet.h>
3825ec3e3dSEric Cheng #include <sys/vlan.h>
3925ec3e3dSEric Cheng #include <sys/dlpi.h>
400dc2366fSVenugopal Iyer #include <sys/avl.h>
4125ec3e3dSEric Cheng #include <inet/ip.h>
4225ec3e3dSEric Cheng #include <inet/ip6.h>
4325ec3e3dSEric Cheng #include <inet/arp.h>
440dc2366fSVenugopal Iyer #include <netinet/arp.h>
450dc2366fSVenugopal Iyer #include <netinet/udp.h>
460dc2366fSVenugopal Iyer #include <netinet/dhcp.h>
470dc2366fSVenugopal Iyer #include <netinet/dhcp6.h>
4825ec3e3dSEric Cheng
4925ec3e3dSEric Cheng /*
500dc2366fSVenugopal Iyer * Implementation overview for DHCP address detection
510dc2366fSVenugopal Iyer *
520dc2366fSVenugopal Iyer * The purpose of DHCP address detection is to relieve the user of having to
530dc2366fSVenugopal Iyer * manually configure static IP addresses when ip-nospoof protection is turned
540dc2366fSVenugopal Iyer * on. To achieve this, the mac layer needs to intercept DHCP packets to
550dc2366fSVenugopal Iyer * determine the assigned IP addresses.
560dc2366fSVenugopal Iyer *
570dc2366fSVenugopal Iyer * A DHCP handshake between client and server typically requires at least
580dc2366fSVenugopal Iyer * 4 messages:
590dc2366fSVenugopal Iyer *
600dc2366fSVenugopal Iyer * 1. DISCOVER - client attempts to locate DHCP servers via a
610dc2366fSVenugopal Iyer * broadcast message to its subnet.
620dc2366fSVenugopal Iyer * 2. OFFER - server responds to client with an IP address and
630dc2366fSVenugopal Iyer * other parameters.
640dc2366fSVenugopal Iyer * 3. REQUEST - client requests the offered address.
650dc2366fSVenugopal Iyer * 4. ACK - server verifies that the requested address matches
660dc2366fSVenugopal Iyer * the one it offered.
670dc2366fSVenugopal Iyer *
680dc2366fSVenugopal Iyer * DHCPv6 behaves pretty much the same way aside from different message names.
690dc2366fSVenugopal Iyer *
700dc2366fSVenugopal Iyer * Address information is embedded in either the OFFER or REQUEST message.
710dc2366fSVenugopal Iyer * We chose to intercept REQUEST because this is at the last part of the
720dc2366fSVenugopal Iyer * handshake and it indicates that the client intends to keep the address.
730dc2366fSVenugopal Iyer * Intercepting OFFERs is unreliable because the client may receive multiple
740dc2366fSVenugopal Iyer * offers from different servers, and we can't tell which address the client
750dc2366fSVenugopal Iyer * will keep.
760dc2366fSVenugopal Iyer *
770dc2366fSVenugopal Iyer * Each DHCP message has a transaction ID. We use this transaction ID to match
780dc2366fSVenugopal Iyer * REQUESTs with ACKs received from servers.
790dc2366fSVenugopal Iyer *
800dc2366fSVenugopal Iyer * For IPv4, the process to acquire a DHCP-assigned address is as follows:
810dc2366fSVenugopal Iyer *
820dc2366fSVenugopal Iyer * 1. Client sends REQUEST. a new dhcpv4_txn_t object is created and inserted
830dc2366fSVenugopal Iyer * in the the mci_v4_pending_txn table (keyed by xid). This object represents
840dc2366fSVenugopal Iyer * a new transaction. It contains the xid, the client ID and requested IP
850dc2366fSVenugopal Iyer * address.
860dc2366fSVenugopal Iyer *
870dc2366fSVenugopal Iyer * 2. Server responds with an ACK. The xid from this ACK is used to lookup the
880dc2366fSVenugopal Iyer * pending transaction from the mci_v4_pending_txn table. Once the object is
890dc2366fSVenugopal Iyer * found, it is removed from the pending table and inserted into the
900dc2366fSVenugopal Iyer * completed table (mci_v4_completed_txn, keyed by client ID) and the dynamic
910dc2366fSVenugopal Iyer * IP table (mci_v4_dyn_ip, keyed by IP address).
920dc2366fSVenugopal Iyer *
930dc2366fSVenugopal Iyer * 3. An outgoing packet that goes through the ip-nospoof path will be checked
940dc2366fSVenugopal Iyer * against the dynamic IP table. Packets that have the assigned DHCP address
950dc2366fSVenugopal Iyer * as the source IP address will pass the check and be admitted onto the
960dc2366fSVenugopal Iyer * network.
970dc2366fSVenugopal Iyer *
980dc2366fSVenugopal Iyer * IPv4 notes:
990dc2366fSVenugopal Iyer *
1000dc2366fSVenugopal Iyer * If the server never responds with an ACK, there is a timer that is set after
1010dc2366fSVenugopal Iyer * the insertion of the transaction into the pending table. When the timer
1020dc2366fSVenugopal Iyer * fires, it will check whether the transaction is old (by comparing current
1030dc2366fSVenugopal Iyer * time and the txn's timestamp), if so the transaction will be freed. along
1040dc2366fSVenugopal Iyer * with this, any transaction in the completed/dyn-ip tables matching the client
1050dc2366fSVenugopal Iyer * ID of this stale transaction will also be freed. If the client fails to
1060dc2366fSVenugopal Iyer * extend a lease, we want to stop the client from using any IP addresses that
1070dc2366fSVenugopal Iyer * were granted previously.
1080dc2366fSVenugopal Iyer *
1090dc2366fSVenugopal Iyer * A RELEASE message from the client will not cause a transaction to be created.
1100dc2366fSVenugopal Iyer * The client ID in the RELEASE message will be used for finding and removing
1110dc2366fSVenugopal Iyer * transactions in the completed and dyn-ip tables.
1120dc2366fSVenugopal Iyer *
1130dc2366fSVenugopal Iyer *
1140dc2366fSVenugopal Iyer * For IPv6, the process to acquire a DHCPv6-assigned address is as follows:
1150dc2366fSVenugopal Iyer *
1160dc2366fSVenugopal Iyer * 1. Client sends REQUEST. The DUID is extracted and stored into a dhcpv6_cid_t
1170dc2366fSVenugopal Iyer * structure. A new transaction structure (dhcpv6_txn_t) is also created and
1180dc2366fSVenugopal Iyer * it will point to the dhcpv6_cid_t. If an existing transaction with a
1190dc2366fSVenugopal Iyer * matching xid is not found, this dhcpv6_txn_t will be inserted into the
1200dc2366fSVenugopal Iyer * mci_v6_pending_txn table (keyed by xid).
1210dc2366fSVenugopal Iyer *
1220dc2366fSVenugopal Iyer * 2. Server responds with a REPLY. If a pending transaction is found, the
1230dc2366fSVenugopal Iyer * addresses in the reply will be placed into the dhcpv6_cid_t pointed to by
1240dc2366fSVenugopal Iyer * the transaction. The dhcpv6_cid_t will then be moved to the mci_v6_cid
1250dc2366fSVenugopal Iyer * table (keyed by cid). The associated addresses will be added to the
1260dc2366fSVenugopal Iyer * mci_v6_dyn_ip table (while still being pointed to by the dhcpv6_cid_t).
1270dc2366fSVenugopal Iyer *
1280dc2366fSVenugopal Iyer * 3. IPv6 ip-nospoof will now check mci_v6_dyn_ip for matching packets.
1290dc2366fSVenugopal Iyer * Packets with a source address matching one of the DHCPv6-assigned
1300dc2366fSVenugopal Iyer * addresses will be allowed through.
1310dc2366fSVenugopal Iyer *
1320dc2366fSVenugopal Iyer * IPv6 notes:
1330dc2366fSVenugopal Iyer *
1340dc2366fSVenugopal Iyer * The v6 code shares the same timer as v4 for scrubbing stale transactions.
1350dc2366fSVenugopal Iyer * Just like v4, as part of removing an expired transaction, a RELEASE will be
1360dc2366fSVenugopal Iyer * be triggered on the cid associated with the expired transaction.
1370dc2366fSVenugopal Iyer *
1380dc2366fSVenugopal Iyer * The data structures used for v6 are slightly different because a v6 client
1390dc2366fSVenugopal Iyer * may have multiple addresses associated with it.
1400dc2366fSVenugopal Iyer */
1410dc2366fSVenugopal Iyer
1420dc2366fSVenugopal Iyer /*
1430dc2366fSVenugopal Iyer * These are just arbitrary limits meant for preventing abuse (e.g. a user
1440dc2366fSVenugopal Iyer * flooding the network with bogus transactions). They are not meant to be
1450dc2366fSVenugopal Iyer * user-modifiable so they are not exposed as linkprops.
1460dc2366fSVenugopal Iyer */
1470dc2366fSVenugopal Iyer static ulong_t dhcp_max_pending_txn = 512;
1480dc2366fSVenugopal Iyer static ulong_t dhcp_max_completed_txn = 512;
149797f979dSCody Peter Mello static ulong_t slaac_max_allowed = 512;
1504d6a58d3SJosef 'Jeff' Sipek static hrtime_t txn_cleanup_interval = 60 * NANOSEC;
1510dc2366fSVenugopal Iyer
1520dc2366fSVenugopal Iyer /*
1530dc2366fSVenugopal Iyer * DHCPv4 transaction. It may be added to three different tables
1540dc2366fSVenugopal Iyer * (keyed by different fields).
1550dc2366fSVenugopal Iyer */
1560dc2366fSVenugopal Iyer typedef struct dhcpv4_txn {
1570dc2366fSVenugopal Iyer uint32_t dt_xid;
1584d6a58d3SJosef 'Jeff' Sipek hrtime_t dt_timestamp;
1590dc2366fSVenugopal Iyer uint8_t dt_cid[DHCP_MAX_OPT_SIZE];
1600dc2366fSVenugopal Iyer uint8_t dt_cid_len;
1610dc2366fSVenugopal Iyer ipaddr_t dt_ipaddr;
1620dc2366fSVenugopal Iyer avl_node_t dt_node;
1630dc2366fSVenugopal Iyer avl_node_t dt_ipnode;
1640dc2366fSVenugopal Iyer struct dhcpv4_txn *dt_next;
1650dc2366fSVenugopal Iyer } dhcpv4_txn_t;
1660dc2366fSVenugopal Iyer
1670dc2366fSVenugopal Iyer /*
1680dc2366fSVenugopal Iyer * DHCPv6 address. May be added to mci_v6_dyn_ip.
1690dc2366fSVenugopal Iyer * It is always pointed to by its parent dhcpv6_cid_t structure.
1700dc2366fSVenugopal Iyer */
1710dc2366fSVenugopal Iyer typedef struct dhcpv6_addr {
1720dc2366fSVenugopal Iyer in6_addr_t da_addr;
1730dc2366fSVenugopal Iyer avl_node_t da_node;
1740dc2366fSVenugopal Iyer struct dhcpv6_addr *da_next;
1750dc2366fSVenugopal Iyer } dhcpv6_addr_t;
1760dc2366fSVenugopal Iyer
1770dc2366fSVenugopal Iyer /*
1780dc2366fSVenugopal Iyer * DHCPv6 client ID. May be added to mci_v6_cid.
1790dc2366fSVenugopal Iyer * No dhcpv6_txn_t should be pointing to it after it is added to mci_v6_cid.
1800dc2366fSVenugopal Iyer */
1810dc2366fSVenugopal Iyer typedef struct dhcpv6_cid {
1820dc2366fSVenugopal Iyer uchar_t *dc_cid;
1830dc2366fSVenugopal Iyer uint_t dc_cid_len;
1840dc2366fSVenugopal Iyer dhcpv6_addr_t *dc_addr;
1850dc2366fSVenugopal Iyer uint_t dc_addrcnt;
1860dc2366fSVenugopal Iyer avl_node_t dc_node;
1870dc2366fSVenugopal Iyer } dhcpv6_cid_t;
1880dc2366fSVenugopal Iyer
1890dc2366fSVenugopal Iyer /*
1900dc2366fSVenugopal Iyer * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up
1910dc2366fSVenugopal Iyer * as soon as the transaction completes or expires.
1920dc2366fSVenugopal Iyer */
1930dc2366fSVenugopal Iyer typedef struct dhcpv6_txn {
1940dc2366fSVenugopal Iyer uint32_t dt_xid;
1954d6a58d3SJosef 'Jeff' Sipek hrtime_t dt_timestamp;
1960dc2366fSVenugopal Iyer dhcpv6_cid_t *dt_cid;
1970dc2366fSVenugopal Iyer avl_node_t dt_node;
1980dc2366fSVenugopal Iyer struct dhcpv6_txn *dt_next;
1990dc2366fSVenugopal Iyer } dhcpv6_txn_t;
2000dc2366fSVenugopal Iyer
201797f979dSCody Peter Mello /*
202797f979dSCody Peter Mello * Stateless address autoconfiguration (SLAAC) address. May be added to
203797f979dSCody Peter Mello * mci_v6_slaac_ip.
204797f979dSCody Peter Mello */
205797f979dSCody Peter Mello typedef struct slaac_addr {
206797f979dSCody Peter Mello in6_addr_t sla_prefix;
207797f979dSCody Peter Mello in6_addr_t sla_addr;
208797f979dSCody Peter Mello avl_node_t sla_node;
209797f979dSCody Peter Mello } slaac_addr_t;
210797f979dSCody Peter Mello
2110dc2366fSVenugopal Iyer static void start_txn_cleanup_timer(mac_client_impl_t *);
212550b6e40SSowmini Varadhan static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t);
2130dc2366fSVenugopal Iyer
2140dc2366fSVenugopal Iyer #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++
2150dc2366fSVenugopal Iyer
2160dc2366fSVenugopal Iyer /*
2170dc2366fSVenugopal Iyer * Comparison functions for the 3 AVL trees used:
2180dc2366fSVenugopal Iyer * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip
2190dc2366fSVenugopal Iyer */
2200dc2366fSVenugopal Iyer static int
compare_dhcpv4_xid(const void * arg1,const void * arg2)2210dc2366fSVenugopal Iyer compare_dhcpv4_xid(const void *arg1, const void *arg2)
2220dc2366fSVenugopal Iyer {
2230dc2366fSVenugopal Iyer const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2;
2240dc2366fSVenugopal Iyer
2250dc2366fSVenugopal Iyer if (txn1->dt_xid < txn2->dt_xid)
2260dc2366fSVenugopal Iyer return (-1);
2270dc2366fSVenugopal Iyer else if (txn1->dt_xid > txn2->dt_xid)
2280dc2366fSVenugopal Iyer return (1);
2290dc2366fSVenugopal Iyer else
2300dc2366fSVenugopal Iyer return (0);
2310dc2366fSVenugopal Iyer }
2320dc2366fSVenugopal Iyer
2330dc2366fSVenugopal Iyer static int
compare_dhcpv4_cid(const void * arg1,const void * arg2)2340dc2366fSVenugopal Iyer compare_dhcpv4_cid(const void *arg1, const void *arg2)
2350dc2366fSVenugopal Iyer {
2360dc2366fSVenugopal Iyer const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2;
2370dc2366fSVenugopal Iyer int ret;
2380dc2366fSVenugopal Iyer
2390dc2366fSVenugopal Iyer if (txn1->dt_cid_len < txn2->dt_cid_len)
2400dc2366fSVenugopal Iyer return (-1);
2410dc2366fSVenugopal Iyer else if (txn1->dt_cid_len > txn2->dt_cid_len)
2420dc2366fSVenugopal Iyer return (1);
2430dc2366fSVenugopal Iyer
2440dc2366fSVenugopal Iyer if (txn1->dt_cid_len == 0)
2450dc2366fSVenugopal Iyer return (0);
2460dc2366fSVenugopal Iyer
2470dc2366fSVenugopal Iyer ret = memcmp(txn1->dt_cid, txn2->dt_cid, txn1->dt_cid_len);
2480dc2366fSVenugopal Iyer if (ret < 0)
2490dc2366fSVenugopal Iyer return (-1);
2500dc2366fSVenugopal Iyer else if (ret > 0)
2510dc2366fSVenugopal Iyer return (1);
2520dc2366fSVenugopal Iyer else
2530dc2366fSVenugopal Iyer return (0);
2540dc2366fSVenugopal Iyer }
2550dc2366fSVenugopal Iyer
2560dc2366fSVenugopal Iyer static int
compare_dhcpv4_ip(const void * arg1,const void * arg2)2570dc2366fSVenugopal Iyer compare_dhcpv4_ip(const void *arg1, const void *arg2)
2580dc2366fSVenugopal Iyer {
2590dc2366fSVenugopal Iyer const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2;
2600dc2366fSVenugopal Iyer
2610dc2366fSVenugopal Iyer if (txn1->dt_ipaddr < txn2->dt_ipaddr)
2620dc2366fSVenugopal Iyer return (-1);
2630dc2366fSVenugopal Iyer else if (txn1->dt_ipaddr > txn2->dt_ipaddr)
2640dc2366fSVenugopal Iyer return (1);
2650dc2366fSVenugopal Iyer else
2660dc2366fSVenugopal Iyer return (0);
2670dc2366fSVenugopal Iyer }
2680dc2366fSVenugopal Iyer
2690dc2366fSVenugopal Iyer /*
2700dc2366fSVenugopal Iyer * Find the specified DHCPv4 option.
2710dc2366fSVenugopal Iyer */
2720dc2366fSVenugopal Iyer static int
get_dhcpv4_option(struct dhcp * dh4,uchar_t * end,uint8_t type,uchar_t ** opt,uint8_t * opt_len)2730dc2366fSVenugopal Iyer get_dhcpv4_option(struct dhcp *dh4, uchar_t *end, uint8_t type,
2740dc2366fSVenugopal Iyer uchar_t **opt, uint8_t *opt_len)
2750dc2366fSVenugopal Iyer {
2760dc2366fSVenugopal Iyer uchar_t *start = (uchar_t *)dh4->options;
2770dc2366fSVenugopal Iyer uint8_t otype, olen;
2780dc2366fSVenugopal Iyer
2790dc2366fSVenugopal Iyer while (start < end) {
2800dc2366fSVenugopal Iyer if (*start == CD_PAD) {
2810dc2366fSVenugopal Iyer start++;
2820dc2366fSVenugopal Iyer continue;
2830dc2366fSVenugopal Iyer }
2840dc2366fSVenugopal Iyer if (*start == CD_END)
2850dc2366fSVenugopal Iyer break;
2860dc2366fSVenugopal Iyer
2870dc2366fSVenugopal Iyer otype = *start++;
2880dc2366fSVenugopal Iyer olen = *start++;
2890dc2366fSVenugopal Iyer if (otype == type && olen > 0) {
2900dc2366fSVenugopal Iyer *opt = start;
2910dc2366fSVenugopal Iyer *opt_len = olen;
2920dc2366fSVenugopal Iyer return (0);
2930dc2366fSVenugopal Iyer }
2940dc2366fSVenugopal Iyer start += olen;
2950dc2366fSVenugopal Iyer }
2960dc2366fSVenugopal Iyer return (ENOENT);
2970dc2366fSVenugopal Iyer }
2980dc2366fSVenugopal Iyer
2990dc2366fSVenugopal Iyer /*
3000dc2366fSVenugopal Iyer * Locate the start of a DHCPv4 header.
3010dc2366fSVenugopal Iyer * The possible return values and associated meanings are:
3020dc2366fSVenugopal Iyer * 0 - packet is DHCP and has a DHCP header.
3030dc2366fSVenugopal Iyer * EINVAL - packet is not DHCP. the recommended action is to let it pass.
3040dc2366fSVenugopal Iyer * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable.
3050dc2366fSVenugopal Iyer * the recommended action is to drop it.
3060dc2366fSVenugopal Iyer */
3070dc2366fSVenugopal Iyer static int
get_dhcpv4_info(ipha_t * ipha,uchar_t * end,struct dhcp ** dh4)3080dc2366fSVenugopal Iyer get_dhcpv4_info(ipha_t *ipha, uchar_t *end, struct dhcp **dh4)
3090dc2366fSVenugopal Iyer {
3100dc2366fSVenugopal Iyer uint16_t offset_and_flags, client, server;
3110dc2366fSVenugopal Iyer boolean_t first_frag = B_FALSE;
3120dc2366fSVenugopal Iyer struct udphdr *udph;
3130dc2366fSVenugopal Iyer uchar_t *dh;
3140dc2366fSVenugopal Iyer
3150dc2366fSVenugopal Iyer if (ipha->ipha_protocol != IPPROTO_UDP)
3160dc2366fSVenugopal Iyer return (EINVAL);
3170dc2366fSVenugopal Iyer
3180dc2366fSVenugopal Iyer offset_and_flags = ntohs(ipha->ipha_fragment_offset_and_flags);
3190dc2366fSVenugopal Iyer if ((offset_and_flags & (IPH_MF | IPH_OFFSET)) != 0) {
3200dc2366fSVenugopal Iyer /*
3210dc2366fSVenugopal Iyer * All non-initial fragments may pass because we cannot
3220dc2366fSVenugopal Iyer * identify their type. It's safe to let them through
3230dc2366fSVenugopal Iyer * because reassembly will fail if we decide to drop the
3240dc2366fSVenugopal Iyer * initial fragment.
3250dc2366fSVenugopal Iyer */
3260dc2366fSVenugopal Iyer if (((offset_and_flags << 3) & 0xffff) != 0)
3270dc2366fSVenugopal Iyer return (EINVAL);
3280dc2366fSVenugopal Iyer first_frag = B_TRUE;
3290dc2366fSVenugopal Iyer }
3300dc2366fSVenugopal Iyer /* drop packets without a udp header */
3310dc2366fSVenugopal Iyer udph = (struct udphdr *)((uchar_t *)ipha + IPH_HDR_LENGTH(ipha));
3320dc2366fSVenugopal Iyer if ((uchar_t *)&udph[1] > end)
3330dc2366fSVenugopal Iyer return (ENOSPC);
3340dc2366fSVenugopal Iyer
3350dc2366fSVenugopal Iyer client = htons(IPPORT_BOOTPC);
3360dc2366fSVenugopal Iyer server = htons(IPPORT_BOOTPS);
3370dc2366fSVenugopal Iyer if (udph->uh_sport != client && udph->uh_sport != server &&
3380dc2366fSVenugopal Iyer udph->uh_dport != client && udph->uh_dport != server)
3390dc2366fSVenugopal Iyer return (EINVAL);
3400dc2366fSVenugopal Iyer
3410dc2366fSVenugopal Iyer /* drop dhcp fragments */
3420dc2366fSVenugopal Iyer if (first_frag)
3430dc2366fSVenugopal Iyer return (ENOSPC);
3440dc2366fSVenugopal Iyer
3450dc2366fSVenugopal Iyer dh = (uchar_t *)&udph[1];
3460dc2366fSVenugopal Iyer if (dh + BASE_PKT_SIZE > end)
3470dc2366fSVenugopal Iyer return (EINVAL);
3480dc2366fSVenugopal Iyer
3490dc2366fSVenugopal Iyer *dh4 = (struct dhcp *)dh;
3500dc2366fSVenugopal Iyer return (0);
3510dc2366fSVenugopal Iyer }
3520dc2366fSVenugopal Iyer
3530dc2366fSVenugopal Iyer /*
3540dc2366fSVenugopal Iyer * Wrappers for accesses to avl trees to improve readability.
3550dc2366fSVenugopal Iyer * Their purposes are fairly self-explanatory.
3560dc2366fSVenugopal Iyer */
3570dc2366fSVenugopal Iyer static dhcpv4_txn_t *
find_dhcpv4_pending_txn(mac_client_impl_t * mcip,uint32_t xid)3580dc2366fSVenugopal Iyer find_dhcpv4_pending_txn(mac_client_impl_t *mcip, uint32_t xid)
3590dc2366fSVenugopal Iyer {
3600dc2366fSVenugopal Iyer dhcpv4_txn_t tmp_txn;
3610dc2366fSVenugopal Iyer
3620dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
3630dc2366fSVenugopal Iyer tmp_txn.dt_xid = xid;
3640dc2366fSVenugopal Iyer return (avl_find(&mcip->mci_v4_pending_txn, &tmp_txn, NULL));
3650dc2366fSVenugopal Iyer }
3660dc2366fSVenugopal Iyer
3670dc2366fSVenugopal Iyer static int
insert_dhcpv4_pending_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)3680dc2366fSVenugopal Iyer insert_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
3690dc2366fSVenugopal Iyer {
3700dc2366fSVenugopal Iyer avl_index_t where;
3710dc2366fSVenugopal Iyer
3720dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
3730dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v4_pending_txn, txn, &where) != NULL)
3740dc2366fSVenugopal Iyer return (EEXIST);
3750dc2366fSVenugopal Iyer
3760dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v4_pending_txn) >= dhcp_max_pending_txn) {
3770dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped);
3780dc2366fSVenugopal Iyer return (EAGAIN);
3790dc2366fSVenugopal Iyer }
3800dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v4_pending_txn, txn, where);
3810dc2366fSVenugopal Iyer return (0);
3820dc2366fSVenugopal Iyer }
3830dc2366fSVenugopal Iyer
3840dc2366fSVenugopal Iyer static void
remove_dhcpv4_pending_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)3850dc2366fSVenugopal Iyer remove_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
3860dc2366fSVenugopal Iyer {
3870dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
3880dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_pending_txn, txn);
3890dc2366fSVenugopal Iyer }
3900dc2366fSVenugopal Iyer
3910dc2366fSVenugopal Iyer static dhcpv4_txn_t *
find_dhcpv4_completed_txn(mac_client_impl_t * mcip,uint8_t * cid,uint8_t cid_len)3920dc2366fSVenugopal Iyer find_dhcpv4_completed_txn(mac_client_impl_t *mcip, uint8_t *cid,
3930dc2366fSVenugopal Iyer uint8_t cid_len)
3940dc2366fSVenugopal Iyer {
3950dc2366fSVenugopal Iyer dhcpv4_txn_t tmp_txn;
3960dc2366fSVenugopal Iyer
3970dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
3980dc2366fSVenugopal Iyer if (cid_len > 0)
3990dc2366fSVenugopal Iyer bcopy(cid, tmp_txn.dt_cid, cid_len);
4000dc2366fSVenugopal Iyer tmp_txn.dt_cid_len = cid_len;
4010dc2366fSVenugopal Iyer return (avl_find(&mcip->mci_v4_completed_txn, &tmp_txn, NULL));
4020dc2366fSVenugopal Iyer }
4030dc2366fSVenugopal Iyer
4040dc2366fSVenugopal Iyer /*
4050dc2366fSVenugopal Iyer * After a pending txn is removed from the pending table, it is inserted
4060dc2366fSVenugopal Iyer * into both the completed and dyn-ip tables. These two insertions are
4070dc2366fSVenugopal Iyer * done together because a client ID must have 1:1 correspondence with
4080dc2366fSVenugopal Iyer * an IP address and IP addresses must be unique in the dyn-ip table.
4090dc2366fSVenugopal Iyer */
4100dc2366fSVenugopal Iyer static int
insert_dhcpv4_completed_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)4110dc2366fSVenugopal Iyer insert_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
4120dc2366fSVenugopal Iyer {
4130dc2366fSVenugopal Iyer avl_index_t where;
4140dc2366fSVenugopal Iyer
4150dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
4160dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v4_completed_txn, txn, &where) != NULL)
4170dc2366fSVenugopal Iyer return (EEXIST);
4180dc2366fSVenugopal Iyer
4190dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v4_completed_txn) >=
4200dc2366fSVenugopal Iyer dhcp_max_completed_txn) {
4210dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped);
4220dc2366fSVenugopal Iyer return (EAGAIN);
4230dc2366fSVenugopal Iyer }
4240dc2366fSVenugopal Iyer
4250dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v4_completed_txn, txn, where);
4260dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v4_dyn_ip, txn, &where) != NULL) {
4270dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_completed_txn, txn);
4280dc2366fSVenugopal Iyer return (EEXIST);
4290dc2366fSVenugopal Iyer }
4300dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v4_dyn_ip, txn, where);
4310dc2366fSVenugopal Iyer return (0);
4320dc2366fSVenugopal Iyer }
4330dc2366fSVenugopal Iyer
4340dc2366fSVenugopal Iyer static void
remove_dhcpv4_completed_txn(mac_client_impl_t * mcip,dhcpv4_txn_t * txn)4350dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn)
4360dc2366fSVenugopal Iyer {
4370dc2366fSVenugopal Iyer dhcpv4_txn_t *ctxn;
4380dc2366fSVenugopal Iyer
4390dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
4400dc2366fSVenugopal Iyer if ((ctxn = avl_find(&mcip->mci_v4_dyn_ip, txn, NULL)) != NULL &&
4410dc2366fSVenugopal Iyer ctxn == txn)
4420dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_dyn_ip, txn);
4430dc2366fSVenugopal Iyer
4440dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_completed_txn, txn);
4450dc2366fSVenugopal Iyer }
4460dc2366fSVenugopal Iyer
4470dc2366fSVenugopal Iyer /*
4480dc2366fSVenugopal Iyer * Check whether an IP address is in the dyn-ip table.
4490dc2366fSVenugopal Iyer */
4500dc2366fSVenugopal Iyer static boolean_t
check_dhcpv4_dyn_ip(mac_client_impl_t * mcip,ipaddr_t ipaddr)4510dc2366fSVenugopal Iyer check_dhcpv4_dyn_ip(mac_client_impl_t *mcip, ipaddr_t ipaddr)
4520dc2366fSVenugopal Iyer {
4530dc2366fSVenugopal Iyer dhcpv4_txn_t tmp_txn, *txn;
4540dc2366fSVenugopal Iyer
4550dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
4560dc2366fSVenugopal Iyer tmp_txn.dt_ipaddr = ipaddr;
4570dc2366fSVenugopal Iyer txn = avl_find(&mcip->mci_v4_dyn_ip, &tmp_txn, NULL);
4580dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
4590dc2366fSVenugopal Iyer return (txn != NULL);
4600dc2366fSVenugopal Iyer }
4610dc2366fSVenugopal Iyer
4620dc2366fSVenugopal Iyer /*
4630dc2366fSVenugopal Iyer * Create/destroy a DHCPv4 transaction.
4640dc2366fSVenugopal Iyer */
4650dc2366fSVenugopal Iyer static dhcpv4_txn_t *
create_dhcpv4_txn(uint32_t xid,uint8_t * cid,uint8_t cid_len,ipaddr_t ipaddr)4660dc2366fSVenugopal Iyer create_dhcpv4_txn(uint32_t xid, uint8_t *cid, uint8_t cid_len, ipaddr_t ipaddr)
4670dc2366fSVenugopal Iyer {
4680dc2366fSVenugopal Iyer dhcpv4_txn_t *txn;
4690dc2366fSVenugopal Iyer
4700dc2366fSVenugopal Iyer if ((txn = kmem_zalloc(sizeof (*txn), KM_NOSLEEP)) == NULL)
4710dc2366fSVenugopal Iyer return (NULL);
4720dc2366fSVenugopal Iyer
4730dc2366fSVenugopal Iyer txn->dt_xid = xid;
4744d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime();
4750dc2366fSVenugopal Iyer if (cid_len > 0)
4760dc2366fSVenugopal Iyer bcopy(cid, &txn->dt_cid, cid_len);
4770dc2366fSVenugopal Iyer txn->dt_cid_len = cid_len;
4780dc2366fSVenugopal Iyer txn->dt_ipaddr = ipaddr;
4790dc2366fSVenugopal Iyer return (txn);
4800dc2366fSVenugopal Iyer }
4810dc2366fSVenugopal Iyer
4820dc2366fSVenugopal Iyer static void
free_dhcpv4_txn(dhcpv4_txn_t * txn)4830dc2366fSVenugopal Iyer free_dhcpv4_txn(dhcpv4_txn_t *txn)
4840dc2366fSVenugopal Iyer {
4850dc2366fSVenugopal Iyer kmem_free(txn, sizeof (*txn));
4860dc2366fSVenugopal Iyer }
4870dc2366fSVenugopal Iyer
4880dc2366fSVenugopal Iyer /*
4890dc2366fSVenugopal Iyer * Clean up all v4 tables.
4900dc2366fSVenugopal Iyer */
4910dc2366fSVenugopal Iyer static void
flush_dhcpv4(mac_client_impl_t * mcip)4920dc2366fSVenugopal Iyer flush_dhcpv4(mac_client_impl_t *mcip)
4930dc2366fSVenugopal Iyer {
4940dc2366fSVenugopal Iyer void *cookie = NULL;
4950dc2366fSVenugopal Iyer dhcpv4_txn_t *txn;
4960dc2366fSVenugopal Iyer
4970dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
4980dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v4_dyn_ip,
4990dc2366fSVenugopal Iyer &cookie)) != NULL) {
5000dc2366fSVenugopal Iyer /*
5010dc2366fSVenugopal Iyer * No freeing needed here because the same txn exists
5020dc2366fSVenugopal Iyer * in the mci_v4_completed_txn table as well.
5030dc2366fSVenugopal Iyer */
5040dc2366fSVenugopal Iyer }
5050dc2366fSVenugopal Iyer cookie = NULL;
5060dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v4_completed_txn,
5070dc2366fSVenugopal Iyer &cookie)) != NULL) {
5080dc2366fSVenugopal Iyer free_dhcpv4_txn(txn);
5090dc2366fSVenugopal Iyer }
5100dc2366fSVenugopal Iyer cookie = NULL;
5110dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v4_pending_txn,
5120dc2366fSVenugopal Iyer &cookie)) != NULL) {
5130dc2366fSVenugopal Iyer free_dhcpv4_txn(txn);
5140dc2366fSVenugopal Iyer }
5150dc2366fSVenugopal Iyer }
5160dc2366fSVenugopal Iyer
5170dc2366fSVenugopal Iyer /*
5180dc2366fSVenugopal Iyer * Cleanup stale DHCPv4 transactions.
5190dc2366fSVenugopal Iyer */
5200dc2366fSVenugopal Iyer static void
txn_cleanup_v4(mac_client_impl_t * mcip)5210dc2366fSVenugopal Iyer txn_cleanup_v4(mac_client_impl_t *mcip)
5220dc2366fSVenugopal Iyer {
5230dc2366fSVenugopal Iyer dhcpv4_txn_t *txn, *ctxn, *next, *txn_list = NULL;
5240dc2366fSVenugopal Iyer
5250dc2366fSVenugopal Iyer /*
5260dc2366fSVenugopal Iyer * Find stale pending transactions and place them on a list
5270dc2366fSVenugopal Iyer * to be removed.
5280dc2366fSVenugopal Iyer */
5290dc2366fSVenugopal Iyer for (txn = avl_first(&mcip->mci_v4_pending_txn); txn != NULL;
5300dc2366fSVenugopal Iyer txn = avl_walk(&mcip->mci_v4_pending_txn, txn, AVL_AFTER)) {
5314d6a58d3SJosef 'Jeff' Sipek if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) {
5320dc2366fSVenugopal Iyer DTRACE_PROBE2(found__expired__txn,
5330dc2366fSVenugopal Iyer mac_client_impl_t *, mcip,
5340dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
5350dc2366fSVenugopal Iyer
5360dc2366fSVenugopal Iyer txn->dt_next = txn_list;
5370dc2366fSVenugopal Iyer txn_list = txn;
5380dc2366fSVenugopal Iyer }
5390dc2366fSVenugopal Iyer }
5400dc2366fSVenugopal Iyer
5410dc2366fSVenugopal Iyer /*
5420dc2366fSVenugopal Iyer * Remove and free stale pending transactions and completed
5430dc2366fSVenugopal Iyer * transactions with the same client IDs as the stale transactions.
5440dc2366fSVenugopal Iyer */
5450dc2366fSVenugopal Iyer for (txn = txn_list; txn != NULL; txn = next) {
5460dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v4_pending_txn, txn);
5470dc2366fSVenugopal Iyer
5480dc2366fSVenugopal Iyer ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid,
5490dc2366fSVenugopal Iyer txn->dt_cid_len);
5500dc2366fSVenugopal Iyer if (ctxn != NULL) {
5510dc2366fSVenugopal Iyer DTRACE_PROBE2(removing__completed__txn,
5520dc2366fSVenugopal Iyer mac_client_impl_t *, mcip,
5530dc2366fSVenugopal Iyer dhcpv4_txn_t *, ctxn);
5540dc2366fSVenugopal Iyer
5550dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mcip, ctxn);
5560dc2366fSVenugopal Iyer free_dhcpv4_txn(ctxn);
5570dc2366fSVenugopal Iyer }
5580dc2366fSVenugopal Iyer next = txn->dt_next;
5590dc2366fSVenugopal Iyer txn->dt_next = NULL;
5600dc2366fSVenugopal Iyer
5610dc2366fSVenugopal Iyer DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
5620dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
5630dc2366fSVenugopal Iyer free_dhcpv4_txn(txn);
5640dc2366fSVenugopal Iyer }
5650dc2366fSVenugopal Iyer }
5660dc2366fSVenugopal Iyer
5670dc2366fSVenugopal Iyer /*
5680dc2366fSVenugopal Iyer * Core logic for intercepting outbound DHCPv4 packets.
5690dc2366fSVenugopal Iyer */
570550b6e40SSowmini Varadhan static boolean_t
intercept_dhcpv4_outbound(mac_client_impl_t * mcip,ipha_t * ipha,uchar_t * end)5710dc2366fSVenugopal Iyer intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end)
5720dc2366fSVenugopal Iyer {
573550b6e40SSowmini Varadhan struct dhcp *dh4;
574550b6e40SSowmini Varadhan uchar_t *opt;
575550b6e40SSowmini Varadhan dhcpv4_txn_t *txn, *ctxn;
576550b6e40SSowmini Varadhan ipaddr_t ipaddr;
577550b6e40SSowmini Varadhan uint8_t opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len;
578550b6e40SSowmini Varadhan mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
5790dc2366fSVenugopal Iyer
5800dc2366fSVenugopal Iyer if (get_dhcpv4_info(ipha, end, &dh4) != 0)
581550b6e40SSowmini Varadhan return (B_TRUE);
582550b6e40SSowmini Varadhan
583550b6e40SSowmini Varadhan /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
584550b6e40SSowmini Varadhan if (allowed_ips_set(mrp, IPV4_VERSION))
585550b6e40SSowmini Varadhan return (B_FALSE);
5860dc2366fSVenugopal Iyer
5870dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 ||
5880dc2366fSVenugopal Iyer opt_len != 1) {
5890dc2366fSVenugopal Iyer DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip,
5900dc2366fSVenugopal Iyer struct dhcp *, dh4);
591550b6e40SSowmini Varadhan return (B_TRUE);
5920dc2366fSVenugopal Iyer }
5930dc2366fSVenugopal Iyer mtype = *opt;
5940dc2366fSVenugopal Iyer if (mtype != REQUEST && mtype != RELEASE) {
5950dc2366fSVenugopal Iyer DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip,
5960dc2366fSVenugopal Iyer struct dhcp *, dh4, uint8_t, mtype);
597550b6e40SSowmini Varadhan return (B_TRUE);
5980dc2366fSVenugopal Iyer }
5990dc2366fSVenugopal Iyer
6000dc2366fSVenugopal Iyer /* client ID is optional for IPv4 */
6010dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &opt, &opt_len) == 0 &&
6020dc2366fSVenugopal Iyer opt_len >= 2) {
6030dc2366fSVenugopal Iyer bcopy(opt, cid, opt_len);
6040dc2366fSVenugopal Iyer cid_len = opt_len;
6050dc2366fSVenugopal Iyer } else {
6060dc2366fSVenugopal Iyer bzero(cid, DHCP_MAX_OPT_SIZE);
6070dc2366fSVenugopal Iyer cid_len = 0;
6080dc2366fSVenugopal Iyer }
6090dc2366fSVenugopal Iyer
6100dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
6110dc2366fSVenugopal Iyer if (mtype == RELEASE) {
6120dc2366fSVenugopal Iyer DTRACE_PROBE2(release, mac_client_impl_t *, mcip,
6130dc2366fSVenugopal Iyer struct dhcp *, dh4);
6140dc2366fSVenugopal Iyer
6150dc2366fSVenugopal Iyer /* flush any completed txn with this cid */
6160dc2366fSVenugopal Iyer ctxn = find_dhcpv4_completed_txn(mcip, cid, cid_len);
6170dc2366fSVenugopal Iyer if (ctxn != NULL) {
6180dc2366fSVenugopal Iyer DTRACE_PROBE2(release__successful, mac_client_impl_t *,
6190dc2366fSVenugopal Iyer mcip, struct dhcp *, dh4);
6200dc2366fSVenugopal Iyer
6210dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mcip, ctxn);
6220dc2366fSVenugopal Iyer free_dhcpv4_txn(ctxn);
6230dc2366fSVenugopal Iyer }
6240dc2366fSVenugopal Iyer goto done;
6250dc2366fSVenugopal Iyer }
6260dc2366fSVenugopal Iyer
6270dc2366fSVenugopal Iyer /*
6280dc2366fSVenugopal Iyer * If a pending txn already exists, we'll update its timestamp so
6290dc2366fSVenugopal Iyer * it won't get flushed by the timer. We don't need to create new
6300dc2366fSVenugopal Iyer * txns for retransmissions.
6310dc2366fSVenugopal Iyer */
6320dc2366fSVenugopal Iyer if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) != NULL) {
6330dc2366fSVenugopal Iyer DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
6340dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
6354d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime();
6360dc2366fSVenugopal Iyer goto done;
6370dc2366fSVenugopal Iyer }
6380dc2366fSVenugopal Iyer
6390dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_REQUESTED_IP_ADDR,
6400dc2366fSVenugopal Iyer &opt, &opt_len) != 0 || opt_len != sizeof (ipaddr)) {
6410dc2366fSVenugopal Iyer DTRACE_PROBE2(ipaddr__not__found, mac_client_impl_t *, mcip,
6420dc2366fSVenugopal Iyer struct dhcp *, dh4);
6430dc2366fSVenugopal Iyer goto done;
6440dc2366fSVenugopal Iyer }
6450dc2366fSVenugopal Iyer bcopy(opt, &ipaddr, sizeof (ipaddr));
6460dc2366fSVenugopal Iyer if ((txn = create_dhcpv4_txn(dh4->xid, cid, cid_len, ipaddr)) == NULL)
6470dc2366fSVenugopal Iyer goto done;
6480dc2366fSVenugopal Iyer
6490dc2366fSVenugopal Iyer if (insert_dhcpv4_pending_txn(mcip, txn) != 0) {
6500dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
6510dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
6520dc2366fSVenugopal Iyer free_dhcpv4_txn(txn);
6530dc2366fSVenugopal Iyer goto done;
6540dc2366fSVenugopal Iyer }
6550dc2366fSVenugopal Iyer start_txn_cleanup_timer(mcip);
6560dc2366fSVenugopal Iyer
6570dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
6580dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
6590dc2366fSVenugopal Iyer
6600dc2366fSVenugopal Iyer done:
6610dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
662550b6e40SSowmini Varadhan return (B_TRUE);
6630dc2366fSVenugopal Iyer }
6640dc2366fSVenugopal Iyer
6650dc2366fSVenugopal Iyer /*
6660dc2366fSVenugopal Iyer * Core logic for intercepting inbound DHCPv4 packets.
6670dc2366fSVenugopal Iyer */
6680dc2366fSVenugopal Iyer static void
intercept_dhcpv4_inbound(mac_client_impl_t * mcip,uchar_t * end,struct dhcp * dh4)669797f979dSCody Peter Mello intercept_dhcpv4_inbound(mac_client_impl_t *mcip, uchar_t *end,
670797f979dSCody Peter Mello struct dhcp *dh4)
6710dc2366fSVenugopal Iyer {
6720dc2366fSVenugopal Iyer uchar_t *opt;
6730dc2366fSVenugopal Iyer dhcpv4_txn_t *txn, *ctxn;
6740dc2366fSVenugopal Iyer uint8_t opt_len, mtype;
6750dc2366fSVenugopal Iyer
6760dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 ||
6770dc2366fSVenugopal Iyer opt_len != 1) {
6780dc2366fSVenugopal Iyer DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip,
6790dc2366fSVenugopal Iyer struct dhcp *, dh4);
6800dc2366fSVenugopal Iyer return;
6810dc2366fSVenugopal Iyer }
6820dc2366fSVenugopal Iyer mtype = *opt;
6830dc2366fSVenugopal Iyer if (mtype != ACK && mtype != NAK) {
6840dc2366fSVenugopal Iyer DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip,
6850dc2366fSVenugopal Iyer struct dhcp *, dh4, uint8_t, mtype);
6860dc2366fSVenugopal Iyer return;
6870dc2366fSVenugopal Iyer }
6880dc2366fSVenugopal Iyer
6890dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
6900dc2366fSVenugopal Iyer if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) == NULL) {
6910dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip,
6920dc2366fSVenugopal Iyer struct dhcp *, dh4);
6930dc2366fSVenugopal Iyer goto done;
6940dc2366fSVenugopal Iyer }
6950dc2366fSVenugopal Iyer remove_dhcpv4_pending_txn(mcip, txn);
6960dc2366fSVenugopal Iyer
6970dc2366fSVenugopal Iyer /*
6980dc2366fSVenugopal Iyer * We're about to move a txn from the pending table to the completed/
6990dc2366fSVenugopal Iyer * dyn-ip tables. If there is an existing completed txn with the
7000dc2366fSVenugopal Iyer * same cid as our txn, we need to remove and free it.
7010dc2366fSVenugopal Iyer */
7020dc2366fSVenugopal Iyer ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid, txn->dt_cid_len);
7030dc2366fSVenugopal Iyer if (ctxn != NULL) {
7040dc2366fSVenugopal Iyer DTRACE_PROBE2(replacing__old__txn, mac_client_impl_t *, mcip,
7050dc2366fSVenugopal Iyer dhcpv4_txn_t *, ctxn);
7060dc2366fSVenugopal Iyer remove_dhcpv4_completed_txn(mcip, ctxn);
7070dc2366fSVenugopal Iyer free_dhcpv4_txn(ctxn);
7080dc2366fSVenugopal Iyer }
7090dc2366fSVenugopal Iyer if (mtype == NAK) {
7100dc2366fSVenugopal Iyer DTRACE_PROBE2(nak__received, mac_client_impl_t *, mcip,
7110dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
7120dc2366fSVenugopal Iyer free_dhcpv4_txn(txn);
7130dc2366fSVenugopal Iyer goto done;
7140dc2366fSVenugopal Iyer }
7150dc2366fSVenugopal Iyer if (insert_dhcpv4_completed_txn(mcip, txn) != 0) {
7160dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
7170dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
7180dc2366fSVenugopal Iyer free_dhcpv4_txn(txn);
7190dc2366fSVenugopal Iyer goto done;
7200dc2366fSVenugopal Iyer }
7210dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip,
7220dc2366fSVenugopal Iyer dhcpv4_txn_t *, txn);
7230dc2366fSVenugopal Iyer
7240dc2366fSVenugopal Iyer done:
7250dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
7260dc2366fSVenugopal Iyer }
7270dc2366fSVenugopal Iyer
7280dc2366fSVenugopal Iyer
7290dc2366fSVenugopal Iyer /*
7300dc2366fSVenugopal Iyer * Comparison functions for the DHCPv6 AVL trees.
7310dc2366fSVenugopal Iyer */
7320dc2366fSVenugopal Iyer static int
compare_dhcpv6_xid(const void * arg1,const void * arg2)7330dc2366fSVenugopal Iyer compare_dhcpv6_xid(const void *arg1, const void *arg2)
7340dc2366fSVenugopal Iyer {
7350dc2366fSVenugopal Iyer const dhcpv6_txn_t *txn1 = arg1, *txn2 = arg2;
7360dc2366fSVenugopal Iyer
7370dc2366fSVenugopal Iyer if (txn1->dt_xid < txn2->dt_xid)
7380dc2366fSVenugopal Iyer return (-1);
7390dc2366fSVenugopal Iyer else if (txn1->dt_xid > txn2->dt_xid)
7400dc2366fSVenugopal Iyer return (1);
7410dc2366fSVenugopal Iyer else
7420dc2366fSVenugopal Iyer return (0);
7430dc2366fSVenugopal Iyer }
7440dc2366fSVenugopal Iyer
7450dc2366fSVenugopal Iyer static int
compare_dhcpv6_ip(const void * arg1,const void * arg2)7460dc2366fSVenugopal Iyer compare_dhcpv6_ip(const void *arg1, const void *arg2)
7470dc2366fSVenugopal Iyer {
7480dc2366fSVenugopal Iyer const dhcpv6_addr_t *ip1 = arg1, *ip2 = arg2;
7490dc2366fSVenugopal Iyer int ret;
7500dc2366fSVenugopal Iyer
7510dc2366fSVenugopal Iyer ret = memcmp(&ip1->da_addr, &ip2->da_addr, sizeof (in6_addr_t));
7520dc2366fSVenugopal Iyer if (ret < 0)
7530dc2366fSVenugopal Iyer return (-1);
7540dc2366fSVenugopal Iyer else if (ret > 0)
7550dc2366fSVenugopal Iyer return (1);
7560dc2366fSVenugopal Iyer else
7570dc2366fSVenugopal Iyer return (0);
7580dc2366fSVenugopal Iyer }
7590dc2366fSVenugopal Iyer
7600dc2366fSVenugopal Iyer static int
compare_dhcpv6_cid(const void * arg1,const void * arg2)7610dc2366fSVenugopal Iyer compare_dhcpv6_cid(const void *arg1, const void *arg2)
7620dc2366fSVenugopal Iyer {
7630dc2366fSVenugopal Iyer const dhcpv6_cid_t *cid1 = arg1, *cid2 = arg2;
7640dc2366fSVenugopal Iyer int ret;
7650dc2366fSVenugopal Iyer
7660dc2366fSVenugopal Iyer if (cid1->dc_cid_len < cid2->dc_cid_len)
7670dc2366fSVenugopal Iyer return (-1);
7680dc2366fSVenugopal Iyer else if (cid1->dc_cid_len > cid2->dc_cid_len)
7690dc2366fSVenugopal Iyer return (1);
7700dc2366fSVenugopal Iyer
7710dc2366fSVenugopal Iyer if (cid1->dc_cid_len == 0)
7720dc2366fSVenugopal Iyer return (0);
7730dc2366fSVenugopal Iyer
7740dc2366fSVenugopal Iyer ret = memcmp(cid1->dc_cid, cid2->dc_cid, cid1->dc_cid_len);
7750dc2366fSVenugopal Iyer if (ret < 0)
7760dc2366fSVenugopal Iyer return (-1);
7770dc2366fSVenugopal Iyer else if (ret > 0)
7780dc2366fSVenugopal Iyer return (1);
7790dc2366fSVenugopal Iyer else
7800dc2366fSVenugopal Iyer return (0);
7810dc2366fSVenugopal Iyer }
7820dc2366fSVenugopal Iyer
783797f979dSCody Peter Mello static int
compare_slaac_ip(const void * arg1,const void * arg2)784797f979dSCody Peter Mello compare_slaac_ip(const void *arg1, const void *arg2)
785797f979dSCody Peter Mello {
786797f979dSCody Peter Mello const slaac_addr_t *ip1 = arg1, *ip2 = arg2;
787797f979dSCody Peter Mello int ret;
788797f979dSCody Peter Mello
789797f979dSCody Peter Mello ret = memcmp(&ip1->sla_addr, &ip2->sla_addr, sizeof (in6_addr_t));
790797f979dSCody Peter Mello if (ret < 0)
791797f979dSCody Peter Mello return (-1);
792797f979dSCody Peter Mello else if (ret > 0)
793797f979dSCody Peter Mello return (1);
794797f979dSCody Peter Mello else
795797f979dSCody Peter Mello return (0);
796797f979dSCody Peter Mello }
797797f979dSCody Peter Mello
7980dc2366fSVenugopal Iyer /*
7990dc2366fSVenugopal Iyer * Locate the start of a DHCPv6 header.
8000dc2366fSVenugopal Iyer * The possible return values and associated meanings are:
8010dc2366fSVenugopal Iyer * 0 - packet is DHCP and has a DHCP header.
8020dc2366fSVenugopal Iyer * EINVAL - packet is not DHCP. the recommended action is to let it pass.
8030dc2366fSVenugopal Iyer * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable.
8040dc2366fSVenugopal Iyer * the recommended action is to drop it.
8050dc2366fSVenugopal Iyer */
8060dc2366fSVenugopal Iyer static int
get_dhcpv6_info(ip6_t * ip6h,uchar_t * end,dhcpv6_message_t ** dh6)8070dc2366fSVenugopal Iyer get_dhcpv6_info(ip6_t *ip6h, uchar_t *end, dhcpv6_message_t **dh6)
8080dc2366fSVenugopal Iyer {
8090dc2366fSVenugopal Iyer uint16_t hdrlen, client, server;
8100dc2366fSVenugopal Iyer boolean_t first_frag = B_FALSE;
8110dc2366fSVenugopal Iyer ip6_frag_t *frag = NULL;
8120dc2366fSVenugopal Iyer uint8_t proto;
8130dc2366fSVenugopal Iyer struct udphdr *udph;
8140dc2366fSVenugopal Iyer uchar_t *dh;
8150dc2366fSVenugopal Iyer
8160dc2366fSVenugopal Iyer if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag))
8170dc2366fSVenugopal Iyer return (ENOSPC);
8180dc2366fSVenugopal Iyer
8190dc2366fSVenugopal Iyer if (proto != IPPROTO_UDP)
8200dc2366fSVenugopal Iyer return (EINVAL);
8210dc2366fSVenugopal Iyer
8220dc2366fSVenugopal Iyer if (frag != NULL) {
8230dc2366fSVenugopal Iyer /*
8240dc2366fSVenugopal Iyer * All non-initial fragments may pass because we cannot
8250dc2366fSVenugopal Iyer * identify their type. It's safe to let them through
8260dc2366fSVenugopal Iyer * because reassembly will fail if we decide to drop the
8270dc2366fSVenugopal Iyer * initial fragment.
8280dc2366fSVenugopal Iyer */
8290dc2366fSVenugopal Iyer if ((ntohs(frag->ip6f_offlg) & ~7) != 0)
8300dc2366fSVenugopal Iyer return (EINVAL);
8310dc2366fSVenugopal Iyer first_frag = B_TRUE;
8320dc2366fSVenugopal Iyer }
8330dc2366fSVenugopal Iyer /* drop packets without a udp header */
8340dc2366fSVenugopal Iyer udph = (struct udphdr *)((uchar_t *)ip6h + hdrlen);
8350dc2366fSVenugopal Iyer if ((uchar_t *)&udph[1] > end)
8360dc2366fSVenugopal Iyer return (ENOSPC);
8370dc2366fSVenugopal Iyer
8380dc2366fSVenugopal Iyer client = htons(IPPORT_DHCPV6C);
8390dc2366fSVenugopal Iyer server = htons(IPPORT_DHCPV6S);
8400dc2366fSVenugopal Iyer if (udph->uh_sport != client && udph->uh_sport != server &&
8410dc2366fSVenugopal Iyer udph->uh_dport != client && udph->uh_dport != server)
8420dc2366fSVenugopal Iyer return (EINVAL);
8430dc2366fSVenugopal Iyer
8440dc2366fSVenugopal Iyer /* drop dhcp fragments */
8450dc2366fSVenugopal Iyer if (first_frag)
8460dc2366fSVenugopal Iyer return (ENOSPC);
8470dc2366fSVenugopal Iyer
8480dc2366fSVenugopal Iyer dh = (uchar_t *)&udph[1];
8490dc2366fSVenugopal Iyer if (dh + sizeof (dhcpv6_message_t) > end)
8500dc2366fSVenugopal Iyer return (EINVAL);
8510dc2366fSVenugopal Iyer
8520dc2366fSVenugopal Iyer *dh6 = (dhcpv6_message_t *)dh;
8530dc2366fSVenugopal Iyer return (0);
8540dc2366fSVenugopal Iyer }
8550dc2366fSVenugopal Iyer
856797f979dSCody Peter Mello static int
get_ra_info(ip6_t * ip6h,uchar_t * end,nd_router_advert_t ** ra)857797f979dSCody Peter Mello get_ra_info(ip6_t *ip6h, uchar_t *end, nd_router_advert_t **ra)
858797f979dSCody Peter Mello {
859797f979dSCody Peter Mello uint16_t hdrlen;
860797f979dSCody Peter Mello ip6_frag_t *frag = NULL;
861797f979dSCody Peter Mello uint8_t proto;
862797f979dSCody Peter Mello uchar_t *hdrp;
863797f979dSCody Peter Mello struct icmp6_hdr *icmp;
864797f979dSCody Peter Mello
865797f979dSCody Peter Mello if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag))
866797f979dSCody Peter Mello return (ENOSPC);
867797f979dSCody Peter Mello
868797f979dSCody Peter Mello if (proto != IPPROTO_ICMPV6)
869797f979dSCody Peter Mello return (EINVAL);
870797f979dSCody Peter Mello
871797f979dSCody Peter Mello if (frag != NULL) {
872797f979dSCody Peter Mello /*
873797f979dSCody Peter Mello * All non-initial fragments may pass because we cannot
874797f979dSCody Peter Mello * identify their type. It's safe to let them through
875797f979dSCody Peter Mello * because reassembly will fail if we decide to drop the
876797f979dSCody Peter Mello * initial fragment.
877797f979dSCody Peter Mello */
878797f979dSCody Peter Mello if ((ntohs(frag->ip6f_offlg) & ~7) != 0)
879797f979dSCody Peter Mello return (EINVAL);
880797f979dSCody Peter Mello return (ENOSPC);
881797f979dSCody Peter Mello }
882797f979dSCody Peter Mello
883797f979dSCody Peter Mello /*
884797f979dSCody Peter Mello * Ensure that the ICMP header falls w/in packet boundaries, in case
885797f979dSCody Peter Mello * we've received a malicious packet that reports incorrect lengths.
886797f979dSCody Peter Mello */
887797f979dSCody Peter Mello hdrp = (uchar_t *)ip6h + hdrlen;
888797f979dSCody Peter Mello if ((hdrp + sizeof (struct icmp6_hdr)) > end) {
889797f979dSCody Peter Mello return (EINVAL);
890797f979dSCody Peter Mello }
891797f979dSCody Peter Mello icmp = (struct icmp6_hdr *)hdrp;
892797f979dSCody Peter Mello
893797f979dSCody Peter Mello if (icmp->icmp6_type != ND_ROUTER_ADVERT ||
894797f979dSCody Peter Mello icmp->icmp6_code != 0)
895797f979dSCody Peter Mello return (EINVAL);
896797f979dSCody Peter Mello
897797f979dSCody Peter Mello *ra = (nd_router_advert_t *)icmp;
898797f979dSCody Peter Mello return (0);
899797f979dSCody Peter Mello }
900797f979dSCody Peter Mello
9010dc2366fSVenugopal Iyer /*
9020dc2366fSVenugopal Iyer * Find the specified DHCPv6 option.
9030dc2366fSVenugopal Iyer */
9040dc2366fSVenugopal Iyer static dhcpv6_option_t *
get_dhcpv6_option(void * buf,size_t buflen,dhcpv6_option_t * oldopt,uint16_t codenum,uint_t * retlenp)9050dc2366fSVenugopal Iyer get_dhcpv6_option(void *buf, size_t buflen, dhcpv6_option_t *oldopt,
9060dc2366fSVenugopal Iyer uint16_t codenum, uint_t *retlenp)
9070dc2366fSVenugopal Iyer {
9080dc2366fSVenugopal Iyer uchar_t *bp;
9090dc2366fSVenugopal Iyer dhcpv6_option_t d6o;
9100dc2366fSVenugopal Iyer uint_t olen;
9110dc2366fSVenugopal Iyer
9120dc2366fSVenugopal Iyer codenum = htons(codenum);
9130dc2366fSVenugopal Iyer bp = buf;
9140dc2366fSVenugopal Iyer while (buflen >= sizeof (dhcpv6_option_t)) {
9150dc2366fSVenugopal Iyer bcopy(bp, &d6o, sizeof (d6o));
9160dc2366fSVenugopal Iyer olen = ntohs(d6o.d6o_len) + sizeof (d6o);
9170dc2366fSVenugopal Iyer if (olen > buflen)
9180dc2366fSVenugopal Iyer break;
9190dc2366fSVenugopal Iyer if (d6o.d6o_code != codenum || d6o.d6o_len == 0 ||
9200dc2366fSVenugopal Iyer (oldopt != NULL && bp <= (uchar_t *)oldopt)) {
9210dc2366fSVenugopal Iyer bp += olen;
9220dc2366fSVenugopal Iyer buflen -= olen;
9230dc2366fSVenugopal Iyer continue;
9240dc2366fSVenugopal Iyer }
9250dc2366fSVenugopal Iyer if (retlenp != NULL)
9260dc2366fSVenugopal Iyer *retlenp = olen;
9270dc2366fSVenugopal Iyer /* LINTED : alignment */
9280dc2366fSVenugopal Iyer return ((dhcpv6_option_t *)bp);
9290dc2366fSVenugopal Iyer }
9300dc2366fSVenugopal Iyer return (NULL);
9310dc2366fSVenugopal Iyer }
9320dc2366fSVenugopal Iyer
9330dc2366fSVenugopal Iyer /*
9340dc2366fSVenugopal Iyer * Get the status code from a reply message.
9350dc2366fSVenugopal Iyer */
9360dc2366fSVenugopal Iyer static int
get_dhcpv6_status(dhcpv6_message_t * dh6,uchar_t * end,uint16_t * status)9370dc2366fSVenugopal Iyer get_dhcpv6_status(dhcpv6_message_t *dh6, uchar_t *end, uint16_t *status)
9380dc2366fSVenugopal Iyer {
9390dc2366fSVenugopal Iyer dhcpv6_option_t *d6o;
9400dc2366fSVenugopal Iyer uint_t olen;
9410dc2366fSVenugopal Iyer uint16_t s;
9420dc2366fSVenugopal Iyer
9430dc2366fSVenugopal Iyer d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
9440dc2366fSVenugopal Iyer DHCPV6_OPT_STATUS_CODE, &olen);
9450dc2366fSVenugopal Iyer
9460dc2366fSVenugopal Iyer /* Success is implied if status code is missing */
9470dc2366fSVenugopal Iyer if (d6o == NULL) {
9480dc2366fSVenugopal Iyer *status = DHCPV6_STAT_SUCCESS;
9490dc2366fSVenugopal Iyer return (0);
9500dc2366fSVenugopal Iyer }
9510dc2366fSVenugopal Iyer if ((uchar_t *)d6o + olen > end)
9520dc2366fSVenugopal Iyer return (EINVAL);
9530dc2366fSVenugopal Iyer
9540dc2366fSVenugopal Iyer olen -= sizeof (*d6o);
9550dc2366fSVenugopal Iyer if (olen < sizeof (s))
9560dc2366fSVenugopal Iyer return (EINVAL);
9570dc2366fSVenugopal Iyer
9580dc2366fSVenugopal Iyer bcopy(&d6o[1], &s, sizeof (s));
9590dc2366fSVenugopal Iyer *status = ntohs(s);
9600dc2366fSVenugopal Iyer return (0);
9610dc2366fSVenugopal Iyer }
9620dc2366fSVenugopal Iyer
9630dc2366fSVenugopal Iyer /*
9640dc2366fSVenugopal Iyer * Get the addresses from a reply message.
9650dc2366fSVenugopal Iyer */
9660dc2366fSVenugopal Iyer static int
get_dhcpv6_addrs(dhcpv6_message_t * dh6,uchar_t * end,dhcpv6_cid_t * cid)9670dc2366fSVenugopal Iyer get_dhcpv6_addrs(dhcpv6_message_t *dh6, uchar_t *end, dhcpv6_cid_t *cid)
9680dc2366fSVenugopal Iyer {
9690dc2366fSVenugopal Iyer dhcpv6_option_t *d6o;
9700dc2366fSVenugopal Iyer dhcpv6_addr_t *next;
9710dc2366fSVenugopal Iyer uint_t olen;
9720dc2366fSVenugopal Iyer
9730dc2366fSVenugopal Iyer d6o = NULL;
9740dc2366fSVenugopal Iyer while ((d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1],
9750dc2366fSVenugopal Iyer d6o, DHCPV6_OPT_IA_NA, &olen)) != NULL) {
9760dc2366fSVenugopal Iyer dhcpv6_option_t *d6so;
9770dc2366fSVenugopal Iyer dhcpv6_iaaddr_t d6ia;
9780dc2366fSVenugopal Iyer dhcpv6_addr_t **addrp;
9790dc2366fSVenugopal Iyer uchar_t *obase;
9800dc2366fSVenugopal Iyer uint_t solen;
9810dc2366fSVenugopal Iyer
9820dc2366fSVenugopal Iyer if (olen < sizeof (dhcpv6_ia_na_t) ||
9830dc2366fSVenugopal Iyer (uchar_t *)d6o + olen > end)
9840dc2366fSVenugopal Iyer goto fail;
9850dc2366fSVenugopal Iyer
9860dc2366fSVenugopal Iyer obase = (uchar_t *)d6o + sizeof (dhcpv6_ia_na_t);
9870dc2366fSVenugopal Iyer olen -= sizeof (dhcpv6_ia_na_t);
9880dc2366fSVenugopal Iyer d6so = NULL;
9890dc2366fSVenugopal Iyer while ((d6so = get_dhcpv6_option(obase, olen, d6so,
9900dc2366fSVenugopal Iyer DHCPV6_OPT_IAADDR, &solen)) != NULL) {
9910dc2366fSVenugopal Iyer if (solen < sizeof (dhcpv6_iaaddr_t) ||
9920dc2366fSVenugopal Iyer (uchar_t *)d6so + solen > end)
9930dc2366fSVenugopal Iyer goto fail;
9940dc2366fSVenugopal Iyer
9950dc2366fSVenugopal Iyer bcopy(d6so, &d6ia, sizeof (d6ia));
9960dc2366fSVenugopal Iyer for (addrp = &cid->dc_addr; *addrp != NULL;
9970dc2366fSVenugopal Iyer addrp = &(*addrp)->da_next) {
9980dc2366fSVenugopal Iyer if (bcmp(&(*addrp)->da_addr, &d6ia.d6ia_addr,
9990dc2366fSVenugopal Iyer sizeof (in6_addr_t)) == 0)
10000dc2366fSVenugopal Iyer goto fail;
10010dc2366fSVenugopal Iyer }
10020dc2366fSVenugopal Iyer if ((*addrp = kmem_zalloc(sizeof (dhcpv6_addr_t),
10030dc2366fSVenugopal Iyer KM_NOSLEEP)) == NULL)
10040dc2366fSVenugopal Iyer goto fail;
10050dc2366fSVenugopal Iyer
10060dc2366fSVenugopal Iyer bcopy(&d6ia.d6ia_addr, &(*addrp)->da_addr,
10070dc2366fSVenugopal Iyer sizeof (in6_addr_t));
10080dc2366fSVenugopal Iyer cid->dc_addrcnt++;
10090dc2366fSVenugopal Iyer }
10100dc2366fSVenugopal Iyer }
10110dc2366fSVenugopal Iyer if (cid->dc_addrcnt == 0)
10120dc2366fSVenugopal Iyer return (ENOENT);
10130dc2366fSVenugopal Iyer
10140dc2366fSVenugopal Iyer return (0);
10150dc2366fSVenugopal Iyer
10160dc2366fSVenugopal Iyer fail:
10170dc2366fSVenugopal Iyer for (; cid->dc_addr != NULL; cid->dc_addr = next) {
10180dc2366fSVenugopal Iyer next = cid->dc_addr->da_next;
10190dc2366fSVenugopal Iyer kmem_free(cid->dc_addr, sizeof (dhcpv6_addr_t));
10200dc2366fSVenugopal Iyer cid->dc_addrcnt--;
10210dc2366fSVenugopal Iyer }
10220dc2366fSVenugopal Iyer ASSERT(cid->dc_addrcnt == 0);
10230dc2366fSVenugopal Iyer return (EINVAL);
10240dc2366fSVenugopal Iyer }
10250dc2366fSVenugopal Iyer
10260dc2366fSVenugopal Iyer /*
10270dc2366fSVenugopal Iyer * Free a cid.
10280dc2366fSVenugopal Iyer * Before this gets called the caller must ensure that all the
10290dc2366fSVenugopal Iyer * addresses are removed from the mci_v6_dyn_ip table.
10300dc2366fSVenugopal Iyer */
10310dc2366fSVenugopal Iyer static void
free_dhcpv6_cid(dhcpv6_cid_t * cid)10320dc2366fSVenugopal Iyer free_dhcpv6_cid(dhcpv6_cid_t *cid)
10330dc2366fSVenugopal Iyer {
10340dc2366fSVenugopal Iyer dhcpv6_addr_t *addr, *next;
10350dc2366fSVenugopal Iyer uint_t cnt = 0;
10360dc2366fSVenugopal Iyer
10370dc2366fSVenugopal Iyer kmem_free(cid->dc_cid, cid->dc_cid_len);
10380dc2366fSVenugopal Iyer for (addr = cid->dc_addr; addr != NULL; addr = next) {
10390dc2366fSVenugopal Iyer next = addr->da_next;
10400dc2366fSVenugopal Iyer kmem_free(addr, sizeof (*addr));
10410dc2366fSVenugopal Iyer cnt++;
10420dc2366fSVenugopal Iyer }
10430dc2366fSVenugopal Iyer ASSERT(cnt == cid->dc_addrcnt);
10440dc2366fSVenugopal Iyer kmem_free(cid, sizeof (*cid));
10450dc2366fSVenugopal Iyer }
10460dc2366fSVenugopal Iyer
10470dc2366fSVenugopal Iyer /*
10480dc2366fSVenugopal Iyer * Extract the DUID from a message. The associated addresses will be
10490dc2366fSVenugopal Iyer * extracted later from the reply message.
10500dc2366fSVenugopal Iyer */
10510dc2366fSVenugopal Iyer static dhcpv6_cid_t *
create_dhcpv6_cid(dhcpv6_message_t * dh6,uchar_t * end)10520dc2366fSVenugopal Iyer create_dhcpv6_cid(dhcpv6_message_t *dh6, uchar_t *end)
10530dc2366fSVenugopal Iyer {
10540dc2366fSVenugopal Iyer dhcpv6_option_t *d6o;
10550dc2366fSVenugopal Iyer dhcpv6_cid_t *cid;
10560dc2366fSVenugopal Iyer uchar_t *rawcid;
10570dc2366fSVenugopal Iyer uint_t olen, rawcidlen;
10580dc2366fSVenugopal Iyer
10590dc2366fSVenugopal Iyer d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
10600dc2366fSVenugopal Iyer DHCPV6_OPT_CLIENTID, &olen);
10610dc2366fSVenugopal Iyer if (d6o == NULL || (uchar_t *)d6o + olen > end)
10620dc2366fSVenugopal Iyer return (NULL);
10630dc2366fSVenugopal Iyer
10640dc2366fSVenugopal Iyer rawcidlen = olen - sizeof (*d6o);
10650dc2366fSVenugopal Iyer if ((rawcid = kmem_zalloc(rawcidlen, KM_NOSLEEP)) == NULL)
10660dc2366fSVenugopal Iyer return (NULL);
10670dc2366fSVenugopal Iyer bcopy(d6o + 1, rawcid, rawcidlen);
10680dc2366fSVenugopal Iyer
10690dc2366fSVenugopal Iyer if ((cid = kmem_zalloc(sizeof (*cid), KM_NOSLEEP)) == NULL) {
10700dc2366fSVenugopal Iyer kmem_free(rawcid, rawcidlen);
10710dc2366fSVenugopal Iyer return (NULL);
10720dc2366fSVenugopal Iyer }
10730dc2366fSVenugopal Iyer cid->dc_cid = rawcid;
10740dc2366fSVenugopal Iyer cid->dc_cid_len = rawcidlen;
10750dc2366fSVenugopal Iyer return (cid);
10760dc2366fSVenugopal Iyer }
10770dc2366fSVenugopal Iyer
10780dc2366fSVenugopal Iyer /*
10790dc2366fSVenugopal Iyer * Remove a cid from mci_v6_cid. The addresses owned by the cid
10800dc2366fSVenugopal Iyer * are also removed from mci_v6_dyn_ip.
10810dc2366fSVenugopal Iyer */
10820dc2366fSVenugopal Iyer static void
remove_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)10830dc2366fSVenugopal Iyer remove_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
10840dc2366fSVenugopal Iyer {
10850dc2366fSVenugopal Iyer dhcpv6_addr_t *addr, *tmp_addr;
10860dc2366fSVenugopal Iyer
10870dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
10880dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_cid, cid);
10890dc2366fSVenugopal Iyer for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) {
10900dc2366fSVenugopal Iyer tmp_addr = avl_find(&mcip->mci_v6_dyn_ip, addr, NULL);
10910dc2366fSVenugopal Iyer if (tmp_addr == addr)
10920dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_dyn_ip, addr);
10930dc2366fSVenugopal Iyer }
10940dc2366fSVenugopal Iyer }
10950dc2366fSVenugopal Iyer
10960dc2366fSVenugopal Iyer /*
10970dc2366fSVenugopal Iyer * Find and remove a matching cid and associated addresses from
10980dc2366fSVenugopal Iyer * their respective tables.
10990dc2366fSVenugopal Iyer */
11000dc2366fSVenugopal Iyer static void
release_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)11010dc2366fSVenugopal Iyer release_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
11020dc2366fSVenugopal Iyer {
11030dc2366fSVenugopal Iyer dhcpv6_cid_t *oldcid;
11040dc2366fSVenugopal Iyer
11050dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
11060dc2366fSVenugopal Iyer if ((oldcid = avl_find(&mcip->mci_v6_cid, cid, NULL)) == NULL)
11070dc2366fSVenugopal Iyer return;
11080dc2366fSVenugopal Iyer
11090dc2366fSVenugopal Iyer /*
11100dc2366fSVenugopal Iyer * Since cid belongs to a pending txn, it can't possibly be in
11110dc2366fSVenugopal Iyer * mci_v6_cid. Anything that's found must be an existing cid.
11120dc2366fSVenugopal Iyer */
11130dc2366fSVenugopal Iyer ASSERT(oldcid != cid);
11140dc2366fSVenugopal Iyer remove_dhcpv6_cid(mcip, oldcid);
11150dc2366fSVenugopal Iyer free_dhcpv6_cid(oldcid);
11160dc2366fSVenugopal Iyer }
11170dc2366fSVenugopal Iyer
11180dc2366fSVenugopal Iyer /*
11190dc2366fSVenugopal Iyer * Insert cid into mci_v6_cid.
11200dc2366fSVenugopal Iyer */
11210dc2366fSVenugopal Iyer static int
insert_dhcpv6_cid(mac_client_impl_t * mcip,dhcpv6_cid_t * cid)11220dc2366fSVenugopal Iyer insert_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
11230dc2366fSVenugopal Iyer {
11240dc2366fSVenugopal Iyer avl_index_t where;
11250dc2366fSVenugopal Iyer dhcpv6_addr_t *addr;
11260dc2366fSVenugopal Iyer
11270dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
11280dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v6_cid, cid, &where) != NULL)
11290dc2366fSVenugopal Iyer return (EEXIST);
11300dc2366fSVenugopal Iyer
11310dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v6_cid) >= dhcp_max_completed_txn) {
11320dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped);
11330dc2366fSVenugopal Iyer return (EAGAIN);
11340dc2366fSVenugopal Iyer }
11350dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v6_cid, cid, where);
11360dc2366fSVenugopal Iyer for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) {
11370dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v6_dyn_ip, addr, &where) != NULL)
11380dc2366fSVenugopal Iyer goto fail;
11390dc2366fSVenugopal Iyer
11400dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v6_dyn_ip, addr, where);
11410dc2366fSVenugopal Iyer }
11420dc2366fSVenugopal Iyer return (0);
11430dc2366fSVenugopal Iyer
11440dc2366fSVenugopal Iyer fail:
11450dc2366fSVenugopal Iyer remove_dhcpv6_cid(mcip, cid);
11460dc2366fSVenugopal Iyer return (EEXIST);
11470dc2366fSVenugopal Iyer }
11480dc2366fSVenugopal Iyer
11490dc2366fSVenugopal Iyer /*
11500dc2366fSVenugopal Iyer * Check whether an IP address is in the dyn-ip table.
11510dc2366fSVenugopal Iyer */
11520dc2366fSVenugopal Iyer static boolean_t
check_dhcpv6_dyn_ip(mac_client_impl_t * mcip,in6_addr_t * addr)11530dc2366fSVenugopal Iyer check_dhcpv6_dyn_ip(mac_client_impl_t *mcip, in6_addr_t *addr)
11540dc2366fSVenugopal Iyer {
11550dc2366fSVenugopal Iyer dhcpv6_addr_t tmp_addr, *a;
11560dc2366fSVenugopal Iyer
11570dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
11580dc2366fSVenugopal Iyer bcopy(addr, &tmp_addr.da_addr, sizeof (in6_addr_t));
11590dc2366fSVenugopal Iyer a = avl_find(&mcip->mci_v6_dyn_ip, &tmp_addr, NULL);
11600dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
11610dc2366fSVenugopal Iyer return (a != NULL);
11620dc2366fSVenugopal Iyer }
11630dc2366fSVenugopal Iyer
11640dc2366fSVenugopal Iyer static dhcpv6_txn_t *
find_dhcpv6_pending_txn(mac_client_impl_t * mcip,uint32_t xid)11650dc2366fSVenugopal Iyer find_dhcpv6_pending_txn(mac_client_impl_t *mcip, uint32_t xid)
11660dc2366fSVenugopal Iyer {
11670dc2366fSVenugopal Iyer dhcpv6_txn_t tmp_txn;
11680dc2366fSVenugopal Iyer
11690dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
11700dc2366fSVenugopal Iyer tmp_txn.dt_xid = xid;
11710dc2366fSVenugopal Iyer return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL));
11720dc2366fSVenugopal Iyer }
11730dc2366fSVenugopal Iyer
11740dc2366fSVenugopal Iyer static void
remove_dhcpv6_pending_txn(mac_client_impl_t * mcip,dhcpv6_txn_t * txn)11750dc2366fSVenugopal Iyer remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
11760dc2366fSVenugopal Iyer {
11770dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
11780dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_pending_txn, txn);
11790dc2366fSVenugopal Iyer }
11800dc2366fSVenugopal Iyer
11810dc2366fSVenugopal Iyer static dhcpv6_txn_t *
create_dhcpv6_txn(uint32_t xid,dhcpv6_cid_t * cid)11820dc2366fSVenugopal Iyer create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid)
11830dc2366fSVenugopal Iyer {
11840dc2366fSVenugopal Iyer dhcpv6_txn_t *txn;
11850dc2366fSVenugopal Iyer
11860dc2366fSVenugopal Iyer if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL)
11870dc2366fSVenugopal Iyer return (NULL);
11880dc2366fSVenugopal Iyer
11890dc2366fSVenugopal Iyer txn->dt_xid = xid;
11900dc2366fSVenugopal Iyer txn->dt_cid = cid;
11914d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime();
11920dc2366fSVenugopal Iyer return (txn);
11930dc2366fSVenugopal Iyer }
11940dc2366fSVenugopal Iyer
11950dc2366fSVenugopal Iyer static void
free_dhcpv6_txn(dhcpv6_txn_t * txn)11960dc2366fSVenugopal Iyer free_dhcpv6_txn(dhcpv6_txn_t *txn)
11970dc2366fSVenugopal Iyer {
11980dc2366fSVenugopal Iyer if (txn->dt_cid != NULL)
11990dc2366fSVenugopal Iyer free_dhcpv6_cid(txn->dt_cid);
12000dc2366fSVenugopal Iyer kmem_free(txn, sizeof (dhcpv6_txn_t));
12010dc2366fSVenugopal Iyer }
12020dc2366fSVenugopal Iyer
12030dc2366fSVenugopal Iyer static int
insert_dhcpv6_pending_txn(mac_client_impl_t * mcip,dhcpv6_txn_t * txn)12040dc2366fSVenugopal Iyer insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
12050dc2366fSVenugopal Iyer {
12060dc2366fSVenugopal Iyer avl_index_t where;
12070dc2366fSVenugopal Iyer
12080dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
12090dc2366fSVenugopal Iyer if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL)
12100dc2366fSVenugopal Iyer return (EEXIST);
12110dc2366fSVenugopal Iyer
12120dc2366fSVenugopal Iyer if (avl_numnodes(&mcip->mci_v6_pending_txn) >= dhcp_max_pending_txn) {
12130dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpdropped);
12140dc2366fSVenugopal Iyer return (EAGAIN);
12150dc2366fSVenugopal Iyer }
12160dc2366fSVenugopal Iyer avl_insert(&mcip->mci_v6_pending_txn, txn, where);
12170dc2366fSVenugopal Iyer return (0);
12180dc2366fSVenugopal Iyer }
12190dc2366fSVenugopal Iyer
12200dc2366fSVenugopal Iyer /*
12210dc2366fSVenugopal Iyer * Clean up all v6 tables.
12220dc2366fSVenugopal Iyer */
12230dc2366fSVenugopal Iyer static void
flush_dhcpv6(mac_client_impl_t * mcip)12240dc2366fSVenugopal Iyer flush_dhcpv6(mac_client_impl_t *mcip)
12250dc2366fSVenugopal Iyer {
12260dc2366fSVenugopal Iyer void *cookie = NULL;
12270dc2366fSVenugopal Iyer dhcpv6_cid_t *cid;
12280dc2366fSVenugopal Iyer dhcpv6_txn_t *txn;
12290dc2366fSVenugopal Iyer
12300dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
12310dc2366fSVenugopal Iyer while (avl_destroy_nodes(&mcip->mci_v6_dyn_ip, &cookie) != NULL) {
12320dc2366fSVenugopal Iyer }
12330dc2366fSVenugopal Iyer cookie = NULL;
12340dc2366fSVenugopal Iyer while ((cid = avl_destroy_nodes(&mcip->mci_v6_cid, &cookie)) != NULL) {
12350dc2366fSVenugopal Iyer free_dhcpv6_cid(cid);
12360dc2366fSVenugopal Iyer }
12370dc2366fSVenugopal Iyer cookie = NULL;
12380dc2366fSVenugopal Iyer while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn,
12390dc2366fSVenugopal Iyer &cookie)) != NULL) {
12400dc2366fSVenugopal Iyer free_dhcpv6_txn(txn);
12410dc2366fSVenugopal Iyer }
12420dc2366fSVenugopal Iyer }
12430dc2366fSVenugopal Iyer
1244797f979dSCody Peter Mello void
flush_slaac(mac_client_impl_t * mcip)1245797f979dSCody Peter Mello flush_slaac(mac_client_impl_t *mcip)
1246797f979dSCody Peter Mello {
1247797f979dSCody Peter Mello void *cookie = NULL;
1248797f979dSCody Peter Mello slaac_addr_t *addr = NULL;
1249797f979dSCody Peter Mello
1250797f979dSCody Peter Mello while ((addr = avl_destroy_nodes(&mcip->mci_v6_slaac_ip, &cookie)) !=
1251797f979dSCody Peter Mello NULL) {
1252797f979dSCody Peter Mello kmem_free(addr, sizeof (slaac_addr_t));
1253797f979dSCody Peter Mello }
1254797f979dSCody Peter Mello }
1255797f979dSCody Peter Mello
12560dc2366fSVenugopal Iyer /*
12570dc2366fSVenugopal Iyer * Cleanup stale DHCPv6 transactions.
12580dc2366fSVenugopal Iyer */
12590dc2366fSVenugopal Iyer static void
txn_cleanup_v6(mac_client_impl_t * mcip)12600dc2366fSVenugopal Iyer txn_cleanup_v6(mac_client_impl_t *mcip)
12610dc2366fSVenugopal Iyer {
12620dc2366fSVenugopal Iyer dhcpv6_txn_t *txn, *next, *txn_list = NULL;
12630dc2366fSVenugopal Iyer
12640dc2366fSVenugopal Iyer /*
12650dc2366fSVenugopal Iyer * Find stale pending transactions and place them on a list
12660dc2366fSVenugopal Iyer * to be removed.
12670dc2366fSVenugopal Iyer */
12680dc2366fSVenugopal Iyer for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL;
12690dc2366fSVenugopal Iyer txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) {
12704d6a58d3SJosef 'Jeff' Sipek if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) {
12710dc2366fSVenugopal Iyer DTRACE_PROBE2(found__expired__txn,
12720dc2366fSVenugopal Iyer mac_client_impl_t *, mcip,
12730dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
12740dc2366fSVenugopal Iyer
12750dc2366fSVenugopal Iyer txn->dt_next = txn_list;
12760dc2366fSVenugopal Iyer txn_list = txn;
12770dc2366fSVenugopal Iyer }
12780dc2366fSVenugopal Iyer }
12790dc2366fSVenugopal Iyer
12800dc2366fSVenugopal Iyer /*
12810dc2366fSVenugopal Iyer * Remove and free stale pending transactions.
12820dc2366fSVenugopal Iyer * Release any existing cids matching the stale transactions.
12830dc2366fSVenugopal Iyer */
12840dc2366fSVenugopal Iyer for (txn = txn_list; txn != NULL; txn = next) {
12850dc2366fSVenugopal Iyer avl_remove(&mcip->mci_v6_pending_txn, txn);
12860dc2366fSVenugopal Iyer release_dhcpv6_cid(mcip, txn->dt_cid);
12870dc2366fSVenugopal Iyer next = txn->dt_next;
12880dc2366fSVenugopal Iyer txn->dt_next = NULL;
12890dc2366fSVenugopal Iyer
12900dc2366fSVenugopal Iyer DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
12910dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
12920dc2366fSVenugopal Iyer free_dhcpv6_txn(txn);
12930dc2366fSVenugopal Iyer }
12940dc2366fSVenugopal Iyer
12950dc2366fSVenugopal Iyer }
12960dc2366fSVenugopal Iyer
12970dc2366fSVenugopal Iyer /*
12980dc2366fSVenugopal Iyer * Core logic for intercepting outbound DHCPv6 packets.
12990dc2366fSVenugopal Iyer */
1300550b6e40SSowmini Varadhan static boolean_t
intercept_dhcpv6_outbound(mac_client_impl_t * mcip,ip6_t * ip6h,uchar_t * end)13010dc2366fSVenugopal Iyer intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end)
13020dc2366fSVenugopal Iyer {
13030dc2366fSVenugopal Iyer dhcpv6_message_t *dh6;
13040dc2366fSVenugopal Iyer dhcpv6_txn_t *txn;
13050dc2366fSVenugopal Iyer dhcpv6_cid_t *cid = NULL;
13060dc2366fSVenugopal Iyer uint32_t xid;
13070dc2366fSVenugopal Iyer uint8_t mtype;
1308550b6e40SSowmini Varadhan mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
13090dc2366fSVenugopal Iyer
13100dc2366fSVenugopal Iyer if (get_dhcpv6_info(ip6h, end, &dh6) != 0)
1311550b6e40SSowmini Varadhan return (B_TRUE);
1312550b6e40SSowmini Varadhan
1313550b6e40SSowmini Varadhan /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
1314550b6e40SSowmini Varadhan if (allowed_ips_set(mrp, IPV6_VERSION))
1315550b6e40SSowmini Varadhan return (B_FALSE);
13160dc2366fSVenugopal Iyer
1317146e34b6SCody Peter Mello /*
1318146e34b6SCody Peter Mello * We want to act on packets that result in DHCPv6 Reply messages, or
1319146e34b6SCody Peter Mello * on packets that give up an IPv6 address. For example, a Request or
1320146e34b6SCody Peter Mello * Solicit (w/ the Rapid Commit option) will cause the server to send a
1321146e34b6SCody Peter Mello * Reply, ending the transaction.
1322146e34b6SCody Peter Mello */
13230dc2366fSVenugopal Iyer mtype = dh6->d6m_msg_type;
1324146e34b6SCody Peter Mello if (mtype != DHCPV6_MSG_SOLICIT && mtype != DHCPV6_MSG_REQUEST &&
1325146e34b6SCody Peter Mello mtype != DHCPV6_MSG_RENEW && mtype != DHCPV6_MSG_REBIND &&
1326146e34b6SCody Peter Mello mtype != DHCPV6_MSG_RELEASE)
1327550b6e40SSowmini Varadhan return (B_TRUE);
13280dc2366fSVenugopal Iyer
13290dc2366fSVenugopal Iyer if ((cid = create_dhcpv6_cid(dh6, end)) == NULL)
1330550b6e40SSowmini Varadhan return (B_TRUE);
13310dc2366fSVenugopal Iyer
13320dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
13330dc2366fSVenugopal Iyer if (mtype == DHCPV6_MSG_RELEASE) {
13340dc2366fSVenugopal Iyer release_dhcpv6_cid(mcip, cid);
13350dc2366fSVenugopal Iyer goto done;
13360dc2366fSVenugopal Iyer }
13370dc2366fSVenugopal Iyer xid = DHCPV6_GET_TRANSID(dh6);
13380dc2366fSVenugopal Iyer if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) {
13390dc2366fSVenugopal Iyer DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
13400dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
13414d6a58d3SJosef 'Jeff' Sipek txn->dt_timestamp = gethrtime();
13420dc2366fSVenugopal Iyer goto done;
13430dc2366fSVenugopal Iyer }
13440dc2366fSVenugopal Iyer if ((txn = create_dhcpv6_txn(xid, cid)) == NULL)
13450dc2366fSVenugopal Iyer goto done;
13460dc2366fSVenugopal Iyer
13470dc2366fSVenugopal Iyer cid = NULL;
13480dc2366fSVenugopal Iyer if (insert_dhcpv6_pending_txn(mcip, txn) != 0) {
13490dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
13500dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
13510dc2366fSVenugopal Iyer free_dhcpv6_txn(txn);
13520dc2366fSVenugopal Iyer goto done;
13530dc2366fSVenugopal Iyer }
13540dc2366fSVenugopal Iyer start_txn_cleanup_timer(mcip);
13550dc2366fSVenugopal Iyer
13560dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
13570dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
13580dc2366fSVenugopal Iyer
13590dc2366fSVenugopal Iyer done:
13600dc2366fSVenugopal Iyer if (cid != NULL)
13610dc2366fSVenugopal Iyer free_dhcpv6_cid(cid);
13620dc2366fSVenugopal Iyer
13630dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
1364550b6e40SSowmini Varadhan return (B_TRUE);
13650dc2366fSVenugopal Iyer }
13660dc2366fSVenugopal Iyer
13670dc2366fSVenugopal Iyer /*
13680dc2366fSVenugopal Iyer * Core logic for intercepting inbound DHCPv6 packets.
13690dc2366fSVenugopal Iyer */
13700dc2366fSVenugopal Iyer static void
intercept_dhcpv6_inbound(mac_client_impl_t * mcip,uchar_t * end,dhcpv6_message_t * dh6)1371797f979dSCody Peter Mello intercept_dhcpv6_inbound(mac_client_impl_t *mcip, uchar_t *end,
1372797f979dSCody Peter Mello dhcpv6_message_t *dh6)
13730dc2366fSVenugopal Iyer {
13740dc2366fSVenugopal Iyer dhcpv6_txn_t *txn;
13750dc2366fSVenugopal Iyer uint32_t xid;
13760dc2366fSVenugopal Iyer uint8_t mtype;
13770dc2366fSVenugopal Iyer uint16_t status;
13780dc2366fSVenugopal Iyer
13790dc2366fSVenugopal Iyer mtype = dh6->d6m_msg_type;
13800dc2366fSVenugopal Iyer if (mtype != DHCPV6_MSG_REPLY)
13810dc2366fSVenugopal Iyer return;
13820dc2366fSVenugopal Iyer
13830dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
13840dc2366fSVenugopal Iyer xid = DHCPV6_GET_TRANSID(dh6);
13850dc2366fSVenugopal Iyer if ((txn = find_dhcpv6_pending_txn(mcip, xid)) == NULL) {
13860dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip,
13870dc2366fSVenugopal Iyer dhcpv6_message_t *, dh6);
13880dc2366fSVenugopal Iyer goto done;
13890dc2366fSVenugopal Iyer }
13900dc2366fSVenugopal Iyer remove_dhcpv6_pending_txn(mcip, txn);
13910dc2366fSVenugopal Iyer release_dhcpv6_cid(mcip, txn->dt_cid);
13920dc2366fSVenugopal Iyer
13930dc2366fSVenugopal Iyer if (get_dhcpv6_status(dh6, end, &status) != 0 ||
13940dc2366fSVenugopal Iyer status != DHCPV6_STAT_SUCCESS) {
13950dc2366fSVenugopal Iyer DTRACE_PROBE2(error__status, mac_client_impl_t *, mcip,
13960dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
13970dc2366fSVenugopal Iyer goto done;
13980dc2366fSVenugopal Iyer }
13990dc2366fSVenugopal Iyer if (get_dhcpv6_addrs(dh6, end, txn->dt_cid) != 0) {
14000dc2366fSVenugopal Iyer DTRACE_PROBE2(no__addrs, mac_client_impl_t *, mcip,
14010dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
14020dc2366fSVenugopal Iyer goto done;
14030dc2366fSVenugopal Iyer }
14040dc2366fSVenugopal Iyer if (insert_dhcpv6_cid(mcip, txn->dt_cid) != 0) {
14050dc2366fSVenugopal Iyer DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
14060dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
14070dc2366fSVenugopal Iyer goto done;
14080dc2366fSVenugopal Iyer }
14090dc2366fSVenugopal Iyer DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip,
14100dc2366fSVenugopal Iyer dhcpv6_txn_t *, txn);
14110dc2366fSVenugopal Iyer
14120dc2366fSVenugopal Iyer txn->dt_cid = NULL;
14130dc2366fSVenugopal Iyer
14140dc2366fSVenugopal Iyer done:
14150dc2366fSVenugopal Iyer if (txn != NULL)
14160dc2366fSVenugopal Iyer free_dhcpv6_txn(txn);
14170dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
14180dc2366fSVenugopal Iyer }
14190dc2366fSVenugopal Iyer
1420797f979dSCody Peter Mello /*
1421797f979dSCody Peter Mello * Check whether an IP address is in the SLAAC table.
1422797f979dSCody Peter Mello */
1423797f979dSCody Peter Mello static boolean_t
check_slaac_ip(mac_client_impl_t * mcip,in6_addr_t * addr)1424797f979dSCody Peter Mello check_slaac_ip(mac_client_impl_t *mcip, in6_addr_t *addr)
1425797f979dSCody Peter Mello {
1426797f979dSCody Peter Mello slaac_addr_t tmp_addr, *a;
1427797f979dSCody Peter Mello
1428797f979dSCody Peter Mello mutex_enter(&mcip->mci_protect_lock);
1429797f979dSCody Peter Mello bcopy(addr, &tmp_addr.sla_addr, sizeof (in6_addr_t));
1430797f979dSCody Peter Mello a = avl_find(&mcip->mci_v6_slaac_ip, &tmp_addr, NULL);
1431797f979dSCody Peter Mello mutex_exit(&mcip->mci_protect_lock);
1432797f979dSCody Peter Mello return (a != NULL);
1433797f979dSCody Peter Mello }
1434797f979dSCody Peter Mello
1435797f979dSCody Peter Mello static boolean_t
insert_slaac_ip(avl_tree_t * tree,in6_addr_t * token,slaac_addr_t * addr)1436797f979dSCody Peter Mello insert_slaac_ip(avl_tree_t *tree, in6_addr_t *token, slaac_addr_t *addr)
1437797f979dSCody Peter Mello {
1438797f979dSCody Peter Mello uint_t i;
1439797f979dSCody Peter Mello avl_index_t where;
1440797f979dSCody Peter Mello in6_addr_t *prefix = &addr->sla_prefix;
1441797f979dSCody Peter Mello in6_addr_t *in6p = &addr->sla_addr;
1442797f979dSCody Peter Mello
1443797f979dSCody Peter Mello bcopy(prefix, in6p, sizeof (struct in6_addr));
1444797f979dSCody Peter Mello
1445797f979dSCody Peter Mello for (i = 0; i < 4; i++) {
1446797f979dSCody Peter Mello in6p->s6_addr32[i] = token->s6_addr32[i] |
1447797f979dSCody Peter Mello in6p->s6_addr32[i];
1448797f979dSCody Peter Mello }
1449797f979dSCody Peter Mello
1450797f979dSCody Peter Mello DTRACE_PROBE1(generated__addr, in6_addr_t *, in6p);
1451797f979dSCody Peter Mello
1452797f979dSCody Peter Mello if (avl_find(tree, addr, &where) != NULL)
1453797f979dSCody Peter Mello return (B_FALSE);
1454797f979dSCody Peter Mello
1455797f979dSCody Peter Mello avl_insert(tree, addr, where);
1456797f979dSCody Peter Mello return (B_TRUE);
1457797f979dSCody Peter Mello }
1458797f979dSCody Peter Mello
1459797f979dSCody Peter Mello static void
insert_slaac_prefix(mac_client_impl_t * mcip,nd_opt_prefix_info_t * po)1460797f979dSCody Peter Mello insert_slaac_prefix(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po)
1461797f979dSCody Peter Mello {
1462797f979dSCody Peter Mello slaac_addr_t *addr = NULL;
1463797f979dSCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token;
1464797f979dSCody Peter Mello
1465797f979dSCody Peter Mello ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1466797f979dSCody Peter Mello
1467797f979dSCody Peter Mello if (avl_numnodes(&mcip->mci_v6_slaac_ip) >= slaac_max_allowed) {
1468797f979dSCody Peter Mello DTRACE_PROBE(limit__reached);
1469797f979dSCody Peter Mello return;
1470797f979dSCody Peter Mello }
1471797f979dSCody Peter Mello
1472*ca783257SDan McDonald if ((addr = kmem_zalloc(sizeof (slaac_addr_t), KM_NOSLEEP_LAZY)) ==
1473*ca783257SDan McDonald NULL)
1474797f979dSCody Peter Mello return;
1475797f979dSCody Peter Mello
1476797f979dSCody Peter Mello bcopy(&po->nd_opt_pi_prefix, &addr->sla_prefix,
1477797f979dSCody Peter Mello sizeof (struct in6_addr));
1478797f979dSCody Peter Mello
1479797f979dSCody Peter Mello if (!insert_slaac_ip(&mcip->mci_v6_slaac_ip, token, addr)) {
1480797f979dSCody Peter Mello kmem_free(addr, sizeof (slaac_addr_t));
1481797f979dSCody Peter Mello }
1482797f979dSCody Peter Mello }
1483797f979dSCody Peter Mello
1484797f979dSCody Peter Mello static void
intercept_prefix_info(mac_client_impl_t * mcip,nd_opt_prefix_info_t * po)1485797f979dSCody Peter Mello intercept_prefix_info(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po)
1486797f979dSCody Peter Mello {
1487797f979dSCody Peter Mello if (8 * po->nd_opt_pi_len != sizeof (nd_opt_prefix_info_t)) {
1488797f979dSCody Peter Mello DTRACE_PROBE(invalid__length);
1489797f979dSCody Peter Mello return;
1490797f979dSCody Peter Mello }
1491797f979dSCody Peter Mello
1492797f979dSCody Peter Mello if (po->nd_opt_pi_prefix_len > 128) {
1493797f979dSCody Peter Mello DTRACE_PROBE(invalid__plen);
1494797f979dSCody Peter Mello return;
1495797f979dSCody Peter Mello }
1496797f979dSCody Peter Mello
1497797f979dSCody Peter Mello if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1498797f979dSCody Peter Mello DTRACE_PROBE(link__local);
1499797f979dSCody Peter Mello return;
1500797f979dSCody Peter Mello }
1501797f979dSCody Peter Mello
1502797f979dSCody Peter Mello if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) == 0)
1503797f979dSCody Peter Mello return;
1504797f979dSCody Peter Mello
1505797f979dSCody Peter Mello mutex_enter(&mcip->mci_protect_lock);
1506797f979dSCody Peter Mello insert_slaac_prefix(mcip, po);
1507797f979dSCody Peter Mello mutex_exit(&mcip->mci_protect_lock);
1508797f979dSCody Peter Mello }
1509797f979dSCody Peter Mello
1510797f979dSCody Peter Mello /*
1511797f979dSCody Peter Mello * If we receive a Router Advertisement carrying prefix information and
1512797f979dSCody Peter Mello * indicating that SLAAC should be performed, then track the prefix.
1513797f979dSCody Peter Mello */
1514797f979dSCody Peter Mello static void
intercept_ra_inbound(mac_client_impl_t * mcip,ip6_t * ip6h,uchar_t * end,nd_router_advert_t * ra)1515797f979dSCody Peter Mello intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end,
1516797f979dSCody Peter Mello nd_router_advert_t *ra)
1517797f979dSCody Peter Mello {
1518797f979dSCody Peter Mello struct nd_opt_hdr *opt;
1519797f979dSCody Peter Mello int len, optlen;
1520797f979dSCody Peter Mello
1521797f979dSCody Peter Mello if (ip6h->ip6_hlim != 255) {
1522797f979dSCody Peter Mello DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim);
1523797f979dSCody Peter Mello return;
1524797f979dSCody Peter Mello }
1525797f979dSCody Peter Mello
1526797f979dSCody Peter Mello len = ip6h->ip6_plen - sizeof (nd_router_advert_t);
1527797f979dSCody Peter Mello opt = (struct nd_opt_hdr *)&ra[1];
1528797f979dSCody Peter Mello while (len >= sizeof (struct nd_opt_hdr) &&
1529797f979dSCody Peter Mello ((uchar_t *)opt + sizeof (struct nd_opt_hdr)) <= end) {
1530797f979dSCody Peter Mello optlen = opt->nd_opt_len * 8;
1531797f979dSCody Peter Mello
1532797f979dSCody Peter Mello if (optlen < sizeof (struct nd_opt_hdr) ||
1533797f979dSCody Peter Mello ((uchar_t *)opt + optlen) > end) {
1534797f979dSCody Peter Mello DTRACE_PROBE(invalid__length);
1535797f979dSCody Peter Mello return;
1536797f979dSCody Peter Mello }
1537797f979dSCody Peter Mello
1538797f979dSCody Peter Mello if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION) {
1539797f979dSCody Peter Mello intercept_prefix_info(mcip,
1540797f979dSCody Peter Mello (nd_opt_prefix_info_t *)opt);
1541797f979dSCody Peter Mello }
1542797f979dSCody Peter Mello
1543797f979dSCody Peter Mello opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1544797f979dSCody Peter Mello len -= optlen;
1545797f979dSCody Peter Mello }
1546797f979dSCody Peter Mello }
1547797f979dSCody Peter Mello
15480dc2366fSVenugopal Iyer /*
15490dc2366fSVenugopal Iyer * Timer for cleaning up stale transactions.
15500dc2366fSVenugopal Iyer */
15510dc2366fSVenugopal Iyer static void
txn_cleanup_timer(void * arg)15520dc2366fSVenugopal Iyer txn_cleanup_timer(void *arg)
15530dc2366fSVenugopal Iyer {
15540dc2366fSVenugopal Iyer mac_client_impl_t *mcip = arg;
15550dc2366fSVenugopal Iyer
15560dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
15570dc2366fSVenugopal Iyer if (mcip->mci_txn_cleanup_tid == 0) {
15580dc2366fSVenugopal Iyer /* do nothing if timer got cancelled */
15590dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
15600dc2366fSVenugopal Iyer return;
15610dc2366fSVenugopal Iyer }
15620dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0;
15630dc2366fSVenugopal Iyer
15640dc2366fSVenugopal Iyer txn_cleanup_v4(mcip);
15650dc2366fSVenugopal Iyer txn_cleanup_v6(mcip);
15660dc2366fSVenugopal Iyer
15670dc2366fSVenugopal Iyer /*
15680dc2366fSVenugopal Iyer * Restart timer if pending transactions still exist.
15690dc2366fSVenugopal Iyer */
15700dc2366fSVenugopal Iyer if (!avl_is_empty(&mcip->mci_v4_pending_txn) ||
15710dc2366fSVenugopal Iyer !avl_is_empty(&mcip->mci_v6_pending_txn)) {
15720dc2366fSVenugopal Iyer DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip);
15730dc2366fSVenugopal Iyer
15740dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
15754d6a58d3SJosef 'Jeff' Sipek drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC)));
15760dc2366fSVenugopal Iyer }
15770dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
15780dc2366fSVenugopal Iyer }
15790dc2366fSVenugopal Iyer
15800dc2366fSVenugopal Iyer static void
start_txn_cleanup_timer(mac_client_impl_t * mcip)15810dc2366fSVenugopal Iyer start_txn_cleanup_timer(mac_client_impl_t *mcip)
15820dc2366fSVenugopal Iyer {
15830dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
15840dc2366fSVenugopal Iyer if (mcip->mci_txn_cleanup_tid == 0) {
15850dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
15864d6a58d3SJosef 'Jeff' Sipek drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC)));
15870dc2366fSVenugopal Iyer }
15880dc2366fSVenugopal Iyer }
15890dc2366fSVenugopal Iyer
15900dc2366fSVenugopal Iyer static void
cancel_txn_cleanup_timer(mac_client_impl_t * mcip)15910dc2366fSVenugopal Iyer cancel_txn_cleanup_timer(mac_client_impl_t *mcip)
15920dc2366fSVenugopal Iyer {
15930dc2366fSVenugopal Iyer timeout_id_t tid;
15940dc2366fSVenugopal Iyer
15950dc2366fSVenugopal Iyer ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
15960dc2366fSVenugopal Iyer
15970dc2366fSVenugopal Iyer /*
15980dc2366fSVenugopal Iyer * This needs to be a while loop because the timer could get
15990dc2366fSVenugopal Iyer * rearmed during untimeout().
16000dc2366fSVenugopal Iyer */
16010dc2366fSVenugopal Iyer while ((tid = mcip->mci_txn_cleanup_tid) != 0) {
16020dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0;
16030dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
16040dc2366fSVenugopal Iyer (void) untimeout(tid);
16050dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
16060dc2366fSVenugopal Iyer }
16070dc2366fSVenugopal Iyer }
16080dc2366fSVenugopal Iyer
16090dc2366fSVenugopal Iyer /*
16100dc2366fSVenugopal Iyer * Get the start/end pointers of an L3 packet and also do pullup if needed.
16110dc2366fSVenugopal Iyer * pulled-up packet needs to be freed by the caller.
16120dc2366fSVenugopal Iyer */
16130dc2366fSVenugopal Iyer static int
get_l3_info(mblk_t * mp,size_t hdrsize,uchar_t ** start,uchar_t ** end,mblk_t ** nmp)16140dc2366fSVenugopal Iyer get_l3_info(mblk_t *mp, size_t hdrsize, uchar_t **start, uchar_t **end,
16150dc2366fSVenugopal Iyer mblk_t **nmp)
16160dc2366fSVenugopal Iyer {
16170dc2366fSVenugopal Iyer uchar_t *s, *e;
16180dc2366fSVenugopal Iyer mblk_t *newmp = NULL;
16190dc2366fSVenugopal Iyer
16200dc2366fSVenugopal Iyer /*
16210dc2366fSVenugopal Iyer * Pullup if necessary but reject packets that do not have
16220dc2366fSVenugopal Iyer * a proper mac header.
16230dc2366fSVenugopal Iyer */
16240dc2366fSVenugopal Iyer s = mp->b_rptr + hdrsize;
16250dc2366fSVenugopal Iyer e = mp->b_wptr;
16260dc2366fSVenugopal Iyer
16270dc2366fSVenugopal Iyer if (s > mp->b_wptr)
16280dc2366fSVenugopal Iyer return (EINVAL);
16290dc2366fSVenugopal Iyer
16300dc2366fSVenugopal Iyer if (!OK_32PTR(s) || mp->b_cont != NULL) {
16310dc2366fSVenugopal Iyer /*
16320dc2366fSVenugopal Iyer * Temporarily adjust mp->b_rptr to ensure proper
16330dc2366fSVenugopal Iyer * alignment of IP header in newmp.
16340dc2366fSVenugopal Iyer */
16350dc2366fSVenugopal Iyer DTRACE_PROBE1(pullup__needed, mblk_t *, mp);
16360dc2366fSVenugopal Iyer
16370dc2366fSVenugopal Iyer mp->b_rptr += hdrsize;
16380dc2366fSVenugopal Iyer newmp = msgpullup(mp, -1);
16390dc2366fSVenugopal Iyer mp->b_rptr -= hdrsize;
16400dc2366fSVenugopal Iyer
16410dc2366fSVenugopal Iyer if (newmp == NULL)
16420dc2366fSVenugopal Iyer return (ENOMEM);
16430dc2366fSVenugopal Iyer
16440dc2366fSVenugopal Iyer s = newmp->b_rptr;
16450dc2366fSVenugopal Iyer e = newmp->b_wptr;
16460dc2366fSVenugopal Iyer }
16470dc2366fSVenugopal Iyer
16480dc2366fSVenugopal Iyer *start = s;
16490dc2366fSVenugopal Iyer *end = e;
16500dc2366fSVenugopal Iyer *nmp = newmp;
16510dc2366fSVenugopal Iyer return (0);
16520dc2366fSVenugopal Iyer }
16530dc2366fSVenugopal Iyer
16540dc2366fSVenugopal Iyer void
mac_protect_intercept_dynamic_one(mac_client_impl_t * mcip,mblk_t * mp)1655797f979dSCody Peter Mello mac_protect_intercept_dynamic_one(mac_client_impl_t *mcip, mblk_t *mp)
16560dc2366fSVenugopal Iyer {
16570dc2366fSVenugopal Iyer mac_impl_t *mip = mcip->mci_mip;
16580dc2366fSVenugopal Iyer uchar_t *start, *end;
16590dc2366fSVenugopal Iyer mblk_t *nmp = NULL;
16600dc2366fSVenugopal Iyer mac_header_info_t mhi;
16610dc2366fSVenugopal Iyer int err;
16620dc2366fSVenugopal Iyer
16630dc2366fSVenugopal Iyer err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
16640dc2366fSVenugopal Iyer if (err != 0) {
16650dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
16660dc2366fSVenugopal Iyer mblk_t *, mp);
16670dc2366fSVenugopal Iyer return;
16680dc2366fSVenugopal Iyer }
16690dc2366fSVenugopal Iyer
16700dc2366fSVenugopal Iyer err = get_l3_info(mp, mhi.mhi_hdrsize, &start, &end, &nmp);
16710dc2366fSVenugopal Iyer if (err != 0) {
16720dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
16730dc2366fSVenugopal Iyer mblk_t *, mp);
16740dc2366fSVenugopal Iyer return;
16750dc2366fSVenugopal Iyer }
16760dc2366fSVenugopal Iyer
16770dc2366fSVenugopal Iyer switch (mhi.mhi_bindsap) {
16780dc2366fSVenugopal Iyer case ETHERTYPE_IP: {
1679797f979dSCody Peter Mello struct dhcp *dh4;
1680797f979dSCody Peter Mello ipha_t *ipha = (ipha_t *)start;
16810dc2366fSVenugopal Iyer
16820dc2366fSVenugopal Iyer if (start + sizeof (ipha_t) > end)
16830dc2366fSVenugopal Iyer return;
16840dc2366fSVenugopal Iyer
1685797f979dSCody Peter Mello if (get_dhcpv4_info(ipha, end, &dh4) == 0) {
1686797f979dSCody Peter Mello intercept_dhcpv4_inbound(mcip, end, dh4);
1687797f979dSCody Peter Mello }
16880dc2366fSVenugopal Iyer break;
16890dc2366fSVenugopal Iyer }
16900dc2366fSVenugopal Iyer case ETHERTYPE_IPV6: {
1691797f979dSCody Peter Mello dhcpv6_message_t *dh6;
1692797f979dSCody Peter Mello nd_router_advert_t *ra;
1693797f979dSCody Peter Mello ip6_t *ip6h = (ip6_t *)start;
16940dc2366fSVenugopal Iyer
16950dc2366fSVenugopal Iyer if (start + sizeof (ip6_t) > end)
16960dc2366fSVenugopal Iyer return;
16970dc2366fSVenugopal Iyer
1698797f979dSCody Peter Mello if (get_dhcpv6_info(ip6h, end, &dh6) == 0) {
1699797f979dSCody Peter Mello intercept_dhcpv6_inbound(mcip, end, dh6);
1700797f979dSCody Peter Mello } else if (get_ra_info(ip6h, end, &ra) == 0) {
1701797f979dSCody Peter Mello intercept_ra_inbound(mcip, ip6h, end, ra);
1702797f979dSCody Peter Mello }
1703797f979dSCody Peter Mello
17040dc2366fSVenugopal Iyer break;
17050dc2366fSVenugopal Iyer }
17060dc2366fSVenugopal Iyer }
17070dc2366fSVenugopal Iyer freemsg(nmp);
17080dc2366fSVenugopal Iyer }
17090dc2366fSVenugopal Iyer
17100dc2366fSVenugopal Iyer void
mac_protect_intercept_dynamic(mac_client_impl_t * mcip,mblk_t * mp)1711797f979dSCody Peter Mello mac_protect_intercept_dynamic(mac_client_impl_t *mcip, mblk_t *mp)
17120dc2366fSVenugopal Iyer {
17130dc2366fSVenugopal Iyer /*
17140dc2366fSVenugopal Iyer * Skip checks if we are part of an aggr.
17150dc2366fSVenugopal Iyer */
17160dc2366fSVenugopal Iyer if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
17170dc2366fSVenugopal Iyer return;
17180dc2366fSVenugopal Iyer
17190dc2366fSVenugopal Iyer for (; mp != NULL; mp = mp->b_next)
1720797f979dSCody Peter Mello mac_protect_intercept_dynamic_one(mcip, mp);
17210dc2366fSVenugopal Iyer }
17220dc2366fSVenugopal Iyer
17230dc2366fSVenugopal Iyer void
mac_protect_flush_dynamic(mac_client_impl_t * mcip)1724797f979dSCody Peter Mello mac_protect_flush_dynamic(mac_client_impl_t *mcip)
17250dc2366fSVenugopal Iyer {
17260dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
17270dc2366fSVenugopal Iyer flush_dhcpv4(mcip);
17280dc2366fSVenugopal Iyer flush_dhcpv6(mcip);
1729797f979dSCody Peter Mello flush_slaac(mcip);
17300dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
17310dc2366fSVenugopal Iyer }
17320dc2366fSVenugopal Iyer
17330dc2366fSVenugopal Iyer void
mac_protect_cancel_timer(mac_client_impl_t * mcip)17340dc2366fSVenugopal Iyer mac_protect_cancel_timer(mac_client_impl_t *mcip)
17350dc2366fSVenugopal Iyer {
17360dc2366fSVenugopal Iyer mutex_enter(&mcip->mci_protect_lock);
17370dc2366fSVenugopal Iyer cancel_txn_cleanup_timer(mcip);
17380dc2366fSVenugopal Iyer mutex_exit(&mcip->mci_protect_lock);
17390dc2366fSVenugopal Iyer }
17400dc2366fSVenugopal Iyer
17410dc2366fSVenugopal Iyer /*
17420dc2366fSVenugopal Iyer * Check if addr is in the 'allowed-ips' list.
174325ec3e3dSEric Cheng */
17440dc2366fSVenugopal Iyer
17450dc2366fSVenugopal Iyer /* ARGSUSED */
17460dc2366fSVenugopal Iyer static boolean_t
ipnospoof_check_v4(mac_client_impl_t * mcip,mac_protect_t * protect,ipaddr_t * addr)17470dc2366fSVenugopal Iyer ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
17480dc2366fSVenugopal Iyer ipaddr_t *addr)
17490dc2366fSVenugopal Iyer {
17500dc2366fSVenugopal Iyer uint_t i;
17510dc2366fSVenugopal Iyer
17520dc2366fSVenugopal Iyer /*
17530dc2366fSVenugopal Iyer * The unspecified address is allowed.
17540dc2366fSVenugopal Iyer */
17550dc2366fSVenugopal Iyer if (*addr == INADDR_ANY)
17560dc2366fSVenugopal Iyer return (B_TRUE);
17570dc2366fSVenugopal Iyer
17580dc2366fSVenugopal Iyer for (i = 0; i < protect->mp_ipaddrcnt; i++) {
17590dc2366fSVenugopal Iyer mac_ipaddr_t *v4addr = &protect->mp_ipaddrs[i];
17600dc2366fSVenugopal Iyer
1761e03914f9SRobert Mustacchi if (v4addr->ip_version == IPV4_VERSION) {
1762e03914f9SRobert Mustacchi uint32_t mask;
1763e03914f9SRobert Mustacchi
1764e03914f9SRobert Mustacchi /* LINTED E_SUSPICIOUS_COMPARISON */
1765e03914f9SRobert Mustacchi ASSERT(v4addr->ip_netmask >= 0 &&
1766e03914f9SRobert Mustacchi v4addr->ip_netmask <= 32);
1767e03914f9SRobert Mustacchi mask = 0xFFFFFFFFu << (32 - v4addr->ip_netmask);
1768e03914f9SRobert Mustacchi /*
1769e03914f9SRobert Mustacchi * Since we have a netmask we know this entry
1770e03914f9SRobert Mustacchi * signifies the entire subnet. Check if the
1771e03914f9SRobert Mustacchi * given address is on the subnet.
1772e03914f9SRobert Mustacchi */
1773e03914f9SRobert Mustacchi if (htonl(V4_PART_OF_V6(v4addr->ip_addr)) ==
1774e03914f9SRobert Mustacchi (htonl(*addr) & mask))
1775e03914f9SRobert Mustacchi return (B_TRUE);
1776e03914f9SRobert Mustacchi }
17770dc2366fSVenugopal Iyer }
1778550b6e40SSowmini Varadhan return (protect->mp_ipaddrcnt == 0 ?
1779550b6e40SSowmini Varadhan check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE);
17800dc2366fSVenugopal Iyer }
17810dc2366fSVenugopal Iyer
178225ec3e3dSEric Cheng static boolean_t
ipnospoof_check_v6(mac_client_impl_t * mcip,mac_protect_t * protect,in6_addr_t * addr)17830dc2366fSVenugopal Iyer ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
17840dc2366fSVenugopal Iyer in6_addr_t *addr)
178525ec3e3dSEric Cheng {
17860dc2366fSVenugopal Iyer uint_t i;
178725ec3e3dSEric Cheng
178825ec3e3dSEric Cheng /*
17890dc2366fSVenugopal Iyer * The unspecified address and the v6 link local address are allowed.
179025ec3e3dSEric Cheng */
17910dc2366fSVenugopal Iyer if (IN6_IS_ADDR_UNSPECIFIED(addr) ||
17920dc2366fSVenugopal Iyer ((mcip->mci_protect_flags & MPT_FLAG_V6_LOCAL_ADDR_SET) != 0 &&
17930dc2366fSVenugopal Iyer IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr)))
179425ec3e3dSEric Cheng return (B_TRUE);
179525ec3e3dSEric Cheng
17960dc2366fSVenugopal Iyer
179725ec3e3dSEric Cheng for (i = 0; i < protect->mp_ipaddrcnt; i++) {
17980dc2366fSVenugopal Iyer mac_ipaddr_t *v6addr = &protect->mp_ipaddrs[i];
17990dc2366fSVenugopal Iyer
18000dc2366fSVenugopal Iyer if (v6addr->ip_version == IPV6_VERSION &&
1801e03914f9SRobert Mustacchi /* LINTED E_SUSPICIOUS_COMPARISON */
1802e03914f9SRobert Mustacchi IN6_ARE_PREFIXEDADDR_EQUAL(&v6addr->ip_addr, addr,
1803e03914f9SRobert Mustacchi v6addr->ip_netmask))
180425ec3e3dSEric Cheng return (B_TRUE);
180525ec3e3dSEric Cheng }
1806797f979dSCody Peter Mello
1807797f979dSCody Peter Mello if (protect->mp_ipaddrcnt == 0) {
1808797f979dSCody Peter Mello return (check_slaac_ip(mcip, addr) ||
1809797f979dSCody Peter Mello check_dhcpv6_dyn_ip(mcip, addr));
1810797f979dSCody Peter Mello } else {
1811797f979dSCody Peter Mello return (B_FALSE);
1812797f979dSCody Peter Mello }
181325ec3e3dSEric Cheng }
181425ec3e3dSEric Cheng
181525ec3e3dSEric Cheng /*
18160dc2366fSVenugopal Iyer * Checks various fields within an IPv6 NDP packet.
181725ec3e3dSEric Cheng */
18180dc2366fSVenugopal Iyer static boolean_t
ipnospoof_check_ndp(mac_client_impl_t * mcip,mac_protect_t * protect,ip6_t * ip6h,uchar_t * end)18190dc2366fSVenugopal Iyer ipnospoof_check_ndp(mac_client_impl_t *mcip, mac_protect_t *protect,
18200dc2366fSVenugopal Iyer ip6_t *ip6h, uchar_t *end)
182125ec3e3dSEric Cheng {
18220dc2366fSVenugopal Iyer icmp6_t *icmp_nd = (icmp6_t *)&ip6h[1];
18230dc2366fSVenugopal Iyer int hdrlen, optlen, opttype, len;
18240dc2366fSVenugopal Iyer uint_t addrlen, maclen;
18250dc2366fSVenugopal Iyer uint8_t type;
18260dc2366fSVenugopal Iyer nd_opt_hdr_t *opt;
18270dc2366fSVenugopal Iyer struct nd_opt_lla *lla = NULL;
182825ec3e3dSEric Cheng
182925ec3e3dSEric Cheng /*
18300dc2366fSVenugopal Iyer * NDP packets do not have extension headers so the ICMPv6 header
18310dc2366fSVenugopal Iyer * must immediately follow the IPv6 header.
183225ec3e3dSEric Cheng */
18330dc2366fSVenugopal Iyer if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
18340dc2366fSVenugopal Iyer return (B_TRUE);
183525ec3e3dSEric Cheng
18360dc2366fSVenugopal Iyer /* ICMPv6 header missing */
18370dc2366fSVenugopal Iyer if ((uchar_t *)&icmp_nd[1] > end)
18380dc2366fSVenugopal Iyer return (B_FALSE);
18390dc2366fSVenugopal Iyer
18400dc2366fSVenugopal Iyer len = end - (uchar_t *)icmp_nd;
18410dc2366fSVenugopal Iyer type = icmp_nd->icmp6_type;
18420dc2366fSVenugopal Iyer
18430dc2366fSVenugopal Iyer switch (type) {
18440dc2366fSVenugopal Iyer case ND_ROUTER_SOLICIT:
18450dc2366fSVenugopal Iyer hdrlen = sizeof (nd_router_solicit_t);
18460dc2366fSVenugopal Iyer break;
18470dc2366fSVenugopal Iyer case ND_ROUTER_ADVERT:
18480dc2366fSVenugopal Iyer hdrlen = sizeof (nd_router_advert_t);
18490dc2366fSVenugopal Iyer break;
18500dc2366fSVenugopal Iyer case ND_NEIGHBOR_SOLICIT:
18510dc2366fSVenugopal Iyer hdrlen = sizeof (nd_neighbor_solicit_t);
18520dc2366fSVenugopal Iyer break;
18530dc2366fSVenugopal Iyer case ND_NEIGHBOR_ADVERT:
18540dc2366fSVenugopal Iyer hdrlen = sizeof (nd_neighbor_advert_t);
18550dc2366fSVenugopal Iyer break;
18560dc2366fSVenugopal Iyer case ND_REDIRECT:
18570dc2366fSVenugopal Iyer hdrlen = sizeof (nd_redirect_t);
18580dc2366fSVenugopal Iyer break;
18590dc2366fSVenugopal Iyer default:
18600dc2366fSVenugopal Iyer return (B_TRUE);
18610dc2366fSVenugopal Iyer }
18620dc2366fSVenugopal Iyer
18630dc2366fSVenugopal Iyer if (len < hdrlen)
18640dc2366fSVenugopal Iyer return (B_FALSE);
18650dc2366fSVenugopal Iyer
18660dc2366fSVenugopal Iyer /* SLLA option checking is needed for RS/RA/NS */
18670dc2366fSVenugopal Iyer opttype = ND_OPT_SOURCE_LINKADDR;
18680dc2366fSVenugopal Iyer
18690dc2366fSVenugopal Iyer switch (type) {
18700dc2366fSVenugopal Iyer case ND_NEIGHBOR_ADVERT: {
18710dc2366fSVenugopal Iyer nd_neighbor_advert_t *na = (nd_neighbor_advert_t *)icmp_nd;
18720dc2366fSVenugopal Iyer
18730dc2366fSVenugopal Iyer if (!ipnospoof_check_v6(mcip, protect, &na->nd_na_target)) {
18740dc2366fSVenugopal Iyer DTRACE_PROBE2(ndp__na__fail,
18750dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, ip6_t *, ip6h);
18760dc2366fSVenugopal Iyer return (B_FALSE);
18770dc2366fSVenugopal Iyer }
18780dc2366fSVenugopal Iyer
18790dc2366fSVenugopal Iyer /* TLLA option for NA */
18800dc2366fSVenugopal Iyer opttype = ND_OPT_TARGET_LINKADDR;
18810dc2366fSVenugopal Iyer break;
18820dc2366fSVenugopal Iyer }
18830dc2366fSVenugopal Iyer case ND_REDIRECT: {
18840dc2366fSVenugopal Iyer /* option checking not needed for RD */
18850dc2366fSVenugopal Iyer return (B_TRUE);
18860dc2366fSVenugopal Iyer }
18870dc2366fSVenugopal Iyer default:
18880dc2366fSVenugopal Iyer break;
18890dc2366fSVenugopal Iyer }
18900dc2366fSVenugopal Iyer
18910dc2366fSVenugopal Iyer if (len == hdrlen) {
18920dc2366fSVenugopal Iyer /* no options, we're done */
18930dc2366fSVenugopal Iyer return (B_TRUE);
18940dc2366fSVenugopal Iyer }
18950dc2366fSVenugopal Iyer opt = (nd_opt_hdr_t *)((uchar_t *)icmp_nd + hdrlen);
18960dc2366fSVenugopal Iyer optlen = len - hdrlen;
18970dc2366fSVenugopal Iyer
18980dc2366fSVenugopal Iyer /* find the option header we need */
18990dc2366fSVenugopal Iyer while (optlen > sizeof (nd_opt_hdr_t)) {
19000dc2366fSVenugopal Iyer if (opt->nd_opt_type == opttype) {
19010dc2366fSVenugopal Iyer lla = (struct nd_opt_lla *)opt;
19020dc2366fSVenugopal Iyer break;
19030dc2366fSVenugopal Iyer }
19040dc2366fSVenugopal Iyer optlen -= 8 * opt->nd_opt_len;
19050dc2366fSVenugopal Iyer opt = (nd_opt_hdr_t *)
19060dc2366fSVenugopal Iyer ((uchar_t *)opt + 8 * opt->nd_opt_len);
19070dc2366fSVenugopal Iyer }
19080dc2366fSVenugopal Iyer if (lla == NULL)
19090dc2366fSVenugopal Iyer return (B_TRUE);
19100dc2366fSVenugopal Iyer
19110dc2366fSVenugopal Iyer addrlen = lla->nd_opt_lla_len * 8 - sizeof (nd_opt_hdr_t);
19120dc2366fSVenugopal Iyer maclen = mcip->mci_mip->mi_info.mi_addr_length;
19130dc2366fSVenugopal Iyer
19140dc2366fSVenugopal Iyer if (addrlen != maclen ||
19150dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr,
19160dc2366fSVenugopal Iyer lla->nd_opt_lla_hdw_addr, maclen) != 0) {
19170dc2366fSVenugopal Iyer DTRACE_PROBE2(ndp__lla__fail,
19180dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, ip6_t *, ip6h);
19190dc2366fSVenugopal Iyer return (B_FALSE);
19200dc2366fSVenugopal Iyer }
19210dc2366fSVenugopal Iyer
19220dc2366fSVenugopal Iyer DTRACE_PROBE2(ndp__lla__ok, mac_client_impl_t *, mcip, ip6_t *, ip6h);
19230dc2366fSVenugopal Iyer return (B_TRUE);
19240dc2366fSVenugopal Iyer }
19250dc2366fSVenugopal Iyer
19260dc2366fSVenugopal Iyer /*
19270dc2366fSVenugopal Iyer * Enforce ip-nospoof protection.
19280dc2366fSVenugopal Iyer */
19290dc2366fSVenugopal Iyer static int
ipnospoof_check(mac_client_impl_t * mcip,mac_protect_t * protect,mblk_t * mp,mac_header_info_t * mhip)19300dc2366fSVenugopal Iyer ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
19310dc2366fSVenugopal Iyer mblk_t *mp, mac_header_info_t *mhip)
19320dc2366fSVenugopal Iyer {
19330dc2366fSVenugopal Iyer size_t hdrsize = mhip->mhi_hdrsize;
19340dc2366fSVenugopal Iyer uint32_t sap = mhip->mhi_bindsap;
19350dc2366fSVenugopal Iyer uchar_t *start, *end;
19360dc2366fSVenugopal Iyer mblk_t *nmp = NULL;
19370dc2366fSVenugopal Iyer int err;
193825ec3e3dSEric Cheng
19390dc2366fSVenugopal Iyer err = get_l3_info(mp, hdrsize, &start, &end, &nmp);
19400dc2366fSVenugopal Iyer if (err != 0) {
19410dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
19420dc2366fSVenugopal Iyer mblk_t *, mp);
19430dc2366fSVenugopal Iyer return (err);
194425ec3e3dSEric Cheng }
19450dc2366fSVenugopal Iyer err = EINVAL;
194625ec3e3dSEric Cheng
194725ec3e3dSEric Cheng switch (sap) {
194825ec3e3dSEric Cheng case ETHERTYPE_IP: {
194925ec3e3dSEric Cheng ipha_t *ipha = (ipha_t *)start;
195025ec3e3dSEric Cheng
19510dc2366fSVenugopal Iyer if (start + sizeof (ipha_t) > end)
195225ec3e3dSEric Cheng goto fail;
195325ec3e3dSEric Cheng
19540dc2366fSVenugopal Iyer if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src))
195525ec3e3dSEric Cheng goto fail;
195625ec3e3dSEric Cheng
1957550b6e40SSowmini Varadhan if (!intercept_dhcpv4_outbound(mcip, ipha, end))
1958550b6e40SSowmini Varadhan goto fail;
195925ec3e3dSEric Cheng break;
196025ec3e3dSEric Cheng }
196125ec3e3dSEric Cheng case ETHERTYPE_ARP: {
196225ec3e3dSEric Cheng arh_t *arh = (arh_t *)start;
196325ec3e3dSEric Cheng uint32_t maclen, hlen, plen, arplen;
196425ec3e3dSEric Cheng ipaddr_t spaddr;
196525ec3e3dSEric Cheng uchar_t *shaddr;
196625ec3e3dSEric Cheng
19670dc2366fSVenugopal Iyer if (start + sizeof (arh_t) > end)
196825ec3e3dSEric Cheng goto fail;
196925ec3e3dSEric Cheng
197025ec3e3dSEric Cheng maclen = mcip->mci_mip->mi_info.mi_addr_length;
197125ec3e3dSEric Cheng hlen = arh->arh_hlen;
197225ec3e3dSEric Cheng plen = arh->arh_plen;
197325ec3e3dSEric Cheng if ((hlen != 0 && hlen != maclen) ||
197425ec3e3dSEric Cheng plen != sizeof (ipaddr_t))
197525ec3e3dSEric Cheng goto fail;
197625ec3e3dSEric Cheng
197725ec3e3dSEric Cheng arplen = sizeof (arh_t) + 2 * hlen + 2 * plen;
19780dc2366fSVenugopal Iyer if (start + arplen > end)
197925ec3e3dSEric Cheng goto fail;
198025ec3e3dSEric Cheng
198125ec3e3dSEric Cheng shaddr = start + sizeof (arh_t);
198225ec3e3dSEric Cheng if (hlen != 0 &&
198325ec3e3dSEric Cheng bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0)
198425ec3e3dSEric Cheng goto fail;
198525ec3e3dSEric Cheng
198625ec3e3dSEric Cheng bcopy(shaddr + hlen, &spaddr, sizeof (spaddr));
19870dc2366fSVenugopal Iyer if (!ipnospoof_check_v4(mcip, protect, &spaddr))
198825ec3e3dSEric Cheng goto fail;
198925ec3e3dSEric Cheng break;
199025ec3e3dSEric Cheng }
19910dc2366fSVenugopal Iyer case ETHERTYPE_IPV6: {
19920dc2366fSVenugopal Iyer ip6_t *ip6h = (ip6_t *)start;
19930dc2366fSVenugopal Iyer
19940dc2366fSVenugopal Iyer if (start + sizeof (ip6_t) > end)
19950dc2366fSVenugopal Iyer goto fail;
19960dc2366fSVenugopal Iyer
19970dc2366fSVenugopal Iyer if (!ipnospoof_check_v6(mcip, protect, &ip6h->ip6_src))
19980dc2366fSVenugopal Iyer goto fail;
19990dc2366fSVenugopal Iyer
20000dc2366fSVenugopal Iyer if (!ipnospoof_check_ndp(mcip, protect, ip6h, end))
20010dc2366fSVenugopal Iyer goto fail;
20020dc2366fSVenugopal Iyer
2003550b6e40SSowmini Varadhan if (!intercept_dhcpv6_outbound(mcip, ip6h, end))
2004550b6e40SSowmini Varadhan goto fail;
20050dc2366fSVenugopal Iyer break;
20060dc2366fSVenugopal Iyer }
20070dc2366fSVenugopal Iyer }
20080dc2366fSVenugopal Iyer freemsg(nmp);
20090dc2366fSVenugopal Iyer return (0);
20100dc2366fSVenugopal Iyer
20110dc2366fSVenugopal Iyer fail:
20120dc2366fSVenugopal Iyer freemsg(nmp);
20130dc2366fSVenugopal Iyer return (err);
20140dc2366fSVenugopal Iyer }
20150dc2366fSVenugopal Iyer
20160dc2366fSVenugopal Iyer static boolean_t
dhcpnospoof_check_cid(mac_protect_t * p,uchar_t * cid,uint_t cidlen)20170dc2366fSVenugopal Iyer dhcpnospoof_check_cid(mac_protect_t *p, uchar_t *cid, uint_t cidlen)
20180dc2366fSVenugopal Iyer {
20190dc2366fSVenugopal Iyer int i;
20200dc2366fSVenugopal Iyer
20210dc2366fSVenugopal Iyer for (i = 0; i < p->mp_cidcnt; i++) {
20220dc2366fSVenugopal Iyer mac_dhcpcid_t *dcid = &p->mp_cids[i];
20230dc2366fSVenugopal Iyer
20240dc2366fSVenugopal Iyer if (dcid->dc_len == cidlen &&
20250dc2366fSVenugopal Iyer bcmp(dcid->dc_id, cid, cidlen) == 0)
20260dc2366fSVenugopal Iyer return (B_TRUE);
20270dc2366fSVenugopal Iyer }
20280dc2366fSVenugopal Iyer return (B_FALSE);
20290dc2366fSVenugopal Iyer }
20300dc2366fSVenugopal Iyer
20310dc2366fSVenugopal Iyer static boolean_t
dhcpnospoof_check_v4(mac_client_impl_t * mcip,mac_protect_t * p,ipha_t * ipha,uchar_t * end)20320dc2366fSVenugopal Iyer dhcpnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *p,
20330dc2366fSVenugopal Iyer ipha_t *ipha, uchar_t *end)
20340dc2366fSVenugopal Iyer {
20350dc2366fSVenugopal Iyer struct dhcp *dh4;
20360dc2366fSVenugopal Iyer uchar_t *cid;
20370dc2366fSVenugopal Iyer uint_t maclen, cidlen = 0;
20380dc2366fSVenugopal Iyer uint8_t optlen;
20390dc2366fSVenugopal Iyer int err;
20400dc2366fSVenugopal Iyer
20410dc2366fSVenugopal Iyer if ((err = get_dhcpv4_info(ipha, end, &dh4)) != 0)
20420dc2366fSVenugopal Iyer return (err == EINVAL);
20430dc2366fSVenugopal Iyer
20440dc2366fSVenugopal Iyer maclen = mcip->mci_mip->mi_info.mi_addr_length;
20450dc2366fSVenugopal Iyer if (dh4->hlen == maclen &&
20460dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, dh4->chaddr, maclen) != 0) {
20470dc2366fSVenugopal Iyer return (B_FALSE);
20480dc2366fSVenugopal Iyer }
20490dc2366fSVenugopal Iyer if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &cid, &optlen) == 0)
20500dc2366fSVenugopal Iyer cidlen = optlen;
20510dc2366fSVenugopal Iyer
20520dc2366fSVenugopal Iyer if (cidlen == 0)
20530dc2366fSVenugopal Iyer return (B_TRUE);
20540dc2366fSVenugopal Iyer
20550dc2366fSVenugopal Iyer if (*cid == ARPHRD_ETHER && cidlen - 1 == maclen &&
20560dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, cid + 1, maclen) == 0)
20570dc2366fSVenugopal Iyer return (B_TRUE);
20580dc2366fSVenugopal Iyer
20590dc2366fSVenugopal Iyer return (dhcpnospoof_check_cid(p, cid, cidlen));
20600dc2366fSVenugopal Iyer }
20610dc2366fSVenugopal Iyer
20620dc2366fSVenugopal Iyer static boolean_t
dhcpnospoof_check_v6(mac_client_impl_t * mcip,mac_protect_t * p,ip6_t * ip6h,uchar_t * end)20630dc2366fSVenugopal Iyer dhcpnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *p,
20640dc2366fSVenugopal Iyer ip6_t *ip6h, uchar_t *end)
20650dc2366fSVenugopal Iyer {
20660dc2366fSVenugopal Iyer dhcpv6_message_t *dh6;
20670dc2366fSVenugopal Iyer dhcpv6_option_t *d6o;
20680dc2366fSVenugopal Iyer uint8_t mtype;
20690dc2366fSVenugopal Iyer uchar_t *cid, *lladdr = NULL;
20700dc2366fSVenugopal Iyer uint_t cidlen, maclen, addrlen = 0;
20710dc2366fSVenugopal Iyer uint16_t cidtype;
20720dc2366fSVenugopal Iyer int err;
20730dc2366fSVenugopal Iyer
20740dc2366fSVenugopal Iyer if ((err = get_dhcpv6_info(ip6h, end, &dh6)) != 0)
20750dc2366fSVenugopal Iyer return (err == EINVAL);
20760dc2366fSVenugopal Iyer
20770dc2366fSVenugopal Iyer /*
20780dc2366fSVenugopal Iyer * We only check client-generated messages.
20790dc2366fSVenugopal Iyer */
20800dc2366fSVenugopal Iyer mtype = dh6->d6m_msg_type;
20810dc2366fSVenugopal Iyer if (mtype == DHCPV6_MSG_ADVERTISE || mtype == DHCPV6_MSG_REPLY ||
20820dc2366fSVenugopal Iyer mtype == DHCPV6_MSG_RECONFIGURE)
20830dc2366fSVenugopal Iyer return (B_TRUE);
20840dc2366fSVenugopal Iyer
20850dc2366fSVenugopal Iyer d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
20860dc2366fSVenugopal Iyer DHCPV6_OPT_CLIENTID, &cidlen);
20870dc2366fSVenugopal Iyer if (d6o == NULL || (uchar_t *)d6o + cidlen > end)
20880dc2366fSVenugopal Iyer return (B_TRUE);
20890dc2366fSVenugopal Iyer
20900dc2366fSVenugopal Iyer cid = (uchar_t *)&d6o[1];
20910dc2366fSVenugopal Iyer cidlen -= sizeof (*d6o);
20920dc2366fSVenugopal Iyer if (cidlen < sizeof (cidtype))
20930dc2366fSVenugopal Iyer return (B_TRUE);
20940dc2366fSVenugopal Iyer
20950dc2366fSVenugopal Iyer bcopy(cid, &cidtype, sizeof (cidtype));
20960dc2366fSVenugopal Iyer cidtype = ntohs(cidtype);
20970dc2366fSVenugopal Iyer if (cidtype == DHCPV6_DUID_LLT && cidlen >= sizeof (duid_llt_t)) {
20980dc2366fSVenugopal Iyer lladdr = cid + sizeof (duid_llt_t);
20990dc2366fSVenugopal Iyer addrlen = cidlen - sizeof (duid_llt_t);
21000dc2366fSVenugopal Iyer }
21010dc2366fSVenugopal Iyer if (cidtype == DHCPV6_DUID_LL && cidlen >= sizeof (duid_ll_t)) {
21020dc2366fSVenugopal Iyer lladdr = cid + sizeof (duid_ll_t);
21030dc2366fSVenugopal Iyer addrlen = cidlen - sizeof (duid_ll_t);
21040dc2366fSVenugopal Iyer }
21050dc2366fSVenugopal Iyer maclen = mcip->mci_mip->mi_info.mi_addr_length;
21060dc2366fSVenugopal Iyer if (lladdr != NULL && addrlen == maclen &&
21070dc2366fSVenugopal Iyer bcmp(mcip->mci_unicast->ma_addr, lladdr, maclen) == 0) {
21080dc2366fSVenugopal Iyer return (B_TRUE);
21090dc2366fSVenugopal Iyer }
21100dc2366fSVenugopal Iyer return (dhcpnospoof_check_cid(p, cid, cidlen));
21110dc2366fSVenugopal Iyer }
21120dc2366fSVenugopal Iyer
21130dc2366fSVenugopal Iyer /*
21140dc2366fSVenugopal Iyer * Enforce dhcp-nospoof protection.
21150dc2366fSVenugopal Iyer */
21160dc2366fSVenugopal Iyer static int
dhcpnospoof_check(mac_client_impl_t * mcip,mac_protect_t * protect,mblk_t * mp,mac_header_info_t * mhip)21170dc2366fSVenugopal Iyer dhcpnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
21180dc2366fSVenugopal Iyer mblk_t *mp, mac_header_info_t *mhip)
21190dc2366fSVenugopal Iyer {
21200dc2366fSVenugopal Iyer size_t hdrsize = mhip->mhi_hdrsize;
21210dc2366fSVenugopal Iyer uint32_t sap = mhip->mhi_bindsap;
21220dc2366fSVenugopal Iyer uchar_t *start, *end;
21230dc2366fSVenugopal Iyer mblk_t *nmp = NULL;
21240dc2366fSVenugopal Iyer int err;
21250dc2366fSVenugopal Iyer
21260dc2366fSVenugopal Iyer err = get_l3_info(mp, hdrsize, &start, &end, &nmp);
21270dc2366fSVenugopal Iyer if (err != 0) {
21280dc2366fSVenugopal Iyer DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
21290dc2366fSVenugopal Iyer mblk_t *, mp);
21300dc2366fSVenugopal Iyer return (err);
21310dc2366fSVenugopal Iyer }
21320dc2366fSVenugopal Iyer err = EINVAL;
21330dc2366fSVenugopal Iyer
21340dc2366fSVenugopal Iyer switch (sap) {
21350dc2366fSVenugopal Iyer case ETHERTYPE_IP: {
21360dc2366fSVenugopal Iyer ipha_t *ipha = (ipha_t *)start;
21370dc2366fSVenugopal Iyer
21380dc2366fSVenugopal Iyer if (start + sizeof (ipha_t) > end)
21390dc2366fSVenugopal Iyer goto fail;
21400dc2366fSVenugopal Iyer
21410dc2366fSVenugopal Iyer if (!dhcpnospoof_check_v4(mcip, protect, ipha, end))
21420dc2366fSVenugopal Iyer goto fail;
21430dc2366fSVenugopal Iyer
21440dc2366fSVenugopal Iyer break;
21450dc2366fSVenugopal Iyer }
21460dc2366fSVenugopal Iyer case ETHERTYPE_IPV6: {
21470dc2366fSVenugopal Iyer ip6_t *ip6h = (ip6_t *)start;
21480dc2366fSVenugopal Iyer
21490dc2366fSVenugopal Iyer if (start + sizeof (ip6_t) > end)
21500dc2366fSVenugopal Iyer goto fail;
21510dc2366fSVenugopal Iyer
21520dc2366fSVenugopal Iyer if (!dhcpnospoof_check_v6(mcip, protect, ip6h, end))
21530dc2366fSVenugopal Iyer goto fail;
21540dc2366fSVenugopal Iyer
215525ec3e3dSEric Cheng break;
215625ec3e3dSEric Cheng }
21570dc2366fSVenugopal Iyer }
21580dc2366fSVenugopal Iyer freemsg(nmp);
215925ec3e3dSEric Cheng return (0);
216025ec3e3dSEric Cheng
216125ec3e3dSEric Cheng fail:
21620dc2366fSVenugopal Iyer /* increment dhcpnospoof stat here */
21630dc2366fSVenugopal Iyer freemsg(nmp);
216425ec3e3dSEric Cheng return (err);
216525ec3e3dSEric Cheng }
216625ec3e3dSEric Cheng
21670dc2366fSVenugopal Iyer /*
2168797f979dSCody Peter Mello * This is called whenever the mac client's mac address changes, to make sure
2169797f979dSCody Peter Mello * we allow use of the new link-local address.
21700dc2366fSVenugopal Iyer */
2171797f979dSCody Peter Mello static void
mac_protect_update_v6_local_addr(mac_client_impl_t * mcip)21720dc2366fSVenugopal Iyer mac_protect_update_v6_local_addr(mac_client_impl_t *mcip)
21730dc2366fSVenugopal Iyer {
2174797f979dSCody Peter Mello uint_t i;
2175797f979dSCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token;
2176797f979dSCody Peter Mello in6_addr_t *v6addr = &mcip->mci_v6_local_addr;
21770dc2366fSVenugopal Iyer in6_addr_t ll_template = {(uint32_t)V6_LINKLOCAL, 0x0, 0x0, 0x0};
21780dc2366fSVenugopal Iyer
2179797f979dSCody Peter Mello for (i = 0; i < 4; i++) {
2180797f979dSCody Peter Mello v6addr->s6_addr32[i] = token->s6_addr32[i] |
2181797f979dSCody Peter Mello ll_template.s6_addr32[i];
2182797f979dSCody Peter Mello }
2183797f979dSCody Peter Mello mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET;
2184797f979dSCody Peter Mello }
21850dc2366fSVenugopal Iyer
2186797f979dSCody Peter Mello /*
2187797f979dSCody Peter Mello * This is called whenever the mac client's mac address changes, to make sure
2188797f979dSCody Peter Mello * that any existing addresses gained via SLAAC are appropriately updated.
2189797f979dSCody Peter Mello */
2190797f979dSCody Peter Mello static void
mac_protect_update_v6_slaac_addr(mac_client_impl_t * mcip)2191797f979dSCody Peter Mello mac_protect_update_v6_slaac_addr(mac_client_impl_t *mcip)
2192797f979dSCody Peter Mello {
2193797f979dSCody Peter Mello void *cookie = NULL;
2194797f979dSCody Peter Mello avl_tree_t temp_tree;
2195797f979dSCody Peter Mello avl_tree_t *ttp = &temp_tree, *sip = &mcip->mci_v6_slaac_ip;
2196797f979dSCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token;
2197797f979dSCody Peter Mello slaac_addr_t *addr = NULL;
2198797f979dSCody Peter Mello
2199797f979dSCody Peter Mello avl_create(ttp, compare_slaac_ip, sizeof (slaac_addr_t),
2200797f979dSCody Peter Mello offsetof(slaac_addr_t, sla_node));
2201797f979dSCody Peter Mello
2202797f979dSCody Peter Mello /* Copy everything over to the temporary tree, and fix the IP address */
2203797f979dSCody Peter Mello while ((addr = avl_destroy_nodes(sip, &cookie)) != NULL) {
2204797f979dSCody Peter Mello VERIFY(insert_slaac_ip(ttp, token, addr) == B_TRUE);
2205797f979dSCody Peter Mello }
2206797f979dSCody Peter Mello
2207797f979dSCody Peter Mello /*
2208797f979dSCody Peter Mello * Now that the tempory tree has all of the modified addresses, we can
2209797f979dSCody Peter Mello * swap them over to the original tree once it's reset.
2210797f979dSCody Peter Mello */
2211797f979dSCody Peter Mello avl_destroy(sip);
2212797f979dSCody Peter Mello avl_create(sip, compare_slaac_ip, sizeof (slaac_addr_t),
2213797f979dSCody Peter Mello offsetof(slaac_addr_t, sla_node));
2214797f979dSCody Peter Mello avl_swap(ttp, sip);
2215797f979dSCody Peter Mello }
2216797f979dSCody Peter Mello
2217797f979dSCody Peter Mello /*
2218797f979dSCody Peter Mello * After the unicast MAC address changes, we need to update the derived token,
2219797f979dSCody Peter Mello * and update the IPv6 addresses that use the token.
2220797f979dSCody Peter Mello */
2221797f979dSCody Peter Mello void
mac_protect_update_mac_token(mac_client_impl_t * mcip)2222797f979dSCody Peter Mello mac_protect_update_mac_token(mac_client_impl_t *mcip)
2223797f979dSCody Peter Mello {
2224797f979dSCody Peter Mello uint_t media = mcip->mci_mip->mi_info.mi_media;
2225797f979dSCody Peter Mello uint8_t *p, *macaddr = mcip->mci_unicast->ma_addr;
2226797f979dSCody Peter Mello in6_addr_t *token = &mcip->mci_v6_mac_token;
2227797f979dSCody Peter Mello
2228797f979dSCody Peter Mello bzero(token, sizeof (in6_addr_t));
2229797f979dSCody Peter Mello p = (uint8_t *)&token->s6_addr32[2];
22300dc2366fSVenugopal Iyer
22310dc2366fSVenugopal Iyer switch (media) {
22320dc2366fSVenugopal Iyer case DL_ETHER:
22330dc2366fSVenugopal Iyer bcopy(macaddr, p, 3);
22340dc2366fSVenugopal Iyer p[0] ^= 0x2;
22350dc2366fSVenugopal Iyer p[3] = 0xff;
22360dc2366fSVenugopal Iyer p[4] = 0xfe;
22370dc2366fSVenugopal Iyer bcopy(macaddr + 3, p + 5, 3);
22380dc2366fSVenugopal Iyer break;
22390dc2366fSVenugopal Iyer case DL_IB:
22400dc2366fSVenugopal Iyer ASSERT(mcip->mci_mip->mi_info.mi_addr_length == 20);
22410dc2366fSVenugopal Iyer bcopy(macaddr + 12, p, 8);
22420dc2366fSVenugopal Iyer p[0] |= 2;
22430dc2366fSVenugopal Iyer break;
22440dc2366fSVenugopal Iyer default:
22450dc2366fSVenugopal Iyer /*
22460dc2366fSVenugopal Iyer * We do not need to generate the local address for link types
22470dc2366fSVenugopal Iyer * that do not support link protection. Wifi pretends to be
2248797f979dSCody Peter Mello * Ethernet so it is covered by the DL_ETHER case (note the
22490dc2366fSVenugopal Iyer * use of mi_media instead of mi_nativemedia).
22500dc2366fSVenugopal Iyer */
22510dc2366fSVenugopal Iyer return;
22520dc2366fSVenugopal Iyer }
22530dc2366fSVenugopal Iyer
2254797f979dSCody Peter Mello mac_protect_update_v6_local_addr(mcip);
2255797f979dSCody Peter Mello mac_protect_update_v6_slaac_addr(mcip);
22560dc2366fSVenugopal Iyer }
22570dc2366fSVenugopal Iyer
2258797f979dSCody Peter Mello
2259797f979dSCody Peter Mello
226025ec3e3dSEric Cheng /*
226125ec3e3dSEric Cheng * Enforce link protection on one packet.
226225ec3e3dSEric Cheng */
226325ec3e3dSEric Cheng static int
mac_protect_check_one(mac_client_impl_t * mcip,mblk_t * mp)226425ec3e3dSEric Cheng mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp)
226525ec3e3dSEric Cheng {
226625ec3e3dSEric Cheng mac_impl_t *mip = mcip->mci_mip;
226725ec3e3dSEric Cheng mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
226825ec3e3dSEric Cheng mac_protect_t *protect;
226925ec3e3dSEric Cheng mac_header_info_t mhi;
227025ec3e3dSEric Cheng uint32_t types;
227125ec3e3dSEric Cheng int err;
227225ec3e3dSEric Cheng
227325ec3e3dSEric Cheng ASSERT(mp->b_next == NULL);
227425ec3e3dSEric Cheng ASSERT(mrp != NULL);
227525ec3e3dSEric Cheng
227625ec3e3dSEric Cheng err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
227725ec3e3dSEric Cheng if (err != 0) {
227825ec3e3dSEric Cheng DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
227925ec3e3dSEric Cheng mblk_t *, mp);
228025ec3e3dSEric Cheng return (err);
228125ec3e3dSEric Cheng }
228225ec3e3dSEric Cheng protect = &mrp->mrp_protect;
228325ec3e3dSEric Cheng types = protect->mp_types;
228425ec3e3dSEric Cheng
228525ec3e3dSEric Cheng if ((types & MPT_MACNOSPOOF) != 0) {
228625ec3e3dSEric Cheng if (mhi.mhi_saddr != NULL &&
228725ec3e3dSEric Cheng bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr,
228825ec3e3dSEric Cheng mip->mi_info.mi_addr_length) != 0) {
22890dc2366fSVenugopal Iyer BUMP_STAT(mcip, macspoofed);
229025ec3e3dSEric Cheng DTRACE_PROBE2(mac__nospoof__fail,
229125ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp);
229225ec3e3dSEric Cheng return (EINVAL);
229325ec3e3dSEric Cheng }
229425ec3e3dSEric Cheng }
229525ec3e3dSEric Cheng if ((types & MPT_RESTRICTED) != 0) {
229625ec3e3dSEric Cheng uint32_t vid = VLAN_ID(mhi.mhi_tci);
229725ec3e3dSEric Cheng uint32_t sap = mhi.mhi_bindsap;
229825ec3e3dSEric Cheng
229925ec3e3dSEric Cheng /*
230025ec3e3dSEric Cheng * ETHERTYPE_VLAN packets are allowed through, provided that
230125ec3e3dSEric Cheng * the vid is not spoofed.
230225ec3e3dSEric Cheng */
230325ec3e3dSEric Cheng if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) {
23040dc2366fSVenugopal Iyer BUMP_STAT(mcip, restricted);
230525ec3e3dSEric Cheng DTRACE_PROBE2(restricted__vid__invalid,
230625ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp);
230725ec3e3dSEric Cheng return (EINVAL);
230825ec3e3dSEric Cheng }
230925ec3e3dSEric Cheng
231025ec3e3dSEric Cheng if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 &&
231125ec3e3dSEric Cheng sap != ETHERTYPE_ARP) {
23120dc2366fSVenugopal Iyer BUMP_STAT(mcip, restricted);
231325ec3e3dSEric Cheng DTRACE_PROBE2(restricted__fail,
231425ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp);
231525ec3e3dSEric Cheng return (EINVAL);
231625ec3e3dSEric Cheng }
231725ec3e3dSEric Cheng }
231825ec3e3dSEric Cheng if ((types & MPT_IPNOSPOOF) != 0) {
23190dc2366fSVenugopal Iyer if ((err = ipnospoof_check(mcip, protect, mp, &mhi)) != 0) {
23200dc2366fSVenugopal Iyer BUMP_STAT(mcip, ipspoofed);
232125ec3e3dSEric Cheng DTRACE_PROBE2(ip__nospoof__fail,
232225ec3e3dSEric Cheng mac_client_impl_t *, mcip, mblk_t *, mp);
232325ec3e3dSEric Cheng return (err);
232425ec3e3dSEric Cheng }
232525ec3e3dSEric Cheng }
23260dc2366fSVenugopal Iyer if ((types & MPT_DHCPNOSPOOF) != 0) {
23270dc2366fSVenugopal Iyer if ((err = dhcpnospoof_check(mcip, protect, mp, &mhi)) != 0) {
23280dc2366fSVenugopal Iyer BUMP_STAT(mcip, dhcpspoofed);
23290dc2366fSVenugopal Iyer DTRACE_PROBE2(dhcp__nospoof__fail,
23300dc2366fSVenugopal Iyer mac_client_impl_t *, mcip, mblk_t *, mp);
23310dc2366fSVenugopal Iyer return (err);
23320dc2366fSVenugopal Iyer }
23330dc2366fSVenugopal Iyer }
233425ec3e3dSEric Cheng return (0);
233525ec3e3dSEric Cheng }
233625ec3e3dSEric Cheng
233725ec3e3dSEric Cheng /*
233825ec3e3dSEric Cheng * Enforce link protection on a packet chain.
233925ec3e3dSEric Cheng * Packets that pass the checks are returned back to the caller.
234025ec3e3dSEric Cheng */
234125ec3e3dSEric Cheng mblk_t *
mac_protect_check(mac_client_handle_t mch,mblk_t * mp)234225ec3e3dSEric Cheng mac_protect_check(mac_client_handle_t mch, mblk_t *mp)
234325ec3e3dSEric Cheng {
234425ec3e3dSEric Cheng mac_client_impl_t *mcip = (mac_client_impl_t *)mch;
234525ec3e3dSEric Cheng mblk_t *ret_mp = NULL, **tailp = &ret_mp, *next;
234625ec3e3dSEric Cheng
234725ec3e3dSEric Cheng /*
234825ec3e3dSEric Cheng * Skip checks if we are part of an aggr.
234925ec3e3dSEric Cheng */
235025ec3e3dSEric Cheng if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
235125ec3e3dSEric Cheng return (mp);
235225ec3e3dSEric Cheng
235325ec3e3dSEric Cheng for (; mp != NULL; mp = next) {
235425ec3e3dSEric Cheng next = mp->b_next;
235525ec3e3dSEric Cheng mp->b_next = NULL;
235625ec3e3dSEric Cheng
235725ec3e3dSEric Cheng if (mac_protect_check_one(mcip, mp) == 0) {
235825ec3e3dSEric Cheng *tailp = mp;
235925ec3e3dSEric Cheng tailp = &mp->b_next;
236025ec3e3dSEric Cheng } else {
236125ec3e3dSEric Cheng freemsg(mp);
236225ec3e3dSEric Cheng }
236325ec3e3dSEric Cheng }
236425ec3e3dSEric Cheng return (ret_mp);
236525ec3e3dSEric Cheng }
236625ec3e3dSEric Cheng
236725ec3e3dSEric Cheng /*
236825ec3e3dSEric Cheng * Check if a particular protection type is enabled.
236925ec3e3dSEric Cheng */
237025ec3e3dSEric Cheng boolean_t
mac_protect_enabled(mac_client_handle_t mch,uint32_t type)237125ec3e3dSEric Cheng mac_protect_enabled(mac_client_handle_t mch, uint32_t type)
237225ec3e3dSEric Cheng {
23730dc2366fSVenugopal Iyer return (MAC_PROTECT_ENABLED((mac_client_impl_t *)mch, type));
23740dc2366fSVenugopal Iyer }
237525ec3e3dSEric Cheng
23760dc2366fSVenugopal Iyer static int
validate_ips(mac_protect_t * p)23770dc2366fSVenugopal Iyer validate_ips(mac_protect_t *p)
23780dc2366fSVenugopal Iyer {
23790dc2366fSVenugopal Iyer uint_t i, j;
23800dc2366fSVenugopal Iyer
23810dc2366fSVenugopal Iyer if (p->mp_ipaddrcnt == MPT_RESET)
23820dc2366fSVenugopal Iyer return (0);
23830dc2366fSVenugopal Iyer
23840dc2366fSVenugopal Iyer if (p->mp_ipaddrcnt > MPT_MAXIPADDR)
23850dc2366fSVenugopal Iyer return (EINVAL);
23860dc2366fSVenugopal Iyer
23870dc2366fSVenugopal Iyer for (i = 0; i < p->mp_ipaddrcnt; i++) {
23880dc2366fSVenugopal Iyer mac_ipaddr_t *addr = &p->mp_ipaddrs[i];
23890dc2366fSVenugopal Iyer
23900dc2366fSVenugopal Iyer /*
2391e03914f9SRobert Mustacchi * The unspecified address is implicitly allowed so there's no
2392e03914f9SRobert Mustacchi * need to add it to the list. Also, validate that the netmask,
2393e03914f9SRobert Mustacchi * if any, is sane for the specific version of IP. A mask of
2394e03914f9SRobert Mustacchi * some kind is always required.
23950dc2366fSVenugopal Iyer */
2396e03914f9SRobert Mustacchi if (addr->ip_netmask == 0)
2397e03914f9SRobert Mustacchi return (EINVAL);
2398e03914f9SRobert Mustacchi
23990dc2366fSVenugopal Iyer if (addr->ip_version == IPV4_VERSION) {
24000dc2366fSVenugopal Iyer if (V4_PART_OF_V6(addr->ip_addr) == INADDR_ANY)
24010dc2366fSVenugopal Iyer return (EINVAL);
2402e03914f9SRobert Mustacchi if (addr->ip_netmask > 32)
2403e03914f9SRobert Mustacchi return (EINVAL);
24040dc2366fSVenugopal Iyer } else if (addr->ip_version == IPV6_VERSION) {
24050dc2366fSVenugopal Iyer if (IN6_IS_ADDR_UNSPECIFIED(&addr->ip_addr))
24060dc2366fSVenugopal Iyer return (EINVAL);
2407e03914f9SRobert Mustacchi
2408e03914f9SRobert Mustacchi if (IN6_IS_ADDR_V4MAPPED_ANY(&addr->ip_addr))
2409e03914f9SRobert Mustacchi return (EINVAL);
2410e03914f9SRobert Mustacchi
2411e03914f9SRobert Mustacchi if (addr->ip_netmask > 128)
2412e03914f9SRobert Mustacchi return (EINVAL);
24130dc2366fSVenugopal Iyer } else {
24140dc2366fSVenugopal Iyer /* invalid ip version */
24150dc2366fSVenugopal Iyer return (EINVAL);
24160dc2366fSVenugopal Iyer }
24170dc2366fSVenugopal Iyer
24180dc2366fSVenugopal Iyer for (j = 0; j < p->mp_ipaddrcnt; j++) {
24190dc2366fSVenugopal Iyer mac_ipaddr_t *addr1 = &p->mp_ipaddrs[j];
24200dc2366fSVenugopal Iyer
24210dc2366fSVenugopal Iyer if (i == j || addr->ip_version != addr1->ip_version)
24220dc2366fSVenugopal Iyer continue;
24230dc2366fSVenugopal Iyer
24240dc2366fSVenugopal Iyer /* found a duplicate */
24250dc2366fSVenugopal Iyer if ((addr->ip_version == IPV4_VERSION &&
24260dc2366fSVenugopal Iyer V4_PART_OF_V6(addr->ip_addr) ==
24270dc2366fSVenugopal Iyer V4_PART_OF_V6(addr1->ip_addr)) ||
24280dc2366fSVenugopal Iyer IN6_ARE_ADDR_EQUAL(&addr->ip_addr,
24290dc2366fSVenugopal Iyer &addr1->ip_addr))
24300dc2366fSVenugopal Iyer return (EINVAL);
24310dc2366fSVenugopal Iyer }
24320dc2366fSVenugopal Iyer }
24330dc2366fSVenugopal Iyer return (0);
24340dc2366fSVenugopal Iyer }
24350dc2366fSVenugopal Iyer
24360dc2366fSVenugopal Iyer /* ARGSUSED */
24370dc2366fSVenugopal Iyer static int
validate_cids(mac_protect_t * p)24380dc2366fSVenugopal Iyer validate_cids(mac_protect_t *p)
24390dc2366fSVenugopal Iyer {
24400dc2366fSVenugopal Iyer uint_t i, j;
24410dc2366fSVenugopal Iyer
24420dc2366fSVenugopal Iyer if (p->mp_cidcnt == MPT_RESET)
24430dc2366fSVenugopal Iyer return (0);
24440dc2366fSVenugopal Iyer
24450dc2366fSVenugopal Iyer if (p->mp_cidcnt > MPT_MAXCID)
24460dc2366fSVenugopal Iyer return (EINVAL);
24470dc2366fSVenugopal Iyer
24480dc2366fSVenugopal Iyer for (i = 0; i < p->mp_cidcnt; i++) {
24490dc2366fSVenugopal Iyer mac_dhcpcid_t *cid = &p->mp_cids[i];
24500dc2366fSVenugopal Iyer
24510dc2366fSVenugopal Iyer if (cid->dc_len > MPT_MAXCIDLEN ||
24520dc2366fSVenugopal Iyer (cid->dc_form != CIDFORM_TYPED &&
24530dc2366fSVenugopal Iyer cid->dc_form != CIDFORM_HEX &&
24540dc2366fSVenugopal Iyer cid->dc_form != CIDFORM_STR))
24550dc2366fSVenugopal Iyer return (EINVAL);
24560dc2366fSVenugopal Iyer
24570dc2366fSVenugopal Iyer for (j = 0; j < p->mp_cidcnt; j++) {
24580dc2366fSVenugopal Iyer mac_dhcpcid_t *cid1 = &p->mp_cids[j];
24590dc2366fSVenugopal Iyer
24600dc2366fSVenugopal Iyer if (i == j || cid->dc_len != cid1->dc_len)
24610dc2366fSVenugopal Iyer continue;
24620dc2366fSVenugopal Iyer
24630dc2366fSVenugopal Iyer /* found a duplicate */
24640dc2366fSVenugopal Iyer if (bcmp(cid->dc_id, cid1->dc_id, cid->dc_len) == 0)
24650dc2366fSVenugopal Iyer return (EINVAL);
24660dc2366fSVenugopal Iyer }
24670dc2366fSVenugopal Iyer }
24680dc2366fSVenugopal Iyer return (0);
246925ec3e3dSEric Cheng }
247025ec3e3dSEric Cheng
247125ec3e3dSEric Cheng /*
247225ec3e3dSEric Cheng * Sanity-checks parameters given by userland.
247325ec3e3dSEric Cheng */
247425ec3e3dSEric Cheng int
mac_protect_validate(mac_resource_props_t * mrp)247525ec3e3dSEric Cheng mac_protect_validate(mac_resource_props_t *mrp)
247625ec3e3dSEric Cheng {
247725ec3e3dSEric Cheng mac_protect_t *p = &mrp->mrp_protect;
24780dc2366fSVenugopal Iyer int err;
247925ec3e3dSEric Cheng
248025ec3e3dSEric Cheng /* check for invalid types */
248125ec3e3dSEric Cheng if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0)
248225ec3e3dSEric Cheng return (EINVAL);
248325ec3e3dSEric Cheng
24840dc2366fSVenugopal Iyer if ((err = validate_ips(p)) != 0)
24850dc2366fSVenugopal Iyer return (err);
248625ec3e3dSEric Cheng
24870dc2366fSVenugopal Iyer if ((err = validate_cids(p)) != 0)
24880dc2366fSVenugopal Iyer return (err);
248925ec3e3dSEric Cheng
249025ec3e3dSEric Cheng return (0);
249125ec3e3dSEric Cheng }
249225ec3e3dSEric Cheng
249325ec3e3dSEric Cheng /*
249425ec3e3dSEric Cheng * Enable/disable link protection.
249525ec3e3dSEric Cheng */
249625ec3e3dSEric Cheng int
mac_protect_set(mac_client_handle_t mch,mac_resource_props_t * mrp)249725ec3e3dSEric Cheng mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp)
249825ec3e3dSEric Cheng {
249925ec3e3dSEric Cheng mac_client_impl_t *mcip = (mac_client_impl_t *)mch;
250025ec3e3dSEric Cheng mac_impl_t *mip = mcip->mci_mip;
250125ec3e3dSEric Cheng uint_t media = mip->mi_info.mi_nativemedia;
250225ec3e3dSEric Cheng int err;
250325ec3e3dSEric Cheng
250425ec3e3dSEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
250525ec3e3dSEric Cheng
250625ec3e3dSEric Cheng /* tunnels are not supported */
250725ec3e3dSEric Cheng if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4)
250825ec3e3dSEric Cheng return (ENOTSUP);
250925ec3e3dSEric Cheng
251025ec3e3dSEric Cheng if ((err = mac_protect_validate(mrp)) != 0)
251125ec3e3dSEric Cheng return (err);
251225ec3e3dSEric Cheng
2513550b6e40SSowmini Varadhan if (err != 0)
2514550b6e40SSowmini Varadhan return (err);
2515550b6e40SSowmini Varadhan
251625ec3e3dSEric Cheng mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE);
2517550b6e40SSowmini Varadhan i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ?
2518550b6e40SSowmini Varadhan mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS);
251925ec3e3dSEric Cheng return (0);
252025ec3e3dSEric Cheng }
252125ec3e3dSEric Cheng
252225ec3e3dSEric Cheng void
mac_protect_update(mac_resource_props_t * new,mac_resource_props_t * curr)252325ec3e3dSEric Cheng mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr)
252425ec3e3dSEric Cheng {
252525ec3e3dSEric Cheng mac_protect_t *np = &new->mrp_protect;
252625ec3e3dSEric Cheng mac_protect_t *cp = &curr->mrp_protect;
252725ec3e3dSEric Cheng uint32_t types = np->mp_types;
252825ec3e3dSEric Cheng
252925ec3e3dSEric Cheng if (types == MPT_RESET) {
253025ec3e3dSEric Cheng cp->mp_types = 0;
253125ec3e3dSEric Cheng curr->mrp_mask &= ~MRP_PROTECT;
253225ec3e3dSEric Cheng } else {
253325ec3e3dSEric Cheng if (types != 0) {
253425ec3e3dSEric Cheng cp->mp_types = types;
253525ec3e3dSEric Cheng curr->mrp_mask |= MRP_PROTECT;
253625ec3e3dSEric Cheng }
253725ec3e3dSEric Cheng }
253825ec3e3dSEric Cheng if (np->mp_ipaddrcnt != 0) {
25390dc2366fSVenugopal Iyer if (np->mp_ipaddrcnt <= MPT_MAXIPADDR) {
254025ec3e3dSEric Cheng bcopy(np->mp_ipaddrs, cp->mp_ipaddrs,
254125ec3e3dSEric Cheng sizeof (cp->mp_ipaddrs));
254225ec3e3dSEric Cheng cp->mp_ipaddrcnt = np->mp_ipaddrcnt;
254325ec3e3dSEric Cheng } else if (np->mp_ipaddrcnt == MPT_RESET) {
254425ec3e3dSEric Cheng bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs));
254525ec3e3dSEric Cheng cp->mp_ipaddrcnt = 0;
254625ec3e3dSEric Cheng }
254725ec3e3dSEric Cheng }
25480dc2366fSVenugopal Iyer if (np->mp_cidcnt != 0) {
25490dc2366fSVenugopal Iyer if (np->mp_cidcnt <= MPT_MAXCID) {
25500dc2366fSVenugopal Iyer bcopy(np->mp_cids, cp->mp_cids, sizeof (cp->mp_cids));
25510dc2366fSVenugopal Iyer cp->mp_cidcnt = np->mp_cidcnt;
25520dc2366fSVenugopal Iyer } else if (np->mp_cidcnt == MPT_RESET) {
25530dc2366fSVenugopal Iyer bzero(cp->mp_cids, sizeof (cp->mp_cids));
25540dc2366fSVenugopal Iyer cp->mp_cidcnt = 0;
25550dc2366fSVenugopal Iyer }
25560dc2366fSVenugopal Iyer }
25570dc2366fSVenugopal Iyer }
25580dc2366fSVenugopal Iyer
25590dc2366fSVenugopal Iyer void
mac_protect_init(mac_client_impl_t * mcip)25600dc2366fSVenugopal Iyer mac_protect_init(mac_client_impl_t *mcip)
25610dc2366fSVenugopal Iyer {
25620dc2366fSVenugopal Iyer mutex_init(&mcip->mci_protect_lock, NULL, MUTEX_DRIVER, NULL);
25630dc2366fSVenugopal Iyer mcip->mci_protect_flags = 0;
25640dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0;
25650dc2366fSVenugopal Iyer avl_create(&mcip->mci_v4_pending_txn, compare_dhcpv4_xid,
25660dc2366fSVenugopal Iyer sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node));
25670dc2366fSVenugopal Iyer avl_create(&mcip->mci_v4_completed_txn, compare_dhcpv4_cid,
25680dc2366fSVenugopal Iyer sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node));
25690dc2366fSVenugopal Iyer avl_create(&mcip->mci_v4_dyn_ip, compare_dhcpv4_ip,
25700dc2366fSVenugopal Iyer sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_ipnode));
25710dc2366fSVenugopal Iyer avl_create(&mcip->mci_v6_pending_txn, compare_dhcpv6_xid,
25720dc2366fSVenugopal Iyer sizeof (dhcpv6_txn_t), offsetof(dhcpv6_txn_t, dt_node));
25730dc2366fSVenugopal Iyer avl_create(&mcip->mci_v6_cid, compare_dhcpv6_cid,
25740dc2366fSVenugopal Iyer sizeof (dhcpv6_cid_t), offsetof(dhcpv6_cid_t, dc_node));
25750dc2366fSVenugopal Iyer avl_create(&mcip->mci_v6_dyn_ip, compare_dhcpv6_ip,
25760dc2366fSVenugopal Iyer sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node));
2577797f979dSCody Peter Mello avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip,
2578797f979dSCody Peter Mello sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node));
257910a40492SRobert Mustacchi
258010a40492SRobert Mustacchi if (mcip->mci_state_flags & MCIS_IS_VNIC)
258110a40492SRobert Mustacchi mcip->mci_protect_flags |= MPT_FLAG_PROMISC_FILTERED;
25820dc2366fSVenugopal Iyer }
25830dc2366fSVenugopal Iyer
25840dc2366fSVenugopal Iyer void
mac_protect_fini(mac_client_impl_t * mcip)25850dc2366fSVenugopal Iyer mac_protect_fini(mac_client_impl_t *mcip)
25860dc2366fSVenugopal Iyer {
25870dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v6_dyn_ip);
25880dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v6_cid);
25890dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v6_pending_txn);
25900dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v4_dyn_ip);
25910dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v4_completed_txn);
25920dc2366fSVenugopal Iyer avl_destroy(&mcip->mci_v4_pending_txn);
2593797f979dSCody Peter Mello avl_destroy(&mcip->mci_v6_slaac_ip);
25940dc2366fSVenugopal Iyer mcip->mci_txn_cleanup_tid = 0;
25950dc2366fSVenugopal Iyer mcip->mci_protect_flags = 0;
25960dc2366fSVenugopal Iyer mutex_destroy(&mcip->mci_protect_lock);
259725ec3e3dSEric Cheng }
2598550b6e40SSowmini Varadhan
2599550b6e40SSowmini Varadhan static boolean_t
allowed_ips_set(mac_resource_props_t * mrp,uint32_t af)2600550b6e40SSowmini Varadhan allowed_ips_set(mac_resource_props_t *mrp, uint32_t af)
2601550b6e40SSowmini Varadhan {
2602550b6e40SSowmini Varadhan int i;
2603550b6e40SSowmini Varadhan
2604550b6e40SSowmini Varadhan for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) {
2605550b6e40SSowmini Varadhan if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af)
2606550b6e40SSowmini Varadhan return (B_TRUE);
2607550b6e40SSowmini Varadhan }
2608550b6e40SSowmini Varadhan return (B_FALSE);
2609550b6e40SSowmini Varadhan }
2610550b6e40SSowmini Varadhan
261189c6130dSSowmini Varadhan mac_protect_t *
mac_protect_get(mac_handle_t mh)261289c6130dSSowmini Varadhan mac_protect_get(mac_handle_t mh)
2613550b6e40SSowmini Varadhan {
2614550b6e40SSowmini Varadhan mac_impl_t *mip = (mac_impl_t *)mh;
2615550b6e40SSowmini Varadhan
261689c6130dSSowmini Varadhan return (&mip->mi_resource_props.mrp_protect);
2617550b6e40SSowmini Varadhan }
2618