diff -uprN -X linux-3.2.1-orig/Documentation/dontdiff linux-3.2.1-orig/drivers/net/macvlan.c linux-3.2.1-macvlan/drivers/net/macvlan.c
--- linux-3.2.1-orig/drivers/net/macvlan.c	2012-01-27 09:20:02.000000000 +0100
+++ linux-3.2.1-macvlan/drivers/net/macvlan.c	2012-01-27 10:52:29.000000000 +0100
@@ -40,6 +40,7 @@ struct macvlan_port {
 	struct rcu_head		rcu;
 	bool 			passthru;
 	int			count;
+	struct hlist_head	vlan_source_hash[MACVLAN_HASH_SIZE];
 };
 
 static void macvlan_port_destroy(struct net_device *dev);
@@ -155,6 +156,114 @@ static void macvlan_broadcast(struct sk_
 	}
 }
 
+struct macvlan_source_list {
+	struct hlist_node	hlist;
+	struct macvlan_dev	*vlan;
+	unsigned char		addr[ETH_ALEN];
+	struct rcu_head		rcu;
+};
+
+static struct macvlan_source_list *macvlan_hash_lookup_sources_list(
+	const struct macvlan_dev *vlan,
+	const unsigned char *addr)
+{
+	struct macvlan_source_list *list;
+	struct hlist_node *n;
+	struct hlist_head *h = &vlan->port->vlan_source_hash[addr[5]];
+
+	hlist_for_each_entry_rcu(list, n, h, hlist) {
+		if (!compare_ether_addr_64bits(list->addr, addr) &&
+		    list->vlan == vlan)
+			return list;
+	}
+	return NULL;
+}
+
+static int macvlan_hash_add_sources(struct macvlan_dev *vlan,
+				    const unsigned char *addr)
+{
+	struct macvlan_port *port = vlan->port;
+	struct macvlan_source_list *list;
+	struct hlist_head *h;
+
+	list = macvlan_hash_lookup_sources_list(vlan, addr);
+	if (!list) {
+		list = kmalloc(sizeof(*list), GFP_KERNEL);
+		if (list) {
+			memcpy(list->addr, addr, ETH_ALEN);
+			list->vlan = vlan;
+			h = &port->vlan_source_hash[addr[5]];
+			hlist_add_head_rcu(&list->hlist, h);
+		} else
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static void macvlan_hash_del_sources(struct macvlan_source_list *list)
+{
+	hlist_del_rcu(&list->hlist);
+	kfree_rcu(list, rcu);
+}
+
+static void macvlan_flush_sources(struct macvlan_port *port,
+				struct macvlan_dev *vlan)
+{
+	int i;
+
+	for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
+		struct hlist_node *h, *n;
+
+		hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) {
+			struct macvlan_source_list *list;
+
+			list = hlist_entry(h, struct macvlan_source_list,
+					   hlist);
+			if (list->vlan == vlan)
+				macvlan_hash_del_sources(list);
+		}
+	}
+}
+
+static void macvlan_forward_sources_one(struct sk_buff *skb,
+					struct macvlan_dev *vlan)
+{
+	struct sk_buff *nskb;
+	struct net_device *dev;
+	int len;
+	int ret;
+
+	dev = vlan->dev;
+	if (unlikely(!(dev->flags & IFF_UP)))
+		return;
+
+	nskb = skb_clone(skb, GFP_ATOMIC);
+	if (!nskb)
+		return;
+
+	len = nskb->len + ETH_HLEN;
+	nskb->dev = dev;
+	nskb->pkt_type = PACKET_HOST;
+	ret = vlan->receive(nskb);
+	macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, 0);
+}
+
+static void macvlan_forward_sources(struct sk_buff *skb,
+				    struct macvlan_port *port,
+				    const unsigned char *addr)
+{
+	struct macvlan_source_list *list;
+	struct hlist_node *n;
+	struct hlist_head *h = &port->vlan_source_hash[addr[5]];
+
+	hlist_for_each_entry_rcu(list, n, h, hlist) {
+		if (!compare_ether_addr_64bits(list->addr, addr))
+			if (list->vlan->dev->flags & IFF_UP)
+				macvlan_forward_sources_one(skb, list->vlan);
+	}
+	return;
+}
+
 /* called under rcu_read_lock() from netif_receive_skb */
 static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
 {
@@ -172,6 +281,8 @@ static rx_handler_result_t macvlan_handl
 		skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
 		if (!skb)
 			return RX_HANDLER_CONSUMED;
+
+		macvlan_forward_sources(skb, port, eth->h_source);
 		src = macvlan_hash_lookup(port, eth->h_source);
 		if (!src)
 			/* frame comes from an external address */
@@ -202,6 +313,7 @@ static rx_handler_result_t macvlan_handl
 		return RX_HANDLER_PASS;
 	}
 
