summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Protsenko <semen.protsenko@linaro.org>2018-07-26 22:14:08 -0700
committerandroid-build-merger <android-build-merger@google.com>2018-07-26 22:14:08 -0700
commita958c04261d235f9942585cd0e573fb0e78aa1be (patch)
tree4a1fefc53ec2b7d3daf7db87a589b1ddf1a0140b
parent39d92f3eb638b5e389d597359966ee9c5f5bc628 (diff)
parenta62a985cd6f70a67dc25ff8d179c01201c7f1bc5 (diff)
downloadmtpd-a958c04261d235f9942585cd0e573fb0e78aa1be.tar.gz
Merge changes from topic "pppolac" am: 21c8e631ac am: 123ea675fa
am: a62a985cd6 Change-Id: Ie97955c9a794baffc0b6b5472bfb5dd976f787f0
-rw-r--r--l2tp.c163
-rw-r--r--mtpd.c102
-rw-r--r--mtpd.h2
3 files changed, 220 insertions, 47 deletions
diff --git a/l2tp.c b/l2tp.c
index 7dd550a..1d6171e 100644
--- a/l2tp.c
+++ b/l2tp.c
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-/* A simple implementation of L2TP Access Concentrator (RFC 2661) which only
- * creates a single session. The following code only handles control packets.
- * Data packets are handled by PPPoLAC driver which can be found in Android
- * kernel tree. */
+/*
+ * Implementation of L2TP Access Concentrator (RFC 2661). The following code
+ * only handles control packets. Data packets are handled by kernel driver:
+ * - PX_PROTO_OL2TP (upstream impl.), if it's enabled in kernel
+ * - or PX_PROTO_OLAC (Android impl.), if upstream implementation is not
+ * available / not enabled. It will be removed in new Android kernels.
+ */
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -31,6 +35,7 @@
#include <arpa/inet.h>
#include <linux/netdevice.h>
#include <linux/if_pppox.h>
+#include <linux/types.h>
#include <openssl/md5.h>
#include "mtpd.h"
@@ -89,11 +94,11 @@ static char *messages[] = {
#define ATTRIBUTE_HEADER_SIZE 6
#define MAX_ATTRIBUTE_SIZE 1024
-static uint16_t local_tunnel;
-static uint16_t local_session;
+static __be16 local_tunnel;
+static __be16 local_session;
static uint16_t local_sequence;
-static uint16_t remote_tunnel;
-static uint16_t remote_session;
+static __be16 remote_tunnel;
+static __be16 remote_session;
static uint16_t remote_sequence;
static uint16_t state;
@@ -321,7 +326,8 @@ static int l2tp_connect(char **arguments)
local_tunnel = random();
}
- log_print(DEBUG, "Sending SCCRQ (local_tunnel = %d)", local_tunnel);
+ log_print(DEBUG, "Sending SCCRQ (local_tunnel = %u)",
+ (unsigned)ntohs(local_tunnel));
state = SCCRQ;
set_message(0, SCCRQ);
add_attribute_u16(PROTOCOL_VERSION, htons(0x0100));
@@ -347,10 +353,40 @@ static int l2tp_connect(char **arguments)
return TIMEOUT_INTERVAL;
}
-static int create_pppox()
+/**
+ * Check if upstream kernel implementation is enabled.
+ *
+ * @return true if upstream L2TP is enabled in kernel and false otherwise
+ */
+static bool check_ol2tp(void)
{
- int pppox = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OLAC);
+ int fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
+
+ if (fd < 0) {
+ return false;
+ } else {
+ close(fd);
+ return true;
+ }
+}
+
+/**
+ * Create OLAC session.
+ *
+ * @deprecated It will be removed soon in favor of upstream OL2TP.
+ *
+ * @return PPPoX socket file descriptor
+ */
+static int create_pppox_olac(void)
+{
+ int pppox;
+
+ log_print(WARNING, "Using deprecated OLAC protocol. "
+ "Its support will be removed soon. "
+ "Please enable OL2TP support in your kernel");
+
log_print(INFO, "Creating PPPoX socket");
+ pppox = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OLAC);
if (pppox == -1) {
log_print(FATAL, "Socket() %s", strerror(errno));
@@ -371,6 +407,69 @@ static int create_pppox()
return pppox;
}
+/**
+ * Create OL2TP tunnel and session.
+ *
+ * @param[out] tfd Will contain tunnel socket file descriptor
+ * @param[out] sfd Will contain session socket file descriptor
+ */
+static void create_pppox_ol2tp(int *tfd, int *sfd)
+{
+ int tunnel_fd;
+ int session_fd;
+ struct sockaddr_pppol2tp tunnel_sa;
+ struct sockaddr_pppol2tp session_sa;
+
+ log_print(INFO, "Creating PPPoX tunnel socket...");
+ tunnel_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
+ if (tunnel_fd < 0) {
+ log_print(FATAL, "Tunnel socket(): %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ memset(&tunnel_sa, 0, sizeof(tunnel_sa));
+ tunnel_sa.sa_family = AF_PPPOX;
+ tunnel_sa.sa_protocol = PX_PROTO_OL2TP;
+ tunnel_sa.pppol2tp.fd = the_socket; /* UDP socket */
+ tunnel_sa.pppol2tp.s_tunnel = ntohs(local_tunnel);
+ tunnel_sa.pppol2tp.s_session = 0; /* special case: mgmt socket */
+ tunnel_sa.pppol2tp.d_tunnel = ntohs(remote_tunnel);
+ tunnel_sa.pppol2tp.d_session = 0; /* special case: mgmt socket */
+
+ log_print(INFO, "Connecting to tunnel socket...");
+ if (connect(tunnel_fd, (struct sockaddr *)&tunnel_sa,
+ sizeof(tunnel_sa))) {
+ log_print(FATAL, "Tunnel connect(): %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ log_print(INFO, "Creating PPPoX session socket...");
+ session_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
+ if (session_fd < 0) {
+ log_print(FATAL, "Session socket(): %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ memset(&session_sa, 0, sizeof(session_sa));
+ session_sa.sa_family = AF_PPPOX;
+ session_sa.sa_protocol = PX_PROTO_OL2TP;
+ session_sa.pppol2tp.fd = the_socket;
+ session_sa.pppol2tp.s_tunnel = ntohs(local_tunnel);
+ session_sa.pppol2tp.s_session = ntohs(local_session);
+ session_sa.pppol2tp.d_tunnel = ntohs(remote_tunnel);
+ session_sa.pppol2tp.d_session = ntohs(remote_session);
+
+ log_print(INFO, "Connecting to session socket...");
+ if (connect(session_fd, (struct sockaddr *)&session_sa,
+ sizeof(session_sa))) {
+ log_print(FATAL, "Session connect(): %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ *tfd = tunnel_fd;
+ *sfd = session_fd;
+}
+
static uint8_t *compute_response(uint8_t type, void *challenge, int size)
{
static uint8_t response[MD5_DIGEST_LENGTH];
@@ -383,18 +482,18 @@ static uint8_t *compute_response(uint8_t type, void *challenge, int size)
return response;
}
-static int verify_challenge()
+static bool verify_challenge()
{
if (secret) {
uint8_t response[MD5_DIGEST_LENGTH];
if (get_attribute_raw(CHALLENGE_RESPONSE, response, MD5_DIGEST_LENGTH)
!= MD5_DIGEST_LENGTH) {
- return 0;
+ return false;
}
return !memcmp(compute_response(SCCRP, challenge, CHALLENGE_SIZE),
response, MD5_DIGEST_LENGTH);
}
- return 1;
+ return true;
}
static void answer_challenge()
@@ -412,8 +511,8 @@ static void answer_challenge()
static int l2tp_process()
{
uint16_t sequence = local_sequence;
- uint16_t tunnel = 0;
- uint16_t session = 0;
+ __be16 tunnel = 0;
+ __be16 session = 0;
if (!recv_packet(&session)) {
return acknowledged ? 0 : TIMEOUT_INTERVAL;
@@ -427,8 +526,8 @@ static int l2tp_process()
if (get_attribute_u16(ASSIGNED_TUNNEL, &tunnel) && tunnel &&
verify_challenge()) {
remote_tunnel = tunnel;
- log_print(DEBUG, "Received SCCRP (remote_tunnel = %d) -> "
- "Sending SCCCN", remote_tunnel);
+ log_print(DEBUG, "Received SCCRP (remote_tunnel = %u) -> "
+ "Sending SCCCN", (unsigned)ntohs(remote_tunnel));
state = SCCCN;
set_message(0, SCCCN);
answer_challenge();
@@ -445,8 +544,8 @@ static int l2tp_process()
if (state == ICRQ && session == local_session) {
if (get_attribute_u16(ASSIGNED_SESSION, &session) && session) {
remote_session = session;
- log_print(DEBUG, "Received ICRP (remote_session = %d) -> "
- "Sending ICCN", remote_session);
+ log_print(DEBUG, "Received ICRP (remote_session = %u) -> "
+ "Sending ICCN", (unsigned)ntohs(remote_session));
state = ICCN;
set_message(remote_session, ICCN);
add_attribute_u32(CONNECT_SPEED, htonl(100000000));
@@ -467,8 +566,8 @@ static int l2tp_process()
case CDN:
if (session && session == local_session) {
- log_print(DEBUG, "Received CDN (local_session = %d)",
- local_session);
+ log_print(DEBUG, "Received CDN (local_session = %u)",
+ (unsigned)ntohs(local_session));
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
@@ -484,7 +583,8 @@ static int l2tp_process()
local_session = random();
}
log_print(DEBUG, "Received %s -> Sending ICRQ (local_session = "
- "%d)", messages[incoming.message], local_session);
+ "%u)", messages[incoming.message],
+ (unsigned)ntohs(local_session));
log_print(INFO, "Tunnel established");
state = ICRQ;
set_message(0, ICRQ);
@@ -504,7 +604,17 @@ static int l2tp_process()
if (state == ICCN) {
log_print(INFO, "Session established");
state = ACK;
- start_pppd(create_pppox());
+
+ if (check_ol2tp()) {
+ int tunnel_fd, session_fd;
+
+ create_pppox_ol2tp(&tunnel_fd, &session_fd);
+ start_pppd_ol2tp(tunnel_fd, session_fd,
+ ntohs(remote_tunnel),
+ ntohs(remote_session));
+ } else {
+ start_pppd(create_pppox_olac());
+ }
}
return 0;
@@ -513,8 +623,9 @@ static int l2tp_process()
/* Since we run pppd as a client, it does not makes sense to
* accept ICRQ or OCRQ. Always send CDN with a proper error. */
if (get_attribute_u16(ASSIGNED_SESSION, &session) && session) {
- log_print(DEBUG, "Received %s (remote_session = %d) -> "
- "Sending CDN", messages[incoming.message], session);
+ log_print(DEBUG, "Received %s (remote_session = %u) -> "
+ "Sending CDN", messages[incoming.message],
+ (unsigned)ntohs(session));
set_message(session, CDN);
add_attribute_u32(RESULT_CODE, htonl(0x00020006));
add_attribute_u16(ASSIGNED_SESSION, 0);
diff --git a/mtpd.c b/mtpd.c
index ae0a1e6..53ea362 100644
--- a/mtpd.c
+++ b/mtpd.c
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
+#include <limits.h>
#ifdef ANDROID_CHANGES
#include <android/log.h>
@@ -37,6 +38,12 @@
#include "mtpd.h"
#include "NetdClient.h"
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+/* Characters count in string with max value of unsigned type t */
+#define TYPE_STRLEN_U(t) ((((sizeof(t) * CHAR_BIT) * 1233) >> 12) + 1)
+/* Length of string with max file descriptor value */
+#define FD_MAX_LEN TYPE_STRLEN_U(int)
+
int the_socket = -1;
extern struct protocol l2tp;
@@ -322,9 +329,9 @@ void start_pppd(int pppox)
if (!pppd_pid) {
char *args[pppd_argc + 5];
- char number[12];
+ char number[FD_MAX_LEN + 1];
- sprintf(number, "%d", pppox);
+ snprintf(number, FD_MAX_LEN + 1, "%d", pppox);
args[0] = "pppd";
args[1] = "nodetach";
args[2] = "pppox";
@@ -332,29 +339,82 @@ void start_pppd(int pppox)
memcpy(&args[4], pppd_argv, sizeof(char *) * pppd_argc);
args[4 + pppd_argc] = NULL;
-#ifdef ANDROID_CHANGES
- {
- char envargs[65536];
- char *tail = envargs;
- int i;
- /* Hex encode the arguments using [A-P] instead of [0-9A-F]. */
- for (i = 0; args[i]; ++i) {
- char *p = args[i];
- do {
- *tail++ = 'A' + ((*p >> 4) & 0x0F);
- *tail++ = 'A' + (*p & 0x0F);
- } while (*p++);
- }
- *tail = 0;
- setenv("envargs", envargs, 1);
- args[1] = NULL;
- }
-#endif
execvp("pppd", args);
log_print(FATAL, "Exec() %s", strerror(errno));
- exit(1); /* Pretending a fatal error in pppd. */
+ exit(SYSTEM_ERROR); /* Pretending a fatal error in pppd. */
}
log_print(INFO, "Pppd started (pid = %d)", pppd_pid);
close(pppox);
}
+
+/**
+ * Start pppd daemon with pppol2tp-android plugin.
+ *
+ * @param tunnel_fd Tunnel socket file descriptor
+ * @param session_fd Session socket file descriptor
+ * @param tunnel_id Tunnel ID; must be in host byte order
+ * @param session_id Session ID; must be in host byte order
+ */
+void start_pppd_ol2tp(int tunnel_fd, int session_fd, int tunnel_id,
+ int session_id)
+{
+ if (pppd_pid) {
+ log_print(WARNING, "Pppd is already started (pid = %d)", pppd_pid);
+ goto ret;
+ }
+
+ log_print(INFO, "Starting pppd (tunnel_fd = %d, session_fd = %d)",
+ tunnel_fd, session_fd);
+
+ pppd_pid = fork();
+ if (pppd_pid < 0) {
+ log_print(FATAL, "Fork() %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ if (!pppd_pid) {
+ char tunnel_fd_str[FD_MAX_LEN + 1];
+ char session_fd_str[FD_MAX_LEN + 1];
+ char tunnel_id_str[FD_MAX_LEN + 1];
+ char session_id_str[FD_MAX_LEN + 1];
+
+ snprintf(tunnel_fd_str, FD_MAX_LEN + 1, "%d", tunnel_fd);
+ snprintf(session_fd_str, FD_MAX_LEN + 1, "%d", session_fd);
+ snprintf(tunnel_id_str, FD_MAX_LEN + 1, "%d", tunnel_id);
+ snprintf(session_id_str, FD_MAX_LEN + 1, "%d", session_id);
+
+ const char *l2tp_args[] = {
+ "pppd",
+ "nodetach",
+ "plugin",
+ "pppol2tp-android.so",
+ "session_fd",
+ session_fd_str,
+ "tunnel_fd",
+ tunnel_fd_str,
+ "session_id",
+ session_id_str,
+ "tunnel_id",
+ tunnel_id_str,
+ };
+ const size_t args_len = ARRAY_SIZE(l2tp_args) + pppd_argc + 1;
+ char *args[args_len];
+
+ /* Populate args[] from l2tp_args[] and pppd_argv[] */
+ memcpy(args, l2tp_args, sizeof(l2tp_args));
+ memcpy(args + ARRAY_SIZE(l2tp_args), pppd_argv,
+ sizeof(char *) * pppd_argc);
+ args[args_len - 1] = NULL;
+
+ execvp("pppd", args);
+ log_print(FATAL, "Exec() %s", strerror(errno));
+ exit(SYSTEM_ERROR); /* Pretending a fatal error in pppd. */
+ }
+
+ log_print(INFO, "Pppd started (pid = %d)", pppd_pid);
+
+ret:
+ close(session_fd);
+ close(tunnel_fd);
+}
diff --git a/mtpd.h b/mtpd.h
index 8e7799b..dad3ea6 100644
--- a/mtpd.h
+++ b/mtpd.h
@@ -70,6 +70,8 @@ enum log_level {
void log_print(int level, char *format, ...);
void create_socket(int family, int type, char *server, char *port);
void start_pppd(int pppox);
+void start_pppd_ol2tp(int tunnel_fd, int session_fd, int tunnel_id,
+ int session_id);
/* Each protocol must implement everything defined in this structure. Note that
* timeout intervals are in milliseconds, where zero means forever. To indicate