Implement IFB to replace the IMQ
authorNeutron Soutmun <neo.neutron@gmail.com>
Mon, 8 Jun 2009 11:29:43 +0000 (18:29 +0700)
committerNeutron Soutmun <neo.neutron@gmail.com>
Mon, 8 Jun 2009 11:29:43 +0000 (18:29 +0700)
  * IFB (Intermediate Functional Block) is more clearly work in the SMP.
  * Implement IFB on config, bandwidth task and support scripts.
  * IMQ is now deprecated. (not support).

src/rh-config.c
src/rh-config.h
src/rh-task-bandwidth.c
tools/bandwidth.sh.in
tools/firewall.sh.in

index ed99f0e..7747d4d 100755 (executable)
@@ -13,6 +13,9 @@
 #include "rahunasd.h"
 #include "rh-config.h"
 
+GList *interfaces_list = NULL;
+static unsigned long ifb_reserved = 0;
+
 enum lcfg_status rahunas_visitor(const char *key, void *data, size_t size, 
                                  void *user_data) {
   char *value = strndup(data, size);
@@ -345,3 +348,135 @@ int cleanup_mainserver_config(struct rahunas_main_config *config)
 
   return 0;
 }
+
+
+GList *append_interface (GList *inf, 
+                         const char *inf_name)
+{
+  GList *runner = NULL;
+  struct interfaces *iface = NULL;
+  struct interfaces *item  = NULL;
+  int    ifb_ifno;
+
+  if (!inf_name)
+    return inf;
+
+  item = (struct interfaces *) malloc (sizeof (struct interfaces));
+  if (!item)
+    return inf;
+
+  runner = g_list_first (inf);
+  while (runner != NULL)
+    {
+      iface = (struct interfaces *)runner->data;
+      if (strncmp(iface->dev_internal, inf_name, strlen(inf_name)) == 0)
+        {
+          // Already in the list
+          (iface->hit)++;
+          free(item); 
+          return inf;
+        }
+      runner = g_list_next (runner);
+    }
+
+done:
+  ifb_ifno = ifb_interface_reserve ();
+  if (ifb_ifno < 0)
+    {
+      free (item);
+      return inf;
+    }
+
+  strncpy(item->dev_internal, inf_name, 32);
+  sprintf(item->dev_ifb, "ifb%d", ifb_ifno);
+  item->init = 0;
+  item->hit  = 1;
+  DP ("Interface append: %s, %s", item->dev_internal, item->dev_ifb);
+  
+  return g_list_append (inf, item);
+}
+
+GList *remove_interface (GList *inf,
+                         const char *inf_name)
+{
+  GList *runner = NULL;
+  struct interfaces *iface = NULL;
+  int    ifb_ifno;
+
+  if (!inf_name || !inf)
+    return inf;
+
+  runner = g_list_first (inf);
+  while (runner != NULL)
+    {
+      iface = (struct interfaces *)runner->data;
+      if (strncmp (iface->dev_internal, inf_name, strlen (inf_name)) == 0)
+        {
+          iface->hit--;
+          if (iface->hit <=0 )
+            {
+              sscanf (iface->dev_ifb, "ifb%d", &ifb_ifno);
+              ifb_interface_release (ifb_ifno);
+              if (iface)
+                free (iface);
+
+              return g_list_delete_link (inf, runner);
+            }
+        }
+
+      runner = g_list_next (runner);
+    }
+
+  return inf;
+}
+
+
+struct interfaces *get_interface (GList *inf,
+                                  const char *inf_name)
+{
+  GList *runner = NULL;
+  struct interfaces *iface = NULL;
+  
+  if (!inf_name || !inf)
+    return NULL;
+
+  runner = g_list_first (inf);
+
+  while (runner != NULL)
+    {
+      iface = (struct interfaces *) runner->data;
+      if (strncmp (iface->dev_internal, inf_name, strlen (inf_name)) == 0)
+        return iface;
+
+      runner = g_list_next (runner);
+    } 
+
+  return NULL;
+}
+
+int ifb_interface_reserve (void)
+{
+  int i;
+  unsigned long mask = 1;
+
+  for (i=0; i < MAX_IFB_IFACE; i++)
+    {
+      mask <<= i;
+      if (!(ifb_reserved & mask))
+        {
+          ifb_reserved |= mask;
+          return i;
+        } 
+    }
+
+  return -1;
+}
+
+void ifb_interface_release (int ifno)
+{
+  unsigned long mask = 1;
+
+  mask <<= ifno;
+  mask = ~mask;
+  ifb_reserved &= mask;
+}
index 53ccfd3..f2fa4ef 100644 (file)
 #define XMLSERVICE_PORT 80
 #define XMLSERVICE_URL  "/rahunas_service/xmlrpc_service.php"
 
