- fixed retrieving of datarate for isdn (workaround bug #15006)
[opensuse:smpppd.git] / smpppd / connection.cc
1
2
3 /*
4  *  Author: Arvin Schnell <arvin@suse.de>
5  */
6
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <sys/stat.h>
12 #include <errno.h>
13 #include <glob.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <sys/ioctl.h>
17 #include <sys/socket.h>
18 #include <asm/types.h>
19 #include <net/if.h>
20 #include <linux/ppp_defs.h>
21 #include <linux/if_ppp.h>
22
23 #include <iostream>
24 #include <fstream>
25 #include <algorithm>
26
27 using std::cerr;
28
29 #include "link.h"
30 #include "main.h"
31 #include "modem.h"
32 #include "isdn.h"
33 #include "dsl.h"
34 #include "misc.h"
35 #include "utils.h"
36 #include "config.h"
37 #include "log.h"
38
39
40 PPPStat::PPPStat ()
41 {
42     fd = socket (AF_INET, SOCK_DGRAM, 0);
43     if (fd < 0) {
44         perror ("socket");
45         fd = -1;
46     }
47 }
48
49
50 PPPStat::~PPPStat ()
51 {
52     if (fd >= 0)
53         close (fd);
54 }
55
56
57 bool
58 isdn_stat (const char* ifname, long& rx_bytes, long& tx_bytes)
59 {
60     ifstream fin ("/proc/net/dev");
61     if (!fin)
62         return false;
63
64     while (!fin.eof ()) {
65
66         string line;
67         getline (fin, line);
68
69         string::size_type pos = line.find_first_of (":");
70
71         if (pos == string::npos || rmspaces (line.substr (0, pos)) != ifname)
72             continue;
73
74         string da = line.substr (pos + 1, string::npos);
75         char buffer [da.length () + 1];
76         strcpy (buffer, da.c_str ());
77         char *tmp = buffer;
78
79         int cnt = 0;
80
81         while (true) {
82
83             char *t = strsep (&tmp, "\t ");
84
85             if (t == NULL)
86                 break;
87
88             if (*t == '\0')
89                 continue;
90
91             switch (cnt++) {
92                 case 0: rx_bytes = atoll (t); break;
93                 case 8: tx_bytes = atoll (t); break;
94             }
95
96         }
97
98     }
99
100     return true;
101 }
102
103
104 bool
105 PPPStat::get_stat (const char* ifname, long& rx_bytes, long& tx_bytes)
106 {
107     if (ifname[0] == 'i')
108         return isdn_stat (ifname, rx_bytes, tx_bytes);
109
110     struct ifpppstatsreq req;
111     memset (&req, 0, sizeof (req));
112     req.stats_ptr = (caddr_t) &req.stats;
113     strncpy (req.ifr__name, ifname, sizeof (req.ifr__name));
114
115     if (ioctl (fd, SIOCGPPPSTATS, &req) < 0) {
116         perror ("ioctl (SIOCGPPPSTATS)");
117         return false;
118     }
119
120     rx_bytes = req.stats.p.ppp_ibytes;
121     tx_bytes = req.stats.p.ppp_obytes;
122     return true;
123 }
124
125
126 Connection::Connection ()
127 {
128 #ifndef NDEBUG
129     cerr << __PRETTY_FUNCTION__ << '\n';
130 #endif
131
132     defaultroute = true;
133     demand = false;
134     idle_seconds = 600;
135     auto_dns = true;
136     dns1 = dns2 = "";
137
138     username = password = "";
139     ask_password = false;
140
141     run_poll_tcpip = true;
142
143     reconnect = false;
144     reconnect_delay = 15;
145     reconnect_time = 0;
146     reconnect_exits = 256;
147
148     rx_bytes = tx_bytes = 0;
149 }
150
151
152 Connection::~Connection ()
153 {
154 #ifndef NDEBUG
155     cerr << __PRETTY_FUNCTION__ << '\n';
156 #endif
157 }
158
159
160 bool
161 Connection::get_rxtx_bytes (long& rx_bytes, long& tx_bytes)
162 {
163     rx_bytes = Connection::rx_bytes;
164     tx_bytes = Connection::tx_bytes;
165     return true;
166 }
167
168
169 bool
170 Connection::read_rxtx_bytes (const char* ifname)
171 {
172     long old_rx_bytes = rx_bytes;
173     long old_tx_bytes = tx_bytes;
174
175     if (!pppstat.get_stat (ifname, rx_bytes, tx_bytes))
176         return false;
177
178 #ifndef NDEBUG
179     cout << "data total = " << rx_bytes << ' ' << tx_bytes << '\n';
180 #endif
181
182     // FIXME: this is hard to understand
183     if (rx_bytes != old_rx_bytes || tx_bytes != old_tx_bytes) {
184         clients.write_rxtx_bytes ();
185         once_equal = false;
186     } else {
187         if (!once_equal) {
188             clients.write_rxtx_bytes ();
189             once_equal = true;
190         }
191     }
192
193     return true;
194 }
195
196
197 Connection*
198 Connection::create (const Interface* inter, const Provider* prov)
199 {
200     Connection* conn = 0;
201
202     switch (inter->mode) {
203
204         case Interface::MODEM:
205             conn = new ::Modem;
206             break;
207
208         case Interface::ISDN:
209             conn = new ::ISDN;
210             break;
211
212         case Interface::DSL:
213             conn = new ::DSL;
214             break;
215
216     }
217
218     if (conn) {
219         conn->read_config (inter, prov);
220 #ifndef NDEBUG
221         conn->dump_config ();
222 #endif
223         if (!conn->check_config ()) {
224             delete conn;
225             conn = 0;
226         }
227     }
228
229     return conn;
230 }
231
232
233 void
234 Connection::read_config (const Interface* inter, const Provider* prov)
235 {
236     for (int i = 0; i < 2; i++) {
237
238         const string filename = i == 0 ? inter->filename : prov->filename;
239
240         std::ifstream fin (filename.c_str ());
241         if (!fin) {
242             cerr << "error, can't open `" << filename << "' for reading: "
243                  << strerror (errno) << '\n';
244             return;
245         }
246
247         string line, key, value;
248
249         while (getline (fin, line)) {
250
251             int ret = getkeyvalue (line, key, value);
252
253             if (ret == 0)
254                 continue;
255
256             if (ret == -1) {
257                 cerr << "warning, syntax error in line `" << line << "'\n";
258                 continue;
259             }
260
261             eval_config (key, value);
262
263         }
264     }
265 }
266
267
268 void
269 Connection::eval_config (const string& key, const string& value)
270 {
271     if (key == "DEFAULTROUTE") {
272         defaultroute = value == "yes";
273         return;
274     }
275
276     if (key == "DEMAND") {
277         demand = value == "yes";
278         return;
279     }
280
281     if (key == "IDLETIME") {
282         idle_seconds = atoi (value.c_str ());
283         return;
284     }
285
286     if (key == "AUTODNS") {
287         auto_dns = value == "yes";
288         return;
289     }
290
291     if (key == "DNS1") {
292         dns1 = value;
293         return;
294     }
295
296     if (key == "DNS2") {
297         dns2 = value;
298         return;
299     }
300
301     if (key == "USERNAME") {
302         username = value;
303         return;
304     }
305
306     if (key == "PASSWORD") {
307         password = value;
308         return;
309     }
310
311     if (key == "ASKPASSWORD") {
312         ask_password = value == "yes";
313         return;
314     }
315
316     if (key == "AUTO_RECONNECT") {
317         reconnect = value == "yes";
318         return;
319     }
320
321     if (key == "AUTO_RECONNECT_DELAY") {
322         reconnect_delay = atoi (value.c_str ());
323         if (reconnect_delay < 5)
324             reconnect_delay = 5;
325         return;
326     }
327
328     if (key == "AUTO_RECONNECT_EXITS") {
329         reconnect_exits = 0;
330         char* buffer = strdup (value.c_str ());
331         char* tmp = buffer;
332         while (true) {
333             int i = strtol (tmp, &tmp, 10);
334             if (i == 0)
335                 break;
336             bit_set (i, &reconnect_exits);
337         }
338         free (buffer);
339     }
340
341     if (key == "RUN_POLL_TCPIP") {
342         run_poll_tcpip = value != "no";
343         return;
344     }
345
346 }
347
348
349 bool
350 Connection::check_config ()
351 {
352     if (username.empty ()) {
353         mylog.sprintf (true, "Configuration does not specify a username.");
354         return false;
355     }
356
357     return true;
358 }
359
360
361 void
362 Connection::list_config (std::list <string>* config) const
363 {
364     config->push_back ("username = " + username);
365     config->push_back ("password = <hidden>");
366     config->push_back ("ask-password = " + string (ask_password ? "yes" : "no"));
367
368     config->push_back ("defaultroute = " + string (defaultroute ? "yes" : "no"));
369
370     config->push_back ("demand = " + string (demand ? "yes" : "no"));
371     config->push_back ("idle-seconds = " + tostring (idle_seconds));
372
373     config->push_back ("auto-dns = " + string (auto_dns ? "yes" : "no"));
374     config->push_back ("dns1 = " + dns1);
375     config->push_back ("dns2 = " + dns2);
376
377     config->push_back ("auto-reconnect = " + string (reconnect ? "yes" : "no"));
378     config->push_back ("auto-reconnect-delay = " + tostring (reconnect_delay));
379
380     string tmp1;
381     for (int i = 0; i < 32; i++)
382         if (bit_test (i, reconnect_exits)) {
383             if (!tmp1.empty ())
384                 tmp1.append (" ");
385             tmp1.append (tostring (i));
386         }
387     config->push_back ("auto-reconnect-exits = " + tmp1);
388
389     config->push_back ("run-poll-tcpip = " + string (run_poll_tcpip ? "yes" : "no"));
390 }
391
392
393 #ifndef NDEBUG
394 void
395 Connection::dump_config () const
396 {
397     std::list <string> config;
398     list_config (&config);
399     for (std::list <string>::const_iterator it = config.begin ();
400          it != config.end (); it++)
401         cout << *it << '\n';
402 }
403 #endif
404