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 : }
|