multicast_link.c 10.4 KB
Newer Older
1 2 3 4 5
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
Cedric Roux's avatar
Cedric Roux committed
6
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

22
/*! \file multicast.h
Cedric Roux's avatar
Cedric Roux committed
23 24 25 26 27 28 29
 *  \brief
 *  \author Lionel Gauthier and Navid Nikaein
 *  \date 2011
 *  \version 1.1
 *  \company Eurecom
 *  \email: navid.nikaein@eurecom.fr
 */
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <assert.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
Cedric Roux's avatar
Cedric Roux committed
51

52 53 54 55 56
#define MULTICAST_LINK_C

#include "socket.h"
#include "multicast_link.h"

Mongazon's avatar
Mongazon committed
57
#include "UTIL/LOG/log.h"
Cedric Roux's avatar
Cedric Roux committed
58

59 60
extern unsigned short Master_id;

61
const char *multicast_group_list[MULTICAST_LINK_NUM_GROUPS] = {
62 63 64 65
  "239.0.0.161",
  "239.0.0.162",
  "239.0.0.163",
  "239.0.0.164"
66 67 68 69
};

static multicast_group_t group_list[MULTICAST_LINK_NUM_GROUPS];

Cedric Roux's avatar
Cedric Roux committed
70 71 72 73 74
/* Socket file descriptors we want to wake up for, using select() */
static fd_set socks;
/* Highest #'d file descriptor, needed for select() */
static int highsock;
#if ! defined(ENABLE_NEW_MULTICAST)
75
static pthread_t main_loop_thread;
Cedric Roux's avatar
Cedric Roux committed
76 77 78 79
#endif
static void (*rx_handler) (unsigned int, char *);
static unsigned char multicast_group;
static char *multicast_if;
80 81 82

//------------------------------------------------------------------------------
void
83
multicast_link_init(void)
84
{
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
  //------------------------------------------------------------------------------
  int             group;
  int             multicast_loop;
  int             reuse_addr = 1;
  static struct ip_mreq command;
  struct sockaddr_in sin;
  // struct ifreq ifr;

  for (group = 0; group < MULTICAST_LINK_NUM_GROUPS; group++) {
    //strcpy (group_list[group].host_addr, multicast_group_list[group]);
    strncpy (group_list[group].host_addr, multicast_group_list[group], sizeof(group_list[group].host_addr));
    group_list[group].host_addr[sizeof(group_list[group].host_addr) - 1] = 0; // terminate string
    group_list[group].port = 46014 + group;
    group_list[group].socket = make_socket_inet(
                                 SOCK_DGRAM,
                                 &group_list[group].port, &sin);

    LOG_D(EMU, "multicast_link_init(): Created socket %d for group %d, port %d\n",
          group_list[group].socket,group,group_list[group].port);

    /* Used so we can re-bind to our port while a previous connection is still
     * in TIME_WAIT state.
     */
    if (setsockopt(group_list[group].socket, SOL_SOCKET, SO_REUSEADDR,
                   &reuse_addr, sizeof (reuse_addr)) < 0) {
      LOG_E(EMU, "[MULTICAST] ERROR : setsockopt:SO_REUSEADDR, exiting ...");
      exit (EXIT_FAILURE);
    }

    if (multicast_if != NULL) {
      if (setsockopt(group_list[group].socket, SOL_SOCKET,SO_BINDTODEVICE,
116
                     multicast_if, strlen(multicast_if)) < 0) {
117 118 119 120 121 122
        LOG_E(EMU,
              "[MULTICAST] ERROR : setsockopt:SO_BINDTODEVICE on interface %s, exiting ...\n",
              multicast_if);
        LOG_E(EMU,
              "[MULTICAST] make sure that you have a root privilage or run with sudo -E \n");
        exit (EXIT_FAILURE);
nikaeinn's avatar
nikaeinn committed
123
      }
124 125
    }

126
#if !defined(ENABLE_NEW_MULTICAST)
127 128
    /* Make the socket blocking */
    socket_setnonblocking(group_list[group].socket);
Cedric Roux's avatar
Cedric Roux committed
129
#endif
130 131 132 133 134 135 136 137 138

    multicast_loop = 0;

    if (setsockopt (group_list[group].socket, IPPROTO_IP, IP_MULTICAST_LOOP,
                    &multicast_loop, sizeof (multicast_loop)) < 0) {
      LOG_E(EMU,
            "[MULTICAST] ERROR: %s line %d multicast_link_main_loop() IP_MULTICAST_LOOP %m",
            __FILE__, __LINE__);
      exit (EXIT_FAILURE);
Cedric Roux's avatar
Cedric Roux committed
139
    }
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

    // Join the broadcast group:
    command.imr_multiaddr.s_addr = inet_addr (group_list[group].host_addr);
    command.imr_interface.s_addr = htonl (INADDR_ANY);

    if (command.imr_multiaddr.s_addr == -1) {
      LOG_E(EMU, "[MULTICAST] ERROR: %s line %d NO MULTICAST", __FILE__, __LINE__);
      exit (EXIT_FAILURE);
    }

    if (setsockopt (group_list[group].socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                    &command, sizeof (command)) < 0) {
      LOG_E(EMU, "[MULTICAST] ERROR: %s line %d IP_ADD_MEMBERSHIP %m", __FILE__,
            __LINE__);
      exit (EXIT_FAILURE);
    }

    memset (&group_list[group].sock_remote_addr, 0, sizeof (struct sockaddr_in));
    group_list[group].sock_remote_addr.sin_family = AF_INET;
    group_list[group].sock_remote_addr.sin_addr.s_addr = inet_addr (multicast_group_list[group]);
    group_list[group].sock_remote_addr.sin_port = htons (group_list[group].port);
  }
162 163 164 165
}

