Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

macvlan: add source mode

This patch adds a new mode of operation to macvlan, called "source".
It allows one to set a list of allowed mac address, which is used
to match against source mac address from received frames on underlying
interface.
This enables creating mac based VLAN associations, instead of standard
port or tag based. The feature is useful to deploy 802.1x mac based
behavior, where drivers of underlying interfaces doesn't allows that.

Configuration is done through the netlink interface using e.g.:
ip link add link eth0 name macvlan0 type macvlan mode source
ip link add link eth0 name macvlan1 type macvlan mode source
ip link set link dev macvlan0 type macvlan macaddr add 00:11:11:11:11:11
ip link set link dev macvlan0 type macvlan macaddr add 00:22:22:22:22:22
ip link set link dev macvlan0 type macvlan macaddr add 00:33:33:33:33:33
ip link set link dev macvlan1 type macvlan macaddr add 00:33:33:33:33:33
ip link set link dev macvlan1 type macvlan macaddr add 00:44:44:44:44:44

This allows clients with MAC addresses 00:11:11:11:11:11,
00:22:22:22:22:22 to be part of only VLAN associated with macvlan0
interface. Clients with MAC addresses 00:44:44:44:44:44 with only VLAN
associated with macvlan1 interface. And client with MAC address
00:33:33:33:33:33 to be associated with both VLANs.

Based on work of Stefan Gula <steweg@gmail.com>

v8: last version of Stefan Gula for Kernel 3.2.1
v9: rework onto linux-next 2014-03-12 by Michael Braun
add MACADDR_SET command, enable to configure mac for source mode
while creating interface
v10:
- reduce indention level
- rename source_list to source_entry
- use aligned 64bit ether address
- use hash_64 instead of addr[5]
v11:
- rebase for 3.14 / linux-next 20.04.2014
v12
- rebase for linux-next 2014-09-25

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Michael Braun and committed by
David S. Miller
79cf79ab 85224844

