1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/systm.h> 31 #include <sys/stream.h> 32 #include <sys/strsubr.h> 33 #include <sys/pattr.h> 34 #include <sys/dlpi.h> 35 #include <sys/atomic.h> 36 #include <sys/sunddi.h> 37 #include <sys/socket.h> 38 #include <sys/neti.h> 39 40 #include <netinet/in.h> 41 #include <inet/common.h> 42 #include <inet/mib2.h> 43 #include <inet/ip.h> 44 #include <inet/ip6.h> 45 #include <inet/ip_if.h> 46 #include <inet/ip_ire.h> 47 #include <inet/ip_impl.h> 48 #include <inet/ip_ndp.h> 49 #include <inet/ipclassifier.h> 50 #include <inet/ipp_common.h> 51 #include <inet/ip_ftable.h> 52 53 /* 54 * IPv4 netinfo entry point declarations. 55 */ 56 static int ip_getifname(phy_if_t, char *, const size_t, 57 netstack_t *); 58 static int ip_getmtu(phy_if_t, lif_if_t, netstack_t *); 59 static int ip_getpmtuenabled(netstack_t *); 60 static int ip_getlifaddr(phy_if_t, lif_if_t, size_t, 61 net_ifaddr_t [], void *, netstack_t *); 62 static phy_if_t ip_phygetnext(phy_if_t, netstack_t *); 63 static phy_if_t ip_phylookup(const char *, netstack_t *); 64 static lif_if_t ip_lifgetnext(phy_if_t, lif_if_t, netstack_t *); 65 static int ip_inject(inject_t, net_inject_t *, netstack_t *); 66 static phy_if_t ip_routeto(struct sockaddr *, netstack_t *); 67 static int ip_ispartialchecksum(mblk_t *); 68 static int ip_isvalidchecksum(mblk_t *); 69 70 static int ipv6_getifname(phy_if_t, char *, const size_t, 71 netstack_t *); 72 static int ipv6_getmtu(phy_if_t, lif_if_t, netstack_t *); 73 static int ipv6_getlifaddr(phy_if_t, lif_if_t, size_t, 74 net_ifaddr_t [], void *, netstack_t *); 75 static phy_if_t ipv6_phygetnext(phy_if_t, netstack_t *); 76 static phy_if_t ipv6_phylookup(const char *, netstack_t *); 77 static lif_if_t ipv6_lifgetnext(phy_if_t, lif_if_t, netstack_t *); 78 static int ipv6_inject(inject_t, net_inject_t *, netstack_t *); 79 static phy_if_t ipv6_routeto(struct sockaddr *, netstack_t *); 80 static int ipv6_isvalidchecksum(mblk_t *); 81 82 /* Netinfo private functions */ 83 static int ip_getifname_impl(phy_if_t, char *, 84 const size_t, boolean_t, ip_stack_t *); 85 static int ip_getmtu_impl(phy_if_t, lif_if_t, boolean_t, 86 ip_stack_t *); 87 static phy_if_t ip_phylookup_impl(const char *, boolean_t, 88 ip_stack_t *ipst); 89 static lif_if_t ip_lifgetnext_impl(phy_if_t, lif_if_t, boolean_t, 90 ip_stack_t *ipst); 91 static int ip_inject_impl(inject_t, net_inject_t *, boolean_t, 92 ip_stack_t *); 93 static int ip_getifaddr_type(sa_family_t, ipif_t *, lif_if_t, 94 void *); 95 static phy_if_t ip_routeto_impl(struct sockaddr *, ip_stack_t *); 96 static int ip_getlifaddr_impl(sa_family_t, phy_if_t, lif_if_t, 97 size_t, net_ifaddr_t [], struct sockaddr *, 98 ip_stack_t *); 99 static void ip_ni_queue_in_func(void *); 100 static void ip_ni_queue_out_func(void *); 101 static void ip_ni_queue_func_impl(injection_t *, boolean_t); 102 103 104 static net_info_t ipv4info = { 105 NETINFO_VERSION, 106 NHF_INET, 107 ip_getifname, 108 ip_getmtu, 109 ip_getpmtuenabled, 110 ip_getlifaddr, 111 ip_phygetnext, 112 ip_phylookup, 113 ip_lifgetnext, 114 ip_inject, 115 ip_routeto, 116 ip_ispartialchecksum, 117 ip_isvalidchecksum 118 }; 119 120 121 static net_info_t ipv6info = { 122 NETINFO_VERSION, 123 NHF_INET6, 124 ipv6_getifname, 125 ipv6_getmtu, 126 ip_getpmtuenabled, 127 ipv6_getlifaddr, 128 ipv6_phygetnext, 129 ipv6_phylookup, 130 ipv6_lifgetnext, 131 ipv6_inject, 132 ipv6_routeto, 133 ip_ispartialchecksum, 134 ipv6_isvalidchecksum 135 }; 136 137 /* 138 * The taskq eventq_queue_in is used to process the upside inject messages. 139 * The taskq eventq_queue_out is used to process the downside inject messages. 140 * The taskq eventq_queue_nic is used to process the nic event messages. 141 */ 142 static ddi_taskq_t *eventq_queue_in = NULL; 143 static ddi_taskq_t *eventq_queue_out = NULL; 144 ddi_taskq_t *eventq_queue_nic = NULL; 145 146 /* 147 * Initialize queues for inject. 148 */ 149 void 150 ip_net_g_init() 151 { 152 if (eventq_queue_out == NULL) { 153 eventq_queue_out = ddi_taskq_create(NULL, 154 "IP_INJECT_QUEUE_OUT", 1, TASKQ_DEFAULTPRI, 0); 155 156 if (eventq_queue_out == NULL) 157 cmn_err(CE_NOTE, "ipv4_net_init: " 158 "ddi_taskq_create failed for IP_INJECT_QUEUE_OUT"); 159 } 160 161 if (eventq_queue_in == NULL) { 162 eventq_queue_in = ddi_taskq_create(NULL, 163 "IP_INJECT_QUEUE_IN", 1, TASKQ_DEFAULTPRI, 0); 164 165 if (eventq_queue_in == NULL) 166 cmn_err(CE_NOTE, "ipv4_net_init: " 167 "ddi_taskq_create failed for IP_INJECT_QUEUE_IN"); 168 } 169 170 if (eventq_queue_nic == NULL) { 171 eventq_queue_nic = ddi_taskq_create(NULL, 172 "IP_NIC_EVENT_QUEUE", 1, TASKQ_DEFAULTPRI, 0); 173 174 if (eventq_queue_nic == NULL) 175 cmn_err(CE_NOTE, "ipv4_net_init: " 176 "ddi_taskq_create failed for IP_NIC_EVENT_QUEUE"); 177 } 178 } 179 180 /* 181 * Destroy inject queues 182 */ 183 void 184 ip_net_g_destroy() 185 { 186 if (eventq_queue_nic != NULL) { 187 ddi_taskq_destroy(eventq_queue_nic); 188 eventq_queue_nic = NULL; 189 } 190 191 if (eventq_queue_in != NULL) { 192 ddi_taskq_destroy(eventq_queue_in); 193 eventq_queue_in = NULL; 194 } 195 196 if (eventq_queue_out != NULL) { 197 ddi_taskq_destroy(eventq_queue_out); 198 eventq_queue_out = NULL; 199 } 200 } 201 202 /* 203 * Register IPv4 and IPv6 netinfo functions and initialize queues for inject. 204 */ 205 void 206 ip_net_init(ip_stack_t *ipst, netstack_t *ns) 207 { 208 209 ipst->ips_ipv4_net_data = net_register_impl(&ipv4info, ns); 210 ASSERT(ipst->ips_ipv4_net_data != NULL); 211 212 ipst->ips_ipv6_net_data = net_register_impl(&ipv6info, ns); 213 ASSERT(ipst->ips_ipv6_net_data != NULL); 214 } 215 216 217 /* 218 * Unregister IPv4 and IPv6 functions and inject queues 219 */ 220 void 221 ip_net_destroy(ip_stack_t *ipst) 222 { 223 if (ipst->ips_ipv4_net_data != NULL) { 224 if (net_unregister(ipst->ips_ipv4_net_data) == 0) 225 ipst->ips_ipv4_net_data = NULL; 226 } 227 228 if (ipst->ips_ipv6_net_data != NULL) { 229 if (net_unregister(ipst->ips_ipv6_net_data) == 0) 230 ipst->ips_ipv6_net_data = NULL; 231 } 232 } 233 234 /* 235 * Initialize IPv4 hooks family the event 236 */ 237 void 238 ipv4_hook_init(ip_stack_t *ipst) 239 { 240 HOOK_FAMILY_INIT(&ipst->ips_ipv4root, Hn_IPV4); 241 if (net_register_family(ipst->ips_ipv4_net_data, &ipst->ips_ipv4root) 242 != 0) { 243 cmn_err(CE_NOTE, "ipv4_hook_init: " 244 "net_register_family failed for ipv4"); 245 } 246 247 HOOK_EVENT_INIT(&ipst->ips_ip4_physical_in_event, NH_PHYSICAL_IN); 248 ipst->ips_ipv4firewall_physical_in = net_register_event( 249 ipst->ips_ipv4_net_data, &ipst->ips_ip4_physical_in_event); 250 if (ipst->ips_ipv4firewall_physical_in == NULL) { 251 cmn_err(CE_NOTE, "ipv4_hook_init: " 252 "net_register_event failed for ipv4/physical_in"); 253 } 254 255 HOOK_EVENT_INIT(&ipst->ips_ip4_physical_out_event, NH_PHYSICAL_OUT); 256 ipst->ips_ipv4firewall_physical_out = net_register_event( 257 ipst->ips_ipv4_net_data, &ipst->ips_ip4_physical_out_event); 258 if (ipst->ips_ipv4firewall_physical_out == NULL) { 259 cmn_err(CE_NOTE, "ipv4_hook_init: " 260 "net_register_event failed for ipv4/physical_out"); 261 } 262 263 HOOK_EVENT_INIT(&ipst->ips_ip4_forwarding_event, NH_FORWARDING); 264 ipst->ips_ipv4firewall_forwarding = net_register_event( 265 ipst->ips_ipv4_net_data, &ipst->ips_ip4_forwarding_event); 266 if (ipst->ips_ipv4firewall_forwarding == NULL) { 267 cmn_err(CE_NOTE, "ipv4_hook_init: " 268 "net_register_event failed for ipv4/forwarding"); 269 } 270 271 HOOK_EVENT_INIT(&ipst->ips_ip4_loopback_in_event, NH_LOOPBACK_IN); 272 ipst->ips_ipv4firewall_loopback_in = net_register_event( 273 ipst->ips_ipv4_net_data, &ipst->ips_ip4_loopback_in_event); 274 if (ipst->ips_ipv4firewall_loopback_in == NULL) { 275 cmn_err(CE_NOTE, "ipv4_hook_init: " 276 "net_register_event failed for ipv4/loopback_in"); 277 } 278 279 HOOK_EVENT_INIT(&ipst->ips_ip4_loopback_out_event, NH_LOOPBACK_OUT); 280 ipst->ips_ipv4firewall_loopback_out = net_register_event( 281 ipst->ips_ipv4_net_data, &ipst->ips_ip4_loopback_out_event); 282 if (ipst->ips_ipv4firewall_loopback_out == NULL) { 283 cmn_err(CE_NOTE, "ipv4_hook_init: " 284 "net_register_event failed for ipv4/loopback_out"); 285 } 286 287 HOOK_EVENT_INIT(&ipst->ips_ip4_nic_events, NH_NIC_EVENTS); 288 ipst->ips_ip4_nic_events.he_flags = HOOK_RDONLY; 289 ipst->ips_ipv4nicevents = net_register_event( 290 ipst->ips_ipv4_net_data, &ipst->ips_ip4_nic_events); 291 if (ipst->ips_ipv4nicevents == NULL) { 292 cmn_err(CE_NOTE, "ipv4_hook_init: " 293 "net_register_event failed for ipv4/nic_events"); 294 } 295 } 296 297 void 298 ipv4_hook_destroy(ip_stack_t *ipst) 299 { 300 if (ipst->ips_ipv4firewall_forwarding != NULL) { 301 if (net_unregister_event(ipst->ips_ipv4_net_data, 302 &ipst->ips_ip4_forwarding_event) == 0) 303 ipst->ips_ipv4firewall_forwarding = NULL; 304 } 305 306 if (ipst->ips_ipv4firewall_physical_in != NULL) { 307 if (net_unregister_event(ipst->ips_ipv4_net_data, 308 &ipst->ips_ip4_physical_in_event) == 0) 309 ipst->ips_ipv4firewall_physical_in = NULL; 310 } 311 312 if (ipst->ips_ipv4firewall_physical_out != NULL) { 313 if (net_unregister_event(ipst->ips_ipv4_net_data, 314 &ipst->ips_ip4_physical_out_event) == 0) 315 ipst->ips_ipv4firewall_physical_out = NULL; 316 } 317 318 if (ipst->ips_ipv4firewall_loopback_in != NULL) { 319 if (net_unregister_event(ipst->ips_ipv4_net_data, 320 &ipst->ips_ip4_loopback_in_event) == 0) 321 ipst->ips_ipv4firewall_loopback_in = NULL; 322 } 323 324 if (ipst->ips_ipv4firewall_loopback_out != NULL) { 325 if (net_unregister_event(ipst->ips_ipv4_net_data, 326 &ipst->ips_ip4_loopback_out_event) == 0) 327 ipst->ips_ipv4firewall_loopback_out = NULL; 328 } 329 330 if (ipst->ips_ipv4nicevents != NULL) { 331 if (net_unregister_event(ipst->ips_ipv4_net_data, 332 &ipst->ips_ip4_nic_events) == 0) 333 ipst->ips_ipv4nicevents = NULL; 334 } 335 336 (void) net_unregister_family(ipst->ips_ipv4_net_data, 337 &ipst->ips_ipv4root); 338 } 339 340 /* 341 * Initialize IPv6 hooks family and event 342 */ 343 void 344 ipv6_hook_init(ip_stack_t *ipst) 345 { 346 347 HOOK_FAMILY_INIT(&ipst->ips_ipv6root, Hn_IPV6); 348 if (net_register_family(ipst->ips_ipv6_net_data, &ipst->ips_ipv6root) 349 != 0) { 350 cmn_err(CE_NOTE, "ipv6_hook_init: " 351 "net_register_family failed for ipv6"); 352 } 353 354 HOOK_EVENT_INIT(&ipst->ips_ip6_physical_in_event, NH_PHYSICAL_IN); 355 ipst->ips_ipv6firewall_physical_in = net_register_event( 356 ipst->ips_ipv6_net_data, &ipst->ips_ip6_physical_in_event); 357 if (ipst->ips_ipv6firewall_physical_in == NULL) { 358 cmn_err(CE_NOTE, "ipv6_hook_init: " 359 "net_register_event failed for ipv6/physical_in"); 360 } 361 362 HOOK_EVENT_INIT(&ipst->ips_ip6_physical_out_event, NH_PHYSICAL_OUT); 363 ipst->ips_ipv6firewall_physical_out = net_register_event( 364 ipst->ips_ipv6_net_data, &ipst->ips_ip6_physical_out_event); 365 if (ipst->ips_ipv6firewall_physical_out == NULL) { 366 cmn_err(CE_NOTE, "ipv6_hook_init: " 367 "net_register_event failed for ipv6/physical_out"); 368 } 369 370 HOOK_EVENT_INIT(&ipst->ips_ip6_forwarding_event, NH_FORWARDING); 371 ipst->ips_ipv6firewall_forwarding = net_register_event( 372 ipst->ips_ipv6_net_data, &ipst->ips_ip6_forwarding_event); 373 if (ipst->ips_ipv6firewall_forwarding == NULL) { 374 cmn_err(CE_NOTE, "ipv6_hook_init: " 375 "net_register_event failed for ipv6/forwarding"); 376 } 377 378 HOOK_EVENT_INIT(&ipst->ips_ip6_loopback_in_event, NH_LOOPBACK_IN); 379 ipst->ips_ipv6firewall_loopback_in = net_register_event( 380 ipst->ips_ipv6_net_data, &ipst->ips_ip6_loopback_in_event); 381 if (ipst->ips_ipv6firewall_loopback_in == NULL) { 382 cmn_err(CE_NOTE, "ipv6_hook_init: " 383 "net_register_event failed for ipv6/loopback_in"); 384 } 385 386 HOOK_EVENT_INIT(&ipst->ips_ip6_loopback_out_event, NH_LOOPBACK_OUT); 387 ipst->ips_ipv6firewall_loopback_out = net_register_event( 388 ipst->ips_ipv6_net_data, &ipst->ips_ip6_loopback_out_event); 389 if (ipst->ips_ipv6firewall_loopback_out == NULL) { 390 cmn_err(CE_NOTE, "ipv6_hook_init: " 391 "net_register_event failed for ipv6/loopback_out"); 392 } 393 394 HOOK_EVENT_INIT(&ipst->ips_ip6_nic_events, NH_NIC_EVENTS); 395 ipst->ips_ip6_nic_events.he_flags = HOOK_RDONLY; 396 ipst->ips_ipv6nicevents = net_register_event( 397 ipst->ips_ipv6_net_data, &ipst->ips_ip6_nic_events); 398 if (ipst->ips_ipv6nicevents == NULL) { 399 cmn_err(CE_NOTE, "ipv6_hook_init: " 400 "net_register_event failed for ipv6/nic_events"); 401 } 402 } 403 404 void 405 ipv6_hook_destroy(ip_stack_t *ipst) 406 { 407 if (ipst->ips_ipv6firewall_forwarding != NULL) { 408 if (net_unregister_event(ipst->ips_ipv6_net_data, 409 &ipst->ips_ip6_forwarding_event) == 0) 410 ipst->ips_ipv6firewall_forwarding = NULL; 411 } 412 413 if (ipst->ips_ipv6firewall_physical_in != NULL) { 414 if (net_unregister_event(ipst->ips_ipv6_net_data, 415 &ipst->ips_ip6_physical_in_event) == 0) 416 ipst->ips_ipv6firewall_physical_in = NULL; 417 } 418 419 if (ipst->ips_ipv6firewall_physical_out != NULL) { 420 if (net_unregister_event(ipst->ips_ipv6_net_data, 421 &ipst->ips_ip6_physical_out_event) == 0) 422 ipst->ips_ipv6firewall_physical_out = NULL; 423 } 424 425 if (ipst->ips_ipv6firewall_loopback_in != NULL) { 426 if (net_unregister_event(ipst->ips_ipv6_net_data, 427 &ipst->ips_ip6_loopback_in_event) == 0) 428 ipst->ips_ipv6firewall_loopback_in = NULL; 429 } 430 431 if (ipst->ips_ipv6firewall_loopback_out != NULL) { 432 if (net_unregister_event(ipst->ips_ipv6_net_data, 433 &ipst->ips_ip6_loopback_out_event) == 0) 434 ipst->ips_ipv6firewall_loopback_out = NULL; 435 } 436 437 if (ipst->ips_ipv6nicevents != NULL) { 438 if (net_unregister_event(ipst->ips_ipv6_net_data, 439 &ipst->ips_ip6_nic_events) == 0) 440 ipst->ips_ipv6nicevents = NULL; 441 } 442 443 (void) net_unregister_family(ipst->ips_ipv6_net_data, 444 &ipst->ips_ipv6root); 445 } 446 447 /* 448 * Determine the name of an IPv4 interface 449 */ 450 static int 451 ip_getifname(phy_if_t phy_ifdata, char *buffer, const size_t buflen, 452 netstack_t *ns) 453 { 454 return (ip_getifname_impl(phy_ifdata, buffer, buflen, B_FALSE, 455 ns->netstack_ip)); 456 } 457 458 /* 459 * Determine the name of an IPv6 interface 460 */ 461 static int 462 ipv6_getifname(phy_if_t phy_ifdata, char *buffer, const size_t buflen, 463 netstack_t *ns) 464 { 465 return (ip_getifname_impl(phy_ifdata, buffer, buflen, B_TRUE, 466 ns->netstack_ip)); 467 } 468 469 /* 470 * Shared implementation to determine the name of a given network interface 471 */ 472 /* ARGSUSED */ 473 static int 474 ip_getifname_impl(phy_if_t phy_ifdata, 475 char *buffer, const size_t buflen, boolean_t isv6, ip_stack_t *ipst) 476 { 477 ill_t *ill; 478 479 ASSERT(buffer != NULL); 480 481 ill = ill_lookup_on_ifindex((uint_t)phy_ifdata, isv6, NULL, NULL, 482 NULL, NULL, ipst); 483 if (ill == NULL) 484 return (1); 485 486 if (ill->ill_name != NULL) { 487 (void) strlcpy(buffer, ill->ill_name, buflen); 488 ill_refrele(ill); 489 return (0); 490 } else { 491 ill_refrele(ill); 492 return (1); 493 } 494 495 } 496 497 /* 498 * Determine the MTU of an IPv4 network interface 499 */ 500 static int 501 ip_getmtu(phy_if_t phy_ifdata, lif_if_t ifdata, netstack_t *ns) 502 { 503 ASSERT(ns != NULL); 504 return (ip_getmtu_impl(phy_ifdata, ifdata, B_FALSE, ns->netstack_ip)); 505 } 506 507 /* 508 * Determine the MTU of an IPv6 network interface 509 */ 510 static int 511 ipv6_getmtu(phy_if_t phy_ifdata, lif_if_t ifdata, netstack_t *ns) 512 { 513 ASSERT(ns != NULL); 514 return (ip_getmtu_impl(phy_ifdata, ifdata, B_TRUE, ns->netstack_ip)); 515 } 516 517 /* 518 * Shared implementation to determine the MTU of a network interface 519 */ 520 /* ARGSUSED */ 521 static int 522 ip_getmtu_impl(phy_if_t phy_ifdata, lif_if_t ifdata, boolean_t isv6, 523 ip_stack_t *ipst) 524 { 525 lif_if_t ipifid; 526 ipif_t *ipif; 527 int mtu; 528 529 ipifid = UNMAP_IPIF_ID(ifdata); 530 531 ipif = ipif_getby_indexes((uint_t)phy_ifdata, (uint_t)ipifid, 532 isv6, ipst); 533 if (ipif == NULL) 534 return (0); 535 536 mtu = ipif->ipif_mtu; 537 ipif_refrele(ipif); 538 539 if (mtu == 0) { 540 ill_t *ill; 541 542 if ((ill = ill_lookup_on_ifindex((uint_t)phy_ifdata, isv6, 543 NULL, NULL, NULL, NULL, ipst)) == NULL) { 544 return (0); 545 } 546 mtu = ill->ill_max_frag; 547 ill_refrele(ill); 548 } 549 550 return (mtu); 551 } 552 553 /* 554 * Determine if path MTU discovery is enabled for IP 555 */ 556 static int 557 ip_getpmtuenabled(netstack_t *ns) 558 { 559 ASSERT(ns != NULL); 560 return ((ns->netstack_ip)->ips_ip_path_mtu_discovery); 561 } 562 563 /* 564 * Get next interface from the current list of IPv4 physical network interfaces 565 */ 566 static phy_if_t 567 ip_phygetnext(phy_if_t phy_ifdata, netstack_t *ns) 568 { 569 ASSERT(ns != NULL); 570 return (ill_get_next_ifindex(phy_ifdata, B_FALSE, ns->netstack_ip)); 571 } 572 573 /* 574 * Get next interface from the current list of IPv6 physical network interfaces 575 */ 576 static phy_if_t 577 ipv6_phygetnext(phy_if_t phy_ifdata, netstack_t *ns) 578 { 579 ASSERT(ns != NULL); 580 return (ill_get_next_ifindex(phy_ifdata, B_TRUE, ns->netstack_ip)); 581 } 582 583 /* 584 * Determine if a network interface name exists for IPv4 585 */ 586 static phy_if_t 587 ip_phylookup(const char *name, netstack_t *ns) 588 { 589 ASSERT(ns != NULL); 590 return (ip_phylookup_impl(name, B_FALSE, ns->netstack_ip)); 591 } 592 593 /* 594 * Determine if a network interface name exists for IPv6 595 */ 596 static phy_if_t 597 ipv6_phylookup(const char *name, netstack_t *ns) 598 { 599 ASSERT(ns != NULL); 600 return (ip_phylookup_impl(name, B_TRUE, ns->netstack_ip)); 601 } 602 603 /* 604 * Implement looking up an ill_t based on the name supplied and matching 605 * it up with either IPv4 or IPv6. ill_get_ifindex_by_name() is not used 606 * because it does not match on the address family in addition to the name. 607 */ 608 static phy_if_t 609 ip_phylookup_impl(const char *name, boolean_t isv6, ip_stack_t *ipst) 610 { 611 phy_if_t phy; 612 ill_t *ill; 613 614 ill = ill_lookup_on_name((char *)name, B_FALSE, isv6, NULL, NULL, 615 NULL, NULL, NULL, ipst); 616 617 if (ill == NULL) 618 return (0); 619 620 phy = ill->ill_phyint->phyint_ifindex; 621 622 ill_refrele(ill); 623 624 return (phy); 625 } 626 627 /* 628 * Get next interface from the current list of IPv4 logical network interfaces 629 */ 630 static lif_if_t 631 ip_lifgetnext(phy_if_t phy_ifdata, lif_if_t ifdata, netstack_t *ns) 632 { 633 ASSERT(ns != NULL); 634 return (ip_lifgetnext_impl(phy_ifdata, ifdata, B_FALSE, 635 ns->netstack_ip)); 636 } 637 638 /* 639 * Get next interface from the current list of IPv6 logical network interfaces 640 */ 641 static lif_if_t 642 ipv6_lifgetnext(phy_if_t phy_ifdata, lif_if_t ifdata, netstack_t *ns) 643 { 644 ASSERT(ns != NULL); 645 return (ip_lifgetnext_impl(phy_ifdata, ifdata, B_TRUE, 646 ns->netstack_ip)); 647 } 648 649 /* 650 * Shared implementation to get next interface from the current list of 651 * logical network interfaces 652 */ 653 static lif_if_t 654 ip_lifgetnext_impl(phy_if_t phy_ifdata, lif_if_t ifdata, boolean_t isv6, 655 ip_stack_t *ipst) 656 { 657 lif_if_t newidx, oldidx; 658 boolean_t nextok; 659 ipif_t *ipif; 660 ill_t *ill; 661 662 ill = ill_lookup_on_ifindex(phy_ifdata, isv6, NULL, NULL, 663 NULL, NULL, ipst); 664 if (ill == NULL) 665 return (0); 666 667 if (ifdata != 0) { 668 oldidx = UNMAP_IPIF_ID(ifdata); 669 nextok = B_FALSE; 670 } else { 671 oldidx = 0; 672 nextok = B_TRUE; 673 } 674 675 mutex_enter(&ill->ill_lock); 676 if (ill->ill_state_flags & ILL_CONDEMNED) { 677 mutex_exit(&ill->ill_lock); 678 ill_refrele(ill); 679 return (0); 680 } 681 682 /* 683 * It's safe to iterate the ill_ipif list when holding an ill_lock. 684 * And it's also safe to access ipif_id without ipif refhold. 685 * See ipif_get_id(). 686 */ 687 for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) { 688 if (!IPIF_CAN_LOOKUP(ipif)) 689 continue; 690 if (nextok) { 691 ipif_refhold_locked(ipif); 692 break; 693 } else if (oldidx == ipif->ipif_id) { 694 nextok = B_TRUE; 695 } 696 } 697 698 mutex_exit(&ill->ill_lock); 699 ill_refrele(ill); 700 701 if (ipif == NULL) 702 return (0); 703 704 newidx = ipif->ipif_id; 705 ipif_refrele(ipif); 706 707 return (MAP_IPIF_ID(newidx)); 708 } 709 710 /* 711 * Inject an IPv4 packet to or from an interface 712 */ 713 static int 714 ip_inject(inject_t style, net_inject_t *packet, netstack_t *ns) 715 { 716 ASSERT(ns != NULL); 717 return (ip_inject_impl(style, packet, B_FALSE, ns->netstack_ip)); 718 } 719 720 721 /* 722 * Inject an IPv6 packet to or from an interface 723 */ 724 static int 725 ipv6_inject(inject_t style, net_inject_t *packet, netstack_t *ns) 726 { 727 ASSERT(ns != NULL); 728 return (ip_inject_impl(style, packet, B_TRUE, ns->netstack_ip)); 729 } 730 731 /* 732 * Shared implementation to inject a packet to or from an interface 733 * Return value: 734 * 0: successful 735 * -1: memory allocation failed 736 * 1: other errors 737 */ 738 static int 739 ip_inject_impl(inject_t style, net_inject_t *packet, boolean_t isv6, 740 ip_stack_t *ipst) 741 { 742 struct sockaddr_in6 *sin6; 743 ddi_taskq_t *tq = NULL; 744 void (* func)(void *); 745 injection_t *inject; 746 ip6_t *ip6h; 747 ire_t *ire; 748 mblk_t *mp; 749 750 ASSERT(packet != NULL); 751 ASSERT(packet->ni_packet != NULL); 752 ASSERT(packet->ni_packet->b_datap->db_type == M_DATA); 753 754 switch (style) { 755 case NI_QUEUE_IN: 756 inject = kmem_alloc(sizeof (*inject), KM_NOSLEEP); 757 if (inject == NULL) 758 return (-1); 759 inject->inj_data = *packet; 760 inject->inj_isv6 = isv6; 761 /* 762 * deliver up into the kernel, immitating its reception by a 763 * network interface, add to list and schedule timeout 764 */ 765 func = ip_ni_queue_in_func; 766 tq = eventq_queue_in; 767 break; 768 769 case NI_QUEUE_OUT: 770 inject = kmem_alloc(sizeof (*inject), KM_NOSLEEP); 771 if (inject == NULL) 772 return (-1); 773 inject->inj_data = *packet; 774 inject->inj_isv6 = isv6; 775 /* 776 * deliver out of the kernel, as if it were being sent via a 777 * raw socket so that IPFilter will see it again, add to list 778 * and schedule timeout 779 */ 780 func = ip_ni_queue_out_func; 781 tq = eventq_queue_out; 782 break; 783 784 case NI_DIRECT_OUT: 785 /* 786 * Note: 787 * For IPv4, the code path below will be greatly simplified 788 * with the delivery of surya - it will become a single 789 * function call to X. A follow on project is aimed to 790 * provide similar functionality for IPv6. 791 */ 792 mp = packet->ni_packet; 793 794 if (!isv6) { 795 struct sockaddr *sock; 796 797 sock = (struct sockaddr *)&packet->ni_addr; 798 /* 799 * ipfil_sendpkt was provided by surya to ease the 800 * problems associated with sending out a packet. 801 * Currently this function only supports IPv4. 802 */ 803 switch (ipfil_sendpkt(sock, mp, packet->ni_physical, 804 netstackid_to_zoneid( 805 ipst->ips_netstack->netstack_stackid))) { 806 case 0 : 807 case EINPROGRESS: 808 return (0); 809 case ECOMM : 810 case ENONET : 811 return (1); 812 default : 813 return (1); 814 } 815 /* NOTREACHED */ 816 817 } 818 819 ip6h = (ip6_t *)mp->b_rptr; 820 sin6 = (struct sockaddr_in6 *)&packet->ni_addr; 821 ASSERT(sin6->sin6_family == AF_INET6); 822 823 ire = ire_route_lookup_v6(&sin6->sin6_addr, 0, 0, 0, 824 NULL, NULL, ALL_ZONES, NULL, 825 MATCH_IRE_DSTONLY|MATCH_IRE_DEFAULT|MATCH_IRE_RECURSIVE, 826 ipst); 827 828 if (ire == NULL) { 829 ip2dbg(("ip_inject: ire_cache_lookup failed\n")); 830 freemsg(mp); 831 return (1); 832 } 833 834 if (ire->ire_stq == NULL) { 835 /* Send to loopback destination. */ 836 if (ire->ire_rfq == NULL) { 837 ip2dbg(("ip_inject: bad nexthop\n")); 838 ire_refrele(ire); 839 freemsg(mp); 840 return (1); 841 } 842 ip_wput_local_v6(ire->ire_rfq, 843 ire->ire_ipif->ipif_ill, ip6h, mp, ire, 0); 844 ire_refrele(ire); 845 return (0); 846 } 847 848 mp->b_queue = ire->ire_stq; 849 850 if (ire->ire_nce == NULL || 851 ire->ire_nce->nce_fp_mp == NULL && 852 ire->ire_nce->nce_res_mp == NULL) { 853 ip_newroute_v6(ire->ire_stq, mp, 854 &sin6->sin6_addr, NULL, NULL, ALL_ZONES, ipst); 855 856 ire_refrele(ire); 857 return (0); 858 } else { 859 /* prepend L2 header for IPv6 packets. */ 860 mblk_t *llmp; 861 862 /* 863 * Lock IREs, see 6420438 864 */ 865 mutex_enter(&ire->ire_lock); 866 llmp = ire->ire_nce->nce_fp_mp ? 867 ire->ire_nce->nce_fp_mp : 868 ire->ire_nce->nce_res_mp; 869 870 if ((mp = dupb(llmp)) == NULL && 871 (mp = copyb(llmp)) == NULL) { 872 ip2dbg(("ip_inject: llhdr failed\n")); 873 mutex_exit(&ire->ire_lock); 874 ire_refrele(ire); 875 freemsg(mp); 876 return (1); 877 } 878 mutex_exit(&ire->ire_lock); 879 linkb(mp, packet->ni_packet); 880 } 881 882 mp->b_queue = ire->ire_stq; 883 884 break; 885 default: 886 freemsg(packet->ni_packet); 887 return (1); 888 } 889 890 if (tq) { 891 inject->inj_ptr = ipst; 892 if (ddi_taskq_dispatch(tq, func, (void *)inject, 893 DDI_SLEEP) == DDI_FAILURE) { 894 ip2dbg(("ip_inject: ddi_taskq_dispatch failed\n")); 895 freemsg(packet->ni_packet); 896 return (1); 897 } 898 } else { 899 putnext(ire->ire_stq, mp); 900 ire_refrele(ire); 901 } 902 903 return (0); 904 } 905 906 /* 907 * Find the interface used for traffic to a given IPv4 address 908 */ 909 static phy_if_t 910 ip_routeto(struct sockaddr *address, netstack_t *ns) 911 { 912 ASSERT(address != NULL); 913 ASSERT(ns != NULL); 914 915 if (address->sa_family != AF_INET) 916 return (0); 917 return (ip_routeto_impl(address, ns->netstack_ip)); 918 } 919 920 /* 921 * Find the interface used for traffic to a given IPv6 address 922 */ 923 static phy_if_t 924 ipv6_routeto(struct sockaddr *address, netstack_t *ns) 925 { 926 ASSERT(address != NULL); 927 ASSERT(ns != NULL); 928 929 if (address->sa_family != AF_INET6) 930 return (0); 931 return (ip_routeto_impl(address, ns->netstack_ip)); 932 } 933 934 935 /* 936 * Find the interface used for traffic to an address 937 */ 938 static phy_if_t 939 ip_routeto_impl(struct sockaddr *address, ip_stack_t *ipst) 940 { 941 ire_t *ire; 942 ill_t *ill; 943 phy_if_t phy_if; 944 945 if (address->sa_family == AF_INET6) { 946 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)address; 947 ire = ire_route_lookup_v6(&sin6->sin6_addr, NULL, 948 0, 0, NULL, NULL, ALL_ZONES, NULL, 949 MATCH_IRE_DSTONLY|MATCH_IRE_DEFAULT|MATCH_IRE_RECURSIVE, 950 ipst); 951 } else { 952 struct sockaddr_in *sin = (struct sockaddr_in *)address; 953 ire = ire_route_lookup(sin->sin_addr.s_addr, 0, 954 0, 0, NULL, NULL, ALL_ZONES, NULL, 955 MATCH_IRE_DSTONLY|MATCH_IRE_DEFAULT|MATCH_IRE_RECURSIVE, 956 ipst); 957 } 958 959 if (ire == NULL) 960 return (0); 961 962 ill = ire_to_ill(ire); 963 if (ill == NULL) 964 return (0); 965 966 ASSERT(ill != NULL); 967 phy_if = (phy_if_t)ill->ill_phyint->phyint_ifindex; 968 ire_refrele(ire); 969 970 return (phy_if); 971 } 972 973 /* 974 * Determine if checksumming is being used for the given packet. 975 * 976 * Return value: 977 * NET_HCK_NONE: full checksum recalculation is required 978 * NET_HCK_L3_FULL: full layer 3 checksum 979 * NET_HCK_L4_FULL: full layer 4 checksum 980 * NET_HCK_L4_PART: partial layer 4 checksum 981 */ 982 static int 983 ip_ispartialchecksum(mblk_t *mp) 984 { 985 int ret = 0; 986 987 ASSERT(mp != NULL); 988 989 if ((DB_CKSUMFLAGS(mp) & HCK_FULLCKSUM) != 0) { 990 ret |= (int)NET_HCK_L4_FULL; 991 if ((DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM) != 0) 992 ret |= (int)NET_HCK_L3_FULL; 993 } 994 if ((DB_CKSUMFLAGS(mp) & HCK_PARTIALCKSUM) != 0) { 995 ret |= (int)NET_HCK_L4_PART; 996 if ((DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM) != 0) 997 ret |= (int)NET_HCK_L3_FULL; 998 } 999 1000 return (ret); 1001 } 1002 1003 /* 1004 * Return true or false, indicating whether the network and transport 1005 * headers are correct. Use the capabilities flags and flags set in the 1006 * dblk_t to determine whether or not the checksum is valid. 1007 * 1008 * Return: 1009 * 0: the checksum was incorrect 1010 * 1: the original checksum was correct 1011 */ 1012 static int 1013 ip_isvalidchecksum(mblk_t *mp) 1014 { 1015 unsigned char *wptr; 1016 ipha_t *ipha = (ipha_t *)mp->b_rptr; 1017 int hlen; 1018 int ret; 1019 1020 ASSERT(mp != NULL); 1021 1022 if (dohwcksum && 1023 DB_CKSUM16(mp) != 0xFFFF && 1024 (DB_CKSUMFLAGS(mp) & HCK_FULLCKSUM) && 1025 (DB_CKSUMFLAGS(mp) & HCK_FULLCKSUM_OK) && 1026 (DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM)) 1027 return (1); 1028 1029 hlen = (ipha->ipha_version_and_hdr_length & 0x0F) << 2; 1030 1031 /* 1032 * Check that the mblk being passed in has enough data in it 1033 * before blindly checking ip_cksum. 1034 */ 1035 if (msgdsize(mp) < hlen) 1036 return (0); 1037 1038 if (mp->b_wptr < mp->b_rptr + hlen) { 1039 if (pullupmsg(mp, hlen) == 0) 1040 return (0); 1041 wptr = mp->b_wptr; 1042 } else { 1043 wptr = mp->b_wptr; 1044 mp->b_wptr = mp->b_rptr + hlen; 1045 } 1046 1047 if (ipha->ipha_hdr_checksum == ip_cksum(mp, 0, ipha->ipha_hdr_checksum)) 1048 ret = 1; 1049 else 1050 ret = 0; 1051 mp->b_wptr = wptr; 1052 1053 return (ret); 1054 } 1055 1056 /* 1057 * Unsupported with IPv6 1058 */ 1059 /*ARGSUSED*/ 1060 static int 1061 ipv6_isvalidchecksum(mblk_t *mp) 1062 { 1063 return (-1); 1064 } 1065 1066 /* 1067 * Determine the network addresses for an IPv4 interface 1068 */ 1069 static int 1070 ip_getlifaddr(phy_if_t phy_ifdata, lif_if_t ifdata, size_t nelem, 1071 net_ifaddr_t type[], void *storage, netstack_t *ns) 1072 { 1073 ASSERT(ns != NULL); 1074 return (ip_getlifaddr_impl(AF_INET, phy_ifdata, ifdata, 1075 nelem, type, storage, ns->netstack_ip)); 1076 } 1077 1078 /* 1079 * Determine the network addresses for an IPv6 interface 1080 */ 1081 static int 1082 ipv6_getlifaddr(phy_if_t phy_ifdata, lif_if_t ifdata, size_t nelem, 1083 net_ifaddr_t type[], void *storage, netstack_t *ns) 1084 { 1085 ASSERT(ns != NULL); 1086 return (ip_getlifaddr_impl(AF_INET6, phy_ifdata, ifdata, 1087 nelem, type, storage, ns->netstack_ip)); 1088 } 1089 1090 /* 1091 * Shared implementation to determine the network addresses for an interface 1092 */ 1093 /* ARGSUSED */ 1094 static int 1095 ip_getlifaddr_impl(sa_family_t family, phy_if_t phy_ifdata, 1096 lif_if_t ifdata, size_t nelem, net_ifaddr_t type[], 1097 struct sockaddr *storage, ip_stack_t *ipst) 1098 { 1099 struct sockaddr_in6 *sin6; 1100 struct sockaddr_in *sin; 1101 lif_if_t ipifid; 1102 ipif_t *ipif; 1103 int i; 1104 1105 ASSERT(type != NULL); 1106 ASSERT(storage != NULL); 1107 1108 ipifid = UNMAP_IPIF_ID(ifdata); 1109 1110 if (family == AF_INET) { 1111 if ((ipif = ipif_getby_indexes((uint_t)phy_ifdata, 1112 (uint_t)ipifid, B_FALSE, ipst)) == NULL) 1113 return (1); 1114 1115 sin = (struct sockaddr_in *)storage; 1116 for (i = 0; i < nelem; i++, sin++) { 1117 if (ip_getifaddr_type(AF_INET, ipif, type[i], 1118 &sin->sin_addr) < 0) { 1119 ip2dbg(("ip_getlifaddr_impl failed type %d\n", 1120 type[i])); 1121 ipif_refrele(ipif); 1122 return (1); 1123 } 1124 } 1125 } else { 1126 if ((ipif = ipif_getby_indexes((uint_t)phy_ifdata, 1127 (uint_t)ipifid, B_TRUE, ipst)) == NULL) 1128 return (1); 1129 1130 sin6 = (struct sockaddr_in6 *)storage; 1131 for (i = 0; i < nelem; i++, sin6++) { 1132 if (ip_getifaddr_type(AF_INET6, ipif, type[i], 1133 &sin6->sin6_addr) < 0) { 1134 ip2dbg(("ip_getlifaddr_impl failed type %d\n", 1135 type[i])); 1136 ipif_refrele(ipif); 1137 return (1); 1138 } 1139 } 1140 } 1141 ipif_refrele(ipif); 1142 return (0); 1143 } 1144 1145 /* 1146 * ip_getlifaddr private function 1147 */ 1148 static int 1149 ip_getifaddr_type(sa_family_t family, ipif_t *ill_ipif, 1150 lif_if_t type, void *storage) 1151 { 1152 void *src_addr; 1153 int mem_size; 1154 1155 ASSERT(ill_ipif != NULL); 1156 ASSERT(storage != NULL); 1157 1158 if (family == AF_INET) { 1159 mem_size = sizeof (struct in_addr); 1160 1161 switch (type) { 1162 case NA_ADDRESS: 1163 src_addr = &(ill_ipif->ipif_lcl_addr); 1164 break; 1165 case NA_PEER: 1166 src_addr = &(ill_ipif->ipif_pp_dst_addr); 1167 break; 1168 case NA_BROADCAST: 1169 src_addr = &(ill_ipif->ipif_brd_addr); 1170 break; 1171 case NA_NETMASK: 1172 src_addr = &(ill_ipif->ipif_net_mask); 1173 break; 1174 default: 1175 return (-1); 1176 /*NOTREACHED*/ 1177 } 1178 } else { 1179 mem_size = sizeof (struct in6_addr); 1180 1181 switch (type) { 1182 case NA_ADDRESS: 1183 src_addr = &(ill_ipif->ipif_v6lcl_addr); 1184 break; 1185 case NA_PEER: 1186 src_addr = &(ill_ipif->ipif_v6pp_dst_addr); 1187 break; 1188 case NA_BROADCAST: 1189 src_addr = &(ill_ipif->ipif_v6brd_addr); 1190 break; 1191 case NA_NETMASK: 1192 src_addr = &(ill_ipif->ipif_v6net_mask); 1193 break; 1194 default: 1195 return (-1); 1196 /*NOTREACHED*/ 1197 } 1198 } 1199 1200 (void) memcpy(storage, src_addr, mem_size); 1201 return (1); 1202 } 1203 1204 /* 1205 * Deliver packet up into the kernel, immitating its reception by a 1206 * network interface. 1207 */ 1208 static void 1209 ip_ni_queue_in_func(void *inject) 1210 { 1211 ip_ni_queue_func_impl(inject, B_FALSE); 1212 } 1213 1214 /* 1215 * Deliver out of the kernel, as if it were being sent via a 1216 * raw socket so that IPFilter will see it again. 1217 */ 1218 static void 1219 ip_ni_queue_out_func(void *inject) 1220 { 1221 ip_ni_queue_func_impl(inject, B_TRUE); 1222 } 1223 1224 /* 1225 * Shared implementation for inject via ip_output and ip_input 1226 */ 1227 static void 1228 ip_ni_queue_func_impl(injection_t *inject, boolean_t out) 1229 { 1230 net_inject_t *packet; 1231 conn_t *conn; 1232 ill_t *ill; 1233 ip_stack_t *ipst = (ip_stack_t *)inject->inj_ptr; 1234 1235 ASSERT(inject != NULL); 1236 packet = &inject->inj_data; 1237 ASSERT(packet->ni_packet != NULL); 1238 1239 if ((ill = ill_lookup_on_ifindex((uint_t)packet->ni_physical, 1240 B_FALSE, NULL, NULL, NULL, NULL, ipst)) == NULL) { 1241 kmem_free(inject, sizeof (*inject)); 1242 return; 1243 } 1244 1245 if (out == 0) { 1246 if (inject->inj_isv6) { 1247 ip_rput_v6(ill->ill_rq, packet->ni_packet); 1248 } else { 1249 ip_input(ill, NULL, packet->ni_packet, 0); 1250 } 1251 kmem_free(inject, sizeof (*inject)); 1252 ill_refrele(ill); 1253 return; 1254 } 1255 1256 /* 1257 * Even though ipcl_conn_create requests that it be passed 1258 * a different value for "TCP", in this case there may not 1259 * be a TCP connection backing the packet and more than 1260 * likely, non-TCP packets will go here too. 1261 */ 1262 conn = ipcl_conn_create(IPCL_IPCCONN, KM_NOSLEEP, ipst->ips_netstack); 1263 if (conn != NULL) { 1264 if (inject->inj_isv6) { 1265 conn->conn_flags |= IPCL_ISV6; 1266 conn->conn_af_isv6 = B_TRUE; 1267 conn->conn_src_preferences = IPV6_PREFER_SRC_DEFAULT; 1268 conn->conn_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; 1269 ip_output_v6(conn, packet->ni_packet, ill->ill_wq, 1270 IP_WPUT); 1271 } else { 1272 conn->conn_af_isv6 = B_FALSE; 1273 conn->conn_pkt_isv6 = B_FALSE; 1274 conn->conn_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; 1275 ip_output(conn, packet->ni_packet, ill->ill_wq, 1276 IP_WPUT); 1277 } 1278 1279 CONN_DEC_REF(conn); 1280 } 1281 1282 kmem_free(inject, sizeof (*inject)); 1283 ill_refrele(ill); 1284 } 1285 1286 /* 1287 * taskq function for nic events. 1288 */ 1289 void 1290 ip_ne_queue_func(void *arg) 1291 { 1292 hook_event_int_t *hr; 1293 hook_nic_event_t *info = (hook_nic_event_t *)arg; 1294 netstack_t *ns = info->hne_family->netd_netstack; 1295 ip_stack_t *ipst = ns->netstack_ip; 1296 1297 hr = (info->hne_family == ipst->ips_ipv6_net_data) ? 1298 ipst->ips_ipv6nicevents : ipst->ips_ipv4nicevents; 1299 (void) hook_run(hr, (hook_data_t)info, ns); 1300 1301 if (info->hne_data != NULL) 1302 kmem_free(info->hne_data, info->hne_datalen); 1303 kmem_free(arg, sizeof (hook_nic_event_t)); 1304 } 1305