Exemple #1
0
/**
 * Send a MLD message (report or done).
 *
 * An IPv6 hop-by-hop options header with a router alert option
 * is prepended.
 *
 * @param group the group to report or quit
 * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
 */
static void
mld6_send(struct mld_group *group, u8_t type)
{
  struct mld_header * mld_hdr;
  struct pbuf * p;
  ip6_addr_t * src_addr;

  /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
  p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
  if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) {
    /* We couldn't allocate a suitable pbuf. drop it. */
    if (p != NULL) {
      pbuf_free(p);
    }
    MLD6_STATS_INC(mld6.memerr);
    return;
  }

  /* Move to make room for Hop-by-hop options header. */
  if (pbuf_header(p, -IP6_HBH_HLEN)) {
    pbuf_free(p);
    MLD6_STATS_INC(mld6.lenerr);
    return;
  }

  /* Select our source address. */
  if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) {
    /* This is a special case, when we are performing duplicate address detection.
     * We must join the multicast group, but we don't have a valid address yet. */
    src_addr = IP6_ADDR_ANY;
  } else {
    /* Use link-local address as source address. */
    src_addr = netif_ip6_addr(group->netif, 0);
  }

  /* MLD message header pointer. */
  mld_hdr = (struct mld_header *)p->payload;

  /* Set fields. */
  mld_hdr->type = type;
  mld_hdr->code = 0;
  mld_hdr->chksum = 0;
  mld_hdr->max_resp_delay = 0;
  mld_hdr->reserved = 0;
  ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));

#if CHECKSUM_GEN_ICMP6
  mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
    src_addr, &(group->group_address));
#endif /* CHECKSUM_GEN_ICMP6 */

  /* Add hop-by-hop headers options: router alert with MLD value. */
  ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);

  /* Send the packet out. */
  MLD6_STATS_INC(mld6.xmit);
  ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
      MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif);
  pbuf_free(p);
}
Exemple #2
0
/**
 * Leave a group on a network interface.
 *
 * @param srcaddr ipv6 address of the network interface which should
 *                leave the group. If IP6_ISANY, leave on all netifs
 * @param groupaddr the ipv6 address of the group to leave
 * @return ERR_OK if group was left on the netif(s), an err_t otherwise
 */
err_t
mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
{
  err_t              err = ERR_VAL; /* no matching interface */
  struct mld_group  *group;
  struct netif      *netif;
  u8_t               match;
  u8_t               i;

  /* loop through netif's */
  netif = netif_list;
  while (netif != NULL) {
    /* Should we leave this interface ? */
    match = 0;
    if (ip6_addr_isany(srcaddr)) {
      match = 1;
    }
    else {
      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
        if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
            ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) {
          match = 1;
          break;
        }
      }
    }
    if (match) {
      /* find group */
      group = mld6_lookfor_group(netif, groupaddr);

      if (group != NULL) {
        /* Leave if there is no other use of the group */
        if (group->use <= 1) {
          /* If we are the last reporter for this group */
          if (group->last_reporter_flag) {
            MLD6_STATS_INC(mld6.tx_leave);
            mld6_send(group, ICMP6_TYPE_MLD);
          }

          /* Disable the group at the MAC level */
          if (netif->mld_mac_filter != NULL) {
            netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER);
          }

          /* Free the group */
          mld6_free_group(group);
        } else {
          /* Decrement group use */
          group->use--;
        }
        /* Leave on this interface */
        err = ERR_OK;
      }
    }
    /* proceed to next network interface */
    netif = netif->next;
  }

  return err;
}
Exemple #3
0
/**
 * timer for mld processing. Must be called based on smallest
 * timer in group
 * When a delaying member expires, a membership report is sent.
 */
void
mld6_tmr(void)
{
  struct mld_group *group = mld_group_list;

  unsigned long curr_ticks = sys_arch_get_os_ticks() / MLD_TIMER_OFFSET;

  while (group != NULL) {
    if (group->timer <= curr_ticks
        && group->timer != 0) {
            group->timer = 0;
        /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
        if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
          MLD6_STATS_INC(mld6.tx_report);
          mld6_send(group, ICMP6_TYPE_MLR);
          group->group_state = MLD6_GROUP_IDLE_MEMBER;
        }
      }
    group = group->next;
  }
  struct mld_group* min_timeout_grp = lwip_get_min_timeout_mld_grp();
  if (min_timeout_grp &&
      (min_timeout_grp->timer)  >  curr_ticks)  {
        unsigned int new_timer_val =
                ((min_timeout_grp->timer) - curr_ticks) * MLD_TIMER_OFFSET;
        sys_timeout(new_timer_val, mld6_timer, NULL);
   }
}
Exemple #4
0
/**
 * Join a group on a network interface.
 *
 * @param srcaddr ipv6 address of the network interface which should
 *                join a new group. If IP6_ADDR_ANY, join on all netifs
 * @param groupaddr the ipv6 address of the group to join
 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
 */
