f9c737b
#!/usr/bin/env python
680eb4b
# This program is free software; you can redistribute it and/or modify
680eb4b
# it under the terms of the GNU General Public License as published by
680eb4b
# the Free Software Foundation; either version 2 of the License, or
680eb4b
# (at your option) any later version.
680eb4b
#
680eb4b
# This program is distributed in the hope that it will be useful,
680eb4b
# but WITHOUT ANY WARRANTY; without even the implied warranty of
680eb4b
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
680eb4b
# GNU General Public License for more details.
680eb4b
#
680eb4b
# You should have received a copy of the GNU General Public License
680eb4b
# along with this program; if not, write to the Free Software
680eb4b
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
680eb4b
#
680eb4b
# (C) Copyright 2007 One Laptop Per Child
680eb4b
#
680eb4b
680eb4b
import commands, sys, syslog, os
680eb4b
680eb4b
def get_ip4_address(iface):
680eb4b
    import socket
680eb4b
    import fcntl
680eb4b
    import struct
680eb4b
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
680eb4b
    fd = s.fileno()
680eb4b
    SIOCGIFADDR = 0x8915
680eb4b
    addr = fcntl.ioctl(fd, SIOCGIFADDR, struct.pack('256s', iface[:15]))[20:24]
680eb4b
    s.close()
680eb4b
    return socket.inet_ntoa(addr)
680eb4b
680eb4b
def get_hw_address(iface):
680eb4b
    import struct
680eb4b
    import fcntl
680eb4b
    import socket
680eb4b
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
680eb4b
    fd = s.fileno()
680eb4b
    SIOCGIFHWADDR = 0x8927
680eb4b
    req = fcntl.ioctl(fd, SIOCGIFHWADDR, struct.pack('16s16s', iface, ''))
680eb4b
    s.close()
680eb4b
    ignore1, ignore2, addr_high, addr_low = struct.unpack('>16sHHL8x', req)
680eb4b
    addr = (long(addr_high)<<32) + addr_low
680eb4b
    b0 = addr & 0xFF
680eb4b
    b1 = (addr >> 8) & 0xFF
680eb4b
    b2 = (addr >> 16) & 0xFF
680eb4b
    b3 = (addr >> 24) & 0xFF
680eb4b
    b4 = (addr >> 32) & 0xFF
680eb4b
    b5 = (addr >> 40) & 0xFF
680eb4b
    return "%02X:%02X:%02X:%02X:%02X:%02X" % (b5, b4, b3, b2, b1, b0)
680eb4b
680eb4b
def set_anycast(mask, mesh_dev):
680eb4b
    commands.getstatusoutput('echo "%s" > /sys/class/net/%s/anycast_mask' % (mask, mesh_dev))
680eb4b
680eb4b
def ipt(args):
680eb4b
    (s, o) = commands.getstatusoutput("/sbin/iptables %s" % args)
680eb4b
    if (s != 0):
680eb4b
        syslog("Error executing iptables command '%s': %s" % (args, o))
680eb4b
680eb4b
def masq_start(extif, intif):
680eb4b
    os.system('echo "1" > /proc/sys/net/ipv4/ip_forward')
680eb4b
    os.system('echo "1" > /proc/sys/net/ipv4/ip_dynaddr')
680eb4b
680eb4b
    modules = ["ip_tables", "ip_conntrack", "ip_conntrack_ftp", "ip_conntrack_irc",
680eb4b
                "iptable_nat", "ip_nat_ftp", "ip_nat_irc"]
680eb4b
680eb4b
    os.system("/sbin/modprobe " + " ".join(modules))
680eb4b
680eb4b
    ipt("-P INPUT ACCEPT")
680eb4b
    ipt("-F INPUT")
680eb4b
    ipt("-P OUTPUT ACCEPT")
680eb4b
    ipt("-F OUTPUT")
680eb4b
    ipt("-P FORWARD DROP")
680eb4b
    ipt("-F FORWARD")
680eb4b
    ipt("-t nat -F")
680eb4b
680eb4b
    # FWD: Allow all connections OUT and only existing and related ones IN
680eb4b
    ipt("-A FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT" % (extif, intif))
680eb4b
    ipt("-A FORWARD -i %s -o %s -j ACCEPT" % (extif, intif))
680eb4b
    ipt("-A FORWARD -i %s -o %s -j ACCEPT" % (intif, extif))
680eb4b
    #ipt("-A FORWARD -j LOG")
680eb4b
    
680eb4b
    # Enabling SNAT (MASQUERADE) functionality on $EXTIF
680eb4b
    ipt("-t nat -A POSTROUTING -o %s -j MASQUERADE" % extif)
