1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdlib.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <string.h>
21
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26
27#include <linux/netlink.h>
28#include <linux/rtnetlink.h>
29#include <linux/pkt_sched.h>
30
31#define LOG_TAG "ThrottleController"
32#include <cutils/log.h>
33
34
35#include "ThrottleController.h"
36#include "NetdConstants.h"
37
38extern "C" int system_nosh(const char *command);
39extern "C" int ifc_init(void);
40extern "C" int ifc_up(const char *name);
41extern "C" int ifc_down(const char *name);
42
43int ThrottleController::runTcCmd(const char *cmd) {
44    char *buffer;
45    size_t len = strnlen(cmd, 255);
46    int res;
47
48    if (len == 255) {
49        ALOGE("tc command too long");
50        errno = E2BIG;
51        return -1;
52    }
53
54    asprintf(&buffer, "%s %s", TC_PATH, cmd);
55    res = system_nosh(buffer);
56    free(buffer);
57    return res;
58}
59
60int ThrottleController::setInterfaceThrottle(const char *iface, int rxKbps, int txKbps) {
61    char cmd[512];
62    char ifn[65];
63    int rc;
64
65    memset(ifn, 0, sizeof(ifn));
66    strncpy(ifn, iface, sizeof(ifn)-1);
67
68    if (txKbps == -1) {
69        reset(ifn);
70        return 0;
71    }
72
73    /*
74     *
75     * Target interface configuration
76     *
77     */
78
79    /*
80     * Add root qdisc for the interface
81     */
82    sprintf(cmd, "qdisc add dev %s root handle 1: htb default 1 r2q 1000", ifn);
83    if (runTcCmd(cmd)) {
84        ALOGE("Failed to add root qdisc (%s)", strerror(errno));
85        goto fail;
86    }
87
88    /*
89     * Add our egress throttling class
90     */
91    sprintf(cmd, "class add dev %s parent 1: classid 1:1 htb rate %dkbit", ifn, txKbps);
92    if (runTcCmd(cmd)) {
93        ALOGE("Failed to add egress throttling class (%s)", strerror(errno));
94        goto fail;
95    }
96
97    /*
98     * Bring up the IFD device
99     */
100    ifc_init();
101    if (ifc_up("ifb0")) {
102        ALOGE("Failed to up ifb0 (%s)", strerror(errno));
103        goto fail;
104    }
105
106    /*
107     * Add root qdisc for IFD
108     */
109    sprintf(cmd, "qdisc add dev ifb0 root handle 1: htb default 1 r2q 1000");
110    if (runTcCmd(cmd)) {
111        ALOGE("Failed to add root ifb qdisc (%s)", strerror(errno));
112        goto fail;
113    }
114
115    /*
116     * Add our ingress throttling class
117     */
118    sprintf(cmd, "class add dev ifb0 parent 1: classid 1:1 htb rate %dkbit", rxKbps);
119    if (runTcCmd(cmd)) {
120        ALOGE("Failed to add ingress throttling class (%s)", strerror(errno));
121        goto fail;
122    }
123
124    /*
125     * Add ingress qdisc for pkt redirection
126     */
127    sprintf(cmd, "qdisc add dev %s ingress", ifn);
128    if (runTcCmd(cmd)) {
129        ALOGE("Failed to add ingress qdisc (%s)", strerror(errno));
130        goto fail;
131    }
132
133    /*
134     * Add filter to link <ifn> -> ifb0
135     */
136    sprintf(cmd, "filter add dev %s parent ffff: protocol ip prio 10 u32 match "
137            "u32 0 0 flowid 1:1 action mirred egress redirect dev ifb0", ifn);
138    if (runTcCmd(cmd)) {
139        ALOGE("Failed to add ifb filter (%s)", strerror(errno));
140        goto fail;
141    }
142
143    return 0;
144fail:
145    reset(ifn);
146    return -1;
147}
148
149void ThrottleController::reset(const char *iface) {
150    char cmd[128];
151
152    sprintf(cmd, "qdisc del dev %s root", iface);
153    runTcCmd(cmd);
154    sprintf(cmd, "qdisc del dev %s ingress", iface);
155    runTcCmd(cmd);
156
157    runTcCmd("qdisc del dev ifb0 root");
158}
159
160int ThrottleController::getInterfaceRxThrottle(const char *iface, int *rx) {
161    *rx = 0;
162    return 0;
163}
164
165int ThrottleController::getInterfaceTxThrottle(const char *iface, int *tx) {
166    *tx = 0;
167    return 0;
168}
169