]> git.stg.codes - ssmd.git/blob - src/switch.cpp
444324b946edeb84179eb67a9a6f02fc06e75ec7
[ssmd.git] / src / switch.cpp
1 #include <cstdio>
2 #include <fstream>
3 #include <boost/lexical_cast.hpp>
4
5 #include "snmp_pp/snmp_pp.h"
6
7 #include "switch.h"
8 #include "settings.h"
9 #include "subscriber.h"
10 #include "acl.h"
11 #include "logger.h"
12 #include "snmptable.h"
13 #include "oids.h"
14
15 using SSMD::Switch;
16 using SSMD::SNMPTable;
17
18 Switch::Switch(const Settings & settings,
19                Snmp & snmp,
20                const std::string & ip,
21                const std::string & readCommunity,
22                const std::string & writeCommunity,
23                unsigned uplinkPort)
24     : _settings(settings),
25       _snmp(snmp),
26       _ip(ip),
27       _readCommunity(readCommunity),
28       _writeCommunity(writeCommunity),
29       _uplinkPort(uplinkPort),
30       _nextUpACL(1),
31       _nextDownACL(1),
32       _aclsCreated(false)
33 {
34 }
35
36 Switch::Switch(const Switch & rvalue)
37     : _settings(rvalue._settings),
38       _snmp(rvalue._snmp),
39       _ip(rvalue._ip),
40       _readCommunity(rvalue._readCommunity),
41       _writeCommunity(rvalue._writeCommunity),
42       _uplinkPort(rvalue._uplinkPort),
43       _nextUpACL(rvalue._nextUpACL),
44       _nextDownACL(rvalue._nextDownACL),
45       _acls(rvalue._acls),
46       _aclsCreated(rvalue._aclsCreated)
47 {
48 }
49
50 Switch::~Switch()
51 {
52     if (_aclsCreated) {
53         IpAddress addr(_ip.c_str());
54         if (!addr.valid()) {
55             logger << "Switch::~Switch() - ivalid switch ip: '" << _ip << "'" << std::endl;
56             return;
57         }
58
59         CTarget target(addr, _readCommunity.c_str(), _writeCommunity.c_str());
60         if (!target.valid()) {
61             logger << "Switch::~Switch() - failed to create target for the switch '" << _ip << "'" << std::endl;
62             return;
63         }
64
65         target.set_version(version2c);
66
67         if (!checkProfiles(target)) {
68             logger << "Switch::~Switch() - no upload and download profiles defined for the switch '" << _ip << "'" << std::endl;
69             return;
70         }
71
72         if (!dropACLs(target, std::cerr)) {
73             logger << "Switch::~Switch() - failed to drop ACLs for the switch '" << _ip << "'" << std::endl;
74             return;
75         }
76     }
77 }
78
79 Switch & Switch::operator=(const Switch & rvalue)
80 {
81     _ip = rvalue._ip;
82     _readCommunity = rvalue._readCommunity;
83     _writeCommunity = rvalue._writeCommunity;
84     _uplinkPort = rvalue._uplinkPort;
85     _nextUpACL = rvalue._nextUpACL;
86     _nextDownACL = rvalue._nextDownACL;
87     _acls = rvalue._acls;
88     _aclsCreated = rvalue._aclsCreated;
89
90     return *this;
91 }
92
93 void Switch::addSubscriber(const Subscriber & subscriber)
94 {
95     _acls.push_back(ACL(_nextUpACL++,
96                         _settings.upProfileId(),
97                         subscriber.getMAC(),
98                         subscriber.getPort(),
99                         subscriber.getUpShape(),
100                         subscriber.getUpBurst(),
101                         true));
102     _acls.push_back(ACL(_nextDownACL++,
103                         _settings.downProfileId(),
104                         subscriber.getMAC(),
105                         _uplinkPort,
106                         subscriber.getDownShape(),
107                         subscriber.getDownBurst(),
108                         false));
109 }
110
111 void Switch::sync()
112 {
113     IpAddress addr(_ip.c_str());
114     if (!addr.valid()) {
115         logger << "Switch::sync() - ivalid switch ip: '" << _ip << "'" << std::endl;
116         return;
117     }
118
119     CTarget target(addr, _readCommunity.c_str(), _writeCommunity.c_str());
120     if (!target.valid()) {
121         logger << "Switch::sync() - failed to create target for the switch '" << _ip << "'" << std::endl;
122         return;
123     }
124
125     target.set_version(version2c);
126
127     if (!checkProfiles(target)) {
128         logger << "Switch::sync() - no upload and download profiles defined for the switch '" << _ip << "'" << std::endl;
129         return;
130     }
131
132     {
133         std::string fileName(_ip + ".sh");
134         std::string newFileName(fileName + ".new");
135         std::ofstream script(newFileName.c_str());
136         script << "#!/bin/sh\n";
137         if (!dropACLs(target, script)) {
138             logger << "Switch::sync() - failed to drop ACLs for the switch '" << _ip << "'" << std::endl;
139             return;
140         }
141
142         if (!createACLs(target, script)) {
143             logger << "Switch::sync() - failed to create ACLs for the switch '" << _ip << "'" << std::endl;
144             return;
145         }
146         script.close();
147         rename(newFileName.c_str(), fileName.c_str());
148     }
149
150     if (_settings.isDebug()) {
151         logger << "Switch::sync() - switch '" << _ip << "' synchronized successfully, ACLs: " << _acls.size() << std::endl;
152     }
153 }
154
155 bool Switch::checkProfiles(const CTarget & target)
156 {
157     SNMPTable table(_snmp, target, Oid(swACLEtherRuleProfileID));
158     if (!table.valid()) {
159         logger << "Switch::checkProfiles() - profiles SNMPTable is invalid for the switch '" << _ip << "'" << std::endl;
160         return false;
161     }
162     if (table.empty()) {
163         // Ok, just an empty table
164         return false;
165     }
166     if (table.valueExists(
167             static_cast<int>(_settings.upProfileId())
168         ) &&
169         table.valueExists(
170             static_cast<int>(_settings.downProfileId())
171         )) {
172         return true;
173     }
174     return false;
175 }
176
177 bool Switch::dropACLs(const CTarget & target, std::ostream & stream)
178 {
179     std::string upOidValue(swACLEtherRuleAccessID);
180     upOidValue += ".";
181     upOidValue += boost::lexical_cast<std::string>(_settings.upProfileId());
182     std::string downOidValue(swACLEtherRuleAccessID);
183     downOidValue += ".";
184     downOidValue += boost::lexical_cast<std::string>(_settings.downProfileId());
185     SNMPTable aclsUpTable(_snmp, target, Oid(upOidValue.c_str()));
186     SNMPTable aclsDownTable(_snmp, target, Oid(downOidValue.c_str()));
187     if (!aclsUpTable.valid()) {
188         logger << "Switch::dropACLs() - upload profile acls SNMPTable is invalid for the switch '" << _ip << "'" << std::endl;
189         return false;
190     }
191     if (!aclsDownTable.valid()) {
192         logger << "Switch::dropACLs() - download profile acls SNMPTable is invalid for the switch '" << _ip << "'" << std::endl;
193         return false;
194     }
195     if (!aclsUpTable.empty()) {
196         if (!dropACLsByTable(target, _settings.upProfileId(), aclsUpTable, stream)) {
197             logger << "Switch::dropACLs() - failed to drop acls from upload table for the switch '" << _ip << "'" << std::endl;
198             return false;
199         }
200     }
201     if (!aclsDownTable.empty()) {
202         if (!dropACLsByTable(target, _settings.downProfileId(), aclsDownTable, stream)) {
203             logger << "Switch::dropACLs() - failed to drop acls from download table for the switch '" << _ip << "'" << std::endl;
204             return false;
205         }
206     }
207     return true;
208 }
209
210 bool Switch::dropACLsByTable(const CTarget & target, unsigned profileId, const SNMPTable & table, std::ostream & stream)
211 {
212     std::string dropACLOidPrefix(swACLEtherRuleRowStatus);
213     dropACLOidPrefix += ".";
214     dropACLOidPrefix += boost::lexical_cast<std::string>(profileId);
215     SNMPList aclsList(table.getList());
216     SNMPList::const_iterator it(aclsList.begin());
217     size_t chunks = aclsList.size() / _settings.maxACLPerPDU() + 1;
218     for (size_t i = 0; i < chunks && it != aclsList.end(); ++i) {
219         Pdu pdu;
220         stream << "snmpset -v2c -cgts_community_w " << _ip;
221         for (size_t j = 0; j < _settings.maxACLPerPDU() && it != aclsList.end(); ++j, ++it) {
222             int id;
223             if (int c = it->get_value(id) != SNMP_CLASS_SUCCESS) {
224                 logger << "Switch::dropACLsByTable() - failed to get ACL id for the switch '" << _ip << "'. Error message: '" << Snmp::error_msg(c) << "'" << std::endl;
225                 return false;
226             }
227             std::string dropACLOid(dropACLOidPrefix);
228             dropACLOid += ".";
229             dropACLOid += boost::lexical_cast<std::string>(id);
230             Vb vb(Oid(dropACLOid.c_str()));
231             vb.set_value(int(6));
232             pdu += vb;
233             stream << " " << dropACLOid << " i 6";
234         }
235         stream << "\n";
236         if (int c = _snmp.set(pdu, target) != SNMP_CLASS_SUCCESS) {
237             if (c != SNMP_ERROR_TOO_BIG) {
238                 logger << "Switch::dropACLsByTable() - failed to invoke Snmp::set for the switch '" << _ip << "'. Error message: '" << Snmp::error_msg(c) << "'" << std::endl;
239                 return false;
240             }
241         }
242     }
243     return true;
244 }
245
246 bool Switch::createACLs(const CTarget & target, std::ostream & stream)
247 {
248     std::vector<ACL>::const_iterator it;
249     size_t pos = 0;
250     for (it = _acls.begin(); it != _acls.end(); ++it) {
251         Pdu pdu;
252         it->appendPdu(pdu);
253         if (int c = _snmp.set(pdu, target) != SNMP_CLASS_SUCCESS) {
254             if (c != SNMP_ERROR_TOO_BIG) {
255                 logger << "Switch::createACLs() - failed to invoke Snmp::set for the switch '" << _ip << "'. Error message: '" << Snmp::error_msg(c) << "'. Error occured at creation of " << (pos + 1) << " from " << _acls.size() << " ACL's" << std::endl;
256                 logger << "Switch::createACLs() - ACL dump: " << *it << std::endl;
257                 return false;
258             }
259         }
260         pdu.clear();
261         _aclsCreated = true;
262         ++pos;
263         stream << "snmpset -v2c -cgts_community_w " << _ip << " " << *it << "\n";
264     }
265     return true;
266 }