linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch v3, kernel version 3.2.1] Source mode for macvlan interface
       [not found] <12446987.2261327416407141.JavaMail.root@5-MeO-DMT.ynet.sk>
@ 2012-01-24 14:55 ` Stefan Gula
  2012-01-25  4:03   ` David Miller
  0 siblings, 1 reply; 5+ messages in thread
From: Stefan Gula @ 2012-01-24 14:55 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, linux-kernel

From: Stefan Gula <steweg@gmail.com>

New mode of macvlan interface called "source" 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 allows
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 macvlan0 type macvlan macaddr add 00:11:11:11:11:11
  ip link set link macvlan0 type macvlan macaddr add 00:22:22:22:22:22
  ip link set link macvlan0 type macvlan macaddr add 00:33:33:33:33:33
  ip link set link macvlan1 type macvlan macaddr add 00:33:33:33:33:33
  ip link set link 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.

Signed-off-by: Stefan Gula <steweg@gmail.com>

---

V3: list of changes:
 - changed GFP_ATOMIC to GFP_KERNEL
 - modified rtnetlink.c to automatic adjust min_ifinfo_dump_size during 
   do_setlink to adjust size of allocated sk_buff structure
 - removed IFLA_MACVLAN_MACADDR_ADT - not needed anymore

diff -uprN -X linux/Documentation/dontdiff linux-3.2.1-orig/drivers/net/macvlan.c linux/drivers/net/macvlan.c
--- linux-3.2.1-orig/drivers/net/macvlan.c	2012-01-12 20:42:45.000000000 +0100
+++ linux/drivers/net/macvlan.c	2012-01-24 14:25:38.176220426 +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;
+
+	hlist_for_each_entry_rcu(list, n,
+		&vlan->port->vlan_source_hash[addr[5]], 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;
+
+	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;
+			hlist_add_head_rcu(&list->hlist,
+				&port->vlan_source_hash[addr[5]]);
+		} 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
+				= 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,
+	const struct macvlan_port *port,
+	const unsigned char *addr)
+{
+	struct macvlan_source_list *list;
+	struct hlist_node *n;
+
+	hlist_for_each_entry_rcu(list, n, &port->vlan_source_hash[addr[5]],
+				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,6 +883,8 @@ 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);
 }
