Re: [Patch BlueZ 1/1] Make network server interface handling more robust

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Bobby,

On Sun, Sep 18, 2016 at 7:35 PM, Bobby Noelte <b0661n0e17e@xxxxxxxxx> wrote:
> From: "b0661n0e71e@xxxxxxxxx" <b0661.n0e17e@xxxxxxxxx>
>
> Interface names may be renamed by udev (systemd-udev) after creation
> before the interface is up. Interfaces may also be renamed when
> the interface is down due to a bluetoothd restart.

I guess there should probably be a way to prevent systemd-udev to
change to rename the interface or then we need kernel changes to make
sure the interface index is sent.

> Use the interface index, that does not change on renaming, where possible.
> Also do some retries when the creation of an interface fails.
> ---
>  profiles/network/bnep.c | 311 ++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 260 insertions(+), 51 deletions(-)
>
> diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
> index 9bf0b18..cba0f63 100644
> --- a/profiles/network/bnep.c
> +++ b/profiles/network/bnep.c
> @@ -65,6 +65,7 @@ struct bnep {
>         uint16_t        dst;
>         bdaddr_t        dst_addr;
>         char    iface[16];
> +       unsigned int    ifindex;
>         guint   attempts;
>         guint   setup_to;
>         guint   watch;
> @@ -98,25 +99,109 @@ int bnep_cleanup(void)
>         return 0;
>  }
>
> +/**
> + * private: for local use only.
> + *
> + * bnep_indextoname() - Return interface name corresponding to interface index.
> + * @ifindex interface index.
> + * @iface interface name, output only, will be filled with '<unknown>' in case of error.
> + *
> + * The name is placed in the buffer pointed to by ifname.
> + * The buffer must allow for the storage of at least IF_NAMESIZE bytes.
> + *
> + * On success 0 is returned. On error errno as a negative value is returned.
> + */
> +static int bnep_indextoname(unsigned int ifindex, char *iface)
> +{
> +       int err;
> +
> +       err = 0;
> +       if (if_indextoname(ifindex, iface) == 0) {
> +               err = -errno;
> +               strncpy(iface, "<unknown>", 10);
> +       }
> +       return err;
> +}
> +
> +/**
> + * private: for local use only.
> + *
> + * bnep_nametoindext() - Return interface index corresponding to interface name.
> + * @iface interface name
> + * @ifindex interface index, output only, will be filled with 0 in case of error.
> + *
> + * The interface index is placed in the variable pointed to by ifindex.
> + *
> + * In case the interface is temporarily unavailable do a retry.
> + *
> + * On success 0 is returned. On error errno as a negative value is returned.
> + */
> +static int bnep_nametoindex(const char *iface, unsigned int *ifindex)
> +{
> +       int retry;
> +       int err;
> +
> +       retry = 0;
> +       while (retry < 2) {
> +               DBG("get interface index for device %s retry %d", iface, retry);
> +               *ifindex = if_nametoindex(iface);
> +               if (*ifindex != 0) {
> +                       DBG("got interface index %d for added device %s retry %d",
> +                               *ifindex, iface, retry);
> +                       err = 0;
> +               } else {
> +                       err = -errno;
> +               }
> +               if (err != -EAGAIN)
> +                       break;
> +               /* EAGAIN - Resource temporarily unavailable */
> +               retry++;
> +               sleep(1);

sleep is no acceptable in the daemon code, it should never block in
any circumstances.

> +       }
> +       return err;
> +}
> +
>  static int bnep_conndel(const bdaddr_t *dst)
>  {
> +       int err;
>         struct bnep_conndel_req req;
> +       char addr[20];
>
> +       ba2str(dst, &addr[0]);
>         memset(&req, 0, sizeof(req));
>         baswap((bdaddr_t *)&req.dst, dst);
>         req.flags = 0;
> -       if (ioctl(ctl, BNEPCONNDEL, &req) < 0) {
> -               int err = -errno;
> -               error("bnep: Failed to kill connection: %s (%d)",
> -                                                       strerror(-err), -err);
> +       err = ioctl(ctl, BNEPCONNDEL, &req);
> +       if (err < 0) {
> +               err = -errno;
> +               error("bnep: Failed to kill connection %s: %s (%d)",
> +                       addr, strerror(-err), -err);
>                 return err;
>         }
> +       DBG("deleted device %s, %d", addr, err);
> +
>         return 0;
>  }
>
> -static int bnep_connadd(int sk, uint16_t role, char *dev)
> +/**
> + * private: for local use only.
> + *
> + * bnep_connadd() - Add bluetooth ethernet connection.
> + * @sk bluez control socket.
> + * @role
> + * @dev device name, template for new interface name (e.g. "bnep%d") or name.
> + * @dst bluetooth address, only needed for retries.
> + * @ifindex interface index, ouput only - new interface index
> + *
> + * In case the interface can not be accessed by the device name (e.g. due to udev rename) do a retry.
> + *
> + * On success 0 is returned. On error errno as a negative value is returned.
> + */
> +static int bnep_connadd(int sk, uint16_t role, char *dev, const bdaddr_t *dst, unsigned int *ifindex)
>  {
>         struct bnep_connadd_req req;
> +       int err;
> +       int retry;
>
>         memset(&req, 0, sizeof(req));
>         strncpy(req.device, dev, 16);
> @@ -125,15 +210,40 @@ static int bnep_connadd(int sk, uint16_t role, char *dev)
>         req.sock = sk;
>         req.role = role;
>         req.flags = (1 << BNEP_SETUP_RESPONSE);
> -       if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
> -               int err = -errno;
> -               error("bnep: Failed to add device %s: %s(%d)",
> -                                               dev, strerror(-err), -err);
> -               return err;
> +
> +       retry = 0;
> +
> +       while (retry < 2) {
> +               DBG("add device %s retry %d", req.device, retry);
> +
> +               err = ioctl(ctl, BNEPCONNADD, &req);
> +               if (err < 0) {
> +                       err = -errno;
> +                       break;
> +               }
> +               DBG("added device %s retry %d", req.device, retry);
> +               /*
> +               * There is a potential race condition between udev renaming the interface
> +               * after being created by BNEPCONNADD and bnep_nametoindex providing
> +               * the interface index.
> +               */
> +               err = bnep_nametoindex(&req.device[0], ifindex);
> +               if (!err)
> +                       break;
> +
> +               /* Prepare retry */
> +               retry++;
> +               bnep_conndel(dst);
> +               /* Give BT some time to really delete. */
> +               sleep(1);

We aint gonna fix any race condition with sleep.

>         }
>
> -       strncpy(dev, req.device, 16);
> -       return 0;
> +       if (err) {
> +               error("bnep: Failed to add device %s with interface index %d: %s(%d)",
> +                       req.device, *ifindex, strerror(-err), -err);
> +       }
> +
> +       return err;
>  }
>
>  static uint32_t bnep_getsuppfeat(void)
> @@ -148,51 +258,108 @@ static uint32_t bnep_getsuppfeat(void)
>         return feat;
>  }
>
> -static int bnep_if_up(const char *devname)
> +/**
> + * private: for local use only.
> + *
> + * bnep_if_up() - Bring interface up.
> + * @ifindex interface index
> + *
> + * In case the interface can not be accessed (e.g. due to udev rename) do a retry.
> + *
> + * On success 0 is returned. On error errno as a negative value is returned.
> + */
> +static int bnep_if_up(unsigned int ifindex)
>  {
>         struct ifreq ifr;
> -       int sk, err = 0;
> +       int sk, err;
> +       int retry;
>
>         sk = socket(AF_INET, SOCK_DGRAM, 0);
>
>         memset(&ifr, 0, sizeof(ifr));
> -       strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
> -
>         ifr.ifr_flags |= IFF_UP;
>         ifr.ifr_flags |= IFF_MULTICAST;
>
> -       if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) < 0) {
> +       retry = 0;
> +       while (retry < 2) {
> +               err = bnep_indextoname(ifindex, ifr.ifr_name);
> +               if (err)
> +                       break;
> +               /*
> +               * There is a potential race condition between udev renaming the interface
> +               * after the name is retrieved by bnep_nametoindex and the usage of the
> +               * interface name by ioctl.
> +               */
> +               if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) == 0)
> +                       break;
>                 err = -errno;
> -               error("bnep: Could not bring up %s: %s(%d)",
> -                                               devname, strerror(-err), -err);
> +               DBG("could not bring up interface %s with index %d: %s(%d) retry %d",
> +                       ifr.ifr_name, ifindex, strerror(-err), -err, retry);
> +
> +               /* Prepare retry */
> +               retry++;
> +               sleep(1);

