Logo Search packages:      
Sourcecode: dcbd version File versions  Download package

ctrl_iface.c

/*******************************************************************************

  Intel Data Center Bridging (DCB) Software
  Copyright(c) 2007-2009 Intel Corporation.

  Substantially modified from:
  hostapd-0.5.7
  Copyright (c) 2002-2007, Jouni Malinen <jkmaline@cc.hut.fi> and
  contributors

  This program is free software; you can redistribute it and/or modify it
  under the terms and conditions of the GNU General Public License,
  version 2, as published by the Free Software Foundation.

  This program is distributed in the hope 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.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.

  The full GNU General Public License is included in this distribution in
  the file called "COPYING".

  Contact Information:
  e1000-eedc Mailing List <e1000-eedc@lists.sourceforge.net>
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

*******************************************************************************/

#include "includes.h"
#include <sys/un.h>
#include <sys/stat.h>
#include "common.h"
#include "dcbd.h"
#include "eloop.h"
#include "ctrl_iface.h"
#include "clif_cmds.h"

struct ctrl_dst {
      struct ctrl_dst *next;
      struct sockaddr_un addr;
      socklen_t addrlen;
      int debug_level;
      int errors;
};


/*
 *    Returns a status value.  0 is successful, 1 is an error.
*/
int clif_iface_cmd_unknown(struct dcbd_data *dcbdd,
                             struct sockaddr_un *from,
                             socklen_t fromlen,
                             char *ibuf, int ilen,
                             char *rbuf)
{
      return 1;
}

/*
 *    Returns a status value.  0 is successful, 1 is an error.
*/
int clif_iface_ping(struct dcbd_data *dcbdd,
                             struct sockaddr_un *from,
                             socklen_t fromlen,
                             char *ibuf, int ilen,
                             char *rbuf)
{
      sprintf(rbuf, "%cPONG", PING_CMD);

      return 0;
}

/*
 *    Returns a status value.  0 is successful, 1 is an error.
*/
int clif_iface_attach(struct dcbd_data *dcbdd,
                             struct sockaddr_un *from,
                             socklen_t fromlen,
                             char *ibuf, int ilen,
                             char *rbuf)
{
      struct ctrl_dst *dst;

      dst = wpa_zalloc(sizeof(*dst));
      if (dst == NULL)
            return 1;
      memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
      dst->addrlen = fromlen;
      dst->debug_level = MSG_INFO;
      dst->next = dcbdd->ctrl_dst;
      dcbdd->ctrl_dst = dst;
      wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
                (u8 *) from->sun_path, fromlen);
      sprintf(rbuf, "%c", ATTACH_CMD);

      return 0;
}

/*
 *    Returns a status value.  0 is successful, 1 is an error.
*/
static int detach_clif_monitor(struct dcbd_data *dcbdd,
                             struct sockaddr_un *from,
                             socklen_t fromlen)
{
      struct ctrl_dst *dst, *prev = NULL;

      dst = dcbdd->ctrl_dst;
      while (dst) {
            if (fromlen == dst->addrlen &&
                memcmp(from->sun_path, dst->addr.sun_path,
                  fromlen-sizeof(from->sun_family)) == 0) {
                  if (prev == NULL)
                        dcbdd->ctrl_dst = dst->next;
                  else
                        prev->next = dst->next;
                  free(dst);
                  wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
                            (u8 *) from->sun_path, fromlen);

                  return 0;
            }
            prev = dst;
            dst = dst->next;
      }
      return 1;
}

/*
 *    Returns a status value.  0 is successful, 1 is an error.
*/
int clif_iface_detach(struct dcbd_data *dcbdd,
                             struct sockaddr_un *from,
                             socklen_t fromlen,
                             char *ibuf, int ilen,
                             char *rbuf)
{
      sprintf(rbuf, "%c", DETACH_CMD);
      return detach_clif_monitor(dcbdd, from, fromlen);
}


/*
 *    Returns a status value.  0 is successful, 1 is an error.
*/
int clif_iface_level(struct dcbd_data *dcbdd,
                            struct sockaddr_un *from,
                            socklen_t fromlen,
                            char *ibuf, int ilen,
                            char *rbuf)
{
      struct ctrl_dst *dst;
      char *level;

      level = ibuf+1;
      sprintf(rbuf, "%c", LEVEL_CMD);

      wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);

      dst = dcbdd->ctrl_dst;
      while (dst) {
            if (fromlen == dst->addrlen &&
                memcmp(from->sun_path, dst->addr.sun_path,
                  fromlen-sizeof(from->sun_family)) == 0) {
                  wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
                            "level", (u8 *) from->sun_path, fromlen);
                  dst->debug_level = atoi(level);

                  return 0;
            }
            dst = dst->next;
      }

      return 1;
}



static void ctrl_iface_receive(int sock, void *eloop_ctx,
                               void *sock_ctx)
{
      struct dcbd_data *dcbdd = eloop_ctx;
      char buf[MAX_CLIF_MSGBUF];
      int res;
      struct sockaddr_un from;
      socklen_t fromlen = sizeof(from);
      char *reply;
      const int reply_size = MAX_CLIF_MSGBUF;
      int reply_len;

      res = recvfrom(sock, buf, sizeof(buf) - 1, MSG_DONTWAIT,
                   (struct sockaddr *) &from, &fromlen);
      if (res < 0) {
            perror("recvfrom(ctrl_iface)");
            return;
      }
      buf[res] = '\0';
      wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);

      reply = malloc(reply_size);
      if (reply == NULL) {
            sendto(sock, "FAIL", 4, 0, (struct sockaddr *) &from,
                   fromlen);
            return;
      }

      process_clif_cmd(dcbdd, &from, fromlen, buf, res, reply, &reply_len);

      sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
      free(reply);
}