680eb4b
680eb4b
def masq_stop():
680eb4b
    ipt("-F INPUT")
680eb4b
    ipt("-F OUTPUT")
680eb4b
    ipt("-P FORWARD DROP")
680eb4b
    ipt("-F FORWARD")
680eb4b
    ipt("-F -t nat")
680eb4b
680eb4b
    # Delete all User-specified chains
680eb4b
    ipt("-X")
680eb4b
680eb4b
    # Reset all IPTABLES counters
680eb4b
    ipt("-Z")
680eb4b
680eb4b
def mpp_start(mesh_dev, primary_dev):
680eb4b
    dns_file = file('/etc/resolv.conf','r')
680eb4b
    dns_addresses = ""
680eb4b
    for line in dns_file.readlines():
680eb4b
        if len(line.split()) >= 2 and line.split()[0] == "nameserver":
680eb4b
            dns_addresses += line.split()[1] + ", "
680eb4b
    dns_addresses = dns_addresses[:len(dns_addresses) - 2]
680eb4b
    dns_file.close()
680eb4b
680eb4b
    mesh_ip4_addr = get_ip4_address(mesh_dev)
680eb4b
    if not mesh_ip4_addr or not len(mesh_ip4_addr):
680eb4b
        return
680eb4b
680eb4b
    primary_hw_addr = get_hw_address(primary_dev).lower()
680eb4b
680eb4b
    #copy parameters to the DHCP conf file
680eb4b
    dhcpd_conf_text = """#Custom DHCP daemon configuration file - XO as MPP
680eb4b
ddns-update-style interim;
680eb4b
680eb4b
# free the addresses quickly, clients ignore them anyway
680eb4b
default-lease-time      60;
680eb4b
max-lease-time          60;
680eb4b
680eb4b
option domain-name-servers %s;
680eb4b
680eb4b
# Ignore requests from ourselves.  Because the 8388's mesh interface has
680eb4b
# the same MAC address as the eth interface, dhclient gets confused
680eb4b
class "me" {
680eb4b
    match if hardware = 01:%s;
680eb4b
}
680eb4b
680eb4b
subnet 169.254.0.0 netmask 255.255.0.0 {
680eb4b
    pool {
680eb4b
        deny members of "me";
680eb4b
        option routers            %s;
680eb4b
        # range of link-local addresses, won't be used by XO's
680eb4b
        range 169.254.0.1  169.254.255.254;
680eb4b
    }
680eb4b
}
680eb4b
"""   % (dns_addresses, primary_hw_addr, mesh_ip4_addr)
680eb4b
    
680eb4b
    fd = open("/etc/dhcpd.conf","w")
680eb4b
    fd.write(dhcpd_conf_text)
680eb4b
    fd.flush()
680eb4b
    fd.close()
680eb4b
    
680eb4b
    masq_start(primary_dev, mesh_dev)
680eb4b
    
680eb4b
    # Start MPP functionality in mesh firmware
680eb4b
    set_anycast("0x2", mesh_dev)   # mask for xo-as-mpp
680eb4b
    
680eb4b
    # Tell dhcpd to only listen on the mesh interface
680eb4b
    fd = open("/etc/sysconfig/dhcpd", "w")
680eb4b
    fd.write('DHCPDARGS="%s"' % mesh_dev)
680eb4b
    fd.close()
680eb4b
    (s, o) = commands.getstatusoutput("service dhcpd restart")
680eb4b
680eb4b
def mpp_stop(mesh_dev, primary_dev):
680eb4b
    masq_stop()
680eb4b
680eb4b
    (s, o) = commands.getstatusoutput("service dhcpd stop")
680eb4b
    try:
680eb4b
        os.remove("/etc/sysconfig/dhcpd")
680eb4b
        os.remove("/etc/dhcpd.conf")
680eb4b
    except OSError, e:
680eb4b
        pass
680eb4b
680eb4b
    # Stop MPP functionality in mesh firmware
680eb4b
    set_anycast("0x0", mesh_dev)   # mask for off
680eb4b
680eb4b
680eb4b
def main():
680eb4b
    if len(sys.argv) < 4:
680eb4b
        sys.exit()
680eb4b
680eb4b
    mesh_dev = sys.argv[1]
680eb4b
    action = sys.argv[2]
680eb4b
    primary_dev = sys.argv[3]
680eb4b
680eb4b
    if action == "meshready":
680eb4b
        mpp_start(mesh_dev, primary_dev)
680eb4b
    elif action == "meshdown":
680eb4b
        mpp_stop(mesh_dev, primary_dev)    
680eb4b
680eb4b
if __name__ == "__main__":
680eb4b
    main()