#!/usr/bin/perl # pppd wrapper script for Mac OS X # For use with the Sony Ericsson GCxx series of PC cards # Ed Poe, http://fedward.org/GC82/ use strict; # @args is the list of arguments the script will pass to pppd my @args; # $command is a simple string of the exact command line from Internet Connect my $command = join(" ", @ARGV); # @privnets specifies the IP space to use, in order of preference # If you prefer different IP space, change the order here. my @privnets = ("192.168", "172.16", "10"); # @stopnets correspond to the first *invalid* address after the ones in @privnets # They don't have to be in any particular order (unlike @privnets) my @stopnets = ("192.169", "172.32", "11"); # The IP address to assign to the "remote" end (the GCxx card) my $target; # The list of known hosts and networks with defined routes on the machine my @hosts; # %subnets and %stops are used to test if an IP is good or not my %subnets; my %stops; # Parse the command line and look for the name of the card if ($command =~ /Sony\sEricsson\sGC/) { # GCxx cards have a faulty built-in PPP server. # This grabs the command line arguments from Internet Connect. # The arguments are rewritten and passed to the original pppd. # Get the routing table and populate @hosts with it my $table = `netstat -rn -f inet`; foreach my $line (split("\n", $table)) { push @hosts, $1 if (($line =~ /^\d/) && ($line =~ /^(\S+)\s/)); } # convert each set of addresses from dotted decimal to longs @hosts = longify (@hosts); @privnets = longify (@privnets); @stopnets = longify (@stopnets); # Populate the two hashes for testing foreach my $host (@hosts) { $subnets{$host} = $host; } foreach my $host (@stopnets) { $stops{$host} = $host; } # Flag for MTU and MRU settings my $mtureset = 0; # Run through the arguments in order. foreach my $arg (@ARGV) { if ($arg eq "0:0") { # Option from Internet Connect # 0:0 allows remote to set local and remote addresses. # The card will reject any attempt at setting the local IP. # Change the remote address to something pppd will allow. $target = get_target (@privnets); push @args, "0:$target"; } elsif ($arg eq "ipcp-accept-remote") { # Option from Internet Connect # Allows remote [the GCxx card] to set its own (non-RFC) address. # Just skip it. next; } elsif (($arg eq "mru") || ($arg eq "mtu")) { # Flag both options for change below. $mtureset = 1; push @args, $arg; } elsif (($arg eq "1500") && ($mtureset == 1)) { # Sony Ericsson recommends 1450. $mtureset = 0; push @args, "1450"; } else { # Other arguments are passed unchanged. push @args, $arg; } } } else { # All other PPP connections are passed as-is. @args = @ARGV; } # Pass the arguments to the original pppd. exec "/usr/sbin/pppd.orig", @args; exit; # If an IP range exists in the routing table, grab the next Class C network sub add_octet { my $net = shift; $net += 0x100; if ($stops{$net}) { # The next Class C isn't in the allowed range, so return undef return; } return $net; } # Find the first available IP from RFC1918 (or user-defined) space sub get_target { my $net; my $pppnet; # Go through the private networks specified while ($net = shift) { $pppnet = $net; # Test the given IP range and loop through the class(es) while (($pppnet) && ($subnets{$pppnet})) { # add_octet returned a valid IP range, but a route already exists $pppnet = add_octet ($pppnet); } unless ($pppnet) { # add_octet returned undef; that whole range isn't available next; } else { # add_octet returned a valid IP range, and there's no route to it # Grab the "last" host of that IP range $pppnet+= 255; # Reformat the IP from a long to hexadecimal $pppnet = unpack ("H8", pack ("L", $pppnet)); # Now split that hexadecimal address into four octets my @octets = unpack ("C4", pack ("H8", $pppnet)); # Reformat that into dotted decimal $pppnet = join (".", @octets); # IP found; no need to loop anymore last; } } # This will return undef if no IP was available in any specified network return ($pppnet); } # Convert an IP address in dotted decimal format to a long. sub longify { my @hosts; my $counter = 0; my $host; while ($host = shift) { # No sanity check, since the addresses come from the system my @octets = split (/\./, $host); my $j = 0; while ($j < 4) { $octets[$j] = unpack ("B8", pack ("C8", $octets[$j])); $j++; } $hosts[$counter] = unpack ("L", pack ("B32", join ("", @octets))); $counter++; } return @hosts; }