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