diff options
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r-- | net/mac80211/iface.c | 116 |
1 files changed, 114 insertions, 2 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0793d7a8d743..d5571b9420cd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -815,6 +815,118 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, | |||
815 | return 0; | 815 | return 0; |
816 | } | 816 | } |
817 | 817 | ||
818 | static void ieee80211_assign_perm_addr(struct ieee80211_local *local, | ||
819 | struct net_device *dev, | ||
820 | enum nl80211_iftype type) | ||
821 | { | ||
822 | struct ieee80211_sub_if_data *sdata; | ||
823 | u64 mask, start, addr, val, inc; | ||
824 | u8 *m; | ||
825 | u8 tmp_addr[ETH_ALEN]; | ||
826 | int i; | ||
827 | |||
828 | /* default ... something at least */ | ||
829 | memcpy(dev->perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN); | ||
830 | |||
831 | if (is_zero_ether_addr(local->hw.wiphy->addr_mask) && | ||
832 | local->hw.wiphy->n_addresses <= 1) | ||
833 | return; | ||
834 | |||
835 | |||
836 | mutex_lock(&local->iflist_mtx); | ||
837 | |||
838 | switch (type) { | ||
839 | case NL80211_IFTYPE_MONITOR: | ||
840 | /* doesn't matter */ | ||
841 | break; | ||
842 | case NL80211_IFTYPE_WDS: | ||
843 | case NL80211_IFTYPE_AP_VLAN: | ||
844 | /* match up with an AP interface */ | ||
845 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
846 | if (sdata->vif.type != NL80211_IFTYPE_AP) | ||
847 | continue; | ||
848 | memcpy(dev->perm_addr, sdata->vif.addr, ETH_ALEN); | ||
849 | break; | ||
850 | } | ||
851 | /* keep default if no AP interface present */ | ||
852 | break; | ||
853 | default: | ||
854 | /* assign a new address if possible -- try n_addresses first */ | ||
855 | for (i = 0; i < local->hw.wiphy->n_addresses; i++) { | ||
856 | bool used = false; | ||
857 | |||
858 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
859 | if (memcmp(local->hw.wiphy->addresses[i].addr, | ||
860 | sdata->vif.addr, ETH_ALEN) == 0) { | ||
861 | used = true; | ||
862 | break; | ||
863 | } | ||
864 | } | ||
865 | |||
866 | if (!used) { | ||
867 | memcpy(dev->perm_addr, | ||
868 | local->hw.wiphy->addresses[i].addr, | ||
869 | ETH_ALEN); | ||
870 | break; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | /* try mask if available */ | ||
875 | if (is_zero_ether_addr(local->hw.wiphy->addr_mask)) | ||
876 | break; | ||
877 | |||
878 | m = local->hw.wiphy->addr_mask; | ||
879 | mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | | ||
880 | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | | ||
881 | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); | ||
882 | |||
883 | if (__ffs64(mask) + hweight64(mask) != fls64(mask)) { | ||
884 | /* not a contiguous mask ... not handled now! */ | ||
885 | printk(KERN_DEBUG "not contiguous\n"); | ||
886 | break; | ||
887 | } | ||
888 | |||
889 | m = local->hw.wiphy->perm_addr; | ||
890 | start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | | ||
891 | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | | ||
892 | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); | ||
893 | |||
894 | inc = 1ULL<<__ffs64(mask); | ||
895 | val = (start & mask); | ||
896 | addr = (start & ~mask) | (val & mask); | ||
897 | do { | ||
898 | bool used = false; | ||
899 | |||
900 | tmp_addr[5] = addr >> 0*8; | ||
901 | tmp_addr[4] = addr >> 1*8; | ||
902 | tmp_addr[3] = addr >> 2*8; | ||
903 | tmp_addr[2] = addr >> 3*8; | ||
904 | tmp_addr[1] = addr >> 4*8; | ||
905 | tmp_addr[0] = addr >> 5*8; | ||
906 | |||
907 | val += inc; | ||
908 | |||
909 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
910 | if (memcmp(tmp_addr, sdata->vif.addr, | ||
911 | ETH_ALEN) == 0) { | ||
912 | used = true; | ||
913 | break; | ||
914 | } | ||
915 | } | ||
916 | |||
917 | if (!used) { | ||
918 | memcpy(dev->perm_addr, tmp_addr, ETH_ALEN); | ||
919 | break; | ||
920 | } | ||
921 | addr = (start & ~mask) | (val & mask); | ||
922 | } while (addr != start); | ||
923 | |||
924 | break; | ||
925 | } | ||
926 | |||
927 | mutex_unlock(&local->iflist_mtx); | ||
928 | } | ||
929 | |||
818 | int ieee80211_if_add(struct ieee80211_local *local, const char *name, | 930 | int ieee80211_if_add(struct ieee80211_local *local, const char *name, |
819 | struct net_device **new_dev, enum nl80211_iftype type, | 931 | struct net_device **new_dev, enum nl80211_iftype type, |
820 | struct vif_params *params) | 932 | struct vif_params *params) |
@@ -844,8 +956,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
844 | if (ret < 0) | 956 | if (ret < 0) |
845 | goto fail; | 957 | goto fail; |
846 | 958 | ||
847 | memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); | 959 | ieee80211_assign_perm_addr(local, ndev, type); |
848 | memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN); | 960 | memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); |
849 | SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); | 961 | SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); |
850 | 962 | ||
851 | /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */ | 963 | /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */ |