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 <boost/uuid/uuid_io.hpp>
7 :
8 : #include <cmn/agent_cmn.h>
9 : #include <route/route.h>
10 :
11 : #include <oper/route_common.h>
12 : #include <oper/vrf.h>
13 : #include <oper/tunnel_nh.h>
14 : #include <oper/mpls.h>
15 : #include <oper/mirror_table.h>
16 : #include <controller/controller_export.h>
17 : #include <controller/controller_peer.h>
18 : #include <controller/controller_route_path.h>
19 : #include <oper/agent_sandesh.h>
20 :
21 : using namespace std;
22 : using namespace boost::asio;
23 :
24 : /////////////////////////////////////////////////////////////////////////////
25 : // Utility functions
26 : /////////////////////////////////////////////////////////////////////////////
27 1634 : static void BridgeTableEnqueue(Agent *agent, DBRequest *req) {
28 1634 : AgentRouteTable *table = agent->fabric_l2_unicast_table();
29 1634 : if (table) {
30 1626 : table->Enqueue(req);
31 : }
32 1634 : }
33 :
34 3308 : static void BridgeTableProcess(Agent *agent, const string &vrf_name,
35 : DBRequest &req) {
36 : AgentRouteTable *table =
37 3308 : agent->vrf_table()->GetBridgeRouteTable(vrf_name);
38 3308 : if (table) {
39 3308 : table->Process(req);
40 : }
41 3308 : }
42 :
43 : /////////////////////////////////////////////////////////////////////////////
44 : // BridgeRouteKey methods
45 : /////////////////////////////////////////////////////////////////////////////
46 0 : string BridgeRouteKey::ToString() const {
47 0 : return dmac_.ToString();
48 : }
49 :
50 436 : BridgeRouteKey *BridgeRouteKey::Clone() const {
51 436 : return new BridgeRouteKey(peer(), vrf_name_, dmac_);
52 : }
53 :
54 : AgentRoute *
55 7392 : BridgeRouteKey::AllocRouteEntry(VrfEntry *vrf, bool is_multicast) const
56 : {
57 7392 : BridgeRouteEntry *entry = new BridgeRouteEntry(vrf, dmac_,
58 7392 : peer()->GetType(),
59 7392 : is_multicast);
60 7392 : return static_cast<AgentRoute *>(entry);
61 : }
62 :
63 : /////////////////////////////////////////////////////////////////////////////
64 : // BridgeAgentRouteTable methods
65 : /////////////////////////////////////////////////////////////////////////////
66 74 : DBTableBase *BridgeAgentRouteTable::CreateTable(DB *db,
67 : const std::string &name) {
68 74 : AgentRouteTable *table = new BridgeAgentRouteTable(db, name);
69 74 : table->Init();
70 74 : return table;
71 : }
72 :
73 1008 : BridgeRouteEntry *BridgeAgentRouteTable::FindRoute(const MacAddress &mac) {
74 1008 : BridgeRouteEntry entry(vrf_entry(), mac, Peer::LOCAL_PEER, false);
75 2016 : return static_cast<BridgeRouteEntry *>(FindActiveEntry(&entry));
76 1008 : }
77 :
78 117 : BridgeRouteEntry *BridgeAgentRouteTable::FindRouteNoLock(const MacAddress &mac){
79 117 : BridgeRouteEntry entry(vrf_entry(), mac, Peer::LOCAL_PEER, false);
80 234 : return static_cast<BridgeRouteEntry *>(FindActiveEntryNoLock(&entry));
81 117 : }
82 :
83 0 : BridgeRouteEntry *BridgeAgentRouteTable::FindRoute(const MacAddress &mac,
84 : Peer::Type peer) {
85 0 : BridgeRouteEntry entry(vrf_entry(), mac, peer, false);
86 0 : return static_cast<BridgeRouteEntry *>(FindActiveEntry(&entry));
87 0 : }
88 :
89 : /////////////////////////////////////////////////////////////////////////////
90 : // BridgeAgentRouteTable utility methods to add/delete routes
91 : /////////////////////////////////////////////////////////////////////////////
92 148 : void BridgeAgentRouteTable::AddBridgeReceiveRoute(const Peer *peer,
93 : const string &vrf_name,
94 : const MacAddress &mac,
95 : const string &vn_name,
96 : const string &interface,
97 : bool policy) {
98 148 : Agent *agent = Agent::GetInstance();
99 148 : DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
100 148 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, 0));
101 :
102 148 : PacketInterfaceKey intf_key(boost::uuids::nil_uuid(),
103 296 : agent->pkt_interface_name());
104 148 : req.data.reset(new HostRoute(intf_key, vn_name));
105 :
106 148 : BridgeTableEnqueue(agent, &req);
107 148 : }
108 :
109 148 : void BridgeAgentRouteTable::AddBridgeReceiveRoute(const Peer *peer,
110 : const string &vrf_name,
111 : uint32_t vxlan_id,
112 : const MacAddress &mac,
113 : const string &vn_name) {
114 148 : DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
115 148 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, vxlan_id));
116 296 : req.data.reset(new L2ReceiveRoute(vn_name, vxlan_id, 0, PathPreference(),
117 148 : peer->sequence_number()));
118 148 : Process(req);
119 148 : }
120 :
121 1509 : void BridgeAgentRouteTable::AddBridgeRoute(const AgentRoute *rt) {
122 1509 : const EvpnRouteEntry *evpn_rt =
123 : static_cast<const EvpnRouteEntry *>(rt);
124 1509 : DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
125 3018 : req.key.reset(new BridgeRouteKey(agent()->evpn_peer(),
126 1509 : evpn_rt->vrf()->GetName(),
127 1509 : evpn_rt->mac(), 0));
128 1509 : req.data.reset(new EvpnDerivedPathData(evpn_rt));
129 1509 : BridgeTableProcess(agent(), vrf_name(), req);
130 1509 : }
131 :
132 :
133 996 : void BridgeAgentRouteTable::AddMacVmBindingRoute(const Peer *peer,
134 : const std::string &vrf_name,
135 : const MacAddress &mac,
136 : const VmInterface *vm_intf,
137 : bool flood_dhcp) {
138 996 : DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
139 996 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, 0));
140 996 : req.data.reset(new MacVmBindingPathData(vm_intf, flood_dhcp));
141 996 : BridgeTableProcess(agent(), vrf_name, req);
142 996 : }
143 :
144 171 : void BridgeAgentRouteTable::DeleteMacVmBindingRoute(const Peer *peer,
145 : const std::string &vrf_name,
146 : const MacAddress &mac,
147 : const VmInterface *vm_intf) {
148 171 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
149 171 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, 0));
150 171 : req.data.reset(new MacVmBindingPathData(vm_intf, false));
151 171 : BridgeTableProcess(agent(), vrf_name, req);
152 171 : }
153 :
154 338 : void BridgeAgentRouteTable::DeleteBridgeRoute(const AgentRoute *rt) {
155 338 : const EvpnRouteEntry *evpn_rt =
156 : static_cast<const EvpnRouteEntry *>(rt);
157 338 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
158 676 : req.key.reset(new BridgeRouteKey(agent()->evpn_peer(),
159 338 : evpn_rt->vrf()->GetName(),
160 338 : evpn_rt->mac(), 0));
161 338 : req.data.reset(new EvpnDerivedPathData(evpn_rt));
162 338 : BridgeTableProcess(Agent::GetInstance(), evpn_rt->vrf()->GetName(), req);
163 338 : }
164 :
165 294 : void BridgeAgentRouteTable::Delete(const Peer *peer, const string &vrf_name,
166 : const MacAddress &mac,
167 : uint32_t ethernet_tag) {
168 294 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
169 294 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, ethernet_tag));
170 294 : req.data.reset(NULL);
171 294 : BridgeTableProcess(Agent::GetInstance(), vrf_name, req);
172 294 : }
173 :
174 1301 : AgentRouteData *BridgeAgentRouteTable::BuildNonBgpPeerData(const string &vrf_name,
175 : const std::string &vn_name,
176 : uint32_t label,
177 : int vxlan_id,
178 : uint32_t tunnel_type,
179 : Composite::Type type,
180 : ComponentNHKeyList
181 : &component_nh_key_list,
182 : bool pbb_nh,
183 : bool learning_enabled) {
184 1301 : DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE);
185 1301 : nh_req.key.reset(new CompositeNHKey(type, false, component_nh_key_list,
186 1301 : vrf_name));
187 1301 : nh_req.data.reset(new CompositeNHData(pbb_nh, learning_enabled, false));
188 : return (new MulticastRoute(vn_name, label,
189 : vxlan_id, tunnel_type,
190 2602 : nh_req, type, 0));
191 1301 : }
192 :
193 0 : AgentRouteData *BridgeAgentRouteTable::BuildBgpPeerData(const Peer *peer,
194 : const string &vrf_name,
195 : const std::string &vn_name,
196 : uint32_t label,
197 : int vxlan_id,
198 : uint32_t ethernet_tag,
199 : uint32_t tunnel_type,
200 : Composite::Type type,
201 : ComponentNHKeyList
202 : &component_nh_key_list,
203 : bool pbb_nh,
204 : bool learning_enabled) {
205 0 : const BgpPeer *bgp_peer = dynamic_cast<const BgpPeer *>(peer);
206 0 : assert(bgp_peer != NULL);
207 0 : DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE);
208 0 : nh_req.key.reset(new CompositeNHKey(type, false, component_nh_key_list,
209 0 : vrf_name));
210 0 : nh_req.data.reset(new CompositeNHData(pbb_nh, learning_enabled, false));
211 : return (new MulticastRoute(vn_name, label, ethernet_tag, tunnel_type,
212 0 : nh_req, type, bgp_peer->sequence_number()));
213 0 : }
214 :
215 0 : void BridgeAgentRouteTable::AddBridgeRoute(const Peer *peer,
216 : const string &vrf_name,
217 : const MacAddress &mac,
218 : uint32_t ethernet_tag,
219 : AgentRouteData *data) {
220 :
221 0 : DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
222 0 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, ethernet_tag));
223 0 : req.data.reset(data);
224 0 : BridgeTableEnqueue(Agent::GetInstance(), &req);
225 0 : }
226 :
227 0 : void BridgeAgentRouteTable::DeleteBridgeRoute(const Peer *peer,
228 : const string &vrf_name,
229 : const MacAddress &mac,
230 : uint32_t ethernet_tag,
231 : COMPOSITETYPE type) {
232 :
233 0 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
234 0 : req.key.reset(new BridgeRouteKey(peer, vrf_name, mac, ethernet_tag));
235 0 : DBRequest nh_req;
236 :
237 : //For same BGP peer type comp type helps in identifying if its a delete
238 : //for TOR or EVPN path.
239 : //Only ethernet tag is required, rest are dummy.
240 0 : const BgpPeer *bgp_peer = dynamic_cast<const BgpPeer *>(peer);
241 0 : if (bgp_peer) {
242 0 : req.data.reset(new MulticastRoute("", 0,
243 0 : ethernet_tag, TunnelType::AllType(),
244 : nh_req, type,
245 0 : bgp_peer->sequence_number()));
246 : } else {
247 0 : req.data.reset(new MulticastRoute("", 0, ethernet_tag,
248 0 : TunnelType::AllType(),
249 0 : nh_req, type, 0));
250 : }
251 :
252 0 : BridgeTableEnqueue(Agent::GetInstance(), &req);
253 0 : }
254 :
255 1301 : void BridgeAgentRouteTable::AddBridgeBroadcastRoute(const Peer *peer,
256 : const string &vrf_name,
257 : uint32_t ethernet_tag,
258 : AgentRouteData *data) {
259 :
260 1301 : DBRequest req(DBRequest::DB_ENTRY_ADD_CHANGE);
261 1301 : req.key.reset(new BridgeRouteKey(peer, vrf_name,
262 1301 : MacAddress::BroadcastMac(), ethernet_tag));
263 1301 : req.data.reset(data);
264 1301 : BridgeTableEnqueue(Agent::GetInstance(), &req);
265 1301 : }
266 :
267 185 : void BridgeAgentRouteTable::DeleteBroadcastReq(const Peer *peer,
268 : const string &vrf_name,
269 : uint32_t ethernet_tag,
270 : COMPOSITETYPE type) {
271 185 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
272 185 : req.key.reset(new BridgeRouteKey(peer, vrf_name,
273 185 : MacAddress::BroadcastMac(), ethernet_tag));
274 185 : DBRequest nh_req;
275 : //For same BGP peer type comp type helps in identifying if its a delete
276 : //for TOR or EVPN path.
277 : //Only ethernet tag is required, rest are dummy.
278 185 : const BgpPeer *bgp_peer = dynamic_cast<const BgpPeer *>(peer);
279 185 : if (bgp_peer) {
280 0 : req.data.reset(new MulticastRoute("", 0,
281 0 : ethernet_tag, TunnelType::AllType(),
282 : nh_req, type,
283 0 : bgp_peer->sequence_number()));
284 : } else {
285 370 : req.data.reset(new MulticastRoute("", 0, ethernet_tag,
286 185 : TunnelType::AllType(),
287 185 : nh_req, type, 0));
288 : }
289 :
290 185 : BridgeTableEnqueue(Agent::GetInstance(), &req);
291 185 : }
292 :
293 4 : const VmInterface *BridgeAgentRouteTable::FindVmFromDhcpBinding
294 : (const MacAddress &mac) {
295 4 : const BridgeRouteEntry *l2_rt = FindRoute(mac);
296 4 : if (l2_rt == NULL)
297 1 : return NULL;
298 :
299 3 : const MacVmBindingPath *dhcp_path = l2_rt->FindMacVmBindingPath();
300 3 : if (dhcp_path == NULL)
301 2 : return NULL;
302 1 : return dhcp_path->vm_interface();
303 : }
304 :
305 : /////////////////////////////////////////////////////////////////////////////
306 : // BridgeRouteEntry methods
307 : /////////////////////////////////////////////////////////////////////////////
308 1349 : const std::string BridgeRouteEntry::GetAddressString() const {
309 : //For broadcast, xmpp message is sent with address as 255.255.255.255
310 1349 : if (prefix_address_ == MacAddress::BroadcastMac()) {
311 295 : return "255.255.255.255";
312 : }
313 1054 : return ToString();
314 : }
315 :
316 86 : const std::string BridgeRouteEntry::GetSourceAddressString() const {
317 86 : if (prefix_address_ == MacAddress::BroadcastMac()) {
318 86 : return "0.0.0.0";
319 : }
320 0 : return (MacAddress::kZeroMac).ToString();
321 : }
322 :
323 8633 : string BridgeRouteEntry::ToString() const {
324 8633 : return prefix_address_.ToString();
325 : }
326 :
327 40862 : int BridgeRouteEntry::CompareTo(const Route &rhs) const {
328 40862 : const BridgeRouteEntry &a = static_cast<const BridgeRouteEntry &>(rhs);
329 :
330 40862 : return prefix_address_.CompareTo(a.prefix_address_);
331 : }
332 :
333 1817 : DBEntryBase::KeyPtr BridgeRouteEntry::GetDBRequestKey() const {
334 : BridgeRouteKey *key =
335 1817 : new BridgeRouteKey(Agent::GetInstance()->local_vm_peer(),
336 1817 : vrf()->GetName(), prefix_address_);
337 1817 : return DBEntryBase::KeyPtr(key);
338 : }
339 :
340 0 : void BridgeRouteEntry::SetKey(const DBRequestKey *key) {
341 0 : const BridgeRouteKey *k = static_cast<const BridgeRouteKey *>(key);
342 0 : SetVrf(Agent::GetInstance()->vrf_table()->FindVrfFromName(k->vrf_name()));
343 0 : prefix_address_ = k->GetMac();
344 0 : }
345 :
346 0 : uint32_t BridgeRouteEntry::GetActiveLabel() const {
347 0 : uint32_t label = 0;
348 :
349 0 : if (is_multicast()) {
350 0 : if (TunnelType::ComputeType(TunnelType::AllType()) ==
351 : (1 << TunnelType::VXLAN)) {
352 0 : label = GetActivePath()->vxlan_id();
353 : } else {
354 0 : label = GetActivePath()->label();
355 : }
356 : } else {
357 0 : label = GetActivePath()->GetActiveLabel();
358 : }
359 0 : return label;
360 : }
361 :
362 4094 : AgentPath *BridgeRouteEntry::FindPathUsingKeyData
363 : (const AgentRouteKey *key, const AgentRouteData *data) const {
364 4094 : const Peer *peer = key->peer();
365 4094 : if (is_multicast())
366 1301 : return FindMulticastPathUsingKeyData(key, data);
367 2793 : const EvpnPeer *evpn_peer = dynamic_cast<const EvpnPeer*>(peer);
368 2793 : if (evpn_peer != NULL)
369 1509 : return FindEvpnPathUsingKeyData(key, data);
370 :
371 1284 : return FindPath(peer);
372 : }
373 :
374 1301 : AgentPath *BridgeRouteEntry::FindMulticastPathUsingKeyData
375 : (const AgentRouteKey *key, const AgentRouteData *data) const {
376 1301 : assert(is_multicast());
377 :
378 : Route::PathList::const_iterator it;
379 6511 : for (it = GetPathList().begin(); it != GetPathList().end();
380 : it++) {
381 2474 : const AgentPath *path = static_cast<const AgentPath *>(it.operator->());
382 2474 : if (path->peer() != key->peer())
383 1304 : continue;
384 :
385 : //Handle multicast peer matching,
386 : //In case of BGP peer also match VXLAN id.
387 1170 : if (path->peer()->GetType() != Peer::BGP_PEER)
388 1170 : return const_cast<AgentPath *>(path);
389 :
390 : const MulticastRoute *multicast_data =
391 0 : dynamic_cast<const MulticastRoute *>(data);
392 0 : assert(multicast_data != NULL);
393 0 : if (multicast_data->vxlan_id() != path->vxlan_id())
394 0 : continue;
395 :
396 : //In multicast from same peer, TOR and EVPN comp can
397 : //come. These should not overlap and be installed as
398 : //different path.
399 0 : const CompositeNH *cnh = dynamic_cast<CompositeNH *>(path->nexthop());
400 0 : if ((cnh != NULL) &&
401 0 : (multicast_data->comp_nh_type() != cnh->composite_nh_type()))
402 0 : continue;
403 :
404 0 : return const_cast<AgentPath *>(path);
405 : }
406 131 : return NULL;
407 : }
408 :
409 1509 : AgentPath *BridgeRouteEntry::FindEvpnPathUsingKeyData
410 : (const AgentRouteKey *key, const AgentRouteData *data) const {
411 1509 : const Peer *peer = key->peer();
412 1509 : const EvpnPeer *evpn_peer = dynamic_cast<const EvpnPeer*>(peer);
413 1509 : assert(evpn_peer != NULL);
414 :
415 : Route::PathList::const_iterator it;
416 7191 : for (it = GetPathList().begin(); it != GetPathList().end(); it++) {
417 2503 : const AgentPath *path = static_cast<const AgentPath *>(it.operator->());
418 2503 : if (path->peer() != key->peer())
419 316 : continue;
420 :
421 : //Handle mac route added via evpn route.
422 : const EvpnDerivedPath *evpn_path =
423 2187 : dynamic_cast<const EvpnDerivedPath *>(path);
424 : const EvpnDerivedPathData *evpn_data =
425 2187 : dynamic_cast<const EvpnDerivedPathData *>(data);
426 2187 : assert(evpn_path != NULL);
427 2187 : assert(evpn_data != NULL);
428 2187 : if (evpn_path->ethernet_tag() != evpn_data->ethernet_tag())
429 0 : continue;
430 2187 : if (evpn_path->ip_addr() != evpn_data->ip_addr())
431 1016 : continue;
432 1171 : return const_cast<AgentPath *>(path);
433 : }
434 338 : return NULL;
435 : }
436 :
437 2208 : const MacVmBindingPath *BridgeRouteEntry::FindMacVmBindingPath() const {
438 2208 : Agent *agent = (static_cast<AgentRouteTable *> (get_table()))->agent();
439 2208 : return dynamic_cast<MacVmBindingPath*>(FindPath(agent->mac_vm_binding_peer()));
440 : }
441 :
442 4094 : bool BridgeRouteEntry::ReComputePathAdd(AgentPath *path) {
443 4094 : if (is_multicast()) {
444 : //evaluate add of path
445 1301 : return ReComputeMulticastPaths(path, false);
446 : }
447 2793 : return false;
448 : }
449 :
450 891 : bool BridgeRouteEntry::ReComputePathDeletion(AgentPath *path) {
451 891 : if (is_multicast()) {
452 : //evaluate delete of path
453 131 : return ReComputeMulticastPaths(path, true);
454 : }
455 760 : return false;
456 : }
457 :
458 : /////////////////////////////////////////////////////////////////////////////
459 : // Sandesh related methods
460 : /////////////////////////////////////////////////////////////////////////////
461 0 : void BridgeRouteReq::HandleRequest() const {
462 : VrfEntry *vrf =
463 0 : Agent::GetInstance()->vrf_table()->FindVrfFromId(get_vrf_index());
464 0 : if (!vrf) {
465 0 : ErrorResp *resp = new ErrorResp();
466 0 : resp->set_context(context());
467 0 : resp->Response();
468 0 : return;
469 : }
470 :
471 0 : AgentSandeshPtr sand(new AgentBridgeRtSandesh(vrf, context(), "",
472 0 : get_stale(), get_mac()));
473 0 : sand->DoSandesh(sand);
474 0 : }
475 :
476 0 : AgentSandeshPtr BridgeAgentRouteTable::GetAgentSandesh
477 : (const AgentSandeshArguments *args, const std::string &context) {
478 0 : return AgentSandeshPtr(new AgentBridgeRtSandesh(vrf_entry(), context, "",
479 0 : false, args->GetString("mac")));
480 : }
481 :
482 0 : bool BridgeRouteEntry::DBEntrySandesh(Sandesh *sresp, bool stale) const {
483 0 : BridgeRouteResp *resp = static_cast<BridgeRouteResp *>(sresp);
484 0 : RouteL2SandeshData data;
485 0 : data.set_mac(ToString());
486 0 : data.set_src_vrf(vrf()->GetName());
487 :
488 0 : for (Route::PathList::const_iterator it = GetPathList().begin();
489 0 : it != GetPathList().end(); it++) {
490 0 : const AgentPath *path = static_cast<const AgentPath *>(it.operator->());
491 0 : if (path) {
492 0 : PathSandeshData pdata;
493 0 : path->SetSandeshData(pdata);
494 0 : if (is_multicast()) {
495 0 : pdata.set_vxlan_id(path->vxlan_id());
496 : }
497 0 : const EvpnDerivedPath *evpn_path = dynamic_cast<const EvpnDerivedPath *>(path);
498 0 : if (evpn_path) {
499 0 : pdata.set_info(evpn_path->parent());
500 : }
501 0 : data.path_list.push_back(pdata);
502 0 : }
503 : }
504 : std::vector<RouteL2SandeshData> &list =
505 0 : const_cast<std::vector<RouteL2SandeshData>&>(resp->get_route_list());
506 0 : list.push_back(data);
507 0 : return true;
508 0 : }
509 :
510 : //Supporting deprecated layer2 requests
511 0 : void Layer2RouteReq::HandleRequest() const {
512 : VrfEntry *vrf =
513 0 : Agent::GetInstance()->vrf_table()->FindVrfFromId(get_vrf_index());
514 0 : if (!vrf) {
515 0 : ErrorResp *resp = new ErrorResp();
516 0 : resp->set_context(context());
517 0 : resp->Response();
518 0 : return;
519 : }
520 :
521 0 : AgentSandeshPtr sand(new AgentLayer2RtSandesh(vrf, context(), "",
522 0 : get_stale()));
523 0 : sand->DoSandesh(sand);
524 0 : }
|