mac80211: fix multi-bss related rx handling bug
[openwrt.git] / package / ead / src / ead.c
index 7367c38..3623520 100644 (file)
 #include "libbridge_init.c"
 #endif
 
+#ifdef linux
+#include <linux/if_packet.h>
+#endif
+
 #define PASSWD_FILE    "/etc/passwd"
 
 #ifndef DEFAULT_IFNAME
 #define DEBUG(n, format, ...) do {} while(0)
 #endif
 
+struct ead_instance {
+       struct list_head list;
+       char ifname[16];
+       int pid;
+       char id;
+#ifdef linux
+       char bridge[16];
+       bool br_check;
+#endif
+};
+
 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 const char *ifname = DEFAULT_IFNAME;
 static char pktbuf_b[PCAP_MRU];
 static struct ead_packet *pktbuf = (struct ead_packet *)pktbuf_b;
 static u16_t nid = 0xffff; /* node id */
@@ -85,20 +99,7 @@ static unsigned char pw_saltbuf[MAXSALTLEN];
 static struct list_head instances;
 static const char *dev_name = DEFAULT_DEVNAME;
 static bool nonfork = false;
-
-#ifdef linux
-static const char *brname = NULL;
-#endif
-
-struct ead_instance {
-       struct list_head list;
-       char name[16];
-       int pid;
-#ifdef linux
-       char bridge[16];
-       bool br_check;
-#endif
-};
+static struct ead_instance *instance = NULL;
 
 static struct t_pwent tpe = {
        .name = username,
@@ -113,6 +114,65 @@ 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 ifindex, mask;
+       int fd, ret;
+
+       fd = pcap_get_selectable_fd(p);
+       if (fd < 0)
+               return;
+
+       if (rx)
+               mask = 1 << PACKET_BROADCAST;
+       else
+               mask = 0;
+
+       ret = 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)
 {
@@ -250,7 +310,7 @@ ead_send_packet_clone(struct ead_packet *pkt)
        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->destipaddr, 4);
+       memcpy(pktbuf->srcipaddr, &pkt->msg.ip, 4);
        memcpy(pktbuf->destipaddr, pkt->srcipaddr, 4);
 
        /* ip checksum */
@@ -330,7 +390,7 @@ handle_ping(struct ead_packet *pkt, int len, int *nstate)
 
        msg->len = htonl(sizeof(struct ead_msg_pong) + slen);
        strncpy(pong->name, dev_name, slen);
-       pong->name[len] = 0;
+       pong->name[slen] = 0;
        pong->auth_type = htons(EAD_AUTH_MD5);
 
        return true;
@@ -344,7 +404,7 @@ handle_set_username(struct ead_packet *pkt, int len, int *nstate)
 
        set_state(EAD_TYPE_SET_USERNAME); /* clear old state */
        strncpy(username, user->username, sizeof(username));
-       username[sizeof(username)] = 0;
+       username[sizeof(username) - 1] = 0;
 
        msg = &pktbuf->msg;
        msg->len = 0;
@@ -539,6 +599,11 @@ parse_message(struct ead_packet *pkt, int len)
                (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;
@@ -574,6 +639,7 @@ parse_message(struct ead_packet *pkt, int len)
        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)) {
@@ -622,30 +688,34 @@ ead_pcap_reopen(bool first)
 {
        static char errbuf[PCAP_ERRBUF_SIZE] = "";
 
-       if (pcap_fp_rx != pcap_fp)
+       if (pcap_fp_rx && (pcap_fp_rx != pcap_fp))
                pcap_close(pcap_fp_rx);
 
        if (pcap_fp)
                pcap_close(pcap_fp);
 
-       pcap_fp_rx = pcap_fp;
+       pcap_fp_rx = NULL;
        do {
-               pcap_fp = pcap_open_live(ifname, PCAP_MRU, 1, PCAP_TIMEOUT, errbuf);
 #ifdef linux
-               if (brname) {
-                       pcap_fp_rx = pcap_open_live(brname, PCAP_MRU, 1, PCAP_TIMEOUT, errbuf);
-                       if (!pcap_fp_rx)
-                               pcap_fp_rx = pcap_fp;
-               }
+               if (instance->bridge[0]) {
+                       pcap_fp_rx = ead_open_pcap(instance->bridge, errbuf, 1);
+                       pcap_fp = ead_open_pcap(instance->ifname, errbuf, 0);
+               } else
 #endif
-               pcap_setfilter(pcap_fp_rx, &pktfilter);
+               {
+                       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", ifname);
+                       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);
 }
 
 
@@ -713,12 +783,8 @@ start_server(struct ead_instance *i)
                }
        }
 
+       instance = i;
        signal(SIGCHLD, instance_handle_sigchld);
-       ifname = i->name;
-#ifdef linux
-       if (i->bridge[0])
-               brname = i->bridge;
-#endif
        ead_pcap_reopen(true);
        ead_pktloop();
        pcap_close(pcap_fp);
@@ -780,7 +846,7 @@ check_bridge_port(const char *br, const char *port, void *arg)
        list_for_each(p, &instances) {
                in = list_entry(p, struct ead_instance, list);
 
-               if (strcmp(in->name, port) != 0)
+               if (strcmp(in->ifname, port) != 0)
                        continue;
 
                in->br_check = true;
@@ -788,7 +854,7 @@ check_bridge_port(const char *br, const char *port, void *arg)
                        break;
 
                strncpy(in->bridge, br, sizeof(in->bridge));
-               DEBUG(2, "assigning port %s to bridge %s\n", in->name, in->bridge);
+               DEBUG(2, "assigning port %s to bridge %s\n", in->ifname, in->bridge);
                stop_server(in, false);
        }
        return 0;
@@ -818,7 +884,7 @@ check_all_interfaces(void)
                if (in->br_check) {
                        in->br_check = false;
                } else if (in->bridge[0]) {
-                       DEBUG(2, "removing port %s from bridge %s\n", in->name, in->bridge);
+                       DEBUG(2, "removing port %s from bridge %s\n", in->ifname, in->bridge);
                        in->bridge[0] = 0;
                        stop_server(in, false);
                }
@@ -831,10 +897,10 @@ int main(int argc, char **argv)
 {
        struct ead_instance *in;
        struct timeval tv;
-       int fd, ch;
        const char *pidfile = NULL;
        bool background = false;
        int n_iface = 0;
+       int fd, ch;
 
        if (argc == 1)
                return usage(argv[0]);
@@ -854,9 +920,9 @@ int main(int argc, char **argv)
                        in = malloc(sizeof(struct ead_instance));
                        memset(in, 0, sizeof(struct ead_instance));
                        INIT_LIST_HEAD(&in->list);
-                       strncpy(in->name, optarg, sizeof(in->name) - 1);
+                       strncpy(in->ifname, optarg, sizeof(in->ifname) - 1);
                        list_add(&in->list, &instances);
-                       n_iface++;
+                       in->id = n_iface++;
                        break;
                case 'D':
                        dev_name = optarg;
@@ -903,13 +969,7 @@ int main(int argc, char **argv)
        }
 
        /* randomize the mac address */
-       fd = open("/dev/urandom", O_RDONLY);
-       if (fd < 0) {
-               perror("open");
-               exit(1);
-       }
-       read(fd, ethmac + 3, 3);
-       close(fd);
+       get_random_bytes(ethmac + 3, 3);
        nid = *(((u16_t *) ethmac) + 2);
 
        start_servers(false);
This page took 0.03246 seconds and 4 git commands to generate.