Commit | Line | Data |
---|---|---|
672837a0 WJ |
1 | /* UNFINISHED CONVERSION TO 386BSD -wfj */ |
2 | ||
3 | /* if_tun.c - tunnel interface module & driver */ | |
4 | ||
5 | /* | |
6 | * Copyright (c) 1988, Julian Onions. | |
7 | * | |
8 | * This source may be freely distributed, however I would be interested | |
9 | * in any changes that are made. | |
10 | */ | |
11 | ||
12 | /* | |
13 | * 90/02/06 15:03 - Fixed a bug in where TIOCGPGRP and TIOCSPGRP were | |
14 | * mixed up. Anders Klemets - klemets@sics.se | |
15 | * | |
16 | * $Header: if_tun.c,v 1.13 88/07/11 08:28:51 jpo Exp $ | |
17 | * | |
18 | * $Log: if_tun.c,v $ | |
19 | * Revision 1.13 88/07/11 08:28:51 jpo | |
20 | * Some tidying up | |
21 | * | |
22 | * Revision 1.12 87/12/10 09:16:29 jpo | |
23 | * Decided the vax/mtpr was unnecessay. | |
24 | * | |
25 | * Revision 1.11 87/12/10 09:10:36 jpo | |
26 | * Fixed some minor things & 1 major bug. | |
27 | * | |
28 | * Revision 1.10 87/11/04 14:27:41 jpo | |
29 | * A few sanity checks added. | |
30 | * | |
31 | * Revision 1.9 87/11/04 14:13:45 jpo | |
32 | * Added some ioctls for non-blocking & async I/O | |
33 | * | |
34 | * Revision 1.8 87/10/19 10:28:14 jpo | |
35 | * Another touch up (sigh) | |
36 | * | |
37 | * Revision 1.7 87/10/19 10:25:48 jpo | |
38 | * Touch up. | |
39 | * | |
40 | * Revision 1.6 87/10/19 09:15:14 jpo | |
41 | * Touch up. | |
42 | * | |
43 | * Revision 1.5 87/10/19 08:34:51 jpo | |
44 | * General clean up - plus sun specific fixes | |
45 | * | |
46 | * Revision 1.4 87/10/16 17:10:12 jpo | |
47 | * Purged all ioctl read/writes and non-standard routing stuff. | |
48 | * | |
49 | * Revision 1.3 87/10/05 11:57:09 jpo | |
50 | * More debugging - in error mainly. | |
51 | * | |
52 | * Revision 1.2 87/10/04 18:29:45 jpo | |
53 | * Select & read/write working. | |
54 | */ | |
55 | ||
56 | #include "tun.h" | |
57 | #if NTUN > 0 | |
58 | ||
59 | /* | |
60 | * Tunnel driver. | |
61 | * | |
62 | * This driver takes packets off the IP i/f and hands them up to a | |
63 | * user process to have it's wicked way with. This driver has it's | |
64 | * roots in a similar driver written by Phil Cockcroft (formerly) at | |
65 | * UCL. This driver is based much more on read/write/select mode of | |
66 | * operation though. | |
67 | * | |
68 | * Julian Onions <jpo@cs.nott.ac.uk> | |
69 | * Nottingham University 1987. | |
70 | */ | |
71 | ||
72 | #include "param.h" | |
73 | #include "systm.h" | |
74 | #include "mbuf.h" | |
75 | #include "buf.h" | |
76 | #include "protosw.h" | |
77 | #include "socket.h" | |
78 | #include "ioctl.h" | |
79 | #include "errno.h" | |
80 | #include "syslog.h" | |
81 | ||
82 | #include "net/if.h" | |
83 | #include "net/netisr.h" | |
84 | #include "net/route.h" | |
85 | ||
86 | #ifdef INET | |
87 | #include "netinet/in.h" | |
88 | #include "netinet/in_systm.h" | |
89 | #include "netinet/in_var.h" | |
90 | #include "netinet/ip.h" | |
91 | #include "netinet/if_ether.h" | |
92 | #endif | |
93 | ||
94 | #ifdef NS | |
95 | #include "netns/ns.h" | |
96 | #include "netns/ns_if.h" | |
97 | #endif | |
98 | ||
99 | #define TUNDEBUG if (tundebug) printf | |
100 | ||
101 | int tundebug = 0; | |
102 | struct tunctl | |
103 | { | |
104 | u_short tun_flags; /* misc flags */ | |
105 | struct ifnet tun_if; /* the interface */ | |
106 | int tun_pgrp; /* the process group - if any */ | |
107 | struct proc *tun_rsel; /* read select */ | |
108 | struct proc *tun_wsel; /* write select (not used) */ | |
109 | } tunctl[NTUN]; | |
110 | ||
111 | extern int ifqmaxlen; | |
112 | ||
113 | int tunoutput (), tunioctl (), tuninit (); | |
114 | ||
115 | /* tunnel open - must be superuser & the device must be configured in */ | |
116 | tunopen (dev, flag) | |
117 | dev_t dev; | |
118 | { | |
119 | register int unit; | |
120 | struct tunctl *tp; | |
121 | register struct ifnet *ifp; | |
122 | ||
123 | if (!suser ()) | |
124 | return EACCES; | |
125 | if ((unit = minor (dev)) >= NTUN) | |
126 | return (ENXIO); | |
127 | tp = &tunctl[unit]; | |
128 | if (tp->tun_flags & TUN_OPEN) | |
129 | return ENXIO; | |
130 | if ((tp->tun_flags & TUN_INITED) == 0) { | |
131 | tp->tun_flags = TUN_INITED; | |
132 | tunattach (unit); | |
133 | } | |
134 | ifp = &tp->tun_if; | |
135 | tp->tun_flags |= TUN_OPEN; | |
136 | TUNDEBUG ("%s%d: open\n", ifp->if_name, ifp->if_unit); | |
137 | return (0); | |
138 | } | |
139 | ||
140 | /* tunclose - close the device - mark i/f down & delete routing info */ | |
141 | tunclose (dev, flag) | |
142 | dev_t dev; | |
143 | { | |
144 | int s; | |
145 | int rcoll; | |
146 | register int unit = minor (dev); | |
147 | register struct tunctl *tp = &tunctl[unit]; | |
148 | register struct ifnet *ifp = &tp->tun_if; | |
149 | register struct mbuf *m; | |
150 | ||
151 | rcoll = tp->tun_flags & TUN_RCOLL; | |
152 | tp->tun_flags &= TUN_INITED; | |
153 | ||
154 | /* | |
155 | * junk all pending output | |
156 | */ | |
157 | do { | |
158 | s = splimp (); | |
159 | IF_DEQUEUE (&ifp->if_snd, m); | |
160 | splx (s); | |
161 | if (m) /* actually - m_freem checks this - but what the hell */ | |
162 | m_freem (m); | |
163 | } while (m != 0); | |
164 | if (ifp->if_flags & IFF_UP) { | |
165 | s = splimp (); | |
166 | if_down (ifp); | |
167 | if (ifp->if_flags & IFF_RUNNING) | |
168 | rtinit (ifp->if_addrlist, (int)SIOCDELRT, RTF_HOST); | |
169 | splx (s); | |
170 | } | |
171 | tp -> tun_pgrp = 0; | |
172 | if (tp -> tun_rsel) | |
173 | selwakeup (tp->tun_rsel, rcoll); | |
174 | ||
175 | tp -> tun_rsel = tp -> tun_wsel = (struct proc *)0; | |
176 | ||
177 | TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit); | |
178 | return (0); | |
179 | } | |
180 | ||
181 | /* | |
182 | * attach an interface N.B. argument is not same as other drivers | |
183 | */ | |
184 | int | |
185 | tunattach (unit) | |
186 | int unit; | |
187 | { | |
188 | register struct ifnet *ifp = &tunctl[unit].tun_if; | |
189 | register struct sockaddr_in *sin; | |
190 | ||
191 | ifp->if_unit = unit; | |
192 | ifp->if_name = "tun"; | |
193 | ifp->if_mtu = TUNMTU; | |
194 | ifp->if_ioctl = tunioctl; | |
195 | ifp->if_output = tunoutput; | |
196 | ifp->if_init = tuninit; | |
197 | #ifndef BSD4_3 | |
198 | sin = (struct sockaddr_in *) & ifp->if_addr; | |
199 | sin->sin_family = AF_INET; | |
200 | #endif | |
201 | ifp->if_flags = IFF_POINTOPOINT; | |
202 | ifp->if_snd.ifq_maxlen = ifqmaxlen; | |
203 | ifp->if_collisions = ifp->if_ierrors = ifp->if_oerrors = 0; | |
204 | ifp->if_ipackets = ifp->if_opackets = 0; | |
205 | if_attach (ifp); | |
206 | TUNDEBUG ("%s%d: tunattach\n", ifp->if_name, ifp->if_unit); | |
207 | return 0; | |
208 | } | |
209 | ||
210 | int | |
211 | tuninit (unit) | |
212 | int unit; | |
213 | { | |
214 | register struct tunctl *tp = &tunctl[unit]; | |
215 | register struct ifnet *ifp = &tp->tun_if; | |
216 | #ifndef BSD4_3 | |
217 | register struct sockaddr_in *sin; | |
218 | ||
219 | sin = (struct sockaddr_in *) & ifp->if_addr; | |
220 | if (sin->sin_addr.s_addr == 0) /* if address still unknown */ | |
221 | return; | |
222 | if_rtinit (ifp, RTF_UP); | |
223 | #endif | |
224 | ifp->if_flags |= IFF_UP | IFF_RUNNING; | |
225 | tp->tun_flags |= TUN_IASET; | |
226 | TUNDEBUG ("%s%d: tuninit\n", ifp->if_name, ifp->if_unit); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | /* | |
231 | * Process an ioctl request. | |
232 | * The problem here is 4.2 pass a struct ifreq * to this routine, | |
233 | * sun only passes a struct sockaddr * since 3.2 at least. This is | |
234 | * rather upsetting! Also, sun doesn't pass the SIOCDSTADDR ioctl through | |
235 | * so we can't detect this being set directly. This is the reason for | |
236 | * tuncheckready. | |
237 | * Under 4.3 and SunOs 4.0 we now get the SIOCSIFDSTADDR ioctl, and we get a | |
238 | * struct in_ifaddr * for data. (tte) | |
239 | */ | |
240 | ||
241 | #if !defined(BSD4_3) && defined(sun) | |
242 | ||
243 | /* | |
244 | * workaround for not being able to detect DSTADDR being set. | |
245 | */ | |
246 | ||
247 | tuncheckready (ifp) | |
248 | struct ifnet *ifp; | |
249 | { | |
250 | struct tunctl *tp = &tunctl[ifp->if_unit]; | |
251 | struct sockaddr_in *sin; | |
252 | ||
253 | sin = (struct sockaddr_in *) &ifp->if_dstaddr; | |
254 | if (sin->sin_addr.s_addr == 0) | |
255 | return 0; | |
256 | tp -> tun_flags |= TUN_DSTADDR; | |
257 | return 1; | |
258 | } | |
259 | #else | |
260 | #define tuncheckready(dummy) 1 | |
261 | #endif /* !defined(BSD4_3) && defined(sun) */ | |
262 | ||
263 | int | |
264 | tunioctl (ifp, cmd, data) | |
265 | register struct ifnet *ifp; | |
266 | int cmd; | |
267 | caddr_t data; | |
268 | { | |
269 | #ifndef BSD4_3 | |
270 | #ifdef sun | |
271 | struct sockaddr_in *sin = (struct sockaddr_in *) data; | |
272 | #else | |
273 | struct sockaddr_in *sin; | |
274 | struct ifreq *ifr = (struct ifreq *) data; | |
275 | #endif | |
276 | #endif /* BSD4_3 */ | |
277 | ||
278 | int s = splimp (), error = 0; | |
279 | struct tunctl *tp = &tunctl[ifp->if_unit]; | |
280 | ||
281 | switch (cmd) | |
282 | { | |
283 | case SIOCSIFADDR: | |
284 | #ifndef BSD4_3 | |
285 | if (ifp->if_flags & IFF_RUNNING) | |
286 | if_rtinit (ifp, -1); /* delete previous route */ | |
287 | #ifndef sun | |
288 | sin = (struct sockaddr_in *)&ifr -> ifr_addr; | |
289 | #endif | |
290 | ifp->if_addr = *((struct sockaddr *) sin); | |
291 | ifp->if_net = in_netof (sin->sin_addr); | |
292 | ifp->if_host[0] = in_lnaof (sin->sin_addr); | |
293 | #endif | |
294 | tuninit (ifp->if_unit); | |
295 | break; | |
296 | case SIOCSIFDSTADDR: | |
297 | tp->tun_flags |= TUN_DSTADDR; | |
298 | #ifndef BSD4_3 | |
299 | #ifndef sun | |
300 | sin = (struct sockaddr_in *)&ifr -> ifr_addr; | |
301 | #endif | |
302 | ifp->if_dstaddr = *((struct sockaddr *)sin); | |
303 | #endif BSD4_3 | |
304 | TUNDEBUG ("%s%d: destination addres set\n", ifp->if_name, | |
305 | ifp -> if_unit); | |
306 | break; | |
307 | default: | |
308 | error = EINVAL; | |
309 | } | |
310 | splx (s); | |
311 | return (error); | |
312 | } | |
313 | ||
314 | /* | |
315 | * tunoutput - queue packets from higher level ready to put out. | |
316 | */ | |
317 | tunoutput (ifp, m0, dst) | |
318 | struct ifnet *ifp; | |
319 | struct mbuf *m0; | |
320 | struct sockaddr *dst; | |
321 | { | |
322 | struct tunctl *tp; | |
323 | struct proc *p; | |
324 | int s; | |
325 | ||
326 | TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit); | |
327 | tp = &tunctl[ifp->if_unit]; | |
328 | if ((tp->tun_flags & TUN_READY) != TUN_READY) { | |
329 | if(tuncheckready(ifp) == 0) { | |
330 | TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, | |
331 | ifp->if_unit, tp->tun_flags); | |
332 | m_freem (m0); | |
333 | return EHOSTDOWN; | |
334 | } | |
335 | } | |
336 | ||
337 | switch (dst->sa_family) { | |
338 | #ifdef INET | |
339 | case AF_INET: | |
340 | s = splimp (); | |
341 | if (IF_QFULL (&ifp->if_snd)) | |
342 | { | |
343 | IF_DROP (&ifp->if_snd); | |
344 | m_freem (m0); | |
345 | splx (s); | |
346 | ifp->if_collisions++; | |
347 | return (ENOBUFS); | |
348 | } | |
349 | IF_ENQUEUE (&ifp->if_snd, m0); | |
350 | splx (s); | |
351 | ifp->if_opackets++; | |
352 | break; | |
353 | #endif | |
354 | default: | |
355 | m_freem (m0); | |
356 | return EAFNOSUPPORT; | |
357 | } | |
358 | ||
359 | if (tp->tun_flags & TUN_RWAIT) { | |
360 | tp->tun_flags &= ~TUN_RWAIT; | |
361 | wakeup ((caddr_t) tp); | |
362 | } | |
363 | if (tp->tun_flags & TUN_ASYNC && tp -> tun_pgrp != 0) { | |
364 | if (tp->tun_pgrp > 0) | |
365 | gsignal (tp->tun_pgrp, SIGIO); | |
366 | else if ((p = pfind (-tp->tun_pgrp)) != 0) | |
367 | psignal (p, SIGIO); | |
368 | } | |
369 | if (tp->tun_rsel) { | |
370 | selwakeup (tp->tun_rsel, tp->tun_flags & TUN_RCOLL); | |
371 | tp->tun_flags &= ~TUN_RCOLL; | |
372 | tp->tun_rsel = (struct proc *) 0; | |
373 | } | |
374 | return 0; | |
375 | } | |
376 | ||
377 | /* | |
378 | * the cdevsw interface is now pretty minimal. | |
379 | */ | |
380 | ||
381 | tuncioctl (dev, cmd, data, flag) | |
382 | dev_t dev; | |
383 | caddr_t data; | |
384 | { | |
385 | int unit = minor(dev); | |
386 | struct tunctl *tp = &tunctl[unit]; | |
387 | int s; | |
388 | ||
389 | switch (cmd) { | |
390 | case TUNSDEBUG: | |
391 | tundebug = *(int *)data; | |
392 | break; | |
393 | ||
394 | case TUNGDEBUG: | |
395 | *(int *)data = tundebug; | |
396 | break; | |
397 | ||
398 | case FIONBIO: | |
399 | if (*(int *)data) | |
400 | tp->tun_flags |= TUN_NBIO; | |
401 | else | |
402 | tp->tun_flags &= ~TUN_NBIO; | |
403 | break; | |
404 | ||
405 | case FIOASYNC: | |
406 | if (*(int *)data) | |
407 | tp->tun_flags |= TUN_ASYNC; | |
408 | else | |
409 | tp->tun_flags &= ~TUN_ASYNC; | |
410 | break; | |
411 | ||
412 | case FIONREAD: | |
413 | s = splimp (); | |
414 | if (tp->tun_if.if_snd.ifq_head) | |
415 | *(int *)data = tp->tun_if.if_snd.ifq_head->m_len; | |
416 | else *(int *)data = 0; | |
417 | splx (s); | |
418 | break; | |
419 | ||
420 | case TIOCSPGRP: | |
421 | tp->tun_pgrp = *(int *)data; | |
422 | break; | |
423 | ||
424 | case TIOCGPGRP: | |
425 | *(int *)data = tp->tun_pgrp; | |
426 | break; | |
427 | ||
428 | default: | |
429 | return (ENOTTY); | |
430 | } | |
431 | return (0); | |
432 | } | |
433 | ||
434 | /* | |
435 | * The cdevsw read interface - reads a packet at a time, or at least as much | |
436 | * of a packet as can be read. | |
437 | */ | |
438 | tunread (dev, uio) | |
439 | dev_t dev; | |
440 | struct uio *uio; | |
441 | { | |
442 | register struct ifnet *ifp; | |
443 | register struct mbuf *m, *m0; | |
444 | int unit = minor (dev); | |
445 | int len, s; | |
446 | int error = 0; | |
447 | struct tunctl *tp; | |
448 | ||
449 | tp = &tunctl[unit]; | |
450 | ifp = &tp->tun_if; | |
451 | TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit); | |
452 | if ((tp->tun_flags & TUN_READY) != TUN_READY) { | |
453 | if(tuncheckready(ifp) == 0) { | |
454 | TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, | |
455 | ifp->if_unit, tp->tun_flags); | |
456 | return EHOSTDOWN; | |
457 | } | |
458 | } | |
459 | ||
460 | tp->tun_flags &= ~TUN_RWAIT; | |
461 | ||
462 | s = splimp (); | |
463 | do { | |
464 | IF_DEQUEUE (&ifp->if_snd, m0); | |
465 | if (m0 == 0) { | |
466 | if (tp -> tun_flags & TUN_NBIO) { | |
467 | splx (s); | |
468 | return EWOULDBLOCK; | |
469 | } | |
470 | tp->tun_flags |= TUN_RWAIT; | |
471 | sleep ((caddr_t) tp, PZERO + 1); | |
472 | } | |
473 | } while (m0 == 0); | |
474 | splx (s); | |
475 | ||
476 | while (m0 && uio->uio_resid > 0 && error == 0) { | |
477 | len = MIN (uio->uio_resid, m0->m_len); | |
478 | if (len == 0) | |
479 | break; | |
480 | error = uiomove (mtod (m0, caddr_t), len, | |
481 | UIO_READ, uio); | |
482 | MFREE (m0, m); | |
483 | m0 = m; | |
484 | } | |
485 | ||
486 | if (m0 != 0) { | |
487 | TUNDEBUG ("Dropping mbuf\n"); | |
488 | m_freem (m0); | |
489 | } | |
490 | return error; | |
491 | } | |
492 | ||
493 | /* | |
494 | * the cdevsw write interface - an atomic write is a packet - or else! | |
495 | */ | |
496 | ||
497 | tunwrite (dev, uio) | |
498 | int dev; | |
499 | struct uio *uio; | |
500 | { | |
501 | int error = 0; | |
502 | int unit = minor (dev); | |
503 | struct mbuf *top, **mp, *m; | |
504 | struct ifnet *ifp = &(tunctl[unit].tun_if); | |
505 | int s; | |
506 | ||
507 | TUNDEBUG ("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit); | |
508 | ||
509 | if (uio->uio_resid < 0 || uio->uio_resid > TUNMTU) { | |
510 | TUNDEBUG ("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit, | |
511 | uio->uio_resid); | |
512 | return EIO; | |
513 | } | |
514 | top = 0; | |
515 | mp = ⊤ | |
516 | while (error == 0 && uio->uio_resid > 0) { | |
517 | MGET (m, M_DONTWAIT, MT_DATA); | |
518 | if (m == 0) { | |
519 | error = ENOBUFS; | |
520 | break; | |
521 | } | |
522 | m->m_len = MIN (MLEN, uio->uio_resid); | |
523 | error = uiomove (mtod (m, caddr_t), m->m_len, UIO_WRITE, uio); | |
524 | *mp = m; | |
525 | mp = &m->m_next; | |
526 | } | |
527 | if (error) { | |
528 | if (top) | |
529 | m_freem (top); | |
530 | return error; | |
531 | } | |
532 | ||
533 | #ifdef BSD4_3 | |
534 | /* | |
535 | * Place interface pointer before the data | |
536 | * for the receiving protocol. | |
537 | */ | |
538 | if (top->m_off <= MMAXOFF && | |
539 | top->m_off >= MMINOFF + sizeof(struct ifnet *)) { | |
540 | top->m_off -= sizeof(struct ifnet *); | |
541 | top->m_len += sizeof(struct ifnet *); | |
542 | } else { | |
543 | MGET(m, M_DONTWAIT, MT_HEADER); | |
544 | if (m == (struct mbuf *)0) | |
545 | return (ENOBUFS); | |
546 | m->m_len = sizeof(struct ifnet *); | |
547 | m->m_next = top; | |
548 | top = m; | |
549 | } | |
550 | *(mtod(top, struct ifnet **)) = ifp; | |
551 | #endif /* BSD4_3 */ | |
552 | ||
553 | s = splimp (); | |
554 | if (IF_QFULL (&ipintrq)) { | |
555 | IF_DROP (&ipintrq); | |
556 | splx (s); | |
557 | ifp->if_collisions++; | |
558 | m_freem (top); | |
559 | return ENOBUFS; | |
560 | } | |
561 | IF_ENQUEUE (&ipintrq, top); | |
562 | splx (s); | |
563 | ifp->if_ipackets++; | |
564 | schednetisr (NETISR_IP); | |
565 | return error; | |
566 | } | |
567 | ||
568 | /* | |
569 | * tunselect - the select interface, this is only useful on reads really. | |
570 | * The write detect always returns true, write never blocks anyway, it either | |
571 | * accepts the packet or drops it. | |
572 | */ | |
573 | tunselect (dev, rw) | |
574 | dev_t dev; | |
575 | int rw; | |
576 | { | |
577 | int unit = minor (dev); | |
578 | register struct tunctl *tp = &tunctl[unit]; | |
579 | struct ifnet *ifp = &tp->tun_if; | |
580 | int s = splimp (); | |
581 | ||
582 | TUNDEBUG ("%s%d: tunselect\n", ifp->if_name, ifp->if_unit); | |
583 | switch (rw) { | |
584 | case FREAD: | |
585 | if (ifp->if_snd.ifq_len > 0) { | |
586 | splx (s); | |
587 | TUNDEBUG ("%s%d: tunselect q=%d\n", ifp->if_name, | |
588 | ifp->if_unit, ifp->if_snd.ifq_len); | |
589 | return 1; | |
590 | } | |
591 | if (tp->tun_rsel && tp->tun_rsel->p_wchan == | |
592 | (caddr_t) & selwait) | |
593 | tp->tun_flags |= TUN_RCOLL; | |
594 | else | |
595 | tp->tun_rsel = u.u_procp; | |
596 | break; | |
597 | case FWRITE: | |
598 | splx (s); | |
599 | return 1; | |
600 | } | |
601 | splx (s); | |
602 | TUNDEBUG ("%s%d: tunselect waiting\n", ifp->if_name, ifp->if_unit); | |
603 | return 0; | |
604 | } | |
605 | #endif NTUN |