OpenWrt – Rev 4

Subversion Repositories:
Rev:
/*
 * Emergency Access Daemon
 * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>
#include <signal.h>
#include <pcap.h>
#include <pcap-bpf.h>
#include <t_pwd.h>
#include <t_read.h>
#include <t_sha.h>
#include <t_defines.h>
#include <t_server.h>
#include <net/if.h>

#include "list.h"
#include "ead.h"
#include "ead-pcap.h"
#include "ead-crypt.h"
#include "libbridge.h"

#include "filter.c"

#ifdef linux
#include <linux/if_packet.h>
#endif

#define PASSWD_FILE     "/etc/passwd"

#ifndef DEFAULT_IFNAME
#define DEFAULT_IFNAME "eth0"
#endif

#ifndef DEFAULT_DEVNAME
#define DEFAULT_DEVNAME "Unknown"
#endif

#define PCAP_MRU                1600
#define PCAP_TIMEOUT    200

#if EAD_DEBUGLEVEL >= 1
#define DEBUG(n, format, ...) do { \
        if (EAD_DEBUGLEVEL >= n) \
                fprintf(stderr, format, ##__VA_ARGS__); \
} while (0);

#else
#define DEBUG(n, format, ...) do {} while(0)
#endif

struct ead_instance {
        struct list_head list;
        char ifname[16];
        int pid;
        char id;
        char bridge[16];
        bool br_check;
};

static char ethmac[6] = "\x00\x13\x37\x00\x00\x00"; /* last 3 bytes will be randomized */
static pcap_t *pcap_fp = NULL;
static pcap_t *pcap_fp_rx = NULL;
static char pktbuf_b[PCAP_MRU];
static struct ead_packet *pktbuf = (struct ead_packet *)pktbuf_b;
static u16_t nid = 0xffff; /* node id */
static char username[32] = "";
static int state = EAD_TYPE_SET_USERNAME;
static const char *passwd_file = PASSWD_FILE;
static const char password[MAXPARAMLEN];
static bool child_pending = false;

static unsigned char abuf[MAXPARAMLEN + 1];
static unsigned char pwbuf[MAXPARAMLEN];
static unsigned char saltbuf[MAXSALTLEN];
static unsigned char pw_saltbuf[MAXSALTLEN];
static struct list_head instances;
static const char *dev_name = DEFAULT_DEVNAME;
static bool nonfork = false;
static struct ead_instance *instance = NULL;

static struct t_pwent tpe = {
        .name = username,
        .index = 1,
        .password.data = pwbuf,
        .password.len = 0,
        .salt.data = saltbuf,
        .salt.len = 0,
};
struct t_confent *tce = NULL;
static struct t_server *ts = NULL;
static struct t_num A, *B = NULL;
unsigned char *skey;

static void
set_recv_type(pcap_t *p, bool rx)
{
#ifdef PACKET_RECV_TYPE
        struct sockaddr_ll sll;
        struct ifreq ifr;
        int mask;
        int fd;

        fd = pcap_get_selectable_fd(p);
        if (fd < 0)
                return;

        if (rx)
                mask = 1 << PACKET_BROADCAST;
        else
                mask = 0;

        setsockopt(fd, SOL_PACKET, PACKET_RECV_TYPE, &mask, sizeof(mask));
#endif
}


static pcap_t *
ead_open_pcap(const char *ifname, char *errbuf, bool rx)
{
        pcap_t *p;

        p = pcap_create(ifname, errbuf);
        if (p == NULL)
                goto out;

        pcap_set_snaplen(p, PCAP_MRU);
        pcap_set_promisc(p, rx);
        pcap_set_timeout(p, PCAP_TIMEOUT);
#ifdef HAS_PROTO_EXTENSION
        pcap_set_protocol(p, (rx ? htons(ETH_P_IP) : 0));
#endif
        pcap_set_buffer_size(p, (rx ? 10 : 1) * PCAP_MRU);
        pcap_activate(p);
        set_recv_type(p, rx);
out:
        return p;
}