+#define MAX_IFB_IFACE   64
+
 #define CONFIG_FILE RAHUNAS_CONF_DIR "rahunas.conf"
 #define DEFAULT_PID RAHUNAS_RUN_DIR "rahunasd.pid"
 #define DB_NAME "rahunas"
 
+struct interfaces {
+  char dev_internal[32];
+  char dev_ifb[32];
+  int  hit;
+  int  init;
+};
+
 struct rahunas_main_config {
   char *conf_dir;
   char *log_file;
@@ -39,6 +48,7 @@ struct rahunas_vserver_config {
   int  init_flag;
   char *dev_external;
   char *dev_internal;
+  struct interfaces *iface;
   char *vlan;
   char *vlan_raw_dev_external;
   char *vlan_raw_dev_internal;
@@ -90,6 +100,8 @@ enum vserver_config_init_flag {
   VS_DONE
 };
 
+extern GList *interfaces_list;
+
 int get_config(const char *cfg_file, union rahunas_config *config);
 int get_value(const char *cfg_file, const char *key, void **data, size_t *len);
 int get_vservers_config(const char *conf_dir, struct main_server *server);
@@ -97,4 +109,13 @@ int cleanup_vserver_config(struct rahunas_vserver_config *config);
 int cleanup_mainserver_config(struct rahunas_main_config *config);
 enum lcfg_status rahunas_visitor(const char *key, void *data, size_t size, 
                                  void *user_data);
+
+GList *append_interface (GList *inf, 
+                         const char *inf_name);
+GList *remove_interface (GList *inf,
+                         const char *inf_name);
+struct interfaces *get_interface (GList *inf,
+                                  const char *inf_name);
+int    ifb_interface_reserve (void);
+void    ifb_interface_release (int ifno);
 #endif // __RH_CONFIG_H 
index 5de9ccb..c400254 100644 (file)
@@ -18,8 +18,6 @@
 #include "rh-task-memset.h"
 #include "rh-utils.h"
 
-
-
 #define BANDWIDTH_WRAPPER "/etc/rahunas/bandwidth.sh"
 
 static unsigned short slot_flags[MAX_SLOT_PAGE] = {1};
@@ -135,35 +133,46 @@ int bandwidth_exec(struct vserver *vs, char *const args[])
   return ret;
 }
 
-int bandwidth_start(void)
+int bandwidth_start(struct vserver *vs)
 {
-  char *args[3];
+  char *args[5];
+  struct interfaces *iface = vs->vserver_config->iface;
+  int  ret;
 
   DP("Bandwidth: start");
 
   args[0] = BANDWIDTH_WRAPPER;
   args[1] = "start";
-  args[2] = (char *) 0;
+  args[2] = iface->dev_internal;
+  args[3] = iface->dev_ifb;
+  args[4] = (char *) 0;
 
-  return bandwidth_exec(NULL, args);
+  ret = bandwidth_exec(vs, args);
+  return ret; 
 }
 
-int bandwidth_stop(void)
+int bandwidth_stop(struct vserver *vs)
 {
-  char *args[3];
+  char *args[5];
+  struct interfaces *iface = vs->vserver_config->iface;
+  int  ret;
 
   DP("Bandwidth: stop");
 
   args[0] = BANDWIDTH_WRAPPER;
   args[1] = "stop";
-  args[2] = (char *) 0;
+  args[2] = iface->dev_internal;
+  args[3] = iface->dev_ifb;
+  args[4] = (char *) 0;
 
-  return bandwidth_exec(NULL, args);
+  ret = bandwidth_exec(vs, args);
+  return ret; 
 }
 
 int bandwidth_add(struct vserver *vs, struct bandwidth_req *bw_req)
 {
-  char *args[7];
+  char *args[9];
+  struct interfaces *iface = vs->vserver_config->iface;
 
   DP("Bandwidth: request %s %s %s %s", bw_req->slot_id, 
      bw_req->ip, bw_req->bandwidth_max_down, bw_req->bandwidth_max_up);
@@ -174,21 +183,26 @@ int bandwidth_add(struct vserver *vs, struct bandwidth_req *bw_req)
   args[3] = bw_req->ip;
   args[4] = bw_req->bandwidth_max_down;
   args[5] = bw_req->bandwidth_max_up;
-  args[6] = (char *) 0;
+  args[6] = iface->dev_internal;
+  args[7] = iface->dev_ifb;
+  args[8] = (char *) 0;
 
   return bandwidth_exec(vs, args);
 }
 
 int bandwidth_del(struct vserver *vs, struct bandwidth_req *bw_req)
 {
-  char *args[4];
+  char *args[6];
+  struct interfaces *iface = vs->vserver_config->iface;
 
   DP("Bandwidth: request %s", bw_req->slot_id);
 
   args[0] = BANDWIDTH_WRAPPER;
   args[1] = "del";
   args[2] = bw_req->slot_id;
-  args[3] = (char *) 0;
+  args[3] = iface->dev_internal;
+  args[4] = iface->dev_ifb;
+  args[5] = (char *) 0;
 
   return bandwidth_exec(vs, args);
 }
@@ -196,25 +210,53 @@ int bandwidth_del(struct vserver *vs, struct bandwidth_req *bw_req)
 /* Start service task */
 static int startservice (void)
 {
-  return bandwidth_start();
+  /* Do nothing */
 }
 
 /* Stop service task */
 static int stopservice (void)
 {
-  return bandwidth_stop();
+  /* Do nothing */
 }
 
 /* Initialize */
 static void init (struct vserver *vs)
 {
-  /* Do nothing */
+  struct interfaces *iface = NULL;
+  if (!vs)
+    return;
+
+  if (vs->vserver_config->init_flag == VS_RELOAD)
+    return;
+
+  interfaces_list = append_interface (interfaces_list, 
+                                      vs->vserver_config->dev_internal);
+  vs->vserver_config->iface = get_interface (interfaces_list, 
+                                             vs->vserver_config->dev_internal);
+  iface = vs->vserver_config->iface;
+  if (!iface->init)
+    if (bandwidth_start(vs) >= 0)
+      iface->init = 1;
 }
 
 /* Cleanup */
 static int cleanup (struct vserver *vs)
 {
-  /* Do nothing */
+  struct interfaces *iface = NULL;
+  if (!vs)
+    return;
+
+  if (vs->vserver_config->init_flag == VS_RELOAD)
+    return;
+
+  iface = vs->vserver_config->iface;
+  DP ("Bandwidth Cleanup: init=%d, hit=%d", iface->init, iface->hit);
+  if (iface->init && iface->hit <= 1)
+    if (bandwidth_stop(vs) >= 0)
+      iface->init = 0;
+
+  interfaces_list = remove_interface (interfaces_list, 
+                                      vs->vserver_config->dev_internal);
 }
 
 /* Start session task */
index 13fda16..6904886 100755 (executable)
@@ -8,6 +8,7 @@ exec_prefix=@exec_prefix@
 
 TC=/sbin/tc
 IP=/sbin/ip
+IFCONFIG=/sbin/ifconfig
 
 INIT=@sysconfdir@/default/rahunas
 RUN=@localstatedir@/run/rahunas-bandwidth
@@ -15,9 +16,6 @@ RUN=@localstatedir@/run/rahunas-bandwidth
 # Interface Speed (Kbit), Default: 100 Mbps
 INTERFACE_SPEED=102400
 
-SHAPING_DOWN_INF=imq0
-SHAPING_UP_INF=imq1
-
 INTERFACE_ID=9999
 BITTORRENT_ID=9998
 
@@ -29,6 +27,9 @@ test -f $INIT || exit 0
 test "$RUN_DAEMON" = "yes" || exit 0
 test -f $RAHUNAS_CONFIG || exit 1
 
+SHAPING_DOWN_INF=""
+SHAPING_UP_INF=""
+
 set -e
 
 get_config_value () {
@@ -55,17 +56,32 @@ interface_setting () {
         htb rate ${INTERFACE_SPEED}Kbit
 
       # Interface Uplink
+      $IFCONFIG $SHAPING_UP_INF up
       $TC qdisc add dev $SHAPING_UP_INF root handle 2: htb \
         default ${INTERFACE_ID} 
       $TC class add dev $SHAPING_UP_INF parent 2: classid 2:${INTERFACE_ID} \
         htb rate ${INTERFACE_SPEED}Kbit
+
+      # Redirect incoming traffic to IFB
+      $TC qdisc add dev $SHAPING_DOWN_INF ingress
+      $TC filter add dev $SHAPING_DOWN_INF parent ffff: protocol ip prio 2 u32 \
+        match u32 0 0 flowid 1:${INTERFACE_ID} \
+        action mirred egress redirect dev $SHAPING_UP_INF
       ;;
   stop)
+      # Redirect incoming traffic to IFB
+      $TC filter del dev $SHAPING_DOWN_INF parent ffff: protocol ip prio 2 u32 \
+        match u32 0 0 flowid 1:${INTERFACE_ID} \
+        action mirred egress redirect dev $SHAPING_UP_INF
+      $TC qdisc del dev $SHAPING_DOWN_INF ingress
+
       # Interface Downlink
       $TC qdisc del dev $SHAPING_DOWN_INF root
 
       # Interface Uplink
       $TC qdisc del dev $SHAPING_UP_INF root
+
+      $IFCONFIG $SHAPING_UP_INF down
       ;;
   esac
 }
@@ -113,16 +129,20 @@ stop () {
 }
 
 usage_add() {
-  echo "Usage: $1 add ID IP DOWNSPEED UPSPEED"
+  echo "Usage: $1 add ID IP DOWNSPEED UPSPEED DOWN_IF UP_IF"
   echo "         ID - ID number from 1 to 9900"
   echo "         IP - IPv4 Address"
   echo "  DOWNSPEED - Download speed (bits/s)"
   echo "    UPSPEED - Upload speed (bits/s)"
+  echo "    DOWN_IF - Downstream interface (eth0, eth1, vlan1, ...)"
+  echo "      UP_IF - Upstream interface (ifb0, ifb1, ...)"
 }
 
 usage_del() {
-  echo "Usage: $1 del ID"
+  echo "Usage: $1 del ID DOWN_IF UP_IF"
   echo "         ID - ID number from 1 to 9900"
+  echo "    DOWN_IF - Downstream interface (eth0, eth1, vlan1, ...)"
+  echo "      UP_IF - Upstream interface (ifb0, ifb1, ...)"
 }
 
 usage() {
@@ -174,22 +194,43 @@ N=@sysconfdir@/rahunas/bandwidth.sh
 
 case "$1" in
   start)
-    start || true 
+    if [ -z "$2" ] || [ -z "$3" ]; then
+      MESSAGE="NOT COMPLETED"  
+    else
+      SHAPING_DOWN_INF=$2
+      SHAPING_UP_INF=$3
+      RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
+      start || true 
+    fi
+
     test -n "$MESSAGE" || MESSAGE="NOT COMPLETED"
     echo $MESSAGE
     ;;
   stop)
