Hello,
I wanted to connect to the home network with openvpn, and feeling as close to the local lan as possible. From good old days I remembered that there is a proxyarp option for pppd that allows to to give out an IP address from the local lan to the machine calling in without complicated routing or bridging setup.
openvpn can do p2p tun links, however unfortunately I did not find anything in conjunction with openvpn and proxyarp, so I hacked together an up/down script that does it.
It uses old style ioctl approach and is ipv4 only, nevertheless I hope you get the idea and find it useful, or even extend it to netlink and ipv6.
Bye,
Joerg
#!/usr/bin/perl
# -w gives some irritating warning when requirering ioctls.ph
# Sets up a proxyarp entry on most appropriate interface
#
# This makes most sense if IP FORWARDING is enabled, too!
# ( echo 1 > /proc//sys/net/ipv4/ip_forward )
#
# Usage:
# On the home side, put in your config
# ifconfig <your local lan ip, e.g. the same as on eth0> <unused ip in the same ip network>
# up proxyarp
# down proxyarp
#
# Tries to find the proper interface the same way as the proxyarp
# option in pppd:
# In some cases it is desirable to use proxy ARP, for example on a server
# machine connected to a LAN, in order to allow other hosts to communi‐
# cate with the remote host. The proxyarp option causes pppd to look for
# a network interface on the same subnet as the remote host (an interface
# supporting broadcast and ARP, which is up and not a point-to-point or
# loopback interface). If found, pppd creates a permanent, published ARP
# entry with the IP address of the remote host and the hardware address
# of the network interface found.
#
# the actual code is improvable
use IO::Socket;
use IO::Interface qw(:flags);
use NetAddr::IP;
require "sys/ioctl.ph";
# There seems to be no if_arp.ph...
sub ARPHRD_ETHER () { 1;}
sub ATF_PERM () { 0x4;}
sub ATF_PUBL () { 0x8;}
my $remote_ip = new NetAddr::IP($ENV{ifconfig_remote});
my $s = IO::Socket::INET->new(Proto => 'udp') or die "Cannot open socket\n";
my @interfaces = $s->if_list;
my $bestmasklen = 0;
for my $if (@interfaces) {
my $flags = $s->if_flags($if);
next if not $flags & IFF_UP;
next if not $flags & IFF_BROADCAST;
next if $flags & IFF_LOOPBACK;
next if $flags & IFF_POINTOPOINT;
next if $flags & IFF_NOARP;
next if not $s->if_addr($if);
my $if_ip = new NetAddr::IP($s->if_addr($if), $s->if_netmask($if));
next if not $if_ip->contains($remote_ip);
next if $if_ip->masklen() < $bestmasklen;
$bestmasklen = $if_ip->masklen();
$bestif = $if;
}
exit if not $bestif;
$bestif =~ s/:.+//; # trim off :1 in eth0:1, so we get eth0
#
# When sombody knows a better way the deal with with this, please
# send patches. If did not find anything on cpan nor other examples.
#
# Esp. an implementation with netlink sockets would be great.
#
# We need to deal with the follwoing structure for arp manipulation
#
#struct arpreq {
# struct sockaddr arp_pa; /* protocol address */
# struct sockaddr arp_ha; /* hardware address */
# int arp_flags; /* flags */
# struct sockaddr arp_netmask; /* netmask of protocol address */
# char arp_dev[16];
#};
#
#struct sockaddr {
# sa_family_t sa_family; /* Address family (AF_* cotant) */
# char sa_data[14]; /* socket address size varies */
#};
#typedef unsigned short int sa_family_t;
#
# so the serialised version of all this looks like this
# arpreq {AF_INET, 0, {rem_addr}, pad}, {ARPHRD_ETHER, hardware address starting from byte 0, pad}, ATF_PERM | ATF_PUBL, {not used so zeroed}, {ifname, pad}
$arpreq = pack("SSC4x!16 S(H2)6x!16 i x16 a16",
AF_INET(), 0, split(/\./,$remote_ip->addr()),
ARPHRD_ETHER(), split(/:/,$s->if_hwaddr($bestif)),
ATF_PERM() | ATF_PUBL(),
$bestif
);
if ($ENV{script_type} eq "up") {
exit ioctl($s,SIOCSARP(),$arpreq),"\n";
} elsif ($ENV{script_type} eq "down") {
exit ioctl($s,SIOCDARP(),$arpreq),"\n";
}