static void
get_random_bytes(void *ptr, int len)
{
        int fd;

        fd = open("/dev/urandom", O_RDONLY);
        if (fd < 0) {
                perror("open");
                exit(1);
        }
        read(fd, ptr, len);
        close(fd);
}

static bool
prepare_password(void)
{
        static char lbuf[1024];
        unsigned char dig[SHA_DIGESTSIZE];
        BigInteger x, v, n, g;
        SHA1_CTX ctxt;
        int ulen = strlen(username);
        FILE *f;

        lbuf[sizeof(lbuf) - 1] = 0;

        f = fopen(passwd_file, "r");
        if (!f)
                return false;

        while (fgets(lbuf, sizeof(lbuf) - 1, f) != NULL) {
                char *str, *s2;

                if (strncmp(lbuf, username, ulen) != 0)
                        continue;

                if (lbuf[ulen] != ':')
                        continue;

                str = &lbuf[ulen + 1];

                if (strncmp(str, "$1$", 3) != 0)
                        continue;

                s2 = strchr(str + 3, '$');
                if (!s2)
                        continue;

                if (s2 - str >= MAXSALTLEN)
                        continue;

                strncpy((char *) pw_saltbuf, str, s2 - str);
                pw_saltbuf[s2 - str] = 0;

                s2 = strchr(s2, ':');
                if (!s2)
                        continue;

                *s2 = 0;
                if (s2 - str >= MAXPARAMLEN)
                        continue;

                strncpy((char *)password, str, MAXPARAMLEN);
                fclose(f);
                goto hash_password;
        }

        /* not found */
        fclose(f);
        return false;

hash_password:
        tce = gettcid(tpe.index);
        do {
                t_random(tpe.password.data, SALTLEN);
        } while (memcmp(saltbuf, (char *)dig, sizeof(saltbuf)) == 0);
        if (saltbuf[0] == 0)
                saltbuf[0] = 0xff;

        n = BigIntegerFromBytes(tce->modulus.data, tce->modulus.len);
        g = BigIntegerFromBytes(tce->generator.data, tce->generator.len);
        v = BigIntegerFromInt(0);

        SHA1Init(&ctxt);
        SHA1Update(&ctxt, (unsigned char *) username, strlen(username));
        SHA1Update(&ctxt, (unsigned char *) ":", 1);
        SHA1Update(&ctxt, (unsigned char *) password, strlen(password));
        SHA1Final(dig, &ctxt);

        SHA1Init(&ctxt);
        SHA1Update(&ctxt, saltbuf, tpe.salt.len);
        SHA1Update(&ctxt, dig, sizeof(dig));
        SHA1Final(dig, &ctxt);

        /* x = H(s, H(u, ':', p)) */
        x = BigIntegerFromBytes(dig, sizeof(dig));

        BigIntegerModExp(v, g, x, n);
        tpe.password.len = BigIntegerToBytes(v, (unsigned char *)pwbuf);

        BigIntegerFree(v);
        BigIntegerFree(x);
        BigIntegerFree(g);
        BigIntegerFree(n);
        return true;
}

static u16_t
chksum(u16_t sum, const u8_t *data, u16_t len)
{
        u16_t t;
        const u8_t *dataptr;
        const u8_t *last_byte;

        dataptr = data;
        last_byte = data + len - 1;

        while(dataptr < last_byte) {    /* At least two more bytes */
                t = (dataptr[0] << 8) + dataptr[1];
                sum += t;
                if(sum < t) {
                        sum++;          /* carry */
                }
                dataptr += 2;
        }

        if(dataptr == last_byte) {
                t = (dataptr[0] << 8) + 0;
                sum += t;
                if(sum < t) {
                        sum++;          /* carry */
                }
        }

        /* Return sum in host byte order. */
        return sum;
}