//------------------------------------------------------------------------------
void
166
multicast_link_build_select_list (void)
167
{
168 169 170 171 172 173 174
  //------------------------------------------------------------------------------
  int             group;

  /* First put together fd_set for select(), which will
     consist of the sock veriable in case a new connection
     is coming in, plus all the sockets we have already
     accepted. */
175 176


177 178
  /* FD_ZERO() clears out the fd_set called socks, so that
     it doesn't contain any file descriptors. */
179

180
  FD_ZERO (&socks);
181 182


183 184
  /* Loops through all the possible connections and adds
     those sockets to the fd_set */
185

186 187 188
  for (group = 0; group < MULTICAST_LINK_NUM_GROUPS; group++) {
    if (group_list[group].socket != 0) {
      FD_SET (group_list[group].socket, &socks);
189

190 191 192
      if (group_list[group].socket > highsock) {
        highsock = group_list[group].socket;
      }
193
    }
194
  }
195
}
Cedric Roux's avatar
Cedric Roux committed
196

197 198 199
void
multicast_link_read_data (int groupP)
{
200 201 202 203 204 205 206
  int num_bytes;

  if ((groupP < MULTICAST_LINK_NUM_GROUPS) && (groupP >= 0)) {
    if ((num_bytes = recvfrom (group_list[groupP].socket,
                               group_list[groupP].rx_buffer, 40000, 0, 0, 0)) < 0) {
      LOG_E(EMU, "[MULTICAST] recvfrom has failed (%d:%s)\n   (%s:%d)\n",
            errno, strerror(errno), __FILE__, __LINE__);
207
    } else {
208
      rx_handler(num_bytes,group_list[groupP].rx_buffer);
209
    }
210 211 212
  } else {
    LOG_E(EMU, "[MULTICAST] ERROR: groupP out of bounds %d\n", groupP);
  }
213
}
Cedric Roux's avatar
Cedric Roux committed
214

215 216
//------------------------------------------------------------------------------
void
217
multicast_link_read (void)
218
{
219 220
  //------------------------------------------------------------------------------
  int             group;        /* Current item in connectlist for for loops */
221

222
  /* Now check connectlist for available data */
223

224 225
  /* Run through our sockets and check to see if anything
     happened with them, if so 'service' them. */
Cedric Roux's avatar
Cedric Roux committed
226

227 228 229 230 231 232
  for (group = multicast_group; group < MULTICAST_LINK_NUM_GROUPS ; group++) {
    if (FD_ISSET (group_list[group].socket, &socks)) {
      multicast_link_read_data (group);
      break;
    }
  }                             /* for (all entries in queue) */
233 234 235 236
}

