Annotation of sys/netinet/ip_ether.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ip_ether.c,v 1.50 2007/02/20 19:37:40 claudio Exp $ */
! 2: /*
! 3: * The author of this code is Angelos D. Keromytis (kermit@adk.gr)
! 4: *
! 5: * This code was written by Angelos D. Keromytis for OpenBSD in October 1999.
! 6: *
! 7: * Copyright (C) 1999-2001 Angelos D. Keromytis.
! 8: *
! 9: * Permission to use, copy, and modify this software with or without fee
! 10: * is hereby granted, provided that this entire notice is included in
! 11: * all copies of any software which is or includes a copy or
! 12: * modification of this software.
! 13: * You may use this code under the GNU public license if you so wish. Please
! 14: * contribute changes back to the authors under this freer than GPL license
! 15: * so that we may further the use of strong encryption without limitations to
! 16: * all.
! 17: *
! 18: * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
! 19: * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
! 20: * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
! 21: * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
! 22: * PURPOSE.
! 23: */
! 24:
! 25: /*
! 26: * Ethernet-inside-IP processing (RFC3378).
! 27: */
! 28:
! 29: #include "bridge.h"
! 30:
! 31: #include <sys/param.h>
! 32: #include <sys/systm.h>
! 33: #include <sys/mbuf.h>
! 34: #include <sys/socket.h>
! 35: #include <sys/sysctl.h>
! 36:
! 37: #include <net/if.h>
! 38: #include <net/route.h>
! 39: #include <net/bpf.h>
! 40:
! 41: #ifdef INET
! 42: #include <netinet/in.h>
! 43: #include <netinet/in_systm.h>
! 44: #include <netinet/ip.h>
! 45: #include <netinet/in_pcb.h>
! 46: #include <netinet/ip_var.h>
! 47: #endif /* INET */
! 48:
! 49: #include <netinet/ip_ether.h>
! 50: #include <netinet/if_ether.h>
! 51: #include <net/if_bridge.h>
! 52: #include <net/if_gif.h>
! 53:
! 54: #include "gif.h"
! 55: #include "bpfilter.h"
! 56:
! 57: #ifdef ENCDEBUG
! 58: #define DPRINTF(x) if (encdebug) printf x
! 59: #else
! 60: #define DPRINTF(x)
! 61: #endif
! 62:
! 63: /*
! 64: * We can control the acceptance of EtherIP packets by altering the sysctl
! 65: * net.inet.etherip.allow value. Zero means drop them, all else is acceptance.
! 66: */
! 67: int etherip_allow = 0;
! 68:
! 69: struct etheripstat etheripstat;
! 70:
! 71: /*
! 72: * etherip_input gets called when we receive an encapsulated packet,
! 73: * either because we got it at a real interface, or because AH or ESP
! 74: * were being used in tunnel mode (in which case the rcvif element will
! 75: * contain the address of the encX interface associated with the tunnel.
! 76: */
! 77:
! 78: void
! 79: etherip_input(struct mbuf *m, ...)
! 80: {
! 81: union sockaddr_union ssrc, sdst;
! 82: struct ether_header eh;
! 83: int iphlen;
! 84: struct etherip_header eip;
! 85: u_int8_t v;
! 86: va_list ap;
! 87:
! 88: #if NGIF > 0
! 89: struct gif_softc *sc;
! 90: #if NBRIDGE > 0
! 91: int s;
! 92: #endif /* NBRIDGE */
! 93: #endif /* NGIF */
! 94:
! 95: va_start(ap, m);
! 96: iphlen = va_arg(ap, int);
! 97: va_end(ap);
! 98:
! 99: etheripstat.etherip_ipackets++;
! 100:
! 101: /* If we do not accept EtherIP explicitly, drop. */
! 102: if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
! 103: DPRINTF(("etherip_input(): dropped due to policy\n"));
! 104: etheripstat.etherip_pdrops++;
! 105: m_freem(m);
! 106: return;
! 107: }
! 108:
! 109: /*
! 110: * Make sure there's at least an ethernet header's and an EtherIP
! 111: * header's of worth of data after the outer IP header.
! 112: */
! 113: if (m->m_pkthdr.len < iphlen + sizeof(struct ether_header) +
! 114: sizeof(struct etherip_header)) {
! 115: DPRINTF(("etherip_input(): encapsulated packet too short\n"));
! 116: etheripstat.etherip_hdrops++;
! 117: m_freem(m);
! 118: return;
! 119: }
! 120:
! 121: /* Verify EtherIP version number */
! 122: m_copydata(m, iphlen, sizeof(struct etherip_header), (caddr_t)&eip);
! 123: if ((eip.eip_ver & ETHERIP_VER_VERS_MASK) != ETHERIP_VERSION) {
! 124: DPRINTF(("etherip_input(): received EtherIP version number "
! 125: "%d not suppoorted\n", (v >> 4) & 0xff));
! 126: etheripstat.etherip_adrops++;
! 127: m_freem(m);
! 128: return;
! 129: }
! 130:
! 131: /*
! 132: * Note that the other potential failure of the above check is that the
! 133: * second nibble of the EtherIP header (the reserved part) is not
! 134: * zero; this is also invalid protocol behaviour.
! 135: */
! 136: if (eip.eip_ver & ETHERIP_VER_RSVD_MASK) {
! 137: DPRINTF(("etherip_input(): received EtherIP invalid EtherIP "
! 138: "header (reserved field non-zero\n"));
! 139: etheripstat.etherip_adrops++;
! 140: m_freem(m);
! 141: return;
! 142: }
! 143:
! 144: /* Finally, the pad value must be zero. */
! 145: if (eip.eip_pad) {
! 146: DPRINTF(("etherip_input(): received EtherIP invalid "
! 147: "pad value\n"));
! 148: etheripstat.etherip_adrops++;
! 149: m_freem(m);
! 150: return;
! 151: }
! 152:
! 153: /* Make sure the ethernet header at least is in the first mbuf. */
! 154: if (m->m_len < iphlen + sizeof(struct ether_header) +
! 155: sizeof(struct etherip_header)) {
! 156: if ((m = m_pullup(m, iphlen + sizeof(struct ether_header) +
! 157: sizeof(struct etherip_header))) == NULL) {
! 158: DPRINTF(("etherip_input(): m_pullup() failed\n"));
! 159: etheripstat.etherip_adrops++;
! 160: return;
! 161: }
! 162: }
! 163:
! 164: /* Copy the addresses for use later. */
! 165: bzero(&ssrc, sizeof(ssrc));
! 166: bzero(&sdst, sizeof(sdst));
! 167:
! 168: v = *mtod(m, u_int8_t *);
! 169: switch (v >> 4) {
! 170: #ifdef INET
! 171: case 4:
! 172: ssrc.sa.sa_len = sdst.sa.sa_len = sizeof(struct sockaddr_in);
! 173: ssrc.sa.sa_family = sdst.sa.sa_family = AF_INET;
! 174: m_copydata(m, offsetof(struct ip, ip_src),
! 175: sizeof(struct in_addr),
! 176: (caddr_t) &ssrc.sin.sin_addr);
! 177: m_copydata(m, offsetof(struct ip, ip_dst),
! 178: sizeof(struct in_addr),
! 179: (caddr_t) &sdst.sin.sin_addr);
! 180: break;
! 181: #endif /* INET */
! 182: #ifdef INET6
! 183: case 6:
! 184: ssrc.sa.sa_len = sdst.sa.sa_len = sizeof(struct sockaddr_in6);
! 185: ssrc.sa.sa_family = sdst.sa.sa_family = AF_INET6;
! 186: m_copydata(m, offsetof(struct ip6_hdr, ip6_src),
! 187: sizeof(struct in6_addr),
! 188: (caddr_t) &ssrc.sin6.sin6_addr);
! 189: m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
! 190: sizeof(struct in6_addr),
! 191: (caddr_t) &sdst.sin6.sin6_addr);
! 192: break;
! 193: #endif /* INET6 */
! 194: default:
! 195: DPRINTF(("etherip_input(): invalid protocol %d\n", v));
! 196: m_freem(m);
! 197: etheripstat.etherip_hdrops++;
! 198: return /* EAFNOSUPPORT */;
! 199: }
! 200:
! 201: /* Chop off the `outer' IP and EtherIP headers and reschedule. */
! 202: m_adj(m, iphlen + sizeof(struct etherip_header));
! 203:
! 204: /* Statistics */
! 205: etheripstat.etherip_ibytes += m->m_pkthdr.len;
! 206:
! 207: /* Copy ethernet header */
! 208: m_copydata(m, 0, sizeof(eh), (void *) &eh);
! 209:
! 210: /* Reset the flags based on the inner packet */
! 211: m->m_flags &= ~(M_BCAST|M_MCAST|M_AUTH|M_CONF|M_AUTH_AH);
! 212: if (eh.ether_dhost[0] & 1) {
! 213: if (bcmp((caddr_t) etherbroadcastaddr,
! 214: (caddr_t)eh.ether_dhost, sizeof(etherbroadcastaddr)) == 0)
! 215: m->m_flags |= M_BCAST;
! 216: else
! 217: m->m_flags |= M_MCAST;
! 218: }
! 219:
! 220: #if NGIF > 0
! 221: /* Find appropriate gif(4) interface */
! 222: LIST_FOREACH(sc, &gif_softc_list, gif_list) {
! 223: if ((sc->gif_psrc == NULL) ||
! 224: (sc->gif_pdst == NULL) ||
! 225: !(sc->gif_if.if_flags & (IFF_UP|IFF_RUNNING)))
! 226: continue;
! 227:
! 228: if (!bcmp(sc->gif_psrc, &sdst, sc->gif_psrc->sa_len) &&
! 229: !bcmp(sc->gif_pdst, &ssrc, sc->gif_pdst->sa_len) &&
! 230: sc->gif_if.if_bridge != NULL)
! 231: break;
! 232: }
! 233:
! 234: /* None found. */
! 235: if (sc == NULL) {
! 236: DPRINTF(("etherip_input(): no interface found\n"));
! 237: etheripstat.etherip_noifdrops++;
! 238: m_freem(m);
! 239: return;
! 240: }
! 241: #if NBPFILTER > 0
! 242: if (sc->gif_if.if_bpf)
! 243: bpf_mtap_af(sc->gif_if.if_bpf, AF_LINK, m, BPF_DIRECTION_IN);
! 244: #endif
! 245:
! 246: /* Trim the beginning of the mbuf, to remove the ethernet header. */
! 247: m_adj(m, sizeof(struct ether_header));
! 248:
! 249: #if NBRIDGE > 0
! 250: /*
! 251: * Tap the packet off here for a bridge. bridge_input() returns
! 252: * NULL if it has consumed the packet. In the case of gif's,
! 253: * bridge_input() returns non-NULL when an error occurs.
! 254: */
! 255: m->m_pkthdr.rcvif = &sc->gif_if;
! 256: if (m->m_flags & (M_BCAST|M_MCAST))
! 257: sc->gif_if.if_imcasts++;
! 258:
! 259: s = splnet();
! 260: m = bridge_input(&sc->gif_if, &eh, m);
! 261: splx(s);
! 262: if (m == NULL)
! 263: return;
! 264: #endif /* NBRIDGE */
! 265: #endif /* NGIF */
! 266:
! 267: etheripstat.etherip_noifdrops++;
! 268: m_freem(m);
! 269: return;
! 270: }
! 271:
! 272: int
! 273: etherip_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
! 274: int protoff)
! 275: {
! 276: #ifdef INET
! 277: struct ip *ipo;
! 278: #endif /* INET */
! 279:
! 280: #ifdef INET6
! 281: struct ip6_hdr *ip6;
! 282: #endif /* INET6 */
! 283:
! 284: struct etherip_header eip;
! 285: struct mbuf *m0;
! 286: ushort hlen;
! 287:
! 288: /* Some address family sanity checks. */
! 289: if ((tdb->tdb_src.sa.sa_family != 0) &&
! 290: (tdb->tdb_src.sa.sa_family != AF_INET) &&
! 291: (tdb->tdb_src.sa.sa_family != AF_INET6)) {
! 292: DPRINTF(("etherip_output(): IP in protocol-family <%d> "
! 293: "attempted, aborting", tdb->tdb_src.sa.sa_family));
! 294: etheripstat.etherip_adrops++;
! 295: m_freem(m);
! 296: return EINVAL;
! 297: }
! 298:
! 299: if ((tdb->tdb_dst.sa.sa_family != AF_INET) &&
! 300: (tdb->tdb_dst.sa.sa_family != AF_INET6)) {
! 301: DPRINTF(("etherip_output(): IP in protocol-family <%d> "
! 302: "attempted, aborting", tdb->tdb_dst.sa.sa_family));
! 303: etheripstat.etherip_adrops++;
! 304: m_freem(m);
! 305: return EINVAL;
! 306: }
! 307:
! 308: if (tdb->tdb_dst.sa.sa_family != tdb->tdb_src.sa.sa_family) {
! 309: DPRINTF(("etherip_output(): mismatch in tunnel source and "
! 310: "destination address protocol families (%d/%d), aborting",
! 311: tdb->tdb_src.sa.sa_family, tdb->tdb_dst.sa.sa_family));
! 312: etheripstat.etherip_adrops++;
! 313: m_freem(m);
! 314: return EINVAL;
! 315: }
! 316:
! 317: switch (tdb->tdb_dst.sa.sa_family) {
! 318: #ifdef INET
! 319: case AF_INET:
! 320: hlen = sizeof(struct ip);
! 321: break;
! 322: #endif /* INET */
! 323: #ifdef INET6
! 324: case AF_INET6:
! 325: hlen = sizeof(struct ip6_hdr);
! 326: break;
! 327: #endif /* INET6 */
! 328: default:
! 329: DPRINTF(("etherip_output(): unsupported tunnel protocol "
! 330: "family <%d>, aborting", tdb->tdb_dst.sa.sa_family));
! 331: etheripstat.etherip_adrops++;
! 332: m_freem(m);
! 333: return EINVAL;
! 334: }
! 335:
! 336: /* Don't forget the EtherIP header. */
! 337: hlen += sizeof(struct etherip_header);
! 338:
! 339: if (!(m->m_flags & M_PKTHDR)) {
! 340: DPRINTF(("etherip_output(): mbuf is not a header\n"));
! 341: m_freem(m);
! 342: return (ENOBUFS);
! 343: }
! 344:
! 345: MGETHDR(m0, M_DONTWAIT, MT_DATA);
! 346: if (m0 == NULL) {
! 347: DPRINTF(("etherip_output(): M_GETHDR failed\n"));
! 348: etheripstat.etherip_adrops++;
! 349: m_freem(m);
! 350: return ENOBUFS;
! 351: }
! 352: M_MOVE_PKTHDR(m0, m);
! 353: m0->m_next = m;
! 354: m0->m_len = hlen;
! 355: m0->m_pkthdr.len += hlen;
! 356: m = m0;
! 357:
! 358: /* Statistics */
! 359: etheripstat.etherip_opackets++;
! 360: etheripstat.etherip_obytes += m->m_pkthdr.len - hlen;
! 361:
! 362: switch (tdb->tdb_dst.sa.sa_family) {
! 363: #ifdef INET
! 364: case AF_INET:
! 365: ipo = mtod(m, struct ip *);
! 366:
! 367: ipo->ip_v = IPVERSION;
! 368: ipo->ip_hl = 5;
! 369: ipo->ip_len = htons(m->m_pkthdr.len);
! 370: ipo->ip_ttl = ip_defttl;
! 371: ipo->ip_p = IPPROTO_ETHERIP;
! 372: ipo->ip_tos = 0;
! 373: ipo->ip_off = 0;
! 374: ipo->ip_sum = 0;
! 375: ipo->ip_id = htons(ip_randomid());
! 376:
! 377: /*
! 378: * We should be keeping tunnel soft-state and send back
! 379: * ICMPs as needed.
! 380: */
! 381:
! 382: ipo->ip_src = tdb->tdb_src.sin.sin_addr;
! 383: ipo->ip_dst = tdb->tdb_dst.sin.sin_addr;
! 384: break;
! 385: #endif /* INET */
! 386: #ifdef INET6
! 387: case AF_INET6:
! 388: ip6 = mtod(m, struct ip6_hdr *);
! 389:
! 390: ip6->ip6_flow = 0;
! 391: ip6->ip6_nxt = IPPROTO_ETHERIP;
! 392: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
! 393: ip6->ip6_vfc |= IPV6_VERSION;
! 394: ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
! 395: ip6->ip6_hlim = ip_defttl;
! 396: ip6->ip6_dst = tdb->tdb_dst.sin6.sin6_addr;
! 397: ip6->ip6_src = tdb->tdb_src.sin6.sin6_addr;
! 398: break;
! 399: #endif /* INET6 */
! 400: }
! 401:
! 402: /* Set the version number */
! 403: eip.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK;
! 404: eip.eip_pad = 0;
! 405: m_copyback(m, hlen - sizeof(struct etherip_header),
! 406: sizeof(struct etherip_header), &eip);
! 407:
! 408: *mp = m;
! 409:
! 410: return 0;
! 411: }
! 412:
! 413: int
! 414: etherip_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
! 415: int *name;
! 416: u_int namelen;
! 417: void *oldp, *newp;
! 418: size_t *oldlenp, newlen;
! 419: {
! 420: /* All sysctl names at this level are terminal. */
! 421: if (namelen != 1)
! 422: return (ENOTDIR);
! 423:
! 424: switch (name[0]) {
! 425: case ETHERIPCTL_ALLOW:
! 426: return (sysctl_int(oldp, oldlenp, newp, newlen,
! 427: ðerip_allow));
! 428: default:
! 429: return (ENOPROTOOPT);
! 430: }
! 431: /* NOTREACHED */
! 432: }
CVSweb