static void
ead_send_packet_clone(struct ead_packet *pkt)
{
        u16_t len, sum;

        memcpy(pktbuf, pkt, offsetof(struct ead_packet, msg));
        memcpy(pktbuf->eh.ether_shost, ethmac, 6);
        memcpy(pktbuf->eh.ether_dhost, pkt->eh.ether_shost, 6);

        /* ip header */
        len = sizeof(struct ead_packet) - sizeof(struct ether_header) + ntohl(pktbuf->msg.len);
        pktbuf->len[0] = len >> 8;
        pktbuf->len[1] = len & 0xff;
        memcpy(pktbuf->srcipaddr, &pkt->msg.ip, 4);
        memcpy(pktbuf->destipaddr, pkt->srcipaddr, 4);

        /* ip checksum */
        pktbuf->ipchksum = 0;
        sum = chksum(0, (void *) &pktbuf->vhl, UIP_IPH_LEN);
        if (sum == 0)
                sum = 0xffff;
        pktbuf->ipchksum = htons(~sum);

        /* udp header */
        pktbuf->srcport = pkt->destport;
        pktbuf->destport = pkt->srcport;

        /* udp checksum */
        len -= UIP_IPH_LEN;
        pktbuf->udplen = htons(len);
        pktbuf->udpchksum = 0;
        sum = len + UIP_PROTO_UDP;
        sum = chksum(sum, (void *) &pktbuf->srcipaddr[0], 8); /* src, dest ip */
        sum = chksum(sum, (void *) &pktbuf->srcport, len);
        if (sum == 0)
                sum = 0xffff;
        pktbuf->udpchksum = htons(~sum);
        pcap_sendpacket(pcap_fp, (void *) pktbuf, sizeof(struct ead_packet) + ntohl(pktbuf->msg.len));
}

static void
set_state(int nstate)
{
        if (state == nstate)
                return;

        if (nstate < state) {
                if ((nstate < EAD_TYPE_GET_PRIME) &&
                        (state >= EAD_TYPE_GET_PRIME)) {
                        t_serverclose(ts);
                        ts = NULL;
                }
                goto done;
        }

        switch(state) {
        case EAD_TYPE_SET_USERNAME:
                if (!prepare_password())
                        goto error;
                ts = t_serveropenraw(&tpe, tce);
                if (!ts)
                        goto error;
                break;
        case EAD_TYPE_GET_PRIME:
                B = t_servergenexp(ts);
                break;
        case EAD_TYPE_SEND_A:
                skey = t_servergetkey(ts, &A);
                if (!skey)
                        goto error;

                ead_set_key(skey);
                break;
        }
done:
        state = nstate;
error:
        return;
}

static bool
handle_ping(struct ead_packet *pkt, int len, int *nstate)
{
        struct ead_msg *msg = &pktbuf->msg;
        struct ead_msg_pong *pong = EAD_DATA(msg, pong);
        int slen;

        slen = strlen(dev_name);
        if (slen > 1024)
                slen = 1024;

        msg->len = htonl(sizeof(struct ead_msg_pong) + slen);
        strncpy(pong->name, dev_name, slen);
        pong->name[slen] = 0;
        pong->auth_type = htons(EAD_AUTH_MD5);

        return true;
}

static bool
handle_set_username(struct ead_packet *pkt, int len, int *nstate)
{
        struct ead_msg *msg = &pkt->msg;
        struct ead_msg_user *user = EAD_DATA(msg, user);

        set_state(EAD_TYPE_SET_USERNAME); /* clear old state */
        strncpy(username, user->username, sizeof(username));
        username[sizeof(username) - 1] = 0;

        msg = &pktbuf->msg;
        msg->len = 0;

        *nstate = EAD_TYPE_GET_PRIME;
        return true;
}