+	macvlan_forward_sources(skb, port, eth->h_source);
 	if (port->passthru)
 		vlan = list_first_entry(&port->vlans, struct macvlan_dev, list);
 	else
@@ -474,6 +586,7 @@ static void macvlan_uninit(struct net_de
 
 	free_percpu(vlan->pcpu_stats);
 
+	macvlan_flush_sources(port, vlan);
 	port->count -= 1;
 	if (!port->count)
 		macvlan_port_destroy(port->dev);
@@ -615,7 +728,8 @@ static int macvlan_port_create(struct ne
 	INIT_LIST_HEAD(&port->vlans);
 	for (i = 0; i < MACVLAN_HASH_SIZE; i++)
 		INIT_HLIST_HEAD(&port->vlan_hash[i]);
-
+	for (i = 0; i < MACVLAN_HASH_SIZE; i++)
+		INIT_HLIST_HEAD(&port->vlan_source_hash[i]);
 	err = netdev_rx_handler_register(dev, macvlan_handle_frame, port);
 	if (err)
 		kfree(port);
@@ -648,11 +762,31 @@ static int macvlan_validate(struct nlatt
 		case MACVLAN_MODE_VEPA:
 		case MACVLAN_MODE_BRIDGE:
 		case MACVLAN_MODE_PASSTHRU:
+		case MACVLAN_MODE_SOURCE:
 			break;
 		default:
 			return -EINVAL;
 		}
 	}
+
+	if (data && data[IFLA_MACVLAN_MACADDR_MODE]) {
+		switch (nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE])) {
+		case MACVLAN_MACADDR_ADD:
+		case MACVLAN_MACADDR_DEL:
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (data && data[IFLA_MACVLAN_MACADDR]) {
+		if (nla_len(data[IFLA_MACVLAN_MACADDR]) != ETH_ALEN)
+			return -EINVAL;
+
+		if (!is_valid_ether_addr(nla_data(data[IFLA_MACVLAN_MACADDR])))
+			return -EADDRNOTAVAIL;
+	}
+
 	return 0;
 }
 
@@ -749,39 +883,141 @@ void macvlan_dellink(struct net_device *
 {
 	struct macvlan_dev *vlan = netdev_priv(dev);
 
+	if (vlan->mode == MACVLAN_MODE_SOURCE)
+		macvlan_flush_sources(vlan->port, vlan);
 	list_del(&vlan->list);
 	unregister_netdevice_queue(dev, head);
 }
 EXPORT_SYMBOL_GPL(macvlan_dellink);
 
+static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode,
+				      unsigned char *addr)
+{
+	if (mode == MACVLAN_MACADDR_ADD)
+		return macvlan_hash_add_sources(vlan, addr);
+
+	else if (mode == MACVLAN_MACADDR_DEL) {
+		struct macvlan_source_list *list;
+
+		list = macvlan_hash_lookup_sources_list(vlan, addr);
+		if (list)
+			macvlan_hash_del_sources(list);
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
 static int macvlan_changelink(struct net_device *dev,
 		struct nlattr *tb[], struct nlattr *data[])
 {
 	struct macvlan_dev *vlan = netdev_priv(dev);
-	if (data && data[IFLA_MACVLAN_MODE])
+	u32 mode;
+	unsigned char *addr;
+
+	if (data && data[IFLA_MACVLAN_MODE]) {
+		if (vlan->mode == MACVLAN_MODE_SOURCE &&
+		    vlan->mode != nla_get_u32(data[IFLA_MACVLAN_MODE]))
+			macvlan_flush_sources(vlan->port, vlan);
 		vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
+	}
+
+	if (data && data[IFLA_MACVLAN_MACADDR_MODE] &&
+	    data[IFLA_MACVLAN_MACADDR]) {
+		mode = nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]);
+		addr = nla_data(nla_data(data[IFLA_MACVLAN_MACADDR]));
+		if (vlan->mode == MACVLAN_MODE_SOURCE)
+			return macvlan_changelink_sources(vlan, mode, addr);
+		else
+			return -EINVAL;
+	}
+
 	return 0;
 }
 
 static size_t macvlan_get_size(const struct net_device *dev)
 {
-	return nla_total_size(4);
+	struct macvlan_dev *vlan = netdev_priv(dev);
+	struct macvlan_port *port = vlan->port;
+	u16 source_size = 0;
+	int count = 0;
+
+	if (vlan->mode == MACVLAN_MODE_SOURCE) {
+		int i;
+		for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
+			struct hlist_node *n;
+			struct hlist_head *h = &port->vlan_source_hash[i];
+			struct macvlan_source_list *list;
+
+			hlist_for_each_entry_rcu(list, n, h, hlist) {
+				if (list->vlan == vlan)
+					count++;
+			}
+		}
+		if (count)
+			source_size = nla_total_size(sizeof(struct nlattr))
+				+ nla_total_size(sizeof(u8) * ETH_ALEN) * count;
+	}
+
+	return nla_total_size(4) + source_size;
+}
+
+static int macvlan_fill_nested(struct sk_buff *skb, const char *addr)
+{
+	NLA_PUT(skb, IFLA_MACVLAN_MACADDR, ETH_ALEN, addr);
+
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
 }
 
 static int macvlan_fill_info(struct sk_buff *skb,
 				const struct net_device *dev)
 {
 	struct macvlan_dev *vlan = netdev_priv(dev);
+	struct macvlan_port *port = vlan->port;
+	struct nlattr *adt;
 
 	NLA_PUT_U32(skb, IFLA_MACVLAN_MODE, vlan->mode);
+
+	if (vlan->mode == MACVLAN_MODE_SOURCE) {
+		int i;
+
+		adt = nla_nest_start(skb, IFLA_MACVLAN_MACADDR_DATA);
+		if (!adt)
+			goto nla_put_failure;
+
+		for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
+			struct hlist_node *n;
+			struct hlist_head *h = &port->vlan_source_hash[i];
+			struct macvlan_source_list *list;
+
+			hlist_for_each_entry_rcu(list, n, h, hlist) {
+				if (list->vlan == vlan)
+					if (macvlan_fill_nested(skb,
+								list->addr))
+						goto nla_nested_failure;
+			}
+		}
+		nla_nest_end(skb, adt);
+	}
+
 	return 0;
 
+nla_nested_failure:
+	nla_nest_end(skb, adt);
+	return -EMSGSIZE;
+
 nla_put_failure:
 	return -EMSGSIZE;
 }
 
 static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
 	[IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
+	[IFLA_MACVLAN_MACADDR_MODE] = { .type = NLA_U32 },
+	[IFLA_MACVLAN_MACADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
+	[IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED },
 };
 
 int macvlan_link_register(struct rtnl_link_ops *ops)
diff -uprN -X linux-3.2.1-orig/Documentation/dontdiff linux-3.2.1-orig/include/linux/if_link.h linux-3.2.1-macvlan/include/linux/if_link.h
--- linux-3.2.1-orig/include/linux/if_link.h	2012-01-27 09:20:24.000000000 +0100
+++ linux-3.2.1-macvlan/include/linux/if_link.h	2012-01-27 09:23:38.000000000 +0100
@@ -252,6 +252,9 @@ struct ifla_vlan_qos_mapping {
 enum {
 	IFLA_MACVLAN_UNSPEC,
 	IFLA_MACVLAN_MODE,
+	IFLA_MACVLAN_MACADDR_MODE,
+	IFLA_MACVLAN_MACADDR,
+	IFLA_MACVLAN_MACADDR_DATA,
 	__IFLA_MACVLAN_MAX,
 };
 
@@ -262,6 +265,12 @@ enum macvlan_mode {
 	MACVLAN_MODE_VEPA    = 2, /* talk to other ports through ext bridge */
 	MACVLAN_MODE_BRIDGE  = 4, /* talk to bridge ports directly */
 	MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
+	MACVLAN_MODE_SOURCE  = 16,/* use source MAC address list to assign */
+};
+
+enum macvlan_macaddr_mode {
+	MACVLAN_MACADDR_ADD,
+	MACVLAN_MACADDR_DEL,
 };
 
 /* SR-IOV virtual function management section */
