--- src/gprs.c | 285 +++++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 213 insertions(+), 72 deletions(-)
diff --git a/src/gprs.c b/src/gprs.c index fa56f62..5345fa1 100644 --- a/src/gprs.c +++ b/src/gprs.c @@ -34,6 +34,8 @@ #include <net/route.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> #include <glib.h> #include <gdbus.h> @@ -99,10 +101,12 @@ struct ofono_gprs { struct ofono_atom *atom; }; +struct pri_context; + struct ofono_gprs_context { struct ofono_gprs *gprs; + struct pri_context *pri; enum ofono_gprs_context_type type; - ofono_bool_t inuse; const struct ofono_gprs_context_driver *driver; void *driver_data; struct ofono_atom *atom; @@ -111,10 +115,11 @@ struct ofono_gprs_context { struct context_settings { enum ofono_gprs_context_type type; char *interface; - gboolean static_ip; + enum ofono_gprs_addrconf method; char *ip; char *netmask; char *gateway; + char *ipv6; char **dns; char *proxy; }; @@ -226,6 +231,8 @@ static const char *gprs_proto_to_string(enum ofono_gprs_proto proto) return "ip"; case OFONO_GPRS_PROTO_IPV6: return "ipv6"; + case OFONO_GPRS_PROTO_IPV4V6: + return "ipv4v6"; }; return NULL; @@ -240,11 +247,28 @@ static gboolean gprs_proto_from_string(const char *str, } else if (g_str_equal(str, "ipv6")) { *proto = OFONO_GPRS_PROTO_IPV6; return TRUE; + } else if (g_str_equal(str, "ipv4v6")) { + *proto = OFONO_GPRS_PROTO_IPV4V6; + return TRUE; } return FALSE; } +static const char *gprs_addrconf_to_string(enum ofono_gprs_addrconf method) +{ + switch (method) { + case OFONO_GPRS_ADDRCONF_NONE: + return NULL; + case OFONO_GPRS_ADDRCONF_STATIC: + return "static"; + case OFONO_GPRS_ADDRCONF_DHCP: + return "dhcp"; + } + + return NULL; +} + static unsigned int gprs_cid_alloc(struct ofono_gprs *gprs) { return idmap_alloc(gprs->cid_map); @@ -273,13 +297,13 @@ static gboolean assign_context(struct pri_context *ctx) for (l = ctx->gprs->context_drivers; l; l = l->next) { struct ofono_gprs_context *gc = l->data; - if (gc->inuse == TRUE) + if (gc->pri != NULL) continue; if (gc->type == OFONO_GPRS_CONTEXT_TYPE_ANY || gc->type == ctx->type) { ctx->context_driver = gc; - ctx->context_driver->inuse = TRUE; + gc->pri = ctx; return TRUE; } } @@ -294,7 +318,7 @@ static void release_context(struct pri_context *ctx) gprs_cid_release(ctx->gprs, ctx->context.cid); ctx->context.cid = 0; - ctx->context_driver->inuse = FALSE; + ctx->context_driver->pri = NULL; ctx->context_driver = NULL; } @@ -313,14 +337,22 @@ static struct pri_context *gprs_context_by_path(struct ofono_gprs *gprs, return NULL; } -static void context_settings_free(struct context_settings *settings) +static void pri_context_settings_free(struct pri_context *ctx) { + struct context_settings *settings = ctx->settings; + + if (settings == NULL) + return; + + ctx->settings = NULL; + g_free(settings->interface); g_free(settings->ip); g_free(settings->netmask); g_free(settings->gateway); g_strfreev(settings->dns); g_free(settings->proxy); + g_free(settings->ipv6); g_free(settings); } @@ -359,12 +391,10 @@ static void context_settings_append_variant(struct context_settings *settings, goto done; } - if (settings->static_ip == TRUE) - method = "static"; - else - method = "dhcp"; - - ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method); + method = gprs_addrconf_to_string(settings->method); + if (method != NULL) + ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, + &method); if (settings->ip) ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING, @@ -378,6 +408,10 @@ static void context_settings_append_variant(struct context_settings *settings, ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING, &settings->gateway); + if (settings->ipv6 != NULL) + ofono_dbus_dict_append(&array, "IPv6Address", DBUS_TYPE_STRING, + &settings->ipv6); + if (settings->dns) ofono_dbus_dict_append_array(&array, "DomainNameServers", DBUS_TYPE_STRING, @@ -555,6 +589,65 @@ done: close(sk); } +static void pri_setipv6(const char *interface, const char *address, + ofono_bool_t up) +{ + struct ifreq ifr; + struct nlmsghdr *nlh; + struct ifaddrmsg *ifa; + struct rtattr *rta; + struct sockaddr_nl sa = {.nl_family = AF_NETLINK }; + int sk; + char buf[256]; + + if (interface == NULL) + return; + + sk = socket(PF_INET6, SOCK_DGRAM, 0); + if (sk < 0) + return; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, interface, IFNAMSIZ); + + if (ioctl(sk, SIOCGIFINDEX, &ifr) < 0) + goto done; + + close(sk); + + sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sk < 0) + return; + + nlh = (struct nlmsghdr *)buf; + + nlh->nlmsg_type = up ? RTM_NEWADDR : RTM_DELADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_seq = 0; + + ifa = NLMSG_DATA(nlh); + + ifa->ifa_family = AF_INET6; + ifa->ifa_prefixlen = 64; + ifa->ifa_flags = IFA_F_PERMANENT | IFA_F_NODAD; + ifa->ifa_scope = RT_SCOPE_LINK; + ifa->ifa_index = ifr.ifr_ifindex; + + rta = IFA_RTA(ifa); + + rta->rta_type = IFA_ADDRESS; + rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); + inet_pton(AF_INET6, address, RTA_DATA(rta)); + + nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa) + rta->rta_len); + + sendto(sk, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); + +done: + close(sk); +} + static void pri_setproxy(const char *interface, const char *proxy) { struct rtentry rt; @@ -595,68 +688,54 @@ static void pri_setproxy(const char *interface, const char *proxy) static void pri_reset_context_settings(struct pri_context *ctx) { - char *interface; + struct context_settings *settings = ctx->settings; - if (ctx->settings == NULL) + if (settings == NULL) return; - interface = ctx->settings->interface; - ctx->settings->interface = NULL; - - context_settings_free(ctx->settings); - ctx->settings = NULL; - - pri_context_signal_settings(ctx); - if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) { - pri_setaddr(interface, NULL); + pri_setaddr(settings->interface, NULL); g_free(ctx->proxy_host); ctx->proxy_host = NULL; ctx->proxy_port = 0; } - pri_ifupdown(interface, FALSE); + if (settings->ipv6 != NULL) + pri_setipv6(settings->interface, settings->ipv6, FALSE); - g_free(interface); + pri_ifupdown(settings->interface, FALSE); + + pri_context_settings_free(ctx); + pri_context_signal_settings(ctx); } -static void pri_update_context_settings(struct pri_context *ctx, - const char *interface, - ofono_bool_t static_ip, - const char *ip, const char *netmask, - const char *gateway, const char **dns) +static void pri_update_context_settings(struct pri_context *ctx) { - if (ctx->settings) - context_settings_free(ctx->settings); + struct context_settings *settings = ctx->settings; - ctx->settings = g_try_new0(struct context_settings, 1); - if (ctx->settings == NULL) + if (settings == NULL || settings->interface == NULL) return; - ctx->settings->type = ctx->type; - - ctx->settings->interface = g_strdup(interface); - ctx->settings->static_ip = static_ip; - ctx->settings->ip = g_strdup(ip); - ctx->settings->netmask = g_strdup(netmask); - ctx->settings->gateway = g_strdup(gateway); - ctx->settings->dns = g_strdupv((char **)dns); + settings->type = ctx->type; if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS && ctx->message_proxy) - ctx->settings->proxy = g_strdup(ctx->message_proxy); + settings->proxy = g_strdup(ctx->message_proxy); + + if (settings->ipv6 != NULL) + pri_setipv6(settings->interface, settings->ipv6, TRUE); - pri_ifupdown(interface, TRUE); + pri_ifupdown(settings->interface, TRUE); if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) { pri_parse_proxy(ctx, ctx->message_proxy); DBG("proxy %s port %u", ctx->proxy_host, ctx->proxy_port); - pri_setaddr(interface, ip); + pri_setaddr(settings->interface, settings->ip); if (ctx->proxy_host) - pri_setproxy(interface, ctx->proxy_host); + pri_setproxy(settings->interface, ctx->proxy_host); } pri_context_signal_settings(ctx); @@ -728,25 +807,19 @@ static DBusMessage *pri_get_properties(DBusConnection *conn, return reply; } -static void pri_activate_callback(const struct ofono_error *error, - const char *interface, - ofono_bool_t static_ip, - const char *ip, const char *netmask, - const char *gateway, const char **dns, - void *data) +static void pri_activate_callback(const struct ofono_error *error, void *data) { struct pri_context *ctx = data; DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t value; - DBG("%p %s", ctx, interface); - if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Activating context failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&ctx->pending, __ofono_error_failed(ctx->pending)); release_context(ctx); + pri_context_settings_free(ctx); return; } @@ -754,14 +827,7 @@ static void pri_activate_callback(const struct ofono_error *error, __ofono_dbus_pending_reply(&ctx->pending, dbus_message_new_method_return(ctx->pending)); - /* - * If we don't have the interface, don't bother emitting any settings, - * as nobody can make use of them - */ - if (interface != NULL) - pri_update_context_settings(ctx, interface, static_ip, - ip, netmask, gateway, dns); - + pri_update_context_settings(ctx); value = ctx->active; ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, @@ -1078,15 +1144,22 @@ static DBusMessage *pri_set_property(DBusConnection *conn, return __ofono_error_attach_in_progress(msg); if (value) { - if (assign_context(ctx) == FALSE) - return __ofono_error_not_implemented(msg); + pri_context_settings_free(ctx); + ctx->settings = g_try_new0(struct context_settings, 1); + if (ctx->settings == NULL) + return __ofono_error_failed(msg); + + assign_context(ctx); } gc = ctx->context_driver; if (gc == NULL || gc->driver == NULL || gc->driver->activate_primary == NULL || - gc->driver->deactivate_primary == NULL) + gc->driver->deactivate_primary == NULL) { + release_context(ctx); + pri_context_settings_free(ctx); return __ofono_error_not_implemented(msg); + } ctx->pending = dbus_message_ref(msg); @@ -1210,15 +1283,9 @@ static void pri_context_destroy(gpointer userdata) { struct pri_context *ctx = userdata; - if (ctx->settings) { - context_settings_free(ctx->settings); - ctx->settings = NULL; - } - + pri_context_settings_free(ctx); g_free(ctx->proxy_host); - g_free(ctx->path); - g_free(ctx); } @@ -2119,6 +2186,8 @@ static void gprs_context_remove(struct ofono_atom *atom) if (gc == NULL) return; + release_context(gc->pri); + if (gc->driver && gc->driver->remove) gc->driver->remove(gc); @@ -2191,6 +2260,78 @@ void ofono_gprs_context_set_type(struct ofono_gprs_context *gc, gc->type = type; } +void ofono_gprs_context_set_interface(struct ofono_gprs_context *gc, + const char *interface) +{ + struct context_settings *settings = gc->pri->settings; + + g_free(settings->interface); + settings->interface = g_strdup(interface); +} + +void ofono_gprs_context_set_ip_addrconf(struct ofono_gprs_context *gc, + enum ofono_gprs_addrconf method) +{ + struct context_settings *settings = gc->pri->settings; + + settings->method = method; +} + +void ofono_gprs_context_set_ip_address(struct ofono_gprs_context *gc, + const char *address) +{ + struct context_settings *settings = gc->pri->settings; + + g_free(settings->ip); + settings->ip = g_strdup(address); +} + +void ofono_gprs_context_set_ip_netmask(struct ofono_gprs_context *gc, + const char *netmask) +{ + struct context_settings *settings = gc->pri->settings; + + g_free(settings->netmask); + settings->netmask = g_strdup(netmask); +} + +void ofono_gprs_context_set_ip_gateway(struct ofono_gprs_context *gc, + const char *gateway) +{ + struct context_settings *settings = gc->pri->settings; + + g_free(settings->gateway); + settings->gateway = g_strdup(gateway); +} + +void ofono_gprs_context_set_ipv6_address(struct ofono_gprs_context *gc, + const char *address) +{ + struct context_settings *settings = gc->pri->settings; + uint8_t *addr = alloca(sizeof(struct in6_addr)); + char *straddr = alloca(INET6_ADDRSTRLEN); + + if (!inet_pton(AF_INET6, address, addr)) + return; + + memset(addr, 0, 8); + addr[0] = 0xfe; + addr[1] = 0x80; + inet_ntop(AF_INET6, addr, straddr, INET6_ADDRSTRLEN); + + g_free(settings->ipv6); + settings->ipv6 = g_strdup(straddr); +} + +void ofono_gprs_context_set_dns_servers(struct ofono_gprs_context *gc, + const char **dns) +{ + struct context_settings *settings = gc->pri->settings; + + g_strfreev(settings->dns); + settings->dns = g_strdupv((char **)dns); +} + int ofono_gprs_driver_register(const struct ofono_gprs_driver *d) { DBG("driver: %p, name: %s", d, d->name); -- 1.7.1 _______________________________________________ ofono mailing list ofono@ofono.org http://lists.ofono.org/listinfo/ofono