1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 * 11 * Copyright 2019 Joyent, Inc. 12 */ 13 14 #include <sys/hook.h> 15 #include <sys/hook_event.h> 16 17 #include "viona_impl.h" 18 19 20 /* 21 * Global linked list of viona_neti_ts. Access is protected by viona_neti_lock 22 */ 23 static list_t viona_neti_list; 24 static kmutex_t viona_neti_lock; 25 26 /* 27 * viona_neti is allocated and initialized during attach, and read-only 28 * until detach (where it's also freed) 29 */ 30 static net_instance_t *viona_neti; 31 32 33 /* 34 * Generate a hook event for the packet in *mpp headed in the direction 35 * indicated by 'out'. If the packet is accepted, 0 is returned. If the 36 * packet is rejected, an error is returned. The hook function may or may not 37 * alter or even free *mpp. The caller is expected to deal with either 38 * situation. 39 */ 40 int 41 viona_hook(viona_link_t *link, viona_vring_t *ring, mblk_t **mpp, boolean_t out) 42 { 43 viona_neti_t *nip = link->l_neti; 44 viona_nethook_t *vnh = &nip->vni_nethook; 45 hook_pkt_event_t info; 46 hook_event_t he; 47 hook_event_token_t het; 48 int ret; 49 50 he = out ? vnh->vnh_event_out : vnh->vnh_event_in; 51 het = out ? vnh->vnh_token_out : vnh->vnh_token_in; 52 53 if (!he.he_interested) 54 return (0); 55 56 info.hpe_protocol = vnh->vnh_neti; 57 info.hpe_ifp = (phy_if_t)link; 58 info.hpe_ofp = (phy_if_t)link; 59 info.hpe_mp = mpp; 60 info.hpe_flags = 0; 61 62 ret = hook_run(vnh->vnh_neti->netd_hooks, het, (hook_data_t)&info); 63 if (ret == 0) 64 return (0); 65 66 if (out) { 67 VIONA_PROBE3(tx_hook_drop, viona_vring_t *, ring, 68 mblk_t *, *mpp, int, ret); 69 VIONA_RING_STAT_INCR(ring, tx_hookdrop); 70 } else { 71 VIONA_PROBE3(rx_hook_drop, viona_vring_t *, ring, 72 mblk_t *, *mpp, int, ret); 73 VIONA_RING_STAT_INCR(ring, rx_hookdrop); 74 } 75 return (ret); 76 } 77 78 /* 79 * netinfo stubs - required by the nethook framework, but otherwise unused 80 * 81 * Currently, all ipf rules are applied against all interfaces in a given 82 * netstack (e.g. all interfaces in a zone). In the future if we want to 83 * support being able to apply different rules to different interfaces, I 84 * believe we would need to implement some of these stubs to map an interface 85 * name in a rule (e.g. 'net0', back to an index or viona_link_t); 86 */ 87 static int 88 viona_neti_getifname(net_handle_t neti __unused, phy_if_t phy __unused, 89 char *buf __unused, const size_t len __unused) 90 { 91 return (-1); 92 } 93 94 static int 95 viona_neti_getmtu(net_handle_t neti __unused, phy_if_t phy __unused, 96 lif_if_t ifdata __unused) 97 { 98 return (-1); 99 } 100 101 static int 102 viona_neti_getptmue(net_handle_t neti __unused) 103 { 104 return (-1); 105 } 106 107 static int 108 viona_neti_getlifaddr(net_handle_t neti __unused, phy_if_t phy __unused, 109 lif_if_t ifdata __unused, size_t nelem __unused, 110 net_ifaddr_t type[] __unused, void *storage __unused) 111 { 112 return (-1); 113 } 114 115 static int 116 viona_neti_getlifzone(net_handle_t neti __unused, phy_if_t phy __unused, 117 lif_if_t ifdata __unused, zoneid_t *zid __unused) 118 { 119 return (-1); 120 } 121 122 static int 123 viona_neti_getlifflags(net_handle_t neti __unused, phy_if_t phy __unused, 124 lif_if_t ifdata __unused, uint64_t *flags __unused) 125 { 126 return (-1); 127 } 128 129 static phy_if_t 130 viona_neti_phygetnext(net_handle_t neti __unused, phy_if_t phy __unused) 131 { 132 return ((phy_if_t)-1); 133 } 134 135 static phy_if_t 136 viona_neti_phylookup(net_handle_t neti __unused, const char *name __unused) 137 { 138 return ((phy_if_t)-1); 139 } 140 141 static lif_if_t 142 viona_neti_lifgetnext(net_handle_t neti __unused, phy_if_t phy __unused, 143 lif_if_t ifdata __unused) 144 { 145 return (-1); 146 } 147 148 static int 149 viona_neti_inject(net_handle_t neti __unused, inject_t style __unused, 150 net_inject_t *packet __unused) 151 { 152 return (-1); 153 } 154 155 static phy_if_t 156 viona_neti_route(net_handle_t neti __unused, struct sockaddr *address __unused, 157 struct sockaddr *next __unused) 158 { 159 return ((phy_if_t)-1); 160 } 161 162 static int 163 viona_neti_ispchksum(net_handle_t neti __unused, mblk_t *mp __unused) 164 { 165 return (-1); 166 } 167 168 static int 169 viona_neti_isvchksum(net_handle_t neti __unused, mblk_t *mp __unused) 170 { 171 return (-1); 172 } 173 174 static net_protocol_t viona_netinfo = { 175 NETINFO_VERSION, 176 NHF_VIONA, 177 viona_neti_getifname, 178 viona_neti_getmtu, 179 viona_neti_getptmue, 180 viona_neti_getlifaddr, 181 viona_neti_getlifzone, 182 viona_neti_getlifflags, 183 viona_neti_phygetnext, 184 viona_neti_phylookup, 185 viona_neti_lifgetnext, 186 viona_neti_inject, 187 viona_neti_route, 188 viona_neti_ispchksum, 189 viona_neti_isvchksum 190 }; 191 192 /* 193 * Create/register our nethooks 194 */ 195 static int 196 viona_nethook_init(netid_t nid, viona_nethook_t *vnh, char *nh_name, 197 net_protocol_t *netip) 198 { 199 int ret; 200 201 if ((vnh->vnh_neti = net_protocol_register(nid, netip)) == NULL) { 202 cmn_err(CE_NOTE, "%s: net_protocol_register failed " 203 "(netid=%d name=%s)", __func__, nid, nh_name); 204 goto fail_init_proto; 205 } 206 207 HOOK_FAMILY_INIT(&vnh->vnh_family, nh_name); 208 if ((ret = net_family_register(vnh->vnh_neti, &vnh->vnh_family)) != 0) { 209 cmn_err(CE_NOTE, "%s: net_family_register failed " 210 "(netid=%d name=%s err=%d)", __func__, 211 nid, nh_name, ret); 212 goto fail_init_family; 213 } 214 215 HOOK_EVENT_INIT(&vnh->vnh_event_in, NH_PHYSICAL_IN); 216 if ((vnh->vnh_token_in = net_event_register(vnh->vnh_neti, 217 &vnh->vnh_event_in)) == NULL) { 218 cmn_err(CE_NOTE, "%s: net_event_register %s failed " 219 "(netid=%d name=%s)", __func__, NH_PHYSICAL_IN, nid, 220 nh_name); 221 goto fail_init_event_in; 222 } 223 224 HOOK_EVENT_INIT(&vnh->vnh_event_out, NH_PHYSICAL_OUT); 225 if ((vnh->vnh_token_out = net_event_register(vnh->vnh_neti, 226 &vnh->vnh_event_out)) == NULL) { 227 cmn_err(CE_NOTE, "%s: net_event_register %s failed " 228 "(netid=%d name=%s)", __func__, NH_PHYSICAL_OUT, nid, 229 nh_name); 230 goto fail_init_event_out; 231 } 232 return (0); 233 234 /* 235 * On failure, we undo all the steps that succeeded in the 236 * reverse order of initialization, starting at the last 237 * successful step (the labels denoting the failing step). 238 */ 239 fail_init_event_out: 240 VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_in)); 241 VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_in)); 242 vnh->vnh_token_in = NULL; 243 244 fail_init_event_in: 245 VERIFY0(net_family_shutdown(vnh->vnh_neti, &vnh->vnh_family)); 246 VERIFY0(net_family_unregister(vnh->vnh_neti, &vnh->vnh_family)); 247 248 fail_init_family: 249 VERIFY0(net_protocol_unregister(vnh->vnh_neti)); 250 vnh->vnh_neti = NULL; 251 252 fail_init_proto: 253 return (1); 254 } 255 256 /* 257 * Shutdown the nethooks for a protocol family. This triggers notification 258 * callbacks to anything that has registered interest to allow hook consumers 259 * to unhook prior to the removal of the hooks as well as makes them unavailable 260 * to any future consumers as the first step of removal. 261 */ 262 static void 263 viona_nethook_shutdown(viona_nethook_t *vnh) 264 { 265 VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_out)); 266 VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_in)); 267 VERIFY0(net_family_shutdown(vnh->vnh_neti, &vnh->vnh_family)); 268 } 269 270 /* 271 * Remove the nethooks for a protocol family. 272 */ 273 static void 274 viona_nethook_fini(viona_nethook_t *vnh) 275 { 276 VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_out)); 277 VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_in)); 278 VERIFY0(net_family_unregister(vnh->vnh_neti, &vnh->vnh_family)); 279 VERIFY0(net_protocol_unregister(vnh->vnh_neti)); 280 vnh->vnh_neti = NULL; 281 } 282 283 /* 284 * Callback invoked by the neti module. This creates/registers our hooks 285 * {IPv4,IPv6}{in,out} with the nethook framework so they are available to 286 * interested consumers (e.g. ipf). 287 * 288 * During attach, viona_neti_create is called once for every netstack 289 * present on the system at the time of attach. Thereafter, it is called 290 * during the creation of additional netstack instances (i.e. zone boot). As a 291 * result, the viona_neti_t that is created during this call always occurs 292 * prior to any viona instances that will use it to send hook events. 293 * 294 * It should never return NULL. If we cannot register our hooks, we do not 295 * set vnh_hooked of the respective protocol family, which will prevent the 296 * creation of any viona instances on this netstack (see viona_ioc_create). 297 * This can only occur if after a shutdown event (which means destruction is 298 * imminent) we are trying to create a new instance. 299 */ 300 static void * 301 viona_neti_create(const netid_t netid) 302 { 303 viona_neti_t *nip; 304 305 VERIFY(netid != -1); 306 307 nip = kmem_zalloc(sizeof (*nip), KM_SLEEP); 308 nip->vni_netid = netid; 309 nip->vni_zid = net_getzoneidbynetid(netid); 310 mutex_init(&nip->vni_lock, NULL, MUTEX_DRIVER, NULL); 311 list_create(&nip->vni_dev_list, sizeof (viona_soft_state_t), 312 offsetof(viona_soft_state_t, ss_node)); 313 314 if (viona_nethook_init(netid, &nip->vni_nethook, Hn_VIONA, 315 &viona_netinfo) == 0) 316 nip->vni_nethook.vnh_hooked = B_TRUE; 317 318 mutex_enter(&viona_neti_lock); 319 list_insert_tail(&viona_neti_list, nip); 320 mutex_exit(&viona_neti_lock); 321 322 return (nip); 323 } 324 325 /* 326 * Called during netstack teardown by the neti module. During teardown, all 327 * the shutdown callbacks are invoked, allowing consumers to release any holds 328 * and otherwise quiesce themselves prior to destruction, followed by the 329 * actual destruction callbacks. 330 */ 331 static void 332 viona_neti_shutdown(netid_t nid, void *arg) 333 { 334 viona_neti_t *nip = arg; 335 336 ASSERT(nip != NULL); 337 VERIFY(nid == nip->vni_netid); 338 339 mutex_enter(&viona_neti_lock); 340 list_remove(&viona_neti_list, nip); 341 mutex_exit(&viona_neti_lock); 342 343 if (nip->vni_nethook.vnh_hooked) 344 viona_nethook_shutdown(&nip->vni_nethook); 345 } 346 347 /* 348 * Called during netstack teardown by the neti module. Destroys the viona 349 * netinst data. This is invoked after all the netstack and neti shutdown 350 * callbacks have been invoked. 351 */ 352 static void 353 viona_neti_destroy(netid_t nid, void *arg) 354 { 355 viona_neti_t *nip = arg; 356 357 ASSERT(nip != NULL); 358 VERIFY(nid == nip->vni_netid); 359 360 mutex_enter(&nip->vni_lock); 361 while (nip->vni_ref != 0) 362 cv_wait(&nip->vni_ref_change, &nip->vni_lock); 363 mutex_exit(&nip->vni_lock); 364 365 VERIFY(!list_link_active(&nip->vni_node)); 366 367 if (nip->vni_nethook.vnh_hooked) 368 viona_nethook_fini(&nip->vni_nethook); 369 370 mutex_destroy(&nip->vni_lock); 371 list_destroy(&nip->vni_dev_list); 372 kmem_free(nip, sizeof (*nip)); 373 } 374 375 /* 376 * Find the viona netinst data by zone id. This is only used during 377 * viona instance creation (and thus is only called by a zone that is running). 378 */ 379 viona_neti_t * 380 viona_neti_lookup_by_zid(zoneid_t zid) 381 { 382 viona_neti_t *nip; 383 384 mutex_enter(&viona_neti_lock); 385 for (nip = list_head(&viona_neti_list); nip != NULL; 386 nip = list_next(&viona_neti_list, nip)) { 387 if (nip->vni_zid == zid) { 388 mutex_enter(&nip->vni_lock); 389 nip->vni_ref++; 390 mutex_exit(&nip->vni_lock); 391 mutex_exit(&viona_neti_lock); 392 return (nip); 393 } 394 } 395 mutex_exit(&viona_neti_lock); 396 return (NULL); 397 } 398 399 void 400 viona_neti_rele(viona_neti_t *nip) 401 { 402 mutex_enter(&nip->vni_lock); 403 VERIFY3S(nip->vni_ref, >, 0); 404 nip->vni_ref--; 405 mutex_exit(&nip->vni_lock); 406 cv_broadcast(&nip->vni_ref_change); 407 } 408 409 void 410 viona_neti_attach(void) 411 { 412 mutex_init(&viona_neti_lock, NULL, MUTEX_DRIVER, NULL); 413 list_create(&viona_neti_list, sizeof (viona_neti_t), 414 offsetof(viona_neti_t, vni_node)); 415 416 /* This can only fail if NETINFO_VERSION is wrong */ 417 viona_neti = net_instance_alloc(NETINFO_VERSION); 418 VERIFY(viona_neti != NULL); 419 420 viona_neti->nin_name = "viona"; 421 viona_neti->nin_create = viona_neti_create; 422 viona_neti->nin_shutdown = viona_neti_shutdown; 423 viona_neti->nin_destroy = viona_neti_destroy; 424 /* This can only fail if we've registered ourselves multiple times */ 425 VERIFY3S(net_instance_register(viona_neti), ==, DDI_SUCCESS); 426 } 427 428 void 429 viona_neti_detach(void) 430 { 431 /* This can only fail if we've not registered previously */ 432 VERIFY3S(net_instance_unregister(viona_neti), ==, DDI_SUCCESS); 433 net_instance_free(viona_neti); 434 viona_neti = NULL; 435 436 list_destroy(&viona_neti_list); 437 mutex_destroy(&viona_neti_lock); 438 } 439