1 /*- 2 * 3 * Copyright (c) 1999-2001, Vitaly V Belekhov 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice unmodified, this list of conditions, and the following 11 * disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/errno.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/mbuf.h> 37 #include <sys/errno.h> 38 #include <sys/proc.h> 39 #include <sys/sockio.h> 40 #include <sys/socket.h> 41 #include <sys/syslog.h> 42 43 #include <net/if.h> 44 #include <net/if_var.h> 45 #include <net/if_media.h> 46 #include <net/if_types.h> 47 #include <net/netisr.h> 48 #include <net/route.h> 49 #include <net/vnet.h> 50 51 #include <netgraph/ng_message.h> 52 #include <netgraph/netgraph.h> 53 #include <netgraph/ng_parse.h> 54 #include <netgraph/ng_eiface.h> 55 56 #include <net/bpf.h> 57 #include <net/ethernet.h> 58 #include <net/if_arp.h> 59 60 static const struct ng_cmdlist ng_eiface_cmdlist[] = { 61 { 62 NGM_EIFACE_COOKIE, 63 NGM_EIFACE_GET_IFNAME, 64 "getifname", 65 NULL, 66 &ng_parse_string_type 67 }, 68 { 69 NGM_EIFACE_COOKIE, 70 NGM_EIFACE_SET, 71 "set", 72 &ng_parse_enaddr_type, 73 NULL 74 }, 75 { 0 } 76 }; 77 78 /* Node private data */ 79 struct ng_eiface_private { 80 struct ifnet *ifp; /* per-interface network data */ 81 struct ifmedia media; /* (fake) media information */ 82 int link_status; /* fake */ 83 int unit; /* Interface unit number */ 84 node_p node; /* Our netgraph node */ 85 hook_p ether; /* Hook for ethernet stream */ 86 }; 87 typedef struct ng_eiface_private *priv_p; 88 89 /* Interface methods */ 90 static void ng_eiface_init(void *xsc); 91 static void ng_eiface_start(struct ifnet *ifp); 92 static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 93 #ifdef DEBUG 94 static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); 95 #endif 96 97 /* Netgraph methods */ 98 static int ng_eiface_mod_event(module_t, int, void *); 99 static ng_constructor_t ng_eiface_constructor; 100 static ng_rcvmsg_t ng_eiface_rcvmsg; 101 static ng_shutdown_t ng_eiface_rmnode; 102 static ng_newhook_t ng_eiface_newhook; 103 static ng_rcvdata_t ng_eiface_rcvdata; 104 static ng_disconnect_t ng_eiface_disconnect; 105 106 /* Node type descriptor */ 107 static struct ng_type typestruct = { 108 .version = NG_ABI_VERSION, 109 .name = NG_EIFACE_NODE_TYPE, 110 .mod_event = ng_eiface_mod_event, 111 .constructor = ng_eiface_constructor, 112 .rcvmsg = ng_eiface_rcvmsg, 113 .shutdown = ng_eiface_rmnode, 114 .newhook = ng_eiface_newhook, 115 .rcvdata = ng_eiface_rcvdata, 116 .disconnect = ng_eiface_disconnect, 117 .cmdlist = ng_eiface_cmdlist 118 }; 119 NETGRAPH_INIT(eiface, &typestruct); 120 121 static VNET_DEFINE(struct unrhdr *, ng_eiface_unit); 122 #define V_ng_eiface_unit VNET(ng_eiface_unit) 123 124 /************************************************************************ 125 INTERFACE STUFF 126 ************************************************************************/ 127 128 /* 129 * Process an ioctl for the virtual interface 130 */ 131 static int 132 ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 133 { 134 const priv_p priv = (priv_p)ifp->if_softc; 135 struct ifreq *const ifr = (struct ifreq *)data; 136 int error = 0; 137 138 #ifdef DEBUG 139 ng_eiface_print_ioctl(ifp, command, data); 140 #endif 141 switch (command) { 142 143 /* These two are mostly handled at a higher layer */ 144 case SIOCSIFADDR: 145 error = ether_ioctl(ifp, command, data); 146 break; 147 case SIOCGIFADDR: 148 break; 149 150 /* Set flags */ 151 case SIOCSIFFLAGS: 152 /* 153 * If the interface is marked up and stopped, then start it. 154 * If it is marked down and running, then stop it. 155 */ 156 if (ifp->if_flags & IFF_UP) { 157 if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 158 ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); 159 ifp->if_drv_flags |= IFF_DRV_RUNNING; 160 } 161 } else { 162 if (ifp->if_drv_flags & IFF_DRV_RUNNING) 163 ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | 164 IFF_DRV_OACTIVE); 165 } 166 break; 167 168 /* Set the interface MTU */ 169 case SIOCSIFMTU: 170 if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX || 171 ifr->ifr_mtu < NG_EIFACE_MTU_MIN) 172 error = EINVAL; 173 else 174 ifp->if_mtu = ifr->ifr_mtu; 175 break; 176 177 /* (Fake) media type manipulation */ 178 case SIOCSIFMEDIA: 179 case SIOCGIFMEDIA: 180 error = ifmedia_ioctl(ifp, ifr, &priv->media, command); 181 break; 182 183 /* Stuff that's not supported */ 184 case SIOCADDMULTI: 185 case SIOCDELMULTI: 186 error = 0; 187 break; 188 case SIOCSIFPHYS: 189 error = EOPNOTSUPP; 190 break; 191 192 default: 193 error = EINVAL; 194 break; 195 } 196 return (error); 197 } 198 199 static void 200 ng_eiface_init(void *xsc) 201 { 202 priv_p sc = xsc; 203 struct ifnet *ifp = sc->ifp; 204 205 ifp->if_drv_flags |= IFF_DRV_RUNNING; 206 ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 207 } 208 209 /* 210 * We simply relay the packet to the "ether" hook, if it is connected. 211 * We have been through the netgraph locking and are guaranteed to 212 * be the only code running in this node at this time. 213 */ 214 static void 215 ng_eiface_start2(node_p node, hook_p hook, void *arg1, int arg2) 216 { 217 struct ifnet *ifp = arg1; 218 const priv_p priv = (priv_p)ifp->if_softc; 219 int error = 0; 220 struct mbuf *m; 221 222 /* Check interface flags */ 223 224 if (!((ifp->if_flags & IFF_UP) && 225 (ifp->if_drv_flags & IFF_DRV_RUNNING))) 226 return; 227 228 for (;;) { 229 /* 230 * Grab a packet to transmit. 231 */ 232 IF_DEQUEUE(&ifp->if_snd, m); 233 234 /* If there's nothing to send, break. */ 235 if (m == NULL) 236 break; 237 238 /* 239 * Berkeley packet filter. 240 * Pass packet to bpf if there is a listener. 241 * XXX is this safe? locking? 242 */ 243 BPF_MTAP(ifp, m); 244 245 if (ifp->if_flags & IFF_MONITOR) { 246 ifp->if_ipackets++; 247 m_freem(m); 248 continue; 249 } 250 251 /* 252 * Send packet; if hook is not connected, mbuf will get 253 * freed. 254 */ 255 NG_OUTBOUND_THREAD_REF(); 256 NG_SEND_DATA_ONLY(error, priv->ether, m); 257 NG_OUTBOUND_THREAD_UNREF(); 258 259 /* Update stats */ 260 if (error == 0) 261 ifp->if_opackets++; 262 else 263 ifp->if_oerrors++; 264 } 265 266 ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 267 268 return; 269 } 270 271 /* 272 * This routine is called to deliver a packet out the interface. 273 * We simply queue the netgraph version to be called when netgraph locking 274 * allows it to happen. 275 * Until we know what the rest of the networking code is doing for 276 * locking, we don't know how we will interact with it. 277 * Take comfort from the fact that the ifnet struct is part of our 278 * private info and can't go away while we are queued. 279 * [Though we don't know it is still there now....] 280 * it is possible we don't gain anything from this because 281 * we would like to get the mbuf and queue it as data 282 * somehow, but we can't and if we did would we solve anything? 283 */ 284 static void 285 ng_eiface_start(struct ifnet *ifp) 286 { 287 const priv_p priv = (priv_p)ifp->if_softc; 288 289 /* Don't do anything if output is active */ 290 if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 291 return; 292 293 ifp->if_drv_flags |= IFF_DRV_OACTIVE; 294 295 if (ng_send_fn(priv->node, NULL, &ng_eiface_start2, ifp, 0) != 0) 296 ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 297 } 298 299 #ifdef DEBUG 300 /* 301 * Display an ioctl to the virtual interface 302 */ 303 304 static void 305 ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) 306 { 307 char *str; 308 309 switch (command & IOC_DIRMASK) { 310 case IOC_VOID: 311 str = "IO"; 312 break; 313 case IOC_OUT: 314 str = "IOR"; 315 break; 316 case IOC_IN: 317 str = "IOW"; 318 break; 319 case IOC_INOUT: 320 str = "IORW"; 321 break; 322 default: 323 str = "IO??"; 324 } 325 log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", 326 ifp->if_xname, 327 str, 328 IOCGROUP(command), 329 command & 0xff, 330 IOCPARM_LEN(command)); 331 } 332 #endif /* DEBUG */ 333 334 /* 335 * ifmedia stuff 336 */ 337 static int 338 ng_eiface_mediachange(struct ifnet *ifp) 339 { 340 const priv_p priv = (priv_p)ifp->if_softc; 341 struct ifmedia *ifm = &priv->media; 342 343 if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 344 return (EINVAL); 345 if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) 346 ifp->if_baudrate = ifmedia_baudrate(IFM_ETHER | IFM_1000_T); 347 else 348 ifp->if_baudrate = ifmedia_baudrate(ifm->ifm_media); 349 350 return (0); 351 } 352 353 static void 354 ng_eiface_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 355 { 356 const priv_p priv = (priv_p)ifp->if_softc; 357 struct ifmedia *ifm = &priv->media; 358 359 if (ifm->ifm_cur->ifm_media == (IFM_ETHER | IFM_AUTO) && 360 (priv->link_status & IFM_ACTIVE)) 361 ifmr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX; 362 else 363 ifmr->ifm_active = ifm->ifm_cur->ifm_media; 364 ifmr->ifm_status = priv->link_status; 365 366 return; 367 } 368 369 /************************************************************************ 370 NETGRAPH NODE STUFF 371 ************************************************************************/ 372 373 /* 374 * Constructor for a node 375 */ 376 static int 377 ng_eiface_constructor(node_p node) 378 { 379 struct ifnet *ifp; 380 priv_p priv; 381 u_char eaddr[6] = {0,0,0,0,0,0}; 382 383 /* Allocate node and interface private structures */ 384 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 385 386 ifp = priv->ifp = if_alloc(IFT_ETHER); 387 if (ifp == NULL) { 388 free(priv, M_NETGRAPH); 389 return (ENOSPC); 390 } 391 392 /* Link them together */ 393 ifp->if_softc = priv; 394 395 /* Get an interface unit number */ 396 priv->unit = alloc_unr(V_ng_eiface_unit); 397 398 /* Link together node and private info */ 399 NG_NODE_SET_PRIVATE(node, priv); 400 priv->node = node; 401 402 /* Initialize interface structure */ 403 if_initname(ifp, NG_EIFACE_EIFACE_NAME, priv->unit); 404 ifp->if_init = ng_eiface_init; 405 ifp->if_output = ether_output; 406 ifp->if_start = ng_eiface_start; 407 ifp->if_ioctl = ng_eiface_ioctl; 408 ifp->if_snd.ifq_maxlen = ifqmaxlen; 409 ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); 410 ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU; 411 ifp->if_capenable = IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU; 412 ifmedia_init(&priv->media, 0, ng_eiface_mediachange, 413 ng_eiface_mediastatus); 414 ifmedia_add(&priv->media, IFM_ETHER | IFM_10_T, 0, NULL); 415 ifmedia_add(&priv->media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); 416 ifmedia_add(&priv->media, IFM_ETHER | IFM_100_TX, 0, NULL); 417 ifmedia_add(&priv->media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); 418 ifmedia_add(&priv->media, IFM_ETHER | IFM_1000_T, 0, NULL); 419 ifmedia_add(&priv->media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); 420 ifmedia_add(&priv->media, IFM_ETHER | IFM_10G_T | IFM_FDX, 0, NULL); 421 ifmedia_add(&priv->media, IFM_ETHER | IFM_AUTO, 0, NULL); 422 ifmedia_set(&priv->media, IFM_ETHER | IFM_AUTO); 423 priv->link_status = IFM_AVALID; 424 425 /* Give this node the same name as the interface (if possible) */ 426 if (ng_name_node(node, ifp->if_xname) != 0) 427 log(LOG_WARNING, "%s: can't acquire netgraph name\n", 428 ifp->if_xname); 429 430 /* Attach the interface */ 431 ether_ifattach(ifp, eaddr); 432 ifp->if_baudrate = ifmedia_baudrate(IFM_ETHER | IFM_1000_T); 433 434 /* Done */ 435 return (0); 436 } 437 438 /* 439 * Give our ok for a hook to be added 440 */ 441 static int 442 ng_eiface_newhook(node_p node, hook_p hook, const char *name) 443 { 444 priv_p priv = NG_NODE_PRIVATE(node); 445 struct ifnet *ifp = priv->ifp; 446 447 if (strcmp(name, NG_EIFACE_HOOK_ETHER)) 448 return (EPFNOSUPPORT); 449 if (priv->ether != NULL) 450 return (EISCONN); 451 priv->ether = hook; 452 NG_HOOK_SET_PRIVATE(hook, &priv->ether); 453 NG_HOOK_SET_TO_INBOUND(hook); 454 455 priv->link_status |= IFM_ACTIVE; 456 CURVNET_SET_QUIET(ifp->if_vnet); 457 if_link_state_change(ifp, LINK_STATE_UP); 458 CURVNET_RESTORE(); 459 460 return (0); 461 } 462 463 /* 464 * Receive a control message 465 */ 466 static int 467 ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook) 468 { 469 const priv_p priv = NG_NODE_PRIVATE(node); 470 struct ifnet *const ifp = priv->ifp; 471 struct ng_mesg *resp = NULL; 472 int error = 0; 473 struct ng_mesg *msg; 474 475 NGI_GET_MSG(item, msg); 476 switch (msg->header.typecookie) { 477 case NGM_EIFACE_COOKIE: 478 switch (msg->header.cmd) { 479 480 case NGM_EIFACE_SET: 481 { 482 if (msg->header.arglen != ETHER_ADDR_LEN) { 483 error = EINVAL; 484 break; 485 } 486 error = if_setlladdr(priv->ifp, 487 (u_char *)msg->data, ETHER_ADDR_LEN); 488 EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp); 489 break; 490 } 491 492 case NGM_EIFACE_GET_IFNAME: 493 NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); 494 if (resp == NULL) { 495 error = ENOMEM; 496 break; 497 } 498 strlcpy(resp->data, ifp->if_xname, IFNAMSIZ); 499 break; 500 501 case NGM_EIFACE_GET_IFADDRS: 502 { 503 struct ifaddr *ifa; 504 caddr_t ptr; 505 int buflen; 506 507 /* Determine size of response and allocate it */ 508 buflen = 0; 509 if_addr_rlock(ifp); 510 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 511 buflen += SA_SIZE(ifa->ifa_addr); 512 NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); 513 if (resp == NULL) { 514 if_addr_runlock(ifp); 515 error = ENOMEM; 516 break; 517 } 518 519 /* Add addresses */ 520 ptr = resp->data; 521 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 522 const int len = SA_SIZE(ifa->ifa_addr); 523 524 if (buflen < len) { 525 log(LOG_ERR, "%s: len changed?\n", 526 ifp->if_xname); 527 break; 528 } 529 bcopy(ifa->ifa_addr, ptr, len); 530 ptr += len; 531 buflen -= len; 532 } 533 if_addr_runlock(ifp); 534 break; 535 } 536 537 default: 538 error = EINVAL; 539 break; 540 } /* end of inner switch() */ 541 break; 542 case NGM_FLOW_COOKIE: 543 CURVNET_SET_QUIET(ifp->if_vnet); 544 switch (msg->header.cmd) { 545 case NGM_LINK_IS_UP: 546 priv->link_status |= IFM_ACTIVE; 547 if_link_state_change(ifp, LINK_STATE_UP); 548 break; 549 case NGM_LINK_IS_DOWN: 550 priv->link_status &= ~IFM_ACTIVE; 551 if_link_state_change(ifp, LINK_STATE_DOWN); 552 break; 553 default: 554 break; 555 } 556 CURVNET_RESTORE(); 557 break; 558 default: 559 error = EINVAL; 560 break; 561 } 562 NG_RESPOND_MSG(error, node, item, resp); 563 NG_FREE_MSG(msg); 564 return (error); 565 } 566 567 /* 568 * Receive data from a hook. Pass the packet to the ether_input routine. 569 */ 570 static int 571 ng_eiface_rcvdata(hook_p hook, item_p item) 572 { 573 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 574 struct ifnet *const ifp = priv->ifp; 575 struct mbuf *m; 576 577 NGI_GET_M(item, m); 578 NG_FREE_ITEM(item); 579 580 if (!((ifp->if_flags & IFF_UP) && 581 (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 582 NG_FREE_M(m); 583 return (ENETDOWN); 584 } 585 586 if (m->m_len < ETHER_HDR_LEN) { 587 m = m_pullup(m, ETHER_HDR_LEN); 588 if (m == NULL) 589 return (EINVAL); 590 } 591 592 /* Note receiving interface */ 593 m->m_pkthdr.rcvif = ifp; 594 595 /* Update interface stats */ 596 ifp->if_ipackets++; 597 598 (*ifp->if_input)(ifp, m); 599 600 /* Done */ 601 return (0); 602 } 603 604 /* 605 * Shutdown processing. 606 */ 607 static int 608 ng_eiface_rmnode(node_p node) 609 { 610 const priv_p priv = NG_NODE_PRIVATE(node); 611 struct ifnet *const ifp = priv->ifp; 612 613 /* 614 * the ifnet may be in a different vnet than the netgraph node, 615 * hence we have to change the current vnet context here. 616 */ 617 CURVNET_SET_QUIET(ifp->if_vnet); 618 ifmedia_removeall(&priv->media); 619 ether_ifdetach(ifp); 620 if_free(ifp); 621 CURVNET_RESTORE(); 622 free_unr(V_ng_eiface_unit, priv->unit); 623 free(priv, M_NETGRAPH); 624 NG_NODE_SET_PRIVATE(node, NULL); 625 NG_NODE_UNREF(node); 626 return (0); 627 } 628 629 /* 630 * Hook disconnection 631 */ 632 static int 633 ng_eiface_disconnect(hook_p hook) 634 { 635 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 636 637 priv->ether = NULL; 638 priv->link_status &= ~IFM_ACTIVE; 639 CURVNET_SET_QUIET(priv->ifp->if_vnet); 640 if_link_state_change(priv->ifp, LINK_STATE_DOWN); 641 CURVNET_RESTORE(); 642 return (0); 643 } 644 645 /* 646 * Handle loading and unloading for this node type. 647 */ 648 static int 649 ng_eiface_mod_event(module_t mod, int event, void *data) 650 { 651 int error = 0; 652 653 switch (event) { 654 case MOD_LOAD: 655 case MOD_UNLOAD: 656 break; 657 default: 658 error = EOPNOTSUPP; 659 break; 660 } 661 return (error); 662 } 663 664 static void 665 vnet_ng_eiface_init(const void *unused) 666 { 667 668 V_ng_eiface_unit = new_unrhdr(0, 0xffff, NULL); 669 } 670 VNET_SYSINIT(vnet_ng_eiface_init, SI_SUB_PSEUDO, SI_ORDER_ANY, 671 vnet_ng_eiface_init, NULL); 672 673 static void 674 vnet_ng_eiface_uninit(const void *unused) 675 { 676 677 delete_unrhdr(V_ng_eiface_unit); 678 } 679 VNET_SYSUNINIT(vnet_ng_eiface_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY, 680 vnet_ng_eiface_uninit, NULL); 681