---
 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

Reply via email to