/*
 * OLSR Basic Multicast Forwarding (BMF) plugin.
 * Copyright (c) 2005 - 2007, Thales Communications, Huizen, The Netherlands.
 * Written by Erik Tromp.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright 
 *   notice, this list of conditions and the following disclaimer in 
 *   the documentation and/or other materials provided with the 
 *   distribution.
 * * Neither the name of Thales, BMF nor the names of its 
 *   contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* -------------------------------------------------------------------------
 * File       : Bmf.c
 * Description: Multicast forwarding functions
 * Created    : 29 Jun 2006
 *
 * ------------------------------------------------------------------------- */

#define _MULTI_THREADED

#include "Bmf.h"

/* System includes */
#include <stddef.h> /* NULL */
#include <sys/types.h> /* ssize_t */
#include <string.h> /* strerror() */
#include <stdarg.h> /* va_list, va_start, va_end */
#include <errno.h> /* errno */
#include <assert.h> /* assert() */
#include <linux/if_ether.h> /* ETH_P_IP */
#include <linux/if_packet.h> /* struct sockaddr_ll, PACKET_MULTICAST */
#include <pthread.h> /* pthread_t, pthread_create() */
#include <signal.h> /* sigset_t, sigfillset(), sigdelset(), SIGINT */
#include <netinet/ip.h> /* struct ip */
#include <netinet/udp.h> /* struct udphdr */

/* OLSRD includes */
#include "defs.h" /* olsr_cnf, OLSR_PRINTF */
#include "olsr.h" /* olsr_printf */
#include "scheduler.h" /* olsr_register_scheduler_event */
#include "mid_set.h" /* mid_lookup_main_addr() */
#include "mpr_selector_set.h" /* olsr_lookup_mprs_set() */
#include "link_set.h" /* get_best_link_to_neighbor() */

/* BMF includes */
#include "NetworkInterfaces.h" /* TBmfInterface, CreateBmfNetworkInterfaces(), CloseBmfNetworkInterfaces() */
#include "Address.h" /* IsMulticast() */
#include "Packet.h" /* ENCAP_HDR_LEN, BMF_ENCAP_TYPE, BMF_ENCAP_LEN etc. */
#include "PacketHistory.h" /* InitPacketHistory() */

static pthread_t BmfThread;
static int BmfThreadRunning = 0;

/* -------------------------------------------------------------------------
 * Function   : BmfPError
 * Description: Prints an error message at OLSR debug level 1.
 *              First the plug-in name is printed. Then (if format is not NULL
 *              and *format is not empty) the arguments are printed, followed
 *              by a colon and a blank. Then the message and a new-line.
 * Input      : format, arguments
 * Output     : none
 * Return     : none
 * Data Used  : none
 * ------------------------------------------------------------------------- */
void BmfPError(char* format, ...)
{
#define MAX_STR_DESC 255
  char* strErr = strerror(errno);
  char strDesc[MAX_STR_DESC];

  /* Rely on short-circuit boolean evaluation */
  if (format == NULL || *format == '\0')
  {
    olsr_printf(1, "%s: %s\n", PLUGIN_NAME, strErr);
  }
  else
  {
    va_list arglist;

    olsr_printf(1, "%s: ", PLUGIN_NAME);

    va_start(arglist, format);
    vsnprintf(strDesc, MAX_STR_DESC, format, arglist);
    va_end(arglist);

    strDesc[MAX_STR_DESC - 1] = '\0'; /* Ensures null termination */

    olsr_printf(1, "%s: %s\n", strDesc, strErr);
  }
} /* BmfPError */

/* -------------------------------------------------------------------------
 * Function   : MainAddressOf
 * Description: Lookup the main address of a node
 * Input      : ip - IP address of the node
 * Output     : none
 * Return     : The main IP address of the node
 * Data Used  : none
 * ------------------------------------------------------------------------- */
union olsr_ip_addr* MainAddressOf(union olsr_ip_addr* ip)
{
  union olsr_ip_addr* result;

  /* TODO: mid_lookup_main_addr() is not thread-safe! */
  result = mid_lookup_main_addr(ip);
  if (result == NULL)
  {
    result = ip;
  }
  return result;
} /* MainAddressOf */

/* -------------------------------------------------------------------------
 * Function   : EncapsulateAndForwardPacket
 * Description: Encapsulate a captured raw IP packet and forward it
 * Input      : intf - the network interface on which to forward the packet
 *              encapsulationUdpData - The encapsulation header, followed by
 *                the encapsulated IP packet
 * Output     : none
 * Return     : none
 * Data Used  : none
 * ------------------------------------------------------------------------- */
static void EncapsulateAndForwardPacket(
  struct TBmfInterface* intf,
  unsigned char* encapsulationUdpData)
{
  /* The packet */
  u_int16_t udpDataLen = GetEncapsulationUdpDataLength(encapsulationUdpData);

  /* The next destination(s) */
  struct TBestNeighbors bestNeighborLinks;
  int nPossibleNeighbors;
  struct sockaddr_in forwardTo; /* Next destination of encapsulation packet */

  int nPacketsToSend;
  int nBytesWritten;
  int i;

  /* Retrieve at most two best neigbors to forward the packet to */
  GetBestTwoNeighbors(&bestNeighborLinks, intf, NULL, NULL, NULL, &nPossibleNeighbors);

  if (nPossibleNeighbors <= 0)
  {
    OLSR_PRINTF(
      8,
      "%s: --> not encap-forwarding on \"%s\": there is no neighbor that needs my retransmission\n",
      PLUGIN_NAME_SHORT,
      intf->ifName);
    return;
  }

  /* Compose destination of encapsulation packet */

  memset(&forwardTo, 0, sizeof(forwardTo));
  forwardTo.sin_family = AF_INET;
  forwardTo.sin_port = htons(BMF_ENCAP_PORT);

  /* Start by filling in the local broadcast address */
  COPY_IP(&forwardTo.sin_addr.s_addr, &intf->broadAddr);

  /* - If the BMF mechanism is BM_UNICAST_PROMISCUOUS, always send just one
   *   packet (to the best neighbor).
   * - If the BMF mechanism is BM_BROADCAST,
   *   - send one unicast packet if there is one possible neighbor,
   *   - send two unicast packets if there are two possible neighbors, and
   *   - only if there are more than two possible neighbors, then send an
   *     (WLAN-air-expensive, less reliable) broadcast packet. */
  if (BmfMechanism == BM_UNICAST_PROMISCUOUS || nPossibleNeighbors < 2)
  {
    nPacketsToSend = 1;
  }
  else /* BmfMechanism == BM_BROADCAST && nPossibleNeighbors >= 2 */
  {
    nPacketsToSend = 2;
  }

  for (i = 0; i < nPacketsToSend; i++)
  {
    if (BmfMechanism == BM_UNICAST_PROMISCUOUS || nPossibleNeighbors <= 2)
    {
      COPY_IP(&forwardTo.sin_addr.s_addr, &bestNeighborLinks.links[i]->neighbor_iface_addr);
    }

    /* Forward the BMF packet via the encapsulation socket */
    nBytesWritten = sendto(
      intf->encapsulatingSkfd,
      encapsulationUdpData,
      udpDataLen,
      MSG_DONTROUTE,
      (struct sockaddr*) &forwardTo,
      sizeof(forwardTo));                   

    /* Evaluate and display result */
    if (nBytesWritten != udpDataLen)
    {
      BmfPError("sendto() error forwarding pkt on \"%s\"", intf->ifName);
    }
    else
    {
      /* Increase counter */
      intf->nBmfPacketsTx++;

      OLSR_PRINTF(
        8,
        "%s: --> encapsulated and forwarded on \"%s\" to %s\n",
        PLUGIN_NAME_SHORT,
        intf->ifName,
        inet_ntoa(forwardTo.sin_addr));
    } /* if (nBytesWritten != udpDataLen) */
  } /* for */
} /* EncapsulateAndForwardPacket */

