Upgrade ofono to 1.2
[profile/ivi/ofono.git] / gatchat / ppp_net.c
1 /*
2  *
3  *  PPP library with GLib integration
4  *
5  *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <net/if.h>
31 #include <linux/if_tun.h>
32 #include <sys/ioctl.h>
33 #include <arpa/inet.h>
34
35 #include <glib.h>
36
37 #include "gatutil.h"
38 #include "gatppp.h"
39 #include "ppp.h"
40
41 #define MAX_PACKET 1500
42
43 struct ppp_net {
44         GAtPPP *ppp;
45         char *if_name;
46         GIOChannel *channel;
47         guint watch;
48         gint mtu;
49         struct ppp_header *ppp_packet;
50 };
51
52 gboolean ppp_net_set_mtu(struct ppp_net *net, guint16 mtu)
53 {
54         struct ifreq ifr;
55         int sk, err;
56
57         if (net == NULL || mtu > MAX_PACKET)
58                 return FALSE;
59
60         net->mtu = mtu;
61
62         sk = socket(AF_INET, SOCK_DGRAM, 0);
63         if (sk < 0)
64                 return FALSE;
65
66         memset(&ifr, 0, sizeof(ifr));
67         strncpy(ifr.ifr_name, net->if_name, sizeof(ifr.ifr_name));
68         ifr.ifr_mtu = mtu;
69
70         err = ioctl(sk, SIOCSIFMTU, (caddr_t) &ifr);
71
72         close(sk);
73
74         if (err < 0)
75                 return FALSE;
76
77         return TRUE;
78 }
79
80 void ppp_net_process_packet(struct ppp_net *net, const guint8 *packet,
81                                 gsize plen)
82 {
83         GIOStatus status;
84         gsize bytes_written;
85         guint16 len;
86
87         if (plen < 4)
88                 return;
89
90         /* find the length of the packet to transmit */
91         len = get_host_short(&packet[2]);
92         status = g_io_channel_write_chars(net->channel, (gchar *) packet,
93                                                 MIN(len, plen),
94                                                 &bytes_written, NULL);
95
96         if (status != G_IO_STATUS_NORMAL)
97                 return;
98 }
99
100 /*
101  * packets received by the tun interface need to be written to
102  * the modem.  So, just read a packet, write out to the modem
103  */
104 static gboolean ppp_net_callback(GIOChannel *channel, GIOCondition cond,
105                                 gpointer userdata)
106 {
107         struct ppp_net *net = (struct ppp_net *) userdata;
108         GIOStatus status;
109         gsize bytes_read;
110         gchar *buf = (gchar *) net->ppp_packet->info;
111
112         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
113                 return FALSE;
114
115         if (cond & G_IO_IN) {
116                 /* leave space to add PPP protocol field */
117                 status = g_io_channel_read_chars(channel, buf, net->mtu,
118                                                         &bytes_read, NULL);
119                 if (bytes_read > 0)
120                         ppp_transmit(net->ppp, (guint8 *) net->ppp_packet,
121                                         bytes_read);
122
123                 if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
124                         return FALSE;
125         }
126         return TRUE;
127 }
128
129 const char *ppp_net_get_interface(struct ppp_net *net)
130 {
131         return net->if_name;
132 }
133
134 struct ppp_net *ppp_net_new(GAtPPP *ppp, int fd)
135 {
136         struct ppp_net *net;
137         GIOChannel *channel = NULL;
138         struct ifreq ifr;
139         int err;
140
141         net = g_try_new0(struct ppp_net, 1);
142         if (net == NULL)
143                 goto badalloc;
144
145         net->ppp_packet = ppp_packet_new(MAX_PACKET, PPP_IP_PROTO);
146         if (net->ppp_packet == NULL)
147                 goto error;
148
149         /*
150          * If the fd value is still the default one,
151          * open the tun interface and configure it.
152          */
153         memset(&ifr, 0, sizeof(ifr));
154
155         if (fd < 0) {
156                 /* open a tun interface */
157                 fd = open("/dev/net/tun", O_RDWR);
158                 if (fd < 0) {
159                         ppp_debug(ppp, "Couldn't open tun device. "
160                                         "Do you run oFono as root and do you "
161                                         "have the TUN module loaded?");
162                         goto error;
163                 }
164
165                 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
166                 strcpy(ifr.ifr_name, "ppp%d");
167
168                 err = ioctl(fd, TUNSETIFF, (void *) &ifr);
169                 if (err < 0)
170                         goto error;
171         } else {
172                 err = ioctl(fd, TUNGETIFF, (void *) &ifr);
173                 if (err < 0)
174                         goto error;
175         }
176
177         net->if_name = strdup(ifr.ifr_name);
178
179         /* create a channel for reading and writing to this interface */
180         channel = g_io_channel_unix_new(fd);
181         if (channel == NULL)
182                 goto error;
183
184         if (!g_at_util_setup_io(channel, 0))
185                 goto error;
186
187         g_io_channel_set_buffered(channel, FALSE);
188
189         net->channel = channel;
190         net->watch = g_io_add_watch(channel,
191                         G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
192                         ppp_net_callback, net);
193         net->ppp = ppp;
194
195         net->mtu = MAX_PACKET;
196         return net;
197
198 error:
199         if (channel)
200                 g_io_channel_unref(channel);
201
202         g_free(net->if_name);
203         g_free(net->ppp_packet);
204         g_free(net);
205
206 badalloc:
207         if (fd >= 0)
208                 close(fd);
209
210         return NULL;
211 }
212
213 void ppp_net_free(struct ppp_net *net)
214 {
215         if (net->watch) {
216                 g_source_remove(net->watch);
217                 net->watch = 0;
218         }
219
220         g_io_channel_unref(net->channel);
221
222         g_free(net->ppp_packet);
223         g_free(net->if_name);
224         g_free(net);
225 }
226
227 void ppp_net_suspend_interface(struct ppp_net *net)
228 {
229         if (net == NULL || net->channel == NULL)
230                 return;
231
232         if (net->watch == 0)
233                 return;
234
235         g_source_remove(net->watch);
236         net->watch = 0;
237 }
238
239 void ppp_net_resume_interface(struct ppp_net *net)
240 {
241         if (net == NULL || net->channel == NULL)
242                 return;
243
244         net->watch = g_io_add_watch(net->channel,
245                         G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
246                         ppp_net_callback, net);
247 }