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: 9 410 2.2 %
Date: 2026-11-09 02:10:49 Functions: 3 24 12.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14