/* libdhcp/dhcp_common.h - Base definitions for the DHCP protocol
 *
 * Copyright (C) 2024  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 *
 * -----------------------------------------------------------------------------
 * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.c
 *
 * Copyright (c)  2013, WIZnet Co., LTD.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <ORGANIZATION> nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * -----------------------------------------------------------------------------
 * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/license.txt
 *
 * Copyright (c) 2014 WIZnet Co.,Ltd.
 * Copyright (c) WIZnet ioLibrary Project.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * SPDX-License-Identifier: MIT
 */

#ifndef _LIBDHCP_DHCP_COMMON_H_
#define _LIBDHCP_DHCP_COMMON_H_

#include <libmisc/endian.h>
#include <libmisc/log.h> /* for const_byte_str() */

/* Config *********************************************************************/

#include "config.h"

#ifndef CONFIG_DHCP_OPT_SIZE
	#error config.h must define CONFIG_DHCP_OPT_SIZE (minimum 312)
#endif

/* RFC 2131 definitions *******************************************************/

/**
 * "A DHCP client must be prepared to receive DHCP messages with an
 * 'options' field of at least length 312 octets." --
 * https://datatracker.ietf.org/doc/html/rfc2131#page-9
 */
#define DHCP_MSG_MIN_MAX_OPT_SIZE       312
static_assert(CONFIG_DHCP_OPT_SIZE >= DHCP_MSG_MIN_MAX_OPT_SIZE);

/** https://datatracker.ietf.org/doc/html/rfc2131#page-9 */
struct dhcp_msg {
	uint8_t                 op;         /* DHCP_OP_{x} */
	uint8_t                 htype;      /* DHCP_HTYPE_{x} */
	uint8_t                 hlen;       /* length of ->chaddr (`sizeof(struct net_eth_addr)`) */
	uint8_t                 hops;

	uint32be_t              xid;        /* transaction ID */

	uint16be_t              secs;
	uint16be_t              flags;      /* DHCP_FLAG_{x} */

	struct net_ip4_addr     ciaddr;     /* Request IP to DHCP sever */
	struct net_ip4_addr     yiaddr;     /* Offered IP from DHCP server */
	struct net_ip4_addr     siaddr;     /* next-server IP address */
	struct net_ip4_addr     giaddr;     /* relay-agent IP address */
	uint8_t                 chaddr[16]; /* client hardware (MAC) address */
	uint8_t                 sname[64];  /* server name (not used) */
	uint8_t                 file[128];  /* boot file name (not used) */

	uint8_t                 options[CONFIG_DHCP_OPT_SIZE];
};
static_assert(offsetof(struct dhcp_msg, options) == 236);
#define DHCP_MSG_BASE_SIZE      offsetof(struct dhcp_msg, options)

/** https://datatracker.ietf.org/doc/html/rfc2131#page-10 */
#define DHCP_OP_BOOTREQUEST     1
#define DHCP_OP_BOOTREPLY       2

/** https://datatracker.ietf.org/doc/html/rfc2131#page-11 */
#define DHCP_FLAG_BROADCAST     0x8000 /* The BROADCAST flag (set by the client)
                                        * indicates that the server should use
                                        * broadcast-IP replies instead of
                                        * unicast-IP because the client is not
                                        * yet capable of receiving unicast-IP at
                                        * this stage of configuration.  */

/** https://datatracker.ietf.org/doc/html/rfc2131#section-3 */
static const uint8_t dhcp_magic_cookie[] = {99, 130, 83, 99};

/** https://datatracker.ietf.org/doc/html/rfc2131#section-3.3 */
#define DHCP_INFINITY           0xffffffff

/* IANA assignments ***********************************************************/

/**
 * Port Numbers
 * https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
 */
#define DHCP_PORT_SERVER                ((uint16_t)67)
#define DHCP_PORT_CLIENT                ((uint16_t)68)

/**
 * Hardware Types
 * https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2
 */
#define DHCP_HTYPE_ETHERNET             ((uint8_t)  1)

/**
 * DHCP Options
 * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options
 */
