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 1 : Icmpv6Handler::Icmpv6Handler(Agent *agent, boost::shared_ptr<PktInfo> info,
30 1 : boost::asio::io_context &io)
31 1 : : ProtoHandler(agent, info, io), icmp_(pkt_info_->transp.icmp6) {
32 : // payload length - length of ipv6 extension headers
33 1 : 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 1 : icmp_len_ = 0;
38 1 : refcount_ = 0;
39 1 : }
40 :
41 2 : Icmpv6Handler::~Icmpv6Handler() {
42 2 : }
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 : }
|