Line data Source code
1 : /*
2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
3 : */
4 : #include <init/agent_param.h>
5 : #include <oper/vn.h>
6 : #include <oper/sg.h>
7 : #include <oper/vrf.h>
8 : #include <oper/nexthop.h>
9 : #include <oper/interface_common.h>
10 : #include <oper/route_common.h>
11 : #include "mac_learning_proto.h"
12 : #include "mac_ip_learning_proto_handler.h"
13 : #include "mac_learning_init.h"
14 : #include "mac_ip_learning.h"
15 : #include "mac_learning_mgmt.h"
16 : #include "mac_learning_event.h"
17 :
18 :
19 3 : MacIpLearningTable::MacIpLearningTable(Agent *agent, MacLearningProto *proto) :
20 3 : agent_(agent),
21 3 : work_queue_(this), macip_map_mutex_() {
22 3 : agent->health_check_table()->RegisterHealthCheckNotifyCallback(
23 : boost::bind(&MacIpLearningTable::MacIpEntryHcNotify, this, _1));
24 3 : }
25 :
26 5 : bool MacIpLearningTable::RequestHandler(MacLearningEntryRequestPtr ptr) {
27 5 : switch(ptr->event()) {
28 0 : case MacLearningEntryRequest::VROUTER_MSG:
29 0 : agent()->mac_learning_proto()->ProcessProto(ptr->pkt_info());
30 0 : break;
31 :
32 0 : case MacLearningEntryRequest::ADD_MAC:
33 0 : Add(ptr->mac_learning_entry());
34 0 : break;
35 :
36 0 : case MacLearningEntryRequest::RESYNC_MAC:
37 0 : Resync(ptr->mac_learning_entry());
38 0 : break;
39 :
40 0 : case MacLearningEntryRequest::DELETE_MAC:
41 0 : Delete(ptr->mac_learning_entry());
42 0 : break;
43 5 : case MacLearningEntryRequest::REMOTE_MAC_IP:
44 5 : DetectIpMove(ptr);
45 5 : break;
46 :
47 0 : case MacLearningEntryRequest::MAC_IP_UNREACHABLE:
48 0 : MacIpEntryUnreachable(ptr);
49 0 : break;
50 :
51 0 : default:
52 0 : assert(0);
53 : }
54 5 : return true;
55 : }
56 0 : void MacIpLearningTable::EnqueueMgmtReq(MacLearningEntryPtr ptr, bool add) {
57 0 : MacLearningMgmtRequest::Event e = MacLearningMgmtRequest::ADD_MAC_IP;
58 0 : if (add == false) {
59 0 : e = MacLearningMgmtRequest::DELETE_MAC_IP;
60 : }
61 0 : MacLearningMgmtRequestPtr req(new MacLearningMgmtRequest(e, ptr));
62 0 : agent()->mac_learning_module()->mac_learning_mgmt()->Enqueue(req);
63 0 : }
64 0 : void MacIpLearningTable::Add(MacLearningEntryPtr ptr) {
65 0 : MacIpLearningEntry *entry = dynamic_cast<MacIpLearningEntry *>(ptr.get());
66 0 : MacIpLearningKey key(entry->vrf_id(), entry->IpAddr());
67 : //check whether IP belongs to subnet of VN
68 : const VmInterface *vm_intf =
69 0 : dynamic_cast<const VmInterface *>(entry->intf());
70 0 : if (!vm_intf ||
71 0 : !vm_intf->vn()->GetIpam(entry->IpAddr())) {
72 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, entry->vrf()->GetName(),
73 : entry->IpAddr().to_string(), entry->Mac().ToString(),
74 : entry->intf()->name(),
75 : "ip address does not belong to same subnet, ignoring");
76 0 : return;
77 : }
78 0 : std::scoped_lock lock(macip_map_mutex_);
79 0 : MacIpLearningEntryMap::iterator it = mac_ip_learning_entry_map_.find(key);
80 0 : if (it != mac_ip_learning_entry_map_.end()) {
81 : MacIpLearningEntry *existing_entry =
82 0 : dynamic_cast<MacIpLearningEntry *>(it->second.get());
83 0 : if ( existing_entry && existing_entry->Mac() == entry->Mac()) {
84 : // ignore duplicate add requests, it is possible that
85 : // duplicate requests may come till route processing is done.
86 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, entry->vrf()->GetName(),
87 : entry->IpAddr().to_string(), entry->Mac().ToString(),
88 : entry->intf()->name(),
89 : "duplicate add request, ingoring");
90 0 : return;
91 : }
92 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, entry->vrf()->GetName(),
93 : entry->IpAddr().to_string(), entry->Mac().ToString(),
94 : entry->intf()->name(),
95 : "local IP move detected, delete and add with new mac");
96 : //Entry already present, clear the entry
97 0 : if (it->second->deleted() == false) {
98 0 : it->second->Delete();
99 0 : EnqueueMgmtReq(it->second, false);
100 : }
101 0 : mac_ip_learning_entry_map_[key] = ptr;
102 : } else {
103 : // check whether mac limit reached for learning new mac-ip
104 : // on the interface
105 0 : if (vm_intf->IsMaxMacIpLearnt()) {
106 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, entry->vrf()->GetName(),
107 : entry->IpAddr().to_string(), entry->Mac().ToString(),
108 : entry->intf()->name(),
109 : "max mac ip learnt limit reached on interface");
110 0 : return;
111 : }
112 :
113 0 : mac_ip_learning_entry_map_.insert(MacIpLearningEntryPair(key, ptr));
114 : }
115 0 : ptr->Add();
116 0 : EnqueueMgmtReq(ptr, true);
117 0 : }
118 :
119 0 : void MacIpLearningTable::Delete(const MacLearningEntryPtr ptr) {
120 0 : if (ptr->deleted() == true) {
121 0 : return;
122 : }
123 0 : MacIpLearningEntry *entry = dynamic_cast<MacIpLearningEntry *>(ptr.get());
124 0 : if (entry == NULL) {
125 0 : return;
126 : }
127 :
128 0 : std::scoped_lock lock(macip_map_mutex_);
129 0 : MacIpLearningKey key(ptr->vrf_id(), entry->IpAddr());
130 0 : if (mac_ip_learning_entry_map_.find(key) == mac_ip_learning_entry_map_.end()) {
131 0 : return;
132 : }
133 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, entry->vrf()->GetName(),
134 : entry->IpAddr().to_string(), entry->Mac().ToString(),
135 : entry->intf()->name(), "Delete");
136 :
137 0 : ptr->Delete();
138 0 : mac_ip_learning_entry_map_.erase(key);
139 0 : EnqueueMgmtReq(ptr, false);
140 0 : }
141 :
142 0 : void MacIpLearningTable::Resync(MacLearningEntryPtr ptr) {
143 0 : MacIpLearningEntry *entry = dynamic_cast<MacIpLearningEntry *>(ptr.get());
144 0 : if (entry == NULL) {
145 0 : return;
146 : }
147 0 : MacIpLearningKey key(ptr->vrf_id(), entry->IpAddr());
148 0 : std::scoped_lock lock(macip_map_mutex_);
149 0 : if (mac_ip_learning_entry_map_.find(key) == mac_ip_learning_entry_map_.end()) {
150 0 : return;
151 : }
152 :
153 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, entry->vrf()->GetName(),
154 : entry->IpAddr().to_string(), entry->Mac().ToString(),
155 : entry->intf()->name(), "Resync");
156 0 : ptr->Resync();
157 0 : EnqueueMgmtReq(ptr, true);
158 0 : }
159 :
160 5 : void MacIpLearningTable::DetectIpMove(MacLearningEntryRequestPtr ptr) {
161 5 : MacIpLearningKey key(ptr->vrf_id(), ptr->ip());
162 5 : std::scoped_lock lock(macip_map_mutex_);
163 5 : MacIpLearningEntryMap::iterator it = mac_ip_learning_entry_map_.find(key);
164 5 : if (it == mac_ip_learning_entry_map_.end()) {
165 5 : return;
166 : }
167 0 : MacIpLearningEntry *mac_ip_entry = dynamic_cast<MacIpLearningEntry *>( it->second.get());
168 0 : if (!mac_ip_entry || mac_ip_entry->Mac() == ptr->mac()) {
169 0 : return;
170 : }
171 :
172 0 : VrfEntry *vrf = Agent::GetInstance()->vrf_table()->FindVrfFromId(ptr->vrf_id());
173 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, vrf?vrf->GetName():"NULL",
174 : ptr->ip().to_string(), mac_ip_entry->Mac().ToString(),
175 : mac_ip_entry->intf()->name(),
176 : "IP Move detected, deleting local entry");
177 :
178 0 : it->second->Delete();
179 0 : EnqueueMgmtReq(it->second, false);
180 0 : mac_ip_learning_entry_map_.erase(key);
181 5 : }
182 0 : void MacIpLearningTable::MacIpEntryUnreachable(uint32_t vrf_id, IpAddress &ip,
183 : MacAddress &mac) {
184 : MacLearningEntryRequestPtr ptr(new MacLearningEntryRequest(
185 : MacLearningEntryRequest::MAC_IP_UNREACHABLE,
186 0 : vrf_id, ip, mac));
187 0 : Enqueue(ptr);
188 0 : }
189 0 : void MacIpLearningTable::MacIpEntryHcNotify(
190 : const HealthCheckInstanceService *instance_service) {
191 : const HealthCheckMacIpInstanceService *mac_ip_instance_service =
192 0 : dynamic_cast<const HealthCheckMacIpInstanceService *>(instance_service);
193 0 : if (mac_ip_instance_service) {
194 0 : IpAddress ip = mac_ip_instance_service->destination_ip();
195 0 : MacAddress mac = mac_ip_instance_service->destination_mac();
196 : uint32_t vrf_id =
197 0 : mac_ip_instance_service->interface()->vrf()->vrf_id();
198 : MacLearningEntryRequestPtr ptr(new MacLearningEntryRequest(
199 : MacLearningEntryRequest::MAC_IP_UNREACHABLE,
200 0 : vrf_id, ip, mac));
201 0 : Enqueue(ptr);
202 0 : }
203 0 : }
204 0 : void MacIpLearningTable::MacIpEntryUnreachable(MacLearningEntryRequestPtr ptr) {
205 0 : MacIpLearningKey key(ptr->vrf_id(), ptr->ip());
206 0 : std::scoped_lock lock(macip_map_mutex_);
207 0 : MacIpLearningEntryMap::iterator it = mac_ip_learning_entry_map_.find(key);
208 0 : if (it == mac_ip_learning_entry_map_.end()) {
209 0 : return;
210 : }
211 0 : MacIpLearningEntry *mac_ip_entry = dynamic_cast<MacIpLearningEntry *>( it->second.get());
212 0 : if (mac_ip_entry->Mac() == ptr->mac()) {
213 0 : MAC_IP_LEARNING_TRACE(MacLearningTraceBuf, mac_ip_entry->vrf()->GetName(),
214 : mac_ip_entry->IpAddr().to_string(), mac_ip_entry->Mac().ToString(),
215 : mac_ip_entry->intf()->name(), "MACIP unreachable, trigger delete");
216 0 : it->second->Delete();
217 0 : EnqueueMgmtReq(it->second, false);
218 0 : mac_ip_learning_entry_map_.erase(key);
219 : }
220 0 : }
221 :
222 5 : void MacIpLearningTable::Enqueue(MacLearningEntryRequestPtr req) {
223 5 : work_queue_.Enqueue(req);
224 5 : return;
225 : }
226 : MacIpLearningEntry*
227 96 : MacIpLearningTable::Find(const MacIpLearningKey &key) {
228 96 : std::scoped_lock lock(macip_map_mutex_);
229 96 : MacIpLearningEntryMap::iterator it = mac_ip_learning_entry_map_.find(key);
230 96 : if (it == mac_ip_learning_entry_map_.end()) {
231 96 : return NULL;
232 : }
233 :
234 0 : MacIpLearningEntry *mac_ip_entry = dynamic_cast<MacIpLearningEntry *>( it->second.get());
235 0 : return mac_ip_entry;
236 96 : }
237 96 : MacAddress MacIpLearningTable::GetPairedMacAddress(uint32_t vrf_id, const IpAddress& ip) {
238 96 : MacIpLearningKey key(vrf_id, ip);
239 96 : MacIpLearningEntry *entry = Find(key);
240 96 : if (entry) {
241 0 : return entry->Mac();
242 : }
243 96 : return MacAddress();
244 :
245 : }
246 :
247 0 : MacIpLearningEntry::MacIpLearningEntry(MacIpLearningTable *table,
248 : uint32_t vrf_id, const IpAddress &ip,
249 0 : const MacAddress &mac, InterfaceConstRef intf) :
250 0 : MacLearningEntry(vrf_id), mac_ip_learning_table_(table),
251 0 : key_(vrf_id, ip), ip_(ip), mac_(mac),
252 0 : intf_(intf), vn_(NULL), hc_service_(NULL), hc_instance_(NULL) {
253 0 : const VmInterface *vm_port = dynamic_cast<const VmInterface *>(intf_.get());
254 0 : assert(vm_port);
255 0 : vn_ = vm_port->vn();
256 :
257 0 : }
258 0 : bool MacIpLearningEntry::Add() {
259 0 : VmInterfaceLearntMacIpData *data = new VmInterfaceLearntMacIpData();
260 0 : data->is_add = true;
261 0 : data->mac_ip_list_.list_.insert(
262 0 : VmInterface::LearntMacIp(ip_, mac_));
263 0 : DBRequest req;
264 0 : req.oper = DBRequest::DB_ENTRY_ADD_CHANGE;
265 0 : req.key.reset(new VmInterfaceKey(AgentKey::RESYNC, intf_->GetUuid(), ""));
266 0 : req.data.reset(data);
267 0 : mac_ip_learning_table_->agent()->interface_table()->Enqueue(&req);
268 0 : UpdateHealthCheckService();
269 0 : return 0;
270 0 : }
271 :
272 0 : void MacIpLearningEntry::Delete() {
273 0 : if (hc_instance_ && hc_service_) {
274 0 : hc_service_->StopHealthCheckService(hc_instance_);
275 0 : hc_instance_ = NULL;
276 0 : hc_service_ = NULL;
277 : }
278 0 : VmInterfaceLearntMacIpData *data = new VmInterfaceLearntMacIpData();
279 0 : data->is_add = false;
280 0 : data->mac_ip_list_.list_.insert(
281 0 : VmInterface::LearntMacIp(ip_, mac_));
282 0 : DBRequest req;
283 0 : req.oper = DBRequest::DB_ENTRY_ADD_CHANGE;
284 0 : req.key.reset(new VmInterfaceKey(AgentKey::RESYNC, intf_->GetUuid(), ""));
285 0 : req.data.reset(data);
286 0 : mac_ip_learning_table_->agent()->interface_table()->Enqueue(&req);
287 0 : }
288 :
289 0 : void MacIpLearningEntry::Resync() {
290 0 : UpdateHealthCheckService();
291 0 : }
292 :
293 0 : void MacIpLearningEntry::EnqueueToTable(MacLearningEntryRequestPtr req) {
294 0 : mac_ip_learning_table_->Enqueue(req);
295 0 : }
296 :
297 0 : void MacIpLearningEntry::UpdateHealthCheckService() {
298 : HealthCheckService *hc_service =
299 0 : GetHealthCheckService(vn_.get());
300 0 : if (hc_service_ != hc_service) {
301 0 : if (hc_service_ == NULL) {
302 0 : AddHealthCheckService(hc_service);
303 : } else {
304 0 : if (hc_instance_) {
305 0 : hc_service_->StopHealthCheckService(hc_instance_);
306 : }
307 0 : hc_instance_ = NULL;
308 0 : hc_service_= NULL;
309 0 : if (hc_service) {
310 0 : AddHealthCheckService(hc_service);
311 : }
312 : }
313 0 : } else if (hc_service_) {
314 0 : hc_instance_->UpdateInstanceTask();
315 : }
316 0 : }
317 0 : void MacIpLearningEntry::AddHealthCheckService(HealthCheckService *service) {
318 0 : if (service) {
319 0 : hc_service_ = service;
320 0 : IpAddress gateway_ip = vn_->GetGatewayFromIpam(ip_);
321 0 : const VmInterface *vm_intf = static_cast< const VmInterface *>(intf_.get());
322 : //TODO: add validation check for null gateway ip
323 0 : hc_instance_ = service->StartHealthCheckService(
324 : const_cast< VmInterface *>(vm_intf), NULL,
325 0 : gateway_ip, ip_, mac_, false, false);
326 0 : hc_instance_->set_service(hc_service_);
327 : }
328 0 : }
329 :
330 0 : HealthCheckService* MacIpLearningEntry::GetHealthCheckService(const VnEntry *vn) {
331 : //1. check if hc is attached
332 0 : const boost::uuids::uuid hc_uuid = vn->health_check_uuid();
333 0 : if (hc_uuid == boost::uuids::nil_uuid()) {
334 0 : return NULL;
335 : }
336 : HealthCheckService *hc_service = Agent::GetInstance()->
337 0 : health_check_table()->Find(hc_uuid);
338 : //2. if attached , check whether target ip present in tragte ip list
339 0 : if (!hc_service) {
340 0 : return NULL;
341 : }
342 0 : if (hc_service->IsTargetIpPresent(IpAddr())) {
343 0 : return hc_service;
344 : }
345 :
346 0 : return NULL;
347 : }
348 3 : MacIpLearningRequestQueue::MacIpLearningRequestQueue(MacIpLearningTable *table):
349 3 : table_(table),
350 3 : queue_(table_->agent()->task_scheduler()->GetTaskId(kTaskMacLearning),
351 : 0,
352 : boost::bind(&MacIpLearningRequestQueue::HandleEvent,this,_1)) {
353 3 : }
354 :
355 5 : bool MacIpLearningRequestQueue::HandleEvent(MacLearningEntryRequestPtr ptr) {
356 5 : return table_->RequestHandler(ptr);
357 : }
|