1 /* 2 * ng_eiface.c 3 * 4 * Copyright (c) 1999-2000, Vitaly V Belekhov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Id: ng_eiface.c,v 1.14 2000/03/15 12:28:44 vitaly Exp $ 30 * $FreeBSD: src/sys/netgraph/ng_eiface.c,v 1.4.2.5 2002/12/17 21:47:48 julian Exp $ 31 * $DragonFly: src/sys/netgraph/eiface/ng_eiface.c,v 1.6 2004/07/23 07:16:31 joerg Exp $ 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/errno.h> 37 #include <sys/kernel.h> 38 #include <sys/malloc.h> 39 #include <sys/mbuf.h> 40 #include <sys/errno.h> 41 #include <sys/sockio.h> 42 #include <sys/socket.h> 43 #include <sys/syslog.h> 44 45 #include <net/if.h> 46 #include <net/if_types.h> 47 #include <net/netisr.h> 48 49 50 #include <netgraph/ng_message.h> 51 #include <netgraph/netgraph.h> 52 #include <netgraph/ng_parse.h> 53 #include "ng_eiface.h" 54 55 #include <net/bpf.h> 56 #include <net/ethernet.h> 57 #include <net/if_arp.h> 58 59 static const struct ng_parse_struct_field ng_eiface_par_fields[] 60 = NG_EIFACE_PAR_FIELDS; 61 62 static const struct ng_parse_type ng_eiface_par_type = { 63 &ng_parse_struct_type, 64 &ng_eiface_par_fields 65 }; 66 67 static const struct ng_cmdlist ng_eiface_cmdlist[] = { 68 { 69 NGM_EIFACE_COOKIE, 70 NGM_EIFACE_SET, 71 "set", 72 &ng_eiface_par_type, 73 NULL 74 }, 75 { 0 } 76 }; 77 78 /* Node private data */ 79 struct ng_eiface_private { 80 struct arpcom arpcom; /* per-interface network data */ 81 struct ifnet *ifp; /* This interface */ 82 node_p node; /* Our netgraph node */ 83 hook_p ether; /* Hook for ethernet stream */ 84 struct private *next; /* When hung on the free list */ 85 }; 86 typedef struct ng_eiface_private *priv_p; 87 88 /* Interface methods */ 89 static void ng_eiface_init(void *xsc); 90 static void ng_eiface_start(struct ifnet *ifp); 91 static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 92 #ifdef DEBUG 93 static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); 94 #endif 95 96 /* Netgraph methods */ 97 static ng_constructor_t ng_eiface_constructor; 98 static ng_rcvmsg_t ng_eiface_rcvmsg; 99 static ng_shutdown_t ng_eiface_rmnode; 100 static ng_newhook_t ng_eiface_newhook; 101 static ng_rcvdata_t ng_eiface_rcvdata; 102 static ng_connect_t ng_eiface_connect; 103 static ng_disconnect_t ng_eiface_disconnect; 104 105 /* Node type descriptor */ 106 static struct ng_type typestruct = { 107 NG_VERSION, 108 NG_EIFACE_NODE_TYPE, 109 NULL, 110 ng_eiface_constructor, 111 ng_eiface_rcvmsg, 112 ng_eiface_rmnode, 113 ng_eiface_newhook, 114 NULL, 115 ng_eiface_connect, 116 ng_eiface_rcvdata, 117 ng_eiface_rcvdata, 118 ng_eiface_disconnect, 119 ng_eiface_cmdlist 120 }; 121 NETGRAPH_INIT(eiface, &typestruct); 122 123 static char ng_eiface_ifname[] = NG_EIFACE_EIFACE_NAME; 124 static int ng_eiface_next_unit; 125 126 /************************************************************************ 127 INTERFACE STUFF 128 ************************************************************************/ 129 130 /* 131 * Process an ioctl for the virtual interface 132 */ 133 static int 134 ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 135 { 136 struct ifreq *const ifr = (struct ifreq *) data; 137 int s, error = 0; 138 139 #ifdef DEBUG 140 ng_eiface_print_ioctl(ifp, command, data); 141 #endif 142 s = splimp(); 143 switch (command) { 144 145 /* These two are mostly handled at a higher layer */ 146 case SIOCSIFADDR: 147 error = ether_ioctl(ifp, command, data); 148 break; 149 case SIOCGIFADDR: 150 break; 151 152 /* Set flags */ 153 case SIOCSIFFLAGS: 154 /* 155 * If the interface is marked up and stopped, then start it. 156 * If it is marked down and running, then stop it. 157 */ 158 if (ifr->ifr_flags & IFF_UP) { 159 if (!(ifp->if_flags & IFF_RUNNING)) { 160 ifp->if_flags &= ~(IFF_OACTIVE); 161 ifp->if_flags |= IFF_RUNNING; 162 } 163 } else { 164 if (ifp->if_flags & IFF_RUNNING) 165 ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 166 } 167 break; 168 169 /* Set the interface MTU */ 170 case SIOCSIFMTU: 171 if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX 172 || ifr->ifr_mtu < NG_EIFACE_MTU_MIN) 173 error = EINVAL; 174 else 175 ifp->if_mtu = ifr->ifr_mtu; 176 break; 177 178 /* Stuff that's not supported */ 179 case SIOCADDMULTI: 180 case SIOCDELMULTI: 181 error = 0; 182 break; 183 case SIOCSIFPHYS: 184 error = EOPNOTSUPP; 185 break; 186 187 default: 188 error = EINVAL; 189 break; 190 } 191 (void) splx(s); 192 return (error); 193 } 194 195 static void 196 ng_eiface_init(void *xsc) 197 { 198 priv_p sc = xsc; 199 struct ifnet *ifp = sc->ifp; 200 int s; 201 202 s = splimp(); 203 204 ifp->if_flags |= IFF_RUNNING; 205 ifp->if_flags &= ~IFF_OACTIVE; 206 207 splx(s); 208 209 } 210 211 /* 212 * This routine is called to deliver a packet out the interface. 213 * We simply relay the packet to 214 * the ether hook, if it is connected. 215 */ 216 217 static void 218 ng_eiface_start(struct ifnet *ifp) 219 { 220 const priv_p priv = (priv_p) ifp->if_softc; 221 meta_p meta = NULL; 222 int len, error = 0; 223 struct mbuf *m; 224 225 /* Check interface flags */ 226 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 227 return; 228 229 /* Don't do anything if output is active */ 230 if( ifp->if_flags & IFF_OACTIVE ) 231 return; 232 233 ifp->if_flags |= IFF_OACTIVE; 234 235 /* 236 * Grab a packet to transmit. 237 */ 238 IF_DEQUEUE(&ifp->if_snd, m); 239 240 /* If there's nothing to send, return. */ 241 if(m == NULL) 242 { 243 ifp->if_flags &= ~IFF_OACTIVE; 244 return; 245 } 246 247 /* Berkeley packet filter */ 248 /* 249 * Pass packet to bpf if there is a listener. 250 */ 251 if (ifp->if_bpf) 252 bpf_mtap(ifp, m); 253 254 /* Copy length before the mbuf gets invalidated */ 255 len = m->m_pkthdr.len; 256 257 /* Send packet; if hook is not connected, mbuf will get freed. */ 258 NG_SEND_DATA(error, priv->ether, m, meta); 259 260 /* Update stats */ 261 if (error == 0) { 262 ifp->if_obytes += len; 263 ifp->if_opackets++; 264 } 265 266 ifp->if_flags &= ~IFF_OACTIVE; 267 268 return; 269 } 270 271 #ifdef DEBUG 272 /* 273 * Display an ioctl to the virtual interface 274 */ 275 276 static void 277 ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) 278 { 279 char *str; 280 281 switch (command & IOC_DIRMASK) { 282 case IOC_VOID: 283 str = "IO"; 284 break; 285 case IOC_OUT: 286 str = "IOR"; 287 break; 288 case IOC_IN: 289 str = "IOW"; 290 break; 291 case IOC_INOUT: 292 str = "IORW"; 293 break; 294 default: 295 str = "IO??"; 296 } 297 log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", 298 ifp->if_xname, 299 str, 300 IOCGROUP(command), 301 command & 0xff, 302 IOCPARM_LEN(command)); 303 } 304 #endif /* DEBUG */ 305 306 /************************************************************************ 307 NETGRAPH NODE STUFF 308 ************************************************************************/ 309 310 /* 311 * Constructor for a node 312 */ 313 static int 314 ng_eiface_constructor(node_p *nodep) 315 { 316 struct ifnet *ifp; 317 node_p node; 318 priv_p priv; 319 int error = 0; 320 321 /* Allocate node and interface private structures */ 322 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 323 if (priv == NULL) 324 return (ENOMEM); 325 bzero(priv, sizeof(*priv)); 326 327 ifp = &(priv->arpcom.ac_if); 328 329 /* Link them together */ 330 ifp->if_softc = priv; 331 priv->ifp = ifp; 332 333 /* Call generic node constructor */ 334 if ((error = ng_make_node_common(&typestruct, nodep))) { 335 FREE(priv, M_NETGRAPH); 336 return (error); 337 } 338 node = *nodep; 339 340 /* Link together node and private info */ 341 node->private = priv; 342 priv->node = node; 343 344 /* Initialize interface structure */ 345 if_initname(ifp, ng_eiface_ifname, ng_eiface_next_unit++); 346 ifp->if_init = ng_eiface_init; 347 ifp->if_start = ng_eiface_start; 348 ifp->if_ioctl = ng_eiface_ioctl; 349 ifp->if_watchdog = NULL; 350 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 351 ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); 352 353 TAILQ_INIT(&ifp->if_addrhead); 354 355 /* Give this node name * 356 bzero(ifname, sizeof(ifname)); 357 sprintf(ifname, "if%s", ifp->if_xname); 358 (void) ng_name_node(node, ifname); 359 */ 360 361 /* Attach the interface */ 362 ether_ifattach(ifp, priv->arpcom.ac_enaddr); 363 364 /* Done */ 365 return (0); 366 } 367 368 /* 369 * Give our ok for a hook to be added 370 */ 371 static int 372 ng_eiface_newhook(node_p node, hook_p hook, const char *name) 373 { 374 priv_p priv = node->private; 375 376 if (strcmp(name, NG_EIFACE_HOOK_ETHER)) 377 return (EPFNOSUPPORT); 378 if (priv->ether != NULL) 379 return (EISCONN); 380 priv->ether = hook; 381 hook->private = &priv->ether; 382 383 return (0); 384 } 385 386 /* 387 * Receive a control message 388 */ 389 static int 390 ng_eiface_rcvmsg(node_p node, struct ng_mesg *msg, 391 const char *retaddr, struct ng_mesg **rptr) 392 { 393 const priv_p priv = node->private; 394 struct ifnet *const ifp = priv->ifp; 395 struct ng_mesg *resp = NULL; 396 int error = 0; 397 398 switch (msg->header.typecookie) { 399 case NGM_EIFACE_COOKIE: 400 switch (msg->header.cmd) { 401 402 case NGM_EIFACE_SET: 403 { 404 struct ng_eiface_par *eaddr; 405 406 if (msg->header.arglen != sizeof(struct ng_eiface_par)) 407 { 408 error = EINVAL; 409 break; 410 } 411 eaddr = (struct ng_eiface_par *)(msg->data); 412 413 priv->arpcom.ac_enaddr[0] = eaddr->oct0; 414 priv->arpcom.ac_enaddr[1] = eaddr->oct1; 415 priv->arpcom.ac_enaddr[2] = eaddr->oct2; 416 priv->arpcom.ac_enaddr[3] = eaddr->oct3; 417 priv->arpcom.ac_enaddr[4] = eaddr->oct4; 418 priv->arpcom.ac_enaddr[5] = eaddr->oct5; 419 420 break; 421 } 422 423 case NGM_EIFACE_GET_IFNAME: 424 { 425 struct ng_eiface_ifname *arg; 426 427 NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT); 428 if (resp == NULL) { 429 error = ENOMEM; 430 break; 431 } 432 arg = (struct ng_eiface_ifname *) resp->data; 433 sprintf(arg->ngif_name, 434 "%s", ifp->if_xname); /* XXX: strings */ 435 break; 436 } 437 438 case NGM_EIFACE_GET_IFADDRS: 439 { 440 struct ifaddr *ifa; 441 caddr_t ptr; 442 int buflen; 443 444 #define SA_SIZE(s) ((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len) 445 446 /* Determine size of response and allocate it */ 447 buflen = 0; 448 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) 449 buflen += SA_SIZE(ifa->ifa_addr); 450 NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); 451 if (resp == NULL) { 452 error = ENOMEM; 453 break; 454 } 455 456 /* Add addresses */ 457 ptr = resp->data; 458 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 459 const int len = SA_SIZE(ifa->ifa_addr); 460 461 if (buflen < len) { 462 log(LOG_ERR, "%s: len changed?\n", 463 ifp->if_xname); 464 break; 465 } 466 bcopy(ifa->ifa_addr, ptr, len); 467 ptr += len; 468 buflen -= len; 469 } 470 break; 471 #undef SA_SIZE 472 } 473 474 default: 475 error = EINVAL; 476 break; 477 } 478 break; 479 default: 480 error = EINVAL; 481 break; 482 } 483 if (rptr) 484 *rptr = resp; 485 else if (resp) 486 FREE(resp, M_NETGRAPH); 487 FREE(msg, M_NETGRAPH); 488 return (error); 489 } 490 491 /* 492 * Recive data from a hook. Pass the packet to the ether_input routine. 493 */ 494 static int 495 ng_eiface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 496 { 497 const priv_p priv = hook->node->private; 498 struct ifnet *const ifp = priv->ifp; 499 int s, error = 0; 500 struct ether_header *eh; 501 u_short ether_type; 502 503 /* Meta-data is end its life here... */ 504 NG_FREE_META(meta); 505 506 if (m == NULL) 507 { 508 printf("ng_eiface: mbuf is null.\n"); 509 return (EINVAL); 510 } 511 512 if ( !(ifp->if_flags & IFF_UP) ) { 513 return (ENETDOWN); 514 } 515 516 /* Note receiving interface */ 517 m->m_pkthdr.rcvif = ifp; 518 519 /* Update interface stats */ 520 ifp->if_ipackets++; 521 522 /* Berkeley packet filter */ 523 if (ifp->if_bpf) 524 bpf_mtap(ifp, m); 525 526 (*ifp->if_input)(ifp, m); 527 528 /* Done */ 529 return (error); 530 } 531 532 /* 533 * Because the BSD networking code doesn't support the removal of 534 * networking interfaces, iface nodes (once created) are persistent. 535 * So this method breaks all connections and marks the interface 536 * down, but does not remove the node. 537 */ 538 static int 539 ng_eiface_rmnode(node_p node) 540 { 541 const priv_p priv = node->private; 542 struct ifnet *const ifp = priv->ifp; 543 544 ng_cutlinks(node); 545 node->flags &= ~NG_INVALID; 546 ifp->if_flags &= ~(IFF_UP | IFF_RUNNING | IFF_OACTIVE); 547 return (0); 548 } 549 550 551 /* 552 * This is called once we've already connected a new hook to the other node. 553 * It gives us a chance to balk at the last minute. 554 */ 555 static int 556 ng_eiface_connect(hook_p hook) 557 { 558 /* be really amiable and just say "YUP that's OK by me! " */ 559 return (0); 560 } 561 562 /* 563 * Hook disconnection 564 */ 565 static int 566 ng_eiface_disconnect(hook_p hook) 567 { 568 const priv_p priv = hook->node->private; 569 570 priv->ether = NULL; 571 return (0); 572 } 573