/* -------------------------------------------------------------------------
 * Function   : BmfPacketCaptured
 * Description: Handle a captured IP packet
 * Input      : intf - the network interface on which the packet was captured
 *              sllPkttype - the type of packet. Either PACKET_OUTGOING,
 *                PACKET_BROADCAST or PACKET_MULTICAST.
 *              encapsulationUdpData - space for the encapsulation header, followed by
 *                the captured IP packet
 * Output     : none
 * Return     : none
 * Data Used  : BmfInterfaces
 * Notes      : The IP packet is assumed to be captured on a socket of family
 *              PF_PACKET and type SOCK_DGRAM (cooked).
 * ------------------------------------------------------------------------- */
static void BmfPacketCaptured(
  struct TBmfInterface* intf,
  unsigned char sllPkttype,
  unsigned char* encapsulationUdpData)
{
  union olsr_ip_addr src; /* Source IP address in captured packet */
  union olsr_ip_addr dst; /* Destination IP address in captured packet */
  union olsr_ip_addr* origIp; /* Main OLSR address of source of captured packet */
  struct TBmfInterface* walker;
  int isFromOlsrIntf;
  int isFromOlsrNeighbor;
  int iAmMpr;
  unsigned char* ipPacket; /* The captured IP packet... */
  u_int16_t ipPacketLen; /* ...and its length */
  struct ip* ipHeader; /* The IP header inside the captured IP packet */
  u_int32_t crc32;
  struct TEncapHeader* encapHdr;

  ipHeader = GetIpHeader(encapsulationUdpData);

  COPY_IP(&dst, &ipHeader->ip_dst);

  /* Only forward multicast packets. If configured, also forward local broadcast packets */
  if (IsMulticast(&dst) ||
      (EnableLocalBroadcast != 0 && COMP_IP(&dst, &intf->broadAddr)))
  {
    /* continue */
  }
  else
  {
    return;
  }

  ipPacket = GetIpPacket(encapsulationUdpData);

  /* Don't forward fragments of IP packets. Also, don't forward OLSR packets (UDP
   * port 698) and BMF encapsulated packets */
  if (IsIpFragment(ipPacket) || IsOlsrOrBmfPacket(ipPacket))
  {
    return;
  }

  /* Increase counter */
  intf->nBmfPacketsRx++;

  /* Check if the frame is captured on an OLSR-enabled interface */
  isFromOlsrIntf = (intf->olsrIntf != NULL);

  /* Retrieve the length of the captured packet */
  ipPacketLen = GetIpTotalLength(ipPacket);

  COPY_IP(&src, &ipHeader->ip_src);
  OLSR_PRINTF(
    8,
    "%s: %s pkt of %ld bytes captured on %s interface \"%s\": %s->%s\n",
    PLUGIN_NAME_SHORT,
    sllPkttype == PACKET_OUTGOING ? "outgoing" : "incoming",
    (long)ipPacketLen,
    isFromOlsrIntf ? "OLSR" : "non-OLSR",
    intf->ifName,
    olsr_ip_to_string(&src),
    olsr_ip_to_string(&dst));

  /* Lookup main address of source in the MID table of OLSR */
  origIp = MainAddressOf(&src);

  /* Calculate packet fingerprint */
  crc32 = PacketCrc32(ipPacket, ipPacketLen);

  /* Check if this packet was seen recently */
  if (CheckAndMarkRecentPacket(crc32))
  {
    /* Increase counter */
    intf->nBmfPacketsRxDup++;

    OLSR_PRINTF(
      8,
      "%s: --> discarding: packet is duplicate\n",
      PLUGIN_NAME_SHORT);
    return;
  }

  /* Compose encapsulation header */
  encapHdr = (struct TEncapHeader*) encapsulationUdpData;
  memset (encapHdr, 0, ENCAP_HDR_LEN);
  encapHdr->type = BMF_ENCAP_TYPE;
  encapHdr->len = BMF_ENCAP_LEN;
  encapHdr->reserved = 0;
  encapHdr->crc32 = htonl(crc32);

  /* Check if the frame is captured on an OLSR interface from an OLSR neighbor.
   * TODO1: get_best_link_to_neighbor() is not thread-safe.
   * TODO2: get_best_link_to_neighbor() may be very CPU-expensive, a simpler call
   * would do here (something like 'get_any_link_to_neighbor()'). */
  isFromOlsrNeighbor =
    (isFromOlsrIntf /* The frame is captured on an OLSR interface... */
    && get_best_link_to_neighbor(origIp) != NULL); /* ...from an OLSR neighbor */ 

  /* Check with OLSR if I am MPR for that neighbor */
  /* TODO: olsr_lookup_mprs_set() is not thread-safe! */
  iAmMpr = olsr_lookup_mprs_set(origIp) != NULL;

  /* Check with each network interface what needs to be done on it */
  for (walker = BmfInterfaces; walker != NULL; walker = walker->next)
  {
    /* Is the forwarding interface OLSR-enabled? */
    int isToOlsrIntf = (walker->olsrIntf != NULL);

    /* Depending on certain conditions, we decide whether or not to forward
     * the packet, and if it is forwarded, in which form (encapsulated
     * or not, TTL decreased or not). These conditions are:
     * - is the packet is coming in on an OLSR interface or not? (isFromOlsrIntf)
     * - is the packet going out on an OLSR interface or not? (isToOlsrIntf)
     * - if the packet if coming in on an OLSR interface:
     *   - is the node that forwarded the packet my OLSR-neighbor? (isFromOlsrNeighbor)
     *   - has the node that forwarded the packet selected me as MPR? (iAmMpr)
     *
     * Based on these conditions, the following cases can be distinguished:
     *
     * - Case 1: Packet coming in on an OLSR interface. What to
     *   do with it on an OLSR interface?
     *   Answer:
     *   - Case 1.1: If the forwarding node is an OLSR neighbor that has *not*
     *     selected me as MPR: don't forward the packet.
     *   - Case 1.2: If the forwarding node is an OLSR neighbor that has selected
     *     me as MPR: encapsulate and forward the packet.
     *   - Case 1.3: If the forwarding node is not an OLSR neighbor: encapsulate
     *     and forward the packet.
     *     NOTE: Case 1.3 is a special case. In the perfect world, we expect to
     *     see only OLSR-neighbors on OLSR-enabled interfaces. Sometimes, however,
     *     ignorant users will connect a host not running OLSR, to a LAN in
     *     which there are hosts running OLSR. Of course these ignorant users,
     *     expecting magic, want to see their multicast packets being forwarded
     *     through the network.
     *
     * - Case 2: Packet coming in on an OLSR interface. What to do with it on a
     *   non-OLSR interface?
     *   Answer: Forward it.
     *
     * - Case 3: Packet coming in on a non-OLSR interface. What to
     *   do with it on an OLSR interface?
     *   Answer: Encapsulate and forward it.
     *
     * - Case 4: Packet coming in on non-OLSR interface. What to do with it on a
     *   non-OLSR interface?
     *   Answer 1: nothing. Multicast routing between non-OLSR interfaces
     *   is to be done by other protocols (e.g. PIM, DVMRP).
     *   Answer 2 (better): Forward it.
     */

    if (isFromOlsrIntf && isToOlsrIntf)
    {
      /* Case 1: Forward from an OLSR interface to an OLSR interface */

      if (isFromOlsrNeighbor && !iAmMpr)
      {
        /* Case 1.1 */
        {
          OLSR_PRINTF(
            8,
            "%s: --> not encap-forwarding on \"%s\": I am not selected as MPR by neighbor %s\n",
            PLUGIN_NAME_SHORT,
            walker->ifName,
            olsr_ip_to_string(&src));
        }    
      }
      else if (sllPkttype == PACKET_OUTGOING && intf == walker)
      {
        OLSR_PRINTF(
          8,
          "%s: --> not encap-forwarding on \"%s\": pkt was captured on that interface\n",
          PLUGIN_NAME_SHORT,
          walker->ifName);
      }
      else
      {
        /* Case 1.2 and 1.3 */
        EncapsulateAndForwardPacket(walker, encapsulationUdpData);
      }
    } /* if (isFromOlsrIntf && isToOlsrIntf) */

    else if (isFromOlsrIntf && !isToOlsrIntf)
    {
      /* Case 2: Forward from OLSR interface to non-OLSR interface */

      int nBytesWritten;
      struct sockaddr_ll dest;

      /* If the encapsulated IP packet is a local broadcast packet,
       * update its destination address to match the subnet of the network
       * interface on which the packet is being sent. */
      CheckAndUpdateLocalBroadcast(ipPacket, &walker->broadAddr);

      memset(&dest, 0, sizeof(dest));
      dest.sll_family = AF_PACKET;
      dest.sll_protocol = htons(ETH_P_IP);
      dest.sll_ifindex = if_nametoindex(walker->ifName);
      dest.sll_halen = IFHWADDRLEN;

      /* Use all-ones as destination MAC address. When the IP destination is
       * a multicast address, the destination MAC address should normally also
       * be a multicast address. E.g., when the destination IP is 224.0.0.1,
       * the destination MAC should be 01:00:5e:00:00:01. However, it does not
       * seem to matter when the destination MAC address is set to all-ones
       * in that case. */
      memset(dest.sll_addr, 0xFF, IFHWADDRLEN);

      nBytesWritten = sendto(
        walker->capturingSkfd,
        ipPacket,
        ipPacketLen,
        0,
        (struct sockaddr*) &dest,
        sizeof(dest));
      if (nBytesWritten != ipPacketLen)
      {
        BmfPError("sendto() error forwarding pkt on \"%s\"", walker->ifName);
      }
      else
      {
        /* Increase counter */
        walker->nBmfPacketsTx++;

        OLSR_PRINTF(8, "%s: --> forwarded on \"%s\"\n", PLUGIN_NAME_SHORT, walker->ifName);
      }
    } /* else if (isFromOlsrIntf && !isToOlsrIntf) */

    else if (!isFromOlsrIntf && isToOlsrIntf)
    {
      /* Case 3: Forward from a non-OLSR interface to an OLSR interface.
       * Encapsulate and forward packet. */

      EncapsulateAndForwardPacket(walker, encapsulationUdpData);
    } /* else if (!isFromOlsrIntf && isToOlsrIntf) */

    else
    {
      /* Case 4: Forward from non-OLSR interface to non-OLSR interface. */

      /* Don't forward on interface on which packet was received */
      if (intf == walker)
      {
        OLSR_PRINTF(
          8,
          "%s: --> not forwarding on \"%s\": pkt was captured on that interface\n",
          PLUGIN_NAME_SHORT,
          walker->ifName);
      }

      else
      {
        int nBytesWritten;
        struct sockaddr_ll dest;

        /* If the encapsulated IP packet is a local broadcast packet,
         * update its destination address to match the subnet of the network
         * interface on which the packet is being sent. */
        CheckAndUpdateLocalBroadcast(ipPacket, &walker->broadAddr);

        memset(&dest, 0, sizeof(dest));
        dest.sll_family = AF_PACKET;
        dest.sll_protocol = htons(ETH_P_IP);
        dest.sll_ifindex = if_nametoindex(walker->ifName);
        dest.sll_halen = IFHWADDRLEN;

        /* Use all-ones as destination MAC address. When the IP destination is
         * a multicast address, the destination MAC address should normally also
         * be a multicast address. E.g., when the destination IP is 224.0.0.1,
         * the destination MAC should be 01:00:5e:00:00:01. However, it does not
         * seem to matter when the destination MAC address is set to all-ones
         * in that case. */
        memset(dest.sll_addr, 0xFF, IFHWADDRLEN);

        nBytesWritten = sendto(
          walker->capturingSkfd,
          ipPacket,
          ipPacketLen,
          0,
          (struct sockaddr*) &dest,
          sizeof(dest));
        if (nBytesWritten != ipPacketLen)
        {
          BmfPError("sendto() error forwarding pkt on \"%s\"", walker->ifName);
        }
        else
        {
          /* Increase counter */
          walker->nBmfPacketsTx++;

          OLSR_PRINTF(
            8,
            "%s: --> forwarded from non-OLSR on non-OLSR \"%s\"\n",
            PLUGIN_NAME_SHORT,
            walker->ifName);
        }
      } /* if (intf == walker) */
    } /* if */
  } /* for */
} /* BmfPacketCaptured */

