4 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
22 * @file net-restriction.c
24 * @desc Kernel communication routins for bloking network traffic based on
27 * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
33 #include <sys/socket.h> /*should be before linux/netlink */
34 #include <sys/types.h>
38 #include <linux/netlink.h> /*nlmsghdr */
39 #include <linux/pkt_sched.h>
40 #include <linux/rtnetlink.h>
42 #include "resourced.h"
43 #include "generic-netlink.h"
46 #include "netlink-restriction.h"
47 #include "nl-helper.h"
50 static const char kind_name[] = "htb";
60 enum traffic_restriction_type restriction_type;
61 resourced_iface_type iftype;
63 struct rst_context *context;
66 int snd_warning_threshold;
67 int rcv_warning_threshold;
70 static struct rst_context context;
74 static int send_nl_msg(int sock, pid_t pid, const rt_param *arg)
76 struct sockaddr_nl nladdr = { 0, };
77 struct iovec iov = { 0, };
78 struct msghdr msg = { 0, };
84 iov.iov_base = (void *)(&(arg->n));
85 iov.iov_len = arg->n.nlmsg_len;
87 msg.msg_name = &nladdr;
88 msg.msg_namelen = sizeof(nladdr);
92 nladdr.nl_family = AF_NETLINK;
96 ret = sendmsg(sock, &msg, 0);
102 static int fill_netlink_argument(u_int32_t classid, u_int32_t *seq,
103 int command, int flags, rt_param *argument)
108 memset(argument, 0, sizeof(rt_param));
110 argument->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
111 argument->n.nlmsg_flags = flags;
112 argument->n.nlmsg_type = command;
113 argument->n.nlmsg_seq = ++(*seq);
114 argument->t.tcm_family = AF_UNSPEC;
115 argument->t.tcm_handle = classid;
116 argument->t.tcm_parent = TC_H_ROOT;
117 argument->t.tcm_ifindex = 2; /*TODO: use iface by sysctl
118 termination, hardcoded eth0 */
122 static void add_htb_param(rt_param *arg)
124 struct tc_htb_glob opt = { 0, };
125 struct rtattr *tail = 0;
127 opt.rate2quantum = 1;
130 put_attr(arg, TCA_KIND, kind_name, sizeof(kind_name) + 1);
131 tail = NLMSG_TAIL(&arg->n);
132 put_attr(arg, TCA_OPTIONS, NULL, 0);
133 put_attr(arg, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt)));
134 /*I don't know why but it present in TC */
135 tail->rta_len = (void *)NLMSG_TAIL(&arg->n) - (void *)tail;
138 static u_int32_t strict_classid(u_int32_t classid)
140 return classid & 0xFFFF0000; /* number: in TC termins */
143 static int add_root_qdisc(int sock, u_int32_t *seq, pid_t pid)
146 fill_netlink_argument(0, seq, RTM_NEWQDISC,
147 NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
148 return send_nl_msg(sock, pid, &arg);
151 /*Create root queue discipline and create base queue discipline*/
152 static int add_qdisc(int sock, u_int32_t *seq, pid_t pid, u_int32_t classid)
155 fill_netlink_argument(classid, seq, RTM_NEWQDISC,
156 NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
158 arg.t.tcm_handle = strict_classid(classid);
159 return send_nl_msg(sock, pid, &arg);
162 /*At present we support only one type of class*/
163 static int add_class(int sock, u_int32_t *seq, pid_t pid, u_int32_t classid,
167 fill_netlink_argument(classid, seq, RTM_NEWTCLASS,
168 NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
169 return send_nl_msg(sock, pid, &arg);
172 /*At present we support only one type of filter by cgroup*/
173 static int add_filter(int sock, u_int32_t *seq, pid_t pid, u_int32_t classid)
176 fill_netlink_argument(classid, seq, RTM_NEWTFILTER,
177 NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
178 return send_nl_msg(sock, pid, &arg);
182 static resourced_ret_c send_nf_restriction(int ifindex, resourced_iface_type iftype,
185 struct nf_arg *nfarg = data;
188 return RESOURCED_ERROR_FAIL;
190 /* use netfilter (ktgrabber) approach */
191 if (nfarg->iftype == iftype) {
192 _D("Sending restriction to kernel:"\
193 " classid %d, ifindex %d, iftype %d, rest_type %d "\
195 nfarg->classid, ifindex, iftype, nfarg->restriction_type,
196 nfarg->rcv_limit, nfarg->send_limit);
198 if (send_restriction(nfarg->context->sock, nfarg->context->pid,
199 nfarg->context->family_id, nfarg->classid,
200 ifindex, nfarg->restriction_type,
203 nfarg->snd_warning_threshold,
204 nfarg->rcv_warning_threshold) < 0) {
205 _E("Failed to sent restriction to kernel");
206 nfarg->error = errno;
210 return RESOURCED_ERROR_NONE;
213 static gboolean send_each_restriction(gpointer key, gpointer value, gpointer data)
215 return send_nf_restriction((int)key,
216 *(resourced_iface_type *)(value), data) == RESOURCED_ERROR_NONE ?
217 FALSE /* Glib continue iteration */ : TRUE;
220 static resourced_ret_c init_restriction_context(void)
225 context.sock = create_netlink(NETLINK_GENERIC, 0);
227 if (context.sock < 0) {
228 _D("Failed to create and bind netlink socket.");
229 return RESOURCED_ERROR_FAIL;
232 context.family_id = get_family_id(context.sock,
233 context.pid, "TRAF_STAT");
235 if (context.family_id < 0) {
236 _D("Failed to get family id.");
237 return RESOURCED_ERROR_FAIL;
240 context.pid = getpid(); /* for user/kernel space communication */
241 return RESOURCED_ERROR_NONE;
244 int send_net_restriction(const enum traffic_restriction_type rst_type,
245 const u_int32_t classid,
246 const resourced_iface_type iftype,
247 const int send_limit, const int rcv_limit,
248 const int snd_warning_threshold,
249 const int rcv_warning_threshold)
253 /* initialize context variables */
254 if (init_restriction_context() < 0)
255 return RESOURCED_ERROR_FAIL;
257 /* emulate old behaviour, no iftype mean block all network interfaces */
258 if (iftype == RESOURCED_IFACE_UNKNOWN ||
259 iftype == RESOURCED_IFACE_ALL) {
260 _D("Sending restriction to kernel: classid %d, ifindex %d "
261 "iftype %d, restriction_type %d, rcv %d, snd %d\n",
262 classid, RESOURCED_ALL_IFINDEX, iftype, rst_type,
263 send_limit, rcv_limit);
264 return send_restriction(context.sock, context.pid,
265 context.family_id, classid,
266 RESOURCED_ALL_IFINDEX, rst_type,
267 send_limit, rcv_limit,
268 snd_warning_threshold,
269 rcv_warning_threshold);
272 nfarg.context = &context;
274 nfarg.restriction_type = rst_type;
275 nfarg.iftype = iftype;
276 nfarg.classid = classid;
277 nfarg.send_limit = send_limit;
278 nfarg.rcv_limit = rcv_limit;
279 nfarg.snd_warning_threshold = snd_warning_threshold;
280 nfarg.rcv_warning_threshold = rcv_warning_threshold;
282 /* apply a given type of restriction for each network
283 interface of the given network type */
285 for_each_ifindex((ifindex_iterator)send_each_restriction, NULL, &nfarg);