#define DHCP_OPT_PAD                    ((uint8_t)  0) /* RFC2132: length: 0; meaning: None */
#define DHCP_OPT_SUBNET_MASK            ((uint8_t)  1) /* RFC2132: length: 4; meaning: Subnet Mask Value */
#define DHCP_OPT_TIME_OFFSET            ((uint8_t)  2) /* RFC2132: length: 4; meaning: Time Offset in Seconds from UTC (note: deprecated by 100 and 101 */
#define DHCP_OPT_ROUTER                 ((uint8_t)  3) /* RFC2132: length: N; meaning: N/4 Router addresses */
#define DHCP_OPT_TIME_SERVER            ((uint8_t)  4) /* RFC2132: length: N; meaning: N/4 Timeserver addresses */
#define DHCP_OPT_NAME_SERVER            ((uint8_t)  5) /* RFC2132: length: N; meaning: N/4 IEN-116 Server addresses */
#define DHCP_OPT_DOMAIN_SERVER          ((uint8_t)  6) /* RFC2132: length: N; meaning: N/4 DNS Server addresses */
#define DHCP_OPT_LOG_SERVER             ((uint8_t)  7) /* RFC2132: length: N; meaning: N/4 Logging Server addresses */
#define DHCP_OPT_QUOTES_SERVER          ((uint8_t)  8) /* RFC2132: length: N; meaning: N/4 Quotes Server addresses */
#define DHCP_OPT_LPR_SERVER             ((uint8_t)  9) /* RFC2132: length: N; meaning: N/4 Printer Server addresses */
#define DHCP_OPT_IMPRESS_SERVER         ((uint8_t) 10) /* RFC2132: length: N; meaning: N/4 Impress Server addresses */
#define DHCP_OPT_RLP_SERVER             ((uint8_t) 11) /* RFC2132: length: N; meaning: N/4 RLP Server addresses */
#define DHCP_OPT_HOSTNAME               ((uint8_t) 12) /* RFC2132: length: N; meaning: Hostname string */
#define DHCP_OPT_BOOT_FILE_SIZE         ((uint8_t) 13) /* RFC2132: length: 2; meaning: Size of boot file in 512 byte chunks */
#define DHCP_OPT_MERIT_DUMP_FILE        ((uint8_t) 14) /* RFC2132: length: N; meaning: Client to dump and name the file to dump it to */
#define DHCP_OPT_DOMAIN_NAME            ((uint8_t) 15) /* RFC2132: length: N; meaning: The DNS domain name of the client */
#define DHCP_OPT_SWAP_SERVER            ((uint8_t) 16) /* RFC2132: length: N; meaning: Swap Server address */
#define DHCP_OPT_ROOT_PATH              ((uint8_t) 17) /* RFC2132: length: N; meaning: Path name for root disk */
#define DHCP_OPT_EXTENSION_FILE         ((uint8_t) 18) /* RFC2132: length: N; meaning: Path name for more BOOTP info */
#define DHCP_OPT_FORWARD_ONOFF          ((uint8_t) 19) /* RFC2132: length: 1; meaning: Enable/Disable IP Forwarding */
#define DHCP_OPT_SRCRTE_ONOFF           ((uint8_t) 20) /* RFC2132: length: 1; meaning: Enable/Disable Source Routing */
#define DHCP_OPT_POLICY_FILTER          ((uint8_t) 21) /* RFC2132: length: N; meaning: Routing Policy Filters */
#define DHCP_OPT_MAX_DG_ASSEMBLY        ((uint8_t) 22) /* RFC2132: length: 2; meaning: Max Datagram Reassembly Size */
#define DHCP_OPT_DEFAULT_IP_TTL         ((uint8_t) 23) /* RFC2132: length: 1; meaning: Default IP Time to Live */
#define DHCP_OPT_MTU_TIMEOUT            ((uint8_t) 24) /* RFC2132: length: 4; meaning: Path MTU Aging Timeout */
#define DHCP_OPT_MTU_PLATEAU            ((uint8_t) 25) /* RFC2132: length: N; meaning: Path MTU Plateau Table */
#define DHCP_OPT_MTU_INTERFACE          ((uint8_t) 26) /* RFC2132: length: 2; meaning: Interface MTU Size */
#define DHCP_OPT_MTU_SUBNET             ((uint8_t) 27) /* RFC2132: length: 1; meaning: All Subnets are Local */
#define DHCP_OPT_BROADCAST_ADDRESS      ((uint8_t) 28) /* RFC2132: length: 4; meaning: Broadcast Address */
#define DHCP_OPT_MASK_DISCOVERY         ((uint8_t) 29) /* RFC2132: length: 1; meaning: Perform Mask Discovery */
#define DHCP_OPT_MASK_SUPPLIER          ((uint8_t) 30) /* RFC2132: length: 1; meaning: Provide Mask to Others */
#define DHCP_OPT_ROUTER_DISCOVERY       ((uint8_t) 31) /* RFC2132: length: 1; meaning: Perform Router Discovery */
#define DHCP_OPT_ROUTER_REQUEST         ((uint8_t) 32) /* RFC2132: length: 4; meaning: Router Solicitation Address */
#define DHCP_OPT_STATIC_ROUTE           ((uint8_t) 33) /* RFC2132: length: N; meaning: Static Routing Table */
#define DHCP_OPT_TRAILERS               ((uint8_t) 34) /* RFC2132: length: 1; meaning: Trailer Encapsulation */
#define DHCP_OPT_ARP_TIMEOUT            ((uint8_t) 35) /* RFC2132: length: 4; meaning: ARP Cache Timeout */
#define DHCP_OPT_ETHERNET               ((uint8_t) 36) /* RFC2132: length: 1; meaning: Ethernet Encapsulation */
#define DHCP_OPT_DEFAULT_TCP_TTL        ((uint8_t) 37) /* RFC2132: length: 1; meaning: Default TCP Time to Live */
#define DHCP_OPT_KEEPALIVE_TIME         ((uint8_t) 38) /* RFC2132: length: 4; meaning: TCP Keepalive Interval */
#define DHCP_OPT_KEEPALIVE_DATA         ((uint8_t) 39) /* RFC2132: length: 1; meaning: TCP Keepalive Garbage */
#define DHCP_OPT_NIS_DOMAIN             ((uint8_t) 40) /* RFC2132: length: N; meaning: NIS Domain Name */
#define DHCP_OPT_NIS_SERVERS            ((uint8_t) 41) /* RFC2132: length: N; meaning: NIS Server Addresses */
#define DHCP_OPT_NTP_SERVERS            ((uint8_t) 42) /* RFC2132: length: N; meaning: NTP Server Addresses */
#define DHCP_OPT_VENDOR_SPECIFIC        ((uint8_t) 43) /* RFC2132: length: N; meaning: Vendor Specific Information */
#define DHCP_OPT_NETBIOS_NAME_SRV       ((uint8_t) 44) /* RFC2132: length: N; meaning: NETBIOS Name Servers */
#define DHCP_OPT_NETBIOS_DIST_SRV       ((uint8_t) 45) /* RFC2132: length: N; meaning: NETBIOS Datagram Distribution */
#define DHCP_OPT_NETBIOS_NODE_TYPE      ((uint8_t) 46) /* RFC2132: length: 1; meaning: NETBIOS Node Type */
#define DHCP_OPT_NETBIOS_SCOPE          ((uint8_t) 47) /* RFC2132: length: N; meaning: NETBIOS Scope */
#define DHCP_OPT_X_WINDOW_FONT          ((uint8_t) 48) /* RFC2132: length: N; meaning: X Window Font Server */
#define DHCP_OPT_X_WINDOW_MANAGER       ((uint8_t) 49) /* RFC2132: length: N; meaning: X Window Display Manager */
#define DHCP_OPT_ADDRESS_REQUEST        ((uint8_t) 50) /* RFC2132: length: 4; meaning: Requested IP Address */
#define DHCP_OPT_ADDRESS_TIME           ((uint8_t) 51) /* RFC2132: length: 4; meaning: IP Address Lease Time */
#define DHCP_OPT_OVERLOAD               ((uint8_t) 52) /* RFC2132: length: 1; meaning: Overload "sname" or "file */
#define DHCP_OPT_DHCP_MSG_TYPE          ((uint8_t) 53) /* RFC2132: length: 1; meaning: DHCP Message Type */
#define DHCP_OPT_DHCP_SERVER_ID         ((uint8_t) 54) /* RFC2132: length: 4; meaning: DHCP Server Identification */
#define DHCP_OPT_PARAMETER_LIST         ((uint8_t) 55) /* RFC2132: length: N; meaning: Parameter Request List */
#define DHCP_OPT_DHCP_MESSAGE           ((uint8_t) 56) /* RFC2132: length: N; meaning: DHCP Error Message */
#define DHCP_OPT_DHCP_MAX_MSG_SIZE      ((uint8_t) 57) /* RFC2132: length: 2; meaning: DHCP Maximum Message Size */ /* note: includes IP & UDP headers (20 and 8, respectively) */
#define DHCP_OPT_RENEWAL_TIME           ((uint8_t) 58) /* RFC2132: length: 4; meaning: DHCP Renewal (T1) Time */
#define DHCP_OPT_REBINDING_TIME         ((uint8_t) 59) /* RFC2132: length: 4; meaning: DHCP Rebinding (T2) Time */
#define DHCP_OPT_CLASS_ID               ((uint8_t) 60) /* RFC2132: length: N; meaning: Class Identifier */
#define DHCP_OPT_CLIENT_ID              ((uint8_t) 61) /* RFC2132: length: N; meaning: Client Identifier */
                                                       /* ... */
