Line data Source code
1 : /*
2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "cmn/agent_cmn.h"
6 : #include "init/agent_param.h"
7 : #include "oper/vn.h"
8 : #include "oper/route_common.h"
9 : #include "oper/multicast.h"
10 : #include "services/igmp_proto.h"
11 :
12 0 : IgmpProto::IgmpProto(Agent *agent, boost::asio::io_context &io) :
13 : Proto(agent, "Agent::Services", PktHandler::IGMP, io),
14 0 : task_name_("Agent::Services"), io_(io) {
15 :
16 0 : IgmpProtoInit();
17 0 : }
18 :
19 0 : IgmpProto::~IgmpProto() {
20 0 : }
21 :
22 0 : void IgmpProto::IgmpProtoInit(void) {
23 :
24 : // limit the number of entries in the workqueue
25 0 : work_queue_.SetSize(agent_->params()->services_queue_limit());
26 0 : work_queue_.SetBounded(true);
27 :
28 0 : gmp_proto_ = GmpProtoManager::CreateGmpProto(GmpType::IGMP, agent_,
29 0 : task_name_, PktHandler::IGMP, io_);
30 0 : if (gmp_proto_) {
31 0 : gmp_proto_->Register(
32 : boost::bind(&IgmpProto::SendIgmpPacket, this, _1, _2, _3));
33 0 : gmp_proto_->Start();
34 : }
35 :
36 0 : vn_listener_id_ = agent_->vn_table()->Register(
37 : boost::bind(&IgmpProto::VnNotify, this, _1, _2));
38 :
39 0 : ClearStats();
40 0 : }
41 :
42 0 : void IgmpProto::Shutdown() {
43 :
44 0 : agent_->vn_table()->Unregister(vn_listener_id_);
45 :
46 0 : if (gmp_proto_) {
47 0 : gmp_proto_->Stop();
48 0 : GmpProtoManager::DeleteGmpProto(gmp_proto_);
49 : }
50 0 : }
51 :
52 0 : ProtoHandler *IgmpProto::AllocProtoHandler(boost::shared_ptr<PktInfo> info,
53 : boost::asio::io_context &io) {
54 0 : return new IgmpHandler(agent(), info, io);
55 : }
56 :
57 0 : void IgmpProto::VnNotify(DBTablePartBase *part, DBEntryBase *entry) {
58 :
59 : // Registering/Unregistering every IPAM gateway (or) dns_server
60 : // present in the VN with the IGMP module.
61 : // Changes to VN, or VN IPAM info, or gateway or dns server is
62 : // handled below.
63 :
64 0 : VnEntry *vn = static_cast<VnEntry *>(entry);
65 0 : IgmpInfo::IgmpSubnetState *igmp_intf = NULL;
66 :
67 : IgmpInfo::VnIgmpDBState *state =
68 : static_cast<IgmpInfo::VnIgmpDBState *>
69 0 : (entry->GetState(part->parent(), vn_listener_id_));
70 :
71 0 : if (vn->IsDeleted() || !vn->GetVrf()) {
72 0 : if (!state) {
73 0 : return;
74 : }
75 : IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::iterator it =
76 0 : state->igmp_state_map_.begin();
77 0 : for (;it != state->igmp_state_map_.end(); ++it) {
78 0 : igmp_intf = it->second;
79 0 : delete igmp_intf;
80 : }
81 0 : state->igmp_state_map_.clear();
82 :
83 0 : if (vn->IsDeleted()) {
84 0 : entry->ClearState(part->parent(), vn_listener_id_);
85 0 : delete state;
86 : }
87 0 : return;
88 : }
89 :
90 0 : if (!vn->GetVrf()) {
91 0 : return;
92 : }
93 :
94 0 : if ((vn->GetVrf()->GetName() == agent_->fabric_policy_vrf_name()) ||
95 0 : (vn->GetVrf()->GetName() == agent_->fabric_vrf_name())) {
96 0 : return;
97 : }
98 :
99 0 : if (state == NULL) {
100 0 : state = new IgmpInfo::VnIgmpDBState();
101 :
102 0 : entry->SetState(part->parent(), vn_listener_id_, state);
103 : }
104 :
105 : IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::iterator it =
106 0 : state->igmp_state_map_.begin();
107 0 : while (it != state->igmp_state_map_.end()) {
108 0 : const VnIpam *ipam = vn->GetIpam(it->first);
109 0 : if ((ipam != NULL) && ((ipam->default_gw == it->first) ||
110 0 : (ipam->dns_server == it->first))) {
111 0 : it++;
112 0 : continue;
113 : }
114 0 : igmp_intf = it->second;
115 0 : delete igmp_intf;
116 0 : state->igmp_state_map_.erase(it++);
117 : }
118 :
119 0 : const std::vector<VnIpam> &ipam = vn->GetVnIpam();
120 0 : for (unsigned int i = 0; i < ipam.size(); ++i) {
121 0 : if (!ipam[i].IsV4()) {
122 0 : continue;
123 : }
124 0 : if ((ipam[i].default_gw == IpAddress(Ip4Address())) &&
125 0 : (ipam[i].dns_server == IpAddress(Ip4Address()))) {
126 0 : continue;
127 : }
128 :
129 0 : IpAddress igmp_address = IpAddress(Ip4Address());
130 0 : IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it;
131 :
132 0 : if (ipam[i].dns_server != IpAddress(Ip4Address())) {
133 0 : it = state->igmp_state_map_.find(ipam[i].dns_server);
134 0 : igmp_address = ipam[i].dns_server;
135 : }
136 0 : if (ipam[i].default_gw != IpAddress(Ip4Address())) {
137 0 : if (it != state->igmp_state_map_.end()) {
138 0 : igmp_intf = it->second;
139 0 : delete igmp_intf;
140 0 : state->igmp_state_map_.erase(it->first);
141 : }
142 :
143 0 : igmp_address = ipam[i].default_gw;
144 : }
145 :
146 0 : it = state->igmp_state_map_.find(igmp_address);
147 0 : if (it == state->igmp_state_map_.end()) {
148 0 : igmp_intf = new IgmpInfo::IgmpSubnetState;
149 0 : state->igmp_state_map_.insert(
150 0 : std::pair<IpAddress, IgmpInfo::IgmpSubnetState*>
151 : (igmp_address, igmp_intf));
152 : }
153 : }
154 : }
155 :
156 0 : DBTableBase::ListenerId IgmpProto::vn_listener_id () {
157 0 : return vn_listener_id_;
158 : }
159 :
160 : // Send IGMP packets to the VMs part of the IPAM VN
161 0 : bool IgmpProto::SendIgmpPacket(const VrfEntry *vrf, IpAddress gmp_addr,
162 : GmpPacket *packet) {
163 :
164 0 : if (!vrf || !packet) {
165 0 : return false;
166 : }
167 :
168 0 : if (!gmp_addr.is_v4()) {
169 0 : return false;
170 : }
171 :
172 0 : VnEntry *vn = vrf->vn();
173 0 : if (!vn) {
174 0 : return false;
175 : }
176 :
177 0 : const VnIpam *ipam = vn->GetIpam(gmp_addr);
178 0 : if (!ipam) {
179 0 : return false;
180 : }
181 :
182 0 : Ip4Address subnet = (ipam->GetSubnetAddress()).to_v4();
183 : InetUnicastAgentRouteTable *inet_table =
184 0 : vrf->GetInet4UnicastRouteTable();
185 0 : const InetUnicastRouteEntry *rt = inet_table->FindRoute(subnet);
186 0 : if (!rt) {
187 0 : return false;
188 : }
189 :
190 : boost::shared_ptr<PktInfo> pkt(new PktInfo(agent_, 1024, PktHandler::IGMP,
191 0 : 0));
192 : IgmpHandler igmp_handler(agent_, pkt,
193 0 : *(agent_->event_manager()->io_service()));
194 :
195 : do {
196 0 : const NextHop *nh = rt->GetActiveNextHop();
197 0 : if (!nh || nh->IsDeleted()) {
198 0 : continue;
199 : }
200 0 : const InterfaceNH *inh = dynamic_cast<const InterfaceNH *>(nh);
201 0 : if (!inh) {
202 0 : continue;
203 : }
204 0 : const Interface *itf = inh->GetInterface();
205 0 : if (!itf) {
206 0 : continue;
207 : }
208 0 : const VmInterface *vm_itf = dynamic_cast<const VmInterface *>(itf);
209 0 : if (!vm_itf || vm_itf->IsDeleted()) {
210 0 : continue;
211 : }
212 0 : if (vm_itf->vmi_type() == VmInterface::VHOST) {
213 0 : continue;
214 : }
215 0 : if (vm_itf->vrf() && vrf->GetName() != vm_itf->vrf()->GetName()) {
216 0 : continue;
217 : }
218 0 : if (!ipam->IsSubnetMember(IpAddress(vm_itf->primary_ip_addr()))) {
219 0 : break;
220 : }
221 0 : if (!vm_itf->igmp_enabled()) {
222 0 : IncrSendStats(vm_itf, false);
223 0 : continue;
224 : }
225 :
226 0 : igmp_handler.SendPacket(vm_itf, vrf, gmp_addr, packet);
227 :
228 0 : } while ((rt = inet_table->GetNext(rt)) != NULL);
229 :
230 0 : return true;
231 0 : }
232 :
233 0 : void IgmpProto::IncrSendStats(const VmInterface *vm_itf, bool tx_done) {
234 :
235 0 : const VnEntry *vn = vm_itf->vn();
236 0 : IgmpInfo::VnIgmpDBState *state = NULL;
237 0 : state = static_cast<IgmpInfo::VnIgmpDBState *>(vn->GetState(
238 : vn->get_table_partition()->parent(),
239 : vn_listener_id()));
240 0 : if (!state) {
241 0 : return;
242 : }
243 0 : const VnIpam *ipam = vn->GetIpam(vm_itf->primary_ip_addr());
244 0 : if (!ipam) {
245 0 : return;
246 : }
247 : IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it =
248 0 : state->igmp_state_map_.find(ipam->default_gw);
249 0 : if (it == state->igmp_state_map_.end()) {
250 0 : return;
251 : }
252 :
253 0 : IgmpInfo::IgmpSubnetState *igmp_intf = NULL;
254 0 : igmp_intf = it->second;
255 :
256 0 : if (tx_done) {
257 0 : igmp_intf->IncrTxPkt();
258 : } else {
259 0 : igmp_intf->IncrTxDropPkt();
260 : }
261 :
262 0 : return;
263 : }
264 :
265 0 : const bool IgmpProto::GetItfStats(const VnEntry *vn, IpAddress gateway,
266 : IgmpInfo::IgmpItfStats &stats) {
267 :
268 0 : const VnIpam *ipam = vn->GetIpam(gateway);
269 0 : if (!ipam) {
270 0 : return false;
271 : }
272 :
273 0 : IgmpInfo::VnIgmpDBState *state = NULL;
274 0 : state = static_cast<IgmpInfo::VnIgmpDBState *>(vn->GetState(
275 : vn->get_table_partition()->parent(), vn_listener_id_));
276 0 : if (!state) {
277 0 : return false;
278 : }
279 :
280 : IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it =
281 0 : state->igmp_state_map_.find(ipam->default_gw);
282 0 : if (it == state->igmp_state_map_.end()) {
283 0 : return false;
284 : }
285 :
286 0 : IgmpInfo::IgmpSubnetState *igmp_intf = it->second;
287 :
288 0 : stats = igmp_intf->GetItfStats();
289 :
290 0 : return true;
291 : }
292 :
293 0 : void IgmpProto::ClearItfStats(const VnEntry *vn, IpAddress gateway) {
294 :
295 0 : if (!vn) {
296 0 : return;
297 : }
298 :
299 0 : const VnIpam *ipam = vn->GetIpam(gateway);
300 0 : if (!ipam) {
301 0 : return;
302 : }
303 :
304 0 : IgmpInfo::VnIgmpDBState *state = NULL;
305 0 : state = static_cast<IgmpInfo::VnIgmpDBState *>(vn->GetState(
306 : vn->get_table_partition()->parent(), vn_listener_id_));
307 0 : if (!state) {
308 0 : return;
309 : }
310 :
311 : IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it =
312 0 : state->igmp_state_map_.find(ipam->default_gw);
313 0 : if (it == state->igmp_state_map_.end()) {
314 0 : return;
315 : }
316 :
317 0 : IgmpInfo::IgmpSubnetState *igmp_intf = it->second;
318 0 : igmp_intf->ClearItfStats();
319 :
320 0 : return;
321 : }
|