+314 -3
+301 -3
drivers/net/macvlan.c
··· 35 35 #include <net/xfrm.h> 36 36 #include <linux/netpoll.h> 37 37 38 - #define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) 38 + #define MACVLAN_HASH_BITS 8 39 + #define MACVLAN_HASH_SIZE (1<<MACVLAN_HASH_BITS) 39 40 #define MACVLAN_BC_QUEUE_LEN 1000 40 41 41 42 struct macvlan_port { ··· 48 47 struct work_struct bc_work; 49 48 bool passthru; 50 49 int count; 50 + struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE]; 51 + }; 52 + 53 + struct macvlan_source_entry { 54 + struct hlist_node hlist; 55 + struct macvlan_dev *vlan; 56 + unsigned char addr[6+2] __aligned(sizeof(u16)); 57 + struct rcu_head rcu; 51 58 }; 52 59 53 60 struct macvlan_skb_cb { ··· 65 56 #define MACVLAN_SKB_CB(__skb) ((struct macvlan_skb_cb *)&((__skb)->cb[0])) 66 57 67 58 static void macvlan_port_destroy(struct net_device *dev); 59 + 60 + /* Hash Ethernet address */ 61 + static u32 macvlan_eth_hash(const unsigned char *addr) 62 + { 63 + u64 value = get_unaligned((u64 *)addr); 64 + 65 + /* only want 6 bytes */ 66 + #ifdef __BIG_ENDIAN 67 + value >>= 16; 68 + #else 69 + value <<= 16; 70 + #endif 71 + return hash_64(value, MACVLAN_HASH_BITS); 72 + } 68 73 69 74 static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev) 70 75 { ··· 96 73 const unsigned char *addr) 97 74 { 98 75 struct macvlan_dev *vlan; 76 + u32 idx = macvlan_eth_hash(addr); 99 77 100 - hlist_for_each_entry_rcu(vlan, &port->vlan_hash[addr[5]], hlist) { 78 + hlist_for_each_entry_rcu(vlan, &port->vlan_hash[idx], hlist) { 101 79 if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr)) 102 80 return vlan; 103 81 } 104 82 return NULL; 105 83 } 106 84 85 + static struct macvlan_source_entry *macvlan_hash_lookup_source( 86 + const struct macvlan_dev *vlan, 87 + const unsigned char *addr) 88 + { 89 + struct macvlan_source_entry *entry; 90 + u32 idx = macvlan_eth_hash(addr); 91 + struct hlist_head *h = &vlan->port->vlan_source_hash[idx]; 92 + 93 + hlist_for_each_entry_rcu(entry, h, hlist) { 94 + if (ether_addr_equal_64bits(entry->addr, addr) && 95 + entry->vlan == vlan) 96 + return entry; 97 + } 98 + return NULL; 99 + } 100 + 101 + static int macvlan_hash_add_source(struct macvlan_dev *vlan, 102 + const unsigned char *addr) 103 + { 104 + struct macvlan_port *port = vlan->port; 105 + struct macvlan_source_entry *entry; 106 + struct hlist_head *h; 107 + 108 + entry = macvlan_hash_lookup_source(vlan, addr); 109 + if (entry) 110 + return 0; 111 + 112 + entry = kmalloc(sizeof(*entry), GFP_KERNEL); 113 + if (!entry) 114 + return -ENOMEM; 115 + 116 + ether_addr_copy(entry->addr, addr); 117 + entry->vlan = vlan; 118 + h = &port->vlan_source_hash[macvlan_eth_hash(addr)]; 119 + hlist_add_head_rcu(&entry->hlist, h); 120 + vlan->macaddr_count++; 121 + 122 + return 0; 123 + } 124 + 107 125 static void macvlan_hash_add(struct macvlan_dev *vlan) 108 126 { 109 127 struct macvlan_port *port = vlan->port; 110 128 const unsigned char *addr = vlan->dev->dev_addr; 129 + u32 idx = macvlan_eth_hash(addr); 111 130 112 - hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[addr[5]]); 131 + hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[idx]); 132 + } 133 + 134 + static void macvlan_hash_del_source(struct macvlan_source_entry *entry) 135 + { 136 + hlist_del_rcu(&entry->hlist); 137 + kfree_rcu(entry, rcu); 113 138 } 114 139 115 140 static void macvlan_hash_del(struct macvlan_dev *vlan, bool sync) ··· 338 267 atomic_long_inc(&skb->dev->rx_dropped); 339 268 } 340 269 270 + static void macvlan_flush_sources(struct macvlan_port *port, 271 + struct macvlan_dev *vlan) 272 + { 273 + int i; 274 + 275 + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { 276 + struct hlist_node *h, *n; 277 + 278 + hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) { 279 + struct macvlan_source_entry *entry; 280 + 281 + entry = hlist_entry(h, struct macvlan_source_entry, 282 + hlist); 283 + if (entry->vlan == vlan) 284 + macvlan_hash_del_source(entry); 285 + } 286 + } 287 + vlan->macaddr_count = 0; 288 + } 289 + 290 + static void macvlan_forward_source_one(struct sk_buff *skb, 291 + struct macvlan_dev *vlan) 292 + { 293 + struct sk_buff *nskb; 294 + struct net_device *dev; 295 + int len; 296 + int ret; 297 + 298 + dev = vlan->dev; 299 + if (unlikely(!(dev->flags & IFF_UP))) 300 + return; 301 + 302 + nskb = skb_clone(skb, GFP_ATOMIC); 303 + if (!nskb) 304 + return; 305 + 306 + len = nskb->len + ETH_HLEN; 307 + nskb->dev = dev; 308 + nskb->pkt_type = PACKET_HOST; 309 + 310 + ret = netif_rx(nskb); 311 + macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, 0); 312 + } 313 + 314 + static void macvlan_forward_source(struct sk_buff *skb, 315 + struct macvlan_port *port, 316 + const unsigned char *addr) 317 + { 318 + struct macvlan_source_entry *entry; 319 + u32 idx = macvlan_eth_hash(addr); 320 + struct hlist_head *h = &port->vlan_source_hash[idx]; 321 + 322 + hlist_for_each_entry_rcu(entry, h, hlist) { 323 + if (ether_addr_equal_64bits(entry->addr, addr)) 324 + if (entry->vlan->dev->flags & IFF_UP) 325 + macvlan_forward_source_one(skb, entry->vlan); 326 + } 327 + } 328 + 341 329 /* called under rcu_read_lock() from netif_receive_skb */ 342 330 static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) 343 331 { ··· 415 285 if (!skb) 416 286 return RX_HANDLER_CONSUMED; 417 287 eth = eth_hdr(skb); 288 + macvlan_forward_source(skb, port, eth->h_source); 418 289 src = macvlan_hash_lookup(port, eth->h_source); 419 290 if (src && src->mode != MACVLAN_MODE_VEPA && 420 291 src->mode != MACVLAN_MODE_BRIDGE) { ··· 432 301 return RX_HANDLER_PASS; 433 302 } 434 303 304 + macvlan_forward_source(skb, port, eth->h_source); 435 305 if (port->passthru) 436 306 vlan = list_first_or_null_rcu(&port->vlans, 437 307 struct macvlan_dev, list); ··· 799 667 800 668 free_percpu(vlan->pcpu_stats); 801 669 670 + macvlan_flush_sources(port, vlan); 802 671 port->count -= 1; 803 672 if (!port->count) 804 673 macvlan_port_destroy(port->dev); ··· 1058 925 INIT_LIST_HEAD(&port->vlans); 1059 926 for (i = 0; i < MACVLAN_HASH_SIZE; i++) 1060 927 INIT_HLIST_HEAD(&port->vlan_hash[i]); 928 + for (i = 0; i < MACVLAN_HASH_SIZE; i++) 929 + INIT_HLIST_HEAD(&port->vlan_source_hash[i]); 1061 930 1062 931 skb_queue_head_init(&port->bc_queue); 1063 932 INIT_WORK(&port->bc_work, macvlan_process_broadcast); ··· 1101 966 case MACVLAN_MODE_VEPA: 1102 967 case MACVLAN_MODE_BRIDGE: 1103 968 case MACVLAN_MODE_PASSTHRU: 969 + case MACVLAN_MODE_SOURCE: 1104 970 break; 1105 971 default: 1106 972 return -EINVAL; 1107 973 } 1108 974 } 975 + 976 + if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { 977 + switch (nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE])) { 978 + case MACVLAN_MACADDR_ADD: 979 + case MACVLAN_MACADDR_DEL: 980 + case MACVLAN_MACADDR_FLUSH: 981 + case MACVLAN_MACADDR_SET: 982 + break; 983 + default: 984 + return -EINVAL; 985 + } 986 + } 987 + 988 + if (data && data[IFLA_MACVLAN_MACADDR]) { 989 + if (nla_len(data[IFLA_MACVLAN_MACADDR]) != ETH_ALEN) 990 + return -EINVAL; 991 + 992 + if (!is_valid_ether_addr(nla_data(data[IFLA_MACVLAN_MACADDR]))) 993 + return -EADDRNOTAVAIL; 994 + } 995 + 996 + if (data && data[IFLA_MACVLAN_MACADDR_COUNT]) 997 + return -EINVAL; 998 + 999 + return 0; 1000 + } 1001 + 1002 + /** 1003 + * reconfigure list of remote source mac address 1004 + * (only for macvlan devices in source mode) 1005 + * Note regarding alignment: all netlink data is aligned to 4 Byte, which 1006 + * suffices for both ether_addr_copy and ether_addr_equal_64bits usage. 1007 + */ 1008 + static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode, 1009 + struct nlattr *data[]) 1010 + { 1011 + char *addr = NULL; 1012 + int ret, rem, len; 1013 + struct nlattr *nla, *head; 1014 + struct macvlan_source_entry *entry; 1015 + 1016 + if (data[IFLA_MACVLAN_MACADDR]) 1017 + addr = nla_data(data[IFLA_MACVLAN_MACADDR]); 1018 + 1019 + if (mode == MACVLAN_MACADDR_ADD) { 1020 + if (!addr) 1021 + return -EINVAL; 1022 + 1023 + return macvlan_hash_add_source(vlan, addr); 1024 + 1025 + } else if (mode == MACVLAN_MACADDR_DEL) { 1026 + if (!addr) 1027 + return -EINVAL; 1028 + 1029 + entry = macvlan_hash_lookup_source(vlan, addr); 1030 + if (entry) { 1031 + macvlan_hash_del_source(entry); 1032 + vlan->macaddr_count--; 1033 + } 1034 + } else if (mode == MACVLAN_MACADDR_FLUSH) { 1035 + macvlan_flush_sources(vlan->port, vlan); 1036 + } else if (mode == MACVLAN_MACADDR_SET) { 1037 + macvlan_flush_sources(vlan->port, vlan); 1038 + 1039 + if (addr) { 1040 + ret = macvlan_hash_add_source(vlan, addr); 1041 + if (ret) 1042 + return ret; 1043 + } 1044 + 1045 + if (!data || !data[IFLA_MACVLAN_MACADDR_DATA]) 1046 + return 0; 1047 + 1048 + head = nla_data(data[IFLA_MACVLAN_MACADDR_DATA]); 1049 + len = nla_len(data[IFLA_MACVLAN_MACADDR_DATA]); 1050 + 1051 + nla_for_each_attr(nla, head, len, rem) { 1052 + if (nla_type(nla) != IFLA_MACVLAN_MACADDR || 1053 + nla_len(nla) != ETH_ALEN) 1054 + continue; 1055 + 1056 + addr = nla_data(nla); 1057 + ret = macvlan_hash_add_source(vlan, addr); 1058 + if (ret) 1059 + return ret; 1060 + } 1061 + } else { 1062 + return -EINVAL; 1063 + } 1064 + 1109 1065 return 0; 1110 1066 } 1111 1067 ··· 1207 981 struct macvlan_port *port; 1208 982 struct net_device *lowerdev; 1209 983 int err; 984 + int macmode; 1210 985 1211 986 if (!tb[IFLA_LINK]) 1212 987 return -EINVAL; ··· 1261 1034 eth_hw_addr_inherit(dev, lowerdev); 1262 1035 } 1263 1036 1037 + if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { 1038 + if (vlan->mode != MACVLAN_MODE_SOURCE) 1039 + return -EINVAL; 1040 + macmode = nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]); 1041 + err = macvlan_changelink_sources(vlan, macmode, data); 1042 + if (err) 1043 + return err; 1044 + } 1045 + 1264 1046 port->count += 1; 1265 1047 err = register_netdevice(dev); 1266 1048 if (err < 0) ··· 1306 1070 { 1307 1071 struct macvlan_dev *vlan = netdev_priv(dev); 1308 1072 1073 + if (vlan->mode == MACVLAN_MODE_SOURCE) 1074 + macvlan_flush_sources(vlan->port, vlan); 1309 1075 list_del_rcu(&vlan->list); 1310 1076 unregister_netdevice_queue(dev, head); 1311 1077 netdev_upper_dev_unlink(vlan->lowerdev, dev); ··· 1320 1082 struct macvlan_dev *vlan = netdev_priv(dev); 1321 1083 enum macvlan_mode mode; 1322 1084 bool set_mode = false; 1085 + enum macvlan_macaddr_mode macmode; 1086 + int ret; 1323 1087 1324 1088 /* Validate mode, but don't set yet: setting flags may fail. */ 1325 1089 if (data && data[IFLA_MACVLAN_MODE]) { ··· 1331 1091 if ((mode == MACVLAN_MODE_PASSTHRU) != 1332 1092 (vlan->mode == MACVLAN_MODE_PASSTHRU)) 1333 1093 return -EINVAL; 1094 + if (vlan->mode == MACVLAN_MODE_SOURCE && 1095 + vlan->mode != mode) 1096 + macvlan_flush_sources(vlan->port, vlan); 1334 1097 } 1335 1098 1336 1099 if (data && data[IFLA_MACVLAN_FLAGS]) { ··· 1353 1110 } 1354 1111 if (set_mode) 1355 1112 vlan->mode = mode; 1113 + if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { 1114 + if (vlan->mode != MACVLAN_MODE_SOURCE) 1115 + return -EINVAL; 1116 + macmode = nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]); 1117 + ret = macvlan_changelink_sources(vlan, macmode, data); 1118 + if (ret) 1119 + return ret; 1120 + } 1356 1121 return 0; 1122 + } 1123 + 1124 + static size_t macvlan_get_size_mac(const struct macvlan_dev *vlan) 1125 + { 1126 + if (vlan->macaddr_count == 0) 1127 + return 0; 1128 + return nla_total_size(0) /* IFLA_MACVLAN_MACADDR_DATA */ 1129 + + vlan->macaddr_count * nla_total_size(sizeof(u8) * ETH_ALEN); 1357 1130 } 1358 1131 1359 1132 static size_t macvlan_get_size(const struct net_device *dev) 1360 1133 { 1134 + struct macvlan_dev *vlan = netdev_priv(dev); 1135 + 1361 1136 return (0 1362 1137 + nla_total_size(4) /* IFLA_MACVLAN_MODE */ 1363 1138 + nla_total_size(2) /* IFLA_MACVLAN_FLAGS */ 1139 + + nla_total_size(4) /* IFLA_MACVLAN_MACADDR_COUNT */ 1140 + + macvlan_get_size_mac(vlan) /* IFLA_MACVLAN_MACADDR */ 1364 1141 ); 1142 + } 1143 + 1144 + static int macvlan_fill_info_macaddr(struct sk_buff *skb, 1145 + const struct macvlan_dev *vlan, 1146 + const int i) 1147 + { 1148 + struct hlist_head *h = &vlan->port->vlan_source_hash[i]; 1149 + struct macvlan_source_entry *entry; 1150 + 1151 + hlist_for_each_entry_rcu(entry, h, hlist) { 1152 + if (entry->vlan != vlan) 1153 + continue; 1154 + if (nla_put(skb, IFLA_MACVLAN_MACADDR, ETH_ALEN, entry->addr)) 1155 + return 1; 1156 + } 1157 + return 0; 1365 1158 } 1366 1159 1367 1160 static int macvlan_fill_info(struct sk_buff *skb, 1368 1161 const struct net_device *dev) 1369 1162 { 1370 1163 struct macvlan_dev *vlan = netdev_priv(dev); 1164 + int i; 1165 + struct nlattr *nest; 1371 1166 1372 1167 if (nla_put_u32(skb, IFLA_MACVLAN_MODE, vlan->mode)) 1373 1168 goto nla_put_failure; 1374 1169 if (nla_put_u16(skb, IFLA_MACVLAN_FLAGS, vlan->flags)) 1375 1170 goto nla_put_failure; 1171 + if (nla_put_u32(skb, IFLA_MACVLAN_MACADDR_COUNT, vlan->macaddr_count)) 1172 + goto nla_put_failure; 1173 + if (vlan->macaddr_count > 0) { 1174 + nest = nla_nest_start(skb, IFLA_MACVLAN_MACADDR_DATA); 1175 + if (nest == NULL) 1176 + goto nla_put_failure; 1177 + 1178 + for (i = 0; i < MACVLAN_HASH_SIZE; i++) { 1179 + if (macvlan_fill_info_macaddr(skb, vlan, i)) 1180 + goto nla_put_failure; 1181 + } 1182 + nla_nest_end(skb, nest); 1183 + } 1376 1184 return 0; 1377 1185 1378 1186 nla_put_failure: ··· 1433 1139 static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = { 1434 1140 [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, 1435 1141 [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, 1142 + [IFLA_MACVLAN_MACADDR_MODE] = { .type = NLA_U32 }, 1143 + [IFLA_MACVLAN_MACADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 1144 + [IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED }, 1145 + [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 }, 1436 1146 }; 1437 1147 1438 1148 int macvlan_link_register(struct rtnl_link_ops *ops)
+1
include/linux/if_macvlan.h
··· 60 60 #ifdef CONFIG_NET_POLL_CONTROLLER 61 61 struct netpoll *netpoll; 62 62 #endif 63 + unsigned int macaddr_count; 63 64 }; 64 65 65 66 static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
+12
include/uapi/linux/if_link.h
··· 303 303 IFLA_MACVLAN_UNSPEC, 304 304 IFLA_MACVLAN_MODE, 305 305 IFLA_MACVLAN_FLAGS, 306 + IFLA_MACVLAN_MACADDR_MODE, 307 + IFLA_MACVLAN_MACADDR, 308 + IFLA_MACVLAN_MACADDR_DATA, 309 + IFLA_MACVLAN_MACADDR_COUNT, 306 310 __IFLA_MACVLAN_MAX, 307 311 }; 308 312 ··· 317 313 MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ 318 314 MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ 319 315 MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ 316 + MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */ 317 + }; 318 + 319 + enum macvlan_macaddr_mode { 320 + MACVLAN_MACADDR_ADD, 321 + MACVLAN_MACADDR_DEL, 322 + MACVLAN_MACADDR_FLUSH, 323 + MACVLAN_MACADDR_SET, 320 324 }; 321 325 322 326 #define MACVLAN_FLAG_NOPROMISC 1