diff options
author | Harald Welte <laforge@gnumonks.org> | 2017-09-24 20:00:34 +0800 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2017-09-24 20:51:47 +0800 |
commit | f85fe9720be14acc1038ed400654d8ce336274a7 (patch) | |
tree | e12f5279f153547a1d5b433bfbd62c9ef7868831 | |
parent | fed598f41d85cef578925a4be6ce96a2c6afd3b3 (diff) |
ICMPv6: Send router advertisement from own link-local address
I'm not quite sure how I ended up doing this, but for some strange
reason the code before this commit is sending the ICMPv6 Router
Advertisements from some weird non-standard source address. This is
a violation of RFC4861 which clearly states that the source address
of router advertisements "MUST be the link-local address assigned to the
interface from which this message is sent."
Change-Id: Ib444af70fc8f0b433d371281601fd5a37b29039e
-rw-r--r-- | ggsn/ggsn.c | 17 | ||||
-rw-r--r-- | ggsn/ggsn.h | 3 | ||||
-rw-r--r-- | ggsn/icmpv6.c | 12 | ||||
-rw-r--r-- | ggsn/icmpv6.h | 3 | ||||
-rw-r--r-- | lib/tun.c | 32 | ||||
-rw-r--r-- | lib/tun.h | 2 |
6 files changed, 59 insertions, 10 deletions
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 46850a6..d521e57 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -202,6 +202,16 @@ int apn_start(struct apn_ctx *apn) apn->tun.cfg.ipup_script); tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script); } + + if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) { + if (tun_ipv6_linklocal_get(apn->tun.tun, &apn->v6_lladdr) < 0) { + LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of " + "interface: %s\n", strerror(errno)); + apn_stop(apn, false); + return -1; + } + } + /* set back-pointer from TUN device to APN */ apn->tun.tun->priv = apn; break; @@ -573,6 +583,11 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len) { struct iphdr *iph = (struct iphdr *)pack; struct ip6_hdr *ip6h = (struct ip6_hdr *)pack; + struct tun_t *tun = (struct tun_t *)pdp->ipif; + struct apn_ctx *apn = tun->priv; + + OSMO_ASSERT(tun); + OSMO_ASSERT(apn); LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n"); @@ -580,7 +595,7 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len) case 6: /* daddr: all-routers multicast addr */ if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr)) - return handle_router_mcast(pdp->gsn, pdp, pack, len); + return handle_router_mcast(pdp->gsn, pdp, &apn->v6_lladdr, pack, len); break; case 4: break; diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h index 2dd963c..42f8e1c 100644 --- a/ggsn/ggsn.h +++ b/ggsn/ggsn.h @@ -78,6 +78,9 @@ struct apn_ctx { struct osmo_fd fd; } tun; + /* ipv6 link-local address */ + struct in6_addr v6_lladdr; + struct apn_ctx_ip v4; struct apn_ctx_ip v6; }; diff --git a/ggsn/icmpv6.c b/ggsn/icmpv6.c index ac51bb1..11ced24 100644 --- a/ggsn/icmpv6.c +++ b/ggsn/icmpv6.c @@ -179,13 +179,9 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len) return true; } -/* RFC3307 link-local scope multicast address */ -static const struct in6_addr my_local_addr = { - .s6_addr = { 0x01,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0xff } -}; - /* handle incoming packets to the all-routers multicast address */ -int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len) +int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr, + const uint8_t *pack, unsigned len) { struct ippoolm_t *member = pdp->peer; const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack; @@ -222,10 +218,10 @@ int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pac osmo_hexdump(pack, len)); return -1; } - /* FIXME: Send router advertisement from GGSN link-local + /* Send router advertisement from GGSN link-local * address to MS link-local address, including prefix * allocated to this PDP context */ - msg = icmpv6_construct_ra(&my_local_addr, &ip6h->ip6_src, &member->addr.v6); + msg = icmpv6_construct_ra(own_ll_addr, &ip6h->ip6_src, &member->addr.v6); /* Send the constructed RA to the MS */ gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg)); msgb_free(msg); diff --git a/ggsn/icmpv6.h b/ggsn/icmpv6.h index ebff04e..b6eec63 100644 --- a/ggsn/icmpv6.h +++ b/ggsn/icmpv6.h @@ -3,4 +3,5 @@ #include "../gtp/gtp.h" #include "../gtp/pdp.h" -int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const uint8_t *pack, unsigned len); +int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr, + const uint8_t *pack, unsigned len); @@ -748,3 +748,35 @@ int tun_runscript(struct tun_t *tun, char *script) } return 0; } + +#include <ifaddrs.h> + +/* obtain the link-local address of the tun device */ +int tun_ipv6_linklocal_get(const struct tun_t *tun, struct in6_addr *ia) +{ + struct ifaddrs *ifaddr, *ifa; + static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 }; + + if (getifaddrs(&ifaddr) == -1) { + return -1; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr; + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + if (strcmp(ifa->ifa_name, tun->devname)) + continue; + + if (memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix))) + continue; + + *ia = sin6->sin6_addr; + return 0; + } + return -1; +} @@ -85,4 +85,6 @@ extern int tun_set_cb_ind(struct tun_t *this, extern int tun_runscript(struct tun_t *tun, char *script); +int tun_ipv6_linklocal_get(const struct tun_t *tun, struct in6_addr *ia); + #endif /* !_TUN_H */ |