diff options
author | Sam Protsenko <semen.protsenko@linaro.org> | 2018-07-26 22:14:08 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-07-26 22:14:08 -0700 |
commit | a958c04261d235f9942585cd0e573fb0e78aa1be (patch) | |
tree | 4a1fefc53ec2b7d3daf7db87a589b1ddf1a0140b | |
parent | 39d92f3eb638b5e389d597359966ee9c5f5bc628 (diff) | |
parent | a62a985cd6f70a67dc25ff8d179c01201c7f1bc5 (diff) | |
download | mtpd-a958c04261d235f9942585cd0e573fb0e78aa1be.tar.gz |
Merge changes from topic "pppolac" am: 21c8e631ac am: 123ea675fa
am: a62a985cd6
Change-Id: Ie97955c9a794baffc0b6b5472bfb5dd976f787f0
-rw-r--r-- | l2tp.c | 163 | ||||
-rw-r--r-- | mtpd.c | 102 | ||||
-rw-r--r-- | mtpd.h | 2 |
3 files changed, 220 insertions, 47 deletions
@@ -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); @@ -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); +} @@ -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 |