Commit | Line | Data |
---|---|---|
b06ebda0 MD |
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: src/sys/netgraph/ng_eiface.c,v 1.39 2007/07/26 10:54:33 glebius Exp $ | |
5a975a3d | 29 | * $DragonFly: src/sys/netgraph7/ng_eiface.c,v 1.2 2008/06/26 23:05:35 dillon Exp $ |
b06ebda0 MD |
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 | ||
5a975a3d MD |
47 | #include "ng_message.h" |
48 | #include "netgraph.h" | |
49 | #include "ng_parse.h" | |
50 | #include "ng_eiface.h" | |
b06ebda0 MD |
51 | |
52 | #include <net/bpf.h> | |
53 | #include <net/ethernet.h> | |
54 | #include <net/if_arp.h> | |
55 | ||
56 | static const struct ng_cmdlist ng_eiface_cmdlist[] = { | |
57 | { | |
58 | NGM_EIFACE_COOKIE, | |
59 | NGM_EIFACE_GET_IFNAME, | |
60 | "getifname", | |
61 | NULL, | |
62 | &ng_parse_string_type | |
63 | }, | |
64 | { | |
65 | NGM_EIFACE_COOKIE, | |
66 | NGM_EIFACE_SET, | |
67 | "set", | |
68 | &ng_parse_enaddr_type, | |
69 | NULL | |
70 | }, | |
71 | { 0 } | |
72 | }; | |
73 | ||
74 | /* Node private data */ | |
75 | struct ng_eiface_private { | |
76 | struct ifnet *ifp; /* per-interface network data */ | |
77 | int unit; /* Interface unit number */ | |
78 | node_p node; /* Our netgraph node */ | |
79 | hook_p ether; /* Hook for ethernet stream */ | |
80 | }; | |
81 | typedef struct ng_eiface_private *priv_p; | |
82 | ||
83 | /* Interface methods */ | |
84 | static void ng_eiface_init(void *xsc); | |
85 | static void ng_eiface_start(struct ifnet *ifp); | |
86 | static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); | |
87 | #ifdef DEBUG | |
88 | static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); | |
89 | #endif | |
90 | ||
91 | /* Netgraph methods */ | |
92 | static int ng_eiface_mod_event(module_t, int, void *); | |
93 | static ng_constructor_t ng_eiface_constructor; | |
94 | static ng_rcvmsg_t ng_eiface_rcvmsg; | |
95 | static ng_shutdown_t ng_eiface_rmnode; | |
96 | static ng_newhook_t ng_eiface_newhook; | |
97 | static ng_rcvdata_t ng_eiface_rcvdata; | |
98 | static ng_disconnect_t ng_eiface_disconnect; | |
99 | ||
100 | /* Node type descriptor */ | |
101 | static struct ng_type typestruct = { | |
102 | .version = NG_ABI_VERSION, | |
103 | .name = NG_EIFACE_NODE_TYPE, | |
104 | .mod_event = ng_eiface_mod_event, | |
105 | .constructor = ng_eiface_constructor, | |
106 | .rcvmsg = ng_eiface_rcvmsg, | |
107 | .shutdown = ng_eiface_rmnode, | |
108 | .newhook = ng_eiface_newhook, | |
109 | .rcvdata = ng_eiface_rcvdata, | |
110 | .disconnect = ng_eiface_disconnect, | |
111 | .cmdlist = ng_eiface_cmdlist | |
112 | }; | |
113 | NETGRAPH_INIT(eiface, &typestruct); | |
114 | ||
115 | static struct unrhdr *ng_eiface_unit; | |
116 | ||
117 | /************************************************************************ | |
118 | INTERFACE STUFF | |
119 | ************************************************************************/ | |
120 | ||
121 | /* | |
122 | * Process an ioctl for the virtual interface | |
123 | */ | |
124 | static int | |
125 | ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) | |
126 | { | |
127 | struct ifreq *const ifr = (struct ifreq *)data; | |
128 | int s, error = 0; | |
129 | ||
130 | #ifdef DEBUG | |
131 | ng_eiface_print_ioctl(ifp, command, data); | |
132 | #endif | |
133 | s = splimp(); | |
134 | switch (command) { | |
135 | ||
136 | /* These two are mostly handled at a higher layer */ | |
137 | case SIOCSIFADDR: | |
138 | error = ether_ioctl(ifp, command, data); | |
139 | break; | |
140 | case SIOCGIFADDR: | |
141 | break; | |
142 | ||
143 | /* Set flags */ | |
144 | case SIOCSIFFLAGS: | |
145 | /* | |
146 | * If the interface is marked up and stopped, then start it. | |
147 | * If it is marked down and running, then stop it. | |
148 | */ | |
149 | if (ifp->if_flags & IFF_UP) { | |
150 | if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { | |
151 | ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); | |
152 | ifp->if_drv_flags |= IFF_DRV_RUNNING; | |
153 | } | |
154 | } else { | |
155 | if (ifp->if_drv_flags & IFF_DRV_RUNNING) | |
156 | ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | | |
157 | IFF_DRV_OACTIVE); | |
158 | } | |
159 | break; | |
160 | ||
161 | /* Set the interface MTU */ | |
162 | case SIOCSIFMTU: | |
163 | if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX || | |
164 | ifr->ifr_mtu < NG_EIFACE_MTU_MIN) | |
165 | error = EINVAL; | |
166 | else | |
167 | ifp->if_mtu = ifr->ifr_mtu; | |
168 | break; | |
169 | ||
170 | /* Stuff that's not supported */ | |
171 | case SIOCADDMULTI: | |
172 | case SIOCDELMULTI: | |
173 | error = 0; | |
174 | break; | |
175 | case SIOCSIFPHYS: | |
176 | error = EOPNOTSUPP; | |
177 | break; | |
178 | ||
179 | default: | |
180 | error = EINVAL; | |
181 | break; | |
182 | } | |
183 | splx(s); | |
184 | return (error); | |
185 | } | |
186 | ||
187 | static void | |
188 | ng_eiface_init(void *xsc) | |
189 | { | |
190 | priv_p sc = xsc; | |
191 | struct ifnet *ifp = sc->ifp; | |
192 | int s; | |
193 | ||
194 | s = splimp(); | |
195 | ||
196 | ifp->if_drv_flags |= IFF_DRV_RUNNING; | |
197 | ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |
198 | ||
199 | splx(s); | |
200 | } | |
201 | ||
202 | /* | |
203 | * We simply relay the packet to the "ether" hook, if it is connected. | |
204 | * We have been through the netgraph locking and are guaranteed to | |
205 | * be the only code running in this node at this time. | |
206 | */ | |
207 | static void | |
208 | ng_eiface_start2(node_p node, hook_p hook, void *arg1, int arg2) | |
209 | { | |
210 | struct ifnet *ifp = arg1; | |
211 | const priv_p priv = (priv_p)ifp->if_softc; | |
212 | int error = 0; | |
213 | struct mbuf *m; | |
214 | ||
215 | /* Check interface flags */ | |
216 | ||
217 | if (!((ifp->if_flags & IFF_UP) && | |
218 | (ifp->if_drv_flags & IFF_DRV_RUNNING))) | |
219 | return; | |
220 | ||
221 | for (;;) { | |
222 | /* | |
223 | * Grab a packet to transmit. | |
224 | */ | |
225 | IF_DEQUEUE(&ifp->if_snd, m); | |
226 | ||
227 | /* If there's nothing to send, break. */ | |
228 | if (m == NULL) | |
229 | break; | |
230 | ||
231 | /* | |
232 | * Berkeley packet filter. | |
233 | * Pass packet to bpf if there is a listener. | |
234 | * XXX is this safe? locking? | |
235 | */ | |
236 | BPF_MTAP(ifp, m); | |
237 | ||
238 | if (ifp->if_flags & IFF_MONITOR) { | |
239 | ifp->if_ipackets++; | |
240 | m_freem(m); | |
241 | continue; | |
242 | } | |
243 | ||
244 | /* | |
245 | * Send packet; if hook is not connected, mbuf will get | |
246 | * freed. | |
247 | */ | |
248 | NG_SEND_DATA_ONLY(error, priv->ether, m); | |
249 | ||
250 | /* Update stats */ | |
251 | if (error == 0) | |
252 | ifp->if_opackets++; | |
253 | else | |
254 | ifp->if_oerrors++; | |
255 | } | |
256 | ||
257 | ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |
258 | ||
259 | return; | |
260 | } | |
261 | ||
262 | /* | |
263 | * This routine is called to deliver a packet out the interface. | |
264 | * We simply queue the netgraph version to be called when netgraph locking | |
265 | * allows it to happen. | |
266 | * Until we know what the rest of the networking code is doing for | |
267 | * locking, we don't know how we will interact with it. | |
268 | * Take comfort from the fact that the ifnet struct is part of our | |
269 | * private info and can't go away while we are queued. | |
270 | * [Though we don't know it is still there now....] | |
271 | * it is possible we don't gain anything from this because | |
272 | * we would like to get the mbuf and queue it as data | |
273 | * somehow, but we can't and if we did would we solve anything? | |
274 | */ | |
275 | static void | |
276 | ng_eiface_start(struct ifnet *ifp) | |
277 | { | |
278 | ||
279 | const priv_p priv = (priv_p)ifp->if_softc; | |
280 | ||
281 | /* Don't do anything if output is active */ | |
282 | if (ifp->if_drv_flags & IFF_DRV_OACTIVE) | |
283 | return; | |
284 | ||
285 | ifp->if_drv_flags |= IFF_DRV_OACTIVE; | |
286 | ||
287 | if (ng_send_fn(priv->node, NULL, &ng_eiface_start2, ifp, 0) != 0) | |
288 | ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |
289 | } | |
290 | ||
291 | #ifdef DEBUG | |
292 | /* | |
293 | * Display an ioctl to the virtual interface | |
294 | */ | |
295 | ||
296 | static void | |
297 | ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) | |
298 | { | |
299 | char *str; | |
300 | ||
301 | switch (command & IOC_DIRMASK) { | |
302 | case IOC_VOID: | |
303 | str = "IO"; | |
304 | break; | |
305 | case IOC_OUT: | |
306 | str = "IOR"; | |
307 | break; | |
308 | case IOC_IN: | |
309 | str = "IOW"; | |
310 | break; | |
311 | case IOC_INOUT: | |
312 | str = "IORW"; | |
313 | break; | |
314 | default: | |
315 | str = "IO??"; | |
316 | } | |
317 | log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", | |
318 | ifp->if_xname, | |
319 | str, | |
320 | IOCGROUP(command), | |
321 | command & 0xff, | |
322 | IOCPARM_LEN(command)); | |
323 | } | |
324 | #endif /* DEBUG */ | |
325 | ||
326 | /************************************************************************ | |
327 | NETGRAPH NODE STUFF | |
328 | ************************************************************************/ | |
329 | ||
330 | /* | |
331 | * Constructor for a node | |
332 | */ | |
333 | static int | |
334 | ng_eiface_constructor(node_p node) | |
335 | { | |
336 | struct ifnet *ifp; | |
337 | priv_p priv; | |
338 | u_char eaddr[6] = {0,0,0,0,0,0}; | |
339 | ||
340 | /* Allocate node and interface private structures */ | |
fc025606 SW |
341 | priv = kmalloc(sizeof(*priv), M_NETGRAPH, |
342 | M_WAITOK | M_NULLOK | M_ZERO); | |
b06ebda0 MD |
343 | if (priv == NULL) |
344 | return (ENOMEM); | |
345 | ||
346 | ifp = priv->ifp = if_alloc(IFT_ETHER); | |
347 | if (ifp == NULL) { | |
5a975a3d | 348 | kfree(priv, M_NETGRAPH); |
b06ebda0 MD |
349 | return (ENOSPC); |
350 | } | |
351 | ||
352 | /* Link them together */ | |
353 | ifp->if_softc = priv; | |
354 | ||
355 | /* Get an interface unit number */ | |
356 | priv->unit = alloc_unr(ng_eiface_unit); | |
357 | ||
358 | /* Link together node and private info */ | |
359 | NG_NODE_SET_PRIVATE(node, priv); | |
360 | priv->node = node; | |
361 | ||
362 | /* Initialize interface structure */ | |
363 | if_initname(ifp, NG_EIFACE_EIFACE_NAME, priv->unit); | |
364 | ifp->if_init = ng_eiface_init; | |
365 | ifp->if_output = ether_output; | |
366 | ifp->if_start = ng_eiface_start; | |
367 | ifp->if_ioctl = ng_eiface_ioctl; | |
368 | ifp->if_watchdog = NULL; | |
369 | ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; | |
370 | ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); | |
371 | ||
372 | #if 0 | |
373 | /* Give this node name */ | |
374 | bzero(ifname, sizeof(ifname)); | |
375 | sprintf(ifname, "if%s", ifp->if_xname); | |
376 | (void)ng_name_node(node, ifname); | |
377 | #endif | |
378 | ||
379 | /* Attach the interface */ | |
380 | ether_ifattach(ifp, eaddr); | |
381 | ||
382 | /* Done */ | |
383 | return (0); | |
384 | } | |
385 | ||
386 | /* | |
387 | * Give our ok for a hook to be added | |
388 | */ | |
389 | static int | |
390 | ng_eiface_newhook(node_p node, hook_p hook, const char *name) | |
391 | { | |
392 | priv_p priv = NG_NODE_PRIVATE(node); | |
393 | struct ifnet *ifp = priv->ifp; | |
394 | ||
395 | if (strcmp(name, NG_EIFACE_HOOK_ETHER)) | |
396 | return (EPFNOSUPPORT); | |
397 | if (priv->ether != NULL) | |
398 | return (EISCONN); | |
399 | priv->ether = hook; | |
400 | NG_HOOK_SET_PRIVATE(hook, &priv->ether); | |
401 | ||
402 | if_link_state_change(ifp, LINK_STATE_UP); | |
403 | ||
404 | return (0); | |
405 | } | |
406 | ||
407 | /* | |
408 | * Receive a control message | |
409 | */ | |
410 | static int | |
411 | ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook) | |
412 | { | |
413 | const priv_p priv = NG_NODE_PRIVATE(node); | |
414 | struct ifnet *const ifp = priv->ifp; | |
415 | struct ng_mesg *resp = NULL; | |
416 | int error = 0; | |
417 | struct ng_mesg *msg; | |
418 | ||
419 | NGI_GET_MSG(item, msg); | |
420 | switch (msg->header.typecookie) { | |
421 | case NGM_EIFACE_COOKIE: | |
422 | switch (msg->header.cmd) { | |
423 | ||
424 | case NGM_EIFACE_SET: | |
425 | { | |
426 | if (msg->header.arglen != ETHER_ADDR_LEN) { | |
427 | error = EINVAL; | |
428 | break; | |
429 | } | |
430 | error = if_setlladdr(priv->ifp, | |
431 | (u_char *)msg->data, ETHER_ADDR_LEN); | |
432 | break; | |
433 | } | |
434 | ||
435 | case NGM_EIFACE_GET_IFNAME: | |
5a975a3d | 436 | NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_WAITOK | M_NULLOK); |
b06ebda0 MD |
437 | if (resp == NULL) { |
438 | error = ENOMEM; | |
439 | break; | |
440 | } | |
441 | strlcpy(resp->data, ifp->if_xname, IFNAMSIZ); | |
442 | break; | |
443 | ||
444 | case NGM_EIFACE_GET_IFADDRS: | |
445 | { | |
446 | struct ifaddr *ifa; | |
447 | caddr_t ptr; | |
448 | int buflen; | |
449 | ||
450 | #define SA_SIZE(s) ((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len) | |
451 | ||
452 | /* Determine size of response and allocate it */ | |
453 | buflen = 0; | |
454 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) | |
455 | buflen += SA_SIZE(ifa->ifa_addr); | |
5a975a3d | 456 | NG_MKRESPONSE(resp, msg, buflen, M_WAITOK | M_NULLOK); |
b06ebda0 MD |
457 | if (resp == NULL) { |
458 | error = ENOMEM; | |
459 | break; | |
460 | } | |
461 | ||
462 | /* Add addresses */ | |
463 | ptr = resp->data; | |
464 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |
465 | const int len = SA_SIZE(ifa->ifa_addr); | |
466 | ||
467 | if (buflen < len) { | |
468 | log(LOG_ERR, "%s: len changed?\n", | |
469 | ifp->if_xname); | |
470 | break; | |
471 | } | |
472 | bcopy(ifa->ifa_addr, ptr, len); | |
473 | ptr += len; | |
474 | buflen -= len; | |
475 | } | |
476 | break; | |
477 | #undef SA_SIZE | |
478 | } | |
479 | ||
480 | default: | |
481 | error = EINVAL; | |
482 | break; | |
483 | } /* end of inner switch() */ | |
484 | break; | |
485 | case NGM_FLOW_COOKIE: | |
486 | switch (msg->header.cmd) { | |
487 | case NGM_LINK_IS_UP: | |
488 | if_link_state_change(ifp, LINK_STATE_UP); | |
489 | break; | |
490 | case NGM_LINK_IS_DOWN: | |
491 | if_link_state_change(ifp, LINK_STATE_DOWN); | |
492 | break; | |
493 | default: | |
494 | break; | |
495 | } | |
496 | break; | |
497 | default: | |
498 | error = EINVAL; | |
499 | break; | |
500 | } | |
501 | NG_RESPOND_MSG(error, node, item, resp); | |
502 | NG_FREE_MSG(msg); | |
503 | return (error); | |
504 | } | |
505 | ||
506 | /* | |
507 | * Receive data from a hook. Pass the packet to the ether_input routine. | |
508 | */ | |
509 | static int | |
510 | ng_eiface_rcvdata(hook_p hook, item_p item) | |
511 | { | |
512 | const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); | |
513 | struct ifnet *const ifp = priv->ifp; | |
514 | struct mbuf *m; | |
515 | ||
516 | NGI_GET_M(item, m); | |
517 | NG_FREE_ITEM(item); | |
518 | ||
519 | if (!((ifp->if_flags & IFF_UP) && | |
520 | (ifp->if_drv_flags & IFF_DRV_RUNNING))) { | |
521 | NG_FREE_M(m); | |
522 | return (ENETDOWN); | |
523 | } | |
524 | ||
525 | if (m->m_len < ETHER_HDR_LEN) { | |
526 | m = m_pullup(m, ETHER_HDR_LEN); | |
527 | if (m == NULL) | |
528 | return (EINVAL); | |
529 | } | |
530 | ||
531 | /* Note receiving interface */ | |
532 | m->m_pkthdr.rcvif = ifp; | |
533 | ||
534 | /* Update interface stats */ | |
535 | ifp->if_ipackets++; | |
536 | ||
537 | (*ifp->if_input)(ifp, m); | |
538 | ||
539 | /* Done */ | |
540 | return (0); | |
541 | } | |
542 | ||
543 | /* | |
544 | * Shutdown processing. | |
545 | */ | |
546 | static int | |
547 | ng_eiface_rmnode(node_p node) | |
548 | { | |
549 | const priv_p priv = NG_NODE_PRIVATE(node); | |
550 | struct ifnet *const ifp = priv->ifp; | |
551 | ||
552 | ether_ifdetach(ifp); | |
553 | if_free(ifp); | |
554 | free_unr(ng_eiface_unit, priv->unit); | |
fc025606 | 555 | kfree(priv, M_NETGRAPH); |
b06ebda0 MD |
556 | NG_NODE_SET_PRIVATE(node, NULL); |
557 | NG_NODE_UNREF(node); | |
558 | return (0); | |
559 | } | |
560 | ||
561 | /* | |
562 | * Hook disconnection | |
563 | */ | |
564 | static int | |
565 | ng_eiface_disconnect(hook_p hook) | |
566 | { | |
567 | const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); | |
568 | ||
569 | priv->ether = NULL; | |
570 | return (0); | |
571 | } | |
572 | ||
573 | /* | |
574 | * Handle loading and unloading for this node type. | |
575 | */ | |
576 | static int | |
577 | ng_eiface_mod_event(module_t mod, int event, void *data) | |
578 | { | |
579 | int error = 0; | |
580 | ||
581 | switch (event) { | |
582 | case MOD_LOAD: | |
583 | ng_eiface_unit = new_unrhdr(0, 0xffff, NULL); | |
584 | break; | |
585 | case MOD_UNLOAD: | |
586 | delete_unrhdr(ng_eiface_unit); | |
587 | break; | |
588 | default: | |
589 | error = EOPNOTSUPP; | |
590 | break; | |
591 | } | |
592 | return (error); | |
593 | } |