Ditto.

>         }
>
>         close(sk);
>
> +       if (err) {
> +               error("bnep: Could not bring up interface %s with index %d: %s(%d)",
> +                       ifr.ifr_name, ifindex, strerror(-err), -err);
> +       }
> +
>         return err;
>  }
>
> -static int bnep_if_down(const char *devname)
> +/**
> + * private: for local use only.
> + *
> + * bnep_if_down() - Bring interface down.
> + * @ifindex interface index
> + *
> + * In case the interface can not be accessed (e.g. due to udev rename) do a retry.
> + *
> + * On success 0 is returned. On error errno as a negative value is returned.
> + */
> +static int bnep_if_down(unsigned int ifindex)
>  {
>         struct ifreq ifr;
> -       int sk, err = 0;
> +       int sk, err;
> +       int retry;
>
>         sk = socket(AF_INET, SOCK_DGRAM, 0);
>
>         memset(&ifr, 0, sizeof(ifr));
> -       strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
> -
>         ifr.ifr_flags &= ~IFF_UP;
>
> -       /* Bring down the interface */
> -       if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) < 0) {
> +       retry = 0;
> +       while (retry < 2) {
> +               err = bnep_indextoname(ifindex, ifr.ifr_name);
> +               if (err)
> +                       break;
> +               /*
> +               * There is a potential race condition between udev renaming the interface
> +               * after the name is retrieved by bnep_nametoindex and the usage of the
> +               * interface name by ioctl.
> +               */
> +               if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) == 0)
> +                       break;
>                 err = -errno;
> -               error("bnep: Could not bring down %s: %s(%d)",
> -                                               devname, strerror(-err), -err);
> +               DBG("could not bring down interface %s with index %d: %s(%d) retry %d",
> +                       ifr.ifr_name, ifindex, strerror(-err), -err, retry);
> +
> +               /* Prepare retry */
> +               retry++;
> +               sleep(1);

