/** * 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); }
/** * Mapping from local network to loopback for outbound connections. * * Copy "src" to "dst" with ip6_addr_set(dst, src), but if "src" is a * local network address that maps host's loopback address, copy IPv6 * loopback address to "dst". */ int pxremap_outbound_ip6(ip6_addr_t *dst, ip6_addr_t *src) { struct netif *netif; int i; LWIP_ASSERT1(dst != NULL); LWIP_ASSERT1(src != NULL); for (netif = netif_list; netif != NULL; netif = netif->next) { if (!netif_is_up(netif) /* || this is not a proxy netif */) { continue; } for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { if (ip6_addr_ispreferred(netif_ip6_addr_state(netif, i)) && ip6_addr_isuniquelocal(netif_ip6_addr(netif, i))) { ip6_addr_t *ifaddr = netif_ip6_addr(netif, i); if (memcmp(src, ifaddr, sizeof(ip6_addr_t) - 1) == 0 && ((IP6_ADDR_BLOCK8(src) & 0xff) == (IP6_ADDR_BLOCK8(ifaddr) & 0xff) + 1)) { ip6_addr_set_loopback(dst); return PXREMAP_MAPPED; } } } } /* not remapped, just copy src */ ip6_addr_set(dst, src); return PXREMAP_ASIS; }
err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, u8_t ttl, u8_t proto, struct netif *netif) { struct ip6_hdr *iphdr; PERF_START; LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); if (pbuf_header(p, IP6_HLEN)) { LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n"));IP_STATS_INC(ip.err); return ERR_BUF; } LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); iphdr = p->payload; if (dest != IP_HDRINCL) { LWIP_DEBUGF(IP_DEBUG, ("!IP_HDRLINCL\n")); iphdr->hoplim = ttl; iphdr->nexthdr = proto; iphdr->len = htons(p->tot_len - IP6_HLEN); ip6_addr_set(&(iphdr->dest), dest); iphdr->v = 6; if (ip6_addr_isany(src)) { ip6_addr_set(&(iphdr->src), &(netif->ip6_addr)); } else { ip6_addr_set(&(iphdr->src), src); } } else { dest = &(iphdr->dest); } IP_STATS_INC(ip.xmit); LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len)); #if IP_DEBUG ip6_debug_print(p); #endif /* IP_DEBUG */ PERF_STOP("ip_output_if"); if (netif->ip6_output != 0) { return (netif->ip6_output(netif, p, dest)); } else { return (ERR_RTE); } }
/** * Mapping from loopback to local network for inbound (port-forwarded) * connections. * * Copy "src" to "dst" with ip6_addr_set(dst, src), but if "src" is a * host's loopback address, copy local network address that maps it to * "dst". */ int pxremap_inbound_ip6(ip6_addr_t *dst, ip6_addr_t *src) { ip6_addr_t loopback; struct netif *netif; int i; ip6_addr_set_loopback(&loopback); if (!ip6_addr_cmp(src, &loopback)) { ip6_addr_set(dst, src); return PXREMAP_ASIS; } #if 0 /* ?TODO: with multiple interfaces we need to consider fwspec::dst */ netif = ip6_route_fwd(target); if (netif == NULL) { return PXREMAP_FAILED; } #else netif = netif_list; LWIP_ASSERT1(netif != NULL); LWIP_ASSERT1(netif->next == NULL); #endif for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { ip6_addr_t *ifaddr = netif_ip6_addr(netif, i); if (ip6_addr_ispreferred(netif_ip6_addr_state(netif, i)) && ip6_addr_isuniquelocal(ifaddr)) { ip6_addr_set(dst, ifaddr); ++((u8_t *)&dst->addr[3])[3]; return PXREMAP_MAPPED; } } return PXREMAP_FAILED; }
/** Create a link-local IPv6 address on a netif (stored in slot 0) * * @param netif the netif to create the address on * @param from_mac_48bit if != 0, assume hwadr is a 48-bit MAC address (std conversion) * if == 0, use hwaddr directly as interface ID */ void netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) { u8_t i, addr_index; /* Link-local prefix. */ ip_2_ip6(&netif->ip6_addr[0])->addr[0] = PP_HTONL(0xfe800000ul); ip_2_ip6(&netif->ip6_addr[0])->addr[1] = 0; /* Generate interface ID. */ if (from_mac_48bit) { /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ ip_2_ip6(&netif->ip6_addr[0])->addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | ((u32_t)(netif->hwaddr[1]) << 16) | ((u32_t)(netif->hwaddr[2]) << 8) | (0xff)); ip_2_ip6(&netif->ip6_addr[0])->addr[3] = htonl((0xfeul << 24) | ((u32_t)(netif->hwaddr[3]) << 16) | ((u32_t)(netif->hwaddr[4]) << 8) | (netif->hwaddr[5])); } else { /* Use hwaddr directly as interface ID. */ ip_2_ip6(&netif->ip6_addr[0])->addr[2] = 0; ip_2_ip6(&netif->ip6_addr[0])->addr[3] = 0; addr_index = 3; for (i = 0; (i < 8) && (i < netif->hwaddr_len); i++) { if (i == 4) { addr_index--; } ip_2_ip6(&netif->ip6_addr[0])->addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); } } #ifdef LWIP_ESP8266 ip6_addr_set( ip_2_ip6(&netif->link_local_addr), ip_2_ip6(&netif->ip6_addr[0]) ); #endif /* Set address state. */ #if LWIP_IPV6_DUP_DETECT_ATTEMPTS /* Will perform duplicate address detection (DAD). */ netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; #else /* Consider address valid. */ netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; #endif /* LWIP_IPV6_AUTOCONFIG */ }
/** * create a new group * * @param ifp the network interface for which to create * @param addr the new group ipv6 * @return a struct mld_group*, * NULL on memory error. */ static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr) { struct mld_group *group; group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); if (group != NULL) { group->netif = ifp; ip6_addr_set(&(group->group_address), addr); group->timer = 0; /* Not running */ group->group_state = MLD6_GROUP_IDLE_MEMBER; group->last_reporter_flag = 0; group->use = 0; group->next = mld_group_list; mld_group_list = group; } return group; }