/* $Id: ifconfigmodule.c,v 1.1 2002/03/07 14:50:54 gilesc Exp $ */

/*
   By David N. Welton <davidw@dedasys.com>

   You may use this code under the same terms as Python itself.
 */

/*
   Extra functionality by Giles Constant <gilesc@hyperlink-interactive.co.uk>

   . Works on solaris (define __SOLARIS__ to enable)
   . ifup()    Brings an interface up
   . ifdown()  Brings an interface down
   . list()    Lists running interfaces
 */

/*
 To build under Debian GNU/Linux:

 gcc -shared -Wl,-soname,pyifconfig.so -o pyifconfig.so ifmodule.c
 */


#include <Python.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef __SOLARIS__
#include <sys/sockio.h>
#endif

#define IFC_BUFLEN 8192

static PyObject *listAPI(PyObject *self, PyObject *args)
{
    struct ifconf if_request;
    char *ifc_buffer;
    int i, j;
    int fd;
    PyObject *py_iflist;
    PyObject *py_ifname;

    /* Allocate space for the if request */
    if ( (ifc_buffer=malloc(IFC_BUFLEN)) == 0) {
	return NULL;
    }

    bzero(ifc_buffer, IFC_BUFLEN);
    bzero(&if_request, sizeof(if_request));
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    if_request.ifc_buf = ifc_buffer;
    if_request.ifc_len = IFC_BUFLEN;

    ioctl(fd, SIOCGIFCONF, &if_request);

    if ((py_iflist = PyList_New(0)) == NULL) {
	close(fd);
	return NULL;
    }

    for (i=0, j=0; i<if_request.ifc_len; i+=sizeof(struct ifreq), j++) {
	py_ifname = PyString_FromString(if_request.ifc_req[j].ifr_name);
	if (py_ifname==NULL) {
	    Py_DECREF(py_iflist);
	    py_iflist = NULL;
	    break;
	}
	if (PyList_Append(py_iflist, py_ifname) != 0) {
	    Py_DECREF(py_iflist);
	    Py_DECREF(py_ifname);
	    py_iflist = NULL;
	    break;
	}
	Py_DECREF(py_ifname);
    }
    close(fd);
    free(ifc_buffer);
    return py_iflist;
}

static PyObject *ifconfigAPI(PyObject *self, PyObject *args)
{
    int fd;

    struct ifreq ifreq;
    unsigned char *hw;
    struct sockaddr_in *sin;
    char *itf;

    char pyhwaddr[40];
    char pyaddr[20];
    char pybrdaddr[20];
    char pynetmask[20];
    int pyup;
    int pyrunning;
    int pynoarp;

    if (!PyArg_ParseTuple(args, "s", &itf))
        return NULL;

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    strcpy(ifreq.ifr_name, itf);

    /* hardware address */
    ioctl(fd, SIOCGIFHWADDR, &ifreq);
    hw = ifreq.ifr_hwaddr.sa_data;
    sprintf(pyhwaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
            *hw, *(hw + 1), *(hw + 2), *(hw + 3), *(hw + 4), *(hw + 5));

    /* address */
    if (ioctl(fd, SIOCGIFADDR, &ifreq) == 0) {
      sin = (struct sockaddr_in *)&ifreq.ifr_broadaddr;
      sprintf(pyaddr, "%s", inet_ntoa(sin->sin_addr));

      /* broadcast */
      ioctl(fd, SIOCGIFBRDADDR, &ifreq);
      sin = (struct sockaddr_in *)&ifreq.ifr_broadaddr;
      sprintf(pybrdaddr, "%s", inet_ntoa(sin->sin_addr));

      /* netmask */
      ioctl(fd, SIOCGIFNETMASK, &ifreq);
      sin = (struct sockaddr_in *)&ifreq.ifr_broadaddr;
      sprintf(pynetmask, "%s", inet_ntoa(sin->sin_addr));
    } else {
      sprintf(pyaddr, "");
      sprintf(pybrdaddr, "");
      sprintf(pynetmask, "");
    }

    /* flags */
    ioctl(fd, SIOCGIFFLAGS, &ifreq);
    if (ifreq.ifr_flags & IFF_UP) pyup = 1; else pyup = 0;
    if (ifreq.ifr_flags & IFF_RUNNING) pyrunning = 1; else pyrunning = 0;
    if (ifreq.ifr_flags & IFF_NOARP) pynoarp = 1; else pynoarp = 0;

    close(fd);
    return Py_BuildValue("{s:s,s:s,s:s,s:s,s:i,s:i,s:i}",
                         "hwaddr", pyhwaddr,
                         "addr", pyaddr,
                         "brdaddr", pybrdaddr,
                         "netmask", pynetmask,
			 "up", pyup,
			 "running", pyrunning,
			 "noarp", pynoarp);
}

