Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <stdint.h>
6 : #include <math.h>
7 : #include "vr_defs.h"
8 : #include "init/agent_param.h"
9 : #include "oper/route_common.h"
10 : #include "oper/operdb_init.h"
11 : #include "oper/path_preference.h"
12 : #include "pkt/pkt_init.h"
13 : #include "services/arp_proto.h"
14 : #include "services/services_init.h"
15 : #include "services/services_sandesh.h"
16 : #include "mac_learning/mac_learning_proto.h"
17 :
18 251 : ArpHandler::ArpHandler(Agent *agent, boost::shared_ptr<PktInfo> info,
19 251 : boost::asio::io_context &io)
20 251 : : ProtoHandler(agent, info, io), arp_(NULL), arp_tpa_(0) {
21 251 : refcount_ = 0;
22 251 : }
23 :
24 318 : ArpHandler::~ArpHandler() {
25 318 : }
26 :
27 67 : bool ArpHandler::Run() {
28 :
29 : // Process ARP only when the IP Fabric interface is configured
30 :
31 67 : assert(agent());
32 67 : assert(agent()->GetArpProto());
33 67 : ArpProto *arp_proto = agent()->GetArpProto();
34 :
35 67 : if (agent()->GetArpProto()->ip_fabric_interface() == NULL) {
36 0 : arp_proto->IncrementStatsIPFabricNotInst();
37 0 : delete pkt_info_->ipc;
38 0 : return true;
39 : }
40 :
41 67 : switch(pkt_info_->type) {
42 67 : case PktType::MESSAGE:
43 67 : return HandleMessage();
44 :
45 0 : default:
46 0 : return HandlePacket();
47 : }
48 : }
49 :
50 0 : bool ArpHandler::HandlePacket() {
51 0 : ArpProto *arp_proto = agent()->GetArpProto();
52 : uint16_t arp_cmd;
53 0 : if (pkt_info_->ip) {
54 0 : arp_tpa_ = ntohl(pkt_info_->ip->ip_dst.s_addr);
55 0 : arp_cmd = ARPOP_REQUEST;
56 0 : } else if (pkt_info_->arp) {
57 0 : arp_ = pkt_info_->arp;
58 0 : if ((ntohs(arp_->ea_hdr.ar_hrd) != ARPHRD_ETHER) ||
59 0 : (ntohs(arp_->ea_hdr.ar_pro) != ETHERTYPE_IP) ||
60 0 : (arp_->ea_hdr.ar_hln != ETHER_ADDR_LEN) ||
61 0 : (arp_->ea_hdr.ar_pln != IPv4_ALEN)) {
62 0 : arp_proto->IncrementStatsInvalidPackets();
63 0 : ARP_TRACE(Error, "Received Invalid ARP packet");
64 0 : return true;
65 : }
66 0 : arp_cmd = ntohs(arp_->ea_hdr.ar_op);
67 : union {
68 : uint8_t data[sizeof(in_addr_t)];
69 : in_addr_t addr;
70 : } bytes;
71 0 : memcpy(bytes.data, arp_->arp_tpa, sizeof(in_addr_t));
72 0 : in_addr_t tpa = ntohl(bytes.addr);
73 0 : memcpy(bytes.data, arp_->arp_spa, sizeof(in_addr_t));
74 0 : in_addr_t spa = ntohl(bytes.addr);
75 0 : if (arp_cmd == ARPOP_REQUEST)
76 0 : arp_tpa_ = tpa;
77 : else
78 0 : arp_tpa_ = spa;
79 :
80 : // if it is our own, ignore
81 0 : if (arp_tpa_ == agent()->router_id().to_ulong()) {
82 0 : arp_proto->IncrementStatsGratuitous();
83 0 : return true;
84 : }
85 :
86 0 : if (tpa == spa || spa == 0) {
87 0 : arp_cmd = GRATUITOUS_ARP;
88 : }
89 : } else {
90 0 : arp_proto->IncrementStatsInvalidPackets();
91 0 : ARP_TRACE(Error, "ARP : Received Invalid packet");
92 0 : return true;
93 : }
94 :
95 : const Interface *itf =
96 0 : agent()->interface_table()->FindInterface(GetInterfaceIndex());
97 0 : if (!itf || !itf->IsActive()) {
98 0 : arp_proto->IncrementStatsInvalidInterface();
99 0 : ARP_TRACE(Error, "Received ARP packet from invalid / inactive interface");
100 0 : return true;
101 : }
102 :
103 0 : const VrfEntry *vrf = agent()->vrf_table()->FindVrfFromId(pkt_info_->vrf);
104 0 : if (!vrf || !vrf->IsActive()) {
105 0 : arp_proto->IncrementStatsInvalidVrf();
106 0 : ARP_TRACE(Error, "ARP : AgentHdr " + itf->name() +
107 : " has no / inactive VRF ");
108 0 : return true;
109 : }
110 0 : const VrfEntry *nh_vrf = itf->vrf();
111 0 : if (!nh_vrf || !nh_vrf->IsActive()) {
112 0 : arp_proto->IncrementStatsInvalidVrf();
113 0 : ARP_TRACE(Error, "ARP : Interface " + itf->name() +
114 : " has no / inactive VRF");
115 0 : return true;
116 : }
117 :
118 : // if broadcast ip, return
119 0 : Ip4Address arp_addr(arp_tpa_);
120 0 : if (arp_tpa_ == 0xFFFFFFFF || !arp_tpa_) {
121 0 : arp_proto->IncrementStatsInvalidAddress();
122 0 : ARP_TRACE(Error, "ARP : ignoring broadcast address" +
123 : arp_addr.to_string());
124 0 : return true;
125 : }
126 :
127 : //Look for subnet broadcast
128 : AgentRoute *route =
129 : static_cast<InetUnicastAgentRouteTable *>(vrf->
130 0 : GetInet4UnicastRouteTable())->FindLPM(arp_addr);
131 0 : if (route) {
132 0 : const NextHop *anh = route->GetActiveNextHop();
133 0 : if (route->is_multicast()) {
134 0 : arp_proto->IncrementStatsInvalidAddress();
135 0 : ARP_TRACE(Error, "ARP : ignoring multicast address" +
136 : arp_addr.to_string());
137 0 : return true;
138 : }
139 0 : if (anh == NULL) {
140 0 : arp_proto->IncrementStatsInvalidAddress();
141 0 : ARP_TRACE(Error, "ARP : no active nexthop" +
142 : arp_addr.to_string());
143 0 : return true;
144 : }
145 0 : if(anh->GetType() == NextHop::RESOLVE) {
146 0 : const ResolveNH *nh =
147 : static_cast<const ResolveNH *>(anh);
148 0 : itf = nh->get_interface();
149 0 : nh_vrf = itf->vrf();
150 : }
151 : }
152 :
153 0 : ArpKey key(arp_tpa_, vrf);
154 0 : ArpEntry *entry = arp_proto->FindArpEntry(key);
155 :
156 0 : if (nh_vrf->forwarding_vrf()) {
157 0 : nh_vrf = nh_vrf->forwarding_vrf();
158 : }
159 :
160 0 : switch (arp_cmd) {
161 0 : case ARPOP_REQUEST: {
162 0 : arp_proto->IncrementStatsArpReq();
163 0 : arp_proto->IncrementStatsArpRequest(itf->id());
164 0 : if (entry) {
165 0 : entry->HandleArpRequest();
166 0 : return true;
167 : } else {
168 0 : entry = new ArpEntry(io_, this, key, nh_vrf, ArpEntry::INITING,
169 0 : itf);
170 0 : if (arp_proto->AddArpEntry(entry) == false) {
171 0 : delete entry;
172 0 : return true;
173 : }
174 0 : entry->HandleArpRequest();
175 0 : return false;
176 : }
177 : }
178 :
179 0 : case ARPOP_REPLY: {
180 0 : arp_proto->IncrementStatsArpReplies();
181 0 : arp_proto->IncrementStatsArpReply(itf->id());
182 0 : if (itf->type() == Interface::VM_INTERFACE) {
183 : uint32_t ip;
184 0 : memcpy(&ip, arp_->arp_spa, sizeof(ip));
185 0 : ip = ntohl(ip);
186 : /* Enqueue a request to trigger state machine. The prefix-len
187 : * of 32 passed below is not used. We do LPMFind on IP to
188 : * figure out the actual prefix-len inside
189 : * EnqueueTrafficSeen
190 : */
191 : agent()->oper_db()->route_preference_module()->
192 0 : EnqueueTrafficSeen(Ip4Address(ip), 32, itf->id(),
193 : vrf->vrf_id(),
194 0 : MacAddress(arp_->arp_sha));
195 0 : arp_proto->HandlePathPreferenceArpReply(vrf, itf->id(),
196 0 : Ip4Address(ip));
197 :
198 0 : if(entry) {
199 0 : entry->HandleArpReply(MacAddress(arp_->arp_sha));
200 : }
201 0 : return true;
202 : }
203 0 : if(entry) {
204 0 : entry->HandleArpReply(MacAddress(arp_->arp_sha));
205 0 : return true;
206 : } else {
207 0 : entry = new ArpEntry(io_, this, key, nh_vrf, ArpEntry::INITING,
208 0 : itf);
209 0 : if (arp_proto->AddArpEntry(entry) == false) {
210 0 : delete entry;
211 0 : return true;
212 : }
213 0 : entry->HandleArpReply(MacAddress(arp_->arp_sha));
214 0 : arp_ = NULL;
215 0 : return false;
216 : }
217 : }
218 :
219 0 : case GRATUITOUS_ARP: {
220 0 : arp_proto->IncrementStatsGratuitous();
221 0 : if (itf->type() == Interface::VM_INTERFACE) {
222 : uint32_t ip;
223 0 : memcpy(&ip, arp_->arp_spa, sizeof(ip));
224 0 : ip = ntohl(ip);
225 : //Enqueue a request to trigger state machine
226 : agent()->oper_db()->route_preference_module()->
227 0 : EnqueueTrafficSeen(Ip4Address(ip), 32, itf->id(),
228 : vrf->vrf_id(),
229 0 : MacAddress(arp_->arp_sha));
230 0 : return true;
231 0 : } else if (entry) {
232 0 : entry->HandleArpReply(MacAddress(arp_->arp_sha));
233 0 : return true;
234 : } else {
235 : // ignore gratuitous ARP when entry is not present in cache
236 0 : return true;
237 : }
238 : }
239 :
240 0 : default:
241 0 : ARP_TRACE(Error, "Received Invalid ARP command : " +
242 : integerToString(arp_cmd));
243 0 : return true;
244 : }
245 : }
246 :
247 : /* This API is invoked from the following paths
248 : - NextHop notification for ARP_NH
249 : - ARP Timer expiry
250 : - Sending Gratituous ARP for Receive Nexthops
251 : In all these above paths we expect route_vrf and nh_vrf for ArpRoute to
252 : be same
253 : */
254 67 : bool ArpHandler::HandleMessage() {
255 67 : bool ret = true;
256 67 : ArpProto::ArpIpc *ipc = static_cast<ArpProto::ArpIpc *>(pkt_info_->ipc);
257 67 : ArpProto *arp_proto = agent()->GetArpProto();
258 67 : switch(pkt_info_->ipc->cmd) {
259 2 : case ArpProto::ARP_RESOLVE: {
260 2 : ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
261 2 : if (!entry) {
262 2 : entry = new ArpEntry(io_, this, ipc->key, ipc->key.vrf,
263 1 : ArpEntry::INITING, ipc->interface_.get());
264 1 : if (arp_proto->AddArpEntry(entry) == false) {
265 0 : delete entry;
266 0 : break;
267 : }
268 1 : ret = false;
269 : }
270 2 : arp_proto->IncrementStatsArpReq();
271 2 : arp_proto->IncrementStatsArpRequest(ipc->interface_->id());
272 2 : entry->HandleArpRequest();
273 2 : break;
274 : }
275 :
276 64 : case ArpProto::ARP_SEND_GRATUITOUS: {
277 64 : bool key_valid = false;
278 : ArpProto::GratuitousArpIterator it =
279 64 : arp_proto->GratuitousArpEntryIterator(ipc->key, &key_valid);
280 64 : if (key_valid && !ipc->interface_->IsDeleted()) {
281 52 : ArpEntry *entry = NULL;
282 52 : ArpProto::ArpEntrySet::iterator sit = it->second.begin();
283 53 : for (; sit != it->second.end(); sit++) {
284 40 : entry = *sit;
285 40 : if (entry->get_interface() == ipc->interface_.get())
286 39 : break;
287 : }
288 52 : if (sit == it->second.end()) {
289 26 : entry = new ArpEntry(io_, this, ipc->key, ipc->key.vrf,
290 13 : ArpEntry::ACTIVE, ipc->interface_.get());
291 13 : it->second.insert(entry);
292 13 : ret = false;
293 : }
294 52 : if (entry)
295 52 : entry->SendGratuitousArp();
296 52 : break;
297 : }
298 : }
299 :
300 : case ArpProto::ARP_DELETE: {
301 13 : EntryDelete(ipc->key);
302 13 : break;
303 : }
304 :
305 0 : case ArpProto::RETRY_TIMER_EXPIRED: {
306 0 : ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
307 0 : if (entry && !entry->RetryExpiry()) {
308 0 : arp_proto->DeleteArpEntry(entry);
309 : }
310 0 : break;
311 : }
312 :
313 0 : case ArpProto::AGING_TIMER_EXPIRED: {
314 0 : ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
315 0 : if (entry && !entry->AgingExpiry()) {
316 0 : arp_proto->DeleteArpEntry(entry);
317 : }
318 0 : break;
319 : }
320 :
321 0 : case ArpProto::GRATUITOUS_TIMER_EXPIRED: {
322 : ArpEntry *entry =
323 0 : arp_proto->GratuitousArpEntry(ipc->key, ipc->interface_.get());
324 0 : if (entry && entry->retry_count() <= ArpProto::kGratRetries) {
325 0 : entry->SendGratuitousArp();
326 : } else {
327 : // Need to validate deleting the Arp entry upon fabric vrf Delete only
328 0 : if (ipc->key.vrf->GetName() != agent()->fabric_vrf_name()) {
329 0 : arp_proto->DeleteGratuitousArpEntry(entry);
330 : }
331 : }
332 0 : break;
333 : }
334 :
335 0 : default:
336 0 : ARP_TRACE(Error, "Received Invalid internal ARP message : " +
337 : integerToString(pkt_info_->ipc->cmd));
338 0 : break;
339 : }
340 67 : delete ipc;
341 67 : return ret;
342 : }
343 :
344 13 : void ArpHandler::EntryDelete(ArpKey &key) {
345 13 : ArpProto *arp_proto = agent()->GetArpProto();
346 13 : ArpEntry *entry = arp_proto->FindArpEntry(key);
347 13 : if (entry) {
348 1 : arp_proto->DeleteArpEntry(entry);
349 : // this request comes when ARP NH is deleted; nothing more to do
350 : }
351 13 : }
352 :
353 112 : uint16_t ArpHandler::ArpHdr(const MacAddress &smac, in_addr_t sip,
354 : const MacAddress &tmac, in_addr_t tip, uint16_t op) {
355 112 : arp_->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
356 112 : arp_->ea_hdr.ar_pro = htons(0x800);
357 112 : arp_->ea_hdr.ar_hln = ETHER_ADDR_LEN;
358 112 : arp_->ea_hdr.ar_pln = IPv4_ALEN;
359 112 : arp_->ea_hdr.ar_op = htons(op);
360 112 : smac.ToArray(arp_->arp_sha, sizeof(arp_->arp_sha));
361 112 : sip = htonl(sip);
362 112 : memcpy(arp_->arp_spa, &sip, sizeof(in_addr_t));
363 112 : tmac.ToArray(arp_->arp_tha, sizeof(arp_->arp_tha));
364 112 : tip = htonl(tip);
365 112 : memcpy(arp_->arp_tpa, &tip, sizeof(in_addr_t));
366 112 : return sizeof(ether_arp);
367 : }
368 :
369 112 : void ArpHandler::SendArp(uint16_t op, const MacAddress &smac, in_addr_t sip,
370 : const MacAddress &tmac, const MacAddress &dmac,
371 : in_addr_t tip, uint32_t itf, uint32_t vrf) {
372 :
373 112 : if (pkt_info_->packet_buffer() == NULL) {
374 5 : pkt_info_->AllocPacketBuffer(agent(), PktHandler::ARP, ARP_TX_BUFF_LEN,
375 : 0);
376 : }
377 :
378 112 : char *buf = (char *)pkt_info_->packet_buffer()->data();
379 112 : memset(buf, 0, pkt_info_->packet_buffer()->data_len());
380 112 : pkt_info_->eth = (struct ether_header *)buf;
381 112 : int l2_len = EthHdr(buf, pkt_info_->packet_buffer()->data_len(),
382 : itf, smac, dmac, ETHERTYPE_ARP);
383 112 : arp_ = pkt_info_->arp = (ether_arp *) (buf + l2_len);
384 112 : arp_tpa_ = tip;
385 :
386 112 : ArpHdr(smac, sip, tmac, tip, op);
387 112 : pkt_info_->set_len(l2_len + sizeof(ether_arp));
388 :
389 112 : Send(itf, vrf, AgentHdr::TX_SWITCH, PktHandler::ARP);
390 112 : }
391 :
392 98 : void ArpHandler::SendArpRequestByPlen(const VmInterface *vm_interface, const MacAddress &smac,
393 : const ArpPathPreferenceState *data,
394 : const Ip4Address &tpa) {
395 98 : Ip4Address service_ip = vm_interface->GetServiceIp(data->ip()).to_v4();
396 98 : bool aap_ip = vm_interface->MatchAapIp(data->ip(), data->plen());
397 : #if 0
398 : MacAddress mac = agent()->mac_learning_proto()->
399 : GetMacIpLearningTable()->GetPairedMacAddress(
400 : vm_interface->vrf()->vrf_id(),
401 : data->ip());
402 : #endif
403 98 : MacAddress mac = data->mac();
404 :
405 98 : if (mac == MacAddress()) {
406 11 : mac = vm_interface->vm_mac();
407 : }
408 :
409 98 : if (data->plen() == Address::kMaxV4PrefixLen) {
410 196 : SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(), MacAddress(),
411 0 : ((aap_ip) ? MacAddress::BroadcastMac(): mac),
412 98 : data->ip().to_v4().to_ulong(), vm_interface->id(), data->vrf_id());
413 98 : agent()->GetArpProto()->IncrementStatsVmArpReq();
414 : } else {
415 0 : if (!tpa.is_unspecified()) {
416 0 : SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(),
417 0 : MacAddress(), ((aap_ip) ? MacAddress::BroadcastMac(): mac),
418 0 : tpa.to_ulong(), vm_interface->id(), data->vrf_id());
419 0 : agent()->GetArpProto()->IncrementStatsVmArpReq();
420 0 : return;
421 : }
422 : /* Loop through all the IPs for the prefix-len and send Arp for each
423 : * IP*/
424 0 : uint8_t diff_plen = Address::kMaxV4PrefixLen - data->plen();
425 0 : uint32_t num_addresses = pow(2, diff_plen);
426 0 : const uint32_t &max_addresses = MaxArpProbeAddresses();
427 0 : if (num_addresses > max_addresses) {
428 0 : num_addresses = max_addresses;
429 : /* When min_aap_prefix_len (configured by user in agent config file)
430 : * we need to visit all addresses specified by user, because
431 : * base address in this case will be formed by prefix-len lower
432 : * than min_aap_prefix_len. This prefix len is determined by
433 : * data->plen(). The loop below which goes through all the
434 : * addresses will visit 2 addresses less after discounting base
435 : * address and broadcast address. Because base address in this case
436 : * is formed by prefix-len lower than min_aap_prefix_len, we should
437 : * not discount the two addresses. Hence increment by 2.
438 : */
439 0 : num_addresses += 2;
440 : }
441 0 : uint32_t base_addr = data->ip().to_v4().to_ulong();
442 0 : for (uint32_t i = 1; i < num_addresses; ++i) {
443 0 : uint32_t addr = base_addr + i;
444 0 : SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(),
445 0 : MacAddress(), ((aap_ip) ? MacAddress::BroadcastMac(): mac),
446 : addr, vm_interface->id(), data->vrf_id());
447 0 : agent()->GetArpProto()->IncrementStatsVmArpReq();
448 : }
449 : }
450 : }
451 :
452 0 : uint32_t ArpHandler::MaxArpProbeAddresses() const {
453 0 : uint32_t diff_plen = Address::kMaxV4PrefixLen -
454 0 : agent()->params()->min_aap_prefix_len();
455 0 : return pow(2, diff_plen);
456 : }
457 :
458 14 : void intrusive_ptr_add_ref(const ArpHandler *p) {
459 14 : p->refcount_++;
460 14 : }
461 :
462 14 : void intrusive_ptr_release(const ArpHandler *p) {
463 28 : if (p->refcount_.fetch_sub(1) == 1) {
464 14 : delete p;
465 : }
466 14 : }
|