LCOV - code coverage report
Current view: top level - vnsw/agent/services - icmpv6_handler.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 0 410 0.0 %
Date: 2026-06-18 01:51:13 Functions: 0 24 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "base/os.h"
       6             : #include <netinet/icmp6.h>
       7             : 
       8             : #include <vr_defs.h>
       9             : #include <cmn/agent_cmn.h>
      10             : #include <pkt/pkt_init.h>
      11             : #include <oper/interface_common.h>
      12             : #include "services/services_types.h"
      13             : #include <services/services_init.h>
      14             : #include <services/icmpv6_proto.h>
      15             : #include <oper/route_common.h>
      16             : #include <oper/operdb_init.h>
      17             : #include <oper/path_preference.h>
      18             : #include <oper/vn.h>
      19             : #include <boost/scoped_array.hpp>
      20             : 
      21             : const Ip6Address::bytes_type Icmpv6Handler::kPrefix =
      22             :     { {0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0xFF, 0, 0, 0} };
      23             : const Ip6Address::bytes_type Icmpv6Handler::kSuffix =
      24             :     { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF} };
      25             : const Ip6Address Icmpv6Handler::kSolicitedNodeIpPrefix(kPrefix);
      26             : const Ip6Address Icmpv6Handler::kSolicitedNodeIpSuffixMask(kSuffix);
      27             : const uint8_t Icmpv6Handler::kIPv6AddrUnspecifiedBytes[IPV6_ADDR_SIZE_BYTES] = { 0 };
      28             : 
      29           0 : Icmpv6Handler::Icmpv6Handler(Agent *agent, boost::shared_ptr<PktInfo> info,
      30           0 :                              boost::asio::io_context &io)
      31           0 :     : ProtoHandler(agent, info, io), icmp_(pkt_info_->transp.icmp6) {
      32             :     // payload length - length of ipv6 extension headers
      33           0 :     if (icmp_)
      34           0 :         icmp_len_ = ntohs(pkt_info_->ip6->ip6_plen) + sizeof(ip6_hdr) -
      35           0 :                     ((uint8_t *)icmp_ - (uint8_t *)pkt_info_->ip6);
      36             :     else
      37           0 :         icmp_len_ = 0;
      38           0 :     refcount_ = 0;
      39           0 : }
      40             : 
      41           0 : Icmpv6Handler::~Icmpv6Handler() {
      42           0 : }
      43             : 
      44           0 : bool Icmpv6Handler::Run() {
      45           0 :     assert(agent());
      46           0 :     assert(agent()->icmpv6_proto());
      47             : 
      48           0 :     switch(pkt_info_->type) {
      49           0 :         case PktType::MESSAGE:
      50           0 :             return HandleMessage();
      51             : 
      52           0 :         default:
      53           0 :             return HandlePacket();
      54             :     }
      55             : }
      56             : 
      57           0 : bool Icmpv6Handler::HandlePacket() {
      58           0 :     Icmpv6Proto *icmpv6_proto = agent()->icmpv6_proto();
      59             :     Interface *itf =
      60           0 :         agent()->interface_table()->FindInterface(GetInterfaceIndex());
      61           0 :     if (itf == NULL) {
      62           0 :         icmpv6_proto->IncrementStatsDrop();
      63           0 :         ICMPV6_TRACE(Trace, "Received ICMP from invalid interface");
      64           0 :         return true;
      65             :     }
      66           0 :     if (itf->type() != Interface::VM_INTERFACE) {
      67           0 :         icmpv6_proto->IncrementStatsDrop();
      68           0 :         ICMPV6_TRACE(Trace, "Received ICMP from non-vm interface");
      69           0 :         return true;
      70             :     }
      71           0 :     VmInterface *vm_itf = static_cast<VmInterface *>(itf);
      72           0 :     if (!vm_itf->layer3_forwarding() || !vm_itf->ipv6_active()) {
      73           0 :         icmpv6_proto->IncrementStatsDrop();
      74           0 :         ICMPV6_TRACE(Trace, "Received ICMP with l3 disabled / ipv6 inactive");
      75           0 :         return true;
      76             :     }
      77           0 :     nd_neighbor_advert *icmp = (nd_neighbor_advert *)icmp_;
      78           0 :     nd_neighbor_solicit *ns = (nd_neighbor_solicit *)icmp_;
      79           0 :     switch (icmp_->icmp6_type) {
      80           0 :         case ND_ROUTER_SOLICIT:
      81           0 :             icmpv6_proto->IncrementStatsRouterSolicit(vm_itf);
      82           0 :             if (CheckPacket()) {
      83           0 :                 Ip6Address prefix;
      84             :                 uint8_t plen;
      85           0 :                 if (vm_itf->vn() &&
      86           0 :                     vm_itf->vn()->GetPrefix(vm_itf->primary_ip6_addr(),
      87             :                                             &prefix, &plen)) {
      88           0 :                     boost::system::error_code ec;
      89           0 :                     Ip6Address src_addr = Ip6Address::from_string(PKT0_LINKLOCAL_ADDRESS, ec);
      90             :                     uint32_t interface =
      91           0 :                         (pkt_info_->agent_hdr.cmd == AgentHdr::TRAP_TOR_CONTROL_PKT) ?
      92           0 :                         pkt_info_->agent_hdr.cmd_param : GetInterfaceIndex();
      93           0 :                     VmInterface *vmi = NULL;
      94             :                     Interface *intf =
      95           0 :                         agent()->interface_table()->FindInterface(interface);
      96           0 :                     if (intf->type() == Interface::VM_INTERFACE) {
      97           0 :                         vmi = static_cast<VmInterface *>(intf);
      98             :                     }
      99           0 :                     SendRAResponse(interface,
     100           0 :                                    pkt_info_->vrf,
     101           0 :                                    src_addr.to_bytes().data(),
     102           0 :                                    pkt_info_->ip_saddr.to_v6().to_bytes().data(),
     103           0 :                                    MacAddress(pkt_info_->eth->ether_shost), prefix, plen);
     104           0 :                     icmpv6_proto->IncrementStatsRouterAdvert(vmi);
     105           0 :                     return true;
     106             :                 }
     107           0 :                 ICMPV6_TRACE(Trace, "Ignoring ND Router Solicit : VN / prefix not present");
     108             :             } else {
     109           0 :                 ICMPV6_TRACE(Trace, "Ignoring Echo request with wrong cksum");
     110             :             }
     111           0 :             break;
     112             : 
     113           0 :         case ICMP6_ECHO_REQUEST:
     114           0 :             icmpv6_proto->IncrementStatsPingRequest(vm_itf);
     115           0 :             if (CheckPacket()) {
     116           0 :                 SendPingResponse();
     117           0 :                 icmpv6_proto->IncrementStatsPingResponse(vm_itf);
     118           0 :                 return true;
     119             :             }
     120           0 :             ICMPV6_TRACE(Trace, "Ignoring Echo request with wrong cksum");
     121           0 :             break;
     122             : 
     123           0 :         case ND_NEIGHBOR_ADVERT:
     124           0 :             if (icmp->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED) {
     125           0 :                 icmpv6_proto->IncrementStatsNeighborAdvertSolicited(vm_itf);
     126             :             } else {
     127           0 :                 icmpv6_proto->IncrementStatsNeighborAdvertUnSolicited(vm_itf);
     128             :             }
     129           0 :             if (CheckPacket()) {
     130             :                 Ip6Address::bytes_type bytes;
     131           0 :                 for (int i = 0; i < 16; i++) {
     132           0 :                     bytes[i] = icmp->nd_na_target.s6_addr[i];
     133             :                 }
     134           0 :                 Ip6Address addr(bytes);
     135           0 :                 uint16_t offset = sizeof(nd_neighbor_advert);
     136           0 :                 nd_opt_hdr *opt = (nd_opt_hdr *) (((uint8_t *)icmp) + offset);
     137           0 :                 if (opt->nd_opt_type != ND_OPT_TARGET_LINKADDR) {
     138           0 :                     ICMPV6_TRACE(Trace, "Ignoring Neighbor Advert with no"
     139             :                                  "Target Link-layer address option");
     140           0 :                     icmpv6_proto->IncrementStatsDrop();
     141           0 :                     return true;
     142             :                 }
     143             : 
     144           0 :                 uint8_t *buf = (((uint8_t *)icmp) + offset + 2);
     145           0 :                 MacAddress mac(buf);
     146             : 
     147             :                 //Enqueue a request to trigger state machine
     148             :                 agent()->oper_db()->route_preference_module()->
     149           0 :                     EnqueueTrafficSeen(addr, 128, itf->id(),
     150             :                                        itf->vrf()->vrf_id(), mac);
     151             : 
     152           0 :                 icmpv6_proto->HandlePathPreferenceNA(itf->vrf(), itf->id(),
     153           0 :                                                     IpAddress(addr));
     154           0 :                 NdpKey key(addr, itf->vrf());
     155           0 :                 NdpEntry *entry = icmpv6_proto->FindNdpEntry(key);
     156           0 :                 if (entry) {
     157           0 :                     entry->EnqueueNaIn(*icmp, mac);
     158             :                 }
     159           0 :                 return true;
     160             :             }
     161           0 :             ICMPV6_TRACE(Trace, "Ignoring Neighbor Advert with wrong cksum");
     162           0 :             break;
     163             : 
     164           0 :         case ND_NEIGHBOR_SOLICIT:
     165           0 :             icmpv6_proto->IncrementStatsNeighborSolicited(vm_itf);
     166           0 :             if (CheckPacket()) {
     167             :                 Ip6Address::bytes_type bytes;
     168           0 :                 for (int i = 0; i < 16; i++) {
     169           0 :                     bytes[i] = pkt_info_->ip6->ip6_src.s6_addr[i];
     170             :                 }
     171           0 :                 Ip6Address addr(bytes);
     172           0 :                 uint16_t offset = sizeof(nd_neighbor_solicit);
     173           0 :                 nd_opt_hdr *opt = (nd_opt_hdr *) (((uint8_t *)ns) + offset);
     174           0 :                 if (addr.is_unspecified()) {
     175           0 :                     ICMPV6_TRACE(Trace, "Ignoring Neighbor Solicit with "
     176             :                                  "unspecified address");
     177           0 :                     icmpv6_proto->IncrementStatsDrop();
     178           0 :                     return true;
     179             :                 }
     180           0 :                 uint8_t *buf = NULL;
     181           0 :                 if (opt->nd_opt_type == ND_OPT_TARGET_LINKADDR) {
     182           0 :                     offset += 2;
     183           0 :                     buf = (((uint8_t *)icmp) + offset);
     184             :                 }
     185             : 
     186             :                 //Enqueue a request to trigger state machine
     187           0 :                 NdpKey key(addr, itf->vrf());
     188           0 :                 NdpEntry *entry = icmpv6_proto->FindNdpEntry(key);
     189           0 :                 bool ret = true;
     190           0 :                 if (!entry) {
     191           0 :                     entry = new NdpEntry(io_, this, key, itf->vrf(), itf);
     192           0 :                     if (!icmpv6_proto->AddNdpEntry(entry)) {
     193           0 :                         delete entry;
     194           0 :                         return true;
     195             :                     }
     196           0 :                     ret = false;
     197             :                 }
     198           0 :                 if (entry) {
     199           0 :                     if (buf) {
     200           0 :                         entry->HandleNsRequest(*ns, MacAddress(buf));
     201             :                     } else {
     202           0 :                         entry->HandleNsRequest(*ns, MacAddress());
     203             :                     }
     204             :                 }
     205           0 :                 return ret;
     206             :             }
     207           0 :             ICMPV6_TRACE(Trace, "Ignoring Neighbor Solicit with wrong cksum");
     208           0 :             break;
     209           0 :         default:
     210           0 :             break;
     211             :     }
     212           0 :     icmpv6_proto->IncrementStatsDrop();
     213           0 :     return true;
     214             : }
     215             : 
     216           0 : bool Icmpv6Handler::HandleMessage() {
     217           0 :     bool ret = true;
     218             :     Icmpv6Proto::Icmpv6Ipc *ipc = static_cast<Icmpv6Proto::Icmpv6Ipc *>(
     219           0 :                                   pkt_info_->ipc);
     220           0 :     Icmpv6Proto *icmp_proto = agent()->icmpv6_proto();
     221           0 :     switch(pkt_info_->ipc->cmd) {
     222           0 :         case Icmpv6Proto::NDP_RESOLVE: {
     223           0 :             NdpEntry *entry = icmp_proto->FindNdpEntry(ipc->key);
     224           0 :             if (!entry) {
     225           0 :                 entry = new NdpEntry(io_, this, ipc->key, ipc->key.vrf,
     226           0 :                                      ipc->interface_.get());
     227           0 :                 if (icmp_proto->AddNdpEntry(entry) == false) {
     228           0 :                     delete entry;
     229           0 :                     break;
     230             :                 }
     231           0 :                 ret = false;
     232             :             }
     233           0 :             const Interface* intf = ipc->interface_.get();
     234           0 :             const VmInterface *vmi = dynamic_cast<const VmInterface *>(intf);
     235           0 :             uint32_t vrf_id = intf->vrf_id();;
     236           0 :             IpAddress ip = agent()->router_id6();
     237           0 :             if (vrf_id != VrfEntry::kInvalidIndex) {
     238           0 :                 if (ip.is_v6()) {
     239           0 :                     SendNeighborSolicit(ip.to_v6(), ipc->key.ip, vmi, vrf_id);
     240           0 :                     VmInterface *vmif = const_cast<VmInterface *>(vmi);
     241           0 :                     icmp_proto->IncrementStatsNeighborSolicited(vmif);
     242             :                 }
     243             :             }
     244           0 :             break;
     245             :         }
     246             : 
     247           0 :         case Icmpv6Proto::NDP_DELETE: {
     248           0 :             EntryDelete(ipc->key);
     249           0 :             break;
     250             :         }
     251             : 
     252           0 :         case Icmpv6Proto::NDP_SEND_UNSOL_NA: {
     253           0 :             bool key_valid = false;
     254             :             Icmpv6Proto::UnsolNaIterator it =
     255           0 :                 icmp_proto->UnsolNaEntryIterator(ipc->key, &key_valid);
     256           0 :             if (key_valid && !ipc->interface_->IsDeleted()) {
     257           0 :                 NdpEntry *entry = NULL;
     258           0 :                 Icmpv6Proto::NdpEntrySet::iterator sit = it->second.begin();
     259           0 :                 for (; sit != it->second.end(); sit++) {
     260           0 :                     entry = *sit;
     261           0 :                     if (entry->get_interface() == ipc->interface_.get())
     262           0 :                         break;
     263             :                 }
     264           0 :                 if (sit == it->second.end()) {
     265           0 :                     entry = new NdpEntry(io_, this, ipc->key, ipc->key.vrf,
     266           0 :                                          ipc->interface_.get());
     267           0 :                     it->second.insert(entry);
     268           0 :                     ret = false;
     269             :                 }
     270           0 :                 if (entry) {
     271           0 :                     entry->SendNeighborAdvert(false);
     272             :                     const VmInterface *vmi = static_cast<const VmInterface *>(
     273           0 :                                                        ipc->interface_.get());
     274           0 :                     VmInterface *vmif = const_cast<VmInterface *>(vmi);
     275           0 :                     icmp_proto->IncrementStatsNeighborAdvertUnSolicited(vmif);
     276             :                 }
     277             :             }
     278           0 :             break;
     279             :         }
     280             : 
     281           0 :         default:
     282           0 :             ICMPV6_TRACE(Trace, "Received Invalid internal NDP message : " +
     283             :                       integerToString(pkt_info_->ipc->cmd));
     284           0 :             break;
     285             :     }
     286             : 
     287           0 :     delete ipc;
     288           0 :     return ret;
     289             : }
     290             : 
     291           0 : void Icmpv6Handler::EntryDelete(NdpKey &key) {
     292           0 :     Icmpv6Proto *icmp_proto = agent()->icmpv6_proto();
     293           0 :     NdpEntry *entry = icmp_proto->FindNdpEntry(key);
     294           0 :     if (entry) {
     295             :         // this request comes when NDP NH is deleted; nothing more to do
     296           0 :         icmp_proto->DeleteNdpEntry(entry);
     297             :     }
     298           0 : }
     299             : 
     300           0 : bool Icmpv6Handler::RouterAdvertisement(Icmpv6Proto *proto) {
     301           0 :     const Icmpv6Proto::VmInterfaceMap &interfaces = proto->vm_interfaces();
     302           0 :     boost::system::error_code ec;
     303           0 :     Ip6Address src_addr = Ip6Address::from_string(PKT0_LINKLOCAL_ADDRESS, ec);
     304           0 :     Ip6Address dest_addr = Ip6Address::from_string(IPV6_ALL_NODES_ADDRESS, ec);
     305             :     // Ethernet mcast address corresponding to IPv6 mcast address ff02::1
     306           0 :     MacAddress dest_mac(0x33, 0x33, 0x00, 0x00, 0x00, 0x01);
     307           0 :     for (Icmpv6Proto::VmInterfaceMap::const_iterator it = interfaces.begin();
     308           0 :          it != interfaces.end(); ++it) {
     309           0 :         VmInterface *vmi = it->first;
     310           0 :         if (vmi->vmi_type() == VmInterface::VHOST ||
     311           0 :             (vmi->vmi_type() == VmInterface::BAREMETAL &&
     312           0 :              vmi->vn() != NULL && vmi->vn()->lr_vrf() != NULL)) {
     313           0 :             continue;
     314             :         }
     315             : 
     316           0 :         if (vmi->IsIpv6Active() && !vmi->HasServiceVlan()) {
     317           0 :             pkt_info_->AllocPacketBuffer(agent(), PktHandler::ICMPV6, ICMP_PKT_SIZE, 0);
     318           0 :             pkt_info_->eth = (struct ether_header *)(pkt_info_->pkt);
     319           0 :             pkt_info_->ip6 = (ip6_hdr *)(pkt_info_->pkt + sizeof(struct ether_header));
     320             :             /* For baremetal/local vm set the appropriate information that will be used
     321             :              * to decide to route/switch the RA packet to the TOR switch
     322             :              */
     323           0 :             pkt_info_->agent_hdr.cmd =
     324           0 :                 (vmi->vmi_type() == VmInterface::BAREMETAL) ?
     325             :                     AgentHdr::TRAP_TOR_CONTROL_PKT:AgentHdr::TX_SWITCH;
     326           0 :             uint32_t vlan_offset = 0;
     327           0 :             if (vmi->tx_vlan_id() != VmInterface::kInvalidVlanId &&
     328           0 :                   !(agent()->tsn_enabled())) {
     329           0 :                 vlan_offset += 4;
     330             :             }
     331           0 :             icmp_ = pkt_info_->transp.icmp6 =
     332           0 :                 (icmp6_hdr *)(pkt_info_->pkt + sizeof(struct ether_header) +
     333           0 :                               vlan_offset + sizeof(ip6_hdr));
     334           0 :             Ip6Address prefix;
     335             :             uint8_t plen;
     336           0 :             if (vmi->vn()->GetPrefix(vmi->primary_ip6_addr(), &prefix, &plen)) {
     337           0 :                 SendRAResponse(vmi->id(), vmi->vrf()->vrf_id(),
     338           0 :                                src_addr.to_bytes().data(),
     339           0 :                                dest_addr.to_bytes().data(),
     340             :                                dest_mac, prefix, plen);
     341           0 :                 proto->IncrementStatsRouterAdvert(vmi);
     342             :             }
     343             :         }
     344             :     }
     345             : 
     346           0 :     return true;
     347             : }
     348             : 
     349           0 : bool Icmpv6Handler::CheckPacket() {
     350           0 :     if (pkt_info_->len < (sizeof(struct ether_header) + sizeof(ip6_hdr) +
     351           0 :                           ntohs(pkt_info_->ip6->ip6_plen)))
     352           0 :         return false;
     353             : 
     354           0 :     uint16_t checksum = icmp_->icmp6_cksum;
     355           0 :     icmp_->icmp6_cksum = 0;
     356           0 :     if (checksum == Icmpv6Csum(pkt_info_->ip6->ip6_src.s6_addr,
     357           0 :                                pkt_info_->ip6->ip6_dst.s6_addr,
     358           0 :                                icmp_, icmp_len_))
     359           0 :         return true;
     360             : 
     361           0 :     return false;
     362             : }
     363             : 
     364           0 : uint16_t Icmpv6Handler::FillRouterAdvertisement(uint8_t *buf, uint32_t ifindex,
     365             :                                                 uint8_t *src, uint8_t *dest,
     366             :                                                 const Ip6Address &prefix,
     367             :                                                 uint8_t plen) {
     368           0 :     nd_router_advert *icmp = (nd_router_advert *)buf;
     369           0 :     icmp->nd_ra_type = ND_ROUTER_ADVERT;
     370           0 :     icmp->nd_ra_code = 0;
     371           0 :     icmp->nd_ra_cksum = 0;
     372           0 :     icmp->nd_ra_curhoplimit = 64;
     373           0 :     icmp->nd_ra_flags_reserved = ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER; //DHCPv6
     374           0 :     icmp->nd_ra_reachable = 0;
     375           0 :     icmp->nd_ra_retransmit = 0;
     376             : 
     377           0 :     bool def_gw = IsDefaultGatewayConfigured(ifindex, prefix);
     378           0 :     if (def_gw) {
     379           0 :         icmp->nd_ra_router_lifetime = htons(9000);
     380             :     } else {
     381           0 :         icmp->nd_ra_router_lifetime = 0;
     382             :     }
     383             : 
     384             :     // add source linklayer address information
     385           0 :     uint16_t offset = sizeof(nd_router_advert);
     386           0 :     nd_opt_hdr *src_linklayer_addr = (nd_opt_hdr *)(buf + offset);
     387           0 :     src_linklayer_addr->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
     388           0 :     src_linklayer_addr->nd_opt_len = 1;
     389             :     //XXX instead of ETHER_ADDR_LEN, actual buffer size should be given
     390             :     //to preven buffer overrun.
     391           0 :     agent()->vrrp_mac().ToArray(buf + offset + 2, ETHER_ADDR_LEN);
     392             : 
     393             :     // add prefix information
     394           0 :     offset += sizeof(nd_opt_hdr) + ETHER_ADDR_LEN;
     395           0 :     nd_opt_prefix_info *prefix_info = (nd_opt_prefix_info *)(buf + offset);
     396           0 :     prefix_info->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
     397           0 :     prefix_info->nd_opt_pi_len = 4;
     398           0 :     prefix_info->nd_opt_pi_prefix_len = plen;
     399             :     // not setting ND_OPT_PI_FLAG_AUTO or ND_OPT_PI_FLAG_RADDR
     400           0 :     prefix_info->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK;
     401           0 :     prefix_info->nd_opt_pi_valid_time = htonl(0xFFFFFFFF);
     402           0 :     prefix_info->nd_opt_pi_preferred_time = htonl(0xFFFFFFFF);
     403           0 :     prefix_info->nd_opt_pi_reserved2 = 0;
     404           0 :     memcpy(prefix_info->nd_opt_pi_prefix.s6_addr, prefix.to_bytes().data(), 16);
     405             : 
     406           0 :     offset += sizeof(nd_opt_prefix_info);
     407           0 :     icmp->nd_ra_cksum = Icmpv6Csum(src, dest, (icmp6_hdr *)icmp, offset);
     408             : 
     409           0 :     return offset;
     410             : }
     411             : 
     412           0 : void Icmpv6Handler::SendRAResponse(uint32_t ifindex, uint32_t vrfindex,
     413             :                                    uint8_t *src_ip, uint8_t *dest_ip,
     414             :                                    const MacAddress &dest_mac,
     415             :                                    const Ip6Address &prefix, uint8_t plen) {
     416             :     // fill in the response
     417           0 :     uint16_t len = FillRouterAdvertisement((uint8_t *)icmp_, ifindex, src_ip,
     418             :                                            dest_ip, prefix, plen);
     419           0 :     SendIcmpv6Response(ifindex, vrfindex, src_ip, dest_ip, dest_mac, len);
     420           0 : }
     421             : 
     422           0 : void Icmpv6Handler::SendPingResponse() {
     423           0 :     icmp_->icmp6_type = ICMP6_ECHO_REPLY;
     424           0 :     icmp_->icmp6_cksum = 0;
     425           0 :     icmp_->icmp6_cksum =
     426           0 :         Icmpv6Csum(pkt_info_->ip_daddr.to_v6().to_bytes().data(),
     427           0 :                    pkt_info_->ip_saddr.to_v6().to_bytes().data(),
     428           0 :                    icmp_, ntohs(pkt_info_->ip6->ip6_plen));
     429             :     uint32_t interface =
     430           0 :         (pkt_info_->agent_hdr.cmd == AgentHdr::TRAP_TOR_CONTROL_PKT) ?
     431           0 :         pkt_info_->agent_hdr.cmd_param : GetInterfaceIndex();
     432           0 :     SendIcmpv6Response(interface, pkt_info_->vrf,
     433           0 :                        pkt_info_->ip_daddr.to_v6().to_bytes().data(),
     434           0 :                        pkt_info_->ip_saddr.to_v6().to_bytes().data(),
     435           0 :                        MacAddress(pkt_info_->eth->ether_shost),
     436           0 :                        ntohs(pkt_info_->ip6->ip6_plen));
     437           0 : }
     438             : 
     439           0 : void Icmpv6Handler::SendIcmpv6Response(uint32_t ifindex, uint32_t vrfindex,
     440             :                                        uint8_t *src_ip, uint8_t *dest_ip,
     441             :                                        const MacAddress &dest_mac,
     442             :                                        uint16_t len) {
     443             : 
     444           0 :     char *buff = (char *)pkt_info_->pkt;
     445           0 :     uint16_t buff_len = pkt_info_->packet_buffer()->data_len();
     446           0 :     boost::scoped_array<char> icmpv6_payload(new char[icmp_len_]);
     447           0 :     memcpy(icmpv6_payload.get(),icmp_,icmp_len_);
     448           0 :     uint16_t eth_len = EthHdr(buff, buff_len, ifindex, agent()->vrrp_mac(),
     449           0 :                               dest_mac, ETHERTYPE_IPV6);
     450             : 
     451           0 :     pkt_info_->ip6 = (struct ip6_hdr *)(buff + eth_len);
     452           0 :     Ip6Hdr(pkt_info_->ip6, len, IPV6_ICMP_NEXT_HEADER, 255, src_ip, dest_ip);
     453           0 :     memcpy(buff + sizeof(ip6_hdr) + eth_len, icmpv6_payload.get(), icmp_len_);
     454           0 :     pkt_info_->set_len(len + sizeof(ip6_hdr) + eth_len);
     455             :     uint16_t command =
     456           0 :         (pkt_info_->agent_hdr.cmd == AgentHdr::TRAP_TOR_CONTROL_PKT) ?
     457           0 :         (uint16_t)AgentHdr::TX_ROUTE : AgentHdr::TX_SWITCH;
     458           0 :     Send(ifindex, vrfindex, command, PktHandler::ICMPV6);
     459           0 : }
     460             : 
     461           0 : bool Icmpv6Handler::IsIPv6AddrUnspecifiedBytes(const uint8_t *ip) {
     462           0 :     return !memcmp(ip, Icmpv6Handler::kIPv6AddrUnspecifiedBytes, IPV6_ADDR_SIZE_BYTES);
     463             : }
     464             : 
     465           0 : uint16_t Icmpv6Handler::FillNeighborSolicit(uint8_t *buf,
     466             :                                             const Ip6Address &target,
     467             :                                             uint8_t *sip, uint8_t *dip) {
     468           0 :     nd_neighbor_solicit *icmp = (nd_neighbor_solicit *)buf;
     469           0 :     icmp->nd_ns_type = ND_NEIGHBOR_SOLICIT;
     470           0 :     icmp->nd_ns_code = 0;
     471           0 :     icmp->nd_ns_cksum = 0;
     472           0 :     icmp->nd_ns_reserved = 0;
     473           0 :     memcpy(icmp->nd_ns_target.s6_addr, target.to_bytes().data(), 16);
     474           0 :     uint16_t offset = sizeof(nd_neighbor_solicit);
     475             : 
     476           0 :     if (!IsIPv6AddrUnspecifiedBytes(sip)) {
     477             :         // add source linklayer address information
     478           0 :         nd_opt_hdr *src_linklayer_addr = (nd_opt_hdr *)(buf + offset);
     479           0 :         src_linklayer_addr->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
     480           0 :         src_linklayer_addr->nd_opt_len = 1;
     481             :         //XXX instead of ETHER_ADDR_LEN, actual buffer size should be given
     482             :         //to preven buffer overrun.
     483           0 :         agent()->vrrp_mac().ToArray(buf + offset + 2, ETHER_ADDR_LEN);
     484           0 :         offset += sizeof(nd_opt_hdr) + ETHER_ADDR_LEN;
     485             :     }
     486             : 
     487           0 :     icmp->nd_ns_cksum = Icmpv6Csum(sip, dip, (icmp6_hdr *)icmp, offset);
     488           0 :     return offset;
     489             : }
     490             : 
     491           0 : uint16_t Icmpv6Handler::FillNeighborAdvertisement(uint8_t *buf,
     492             :                                                   uint8_t *dip, uint8_t *sip,
     493             :                                                   const Ip6Address &target,
     494             :                                                   const MacAddress &dmac,
     495             :                                                   bool solicited) {
     496           0 :     nd_neighbor_advert *icmp = (nd_neighbor_advert *)buf;
     497           0 :     icmp->nd_na_type = ND_NEIGHBOR_ADVERT;
     498           0 :     icmp->nd_na_code = 0;
     499           0 :     icmp->nd_na_cksum = 0;
     500           0 :     icmp->nd_na_flags_reserved = 0;
     501           0 :     memcpy(icmp->nd_na_target.s6_addr, target.to_bytes().data(), 16);
     502           0 :     if (solicited)
     503           0 :         icmp->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED;
     504           0 :     uint16_t offset = sizeof(nd_neighbor_advert);
     505             : 
     506             :     // add source linklayer address information
     507           0 :     nd_opt_hdr *src_linklayer_addr = (nd_opt_hdr *)(buf + offset);
     508           0 :     src_linklayer_addr->nd_opt_type = ND_OPT_TARGET_LINKADDR;
     509           0 :     src_linklayer_addr->nd_opt_len = 1;
     510             :     //XXX instead of ETHER_ADDR_LEN, actual buffer size should be given
     511             :     //to preven buffer overrun.
     512           0 :     dmac.ToArray(buf + offset + 2, ETHER_ADDR_LEN);
     513           0 :     offset += sizeof(nd_opt_hdr) + ETHER_ADDR_LEN;
     514             : 
     515           0 :     icmp->nd_na_cksum = Icmpv6Csum(sip, dip, (icmp6_hdr *)icmp, offset);
     516           0 :     return offset;
     517             : }
     518             : 
     519           0 : void Icmpv6Handler::Ipv6Lower24BitsExtract(uint8_t *dst, uint8_t *src) {
     520           0 :     for (int i = 0; i < 16; i++) {
     521           0 :         dst[i] &= src[i];
     522             :     }
     523           0 : }
     524             : 
     525           0 : void Icmpv6Handler::Ipv6AddressBitwiseOr(uint8_t *dst, uint8_t *src) {
     526           0 :     for (int i = 0; i < 16; i++) {
     527           0 :         dst[i] |= src[i];
     528             :     }
     529           0 : }
     530             : 
     531           0 : void Icmpv6Handler::SolicitedMulticastIpAndMac(const Ip6Address &dip,
     532             :                                                uint8_t *ip, MacAddress &mac) {
     533             :     /* A solicited-node multicast address is formed by taking the low-order
     534             :      * 24 bits of an address (unicast or anycast) and appending those bits to
     535             :      * the prefix FF02:0:0:0:0:1:FF00::/104 */
     536             : 
     537             :     /* Copy the higher order 104 bits of solicited node multicast IP */
     538           0 :     memcpy(ip, kSolicitedNodeIpPrefix.to_bytes().data(), 16);
     539             : 
     540             :     uint8_t ip_bytes[16], suffix_mask_bytes[16];
     541           0 :     memcpy(ip_bytes, dip.to_bytes().data(), 16);
     542           0 :     memcpy(suffix_mask_bytes, kSolicitedNodeIpSuffixMask.to_bytes().data(), 16);
     543             :     /* Extract lower order 24 bits of Destination IP */
     544           0 :     Ipv6Lower24BitsExtract(ip_bytes, suffix_mask_bytes);
     545             : 
     546             :     /* Build the solicited node multicast address by joining upper order 104
     547             :      * bits of FF02:0:0:0:0:1:FF00::/104 with lower 24 bits of destination IP*/
     548           0 :     Ipv6AddressBitwiseOr(ip, ip_bytes);
     549             : 
     550             :     /* The ethernet address for IPv6 multicast address is 0x33-33-mm-mm-mm-mm,
     551             :      * where mm-mm-mm-mm is a direct mapping of the last 32 bits of the
     552             :      * IPv6 multicast address */
     553           0 :     mac[0] = mac[1] = 0x33;
     554           0 :     mac[2] = ip[12];
     555           0 :     mac[3] = ip[13];
     556           0 :     mac[4] = ip[14];
     557           0 :     mac[5] = ip[15];
     558           0 : }
     559             : 
     560           0 : void Icmpv6Handler::SendNeighborAdvert(const Ip6Address &sip,
     561             :                                        const Ip6Address &tip,
     562             :                                        const MacAddress &smac,
     563             :                                        const MacAddress &dmac,
     564             :                                        uint32_t itf, uint32_t vrf,
     565             :                                        bool solicited) {
     566           0 :     if (pkt_info_->packet_buffer() == NULL) {
     567           0 :         pkt_info_->AllocPacketBuffer(agent(), PktHandler::ICMPV6, ICMP_PKT_SIZE,
     568             :                                      0);
     569             :     }
     570             : 
     571           0 :     char *buf = (char *)pkt_info_->pkt;
     572           0 :     memset(buf, 0, pkt_info_->max_pkt_len);
     573           0 :     pkt_info_->eth = (struct ether_header *)buf;
     574           0 :     pkt_info_->ip6 = (ip6_hdr *)(pkt_info_->pkt + sizeof(struct ether_header));
     575           0 :     icmp_ = pkt_info_->transp.icmp6 =
     576           0 :             (icmp6_hdr *)(pkt_info_->pkt + sizeof(struct ether_header) +
     577             :                           sizeof(ip6_hdr));
     578             :     uint8_t dip[16], source_ip[16];
     579           0 :     memcpy(source_ip, sip.to_bytes().data(), sizeof(source_ip));
     580           0 :     boost::system::error_code ec;
     581           0 :     Ip6Address ip = Ip6Address::from_string(IPV6_ALL_NODES_ADDRESS, ec);
     582           0 :     memcpy(dip, ip.to_bytes().data(), sizeof(dip));
     583           0 :     uint16_t len = FillNeighborAdvertisement((uint8_t *)icmp_, &dip[0], &source_ip[0],
     584             :                                              tip, dmac, solicited);
     585           0 :     icmp_len_ = len;
     586           0 :     SendIcmpv6Response(itf, vrf, source_ip, &dip[0], dmac, len);
     587           0 : }
     588             : 
     589           0 : void Icmpv6Handler::SendNeighborSolicit(const Ip6Address &sip,
     590             :                                         const Ip6Address &tip,
     591             :                                         const VmInterface *vmi,
     592             :                                         uint32_t vrf, bool send_unicast) {
     593           0 :     if (pkt_info_->packet_buffer() == NULL) {
     594           0 :         pkt_info_->AllocPacketBuffer(agent(), PktHandler::ICMPV6, ICMP_PKT_SIZE,
     595             :                                      0);
     596             :     }
     597             : 
     598           0 :     pkt_info_->eth = (struct ether_header *)(pkt_info_->pkt);
     599           0 :     pkt_info_->ip6 = (ip6_hdr *)(pkt_info_->pkt + sizeof(struct ether_header));
     600           0 :     uint32_t vlan_offset = 0;
     601           0 :     if (vmi && vmi->tx_vlan_id() != VmInterface::kInvalidVlanId &&
     602           0 :           !(agent()->tsn_enabled())) {
     603           0 :         vlan_offset += 4;
     604             :     }
     605           0 :     icmp_ = pkt_info_->transp.icmp6 =
     606           0 :             (icmp6_hdr *)(pkt_info_->pkt + sizeof(struct ether_header) +
     607           0 :                           vlan_offset + sizeof(ip6_hdr));
     608             :     uint8_t solicited_mcast_ip[16], source_ip_bytes[16];
     609           0 :     MacAddress dmac;
     610           0 :     memcpy(source_ip_bytes, sip.to_bytes().data(), sizeof(source_ip_bytes));
     611           0 :     if (send_unicast)
     612           0 :         memcpy(solicited_mcast_ip, tip.to_bytes().data(), sizeof(solicited_mcast_ip));
     613             :     else
     614           0 :         SolicitedMulticastIpAndMac(tip, solicited_mcast_ip, dmac);
     615           0 :     uint16_t len = FillNeighborSolicit((uint8_t *)icmp_, tip, source_ip_bytes,
     616             :                                        solicited_mcast_ip);
     617           0 :     SendIcmpv6Response(vmi?vmi->id():0, vrf, source_ip_bytes,
     618             :                        solicited_mcast_ip, dmac, len);
     619           0 : }
     620             : 
     621           0 : bool Icmpv6Handler::IsDefaultGatewayConfigured(uint32_t ifindex,
     622             :                                                const Ip6Address &addr) {
     623           0 :     Interface *intf = agent()->interface_table()->FindInterface(ifindex);
     624           0 :     if (!intf || intf->type() != Interface::VM_INTERFACE) {
     625           0 :         return false;
     626             :     }
     627           0 :     VmInterface *vmi = static_cast<VmInterface *>(intf);
     628           0 :     if (!vmi->vn()) {
     629           0 :         return false;
     630             :     }
     631           0 :     const VnIpam *ipam = vmi->vn()->GetIpam(addr);
     632           0 :     if (!ipam || !ipam->default_gw.is_v6()) {
     633           0 :         return false;
     634             :     }
     635           0 :     return !(ipam->default_gw.to_v6().is_unspecified());
     636             : }
     637             : 
     638           0 : void intrusive_ptr_add_ref(const Icmpv6Handler *p) {
     639           0 :     p->refcount_++;
     640           0 : }
     641             : 
     642           0 : void intrusive_ptr_release(const Icmpv6Handler *p) {
     643           0 :     if (p->refcount_.fetch_sub(1) == 1) {
     644           0 :         delete p;
     645             :     }
     646           0 : }

Generated by: LCOV version 1.14