static bool
handle_get_prime(struct ead_packet *pkt, int len, int *nstate)
{
        struct ead_msg *msg = &pktbuf->msg;
        struct ead_msg_salt *salt = EAD_DATA(msg, salt);

        msg->len = htonl(sizeof(struct ead_msg_salt));
        salt->prime = tce->index - 1;
        salt->len = ts->s.len;
        memcpy(salt->salt, ts->s.data, ts->s.len);
        memcpy(salt->ext_salt, pw_saltbuf, MAXSALTLEN);

        *nstate = EAD_TYPE_SEND_A;
        return true;
}

static bool
handle_send_a(struct ead_packet *pkt, int len, int *nstate)
{
        struct ead_msg *msg = &pkt->msg;
        struct ead_msg_number *number = EAD_DATA(msg, number);
        len = ntohl(msg->len) - sizeof(struct ead_msg_number);

        if (len > MAXPARAMLEN + 1)
                return false;

        A.len = len;
        A.data = abuf;
        memcpy(A.data, number->data, len);

        msg = &pktbuf->msg;
        number = EAD_DATA(msg, number);
        msg->len = htonl(sizeof(struct ead_msg_number) + B->len);
        memcpy(number->data, B->data, B->len);

        *nstate = EAD_TYPE_SEND_AUTH;
        return true;
}

static bool
handle_send_auth(struct ead_packet *pkt, int len, int *nstate)
{
        struct ead_msg *msg = &pkt->msg;
        struct ead_msg_auth *auth = EAD_DATA(msg, auth);

        if (t_serververify(ts, auth->data) != 0) {
                DEBUG(2, "Client authentication failed\n");
                *nstate = EAD_TYPE_SET_USERNAME;
                return false;
        }

        msg = &pktbuf->msg;
        auth = EAD_DATA(msg, auth);
        msg->len = htonl(sizeof(struct ead_msg_auth));

        DEBUG(2, "Client authentication successful\n");
        memcpy(auth->data, t_serverresponse(ts), sizeof(auth->data));

        *nstate = EAD_TYPE_SEND_CMD;
        return true;
}

static bool
handle_send_cmd(struct ead_packet *pkt, int len, int *nstate)
{
        struct ead_msg *msg = &pkt->msg;
        struct ead_msg_cmd *cmd = EAD_ENC_DATA(msg, cmd);
        struct ead_msg_cmd_data *cmddata;
        struct timeval tv, to, tn;
        int pfd[2], fd;
        fd_set fds;
        pid_t pid;
        bool stream = false;
        int timeout;
        int type;
        int datalen;

        datalen = ead_decrypt_message(msg) - sizeof(struct ead_msg_cmd);
        if (datalen <= 0)
                return false;

        type = ntohs(cmd->type);
        timeout = ntohs(cmd->timeout);

        FD_ZERO(&fds);
        cmd->data[datalen] = 0;
        switch(type) {
        case EAD_CMD_NORMAL:
                if (pipe(pfd) < 0)
                        return false;

                fcntl(pfd[0], F_SETFL, O_NONBLOCK | fcntl(pfd[0], F_GETFL));
                child_pending = true;
                pid = fork();
                if (pid == 0) {
                        close(pfd[0]);
                        fd = open("/dev/null", O_RDWR);
                        if (fd > 0) {
                                dup2(fd, 0);
                                dup2(pfd[1], 1);
                                dup2(pfd[1], 2);
                        }
                        system((char *)cmd->data);
                        exit(0);
                } else if (pid > 0) {
                        close(pfd[1]);
                        if (!timeout)
                                timeout = EAD_CMD_TIMEOUT;

                        stream = true;
                        break;
                }
                return false;
        case EAD_CMD_BACKGROUND:
                pid = fork();
                if (pid == 0) {
                        /* close stdin, stdout, stderr, replace with fd to /dev/null */
                        fd = open("/dev/null", O_RDWR);
                        if (fd > 0) {
                                dup2(fd, 0);
                                dup2(fd, 1);
                                dup2(fd, 2);
                        }
                        system((char *)cmd->data);
                        exit(0);
                } else if (pid > 0) {
                        break;
                }
                return false;
        default:
                return false;
        }

        msg = &pktbuf->msg;
        cmddata = EAD_ENC_DATA(msg, cmd_data);

        if (stream) {
                int nfds, bytes;

                /* send keepalive packets every 200 ms so that the client doesn't timeout */
                gettimeofday(&to, NULL);
                memcpy(&tn, &to, sizeof(tn));
                tv.tv_usec = PCAP_TIMEOUT * 1000;
                tv.tv_sec = 0;
                do {
                        cmddata->done = 0;
                        FD_SET(pfd[0], &fds);
                        nfds = select(pfd[0] + 1, &fds, NULL, NULL, &tv);
                        bytes = 0;
                        if (nfds > 0) {
                                bytes = read(pfd[0], cmddata->data, 1024);
                                if (bytes < 0)
                                        bytes = 0;
                        }
                        if (!bytes && !child_pending)
                                break;
                        DEBUG(3, "Sending %d bytes of console data, type=%d, timeout=%d\n", bytes, ntohl(msg->type), timeout);
                        ead_encrypt_message(msg, sizeof(struct ead_msg_cmd_data) + bytes);
                        ead_send_packet_clone(pkt);
                        gettimeofday(&tn, NULL);
                } while (tn.tv_sec < to.tv_sec + timeout);
                if (child_pending) {
                        kill(pid, SIGKILL);
                        return false;
                }
        }
        cmddata->done = 1;
        ead_encrypt_message(msg, sizeof(struct ead_msg_cmd_data));

        return true;
}



