#!/usr/bin/perl -w # (C) Copyright 2008 by Marc Haber # inspired by the provider script by mog from # http://www.pharscape.org/component/option,com_forum/Itemid,68/page,viewtopic/t,479/sid,6e66346327164aa5d17a5526f98aec51/ # License: GPL v2 or later # this cannot run tainted due to the Serialport module use strict; use Device::Modem; use Getopt::Long; use Unix::Syslog qw(:macros); use Unix::Syslog qw(:subs); use Config::General; my ( $verbose, $provider, $configfile, $controldevice ); GetOptions( "verbose" => \$verbose, "provider=s" => \$provider, "controldevice=s" => \$controldevice, "configfile=s" => \$configfile); my $device; if( $ENV{IFACE} =~ /^([a-z0-9]+)$/) { $device="$1"; } else { die "Bad data in $ENV{IFACE}"; } if( defined $ENV{IF_VERBOSE} ) { if( $ENV{IF_VERBOSE} =~ /^([1]+)$/) { $verbose="$1"; } else { die "Bad data in $ENV{IFACE}"; } } my $phase; if( $ENV{PHASE} =~ /^([-a-z]+)$/) { $phase="$1"; } else { die "Bad data in $ENV{PHASE}"; } $configfile||="/etc/umts/hso.conf"; my $conf = new Config::General("$configfile"); my %config = $conf->getall; $provider ||= $config{umts}{provider}; $controldevice ||= $config{umts}{controldevice}; my $pin = $config{umts}{override}{pin}; $pin ||= $config{umts}{$provider}{pin}; $pin ||= $config{umts}{default}{pin}; if( $pin =~ /^([0-9]+)$/) { $pin="$1"; } else { die "Bad data in $pin"; } my $apn = $config{umts}{override}{apn}; $apn ||= $config{umts}{$provider}{apn}; $apn ||= $config{umts}{default}{apn}; if( $apn =~ /^([-0-9a-z\.]+)$/) { $apn="$1"; } else { die "Bad data in $pin"; } openlog( "hso-network", LOG_PID, LOG_USER); syslog(LOG_INFO, "connecting to %s", $controldevice); my $modem; if( $verbose ) { $modem = new Device::Modem( port => "$controldevice", log => "syslog" ); } else { $modem = new Device::Modem( port => "$controldevice" ); } sub trim { my ($string)=@_; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } sub modemcommand { my ($command)=@_; if( $verbose ) { syslog(LOG_DEBUG, "send AT%s", $command); print "send AT$command\n"; } $modem->atsend("AT$command". Device::Modem::CR); } sub modem_parse_answer { my ($result,@lines)=$modem->parse_answer; if( $verbose ) { syslog(LOG_DEBUG, "result %s", $result); print "result $result\n"; foreach my $line( @lines ) { syslog(LOG_DEBUG, "line %s", $line); print "line $line\n"; } } return ($result, @lines); } sub pin_status { modemcommand( '+CPIN?' ); my ($result, @lines) = modem_parse_answer(); if( $result eq "OK" && grep(/^\+CPIN: SIM PIN$/, @lines) == 1 ) { return 0; } else { return 1; } } sub send_pin { modemcommand( "+CPIN=". $pin ); my ($result, @lines) = modem_parse_answer(); if( $result ne "OK" ) { syslog(LOG_ERR, "cannot send PIN: %s", $!); die "cannot send PIN: $!"; } } if( ! $modem->connect( baudrate => 115200 ) ) { syslog(LOG_ERR, "cannot connect to %s: %s", $controldevice, $!); die "cannot connect to $controldevice: $!\n"; } $modem->echo(0); $modem->attention(); my $result; modemcommand( '&F' ); $result = $modem->parse_answer(); if ( $phase eq "pre-up" ) { if( !pin_status() ) { syslog(LOG_INFO, "sending PIN"); send_pin(); } if( !pin_status() ) { syslog(LOG_INFO, "first PIN attempt unsuccessful, sleeping 10 seconds"); sleep 10; send_pin(); } if( !pin_status() ) { syslog(LOG_ERR, "wrong PIN?"); die "wrong PIN?\n"; } syslog(LOG_INFO, "AT+CGDCONT=1,\"IP\",\"%s\"", $apn ); modemcommand( "+CGDCONT=1,\"IP\",\"". $apn. "\"" ); my ($result, @lines) = modem_parse_answer(); if( $result ne "OK" ) { syslog(LOG_ERR, "error setting APN %s", $apn); die "error setting APN $apn\n"; } syslog(LOG_INFO, "AT_OWANCALL=1,1,0" ); modemcommand( "_OWANCALL=1,1,0" ); ($result, @lines) = modem_parse_answer(); if( $result ne "OK" ) { syslog(LOG_ERR, "error connecting"); die "error connecting\n"; } } elsif ( $phase eq "post-up" ) { syslog(LOG_INFO, "AT_OWANDATA?" ); modemcommand( '_OWANDATA?' ); my ($result, @lines) = modem_parse_answer(); my $i=0; while( $result eq "OK" && grep(/^\_OWANDATA: /, @lines) != 1 ) { sleep 2; syslog(LOG_INFO, "AT_OWANDATA?" ); modemcommand( '_OWANDATA?' ); ($result, @lines) = modem_parse_answer(); if( $i++ > 60 ) { $result="TIMEOUT"; } } if( $result eq "OK" && grep(/^\_OWANDATA: /, @lines) == 1 ) { my ($line)=grep(/^\_OWANDATA: /, @lines); my ($data1, $ip, $data2, $dns1, $dns2)=split(/,/, $line); syslog(LOG_INFO, "IP %s, DNS %s %s", $ip, $dns1, $dns2); $ip=trim($ip); $dns1=trim($dns1); $dns2=trim($dns2); # TODO: capture output, error handling syslog(LOG_DEBUG, "ip link set dev %s up", $device); system( "ip", "link", "set", "dev", $device, "up" ); syslog(LOG_DEBUG, "ip addr add dev %s %s", $device, $ip); system( "ip", "addr", "add", "dev", $device, $ip ); syslog(LOG_DEBUG, "ip route add default via %s", $ip); system( "ip", "route", "add", "default", "via", $ip ); syslog(LOG_DEBUG, "echo -e nameserver $dns1 $dns2 | resolvconf -a %s", $device); system("echo -e nameserver $dns1 $dns2 | resolvconf -a $device"); } } elsif ( $phase eq "pre-down" ) { syslog(LOG_DEBUG, "ip route del default"); system( "ip", "route", "del", "default" ); syslog(LOG_DEBUG, "ip addr flush dev %s", $device); system( "ip", "addr", "flush", "dev", $device ); syslog(LOG_DEBUG, "resolvconf -d %s", $device); system("resolvconf -d $device"); } elsif ( $phase eq "post-down" ) { syslog(LOG_INFO, "AT_OWANCALL=1,0,0" ); modemcommand( "_OWANCALL=1,0,0" ); my ($result, @lines) = modem_parse_answer(); if( $result ne "OK" ) { syslog(LOG_ERR, "error connecting"); die "error connecting\n"; } } else { syslog( LOG_ERR, "ERROR: illegal phase $phase" ); die "ERROR: illegal phase $phase\n"; } $modem->disconnect; syslog(LOG_INFO, "end script"); if ($verbose) { print "end script\n"; }