err_t
mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
{
  err_t              err = ERR_VAL; /* no matching interface */
  struct mld_group  *group;
  struct netif      *netif;
  u8_t               match;
  u8_t               i;

  /* loop through netif's */
  netif = netif_list;
  while (netif != NULL) {
    /* Should we join this interface ? */
    match = 0;
    if (ip6_addr_isany(srcaddr)) {
      match = 1;
    }
    else {
      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
        if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
            ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) {
          match = 1;
          break;
        }
      }
    }
    if (match) {
      /* find group or create a new one if not found */
      group = mld6_lookfor_group(netif, groupaddr);

      if (group == NULL) {
        /* Joining a new group. Create a new group entry. */
        group = mld6_new_group(netif, groupaddr);
        if (group == NULL) {
          return ERR_MEM;
        }

        /* Activate this address on the MAC layer. */
        if (netif->mld_mac_filter != NULL) {
          netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER);
        }

        /* Report our membership. */
        MLD6_STATS_INC(mld6.tx_report);
        mld6_send(group, ICMP6_TYPE_MLR);
        mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
      }

      /* Increment group use */
      group->use++;
      err = ERR_OK;
    }

    /* proceed to next network interface */
    netif = netif->next;
  }

  return err;
}
Exemple #5
0
/**
 * Periodic timer for mld processing. Must be called every
 * MLD6_TMR_INTERVAL milliseconds (100).
 *
 * When a delaying member expires, a membership report is sent.
 */
void
mld6_tmr(void)
{
  struct mld_group *group = mld_group_list;

  while (group != NULL) {
    if (group->timer > 0) {
      group->timer--;
      if (group->timer == 0) {
        /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
        if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
          MLD6_STATS_INC(mld6.tx_report);
          mld6_send(group, ICMP6_TYPE_MLR);
          group->group_state = MLD6_GROUP_IDLE_MEMBER;
        }
      }
    }
    group = group->next;
  }
}
Exemple #6
0
/**
 * Process an input MLD message. Called by icmp6_input.
 *
 * @param p the mld packet, p->payload pointing to the icmpv6 header
 * @param inp the netif on which this packet was received
 */
void
mld6_input(struct pbuf *p, struct netif *inp)
{
  struct mld_header * mld_hdr;
  struct mld_group* group;

  MLD6_STATS_INC(mld6.recv);

  /* Check that mld header fits in packet. */
  if (p->len < sizeof(struct mld_header)) {
    // TODO debug message
    pbuf_free(p);
    MLD6_STATS_INC(mld6.lenerr);
    MLD6_STATS_INC(mld6.drop);
    return;
  }

  mld_hdr = (struct mld_header *)p->payload;

  switch (mld_hdr->type) {
  case ICMP6_TYPE_MLQ: /* Multicast listener query. */
  {
    /* Is it a general query? */
    if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
        ip6_addr_isany(&(mld_hdr->multicast_address))) {
      MLD6_STATS_INC(mld6.rx_general);
      /* Report all groups, except all nodes group, and if-local groups. */
      group = mld_group_list;
      while (group != NULL) {
        if ((group->netif == inp) &&
            (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
            (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
          mld6_delayed_report(group, mld_hdr->max_resp_delay);
        }
        group = group->next;
      }
    }
    else {
      /* Have we joined this group?
       * We use IP6 destination address to have a memory aligned copy.
       * mld_hdr->multicast_address should be the same. */
      MLD6_STATS_INC(mld6.rx_group);
      group = mld6_lookfor_group(inp, ip6_current_dest_addr());
      if (group != NULL) {
        /* Schedule a report. */
        mld6_delayed_report(group, mld_hdr->max_resp_delay);
      }
    }
    break; /* ICMP6_TYPE_MLQ */
  }
  case ICMP6_TYPE_MLR: /* Multicast listener report. */
  {
    /* Have we joined this group?
     * We use IP6 destination address to have a memory aligned copy.
     * mld_hdr->multicast_address should be the same. */
    MLD6_STATS_INC(mld6.rx_report);
    group = mld6_lookfor_group(inp, ip6_current_dest_addr());
    if (group != NULL) {
      /* If we are waiting to report, cancel it. */
      if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
        group->timer = 0; /* stopped */
        group->group_state = MLD6_GROUP_IDLE_MEMBER;
        group->last_reporter_flag = 0;
      }
    }
    break; /* ICMP6_TYPE_MLR */
  }
  case ICMP6_TYPE_MLD: /* Multicast listener done. */
  {
    /* Do nothing, router will query us. */
    break; /* ICMP6_TYPE_MLD */
  }
  default:
    MLD6_STATS_INC(mld6.proterr);
    MLD6_STATS_INC(mld6.drop);
    break;
  }

  pbuf_free(p);
}