-    stop || true
+    if [ -z "$2" ] || [ -z "$3" ]; then
+      MESSAGE="NOT COMPLETED"  
+    else
+      SHAPING_DOWN_INF=$2
+      SHAPING_UP_INF=$3
+      RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
+      stop || true
+    fi
+
     test -n "$MESSAGE" || MESSAGE="NOT COMPLETED"
     echo $MESSAGE
 
     ;;
   restart)
-    stop || true
-    test -n "$MESSAGE" || MESSAGE="NOT COMPLETED"
-    echo $MESSAGE
+    if [ -z "$2" ] || [ -z "$3" ]; then
+      MESSAGE="NOT COMPLETED"  
+    else
+      SHAPING_DOWN_INF=$2
+      SHAPING_UP_INF=$3
+      RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
+      stop || true
+      start || true
+    fi
 
-    start || true
     test -n "$MESSAGE" || MESSAGE="NOT COMPLETED"
     echo $MESSAGE
 
@@ -200,13 +241,20 @@ case "$1" in
       exit 3
     fi
 
-    if [ $# != 5 ]; then
+    if [ $# != 7 ]; then
       echo "Error: too few arguments"
       usage_add $N
       exit 1
     fi
 
-    bw_add $2 $3 $4 $5 || true
+    if [ -z "$6" ] || [ -z "$7" ]; then
+      MESSAGE="NOT COMPLETED"  
+    else
+      SHAPING_DOWN_INF=$6
+      SHAPING_UP_INF=$7
+      RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
+      bw_add $2 $3 $4 $5 || true
+    fi
     echo $MESSAGE
     ;;
   del)
@@ -215,13 +263,19 @@ case "$1" in
       exit 3
     fi
 
-    if [ $# != 2 ]; then
+    if [ $# != 4 ]; then
       echo "Error: too few arguments"
       usage_del $N
       exit 1
     fi
-
-    bw_del $2 || true
+    if [ -z "$3" ] || [ -z "$4" ]; then
+      MESSAGE="NOT COMPLETED"  
+    else
+      SHAPING_DOWN_INF=$3
+      SHAPING_UP_INF=$4
+      RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
+      bw_del $2 || true
+    fi
     echo $MESSAGE
     ;;
   *)
index 666d42e..612808e 100755 (executable)
@@ -584,14 +584,6 @@ rules () {
     $IPTABLES -t mangle -I $CHAIN_MANGLE_PREROUTING -d $excluded \
       -j CONNMARK --set-mark 2
   done 
-  
-  ##
-  # Bandwidth Shaping: IMQ - Intermediate Queueing Device
-  ##
-  if [ "$BANDWIDTH_SHAPE" = "yes" ]; then
-    $IPTABLES -t mangle -I $CHAIN_MANGLE_POSTROUTING $DEV_OUT_PARAM $DEV_INTERNAL -j IMQ --todev 0
-    $IPTABLES -t mangle -I $CHAIN_MANGLE_PREROUTING $DEV_IN_PARAM $DEV_INTERNAL -j IMQ --todev 1
-  fi
 
   ##
   # Return to main chains