/* * cleopatre/application/managerd/src/mme_nl.c * * (C) Copyright 2010 SPiDCOM Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * 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. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ioctl.h" #include "mme_nl.h" #define PLC_IFNAME "plc0" /** * Receive a frame from MME interface. * * \param ctx managerd context. * \param buffer frame pointer. * \param len length allowed. * \return received length or error code. */ int mme_nl_receive(struct managerd_ctx *ctx, uint8_t *buffer, int len) { struct msghdr msg; struct nlmsghdr *nlh; struct sockaddr_nl kernel_addr; struct iovec iov; int msg_len; /* create message from MME netlink */ nlh=(struct nlmsghdr *)malloc (sizeof(struct nlmsghdr) + len); memset(nlh, 0, sizeof(struct nlmsghdr) + len); memset(&kernel_addr, 0, sizeof(kernel_addr)); iov.iov_base = (void *)nlh; iov.iov_len = NLMSG_LENGTH(len); msg.msg_name = (void *)&kernel_addr; msg.msg_namelen = sizeof(kernel_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; if((recvmsg (ctx->sock_mme, &msg, 0)) < 0) { syslog (LOG_WARNING, "mme receive error (%d)", errno); free (nlh); return -1; } msg_len = nlh->nlmsg_len - NLMSG_HDRLEN; if (msg_len > len) { syslog (LOG_WARNING, "mme receive too big (%d)", errno); free (nlh); return -1; } memcpy (buffer, NLMSG_DATA (nlh), msg_len); free (nlh); return msg_len; } /** * Send a frame to MME interface. * * \param ctx managerd context. * \param buffer frame pointer. * \param len length to send. * \return error code. */ int mme_nl_send(struct managerd_ctx *ctx, uint8_t *buffer, int len) { struct sockaddr_nl kernel_addr; struct nlmsghdr *nlh; int msg_len; struct iovec iov; struct msghdr msg; /* fill the netlink address to send msg */ memset(&kernel_addr, 0, sizeof(kernel_addr)); kernel_addr.nl_family = AF_NETLINK; kernel_addr.nl_pid = 0; /* For Linux Kernel */ kernel_addr.nl_groups = 0; /* unicast */ /* fill message */ nlh=(struct nlmsghdr *)malloc (sizeof(struct nlmsghdr) + len); memset(nlh, 0, sizeof(struct nlmsghdr)); memcpy (NLMSG_DATA (nlh), buffer, len); /* fill the netlink message header */ nlh->nlmsg_len = NLMSG_LENGTH(len); nlh->nlmsg_pid = getpid(); /* self pid */ nlh->nlmsg_flags = 0; /* fill message info */ memset(&msg, 0, sizeof(msg)); msg.msg_name = (void *)&kernel_addr; msg.msg_namelen = sizeof(kernel_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = (void *)nlh; iov.iov_len = NLMSG_LENGTH(len); if((msg_len = sendmsg (ctx->sock_mme, &msg, 0)) < 0) { syslog (LOG_WARNING, "sendmsg error (%d)", errno); free (nlh); return -1; } free (nlh); return (len); } /** * Initialize and prepare connections. * * \param ctx managerd context. * \return error code. */ int mme_nl_init(struct managerd_ctx *ctx) { struct plcdrv_setpid user_data; struct ifreq ifr; int sock_plc; struct sockaddr_ll plc_sll; //Check arguments assert(ctx != NULL); memset (&user_data, 0, sizeof (struct plcdrv_setpid)); memset (&ifr, 0, sizeof (struct ifreq)); /* Create a receive connection on PLC interface */ if (0 > (sock_plc = socket (AF_PACKET, SOCK_RAW, ETH_P_HPAV))) { syslog (LOG_WARNING, "cannot open socket on %s (%s)", PLC_IFNAME, strerror (errno)); return -1; } /* Prepare PLC socket address */ strncpy (ifr.ifr_name, (char*) PLC_IFNAME, IFNAMSIZ); if (-1 == (ioctl (sock_plc, SIOCGIFINDEX, &ifr))) { syslog (LOG_WARNING, "cannot get interface %s index (%s)", PLC_IFNAME, strerror (errno)); close (sock_plc); return -1; } plc_sll.sll_family = AF_PACKET; plc_sll.sll_ifindex = ifr.ifr_ifindex; plc_sll.sll_protocol = htons (ETH_P_HPAV); /* Bind PLC socket to this interface */ if (-1 == (bind (sock_plc, (struct sockaddr *) &plc_sll, sizeof (struct sockaddr_ll)))) { syslog (LOG_WARNING, "cannot bind raw socket to interface %s (%s)", PLC_IFNAME, strerror (errno)); close (sock_plc); return -1; } /* Set managerd pid for reception on mme netlink */ user_data.nl = NETLINK_PLC_MME; user_data.pid = getpid (); ifr.ifr_data = (void *) &user_data; strncpy (ifr.ifr_name, (char*) PLC_IFNAME, IFNAMSIZ); if (0 > (ioctl (sock_plc, PLCDRV_IOCTL_SETPID, &ifr))) { syslog (LOG_WARNING, "cannot call ioctl SETPID (%s)", strerror (errno)); close (sock_plc); return -1; } /* We do not need PLC connection anymore => close it. */ close (sock_plc); /* open netlink socket */ if (0 > (ctx->sock_mme = socket (PF_NETLINK, SOCK_RAW, NETLINK_PLC_MME))) { syslog (LOG_WARNING, "cannot open socket on %s (%s)", MME_IFNAME, strerror(errno)); return -1; } memset (&ctx->mme_snl, 0, sizeof (ctx->mme_snl)); ctx->mme_snl.nl_family = AF_NETLINK; ctx->mme_snl.nl_pid = getpid (); /* self pid */ ctx->mme_snl.nl_groups = 0; /* not in mcast group */ if (0 > bind (ctx->sock_mme, (struct sockaddr *) &ctx->mme_snl, sizeof (ctx->mme_snl))) { syslog (LOG_WARNING, "cannot bind socket on %s (%s)", MME_IFNAME, strerror (errno)); close (ctx->sock_mme); return -1; } return 0; } /** * Close connections. * * \param ctx managerd context. */ void mme_nl_uninit(struct managerd_ctx *ctx) { //Check arguments assert(ctx != NULL); //Close MME connections close(ctx->sock_mme); }