- added pptp support (bug #18407)
[opensuse:smpppd.git] / smpppd / dsl.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 <signal.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <paths.h>
15 #include <errno.h>
16
17 #include "dsl.h"
18 #include "defines.h"
19 #include "utils.h"
20
21
22 DSL::DSL (const DSLConfig* dslconfig, void (*lcb) (bool, const string&),
23           void (*scb) (Status), void (*ecb) ())
24     : Connection (dslconfig, lcb, scb, ecb),
25       dslconfig (dslconfig),
26       pppd_log (0)
27 {
28     dprintf ("%s\n", __PRETTY_FUNCTION__);
29
30     padi_timeout = -1;
31 }
32
33
34 DSL::~DSL ()
35 {
36     dprintf ("%s\n", __PRETTY_FUNCTION__);
37
38     if (pppd.is_running ())
39         pppd.kill (2);
40
41     // It may be that the pppd was stopped but we didn't see the
42     // ip-down message yet. So make sure the accounting if flushed.
43     // Ugly, I know.
44     accounting_down ();
45 }
46
47
48 bool
49 DSL::start ()
50 {
51     dprintf ("%s\n", __PRETTY_FUNCTION__);
52
53     string interface = dslconfig->device;
54
55     // For those hotplug devices we must lookup the real interface
56     // in /var/run/sysconfig/if-<device>.
57     if (endswith (interface, "-pcmcia") || endswith (interface, "-usb")) {
58         string tmp1 = string (SYSCONFIG_RUN_PATH "if-" + interface);
59         if (!read_interface_from_sysconfig_file (tmp1.c_str (), &interface)) {
60             logit (true, "error: cannot read real interface for %s.",
61                    interface.c_str ());
62             return false;
63         }
64     }
65
66     logit (false, "Interface is %s.", interface.c_str ());
67
68     pppd.clear ();
69
70     pppd << _PATH_PPPD;
71
72     if (dslconfig->ppp_mode == DSLConfig::PPP) {
73         pppd << interface;
74         pppd << dslconfig->baud_rate;
75     }
76
77     if (!dslconfig->pppd_options.empty ())
78         pppd << split (dslconfig->pppd_options, " ");
79
80     // open a pipe to access the messages of pppd
81     int pppd_msgfd[2];
82     if (pipe (pppd_msgfd) == -1) {
83         dprintf ("error, pipe failed: %m\n");
84         return false;
85     }
86     pppd << "logfd" << pppd_msgfd[1];
87     pppd.add_fd_notclose (pppd_msgfd[1]);
88
89     switch (dslconfig->ppp_mode) {
90
91         case DSLConfig::PPP: {
92
93             // device and baud-rate already handled above
94
95             // call peers file ppp
96             if (check_peers_file ("ppp"))
97                 pppd << "call" << "ppp";
98
99         } break;
100
101         case DSLConfig::PPPOE: {
102
103             // call peers file pppoe
104             if (check_peers_file ("pppoe"))
105                 pppd << "call" << "pppoe";
106
107             // The device must be passed after the "call pppoe", since the
108             // "call pppoe" loads the plugin that can handle the device.
109             pppd << interface.c_str ();
110
111         } break;
112
113         case DSLConfig::PPPOE_RP: {
114
115             // call peers file pppoe-rp
116             if (check_peers_file ("pppoe-rp"))
117                 pppd << "call" << "pppoe-rp";
118
119             string huhu = "/usr/sbin/pppoe";
120             huhu += " -I " + interface + " -T 120 -m " +
121                 tostring (dslconfig->mtu > 1452 ? 1452 : dslconfig->mtu);
122             pppd << "pty" << huhu;
123
124         } break;
125
126         case DSLConfig::PPPOATM: {
127
128             // call peers file pppoatm
129             if (check_peers_file ("pppoatm"))
130                 pppd << "call" << "pppoatm";
131
132             // The vpivci must be passed after the "call pppoatm", since the
133             // "call pppoatm" loads the plugin that can handle the vpivci.
134             pppd << dslconfig->vpivci.c_str ();
135
136         } break;
137
138         case DSLConfig::PPTP: {
139
140             // call peers file pptp
141             if (check_peers_file ("pptp"))
142                 pppd << "call" << "pptp";
143
144             string huhu = "/usr/sbin/pptp";
145             huhu += " " + dslconfig->modem_ip + " --nolaunchpppd";
146             pppd << "pty" << huhu;
147
148         } break;
149
150         case DSLConfig::CAPI_ADSL: {
151
152             // call peers file capi-adsl
153             if (check_peers_file ("capi-adsl"))
154                 pppd << "call" << "capi-adsl";
155
156         } break;
157
158     }
159
160     pppd << "mtu" << dslconfig->mtu << "mru" << dslconfig->mru;
161
162     if (!add_ip_options (&pppd))
163         return false;
164
165     if (dslconfig->demand)
166         pppd << "demand";
167
168     pppd << "idle" << dslconfig->idle_seconds;
169
170     if (dslconfig->defaultroute)
171         pppd << "defaultroute" << "replacedefaultroute";
172
173     pppd << "nodetach";
174
175     if (dslconfig->modify_dns && dslconfig->auto_dns)
176         pppd << "usepeerdns";
177
178     if (!add_ipparam (&pppd))
179         return false;
180
181     if (!add_user_pass_options (&pppd))
182         return false;
183
184     pppd_ifname = "";
185
186     if (dslconfig->ppp_mode == DSLConfig::PPPOE ||
187         dslconfig->ppp_mode == DSLConfig::PPPOE_RP)
188     {
189         if (dslconfig->kill_dhcp_client)
190             kill_dhcpcd (interface);
191
192         if (!ifconfig_up (interface))
193             logit (false, "warning: failed to bring %s up",
194                    interface.c_str ());
195
196         padi_timeout = -1;
197     }
198
199     if (dslconfig->modify_dns && (dslconfig->demand || !dslconfig->auto_dns)) {
200         dns_modify (dslconfig->dns1, dslconfig->dns2);
201     } else {
202         dns_changed = false;
203     }
204
205     if (dslconfig->debug) {
206         logit (false, "debug: starting %s", pppd.dump_args ().c_str ());
207         pppd << "debug" << "dump";
208     }
209
210     if (!pppd.start ()) {
211         dns_restore ();
212         return false;
213     }
214
215     if (pppd_log)
216         delete pppd_log;
217
218     // The pppd will setup the interface so the rx/tx count start at zero.
219     // PPPStat must know about that to correctly count the overruns.
220     pppstat.reset ();
221
222     pppd_log = new Stream (pppd_msgfd[0]);
223
224     status_callback (dslconfig->demand ? LURKING : CONNECTING);
225
226     return true;
227 }
228
229
230 bool
231 DSL::dialin ()
232 {
233     dprintf ("%s\n", __PRETTY_FUNCTION__);
234
235     return false;
236 }
237
238
239 bool
240 DSL::hangup ()
241 {
242     dprintf ("%s\n", __PRETTY_FUNCTION__);
243
244     if (dslconfig->demand)
245     {
246         if (dslconfig->debug)
247             logit (false, "debug: SIGHUP pppd");
248
249         kill (pppd.get_pid (), SIGHUP);
250     }
251
252     return true;
253 }
254
255
256 bool
257 DSL::stop ()
258 {
259     dprintf ("%s\n", __PRETTY_FUNCTION__);
260
261     if (dslconfig->debug)
262         logit (false, "debug: stopping pppd");
263
264     pppd.kill (2);
265
266     padi_timeout = -1;
267
268     if (status != LURKING)
269         status_callback (DISCONNECTING);
270
271     return true;
272 }
273
274
275 int
276 DSL::action_flags () const
277 {
278     int ret = 0;
279
280     if (status == DISCONNECTED)
281         ret += 0x01;
282
283     if (status == CONNECTED && dslconfig->demand)
284         ret += 0x04;
285
286     if (status != DISCONNECTED)
287         ret += 0x08;
288
289     return ret;
290 }
291
292
293 void
294 DSL::timer ()
295 {
296     if (!pppd.is_running () && status != DISCONNECTED) {
297         status_callback (DISCONNECTED);
298         dns_restore ();
299     }
300
301     if (pppd.has_died ()) {
302
303         if (!pppd.normal_exit ()) {
304
305             logit (false, "pppd died: non-normal exit");
306             weexit ();
307
308         } else {
309
310             const int exit_status = pppd.exit_status ();
311             logit (false, "pppd died: %s (exit code %d)",
312                    pppd_exit_msg (exit_status).c_str (),
313                    exit_status);
314
315             if (dslconfig->reconnect && bit_test (exit_status, dslconfig->reconnect_exits)) {
316                 logit (true, "Auto reconnect in %d seconds.",
317                        dslconfig->reconnect_delay);
318                 reconnect_time = time (0) + dslconfig->reconnect_delay;
319             } else {
320                 weexit ();
321             }
322         }
323     }
324
325     if (reconnect_time != (time_t)(-1) && reconnect_time <= time (0)) {
326         reconnect_time = (time_t)(-1);
327         logit (true, "Auto reconnecting.");
328         start ();
329     }
330
331     if (status != DISCONNECTED && !pppd_ifname.empty ())
332         read_rxtx_bytes (pppd_ifname.c_str ());
333
334     if (dslconfig->ppp_mode == DSLConfig::PPPOE) {
335         if (padi_timeout > 0 && --padi_timeout == 0) {
336             logit (true, "Waiting for PADO. Please check your setup and "
337                    "try again.");
338             padi_timeout = -1;
339         }
340     }
341
342     Connection::timer ();
343 }
344
345
346 void
347 DSL::analyse (const string& line)
348 {
349     dprintf ("*** %s ***\n", line.c_str ());
350
351     if (dslconfig->ppp_mode == DSLConfig::PPPOE) {
352         if (line == "Sending PADI") {
353             padi_timeout = 15;
354             return;
355         }
356         padi_timeout = -1;
357     }
358
359     if (line == "Starting link") {
360         status_callback (CONNECTING);
361         logit (true, "Establishing connection due to activity.");
362         return;
363     }
364
365     if (startswith (line, "Script /etc/ppp/ip-up finished")) {
366         status_callback (CONNECTED);
367         accounting_up ();       // FIXME
368         if (!endswith (line, "status = 0x0"))
369             logit (true, "The ip-up script returned an error. "
370                    "Your connection may not work.");
371         if (dslconfig->demand && dslconfig->modify_dns && dslconfig->auto_dns)
372             dns_keep ();
373         return;
374     }
375
376     if (startswith (line, "Script /etc/ppp/ip-down finished")) {
377         status_callback (dslconfig->demand ? LURKING : DISCONNECTED);
378         accounting_down ();     // FIXME
379         if (!endswith (line, "status = 0x0"))
380             logit (true, "The ip-down script returned an error.");
381         return;
382     }
383
384     if (line == "Terminating connection due to lack of activity.") {
385         status_callback (DISCONNECTING);
386         logit (true, "The connection was idle and shut down.");
387         return;
388     }
389
390     if (line == "PAP authentication failed" ||
391         line == "CHAP authentication failed") {
392         logit (true, "Authentication error. Maybe bad account "
393                "or password.");
394         return;
395     }
396
397     if (line == "Terminating on signal 15.") {
398         if (status != LURKING)
399             status_callback (DISCONNECTING);
400         return;
401     }
402
403     if (startswith (line, "Using interface ")) {
404         pppd_ifname = line.substr (16, string::npos);
405         return;
406     }
407 }
408
409
410 void
411 DSL::prepare_fdset (FDSet* fdset) const
412 {
413     if (pppd_log)
414         pppd_log->prepare_fdset (fdset);
415 }
416
417
418 void
419 DSL::handle_fdset (FDSet* fdset)
420 {
421     if (pppd_log)
422     {
423         pppd_log->handle_fdset (fdset);
424
425         string tmp;
426         while (pppd_log->read_line (tmp)) {
427             logit (false, "pppd: %s", tmp.c_str ());
428             analyse (tmp);
429         }
430     }
431 }