/** * \brief Get Neighbor_AO by IP Address and send an ICMPv6 Echo Request * * \param destination ip address */ void uip_icmp6_echo_request_output(uip_ipaddr_t * dst) { Neighbor_AO * neighbor; neighbor = uip_nd6_nbrcache_lookup(dst); if (NULL != neighbor) uip_icmp6_echo_request_output_to_neighbor(neighbor); }
/** * \brief handle incoming echo request * * \param the incoming NewDataEvent */ void uip_icmp6_echo_request_input(NewDataEvent * e) { struct uip_icmp_hdr * icmp_hdr; struct uip_tcpip_hdr * tcpip_hdr; Neighbor_AO * neighbor; static QEvent const pr_sig = { PACKET_RECEIVED_SIG}; static QEvent const ps_sig = { PACKET_SENT_SIG}; tcpip_hdr = ( struct uip_tcpip_hdr *) &e->buf[UIP_LLH_LEN]; icmp_hdr = (struct uip_icmp_hdr *)&e->buf[uip_l2_l3_hdr_len]; neighbor = uip_nd6_nbrcache_lookup(&tcpip_hdr->srcipaddr); if (NULL != neighbor) QActive_postFIFO((QActive *)neighbor, &pr_sig); switch_eth_addr(e); memcpy(&tcpip_hdr->destipaddr, &tcpip_hdr->srcipaddr, sizeof(uip_ipaddr_t)); memcpy(&tcpip_hdr->srcipaddr, &uip_hostaddr, sizeof(uip_ipaddr_t)); icmp_hdr->type = ICMP6_ECHO_REPLY; icmp_hdr->icode = 0; icmp_hdr->icmpchksum = 0; icmp_hdr->icmpchksum = ~upper_layer_chksum6((unsigned char *) &e->buf); e->super.sig = SEND_DATA_SIG; QF_publish((QEvent *) e); QActive_postFIFO((QActive *)neighbor, &ps_sig); }
/** * \brief Reply incoming ICMPv6 Echo Request with an Echo Reply * * \param incoming NewDataEvent */ void uip_icmp6_echo_reply_input(NewDataEvent * e) { Neighbor_AO * neighbor; struct uip_icmpip_hdr * icmpip_hdr; static QEvent const icmpr_sig = { ICMPV6_ECHO_REPLY_SIG}; icmpip_hdr = ( struct uip_icmpip_hdr *) &e->buf[UIP_LLH_LEN]; neighbor = uip_nd6_nbrcache_lookup(&icmpip_hdr->srcipaddr); if (NULL != neighbor) QActive_postFIFO((QActive *)neighbor, &icmpr_sig); }
/*------------------------------------------------------------------*/ void uip_nd6_io_ns_input(void) { PRINTF("Received NS from"); PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINTF("to"); PRINT6ADDR(&UIP_IP_BUF->destipaddr); PRINTF("with target address"); PRINT6ADDR((uip_ipaddr_t *)(&UIP_ND6_NS_BUF->tgtipaddr)); PRINTF("\n"); UIP_STAT(++uip_stat.nd6.recv); u8_t flags; #if UIP_CONF_IPV6_CHECKS if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) || (uip_is_addr_mcast(&UIP_ND6_NS_BUF->tgtipaddr)) || (UIP_ICMP_BUF->icode != 0)) { goto badpkt; } #endif /* UIP_CONF_IPV6_CHECKS */ /* Options reading: we handle only SLLAO for now */ nd6_opt_llao = NULL; nd6_opt_offset = UIP_ND6_NS_LEN; while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) { #if UIP_CONF_IPV6_CHECKS if(UIP_ND6_OPT_HDR_BUF->len == 0) { goto badpkt; } #endif /* UIP_CONF_IPV6_CHECKS */ switch(UIP_ND6_OPT_HDR_BUF->type) { case UIP_ND6_OPT_SLLAO: nd6_opt_llao = (struct uip_nd6_opt_llao *)UIP_ND6_OPT_HDR_BUF; break; default: UIP_LOG("ND option not supported in NS"); break; } nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3); } /* Options processing: only SLLAO */ if(nd6_opt_llao != NULL) { #if UIP_CONF_IPV6_CHECKS /* There must be NO option in a DAD NS */ if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) { goto badpkt; } else { #endif /*UIP_CONF_IPV6_CHECKS*/ neighbor = uip_nd6_nbrcache_lookup(&UIP_IP_BUF->srcipaddr); if(neighbor == NULL) { /* we need to add the neighbor*/ uip_nd6_nbrcache_add(&UIP_IP_BUF->srcipaddr, &nd6_opt_llao->addr, 0, STALE); } else { /* If LL address changed, set neighbor state to stale */ if(memcmp(&nd6_opt_llao->addr, &neighbor->lladdr, UIP_LLADDR_LEN) != 0) { memcpy(&neighbor->lladdr, &nd6_opt_llao->addr, UIP_LLADDR_LEN); neighbor->state = STALE; } else { /* If neighbor state is INCOMPLETE, set to STALE */ if(neighbor->state == INCOMPLETE) { neighbor->state = STALE; } } } #if UIP_CONF_IPV6_CHECKS } #endif /*UIP_CONF_IPV6_CHECKS*/ } /* * Rest of NS processing: Depends on the purpose of the NS: NUD or DAD or * Address Resolution */ /** \note we use ifaddr to remember the target address */ ifaddr = uip_netif_addr_lookup(&UIP_ND6_NS_BUF->tgtipaddr, 128, 0); if(ifaddr != NULL) { if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)){ /* DAD CASE */ #if UIP_CONF_IPV6_CHECKS /* Dst address must be solicited node mcast address */ if(!uip_netif_is_addr_my_solicited(&UIP_IP_BUF->destipaddr)){ goto badpkt; } #endif /* UIP_CONF_IPV6_CHECKS */ /* * If my address is not tentative, then send a NA to all nodes with * TLLAO flags are: override = yes. */ if(ifaddr->state!=TENTATIVE) { /* * we need to send a NA, we set the src, dest, flags. tgt remains the * same and the rest is done at "create_na" */ uip_create_linklocal_allnodes_mcast(&UIP_IP_BUF->destipaddr); uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); flags = UIP_ND6_NA_FLAG_OVERRIDE; goto create_na; } else { /** \todo if I sent a NS before him, I win */ uip_netif_dad_failed(&UIP_ND6_NS_BUF->tgtipaddr); goto discard; } } #if UIP_CONF_IPV6_CHECKS /* Duplicate check */ if(uip_netif_is_addr_my_unicast(&UIP_IP_BUF->srcipaddr)) { /** * \NOTE do we do something here? we both are using the same address. * If we are doing dad, we could cancel it, though we should receive a * NA in response of DAD NS we sent, hence DAD will fail anyway. If we * were not doing DAD, it means there is a duplicate in the network! */ goto badpkt; } #endif /*UIP_CONF_IPV6_CHECKS*/ /* Address resolution case */ if(uip_netif_is_addr_my_solicited(&UIP_IP_BUF->destipaddr)){ /* * we need to send a NA, we set the src, dest, flags. The rest is * set at the "create_na" label. */ uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_ND6_NS_BUF->tgtipaddr); flags = UIP_ND6_NA_FLAG_SOLICITED | UIP_ND6_NA_FLAG_OVERRIDE; goto create_na; } /* * NUD CASE. at this point the packet must be for us! we check this, * and at the same time if target == dest */ if(uip_netif_addr_lookup(&UIP_IP_BUF->destipaddr, 128, 0) == ifaddr){ /* * we need to send a NA, we set the src, dest, flags. The rest is set * at the "create_na" label. */ uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_ND6_NS_BUF->tgtipaddr); flags = UIP_ND6_NA_FLAG_SOLICITED | UIP_ND6_NA_FLAG_OVERRIDE; goto create_na; } else { #if UIP_CONF_IPV6_CHECKS goto badpkt; #endif /* UIP_CONF_IPV6_CHECKS */ } } else { goto discard; } create_na: /* * Fill the part of the NA which is common to all NAs. If the NS contained * extension headers, we must set the target address properly */ uip_ext_len = 0; /* IP header */ UIP_IP_BUF->vtc = 0x60; UIP_IP_BUF->tcflow = 0; UIP_IP_BUF->flow = 0; UIP_IP_BUF->len[0] = 0; /* length will not be more than 255 */ UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_NA_LEN + UIP_ND6_OPT_LLAO_LEN; UIP_IP_BUF->proto = UIP_PROTO_ICMP6; UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT; /* ICMP header */ UIP_ICMP_BUF->type = ICMP6_NA; UIP_ICMP_BUF->icode = 0; /* NA static part */ UIP_ND6_NA_BUF->flagsreserved = flags; memcpy(&UIP_ND6_NA_BUF->tgtipaddr, &ifaddr->ipaddr, sizeof(uip_ipaddr_t)); /* NA option: TLLAO. note that length field is in unit of 8 bytes */ uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NA_LEN + UIP_ND6_OPT_LLAO_LEN; nd6_opt_llao = (struct uip_nd6_opt_llao *)&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_NA_LEN]; nd6_opt_llao->type = UIP_ND6_OPT_TLLAO; nd6_opt_llao->len = UIP_ND6_OPT_LLAO_LEN >> 3; memcpy(&(nd6_opt_llao->addr), &uip_lladdr, UIP_LLADDR_LEN); /* padding if needed */ memset((void *)(&nd6_opt_llao->addr) + UIP_LLADDR_LEN, 0, UIP_ND6_OPT_LLAO_LEN - 2 - UIP_LLADDR_LEN); /*ICMP checksum*/ UIP_ICMP_BUF->icmpchksum = 0; UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); UIP_STAT(++uip_stat.nd6.sent); PRINTF("Sending NA to"); PRINT6ADDR(&UIP_IP_BUF->destipaddr); PRINTF("from"); PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINTF("with target address"); PRINT6ADDR(&UIP_ND6_NA_BUF->tgtipaddr); PRINTF("\n"); return; #if UIP_CONF_IPV6_CHECKS badpkt: UIP_STAT(++uip_stat.nd6.drop); UIP_LOG("NS received is bad"); #endif /* UIP_CONF_IPV6_CHECKS */ discard: uip_len = 0; return; }
void tcpip_ipv6_output(void) { struct uip_nd6_neighbor *nbc = NULL; struct uip_nd6_defrouter *dr = NULL; if(uip_len == 0) return; if(uip_len > UIP_LINK_MTU){ UIP_LOG("tcpip_ipv6_output: Packet to big"); uip_len = 0; return; } if(uip_is_addr_unspecified(&UIP_IP_BUF->destipaddr)){ UIP_LOG("tcpip_ipv6_output: Destination address unspecified"); uip_len = 0; return; } if(!uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) { /*If destination is on link */ nbc = NULL; if(uip_nd6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){ nbc = uip_nd6_nbrcache_lookup(&UIP_IP_BUF->destipaddr); } else { #if UIP_CONF_ROUTER /*destination is not on link*/ uip_ipaddr_t ipaddr; uip_ipaddr_t *next_hop; /* Try to find the next hop address in the local routing table. */ next_hop = uip_router != NULL ? uip_router->lookup(&UIP_IP_BUF->destipaddr, &ipaddr) : NULL; if(next_hop != NULL) { /* Look for the next hop of the route in the neighbor cache. Add a cache entry if we can't find it. */ nbc = uip_nd6_nbrcache_lookup(next_hop); if(nbc == NULL) { nbc = uip_nd6_nbrcache_add(next_hop, NULL, 1, NO_STATE); } } else { #endif /* UIP_CONF_ROUTER */ /* No route found, check if a default router exists and use it then. */ dr = uip_nd6_choose_defrouter(); if(dr != NULL){ nbc = dr->nb; } else { /* shall we send a icmp error message destination unreachable ?*/ UIP_LOG("tcpip_ipv6_output: Destination off-link but no router"); uip_len = 0; return; } #if UIP_CONF_ROUTER } #endif /* UIP_CONF_ROUTER */ } /* there are two cases where the entry logically does not exist: * 1 it really does not exist. 2 it is in the NO_STATE state */ if (nbc == NULL || nbc->state == NO_STATE) { if (nbc == NULL) { /* create neighbor cache entry, original packet is replaced by NS*/ nbc = uip_nd6_nbrcache_add(&UIP_IP_BUF->destipaddr, NULL, 0, INCOMPLETE); } else { nbc->state = INCOMPLETE; } #if UIP_CONF_IPV6_QUEUE_PKT /* copy outgoing pkt in the queuing buffer for later transmmit */ memcpy(nbc->queue_buf, UIP_IP_BUF, uip_len); nbc->queue_buf_len = uip_len; #endif /* RFC4861, 7.2.2: * "If the source address of the packet prompting the solicitation is the * same as one of the addresses assigned to the outgoing interface, that * address SHOULD be placed in the IP Source Address of the outgoing * solicitation. Otherwise, any one of the addresses assigned to the * interface should be used."*/ if(uip_netif_is_addr_my_unicast(&UIP_IP_BUF->srcipaddr)){ uip_nd6_io_ns_output(&UIP_IP_BUF->srcipaddr, NULL, &nbc->ipaddr); } else { uip_nd6_io_ns_output(NULL, NULL, &nbc->ipaddr); } stimer_set(&(nbc->last_send), uip_netif_physical_if.retrans_timer / 1000); nbc->count_send = 1; } else { if (nbc->state == INCOMPLETE){ PRINTF("tcpip_ipv6_output: neighbor cache entry incomplete\n"); #if UIP_CONF_IPV6_QUEUE_PKT /* copy outgoing pkt in the queuing buffer for later transmmit and set the destination neighbor to nbc */ memcpy(nbc->queue_buf, UIP_IP_BUF, uip_len); nbc->queue_buf_len = uip_len; uip_len = 0; #endif /*UIP_CONF_IPV6_QUEUE_PKT*/ return; } /* if running NUD (nbc->state == STALE, DELAY, or PROBE ) keep sending in parallel see rfc 4861 Node behavior in section 7.7.3*/ if (nbc->state == STALE){ nbc->state = DELAY; stimer_set(&(nbc->reachable), UIP_ND6_DELAY_FIRST_PROBE_TIME); PRINTF("tcpip_ipv6_output: neighbor cache entry stale moving to delay\n"); } stimer_set(&(nbc->last_send), uip_netif_physical_if.retrans_timer / 1000); tcpip_output(&(nbc->lladdr)); #if UIP_CONF_IPV6_QUEUE_PKT /* Send the queued packets from here, may not be 100% perfect though. * This happens in a few cases, for example when instead of receiving a * NA after sendiong a NS, you receive a NS with SLLAO: the entry moves *to STALE, and you must both send a NA and the queued packet */ if(nbc->queue_buf_len != 0) { uip_len = nbc->queue_buf_len; memcpy(UIP_IP_BUF, nbc->queue_buf, uip_len); nbc->queue_buf_len = 0; tcpip_output(&(nbc->lladdr)); } #endif /*UIP_CONF_IPV6_QUEUE_PKT*/ uip_len = 0; return; } } /*multicast IP destination address */ tcpip_output(NULL); uip_len = 0; uip_ext_len = 0; }