root/netinet/ip_ether.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. etherip_input
  2. etherip_output
  3. etherip_sysctl

    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                     &etherip_allow));
  428         default:
  429                 return (ENOPROTOOPT);
  430         }
  431         /* NOTREACHED */
  432 }

/* [<][>][^][v][top][bottom][index][help] */