/* $Id: mcl_layer.cpp,v 1.2 2003/10/14 09:27:03 chneuman Exp $ */ /* * Copyright (c) 1999-2003 INRIA - Universite Paris 6 - All rights reserved * (main author: Vincent Roca - vincent.roca@inrialpes.fr) * * 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. */ /* * Layer management functions. */ #include "mcl_includes.h" /* * init the number of layers as required */ #ifdef STREAM int mcl_init_layer_nb (mclcb_t *mclcb, int nb_level) #else int mcl_init_layer_nb (mclcb_t *mclcb) #endif { TRACELVL(5, (mcl_stdout, "-> mcl_init_layer_nb:\n")) /* if (mclcb->receiver == 1 && (mode & MODE_MCAST_RX)) */ ASSERT(mclcb->max_level <= MAX_TX_LEVEL); if (mclcb->receiver == 1) { #if defined(RLC) || defined(FLIDS) #if 0 if (mclcb->scheduler == MCL_SCHED_LCT2) { /* with the MCL_SCHED_LCT2 sched, it is preferable * to start with 2 layers. The reason is that data is * sent on layer 0, fec on layer 1 */ mclcb->nb_level = 1; } else #endif /* NEVERDEF */ /* otherwise (general case) use only 1 layer */ #ifdef STREAM mclcb->nb_level = nb_level; #else mclcb->nb_level = 1; #endif #else /* RLC | FLIDS */ /* no congestion control here, use a fixed nb of layers */ mclcb->nb_level = mclcb->max_level; #endif /* RLC | FLIDS */ } else if (mclcb->sender == 1) { mclcb->nb_level = mclcb->max_level; } TRACELVL(5, (mcl_stdout, "<- mcl_init_layer_nb: (%s) nb_level=%d, max_level=%d\n", (mclcb->receiver == 1 ? "receiver" : "sender"), mclcb->nb_level, mclcb->max_level)) return 0; } /** * Determine the possible number of FEC layers with scheduling 2 and 3, * given k, the maximum n, and the maximum fec_ratio. */ INT32 mcl_get_nb_fec_layers (mclcb_t *mclcb, int k) { int layers; /* # of layers except layer 0 */ int max_fec_nb; ASSERT(k > 0); ASSERT(mclcb->scheduler == MCL_SCHED_LCT2 || mclcb->scheduler == MCL_SCHED_LCT3); /* we can generate that number of fec DUs */ max_fec_nb = mclcb->fec.get_n() - k; /* but it must be limited by k * fec_ratio */ max_fec_nb = min(max_fec_nb, (int)floor(mclcb->fec.get_max_fec_ratio() - 1.0) * k); /* we can generate that number of fec layers */ layers = min(mclcb->max_level - 1, (int)(max_fec_nb / k)); return (layers); } /* * add the specified layer or highest layer if layer is MCL_HIGHEST_LAYER * returns 0 if OK, < 0 if error * * TO DO: DSS case (specify layer) */ int mcl_add_layer (mclcb_t *mclcb, int layer) { //struct sockaddr_in saddr; mcl_addr addr; TRACELVL(5, (mcl_stdout, "-> mcl_add_layer: %d\n", layer)) if (layer == MCL_HIGHEST_LAYER) layer = mclcb->nb_level; if (layer != mclcb->nb_level) { /* cumulative scheme so do not allow gaps in layers */ goto bad; } if (mclcb->nb_level + 1 > mclcb->max_level) { /* we have reached the maximum nb of layer */ goto bad; } if (mclcb->mode & MODE_MCAST_RX) { /* only with multicast */ ASSERT(mclcb->addr.is_multicast_addr()); #if 0 memset((void *)&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(mclcb->addr + layer); saddr.sin_port = htons((u_int16_t)(mclcb->myport + layer)); #endif addr = mclcb->addr; addr.set_addr(addr.get_addr() + layer); /* changing the port number is in fact only required * by Solaris, not by Linux! */ addr.set_port(addr.get_port() + layer); /* * now init everything */ mcl_layer_sock_init(mclcb, layer, &(mclcb->mcgroup_tab[layer]), &addr); } mclcb->nb_level++; if (mclcb->verbose == 2) { struct timeval time; time = mcl_get_tvtime(); PRINT_OUT((mcl_stdout, "\n%ld.%06ld\tadd_layer %d\n", time.tv_sec, time.tv_usec, layer)) } TRACELVL(5, (mcl_stdout, "<- mcl_add_layer: new nb_layer=%d\n", mclcb->nb_level)) return 0; bad: if (mclcb->verbose >= 4) PRINT_ERR((mcl_stderr, "mcl_add_layer: ERROR, can't add layer %d\n", layer)) TRACELVL(5, (mcl_stdout, "<- mcl_add_layer: ERROR\n")) return -1; } /* * drop the specified layer or highest layer if layer is MCL_HIGHEST_LAYER * after unsubscribing to multicast group * returns 0 if OK, < 0 if error * It is possible to call it in "check" mode just to see if dropping * would be possible... * * TO DO: case where multiple layer -> single mg */ int mcl_drop_layer (mclcb_t *mclcb, int layer, /* layer to drop */ int check) /* MCL_CHECK_ONLY if check w/o */ /* dropping MCL_DO_IT otherwise */ { TRACELVL(5, (mcl_stdout, "-> mcl_drop_layer: layer=%d\n", layer)) if (mclcb->nb_level <= 0) { /* already done, return */ TRACELVL(5, (mcl_stdout, " mcl_drop_layer: already dropped\n")) goto ok; } if (layer == MCL_ALL_LAYERS) { /* drop everything, including layer 0 if possible */ #if defined(LCT_SCHED2) || defined(LCT_SCHED3) if (mclcb->scheduler == MCL_SCHED_LCT2 || mclcb->scheduler == MCL_SCHED_LCT3) { while (mclcb->nb_level > 2) { mcl_drop_layer(mclcb, MCL_HIGHEST_LAYER, MCL_DO_IT); } if (mclcb->nb_level == 2) { /* dont forget layer 1 */ mcl_drop_this_layer(mclcb, 1); } } else { #endif /* LCT_SCHED2|3 */ while (mclcb->nb_level > 1) { mcl_drop_layer(mclcb, MCL_HIGHEST_LAYER, MCL_DO_IT); } #if defined(LCT_SCHED2) || defined(LCT_SCHED3) } #endif /* LCT_SCHED2|3 */ ASSERT(mclcb->nb_level == 1); if (!mclcb->never_leave_base_layer) mcl_drop_this_layer(mclcb, 0); /* and layer 0 */ goto ok; } else if (layer == MCL_HIGHEST_LAYER) { layer = mclcb->nb_level - 1; } else if (layer != mclcb->nb_level - 1) { /* this is a cumulative scheme */ /* => can only drop highest layer */ goto bad; } #if defined(LCT_SCHED2) || defined(LCT_SCHED3) if ((mclcb->scheduler == MCL_SCHED_LCT2 || mclcb->scheduler == MCL_SCHED_LCT3) && layer <= 1) { /* with the MCL_SCHED_LCT2 and MCL_SCHED_LCT3 sched, it is * preferable to always have 2 layers */ goto bad; } #endif /* LCT_SCHED2|3 */ if (layer <= 0) { /* can't drop layer 0, no matter wether */ /* never_leave_base_layer is set or not */ goto bad; } if (check == MCL_CHECK_ONLY) { TRACELVL(5, (mcl_stdout, "<- mcl_drop_layer: drop possible\n")) return 0; /* do nothing */ } /* ok, possible so drop it */ mcl_drop_this_layer(mclcb, layer); ok: TRACELVL(5, (mcl_stdout, "<- mcl_drop_layer:\n")) return 0; bad: if (mclcb->verbose >= 4) PRINT_ERR((mcl_stderr, "mcl_drop_layer: ERROR cant drop layer %d\n", layer)) TRACELVL(5, (mcl_stdout, "<- mcl_drop_layer: ERROR\n")) return -1; } /* * same as mcl_drop_layer except that there is no check (which enables * to drop layer 0!). Use with care... */ int mcl_drop_this_layer (mclcb_t *mclcb, int layer) /* layer to drop */ { mcgroup_t *mg; TRACELVL(5, (mcl_stdout, "-> mcl_drop_this_layer: layer=%d\n", layer)) mclcb->nb_level--; if (mclcb->mode & MODE_MCAST_RX) { /* * drop multicast group first... * NB: check consistency with mcl_layer_sock_init() */ struct ip_mreq imr; mg = &(mclcb->mcgroup_tab[layer]); #if 0 imr.imr_multiaddr.s_addr = mg->saddr.sin_addr.s_addr; imr.imr_interface.s_addr = htonl(mclcb->mcast_if); #endif imr.imr_multiaddr.s_addr = htonl(mg->addr.get_addr()); imr.imr_interface.s_addr = htonl(mclcb->mcast_if.get_addr()); if (setsockopt(mg->ses_sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&imr, sizeof(imr)) < 0) { perror("mcl_drop_this_layer: IP_DROP_MEMBERSHIP"); mcl_exit(1); } if (mg->can_rx) { /* remove from select() */ FD_CLR((u_int) mg->ses_sock, &(mclcb->rxlvl.fds)); if (mclcb->rxlvl.nfds == (int)mg->ses_sock + 1) /* TODO WIN32 compliant? */ mclcb->rxlvl.nfds--; mclcb->rxlvl.n_fd--; } mg->can_rx = mg->can_tx = 0; if (mcl_is_valid_sock(mg->ses_sock)) { #ifdef WIN32 closesocket(mg->ses_sock); mg->ses_sock = INVALID_SOCKET; #else /* UNIX */ close(mg->ses_sock); mg->ses_sock = 0; #endif } if (mcl_is_valid_sock(mg->priv_sock)) { #ifdef WIN32 closesocket(mg->priv_sock); mg->priv_sock = INVALID_SOCKET; #else /* UNIX */ close(mg->priv_sock); mg->priv_sock = 0; #endif } memset(mg, 0, sizeof(*mg)); } else if ((mclcb->mode & MODE_UNI_RX) && layer == 0) { /* * we are a unicast rx... * do nothing (layering is only simulated) except if * layer 0 is dropped in which case we close everything! */ ASSERT(!(mclcb->mode & MODE_MCAST_RX)); mg = &(mclcb->mcgroup_tab[0]); if (mg->can_rx) { /* remove from select() */ FD_CLR((u_int) mg->ses_sock, &(mclcb->rxlvl.fds)); if (mclcb->rxlvl.nfds == (int)mg->ses_sock + 1) /* TODO WIN32 compliant? */ mclcb->rxlvl.nfds--; mclcb->rxlvl.n_fd--; } mg->can_rx = mg->can_tx = 0; if (mcl_is_valid_sock(mg->ses_sock)) { #ifdef WIN32 closesocket(mg->ses_sock); mg->ses_sock = INVALID_SOCKET; #else /* UNIX */ close(mg->ses_sock); mg->ses_sock = 0; #endif } if (mcl_is_valid_sock(mg->priv_sock)) { #ifdef WIN32 closesocket(mg->priv_sock); mg->priv_sock = INVALID_SOCKET; #else /* UNIX */ close(mg->priv_sock); mg->priv_sock = 0; #endif } memset(mg, 0, sizeof(*mg)); } else if ((mclcb->mode & MODE_MCAST_TX) || ((mclcb->mode & MODE_UNI_TX) && layer == 0)) { /* * we are a source... * just close the sending socket (there is no session fd) */ mg = &(mclcb->mcgroup_tab[layer]); ASSERT(!mg->ses_sock); if (mcl_is_valid_sock(mg->priv_sock)) { #ifdef WIN32 closesocket(mg->priv_sock); mg->priv_sock = INVALID_SOCKET; #else /* UNIX */ close(mg->priv_sock); mg->priv_sock = 0; #endif } } if (mclcb->verbose == 2) { struct timeval time; time = mcl_get_tvtime(); PRINT_OUT((mcl_stdout, "\n%ld.%06ld\tdrop_layer %d\n", time.tv_sec, time.tv_usec, layer)) } TRACELVL(5, (mcl_stdout, "<- mcl_drop_this_layer: new nb_lvl=%d\n", mclcb->nb_level)) return 0; }