/* -------------------------------------------------------------------------
 * Function   : BmfEncapsulationPacketReceived
 * Description: Handle a received BMF-encapsulation packet
 * Input      : intf - the network interface on which the packet was received
 *              forwardedBy - the IP node that forwarded the packet to me
 *              forwardedTo - the destination IP address of the encapsulation
 *                packet, in case the packet was received promiscuously.
 *                Pass NULL if the packet is received normally (unicast or
 *                broadcast).
 *              encapsulationUdpData - the encapsulating IP UDP data, containting
 *                the BMF encapsulation header, followed by the encapsulated
 *                IP packet
 * Output     : none
 * Return     : none
 * Data Used  : BmfInterfaces
 * ------------------------------------------------------------------------- */
static void BmfEncapsulationPacketReceived(
  struct TBmfInterface* intf,
  union olsr_ip_addr* forwardedBy,
  union olsr_ip_addr* forwardedTo,
  unsigned char* encapsulationUdpData)
{
  int iAmMpr; /* True (1) if I am selected as MPR by 'forwardedBy' */
  struct sockaddr_in forwardTo; /* Next destination of encapsulation packet */
  unsigned char* ipPacket; /* The encapsulated IP packet */
  u_int16_t ipPacketLen; /* Length of the encapsulated IP packet */
  struct ip* ipHeader; /* IP header inside the encapsulated IP packet */
  union olsr_ip_addr mcSrc; /* Original source of the encapsulated multicast packet */
  union olsr_ip_addr mcDst; /* Multicast destination of the encapsulated packet */
  struct TEncapHeader* encapsulationHdr;
  u_int16_t encapsulationUdpDataLen;
  struct TBmfInterface* walker;

  /* Are we talking to ourselves? */
  if (if_ifwithaddr(forwardedBy) != NULL)
  {
    return;
  }

  /* Discard encapsulated packets received on a non-OLSR interface */
  if (intf->olsrIntf == NULL)
  {
    return;
  }

  /* Retrieve details about the encapsulated IP packet */
  ipPacket = GetIpPacket(encapsulationUdpData);
  ipPacketLen = GetIpTotalLength(ipPacket);
  ipHeader = GetIpHeader(encapsulationUdpData);

  COPY_IP(&mcSrc, &ipHeader->ip_src);
  COPY_IP(&mcDst, &ipHeader->ip_dst);

  /* Increase counter */
  intf->nBmfPacketsRx++;

  /* Beware: not possible to call olsr_ip_to_string more than 4 times in same printf */
  OLSR_PRINTF(
    8,
    "%s: encapsulated pkt of %ld bytes incoming on \"%s\": %s->%s, forwarded by %s to %s\n",
    PLUGIN_NAME_SHORT,
    (long)ipPacketLen,
    intf->ifName,
    olsr_ip_to_string(&mcSrc),
    olsr_ip_to_string(&mcDst),
    olsr_ip_to_string(forwardedBy),
    forwardedTo != NULL ? olsr_ip_to_string(forwardedTo) : "me");

  /* Get encapsulation header */
  encapsulationHdr = (struct TEncapHeader*) encapsulationUdpData;

  /* Verify correct format of BMF encapsulation header */
  if (encapsulationHdr->type != BMF_ENCAP_TYPE ||
      encapsulationHdr->len != BMF_ENCAP_LEN ||
      ntohs(encapsulationHdr->reserved != 0))
  {
    OLSR_PRINTF(
      8,
      "%s: --> discarding: format of BMF encapsulation header not recognized\n",
      PLUGIN_NAME_SHORT);
    return;
  }

  /* Check if this packet was seen recently */
  if (CheckAndMarkRecentPacket(ntohl(encapsulationHdr->crc32)))
  {
    /* Increase counter */
    intf->nBmfPacketsRxDup++;

    OLSR_PRINTF(
      8,
      "%s: --> discarding: packet is duplicate\n",
      PLUGIN_NAME_SHORT);
    return;
  }

  if (EtherTunTapFd >= 0)
  {
    /* Unpack the encapsulated IP packet and deliver it locally, by sending
     * a copy into the local IP stack via the EtherTunTap interface */

    union olsr_ip_addr broadAddr;
    int nBytesToWrite, nBytesWritten;
    unsigned char* bufferToWrite;

    /* If the encapsulated IP packet is a local broadcast packet,
     * update its destination address to match the subnet of the EtherTunTap
     * interface */
    broadAddr.v4 = htonl(EtherTunTapIpBroadcast);
    CheckAndUpdateLocalBroadcast(ipPacket, &broadAddr);

    bufferToWrite = ipPacket;
    nBytesToWrite = ipPacketLen;

    /* Write the packet into the EtherTunTap interface for local delivery */
    nBytesWritten = write(EtherTunTapFd, bufferToWrite, nBytesToWrite);
    if (nBytesWritten != nBytesToWrite)
    {
      BmfPError("write() error forwarding encapsulated pkt on \"%s\"", EtherTunTapIfName);
    }
    else
    {
      OLSR_PRINTF(
        8,
        "%s: --> unpacked and delivered locally on \"%s\"\n",
        PLUGIN_NAME_SHORT,
        EtherTunTapIfName);
    }
  } /* if (EtherTunTapFd >= 0) */

  /* Check if I am MPR for the forwarder */
  /* TODO: olsr_lookup_mprs_set() is not thread-safe! */
  iAmMpr = (olsr_lookup_mprs_set(MainAddressOf(forwardedBy)) != NULL);

  /* Compose destination address for next hop */
  memset(&forwardTo, 0, sizeof(forwardTo));
  forwardTo.sin_family = AF_INET;
  forwardTo.sin_port = htons(BMF_ENCAP_PORT);

  /* Retrieve the number of bytes to be forwarded via the encapsulation socket */
  encapsulationUdpDataLen = GetEncapsulationUdpDataLength(encapsulationUdpData);

  /* Check with each network interface what needs to be done on it */
  for (walker = BmfInterfaces; walker != NULL; walker = walker->next)
  {
    /* What to do with the packet on a non-OLSR interface? Unpack
     * encapsulated packet, and forward it.
     *
     * What to do with the packet on an OLSR interface? Forward it only
     * if the forwarding node has selected us as MPR (iAmMpr).
     *
     * Note that the packet is always coming in on an OLSR interface, because
     * it is an encapsulated BMF packet. */

    /* To a non-OLSR interface: unpack the encapsulated IP packet and forward it */
    if (walker->olsrIntf == NULL)
    {
      int nBytesWritten;
      struct sockaddr_ll dest;

      /* If the encapsulated IP packet is a local broadcast packet,
       * update its destination address to match the subnet of the network
       * interface on which the packet is being sent. */
      CheckAndUpdateLocalBroadcast(ipPacket, &walker->broadAddr);

      memset(&dest, 0, sizeof(dest));
      dest.sll_family = AF_PACKET;
      dest.sll_protocol = htons(ETH_P_IP);
      dest.sll_ifindex = if_nametoindex(walker->ifName);
      dest.sll_halen = IFHWADDRLEN;

      /* Use all-ones as destination MAC address. When the IP destination is
       * a multicast address, the destination MAC address should normally also
       * be a multicast address. E.g., when the destination IP is 224.0.0.1,
       * the destination MAC should be 01:00:5e:00:00:01. However, it does not
       * seem to matter when the destination MAC address is set to all-ones
       * in that case. */
      memset(dest.sll_addr, 0xFF, IFHWADDRLEN);

      nBytesWritten = sendto(
        walker->capturingSkfd,
        ipPacket,
        ipPacketLen,
        0,
        (struct sockaddr*) &dest,
        sizeof(dest));
      if (nBytesWritten != ipPacketLen)
      {
        BmfPError("sendto() error forwarding unpacked encapsulated pkt on \"%s\"", walker->ifName);
      }
      else
      {
        /* Increase counter */
        walker->nBmfPacketsTx++;

        OLSR_PRINTF(
          8,
          "%s: --> unpacked and forwarded on \"%s\"\n",
          PLUGIN_NAME_SHORT,
          walker->ifName);
      }
    } /* if (walker->olsrIntf == NULL) */

    /* To an OLSR interface: forward the packet, but only if this node is
     * selected as MPR by the forwarding node */
    else if (iAmMpr)
    {
      struct TBestNeighbors bestNeighborLinks;
      int nPossibleNeighbors;
      int nBytesWritten;
      int nPacketsToSend;
      int i;

      /* Retrieve at most two best neigbors to forward the packet to */
      GetBestTwoNeighbors(
        &bestNeighborLinks,
        walker,
        &mcSrc,
        forwardedBy,
        forwardedTo,
        &nPossibleNeighbors);

      if (nPossibleNeighbors <= 0)
      {
        OLSR_PRINTF(
          8,
          "%s: --> not forwarding on \"%s\": there is no neighbor that needs my retransmission\n",
          PLUGIN_NAME_SHORT,
          walker->ifName);

        continue; /* for */
      }

      /* Compose destination of encapsulation packet.
       * Start by filling in the local broadcast address. */
      COPY_IP(&forwardTo.sin_addr.s_addr, &walker->broadAddr);

      /* - If the BMF mechanism is BM_UNICAST_PROMISCUOUS, always send just one
       *   packet (to the best neighbor). Other neighbors listen promiscuously.
       * - If the BMF mechanism is BM_BROADCAST,
       *   - send one unicast packet if there is one possible neighbor,
       *   - send two unicast packets if there are two possible neighbors, and
       *   - only if there are more than two possible neighbors, then send an
       *     (WLAN-air-expensive, less reliable) broadcast packet. */
      if (BmfMechanism == BM_UNICAST_PROMISCUOUS || nPossibleNeighbors < 2)
      {
        nPacketsToSend = 1;
      }
      else /* BmfMechanism == BM_BROADCAST && nPossibleNeighbors >= 2 */
      {
        nPacketsToSend = 2;
      }

      for (i = 0; i < nPacketsToSend; i++)
      {
        if (BmfMechanism == BM_UNICAST_PROMISCUOUS || nPossibleNeighbors <= 2)
        {
          /* For unicast, overwrite the local broadcast address which was filled in
           * above */
          COPY_IP(&forwardTo.sin_addr.s_addr, &bestNeighborLinks.links[i]->neighbor_iface_addr);
        }

        /* Forward the BMF packet via the encapsulation socket */
        nBytesWritten = sendto(
          walker->encapsulatingSkfd,
          encapsulationUdpData,
          encapsulationUdpDataLen,
          MSG_DONTROUTE,
          (struct sockaddr*) &forwardTo,
          sizeof(forwardTo));                   

        /* Evaluate and display result */
        if (nBytesWritten != encapsulationUdpDataLen)
        {
          BmfPError("sendto() error forwarding encapsulated pkt on \"%s\"", walker->ifName);
        }
        else
        {
          /* Increase counter */
          walker->nBmfPacketsTx++;

          OLSR_PRINTF(
            8,
            "%s: --> forwarded on \"%s\" to %s\n",
            PLUGIN_NAME_SHORT,
            walker->ifName,
            inet_ntoa(forwardTo.sin_addr));
        } /* if */
      } /* for */
    }  /* else if (iAmMpr) */

    else /* walker->olsrIntf != NULL && !iAmMpr */
    {
      /* 'walker' is an OLSR interface, but I am not selected as MPR. In that
       * case, don't forward. */
      OLSR_PRINTF(
        8,
        "%s: --> not forwarding on \"%s\": I am not selected as MPR by %s\n",
        PLUGIN_NAME_SHORT,
        walker->ifName,
        olsr_ip_to_string(forwardedBy));
    } /* else */
  } /* for */
} /* BmfEncapsulationPacketReceived */

