Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 112 additions & 6 deletions drivers/rilmodem/gprs-context.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#include "gprs.h"
#include "rilmodem.h"

#define DEF_IPV6_PREF_LENGTH 128

enum state {
STATE_IDLE,
STATE_ENABLING,
Expand Down Expand Up @@ -132,6 +134,105 @@ static void ril_gprs_context_call_list_changed(struct ril_msg *message,
g_ril_unsol_free_data_call_list(call_list);
}

static gboolean is_ipv4(const char *ip)
{
return strchr(ip, '.') == NULL ? FALSE : TRUE;
}

static gboolean is_ipv6(const char *ip)
{
return strchr(ip, '.') == NULL ? TRUE : FALSE;
}

typedef gboolean (*is_of_type_t)(const char *str);

static char **get_strs_subset(char **strs, is_of_type_t is_of_type)
{
char **out;
char **str;
int n_str = 0, i;

if (strs == NULL)
return NULL;

for (str = strs; *str != NULL; ++str)
if (is_of_type(*str))
++n_str;

if (n_str == 0)
return NULL;

out = g_try_new0(char *, n_str + 1);
if (out == NULL)
return NULL;

for (str = strs, i = 0; i < n_str; ++str) {
if (!is_of_type(*str))
continue;

out[i++] = g_strdup(*str);
}

return out;
}

static void set_ipv4_settings(struct ofono_gprs_context *gc,
struct ril_data_call *call, int addr_i)
{
char **gw;
char **dnses;

ofono_gprs_context_set_ipv4_address(gc, call->ip_addrs[addr_i], TRUE);
ofono_gprs_context_set_ipv4_netmask(gc,
ril_util_get_ipv4_netmask(call->prefix_lengths[addr_i]));

for (gw = call->gateways; *gw != NULL; ++gw) {
if (!is_ipv4(*gw))
continue;

ofono_gprs_context_set_ipv4_gateway(gc, *gw);
break;
}

dnses = get_strs_subset(call->dns_addrs, is_ipv4);
ofono_gprs_context_set_ipv4_dns_servers(gc, (const char **) dnses);
g_strfreev(dnses);
}

static void set_ipv6_settings(struct ofono_gprs_context *gc,
struct ril_data_call *call, int addr_i)
{
char *endp;
char **gw;
int prefix;
char **dnses;

ofono_gprs_context_set_ipv6_address(gc, call->ip_addrs[addr_i]);

if (*call->prefix_lengths[addr_i] == '\0') {
prefix = DEF_IPV6_PREF_LENGTH;
} else {
prefix = strtol(call->prefix_lengths[addr_i], &endp, 10);
if (*endp != '\0' || prefix < 0 ||
prefix > DEF_IPV6_PREF_LENGTH)
prefix = DEF_IPV6_PREF_LENGTH;
}

ofono_gprs_context_set_ipv6_prefix_length(gc, prefix);

for (gw = call->gateways; *gw != NULL; ++gw) {
if (!is_ipv6(*gw))
continue;

ofono_gprs_context_set_ipv6_gateway(gc, *gw);
break;
}

dnses = get_strs_subset(call->dns_addrs, is_ipv6);
ofono_gprs_context_set_ipv6_dns_servers(gc, (const char **) dnses);
g_strfreev(dnses);
}

static void ril_setup_data_call_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
Expand All @@ -140,6 +241,8 @@ static void ril_setup_data_call_cb(struct ril_msg *message, gpointer user_data)
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
struct ril_data_call *call = NULL;
struct ril_data_call_list *call_list = NULL;
int i;
gboolean found_v4 = FALSE, found_v6 = FALSE;

DBG("*gc: %p", gc);

Expand Down Expand Up @@ -194,14 +297,17 @@ static void ril_setup_data_call_cb(struct ril_msg *message, gpointer user_data)
gcd->state = STATE_ACTIVE;

ofono_gprs_context_set_interface(gc, call->ifname);
ofono_gprs_context_set_ipv4_netmask(gc,
ril_util_get_netmask(call->ip_addr));

ofono_gprs_context_set_ipv4_address(gc, call->ip_addr, TRUE);
ofono_gprs_context_set_ipv4_gateway(gc, call->gateways[0]);
for (i = 0; call->ip_addrs[i] != NULL && !(found_v4 && found_v6); ++i) {

ofono_gprs_context_set_ipv4_dns_servers(gc,
(const char **) call->dns_addrs);
if (is_ipv4(call->ip_addrs[i])) {
set_ipv4_settings(gc, call, i);
found_v4 = TRUE;
} else {
set_ipv6_settings(gc, call, i);
found_v6 = TRUE;
}
}

g_ril_unsol_free_data_call_list(call_list);

Expand Down
49 changes: 22 additions & 27 deletions drivers/rilmodem/rilutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <gril.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/log.h>
Expand Down Expand Up @@ -120,39 +121,33 @@ static gboolean cpin_check(gpointer userdata)
return FALSE;
}

