]> git.stg.codes - ssmd.git/blob - src/switch.cpp
Control dumping snmp scripts.
[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::ostream * stream = NULL;
134         std::string fileName(_settings.scriptBase() + "/" + _ip + ".sh");
135         std::string newFileName(fileName + ".new");
136
137         if (_settings.dumpScripts()) {
138             stream = new std::ofstream(newFileName.c_str());
139             *stream << "#!/bin/sh\n";
140         } else {
141             stream = new std::stringstream;
142         }
143
144         if (!dropACLs(target, *stream)) {
145             logger << "Switch::sync() - failed to drop ACLs for the switch '" << _ip << "'" << std::endl;
146             delete stream;
147             return;
148         }
149
150         if (!createACLs(target, *stream)) {
151             logger << "Switch::sync() - failed to create ACLs for the switch '" << _ip << "'" << std::endl;
152             delete stream;
153             return;
154         }
155
156         delete stream;
157         if (_settings.dumpScripts())
158             rename(newFileName.c_str(), fileName.c_str());
159     }
160
161     if (_settings.isDebug()) {
162         logger << "Switch::sync() - switch '" << _ip << "' synchronized successfully, ACLs: " << _acls.size() << std::endl;
163     }
164 }
165
166 bool Switch::checkProfiles(const CTarget & target)
167 {
168     SNMPTable table(_snmp, target, Oid(swACLEtherRuleProfileID));
169     if (!table.valid()) {
170         logger << "Switch::checkProfiles() - profiles SNMPTable is invalid for the switch '" << _ip << "'" << std::endl;
171         return false;
172     }
173     if (table.empty()) {
174         // Ok, just an empty table
175         return false;
176     }
177     if (table.valueExists(
178             static_cast<int>(_settings.upProfileId())
179         ) &&
180         table.valueExists(
181             static_cast<int>(_settings.downProfileId())
182         )) {
183         return true;
184     }
185     return false;
186 }
187
188 bool Switch::dropACLs(const CTarget & target, std::ostream & stream)
189 {
190     std::string upOidValue(swACLEtherRuleAccessID);
191     upOidValue += ".";
192     upOidValue += boost::lexical_cast<std::string>(_settings.upProfileId());
193     std::string downOidValue(swACLEtherRuleAccessID);
194     downOidValue += ".";
195     downOidValue += boost::lexical_cast<std::string>(_settings.downProfileId());
196     SNMPTable aclsUpTable(_snmp, target, Oid(upOidValue.c_str()));
197     SNMPTable aclsDownTable(_snmp, target, Oid(downOidValue.c_str()));
198     if (!aclsUpTable.valid()) {
199         logger << "Switch::dropACLs() - upload profile acls SNMPTable is invalid for the switch '" << _ip << "'" << std::endl;
200         return false;
201     }
202     if (!aclsDownTable.valid()) {
203         logger << "Switch::dropACLs() - download profile acls SNMPTable is invalid for the switch '" << _ip << "'" << std::endl;
204         return false;
205     }
206     if (!aclsUpTable.empty()) {
207         if (!dropACLsByTable(target, _settings.upProfileId(), aclsUpTable, stream)) {
208             logger << "Switch::dropACLs() - failed to drop acls from upload table for the switch '" << _ip << "'" << std::endl;
209             return false;
210         }
211     }
212     if (!aclsDownTable.empty()) {
213         if (!dropACLsByTable(target, _settings.downProfileId(), aclsDownTable, stream)) {
214             logger << "Switch::dropACLs() - failed to drop acls from download table for the switch '" << _ip << "'" << std::endl;
215             return false;
216         }
217     }
218     return true;
219 }
220
221 bool Switch::dropACLsByTable(const CTarget & target, unsigned profileId, const SNMPTable & table, std::ostream & stream)
222 {
223     std::string dropACLOidPrefix(swACLEtherRuleRowStatus);
224     dropACLOidPrefix += ".";
225     dropACLOidPrefix += boost::lexical_cast<std::string>(profileId);
226     SNMPList aclsList(table.getList());
227     SNMPList::const_iterator it(aclsList.begin());
228     size_t chunks = aclsList.size() / _settings.maxACLPerPDU() + 1;
229     for (size_t i = 0; i < chunks && it != aclsList.end(); ++i) {
230         Pdu pdu;
231         if (_settings.dumpScripts())
232             stream << "snmpset -v2c -c" << _writeCommunity << " " << _ip;
233         for (size_t j = 0; j < _settings.maxACLPerPDU() && it != aclsList.end(); ++j, ++it) {
234             int id;
235             if (int c = it->get_value(id) != SNMP_CLASS_SUCCESS) {
236                 logger << "Switch::dropACLsByTable() - failed to get ACL id for the switch '" << _ip << "'. Error message: '" << Snmp::error_msg(c) << "'" << std::endl;
237                 return false;
238             }
239             std::string dropACLOid(dropACLOidPrefix);
240             dropACLOid += ".";
241             dropACLOid += boost::lexical_cast<std::string>(id);
242             Vb vb(Oid(dropACLOid.c_str()));
243             vb.set_value(int(6));
244             pdu += vb;
245             if (_settings.dumpScripts())
246                 stream << " " << dropACLOid << " i 6";
247         }
248         if (_settings.dumpScripts())
249             stream << "\n";
250         if (int c = _snmp.set(pdu, target) != SNMP_CLASS_SUCCESS) {
251             if (c != SNMP_ERROR_TOO_BIG) {
252                 logger << "Switch::dropACLsByTable() - failed to invoke Snmp::set for the switch '" << _ip << "'. Error message: '" << Snmp::error_msg(c) << "'" << std::endl;
253                 return false;
254             }
255         }
256     }
257     return true;
258 }
259
260 bool Switch::createACLs(const CTarget & target, std::ostream & stream)
261 {
262     std::vector<ACL>::const_iterator it;
263     size_t pos = 0;
264     for (it = _acls.begin(); it != _acls.end(); ++it) {
265         Pdu pdu;
266         it->appendPdu(pdu);
267         if (int c = _snmp.set(pdu, target) != SNMP_CLASS_SUCCESS) {
268             if (c != SNMP_ERROR_TOO_BIG) {
269                 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;
270                 logger << "Switch::createACLs() - ACL dump: " << *it << std::endl;
271                 return false;
272             }
273         }
274         pdu.clear();
275         _aclsCreated = true;
276         ++pos;
277         if (_settings.dumpScripts())
278             stream << "snmpset -v2c -c" << _writeCommunity << " " << _ip << " " << *it << "\n";
279     }
280     return true;
281 }