static char *ctrl_iface_path(struct dcbd_data *dcbdd)
{
      char *buf;
      size_t len;

      if (dcbdd->ctrl_interface == NULL)
            return NULL;

      len = strlen(dcbdd->ctrl_interface) + strlen(dcbdd->iface) +
            2;
      buf = malloc(len);
      if (buf == NULL)
            return NULL;

      snprintf(buf, len, "%s/%s",
             dcbdd->ctrl_interface, dcbdd->iface);
      buf[len - 1] = '\0';
      return buf;
}


int ctrl_iface_init(struct dcbd_data *dcbdd)
{
      struct sockaddr_un addr;
      int s = -1;
      char *fname = NULL;
      int retry;

      dcbdd->ctrl_sock = -1;
      dcbdd->ctrl_dst = NULL;

      if (dcbdd->ctrl_interface == NULL)
            return 0;

      if (mkdir(dcbdd->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
            if (errno == EEXIST) {
                  wpa_printf(MSG_DEBUG, "Using existing control "
                           "interface directory.");
            } else {
                  perror("mkdir[ctrl_interface]");
                  goto fail;
            }
      }

      if (dcbdd->ctrl_interface_gid_set &&
          chown(dcbdd->ctrl_interface, 0,
              dcbdd->ctrl_interface_gid) < 0) {
            perror("chown[ctrl_interface]");
            return -1;
      }

      if (strlen(dcbdd->ctrl_interface) + 1 + strlen(dcbdd->iface)
          >= sizeof(addr.sun_path))
            goto fail;

      s = socket(PF_UNIX, SOCK_DGRAM, 0);
      if (s < 0) {
            perror("socket(PF_UNIX)");
            goto fail;
      }

      memset(&addr, 0, sizeof(addr));
      addr.sun_family = AF_UNIX;
      fname = ctrl_iface_path(dcbdd);
      if (fname == NULL)
            goto fail;

      strncpy(addr.sun_path, fname, sizeof(addr.sun_path));
      for (retry = 0; retry < 2; retry++) {
            if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                  if (errno == EADDRINUSE) {
                        unlink(fname);
                  }
            }
            else {
                  break;
            }
      }
      if (retry == 2) {
            perror("bind(PF_UNIX)");
            goto fail;
      }

      if (dcbdd->ctrl_interface_gid_set &&
          chown(fname, 0, dcbdd->ctrl_interface_gid) < 0) {
            perror("chown[ctrl_interface/ifname]");
            goto fail;
      }

      if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
            perror("chmod[ctrl_interface/ifname]");
            goto fail;
      }
      free(fname);

      dcbdd->ctrl_sock = s;
      eloop_register_read_sock(s, ctrl_iface_receive, dcbdd,
                         NULL);

      return 0;

fail:
      if (s >= 0)
            close(s);
      if (fname) {
            unlink(fname);
            free(fname);
      }
      return -1;
}


void ctrl_iface_deinit(struct dcbd_data *dcbdd)
{
      struct ctrl_dst *dst, *prev;

      if (dcbdd->ctrl_sock > -1) {
            eloop_unregister_read_sock(dcbdd->ctrl_sock);
            close(dcbdd->ctrl_sock);
            dcbdd->ctrl_sock = -1;

            if (dcbdd->ctrl_interface &&
                rmdir(dcbdd->ctrl_interface) < 0) {
                  if (errno == ENOTEMPTY) {
                        wpa_printf(MSG_DEBUG, "Control interface "
                                 "directory not empty - leaving it "
                                 "behind");
                  } else {
                        perror("rmdir[ctrl_interface]");
                  }
            }
      }

      dst = dcbdd->ctrl_dst;
      while (dst) {
            prev = dst;
            dst = dst->next;
            free(prev);
      }

      free(dcbdd);
}


void ctrl_iface_send(struct dcbd_data *dcbdd, int level,
                       char *buf, size_t len)
{
      struct ctrl_dst *dst, *next;
      struct msghdr msg;
      int idx;
      struct iovec io[2];
      char levelstr[10];

      dst = dcbdd->ctrl_dst;
      if (dcbdd->ctrl_sock < 0 || dst == NULL)
            return;

      snprintf(levelstr, sizeof(levelstr), "%c%d", EVENT_MSG, level);
      io[0].iov_base = levelstr;
      io[0].iov_len = strlen(levelstr);
      io[1].iov_base = buf;
      io[1].iov_len = len;
      memset(&msg, 0, sizeof(msg));
      msg.msg_iov = io;
      msg.msg_iovlen = 2;

      idx = 0;
      while (dst) {
            next = dst->next;
            if (level >= dst->debug_level) {
                  wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
                            (u8 *) dst->addr.sun_path, dst->addrlen);
                  msg.msg_name = &dst->addr;
                  msg.msg_namelen = dst->addrlen;
                  if (sendmsg(dcbdd->ctrl_sock, &msg, 0) < 0) {
                        fprintf(stderr,
                              "CTRL_IFACE monitor[%d][%d] %d:%s: ",
                              idx, dcbdd->ctrl_sock, dst->addrlen,
                              dst->addr.sun_path);
                        perror("sendmsg");
                        dst->errors++;
                        if (dst->errors > 10) {
                              detach_clif_monitor(
                                    dcbdd, &dst->addr,
                                    dst->addrlen);
                        }
                  } else {
                        dst->errors = 0;
                  }
            }
            idx++;
            dst = next;
      }
}

Generated by  Doxygen 1.6.0   Back to index