gchar *ril_util_get_netmask(const gchar *address)
char *ril_util_get_ipv4_netmask(const char *prefix_length)
{
char *result;
char *endp;
int bits;
uint32_t mask = 0;
int max_bits = sizeof(mask) * CHAR_BIT;

if (g_str_has_suffix(address, "/30")) {
result = PREFIX_30_NETMASK;
} else if (g_str_has_suffix(address, "/29")) {
result = PREFIX_29_NETMASK;
} else if (g_str_has_suffix(address, "/28")) {
result = PREFIX_28_NETMASK;
} else if (g_str_has_suffix(address, "/27")) {
result = PREFIX_27_NETMASK;
} else if (g_str_has_suffix(address, "/26")) {
result = PREFIX_26_NETMASK;
} else if (g_str_has_suffix(address, "/25")) {
result = PREFIX_25_NETMASK;
} else if (g_str_has_suffix(address, "/24")) {
result = PREFIX_24_NETMASK;
result = g_try_malloc(sizeof(mask) * 4);
if (result == NULL)
return result;

if (prefix_length == NULL || prefix_length[0] == '\0') {
bits = max_bits;
} else {
/*
* This handles the case where the
* Samsung RILD returns an address without
* a prefix, however it explicitly sets a
* /24 netmask ( which isn't returned as
* an attribute of the DATA_CALL.
*
* TODO/OEM: this might need to be quirked
* for specific devices.
*/
result = PREFIX_24_NETMASK;
bits = (int) strtol(prefix_length, &endp, 10);
if (*endp != '\0' || bits < 0 || bits > max_bits)
bits = max_bits;
}

DBG("address: %s netmask: %s", address, result);
for (; bits > 0; --bits)
mask |= 1 << (max_bits - bits);

sprintf(result, "%d.%d.%d.%d", mask >> 24, (mask >> 16) & 0xFF,
(mask >> 8) & 0xFF, mask & 0xFF);

DBG("netmask: %s", result);

return result;
}
Expand Down
11 changes: 1 addition & 10 deletions drivers/rilmodem/rilutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@
#include <sim.h>
#include <gprs-context.h>

/* TODO: create a table lookup*/
#define PREFIX_30_NETMASK "255.255.255.252"
#define PREFIX_29_NETMASK "255.255.255.248"
#define PREFIX_28_NETMASK "255.255.255.240"
#define PREFIX_27_NETMASK "255.255.255.224"
#define PREFIX_26_NETMASK "255.255.255.192"
#define PREFIX_25_NETMASK "255.255.255.128"
#define PREFIX_24_NETMASK "255.255.255.0"

#define MODEM_PROP_LTE_CAPABLE "lte-capable"

enum ril_util_sms_store {
Expand Down Expand Up @@ -100,7 +91,7 @@ gint ril_util_call_compare_by_status(gconstpointer a, gconstpointer b);
gint ril_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b);
gint ril_util_call_compare_by_id(gconstpointer a, gconstpointer b);
gint ril_util_call_compare(gconstpointer a, gconstpointer b);
gchar *ril_util_get_netmask(const char *address);
char *ril_util_get_ipv4_netmask(const char *prefix_length);

struct ril_util_sim_state_query *ril_util_sim_state_query_new(GRil *ril,
guint interval, guint num_times,
Expand Down
69 changes: 39 additions & 30 deletions gril/grilunsol.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ static void free_data_call(gpointer data, gpointer user_data)

if (call) {
g_free(call->ifname);
g_free(call->ip_addr);
g_free(call->dns_addrs);
g_free(call->gateways);
g_strfreev(call->ip_addrs);
g_strfreev(call->prefix_lengths);
g_strfreev(call->dns_addrs);
g_strfreev(call->gateways);
g_free(call);
}
}
Expand All @@ -89,7 +90,7 @@ static gboolean handle_settings(struct ril_data_call *call, char *type,
char *raw_dns, char *raw_gws)
{
gboolean result = FALSE;
int protocol;
int protocol, num_addrs, i;
char **dns_addrs = NULL, **gateways = NULL;
char **ip_addrs = NULL, **split_ip_addr = NULL;

Expand Down Expand Up @@ -122,46 +123,46 @@ static gboolean handle_settings(struct ril_data_call *call, char *type,
goto done;
}

/* TODO:
* RILD can return multiple addresses; oFono only supports
* setting a single IPv4 address. At this time, we only
* use the first address. It's possible that a RIL may
* just specify the end-points of the point-to-point
* connection, in which case this code will need to
* changed to handle such a device.
*
* For now split into a maximum of three, and only use
* the first address for the remaining operations.
*/
if (raw_ip_addrs)
ip_addrs = g_strsplit(raw_ip_addrs, " ", 3);
ip_addrs = g_strsplit(raw_ip_addrs, " ", -1);

if (ip_addrs == NULL || g_strv_length(ip_addrs) == 0) {
num_addrs = g_strv_length(ip_addrs);

if (ip_addrs == NULL || num_addrs == 0) {
ofono_error("%s: no IP address: %s", __func__, raw_ip_addrs);
goto done;
}

DBG("num ip addrs is: %d", g_strv_length(ip_addrs));
DBG("num ip addrs is: %d", num_addrs);

if (g_strv_length(ip_addrs) > 1)
if (num_addrs > 1)
ofono_warn("%s: more than one IP addr returned: %s", __func__,
raw_ip_addrs);
/*
* Note - the address may optionally include a prefix size
* ( Eg. "/30" ). As this confuses NetworkManager, we
* explicitly strip any prefix after calculating the netmask.
*/
split_ip_addr = g_strsplit(ip_addrs[0], "/", 2);

if (split_ip_addr == NULL || g_strv_length(split_ip_addr) == 0) {
ofono_error("%s: invalid IP address field returned: %s",
__func__, ip_addrs[0]);
goto done;
/* Build arrays of addresses and prefix lengths */

call->ip_addrs = g_new0(char *, num_addrs + 1);
call->prefix_lengths = g_new0(char *, num_addrs + 1);

for (i = 0; i < num_addrs; ++i) {
int n_parts;

split_ip_addr = g_strsplit(ip_addrs[i], "/", 2);
n_parts = g_strv_length(split_ip_addr);

if (split_ip_addr == NULL || n_parts == 0) {
ofono_error("%s: invalid IP address field returned: %s",
__func__, ip_addrs[i]);
goto done;
}

call->ip_addrs[i] = g_strdup(split_ip_addr[0]);
call->prefix_lengths[i] =
g_strdup(n_parts > 1 ? split_ip_addr[1] : "");
}

call->protocol = protocol;
call->ifname = g_strdup(ifname);
call->ip_addr = g_strdup(split_ip_addr[0]);
call->dns_addrs = g_strdupv(dns_addrs);
call->gateways = g_strdupv(gateways);

Expand All @@ -180,6 +181,14 @@ static gboolean handle_settings(struct ril_data_call *call, char *type,
if (split_ip_addr)
g_strfreev(split_ip_addr);

if (result == FALSE && call->ip_addrs != NULL) {
for (i = 0; call->ip_addrs[i] != NULL; ++i) {
g_free(call->ip_addrs[i]);
g_free(call->prefix_lengths[i]);
}
g_free(call->ip_addrs);
g_free(call->prefix_lengths);
}

return result;
}
Expand Down
3 changes: 2 additions & 1 deletion gril/grilunsol.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ struct ril_data_call {
guint active;
guint protocol;
char *ifname;
gchar *ip_addr;
gchar **ip_addrs;
gchar **prefix_lengths;
gchar **dns_addrs;
gchar **gateways;
};
Expand Down