Annotation of sys/netinet/ip_ipip.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: ip_ipip.c,v 1.39 2007/02/10 15:34:22 claudio Exp $ */
2: /*
3: * The authors of this code are John Ioannidis (ji@tla.org),
4: * Angelos D. Keromytis (kermit@csd.uch.gr) and
5: * Niels Provos (provos@physnet.uni-hamburg.de).
6: *
7: * The original version of this code was written by John Ioannidis
8: * for BSD/OS in Athens, Greece, in November 1995.
9: *
10: * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
11: * by Angelos D. Keromytis.
12: *
13: * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
14: * and Niels Provos.
15: *
16: * Additional features in 1999 by Angelos D. Keromytis.
17: *
18: * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
19: * Angelos D. Keromytis and Niels Provos.
20: * Copyright (c) 2001, Angelos D. Keromytis.
21: *
22: * Permission to use, copy, and modify this software with or without fee
23: * is hereby granted, provided that this entire notice is included in
24: * all copies of any software which is or includes a copy or
25: * modification of this software.
26: * You may use this code under the GNU public license if you so wish. Please
27: * contribute changes back to the authors under this freer than GPL license
28: * so that we may further the use of strong encryption without limitations to
29: * all.
30: *
31: * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
32: * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
33: * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
34: * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
35: * PURPOSE.
36: */
37:
38: /*
39: * IP-inside-IP processing
40: */
41:
42: #include <sys/param.h>
43: #include <sys/systm.h>
44: #include <sys/mbuf.h>
45: #include <sys/socket.h>
46: #include <sys/sysctl.h>
47:
48: #include <net/if.h>
49: #include <net/route.h>
50: #include <net/netisr.h>
51: #include <net/bpf.h>
52:
53: #include <netinet/in.h>
54: #include <netinet/in_systm.h>
55: #include <netinet/ip.h>
56: #include <netinet/in_pcb.h>
57: #include <netinet/in_var.h>
58: #include <netinet/ip_var.h>
59: #include <netinet/ip_ecn.h>
60:
61: #ifdef MROUTING
62: #include <netinet/ip_mroute.h>
63: #endif
64:
65: #include <netinet/ip_ipsp.h>
66: #include <netinet/ip_ipip.h>
67:
68: #include "bpfilter.h"
69:
70: #ifdef ENCDEBUG
71: #define DPRINTF(x) if (encdebug) printf x
72: #else
73: #define DPRINTF(x)
74: #endif
75:
76: /*
77: * We can control the acceptance of IP4 packets by altering the sysctl
78: * net.inet.ipip.allow value. Zero means drop them, all else is acceptance.
79: */
80: int ipip_allow = 0;
81:
82: struct ipipstat ipipstat;
83:
84: #ifdef INET6
85: /*
86: * Really only a wrapper for ipip_input(), for use with IPv6.
87: */
88: int
89: ip4_input6(struct mbuf **m, int *offp, int proto)
90: {
91: /* If we do not accept IP-in-IP explicitly, drop. */
92: if (!ipip_allow && ((*m)->m_flags & (M_AUTH|M_CONF)) == 0) {
93: DPRINTF(("ip4_input6(): dropped due to policy\n"));
94: ipipstat.ipips_pdrops++;
95: m_freem(*m);
96: return IPPROTO_DONE;
97: }
98:
99: ipip_input(*m, *offp, NULL);
100: return IPPROTO_DONE;
101: }
102: #endif /* INET6 */
103:
104: #ifdef INET
105: /*
106: * Really only a wrapper for ipip_input(), for use with IPv4.
107: */
108: void
109: ip4_input(struct mbuf *m, ...)
110: {
111: va_list ap;
112: int iphlen;
113:
114: /* If we do not accept IP-in-IP explicitly, drop. */
115: if (!ipip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
116: DPRINTF(("ip4_input(): dropped due to policy\n"));
117: ipipstat.ipips_pdrops++;
118: m_freem(m);
119: return;
120: }
121:
122: va_start(ap, m);
123: iphlen = va_arg(ap, int);
124: va_end(ap);
125:
126: ipip_input(m, iphlen, NULL);
127: }
128: #endif /* INET */
129:
130: /*
131: * ipip_input gets called when we receive an IP{46} encapsulated packet,
132: * either because we got it at a real interface, or because AH or ESP
133: * were being used in tunnel mode (in which case the rcvif element will
134: * contain the address of the encX interface associated with the tunnel.
135: */
136:
137: void
138: ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp)
139: {
140: struct sockaddr_in *sin;
141: struct ifnet *ifp;
142: struct ifaddr *ifa;
143: struct ifqueue *ifq = NULL;
144: struct ip *ipo;
145: #ifdef INET6
146: struct sockaddr_in6 *sin6;
147: struct ip6_hdr *ip6 = NULL;
148: u_int8_t itos;
149: #endif
150: u_int8_t nxt;
151: int isr;
152: u_int8_t otos;
153: u_int8_t v;
154: int hlen, s;
155:
156: ipipstat.ipips_ipackets++;
157:
158: m_copydata(m, 0, 1, &v);
159:
160: switch (v >> 4) {
161: #ifdef INET
162: case 4:
163: hlen = sizeof(struct ip);
164: break;
165: #endif /* INET */
166: #ifdef INET6
167: case 6:
168: hlen = sizeof(struct ip6_hdr);
169: break;
170: #endif
171: default:
172: ipipstat.ipips_family++;
173: m_freem(m);
174: return /* EAFNOSUPPORT */;
175: }
176:
177: /* Bring the IP header in the first mbuf, if not there already */
178: if (m->m_len < hlen) {
179: if ((m = m_pullup(m, hlen)) == NULL) {
180: DPRINTF(("ipip_input(): m_pullup() failed\n"));
181: ipipstat.ipips_hdrops++;
182: return;
183: }
184: }
185:
186: ipo = mtod(m, struct ip *);
187:
188: /* Keep outer ecn field. */
189: switch (v >> 4) {
190: #ifdef INET
191: case 4:
192: otos = ipo->ip_tos;
193: break;
194: #endif /* INET */
195: #ifdef INET6
196: case 6:
197: otos = (ntohl(mtod(m, struct ip6_hdr *)->ip6_flow) >> 20) & 0xff;
198: break;
199: #endif
200: default:
201: panic("ipip_input: should never reach here");
202: }
203:
204: /* Remove outer IP header */
205: m_adj(m, iphlen);
206:
207: /* Sanity check */
208: if (m->m_pkthdr.len < sizeof(struct ip)) {
209: ipipstat.ipips_hdrops++;
210: m_freem(m);
211: return;
212: }
213:
214: m_copydata(m, 0, 1, &v);
215:
216: switch (v >> 4) {
217: #ifdef INET
218: case 4:
219: hlen = sizeof(struct ip);
220: break;
221: #endif /* INET */
222:
223: #ifdef INET6
224: case 6:
225: hlen = sizeof(struct ip6_hdr);
226: break;
227: #endif
228: default:
229: ipipstat.ipips_family++;
230: m_freem(m);
231: return; /* EAFNOSUPPORT */
232: }
233:
234: /*
235: * Bring the inner IP header in the first mbuf, if not there already.
236: */
237: if (m->m_len < hlen) {
238: if ((m = m_pullup(m, hlen)) == NULL) {
239: DPRINTF(("ipip_input(): m_pullup() failed\n"));
240: ipipstat.ipips_hdrops++;
241: return;
242: }
243: }
244:
245: /*
246: * RFC 1853 specifies that the inner TTL should not be touched on
247: * decapsulation. There's no reason this comment should be here, but
248: * this is as good as any a position.
249: */
250:
251: /* Some sanity checks in the inner IP header */
252: switch (v >> 4) {
253: #ifdef INET
254: case 4:
255: ipo = mtod(m, struct ip *);
256: nxt = ipo->ip_p;
257: if (!ip_ecn_egress(ECN_ALLOWED, &otos, &ipo->ip_tos)) {
258: m_freem(m);
259: return;
260: }
261: break;
262: #endif /* INET */
263: #ifdef INET6
264: case 6:
265: ip6 = (struct ip6_hdr *) ipo;
266: nxt = ip6->ip6_nxt;
267: itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
268: if (!ip_ecn_egress(ECN_ALLOWED, &otos, &itos)) {
269: m_freem(m);
270: return;
271: }
272: ip6->ip6_flow &= ~htonl(0xff << 20);
273: ip6->ip6_flow |= htonl((u_int32_t) itos << 20);
274: break;
275: #endif
276: default:
277: panic("ipip_input: should never reach here");
278: }
279:
280: /* Check for local address spoofing. */
281: if ((m->m_pkthdr.rcvif == NULL ||
282: !(m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK)) &&
283: ipip_allow != 2) {
284: TAILQ_FOREACH(ifp, &ifnet, if_list) {
285: TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
286: #ifdef INET
287: if (ipo) {
288: if (ifa->ifa_addr->sa_family !=
289: AF_INET)
290: continue;
291:
292: sin = (struct sockaddr_in *) ifa->ifa_addr;
293:
294: if (sin->sin_addr.s_addr ==
295: ipo->ip_src.s_addr) {
296: ipipstat.ipips_spoof++;
297: m_freem(m);
298: return;
299: }
300: }
301: #endif /* INET */
302:
303: #ifdef INET6
304: if (ip6) {
305: if (ifa->ifa_addr->sa_family !=
306: AF_INET6)
307: continue;
308:
309: sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
310:
311: if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_src)) {
312: ipipstat.ipips_spoof++;
313: m_freem(m);
314: return;
315: }
316:
317: }
318: #endif /* INET6 */
319: }
320: }
321: }
322:
323: /* Statistics */
324: ipipstat.ipips_ibytes += m->m_pkthdr.len - iphlen;
325:
326: /*
327: * Interface pointer stays the same; if no IPsec processing has
328: * been done (or will be done), this will point to a normal
329: * interface. Otherwise, it'll point to an enc interface, which
330: * will allow a packet filter to distinguish between secure and
331: * untrusted packets.
332: */
333:
334: switch (v >> 4) {
335: #ifdef INET
336: case 4:
337: ifq = &ipintrq;
338: isr = NETISR_IP;
339: break;
340: #endif
341: #ifdef INET6
342: case 6:
343: ifq = &ip6intrq;
344: isr = NETISR_IPV6;
345: break;
346: #endif
347: default:
348: panic("ipip_input: should never reach here");
349: }
350:
351: #if NBPFILTER > 0
352: if (gifp && gifp->if_bpf)
353: bpf_mtap_af(gifp->if_bpf, ifq == &ipintrq ? AF_INET : AF_INET6,
354: m, BPF_DIRECTION_IN);
355: #endif
356:
357: s = splnet(); /* isn't it already? */
358: if (IF_QFULL(ifq)) {
359: IF_DROP(ifq);
360: m_freem(m);
361: ipipstat.ipips_qfull++;
362:
363: splx(s);
364:
365: DPRINTF(("ipip_input(): packet dropped because of full "
366: "queue\n"));
367: return;
368: }
369:
370: IF_ENQUEUE(ifq, m);
371: schednetisr(isr);
372: splx(s);
373: return;
374: }
375:
376: int
377: ipip_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int dummy,
378: int dummy2)
379: {
380: u_int8_t tp, otos;
381:
382: #ifdef INET
383: u_int8_t itos;
384: struct ip *ipo;
385: #endif /* INET */
386:
387: #ifdef INET6
388: struct ip6_hdr *ip6, *ip6o;
389: #endif /* INET6 */
390:
391: /* XXX Deal with empty TDB source/destination addresses. */
392:
393: m_copydata(m, 0, 1, &tp);
394: tp = (tp >> 4) & 0xff; /* Get the IP version number. */
395:
396: switch (tdb->tdb_dst.sa.sa_family) {
397: #ifdef INET
398: case AF_INET:
399: if (tdb->tdb_src.sa.sa_family != AF_INET ||
400: tdb->tdb_src.sin.sin_addr.s_addr == INADDR_ANY ||
401: tdb->tdb_dst.sin.sin_addr.s_addr == INADDR_ANY) {
402:
403: DPRINTF(("ipip_output(): unspecified tunnel endpoind "
404: "address in SA %s/%08x\n",
405: ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
406:
407: ipipstat.ipips_unspec++;
408: m_freem(m);
409: *mp = NULL;
410: return EINVAL;
411: }
412:
413: M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
414: if (m == 0) {
415: DPRINTF(("ipip_output(): M_PREPEND failed\n"));
416: ipipstat.ipips_hdrops++;
417: *mp = NULL;
418: return ENOBUFS;
419: }
420:
421: ipo = mtod(m, struct ip *);
422:
423: ipo->ip_v = IPVERSION;
424: ipo->ip_hl = 5;
425: ipo->ip_len = htons(m->m_pkthdr.len);
426: ipo->ip_ttl = ip_defttl;
427: ipo->ip_sum = 0;
428: ipo->ip_src = tdb->tdb_src.sin.sin_addr;
429: ipo->ip_dst = tdb->tdb_dst.sin.sin_addr;
430:
431: /*
432: * We do the htons() to prevent snoopers from determining our
433: * endianness.
434: */
435: ipo->ip_id = htons(ip_randomid());
436:
437: /* If the inner protocol is IP... */
438: if (tp == IPVERSION) {
439: /* Save ECN notification */
440: m_copydata(m, sizeof(struct ip) +
441: offsetof(struct ip, ip_tos),
442: sizeof(u_int8_t), (caddr_t) &itos);
443:
444: ipo->ip_p = IPPROTO_IPIP;
445:
446: /*
447: * We should be keeping tunnel soft-state and
448: * send back ICMPs if needed.
449: */
450: m_copydata(m, sizeof(struct ip) +
451: offsetof(struct ip, ip_off),
452: sizeof(u_int16_t), (caddr_t) &ipo->ip_off);
453: NTOHS(ipo->ip_off);
454: ipo->ip_off &= ~(IP_DF | IP_MF | IP_OFFMASK);
455: HTONS(ipo->ip_off);
456: }
457: #ifdef INET6
458: else if (tp == (IPV6_VERSION >> 4)) {
459: u_int32_t itos32;
460:
461: /* Save ECN notification. */
462: m_copydata(m, sizeof(struct ip) +
463: offsetof(struct ip6_hdr, ip6_flow),
464: sizeof(u_int32_t), (caddr_t) &itos32);
465: itos = ntohl(itos32) >> 20;
466: ipo->ip_p = IPPROTO_IPV6;
467: ipo->ip_off = 0;
468: }
469: #endif /* INET6 */
470: else {
471: m_freem(m);
472: *mp = NULL;
473: ipipstat.ipips_family++;
474: return EAFNOSUPPORT;
475: }
476:
477: otos = 0;
478: ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
479: ipo->ip_tos = otos;
480: break;
481: #endif /* INET */
482:
483: #ifdef INET6
484: case AF_INET6:
485: if (IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr) ||
486: tdb->tdb_src.sa.sa_family != AF_INET6 ||
487: IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_src.sin6.sin6_addr)) {
488:
489: DPRINTF(("ipip_output(): unspecified tunnel endpoind "
490: "address in SA %s/%08x\n",
491: ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
492:
493: ipipstat.ipips_unspec++;
494: m_freem(m);
495: *mp = NULL;
496: return ENOBUFS;
497: }
498:
499: /* scoped address handling */
500: ip6 = mtod(m, struct ip6_hdr *);
501: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
502: ip6->ip6_src.s6_addr16[1] = 0;
503: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
504: ip6->ip6_dst.s6_addr16[1] = 0;
505:
506: M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
507: if (m == 0) {
508: DPRINTF(("ipip_output(): M_PREPEND failed\n"));
509: ipipstat.ipips_hdrops++;
510: *mp = NULL;
511: return ENOBUFS;
512: }
513:
514: /* Initialize IPv6 header */
515: ip6o = mtod(m, struct ip6_hdr *);
516: ip6o->ip6_flow = 0;
517: ip6o->ip6_vfc &= ~IPV6_VERSION_MASK;
518: ip6o->ip6_vfc |= IPV6_VERSION;
519: ip6o->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6o));
520: ip6o->ip6_hlim = ip_defttl;
521: in6_embedscope(&ip6o->ip6_src, &tdb->tdb_src.sin6, NULL, NULL);
522: in6_embedscope(&ip6o->ip6_dst, &tdb->tdb_dst.sin6, NULL, NULL);
523:
524: #ifdef INET
525: if (tp == IPVERSION) {
526: /* Save ECN notification */
527: m_copydata(m, sizeof(struct ip6_hdr) +
528: offsetof(struct ip, ip_tos), sizeof(u_int8_t),
529: (caddr_t) &itos);
530:
531: /* This is really IPVERSION. */
532: ip6o->ip6_nxt = IPPROTO_IPIP;
533: }
534: else
535: #endif /* INET */
536: if (tp == (IPV6_VERSION >> 4)) {
537: u_int32_t itos32;
538:
539: /* Save ECN notification. */
540: m_copydata(m, sizeof(struct ip6_hdr) +
541: offsetof(struct ip6_hdr, ip6_flow),
542: sizeof(u_int32_t), (caddr_t) &itos32);
543: itos = ntohl(itos32) >> 20;
544:
545: ip6o->ip6_nxt = IPPROTO_IPV6;
546: } else {
547: m_freem(m);
548: *mp = NULL;
549: ipipstat.ipips_family++;
550: return EAFNOSUPPORT;
551: }
552:
553: otos = 0;
554: ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
555: ip6o->ip6_flow |= htonl((u_int32_t) otos << 20);
556: break;
557: #endif /* INET6 */
558:
559: default:
560: DPRINTF(("ipip_output(): unsupported protocol family %d\n",
561: tdb->tdb_dst.sa.sa_family));
562: m_freem(m);
563: *mp = NULL;
564: ipipstat.ipips_family++;
565: return EAFNOSUPPORT;
566: }
567:
568: ipipstat.ipips_opackets++;
569: *mp = m;
570:
571: #ifdef INET
572: if (tdb->tdb_dst.sa.sa_family == AF_INET) {
573: if (tdb->tdb_xform->xf_type == XF_IP4)
574: tdb->tdb_cur_bytes +=
575: m->m_pkthdr.len - sizeof(struct ip);
576:
577: ipipstat.ipips_obytes += m->m_pkthdr.len - sizeof(struct ip);
578: }
579: #endif /* INET */
580:
581: #ifdef INET6
582: if (tdb->tdb_dst.sa.sa_family == AF_INET6) {
583: if (tdb->tdb_xform->xf_type == XF_IP4)
584: tdb->tdb_cur_bytes +=
585: m->m_pkthdr.len - sizeof(struct ip6_hdr);
586:
587: ipipstat.ipips_obytes +=
588: m->m_pkthdr.len - sizeof(struct ip6_hdr);
589: }
590: #endif /* INET6 */
591:
592: return 0;
593: }
594:
595: #ifdef IPSEC
596: int
597: ipe4_attach()
598: {
599: return 0;
600: }
601:
602: int
603: ipe4_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
604: {
605: tdbp->tdb_xform = xsp;
606: return 0;
607: }
608:
609: int
610: ipe4_zeroize(struct tdb *tdbp)
611: {
612: return 0;
613: }
614:
615: void
616: ipe4_input(struct mbuf *m, ...)
617: {
618: /* This is a rather serious mistake, so no conditional printing. */
619: printf("ipe4_input(): should never be called\n");
620: if (m)
621: m_freem(m);
622: }
623: #endif /* IPSEC */
624:
625: int
626: ipip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
627: size_t newlen)
628: {
629: /* All sysctl names at this level are terminal. */
630: if (namelen != 1)
631: return (ENOTDIR);
632:
633: switch (name[0]) {
634: case IPIPCTL_ALLOW:
635: return (sysctl_int(oldp, oldlenp, newp, newlen, &ipip_allow));
636: default:
637: return (ENOPROTOOPT);
638: }
639: /* NOTREACHED */
640: }
CVSweb