/* -------------------------------------------------------------------------
 * Function   : BmfTunPacketCaptured
 * Description: Handle an IP packet, captured outgoing on the tuntap interface
 * Input      : encapsulationUdpData - space for the encapsulation header, followed by
 *                the captured outgoing IP packet
 * Output     : none
 * Return     : none
 * Data Used  : none
 * Notes      : The packet is assumed to be captured on a socket of family
 *              PF_PACKET and type SOCK_DGRAM (cooked).
 * ------------------------------------------------------------------------- */
static void BmfTunPacketCaptured(unsigned char* encapsulationUdpData)
{
  union olsr_ip_addr srcIp;
  union olsr_ip_addr dstIp;
  union olsr_ip_addr broadAddr;
  struct TBmfInterface* walker;
  unsigned char* ipPacket;
  u_int16_t ipPacketLen;
  struct ip* ipHeader;
  u_int32_t crc32;
  struct TEncapHeader* encapHdr;

  ipPacket = GetIpPacket(encapsulationUdpData);
  ipPacketLen = GetIpTotalLength(ipPacket);
  ipHeader = GetIpHeader(encapsulationUdpData);

  /* Only forward multicast packets. If configured, also forward local broadcast packets */
  COPY_IP(&dstIp, &ipHeader->ip_dst);
  broadAddr.v4 = htonl(EtherTunTapIpBroadcast);
  if (IsMulticast(&dstIp) ||
      (EnableLocalBroadcast != 0 && COMP_IP(&dstIp, &broadAddr)))
  {
    /* continue */
  }
  else
  {
    return;
  }

  COPY_IP(&srcIp, &ipHeader->ip_src);
  OLSR_PRINTF(
    8,
    "%s: outgoing pkt of %ld bytes captured on tuntap interface \"%s\": %s->%s\n",
    PLUGIN_NAME_SHORT,
    (long)ipPacketLen,
    EtherTunTapIfName,
    olsr_ip_to_string(&srcIp),
    olsr_ip_to_string(&dstIp));

  /* Calculate packet fingerprint */
  crc32 = PacketCrc32(ipPacket, ipPacketLen);

  /* Check if this packet was seen recently */
  if (CheckAndMarkRecentPacket(crc32))
  {
    OLSR_PRINTF(
      8,
      "%s: --> discarding: packet is duplicate\n",
      PLUGIN_NAME_SHORT);
    return;
  }

  /* Compose encapsulation header */
  encapHdr = (struct TEncapHeader*) encapsulationUdpData;
  memset (encapHdr, 0, ENCAP_HDR_LEN);
  encapHdr->type = BMF_ENCAP_TYPE;
  encapHdr->len = BMF_ENCAP_LEN;
  encapHdr->reserved = 0;
  encapHdr->crc32 = htonl(crc32);

  /* Check with each network interface what needs to be done on it */
  for (walker = BmfInterfaces; walker != NULL; walker = walker->next)
  {
    /* Is the forwarding interface OLSR-enabled? */
    if (walker->olsrIntf != NULL)
    {
      /* On an OLSR interface: encapsulate and forward packet. */

      EncapsulateAndForwardPacket(walker, encapsulationUdpData);
    }
    else
    {
      /* On a non-OLSR interface: what to do?
       * Answer 1: nothing. Multicast routing between non-OLSR interfaces
       * is to be done by other protocols (e.g. PIM, DVMRP).
       * Answer 2 (better): Forward it. */

      int nBytesWritten;
      struct sockaddr_ll dest;

      /* If the encapsulated IP packet is a local broadcast packet,
       * update its destination address to match the subnet of the network
       * interface on which the packet is being sent. */
      CheckAndUpdateLocalBroadcast(ipPacket, &walker->broadAddr);

      memset(&dest, 0, sizeof(dest));
      dest.sll_family = AF_PACKET;
      dest.sll_protocol = htons(ETH_P_IP);
      dest.sll_ifindex = if_nametoindex(walker->ifName);
      dest.sll_halen = IFHWADDRLEN;

      /* Use all-ones as destination MAC address. When the IP destination is
       * a multicast address, the destination MAC address should normally also
       * be a multicast address. E.g., when the destination IP is 224.0.0.1,
       * the destination MAC should be 01:00:5e:00:00:01. However, it does not
       * seem to matter when the destination MAC address is set to all-ones
       * in that case. */
      memset(dest.sll_addr, 0xFF, IFHWADDRLEN);

      nBytesWritten = sendto(
        walker->capturingSkfd,
        ipPacket,
        ipPacketLen,
        0,
        (struct sockaddr*) &dest,
        sizeof(dest));
      if (nBytesWritten != ipPacketLen)
      {
        BmfPError("sendto() error forwarding pkt on \"%s\"", walker->ifName);
      }
      else
      {
        /* Increase counter */
        walker->nBmfPacketsTx++;

        OLSR_PRINTF(
          8,
          "%s: --> forwarded from non-OLSR to non-OLSR \"%s\"\n",
          PLUGIN_NAME_SHORT,
          walker->ifName);
      } /* if */
    } /* if */
  } /* for */
} /* BmfTunPacketCaptured */

