---
 Makefile.am          |    2 +-
 gatchat/ppp.h        |    8 +
 gatchat/ppp_ipv6cp.c |  377 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 386 insertions(+), 1 deletions(-)
 create mode 100644 gatchat/ppp_ipv6cp.c

diff --git a/Makefile.am b/Makefile.am
index 83b7737..44fdfff 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,7 +76,7 @@ gatchat_sources = gatchat/gatchat.h gatchat/gatchat.c \
                                gatchat/ppp.h gatchat/ppp_cp.h \
                                gatchat/ppp_cp.c gatchat/ppp_lcp.c \
                                gatchat/ppp_auth.c gatchat/ppp_net.c \
-                               gatchat/ppp_ipcp.c
+                               gatchat/ppp_ipcp.c gatchat/ppp_ipv6cp.c
 
 gisi_sources = gisi/client.c gisi/client.h gisi/common.h \
                                gisi/iter.c gisi/iter.h \
diff --git a/gatchat/ppp.h b/gatchat/ppp.h
index a20fe85..718575b 100644
--- a/gatchat/ppp.h
+++ b/gatchat/ppp.h
@@ -24,7 +24,9 @@
 #define LCP_PROTOCOL   0xc021
 #define CHAP_PROTOCOL  0xc223
 #define IPCP_PROTO     0x8021
+#define IPV6CP_PROTO   0x8057
 #define PPP_IP_PROTO   0x0021
+#define PPP_IPV6_PROTO 0x0057
 #define MD5            5
 
 #define DBG(p, fmt, arg...) do {                               \
@@ -95,6 +97,12 @@ void ipcp_free(struct pppcp_data *data);
 void ipcp_set_server_info(struct pppcp_data *ipcp, guint32 peer_addr,
                                guint32 dns1, guint32 dns2);
 
+/* IPv6 CP related functions */
+struct pppcp_data *ipv6cp_new(GAtPPP *ppp, gboolean is_server,
+                                       const char *local, const char *peer,
+                                       GError **error);
+void ipv6cp_free(struct pppcp_data *data);
+
 /* CHAP related functions */
 struct ppp_chap *ppp_chap_new(GAtPPP *ppp, guint8 method);
 void ppp_chap_free(struct ppp_chap *chap);
