1 /* 2 * Copyright (c) 2000-2022 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 /* 29 * Copyright (c) 1980, 1986, 1993 30 * The Regents of the University of California. All rights reserved. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by the University of 43 * California, Berkeley and its contributors. 44 * 4. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 * 60 * @(#)route.h 8.3 (Berkeley) 4/19/94 61 * $FreeBSD: src/sys/net/route.h,v 1.36.2.1 2000/08/16 06:14:23 jayanth Exp $ 62 */ 63 64 #ifndef _NET_ROUTE_PRIVATE_H_ 65 #define _NET_ROUTE_PRIVATE_H_ 66 #include <net/route.h> 67 #include <stdint.h> 68 #include <sys/types.h> 69 #include <sys/socket.h> 70 #include <uuid/uuid.h> 71 72 struct route_old { 73 void *ro_rt; 74 uint32_t ro_flags; 75 struct sockaddr ro_dst; 76 }; 77 78 #ifdef BSD_KERNEL_PRIVATE 79 #include <kern/locks.h> 80 #include <net/radix.h> 81 #include <net/if_llatbl.h> 82 #include <sys/eventhandler.h> 83 #include <net/if_dl.h> 84 85 extern boolean_t trigger_v6_defrtr_select; 86 /* 87 * Kernel resident routing tables. 88 * 89 * The routing tables are initialized when interface addresses 90 * are set by making entries for all directly connected interfaces. 91 */ 92 93 /* forward declarations */ 94 struct ifnet_llreach_info; 95 struct rt_reach_info; 96 97 /* 98 * IP route structure 99 * 100 * A route consists of a destination address and a reference 101 * to a routing entry. These are often held by protocols 102 * in their control blocks, e.g. inpcb. 103 */ 104 struct route { 105 /* 106 * N.B: struct route must begin with ro_{rt, lle, srcia, flags} 107 * because the code does some casts of a 'struct route_in6 *' 108 * to a 'struct route *'. 109 */ 110 struct rtentry *ro_rt; 111 struct llentry *ro_lle; 112 113 struct ifaddr *ro_srcia; 114 uint32_t ro_flags; /* route flags (see below) */ 115 #if __has_ptrcheck 116 struct sockaddr_in ro_dst; 117 #else 118 struct sockaddr ro_dst; 119 #endif 120 }; 121 122 #define ROF_SRCIF_SELECTED 0x0001 /* source interface was selected */ 123 #if 0 124 /* XXX These will be used in the changes coming in later */ 125 #define ROF_NORTREF 0x0002 /* doesn't hold reference on ro_rt */ 126 #define ROF_L2_ME 0x0004 /* dst L2 addr is our address */ 127 #define ROF_MAY_LOOP 0x0008 /* dst may require loop copy */ 128 #define ROF_HAS_HEADER 0x0010 /* mbuf already have its header prepended */ 129 #define ROF_REJECT 0x0020 /* Destination is reject */ 130 #define ROF_BLACKHOLE 0x0040 /* Destination is blackhole */ 131 #define ROF_HAS_GW 0x0080 /* Destination has GW */ 132 #endif 133 #define ROF_LLE_CACHE 0x0100 /* Cache link layer */ 134 135 #define ROUTE_UNUSABLE(_ro) \ 136 ((_ro)->ro_rt == NULL || \ 137 ((_ro)->ro_rt->rt_flags & (RTF_UP|RTF_CONDEMNED)) != RTF_UP || \ 138 RT_GENID_OUTOFSYNC((_ro)->ro_rt)) 139 140 #define _ROUTE_RELEASE_COMMON(_ro, _rnh_locked) do { \ 141 if ((_ro)->ro_rt != NULL) { \ 142 RT_LOCK_ASSERT_NOTHELD((_ro)->ro_rt); \ 143 if (_rnh_locked) \ 144 rtfree_locked((_ro)->ro_rt); \ 145 else \ 146 rtfree((_ro)->ro_rt); \ 147 (_ro)->ro_rt = NULL; \ 148 } \ 149 if ((_ro)->ro_srcia != NULL) { \ 150 IFA_REMREF((_ro)->ro_srcia); \ 151 (_ro)->ro_srcia = NULL; \ 152 (_ro)->ro_flags &= ~ROF_SRCIF_SELECTED; \ 153 } \ 154 if ((_ro)->ro_lle != NULL) { \ 155 LLE_REMREF((_ro)->ro_lle); \ 156 (_ro)->ro_lle = NULL; \ 157 (_ro)->ro_flags &= ~ROF_LLE_CACHE; \ 158 } \ 159 } while (0) 160 161 #define ROUTE_RELEASE_LOCKED(_ro) _ROUTE_RELEASE_COMMON(_ro, TRUE) 162 #define ROUTE_RELEASE(_ro) _ROUTE_RELEASE_COMMON(_ro, FALSE) 163 164 /* 165 * We distinguish between routes to hosts and routes to networks, 166 * preferring the former if available. For each route we infer 167 * the interface to use from the gateway address supplied when 168 * the route was entered. Routes that forward packets through 169 * gateways are marked so that the output routines know to address the 170 * gateway rather than the ultimate destination. 171 */ 172 173 #define NRTT_HIST 10 174 /* 175 * Kernel routing entry structure. 176 */ 177 struct rtentry { 178 struct radix_node rt_nodes[2]; /* tree glue, and other values */ 179 #define rt_key(r) (SA((r)->rt_nodes->rn_key)) 180 #define rt_mask(r) (SA((r)->rt_nodes->rn_mask)) 181 /* 182 * See bsd/net/route.c for synchronization notes. 183 */ 184 decl_lck_mtx_data(, rt_lock); /* lock for routing entry */ 185 uint32_t rt_refcnt; /* # held references */ 186 uint32_t rt_flags; /* up/down?, host/net */ 187 uint32_t rt_genid; /* route generation id */ 188 struct sockaddr *rt_gateway; /* value */ 189 struct ifnet *rt_ifp; /* the answer: interface to use */ 190 struct ifaddr *rt_ifa; /* the answer: interface addr to use */ 191 struct sockaddr *rt_genmask; /* for generation of cloned routes */ 192 void *rt_llinfo; /* pointer to link level info cache */ 193 void (*rt_llinfo_get_ri) /* llinfo get reachability info fn */ 194 (struct rtentry *, struct rt_reach_info *); 195 void (*rt_llinfo_get_iflri) /* ifnet llinfo get reach. info fn */ 196 (struct rtentry *, struct ifnet_llreach_info *); 197 void (*rt_llinfo_purge)(struct rtentry *); /* llinfo purge fn */ 198 void (*rt_llinfo_free)(void *); /* link level info free function */ 199 void (*rt_llinfo_refresh) (struct rtentry *); /* expedite llinfo refresh */ 200 struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols */ 201 #define rt_use rt_rmx.rmx_pksent 202 struct rtentry *rt_gwroute; /* implied entry for gatewayed routes */ 203 struct rtentry *rt_parent; /* cloning parent of this route */ 204 struct nstat_counts *rt_stats; /* route stats */ 205 void (*rt_if_ref_fn)(struct ifnet *, int); /* interface ref func */ 206 207 uint32_t *rt_tree_genid; /* ptr to per-tree route_genid */ 208 uint64_t rt_expire; /* expiration time in uptime seconds */ 209 uint64_t base_calendartime; /* calendar time upon entry creation */ 210 uint64_t base_uptime; /* uptime upon entry creation */ 211 u_int32_t rtt_hist[NRTT_HIST]; /* RTT history sample by TCP connections */ 212 u_int32_t rtt_min; /* minimum RTT computed from history */ 213 u_int32_t rtt_expire_ts; /* RTT history expire timestamp */ 214 u_int8_t rtt_index; /* Index into RTT history */ 215 /* Event handler context for the rtentrt */ 216 struct eventhandler_lists_ctxt rt_evhdlr_ctxt; 217 }; 218 219 #define rt_key_free(r) ({ \ 220 void *__r = rt_key(r); \ 221 kheap_free_addr(KHEAP_DATA_BUFFERS, __r); \ 222 }) 223 224 enum { 225 ROUTE_STATUS_UPDATE = 1, 226 ROUTE_ENTRY_REFRESH, 227 ROUTE_ENTRY_DELETED, 228 ROUTE_LLENTRY_RESOLVED, 229 ROUTE_LLENTRY_UNREACH, 230 ROUTE_LLENTRY_CHANGED, 231 ROUTE_LLENTRY_STALE, 232 ROUTE_LLENTRY_TIMEDOUT, 233 ROUTE_LLENTRY_DELETED, 234 ROUTE_LLENTRY_EXPIRED, 235 ROUTE_LLENTRY_PROBED, 236 ROUTE_EVHDLR_DEREGISTER, 237 }; 238 239 extern const char * route_event2str(int route_event); 240 241 typedef void (*route_event_fn) (struct eventhandler_entry_arg, 242 struct sockaddr *, int, struct sockaddr *, int); 243 EVENTHANDLER_DECLARE(route_event, route_event_fn); 244 245 /* 246 * Synchronize route entry's generation ID with the tree's. 247 */ 248 #define RT_GENID_SYNC(_rt) do { \ 249 if ((_rt)->rt_tree_genid != NULL) \ 250 (_rt)->rt_genid = *(_rt)->rt_tree_genid; \ 251 } while (0) 252 253 /* 254 * Indicates whether or not the route entry's generation ID is stale. 255 */ 256 #define RT_GENID_OUTOFSYNC(_rt) \ 257 ((_rt)->rt_tree_genid != NULL && \ 258 *(_rt)->rt_tree_genid != (_rt)->rt_genid) 259 260 enum { 261 ROUTE_OP_READ, 262 ROUTE_OP_WRITE, 263 }; 264 265 extern int route_op_entitlement_check(struct socket *, kauth_cred_t, int, boolean_t); 266 #endif /* BSD_KERNEL_PRIVATE */ 267 268 struct kev_netevent_apnfallbk_data { 269 pid_t epid; /* effective PID */ 270 uuid_t euuid; /* effective UUID */ 271 }; 272 273 /* 274 * Route reachability info. 275 */ 276 struct rt_reach_info { 277 u_int32_t ri_refcnt; /* reference count */ 278 u_int32_t ri_probes; /* total # of probes */ 279 u_int64_t ri_snd_expire; /* tx expiration (calendar) time */ 280 u_int64_t ri_rcv_expire; /* rx expiration (calendar) time */ 281 int32_t ri_rssi; /* received signal strength */ 282 int32_t ri_lqm; /* link quality metric */ 283 int32_t ri_npm; /* node proximity metric */ 284 }; 285 286 /* 287 * Extended routing message header (private). 288 */ 289 struct rt_msghdr_ext { 290 u_short rtm_msglen; /* to skip over non-understood messages */ 291 u_char rtm_version; /* future binary compatibility */ 292 u_char rtm_type; /* message type */ 293 u_int32_t rtm_index; /* index for associated ifp */ 294 u_int32_t rtm_flags; /* flags, incl. kern & message, e.g. DONE */ 295 u_int32_t rtm_reserved; /* for future use */ 296 u_int32_t rtm_addrs; /* bitmask identifying sockaddrs in msg */ 297 pid_t rtm_pid; /* identify sender */ 298 int rtm_seq; /* for sender to identify action */ 299 int rtm_errno; /* why failed */ 300 u_int32_t rtm_use; /* from rtentry */ 301 u_int32_t rtm_inits; /* which metrics we are initializing */ 302 struct rt_metrics rtm_rmx; /* metrics themselves */ 303 struct rt_reach_info rtm_ri; /* route reachability info */ 304 }; 305 306 /* 307 * Message types. 308 */ 309 #define RTM_GET_SILENT 0x11 310 #define RTM_GET_EXT 0x15 311 312 /* 313 * Bitmask values for rtm_inits and rmx_locks. 314 */ 315 #define RTV_REFRESH_HOST 0x100 /* init host route to expedite refresh */ 316 317 /* 318 * For scoped routing; a zero interface scope value means nil/no scope. 319 */ 320 #define IFSCOPE_NONE 0 321 #define IFSCOPE_UNKNOWN IFSCOPE_NONE 322 323 #ifdef BSD_KERNEL_PRIVATE 324 /* 325 * Generic call trace used by some subsystems (e.g. route, ifaddr) 326 */ 327 #define CTRACE_STACK_SIZE 8 /* depth of stack trace */ 328 #define CTRACE_HIST_SIZE 4 /* refcnt history size */ 329 typedef struct ctrace { 330 void *th; /* thread ptr */ 331 void *pc[CTRACE_STACK_SIZE]; /* PC stack trace */ 332 } ctrace_t; 333 334 extern void ctrace_record(ctrace_t *); 335 336 #define RT_LOCK_ASSERT_HELD(_rt) \ 337 LCK_MTX_ASSERT(&(_rt)->rt_lock, LCK_MTX_ASSERT_OWNED) 338 339 #define RT_LOCK_ASSERT_NOTHELD(_rt) \ 340 LCK_MTX_ASSERT(&(_rt)->rt_lock, LCK_MTX_ASSERT_NOTOWNED) 341 342 #define RT_LOCK(_rt) do { \ 343 rt_lock(_rt, FALSE); \ 344 } while (0) 345 346 #define RT_LOCK_SPIN(_rt) do { \ 347 rt_lock(_rt, TRUE); \ 348 } while (0) 349 350 #define RT_CONVERT_LOCK(_rt) do { \ 351 RT_LOCK_ASSERT_HELD(_rt); \ 352 lck_mtx_convert_spin(&(_rt)->rt_lock); \ 353 } while (0) 354 355 #define RT_UNLOCK(_rt) do { \ 356 rt_unlock(_rt); \ 357 } while (0) 358 359 #define RT_ADDREF_LOCKED(_rt) do { \ 360 rtref(_rt); \ 361 } while (0) 362 363 /* 364 * Spin variant mutex is used here; caller is responsible for 365 * converting any previously-held similar lock to full mutex. 366 */ 367 #define RT_ADDREF(_rt) do { \ 368 RT_LOCK_SPIN(_rt); \ 369 RT_ADDREF_LOCKED(_rt); \ 370 RT_UNLOCK(_rt); \ 371 } while (0) 372 373 #define RT_REMREF_LOCKED(_rt) do { \ 374 (void) rtunref(_rt); \ 375 } while (0) 376 377 /* 378 * Spin variant mutex is used here; caller is responsible for 379 * converting any previously-held similar lock to full mutex. 380 */ 381 #define RT_REMREF(_rt) do { \ 382 RT_LOCK_SPIN(_rt); \ 383 RT_REMREF_LOCKED(_rt); \ 384 RT_UNLOCK(_rt); \ 385 } while (0) 386 387 /* 388 * This macro calculates skew in wall clock, just in case the user changes the 389 * system time. This skew adjustment is required because we now keep the 390 * expiration times in uptime terms in the kernel, but the userland still 391 * expects expiration times in terms of calendar times. This is used when 392 * reporting rt_expire, ln_expire, etc. values to user space. 393 */ 394 #define NET_CALCULATE_CLOCKSKEW(cc, ic, cu, iu) \ 395 ((cc.tv_sec - ic) - (cu - iu)) 396 397 extern unsigned int rt_verbose; 398 extern struct radix_node_head *rt_tables[AF_MAX + 1]; 399 extern lck_mtx_t rnh_lock_data; 400 #define rnh_lock (&rnh_lock_data) 401 extern uint32_t route_genid_inet; /* INET route generation count */ 402 extern uint32_t route_genid_inet6; /* INET6 route generation count */ 403 extern int rttrash; 404 extern unsigned int rte_debug; 405 406 struct ifmultiaddr; 407 struct proc; 408 409 extern void route_init(void); 410 extern void routegenid_update(void); 411 extern void routegenid_inet_update(void); 412 extern void routegenid_inet6_update(void); 413 extern void rt_ifmsg(struct ifnet *); 414 extern void rt_missmsg(u_char, struct rt_addrinfo *, int, int); 415 extern void rt_newaddrmsg(u_char, struct ifaddr *, int, struct rtentry *); 416 extern void rt_newmaddrmsg(u_char, struct ifmultiaddr *); 417 extern int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); 418 extern void set_primary_ifscope(int, unsigned int); 419 extern unsigned int get_primary_ifscope(int); 420 extern boolean_t rt_primary_default(struct rtentry *, struct sockaddr *); 421 extern struct rtentry *rt_lookup(boolean_t, struct sockaddr *, 422 struct sockaddr *, struct radix_node_head *, unsigned int); 423 extern struct rtentry *rt_lookup_coarse(boolean_t, struct sockaddr *, 424 struct sockaddr *, struct radix_node_head *); 425 extern void rtalloc(struct route *); 426 extern void rtalloc_scoped(struct route *, unsigned int); 427 extern void rtalloc_ign(struct route *, uint32_t); 428 extern void rtalloc_scoped_ign(struct route *, uint32_t, unsigned int); 429 extern struct rtentry *rtalloc1(struct sockaddr *, int, uint32_t); 430 extern struct rtentry *rtalloc1_scoped(struct sockaddr *, int, uint32_t, 431 unsigned int); 432 extern struct rtentry *rtalloc1_scoped_locked(struct sockaddr *, int, 433 uint32_t, unsigned int); 434 extern void rtfree_locked(struct rtentry *); 435 extern void rtfree(struct rtentry *); 436 extern void rtref(struct rtentry *); 437 /* 438 * rtunref will decrement the refcount, rtfree will decrement and free if 439 * the refcount has reached zero and the route is not up. 440 * Unless you have good reason to do otherwise, use rtfree. 441 */ 442 extern int rtunref(struct rtentry *); 443 extern void rtsetifa(struct rtentry *, struct ifaddr *); 444 extern int rtinit(struct ifaddr *, uint8_t, int); 445 extern int rtinit_locked(struct ifaddr *, uint8_t, int); 446 extern int rtioctl(unsigned long, caddr_t, struct proc *); 447 extern void rtredirect(struct ifnet *, struct sockaddr *, struct sockaddr *, 448 struct sockaddr *, int, struct sockaddr *, struct rtentry **); 449 extern int rtrequest(int, struct sockaddr *, 450 struct sockaddr *, struct sockaddr *, int, struct rtentry **); 451 extern int rtrequest_scoped(int, struct sockaddr *, struct sockaddr *, 452 struct sockaddr *, int, struct rtentry **, unsigned int); 453 extern int rtrequest_locked(int, struct sockaddr *, 454 struct sockaddr *, struct sockaddr *, int, struct rtentry **); 455 extern int rtrequest_scoped_locked(int, struct sockaddr *, struct sockaddr *, 456 struct sockaddr *, int, struct rtentry **, unsigned int); 457 extern void sin_set_ifscope(struct sockaddr *, unsigned int); 458 extern unsigned int sin_get_ifscope(struct sockaddr *); 459 extern unsigned int sin6_get_ifscope(struct sockaddr *); 460 extern void rt_lock(struct rtentry *, boolean_t); 461 extern void rt_unlock(struct rtentry *); 462 extern struct sockaddr *rtm_scrub(int, int, struct sockaddr *, 463 struct sockaddr *, void *, uint32_t, kauth_cred_t *); 464 extern boolean_t rt_validate(struct rtentry *); 465 extern void rt_set_proxy(struct rtentry *, boolean_t); 466 extern void rt_set_gwroute(struct rtentry *, struct sockaddr *, 467 struct rtentry *); 468 extern void rt_revalidate_gwroute(struct rtentry *, struct rtentry *); 469 extern errno_t route_to_gwroute(const struct sockaddr *, struct rtentry *, 470 struct rtentry **); 471 extern void rt_setexpire(struct rtentry *, uint64_t); 472 extern void rt_str(struct rtentry *, char *, uint32_t, char *, uint32_t); 473 extern const char *rtm2str(int); 474 extern void route_clear(struct route *); 475 extern void route_copyin(struct route *, struct route *, size_t); 476 extern void route_copyout(struct route *, const struct route *, size_t); 477 extern boolean_t rt_ifa_is_dst(struct sockaddr *, struct ifaddr *); 478 extern struct sockaddr *sa_copy(struct sockaddr *, struct sockaddr_storage *, 479 unsigned int *); 480 481 /* 482 * The following is used to enqueue work items for route events 483 * and also used to pass route event while walking the tree 484 */ 485 struct route_event { 486 struct rtentry *rt; 487 /* 488 * There's no reference taken on gwrt. 489 * We only use it to check whether we should 490 * point to rt_gateway or the embedded rt_addr 491 * structure. 492 */ 493 struct rtentry *gwrt; 494 union { 495 union sockaddr_in_4_6 _rtev_ipaddr; 496 char _rtev_addr_bytes[DLIL_SDLMAXLEN]; 497 } rt_addr; 498 uint32_t route_event_code; 499 eventhandler_tag evtag; 500 }; 501 502 #define rtev_ipaddr rt_addr._rtev_ipaddr 503 #define rtev_addr_bytes rt_addr._rtev_addr_bytes 504 505 extern void route_event_init(struct route_event *p_route_ev, struct rtentry *rt, 506 struct rtentry *gwrt, int route_ev_code); 507 extern int route_event_walktree(struct radix_node *rn, void *arg); 508 extern void route_event_enqueue_nwk_wq_entry(struct rtentry *, struct rtentry *, 509 uint32_t, eventhandler_tag, boolean_t); 510 #endif /* BSD_KERNEL_PRIVATE */ 511 #endif /* _NET_ROUTE_PRIVATE_H_ */ 512