inet: Refactor rtnl functions in 6to4.c
[framework/connectivity/connman.git] / src / 6to4.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2011  Nokia Corporation. All rights reserved.
6  *  Copyright (C) Alexey Kuznetsov et al. from iproute2 package.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <net/if.h>
34 #include <linux/ip.h>
35 #include <linux/if_tunnel.h>
36 #include <linux/netlink.h>
37 #include <linux/rtnetlink.h>
38 #include <sys/ioctl.h>
39 #include <unistd.h>
40
41 #include "connman.h"
42 #include <connman/log.h>
43 #include <connman/ipconfig.h>
44 #include "gweb/gweb.h"
45
46 static int tunnel_created;
47 static int tunnel_pending;
48 static char *tunnel_ip_address;
49 static GWeb *web;
50 static guint web_request_id;
51
52 #define STATUS_URL "http://ipv6.connman.net/online/status.html"
53
54 #ifndef IP_DF
55 #define IP_DF           0x4000          /* Flag: "Don't Fragment"       */
56 #endif
57
58 static int tunnel_create(struct in_addr *addr)
59 {
60         struct ip_tunnel_parm p;
61         struct ifreq ifr;
62         int fd = -1;
63         int ret;
64
65         /* ip tunnel add tun6to4 mode sit remote any local 1.2.3.4 ttl 64 */
66
67         memset(&p, 0, sizeof(struct ip_tunnel_parm));
68         memset(&ifr, 0, sizeof(struct ifreq));
69
70         p.iph.version = 4;
71         p.iph.ihl = 5;
72         p.iph.frag_off = htons(IP_DF);
73         p.iph.protocol = IPPROTO_IPV6;
74         p.iph.saddr = addr->s_addr;
75         p.iph.ttl = 64;
76         strncpy(p.name, "tun6to4", IFNAMSIZ);
77
78         strncpy(ifr.ifr_name, "sit0", IFNAMSIZ);
79         ifr.ifr_ifru.ifru_data = (void *)&p;
80         fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
81         ret = ioctl(fd, SIOCADDTUNNEL, &ifr);
82         if (ret)
83                 connman_error("add tunnel %s failed: %s", ifr.ifr_name,
84                                                         strerror(errno));
85         close(fd);
86
87         return ret;
88 }
89
90 static void tunnel_destroy()
91 {
92         struct ip_tunnel_parm p;
93         struct ifreq ifr;
94         int fd = -1;
95         int ret;
96
97         if (tunnel_created == 0)
98                 return;
99
100         /* ip tunnel del tun6to4 */
101
102         memset(&p, 0, sizeof(struct ip_tunnel_parm));
103         memset(&ifr, 0, sizeof(struct ifreq));
104
105         p.iph.version = 4;
106         p.iph.ihl = 5;
107         p.iph.protocol = IPPROTO_IPV6;
108         strncpy(p.name, "tun6to4", IFNAMSIZ);
109
110         strncpy(ifr.ifr_name, "tun6to4", IFNAMSIZ);
111         ifr.ifr_ifru.ifru_data = (void *)&p;
112         fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
113         if (fd < 0) {
114                 connman_error("socket failed: %s", strerror(errno));
115                 return;
116         }
117
118         ret = ioctl(fd, SIOCDELTUNNEL, &ifr);
119         if (ret)
120                 connman_error("del tunnel %s failed: %s", ifr.ifr_name,
121                                                         strerror(errno));
122         else
123                 tunnel_created = 0;
124
125         tunnel_pending = 0;
126         close(fd);
127
128         g_free(tunnel_ip_address);
129         tunnel_ip_address = NULL;
130 }
131
132 static int tunnel_add_route()
133 {
134         struct __connman_inet_rtnl_handle rth;
135         struct in6_addr addr6;
136         int index;
137         int ret = 0;
138
139         /* ip -6 route add ::/0 via ::192.88.99.1 dev tun6to4 metric 1 */
140
141         index = if_nametoindex("tun6to4");
142         if (index == 0) {
143                 DBG("Can not find device tun6to4");
144                 return -1;
145         }
146
147         memset(&rth, 0, sizeof(rth));
148
149         rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
150         rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
151         rth.req.n.nlmsg_type = RTM_NEWROUTE;
152         rth.req.u.r.rt.rtm_family = AF_INET6;
153         rth.req.u.r.rt.rtm_table = RT_TABLE_MAIN;
154         rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT;
155         rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE;
156         rth.req.u.r.rt.rtm_type = RTN_UNICAST;
157         rth.req.u.r.rt.rtm_dst_len = 0;
158
159         inet_pton(AF_INET6, "::192.88.99.1", &addr6);
160
161         __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY,
162                                         &addr6.s6_addr, 16);
163         __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), RTA_OIF,
164                                         index);
165         __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
166                                         RTA_PRIORITY, 1);
167
168         ret = __connman_inet_rtnl_open(&rth);
169         if (ret < 0)
170                 goto done;
171
172         ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
173
174 done:
175         __connman_inet_rtnl_close(&rth);
176         return ret;
177 }
178
179 static int tunnel_set_addr(unsigned int a, unsigned int b,
180                         unsigned int c, unsigned int d)
181 {
182         struct __connman_inet_rtnl_handle rth;
183         struct in6_addr addr6;
184         char *ip6addr;
185         int ret;
186
187         /* ip -6 addr add dev tun6to4 2002:0102:0304::1/64 */
188
189         memset(&rth, 0, sizeof(rth));
190
191         rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
192         rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
193         rth.req.n.nlmsg_type = RTM_NEWADDR;
194         rth.req.u.i.ifa.ifa_family = AF_INET6;
195         rth.req.u.i.ifa.ifa_prefixlen = 64;
196         rth.req.u.i.ifa.ifa_index = if_nametoindex("tun6to4");
197         if (rth.req.u.i.ifa.ifa_index == 0) {
198                 connman_error("Can not find device tun6to4");
199                 ret = -1;
200                 goto done;
201         }
202
203         ip6addr = g_strdup_printf("2002:%02x%02x:%02x%02x::1", a, b, c, d);
204         inet_pton(AF_INET6, ip6addr, &addr6);
205         DBG("ipv6 address %s", ip6addr);
206         g_free(ip6addr);
207
208         __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_LOCAL,
209                                         &addr6.s6_addr, 16);
210         __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_ADDRESS,
211                                         &addr6.s6_addr, 16);
212
213         ret = __connman_inet_rtnl_open(&rth);
214         if (ret < 0)
215                 goto done;
216
217         ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
218
219 done:
220         __connman_inet_rtnl_close(&rth);
221         return ret;
222 }
223
224 static gboolean unref_web(gpointer user_data)
225 {
226         g_web_unref(web);
227         return FALSE;
228 }
229
230 static gboolean web_result(GWebResult *result, gpointer user_data)
231 {
232         guint16 status;
233
234         if (web_request_id == 0)
235                 return FALSE;
236
237         status = g_web_result_get_status(result);
238
239         DBG("status %u", status);
240
241         if (status >= 400 && status < 500)
242                 tunnel_destroy();
243         else
244                 tunnel_pending = 0;
245
246         web_request_id = 0;
247
248         g_timeout_add_seconds(1, unref_web, NULL);
249
250         return FALSE;
251 }
252
253 static int init_6to4(struct in_addr *ip4addr)
254 {
255         unsigned int a, b, c, d;
256         int ret, if_index;
257         in_addr_t addr;
258
259         DBG("");
260
261         addr = ntohl(ip4addr->s_addr);
262
263         a = (addr & 0xff000000) >> 24;
264         b = (addr & 0x00ff0000) >> 16;
265         c = (addr & 0x0000ff00) >> 8;
266         d = addr & 0x000000ff;
267
268         ret = tunnel_create(ip4addr);
269         if (ret)
270                 return -1;
271
272         tunnel_created = 1;
273
274         ret = connman_inet_setup_tunnel("tun6to4", 1472);
275         if (ret)
276                 goto error;
277
278         ret = tunnel_set_addr(a, b, c, d);
279         if (ret)
280                 goto error;
281
282         ret = tunnel_add_route();
283         if (ret)
284                 goto error;
285
286         if_index = connman_inet_ifindex("tun6to4");
287         if (if_index < 0)
288                 goto error;
289
290         /* We try to verify that connectivity through tunnel works ok.
291          */
292         web = g_web_new(if_index);
293         if (web == NULL)
294                 goto error;
295
296         g_web_set_accept(web, NULL);
297         g_web_set_user_agent(web, "ConnMan/%s", VERSION);
298         g_web_set_close_connection(web, TRUE);
299
300         web_request_id = g_web_request_get(web, STATUS_URL, web_result, NULL);
301
302         return 0;
303
304 error:
305         tunnel_destroy();
306         return -1;
307 }
308
309 static void receive_rs_reply(struct nd_router_advert *reply,
310                         unsigned int length, void *user_data)
311 {
312         char *address = user_data;
313         struct in_addr ip4addr;
314
315         DBG("reply %p len %d address %s", reply, length, address);
316
317         /* We try to create tunnel if autoconfiguration did not work i.e.,
318          * we did not receive any reply to router solicitation message.
319          */
320         if (reply == NULL && inet_aton(address, &ip4addr) != 0)
321                 init_6to4(&ip4addr);
322
323         g_free(address);
324 }
325
326 int __connman_6to4_probe(struct connman_service *service)
327 {
328         struct connman_ipconfig *ip4config, *ip6config;
329         enum connman_ipconfig_method method;
330         unsigned int a, b;
331         struct in_addr ip4addr;
332         in_addr_t addr;
333         const char *address;
334         char *ip_address;
335         int index;
336
337         DBG("service %p", service);
338
339         if (tunnel_created || tunnel_pending)
340                 return 0;
341
342         if (service == NULL)
343                 return -1;
344
345         ip4config = __connman_service_get_ip4config(service);
346         if (ip4config == NULL)
347                 return -1;
348
349         ip6config = __connman_service_get_ip6config(service);
350         if (ip6config == NULL)
351                 return -1;
352
353         method = __connman_ipconfig_get_method(ip6config);
354         if (method != CONNMAN_IPCONFIG_METHOD_AUTO)
355                 return -1;
356
357         address = __connman_ipconfig_get_local(ip4config);
358         if (address == NULL)
359                 return -1;
360
361         if (inet_aton(address, &ip4addr) == 0)
362                 return -1;
363
364         addr = ntohl(ip4addr.s_addr);
365
366         a = (addr & 0xff000000) >> 24;
367         b = (addr & 0x00ff0000) >> 16;
368
369         /* 6to4 tunnel is only usable if we have a public IPv4 address */
370         if (a == 10 || (a == 192 && b == 168) ||
371                                         (a == 172 && (b >= 16 && b <= 31)))
372                 return -1;
373
374         index = __connman_ipconfig_get_index(ip4config);
375         ip_address = g_strdup(address);
376         tunnel_pending = 1;
377
378         g_free(tunnel_ip_address);
379         tunnel_ip_address = g_strdup(address);
380
381         return __connman_inet_ipv6_send_rs(index, 2, receive_rs_reply,
382                                                         ip_address);
383 }
384
385 void __connman_6to4_remove(struct connman_ipconfig *ip4config)
386 {
387         const char *address;
388
389         DBG("tunnel ip address %s", tunnel_ip_address);
390
391         if (ip4config == NULL)
392                 return;
393
394         address = __connman_ipconfig_get_local(ip4config);
395         if (address == NULL)
396                 return;
397
398         if (g_strcmp0(address, tunnel_ip_address) != 0)
399                 return;
400
401         if (tunnel_created)
402                 tunnel_destroy();
403 }
404
405 int __connman_6to4_check(struct connman_ipconfig *ip4config)
406 {
407         const char *address;
408
409         if (ip4config == NULL || tunnel_created == 0 ||
410                                         tunnel_pending == 1)
411                 return -1;
412
413         DBG("tunnel ip address %s", tunnel_ip_address);
414
415         address = __connman_ipconfig_get_local(ip4config);
416         if (address == NULL)
417                 return -1;
418
419         if (g_strcmp0(address, tunnel_ip_address) == 0)
420                 return 1;
421
422         return 0;
423 }