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