+ return add_key_and_send_arp(l3->ip_hash, ip_dst, &l3->arp_table[ret], tsc, hz, l3->arp_ndp_retransmit_timeout, MAX_HOP_INDEX, time);
+ } else {
+ // IP has been found
+ return update_mac_and_send_mbuf(&l3->arp_table[ret], mac, tsc, hz, l3->arp_ndp_retransmit_timeout, time);
+ }
+ }
+ // Should not happen
+ return DROP_MBUF;
+}
+
+int write_ip6_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, struct ipv6_addr *ip_dst, uint16_t *vlan)
+{
+ const uint64_t hz = rte_get_tsc_hz();
+ prox_rte_ether_hdr *packet = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+ prox_rte_ether_addr *mac = &packet->d_addr;
+ struct ipv6_addr *used_ip_src;
+
+ uint64_t tsc = rte_rdtsc();
+ uint16_t len = rte_pktmbuf_pkt_len(mbuf);
+
+ struct ipv6_addr *pkt_src_ip6;
+ if ((pkt_src_ip6 = find_ip6(packet, len, ip_dst, vlan)) == NULL) {
+ // Unable to find IP address => non IP packet => send it as it
+ return SEND_MBUF;
+ }
+ struct l3_base *l3 = &(tbase->l3);
+
+ // Configure source IP
+ if (memcmp(&l3->local_ipv6, ip_dst, 8) == 0) {
+ // Same prefix as local -> use local
+ used_ip_src = &l3->local_ipv6;
+ } else if (memcmp(&l3->global_ipv6 , ip_dst, 8) == 0) {
+ // Same prefix as global -> use global
+ used_ip_src = &l3->global_ipv6;
+ } else if (memcmp(&l3->gw.ip6 , &null_addr, sizeof(struct ipv6_addr)) != 0) {
+ used_ip_src = &l3->global_ipv6;
+ memcpy(ip_dst, &l3->gw.ip6, sizeof(struct ipv6_addr));
+ } else if (memcmp(&l3->global_ipv6 , &null_addr, sizeof(struct ipv6_addr)) != 0) {
+ // Global IP is defined -> use it
+ used_ip_src = &l3->global_ipv6;
+ } else {
+ plog_info("Error as trying to send a packet to "IPv6_BYTES_FMT" using "IPv6_BYTES_FMT" (local)\n", IPv6_BYTES(ip_dst->bytes), IPv6_BYTES(l3->local_ipv6.bytes));
+ return DROP_MBUF;
+ }
+ memcpy(pkt_src_ip6, used_ip_src, sizeof(struct ipv6_addr));
+
+ // Configure dst mac
+ if (likely(l3->n_pkts < 4)) {
+ for (unsigned int idx = 0; idx < l3->n_pkts; idx++) {
+ if (memcmp(ip_dst, &l3->optimized_arp_table[idx].ip6, sizeof(struct ipv6_addr)) == 0) {
+ // IP address already in table
+ if ((tsc < l3->optimized_arp_table[idx].arp_ndp_retransmit_timeout) && (tsc < l3->optimized_arp_table[idx].reachable_timeout)) {
+ // MAC address was recently updated in table, use it
+ // plog_dbg("Valid MAC address found => send packet\n");
+ memcpy(mac, &l3->optimized_arp_table[idx].mac, sizeof(prox_rte_ether_addr));
+ return SEND_MBUF;
+ } else if (tsc > l3->optimized_arp_table[idx].arp_ndp_retransmit_timeout) {
+ // NDP not sent since a long time, send NDP
+ l3->optimized_arp_table[idx].arp_ndp_retransmit_timeout = tsc + l3->arp_ndp_retransmit_timeout * hz / 1000;
+ if (tsc < l3->optimized_arp_table[idx].reachable_timeout) {
+ // MAC still valid => also send mbuf
+ plog_dbg("Valid MAC found but NDP retransmit timeout => send packet and NDP\n");
+ memcpy(mac, &l3->optimized_arp_table[idx].mac, sizeof(prox_rte_ether_addr));
+ return SEND_MBUF_AND_ARP_ND;
+ } else {
+ plog_dbg("Unknown MAC => send NDP but cannot send packet\n");
+ // MAC unvalid => only send NDP
+ return SEND_ARP_ND;
+ }
+ } else {
+ // NDP timeout elapsed, MAC not valid anymore but waiting for NDP reply
+ // plog_dbg("NDP reachable timeout elapsed - waiting for NDP reply\n");
+ return DROP_MBUF;
+ }
+ }
+ }
+ // IP address not found in table
+ memcpy(&l3->optimized_arp_table[l3->n_pkts].ip6, ip_dst, sizeof(struct ipv6_addr));
+ l3->optimized_arp_table[l3->n_pkts].arp_ndp_retransmit_timeout = tsc + l3->arp_ndp_retransmit_timeout * hz / 1000;
+ l3->n_pkts++;
+
+ if (l3->n_pkts < 4) {
+ return SEND_ARP_ND;
+ }
+
+ // We have too many IP addresses to search linearly; lets use hash table instead => copy all entries in hash table
+ for (uint32_t idx = 0; idx < l3->n_pkts; idx++) {
+ struct ipv6_addr *ip6 = &l3->optimized_arp_table[idx].ip6;
+ int ret = rte_hash_add_key(l3->ip6_hash, (const void *)ip6);