static PyObject *ifupAPI(PyObject *self, PyObject *args)
{
    int fd;

    struct ifreq ifreq;
    struct sockaddr_in sin_ip;
    struct sockaddr_in sin_subnet;
    char *itf;
    char *ip_address;
    char *subnet_mask;

    if (!PyArg_ParseTuple(args, "sss", &itf, &ip_address, &subnet_mask)) {
	printf("Invalid arguments");
	return NULL;
    }

    strcpy(ifreq.ifr_name, itf);
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    /* Set up IP address */
    memset(&sin_ip, 0, sizeof(struct sockaddr));
    sin_ip.sin_family = AF_INET;
    inet_aton(ip_address, &sin_ip.sin_addr);
    memcpy(&ifreq.ifr_addr, &sin_ip, sizeof(struct sockaddr));
    if (ioctl(fd, SIOCSIFADDR, &ifreq) < 0) {
	return PyErr_SetFromErrno(PyExc_IOError);
    }

    /* Set up netmask */
    memset(&sin_subnet, 0, sizeof(struct sockaddr));
    sin_subnet.sin_family = AF_INET;
    inet_aton(subnet_mask, &sin_subnet.sin_addr);
    memcpy(&ifreq.ifr_netmask, &sin_subnet, sizeof(struct sockaddr));
    if (ioctl(fd, SIOCSIFNETMASK, &ifreq) < 0) {
	return PyErr_SetFromErrno(PyExc_IOError);
    }

    /* Get flags to modify */
    if (ioctl(fd, SIOCGIFFLAGS, &ifreq)) {
	return PyErr_SetFromErrno(PyExc_IOError);
    }
    /* Set the IFF_UP flag, and send the flags back */
    ifreq.ifr_flags |= (IFF_UP | IFF_RUNNING);
    if (ioctl(fd, SIOCSIFFLAGS, &ifreq)) {
	return PyErr_SetFromErrno(PyExc_IOError);
    }
    return Py_BuildValue("z", NULL);
}

static PyObject *ifdownAPI(PyObject *self, PyObject *args)
{
    int fd;

    struct ifreq ifreq;
    char *itf;

    if (!PyArg_ParseTuple(args, "s", &itf)) {
	printf("Invalid arguments");
	return NULL;
    }

    strcpy(ifreq.ifr_name, itf);
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

    /* Get flags to modify */
    if (ioctl(fd, SIOCGIFFLAGS, &ifreq)) {
	return PyErr_SetFromErrno(PyExc_IOError);
    }
    /* Set the IFF_UP flag, and send the flags back */
    ifreq.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
    if (ioctl(fd, SIOCSIFFLAGS, &ifreq)) {
	return PyErr_SetFromErrno(PyExc_IOError);
    }
    return Py_BuildValue("z", NULL);
}

static struct PyMethodDef ifconfigmethods[] = {
    /* The command ifconfig.ifconfig(interface_name), where
       interface_name is the name of the interface, such as 'eth0' or 'lo'
       returns a dictionary with the following keys:

       hwaddr: hardware address
       addr: ip address
       brdaddr: broadcast address
       netmask: netmask
    */
    {"ifconfig", ifconfigAPI, METH_VARARGS},
    /* The command ifconfig.list() returns a list of all IPv4 interfaces */
    {"list", listAPI, METH_VARARGS},
    /* The command ifconfig.ifup(interface_name, ip, subnet) brings the 
       interface up */
    {"ifup", ifupAPI, METH_VARARGS},
    /* The command ifconfig.ifdown(interface_name) brings the interface 
       down */
    {"ifdown", ifdownAPI, METH_VARARGS},
    {NULL, NULL}
};

void initifconfig()
{
    Py_InitModule("ifconfig", ifconfigmethods);
}

