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