ng_eiface.c revision 141140
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: head/sys/netgraph/ng_eiface.c 141140 2005-02-02 13:27:03Z ru $ 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/lock.h> 36#include <sys/malloc.h> 37#include <sys/mbuf.h> 38#include <sys/mutex.h> 39#include <sys/errno.h> 40#include <sys/sockio.h> 41#include <sys/socket.h> 42#include <sys/syslog.h> 43 44#include <net/if.h> 45#include <net/if_dl.h> 46#include <net/if_types.h> 47#include <net/netisr.h> 48 49#include <netinet/in.h> 50#include <netinet/if_ether.h> 51 52#include <netgraph/ng_message.h> 53#include <netgraph/netgraph.h> 54#include <netgraph/ng_parse.h> 55#include <netgraph/ng_eiface.h> 56 57#include <net/bpf.h> 58#include <net/ethernet.h> 59#include <net/if_arp.h> 60 61static const struct ng_cmdlist ng_eiface_cmdlist[] = { 62 { 63 NGM_EIFACE_COOKIE, 64 NGM_EIFACE_SET, 65 "set", 66 &ng_parse_enaddr_type, 67 NULL 68 }, 69 { 0 } 70}; 71 72/* Node private data */ 73struct ng_eiface_private { 74 struct arpcom arpcom; /* per-interface network data */ 75 struct ifnet *ifp; /* This interface */ 76 int unit; /* Interface unit number */ 77 node_p node; /* Our netgraph node */ 78 hook_p ether; /* Hook for ethernet stream */ 79}; 80typedef struct ng_eiface_private *priv_p; 81 82/* Interface methods */ 83static void ng_eiface_init(void *xsc); 84static void ng_eiface_start(struct ifnet *ifp); 85static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 86#ifdef DEBUG 87static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); 88#endif 89 90/* Netgraph methods */ 91static ng_constructor_t ng_eiface_constructor; 92static ng_rcvmsg_t ng_eiface_rcvmsg; 93static ng_shutdown_t ng_eiface_rmnode; 94static ng_newhook_t ng_eiface_newhook; 95static ng_rcvdata_t ng_eiface_rcvdata; 96static ng_connect_t ng_eiface_connect; 97static ng_disconnect_t ng_eiface_disconnect; 98 99/* Node type descriptor */ 100static struct ng_type typestruct = { 101 .version = NG_ABI_VERSION, 102 .name = NG_EIFACE_NODE_TYPE, 103 .constructor = ng_eiface_constructor, 104 .rcvmsg = ng_eiface_rcvmsg, 105 .shutdown = ng_eiface_rmnode, 106 .newhook = ng_eiface_newhook, 107 .connect = ng_eiface_connect, 108 .rcvdata = ng_eiface_rcvdata, 109 .disconnect = ng_eiface_disconnect, 110 .cmdlist = ng_eiface_cmdlist 111}; 112NETGRAPH_INIT(eiface, &typestruct); 113 114/* We keep a bitmap indicating which unit numbers are free. 115 One means the unit number is free, zero means it's taken. */ 116static int *ng_eiface_units = NULL; 117static int ng_eiface_units_len = 0; 118static int ng_units_in_use = 0; 119 120#define UNITS_BITSPERWORD (sizeof(*ng_eiface_units) * NBBY) 121 122static struct mtx ng_eiface_mtx; 123MTX_SYSINIT(ng_eiface, &ng_eiface_mtx, "ng_eiface", MTX_DEF); 124 125/************************************************************************ 126 HELPER STUFF 127 ************************************************************************/ 128/* 129 * Find the first free unit number for a new interface. 130 * Increase the size of the unit bitmap as necessary. 131 */ 132static __inline int 133ng_eiface_get_unit(int *unit) 134{ 135 int index, bit; 136 137 mtx_lock(&ng_eiface_mtx); 138 for (index = 0; index < ng_eiface_units_len 139 && ng_eiface_units[index] == 0; index++); 140 if (index == ng_eiface_units_len) { /* extend array */ 141 int i, *newarray, newlen; 142 143 newlen = (2 * ng_eiface_units_len) + 4; 144 MALLOC(newarray, int *, newlen * sizeof(*ng_eiface_units), 145 M_NETGRAPH, M_NOWAIT); 146 if (newarray == NULL) { 147 mtx_unlock(&ng_eiface_mtx); 148 return (ENOMEM); 149 } 150 bcopy(ng_eiface_units, newarray, 151 ng_eiface_units_len * sizeof(*ng_eiface_units)); 152 for (i = ng_eiface_units_len; i < newlen; i++) 153 newarray[i] = ~0; 154 if (ng_eiface_units != NULL) 155 FREE(ng_eiface_units, M_NETGRAPH); 156 ng_eiface_units = newarray; 157 ng_eiface_units_len = newlen; 158 } 159 bit = ffs(ng_eiface_units[index]) - 1; 160 KASSERT(bit >= 0 && bit <= UNITS_BITSPERWORD - 1, 161 ("%s: word=%d bit=%d", __func__, ng_eiface_units[index], bit)); 162 ng_eiface_units[index] &= ~(1 << bit); 163 *unit = (index * UNITS_BITSPERWORD) + bit; 164 ng_units_in_use++; 165 mtx_unlock(&ng_eiface_mtx); 166 return (0); 167} 168 169/* 170 * Free a no longer needed unit number. 171 */ 172static __inline void 173ng_eiface_free_unit(int unit) 174{ 175 int index, bit; 176 177 index = unit / UNITS_BITSPERWORD; 178 bit = unit % UNITS_BITSPERWORD; 179 mtx_lock(&ng_eiface_mtx); 180 KASSERT(index < ng_eiface_units_len, 181 ("%s: unit=%d len=%d", __func__, unit, ng_eiface_units_len)); 182 KASSERT((ng_eiface_units[index] & (1 << bit)) == 0, 183 ("%s: unit=%d is free", __func__, unit)); 184 ng_eiface_units[index] |= (1 << bit); 185 /* 186 * XXX We could think about reducing the size of ng_eiface_units[] 187 * XXX here if the last portion is all ones 188 * XXX At least free it if no more units. 189 * Needed if we are to eventually be able to unload. 190 */ 191 ng_units_in_use--; 192 if (ng_units_in_use == 0) { /* XXX make SMP safe */ 193 FREE(ng_eiface_units, M_NETGRAPH); 194 ng_eiface_units_len = 0; 195 ng_eiface_units = NULL; 196 } 197 mtx_unlock(&ng_eiface_mtx); 198} 199 200/************************************************************************ 201 INTERFACE STUFF 202 ************************************************************************/ 203 204/* 205 * Process an ioctl for the virtual interface 206 */ 207static int 208ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 209{ 210 struct ifreq *const ifr = (struct ifreq *)data; 211 int s, error = 0; 212 213#ifdef DEBUG 214 ng_eiface_print_ioctl(ifp, command, data); 215#endif 216 s = splimp(); 217 switch (command) { 218 219 /* These two are mostly handled at a higher layer */ 220 case SIOCSIFADDR: 221 error = ether_ioctl(ifp, command, data); 222 break; 223 case SIOCGIFADDR: 224 break; 225 226 /* Set flags */ 227 case SIOCSIFFLAGS: 228 /* 229 * If the interface is marked up and stopped, then start it. 230 * If it is marked down and running, then stop it. 231 */ 232 if (ifr->ifr_flags & IFF_UP) { 233 if (!(ifp->if_flags & IFF_RUNNING)) { 234 ifp->if_flags &= ~(IFF_OACTIVE); 235 ifp->if_flags |= IFF_RUNNING; 236 } 237 } else { 238 if (ifp->if_flags & IFF_RUNNING) 239 ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 240 } 241 break; 242 243 /* Set the interface MTU */ 244 case SIOCSIFMTU: 245 if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX || 246 ifr->ifr_mtu < NG_EIFACE_MTU_MIN) 247 error = EINVAL; 248 else 249 ifp->if_mtu = ifr->ifr_mtu; 250 break; 251 252 /* Stuff that's not supported */ 253 case SIOCADDMULTI: 254 case SIOCDELMULTI: 255 error = 0; 256 break; 257 case SIOCSIFPHYS: 258 error = EOPNOTSUPP; 259 break; 260 261 default: 262 error = EINVAL; 263 break; 264 } 265 splx(s); 266 return (error); 267} 268 269static void 270ng_eiface_init(void *xsc) 271{ 272 priv_p sc = xsc; 273 struct ifnet *ifp = sc->ifp; 274 int s; 275 276 s = splimp(); 277 278 ifp->if_flags |= IFF_RUNNING; 279 ifp->if_flags &= ~IFF_OACTIVE; 280 281 splx(s); 282} 283 284/* 285 * We simply relay the packet to the ether hook, if it is connected. 286 * We have been throughthe netgraph locking an are guaranteed to 287 * be the only code running in this node at this time. 288 */ 289static void 290ng_eiface_start2(node_p node, hook_p hook, void *arg1, int arg2) 291{ 292 struct ifnet *ifp = arg1; 293 const priv_p priv = (priv_p)ifp->if_softc; 294 int len, error = 0; 295 struct mbuf *m; 296 297 /* Check interface flags */ 298 if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) 299 return; 300 301 /* Don't do anything if output is active */ 302 if (ifp->if_flags & IFF_OACTIVE) 303 return; 304 305 ifp->if_flags |= IFF_OACTIVE; 306 307 /* 308 * Grab a packet to transmit. 309 */ 310 IF_DEQUEUE(&ifp->if_snd, m); 311 312 /* If there's nothing to send, return. */ 313 if (m == NULL) { 314 ifp->if_flags &= ~IFF_OACTIVE; 315 return; 316 } 317 318 /* 319 * Berkeley packet filter. 320 * Pass packet to bpf if there is a listener. 321 * XXX is this safe? locking? 322 */ 323 BPF_MTAP(ifp, m); 324 325 /* Copy length before the mbuf gets invalidated */ 326 len = m->m_pkthdr.len; 327 328 /* 329 * Send packet; if hook is not connected, mbuf will get 330 * freed. 331 */ 332 NG_SEND_DATA_ONLY(error, priv->ether, m); 333 334 /* Update stats */ 335 if (error == 0) { 336 ifp->if_obytes += len; 337 ifp->if_opackets++; 338 } 339 340 ifp->if_flags &= ~IFF_OACTIVE; 341 342 return; 343} 344 345/* 346 * This routine is called to deliver a packet out the interface. 347 * We simply queue the netgraph version to be called when netgraph locking 348 * allows it to happen. 349 * Until we know what the rest of the networking code is doing for 350 * locking, we don't know how we will interact with it. 351 * Take comfort from the fact that the ifnet struct is part of our 352 * private info and can't go away while we are queued. 353 * [Though we don't know it is still there now....] 354 * it is possible we don't gain anything from this because 355 * we would like to get the mbuf and queue it as data 356 * somehow, but we can't and if we did would we solve anything? 357 */ 358static void 359ng_eiface_start(struct ifnet *ifp) 360{ 361 362 const priv_p priv = (priv_p)ifp->if_softc; 363 364 ng_send_fn(priv->node, NULL, &ng_eiface_start2, ifp, 0); 365} 366 367#ifdef DEBUG 368/* 369 * Display an ioctl to the virtual interface 370 */ 371 372static void 373ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) 374{ 375 char *str; 376 377 switch (command & IOC_DIRMASK) { 378 case IOC_VOID: 379 str = "IO"; 380 break; 381 case IOC_OUT: 382 str = "IOR"; 383 break; 384 case IOC_IN: 385 str = "IOW"; 386 break; 387 case IOC_INOUT: 388 str = "IORW"; 389 break; 390 default: 391 str = "IO??"; 392 } 393 log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", 394 ifp->if_xname, 395 str, 396 IOCGROUP(command), 397 command & 0xff, 398 IOCPARM_LEN(command)); 399} 400#endif /* DEBUG */ 401 402/************************************************************************ 403 NETGRAPH NODE STUFF 404 ************************************************************************/ 405 406/* 407 * Constructor for a node 408 */ 409static int 410ng_eiface_constructor(node_p node) 411{ 412 struct ifnet *ifp; 413 priv_p priv; 414 int error = 0; 415 416 /* Allocate node and interface private structures */ 417 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 418 if (priv == NULL) 419 return (ENOMEM); 420 bzero(priv, sizeof(*priv)); 421 422 ifp = &(priv->arpcom.ac_if); 423 424 /* Link them together */ 425 ifp->if_softc = priv; 426 priv->ifp = ifp; 427 428 /* Get an interface unit number */ 429 if ((error = ng_eiface_get_unit(&priv->unit)) != 0) { 430 FREE(priv, M_NETGRAPH); 431 return (error); 432 } 433 434 /* Link together node and private info */ 435 NG_NODE_SET_PRIVATE(node, priv); 436 priv->node = node; 437 438 /* Initialize interface structure */ 439 if_initname(ifp, NG_EIFACE_EIFACE_NAME, priv->unit); 440 ifp->if_init = ng_eiface_init; 441 ifp->if_output = ether_output; 442 ifp->if_start = ng_eiface_start; 443 ifp->if_ioctl = ng_eiface_ioctl; 444 ifp->if_watchdog = NULL; 445 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 446 ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); 447 448#if 0 449 /* Give this node name */ 450 bzero(ifname, sizeof(ifname)); 451 sprintf(ifname, "if%s", ifp->if_xname); 452 (void)ng_name_node(node, ifname); 453#endif 454 455 /* Attach the interface */ 456 ether_ifattach(ifp, priv->arpcom.ac_enaddr); 457 458 /* Done */ 459 return (0); 460} 461 462/* 463 * Give our ok for a hook to be added 464 */ 465static int 466ng_eiface_newhook(node_p node, hook_p hook, const char *name) 467{ 468 priv_p priv = NG_NODE_PRIVATE(node); 469 470 if (strcmp(name, NG_EIFACE_HOOK_ETHER)) 471 return (EPFNOSUPPORT); 472 if (priv->ether != NULL) 473 return (EISCONN); 474 priv->ether = hook; 475 NG_HOOK_SET_PRIVATE(hook, &priv->ether); 476 477 return (0); 478} 479 480/* 481 * Receive a control message 482 */ 483static int 484ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook) 485{ 486 const priv_p priv = NG_NODE_PRIVATE(node); 487 struct ifnet *const ifp = priv->ifp; 488 struct ng_mesg *resp = NULL; 489 int error = 0; 490 struct ng_mesg *msg; 491 492 NGI_GET_MSG(item, msg); 493 switch (msg->header.typecookie) { 494 case NGM_EIFACE_COOKIE: 495 switch (msg->header.cmd) { 496 497 case NGM_EIFACE_SET: 498 { 499 struct ether_addr *eaddr; 500 struct ifaddr *ifa; 501 struct sockaddr_dl *sdl; 502 503 if (msg->header.arglen != sizeof(struct ether_addr)) { 504 error = EINVAL; 505 break; 506 } 507 eaddr = (struct ether_addr *)(msg->data); 508 bcopy(eaddr, priv->arpcom.ac_enaddr, ETHER_ADDR_LEN); 509 510 /* And put it in the ifaddr list */ 511 TAILQ_FOREACH(ifa, &(ifp->if_addrhead), ifa_link) { 512 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 513 if (sdl->sdl_type == IFT_ETHER) { 514 bcopy((IFP2AC(ifp))->ac_enaddr, 515 LLADDR(sdl), ifp->if_addrlen); 516 break; 517 } 518 } 519 break; 520 } 521 522 case NGM_EIFACE_GET_IFNAME: 523 { 524 struct ng_eiface_ifname *arg; 525 526 NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT); 527 if (resp == NULL) { 528 error = ENOMEM; 529 break; 530 } 531 arg = (struct ng_eiface_ifname *)resp->data; 532 strlcpy(arg->ngif_name, ifp->if_xname, 533 sizeof(arg->ngif_name)); 534 break; 535 } 536 537 case NGM_EIFACE_GET_IFADDRS: 538 { 539 struct ifaddr *ifa; 540 caddr_t ptr; 541 int buflen; 542 543#define SA_SIZE(s) ((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len) 544 545 /* Determine size of response and allocate it */ 546 buflen = 0; 547 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 548 buflen += SA_SIZE(ifa->ifa_addr); 549 NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); 550 if (resp == NULL) { 551 error = ENOMEM; 552 break; 553 } 554 555 /* Add addresses */ 556 ptr = resp->data; 557 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 558 const int len = SA_SIZE(ifa->ifa_addr); 559 560 if (buflen < len) { 561 log(LOG_ERR, "%s: len changed?\n", 562 ifp->if_xname); 563 break; 564 } 565 bcopy(ifa->ifa_addr, ptr, len); 566 ptr += len; 567 buflen -= len; 568 } 569 break; 570#undef SA_SIZE 571 } 572 573 default: 574 error = EINVAL; 575 break; 576 } /* end of inner switch() */ 577 break; 578 case NGM_FLOW_COOKIE: 579 switch (msg->header.cmd) { 580 case NGM_LINK_IS_UP: 581 ifp->if_flags |= IFF_RUNNING; 582 break; 583 case NGM_LINK_IS_DOWN: 584 ifp->if_flags &= ~IFF_RUNNING; 585 break; 586 default: 587 break; 588 } 589 break; 590 default: 591 error = EINVAL; 592 break; 593 } 594 NG_RESPOND_MSG(error, node, item, resp); 595 NG_FREE_MSG(msg); 596 return (error); 597} 598 599/* 600 * Receive data from a hook. Pass the packet to the ether_input routine. 601 */ 602static int 603ng_eiface_rcvdata(hook_p hook, item_p item) 604{ 605 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 606 struct ifnet *const ifp = priv->ifp; 607 struct mbuf *m; 608 609 NGI_GET_M(item, m); 610 NG_FREE_ITEM(item); 611 612 if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != 613 (IFF_UP | IFF_RUNNING)) { 614 NG_FREE_M(m); 615 return (ENETDOWN); 616 } 617 618 /* Note receiving interface */ 619 m->m_pkthdr.rcvif = ifp; 620 621 /* Update interface stats */ 622 ifp->if_ipackets++; 623 624 (*ifp->if_input)(ifp, m); 625 626 /* Done */ 627 return (0); 628} 629 630/* 631 * Shutdown processing. 632 */ 633static int 634ng_eiface_rmnode(node_p node) 635{ 636 const priv_p priv = NG_NODE_PRIVATE(node); 637 struct ifnet *const ifp = priv->ifp; 638 639 ether_ifdetach(ifp); 640 ng_eiface_free_unit(priv->unit); 641 FREE(priv, M_NETGRAPH); 642 NG_NODE_SET_PRIVATE(node, NULL); 643 NG_NODE_UNREF(node); 644 return (0); 645} 646 647 648/* 649 * This is called once we've already connected a new hook to the other node. 650 * It gives us a chance to balk at the last minute. 651 */ 652static int 653ng_eiface_connect(hook_p hook) 654{ 655 /* be really amiable and just say "YUP that's OK by me! " */ 656 return (0); 657} 658 659/* 660 * Hook disconnection 661 */ 662static int 663ng_eiface_disconnect(hook_p hook) 664{ 665 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 666 667 priv->ether = NULL; 668 return (0); 669} 670