pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmo-netif/+/42464?usp=email )


Change subject: Introduce API osmo_icmpv6
......................................................................

Introduce API osmo_icmpv6

icmpv6.* files are copied from osmo-ggsn.git 1.14.0
(ebd1bf8adbc8d5bec9e36d70e3a78e8838226e7a).

Change-Id: I78ec7270c717af0a1b8ffd9398cd69ea7a0dbee2
---
M TODO-RELEASE
M include/osmocom/netif/Makefile.am
A include/osmocom/netif/icmpv6.h
M src/Makefile.am
A src/icmpv6.c
5 files changed, 323 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmo-netif refs/changes/64/42464/1

diff --git a/TODO-RELEASE b/TODO-RELEASE
index 7aebf84..89a240e 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -8,3 +8,4 @@
 # If any interfaces have been removed or changed since the last public 
release: c:r:0.
 #library       what                    description / commit summary line
 netif     add       osmo_ip_checksum API
+netif     add       osmo_icmpv6 API
diff --git a/include/osmocom/netif/Makefile.am 
b/include/osmocom/netif/Makefile.am
index f509221..1a0cce4 100644
--- a/include/osmocom/netif/Makefile.am
+++ b/include/osmocom/netif/Makefile.am
@@ -20,6 +20,7 @@
 osmonetif_HEADERS = \
        amr.h \
        datagram.h \
+       icmpv6.h \
        ip_checksum.h \
        ipa.h \
        ipa_unit.h \