/* -------------------------------------------------------------------------
 * Function   : DoBmf
 * Description: Wait (blocking) for IP packets, then call the handler for each
 *              received packet
 * Input      : none
 * Output     : none
 * Return     : none
 * Data Used  : BmfInterfaces
 * ------------------------------------------------------------------------- */
static void DoBmf(void)
{
  int nFdBitsSet;
  unsigned char rxBuffer[BMF_BUFFER_SIZE];
  fd_set rxFdSet;

  assert(HighestSkfd >= 0);

  /* Make a local copy of the set of file descriptors that select() can
   * modify to indicate which descriptors actually changed status */
  rxFdSet = InputSet;

  /* Wait (blocking) for packets received on any of the sockets.
   * NOTE: don't use a timeout (last parameter). It causes a high system CPU load! */
  nFdBitsSet = select(HighestSkfd + 1, &rxFdSet, NULL, NULL, NULL);
  if (nFdBitsSet < 0)
  {
    if (errno != EINTR)
    {
      BmfPError("select() error");
    }
    return;
  }

  while (nFdBitsSet > 0)
  {
    struct TBmfInterface* walker;

    /* Check if a packet was received on the capturing socket (if any)
     * of each network interface */
    for (walker = BmfInterfaces; walker != NULL; walker = walker->next)
    {
      int skfd = walker->capturingSkfd;
      if (skfd >= 0 && (FD_ISSET(skfd, &rxFdSet)))
      {
        struct sockaddr_ll pktAddr;
        socklen_t addrLen = sizeof(pktAddr);
        int nBytes;
        unsigned char* ipPacket;

        /* A packet was captured. */

        nFdBitsSet--;

        /* Receive the captured Ethernet frame, leaving space for the BMF
         * encapsulation header */
        ipPacket = GetIpPacket(rxBuffer);
        nBytes = recvfrom(
          skfd,
          ipPacket,
          BMF_BUFFER_SIZE - ENCAP_HDR_LEN,
          0,
          (struct sockaddr*)&pktAddr,
          &addrLen);
        if (nBytes < 0)
        {
          BmfPError("recvfrom() error on \"%s\"", walker->ifName);

          continue; /* for */
        } /* if (nBytes < 0) */

        /* Check if the number of received bytes is large enough for an IP
         * packet which contains at least a minimum-size IP header.
         * Note: There is an apparent bug in the packet socket implementation in
         * combination with VLAN interfaces. On a VLAN interface, the value returned
         * by 'recvfrom' may (but need not) be 4 (bytes) larger than the value
         * returned on a non-VLAN interface, for the same ethernet frame. */
        if (nBytes < (int)sizeof(struct ip))
        {
          olsr_printf(
            1,
            "%s: captured frame too short (%d bytes) on \"%s\"\n",
            PLUGIN_NAME,
            nBytes,
            walker->ifName);

          continue; /* for */
        }

        if (pktAddr.sll_pkttype == PACKET_OUTGOING ||
            pktAddr.sll_pkttype == PACKET_MULTICAST ||
            pktAddr.sll_pkttype == PACKET_BROADCAST)
        {
          /* A multicast or broadcast packet was captured */

          BmfPacketCaptured(walker, pktAddr.sll_pkttype, rxBuffer);

        } /* if (pktAddr.sll_pkttype == ...) */
      } /* if (skfd >= 0 && (FD_ISSET...)) */
    } /* for */
    
    /* Check if a BMF encapsulation packet was received on the listening
     * socket (if any) of each network interface */
    for (walker = BmfInterfaces; walker != NULL; walker = walker->next)
    {
      int skfd = walker->listeningSkfd;
      if (skfd >= 0 && (FD_ISSET(skfd, &rxFdSet)))
      {
        struct sockaddr_ll pktAddr;
        socklen_t addrLen = sizeof(pktAddr);
        int nBytes;
        int minimumLength;
        struct ip* ipHeader;
        struct udphdr* udpHeader;
        u_int16_t destPort;
        union olsr_ip_addr forwardedBy;
        union olsr_ip_addr forwardedTo;

        /* Heard a BMF packet */

        nFdBitsSet--;

        nBytes = recvfrom(
          skfd,
          rxBuffer,
          BMF_BUFFER_SIZE,
          0,
          (struct sockaddr*)&pktAddr,
          &addrLen);
        if (nBytes < 0)
        {
          BmfPError("recvfrom() error on \"%s\"", walker->ifName);

          continue; /* for */
        } /* if (nBytes < 0) */

        /* Check if the received packet is actually directed to another
         * node on the LAN */
        if (pktAddr.sll_pkttype != PACKET_OTHERHOST)
        {
          /* No, the packet is directed to this node. In that case it will
           * be, or will already have been received, via the encapsulating
           * socket. Discard it here. */
          continue; /* for */
        } /* if (pktAddr.sll_pkttype ...) */

        /* Check if the received packet is UDP - BMF port */
        ipHeader = (struct ip*)rxBuffer;
        if (ipHeader->ip_p != SOL_UDP)
        {
          /* Not UDP */
          continue; /* for */
        }

        udpHeader = (struct udphdr*)(rxBuffer + GetIpHeaderLength(rxBuffer));
        destPort = ntohs(udpHeader->dest);
        if (destPort != BMF_ENCAP_PORT)
        {
          /* Not BMF */
          continue; /* for */
        }

        /* Check if the number of received bytes is large enough for a minimal BMF
         * encapsulation packet, at least:
         * - the IP header of the encapsulation IP packet
         * - the UDP header of the encapsulation IP packet
         * - the encapsulation header
         * - a minimum IP header inside the encapsulated packet
         * Note: on a VLAN interface, the value returned by 'recvfrom' may (but need
         * not) be 4 (bytes) larger than the value returned on a non-VLAN interface, for
         * the same ethernet frame. */
        minimumLength =
          GetIpHeaderLength(rxBuffer) +
          sizeof(struct udphdr) +
          ENCAP_HDR_LEN +
          sizeof(struct ip);
        if (nBytes < minimumLength)
        {
          olsr_printf(
            1,
            "%s: captured a too short encapsulation packet (%d bytes) on \"%s\"\n",
            PLUGIN_NAME,
            nBytes,
            walker->ifName);

          continue; /* for */
        }

        COPY_IP(&forwardedBy, &ipHeader->ip_src);
        COPY_IP(&forwardedTo, &ipHeader->ip_dst);
        BmfEncapsulationPacketReceived(
          walker,
          &forwardedBy,
          &forwardedTo,
          rxBuffer + GetIpHeaderLength(rxBuffer) + sizeof(struct udphdr));

      } /* if (skfd >= 0 && (FD_ISSET...)) */
    } /* for */

    /* Check if a packet was received on the encapsulating socket (if any)
     * of each network interface */
    for (walker = BmfInterfaces; walker != NULL; walker = walker->next)
    {
      int skfd = walker->encapsulatingSkfd;
      if (skfd >= 0 && (FD_ISSET(skfd, &rxFdSet)))
      {
        struct sockaddr_in from;
        socklen_t fromLen = sizeof(from);
        int nBytes;
        int minimumLength;
        union olsr_ip_addr forwardedBy;

        /* An encapsulated packet was received */

        nFdBitsSet--;

        nBytes = recvfrom(
          skfd,
          rxBuffer,
          BMF_BUFFER_SIZE,
          0,
          (struct sockaddr*)&from,
          &fromLen);
        if (nBytes < 0)
        {
          BmfPError("recvfrom() error on \"%s\"", walker->ifName);

          continue; /* for */
        } /* if (nBytes < 0) */

        COPY_IP(&forwardedBy, &from.sin_addr.s_addr);

        /* Check if the number of received bytes is large enough for a minimal BMF
         * encapsulation packet, at least:
         * - the encapsulation header
         * - a minimum IP header inside the encapsulated packet */
        minimumLength =
          ENCAP_HDR_LEN +
          sizeof(struct ip);
        if (nBytes < minimumLength)
        {
          olsr_printf(
            1,
            "%s: received a too short encapsulation packet (%d bytes) from %s on \"%s\"\n",
            PLUGIN_NAME,
            nBytes,
            olsr_ip_to_string(&forwardedBy),
            walker->ifName);

          continue; /* for */
        }

        /* Unfortunately, the recvfrom call does not return the destination
         * of the encapsulation packet (the destination may be either the
         * my unicast or my local broadcast address). Therefore we fill in 'NULL'
         * for the 'forwardedTo' parameter. */
        BmfEncapsulationPacketReceived(walker, &forwardedBy, NULL, rxBuffer);

      } /* if (skfd >= 0 && (FD_ISSET...)) */
    } /* for */

    if (nFdBitsSet > 0 && FD_ISSET(EtherTunTapFd, &rxFdSet))
    {
      /* Check if an application has sent a packet out via the tuntap
       * network interface */

      int nBytes;
      unsigned char* ipPacket;
      unsigned char* bufferToRead;
      size_t nBytesToRead;

      nFdBitsSet--;

      /* Receive the packet, leaving space for the BMF encapsulation header */
      ipPacket = GetIpPacket(rxBuffer);
    
      bufferToRead = ipPacket;
      nBytesToRead = BMF_BUFFER_SIZE - ENCAP_HDR_LEN;

      nBytes = read(EtherTunTapFd, bufferToRead, nBytesToRead);

      if (nBytes < 0)
      {
        BmfPError("recvfrom() error on \"%s\"", EtherTunTapIfName);
      }
      else
      {
        /* Check if the number of received bytes is large enough for an IP
         * packet which contains at least a minimum-size IP header */
        if (nBytes < (int)sizeof(struct ip))
        {
          olsr_printf(
            1,
            "%s: captured packet too short (%d bytes) on \"%s\"\n",
            PLUGIN_NAME,
            nBytes,
            EtherTunTapIfName);
        }
        else
        {
          /* An outbound packet was captured */

          BmfTunPacketCaptured(rxBuffer);

        } /* if (nBytes < ... */
      } /* if (nBytes < 0) */
    } /* if (nFdBitsSet > 0 && ... */
  } /* while (nFdBitsSet > 0) */
} /* DoBmf */

