tizen 2.3 release
[kernel/api/system-resource.git] / src / network / ktgrabber-restriction.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  */
19
20
21 /*
22  * @file net-restriction.c
23  *
24  * @desc Kernel communication routins for bloking network traffic based on
25  *      netlink protocol.
26  *
27  * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
28  *
29  */
30
31 #include <errno.h>
32 #include <glib.h>
33 #include <sys/socket.h>         /*should be before linux/netlink */
34 #include <sys/types.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <linux/netlink.h>      /*nlmsghdr */
39 #include <linux/pkt_sched.h>
40 #include <linux/rtnetlink.h>
41
42 #include "resourced.h"
43 #include "generic-netlink.h"
44 #include "iface.h"
45 #include "macro.h"
46 #include "netlink-restriction.h"
47 #include "nl-helper.h"
48 #include "trace.h"
49
50 static const char kind_name[] = "htb";
51
52 struct rst_context {
53         int sock;
54         int family_id;
55         pid_t pid;
56 };
57
58 struct nf_arg {
59         u_int32_t classid;
60         enum traffic_restriction_type restriction_type;
61         resourced_iface_type iftype;
62         int error;
63         struct rst_context *context;
64         int send_limit;
65         int rcv_limit;
66         int snd_warning_threshold;
67         int rcv_warning_threshold;
68 };
69
70 static struct rst_context context;
71
72 #if 0
73
74 static int send_nl_msg(int sock, pid_t pid, const rt_param *arg)
75 {
76         struct sockaddr_nl nladdr = { 0, };
77         struct iovec iov = { 0, };
78         struct msghdr msg = { 0, };
79         int ret = 0;
80
81         if (!arg)
82                 return -1;
83
84         iov.iov_base = (void *)(&(arg->n));
85         iov.iov_len = arg->n.nlmsg_len;
86
87         msg.msg_name = &nladdr;
88         msg.msg_namelen = sizeof(nladdr);
89         msg.msg_iov = &iov;
90         msg.msg_iovlen = 1;
91
92         nladdr.nl_family = AF_NETLINK;
93         nladdr.nl_pid = pid;
94         nladdr.nl_groups = 0;
95
96         ret = sendmsg(sock, &msg, 0);
97         if (ret < 0)
98                 return -errno;
99         return ret;
100 }
101
102 static int fill_netlink_argument(u_int32_t classid, u_int32_t *seq,
103                                  int command, int flags, rt_param *argument)
104 {
105         if (!argument)
106                 return -1;
107
108         memset(argument, 0, sizeof(rt_param));
109
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 */
119         return 0;
120 }
121
122 static void add_htb_param(rt_param *arg)
123 {
124         struct tc_htb_glob opt = { 0, };
125         struct rtattr *tail = 0;
126
127         opt.rate2quantum = 1;
128         opt.version = 3;
129
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;
136 }
137
138 static u_int32_t strict_classid(u_int32_t classid)
139 {
140         return classid & 0xFFFF0000;    /* number: in TC termins */
141 }
142
143 static int add_root_qdisc(int sock, u_int32_t *seq, pid_t pid)
144 {
145         rt_param arg;
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);
149 }
150
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)
153 {
154         rt_param arg;
155         fill_netlink_argument(classid, seq, RTM_NEWQDISC,
156                               NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
157         add_htb_param(&arg);
158         arg.t.tcm_handle = strict_classid(classid);
159         return send_nl_msg(sock, pid, &arg);
160 }
161
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,
164                      int rate_limit)
165 {
166         rt_param arg;
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);
170 }
171
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)
174 {
175         rt_param arg;
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);
179 }
180 #endif
181
182 static resourced_ret_c send_nf_restriction(int ifindex, resourced_iface_type iftype,
183         void *data)
184 {
185         struct nf_arg *nfarg = data;
186
187         if (!nfarg)
188                 return RESOURCED_ERROR_FAIL;
189
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 "\
194                 " rcv %d, snd %d",
195                 nfarg->classid, ifindex, iftype, nfarg->restriction_type,
196                 nfarg->rcv_limit, nfarg->send_limit);
197
198                 if (send_restriction(nfarg->context->sock, nfarg->context->pid,
199                         nfarg->context->family_id, nfarg->classid,
200                         ifindex, nfarg->restriction_type,
201                         nfarg->send_limit,
202                         nfarg->rcv_limit,
203                         nfarg->snd_warning_threshold,
204                         nfarg->rcv_warning_threshold) < 0) {
205                         _E("Failed to sent restriction to kernel");
206                         nfarg->error = errno;
207                 }
208         }
209
210         return RESOURCED_ERROR_NONE;
211 }
212
213 static gboolean send_each_restriction(gpointer key, gpointer value, gpointer data)
214 {
215         return send_nf_restriction((int)key,
216                 *(resourced_iface_type *)(value), data) == RESOURCED_ERROR_NONE ?
217                 FALSE /* Glib continue iteration */ : TRUE;
218 }
219
220 static resourced_ret_c init_restriction_context(void)
221 {
222         if (context.sock)
223                 return 0;
224
225         context.sock = create_netlink(NETLINK_GENERIC, 0);
226
227         if (context.sock < 0) {
228                 _D("Failed to create and bind netlink socket.");
229                 return RESOURCED_ERROR_FAIL;
230         }
231
232         context.family_id = get_family_id(context.sock,
233                 context.pid, "TRAF_STAT");
234
235         if (context.family_id < 0) {
236                 _D("Failed to get family id.");
237                 return RESOURCED_ERROR_FAIL;
238         }
239
240         context.pid = getpid(); /* for user/kernel space communication */
241         return RESOURCED_ERROR_NONE;
242 }
243
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)
250 {
251         struct nf_arg nfarg;
252
253         /* initialize context variables */
254         if (init_restriction_context() < 0)
255                 return RESOURCED_ERROR_FAIL;
256
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);
270         }
271
272         nfarg.context = &context;
273         nfarg.error = 0;
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;
281
282         /* apply a given type of restriction for each network
283            interface of the given network type */
284         init_iftype();
285         for_each_ifindex((ifindex_iterator)send_each_restriction, NULL, &nfarg);
286         return nfarg.error;
287 }