diff --git a/include/osmocom/netif/icmpv6.h b/include/osmocom/netif/icmpv6.h
new file mode 100644
index 0000000..94d97fc
--- /dev/null
+++ b/include/osmocom/netif/icmpv6.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <netinet/in.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/endian.h>
+
+#define ICMPv6_OPT_TYPE_PREFIX_INFO 0x03
+
+struct osmo_icmpv6_hdr {
+       uint8_t type;
+       uint8_t code;
+       uint16_t csum;
+} __attribute__ ((packed));
+
+struct osmo_icmpv6_echo_hdr {
+       struct osmo_icmpv6_hdr hdr;
+       uint16_t ident;         /* Identifier */
+       uint16_t seq;           /* Sequence number */
+       uint8_t data[0];        /* Data */
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.1 */
+struct osmo_icmpv6_rsol_hdr {
+       struct osmo_icmpv6_hdr hdr;
+       uint32_t reserved;
+       uint8_t options[0];
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.2 */
+struct osmo_icmpv6_radv_hdr {
+       struct osmo_icmpv6_hdr hdr;
+       uint8_t cur_ho_limit;
+#if OSMO_IS_LITTLE_ENDIAN
+       uint8_t res:6,
+               m:1,
+               o:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above 
(libosmocore/contrib/struct_endianness.py) */
+       uint8_t o:1, m:1, res:6;
+#endif
+       uint16_t router_lifetime;
+       uint32_t reachable_time;
+       uint32_t retrans_timer;
+       uint8_t options[0];
+} __attribute__ ((packed));
+
+
+/* RFC4861 Section 4.6 */
+struct osmo_icmpv6_opt_hdr {
+       uint8_t type;
+       /* length in units of 8 octets, including type+len! */
+       uint8_t len;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.6.2 */
+struct osmo_icmpv6_opt_prefix {
+       struct osmo_icmpv6_opt_hdr hdr;
+       uint8_t prefix_len;
+#if OSMO_IS_LITTLE_ENDIAN
+       uint8_t res:6,
+               a:1,
+               l:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above 
(libosmocore/contrib/struct_endianness.py) */
+       uint8_t l:1, a:1, res:6;
+#endif
+       uint32_t valid_lifetime;
+       uint32_t preferred_lifetime;
+       uint32_t res2;
+       uint8_t prefix[16];
+} __attribute__ ((packed));
+
+/* RFC4861 Section 4.6.4 */
+struct osmo_icmpv6_opt_mtu {
+       struct osmo_icmpv6_opt_hdr hdr;
+       uint16_t reserved;
+       uint32_t mtu;
+} __attribute__ ((packed));
+
+uint16_t osmo_icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr 
*saddr,
+                                   const struct in6_addr *daddr);
+
+struct msgb *osmo_icmpv6_construct_rs(const struct in6_addr *saddr);
+struct msgb *osmo_icmpv6_construct_ra(const struct in6_addr *saddr,
+                                     const struct in6_addr *daddr,
+                                     const struct in6_addr *prefix,
+                                     uint32_t mtu);
+
+bool osmo_icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len);
+struct osmo_icmpv6_radv_hdr *osmo_icmpv6_validate_router_adv(const uint8_t 
*pack, unsigned len);
+
+#define foreach_icmpv6_opt(icmpv6_pkt, icmpv6_len, opt_hdr) \
+       for (opt_hdr = (struct osmo_icmpv6_opt_hdr *)(icmpv6_pkt)->options; \
+            (uint8_t *)(opt_hdr) + sizeof(struct osmo_icmpv6_opt_hdr) <= 
(((uint8_t *)(icmpv6_pkt)) + (icmpv6_len)); \
+            opt_hdr = (struct osmo_icmpv6_opt_hdr *)((uint8_t *)(opt_hdr) + 
(opt_hdr)->len) \
+           )
+
+/* RFC3307 link-local scope multicast address */
+extern const struct in6_addr osmo_icmpv6_all_router_mcast_addr;
+
+extern const uint8_t osmo_icmpv6_solicited_node_mcast_addr_prefix[13];
diff --git a/src/Makefile.am b/src/Makefile.am
index 4484e02..8c2b95b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,6 +15,7 @@
 
 libosmonetif_la_SOURCES = amr.c                        \
                          datagram.c            \
+                         icmpv6.c              \
                          ip_checksum.c         \
                          ipa.c                 \
                          ipa_keepalive.c       \
diff --git a/src/icmpv6.c b/src/icmpv6.c
new file mode 100644
index 0000000..69e03dd
--- /dev/null
+++ b/src/icmpv6.c
@@ -0,0 +1,215 @@
+/* Minimal ICMPv6 code for generating router advertisements as required by
+ * relevant 3GPP specs for a GGSN with IPv6 PDP contexts */
+
+/* (C) 2017 by Harald Welte <[email protected]>
+ *
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <netinet/in.h>
+#if defined(__FreeBSD__)
+#include <sys/types.h> /* FreeBSD 10.x needs this before ip6.h */
+#include <sys/endian.h>
+#endif
+#include <netinet/ip6.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/netif/ip_checksum.h>
+#include <osmocom/netif/icmpv6.h>
+
+/* 29.061 11.2.1.3.4 IPv6 Router Configuration Variables in GGSN */
+#define GGSN_MaxRtrAdvInterval 21600           /* 6 hours */
+#define GGSN_MinRtrAdvInterval 16200           /* 4.5 hours */
+#define GGSN_AdvValidLifetime  0xffffffff      /* infinite */
+#define GGSN_AdvPreferredLifetime 0xffffffff   /* infinite */
+
+/* RFC3307 link-local scope multicast address */
+const struct in6_addr osmo_icmpv6_all_router_mcast_addr = {
+       .s6_addr = { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }
+};
+
+
+/* RFC4291  link-local solicited-node multicast address, FF02:0:0:0:0:1:FF, 
104 bits = 13 bytes */
+const uint8_t osmo_icmpv6_solicited_node_mcast_addr_prefix[13] = {
+       0xff, 0x02, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x01,
+       0xFF
+};
+
+/* Prepends the ipv6 header and returns checksum content */
+uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
+                              const struct in6_addr *daddr)
+{
+       uint32_t len;
+       uint16_t skb_csum;
+       struct ip6_hdr *i6h;
+
+       /* checksum */
+       skb_csum = osmo_ip_checksum_csum_partial(msgb_data(msg), 
msgb_length(msg), 0);
+       len = msgb_length(msg);
+       skb_csum = osmo_ip_checksum_csum_ipv6_magic(saddr, daddr, len, 
IPPROTO_ICMPV6, skb_csum);
+
+       /* Push IPv6 header in front of ICMPv6 packet */
+       i6h = (struct ip6_hdr *) msgb_push(msg, sizeof(*i6h));
+       /* 4 bits version, 8 bits TC, 20 bits flow-ID */
+       i6h->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
+       i6h->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len);
+       i6h->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
+       i6h->ip6_ctlun.ip6_un1.ip6_un1_hlim = 255;
+       i6h->ip6_src = *saddr;
+       i6h->ip6_dst = *daddr;
+       return skb_csum;
+}
+
+/*! construct a RFC4861 compliant ICMPv6 router soliciation
+ *  \param[in] saddr Source IPv6 address for router advertisement
+ *  \param[in] daddr Destination IPv6 address for router advertisement IPv6 
header
+ *  \param[in] prefix The single prefix to be advertised (/64 implied!)
+ *  \returns callee-allocated message buffer containing router advertisement */
+struct msgb *osmo_icmpv6_construct_rs(const struct in6_addr *saddr)
+{
+       struct msgb *msg = msgb_alloc_headroom(512, 128, "IPv6 RS");
+       struct osmo_icmpv6_rsol_hdr *rs;
+       OSMO_ASSERT(msg);
+       rs = (struct osmo_icmpv6_rsol_hdr *) msgb_put(msg, sizeof(*rs));
+       rs->hdr.type = 133;     /* see RFC4861 4.1 */
+       rs->hdr.code = 0;       /* see RFC4861 4.1 */
+       rs->hdr.csum = 0;       /* updated below */
+       rs->reserved = 0;       /* see RFC4861 4.1 */
+
+       rs->hdr.csum = icmpv6_prepend_ip6hdr(msg, saddr, 
&osmo_icmpv6_all_router_mcast_addr);
+
+       return msg;
+}
+/*! construct a 3GPP 29.061 compliant router advertisement for a given prefix
+ *  \param[in] saddr Source IPv6 address for router advertisement
+ *  \param[in] daddr Destination IPv6 address for router advertisement IPv6 
header
+ *  \param[in] prefix The single prefix to be advertised (/64 implied!)
+ *  \returns callee-allocated message buffer containing router advertisement */
+struct msgb *osmo_icmpv6_construct_ra(const struct in6_addr *saddr,
+                                     const struct in6_addr *daddr,
+                                     const struct in6_addr *prefix,
+                                     uint32_t mtu)
+{
+       struct msgb *msg = msgb_alloc_headroom(512, 128, "IPv6 RA");
+       struct osmo_icmpv6_radv_hdr *ra;
+       struct osmo_icmpv6_opt_prefix *ra_opt_pref;
+       struct osmo_icmpv6_opt_mtu *ra_opt_mtu;
+
+       OSMO_ASSERT(msg);
+
+       ra = (struct osmo_icmpv6_radv_hdr *) msgb_put(msg, sizeof(*ra));
+       ra->hdr.type = 134;     /* see RFC4861 4.2 */
+       ra->hdr.code = 0;       /* see RFC4861 4.2 */
+       ra->hdr.csum = 0;       /* updated below */
+       ra->cur_ho_limit = 64;  /* seems reasonable? */
+       /* the GGSN shall leave the M-flag cleared in the Router
+        * Advertisement messages */
+       ra->m = 0;
+       /* The GGSN may set the O-flag if there are additional
+        * configuration parameters that need to be fetched by the MS */
+       ra->o = 0;              /* no DHCPv6 */
+       ra->res = 0;
+       /* RFC4861 Default: 3 * MaxRtrAdvInterval */
+       ra->router_lifetime = htons(3*GGSN_MaxRtrAdvInterval);
+       ra->reachable_time = 0; /* Unspecified */
+
+       /* RFC4861 Section 4.6.2 */
+       ra_opt_pref = (struct osmo_icmpv6_opt_prefix *) msgb_put(msg, 
sizeof(*ra_opt_pref));
+       ra_opt_pref->hdr.type = 3;      /* RFC4861 4.6.2 */
+       ra_opt_pref->hdr.len = 4;       /* RFC4861 4.6.2 */
+       ra_opt_pref->prefix_len = 64;   /* only prefix length as per 3GPP */
+       /* The Prefix is contained in the Prefix Information Option of
+        * the Router Advertisements and shall have the A-flag set
+        * and the L-flag cleared */
+       ra_opt_pref->a = 1;
+       ra_opt_pref->l = 0;
+       ra_opt_pref->res = 0;
+       /*  The lifetime of the prefix shall be set to infinity */
+       ra_opt_pref->valid_lifetime = htonl(GGSN_AdvValidLifetime);
+       ra_opt_pref->preferred_lifetime = htonl(GGSN_AdvPreferredLifetime);
+       ra_opt_pref->res2 = 0;
+       memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
+
+       /* RFC4861 Section 4.6.4, MTU */
+       ra_opt_mtu = (struct osmo_icmpv6_opt_mtu *) msgb_put(msg, 
sizeof(*ra_opt_mtu));
+       ra_opt_mtu->hdr.type = 5;       /* RFC4861 4.6.4 */
+       ra_opt_mtu->hdr.len = 1;        /* RFC4861 4.6.4 */
+       ra_opt_mtu->reserved = 0;
+       ra_opt_mtu->mtu = htonl(mtu);
+
+       /* The Prefix is contained in the Prefix Information Option of
+        * the Router Advertisements and shall have the A-flag set
+        * and the L-flag cleared */
+       ra_opt_pref->a = 1;
+       ra_opt_pref->l = 0;
+       ra_opt_pref->res = 0;
+       /*  The lifetime of the prefix shall be set to infinity */
+       ra_opt_pref->valid_lifetime = htonl(GGSN_AdvValidLifetime);
+       ra_opt_pref->preferred_lifetime = htonl(GGSN_AdvPreferredLifetime);
+       ra_opt_pref->res2 = 0;
+       memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
+
+       /* checksum */
+       ra->hdr.csum = icmpv6_prepend_ip6hdr(msg, saddr, daddr);
+
+       return msg;
+}
+
+/* Validate an ICMPv6 router solicitation according to RFC4861 6.1.1 */
+bool osmo_icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
+{
+       const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+       //const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + 
sizeof(*ip6h));
+
+       /* Hop limit field must have 255 */
+       if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255)
+               return false;
+       /* FIXME: ICMP checksum is valid */
+       /* ICMP length (derived from IP length) is 8 or more octets */
+       if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 8)
+               return false;
+       /* FIXME: All included options have a length > 0 */
+       /* FIXME: If IP source is unspecified, no source link-layer addr option 
*/
+       return true;
+}
+
+/* Validate an ICMPv6 router advertisement according to RFC4861 6.1.2.
+   Returns pointer packet header on success, NULL otherwise. */
+struct osmo_icmpv6_radv_hdr *osmo_icmpv6_validate_router_adv(const uint8_t 
*pack, unsigned len)
+{
+       const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+       const struct osmo_icmpv6_hdr *ic6h = (struct osmo_icmpv6_hdr *) (pack + 
sizeof(*ip6h));
+
+       /* ICMP length (derived from IP length) is 16 or more octets */
+       if (len < sizeof(*ip6h) + 16)
+               return NULL;
+
+       if (ic6h->type != 134) /* router advertismenet type */
+               return NULL;
+
+       /*Routers must use their link-local address */
+       if (!IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src))
+               return NULL;
+       /* Hop limit field must have 255 */
+       if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255)
+               return NULL;
+       /* ICMP Code is 0 */
+       if (ic6h->code != 0)
+               return NULL;
+       /* ICMP length (derived from IP length) is 16 or more octets */
+       if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 16)
+               return NULL;
+       /* FIXME: All included options have a length > 0 */
+       /* FIXME: If IP source is unspecified, no source link-layer addr option 
*/
+       return (struct osmo_icmpv6_radv_hdr *)ic6h;
+}

--
To view, visit https://gerrit.osmocom.org/c/libosmo-netif/+/42464?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: libosmo-netif
Gerrit-Branch: master
Gerrit-Change-Id: I78ec7270c717af0a1b8ffd9398cd69ea7a0dbee2
Gerrit-Change-Number: 42464
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <[email protected]>

Reply via email to