blob: 4fda9d6ed8d27c4b9cde092b3f4f586672eca86e [file] [log] [blame]
Chia-chi Yeh79e62322009-06-02 08:49:55 +08001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* A simple implementation of L2TP Access Concentrator (RFC 2661) which only
18 * creates a single session. The following code only handles control packets.
19 * Data packets are handled by PPPoLAC driver which can be found in Android
20 * kernel tree. */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <errno.h>
Chia-chi Yehf096f5b2009-06-21 07:40:36 +080026#include <fcntl.h>
Chia-chi Yeh79e62322009-06-02 08:49:55 +080027#include <sys/types.h>
28#include <sys/socket.h>
Chia-chi Yehf096f5b2009-06-21 07:40:36 +080029#include <sys/stat.h>
Chia-chi Yeh79e62322009-06-02 08:49:55 +080030#include <arpa/inet.h>
31#include <linux/if_pppolac.h>
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +080032#include <openssl/md5.h>
Chia-chi Yeh79e62322009-06-02 08:49:55 +080033
34#include "mtpd.h"
35
Chia-chi Yeh79e62322009-06-02 08:49:55 +080036/* To avoid unnecessary endianness conversions, tunnels, sessions, attributes,
37 * and values are all accessed in network order. */
38
39/* 0 is reserved. We put ACK here just for convenience. */
40enum l2tp_message {
41 ACK = 0,
42 SCCRQ = 1,
43 SCCRP = 2,
44 SCCCN = 3,
45 STOPCCN = 4,
46 HELLO = 6,
47 OCRQ = 7,
48 OCRP = 8,
49 OCCN = 9,
50 ICRQ = 10,
51 ICRP = 11,
52 ICCN = 12,
53 CDN = 14,
54 WEN = 15,
55 SLI = 16,
56 MESSAGE_MAX = 16,
57};
58
59static char *messages[] = {
60 "ACK", "SCCRQ", "SCCRP", "SCCCN", "STOPCCN", NULL, "HELLO", "OCRQ",
61 "OCRP", "OCCN", "ICRQ", "ICRP", "ICCN", NULL, "CDN", "WEN", "SLI",
62};
63
64/* This is incomplete. Only those we used are listed here. */
65#define RESULT_CODE htons(1)
66#define PROTOCOL_VERSION htons(2)
67#define FRAMING_CAPABILITIES htons(3)
68#define HOST_NAME htons(7)
69#define ASSIGNED_TUNNEL htons(9)
70#define WINDOW_SIZE htons(10)
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +080071#define CHALLENGE htons(11)
72#define CHALLENGE_RESPONSE htons(13)
Chia-chi Yeh79e62322009-06-02 08:49:55 +080073#define ASSIGNED_SESSION htons(14)
74#define CALL_SERIAL_NUMBER htons(15)
75#define FRAMING_TYPE htons(19)
76#define CONNECT_SPEED htons(24)
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +080077#define RANDOM_VECTOR htons(36)
Chia-chi Yeh79e62322009-06-02 08:49:55 +080078
79#define MESSAGE_FLAG 0xC802
80#define MESSAGE_MASK 0xCB0F
81#define ATTRIBUTE_FLAG(length) (0x8006 + (length))
82#define ATTRIBUTE_LENGTH(flag) (0x03FF & (flag))
83#define ATTRIBUTE_HIDDEN(flag) (0x4000 & (flag))
84
85#define ACK_SIZE 12
86#define MESSAGE_HEADER_SIZE 20
87#define ATTRIBUTE_HEADER_SIZE 6
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +080088#define MAX_ATTRIBUTE_SIZE 1024
Chia-chi Yeh79e62322009-06-02 08:49:55 +080089
90static uint16_t local_tunnel;
91static uint16_t local_session;
92static uint16_t local_sequence;
93static uint16_t remote_tunnel;
94static uint16_t remote_session;
95static uint16_t remote_sequence;
96
97static uint16_t state;
98static int acknowledged;
99
Chia-chi Yehe859c5e2009-06-19 17:32:22 +0800100#define RANDOM_DEVICE "/dev/urandom"
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800101#define CHALLENGE_SIZE 32
102
103static char *secret;
104static int secret_length;
105static uint8_t challenge[CHALLENGE_SIZE];
106
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800107/* According to RFC 2661 page 46, an exponential backoff strategy is required
108 * for retransmission. However, it might waste too much time waiting for IPsec
109 * negotiation. Here we use the same interval to keep things simple. */
110#define TIMEOUT_INTERVAL 2000
111
112#define MAX_PACKET_LENGTH 2048
113
114static struct packet {
115 int message;
116 int length;
Chia-chi Yehf096f5b2009-06-21 07:40:36 +0800117 uint8_t buffer[MAX_PACKET_LENGTH] __attribute__((aligned(4)));
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800118} incoming, outgoing;
119
120struct attribute {
121 uint16_t flag;
122 uint16_t vendor;
123 uint16_t type;
124 uint8_t value[1];
125} __attribute__((packed));
126
127static void set_message(uint16_t session, uint16_t message)
128{
129 uint16_t *p = (uint16_t *)outgoing.buffer;
130 p[0] = htons(MESSAGE_FLAG);
131 /* p[1] will be filled in send_packet(). */
132 p[2] = remote_tunnel;
133 p[3] = session;
134 p[4] = htons(local_sequence);
135 p[5] = htons(remote_sequence);
136 p[6] = htons(ATTRIBUTE_FLAG(2));
137 p[7] = 0;
138 p[8] = 0;
139 p[9] = htons(message);
140 outgoing.message = message;
141 outgoing.length = MESSAGE_HEADER_SIZE;
142 ++local_sequence;
143}
144
145static void add_attribute_raw(uint16_t type, void *value, int size)
146{
147 struct attribute *p = (struct attribute *)&outgoing.buffer[outgoing.length];
148 p->flag = htons(ATTRIBUTE_FLAG(size));
149 p->vendor = 0;
150 p->type = type;
151 memcpy(&p->value, value, size);
152 outgoing.length += ATTRIBUTE_HEADER_SIZE + size;
153}
154
155static void add_attribute_u16(uint16_t attribute, uint16_t value)
156{
157 add_attribute_raw(attribute, &value, sizeof(uint16_t));
158}
159
160static void add_attribute_u32(uint16_t attribute, uint32_t value)
161{
162 add_attribute_raw(attribute, &value, sizeof(uint32_t));
163}
164
165static void send_packet()
166{
167 uint16_t *p = (uint16_t *)outgoing.buffer;
168 p[1] = htons(outgoing.length);
169 send(the_socket, outgoing.buffer, outgoing.length, 0);
170 acknowledged = 0;
171}
172
173static void send_ack()
174{
175 uint16_t buffer[6] = {
176 htons(MESSAGE_FLAG), htons(ACK_SIZE), remote_tunnel, 0,
177 htons(local_sequence), htons(remote_sequence),
178 };
179 send(the_socket, buffer, ACK_SIZE, 0);
180}
181
182static int recv_packet(uint16_t *session)
183{
184 uint16_t *p = (uint16_t *)incoming.buffer;
185
186 incoming.length = recv(the_socket, incoming.buffer, MAX_PACKET_LENGTH, 0);
187 if (incoming.length == -1 && errno != EINTR) {
188 log_print(FATAL, "Recv() %s", strerror(errno));
189 exit(NETWORK_ERROR);
190 }
191
192 /* We only handle packets in our tunnel. */
193 if ((incoming.length != ACK_SIZE && incoming.length < MESSAGE_HEADER_SIZE)
194 || (p[0] & htons(MESSAGE_MASK)) != htons(MESSAGE_FLAG)
195 || p[1] > htons(incoming.length) || p[2] != local_tunnel) {
196 return 0;
197 }
198
199 if (incoming.length == ACK_SIZE) {
200 incoming.message = ACK;
201 } else if (p[6] == htons(ATTRIBUTE_FLAG(2)) && !p[7] && !p[8]) {
202 incoming.message = ntohs(p[9]);
203 } else {
204 return 0;
205 }
206
207 /* Check if the packet is duplicated and send ACK if necessary. */
208 if ((uint16_t)(ntohs(p[4]) - remote_sequence) > 32767) {
209 if (incoming.message != ACK) {
210 send_ack();
211 }
212 return 0;
213 }
214
215 if (ntohs(p[5]) == local_sequence) {
216 acknowledged = 1;
217 }
218
219 /* Our sending and receiving window sizes are both 1. Thus we only handle
220 * this packet if it is their next one and they received our last one. */
221 if (ntohs(p[4]) != remote_sequence || !acknowledged) {
222 return 0;
223 }
224 *session = p[3];
225 if (incoming.message != ACK) {
226 ++remote_sequence;
227 }
228 return 1;
229}
230
231static int get_attribute_raw(uint16_t type, void *value, int size)
232{
233 int offset = MESSAGE_HEADER_SIZE;
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800234 uint8_t *vector = NULL;
235 int vector_length = 0;
236
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800237 while (incoming.length >= offset + ATTRIBUTE_HEADER_SIZE) {
238 struct attribute *p = (struct attribute *)&incoming.buffer[offset];
239 uint16_t flag = ntohs(p->flag);
240 int length = ATTRIBUTE_LENGTH(flag);
241
242 offset += length;
243 length -= ATTRIBUTE_HEADER_SIZE;
244 if (length < 0 || offset > incoming.length) {
245 break;
246 }
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800247 if (p->vendor) {
248 continue;
249 }
250 if (p->type != type) {
251 if (p->type == RANDOM_VECTOR && !ATTRIBUTE_HIDDEN(flag)) {
252 vector = p->value;
253 vector_length = length;
254 }
255 continue;
256 }
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800257
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800258 if (!ATTRIBUTE_HIDDEN(flag)) {
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800259 if (size > length) {
260 size = length;
261 }
262 memcpy(value, p->value, size);
263 return size;
264 }
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800265
Chia-chi Yeh905c2d02009-10-13 15:22:10 +0800266 if (!secret || !vector || length < 2) {
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800267 return 0;
268 } else {
269 uint8_t buffer[MAX_ATTRIBUTE_SIZE];
270 uint8_t hash[MD5_DIGEST_LENGTH];
271 MD5_CTX ctx;
Chia-chi Yeh905c2d02009-10-13 15:22:10 +0800272 int i;
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800273
274 MD5_Init(&ctx);
275 MD5_Update(&ctx, &type, sizeof(uint16_t));
276 MD5_Update(&ctx, secret, secret_length);
277 MD5_Update(&ctx, vector, vector_length);
278 MD5_Final(hash, &ctx);
279
Chia-chi Yeh905c2d02009-10-13 15:22:10 +0800280 for (i = 0; i < length; ++i) {
281 int j = i % MD5_DIGEST_LENGTH;
282 if (i && !j) {
283 MD5_Init(&ctx);
284 MD5_Update(&ctx, secret, secret_length);
285 MD5_Update(&ctx, &p->value[i - MD5_DIGEST_LENGTH],
286 MD5_DIGEST_LENGTH);
287 MD5_Final(hash, &ctx);
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800288 }
Chia-chi Yeh905c2d02009-10-13 15:22:10 +0800289 buffer[i] = p->value[i] ^ hash[j];
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800290 }
291
292 length = buffer[0] << 8 | buffer[1];
Chia-chi Yeh905c2d02009-10-13 15:22:10 +0800293 if (length > i - 2) {
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800294 return 0;
295 }
296 if (size > length) {
297 size = length;
298 }
299 memcpy(value, &buffer[2], size);
300 return size;
301 }
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800302 }
303 return 0;
304}
305
306static int get_attribute_u16(uint16_t type, uint16_t *value)
307{
308 return get_attribute_raw(type, value, sizeof(uint16_t)) == sizeof(uint16_t);
309}
310
311static int l2tp_connect(int argc, char **argv)
312{
313 if (argc < 2) {
314 return -USAGE_ERROR;
315 }
316 create_socket(AF_INET, SOCK_DGRAM, argv[0], argv[1]);
317
318 while (!local_tunnel) {
319 local_tunnel = random();
320 }
321
322 log_print(DEBUG, "Sending SCCRQ (local_tunnel = %d)", local_tunnel);
323 state = SCCRQ;
324 set_message(0, SCCRQ);
325 add_attribute_u16(PROTOCOL_VERSION, htons(0x0100));
326 add_attribute_raw(HOST_NAME, "anonymous", 9);
327 add_attribute_u32(FRAMING_CAPABILITIES, htonl(3));
328 add_attribute_u16(ASSIGNED_TUNNEL, local_tunnel);
329 add_attribute_u16(WINDOW_SIZE, htons(1));
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800330
331 if (argc >= 3) {
Chia-chi Yehf096f5b2009-06-21 07:40:36 +0800332 int fd = open(RANDOM_DEVICE, O_RDONLY);
333 if (fd == -1 || read(fd, challenge, CHALLENGE_SIZE) != CHALLENGE_SIZE) {
Chia-chi Yehe859c5e2009-06-19 17:32:22 +0800334 log_print(FATAL, "Cannot read %s", RANDOM_DEVICE);
335 exit(SYSTEM_ERROR);
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800336 }
Chia-chi Yehf096f5b2009-06-21 07:40:36 +0800337 close(fd);
Chia-chi Yehe859c5e2009-06-19 17:32:22 +0800338
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800339 add_attribute_raw(CHALLENGE, challenge, CHALLENGE_SIZE);
340 secret = argv[2];
341 secret_length = strlen(argv[2]);
342 }
343
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800344 send_packet();
345 return TIMEOUT_INTERVAL;
346}
347
348static int create_pppox()
349{
350 int pppox;
351 log_print(INFO, "Creating PPPoX socket");
352 pppox = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OLAC);
353
354 if (pppox == -1) {
355 log_print(FATAL, "Socket() %s", strerror(errno));
356 exit(SYSTEM_ERROR);
357 } else {
358 struct sockaddr_pppolac address = {
359 .sa_family = AF_PPPOX,
360 .sa_protocol = PX_PROTO_OLAC,
361 .udp_socket = the_socket,
362 .local = {.tunnel = local_tunnel, .session = local_session},
363 .remote = {.tunnel = remote_tunnel, .session = remote_session},
364 };
365 if (connect(pppox, (struct sockaddr *)&address, sizeof(address)) != 0) {
366 log_print(FATAL, "Connect() %s", strerror(errno));
367 exit(SYSTEM_ERROR);
368 }
369 }
370 return pppox;
371}
372
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800373static uint8_t *compute_response(uint8_t type, void *challenge, int size)
374{
375 static uint8_t response[MD5_DIGEST_LENGTH];
376 MD5_CTX ctx;
377 MD5_Init(&ctx);
378 MD5_Update(&ctx, &type, sizeof(uint8_t));
379 MD5_Update(&ctx, secret, secret_length);
380 MD5_Update(&ctx, challenge, size);
381 MD5_Final(response, &ctx);
382 return response;
383}
384
385static int verify_challenge()
386{
387 if (secret) {
388 uint8_t response[MD5_DIGEST_LENGTH];
389 if (get_attribute_raw(CHALLENGE_RESPONSE, response, MD5_DIGEST_LENGTH)
390 != MD5_DIGEST_LENGTH) {
391 return 0;
392 }
393 return !memcmp(compute_response(SCCRP, challenge, CHALLENGE_SIZE),
394 response, MD5_DIGEST_LENGTH);
395 }
396 return 1;
397}
398
399static void answer_challenge()
400{
401 if (secret) {
402 uint8_t challenge[MAX_ATTRIBUTE_SIZE];
403 int size = get_attribute_raw(CHALLENGE, challenge, MAX_ATTRIBUTE_SIZE);
404 if (size > 0) {
405 uint8_t *response = compute_response(SCCCN, challenge, size);
406 add_attribute_raw(CHALLENGE_RESPONSE, response, MD5_DIGEST_LENGTH);
407 }
408 }
409}
410
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800411static int l2tp_process()
412{
413 uint16_t sequence = local_sequence;
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800414 uint16_t tunnel = 0;
415 uint16_t session = 0;
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800416
417 if (!recv_packet(&session)) {
418 return acknowledged ? 0 : TIMEOUT_INTERVAL;
419 }
420
421 /* Here is the fun part. We always try to protect our tunnel and session
422 * from being closed even if we received unexpected messages. */
423 switch(incoming.message) {
424 case SCCRP:
425 if (state == SCCRQ) {
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800426 if (get_attribute_u16(ASSIGNED_TUNNEL, &tunnel) && tunnel
427 && verify_challenge()) {
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800428 remote_tunnel = tunnel;
429 log_print(DEBUG, "Received SCCRP (remote_tunnel = %d) -> "
430 "Sending SCCCN", remote_tunnel);
431 state = SCCCN;
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800432 answer_challenge();
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800433 set_message(0, SCCCN);
434 break;
435 }
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800436 log_print(DEBUG, "Received SCCRP without %s", tunnel ?
437 "valid challenge response" : "assigned tunnel");
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800438 log_print(ERROR, "Protocol error");
Chia-chi Yehe859c5e2009-06-19 17:32:22 +0800439 return tunnel ? -CHALLENGE_FAILED : -PROTOCOL_ERROR;
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800440 }
441 break;
442
443 case ICRP:
444 if (state == ICRQ && session == local_session) {
445 if (get_attribute_u16(ASSIGNED_SESSION, &session) && session) {
446 remote_session = session;
447 log_print(DEBUG, "Received ICRP (remote_session = %d) -> "
448 "Sending ICCN", remote_session);
449 state = ICCN;
450 set_message(remote_session, ICCN);
451 add_attribute_u32(CONNECT_SPEED, htonl(100000000));
452 add_attribute_u32(FRAMING_TYPE, htonl(3));
453 break;
454 }
455 log_print(DEBUG, "Received ICRP without assigned session");
456 log_print(ERROR, "Protocol error");
457 return -PROTOCOL_ERROR;
458 }
459 break;
460
461 case STOPCCN:
462 log_print(DEBUG, "Received STOPCCN");
463 log_print(INFO, "Remote server hung up");
464 state = STOPCCN;
465 return -REMOTE_REQUESTED;
466
467 case CDN:
468 if (session && session == local_session) {
469 log_print(DEBUG, "Received CDN (local_session = %d)",
470 local_session);
471 log_print(INFO, "Remote server hung up");
472 return -REMOTE_REQUESTED;
473 }
474 break;
475
476 case ACK:
477 case HELLO:
478 case WEN:
479 case SLI:
480 /* These are harmless, so we just treat them the same way. */
481 if (state == SCCCN) {
482 while (!local_session) {
483 local_session = random();
484 }
485 log_print(DEBUG, "Received %s -> Sending ICRQ (local_session = "
486 "%d)", messages[incoming.message], local_session);
487 log_print(INFO, "Tunnel established");
488 state = ICRQ;
489 set_message(0, ICRQ);
490 add_attribute_u16(ASSIGNED_SESSION, local_session);
491 add_attribute_u32(CALL_SERIAL_NUMBER, random());
492 break;
493 }
494
495 if (incoming.message == ACK) {
496 log_print(DEBUG, "Received ACK");
497 } else {
498 log_print(DEBUG, "Received %s -> Sending ACK",
499 messages[incoming.message]);
500 send_ack();
501 }
502
503 if (state == ICCN) {
504 log_print(INFO, "Session established");
505 state = ACK;
506 start_pppd(create_pppox());
507 }
508 return 0;
509
510 case ICRQ:
511 case OCRQ:
512 /* Since we run pppd as a client, it does not makes sense to
513 * accept ICRQ or OCRQ. Always send CDN with a proper error. */
514 if (get_attribute_u16(ASSIGNED_SESSION, &session) && session) {
515 log_print(DEBUG, "Received %s (remote_session = %d) -> "
516 "Sending CDN", messages[incoming.message], session);
517 set_message(session, CDN);
518 add_attribute_u32(RESULT_CODE, htonl(0x00020006));
519 add_attribute_u16(ASSIGNED_SESSION, 0);
520 }
521 break;
522 }
523
524 if (sequence != local_sequence) {
525 send_packet();
526 return TIMEOUT_INTERVAL;
527 }
528
529 /* We reach here if we got an unexpected message. Log it and send ACK. */
530 if (incoming.message > MESSAGE_MAX || !messages[incoming.message]) {
531 log_print(DEBUG, "Received UNKNOWN %d -> Sending ACK anyway",
532 incoming.message);
533 } else {
534 log_print(DEBUG, "Received UNEXPECTED %s -> Sending ACK anyway",
535 messages[incoming.message]);
536 }
537 send_ack();
538 return 0;
539}
540
541static int l2tp_timeout()
542{
543 if (acknowledged) {
544 return 0;
545 }
546 log_print(DEBUG, "Timeout -> Sending %s", messages[outgoing.message]);
547 send(the_socket, outgoing.buffer, outgoing.length, 0);
548 return TIMEOUT_INTERVAL;
549}
550
551static void l2tp_shutdown()
552{
553 if (state != STOPCCN) {
554 log_print(DEBUG, "Sending STOPCCN");
555 set_message(0, STOPCCN);
556 add_attribute_u16(ASSIGNED_TUNNEL, local_tunnel);
557 add_attribute_u16(RESULT_CODE, htons(6));
558 send_packet();
559 }
560}
561
562struct protocol l2tp = {
563 .name = "l2tp",
Chia-chi Yeh6c0e6ee2009-06-17 17:55:07 +0800564 .usage = "<server> <port> [secret]",
Chia-chi Yeh79e62322009-06-02 08:49:55 +0800565 .connect = l2tp_connect,
566 .process = l2tp_process,
567 .timeout = l2tp_timeout,
568 .shutdown = l2tp_shutdown,
569};