pespin submitted this change.

View Change

Approvals: Jenkins Builder: Verified laforge: Looks good to me, but someone else must approve fixeria: Looks good to me, but someone else must approve osmith: Looks good to me, but someone else must approve pespin: Looks good to me, approved
ggsn: Avoid forwarding IPv6 solicited-node multicast addr to tun device

There's no need to forward those. Morevoer, they cannot be forwarded
when using gtpu kernel mode since it doesn't support reinjecting packets
we got from it, so avoid erroring out when trying to inject them later
on.

Related: OS#6600
Related: OS#6382
Change-Id: Iac8f14083620d86f3872aa951930fbe8f680ff24
---
M ggsn/ggsn.c
M lib/icmpv6.c
M lib/icmpv6.h
3 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index c73a46d..a02b97f 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -782,10 +782,15 @@
return -1;
}

- /* daddr: all-routers multicast addr */
- if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
- return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
- &apn->v6_lladdr, apn->cfg.mtu, pack, len);
+ if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst)) {
+ /* daddr: all-routers multicast addr */
+ if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
+ return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
+ &apn->v6_lladdr, apn->cfg.mtu, pack, len);
+ /* daddr: solicited-node multicast addr */
+ if (memcmp(&ip6h->ip6_dst.s6_addr, solicited_node_mcast_addr_prefix, sizeof(solicited_node_mcast_addr_prefix)) == 0)
+ return handle_solicited_node_mcast(pack, len);
+ }
break;
case 4:
peer = pdp_get_peer_ipv(pdp, false);
diff --git a/lib/icmpv6.c b/lib/icmpv6.c
index e74fb70..4226fc0 100644
--- a/lib/icmpv6.c
+++ b/lib/icmpv6.c
@@ -42,6 +42,14 @@
.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 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)
@@ -180,6 +188,25 @@
return true;
}

+/* Validate an ICMPv6 neighbor solicitation according to RFC4861 7.1.1 */
+static bool icmpv6_validate_neigh_solicit(const uint8_t *pack, unsigned len)
+{
+ const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+
+ /* 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 24 or more octets */
+ if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 24)
+ return false;
+ /* FIXME: All included options have a length > 0 */
+ /* FIXME: If the IP source address is the unspecified address, the IP
+ * destination address is a solicited-node multicast address. */
+ /* 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 icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned len)
@@ -263,3 +290,46 @@
}
return 0;
}
+
+/* handle incoming packets to the solicited-node multicast address */
+int handle_solicited_node_mcast(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));
+
+ if (len < sizeof(*ip6h)) {
+ LOGP(DICMP6, LOGL_NOTICE, "Packet too short: %u bytes\n", len);
+ return -1;
+ }
+
+ /* we only treat ICMPv6 here */
+ if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_ICMPV6) {
+ LOGP(DICMP6, LOGL_DEBUG, "Ignoring non-ICMP solicited-node mcast\n");
+ return 0;
+ }
+
+ if (len < sizeof(*ip6h) + sizeof(*ic6h)) {
+ LOGP(DICMP6, LOGL_NOTICE, "Short ICMPv6 packet: %s\n", osmo_hexdump(pack, len));
+ return -1;
+ }
+
+ switch (ic6h->type) {
+ case 135: /* Neighbor Solicitation. RFC2461, RFC2462 */
+ if (ic6h->code != 0) {
+ LOGP(DICMP6, LOGL_NOTICE, "ICMPv6 type 135 but code %d\n", ic6h->code);
+ return -1;
+ }
+ if (!icmpv6_validate_neigh_solicit(pack, len)) {
+ LOGP(DICMP6, LOGL_NOTICE, "Invalid Neighbor Solicitation: %s\n",
+ osmo_hexdump(pack, len));
+ return -1;
+ }
+ /* RFC 2462: Ignore Neighbor (Duplicate Address Detection) */
+ LOGP(DICMP6, LOGL_DEBUG, "Ignoring Rx ICMPv6 Neighbor Soliciation: %s\n", osmo_hexdump(pack, len));
+ break;
+ default:
+ LOGP(DICMP6, LOGL_DEBUG, "Unknown ICMPv6 type %u\n", ic6h->type);
+ break;
+ }
+ return 0;
+}
diff --git a/lib/icmpv6.h b/lib/icmpv6.h
index 1040600..2469ed0 100644
--- a/lib/icmpv6.h
+++ b/lib/icmpv6.h
@@ -98,9 +98,12 @@
const struct in6_addr *own_ll_addr,
uint32_t mtu,
const uint8_t *pack, unsigned len);
+int handle_solicited_node_mcast(const uint8_t *pack, unsigned len);

struct icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned len);


/* RFC3307 link-local scope multicast address */
extern const struct in6_addr all_router_mcast_addr;
+
+extern const uint8_t solicited_node_mcast_addr_prefix[13];

To view, visit change 38505. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: merged
Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: Iac8f14083620d86f3872aa951930fbe8f680ff24
Gerrit-Change-Number: 38505
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis@fe80.eu>
Gerrit-Reviewer: osmith <osmith@sysmocom.de>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>