static void
parse_message(struct ead_packet *pkt, int len)
{
        bool (*handler)(struct ead_packet *pkt, int len, int *nstate);
        int min_len = sizeof(struct ead_packet);
        int nstate = state;
        int type = ntohl(pkt->msg.type);

        if ((type >= EAD_TYPE_GET_PRIME) &&
                (state != type))
                return;

        if ((type != EAD_TYPE_PING) &&
                ((ntohs(pkt->msg.sid) & EAD_INSTANCE_MASK) >>
                 EAD_INSTANCE_SHIFT) != instance->id)
                return;

        switch(type) {
        case EAD_TYPE_PING:
                handler = handle_ping;
                break;
        case EAD_TYPE_SET_USERNAME:
                handler = handle_set_username;
                min_len += sizeof(struct ead_msg_user);
                break;
        case EAD_TYPE_GET_PRIME:
                handler = handle_get_prime;
                break;
        case EAD_TYPE_SEND_A:
                handler = handle_send_a;
                min_len += sizeof(struct ead_msg_number);
                break;
        case EAD_TYPE_SEND_AUTH:
                handler = handle_send_auth;
                min_len += sizeof(struct ead_msg_auth);
                break;
        case EAD_TYPE_SEND_CMD:
                handler = handle_send_cmd;
                min_len += sizeof(struct ead_msg_cmd) + sizeof(struct ead_msg_encrypted);
                break;
        default:
                return;
        }

        if (len < min_len) {
                DEBUG(2, "discarding packet: message too small\n");
                return;
        }

        pktbuf->msg.magic = htonl(EAD_MAGIC);
        pktbuf->msg.type = htonl(type + 1);
        pktbuf->msg.nid = htons(nid);
        pktbuf->msg.sid = pkt->msg.sid;
        pktbuf->msg.len = 0;

        if (handler(pkt, len, &nstate)) {
                DEBUG(2, "sending response to packet type %d: %d\n", type + 1, ntohl(pktbuf->msg.len));
                /* format response packet */
                ead_send_packet_clone(pkt);
        }
        set_state(nstate);
}