Ditto.

>         }
>
>         close(sk);
>
> +       if (err) {
> +               error("bnep: Could not bring down interface %s with index %d: %s(%d)",
> +                       ifr.ifr_name, ifindex, strerror(-err), -err);
> +       }
> +
>         return err;
>  }
>
> @@ -270,14 +437,19 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
>         setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
>
>         sk = g_io_channel_unix_get_fd(session->io);
> -       if (bnep_connadd(sk, session->src, session->iface) < 0)
> +       if (bnep_connadd(sk, session->src, session->iface, &session->dst_addr, &session->ifindex) < 0)
>                 goto failed;
>
> -       if (bnep_if_up(session->iface) < 0) {
> +       if (bnep_if_up(session->ifindex) < 0) {
>                 bnep_conndel(&session->dst_addr);
>                 goto failed;
>         }
>
> +       /* Update interface name to current one only after interface is up.
> +        * Only at that time the name should be stable (no udev rename).
> +        */
> +       bnep_indextoname(session->ifindex, session->iface);
> +
>         session->watch = g_io_add_watch(session->io,
>                                         G_IO_ERR | G_IO_HUP | G_IO_NVAL,
>                                         (GIOFunc) bnep_watchdog_cb, session);
> @@ -305,7 +477,7 @@ static int bnep_setup_conn_req(struct bnep *session)
>         req = (void *) pkt;
>         req->type = BNEP_CONTROL;
>         req->ctrl = BNEP_SETUP_CONN_REQ;
> -       req->uuid_size = 2;     /* 16bit UUID */
> +       req->uuid_size = 2;     /* 16bit UUID */
>         s = (void *) req->service;
>         s->src = htons(session->src);
>         s->dst = htons(session->dst);
> @@ -354,6 +526,7 @@ struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
>         session->dst = remote_role;
>         strncpy(session->iface, iface, 16);
>         session->iface[15] = '\0';
> +       session->ifindex = 0;
>
>         g_io_channel_set_close_on_unref(session->io, TRUE);
>         session->watch = g_io_add_watch(session->io,
> @@ -430,20 +603,26 @@ void bnep_disconnect(struct bnep *session)
>                 session->io = NULL;
>         }
>
> -       bnep_if_down(session->iface);
> +       bnep_if_down(session->ifindex);
>         bnep_conndel(&session->dst_addr);
>  }
>
> -static int bnep_add_to_bridge(const char *devname, const char *bridge)
> +static int bnep_add_to_bridge(unsigned int ifindex, const char *bridge)
>  {
> -       int ifindex;
>         struct ifreq ifr;
>         int sk, err = 0;
> +       char devname[IF_NAMESIZE];
>
> -       if (!devname || !bridge)
> +       DBG("add interface index %d to bridge %s", ifindex, bridge);
> +       if (!ifindex || !bridge)
>                 return -EINVAL;
>
> -       ifindex = if_nametoindex(devname);
> +       err = bnep_indextoname(ifindex, &devname[0]);
> +       if (err) {
> +               error("bnep: Can't add interface %s with index %d to the bridge %s: %s(%d)",
> +                                       devname, ifindex, bridge, strerror(-err), -err);
> +               return -EINVAL;
> +       }
>
>         sk = socket(AF_INET, SOCK_STREAM, 0);
>         if (sk < 0)
> @@ -451,7 +630,8 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge)
>
>         memset(&ifr, 0, sizeof(ifr));
>         strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
> -       ifr.ifr_ifindex = ifindex;
> +       ifr.ifr_ifindex = (int)ifindex;
> +
>
>         if (ioctl(sk, SIOCBRADDIF, &ifr) < 0) {
>                 err = -errno;
> @@ -466,16 +646,21 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge)
>         return err;
>  }
>
> -static int bnep_del_from_bridge(const char *devname, const char *bridge)
> +static int bnep_del_from_bridge(unsigned int ifindex, const char *bridge)
>  {
> -       int ifindex;
>         struct ifreq ifr;
>         int sk, err = 0;
> +       char devname[IF_NAMESIZE];
>
> -       if (!devname || !bridge)
> +       if (!ifindex || !bridge)
>                 return -EINVAL;
>
> -       ifindex = if_nametoindex(devname);
> +       err = bnep_indextoname(ifindex, &devname[0]);
> +       if (err) {
> +               error("bnep: Can't delete interface %s with index %d from the bridge %s: %s(%d)",
> +                                       devname, ifindex, bridge, strerror(-err), -err);
> +               return -EINVAL;
> +       }
>
>         sk = socket(AF_INET, SOCK_STREAM, 0);
>         if (sk < 0)
> @@ -483,7 +668,7 @@ static int bnep_del_from_bridge(const char *devname, const char *bridge)
>
>         memset(&ifr, 0, sizeof(ifr));
>         strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
> -       ifr.ifr_ifindex = ifindex;
> +       ifr.ifr_ifindex = (int)ifindex;
>
>         if (ioctl(sk, SIOCBRDELIF, &ifr) < 0) {
>                 err = -errno;
> @@ -606,6 +791,7 @@ static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
>                                         char *iface, const bdaddr_t *addr,
>                                         uint8_t *setup_data, int len)
>  {
> +       unsigned int ifindex;
>         int err, n;
>         uint16_t rsp;
>
> @@ -616,27 +802,32 @@ static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
>                 goto reply;
>         }
>
> -       err = bnep_connadd(sk, dst, iface);
> +       err = bnep_connadd(sk, dst, iface, addr, &ifindex);
>         if (err < 0) {
>                 rsp = BNEP_CONN_NOT_ALLOWED;
>                 goto reply;
>         }
>
> -       err = bnep_add_to_bridge(iface, bridge);
> +       err = bnep_add_to_bridge(ifindex, bridge);
>         if (err < 0) {
>                 bnep_conndel(addr);
>                 rsp = BNEP_CONN_NOT_ALLOWED;
>                 goto reply;
>         }
>
> -       err = bnep_if_up(iface);
> +       err = bnep_if_up(ifindex);
>         if (err < 0) {
> -               bnep_del_from_bridge(iface, bridge);
> +               bnep_del_from_bridge(ifindex, bridge);
>                 bnep_conndel(addr);
>                 rsp = BNEP_CONN_NOT_ALLOWED;
>                 goto reply;
>         }
>
> +       /* Update interface name to current one only after interface is up.
> +        * Only at that time the name should be stable (no udev rename).
> +        */
> +       bnep_indextoname(ifindex, iface);
> +
>         rsp = BNEP_SUCCESS;
>
>  reply:
> @@ -652,6 +843,7 @@ reply:
>  int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
>                                                 uint8_t *setup_data, int len)
>  {
> +       unsigned int ifindex;
>         int err;
>         uint32_t feat;
>         uint16_t rsp, dst;
> @@ -691,24 +883,29 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
>                 return bnep_server_add_legacy(sk, dst, bridge, iface, addr,
>                                                         setup_data, len);
>
> -       err = bnep_connadd(sk, dst, iface);
> +       err = bnep_connadd(sk, dst, iface, addr, &ifindex);
>         if (err < 0) {
>                 rsp = BNEP_CONN_NOT_ALLOWED;
>                 goto failed;
>         }
>
> -       err = bnep_add_to_bridge(iface, bridge);
> +       err = bnep_add_to_bridge(ifindex, bridge);
>         if (err < 0)
>                 goto failed_conn;
>
> -       err = bnep_if_up(iface);
> +       err = bnep_if_up(ifindex);
>         if (err < 0)
>                 goto failed_bridge;
>
> +       /* Update interface name to current one only after interface is up.
> +        * Only at that time the name should be stable (no udev rename).
> +        */
> +       bnep_indextoname(ifindex, iface);
> +
>         return 0;
>
>  failed_bridge:
> -       bnep_del_from_bridge(iface, bridge);
> +       bnep_del_from_bridge(ifindex, bridge);
>
>  failed_conn:
>         bnep_conndel(addr);
> @@ -727,10 +924,22 @@ failed:
>
>  void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
>  {
> +       int err;
> +       unsigned int ifindex;
> +
>         if (!bridge || !iface || !addr)
>                 return;
>
> -       bnep_del_from_bridge(iface, bridge);
> -       bnep_if_down(iface);
> +       err = bnep_nametoindex(iface, &ifindex);
> +       if (err) {
> +               error("bnep: Failed to delete device %s - interface not available/ renamed: %s(%d)",
> +                                               iface, strerror(-err), -err);
> +               goto failed;
> +       }
> +
> +       bnep_del_from_bridge(ifindex, bridge);
> +       bnep_if_down(ifindex);
> +failed:
>         bnep_conndel(addr);
>  }
> +
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Luiz Augusto von Dentz
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux