pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/43585?usp=email )
Change subject: ggsn: use the runtime tun interface name ......................................................................
ggsn: use the runtime tun interface name
The IP pool of an APN excludes the addresses of its own tun interface. alloc_ippool_blacklist() finds them with netdev_ip_local_get(), which walks getifaddrs() and matches on the interface name, and it passes apn->tun.cfg.dev_name, the name from the configuration file. tun_ip_local_get() does the same with tun->devname when it looks up the IPv6 link-local address.
The configured name is not always the name the interface ends up with. libosmocore already knows this: osmo_tundev keeps dev_name current, and tundev_dev_name_chg_cb() rewrites it on rename, logging "netdev changed name". Once that happens, or wherever the kernel picks the name itself, the lookup is done under a name getifaddrs() does not report, no addresses are found, and the blacklist is empty.
An empty blacklist is not a harmless miss. The pool then hands out its first address, which is the tun's own, so the first PDP context gets the address of the GGSN side of the link and collides with it.
Observed on Darwin, where the kernel always names a utun itself and a requested "tun4" comes back as "utun6": the first attached UE was given 10.45.0.1, the tun address. With the fix it gets 10.45.0.2.
Take the name from the open osmo_tundev with osmo_tundev_get_dev_name(), which returns what the interface is called now, in both places. Fall back to the configured name when there is no tundev, which is the gtp kernel mode path. No change where the two names agree, which is the normal case on Linux.
Change-Id: Ibd754ea3a5be4d5227488d0de571723b69e9d39d Signed-off-by: Andrei Gosman andrei.gosman@gmail.com --- M ggsn/ggsn.c M lib/tun.c 2 files changed, 18 insertions(+), 4 deletions(-)
Approvals: laforge: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, approved
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 3177713..79efae3 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -174,23 +174,31 @@
static int alloc_ippool_blacklist(struct apn_ctx *apn, struct in46_prefix **blacklist, bool ipv6) { - + const char *dev_name = apn->tun.cfg.dev_name; int flags, len, len2, i;
*blacklist = NULL;
+ /* The kernel may give the interface another name than the one that + * was configured (a Darwin utun: "tun4" requested, "utun6" assigned). + * getifaddrs() lists the addresses under the real name, so ask the + * open tun device for it; otherwise the blacklist stays empty and the + * pool hands the tun's own address to the first PDP context. */ + if (apn->tun.tun && apn->tun.tun->tundev.tundev) + dev_name = osmo_tundev_get_dev_name(apn->tun.tun->tundev.tundev); + if (ipv6) flags = IP_TYPE_IPv6_NONLINK; else flags = IP_TYPE_IPv4;
while (1) { - len = netdev_ip_local_get(apn->tun.cfg.dev_name, NULL, 0, flags); + len = netdev_ip_local_get(dev_name, NULL, 0, flags); if (len < 1) return len;
*blacklist = talloc_zero_size(apn, len * sizeof(struct in46_prefix)); - len2 = netdev_ip_local_get(apn->tun.cfg.dev_name, *blacklist, len, flags); + len2 = netdev_ip_local_get(dev_name, *blacklist, len, flags); if (len2 < 1) { talloc_free(*blacklist); *blacklist = NULL; diff --git a/lib/tun.c b/lib/tun.c index 60d9b5e..13a8b9d 100644 --- a/lib/tun.c +++ b/lib/tun.c @@ -279,5 +279,11 @@ */ int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags) { - return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags); + /* Ask the tun device for the name the kernel gave the interface; it can + * differ from the configured one (Darwin utun) and getifaddrs() only + * knows the real one. */ + const char *devname = tun->devname; + if (tun->tundev.tundev) + devname = osmo_tundev_get_dev_name(tun->tundev.tundev); + return netdev_ip_local_get(devname, prefix_list, prefix_size, flags); }