Commit 3125f26c authored by Pali Rohár's avatar Pali Rohár Committed by David S. Miller

ppp: Fix generating ppp unit id when ifname is not specified

When registering new ppp interface via PPPIOCNEWUNIT ioctl then kernel has
to choose interface name as this ioctl API does not support specifying it.

Kernel in this case register new interface with name "ppp<id>" where <id>
is the ppp unit id, which can be obtained via PPPIOCGUNIT ioctl. This
applies also in the case when registering new ppp interface via rtnl
without supplying IFLA_IFNAME.

PPPIOCNEWUNIT ioctl allows to specify own ppp unit id which will kernel
assign to ppp interface, in case this ppp id is not already used by other
ppp interface.

In case user does not specify ppp unit id then kernel choose the first free
ppp unit id. This applies also for case when creating ppp interface via
rtnl method as it does not provide a way for specifying own ppp unit id.

If some network interface (does not have to be ppp) has name "ppp<id>"
with this first free ppp id then PPPIOCNEWUNIT ioctl or rtnl call fails.

And registering new ppp interface is not possible anymore, until interface
which holds conflicting name is renamed. Or when using rtnl method with
custom interface name in IFLA_IFNAME.

As list of allocated / used ppp unit ids is not possible to retrieve from
kernel to userspace, userspace has no idea what happens nor which interface
is doing this conflict.

So change the algorithm how ppp unit id is generated. And choose the first
number which is not neither used as ppp unit id nor in some network
interface with pattern "ppp<id>".

This issue can be simply reproduced by following pppd call when there is no
ppp interface registered and also no interface with name pattern "ppp<id>":

    pppd ifname ppp1 +ipv6 noip noauth nolock local nodetach pty "pppd +ipv6 noip noauth nolock local nodetach notty"

Or by creating the one ppp interface (which gets assigned ppp unit id 0),
renaming it to "ppp1" and then trying to create a new ppp interface (which
will always fails as next free ppp unit id is 1, but network interface with
name "ppp1" exists).

This patch fixes above described issue by generating new and new ppp unit
id until some non-conflicting id with network interfaces is generated.
Signed-off-by: default avatarPali Rohár <pali@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2459dcb9
...@@ -284,7 +284,7 @@ static struct channel *ppp_find_channel(struct ppp_net *pn, int unit); ...@@ -284,7 +284,7 @@ static struct channel *ppp_find_channel(struct ppp_net *pn, int unit);
static int ppp_connect_channel(struct channel *pch, int unit); static int ppp_connect_channel(struct channel *pch, int unit);
static int ppp_disconnect_channel(struct channel *pch); static int ppp_disconnect_channel(struct channel *pch);
static void ppp_destroy_channel(struct channel *pch); static void ppp_destroy_channel(struct channel *pch);
static int unit_get(struct idr *p, void *ptr); static int unit_get(struct idr *p, void *ptr, int min);
static int unit_set(struct idr *p, void *ptr, int n); static int unit_set(struct idr *p, void *ptr, int n);
static void unit_put(struct idr *p, int n); static void unit_put(struct idr *p, int n);
static void *unit_find(struct idr *p, int n); static void *unit_find(struct idr *p, int n);
...@@ -1155,9 +1155,20 @@ static int ppp_unit_register(struct ppp *ppp, int unit, bool ifname_is_set) ...@@ -1155,9 +1155,20 @@ static int ppp_unit_register(struct ppp *ppp, int unit, bool ifname_is_set)
mutex_lock(&pn->all_ppp_mutex); mutex_lock(&pn->all_ppp_mutex);
if (unit < 0) { if (unit < 0) {
ret = unit_get(&pn->units_idr, ppp); ret = unit_get(&pn->units_idr, ppp, 0);
if (ret < 0) if (ret < 0)
goto err; goto err;
if (!ifname_is_set) {
while (1) {
snprintf(ppp->dev->name, IFNAMSIZ, "ppp%i", ret);
if (!__dev_get_by_name(ppp->ppp_net, ppp->dev->name))
break;
unit_put(&pn->units_idr, ret);
ret = unit_get(&pn->units_idr, ppp, ret + 1);
if (ret < 0)
goto err;
}
}
} else { } else {
/* Caller asked for a specific unit number. Fail with -EEXIST /* Caller asked for a specific unit number. Fail with -EEXIST
* if unavailable. For backward compatibility, return -EEXIST * if unavailable. For backward compatibility, return -EEXIST
...@@ -3552,9 +3563,9 @@ static int unit_set(struct idr *p, void *ptr, int n) ...@@ -3552,9 +3563,9 @@ static int unit_set(struct idr *p, void *ptr, int n)
} }
/* get new free unit number and associate pointer with it */ /* get new free unit number and associate pointer with it */
static int unit_get(struct idr *p, void *ptr) static int unit_get(struct idr *p, void *ptr, int min)
{ {
return idr_alloc(p, ptr, 0, 0, GFP_KERNEL); return idr_alloc(p, ptr, min, 0, GFP_KERNEL);
} }
/* put unit number back to a pool */ /* put unit number back to a pool */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment