Ticket #79: openvpn-mcast-r2.patch

File openvpn-mcast-r2.patch, 26.2 KB (added by dbertolo, 4 years ago)
  • Makefile.am

    diff --git a/Makefile.am b/Makefile.am
    index ea8bd89..0e2303b 100644
    a b openvpn_SOURCES = \ 
    5757        lzo.c lzo.h \
    5858        manage.c manage.h \
    5959        mbuf.c mbuf.h \
    60         memdbg.h \
     60        mcast.c mcast.h \
     61        memdbg.h \
    6162        misc.c misc.h \
    6263        mroute.c mroute.h \
    6364        mss.c mss.h \
    openvpn_SOURCES = \ 
    6566        mtu.c mtu.h \
    6667        mudp.c mudp.h \
    6768        multi.c multi.h \
    68         ntlm.c ntlm.h \
     69        ntlm.c ntlm.h \
    6970        occ.c occ.h occ-inline.h \
    7071        openvpn.c openvpn.h \
    7172        openvpn-plugin.h \
  • errlevel.h

    diff --git a/errlevel.h b/errlevel.h
    index 2e66250..a9ba6ea 100644
    a b #define D_BACKTRACK LOGLEV(3, 3 
    8888#define D_AUTH               LOGLEV(3, 37, 0)        /* show user/pass auth info */
    8989#define D_MULTI_LOW          LOGLEV(3, 38, 0)        /* show point-to-multipoint low-freq debug info */
    9090#define D_MULTI_DROPPED      LOGLEV(3, 39, 0)        /* show point-to-multipoint packet drops */
     91#define D_MCAST_LOW          LOGLEV(3, 38, 0)        /* show point-to-multipoint low-freq debug info */
    9192#define D_PLUGIN             LOGLEV(3, 40, 0)        /* show plugin calls */
    9293#define D_MANAGEMENT         LOGLEV(3, 41, 0)        /* show --management info */
    9394#define D_SCHED_EXIT         LOGLEV(3, 42, 0)        /* show arming of scheduled exit */
    #define D_WIN32_IO_LOW LOGLEV(7, 7 
    114115#define D_MTU_DEBUG          LOGLEV(7, 70, M_DEBUG)  /* show MTU debugging info */
    115116#define D_PID_DEBUG_LOW      LOGLEV(7, 70, M_DEBUG)  /* show low-freq packet-id debugging info */
    116117#define D_MULTI_DEBUG        LOGLEV(7, 70, M_DEBUG)  /* show medium-freq multi debugging info */
     118#define D_MCAST_DEBUG        LOGLEV(3, 70, M_DEBUG)  /* show medium-freq multi debugging info */
    117119#define D_MSS                LOGLEV(7, 70, M_DEBUG)  /* show MSS adjustments */
    118120#define D_COMP_LOW           LOGLEV(7, 70, M_DEBUG)  /* show adaptive compression state changes */
    119121#define D_REMOTE_LIST        LOGLEV(7, 70, M_DEBUG)  /* show --remote list */
  • list.h

    diff --git a/list.h b/list.h
    index 84b989d..8f0bf87 100644
    a b hash_lookup (struct hash *hash, const vo 
    182182  return hash_lookup_lock (hash, key, hash_value (hash, key));
    183183}
    184184
     185static inline void **
     186hash_lookup_ptr (struct hash *hash, const void *key)
     187{
     188  void **ret = NULL;
     189  struct hash_element *he;
     190  uint32_t hv = hash_value (hash, key);
     191  struct hash_bucket *bucket = &hash->buckets[hv & hash->mask];
     192
     193  mutex_lock (&bucket->mutex);
     194  he = hash_lookup_fast (hash, bucket, key, hv);
     195  if (he)
     196    ret = &he->value;
     197  mutex_unlock (&bucket->mutex);
     198
     199  return ret;
     200}
     201
    185202/* NOTE: assumes that key is not a duplicate */
    186203static inline void
    187204hash_add_fast (struct hash *hash,
  • new file mcast.c

    diff --git a/mcast.c b/mcast.c
    new file mode 100644
    index 0000000..447d958
    - +  
     1/*
     2 *  OpenVPN -- An application to securely tunnel IP networks
     3 *             over a single TCP/UDP port, with support for SSL/TLS-based
     4 *             session authentication and key exchange,
     5 *             packet encryption, packet authentication, and
     6 *             packet compression.
     7 *
     8 *  Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
     9 *
     10 *  This program is free software; you can redistribute it and/or modify
     11 *  it under the terms of the GNU General Public License version 2
     12 *  as published by the Free Software Foundation.
     13 *
     14 *  This program is distributed in the hope that it will be useful,
     15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 *  GNU General Public License for more details.
     18 *
     19 *  You should have received a copy of the GNU General Public License
     20 *  along with this program (see the file COPYING included with this
     21 *  distribution); if not, write to the Free Software Foundation, Inc.,
     22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     23 */
     24
     25#ifdef WIN32
     26#include "config-win32.h"
     27#else
     28#include "config.h"
     29#endif
     30
     31#include "syshead.h"
     32
     33#include "mcast.h"
     34#include "proto.h"
     35#include "list.h"
     36#include "mroute.h"
     37
     38struct mcast_recipient_list
     39{
     40  struct mroute_addr rcpt;
     41  struct multi_instance *mi;
     42  struct mcast_recipient_list *next;
     43};
     44
     45struct mcast_timeout_list
     46{
     47  struct mroute_addr * rcpt;
     48  uint32_t group;
     49  struct timeval timeout;
     50  struct mcast_timeout_list * next;
     51};
     52
     53/** mcast hashing functions */
     54uint32_t mcast_addr_hash(const void *key, uint32_t iv)
     55{
     56  return (*(const int32_t *)key) % iv;
     57}
     58
     59bool mcast_addr_compare(const void *key1, const void *key2)
     60{
     61  return (*(const int32_t *)key1) == (*(const int32_t *)key2);
     62}
     63
     64/** initialization-stuff */
     65void mcast_init(struct multi_context *m)
     66{
     67  m->mcast_group_map = hash_init(4096, mcast_addr_hash, mcast_addr_compare);
     68  msg (D_MCAST_LOW, "MCAST: initialized multicast maps");
     69}
     70
     71/** debug_output_functions */
     72void mcast_print_group_list(const struct multi_context *m, struct gc_arena *gc, uint32_t group)
     73{
     74  struct mcast_recipient_list * current_list;
     75
     76  msg (D_MCAST_DEBUG, "MCAST: current recipients for group %s", print_in_addr_t (group, IA_EMPTY_IF_UNDEF, gc));
     77
     78  for (current_list = (struct mcast_recipient_list *)hash_lookup(m->mcast_group_map, &group); current_list; current_list = current_list->next)
     79    msg (D_MCAST_DEBUG, "MCAST:   %s through %s", mroute_addr_print(&current_list->rcpt, gc), multi_instance_string(current_list->mi, false, gc));
     80}
     81
     82void mcast_print_timeout_list(const struct multi_instance *mi, struct gc_arena *gc)
     83{
     84  struct mcast_timeout_list * current_list;
     85
     86  msg (D_MCAST_DEBUG, "MCAST: current timeouts for multi-instance. (now = %s)", tv_string_abs(&((struct timeval){now,0}), gc));
     87
     88  for (current_list = mi->mcast_timeouts; current_list; current_list = current_list->next)
     89    msg (D_MCAST_DEBUG, "MCAST:   (%s, %s) -> %s", mroute_addr_print(current_list->rcpt, gc), print_in_addr_t (current_list->group, IA_EMPTY_IF_UNDEF, gc), tv_string_abs(&current_list->timeout, gc));
     90}
     91
     92/** functions to manipulate mcast-receiver-lists */
     93inline struct mcast_recipient_list * mcast__create_recv_list_item(struct multi_instance * mi, const struct mroute_addr * rcpt)
     94{
     95  struct mcast_recipient_list *list;
     96  ALLOC_OBJ(list, struct mcast_recipient_list);
     97  list->next = NULL;
     98  list->mi = mi;
     99  memcpy(&list->rcpt, rcpt, sizeof(struct mroute_addr));
     100 
     101  return list;
     102}
     103
     104struct mcast_timeout_list * mcast__create_timeout_list_item(const uint32_t group, struct mroute_addr * rcpt)
     105{
     106  struct mcast_timeout_list * timeout;
     107  ALLOC_OBJ(timeout, struct mcast_timeout_list);
     108  timeout->group = group;
     109  timeout->rcpt = rcpt;
     110  timeout->timeout = (struct timeval){now + MCAST_TIMEOUT_INTERVAL,0};
     111  return timeout;
     112}
     113
     114void mcast__update_time_for_recipient(struct multi_instance * mi, const uint32_t group, struct mroute_addr * rcpt)
     115{
     116  struct mcast_timeout_list **current_list;
     117  struct mcast_timeout_list * tmp_list;
     118  struct mcast_timeout_list * new_list_item = mcast__create_timeout_list_item(group, rcpt);
     119
     120  mutex_lock(mi->mutex);
     121
     122  current_list = &mi->mcast_timeouts; // Start with a pointer to the pointer to the first item.
     123
     124  while ((*current_list) && tv_ge(&new_list_item->timeout, &(*current_list)->timeout))  // Continue while there exist a next item, and the next item is scheduled for removal later than the current. */
     125  {
     126    if (mroute_addr_equal((*current_list)->rcpt, rcpt) && ((*current_list)->group == group)) // If old item exist, remove it.
     127    {
     128      tmp_list = (*current_list)->next;
     129      free(*current_list);
     130      *current_list = tmp_list;
     131    }
     132    else
     133      current_list = &(*current_list)->next; // Bring up the next item in list
     134  }
     135  // We should now be positioned at the right spot in the list, just insert the new item.
     136  new_list_item->next = (*current_list);
     137  *current_list = new_list_item;
     138
     139  mutex_unlock(mi->mutex);
     140}
     141
     142void mcast__clean_times_for_recipient(struct multi_instance * mi, const uint32_t group, struct mroute_addr * rcpt)
     143{
     144  struct mcast_timeout_list **current_list;
     145  struct mcast_timeout_list * tmp_list;
     146
     147  mutex_lock(mi->mutex);
     148
     149  current_list = &mi->mcast_timeouts;  // Start with a pointer to the pointer to the first item.
     150
     151  while (*current_list)  // Continue while there exist a next item
     152  {
     153    if (mroute_addr_equal((*current_list)->rcpt, rcpt) && (*current_list)->group == group) // If old item exist, remove it.
     154    {
     155      tmp_list = (*current_list)->next;
     156      free(*current_list);
     157      *current_list = tmp_list;
     158    }
     159    else
     160      current_list = &(*current_list)->next; // Bring up the next item in list
     161  }
     162  mutex_unlock(mi->mutex);
     163}
     164
     165void mcast_add_rcpt(struct multi_context *m, const uint32_t group, const struct mroute_addr *rcpt, struct multi_instance * mi)
     166{
     167  struct gc_arena gc = gc_new ();
     168  struct mcast_recipient_list **list_item_ptr;
     169
     170  mutex_lock(m->mutex);
     171
     172  list_item_ptr = (struct mcast_recipient_list **)hash_lookup_ptr(m->mcast_group_map, &group);
     173
     174  if (list_item_ptr) // Recipient list for this group already exist
     175  {
     176    while ((*list_item_ptr) && !mroute_addr_equal(rcpt, &(*list_item_ptr)->rcpt))
     177      list_item_ptr = &(*list_item_ptr)->next;
     178
     179    if (*list_item_ptr)
     180    {
     181      /* TODO: Add timers and updates. */
     182      msg (D_MCAST_LOW, "MCAST: refreshed %s in group-list %s", mroute_addr_print(rcpt, &gc), print_in_addr_t (group, IA_EMPTY_IF_UNDEF, &gc));
     183    }
     184    else
     185    {
     186      *list_item_ptr = mcast__create_recv_list_item(mi, rcpt);
     187      msg (D_MCAST_LOW, "MCAST: added %s to group-list %s", mroute_addr_print(rcpt, &gc), print_in_addr_t (group, IA_EMPTY_IF_UNDEF, &gc));
     188    }
     189  }
     190  else // Create new mcast_recipient_list
     191  {
     192    struct mcast_recipient_list * new_list_item = mcast__create_recv_list_item(mi, rcpt);;
     193    uint32_t *group_cpy;
     194    ALLOC_OBJ(group_cpy, uint32_t)
     195    *group_cpy = group;
     196    list_item_ptr = &new_list_item;
     197    hash_add(m->mcast_group_map, group_cpy, new_list_item, false);
     198    msg (D_MCAST_LOW, "MCAST: created group-list %s for %s", print_in_addr_t (group, IA_EMPTY_IF_UNDEF, &gc), mroute_addr_print(rcpt, &gc));
     199  }
     200  mcast__update_time_for_recipient(mi, group, &(*list_item_ptr)->rcpt);
     201  mcast_print_group_list(m, &gc, group);
     202
     203  mutex_unlock(m->mutex);
     204  gc_free(&gc);
     205}
     206
     207void mcast_remove_rcpt(struct multi_context *m, const uint32_t group, const struct mroute_addr *rcpt, struct multi_instance * mi, bool clean_timeouts)
     208{
     209  struct gc_arena gc = gc_new();
     210  struct mcast_recipient_list ** list_item_ptr;
     211  struct mcast_recipient_list * next_item;
     212  struct mroute_addr rcpt_copy = *rcpt;
     213
     214  mutex_lock(m->mutex);
     215
     216  list_item_ptr = (struct mcast_recipient_list **)hash_lookup_ptr(m->mcast_group_map, &group);
     217
     218  while (list_item_ptr && (*list_item_ptr) && !mroute_addr_equal(rcpt, &(*list_item_ptr)->rcpt))
     219    list_item_ptr = &(*list_item_ptr)->next;
     220 
     221  if (list_item_ptr && *list_item_ptr) // We found something
     222  {
     223    if (clean_timeouts)
     224      mcast__clean_times_for_recipient(mi, group, &(*list_item_ptr)->rcpt);
     225
     226    next_item = (*list_item_ptr)->next;
     227    free(*list_item_ptr);
     228    *list_item_ptr = next_item;
     229  }
     230
     231  msg (D_MCAST_LOW, "MCAST: removed %s from group %s", mroute_addr_print(&rcpt_copy, &gc), print_in_addr_t (group, IA_EMPTY_IF_UNDEF, &gc));
     232
     233  mcast_print_group_list(m, &gc, group);
     234
     235  mutex_unlock(m->mutex);
     236
     237  gc_free(&gc);
     238}
     239
     240void mcast_clean_old_groups(struct multi_context *m, struct multi_instance *mi, bool drop_all)
     241{
     242  struct mcast_timeout_list **current_list;
     243  struct mcast_timeout_list * tmp_list;
     244  struct timeval timeval_now = (struct timeval){now, 0};
     245  struct gc_arena gc = gc_new();
     246
     247  update_time();
     248
     249  mutex_lock(mi->mutex);
     250
     251  current_list = &mi->mcast_timeouts; // Start with a pointer to the pointer to the first item.
     252
     253  msg (D_MCAST_DEBUG, "Cleaning old groups for multi_instance");
     254
     255  while ((*current_list) && (tv_ge(&timeval_now, &(*current_list)->timeout) || drop_all))  // Continue while there exist a next item, and either the next item is scheduled for removal before now, or we're due to clean out all joined groups*/
     256  {
     257    msg (D_MCAST_LOW, "MCAST: detected stale recipient %s in group %s", mroute_addr_print((*current_list)->rcpt, &gc), print_in_addr_t ((*current_list)->group, IA_EMPTY_IF_UNDEF, &gc));
     258    mcast_remove_rcpt(m, (*current_list)->group, (*current_list)->rcpt, mi, false); // If old item exist, remove it.
     259
     260    tmp_list = (*current_list)->next;
     261    free(*current_list);
     262    *current_list = tmp_list;
     263  }
     264  mcast_print_timeout_list(mi, &gc);
     265  mutex_unlock(mi->mutex);
     266  gc_free(&gc);
     267}
     268
     269/** functions to parse possible IGMP-headers */
     270void mcast_igmp_snoop(struct multi_context *m, struct multi_instance * mi, const struct openvpn_igmpv3hdr *igmp, const void * buf_end, const struct mroute_addr *src_addr)
     271{
     272  uint16_t i;
     273  struct openvpn_igmpv3_record_hdr * record;
     274
     275  update_time();
     276
     277  if (mi)
     278    mcast_clean_old_groups(m, mi, false);
     279
     280  switch (igmp->type)
     281  {
     282    case OPENVPN_IGMP_QUERY:
     283      msg(D_MCAST_DEBUG, "MCAST: saw IGMP query message");
     284      break;
     285    case OPENVPN_IGMP_REPORT_V2:
     286      mcast_add_rcpt(m, ntohl(igmp->data.igmpv2_group_addr), src_addr, mi);
     287      break;
     288    case OPENVPN_IGMP_LEAVE_V2:
     289      mcast_remove_rcpt(m, ntohl(igmp->data.igmpv2_group_addr), src_addr, mi, true);
     290      break;
     291    case OPENVPN_IGMP_REPORT_V3:
     292      record = (struct openvpn_igmpv3_record_hdr*)((void *)igmp + sizeof(struct openvpn_igmpv3hdr));
     293      for (i = 0; (i < ntohs(igmp->data.igmpv3_num_records)) && ((((void *)record) + sizeof(struct openvpn_igmpv3_record_hdr)) <= buf_end); i++)
     294      {
     295        switch (record->type)
     296        {
     297          case OPENVPN_IGMPV3_FILTER_CHANGE_TO_INCLUDE:
     298            mcast_remove_rcpt(m, ntohl(record->group_address), src_addr, mi, true);
     299            break;
     300          case OPENVPN_IGMPV3_FILTER_CHANGE_TO_EXCLUDE:
     301            mcast_add_rcpt(m, ntohl(record->group_address), src_addr, mi);
     302            break;
     303        }
     304        record = ((void *)record) + sizeof(struct openvpn_igmpv3hdr) + 4*(record->aux_len + record->num_sources);
     305      }
     306      break;
     307  }
     308}
     309
     310/** functions to quickly filter out IGMP-packets **/
     311const struct openvpn_iphdr * mcast_pkt_is_ip(const struct buffer *buf)
     312{
     313  if (BLEN(buf) >= sizeof(struct openvpn_ethhdr) + sizeof(struct openvpn_iphdr))
     314  {
     315    if (ntohs(((struct openvpn_ethhdr *)BPTR(buf))->proto) == OPENVPN_ETH_P_IPV4)
     316      return (const struct openvpn_iphdr *)(BPTR(buf) + sizeof(struct openvpn_ethhdr));
     317  }
     318  return NULL;
     319}
     320
     321const struct openvpn_igmpv3hdr* mcast_pkt_is_igmp(const struct buffer *buf)
     322{
     323  const struct openvpn_iphdr *ip;
     324  if (ip = mcast_pkt_is_ip(buf))
     325  {
     326    if ((ip->protocol == OPENVPN_IPPROTO_IGMP) && (BLEN(buf) >= (sizeof(struct openvpn_ethhdr) + ntohs(ip->tot_len))))
     327      return (const struct openvpn_igmpv3hdr *)openvpn_ip_payload(ip);
     328  }
     329  return NULL;
     330}
     331
     332/** functions hash multi_instances */
     333uint32_t multi_instance_hash(const void *key, uint32_t iv)
     334{
     335  return (uint32_t)key % iv;
     336}
     337
     338bool multi_instance_compare(const void *key1, const void *key2)
     339{
     340  return key1 == key2;
     341}
     342
     343/** functions to actually use the learnt mcast-forward-lists */
     344void mcast_send(struct multi_context *m,
     345                  const struct buffer *buf,
     346                  struct multi_instance *omit)
     347{
     348  struct multi_instance *mi;
     349  struct mbuf_buffer *mb;
     350  struct hash *output_instances;
     351  const struct openvpn_iphdr *ip;
     352  uint32_t group_address;
     353  struct mcast_recipient_list * rcpt_list;
     354  struct gc_arena gc = gc_new ();
     355
     356  if (ip = mcast_pkt_is_ip(buf))
     357    {
     358      group_address = ntohl(ip->daddr);
     359
     360      msg (D_MULTI_DEBUG, "MCAST: sending packet to group %s", print_in_addr_t (group_address, IA_EMPTY_IF_UNDEF, &gc));
     361
     362      mutex_lock(m->mutex);
     363
     364      rcpt_list = (struct mcast_recipient_list *)hash_lookup(m->mcast_group_map, &group_address);
     365      if (rcpt_list)
     366      {
     367        perf_push (PERF_MULTI_MCAST);
     368#ifdef MULTI_DEBUG_EVENT_LOOP
     369        printf ("MCAST len=%d\n", BLEN (buf));
     370#endif
     371        mb = mbuf_alloc_buf (buf);
     372        output_instances = hash_init(16, multi_instance_hash, multi_instance_compare);
     373 
     374 
     375        while (rcpt_list)
     376        {
     377          mi = rcpt_list->mi;
     378          if (mi != omit && !mi->halt && hash_add(output_instances, mi, mi, false)) // Should send here
     379            multi_add_mbuf (m, mi, mb);
     380          rcpt_list = rcpt_list->next;
     381        }
     382 
     383        hash_free(output_instances);
     384        mbuf_free_buf (mb);
     385        perf_pop ();
     386 
     387        mutex_unlock(m->mutex);
     388      }
     389    }
     390
     391    gc_free(&gc);
     392}
     393
     394void mcast_disconnect(struct multi_context *m, struct multi_instance *mi)
     395{
     396  struct gc_arena gc = gc_new ();
     397
     398  // Free dangling multicast-groups
     399  mcast_clean_old_groups(m, mi, true);
     400
     401  msg (D_MCAST_DEBUG, "MCAST: Disconnect: %s has left the building.", multi_instance_string(mi, false, &gc));
     402
     403  gc_free(&gc);
     404}
  • new file mcast.h

    diff --git a/mcast.h b/mcast.h
    new file mode 100644
    index 0000000..e12f23a
    - +  
     1/*
     2 *  OpenVPN -- An application to securely tunnel IP networks
     3 *             over a single TCP/UDP port, with support for SSL/TLS-based
     4 *             session authentication and key exchange,
     5 *             packet encryption, packet authentication, and
     6 *             packet compression.
     7 *
     8 *  Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
     9 *
     10 *  This program is free software; you can redistribute it and/or modify
     11 *  it under the terms of the GNU General Public License version 2
     12 *  as published by the Free Software Foundation.
     13 *
     14 *  This program is distributed in the hope that it will be useful,
     15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 *  GNU General Public License for more details.
     18 *
     19 *  You should have received a copy of the GNU General Public License
     20 *  along with this program (see the file COPYING included with this
     21 *  distribution); if not, write to the Free Software Foundation, Inc.,
     22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     23 */
     24
     25#ifndef MCAST_H
     26#define MCAST_H
     27
     28#include "buffer.h"
     29#include "list.h"
     30#include "multi.h"
     31
     32#define MCAST_TIMEOUT_INTERVAL 80  // The timeout interval in seconds, when a member of a group is considered dead. (Should really be aquired by following the IGMP query-pulses from the Designated Router)
     33
     34void mcast_init();
     35
     36const struct openvpn_igmpv3hdr * mcast_pkt_is_igmp(const struct buffer *buf);
     37
     38void mcast_igmp_snoop(struct multi_context *m, struct multi_instance * mi, const struct openvpn_igmpv3hdr *igmp, const void * buf_end, const struct mroute_addr *src_addr);
     39
     40void mcast_send(struct multi_context *m,
     41                  const struct buffer *buf,
     42                  struct multi_instance *omit);
     43
     44void mcast_clean_old_groups(struct multi_context *m, struct multi_instance *mi, bool drop_all);
     45
     46void mcast_disconnect(struct multi_context *m, struct multi_instance *mi);
     47
     48#endif /* MCAST_H */
  • mroute.c

    diff --git a/mroute.c b/mroute.c
    index 0a7bcf6..807a3e4 100644
    a b mroute_addr_init (struct mroute_addr *ad 
    5050 */
    5151
    5252static inline bool
    53 is_mac_mcast_addr (const uint8_t *mac)
     53is_mac_bcast_addr (const uint8_t *mac)
    5454{
    5555  return (bool) mac[0] & 1;
    5656}
    5757
    5858static inline bool
    59 is_mac_mcast_maddr (const struct mroute_addr *addr)
     59is_mac_bcast_maddr (const struct mroute_addr *addr)
     60{
     61  return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_bcast_addr (addr->addr);
     62}
     63
     64static inline bool
     65is_mac_mcast_addr (const uint8_t *mac)
    6066{
    61   return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr (addr->addr);
     67  return ((*(uint32_t*)mac) & htonl(MAC_MCAST_MASK)) == htonl(MAC_MCAST_ADDRS);
    6268}
    6369
    6470/*
    mroute_learnable_address (const struct m 
    7985      if (b != 0xFF)
    8086        not_all_ones = true;
    8187    }
    82   return not_all_zeros && not_all_ones && !is_mac_mcast_maddr (addr);
     88  return not_all_zeros && not_all_ones && !is_mac_bcast_maddr (addr);
    8389}
    8490
    8591/*
    mroute_extract_addr_from_packet (struct  
    157163              memcpy (dest->addr, eth->dest, 6);
    158164
    159165              /* ethernet broadcast/multicast packet? */
    160               if (is_mac_mcast_addr (eth->dest))
     166              if (is_mac_bcast_addr (eth->dest))
     167              {
    161168                ret |= MROUTE_EXTRACT_BCAST;
     169                if (is_mac_mcast_addr (eth->dest))
     170                  ret |= MROUTE_EXTRACT_MCAST;
     171              }
    162172            }
    163173         
    164174          ret |= MROUTE_EXTRACT_SUCCEEDED;
  • mroute.h

    diff --git a/mroute.h b/mroute.h
    index 87e952c..b4c5178 100644
    a b #include "route.h" 
    3434#define IP_MCAST_SUBNET_MASK  ((in_addr_t)240<<24)
    3535#define IP_MCAST_NETWORK      ((in_addr_t)224<<24)
    3636
     37#define MAC_MCAST_ADDRS        0x01005e00
     38#define MAC_MCAST_MASK         0xFFFFFE00
     39
    3740/* Return status values for mroute_extract_addr_from_packet */
    3841#define MROUTE_EXTRACT_SUCCEEDED (1<<1)
    3942#define MROUTE_EXTRACT_BCAST     (1<<2)
  • multi.c

    diff --git a/multi.c b/multi.c
    index 22ea677..6e7768b 100644
    a b multi_init (struct multi_context *m, str 
    267267  m->mbuf = mbuf_init (t->options.n_bcast_buf);
    268268
    269269  /*
     270   * Initialize the mcast group_maps.
     271   */
     272  mcast_init(m);
     273
     274  /*
    270275   * Different status file format options are available
    271276   */
    272277  m->status_file_version = t->options.status_file_version;
    multi_close_instance (struct multi_conte 
    463468
    464469      schedule_remove_entry (m->schedule, (struct schedule_entry *) mi);
    465470
     471      mcast_disconnect(m, mi);
     472
    466473      ifconfig_pool_release (m->ifconfig_pool, mi->vaddr_handle, false);
    467474     
    468475      if (mi->did_iroutes)
    multi_create_instance (struct multi_cont 
    562569  multi_instance_inc_refcount (mi);
    563570  mi->vaddr_handle = -1;
    564571  mi->created = now;
     572  mi->mcast_timeouts = NULL;
    565573  mroute_addr_init (&mi->real);
    566574
    567575  if (real)
    #ifdef MULTI_DEBUG_EVENT_LOOP 
    15611569              (int)mi->context.c2.timeval.tv_sec,
    15621570              (int)mi->context.c2.timeval.tv_usec);
    15631571#endif
    1564     }
     1572    }
     1573
    15651574
    15661575  if ((flags & MPP_RECORD_TOUCH) && m->mpp_touched)
    15671576    *m->mpp_touched = mi;
    #endif 
    16721681
    16731682              if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
    16741683                {
     1684                  const struct openvpn_igmpv3hdr * igmphdr;
     1685
     1686                  if (igmphdr = (const struct openvpn_igmpv3hdr *)mcast_pkt_is_igmp(&c->c2.to_tun))
     1687                      mcast_igmp_snoop(m, m->pending, igmphdr, BEND(&c->c2.to_tun), &src);
     1688
     1689/*                  if (mroute_flags & MROUTE_EXTRACT_MCAST)
     1690                      mcast_send(m, &c->c2.to_tun, m->pending);
     1691                  else*/
    16751692                  if (multi_learn_addr (m, m->pending, &src, 0) == m->pending)
    16761693                    {
    16771694                      /* check for broadcast */
    16781695                      if (m->enable_c2c)
    16791696                        {
    1680                           if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
    1681                             {
    1682                               multi_bcast (m, &c->c2.to_tun, m->pending);
    1683                             }
     1697                          if ((mroute_flags & MROUTE_EXTRACT_BCAST) && !(mroute_flags & MROUTE_EXTRACT_MCAST))
     1698                            multi_bcast (m, &c->c2.to_tun, m->pending);
    16841699                          else /* try client-to-client routing */
    16851700                            {
    16861701                              mi = multi_get_instance_by_virtual_addr (m, &dest, false);
    #endif 
    17561771        {
    17571772          struct context *c;
    17581773
    1759           /* broadcast or multicast dest addr? */
    1760           if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
     1774          if (mroute_flags & MROUTE_EXTRACT_MCAST)
    17611775            {
    1762               /* for now, treat multicast as broadcast */
    1763               multi_bcast (m, &m->top.c2.buf, NULL);
     1776              const struct openvpn_igmpv3hdr * igmphdr;
     1777
     1778//            Don't intercept IGMP flowing this direction for the moment. Will probably have to do it later to properly implement IGMP-query-snooping
     1779//            For now, treat IGMP queries flowing downstream as broadcasts
     1780              if (igmphdr = (const struct openvpn_igmpv3hdr *)mcast_pkt_is_igmp(&m->top.c2.buf))
     1781              {
     1782                if (igmphdr->type == OPENVPN_IGMP_QUERY)
     1783                  multi_bcast(m, &m->top.c2.buf, NULL);
     1784              }
     1785              else
     1786                mcast_send(m, &m->top.c2.buf, NULL);
    17641787            }
     1788          else if (mroute_flags & MROUTE_EXTRACT_BCAST)
     1789              multi_bcast (m, &m->top.c2.buf, NULL);
    17651790          else
    17661791            {
    17671792              multi_set_pending (m, multi_get_instance_by_virtual_addr (m, &dest, dev_type == DEV_TYPE_TUN));
    #endif 
    18491874  if (m->earliest_wakeup)
    18501875    {
    18511876      set_prefix (m->earliest_wakeup);
     1877      mcast_clean_old_groups(m, m->earliest_wakeup, false);
    18521878      ret = multi_process_post (m, m->earliest_wakeup, mpp_flags);
    18531879      m->earliest_wakeup = NULL;
    18541880      clear_prefix ();
  • multi.h

    diff --git a/multi.h b/multi.h
    index da1e471..df90678 100644
    a b struct multi_instance { 
    6767  ifconfig_pool_handle vaddr_handle;
    6868  const char *msg_prefix;
    6969
     70  struct mcast_timeout_list *mcast_timeouts;
     71
    7072  /* queued outgoing data in Server/TCP mode */
    7173  unsigned int tcp_rwflags;
    7274  struct mbuf_set *tcp_link_out_deferred;
    # define MC_WORK_THREAD ( 
    104106  struct ifconfig_pool *ifconfig_pool;
    105107  struct frequency_limit *new_connection_limiter;
    106108  struct mroute_helper *route_helper;
     109  struct hash *mcast_group_map;
    107110  struct multi_reap *reaper;
    108111  struct mroute_addr local;
    109112  bool enable_c2c;
  • proto.h

    diff --git a/proto.h b/proto.h
    index 440d3d1..aeec2d9 100644
    a b #define DEV_TYPE_TAP 3 /* ethernet  
    4343 */
    4444
    4545#define OPENVPN_ETH_ALEN 6            /* ethernet address length */
    46 struct openvpn_ethhdr 
     46struct openvpn_ethhdr
    4747{
    4848  uint8_t dest[OPENVPN_ETH_ALEN];     /* destination ethernet addr */
    4949  uint8_t source[OPENVPN_ETH_ALEN];   /* source ethernet addr   */
    # define OPENVPN_IPPROTO_UDP 17 /* UDP p 
    7979  /*The options start here. */
    8080};
    8181
     82static inline uint8_t *
     83openvpn_ip_payload (const struct openvpn_iphdr *h)
     84{
     85  return (uint8_t*)(((uint8_t*)h) + OPENVPN_IPH_GET_LEN(h->version_len));
     86}
     87
     88#define OPENVPN_IGMP_QUERY        0x11
     89#define OPENVPN_IGMP_REPORT_V2    0x16
     90#define OPENVPN_IGMP_LEAVE_V2     0x17
     91#define OPENVPN_IGMP_REPORT_V3    0x22
     92struct openvpn_igmpv3hdr {
     93  uint8_t    type;
     94  uint8_t    max_response;
     95  uint16_t   chk_sum;
     96  union
     97  {
     98    uint32_t   igmpv2_group_addr;
     99    struct
     100    {
     101      uint16_t igmpv3_reserved;
     102      uint16_t igmpv3_num_records;
     103    };
     104  } data;
     105};
     106
     107struct openvpn_igmpv3_record_hdr {
     108#define OPENVPN_IGMPV3_FILTER_CHANGE_TO_INCLUDE 3
     109#define OPENVPN_IGMPV3_FILTER_CHANGE_TO_EXCLUDE 4
     110  uint8_t type;
     111  uint8_t aux_len;
     112  uint16_t num_sources;
     113  uint32_t group_address;
     114};
     115
    82116/*
    83117 * UDP header
    84118 */