static void
handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
        struct ead_packet *pkt = (struct ead_packet *) bytes;

        if (h->len < sizeof(struct ead_packet))
                return;

        if (pkt->eh.ether_type != htons(ETHERTYPE_IP))
                return;

        if (memcmp(pkt->eh.ether_dhost, "\xff\xff\xff\xff\xff\xff", 6) != 0)
                return;

        if (pkt->proto != UIP_PROTO_UDP)
                return;

        if (pkt->destport != htons(EAD_PORT))
                return;

        if (pkt->msg.magic != htonl(EAD_MAGIC))
                return;

        if (h->len < sizeof(struct ead_packet) + ntohl(pkt->msg.len))
                return;

        if ((pkt->msg.nid != 0xffff) &&
                (pkt->msg.nid != htons(nid)))
                return;

        parse_message(pkt, h->len);
}

static void
ead_pcap_reopen(bool first)
{
        static char errbuf[PCAP_ERRBUF_SIZE] = "";

        if (pcap_fp_rx && (pcap_fp_rx != pcap_fp))
                pcap_close(pcap_fp_rx);

        if (pcap_fp)
                pcap_close(pcap_fp);

        pcap_fp_rx = NULL;
        do {
                if (instance->bridge[0]) {
                        pcap_fp_rx = ead_open_pcap(instance->bridge, errbuf, 1);
                        pcap_fp = ead_open_pcap(instance->ifname, errbuf, 0);
                } else {
                        pcap_fp = ead_open_pcap(instance->ifname, errbuf, 1);
                }

                if (!pcap_fp_rx)
                        pcap_fp_rx = pcap_fp;
                if (first && !pcap_fp) {
                        DEBUG(1, "WARNING: unable to open interface '%s'\n", instance->ifname);
                        first = false;
                }
                if (!pcap_fp)
                        sleep(1);
        } while (!pcap_fp);
        pcap_setfilter(pcap_fp_rx, &pktfilter);
}


static void
ead_pktloop(void)
{
        while (1) {
                if (pcap_dispatch(pcap_fp_rx, 1, handle_packet, NULL) < 0) {
                        ead_pcap_reopen(false);
                        continue;
                }
        }
}


static int
usage(const char *prog)
{
        fprintf(stderr, "Usage: %s [<options>]\n"
                "Options:\n"
                "\t-B             Run in background mode\n"
                "\t-d <device>    Set the device to listen on\n"
                "\t-D <name>      Set the name of the device visible to clients\n"
                "\t-p <file>      Set the password file for authenticating\n"
                "\t-P <file>      Write a pidfile\n"
                "\n", prog);
        return -1;
}

static void
server_handle_sigchld(int sig)
{
        struct ead_instance *in;
        struct list_head *p;
        int pid = 0;
        wait(&pid);

        list_for_each(p, &instances) {
                in = list_entry(p, struct ead_instance, list);
                if (pid != in->pid)
                        continue;

                in->pid = 0;
                break;
        }
}

static void
instance_handle_sigchld(int sig)
{
        int pid = 0;
        wait(&pid);
        child_pending = false;
}

static void
start_server(struct ead_instance *i)
{
        if (!nonfork) {
                i->pid = fork();
                if (i->pid != 0) {
                        if (i->pid < 0)
                                i->pid = 0;
                        return;
                }
        }

        instance = i;
        signal(SIGCHLD, instance_handle_sigchld);
        ead_pcap_reopen(true);
        ead_pktloop();
        pcap_close(pcap_fp);
        if (pcap_fp_rx != pcap_fp)
                pcap_close(pcap_fp_rx);

        exit(0);
}


static void
start_servers(bool restart)
{
        struct ead_instance *in;
        struct list_head *p;

        list_for_each(p, &instances) {
                in = list_entry(p, struct ead_instance, list);
                if (in->pid > 0)
                        continue;

                sleep(1);
                start_server(in);
        }
}

static void
stop_server(struct ead_instance *in, bool do_free)
{
        if (in->pid > 0)
                kill(in->pid, SIGKILL);
        in->pid = 0;
        if (do_free) {
                list_del(&in->list);
                free(in);
        }
}