#define DHCP_OPT_END                    ((uint8_t)255) /* RFC2132: length: 0; meaning: None */

static inline bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
	switch (opt) {
	/* RFC 2132 */
	case DHCP_OPT_PAD:               return len == 0;
	case DHCP_OPT_SUBNET_MASK:       return len == 4;
	case DHCP_OPT_TIME_OFFSET:       return len == 4;
	case DHCP_OPT_ROUTER:            return len >= 4 && len % 4 == 0;
	case DHCP_OPT_TIME_SERVER:       return len >= 4 && len % 4 == 0;
	case DHCP_OPT_NAME_SERVER:       return len >= 4 && len % 4 == 0;
	case DHCP_OPT_DOMAIN_SERVER:     return len >= 4 && len % 4 == 0;
	case DHCP_OPT_LOG_SERVER:        return len >= 4 && len % 4 == 0;
	case DHCP_OPT_QUOTES_SERVER:     return len >= 4 && len % 4 == 0;
	case DHCP_OPT_LPR_SERVER:        return len >= 4 && len % 4 == 0;
	case DHCP_OPT_IMPRESS_SERVER:    return len >= 4 && len % 4 == 0;
	case DHCP_OPT_RLP_SERVER:        return len >= 4 && len % 4 == 0;
	case DHCP_OPT_HOSTNAME:          return len >= 1;
	case DHCP_OPT_BOOT_FILE_SIZE:    return len == 2;
	case DHCP_OPT_MERIT_DUMP_FILE:   return len >= 1;
	case DHCP_OPT_DOMAIN_NAME:       return len >= 1;
	case DHCP_OPT_SWAP_SERVER:       return len == 4; /* IANA says length is "N", but RFC 2132 says "length is 4"; likely releated to errata ID 487 */
	case DHCP_OPT_ROOT_PATH:         return len >= 1;
	case DHCP_OPT_EXTENSION_FILE:    return len >= 1;
	case DHCP_OPT_FORWARD_ONOFF:     return len == 1;
	case DHCP_OPT_SRCRTE_ONOFF:      return len == 1;
	case DHCP_OPT_POLICY_FILTER:     return len >= 8 && len % 8 == 0;
	case DHCP_OPT_MAX_DG_ASSEMBLY:   return len == 2;
	case DHCP_OPT_DEFAULT_IP_TTL:    return len == 1;
	case DHCP_OPT_MTU_TIMEOUT:       return len == 4;
	case DHCP_OPT_MTU_PLATEAU:       return len >= 2 && len % 2 == 0;
	case DHCP_OPT_MTU_INTERFACE:     return len == 2;
	case DHCP_OPT_MTU_SUBNET:        return len == 1;
	case DHCP_OPT_BROADCAST_ADDRESS: return len == 4;
	case DHCP_OPT_MASK_DISCOVERY:    return len == 1;
	case DHCP_OPT_MASK_SUPPLIER:     return len == 1;
	case DHCP_OPT_ROUTER_DISCOVERY:  return len == 1;
	case DHCP_OPT_ROUTER_REQUEST:    return len == 4;
	case DHCP_OPT_STATIC_ROUTE:      return len >= 8 && len % 8 == 0;
	case DHCP_OPT_TRAILERS:          return len == 1;
	case DHCP_OPT_ARP_TIMEOUT:       return len == 4;
	case DHCP_OPT_ETHERNET:          return len == 1;
	case DHCP_OPT_DEFAULT_TCP_TTL:   return len == 1;
	case DHCP_OPT_KEEPALIVE_TIME:    return len == 4;
	case DHCP_OPT_KEEPALIVE_DATA:    return len == 1;
	case DHCP_OPT_NIS_DOMAIN:        return len >= 1;
	case DHCP_OPT_NIS_SERVERS:       return len >= 4 && len % 4 == 0;
	case DHCP_OPT_NTP_SERVERS:       return len >= 4 && len % 4 == 0;
	case DHCP_OPT_VENDOR_SPECIFIC:   return len >= 1;
	case DHCP_OPT_NETBIOS_NAME_SRV:  return len >= 4 && len % 4 == 0;
	case DHCP_OPT_NETBIOS_DIST_SRV:  return len >= 4 && len % 4 == 0;
	case DHCP_OPT_NETBIOS_NODE_TYPE: return len == 1;
	case DHCP_OPT_NETBIOS_SCOPE:     return len >= 1;
	case DHCP_OPT_X_WINDOW_FONT:     return len >= 4 && len % 4 == 0;
	case DHCP_OPT_X_WINDOW_MANAGER:  return len >= 4 && len % 4 == 0;
	case DHCP_OPT_ADDRESS_REQUEST:   return len == 4;
	case DHCP_OPT_ADDRESS_TIME:      return len == 4;
	case DHCP_OPT_OVERLOAD:          return len == 1;
	case DHCP_OPT_DHCP_MSG_TYPE:     return len == 1;
	case DHCP_OPT_DHCP_SERVER_ID:    return len == 4;
	case DHCP_OPT_PARAMETER_LIST:    return len >= 1;
	case DHCP_OPT_DHCP_MESSAGE:      return len >= 1;
	case DHCP_OPT_DHCP_MAX_MSG_SIZE: return len == 2;
	case DHCP_OPT_RENEWAL_TIME:      return len == 4;
	case DHCP_OPT_REBINDING_TIME:    return len == 4;
	case DHCP_OPT_CLASS_ID:          return len >= 1;
	case DHCP_OPT_CLIENT_ID:         return len >= 2;

	/* RFC 2132 */
	case DHCP_OPT_END:               return len == 0;

	/* Unrecognized */
	default:
		return true;
	}
};