//------------------------------------------------------------------------------
int
237
multicast_link_write_sock(int groupP, char *dataP, uint32_t sizeP)
238
{
239 240 241 242 243 244 245 246 247 248 249 250
  //------------------------------------------------------------------------------
  int             num;

  if ((num = sendto (group_list[groupP].socket, dataP, sizeP, 0,
                     (struct sockaddr *) &group_list[groupP].sock_remote_addr,
                     sizeof (group_list[groupP].sock_remote_addr))) < 0) {
    LOG_E(EMU, "[MULTICAST] sendto has failed (%d:%s)\n   (%s:%d)\n",
          errno, strerror(errno),
          __FILE__, __LINE__);
  }

  return num;
251 252
}

Cedric Roux's avatar
Cedric Roux committed
253
int multicast_link_read_data_from_sock(uint8_t is_master)
254
{
255 256
  struct timeval timeout, *timeout_p;
  int readsocks;    /* Number of sockets ready for reading */
Cedric Roux's avatar
Cedric Roux committed
257

258 259
  timeout.tv_sec = 0;
  timeout.tv_usec = 15000;
Cedric Roux's avatar
Cedric Roux committed
260

261 262 263 264 265 266 267 268 269 270 271 272 273
  if (is_master == 0) {
    /* UE will block indefinetely if no data is sent from eNB
     * NOTE: a NULL timeout for select will block indefinetely
     */
    timeout_p = NULL;
  } else {
    /* In case of eNB set the timeout to 500 usecs after which we consider
     * the packets as dropped.
     */
    timeout_p = &timeout;
  }

  highsock = -1;
Cedric Roux's avatar
Cedric Roux committed
274

275
  multicast_link_build_select_list ();
276

277 278
  LOG_D(EMU, "Stuck on select with timeout %s\n",
        timeout_p == NULL ? "infinite" : "15000 usecs");
279

280
  readsocks = select(highsock + 1, &socks, (fd_set *) 0, (fd_set *) 0, timeout_p);
Cedric Roux's avatar
Cedric Roux committed
281

282 283 284 285
  if (readsocks < 0) {
    LOG_E(EMU, "Multicast select failed (%d:%s)\n", errno, strerror(errno));
    exit(EXIT_FAILURE);
  } else if (readsocks > 0) {
286
#ifdef DEBUG_EMU
287
    LOG_D(EMU, "Multicast Normal read\n");
288
#endif
289 290 291 292 293 294 295 296
    multicast_link_read();
  } else {
    /* Timeout */
    LOG_I(EMU, "Multicast select time-out\n");
    return 1;
  }

  return 0;
Cedric Roux's avatar
Cedric Roux committed
297
}
298

Cedric Roux's avatar
Cedric Roux committed
299 300
void* multicast_link_main_loop (void *param)
{
301 302 303
  while (1) {
    multicast_link_read_data_from_sock(0);
  }
Cedric Roux's avatar
Cedric Roux committed
304

305
  return NULL;
306 307
}

Cedric Roux's avatar
Cedric Roux committed
308
void multicast_link_start(void (*rx_handlerP) (unsigned int, char *),
309
                          unsigned char _multicast_group, char *multicast_ifname)
310
{
311
  rx_handler = rx_handlerP;
312
  multicast_group = _multicast_group;
313 314 315 316 317
  multicast_if =  multicast_ifname;
  LOG_I(EMU, "[MULTICAST] LINK START on interface=%s for group=%d: handler=%p\n",
        (multicast_if == NULL) ? "not specified" : multicast_if, multicast_group,
        rx_handler);
  multicast_link_init ();
Cedric Roux's avatar
Cedric Roux committed
318
#if ! defined(ENABLE_NEW_MULTICAST)
319 320 321 322 323 324 325 326 327 328 329 330
  LOG_D(EMU, "[MULTICAST] multicast link start thread\n");

  if (pthread_create (&main_loop_thread, NULL, multicast_link_main_loop,
                      NULL) != 0) {
    LOG_E(EMU, "[MULTICAST LINK] Error in pthread_create (%d:%s)\n",
          errno, strerror(errno));
    exit(EXIT_FAILURE);
  } else {
    pthread_detach(main_loop_thread);  // disassociate from parent
    LOG_I(EMU, "[MULTICAST LINK] Thread detached\n");
  }

Cedric Roux's avatar
Cedric Roux committed
331
#endif
332
}