| Commit | Line | Data |
|---|---|---|
| b06ebda0 MD |
1 | /* |
| 2 | * ng_iface.c | |
| 3 | */ | |
| 4 | ||
| 5 | /*- | |
| 6 | * Copyright (c) 1996-1999 Whistle Communications, Inc. | |
| 7 | * All rights reserved. | |
| 8 | * | |
| 9 | * Subject to the following obligations and disclaimer of warranty, use and | |
| 10 | * redistribution of this software, in source or object code forms, with or | |
| 11 | * without modifications are expressly permitted by Whistle Communications; | |
| 12 | * provided, however, that: | |
| 13 | * 1. Any and all reproductions of the source or object code must include the | |
| 14 | * copyright notice above and the following disclaimer of warranties; and | |
| 15 | * 2. No rights are granted, in any manner or form, to use Whistle | |
| 16 | * Communications, Inc. trademarks, including the mark "WHISTLE | |
| 17 | * COMMUNICATIONS" on advertising, endorsements, or otherwise except as | |
| 18 | * such appears in the above copyright notice or in the software. | |
| 19 | * | |
| 20 | * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND | |
| 21 | * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO | |
| 22 | * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, | |
| 23 | * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF | |
| 24 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. | |
| 25 | * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY | |
| 26 | * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS | |
| 27 | * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. | |
| 28 | * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES | |
| 29 | * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING | |
| 30 | * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
| 31 | * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| 32 | * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY | |
| 33 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 35 | * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY | |
| 36 | * OF SUCH DAMAGE. | |
| 37 | * | |
| 38 | * Author: Archie Cobbs <archie@freebsd.org> | |
| 39 | * | |
| 40 | * $FreeBSD: src/sys/netgraph/ng_iface.c,v 1.48 2008/01/31 08:51:48 mav Exp $ | |
| 41 | * $Whistle: ng_iface.c,v 1.33 1999/11/01 09:24:51 julian Exp $ | |
| 42 | */ | |
| 43 | ||
| 44 | /* | |
| 45 | * This node is also a system networking interface. It has | |
| 46 | * a hook for each protocol (IP, AppleTalk, IPX, etc). Packets | |
| 47 | * are simply relayed between the interface and the hooks. | |
| 48 | * | |
| 49 | * Interfaces are named ng0, ng1, etc. New nodes take the | |
| 50 | * first available interface name. | |
| 51 | * | |
| 52 | * This node also includes Berkeley packet filter support. | |
| 53 | */ | |
| 54 | ||
| b06ebda0 MD |
55 | #include "opt_inet.h" |
| 56 | #include "opt_inet6.h" | |
| 57 | #include "opt_ipx.h" | |
| 58 | ||
| 59 | #include <sys/param.h> | |
| 60 | #include <sys/systm.h> | |
| 61 | #include <sys/errno.h> | |
| 62 | #include <sys/kernel.h> | |
| 63 | #include <sys/malloc.h> | |
| 64 | #include <sys/mbuf.h> | |
| 65 | #include <sys/errno.h> | |
| 66 | #include <sys/random.h> | |
| 67 | #include <sys/sockio.h> | |
| 68 | #include <sys/socket.h> | |
| 69 | #include <sys/syslog.h> | |
| 70 | #include <sys/libkern.h> | |
| 71 | ||
| 72 | #include <net/if.h> | |
| 73 | #include <net/if_types.h> | |
| 74 | #include <net/bpf.h> | |
| 75 | #include <net/netisr.h> | |
| 76 | ||
| 77 | #include <netinet/in.h> | |
| 78 | ||
| 5a975a3d MD |
79 | #include "ng_message.h" |
| 80 | #include "netgraph.h" | |
| 81 | #include "ng_parse.h" | |
| 82 | #include "ng_iface.h" | |
| 83 | #include "ng_cisco.h" | |
| b06ebda0 MD |
84 | |
| 85 | #ifdef NG_SEPARATE_MALLOC | |
| 86 | MALLOC_DEFINE(M_NETGRAPH_IFACE, "netgraph_iface", "netgraph iface node "); | |
| 87 | #else | |
| 88 | #define M_NETGRAPH_IFACE M_NETGRAPH | |
| 89 | #endif | |
| 90 | ||
| 91 | /* This struct describes one address family */ | |
| 92 | struct iffam { | |
| 93 | sa_family_t family; /* Address family */ | |
| 94 | const char *hookname; /* Name for hook */ | |
| 95 | }; | |
| 96 | typedef const struct iffam *iffam_p; | |
| 97 | ||
| 98 | /* List of address families supported by our interface */ | |
| 99 | const static struct iffam gFamilies[] = { | |
| 100 | { AF_INET, NG_IFACE_HOOK_INET }, | |
| 101 | { AF_INET6, NG_IFACE_HOOK_INET6 }, | |
| b06ebda0 MD |
102 | { AF_IPX, NG_IFACE_HOOK_IPX }, |
| 103 | { AF_ATM, NG_IFACE_HOOK_ATM }, | |
| 104 | { AF_NATM, NG_IFACE_HOOK_NATM }, | |
| 105 | }; | |
| b370aff7 | 106 | #define NUM_FAMILIES NELEM(gFamilies) |
| b06ebda0 MD |
107 | |
| 108 | /* Node private data */ | |
| 109 | struct ng_iface_private { | |
| 110 | struct ifnet *ifp; /* Our interface */ | |
| 111 | int unit; /* Interface unit number */ | |
| 112 | node_p node; /* Our netgraph node */ | |
| 113 | hook_p hooks[NUM_FAMILIES]; /* Hook for each address family */ | |
| 114 | }; | |
| 115 | typedef struct ng_iface_private *priv_p; | |
| 116 | ||
| 117 | /* Interface methods */ | |
| 118 | static void ng_iface_start(struct ifnet *ifp); | |
| 119 | static int ng_iface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); | |
| 120 | static int ng_iface_output(struct ifnet *ifp, struct mbuf *m0, | |
| 121 | struct sockaddr *dst, struct rtentry *rt0); | |
| 122 | static void ng_iface_bpftap(struct ifnet *ifp, | |
| 123 | struct mbuf *m, sa_family_t family); | |
| 124 | static int ng_iface_send(struct ifnet *ifp, struct mbuf *m, | |
| 125 | sa_family_t sa); | |
| 126 | #ifdef DEBUG | |
| 127 | static void ng_iface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); | |
| 128 | #endif | |
| 129 | ||
| 130 | /* Netgraph methods */ | |
| 131 | static int ng_iface_mod_event(module_t, int, void *); | |
| 132 | static ng_constructor_t ng_iface_constructor; | |
| 133 | static ng_rcvmsg_t ng_iface_rcvmsg; | |
| 134 | static ng_shutdown_t ng_iface_shutdown; | |
| 135 | static ng_newhook_t ng_iface_newhook; | |
| 136 | static ng_rcvdata_t ng_iface_rcvdata; | |
| 137 | static ng_disconnect_t ng_iface_disconnect; | |
| 138 | ||
| 139 | /* Helper stuff */ | |
| 140 | static iffam_p get_iffam_from_af(sa_family_t family); | |
| 141 | static iffam_p get_iffam_from_hook(priv_p priv, hook_p hook); | |
| 142 | static iffam_p get_iffam_from_name(const char *name); | |
| 143 | static hook_p *get_hook_from_iffam(priv_p priv, iffam_p iffam); | |
| 144 | ||
| 145 | /* Parse type for struct ng_cisco_ipaddr */ | |
| 146 | static const struct ng_parse_struct_field ng_cisco_ipaddr_type_fields[] | |
| 147 | = NG_CISCO_IPADDR_TYPE_INFO; | |
| 148 | static const struct ng_parse_type ng_cisco_ipaddr_type = { | |
| 149 | &ng_parse_struct_type, | |
| 150 | &ng_cisco_ipaddr_type_fields | |
| 151 | }; | |
| 152 | ||
| 153 | /* List of commands and how to convert arguments to/from ASCII */ | |
| 154 | static const struct ng_cmdlist ng_iface_cmds[] = { | |
| 155 | { | |
| 156 | NGM_IFACE_COOKIE, | |
| 157 | NGM_IFACE_GET_IFNAME, | |
| 158 | "getifname", | |
| 159 | NULL, | |
| 160 | &ng_parse_string_type | |
| 161 | }, | |
| 162 | { | |
| 163 | NGM_IFACE_COOKIE, | |
| 164 | NGM_IFACE_POINT2POINT, | |
| 165 | "point2point", | |
| 166 | NULL, | |
| 167 | NULL | |
| 168 | }, | |
| 169 | { | |
| 170 | NGM_IFACE_COOKIE, | |
| 171 | NGM_IFACE_BROADCAST, | |
| 172 | "broadcast", | |
| 173 | NULL, | |
| 174 | NULL | |
| 175 | }, | |
| 176 | { | |
| 177 | NGM_CISCO_COOKIE, | |
| 178 | NGM_CISCO_GET_IPADDR, | |
| 179 | "getipaddr", | |
| 180 | NULL, | |
| 181 | &ng_cisco_ipaddr_type | |
| 182 | }, | |
| 183 | { | |
| 184 | NGM_IFACE_COOKIE, | |
| 185 | NGM_IFACE_GET_IFINDEX, | |
| 186 | "getifindex", | |
| 187 | NULL, | |
| 188 | &ng_parse_uint32_type | |
| 189 | }, | |
| 190 | { 0 } | |
| 191 | }; | |
| 192 | ||
| 193 | /* Node type descriptor */ | |
| 194 | static struct ng_type typestruct = { | |
| 195 | .version = NG_ABI_VERSION, | |
| 196 | .name = NG_IFACE_NODE_TYPE, | |
| 197 | .mod_event = ng_iface_mod_event, | |
| 198 | .constructor = ng_iface_constructor, | |
| 199 | .rcvmsg = ng_iface_rcvmsg, | |
| 200 | .shutdown = ng_iface_shutdown, | |
| 201 | .newhook = ng_iface_newhook, | |
| 202 | .rcvdata = ng_iface_rcvdata, | |
| 203 | .disconnect = ng_iface_disconnect, | |
| 204 | .cmdlist = ng_iface_cmds, | |
| 205 | }; | |
| 206 | NETGRAPH_INIT(iface, &typestruct); | |
| 207 | ||
| 208 | static struct unrhdr *ng_iface_unit; | |
| 209 | ||
| 210 | /************************************************************************ | |
| 211 | HELPER STUFF | |
| 212 | ************************************************************************/ | |
| 213 | ||
| 214 | /* | |
| 215 | * Get the family descriptor from the family ID | |
| 216 | */ | |
| 217 | static __inline iffam_p | |
| 218 | get_iffam_from_af(sa_family_t family) | |
| 219 | { | |
| 220 | iffam_p iffam; | |
| 221 | int k; | |
| 222 | ||
| 223 | for (k = 0; k < NUM_FAMILIES; k++) { | |
| 224 | iffam = &gFamilies[k]; | |
| 225 | if (iffam->family == family) | |
| 226 | return (iffam); | |
| 227 | } | |
| 228 | return (NULL); | |
| 229 | } | |
| 230 | ||
| 231 | /* | |
| 232 | * Get the family descriptor from the hook | |
| 233 | */ | |
| 234 | static __inline iffam_p | |
| 235 | get_iffam_from_hook(priv_p priv, hook_p hook) | |
| 236 | { | |
| 237 | int k; | |
| 238 | ||
| 239 | for (k = 0; k < NUM_FAMILIES; k++) | |
| 240 | if (priv->hooks[k] == hook) | |
| 241 | return (&gFamilies[k]); | |
| 242 | return (NULL); | |
| 243 | } | |
| 244 | ||
| 245 | /* | |
| 246 | * Get the hook from the iffam descriptor | |
| 247 | */ | |
| 248 | ||
| 249 | static __inline hook_p * | |
| 250 | get_hook_from_iffam(priv_p priv, iffam_p iffam) | |
| 251 | { | |
| 252 | return (&priv->hooks[iffam - gFamilies]); | |
| 253 | } | |
| 254 | ||
| 255 | /* | |
| 256 | * Get the iffam descriptor from the name | |
| 257 | */ | |
| 258 | static __inline iffam_p | |
| 259 | get_iffam_from_name(const char *name) | |
| 260 | { | |
| 261 | iffam_p iffam; | |
| 262 | int k; | |
| 263 | ||
| 264 | for (k = 0; k < NUM_FAMILIES; k++) { | |
| 265 | iffam = &gFamilies[k]; | |
| 266 | if (!strcmp(iffam->hookname, name)) | |
| 267 | return (iffam); | |
| 268 | } | |
| 269 | return (NULL); | |
| 270 | } | |
| 271 | ||
| 272 | /************************************************************************ | |
| 273 | INTERFACE STUFF | |
| 274 | ************************************************************************/ | |
| 275 | ||
| 276 | /* | |
| 277 | * Process an ioctl for the virtual interface | |
| 278 | */ | |
| 279 | static int | |
| 280 | ng_iface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) | |
| 281 | { | |
| 282 | struct ifreq *const ifr = (struct ifreq *) data; | |
| 283 | int s, error = 0; | |
| 284 | ||
| 285 | #ifdef DEBUG | |
| 286 | ng_iface_print_ioctl(ifp, command, data); | |
| 287 | #endif | |
| 288 | s = splimp(); | |
| 289 | switch (command) { | |
| 290 | ||
| 291 | /* These two are mostly handled at a higher layer */ | |
| 292 | case SIOCSIFADDR: | |
| 293 | ifp->if_flags |= IFF_UP; | |
| 294 | ifp->if_drv_flags |= IFF_DRV_RUNNING; | |
| 295 | ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); | |
| 296 | break; | |
| 297 | case SIOCGIFADDR: | |
| 298 | break; | |
| 299 | ||
| 300 | /* Set flags */ | |
| 301 | case SIOCSIFFLAGS: | |
| 302 | /* | |
| 303 | * If the interface is marked up and stopped, then start it. | |
| 304 | * If it is marked down and running, then stop it. | |
| 305 | */ | |
| 306 | if (ifr->ifr_flags & IFF_UP) { | |
| 307 | if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { | |
| 308 | ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); | |
| 309 | ifp->if_drv_flags |= IFF_DRV_RUNNING; | |
| 310 | } | |
| 311 | } else { | |
| 312 | if (ifp->if_drv_flags & IFF_DRV_RUNNING) | |
| 313 | ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | | |
| 314 | IFF_DRV_OACTIVE); | |
| 315 | } | |
| 316 | break; | |
| 317 | ||
| 318 | /* Set the interface MTU */ | |
| 319 | case SIOCSIFMTU: | |
| 320 | if (ifr->ifr_mtu > NG_IFACE_MTU_MAX | |
| 321 | || ifr->ifr_mtu < NG_IFACE_MTU_MIN) | |
| 322 | error = EINVAL; | |
| 323 | else | |
| 324 | ifp->if_mtu = ifr->ifr_mtu; | |
| 325 | break; | |
| 326 | ||
| 327 | /* Stuff that's not supported */ | |
| 328 | case SIOCADDMULTI: | |
| 329 | case SIOCDELMULTI: | |
| 330 | error = 0; | |
| 331 | break; | |
| 332 | case SIOCSIFPHYS: | |
| 333 | error = EOPNOTSUPP; | |
| 334 | break; | |
| 335 | ||
| 336 | default: | |
| 337 | error = EINVAL; | |
| 338 | break; | |
| 339 | } | |
| 340 | (void) splx(s); | |
| 341 | return (error); | |
| 342 | } | |
| 343 | ||
| 344 | /* | |
| 345 | * This routine is called to deliver a packet out the interface. | |
| 346 | * We simply look at the address family and relay the packet to | |
| 347 | * the corresponding hook, if it exists and is connected. | |
| 348 | */ | |
| 349 | ||
| 350 | static int | |
| 351 | ng_iface_output(struct ifnet *ifp, struct mbuf *m, | |
| 352 | struct sockaddr *dst, struct rtentry *rt0) | |
| 353 | { | |
| 354 | uint32_t af; | |
| 355 | int error; | |
| 356 | ||
| 357 | /* Check interface flags */ | |
| 358 | if (!((ifp->if_flags & IFF_UP) && | |
| 359 | (ifp->if_drv_flags & IFF_DRV_RUNNING))) { | |
| 360 | m_freem(m); | |
| 361 | return (ENETDOWN); | |
| 362 | } | |
| 363 | ||
| 364 | /* BPF writes need to be handled specially. */ | |
| 365 | if (dst->sa_family == AF_UNSPEC) { | |
| 366 | bcopy(dst->sa_data, &af, sizeof(af)); | |
| 367 | dst->sa_family = af; | |
| 368 | } | |
| 369 | ||
| 370 | /* Berkeley packet filter */ | |
| 371 | ng_iface_bpftap(ifp, m, dst->sa_family); | |
| 372 | ||
| 373 | if (ALTQ_IS_ENABLED(&ifp->if_snd)) { | |
| 5a975a3d | 374 | M_PREPEND(m, sizeof(sa_family_t), MB_DONTWAIT); |
| b06ebda0 MD |
375 | if (m == NULL) { |
| 376 | IFQ_LOCK(&ifp->if_snd); | |
| 377 | IFQ_INC_DROPS(&ifp->if_snd); | |
| 378 | IFQ_UNLOCK(&ifp->if_snd); | |
| 379 | ifp->if_oerrors++; | |
| 380 | return (ENOBUFS); | |
| 381 | } | |
| 382 | *(sa_family_t *)m->m_data = dst->sa_family; | |
| 383 | IFQ_HANDOFF(ifp, m, error); | |
| 384 | } else | |
| 385 | error = ng_iface_send(ifp, m, dst->sa_family); | |
| 386 | ||
| 387 | return (error); | |
| 388 | } | |
| 389 | ||
| 390 | /* | |
| 391 | * Start method is used only when ALTQ is enabled. | |
| 392 | */ | |
| 393 | static void | |
| 394 | ng_iface_start(struct ifnet *ifp) | |
| 395 | { | |
| 396 | struct mbuf *m; | |
| 397 | sa_family_t sa; | |
| 398 | ||
| 399 | KASSERT(ALTQ_IS_ENABLED(&ifp->if_snd), ("%s without ALTQ", __func__)); | |
| 400 | ||
| 401 | for(;;) { | |
| 402 | IFQ_DRV_DEQUEUE(&ifp->if_snd, m); | |
| 403 | if (m == NULL) | |
| 404 | break; | |
| 405 | sa = *mtod(m, sa_family_t *); | |
| 406 | m_adj(m, sizeof(sa_family_t)); | |
| 407 | ng_iface_send(ifp, m, sa); | |
| 408 | } | |
| 409 | } | |
| 410 | ||
| 411 | /* | |
| 412 | * Flash a packet by the BPF (requires prepending 4 byte AF header) | |
| 413 | * Note the phoney mbuf; this is OK because BPF treats it read-only. | |
| 414 | */ | |
| 415 | static void | |
| 416 | ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, sa_family_t family) | |
| 417 | { | |
| 418 | KASSERT(family != AF_UNSPEC, ("%s: family=AF_UNSPEC", __func__)); | |
| 419 | if (bpf_peers_present(ifp->if_bpf)) { | |
| 420 | int32_t family4 = (int32_t)family; | |
| 421 | bpf_mtap2(ifp->if_bpf, &family4, sizeof(family4), m); | |
| 422 | } | |
| 423 | } | |
| 424 | ||
| 425 | /* | |
| 426 | * This routine does actual delivery of the packet into the | |
| 427 | * netgraph(4). It is called from ng_iface_start() and | |
| 428 | * ng_iface_output(). | |
| 429 | */ | |
| 430 | static int | |
| 431 | ng_iface_send(struct ifnet *ifp, struct mbuf *m, sa_family_t sa) | |
| 432 | { | |
| 433 | const priv_p priv = (priv_p) ifp->if_softc; | |
| 434 | const iffam_p iffam = get_iffam_from_af(sa); | |
| 435 | int error; | |
| 436 | int len; | |
| 437 | ||
| 438 | /* Check address family to determine hook (if known) */ | |
| 439 | if (iffam == NULL) { | |
| 440 | m_freem(m); | |
| 441 | log(LOG_WARNING, "%s: can't handle af%d\n", ifp->if_xname, sa); | |
| 442 | return (EAFNOSUPPORT); | |
| 443 | } | |
| 444 | ||
| 445 | /* Copy length before the mbuf gets invalidated. */ | |
| 446 | len = m->m_pkthdr.len; | |
| 447 | ||
| 448 | /* Send packet. If hook is not connected, | |
| 449 | mbuf will get freed. */ | |
| 450 | NG_SEND_DATA_ONLY(error, *get_hook_from_iffam(priv, iffam), m); | |
| 451 | ||
| 452 | /* Update stats. */ | |
| 453 | if (error == 0) { | |
| 454 | ifp->if_obytes += len; | |
| 455 | ifp->if_opackets++; | |
| 456 | } | |
| 457 | ||
| 458 | return (error); | |
| 459 | } | |
| 460 | ||
| 461 | #ifdef DEBUG | |
| 462 | /* | |
| 463 | * Display an ioctl to the virtual interface | |
| 464 | */ | |
| 465 | ||
| 466 | static void | |
| 467 | ng_iface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) | |
| 468 | { | |
| 469 | char *str; | |
| 470 | ||
| 471 | switch (command & IOC_DIRMASK) { | |
| 472 | case IOC_VOID: | |
| 473 | str = "IO"; | |
| 474 | break; | |
| 475 | case IOC_OUT: | |
| 476 | str = "IOR"; | |
| 477 | break; | |
| 478 | case IOC_IN: | |
| 479 | str = "IOW"; | |
| 480 | break; | |
| 481 | case IOC_INOUT: | |
| 482 | str = "IORW"; | |
| 483 | break; | |
| 484 | default: | |
| 485 | str = "IO??"; | |
| 486 | } | |
| 487 | log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", | |
| 488 | ifp->if_xname, | |
| 489 | str, | |
| 490 | IOCGROUP(command), | |
| 491 | command & 0xff, | |
| 492 | IOCPARM_LEN(command)); | |
| 493 | } | |
| 494 | #endif /* DEBUG */ | |
| 495 | ||
| 496 | /************************************************************************ | |
| 497 | NETGRAPH NODE STUFF | |
| 498 | ************************************************************************/ | |
| 499 | ||
| 500 | /* | |
| 501 | * Constructor for a node | |
| 502 | */ | |
| 503 | static int | |
| 504 | ng_iface_constructor(node_p node) | |
| 505 | { | |
| 506 | struct ifnet *ifp; | |
| 507 | priv_p priv; | |
| 508 | ||
| 509 | /* Allocate node and interface private structures */ | |
| fc025606 SW |
510 | priv = kmalloc(sizeof(*priv), M_NETGRAPH_IFACE, |
| 511 | M_WAITOK | M_NULLOK | M_ZERO); | |
| b06ebda0 MD |
512 | if (priv == NULL) |
| 513 | return (ENOMEM); | |
| 514 | ifp = if_alloc(IFT_PROPVIRTUAL); | |
| 515 | if (ifp == NULL) { | |
| fc025606 | 516 | kfree(priv, M_NETGRAPH_IFACE); |
| b06ebda0 MD |
517 | return (ENOMEM); |
| 518 | } | |
| 519 | ||
| 520 | /* Link them together */ | |
| 521 | ifp->if_softc = priv; | |
| 522 | priv->ifp = ifp; | |
| 523 | ||
| 524 | /* Get an interface unit number */ | |
| 525 | priv->unit = alloc_unr(ng_iface_unit); | |
| 526 | ||
| 527 | /* Link together node and private info */ | |
| 528 | NG_NODE_SET_PRIVATE(node, priv); | |
| 529 | priv->node = node; | |
| 530 | ||
| 531 | /* Initialize interface structure */ | |
| 532 | if_initname(ifp, NG_IFACE_IFACE_NAME, priv->unit); | |
| 533 | ifp->if_output = ng_iface_output; | |
| 534 | ifp->if_start = ng_iface_start; | |
| 535 | ifp->if_ioctl = ng_iface_ioctl; | |
| 536 | ifp->if_watchdog = NULL; | |
| 537 | ifp->if_mtu = NG_IFACE_MTU_DEFAULT; | |
| 538 | ifp->if_flags = (IFF_SIMPLEX|IFF_POINTOPOINT|IFF_NOARP|IFF_MULTICAST); | |
| 539 | ifp->if_type = IFT_PROPVIRTUAL; /* XXX */ | |
| 540 | ifp->if_addrlen = 0; /* XXX */ | |
| 541 | ifp->if_hdrlen = 0; /* XXX */ | |
| 542 | ifp->if_baudrate = 64000; /* XXX */ | |
| 543 | IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); | |
| 544 | ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; | |
| 545 | IFQ_SET_READY(&ifp->if_snd); | |
| 546 | ||
| 547 | /* Give this node the same name as the interface (if possible) */ | |
| 548 | if (ng_name_node(node, ifp->if_xname) != 0) | |
| 549 | log(LOG_WARNING, "%s: can't acquire netgraph name\n", | |
| 550 | ifp->if_xname); | |
| 551 | ||
| 552 | /* Attach the interface */ | |
| 553 | if_attach(ifp); | |
| 554 | bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); | |
| 555 | ||
| 556 | /* Done */ | |
| 557 | return (0); | |
| 558 | } | |
| 559 | ||
| 560 | /* | |
| 561 | * Give our ok for a hook to be added | |
| 562 | */ | |
| 563 | static int | |
| 564 | ng_iface_newhook(node_p node, hook_p hook, const char *name) | |
| 565 | { | |
| 566 | const iffam_p iffam = get_iffam_from_name(name); | |
| 567 | hook_p *hookptr; | |
| 568 | ||
| 569 | if (iffam == NULL) | |
| 570 | return (EPFNOSUPPORT); | |
| 571 | hookptr = get_hook_from_iffam(NG_NODE_PRIVATE(node), iffam); | |
| 572 | if (*hookptr != NULL) | |
| 573 | return (EISCONN); | |
| 574 | *hookptr = hook; | |
| 575 | NG_HOOK_HI_STACK(hook); | |
| 576 | return (0); | |
| 577 | } | |
| 578 | ||
| 579 | /* | |
| 580 | * Receive a control message | |
| 581 | */ | |
| 582 | static int | |
| 583 | ng_iface_rcvmsg(node_p node, item_p item, hook_p lasthook) | |
| 584 | { | |
| 585 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 586 | struct ifnet *const ifp = priv->ifp; | |
| 587 | struct ng_mesg *resp = NULL; | |
| 588 | int error = 0; | |
| 589 | struct ng_mesg *msg; | |
| 590 | ||
| 591 | NGI_GET_MSG(item, msg); | |
| 592 | switch (msg->header.typecookie) { | |
| 593 | case NGM_IFACE_COOKIE: | |
| 594 | switch (msg->header.cmd) { | |
| 595 | case NGM_IFACE_GET_IFNAME: | |
| 5a975a3d | 596 | NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
597 | if (resp == NULL) { |
| 598 | error = ENOMEM; | |
| 599 | break; | |
| 600 | } | |
| 601 | strlcpy(resp->data, ifp->if_xname, IFNAMSIZ); | |
| 602 | break; | |
| 603 | ||
| 604 | case NGM_IFACE_POINT2POINT: | |
| 605 | case NGM_IFACE_BROADCAST: | |
| 606 | { | |
| 607 | ||
| 608 | /* Deny request if interface is UP */ | |
| 609 | if ((ifp->if_flags & IFF_UP) != 0) | |
| 610 | return (EBUSY); | |
| 611 | ||
| 612 | /* Change flags */ | |
| 613 | switch (msg->header.cmd) { | |
| 614 | case NGM_IFACE_POINT2POINT: | |
| 615 | ifp->if_flags |= IFF_POINTOPOINT; | |
| 616 | ifp->if_flags &= ~IFF_BROADCAST; | |
| 617 | break; | |
| 618 | case NGM_IFACE_BROADCAST: | |
| 619 | ifp->if_flags &= ~IFF_POINTOPOINT; | |
| 620 | ifp->if_flags |= IFF_BROADCAST; | |
| 621 | break; | |
| 622 | } | |
| 623 | break; | |
| 624 | } | |
| 625 | ||
| 626 | case NGM_IFACE_GET_IFINDEX: | |
| 5a975a3d | 627 | NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
628 | if (resp == NULL) { |
| 629 | error = ENOMEM; | |
| 630 | break; | |
| 631 | } | |
| 632 | *((uint32_t *)resp->data) = priv->ifp->if_index; | |
| 633 | break; | |
| 634 | ||
| 635 | default: | |
| 636 | error = EINVAL; | |
| 637 | break; | |
| 638 | } | |
| 639 | break; | |
| 640 | case NGM_CISCO_COOKIE: | |
| 641 | switch (msg->header.cmd) { | |
| 642 | case NGM_CISCO_GET_IPADDR: /* we understand this too */ | |
| 643 | { | |
| 644 | struct ifaddr *ifa; | |
| 645 | ||
| 646 | /* Return the first configured IP address */ | |
| 647 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |
| 648 | struct ng_cisco_ipaddr *ips; | |
| 649 | ||
| 650 | if (ifa->ifa_addr->sa_family != AF_INET) | |
| 651 | continue; | |
| 5a975a3d | 652 | NG_MKRESPONSE(resp, msg, sizeof(ips), M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
653 | if (resp == NULL) { |
| 654 | error = ENOMEM; | |
| 655 | break; | |
| 656 | } | |
| 657 | ips = (struct ng_cisco_ipaddr *)resp->data; | |
| 658 | ips->ipaddr = ((struct sockaddr_in *) | |
| 659 | ifa->ifa_addr)->sin_addr; | |
| 660 | ips->netmask = ((struct sockaddr_in *) | |
| 661 | ifa->ifa_netmask)->sin_addr; | |
| 662 | break; | |
| 663 | } | |
| 664 | ||
| 665 | /* No IP addresses on this interface? */ | |
| 666 | if (ifa == NULL) | |
| 667 | error = EADDRNOTAVAIL; | |
| 668 | break; | |
| 669 | } | |
| 670 | default: | |
| 671 | error = EINVAL; | |
| 672 | break; | |
| 673 | } | |
| 674 | break; | |
| 675 | case NGM_FLOW_COOKIE: | |
| 676 | switch (msg->header.cmd) { | |
| 677 | case NGM_LINK_IS_UP: | |
| 678 | ifp->if_drv_flags |= IFF_DRV_RUNNING; | |
| 679 | break; | |
| 680 | case NGM_LINK_IS_DOWN: | |
| 681 | ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | |
| 682 | break; | |
| 683 | default: | |
| 684 | break; | |
| 685 | } | |
| 686 | break; | |
| 687 | default: | |
| 688 | error = EINVAL; | |
| 689 | break; | |
| 690 | } | |
| 691 | NG_RESPOND_MSG(error, node, item, resp); | |
| 692 | NG_FREE_MSG(msg); | |
| 693 | return (error); | |
| 694 | } | |
| 695 | ||
| 696 | /* | |
| 697 | * Recive data from a hook. Pass the packet to the correct input routine. | |
| 698 | */ | |
| 699 | static int | |
| 700 | ng_iface_rcvdata(hook_p hook, item_p item) | |
| 701 | { | |
| 702 | const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); | |
| 703 | const iffam_p iffam = get_iffam_from_hook(priv, hook); | |
| 704 | struct ifnet *const ifp = priv->ifp; | |
| 705 | struct mbuf *m; | |
| 706 | int isr; | |
| 707 | ||
| 708 | NGI_GET_M(item, m); | |
| 709 | NG_FREE_ITEM(item); | |
| 710 | /* Sanity checks */ | |
| 711 | KASSERT(iffam != NULL, ("%s: iffam", __func__)); | |
| 712 | M_ASSERTPKTHDR(m); | |
| 713 | if ((ifp->if_flags & IFF_UP) == 0) { | |
| 714 | NG_FREE_M(m); | |
| 715 | return (ENETDOWN); | |
| 716 | } | |
| 717 | ||
| 718 | /* Update interface stats */ | |
| 719 | ifp->if_ipackets++; | |
| 720 | ifp->if_ibytes += m->m_pkthdr.len; | |
| 721 | ||
| 722 | /* Note receiving interface */ | |
| 723 | m->m_pkthdr.rcvif = ifp; | |
| 724 | ||
| 725 | /* Berkeley packet filter */ | |
| 726 | ng_iface_bpftap(ifp, m, iffam->family); | |
| 727 | ||
| 728 | /* Send packet */ | |
| 729 | switch (iffam->family) { | |
| 730 | #ifdef INET | |
| 731 | case AF_INET: | |
| 732 | isr = NETISR_IP; | |
| 733 | break; | |
| 734 | #endif | |
| 735 | #ifdef INET6 | |
| 736 | case AF_INET6: | |
| 737 | isr = NETISR_IPV6; | |
| 738 | break; | |
| 739 | #endif | |
| 740 | #ifdef IPX | |
| 741 | case AF_IPX: | |
| 742 | isr = NETISR_IPX; | |
| 743 | break; | |
| 744 | #endif | |
| b06ebda0 MD |
745 | default: |
| 746 | m_freem(m); | |
| 747 | return (EAFNOSUPPORT); | |
| 748 | } | |
| 749 | /* First chunk of an mbuf contains good junk */ | |
| 750 | if (harvest.point_to_point) | |
| 751 | random_harvest(m, 16, 3, 0, RANDOM_NET); | |
| c3c96e44 | 752 | netisr_queue(isr, m); |
| b06ebda0 MD |
753 | return (0); |
| 754 | } | |
| 755 | ||
| 756 | /* | |
| 757 | * Shutdown and remove the node and its associated interface. | |
| 758 | */ | |
| 759 | static int | |
| 760 | ng_iface_shutdown(node_p node) | |
| 761 | { | |
| 762 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 763 | ||
| 764 | bpfdetach(priv->ifp); | |
| 765 | if_detach(priv->ifp); | |
| 766 | if_free(priv->ifp); | |
| 767 | priv->ifp = NULL; | |
| 768 | free_unr(ng_iface_unit, priv->unit); | |
| fc025606 | 769 | kfree(priv, M_NETGRAPH_IFACE); |
| b06ebda0 MD |
770 | NG_NODE_SET_PRIVATE(node, NULL); |
| 771 | NG_NODE_UNREF(node); | |
| 772 | return (0); | |
| 773 | } | |
| 774 | ||
| 775 | /* | |
| 776 | * Hook disconnection. Note that we do *not* shutdown when all | |
| 777 | * hooks have been disconnected. | |
| 778 | */ | |
| 779 | static int | |
| 780 | ng_iface_disconnect(hook_p hook) | |
| 781 | { | |
| 782 | const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); | |
| 783 | const iffam_p iffam = get_iffam_from_hook(priv, hook); | |
| 784 | ||
| 785 | if (iffam == NULL) | |
| 786 | panic(__func__); | |
| 787 | *get_hook_from_iffam(priv, iffam) = NULL; | |
| 788 | return (0); | |
| 789 | } | |
| 790 | ||
| 791 | /* | |
| 792 | * Handle loading and unloading for this node type. | |
| 793 | */ | |
| 794 | static int | |
| 795 | ng_iface_mod_event(module_t mod, int event, void *data) | |
| 796 | { | |
| 797 | int error = 0; | |
| 798 | ||
| 799 | switch (event) { | |
| 800 | case MOD_LOAD: | |
| 801 | ng_iface_unit = new_unrhdr(0, 0xffff, NULL); | |
| 802 | break; | |
| 803 | case MOD_UNLOAD: | |
| 804 | delete_unrhdr(ng_iface_unit); | |
| 805 | break; | |
| 806 | default: | |
| 807 | error = EOPNOTSUPP; | |
| 808 | break; | |
| 809 | } | |
| 810 | return (error); | |
| 811 | } |