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