packaging: Bump to 1.17
[platform/upstream/ofono.git] / gatchat / ppp_lcp.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 <termios.h>
31 #include <glib.h>
32 #include <arpa/inet.h>
33
34 #include "gatppp.h"
35 #include "ppp.h"
36
37 #define LCP_SUPPORTED_CODES     ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
38                                 (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
39                                 (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \
40                                 (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \
41                                 (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \
42                                 (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \
43                                 (1 << PPPCP_CODE_TYPE_CODE_REJECT) | \
44                                 (1 << PPPCP_CODE_TYPE_PROTOCOL_REJECT) | \
45                                 (1 << PPPCP_CODE_TYPE_ECHO_REQUEST) | \
46                                 (1 << PPPCP_CODE_TYPE_ECHO_REPLY) | \
47                                 (1 << PPPCP_CODE_TYPE_DISCARD_REQUEST))
48
49 enum lcp_options {
50         RESERVED                = 0,
51         MRU                     = 1,
52         ACCM                    = 2,
53         AUTH_PROTO              = 3,
54         QUAL_PROTO              = 4,
55         MAGIC_NUMBER            = 5,
56         DEPRECATED_QUAL_PROTO   = 6,
57         PFC                     = 7,
58         ACFC                    = 8,
59 };
60
61 /* Maximum size of all options, we only ever request ACCM, MRU, ACFC and PFC */
62 #define MAX_CONFIG_OPTION_SIZE 14
63
64 #define REQ_OPTION_ACCM 0x1
65 #define REQ_OPTION_MRU  0x2
66 #define REQ_OPTION_ACFC 0x4
67 #define REQ_OPTION_PFC  0x8
68
69 struct lcp_data {
70         guint8 options[MAX_CONFIG_OPTION_SIZE];
71         guint16 options_len;
72         guint8 req_options;
73         guint32 accm;                   /* ACCM value */
74         guint16 mru;
75 };
76
77 static void lcp_generate_config_options(struct lcp_data *lcp)
78 {
79         guint16 len = 0;
80
81         if (lcp->req_options & REQ_OPTION_ACCM) {
82                 guint32 accm;
83
84                 accm = htonl(lcp->accm);
85
86                 lcp->options[len] = ACCM;
87                 lcp->options[len + 1] = 6;
88                 memcpy(lcp->options + len + 2, &accm, sizeof(accm));
89
90                 len += 6;
91         }
92
93         if (lcp->req_options & REQ_OPTION_MRU) {
94                 guint16 mru;
95
96                 mru = htons(lcp->mru);
97
98                 lcp->options[len] = MRU;
99                 lcp->options[len + 1] = 4;
100                 memcpy(lcp->options + len + 2, &mru, sizeof(mru));
101
102                 len += 4;
103         }
104
105         if (lcp->req_options & REQ_OPTION_ACFC) {
106                 lcp->options[len] = ACFC;
107                 lcp->options[len + 1] = 2;
108
109                 len += 2;
110         }
111
112         if (lcp->req_options & REQ_OPTION_PFC) {
113                 lcp->options[len] = PFC;
114                 lcp->options[len + 1] = 2;
115
116                 len += 2;
117         }
118
119         lcp->options_len = len;
120 }
121
122 static void lcp_reset_config_options(struct lcp_data *lcp)
123 {
124         /* Using the default ACCM */
125
126         lcp_generate_config_options(lcp);
127 }
128
129 /*
130  * signal the Up event to the NCP
131  */
132 static void lcp_up(struct pppcp_data *pppcp)
133 {
134         ppp_lcp_up_notify(pppcp_get_ppp(pppcp));
135 }
136
137 /*
138  * signal the Down event to the NCP
139  */
140 static void lcp_down(struct pppcp_data *pppcp)
141 {
142         struct lcp_data *lcp = pppcp_get_data(pppcp);
143
144         lcp_reset_config_options(lcp);
145         pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
146         ppp_lcp_down_notify(pppcp_get_ppp(pppcp));
147 }
148
149 /*
150  * Indicate that the lower layer is not needed
151  * Should trigger Down event
152  */
153 static void lcp_finished(struct pppcp_data *pppcp)
154 {
155         ppp_lcp_finished_notify(pppcp_get_ppp(pppcp));
156 }
157
158 static void lcp_rca(struct pppcp_data *pppcp, const struct pppcp_packet *packet)
159 {
160         struct ppp_option_iter iter;
161
162         ppp_option_iter_init(&iter, packet);
163
164         while (ppp_option_iter_next(&iter) == TRUE) {
165                 const guint8 *data = ppp_option_iter_get_data(&iter);
166                 switch (ppp_option_iter_get_type(&iter)) {
167                 case ACCM:
168                         /*
169                          * RFC1662 Section 7.1
170                          * The Configuration Option is used to inform the peer
171                          * which control characters MUST remain mapped when
172                          * the peer sends them.
173                          */
174
175                         ppp_set_recv_accm(pppcp_get_ppp(pppcp),
176                                         get_host_long(data));
177                         break;
178                 default:
179                         break;
180                 }
181         }
182 }
183
184 static void lcp_rcn_nak(struct pppcp_data *pppcp,
185                                 const struct pppcp_packet *packet)
186 {
187         struct lcp_data *lcp = pppcp_get_data(pppcp);
188         struct ppp_option_iter iter;
189
190         ppp_option_iter_init(&iter, packet);
191
192         while (ppp_option_iter_next(&iter) == TRUE) {
193                 const guint8 *data = ppp_option_iter_get_data(&iter);
194
195                 switch (ppp_option_iter_get_type(&iter)) {
196                 case MRU:
197                 {
198                         guint16 mru = get_host_short(data);
199
200                         if (mru < 2048) {
201                                 lcp->mru = get_host_short(data);
202                                 lcp->req_options |= REQ_OPTION_MRU;
203                         }
204
205                         break;
206                 }
207                 default:
208                         break;
209                 }
210         }
211
212         lcp_generate_config_options(lcp);
213         pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
214 }
215
216 static void lcp_rcn_rej(struct pppcp_data *pppcp,
217                                 const struct pppcp_packet *packet)
218 {
219
220 }
221
222 static enum rcr_result lcp_rcr(struct pppcp_data *pppcp,
223                                         const struct pppcp_packet *packet,
224                                         guint8 **new_options, guint16 *new_len)
225 {
226         GAtPPP *ppp = pppcp_get_ppp(pppcp);
227         struct ppp_option_iter iter;
228
229         ppp_option_iter_init(&iter, packet);
230
231         while (ppp_option_iter_next(&iter) == TRUE) {
232                 switch (ppp_option_iter_get_type(&iter)) {
233                 case AUTH_PROTO:
234                 {
235                         const guint8 *option_data =
236                                 ppp_option_iter_get_data(&iter);
237                         guint16 proto = get_host_short(option_data);
238                         guint8 method = option_data[2];
239                         guint8 *option;
240
241                         switch (g_at_ppp_get_auth_method(ppp)) {
242                         case G_AT_PPP_AUTH_METHOD_CHAP:
243                                 if (proto == CHAP_PROTOCOL && method == MD5)
244                                         break;
245
246                                 /*
247                                  * Try to suggest CHAP/MD5.
248                                  * Just reject if we run out of memory.
249                                  */
250                                 option = g_try_malloc0(5);
251                                 if (option == NULL)
252                                         return RCR_REJECT;
253
254                                 option[0] = AUTH_PROTO;
255                                 option[1] = 5;
256                                 put_network_short(&option[2], CHAP_PROTOCOL);
257                                 option[4] = MD5;
258                                 *new_options = option;
259                                 *new_len = 5;
260
261                                 return RCR_NAK;
262
263                         case G_AT_PPP_AUTH_METHOD_PAP:
264                                 if (proto == PAP_PROTOCOL)
265                                         break;
266
267                                 /*
268                                  * Try to suggest PAP.
269                                  * Just reject if we run out of memory.
270                                  */
271                                 option = g_try_malloc0(4);
272                                 if (option == NULL)
273                                         return RCR_REJECT;
274
275                                 option[0] = AUTH_PROTO;
276                                 option[1] = 4;
277                                 put_network_short(&option[2], PAP_PROTOCOL);
278                                 *new_options = option;
279                                 *new_len = 4;
280
281                                 return RCR_NAK;
282                         }
283                         break;
284                 }
285                 case ACCM:
286                 case PFC:
287                 case ACFC:
288                 case MRU:
289                         break;
290
291                 case MAGIC_NUMBER:
292                 {
293                         guint32 magic =
294                                 get_host_long(ppp_option_iter_get_data(&iter));
295
296                         if (magic == 0)
297                                 return RCR_REJECT;
298
299                         break;
300                 }
301                 default:
302                         return RCR_REJECT;
303                 }
304         }
305
306         /* All options were found acceptable, apply them here and return */
307         ppp_option_iter_init(&iter, packet);
308
309         while (ppp_option_iter_next(&iter) == TRUE) {
310                 switch (ppp_option_iter_get_type(&iter)) {
311                 case ACCM:
312                         /*
313                          * RFC1662 Section 7.1
314                          * The Configuration Option is used to inform the peer
315                          * which control characters MUST remain mapped when
316                          * the peer sends them.
317                          */
318                         ppp_set_xmit_accm(ppp,
319                                 get_host_long(ppp_option_iter_get_data(&iter)));
320                         break;
321                 case AUTH_PROTO:
322                         ppp_set_auth(ppp, ppp_option_iter_get_data(&iter));
323                         break;
324                 case MRU:
325                         ppp_set_mtu(ppp, ppp_option_iter_get_data(&iter));
326                         break;
327                 case MAGIC_NUMBER:
328                         /* don't care */
329                         break;
330                 case PFC:
331                 {
332                         struct lcp_data *lcp = pppcp_get_data(pppcp);
333
334                         if (lcp->req_options & REQ_OPTION_PFC)
335                                 ppp_set_xmit_pfc(ppp, TRUE);
336
337                         break;
338                 }
339                 case ACFC:
340                 {
341                         struct lcp_data *lcp = pppcp_get_data(pppcp);
342
343                         if (lcp->req_options & REQ_OPTION_ACFC)
344                                 ppp_set_xmit_acfc(ppp, TRUE);
345
346                         break;
347                 }
348                 }
349         }
350
351         return RCR_ACCEPT;
352 }
353
354 struct pppcp_proto lcp_proto = {
355         .proto                  = LCP_PROTOCOL,
356         .name                   = "lcp",
357         .supported_codes        = LCP_SUPPORTED_CODES,
358         .this_layer_up          = lcp_up,
359         .this_layer_down        = lcp_down,
360         .this_layer_finished    = lcp_finished,
361         .rca                    = lcp_rca,
362         .rcn_nak                = lcp_rcn_nak,
363         .rcn_rej                = lcp_rcn_rej,
364         .rcr                    = lcp_rcr,
365 };
366
367 void lcp_free(struct pppcp_data *pppcp)
368 {
369         struct lcp_data *lcp = pppcp_get_data(pppcp);
370
371         g_free(lcp);
372         pppcp_free(pppcp);
373 }
374
375 struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean is_server)
376 {
377         struct pppcp_data *pppcp;
378         struct lcp_data *lcp;
379
380         lcp = g_try_new0(struct lcp_data, 1);
381         if (lcp == NULL)
382                 return NULL;
383
384         pppcp = pppcp_new(ppp, &lcp_proto, is_server, 0);
385         if (pppcp == NULL) {
386                 g_free(lcp);
387                 return NULL;
388         }
389
390         pppcp_set_data(pppcp, lcp);
391
392         lcp_reset_config_options(lcp);
393         pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
394
395         return pppcp;
396 }
397
398 void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled)
399 {
400         struct lcp_data *lcp = pppcp_get_data(pppcp);
401         guint8 old = lcp->req_options;
402
403         if (enabled == TRUE)
404                 lcp->req_options |= REQ_OPTION_ACFC;
405         else
406                 lcp->req_options &= ~REQ_OPTION_ACFC;
407
408         if (lcp->req_options == old)
409                 return;
410
411         lcp_generate_config_options(lcp);
412         pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
413 }
414
415 void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled)
416 {
417         struct lcp_data *lcp = pppcp_get_data(pppcp);
418         guint8 old = lcp->req_options;
419
420         if (enabled == TRUE)
421                 lcp->req_options |= REQ_OPTION_PFC;
422         else
423                 lcp->req_options &= ~REQ_OPTION_PFC;
424
425         if (lcp->req_options == old)
426                 return;
427
428         lcp_generate_config_options(lcp);
429         pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
430 }