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 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/errno.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/mbuf.h> 39 #include <sys/sockio.h> 40 #include <sys/socket.h> 41 #include <sys/syslog.h> 42 #include <sys/serialize.h> 43 #include <sys/thread2.h> 44 45 #include <net/if.h> 46 #include <net/if_types.h> 47 #include <net/ifq_var.h> 48 #include <net/netisr.h> 49 #include <net/route.h> 50 51 52 #include <netgraph/ng_message.h> 53 #include <netgraph/netgraph.h> 54 #include <netgraph/ng_parse.h> 55 #include "ng_eiface.h" 56 57 #include <net/bpf.h> 58 #include <net/ethernet.h> 59 #include <net/if_arp.h> 60 61 static const struct ng_parse_struct_field ng_eiface_par_fields[] 62 = NG_EIFACE_PAR_FIELDS; 63 64 static const struct ng_parse_type ng_eiface_par_type = { 65 &ng_parse_struct_type, 66 &ng_eiface_par_fields 67 }; 68 69 static const struct ng_cmdlist ng_eiface_cmdlist[] = { 70 { 71 NGM_EIFACE_COOKIE, 72 NGM_EIFACE_SET, 73 "set", 74 &ng_eiface_par_type, 75 NULL 76 }, 77 { 0 } 78 }; 79 80 /* Node private data */ 81 struct ng_eiface_private { 82 struct arpcom arpcom; /* per-interface network data */ 83 struct ifnet *ifp; /* This interface */ 84 node_p node; /* Our netgraph node */ 85 hook_p ether; /* Hook for ethernet stream */ 86 struct private *next; /* When hung on the free list */ 87 }; 88 typedef struct ng_eiface_private *priv_p; 89 90 /* Interface methods */ 91 static void ng_eiface_init(void *xsc); 92 static void ng_eiface_start(struct ifnet *ifp, struct ifaltq_subque *); 93 static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, 94 struct ucred *cr); 95 #ifdef DEBUG 96 static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); 97 #endif 98 99 /* Netgraph methods */ 100 static ng_constructor_t ng_eiface_constructor; 101 static ng_rcvmsg_t ng_eiface_rcvmsg; 102 static ng_shutdown_t ng_eiface_rmnode; 103 static ng_newhook_t ng_eiface_newhook; 104 static ng_rcvdata_t ng_eiface_rcvdata; 105 static ng_connect_t ng_eiface_connect; 106 static ng_disconnect_t ng_eiface_disconnect; 107 108 /* Node type descriptor */ 109 static struct ng_type typestruct = { 110 NG_VERSION, 111 NG_EIFACE_NODE_TYPE, 112 NULL, 113 ng_eiface_constructor, 114 ng_eiface_rcvmsg, 115 ng_eiface_rmnode, 116 ng_eiface_newhook, 117 NULL, 118 ng_eiface_connect, 119 ng_eiface_rcvdata, 120 ng_eiface_rcvdata, 121 ng_eiface_disconnect, 122 ng_eiface_cmdlist 123 }; 124 NETGRAPH_INIT(eiface, &typestruct); 125 126 static char ng_eiface_ifname[] = NG_EIFACE_EIFACE_NAME; 127 static int ng_eiface_next_unit; 128 129 /************************************************************************ 130 INTERFACE STUFF 131 ************************************************************************/ 132 133 /* 134 * Process an ioctl for the virtual interface 135 */ 136 static int 137 ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) 138 { 139 struct ifreq *const ifr = (struct ifreq *) data; 140 int error = 0; 141 142 #ifdef DEBUG 143 ng_eiface_print_ioctl(ifp, cmd, data); 144 #endif 145 crit_enter(); 146 switch (cmd) { 147 148 /* These two are mostly handled at a higher layer */ 149 case SIOCSIFADDR: 150 error = ether_ioctl(ifp, cmd, data); 151 break; 152 case SIOCGIFADDR: 153 break; 154 155 /* Set flags */ 156 case SIOCSIFFLAGS: 157 /* 158 * If the interface is marked up and stopped, then start it. 159 * If it is marked down and running, then stop it. 160 */ 161 if (ifr->ifr_flags & IFF_UP) { 162 if (!(ifp->if_flags & IFF_RUNNING)) { 163 ifq_clr_oactive(&ifp->if_snd); 164 ifp->if_flags |= IFF_RUNNING; 165 } 166 } else { 167 if (ifp->if_flags & IFF_RUNNING) { 168 ifp->if_flags &= ~IFF_RUNNING; 169 ifq_clr_oactive(&ifp->if_snd); 170 } 171 } 172 break; 173 174 /* Set the interface MTU */ 175 case SIOCSIFMTU: 176 if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX 177 || ifr->ifr_mtu < NG_EIFACE_MTU_MIN) 178 error = EINVAL; 179 else 180 ifp->if_mtu = ifr->ifr_mtu; 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 crit_exit(); 197 return (error); 198 } 199 200 static void 201 ng_eiface_init(void *xsc) 202 { 203 priv_p sc = xsc; 204 struct ifnet *ifp = sc->ifp; 205 206 crit_enter(); 207 208 ifp->if_flags |= IFF_RUNNING; 209 ifq_clr_oactive(&ifp->if_snd); 210 211 crit_exit(); 212 213 } 214 215 /* 216 * This routine is called to deliver a packet out the interface. 217 * We simply relay the packet to 218 * the ether hook, if it is connected. 219 */ 220 221 static void 222 ng_eiface_start(struct ifnet *ifp, struct ifaltq_subque *ifsq __unused) 223 { 224 const priv_p priv = (priv_p) ifp->if_softc; 225 meta_p meta = NULL; 226 int len, error = 0; 227 struct mbuf *m; 228 229 /* Check interface flags */ 230 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 231 return; 232 233 /* Don't do anything if output is active */ 234 if(ifq_is_oactive(&ifp->if_snd)) 235 return; 236 237 ifq_set_oactive(&ifp->if_snd); 238 239 /* 240 * Grab a packet to transmit. 241 */ 242 m = ifq_dequeue(&ifp->if_snd); 243 244 /* If there's nothing to send, return. */ 245 if(m == NULL) 246 { 247 ifq_clr_oactive(&ifp->if_snd); 248 return; 249 } 250 251 BPF_MTAP(ifp, m); 252 253 /* Copy length before the mbuf gets invalidated */ 254 len = m->m_pkthdr.len; 255 256 /* Send packet; if hook is not connected, mbuf will get freed. */ 257 NG_SEND_DATA(error, priv->ether, m, meta); 258 259 /* Update stats */ 260 if (error == 0) { 261 IFNET_STAT_INC(ifp, obytes, len); 262 IFNET_STAT_INC(ifp, opackets, 1); 263 } 264 265 ifq_clr_oactive(&ifp->if_snd); 266 267 return; 268 } 269 270 #ifdef DEBUG 271 /* 272 * Display an ioctl to the virtual interface 273 */ 274 275 static void 276 ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data) 277 { 278 char *str; 279 280 switch (cmd & IOC_DIRMASK) { 281 case IOC_VOID: 282 str = "IO"; 283 break; 284 case IOC_OUT: 285 str = "IOR"; 286 break; 287 case IOC_IN: 288 str = "IOW"; 289 break; 290 case IOC_INOUT: 291 str = "IORW"; 292 break; 293 default: 294 str = "IO??"; 295 } 296 log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", 297 ifp->if_xname, 298 str, 299 IOCGROUP(cmd), 300 cmd & 0xff, 301 IOCPARM_LEN(cmd)); 302 } 303 #endif /* DEBUG */ 304 305 /************************************************************************ 306 NETGRAPH NODE STUFF 307 ************************************************************************/ 308 309 /* 310 * Constructor for a node 311 */ 312 static int 313 ng_eiface_constructor(node_p *nodep) 314 { 315 struct ifnet *ifp; 316 node_p node; 317 priv_p priv; 318 int error = 0; 319 320 /* Allocate node and interface private structures */ 321 priv = kmalloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 322 323 ifp = &(priv->arpcom.ac_if); 324 325 /* Link them together */ 326 ifp->if_softc = priv; 327 priv->ifp = ifp; 328 329 /* Call generic node constructor */ 330 if ((error = ng_make_node_common(&typestruct, nodep))) { 331 kfree(priv, M_NETGRAPH); 332 return (error); 333 } 334 node = *nodep; 335 336 /* Link together node and private info */ 337 node->private = priv; 338 priv->node = node; 339 340 /* Initialize interface structure */ 341 if_initname(ifp, ng_eiface_ifname, ng_eiface_next_unit++); 342 ifp->if_init = ng_eiface_init; 343 ifp->if_start = ng_eiface_start; 344 ifp->if_ioctl = ng_eiface_ioctl; 345 ifp->if_watchdog = NULL; 346 ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); 347 ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); 348 349 /* Give this node name * 350 bzero(ifname, sizeof(ifname)); 351 ksprintf(ifname, "if%s", ifp->if_xname); 352 ng_name_node(node, ifname); 353 */ 354 355 /* Attach the interface */ 356 ether_ifattach(ifp, priv->arpcom.ac_enaddr, NULL); 357 358 /* Done */ 359 return (0); 360 } 361 362 /* 363 * Give our ok for a hook to be added 364 */ 365 static int 366 ng_eiface_newhook(node_p node, hook_p hook, const char *name) 367 { 368 priv_p priv = node->private; 369 370 if (strcmp(name, NG_EIFACE_HOOK_ETHER)) 371 return (EPFNOSUPPORT); 372 if (priv->ether != NULL) 373 return (EISCONN); 374 priv->ether = hook; 375 hook->private = &priv->ether; 376 377 return (0); 378 } 379 380 /* 381 * Receive a control message 382 */ 383 static int 384 ng_eiface_rcvmsg(node_p node, struct ng_mesg *msg, 385 const char *retaddr, struct ng_mesg **rptr) 386 { 387 const priv_p priv = node->private; 388 struct ifnet *const ifp = priv->ifp; 389 struct ng_mesg *resp = NULL; 390 int error = 0; 391 392 switch (msg->header.typecookie) { 393 case NGM_EIFACE_COOKIE: 394 switch (msg->header.cmd) { 395 396 case NGM_EIFACE_SET: 397 { 398 struct ng_eiface_par *eaddr; 399 400 if (msg->header.arglen != sizeof(struct ng_eiface_par)) 401 { 402 error = EINVAL; 403 break; 404 } 405 eaddr = (struct ng_eiface_par *)(msg->data); 406 407 priv->arpcom.ac_enaddr[0] = eaddr->oct0; 408 priv->arpcom.ac_enaddr[1] = eaddr->oct1; 409 priv->arpcom.ac_enaddr[2] = eaddr->oct2; 410 priv->arpcom.ac_enaddr[3] = eaddr->oct3; 411 priv->arpcom.ac_enaddr[4] = eaddr->oct4; 412 priv->arpcom.ac_enaddr[5] = eaddr->oct5; 413 414 break; 415 } 416 417 case NGM_EIFACE_GET_IFNAME: 418 { 419 struct ng_eiface_ifname *arg; 420 421 NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT); 422 if (resp == NULL) { 423 error = ENOMEM; 424 break; 425 } 426 arg = (struct ng_eiface_ifname *) resp->data; 427 ksprintf(arg->ngif_name, 428 "%s", ifp->if_xname); /* XXX: strings */ 429 break; 430 } 431 432 case NGM_EIFACE_GET_IFADDRS: 433 { 434 struct ifaddr_container *ifac; 435 caddr_t ptr; 436 int buflen; 437 438 #define SA_SIZE(s) RT_ROUNDUP((s)->sa_len) 439 440 /* Determine size of response and allocate it */ 441 buflen = 0; 442 TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], 443 ifa_link) { 444 /* Ignore marker */ 445 if (ifac->ifa->ifa_addr->sa_family == AF_UNSPEC) 446 continue; 447 buflen += SA_SIZE(ifac->ifa->ifa_addr); 448 } 449 NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT); 450 if (resp == NULL) { 451 error = ENOMEM; 452 break; 453 } 454 455 /* Add addresses */ 456 ptr = resp->data; 457 TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], 458 ifa_link) { 459 struct ifaddr *ifa = ifac->ifa; 460 const int len = SA_SIZE(ifa->ifa_addr); 461 462 /* Ignore marker */ 463 if (ifa->ifa_addr->sa_family == AF_UNSPEC) 464 continue; 465 466 if (buflen < len) { 467 log(LOG_ERR, "%s: len changed?\n", 468 ifp->if_xname); 469 break; 470 } 471 bcopy(ifa->ifa_addr, ptr, len); 472 ptr += len; 473 buflen -= len; 474 } 475 break; 476 #undef SA_SIZE 477 } 478 479 default: 480 error = EINVAL; 481 break; 482 } 483 break; 484 default: 485 error = EINVAL; 486 break; 487 } 488 if (rptr) 489 *rptr = resp; 490 else if (resp) 491 kfree(resp, M_NETGRAPH); 492 kfree(msg, M_NETGRAPH); 493 return (error); 494 } 495 496 /* 497 * Recive data from a hook. Pass the packet to the ether_input routine. 498 */ 499 static int 500 ng_eiface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 501 { 502 const priv_p priv = hook->node->private; 503 struct ifnet *const ifp = priv->ifp; 504 int error = 0; 505 506 /* Meta-data is end its life here... */ 507 NG_FREE_META(meta); 508 509 if (m == NULL) { 510 kprintf("ng_eiface: mbuf is null.\n"); 511 return (EINVAL); 512 } 513 514 if ( !(ifp->if_flags & IFF_UP) ) 515 return (ENETDOWN); 516 517 /* Note receiving interface */ 518 m->m_pkthdr.rcvif = ifp; 519 520 /* Update interface stats */ 521 IFNET_STAT_INC(ifp, ipackets, 1); 522 523 BPF_MTAP(ifp, m); 524 525 ifp->if_input(ifp, m, NULL, -1); 526 527 /* Done */ 528 return (error); 529 } 530 531 /* 532 * Because the BSD networking code doesn't support the removal of 533 * networking interfaces, iface nodes (once created) are persistent. 534 * So this method breaks all connections and marks the interface 535 * down, but does not remove the node. 536 */ 537 static int 538 ng_eiface_rmnode(node_p node) 539 { 540 const priv_p priv = node->private; 541 struct ifnet *const ifp = priv->ifp; 542 543 ng_cutlinks(node); 544 node->flags &= ~NG_INVALID; 545 ifnet_serialize_all(ifp); 546 ifp->if_flags &= ~(IFF_UP | IFF_RUNNING); 547 ifq_clr_oactive(&ifp->if_snd); 548 ifnet_deserialize_all(ifp); 549 return (0); 550 } 551 552 553 /* 554 * This is called once we've already connected a new hook to the other node. 555 * It gives us a chance to balk at the last minute. 556 */ 557 static int 558 ng_eiface_connect(hook_p hook) 559 { 560 /* be really amiable and just say "YUP that's OK by me! " */ 561 return (0); 562 } 563 564 /* 565 * Hook disconnection 566 */ 567 static int 568 ng_eiface_disconnect(hook_p hook) 569 { 570 const priv_p priv = hook->node->private; 571 572 priv->ether = NULL; 573 return (0); 574 } 575