@@ -758,30 +894,120 @@ static int macvlan_changelink(struct net
 		struct nlattr *tb[], struct nlattr *data[])
 {
 	struct macvlan_dev *vlan = netdev_priv(dev);
-	if (data && data[IFLA_MACVLAN_MODE])
+
+	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]) {
+		if (vlan->mode == MACVLAN_MODE_SOURCE) {
+			if ((nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]) ==
+				MACVLAN_MACADDR_ADD))
+				return macvlan_hash_add_sources(vlan,
+					nla_data(data[IFLA_MACVLAN_MACADDR]));
+
+			else if (nla_get_u32(data[IFLA_MACVLAN_MACADDR_MODE]) ==
+				MACVLAN_MACADDR_DEL) {
+				struct macvlan_source_list *list;
+
+				list = macvlan_hash_lookup_sources_list(vlan,
+					nla_data(data[IFLA_MACVLAN_MACADDR]));
+				if (list)
+					macvlan_hash_del_sources(list);
+			} else
+				return -EINVAL;
+		} 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);
+	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 macvlan_source_list *list;
+
+			hlist_for_each_entry_rcu(list, n,
+				&vlan->port->vlan_source_hash[i], 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 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 macvlan_source_list *list;
+
+			hlist_for_each_entry_rcu(list, n,
+				&vlan->port->vlan_source_hash[i], 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/Documentation/dontdiff linux-3.2.1-orig/include/linux/if_link.h linux/include/linux/if_link.h
--- linux-3.2.1-orig/include/linux/if_link.h	2012-01-12 20:42:45.000000000 +0100
+++ linux/include/linux/if_link.h	2012-01-24 12:36:51.885312680 +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 */
diff -uprN -X linux/Documentation/dontdiff linux-3.2.1-orig/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- linux-3.2.1-orig/net/core/rtnetlink.c	2012-01-12 20:42:45.000000000 +0100
+++ linux/net/core/rtnetlink.c	2012-01-24 14:26:58.083219352 +0100
@@ -1506,6 +1506,9 @@ errout:
 
 	if (send_addr_notify)
 		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+	min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev),
+					min_ifinfo_dump_size);
+
 	return err;
 }
 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [patch v3, kernel version 3.2.1] Source mode for macvlan interface
  2012-01-24 14:55 ` [patch v3, kernel version 3.2.1] Source mode for macvlan interface Stefan Gula
@ 2012-01-25  4:03   ` David Miller
  2012-01-25  6:22     ` Štefan Gula
  0 siblings, 1 reply; 5+ messages in thread
From: David Miller @ 2012-01-25  4:03 UTC (permalink / raw)
  To: steweg; +Cc: kaber, netdev, linux-kernel

From: Stefan Gula <steweg@ynet.sk>
Date: Tue, 24 Jan 2012 15:55:53 +0100 (CET)

> diff -uprN -X linux/Documentation/dontdiff linux-3.2.1-orig/net/core/rtnetlink.c linux/net/core/rtnetlink.c
> --- linux-3.2.1-orig/net/core/rtnetlink.c	2012-01-12 20:42:45.000000000 +0100
> +++ linux/net/core/rtnetlink.c	2012-01-24 14:26:58.083219352 +0100
> @@ -1506,6 +1506,9 @@ errout:
>  
>  	if (send_addr_notify)
>  		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
> +	min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev),
> +					min_ifinfo_dump_size);
> +

What is this?  And whatever it is why isn't it 1) described in the
commit message and why isn't it 2) in a seperate patch if it's
unrelated to this macvlan change?

I'm not apply this patch as it is right now, it needs some sort of
change.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [patch v3, kernel version 3.2.1] Source mode for macvlan interface
  2012-01-25  4:03   ` David Miller
@ 2012-01-25  6:22     ` Štefan Gula
  2012-01-25  6:29       ` Eric Dumazet
  0 siblings, 1 reply; 5+ messages in thread
From: Štefan Gula @ 2012-01-25  6:22 UTC (permalink / raw)
  To: David Miller; +Cc: kaber, netdev, linux-kernel

