return err;
        }
 
+       /* host join */
+       if (!port) {
+               /* don't allow any flags for host-joined groups */
+               if (state)
+                       return -EINVAL;
+               if (mp->host_joined)
+                       return -EEXIST;
+
+               br_multicast_host_join(mp, false);
+
+               return 0;
+       }
+
        for (pp = &mp->ports;
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
 {
        struct br_ip ip;
        struct net_device *dev;
-       struct net_bridge_port *p;
+       struct net_bridge_port *p = NULL;
        int ret;
 
        if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
                return -EINVAL;
 
-       dev = __dev_get_by_index(net, entry->ifindex);
-       if (!dev)
-               return -ENODEV;
+       if (entry->ifindex != br->dev->ifindex) {
+               dev = __dev_get_by_index(net, entry->ifindex);
+               if (!dev)
+                       return -ENODEV;
 
-       p = br_port_get_rtnl(dev);
-       if (!p || p->br != br || p->state == BR_STATE_DISABLED)
-               return -EINVAL;
+               p = br_port_get_rtnl(dev);
+               if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+                       return -EINVAL;
+       }
 
        __mdb_entry_to_br_ip(entry, &ip);
 
 {
        struct net *net = sock_net(skb->sk);
        struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
        struct net_device *dev, *pdev;
        struct br_mdb_entry *entry;
-       struct net_bridge_port *p;
        struct net_bridge_vlan *v;
        struct net_bridge *br;
        int err;
 
        br = netdev_priv(dev);
 
-       pdev = __dev_get_by_index(net, entry->ifindex);
-       if (!pdev)
-               return -ENODEV;
+       if (entry->ifindex != br->dev->ifindex) {
+               pdev = __dev_get_by_index(net, entry->ifindex);
+               if (!pdev)
+                       return -ENODEV;
 
-       p = br_port_get_rtnl(pdev);
-       if (!p || p->br != br || p->state == BR_STATE_DISABLED)
-               return -EINVAL;
+               p = br_port_get_rtnl(pdev);
+               if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+                       return -EINVAL;
+               vg = nbp_vlan_group(p);
+       } else {
+               vg = br_vlan_group(br);
+       }
 
-       vg = nbp_vlan_group(p);
        /* If vlan filtering is enabled and VLAN is not specified
         * install mdb entry on all vlans configured on the port.
         */
        if (!mp)
                goto unlock;
 
+       /* host leave */
+       if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
+               br_multicast_host_leave(mp, false);
+               err = 0;
+               if (!mp->ports && netif_running(br->dev))
+                       mod_timer(&mp->timer, jiffies);
+               goto unlock;
+       }
+
        for (pp = &mp->ports;
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
 {
        struct net *net = sock_net(skb->sk);
        struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
        struct net_device *dev, *pdev;
        struct br_mdb_entry *entry;
-       struct net_bridge_port *p;
        struct net_bridge_vlan *v;
        struct net_bridge *br;
        int err;
 
        br = netdev_priv(dev);
 
-       pdev = __dev_get_by_index(net, entry->ifindex);
-       if (!pdev)
-               return -ENODEV;
+       if (entry->ifindex != br->dev->ifindex) {
+               pdev = __dev_get_by_index(net, entry->ifindex);
+               if (!pdev)
+                       return -ENODEV;
 
-       p = br_port_get_rtnl(pdev);
-       if (!p || p->br != br || p->state == BR_STATE_DISABLED)
-               return -EINVAL;
+               p = br_port_get_rtnl(pdev);
+               if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+                       return -EINVAL;
+               vg = nbp_vlan_group(p);
+       } else {
+               vg = br_vlan_group(br);
+       }
 
-       vg = nbp_vlan_group(p);
        /* If vlan filtering is enabled and VLAN is not specified
         * delete mdb entry on all vlans configured on the port.
         */
 
        if (!netif_running(br->dev) || timer_pending(&mp->timer))
                goto out;
 
-       mp->host_joined = false;
-       br_mdb_notify(br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
+       br_multicast_host_leave(mp, true);
 
        if (mp->ports)
                goto out;
        return ether_addr_equal(src, p->eth_addr);
 }
 
+void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
+{
+       if (!mp->host_joined) {
+               mp->host_joined = true;
+               if (notify)
+                       br_mdb_notify(mp->br->dev, NULL, &mp->addr,
+                                     RTM_NEWMDB, 0);
+       }
+       mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
+}
+
+void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
+{
+       if (!mp->host_joined)
+               return;
+
+       mp->host_joined = false;
+       if (notify)
+               br_mdb_notify(mp->br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
+}
+
 static int br_multicast_add_group(struct net_bridge *br,
                                  struct net_bridge_port *port,
                                  struct br_ip *group,
                goto err;
 
        if (!port) {
-               if (!mp->host_joined) {
-                       mp->host_joined = true;
-                       br_mdb_notify(br->dev, NULL, &mp->addr, RTM_NEWMDB, 0);
-               }
-               mod_timer(&mp->timer, now + br->multicast_membership_interval);
+               br_multicast_host_join(mp, true);
                goto out;
        }