/* -------------------------------------------------------------------------
 * Function   : BmfSignalHandler
 * Description: Signal handler function
 * Input      : signo - signal being handled
 * Output     : none
 * Return     : none
 * Data Used  : BmfThreadRunning
 * ------------------------------------------------------------------------- */
static void BmfSignalHandler(int signo __attribute__((unused)))
{
  BmfThreadRunning = 0;
} /* BmfSignalHandler */

/* -------------------------------------------------------------------------
 * Function   : BmfRun
 * Description: Receiver thread function
 * Input      : useless - not used
 * Output     : none
 * Return     : not used
 * Data Used  : BmfThreadRunning
 * Notes      : Another thread can gracefully stop this thread by sending
 *              a SIGALRM signal.
 * ------------------------------------------------------------------------- */
static void* BmfRun(void* useless __attribute__((unused)))
{
  /* Mask all signals except SIGALRM */
  sigset_t blockedSigs;
  sigfillset(&blockedSigs);
  sigdelset(&blockedSigs, SIGALRM);
  if (pthread_sigmask(SIG_BLOCK, &blockedSigs, NULL) != 0)
  {
    BmfPError("pthread_sigmask() error");
  }

  /* Set up the signal handler for the process: use SIGALRM to terminate
   * the BMF thread. Only if a signal handler is specified, does a blocking
   * system call return with errno set to EINTR; if a signal hander is not
   * specified, any system call in which the thread may be waiting will not
   * return. Note that the BMF thread is usually blocked in the select()
   * function (see DoBmf()). */
  if (signal(SIGALRM, BmfSignalHandler) == SIG_ERR)
  {
    BmfPError("signal() error");
  }

  /* Call the thread function until flagged to exit */
  while (BmfThreadRunning != 0)
  {
    DoBmf();
  }

  return NULL;
} /* BmfRun */

/* -------------------------------------------------------------------------
 * Function   : InterfaceChange
 * Description: Callback function passed to OLSRD for it to call whenever a
 *              network interface has been added, removed or updated
 * Input      : interf - the network interface to deal with
 *              action - indicates if the specified network interface was
 *                added, removed or updated.
 * Output     : none
 * Return     : always 0
 * Data Used  : none
 * ------------------------------------------------------------------------- */
int InterfaceChange(struct interface* interf, int action)
{
  switch (action)
  {
  case (IFCHG_IF_ADD):
    AddInterface(interf);
    olsr_printf(1, "%s: interface %s added\n", PLUGIN_NAME, interf->int_name);
    break;

  case (IFCHG_IF_REMOVE):
    /* We cannot just remove the interface, because the receive-thread is likely
     * to be waiting in select(...) for packets coming in via the interface.
     * Therefore we first close BMF (CloseBmf()), interrupting and kiling the
     * receive-thread so that it is safe to remove this (and all other)
     * interfaces. After that, BMF is re-started (InitBmf(interf)). */
    CloseBmf();
    InitBmf(interf);
    olsr_printf(1, "%s: interface %s removed\n", PLUGIN_NAME, interf->int_name);
    break;

  case (IFCHG_IF_UPDATE):
    olsr_printf(1, "%s: interface %s updated\n", PLUGIN_NAME, interf->int_name);
    break;
      
  default:
    olsr_printf(
      1,
      "%s: interface %s: error - unknown action (%d)\n",
      PLUGIN_NAME,
      interf->int_name, action);
    break;
  }

  return 0;
} /* InterfaceChange */

/* -------------------------------------------------------------------------
 * Function   : InitBmf
 * Description: Initialize the BMF plugin
 * Input      : skipThisIntf - specifies which network interface should not
 *              be enabled for BMF. Pass NULL if not applicable.
 * Output     : none
 * Return     : fail (0) or success (1)
 * Data Used  : BmfThreadRunning, BmfThread
 * ------------------------------------------------------------------------- */
int InitBmf(struct interface* skipThisIntf)
{
  CreateBmfNetworkInterfaces(skipThisIntf);

  /* Start running the multicast packet processing thread */
  BmfThreadRunning = 1;
  if (pthread_create(&BmfThread, NULL, BmfRun, NULL) != 0)
  {
    BmfPError("pthread_create() error");
    return 0;
  }

  if (EtherTunTapFd >= 0)
  {
    /* Deactivate IP spoof filter for EtherTunTap interface */
    DeactivateSpoofFilter();

    /* If the BMF network interface has a sensible IP address, it is a good idea
     * to route all multicast traffic through that interface */
    if (EtherTunTapIp != ETHERTUNTAPDEFAULTIP)
    {
      AddMulticastRoute();
    }
  }

  return 1;
} /* InitBmf */

/* -------------------------------------------------------------------------
 * Function   : CloseBmf
 * Description: Close the BMF plugin and clean up
 * Input      : none
 * Output     : none
 * Return     : none
 * Data Used  : BmfThread
 * ------------------------------------------------------------------------- */
void CloseBmf(void)
{
  if (EtherTunTapFd >= 0)
  {
    /* If there is a multicast route, try to delete it first */
    DeleteMulticastRoute();

    /* Restore IP spoof filter for EtherTunTap interface */
    RestoreSpoofFilter();
  }

  if (BmfThreadRunning)
  {
    /* Signal BmfThread to exit */
    /* Strangely enough, all running threads receive the SIGALRM signal. But only the
     * BMF thread is affected by this signal, having specified a handler for this
     * signal in its thread entry function BmfRun(...). */
    if (pthread_kill(BmfThread, SIGALRM) != 0)
    {
      BmfPError("pthread_kill() error");
    }

    /* Wait for BmfThread to acknowledge */
    if (pthread_join(BmfThread, NULL) != 0)
    {
      BmfPError("pthread_join() error");
    }
  }

  /* Clean up after the BmfThread has been killed */
  CloseBmfNetworkInterfaces();
} /* CloseBmf */



syntax highlighted by Code2HTML, v. 0.9.1