2012/1/25 David Miller <davem@davemloft.net>:
> From: Stefan Gula <steweg@ynet.sk>
> Date: Tue, 24 Jan 2012 15:55:53 +0100 (CET)
>
>> diff -uprN -X linux/Documentation/dontdiff linux-3.2.1-orig/net/core/rtnetlink.c linux/net/core/rtnetlink.c
>> --- linux-3.2.1-orig/net/core/rtnetlink.c     2012-01-12 20:42:45.000000000 +0100
>> +++ linux/net/core/rtnetlink.c        2012-01-24 14:26:58.083219352 +0100
>> @@ -1506,6 +1506,9 @@ errout:
>>
>>       if (send_addr_notify)
>>               call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
>> +     min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev),
>> +                                     min_ifinfo_dump_size);
>> +
>
> What is this?  And whatever it is why isn't it 1) described in the
> commit message and why isn't it 2) in a seperate patch if it's
> unrelated to this macvlan change?
>
> I'm not apply this patch as it is right now, it needs some sort of
> change.
>
It's a result of need to have enough space allocated for sk_buff
struct before called macvlan_fill_info. I know that it looks somehow
messy. BUt before the macvlan_fill_info is called there is only one
procedure calcit. The problem of calcit is that it doesn't have access
to struct net_device *dev int it. It has only access to struct sk_buff
*skb, so from that point it cannot be called directly (whole parsing
code had to be copied there to get the *dev a calling
if_nlmsg_size(dev) from it. On the other hand calcit returns global
static value of min_ifinfo_dump_size and that is shared among all
interfaces and modifying like this do the job properly. There was
another different way and that was creating and registering a whole
new PF_MACLVAN netlink group resulting in copying almost 95% percent
of given code and modified it somehow to do the same.

If it should be result of separate commitment than ok, but it was
result apparently of need only for use of macvlan that's why it was
included in my patch.

So how should I proceed with it?

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [patch v3, kernel version 3.2.1] Source mode for macvlan interface
  2012-01-25  6:22     ` Štefan Gula
@ 2012-01-25  6:29       ` Eric Dumazet
  2012-01-25  6:56         ` Štefan Gula
  0 siblings, 1 reply; 5+ messages in thread
From: Eric Dumazet @ 2012-01-25  6:29 UTC (permalink / raw)
  To: Štefan Gula; +Cc: David Miller, kaber, netdev, linux-kernel

Le mercredi 25 janvier 2012 à 07:22 +0100, Štefan Gula a écrit :

> It's a result of need to have enough space allocated for sk_buff
> struct before called macvlan_fill_info. I know that it looks somehow
> messy. BUt before the macvlan_fill_info is called there is only one
> procedure calcit. The problem of calcit is that it doesn't have access
> to struct net_device *dev int it. It has only access to struct sk_buff
> *skb, so from that point it cannot be called directly (whole parsing
> code had to be copied there to get the *dev a calling
> if_nlmsg_size(dev) from it. On the other hand calcit returns global
> static value of min_ifinfo_dump_size and that is shared among all
> interfaces and modifying like this do the job properly. There was
> another different way and that was creating and registering a whole
> new PF_MACLVAN netlink group resulting in copying almost 95% percent
> of given code and modified it somehow to do the same.
> 
> If it should be result of separate commitment than ok, but it was
> result apparently of need only for use of macvlan that's why it was
> included in my patch.
> 
> So how should I proceed with it?

A separate patch for this part, as David said ?

The intent is to catch people attention on a particular point, instead
of unnoticed code in some huge unrelated patch.




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [patch v3, kernel version 3.2.1] Source mode for macvlan interface
  2012-01-25  6:29       ` Eric Dumazet
@ 2012-01-25  6:56         ` Štefan Gula
  0 siblings, 0 replies; 5+ messages in thread
From: Štefan Gula @ 2012-01-25  6:56 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, kaber, netdev, linux-kernel

Dňa 25. januára 2012 7:29, Eric Dumazet <eric.dumazet@gmail.com> napísal/a:
> Le mercredi 25 janvier 2012 à 07:22 +0100, Štefan Gula a écrit :
>
>> It's a result of need to have enough space allocated for sk_buff
>> struct before called macvlan_fill_info. I know that it looks somehow
>> messy. BUt before the macvlan_fill_info is called there is only one
>> procedure calcit. The problem of calcit is that it doesn't have access
>> to struct net_device *dev int it. It has only access to struct sk_buff
>> *skb, so from that point it cannot be called directly (whole parsing
>> code had to be copied there to get the *dev a calling
>> if_nlmsg_size(dev) from it. On the other hand calcit returns global
>> static value of min_ifinfo_dump_size and that is shared among all
>> interfaces and modifying like this do the job properly. There was
>> another different way and that was creating and registering a whole
>> new PF_MACLVAN netlink group resulting in copying almost 95% percent
>> of given code and modified it somehow to do the same.
>>
>> If it should be result of separate commitment than ok, but it was
>> result apparently of need only for use of macvlan that's why it was
>> included in my patch.
>>
>> So how should I proceed with it?
>
> A separate patch for this part, as David said ?
>
> The intent is to catch people attention on a particular point, instead
> of unnoticed code in some huge unrelated patch.
>
>
>
Ok

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-01-25  6:56 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <12446987.2261327416407141.JavaMail.root@5-MeO-DMT.ynet.sk>
2012-01-24 14:55 ` [patch v3, kernel version 3.2.1] Source mode for macvlan interface Stefan Gula
2012-01-25  4:03   ` David Miller
2012-01-25  6:22     ` Štefan Gula
2012-01-25  6:29       ` Eric Dumazet
2012-01-25  6:56         ` Štefan Gula

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).