static void
server_handle_sigint(int sig)
{
        struct ead_instance *in;
        struct list_head *p, *tmp;

        list_for_each_safe(p, tmp, &instances) {
                in = list_entry(p, struct ead_instance, list);
                stop_server(in, true);
        }
        exit(1);
}

static int
check_bridge_port(const char *br, const char *port, void *arg)
{
        struct ead_instance *in;
        struct list_head *p;

        list_for_each(p, &instances) {
                in = list_entry(p, struct ead_instance, list);

                if (strcmp(in->ifname, port) != 0)
                        continue;

                in->br_check = true;
                if (strcmp(in->bridge, br) == 0)
                        break;

                strncpy(in->bridge, br, sizeof(in->bridge));
                DEBUG(2, "assigning port %s to bridge %s\n", in->ifname, in->bridge);
                stop_server(in, false);
        }
        return 0;
}

static int
check_bridge(const char *name, void *arg)
{
        br_foreach_port(name, check_bridge_port, arg);
        return 0;
}

static void
check_all_interfaces(void)
{
        struct ead_instance *in;
        struct list_head *p;

        br_foreach_bridge(check_bridge, NULL);

        /* look for interfaces that are no longer part of a bridge */
        list_for_each(p, &instances) {
                in = list_entry(p, struct ead_instance, list);

                if (in->br_check) {
                        in->br_check = false;
                } else if (in->bridge[0]) {
                        DEBUG(2, "removing port %s from bridge %s\n", in->ifname, in->bridge);
                        in->bridge[0] = 0;
                        stop_server(in, false);
                }
        }
}


int main(int argc, char **argv)
{
        struct ead_instance *in;
        struct timeval tv;
        const char *pidfile = NULL;
        bool background = false;
        int n_iface = 0;
        int fd, ch;

        if (argc == 1)
                return usage(argv[0]);

        INIT_LIST_HEAD(&instances);
        while ((ch = getopt(argc, argv, "Bd:D:fhp:P:")) != -1) {
                switch(ch) {
                case 'B':
                        background = true;
                        break;
                case 'f':
                        nonfork = true;
                        break;
                case 'h':
                        return usage(argv[0]);
                case 'd':
                        in = malloc(sizeof(struct ead_instance));
                        memset(in, 0, sizeof(struct ead_instance));
                        INIT_LIST_HEAD(&in->list);
                        strncpy(in->ifname, optarg, sizeof(in->ifname) - 1);
                        list_add(&in->list, &instances);
                        in->id = n_iface++;
                        break;
                case 'D':
                        dev_name = optarg;
                        break;
                case 'p':
                        passwd_file = optarg;
                        break;
                case 'P':
                        pidfile = optarg;
                        break;
                }
        }
        signal(SIGCHLD, server_handle_sigchld);
        signal(SIGINT, server_handle_sigint);
        signal(SIGTERM, server_handle_sigint);
        signal(SIGKILL, server_handle_sigint);

        if (!n_iface) {
                fprintf(stderr, "Error: ead needs at least one interface\n");
                return -1;
        }

        if (background) {
                if (fork() > 0)
                        exit(0);

                fd = open("/dev/null", O_RDWR);
                dup2(fd, 0);
                dup2(fd, 1);
                dup2(fd, 2);
        }

        if (pidfile) {
                char pid[8];
                int len;

                unlink(pidfile);
                fd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, 0644);
                if (fd > 0) {
                        len = sprintf(pid, "%d\n", getpid());
                        write(fd, pid, len);
                        close(fd);
                }
        }

        /* randomize the mac address */
        get_random_bytes(ethmac + 3, 3);
        nid = *(((u16_t *) ethmac) + 2);

        start_servers(false);
        br_init();
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        while (1) {
                check_all_interfaces();
                start_servers(true);
                sleep(1);
        }
        br_shutdown();

        return 0;
}