diff --git a/gatchat/ppp_ipv6cp.c b/gatchat/ppp_ipv6cp.c
new file mode 100644
index 0000000..ec89fb7
--- /dev/null
+++ b/gatchat/ppp_ipv6cp.c
@@ -0,0 +1,377 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gatppp.h"
+#include "ppp.h"
+
+#define IPV6CP_SUPPORTED_CODES ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CODE_REJECT))
+
+#define OPTION_COPY(_options, _len, _req, _type, _var, _opt_len)       \
+       if (_req) {                                                     \
+               _options[_len] = _type;                                 \
+               _options[_len + 1] = _opt_len + 2;                      \
+               memcpy(_options + _len + 2, _var, _opt_len);            \
+               _len += _opt_len + 2;                                   \
+       }
+
+/* We request only IPv6 Interface Id */
+#define IPV6CP_MAX_CONFIG_OPTION_SIZE  10
+#define IPV6CP_MAX_FAILURE             3
+#define IPV6CP_ERROR ipv6cp_error_quark()
+
+enum ipv6cp_option_types {
+       IPV6CP_INTERFACE_ID = 1,
+};
+
+struct ipv6cp_data {
+       guint8 options[IPV6CP_MAX_CONFIG_OPTION_SIZE];
+       guint16 options_len;
+       guint8 req_options;
+       guint64 local_addr;
+       guint64 peer_addr;
+       gboolean is_server;
+};
+
+static GQuark ipv6cp_error_quark(void)
+{
+       return g_quark_from_static_string("ipv6cp");
+}
+
+static void ipv6cp_generate_config_options(struct ipv6cp_data *ipv6cp)
+{
+       guint16 len = 0;
+
+       OPTION_COPY(ipv6cp->options, len,
+                       ipv6cp->req_options & IPV6CP_INTERFACE_ID,
+                       IPV6CP_INTERFACE_ID, &ipv6cp->local_addr,
+                       sizeof(ipv6cp->local_addr));
+
+       ipv6cp->options_len = len;
+}
+
+static void ipv6cp_reset_config_options(struct ipv6cp_data *ipv6cp)
+{
+       ipv6cp->req_options = IPV6CP_INTERFACE_ID;
+
+       ipv6cp_generate_config_options(ipv6cp);
+}
+
+static void ipv6cp_up(struct pppcp_data *pppcp)
+{
+
+}
+
+static void ipv6cp_down(struct pppcp_data *pppcp)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+
+       ipv6cp_reset_config_options(ipv6cp);
+
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+}
+
+static void ipv6cp_finished(struct pppcp_data *pppcp)
+{
+
+}
+
+static enum rcr_result ipv6cp_server_rcr(struct ipv6cp_data *ipv6cp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ppp_option_iter iter;
+       guint8 nak_options[IPV6CP_MAX_CONFIG_OPTION_SIZE];
+       guint16 len = 0;
+       guint8 *rej_options = NULL;
+       guint16 rej_len = 0;
+       guint64 addr;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               guint8 type = ppp_option_iter_get_type(&iter);
+               const void *data = ppp_option_iter_get_data(&iter);
+
+               switch (type) {
+               case IPV6CP_INTERFACE_ID:
+                       memcpy(&addr, data, sizeof(addr));
+
+                       OPTION_COPY(nak_options, len,
+                                       addr != ipv6cp->peer_addr || addr == 0,
+                                       type, &ipv6cp->peer_addr,
+                                       ppp_option_iter_get_length(&iter));
+                       break;
+               default:
+                       if (rej_options == NULL) {
+                               guint16 max_len = ntohs(packet->length) - 4;
+                               rej_options = g_new0(guint8, max_len);
+                       }
+
+                       OPTION_COPY(rej_options, rej_len, rej_options != NULL,
+                                       type, data,
+                                       ppp_option_iter_get_length(&iter));
+                       break;
+               }
+       }
+
+       if (rej_len > 0) {
+               *new_len = rej_len;
+               *new_options = rej_options;
+
+               return RCR_REJECT;
+       }
+
+       if (len > 0) {
+               *new_len = len;
+               *new_options = g_memdup(nak_options, len);
+
+               return RCR_NAK;
+       }
+
+       return RCR_ACCEPT;
+}
+
+static enum rcr_result ipv6cp_client_rcr(struct ipv6cp_data *ipv6cp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ppp_option_iter iter;
+       guint8 *options = NULL;
+       guint8 len = 0;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               guint8 type = ppp_option_iter_get_type(&iter);
+               const void *data = ppp_option_iter_get_data(&iter);
+
+               switch (type) {
+               case IPV6CP_INTERFACE_ID:
+                       memcpy(&ipv6cp->peer_addr, data,
+                                       sizeof(ipv6cp->peer_addr));
+                       if (ipv6cp->peer_addr != 0)
+                               break;
+                       /*
+                        * Fall through, reject zero Interface ID
+                        */
+                       break;
+               default:
+                       if (options == NULL) {
+                               guint16 max_len = ntohs(packet->length) - 4;
+                               options = g_new0(guint8, max_len);
+                       }
+
+                       OPTION_COPY(options, len, options != NULL,
+                                       type, data,
+                                       ppp_option_iter_get_length(&iter));
+                       break;
+               }
+       }
+
+       if (len > 0) {
+               *new_len = len;
+               *new_options = options;
+
+               return RCR_REJECT;
+       }
+
+       return RCR_ACCEPT;
+}
+
+static enum rcr_result ipv6cp_rcr(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+
+       if (ipv6cp->is_server)
+               return ipv6cp_server_rcr(ipv6cp, packet, new_options, new_len);
+       else
+               return ipv6cp_client_rcr(ipv6cp, packet, new_options, new_len);
+}
+
+static void ipv6cp_rca(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       if (ipv6cp->is_server)
+               return;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IPV6CP_INTERFACE_ID:
+                       memcpy(&ipv6cp->local_addr, data,
+                                       sizeof(ipv6cp->local_addr));
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void ipv6cp_rcn_nak(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       if (ipv6cp->is_server)
+               return;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IPV6CP_INTERFACE_ID:
+                       ipv6cp->req_options |= IPV6CP_INTERFACE_ID;
+                       memcpy(&ipv6cp->local_addr, data,
+                               sizeof(ipv6cp->local_addr));
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ipv6cp_generate_config_options(ipv6cp);
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+}
+
+static void ipv6cp_rcn_rej(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IPV6CP_INTERFACE_ID:
+                       ipv6cp->req_options &= ~IPV6CP_INTERFACE_ID;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ipv6cp_generate_config_options(ipv6cp);
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+}
+
+struct pppcp_proto ipv6cp_proto = {
+       .proto                  = IPV6CP_PROTO,
+       .name                   = "ipv6cp",
+       .supported_codes        = IPV6CP_SUPPORTED_CODES,
+       .this_layer_up          = ipv6cp_up,
+       .this_layer_down        = ipv6cp_down,
+       .this_layer_finished    = ipv6cp_finished,
+       .rca                    = ipv6cp_rca,
+       .rcn_nak                = ipv6cp_rcn_nak,
+       .rcn_rej                = ipv6cp_rcn_rej,
+       .rcr                    = ipv6cp_rcr,
+};
+
+struct pppcp_data *ipv6cp_new(GAtPPP *ppp, gboolean is_server,
+                                       const char *local, const char *peer,
+                                       GError **error)
+{
+       struct ipv6cp_data *ipv6cp;
+       struct pppcp_data *pppcp;
+       struct in6_addr local_addr;
+       struct in6_addr peer_addr;
+
+       if (local == NULL)
+               memset(&local_addr, 0, sizeof(local_addr));
+       else if (inet_pton(AF_INET6, local, &local_addr) != 1) {
+               g_set_error(error, IPV6CP_ERROR, errno,
+                               "Unable to set local Interface ID: %s",
+                               strerror(errno));
+               return NULL;
+       }
+
+       if (peer == NULL)
+               memset(&peer_addr, 0, sizeof(peer_addr));
+       else if (inet_pton(AF_INET6, peer, &peer_addr) != 1) {
+               g_set_error(error, IPV6CP_ERROR, errno,
+                               "Unable to set peer Interface ID: %s",
+                               g_strerror(errno));
+               return NULL;
+       }
+
+       ipv6cp = g_try_new0(struct ipv6cp_data, 1);
+       if (ipv6cp == NULL)
+               return NULL;
+
+       pppcp = pppcp_new(ppp, &ipv6cp_proto, FALSE, IPV6CP_MAX_FAILURE);
+       if (pppcp == NULL) {
+               g_free(ipv6cp);
+               return NULL;
+       }
+
+       memcpy(&ipv6cp->local_addr, &local_addr.s6_addr[8],
+                                               sizeof(ipv6cp->local_addr));
+       memcpy(&ipv6cp->peer_addr, &peer_addr.s6_addr[8],
+                                               sizeof(ipv6cp->peer_addr));
+       ipv6cp->is_server = is_server;
+
+       pppcp_set_data(pppcp, ipv6cp);
+
+       ipv6cp_reset_config_options(ipv6cp);
+
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+
+       return pppcp;
+}
+
+void ipv6cp_free(struct pppcp_data *data)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(data);
+
+       g_free(ipv6cp);
+       pppcp_free(data);
+}
-- 
1.7.5.4

_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono

Reply via email to