/**
 * DHCP Message Type 53 Values
 * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53
 */
#define DHCP_MSGTYP_DISCOVER            ((uint8_t)  1) /* RFC2132, client->server */
#define DHCP_MSGTYP_OFFER               ((uint8_t)  2) /* RFC2132, server->client */
#define DHCP_MSGTYP_REQUEST             ((uint8_t)  3) /* RFC2132, client->server */
#define DHCP_MSGTYP_DECLINE             ((uint8_t)  4) /* RFC2132, client->server */
#define DHCP_MSGTYP_ACK                 ((uint8_t)  5) /* RFC2132, server->client */
#define DHCP_MSGTYP_NAK                 ((uint8_t)  6) /* RFC2132, server->client */
#define DHCP_MSGTYP_RELEASE             ((uint8_t)  7) /* RFC2132, client->server */
#define DHCP_MSGTYP_INFORM              ((uint8_t)  8) /* RFC2132, client->server */

static const char *dhcp_msgtyp_str(uint8_t typ) {
	switch (typ) {
	case DHCP_MSGTYP_DISCOVER: return "DHCP_MSGTYP_DISCOVER";
	case DHCP_MSGTYP_OFFER:    return "DHCP_MSGTYP_OFFER";
	case DHCP_MSGTYP_REQUEST:  return "DHCP_MSGTYP_REQUEST";
	case DHCP_MSGTYP_DECLINE:  return "DHCP_MSGTYP_DECLINE";
	case DHCP_MSGTYP_ACK:      return "DHCP_MSGTYP_ACK";
	case DHCP_MSGTYP_NAK:      return "DHCP_MSGTYP_NAK";
	case DHCP_MSGTYP_RELEASE:  return "DHCP_MSGTYP_RELEASE";
	case DHCP_MSGTYP_INFORM:   return "DHCP_MSGTYP_INFORM";
	default:                   return const_byte_str(typ);
	}
}

#endif /* _LIBDHCP_DHCP_COMMON_H_ */