| From 3125f26c514826077f2a4490b75e9b1c7a644c42 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> |
| Date: Sat, 7 Aug 2021 18:00:50 +0200 |
| Subject: ppp: Fix generating ppp unit id when ifname is not specified |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Pali Rohár <pali@kernel.org> |
| |
| commit 3125f26c514826077f2a4490b75e9b1c7a644c42 upstream. |
| |
| 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: Pali Rohár <pali@kernel.org> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/net/ppp/ppp_generic.c | 19 +++++++++++++++---- |
| 1 file changed, 15 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/net/ppp/ppp_generic.c |
| +++ b/drivers/net/ppp/ppp_generic.c |
| @@ -283,7 +283,7 @@ static struct channel *ppp_find_channel( |
| static int ppp_connect_channel(struct channel *pch, int unit); |
| static int ppp_disconnect_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 void unit_put(struct idr *p, int n); |
| static void *unit_find(struct idr *p, int n); |
| @@ -1045,9 +1045,20 @@ static int ppp_unit_register(struct ppp |
| mutex_lock(&pn->all_ppp_mutex); |
| |
| if (unit < 0) { |
| - ret = unit_get(&pn->units_idr, ppp); |
| + ret = unit_get(&pn->units_idr, ppp, 0); |
| if (ret < 0) |
| 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 { |
| /* Caller asked for a specific unit number. Fail with -EEXIST |
| * if unavailable. For backward compatibility, return -EEXIST |
| @@ -3378,9 +3389,9 @@ static int unit_set(struct idr *p, void |
| } |
| |
| /* 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 */ |