Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2] packet: fix use after free race in send path when dev is released
From: Daniel Borkmann @ 2013-11-21  9:47 UTC (permalink / raw)
  To: davem; +Cc: netdev, Salam Noureddine, Ben Greear, Eric Dumazet

Salam reported a use after free bug in PF_PACKET that occurs when
we're sending out frames on a socket bound device and suddenly the
net device is being unregistered. It appears that commit 827d9780
introduced a possible race condition between {t,}packet_snd() and
packet_notifier(). In the case of a bound socket, packet_notifier()
can drop the last reference to the net_device and {t,}packet_snd()
might end up suddenly sending a packet over a freed net_device.

To avoid reverting 827d9780 and thus introducing a performance
regression compared to the current state of things, we decided to
hold a cached RCU protected pointer to the net device and maintain
it on write side via bind spin_lock protected register_prot_hook()
and __unregister_prot_hook() calls.

In {t,}packet_snd() path, we access this pointer under rcu_read_lock
through packet_cached_dev_get() that holds reference to the device
to prevent it from being freed through packet_notifier() while
we're in send path. This is okay to do as dev_put()/dev_hold() are
per-cpu counters, so this should not be a performance issue. Also,
the code simplifies a bit as we don't need need_rls_dev anymore. For
the notifier section, we're paranoid and defer putting the reference
on the net device, so that we really make sure that we've quite the
RCU read section from packet_cached_dev_get() and waited a grace
period. This fixes the issue reported.

Fixes: 827d978037d7 ("af-packet: Use existing netdev reference for bound sockets.")
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Salam Noureddine <noureddine@aristanetworks.com>
Tested-by: Salam Noureddine <noureddine@aristanetworks.com>
Cc: Ben Greear <greearb@candelatech.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
---
 v1->v2:
  - Applied feedback from Dave and Eric, thanks a lot for this!
  - After back and forth and trying out multiple ideas, we think that this
    patch seems the best way to fix this issue.

 net/packet/af_packet.c | 85 +++++++++++++++++++++++++++++++++-----------------
 net/packet/internal.h  |  2 ++
 2 files changed, 59 insertions(+), 28 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 2e8286b..e6de9cc 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -244,11 +244,15 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
 static void register_prot_hook(struct sock *sk)
 {
 	struct packet_sock *po = pkt_sk(sk);
+
 	if (!po->running) {
-		if (po->fanout)
+		if (po->fanout) {
 			__fanout_link(sk, po);
-		else
+		} else {
 			dev_add_pack(&po->prot_hook);
+			rcu_assign_pointer(po->cached_dev, po->prot_hook.dev);
+		}
+
 		sock_hold(sk);
 		po->running = 1;
 	}
@@ -266,10 +270,13 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
 	struct packet_sock *po = pkt_sk(sk);
 
 	po->running = 0;
-	if (po->fanout)
+	if (po->fanout) {
 		__fanout_unlink(sk, po);
-	else
+	} else {
 		__dev_remove_pack(&po->prot_hook);
+		RCU_INIT_POINTER(po->cached_dev, NULL);
+	}
+
 	__sock_put(sk);
 
 	if (sync) {
@@ -2052,12 +2059,24 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
 	return tp_len;
 }
 
+static struct net_device *packet_cached_dev_get(struct packet_sock *po)
+{
+	struct net_device *dev;
+
+	rcu_read_lock();
+	dev = rcu_dereference(po->cached_dev);
+	if (dev)
+		dev_hold(dev);
+	rcu_read_unlock();
+
+	return dev;
+}
+
 static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 {
 	struct sk_buff *skb;
 	struct net_device *dev;
 	__be16 proto;
-	bool need_rls_dev = false;
 	int err, reserve = 0;
 	void *ph;
 	struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
@@ -2070,7 +2089,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 	mutex_lock(&po->pg_vec_lock);
 
 	if (saddr == NULL) {
-		dev = po->prot_hook.dev;
+		dev	= packet_cached_dev_get(po);
 		proto	= po->num;
 		addr	= NULL;
 	} else {
@@ -2084,19 +2103,17 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 		proto	= saddr->sll_protocol;
 		addr	= saddr->sll_addr;
 		dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
-		need_rls_dev = true;
 	}
 
 	err = -ENXIO;
 	if (unlikely(dev == NULL))
 		goto out;
-
-	reserve = dev->hard_header_len;
-
 	err = -ENETDOWN;
 	if (unlikely(!(dev->flags & IFF_UP)))
 		goto out_put;
 
+	reserve = dev->hard_header_len;
+
 	size_max = po->tx_ring.frame_size
 		- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
 
@@ -2173,8 +2190,7 @@ out_status:
 	__packet_set_status(po, ph, status);
 	kfree_skb(skb);
 out_put:
-	if (need_rls_dev)
-		dev_put(dev);
+	dev_put(dev);
 out:
 	mutex_unlock(&po->pg_vec_lock);
 	return err;
@@ -2212,7 +2228,6 @@ static int packet_snd(struct socket *sock,
 	struct sk_buff *skb;
 	struct net_device *dev;
 	__be16 proto;
-	bool need_rls_dev = false;
 	unsigned char *addr;
 	int err, reserve = 0;
 	struct virtio_net_hdr vnet_hdr = { 0 };
@@ -2228,7 +2243,7 @@ static int packet_snd(struct socket *sock,
 	 */
 
 	if (saddr == NULL) {
-		dev = po->prot_hook.dev;
+		dev	= packet_cached_dev_get(po);
 		proto	= po->num;
 		addr	= NULL;
 	} else {
@@ -2240,19 +2255,17 @@ static int packet_snd(struct socket *sock,
 		proto	= saddr->sll_protocol;
 		addr	= saddr->sll_addr;
 		dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
-		need_rls_dev = true;
 	}
 
 	err = -ENXIO;
-	if (dev == NULL)
+	if (unlikely(dev == NULL))
 		goto out_unlock;
-	if (sock->type == SOCK_RAW)
-		reserve = dev->hard_header_len;
-
 	err = -ENETDOWN;
-	if (!(dev->flags & IFF_UP))
+	if (unlikely(!(dev->flags & IFF_UP)))
 		goto out_unlock;
 
+	if (sock->type == SOCK_RAW)
+		reserve = dev->hard_header_len;
 	if (po->has_vnet_hdr) {
 		vnet_hdr_len = sizeof(vnet_hdr);
 
@@ -2386,15 +2399,14 @@ static int packet_snd(struct socket *sock,
 	if (err > 0 && (err = net_xmit_errno(err)) != 0)
 		goto out_unlock;
 
-	if (need_rls_dev)
-		dev_put(dev);
+	dev_put(dev);
 
 	return len;
 
 out_free:
 	kfree_skb(skb);
 out_unlock:
-	if (dev && need_rls_dev)
+	if (dev)
 		dev_put(dev);
 out:
 	return err;
@@ -2614,6 +2626,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
 	po = pkt_sk(sk);
 	sk->sk_family = PF_PACKET;
 	po->num = proto;
+	RCU_INIT_POINTER(po->cached_dev, NULL);
 
 	sk->sk_destruct = packet_sock_destruct;
 	sk_refcnt_debug_inc(sk);
@@ -3298,6 +3311,22 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 	return 0;
 }
 
+static void packet_dev_put_deferred(struct rcu_head *head)
+{
+	struct packet_sock *po = container_of(head, struct packet_sock, rcu);
+	struct sock *sk = &po->sk;
+
+	spin_lock(&po->bind_lock);
+	po->ifindex = -1;
+
+	if (po->prot_hook.dev) {
+		dev_put(po->prot_hook.dev);
+		po->prot_hook.dev = NULL;
+	}
+
+	spin_unlock(&po->bind_lock);
+	sock_put(sk);
+}
 
 static int packet_notifier(struct notifier_block *this,
 			   unsigned long msg, void *ptr)
@@ -3325,13 +3354,13 @@ static int packet_notifier(struct notifier_block *this,
 					if (!sock_flag(sk, SOCK_DEAD))
 						sk->sk_error_report(sk);
 				}
+				spin_unlock(&po->bind_lock);
+
 				if (msg == NETDEV_UNREGISTER) {
-					po->ifindex = -1;
-					if (po->prot_hook.dev)
-						dev_put(po->prot_hook.dev);
-					po->prot_hook.dev = NULL;
+					sock_hold(sk);
+					call_rcu(&po->rcu,
+						 packet_dev_put_deferred);
 				}
-				spin_unlock(&po->bind_lock);
 			}
 			break;
 		case NETDEV_UP:
diff --git a/net/packet/internal.h b/net/packet/internal.h
index c4e4b45..c6e2d24 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -113,6 +113,8 @@ struct packet_sock {
 	unsigned int		tp_loss:1;
 	unsigned int		tp_tx_has_off:1;
 	unsigned int		tp_tstamp;
+	struct net_device __rcu	*cached_dev;
+	struct rcu_head		rcu;
 	struct packet_type	prot_hook ____cacheline_aligned_in_smp;
 };
 
-- 
1.8.3.1

^ permalink raw reply related

* RE: [PATCH] usb: xhci: Link TRB must not occur with a USB payload burst.
From: David Laight @ 2013-11-21  9:53 UTC (permalink / raw)
  To: Paul Zimmerman, Sarah Sharp; +Cc: Alan Stern, Ben Hutchings, netdev, linux-usb
In-Reply-To: <A2CA0424C0A6F04399FB9E1CD98E030458E28EB7@US01WEMBX2.internal.synopsys.com>

> > My suspicion is that long SG lists are unusual - otherwise the
> > ring expansion code would have been needed much earlier.
> >
> > Can anyone remember whether that was needed because of long SG lists
> > or because of large numbers of outstanding requests?
> >
> > I've seen it for network cards - but only because usbnet sends
> > down far too many tx buffers.
> 
> usb-storage limits the maximum transfer size to 120K. That is a max of
> 31 page-size segments if my math is right. That's probably why mass-storage
> never saw a problem.

I found some references to mass storage running out of ring space.

My best guess as to why mass storage has never had a problem with
the alignment at link TRBs is that the effect of getting it wrong
is to split the transfer. Since almost all the transfers are of page
aligned data that will only happen on a 1k boundary - where it is
invisible to the target.

For USB2 targets the link TRB only has to be 512 byte aligned.
The constraint that mass storage applied for the usb2 controllers
is enough to satisfy it.

	David

^ permalink raw reply

* Re: [PATCH net v3] vti: fix spd lookup: match plaintext pkt, not ipsec pkt
From: Christophe Gouault @ 2013-11-21 10:07 UTC (permalink / raw)
  To: Saurabh Mohan, Steffen Klassert
  Cc: David S. Miller, Herbert Xu, netdev@vger.kernel.org,
	Sergei Shtylyov, Eric Dumazet, Andrew Collins, Fan Du
In-Reply-To: <1902752B0C92F943AB7EA9EE13E2DEEC1273BA977C@HQ1-EXCH02.corp.brocade.com>

Hi Saurabh,

Good to read your rationale.

On 11/18/2013 10:38 PM, Saurabh Mohan wrote:
>
>
>> -----Original Message----- From: Christophe Gouault
>> [mailto:christophe.gouault@6wind.com] Sent: Thursday, November 07,
>> 2013 4:56 AM To: Steffen Klassert Cc: David S. Miller; Herbert Xu;
>> netdev@vger.kernel.org; Saurabh Mohan; Sergei Shtylyov; Eric
>> Dumazet Subject: Re: [PATCH net v3] vti: fix spd lookup: match
>> plaintext pkt, not ipsec pkt
>>
>> Hello Steffen,
>>
>> I am also interested in knowing Saurabh's intentions regarding the
>> behavior of policies bound to vti interfaces.
>>
> The semantics is to match the policy "src 0.0.0.0/0 dst 0.0.0.0/0
> proto any" That is the only policy that VTI should use. The mark is
> needed to distinguish and limit the policy to a specific vti tunnel
> interface only. There is no other policy that may be applied to a vti
> interface. The fact that traffic is going over the tunnel interface
> implies that it must be encrypted/decrypted. Applying the above
> policy is a way to achieve that.

The proposed patch respects this model and accepts the same
configuration, but extends the possibilities: you can still set the
policy "src 0.0.0.0/0 dst 0.0.0.0/0 proto any" (which is the typical use
case), the mark is still used to distinguish and limit the policy to a
specific vti tunnel interface only, the traffic that is going over the
tunnel interface still implies that it must be encrypted/decrypted (in
tunnel mode).

But you can optionally apply differentiated policies within the same
tunnel, by setting SPs with narrower selectors: according to the
plaintext traffic that crosses the tunnel, you can request to use
different protocols (esp/ah), different SAs, maybe drop some traffic.
Only ipsec tunnel mode and drop policies should be bound to a VTI interface.

And the patch restores the SP semantics: the selector is used to match
the plaintext traffic, not the IPsec encrypted traffic.

Best Regards,
Christophe

>> However, please note that setting a policy with a wildcard
>> selector works in both cases (before or after this patch), so a
>> common test case can be defined.
>>
>> Actually the *previous* patch on vti (7263a5187f9e vti: get rid of
>> nf mark rule in prerouting) introduced significant changes, and
>> implies behaviors dependant on the kernel version, but it seemed to
>> meet Saurabh's agreement, as the following thread witnesses:
>>
>> http://www.spinics.net/lists/netdev/msg253134.html
>>
> Getting rid of the pre-routing mark, which had to be done outside of
> the vti tunnel code was prone to misconfiguration. Though it is
> unfortunate that it creates a kernel version dependency.

^ permalink raw reply

* Re: MLD maturity in kernel version 2.6.32.60
From: Daniel Borkmann @ 2013-11-21 10:16 UTC (permalink / raw)
  To: Hannes Frederic Sowa; +Cc: netdev, Simon Schneider
In-Reply-To: <20131121040416.GA4347@order.stressinduktion.org>

On 11/21/2013 05:04 AM, Hannes Frederic Sowa wrote:
> On Tue, Nov 19, 2013 at 01:12:36AM +0100, Daniel Borkmann wrote:
>> There's also still one issue which I am hesitant to implement, as i) I don't
>> think it's overly useful, ii) nobody complained so far. :-)
>>
>> In RFC2710 (MLD v1 spec), it says:
>>
>> 3.7. Other fields
>>
>>     The length of a received MLD message is computed by taking the IPv6
>>     Payload Length value and subtracting the length of any IPv6 extension
>>     headers present between the IPv6 header and the MLD message.  If that
>>     length is greater than 24 octets, that indicates that there are other
>>     fields present beyond the fields described above, perhaps belonging
>>     to a future backwards-compatible version of MLD.  An implementation
>>     of the version of MLD specified in this document MUST NOT send an MLD
>>     message longer than 24 octets and MUST ignore anything past the first
>>     24 octets of a received MLD message.  In all cases, the MLD checksum
>>     MUST be computed over the entire MLD message, not just the first 24
>>     octets.
>>
>> Then, RFC3810 which "updates" RFC2710 obviously has a bigger message length
>> than that. In igmp6_event_query(), we only check for len == MLD_V1_QUERY_LEN
>> and process MLD v1. Now, in case of MLD v1-only fallback, we MUST only
>> operate in v1 mode.
>>
>> Now, in case a v2 message comes in we could assume above statement "if
>> that length is greater than 24 octets, that indicates that there are other
>> fields present beyond the fields described above, perhaps belonging to a
>> future backwards-compatible version of MLD".
>>
>> But then on the other hand, we get completely wrong "Maximum Response Delay"
>> codes in the packet header as they are differently encoded in MLD v1 and MLD
>> v2 thus not really backwards compatible.
>
> Daniel, let's add something where we can easier export the per-interface
> mld status for net-next inclusive the timers either via netlink or procfs.
>
> I guess we cannot change /proc/net/igmp6 because of backward compatibility
> so I favor to add this via Nicolas netconf api.

Yes, sure sounds good to me. We can do that.

^ permalink raw reply

* Re: [PATCH] bonding: If IP route look-up to send an ARP fails, mark in bonding structure as no ARP sent.
From: Veaceslav Falico @ 2013-11-21 11:10 UTC (permalink / raw)
  To: rama nichanamatlu; +Cc: netdev
In-Reply-To: <528D5980.3040309@oracle.com>

On Wed, Nov 20, 2013 at 04:53:20PM -0800, rama nichanamatlu wrote:
>During the creation of VLAN's atop bonding the underlying interfaces 
>are made part of VLAN's, and at the same bonding driver gets aware 
>that VLAN's exists above it and hence would consult IP routing for 
>every ARP to  be sent to determine the route which tells bonding 
>driver the correct VLAN tag to attach to the outgoing ARP packet. 
>But, during the VLAN creation when vlan driver puts the underlying 
>interface into default vlan and then actual vlan, in-between this if 
>bonding driver consults the IP for a route, IP fails to provide a 
>correct route and upon which bonding driver drops the ARP packet. ARP 
>monitor when it
>comes around next time, sees no ARP response and fails-over to the 
>next available slave. Consulting for a IP route, 
>ip_route_output(),happens in bond_arp_send_all().

bonding works as expected - nothing to fix here. And even as a
workaround/hack - I'm not sure we need that to suppress one failover *only*
when vlan is added on top.

>
>To prevent this false fail-over, when bonding driver fails to send an 
>ARP out it marks in its private structure, bonding{},  not to expect 
>an ARP response, when ARP monitor comes around next time ARP sending 
>will be tried again.
>
>Extensively tested in a VM environment; sr-iov intf->bonding 
>intf->vlan intf. All virtual interfaces created at boot time.
>
>Orabug: 17172660
>Signed-off-by: Venkat Venkatsubra <venkat.x.venkatsubra@oracle.com>
>Signed-off-by: Rama Nichanamatlu <rama.nichanamatlu@oracle.com>
>---
> drivers/net/bonding/bond_main.c | 13 ++++++++-----
> drivers/net/bonding/bonding.h   |  1 +
> 2 files changed, 9 insertions(+), 5 deletions(-)
>
>diff --git a/drivers/net/bonding/bond_main.c 
>b/drivers/net/bonding/bond_main.c
>index dde6b4a..d475161 100644
>--- a/drivers/net/bonding/bond_main.c
>+++ b/drivers/net/bonding/bond_main.c
>@@ -2661,7 +2661,7 @@ static int bond_has_this_ip(struct bonding 
>*bond, __be32 ip)
>  * switches in VLAN mode (especially if ports are configured as
>  * "native" to a VLAN) might not pass non-tagged frames.
>  */
>-static void bond_arp_send(struct net_device *slave_dev, int arp_op, 
>__be32 dest_ip, __be32 src_ip, unsigned short vlan_id)
>+static void bond_arp_send(struct bonding *bond, struct net_device 
>*slave_dev, int arp_op, __be32 dest_ip, __be32 src_ip, unsigned short 
>vlan_id)
> {
> 	struct sk_buff *skb;
> @@ -2683,6 +2683,7 @@ static void bond_arp_send(struct net_device 
>*slave_dev, int arp_op, __be32 dest_
> 		}
> 	}
> 	arp_xmit(skb);
>+	bond->arp_sent=true;
> }
>  @@ -2700,7 +2701,7 @@ static void bond_arp_send_all(struct bonding 
>*bond, struct slave *slave)
> 		pr_debug("basa: target %x\n", targets[i]);
> 		if (!bond->vlgrp) {
> 			pr_debug("basa: empty vlan: arp_send\n");
>-			bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
>+			bond_arp_send(bond, slave->dev, ARPOP_REQUEST, targets[i],
> 				      bond->master_ip, 0);
> 			continue;
> 		}
>@@ -2726,7 +2727,7 @@ static void bond_arp_send_all(struct bonding 
>*bond, struct slave *slave)
> 		if (rt->dst.dev == bond->dev) {
> 			ip_rt_put(rt);
> 			pr_debug("basa: rtdev == bond->dev: arp_send\n");
>-			bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
>+			bond_arp_send(bond, slave->dev, ARPOP_REQUEST, targets[i],
> 				      bond->master_ip, 0);
> 			continue;
> 		}
>@@ -2744,7 +2745,7 @@ static void bond_arp_send_all(struct bonding 
>*bond, struct slave *slave)
>  		if (vlan_id) {
> 			ip_rt_put(rt);
>-			bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
>+			bond_arp_send(bond, slave->dev, ARPOP_REQUEST, targets[i],
> 				      vlan->vlan_ip, vlan_id);
> 			continue;
> 		}
>@@ -3206,7 +3207,7 @@ void bond_activebackup_arp_mon(struct 
>work_struct *work)
>  	should_notify_peers = bond_should_notify_peers(bond);
> -	if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
>+	if (bond->arp_sent && bond_ab_arp_inspect(bond, delta_in_ticks)) {
> 		read_unlock(&bond->lock);
> 		rtnl_lock();
> 		read_lock(&bond->lock);
>@@ -3218,6 +3219,7 @@ void bond_activebackup_arp_mon(struct 
>work_struct *work)
> 		read_lock(&bond->lock);
> 	}
> +	bond->arp_sent=false;
> 	bond_ab_arp_probe(bond);
>  re_arm:
>@@ -4425,6 +4427,7 @@ static void bond_setup(struct net_device *bond_dev)
>  	bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_NO_CSUM);
> 	bond_dev->features |= bond_dev->hw_features;
>+	bond->arp_sent=false;
> }
>  static void bond_work_cancel_all(struct bonding *bond)
>diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
>index e9a3c56..3878bbd 100644
>--- a/drivers/net/bonding/bonding.h
>+++ b/drivers/net/bonding/bonding.h
>@@ -253,6 +253,7 @@ struct bonding {
> 	/* debugging suport via debugfs */
> 	struct	 dentry *debug_dir;
> #endif /* CONFIG_DEBUG_FS */
>+        bool arp_sent;
> };
>  #define bond_slave_get_rcu(dev) \
>-- 
>1.8.2.1
>

^ permalink raw reply

* Re: [PATCH net v3] vti: fix spd lookup: match plaintext pkt, not ipsec pkt
From: Steffen Klassert @ 2013-11-21 11:45 UTC (permalink / raw)
  To: Christophe Gouault
  Cc: Saurabh Mohan, David S. Miller, Herbert Xu,
	netdev@vger.kernel.org, Sergei Shtylyov, Eric Dumazet,
	Andrew Collins, Fan Du
In-Reply-To: <528DDB5F.8080400@6wind.com>

On Thu, Nov 21, 2013 at 11:07:27AM +0100, Christophe Gouault wrote:
> 
> But you can optionally apply differentiated policies within the same
> tunnel, by setting SPs with narrower selectors: according to the
> plaintext traffic that crosses the tunnel, you can request to use
> different protocols (esp/ah), different SAs, maybe drop some traffic.

This raises the question about the MTU of a vti device. If the SA
is not unique, it is not clear which MTU we should use for that device.

> Only ipsec tunnel mode and drop policies should be bound to a VTI interface.
> 
> And the patch restores the SP semantics: the selector is used to match
> the plaintext traffic, not the IPsec encrypted traffic.
> 

On the other hand, I've spend quite some time to figure out how
inter address family tunneling can work with vti devices. It
seems that we need plaintext matching to get this to work.

^ permalink raw reply

* Re: kernel BUG at net/core/skbuff.c:2839 RIP  [<ffffffff819109a2>] skb_segment+0x6b2/0x6d0
From: Sander Eikelenboom @ 2013-11-21 12:00 UTC (permalink / raw)
  To: Eric Dumazet, Francois Romieu; +Cc: netdev, David S. Miller
In-Reply-To: <1287049824.20131117201744@eikelenboom.it>

Hello Sander,

Sunday, November 17, 2013, 8:17:44 PM, you wrote:

> Hi Eric,

> With the linux-net changes from this merge window i get the kernel panic below (not with 3.12.0).

> It's on a machine running Xen, 2x rtl8169 nic, and using a bridge for guest networking.
> This panic in the host kernel only seems to occur when generating a lot of network traffic to and from a guest.

> I tried reverting "tcp: gso: fix truesize tracking" 0d08c42cf9a71530fef5ebcfe368f38f2dd0476f, but that didn't help.

> --
> Sander

> [ 1164.511712] ------------[ cut here ]------------
> [ 1164.518446] kernel BUG at net/core/skbuff.c:2839!
> [ 1164.525226] invalid opcode: 0000 [#2] PREEMPT SMP 
> [ 1164.532024] Modules linked in:
> [ 1164.538713] CPU: 0 PID: 3 Comm: ksoftirqd/0 Tainted: G      D      3.12.0-mw-20131117+ #1
> [ 1164.545649] Hardware name: MSI MS-7640/890FXA-GD70 (MS-7640)  , BIOS V1.8B1 09/13/2010
> [ 1164.552659] task: ffff880059b8a180 ti: ffff880059b96000 task.ti: ffff880059b96000
> [ 1164.559649] RIP: e030:[<ffffffff819109a2>]  [<ffffffff819109a2>] skb_segment+0x6b2/0x6d0
> [ 1164.566860] RSP: e02b:ffff880059b97448  EFLAGS: 00010216
> [ 1164.574023] RAX: 0000000000000011 RBX: 0000000000006612 RCX: 0000000000006612
> [ 1164.581169] RDX: 00000000000005a8 RSI: 0000000000006612 RDI: ffff8800478ff682
> [ 1164.588115] RBP: ffff880059b97518 R08: ffff88004ca06a00 R09: 0000000000000011
> [ 1164.595169] R10: 000000000000606a R11: 0000000000000011 R12: 0000000000000000
> [ 1164.602214] R13: ffff88004ca06900 R14: ffff88004ca06a00 R15: ffff88004bb57f00
> [ 1164.609274] FS:  00007eff7dc67700(0000) GS:ffff88005f600000(0000) knlGS:0000000000000000
> [ 1164.616394] CS:  e033 DS: 0000 ES: 0000 CR0: 000000008005003b
> [ 1164.623562] CR2: 00007faa868fff30 CR3: 000000005877e000 CR4: 0000000000000660
> [ 1164.630807] Stack:
> [ 1164.637959]  ffff880059b97458 ffffffff810d90dd ffff880059b97478 ffffffff8109f69a
> [ 1164.645296]  ffff88004b824628 ffff88004b824400 ffff880059b974e8 ffff88004ca06a00
> [ 1164.652626]  0000001100000001 0000000000000040 0000000000000042 ffffffffffffffbe
> [ 1164.659816] Call Trace:
> [ 1164.667034]  [<ffffffff810d90dd>] ? trace_hardirqs_on+0xd/0x10
> [ 1164.674313]  [<ffffffff8109f69a>] ? local_bh_enable+0xaa/0x110
> [ 1164.681543]  [<ffffffff819dafc2>] tcp_gso_segment+0x102/0x3e0
> [ 1164.688691]  [<ffffffff819b8224>] ? ip_queue_xmit+0x194/0x480
> [ 1164.695741]  [<ffffffff819e9fe4>] inet_gso_segment+0x124/0x350
> [ 1164.702836]  [<ffffffff8191cb25>] skb_mac_gso_segment+0xd5/0x1d0
> [ 1164.709735]  [<ffffffff8191ca92>] ? skb_mac_gso_segment+0x42/0x1d0
> [ 1164.716739]  [<ffffffff8191cc7b>] __skb_gso_segment+0x5b/0xc0
> [ 1164.723802]  [<ffffffff8191ce80>] dev_hard_start_xmit+0x1a0/0x500
> [ 1164.730744]  [<ffffffff81939780>] sch_direct_xmit+0x100/0x280
> [ 1164.737531]  [<ffffffff8191d404>] dev_queue_xmit+0x224/0x600
> [ 1164.744403]  [<ffffffff8191d1e0>] ? dev_hard_start_xmit+0x500/0x500
> [ 1164.751317]  [<ffffffff8194765e>] ? nf_hook_slow+0x11e/0x160
> [ 1164.758332]  [<ffffffff81a14040>] ? deliver_clone+0x60/0x60
> [ 1164.765264]  [<ffffffff81a140d7>] br_dev_queue_push_xmit+0x97/0x140
> [ 1164.772082]  [<ffffffff81a1419d>] br_forward_finish+0x1d/0x60
> [ 1164.778925]  [<ffffffff81a12490>] ? br_dev_free+0x30/0x30
> [ 1164.785714]  [<ffffffff81a142f2>] __br_deliver+0x52/0x180
> [ 1164.792355]  [<ffffffff81a146cd>] br_deliver+0x3d/0x50
> [ 1164.798950]  [<ffffffff81a126be>] br_dev_xmit+0x22e/0x290
> [ 1164.805576]  [<ffffffff81a12490>] ? br_dev_free+0x30/0x30
> [ 1164.812106]  [<ffffffff8191d18d>] dev_hard_start_xmit+0x4ad/0x500
> [ 1164.818729]  [<ffffffff8191d55e>] dev_queue_xmit+0x37e/0x600
> [ 1164.825314]  [<ffffffff8191d1e0>] ? dev_hard_start_xmit+0x500/0x500
> [ 1164.831898]  [<ffffffff819b7043>] ip_finish_output+0x293/0x610
> [ 1164.838483]  [<ffffffff819b8a44>] ? ip_output+0x54/0xf0
> [ 1164.845055]  [<ffffffff819b8a44>] ip_output+0x54/0xf0
> [ 1164.851400]  [<ffffffff819b3e71>] ip_forward_finish+0x71/0x1a0
> [ 1164.857725]  [<ffffffff819b42d8>] ip_forward+0x338/0x420
> [ 1164.864167]  [<ffffffff819b1ca0>] ip_rcv_finish+0x150/0x660
> [ 1164.870477]  [<ffffffff819b275b>] ip_rcv+0x22b/0x370
> [ 1164.876707]  [<ffffffff81a0de22>] ? packet_rcv_spkt+0x42/0x190
> [ 1164.883040]  [<ffffffff8191a3a2>] __netif_receive_skb_core+0x6e2/0x8b0
> [ 1164.889193]  [<ffffffff81919dd4>] ? __netif_receive_skb_core+0x114/0x8b0
> [ 1164.895039]  [<ffffffff810f28b9>] ? getnstimeofday+0x9/0x30
> [ 1164.900704]  [<ffffffff8191a58c>] __netif_receive_skb+0x1c/0x70
> [ 1164.906327]  [<ffffffff8191a7af>] netif_receive_skb+0x3f/0x50
> [ 1164.911892]  [<ffffffff8191a8d4>] napi_gro_complete+0x114/0x140
> [ 1164.917459]  [<ffffffff8191a7e0>] ? napi_gro_complete+0x20/0x140
> [ 1164.923048]  [<ffffffff810dcf3a>] ? lock_release+0x12a/0x240
> [ 1164.928595]  [<ffffffff819e9cb7>] ? inet_gro_receive+0x57/0x260
> [ 1164.934042]  [<ffffffff8191b552>] dev_gro_receive+0x2b2/0x3f0
> [ 1164.939384]  [<ffffffff8191b48b>] ? dev_gro_receive+0x1eb/0x3f0
> [ 1164.944704]  [<ffffffff8191b849>] napi_gro_receive+0x29/0xc0
> [ 1164.949906]  [<ffffffff816d9253>] rtl8169_poll+0x2d3/0x680
> [ 1164.955036]  [<ffffffff8191aba1>] net_rx_action+0x171/0x270
> [ 1164.960180]  [<ffffffff8109f27d>] __do_softirq+0xed/0x210
> [ 1164.965285]  [<ffffffff8109f3f5>] run_ksoftirqd+0x55/0x90
> [ 1164.970334]  [<ffffffff810c1e29>] smpboot_thread_fn+0x199/0x2a0
> [ 1164.975402]  [<ffffffff810c1c90>] ? SyS_setgroups+0x150/0x150
> [ 1164.980438]  [<ffffffff810bb00f>] kthread+0xdf/0x100
> [ 1164.985309]  [<ffffffff810baf30>] ? __init_kthread_worker+0x70/0x70
> [ 1164.990221]  [<ffffffff81a8a9cc>] ret_from_fork+0x7c/0xb0
> [ 1164.995085]  [<ffffffff810baf30>] ? __init_kthread_worker+0x70/0x70
> [ 1164.999925] Code: ff 4c 8b 85 68 ff ff ff 44 8b 8d 50 ff ff ff 44 8b 95 48 ff ff ff 44 8b 9d 40 ff ff ff 0f 85 2c fe ff ff e9 23 fe ff ff 90 0f 0b <0f> 0b 48 c7 45 b0 ea ff ff ff e9 cf fc ff ff 0f 0b 0f 0b 66 66 
> [ 1165.010399] RIP  [<ffffffff819109a2>] skb_segment+0x6b2/0x6d0
> [ 1165.015512]  RSP <ffff880059b97448>
> [ 1165.020980] ---[ end trace 88f75f0c791ac25c ]---
> [ 1165.026033] Kernel panic - not syncing: Fatal exception in interrupt

Hi Eric and Francois,

I have tested some more:

First tried with switching off GSO and GRO on the bridge, this didn't help.
Then i only switched off GRO on eth0 (r8169) and left the bridge alone. That helped to prevent the oops.

Below the output of ethtool -k for the bridge and eth0 after boot (so the default situation) with which the oops occurs.
And the part of dmesg where the r8169 get initialized on boot (there are 2, eth0 and eth1).
--

Sander



~# ethtool -k eth0
Features for eth0:
rx-checksumming: on
tx-checksumming: off
        tx-checksum-ipv4: off
        tx-checksum-ip-generic: off [fixed]
        tx-checksum-ipv6: off [fixed]
        tx-checksum-fcoe-crc: off [fixed]
        tx-checksum-sctp: off [fixed]
scatter-gather: off
        tx-scatter-gather: off
        tx-scatter-gather-fraglist: off [fixed]
tcp-segmentation-offload: off
        tx-tcp-segmentation: off
        tx-tcp-ecn-segmentation: off [fixed]
        tx-tcp6-segmentation: off [fixed]
udp-fragmentation-offload: off [fixed]
generic-segmentation-offload: off [requested on]
generic-receive-offload: on
large-receive-offload: off [fixed]
rx-vlan-offload: on
tx-vlan-offload: on
ntuple-filters: off [fixed]
receive-hashing: off [fixed]
highdma: off [fixed]
rx-vlan-filter: off [fixed]
vlan-challenged: off [fixed]
tx-lockless: off [fixed]
netns-local: off [fixed]
tx-gso-robust: off [fixed]
tx-fcoe-segmentation: off [fixed]
tx-gre-segmentation: off [fixed]
tx-ipip-segmentation: off [fixed]
tx-sit-segmentation: off [fixed]
tx-udp_tnl-segmentation: off [fixed]
tx-mpls-segmentation: off [fixed]
fcoe-mtu: off [fixed]
tx-nocache-copy: off
loopback: off [fixed]
rx-fcs: off
rx-all: off
tx-vlan-stag-hw-insert: off [fixed]
rx-vlan-stag-hw-parse: off [fixed]
rx-vlan-stag-filter: off [fixed]
l2-fwd-offload: off [fixed]



~# ethtool -k xen_bridge
Features for xen_bridge:
rx-checksumming: off [fixed]
tx-checksumming: on
        tx-checksum-ipv4: off [fixed]
        tx-checksum-ip-generic: on
        tx-checksum-ipv6: off [fixed]
        tx-checksum-fcoe-crc: off [fixed]
        tx-checksum-sctp: off [fixed]
scatter-gather: on
        tx-scatter-gather: on
        tx-scatter-gather-fraglist: on
tcp-segmentation-offload: on
        tx-tcp-segmentation: on
        tx-tcp-ecn-segmentation: on
        tx-tcp6-segmentation: on
udp-fragmentation-offload: on
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: off [fixed]
rx-vlan-offload: off [fixed]
tx-vlan-offload: on
ntuple-filters: off [fixed]
receive-hashing: off [fixed]
highdma: on
rx-vlan-filter: off [fixed]
vlan-challenged: off [fixed]
tx-lockless: on [fixed]
netns-local: on [fixed]
tx-gso-robust: on
tx-fcoe-segmentation: on
tx-gre-segmentation: on
tx-ipip-segmentation: on
tx-sit-segmentation: on
tx-udp_tnl-segmentation: on
tx-mpls-segmentation: on
fcoe-mtu: off [fixed]
tx-nocache-copy: on
loopback: off [fixed]
rx-fcs: off [fixed]
rx-all: off [fixed]
tx-vlan-stag-hw-insert: off [fixed]
rx-vlan-stag-hw-parse: off [fixed]
rx-vlan-stag-filter: off [fixed]
l2-fwd-offload: off [fixed]


[   12.356379] r8169 0000:0b:00.0 eth0: RTL8168d/8111d at 0xffffc90000334000, 40:61:86:f4:67:d9, XID 081000c0 IRQ 128
[   12.361803] r8169 0000:0b:00.0 eth0: jumbo features [frames: 9200 bytes, tx checksumming: ko]
[   12.367291] r8169 Gigabit Ethernet driver 2.3LK-NAPI loaded
[   12.372748] xen: registering gsi 51 triggering 0 polarity 1
[   12.378225] xen: --> pirq=51 -> irq=51 (gsi=51)
[   12.383612] r8169 0000:0a:00.0: enabling Mem-Wr-Inval
[   12.389505] r8169 0000:0a:00.0 eth1: RTL8168d/8111d at 0xffffc90000336000, 40:61:86:f4:67:d8, XID 081000c0 IRQ 129
[   12.395056] r8169 0000:0a:00.0 eth1: jumbo features [frames: 9200 bytes, tx checksumming: ko]

^ permalink raw reply

* Re: [PATCH net v3] vti: fix spd lookup: match plaintext pkt, not ipsec pkt
From: Steffen Klassert @ 2013-11-21 12:12 UTC (permalink / raw)
  To: Christophe Gouault
  Cc: David S. Miller, Herbert Xu, netdev, Saurabh Mohan,
	Sergei Shtylyov, Eric Dumazet
In-Reply-To: <1383725153-26298-1-git-send-email-christophe.gouault@6wind.com>

On Wed, Nov 06, 2013 at 09:05:53AM +0100, Christophe Gouault wrote:
>  
> @@ -133,7 +134,13 @@ static int vti_rcv(struct sk_buff *skb)
>  		 * only match policies with this mark.
>  		 */
>  		skb->mark = be32_to_cpu(tunnel->parms.o_key);
> +		/* The packet is decrypted, but not yet decapsulated.
> +		 * Temporarily make network_header point to the inner header
> +		 * for policy check.
> +		 */
> +		skb_reset_network_header(skb);
>  		ret = xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb);

If we do it like this, we would do an input policy check even for
packets that should be forwarded. I think that's a bit odd.

If we really change to match plaintext traffic, we should do
it like Fan Du proposed. Remove the policy check here and
let the further layers do the policy enforcement. All we
have to do is to set the skb mark, then the lookup should
match the vti policy.

It is already clear that this packet was IPsec transformed
when it enters vti_rcv, so deferring the policy check should
be ok.

>  
>  	if (skb->protocol != htons(ETH_P_IP))
> @@ -173,17 +182,35 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
>  
>  	tos = old_iph->tos;
>  
> +	/* SPD lookup: we must provide a dst_entry to xfrm_lookup, normally the
> +	 * route to the final destination. However this route is a route via
> +	 * the vti interface. Now vti interfaces typically have the NOXFRM
> +	 * flag, hence xfrm_lookup would bypass IPsec.
> +	 *
> +	 * Therefore, we feed xfrm_lookup with a route to the vti tunnel remote
> +	 * endpoint instead.
> +	 */
>  	memset(&fl4, 0, sizeof(fl4));
>  	flowi4_init_output(&fl4, tunnel->parms.link,
>  			   be32_to_cpu(tunnel->parms.o_key), RT_TOS(tos),
>  			   RT_SCOPE_UNIVERSE,
>  			   IPPROTO_IPIP, 0,
>  			   dst, tiph->saddr, 0, 0);
> -	rt = ip_route_output_key(dev_net(dev), &fl4);
> +	rt = __ip_route_output_key(tunnel->net, &fl4);
>  	if (IS_ERR(rt)) {
>  		dev->stats.tx_carrier_errors++;
>  		goto tx_error_icmp;
>  	}
> +
> +	memset(&fl, 0, sizeof(fl));
> +	/* Temporarily mark the skb with the tunnel o_key, to look up
> +	 * for a policy with this mark, matching the plaintext traffic.
> +	 */
> +	skb->mark = be32_to_cpu(tunnel->parms.o_key);
> +	__xfrm_decode_session(skb, &fl, AF_INET, 0);
> +	skb->mark = oldmark;
> +	rt = (struct rtable *)xfrm_lookup(tunnel->net, &rt->dst, &fl, NULL, 0);
> +

I think we should not do such a workarround for the NOXFRM case.
In particular because this is not really typical, it is not the
default and it is not documented that it should be like that.

^ permalink raw reply

* Re: [PATCH net v3] vti: fix spd lookup: match plaintext pkt, not ipsec pkt
From: Steffen Klassert @ 2013-11-21 12:17 UTC (permalink / raw)
  To: Fan Du
  Cc: Saurabh Mohan, Christophe Gouault, David S. Miller, Herbert Xu,
	netdev@vger.kernel.org, Sergei Shtylyov, Eric Dumazet
In-Reply-To: <528B2C72.5060809@windriver.com>

On Tue, Nov 19, 2013 at 05:16:34PM +0800, Fan Du wrote:
> 
> Or the VTI tunnel is the only tunnel with this specific source/destination address
> in the production deployment. Again the upper layer 4 will check the policy after
> all, that's the right place to do the policy checking.
> 
> So IMO, it's unnecessary to check policy for a net_device like VTI, actually I hold
> a patch of removing the VTI policy checking due to net-next closure for the moment.
> 

Please keep in mind that this will change the lookup from the
IPsec traffic to the plaintext traffic, like Christophe proposed
to do. This means that the transmit side has to be changed too.

^ permalink raw reply

* [RFC] wireless, ipv4, ipv6: drop GTK-protected unicast IP packets
From: Johannes Berg @ 2013-11-21 13:05 UTC (permalink / raw)
  To: linux-wireless, netdev; +Cc: Jouni Malinen

From: Jouni Malinen <jouni@qca.qualcomm.com>

The GTK is shared by all stations in an 802.11 BSS and as such any
one of them can send forged group-addressed frames. To prevent this
kind of attack, drop unicast IP packets if they were protected with
the GTK, i.e. were multicast packets at the 802.11 layer.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
[configuration, IPv6]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/linux/skbuff.h       |  5 ++++-
 include/net/cfg80211.h       |  7 +++++--
 include/uapi/linux/nl80211.h |  6 ++++++
 net/core/skbuff.c            |  1 +
 net/ipv4/ip_input.c          | 12 ++++++++++++
 net/ipv6/ip6_input.c         | 11 +++++++++++
 net/mac80211/ieee80211_i.h   |  3 ++-
 net/mac80211/mlme.c          |  2 ++
 net/mac80211/rx.c            |  7 +++++++
 net/wireless/nl80211.c       |  4 ++++
 10 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 2ddb48d..5ed5a19 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -384,6 +384,8 @@ typedef unsigned char *sk_buff_data_t;
  *	@wifi_acked_valid: wifi_acked was set
  *	@wifi_acked: whether frame was acked on wifi or not
  *	@no_fcs:  Request NIC to treat last 4 bytes as Ethernet FCS
+ *	@drop_unicast: Request protocols to drop frame if it's a unicast frame
+ *		in that protocol - used for frames received over wifi multicast
  *	@dma_cookie: a cookie to one of several possible DMA operations
  *		done by skb DMA functions
   *	@napi_id: id of the NAPI struct this skb came from
@@ -498,7 +500,8 @@ struct sk_buff {
 	 * headers if needed
 	 */
 	__u8			encapsulation:1;
-	/* 7/9 bit hole (depending on ndisc_nodetype presence) */
+	__u8			drop_unicast:1;
+	/* 6/8 bit hole (depending on ndisc_nodetype presence) */
 	kmemcheck_bitfield_end(flags2);
 
 #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6c2bc329..37a4c09 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1560,10 +1560,13 @@ struct cfg80211_auth_request {
  *
  * @ASSOC_REQ_DISABLE_HT:  Disable HT (802.11n)
  * @ASSOC_REQ_DISABLE_VHT:  Disable VHT
+ * @ASSOC_REQ_DROP_GROUP_PROTECTED_UNICAST: Drop protocol unicast packets
+ *	that were group-protected at the link layer.
  */
 enum cfg80211_assoc_req_flags {
-	ASSOC_REQ_DISABLE_HT		= BIT(0),
-	ASSOC_REQ_DISABLE_VHT		= BIT(1),
+	ASSOC_REQ_DISABLE_HT			= BIT(0),
+	ASSOC_REQ_DISABLE_VHT			= BIT(1),
+	ASSOC_REQ_DROP_GROUP_PROTECTED_UNICAST	= BIT(2),
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 4bb8289..8fc8b05 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1518,6 +1518,10 @@ enum nl80211_commands {
  * @NL80211_ATTR_SUPPORT_5_10_MHZ: A flag indicating that the device supports
  *	5 MHz and 10 MHz channel bandwidth.
  *
+ * @NL80211_ATTR_DROP_GROUP_PROTECTED_UNICAST: Drop protocol unicast packets
+ *	that were group protected at the wireless level. This flag attribute
+ *	is valid in the association command.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1836,6 +1840,8 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_SUPPORT_5_10_MHZ,
 
+	NL80211_ATTR_DROP_GROUP_PROTECTED_UNICAST,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index d81cff1..b751e7d 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -737,6 +737,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #endif
 	new->vlan_proto		= old->vlan_proto;
 	new->vlan_tci		= old->vlan_tci;
+	new->drop_unicast	= old->drop_unicast;
 
 	skb_copy_secmark(new, old);
 
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 054a3e9..7de7f84 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -363,6 +363,18 @@ static int ip_rcv_finish(struct sk_buff *skb)
 		IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,
 				skb->len);
 
+	/*
+	 * If the received packet was protected in a way that would allow
+	 * insider attacks (e.g. IEEE 802.11 networks with GTK shared for all
+	 * associated stations in an BSS), group-addressed link layer frames
+	 * can be forged. To reduce the effects of such an attack, drop the
+	 * packet if it is destined to unicast address, but was sent in a
+	 * group-addressed link layer frame.
+	 */
+	if (unlikely(skb->drop_unicast &&
+		     (rt->rt_type == RTN_UNICAST || rt->rt_type == RTN_LOCAL)))
+		goto drop;
+
 	return dst_input(skb);
 
 drop:
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 302d6fb..d9cbf4d 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -151,6 +151,17 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
 	if (ipv6_addr_is_multicast(&hdr->saddr))
 		goto err;
 
+	/*
+	 * If the received packet was protected in a way that would allow
+	 * insider attacks (e.g. IEEE 802.11 networks with GTK shared for all
+	 * associated stations in an BSS), group-addressed link layer frames
+	 * can be forged. To reduce the effects of such an attack, drop the
+	 * packet if it is destined to unicast address, but was sent in a
+	 * group-addressed link layer frame.
+	 */
+	if (unlikely(skb->drop_unicast && !ipv6_addr_is_multicast(&hdr->daddr)))
+		goto err;
+
 	skb->transport_header = skb->network_header + sizeof(*hdr);
 	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 61ccf0d..d39cfb5 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -705,7 +705,8 @@ struct ieee80211_sub_if_data {
 
 	unsigned long state;
 
-	int drop_unencrypted;
+	bool drop_unencrypted;
+	bool drop_group_protected_unicast;
 
 	char name[IFNAMSIZ];
 
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e7f0d53..649902b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4121,6 +4121,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 	sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
 	sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto,
 							sdata->vif.type);
+	sdata->drop_group_protected_unicast =
+		req->flags & ASSOC_REQ_DROP_GROUP_PROTECTED_UNICAST;
 
 	/* kick off associate process */
 
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5638782..2095be1 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1544,6 +1544,13 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 			    !is_multicast_ether_addr(hdr->addr1))
 				rx->key = NULL;
 		}
+
+		if (rx->key && is_multicast_ether_addr(hdr->addr1) &&
+		    rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
+		    rx->sdata->drop_group_protected_unicast &&
+		    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+		    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104)
+			rx->skb->drop_unicast = 1;
 	}
 
 	if (rx->key) {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e39fadc..f8e3c83 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -357,6 +357,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
 	[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
 	[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
 	[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
+	[NL80211_ATTR_DROP_GROUP_PROTECTED_UNICAST] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -6345,6 +6346,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 		       sizeof(req.vht_capa));
 	}
 
+	if (info->attrs[NL80211_ATTR_DROP_GROUP_PROTECTED_UNICAST])
+		req.flags |= ASSOC_REQ_DROP_GROUP_PROTECTED_UNICAST;
+
 	err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
 	if (!err) {
 		wdev_lock(dev->ieee80211_ptr);
-- 
1.8.4.rc3

^ permalink raw reply related

* Re: [RFC] wireless, ipv4, ipv6: drop GTK-protected unicast IP packets
From: Johannes Berg @ 2013-11-21 13:08 UTC (permalink / raw)
  To: linux-wireless; +Cc: netdev, Jouni Malinen
In-Reply-To: <1385039154-20637-1-git-send-email-johannes@sipsolutions.net>

On Thu, 2013-11-21 at 14:05 +0100, Johannes Berg wrote:

> @@ -498,7 +500,8 @@ struct sk_buff {
>  	 * headers if needed
>  	 */
>  	__u8			encapsulation:1;
> -	/* 7/9 bit hole (depending on ndisc_nodetype presence) */
> +	__u8			drop_unicast:1;

The obvious question is here, and for IPv4/IPv6 - should the wireless
stack be responsible for doing this instead?

The upside of this approach as-is is that all other protocols can easily
be extended to do this and no protocol-specific code is needed in
wireless; the downside is that it clobbers all protocol code. Thoughts?

johannes

^ permalink raw reply

* Re: kernel BUG at net/core/skbuff.c:2839 RIP  [<ffffffff819109a2>] skb_segment+0x6b2/0x6d0
From: Eric Dumazet @ 2013-11-21 14:10 UTC (permalink / raw)
  To: Sander Eikelenboom; +Cc: Eric Dumazet, Francois Romieu, netdev, David S. Miller
In-Reply-To: <1392359825.20131121130027@eikelenboom.it>

On Thu, 2013-11-21 at 13:00 +0100, Sander Eikelenboom wrote:
> Hello Sander,
> 
> Sunday, November 17, 2013, 8:17:44 PM, you wrote:
> 
> > Hi Eric,
> 
> > With the linux-net changes from this merge window i get the kernel panic below (not with 3.12.0).
> 
> > It's on a machine running Xen, 2x rtl8169 nic, and using a bridge for guest networking.
> > This panic in the host kernel only seems to occur when generating a lot of network traffic to and from a guest.
> 
> > I tried reverting "tcp: gso: fix truesize tracking" 0d08c42cf9a71530fef5ebcfe368f38f2dd0476f, but that didn't help.

> Hi Eric and Francois,
> 
> I have tested some more:
> 
> First tried with switching off GSO and GRO on the bridge, this didn't help.
> Then i only switched off GRO on eth0 (r8169) and left the bridge alone. That helped to prevent the oops.
> 
> Below the output of ethtool -k for the bridge and eth0 after boot (so the default situation) with which the oops occurs.
> And the part of dmesg where the r8169 get initialized on boot (there are 2, eth0 and eth1).

As mentioned earlier, this problem is known and a patch is under review.

http://marc.info/?l=linux-netdev&m=138419594024851&w=2

Thanks

^ permalink raw reply

* Re: kernel BUG at net/core/skbuff.c:2839 RIP  [<ffffffff819109a2>] skb_segment+0x6b2/0x6d0
From: Sander Eikelenboom @ 2013-11-21 14:14 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Francois Romieu, netdev, David S. Miller
In-Reply-To: <1385043004.10637.34.camel@edumazet-glaptop2.roam.corp.google.com>


Thursday, November 21, 2013, 3:10:04 PM, you wrote:

> On Thu, 2013-11-21 at 13:00 +0100, Sander Eikelenboom wrote:
>> Hello Sander,
>> 
>> Sunday, November 17, 2013, 8:17:44 PM, you wrote:
>> 
>> > Hi Eric,
>> 
>> > With the linux-net changes from this merge window i get the kernel panic below (not with 3.12.0).
>> 
>> > It's on a machine running Xen, 2x rtl8169 nic, and using a bridge for guest networking.
>> > This panic in the host kernel only seems to occur when generating a lot of network traffic to and from a guest.
>> 
>> > I tried reverting "tcp: gso: fix truesize tracking" 0d08c42cf9a71530fef5ebcfe368f38f2dd0476f, but that didn't help.

>> Hi Eric and Francois,
>> 
>> I have tested some more:
>> 
>> First tried with switching off GSO and GRO on the bridge, this didn't help.
>> Then i only switched off GRO on eth0 (r8169) and left the bridge alone. That helped to prevent the oops.
>> 
>> Below the output of ethtool -k for the bridge and eth0 after boot (so the default situation) with which the oops occurs.
>> And the part of dmesg where the r8169 get initialized on boot (there are 2, eth0 and eth1).

> As mentioned earlier, this problem is known and a patch is under review.

> http://marc.info/?l=linux-netdev&m=138419594024851&w=2

> Thanks

Ah ok, sorry but never received that message.
Will test the patch.

Thanks,

Sander

^ permalink raw reply

* Re: [RFC] wireless, ipv4, ipv6: drop GTK-protected unicast IP packets
From: Eric Dumazet @ 2013-11-21 14:24 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Jouni Malinen
In-Reply-To: <1385039307.14273.4.camel-8Nb76shvtaUJvtFkdXX2HixXY32XiHfO@public.gmane.org>

On Thu, 2013-11-21 at 14:08 +0100, Johannes Berg wrote:
> On Thu, 2013-11-21 at 14:05 +0100, Johannes Berg wrote:
> 
> > @@ -498,7 +500,8 @@ struct sk_buff {
> >  	 * headers if needed
> >  	 */
> >  	__u8			encapsulation:1;
> > -	/* 7/9 bit hole (depending on ndisc_nodetype presence) */
> > +	__u8			drop_unicast:1;
> 
> The obvious question is here, and for IPv4/IPv6 - should the wireless
> stack be responsible for doing this instead?

I don't really like the idea of reserving a bit for this in sk_buff,
and propagate it in every cloning ...

Someone should replace __copy_skb_header() by a single memset(),
because copying all these bits one by one is not really clever.

And then, adding a test in fast path (ip_rcv_finish()) is really not
nice.

I am not convinced this patch is the right way to solve the problem.



--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: Bluetooth: oops in rfcomm_sock_getsockopt_old
From: Jiri Kosina @ 2013-11-21 14:29 UTC (permalink / raw)
  To: Johan Hedberg
  Cc: Marcel Holtmann, Gustavo Padovan, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-bluetooth-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20131120150214.GA2364-WMpsmflcW+XDKdsqLTzapw@public.gmane.org>

On Wed, 20 Nov 2013, Johan Hedberg wrote:

> >  BUG: unable to handle kernel paging request at 000000234df5351a
> >  IP: [<ffffffffa05e95b9>] rfcomm_sock_getsockopt_old+0x39/0x190 [rfcomm]
> >  PGD 0 
> >  Oops: 0000 [#1] SMP 
> >  Modules linked in: rfcomm bnep cpufreq_conservative cpufreq_userspace cpufreq_powersa
> > CO_vendor_support kvm_intel snd_hda_codec_conexant kvm iwldvm mac80211 btusb bluetooth
> > d_hda_intel snd_hda_codec snd_hwdep cfg80211 thinkpad_acpi snd_seq pcspkr snd_pcm i2c_i801 lpc_ich mfd_core rfkill e1000e ehci_pci snd_timer snd_page_alloc snd_seq_device ptp pps_core wmi snd tpm_tis soundcore battery ac tpm tpm_bios acpi_cpufreq autofs4 uhci_hcd ehci_hcd i915 usbcore usb_common drm_kms_helper drm i2c_algo_bit button video edd fan processor ata_generic thermal thermal_sys
> >  CPU: 0 PID: 1024 Comm: bluetoothd Not tainted 3.11.0-07976-g8d6083f #1
> >  Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
> >  task: ffff880076c0e000 ti: ffff880036f94000 task.ti: ffff880036f94000
> >  RIP: 0010:[<ffffffffa05e95b9>]  [<ffffffffa05e95b9>] rfcomm_sock_getsockopt_old+0x39/0x190 [rfcomm]
> >  RSP: 0018:ffff880036f95e78  EFLAGS: 00010246
> >  RAX: 000000234df53512 RBX: 00007fff5ebe9e9c RCX: 00007fff5ebe9e9c
> >  RDX: 00007fff5ebe9e98 RSI: 0000000000000003 RDI: ffff8800784eb480
> >  RBP: ffff880036f95ec8 R08: ffff880076c1e000 R09: 00007fff5ebea148
> >  R10: 00007fff5ebe9e98 R11: 0000000000000202 R12: ffff880076c1e000
> >  R13: 0000000000000003 R14: 00007fff5ebe9e9c R15: 00007fff5ebe9e98
> >  FS:  00007fd5d7655700(0000) GS:ffff88007c200000(0000) knlGS:0000000000000000
> >  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> >  CR2: 000000234df5351a CR3: 0000000037b06000 CR4: 00000000000007f0
> >  Stack:
> >   0000000000000004 00007fff5ebe9e98 ffff880036f95ea8 ffffffff81582e8f
> >   ffffffff8148e61b ffff8800784eb480 0000000000000012 0000000000000003
> >   00007fff5ebe9e9c 00007fff5ebe9e98 ffff880036f95f28 ffffffffa05e9763
> >  Call Trace:
> >   [<ffffffff81582e8f>] ? _raw_spin_unlock_bh+0x3f/0x50
> >   [<ffffffff8148e61b>] ? release_sock+0x2b/0xa0
> >   [<ffffffffa05e9763>] rfcomm_sock_getsockopt+0x53/0x190 [rfcomm]
> >   [<ffffffff8158b737>] ? sysret_check+0x1b/0x56
> >   [<ffffffff81486933>] SyS_getsockopt+0x73/0xe0
> >   [<ffffffff8158b712>] system_call_fastpath+0x16/0x1b
> >  Code: 04 48 89 5d d8 4c 89 6d e8 48 89 cb 4c 89 65 e0 4c 89 75 f0 41 89 f5 4c 89 7d f8 48 89 55 b8 4c 8b 67 20 49 8b 84 24 50 04 00 00 <4c> 8b 78 08 0f 85 27 01 00 00 e8 e8 83 b6 e0 48 89 d8 e8 60 40 
> >  RIP  [<ffffffffa05e95b9>] rfcomm_sock_getsockopt_old+0x39/0x190 [rfcomm]
> >   RSP <ffff880036f95e78>
> >  CR2: 000000234df5351a
> >  ---[ end trace d84df5c733bb1019 ]---
> > 
> > 
> > I have bisected this to 94a86df01. I don't immediately see how this could 
> > be causing the issue directly, hence sending out this as a heads-up, and 
> > will continue looking into this eventually.
> > 
> > It seems to be very reliably reproducible, so I expect the bisection to be 
> > correct (to be verified still).
> 
> The issue is already fixed in the bluetooth and wireless trees but
> the patch hasn't yet made it to the net or Linus' tree.

Tested-by: Jiri Kosina <jkosina-AlSwsSmVLrQ@public.gmane.org>

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [RFC] wireless, ipv4, ipv6: drop GTK-protected unicast IP packets
From: Johannes Berg @ 2013-11-21 14:31 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: linux-wireless, netdev, Jouni Malinen
In-Reply-To: <1385043867.10637.39.camel@edumazet-glaptop2.roam.corp.google.com>

On Thu, 2013-11-21 at 06:24 -0800, Eric Dumazet wrote:
> On Thu, 2013-11-21 at 14:08 +0100, Johannes Berg wrote:
> > On Thu, 2013-11-21 at 14:05 +0100, Johannes Berg wrote:
> > 
> > > @@ -498,7 +500,8 @@ struct sk_buff {
> > >  	 * headers if needed
> > >  	 */
> > >  	__u8			encapsulation:1;
> > > -	/* 7/9 bit hole (depending on ndisc_nodetype presence) */
> > > +	__u8			drop_unicast:1;
> > 
> > The obvious question is here, and for IPv4/IPv6 - should the wireless
> > stack be responsible for doing this instead?
> 
> I don't really like the idea of reserving a bit for this in sk_buff,
> and propagate it in every cloning ...
> 
> Someone should replace __copy_skb_header() by a single memset(),
> because copying all these bits one by one is not really clever.
> 
> And then, adding a test in fast path (ip_rcv_finish()) is really not
> nice.

Yeah, that was a concern too. I'll do it entirely in the wireless stack
instead I guess. At least it'll be hidden away inside the if that
already does the group key check etc.

Thanks.

johannes

^ permalink raw reply

* Re: [PATCH net v2] packet: fix use after free race in send path when dev is released
From: Eric Dumazet @ 2013-11-21 14:40 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: davem, netdev, Salam Noureddine, Ben Greear
In-Reply-To: <1385027235-2217-1-git-send-email-dborkman@redhat.com>

On Thu, 2013-11-21 at 10:47 +0100, Daniel Borkmann wrote:

> +static void packet_dev_put_deferred(struct rcu_head *head)
> +{
> +	struct packet_sock *po = container_of(head, struct packet_sock, rcu);
> +	struct sock *sk = &po->sk;
> +
> +	spin_lock(&po->bind_lock);
> +	po->ifindex = -1;
> +
> +	if (po->prot_hook.dev) {
> +		dev_put(po->prot_hook.dev);
> +		po->prot_hook.dev = NULL;
> +	}
> +
> +	spin_unlock(&po->bind_lock);
> +	sock_put(sk);
> +}

I dont think this is needed.

>  
>  static int packet_notifier(struct notifier_block *this,
>  			   unsigned long msg, void *ptr)
> @@ -3325,13 +3354,13 @@ static int packet_notifier(struct notifier_block *this,
>  					if (!sock_flag(sk, SOCK_DEAD))
>  						sk->sk_error_report(sk);
>  				}
> +				spin_unlock(&po->bind_lock);
> +
>  				if (msg == NETDEV_UNREGISTER) {
> -					po->ifindex = -1;
> -					if (po->prot_hook.dev)
> -						dev_put(po->prot_hook.dev);
> -					po->prot_hook.dev = NULL;
> +					sock_hold(sk);
> +					call_rcu(&po->rcu,
> +						 packet_dev_put_deferred);
>  				}
> -				spin_unlock(&po->bind_lock);
>  			}

Its not needed because you now take a reference on dev
in packet_cached_dev_get()

Otherwise, patch looks good !

^ permalink raw reply

* Re: [PATCH] net: rework recvmsg handler msg_name and msg_namelen logic
From: Vlad Yasevich @ 2013-11-21 14:52 UTC (permalink / raw)
  To: netdev, davem, eric.dumazet
In-Reply-To: <20131121003828.GA32587@order.stressinduktion.org>

On 11/20/2013 07:38 PM, Hannes Frederic Sowa wrote:
> diff --git a/include/linux/net.h b/include/linux/net.h
> index b292a04..4bcee94 100644
> --- a/include/linux/net.h
> +++ b/include/linux/net.h
> @@ -164,6 +164,14 @@ struct proto_ops {
>  #endif
>  	int		(*sendmsg)   (struct kiocb *iocb, struct socket *sock,
>  				      struct msghdr *m, size_t total_len);
> +	/* Notes for implementing recvmsg:
> +	 * ===============================
> +	 * msg->msg_namelen should get updated by the recvmsg handlers
> +	 * iff msg_name != NULL. It is by default 0 to prevent
> +	 * returning uninitialized memory to user space.  The recvfrom
> +	 * handlers can assume that msg.msg_name is either NULL or has
> +	 * a minimum size of sizeof(struct sockaddr_storage).
             ^^^^^^^

You meant "maximum", right?

-vlad

^ permalink raw reply

* [PATCH 0/4] bonding: L2DA mode
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut

L2 Destination Address based (L2DA) mode allows bonding to send packets using
different slaves according to packets L2 Destination Address.

In L2DA mode, the bonding maintains a default slave and DA/slave map.

Upon a packet transmission, the bonding examines DA of the packet and tries to
find a corresponding slave within the map. If found, the slave is used for the
packet transmission. Otherwise, the default slave is used. If the default slave
is unable to transmit at this moment, the bonding tries to fall back to an
arbitrary slave that can transmit.

Both the default slave and the map can be controlled via sysfs or by ioctls.

Anton Nayshtut (4):
  bonding: L2DA mode added
  bonding: L2DA mode intergated
  bonding: L2DA command IOCTL
  bonding: L2DA query IOCTL

 drivers/net/bonding/Makefile       |   2 +-
 drivers/net/bonding/bond_l2da.c    | 425 +++++++++++++++++++++++++++++++++++++
 drivers/net/bonding/bond_l2da.h    |  56 +++++
 drivers/net/bonding/bond_main.c    | 172 ++++++++++++++-
 drivers/net/bonding/bond_options.c |  17 +-
 drivers/net/bonding/bond_sysfs.c   | 223 ++++++++++++++++++-
 drivers/net/bonding/bonding.h      |   7 +
 include/uapi/linux/if_bonding.h    |  32 +++
 include/uapi/linux/sockios.h       |   4 +-
 net/core/dev_ioctl.c               |   4 +
 net/socket.c                       |   4 +
 11 files changed, 937 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/bonding/bond_l2da.c
 create mode 100644 drivers/net/bonding/bond_l2da.h

-- 
1.8.3.1

^ permalink raw reply

* [PATCH 1/4] bonding: L2DA mode added
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum,
	Boris Lapshin
In-Reply-To: <1385045738-29726-1-git-send-email-Anton.Nayshtut@wilocity.com>

This patches introduces L2DA bonding module with all the data structures and
interfaces. It's not integrated yet.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/Makefile    |   2 +-
 drivers/net/bonding/bond_l2da.c | 425 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/bonding/bond_l2da.h |  56 ++++++
 drivers/net/bonding/bonding.h   |   2 +
 4 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/bonding/bond_l2da.c
 create mode 100644 drivers/net/bonding/bond_l2da.h

diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 5a5d720..3eecd54 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_BONDING) += bonding.o
 
-bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o
+bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o bond_l2da.o
 
 proc-$(CONFIG_PROC_FS) += bond_procfs.o
 bonding-objs += $(proc-y)
diff --git a/drivers/net/bonding/bond_l2da.c b/drivers/net/bonding/bond_l2da.c
new file mode 100644
index 0000000..01d0d40
--- /dev/null
+++ b/drivers/net/bonding/bond_l2da.c
@@ -0,0 +1,425 @@
+/*  Copyright(c) 2013 Wilocity Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "bonding.h"
+#include "bond_l2da.h"
+#include <linux/etherdevice.h>
+
+struct l2da_bond_matrix_entry {
+	struct hlist_node hnode;
+	unsigned char     h_dest[ETH_ALEN];
+	struct slave     *slave;
+};
+
+
+#define BOND_L2DA_INFO(bond)     ((bond)->l2da_info)
+#define SLAVE_L2DA_INFO(bond)    ((slave)->l2da_info)
+#define SLAVE_BY_L2DA_INFO(info) container_of(info, struct slave, l2da_info)
+
+#define SLAVE_CAN_XMIT(slave)    (IS_UP((slave)->dev) && \
+				  ((slave)->link == BOND_LINK_UP) && \
+				  bond_is_active_slave(slave))
+
+/**
+ * _bond_l2da_slave_name - returns slave name
+ * @slave: slave struct to work on
+ *
+ * Returns @slave network device name, or "null" if it can't be found.
+ */
+static inline const char *_bond_l2da_slave_name(struct slave *slave)
+{
+	if (slave && slave->dev)
+		return netdev_name(slave->dev);
+	return "null";
+}
+
+/**
+ * _bond_l2da_hash_val - hash function for L2DA map hash table
+ * @da: DA to be used as a hash key
+ *
+ * Returns hash value for @da
+ */
+static inline u32 _bond_l2da_hash_val(const unsigned char *da)
+{
+	return da[ETH_ALEN - 2];
+}
+
+/**
+ * _bond_l2da_find_entry_unsafe - searches for DA:iface mapping within the map
+ * @bond_info: L2DA bonding struct to work on
+ * @da: DA to be used as a key
+ *
+ * Returns map entry for @da, or %NULL if it can't be found.
+ *
+ * The function must be called under the L2DA bonding struct lock.
+ */
+static struct l2da_bond_matrix_entry *
+_bond_l2da_find_entry_unsafe(struct l2da_bond_info *bond_info,
+			     const unsigned char *da)
+{
+	struct l2da_bond_matrix_entry *entry = NULL;
+	u32 hash_val = 0;
+	BUG_ON(da == NULL);
+
+	hash_val = _bond_l2da_hash_val(da);
+	hash_for_each_possible(bond_info->da_matrix, entry, hnode, hash_val) {
+		if (!compare_ether_addr(entry->h_dest, da))
+			return entry;
+	}
+	return NULL;
+}
+
+/**
+ * _bond_l2da_select_fallback_slave_unsafe - selects a random fallback slave
+ *                                           (if needed)
+ * @bond: bonding struct to work on
+ *
+ * The function must be called under the L2DA bonding struct lock.
+ */
+static void
+_bond_l2da_select_fallback_slave_unsafe(struct bonding *bond)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct slave *slave;
+	struct slave *fallback_slave = bond_info->fallback_slave;
+	struct list_head *iter;
+
+	/* No need for fallback slave while default slave is OK */
+	if (bond_info->default_slave &&
+	    SLAVE_CAN_XMIT(bond_info->default_slave)) {
+		fallback_slave = NULL;
+		goto out;
+	}
+
+	/* Current fallback slave is OK */
+	if (bond_info->fallback_slave &&
+	    SLAVE_CAN_XMIT(bond_info->fallback_slave))
+		goto out;
+
+	/* Select new fallback slave */
+	fallback_slave = NULL;
+	bond_for_each_slave(bond, slave, iter) {
+		if (slave != bond_info->default_slave &&
+		    SLAVE_CAN_XMIT(slave)) {
+			fallback_slave = slave;
+			goto out;
+		}
+	}
+
+out:
+	if (fallback_slave != bond_info->fallback_slave) {
+		pr_info("bond_l2da fallback slave set to %s\n",
+				_bond_l2da_slave_name(fallback_slave));
+		bond_info->fallback_slave = fallback_slave;
+	}
+}
+
+/**
+ * _bond_l2da_remove_entries_unsafe - removes all iface mappings from the map
+ * @bond_info: L2DA bonding struct to work on
+ * @slave: slave whose mappings have to be removed
+ *
+ * The function must be called under the L2DA bonding struct lock.
+ */
+static void _bond_l2da_remove_entries_unsafe(struct l2da_bond_info *bond_info,
+					     struct slave *slave)
+{
+	struct l2da_bond_matrix_entry *entry = NULL;
+	struct hlist_node *tmp;
+	int bkt;
+	hash_for_each_safe(bond_info->da_matrix, bkt, tmp, entry, hnode) {
+		/* NULL slave means "remove all" */
+		if (!slave || entry->slave == slave) {
+			hash_del(&entry->hnode);
+			kfree(entry);
+		}
+	}
+}
+
+/**
+ * bond_l2da_initialize - initializes a bond's L2DA context
+ * @bond: bonding struct to work on
+ */
+int bond_l2da_initialize(struct bonding *bond)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	memset(bond_info, 0, sizeof(*bond_info));
+	hash_init(bond_info->da_matrix);
+	rwlock_init(&bond_info->lock);
+	bond_info->default_slave = NULL;
+	pr_info("bond_l2da initialized\n");
+	return 0;
+}
+
+/**
+ * bond_l2da_deinitialize - deinitializes a bond's L2DA context
+ * @bond: bonding struct to work on
+ */
+void bond_l2da_deinitialize(struct bonding *bond)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	/* Kick off any bonds registered  */
+	write_lock_bh(&bond_info->lock);
+	_bond_l2da_remove_entries_unsafe(bond_info, NULL);
+	write_unlock_bh(&bond_info->lock);
+	BUG_ON(!hash_empty(bond_info->da_matrix));
+	memset(bond_info, 0, sizeof(*bond_info)); /* for debugging purposes */
+	pr_info("bond_l2da de-initialized\n");
+}
+
+/**
+ * bond_l2da_slave_init - initializes a slave
+ * @bond: bonding struct to work on
+ * @slave: slave struct to work on
+ *
+ * Assigns default and fallback slaves (if needed).
+ */
+int bond_l2da_slave_init(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	write_lock_bh(&bond_info->lock);
+	if (!bond_info->default_slave) {
+		bond_info->default_slave = slave;
+		pr_info("bond_l2da default slave initially set to %s\n",
+			_bond_l2da_slave_name(slave));
+	}
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+	return 0;
+}
+
+/**
+ * bond_l2da_slave_deinit - deinitializes a slave
+ * @slave: slave struct to work on
+ *
+ * Removes all matrix entries for this slave, re-assigns default and fallback
+ * slaves (if needed).
+ */
+void bond_l2da_slave_deinit(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	write_lock_bh(&bond_info->lock);
+	if (slave == bond_info->default_slave) {
+		/* default slave has gone, so let's use some other slave as
+		 * a new default
+		 */
+		bond_info->default_slave = bond_first_slave(bond);
+		pr_info("bond_l2da default slave set to %s\n",
+			_bond_l2da_slave_name(bond_info->default_slave));
+	}
+	_bond_l2da_remove_entries_unsafe(bond_info, slave);
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+}
+
+/**
+ * bond_l2da_xmit - transmits skb in L2DA mode
+ * @skb: skb to transmit
+ * @dev: bonding net device
+ */
+int bond_l2da_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct bonding *bond = netdev_priv(dev);
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct ethhdr *eth_data;
+	struct l2da_bond_matrix_entry *entry;
+	int res = 1;
+
+	skb_reset_mac_header(skb);
+	eth_data = eth_hdr(skb);
+
+	read_lock_bh(&bond_info->lock);
+	entry = _bond_l2da_find_entry_unsafe(bond_info, eth_data->h_dest);
+	if (entry && entry->slave && SLAVE_CAN_XMIT(entry->slave)) {
+		/* if a slave configured  for this DA and it's OK - use it */
+		res = bond_dev_queue_xmit(bond, skb, entry->slave->dev);
+	} else if (bond_info->default_slave &&
+		 SLAVE_CAN_XMIT(bond_info->default_slave)) {
+		/* otherwise, if default slave configured and OK - use it */
+		res = bond_dev_queue_xmit(bond, skb,
+					  bond_info->default_slave->dev);
+	} else if (bond_info->fallback_slave) {
+		/* otherwise, if fallback slave selected - use it */
+		res = bond_dev_queue_xmit(bond, skb,
+					  bond_info->fallback_slave->dev);
+	}
+	read_unlock_bh(&bond_info->lock);
+
+	if (res) {
+		/* no suitable interface, frame not sent */
+		kfree_skb(skb);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * bond_l2da_set_default_slave - sends default slave
+ * @bond: bonding struct to work on
+ * @slave: slave struct whose link status changed
+ */
+void bond_l2da_set_default_slave(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	write_lock_bh(&bond_info->lock);
+	bond_info->default_slave = slave;
+	pr_info("bond_l2da default slave set to %s\n",
+			_bond_l2da_slave_name(slave));
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+}
+
+/**
+ * bond_l2da_get_default_slave_name - gets name of currently configured default
+ *                                    slave
+ * @bond: bonding struct to work on
+ * @buf: destination buffer
+ * @size: destination buffer size
+ */
+int bond_l2da_get_default_slave_name(struct bonding *bond, char *buf, int size)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+
+	if (!buf || size < IFNAMSIZ)
+		return -EINVAL;
+
+	*buf = 0;
+
+	read_lock_bh(&bond_info->lock);
+	if (bond_info->default_slave) {
+		strncpy(buf, netdev_name(bond_info->default_slave->dev),
+			IFNAMSIZ);
+	}
+	read_unlock_bh(&bond_info->lock);
+	return 0;
+}
+
+/**
+ * bond_l2da_set_da_slave - adds DA:slave mapping
+ * @bond: bonding struct to work on
+ * @da: desired L2 destination address to map
+ * @slave: slave to be used for sending packets to desired destination address
+ */
+int bond_l2da_set_da_slave(struct bonding *bond, const unsigned char *da,
+		struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct l2da_bond_matrix_entry *entry;
+	struct slave *prev_slave = NULL;
+
+	write_lock_bh(&bond_info->lock);
+	entry = _bond_l2da_find_entry_unsafe(bond_info, da);
+	if (entry) {
+		prev_slave = entry->slave;
+		entry->slave = slave;
+	} else {
+		entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+		if (entry) {
+			entry->slave = slave;
+			memcpy(entry->h_dest, da, ETH_ALEN);
+			hash_add(bond_info->da_matrix, &entry->hnode,
+					_bond_l2da_hash_val(da));
+		}
+	}
+	write_unlock_bh(&bond_info->lock);
+
+	if (!entry) {
+		pr_err("bond_l2da: pair node cannot be allocated for [%pM:%s]\n",
+			da, _bond_l2da_slave_name(slave));
+		return -ENOMEM;
+	}
+
+	pr_info("bond_l2da: pair %s [%pM:%s]\n",
+		prev_slave ? "changed" : "added",
+		da, _bond_l2da_slave_name(slave));
+
+	return 0;
+}
+
+/**
+ * bond_l2da_del_da - removes DA mapping
+ * @bond: bonding struct to work on
+ * @da: L2 destination address whose mapping has to be removed
+ */
+int bond_l2da_del_da(struct bonding *bond, const unsigned char *da)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct l2da_bond_matrix_entry *entry;
+
+	write_lock_bh(&bond_info->lock);
+	entry = _bond_l2da_find_entry_unsafe(bond_info, da);
+	if (entry)
+		hash_del(&entry->hnode);
+	write_unlock_bh(&bond_info->lock);
+
+	if (!entry) {
+		pr_err("bond_l2da: pair node cannot be found for %pM\n",
+			da);
+		return -ENOENT;
+	}
+
+	pr_info("bond_l2da: pair deleted [%pM:%s]\n",
+			da, _bond_l2da_slave_name(entry->slave));
+	kfree(entry);
+	return 0;
+}
+
+/**
+ * bond_l2da_handle_link_change - handle a slave's link status change indication
+ * @bond: bonding struct to work on
+ * @slave: slave struct whose link status changed
+ *
+ * Handle re-selection of fallback slave (if needed).
+ */
+void bond_l2da_handle_link_change(struct bonding *bond, struct slave *slave)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+
+	write_lock_bh(&bond_info->lock);
+	_bond_l2da_select_fallback_slave_unsafe(bond);
+	write_unlock_bh(&bond_info->lock);
+}
+
+/**
+ * bond_l2da_call_foreach - iterates over L2DA map
+ * @bond: bonding struct to work on
+ * @clb: callback function to be called for every mapping entry found
+ * @ctx: user context to be passed to callback
+ *
+ * Callback function can return non-zero value to stop iteration.
+ */
+void bond_l2da_call_foreach(struct bonding *bond,
+		int (*clb)(const unsigned char *da, struct slave *slave,
+			   void *ctx),
+		void *ctx)
+{
+	struct l2da_bond_info *bond_info = &BOND_L2DA_INFO(bond);
+	struct l2da_bond_matrix_entry *entry;
+	int bkt;
+
+	BUG_ON(!clb);
+
+	read_lock_bh(&bond_info->lock);
+	hash_for_each(bond_info->da_matrix, bkt, entry, hnode) {
+		if (clb(entry->h_dest, entry->slave, ctx))
+			break;
+	}
+	read_unlock_bh(&bond_info->lock);
+}
+
diff --git a/drivers/net/bonding/bond_l2da.h b/drivers/net/bonding/bond_l2da.h
new file mode 100644
index 0000000..4a3b88f
--- /dev/null
+++ b/drivers/net/bonding/bond_l2da.h
@@ -0,0 +1,56 @@
+/* Copyright(c) 2013 Wilocity Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+#ifndef __BOND_L2DA_H__
+#define __BOND_L2DA_H__
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/hashtable.h>
+#include <linux/rwlock.h>
+
+#define BOND_L2A_HASHTABLE_BITS 3
+
+/* L2DA Exported structures to the main bonding code */
+struct l2da_bond_info {
+	DECLARE_HASHTABLE(da_matrix, BOND_L2A_HASHTABLE_BITS); /*DA:iface map*/
+	struct slave    *default_slave; /* Default slave */
+	struct slave    *fallback_slave; /* A random fallback slave to be used
+					  * in case default should be used while
+					  * it cannot transmit
+					  */
+	rwlock_t         lock;
+};
+
+/* l2DA Exported functions to the main bonding code */
+int bond_l2da_initialize(struct bonding *bond);
+void bond_l2da_deinitialize(struct bonding *bond);
+int bond_l2da_slave_init(struct bonding *bond, struct slave *slave);
+void bond_l2da_slave_deinit(struct bonding *bond, struct slave *slave);
+int bond_l2da_xmit(struct sk_buff *skb, struct net_device *dev);
+void bond_l2da_set_default_slave(struct bonding *bond, struct slave *slave);
+int bond_l2da_get_default_slave_name(struct bonding *bond, char *buf, int size);
+int bond_l2da_set_da_slave(struct bonding *bond, const unsigned char *da,
+		struct slave *slave);
+int bond_l2da_del_da(struct bonding *bond, const unsigned char *da);
+void bond_l2da_handle_link_change(struct bonding *bond, struct slave *slave);
+void bond_l2da_call_foreach(struct bonding *bond,
+		int (*clb)(const unsigned char *da,
+			   struct slave *slave, void *ctx),
+		void *ctx);
+
+#endif /* __BOND_L2DA_H__ */
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index ca31286..2069584 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -25,6 +25,7 @@
 #include <linux/etherdevice.h>
 #include "bond_3ad.h"
 #include "bond_alb.h"
+#include "bond_l2da.h"
 
 #define DRV_VERSION	"3.7.1"
 #define DRV_RELDATE	"April 27, 2011"
@@ -229,6 +230,7 @@ struct bonding {
 	u32      rr_tx_counter;
 	struct   ad_bond_info ad_info;
 	struct   alb_bond_info alb_info;
+	struct   l2da_bond_info l2da_info;
 	struct   bond_params params;
 	struct   workqueue_struct *wq;
 	struct   delayed_work mii_work;
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH 2/4] bonding: L2DA mode intergated
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum,
	Boris Lapshin
In-Reply-To: <1385045738-29726-1-git-send-email-Anton.Nayshtut@wilocity.com>

L2DA bonding module integration, including module parameters, sysfs entries and
module calls.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/bond_main.c    |  51 ++++++++-
 drivers/net/bonding/bond_options.c |  17 ++-
 drivers/net/bonding/bond_sysfs.c   | 223 ++++++++++++++++++++++++++++++++++++-
 drivers/net/bonding/bonding.h      |   5 +
 include/uapi/linux/if_bonding.h    |   1 +
 5 files changed, 290 insertions(+), 7 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 4dd5ee2..33733be 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -138,7 +138,7 @@ module_param(mode, charp, 0);
 MODULE_PARM_DESC(mode, "Mode of operation; 0 for balance-rr, "
 		       "1 for active-backup, 2 for balance-xor, "
 		       "3 for broadcast, 4 for 802.3ad, 5 for balance-tlb, "
-		       "6 for balance-alb");
+		       "6 for balance-alb, 7 for l2da");
 module_param(primary, charp, 0);
 MODULE_PARM_DESC(primary, "Primary network device to use");
 module_param(primary_reselect, charp, 0);
@@ -218,6 +218,7 @@ const struct bond_parm_tbl bond_mode_tbl[] = {
 {	"802.3ad",		BOND_MODE_8023AD},
 {	"balance-tlb",		BOND_MODE_TLB},
 {	"balance-alb",		BOND_MODE_ALB},
+{	"l2da",			BOND_MODE_L2DA },
 {	NULL,			-1},
 };
 
@@ -282,9 +283,10 @@ const char *bond_mode_name(int mode)
 		[BOND_MODE_8023AD] = "IEEE 802.3ad Dynamic link aggregation",
 		[BOND_MODE_TLB] = "transmit load balancing",
 		[BOND_MODE_ALB] = "adaptive load balancing",
+		[BOND_MODE_L2DA] = "l2 DA matrix",
 	};
 
-	if (mode < BOND_MODE_ROUNDROBIN || mode > BOND_MODE_ALB)
+	if (mode < BOND_MODE_ROUNDROBIN || mode > BOND_MODE_L2DA)
 		return "unknown";
 
 	return names[mode];
@@ -1546,6 +1548,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 		bond_set_active_slave(new_slave);
 		bond_set_slave_inactive_flags(new_slave);
 		break;
+	case BOND_MODE_L2DA:
+		bond_set_slave_active_flags(new_slave);
+		bond_l2da_slave_init(bond, new_slave);
+		break;
 	default:
 		pr_debug("This slave is always active in trunk mode\n");
 
@@ -1760,7 +1766,8 @@ static int __bond_release_one(struct net_device *bond_dev,
 		write_unlock_bh(&bond->lock);
 		bond_alb_deinit_slave(bond, slave);
 		write_lock_bh(&bond->lock);
-	}
+	} else if (bond_is_l2da(bond))
+		bond_l2da_slave_deinit(bond, slave);
 
 	if (all) {
 		rcu_assign_pointer(bond->curr_active_slave, NULL);
@@ -2058,6 +2065,9 @@ static void bond_miimon_commit(struct bonding *bond)
 				bond_alb_handle_link_change(bond, slave,
 							    BOND_LINK_UP);
 
+			if (bond_is_l2da(bond))
+				bond_l2da_handle_link_change(bond, slave);
+
 			if (!bond->curr_active_slave ||
 			    (slave == bond->primary_slave))
 				goto do_failover;
@@ -2085,6 +2095,9 @@ static void bond_miimon_commit(struct bonding *bond)
 				bond_alb_handle_link_change(bond, slave,
 							    BOND_LINK_DOWN);
 
+			if (bond_is_l2da(bond))
+				bond_l2da_handle_link_change(bond, slave);
+
 			if (slave == bond->curr_active_slave)
 				goto do_failover;
 
@@ -3778,6 +3791,8 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
 	case BOND_MODE_ALB:
 	case BOND_MODE_TLB:
 		return bond_alb_xmit(skb, dev);
+	case BOND_MODE_L2DA:
+		return bond_l2da_xmit(skb, dev);
 	default:
 		/* Should never happen, mode already checked */
 		pr_err("%s: Error: Unknown bonding mode %d\n",
@@ -3969,6 +3984,9 @@ static void bond_uninit(struct net_device *bond_dev)
 	list_del(&bond->bond_list);
 
 	bond_debug_unregister(bond);
+
+	if (bond_is_l2da(bond))
+		bond_l2da_deinitialize(bond);
 }
 
 /*------------------------- Module initialization ---------------------------*/
@@ -4041,7 +4059,8 @@ static int bond_check_params(struct bond_params *params)
 	}
 
 	if (lacp_rate) {
-		if (bond_mode != BOND_MODE_8023AD) {
+		if (bond_mode != BOND_MODE_8023AD ||
+			bond_mode != BOND_MODE_L2DA) {
 			pr_info("lacp_rate param is irrelevant in mode %s\n",
 				bond_mode_name(bond_mode));
 		} else {
@@ -4128,6 +4147,19 @@ static int bond_check_params(struct bond_params *params)
 		all_slaves_active = 0;
 	}
 
+	if (bond_mode == BOND_MODE_L2DA) {
+		if (!all_slaves_active) {
+			pr_warning("Warning: all_slaves_active must be specified, otherwise bonding will not be able to route packets that is essential for l2da operation\n");
+			pr_warning("Forcing all_slaves_active to 1\n");
+			all_slaves_active = 1;
+		}
+		if (!miimon) {
+			pr_warning("Warning: miimon must be specified, otherwise bonding will not detect link failure and link speed which are essential for l2da operation\n");
+			pr_warning("Forcing miimon to 100msec\n");
+			miimon = 100;
+		}
+	}
+
 	if (resend_igmp < 0 || resend_igmp > 255) {
 		pr_warning("Warning: resend_igmp (%d) should be between "
 			   "0 and 255, resetting to %d\n",
@@ -4372,6 +4404,7 @@ static int bond_init(struct net_device *bond_dev)
 	struct bonding *bond = netdev_priv(bond_dev);
 	struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id);
 	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+	int ret;
 
 	pr_debug("Begin bond_init for %s\n", bond_dev->name);
 
@@ -4384,6 +4417,16 @@ static int bond_init(struct net_device *bond_dev)
 	spin_lock_init(&(bond_info->tx_hashtbl_lock));
 	spin_lock_init(&(bond_info->rx_hashtbl_lock));
 
+	if (bond_is_l2da(bond)) {
+		ret = bond_l2da_initialize(bond);
+		if (ret) {
+			pr_err("%s: %s mode cannot be initialized.\n",
+					bond->dev->name,
+					bond_mode_tbl[BOND_MODE_L2DA].modename);
+			return ret;
+		}
+	}
+
 	bond->wq = create_singlethread_workqueue(bond_dev->name);
 	if (!bond->wq)
 		return -ENOMEM;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 9a5223c..b413665 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -45,12 +45,27 @@ int bond_option_mode_set(struct bonding *bond, int mode)
 		return -EPERM;
 	}
 
-	if (BOND_MODE_IS_LB(mode) && bond->params.arp_interval) {
+	if ((BOND_MODE_IS_LB(mode) || bond_is_l2da(bond)) &&
+	    bond->params.arp_interval) {
 		pr_err("%s: %s mode is incompatible with arp monitoring.\n",
 		       bond->dev->name, bond_mode_tbl[mode].modename);
 		return -EINVAL;
 	}
 
+	if (bond->params.mode == mode)
+		;
+	else if (mode == BOND_MODE_L2DA) {
+		int ret = bond_l2da_initialize(bond);
+		if (ret) {
+			pr_err("%s: %s mode cannot be initialized.\n",
+			       bond->dev->name,
+			       bond_mode_tbl[mode].modename);
+			return ret;
+		}
+	} else if (bond_is_l2da(bond))
+		bond_l2da_deinitialize(bond);
+
+
 	/* don't cache arp_validate between modes */
 	bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
 	bond->params.mode = mode;
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 0ec2a7e..d4727d2 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -525,8 +525,9 @@ static ssize_t bonding_store_arp_interval(struct device *d,
 	}
 	if (bond->params.mode == BOND_MODE_ALB ||
 	    bond->params.mode == BOND_MODE_TLB ||
-	    bond->params.mode == BOND_MODE_8023AD) {
-		pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad. Only MII monitoring is supported on %s.\n",
+	    bond->params.mode == BOND_MODE_8023AD ||
+	    bond->params.mode == BOND_MODE_L2DA) {
+		pr_info("%s: ARP monitoring cannot be used with ALB/TLB/802.3ad/L2DA. Only MII monitoring is supported on %s.\n",
 			bond->dev->name, bond->dev->name);
 		ret = -EINVAL;
 		goto out;
@@ -1679,6 +1680,222 @@ static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
 		   bonding_show_packets_per_slave,
 		   bonding_store_packets_per_slave);
 
+static ssize_t bonding_show_l2da_default_slave(struct device *d,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct bonding *bond = to_bond(d);
+	char ifname[IFNAMSIZ + 1];
+	int ret;
+
+	read_lock(&bond->lock);
+	ret = bond_l2da_get_default_slave_name(bond, ifname, sizeof(ifname));
+	read_unlock(&bond->lock);
+
+	ifname[IFNAMSIZ] = 0;
+
+	return sprintf(buf, "%s\n", ifname);
+}
+
+static ssize_t bonding_store_l2da_default_slave(struct device *d,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	int ret = -EINVAL;
+	struct slave *slave;
+	struct bonding *bond = to_bond(d);
+	char ifname[IFNAMSIZ];
+	struct list_head *iter;
+
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	if (sscanf(buf, "%15s", ifname) != 1) {/* IFNAMSIZ */
+		pr_info("%s: no L2DA slave name specified\n",
+			netdev_name(bond->dev));
+		goto rtnl_out;
+	}
+
+	block_netpoll_tx();
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond)) {
+		pr_info("%s: Unable to set default slave ; %s is in mode %d\n",
+			netdev_name(bond->dev), netdev_name(bond->dev),
+			bond->params.mode);
+		goto out;
+	}
+
+	bond_for_each_slave(bond, slave, iter) {
+		if (strncmp(netdev_name(slave->dev), ifname, IFNAMSIZ) == 0) {
+			if (slave->link == BOND_LINK_UP && IS_UP(slave->dev)) {
+				bond_l2da_set_default_slave(bond, slave);
+				ret = count;
+			} else
+				pr_warn("%s: cannot set %s as default slave\n",
+					netdev_name(bond->dev),
+					netdev_name(slave->dev));
+
+			break;
+		}
+	}
+
+	if (ret < 0)
+		pr_warn("%s: Unable to to set %.*s as default slave.\n",
+			netdev_name(bond->dev), (int)strlen(buf) - 1, buf);
+
+out:
+	read_unlock(&bond->lock);
+	unblock_netpoll_tx();
+rtnl_out:
+	rtnl_unlock();
+
+	return ret;
+}
+
+static DEVICE_ATTR(l2da_default_slave, S_IRUGO | S_IWUSR,
+		   bonding_show_l2da_default_slave,
+		   bonding_store_l2da_default_slave);
+
+struct bonding_show_l2da_table_clb_ctx {
+	char *buf;
+	int   res;
+};
+
+static int bonding_show_l2da_table_clb(const unsigned char *da,
+		struct slave *slave, void *_ctx)
+{
+	struct bonding_show_l2da_table_clb_ctx *ctx = _ctx;
+	if (ctx->res > (PAGE_SIZE - 18 - IFNAMSIZ)) {
+		/* not enough space for another da@interface_name pair */
+		if ((PAGE_SIZE - ctx->res) > 9)
+			ctx->res = PAGE_SIZE - 9;
+		ctx->res += sprintf(ctx->buf + ctx->res, "++more++");
+		return 1;
+	}
+	ctx->res += sprintf(ctx->buf + ctx->res, "%pM@%s\n",
+		       da, netdev_name(slave->dev));
+	return 0;
+}
+
+static ssize_t bonding_show_l2da_table(struct device *d,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct bonding *bond = to_bond(d);
+	struct bonding_show_l2da_table_clb_ctx ctx = {
+		.buf = buf,
+	};
+
+	read_lock(&bond->lock);
+	bond_l2da_call_foreach(bond, bonding_show_l2da_table_clb, &ctx);
+	read_unlock(&bond->lock);
+	if (ctx.res)
+		buf[ctx.res-1] = '\n'; /* eat the leftover space */
+
+	return ctx.res;
+}
+
+static ssize_t bonding_store_l2da_table(struct device *d,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct slave *slave;
+	struct bonding *bond = to_bond(d);
+	int ret = -EINVAL;
+	char *delim;
+	unsigned char da[ETH_ALEN];
+	struct net_device *sdev = NULL;
+	struct list_head *iter;
+
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	/* Check command syntax and extract parameters */
+	if (buf[0] == '+') {
+		unsigned char ifname[IFNAMSIZ] = {0, };
+		/* delim will point to slave interface name if successful */
+		delim = strchr(buf, '@');
+		if (!delim) {
+			pr_err("%s: Invalid L2DA command string: %s\n",
+			    netdev_name(bond->dev), buf);
+			goto out;
+		}
+		/* Terminate string that points to device name and bump it
+		 * up one, so we can read the destination address there.
+		 */
+		*delim = '\0';
+		if (sscanf(delim + 1, "%15s", ifname) != 1) { /* IFNAMSIZ */
+			pr_err("%s: no L2DA slave name specified\n",
+			    netdev_name(bond->dev));
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/* Check valid ifname */
+		if (!dev_valid_name(ifname)) {
+			pr_err("%s: Invalid L2DA slave name: '%s'\n",
+			    netdev_name(bond->dev), ifname);
+			ret = -EINVAL;
+			goto out;
+
+		}
+
+		/* Get the pointer to that interface if it exists */
+		sdev = __dev_get_by_name(dev_net(bond->dev), ifname);
+		if (!sdev) {
+			pr_err("%s: No L2DA slave found: %s\n",
+			    netdev_name(bond->dev), ifname);
+			ret = -EINVAL;
+			goto out;
+		}
+	} else if (buf[0] != '-') {
+		pr_err("%s: Invalid L2DA command string: %s\n",
+		    netdev_name(bond->dev), buf);
+		goto out;
+	}
+
+	if (!mac_pton(buf + 1, da)) {
+		pr_err("%s: Invalid L2DA MAC address string: %s\n",
+		    netdev_name(bond->dev), buf + 1);
+		goto out;
+	}
+
+	if (!is_valid_ether_addr(da)) {
+		pr_err("%s: Invalid L2DA MAC address: %pM\n",
+		    netdev_name(bond->dev), da);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	block_netpoll_tx();
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond))
+		pr_info("%s: Unable to set default slave ; %s is in mode %d\n",
+			netdev_name(bond->dev), netdev_name(bond->dev),
+			bond->params.mode);
+	else if (buf[0] == '-')
+		ret = bond_l2da_del_da(bond, da);
+	else {
+		bond_for_each_slave(bond, slave, iter) {
+			if (sdev == slave->dev) {
+				ret = bond_l2da_set_da_slave(bond, da, slave);
+				break;
+			}
+		}
+	}
+
+	read_unlock(&bond->lock);
+	unblock_netpoll_tx();
+out:
+	rtnl_unlock();
+	return ret ? ret : count;
+}
+
+static DEVICE_ATTR(l2da_table, S_IRUGO | S_IWUSR,
+		   bonding_show_l2da_table, bonding_store_l2da_table);
+
 static struct attribute *per_bond_attrs[] = {
 	&dev_attr_slaves.attr,
 	&dev_attr_mode.attr,
@@ -1711,6 +1928,8 @@ static struct attribute *per_bond_attrs[] = {
 	&dev_attr_min_links.attr,
 	&dev_attr_lp_interval.attr,
 	&dev_attr_packets_per_slave.attr,
+	&dev_attr_l2da_default_slave.attr,
+	&dev_attr_l2da_table.attr,
 	NULL,
 };
 
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 2069584..6c8c851 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -273,6 +273,11 @@ static inline bool bond_is_lb(const struct bonding *bond)
 	return BOND_MODE_IS_LB(bond->params.mode);
 }
 
+static inline bool bond_is_l2da(const struct bonding *bond)
+{
+	return bond->params.mode == BOND_MODE_L2DA;
+}
+
 static inline void bond_set_active_slave(struct slave *slave)
 {
 	slave->backup = 0;
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
index 9635a62..b70ff3e 100644
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -70,6 +70,7 @@
 #define BOND_MODE_8023AD        4
 #define BOND_MODE_TLB           5
 #define BOND_MODE_ALB		6 /* TLB + RLB (receive load balancing) */
+#define BOND_MODE_L2DA          7
 
 /* each slave's link has 4 states */
 #define BOND_LINK_UP    0           /* link is up and running */
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH 3/4] bonding: L2DA command IOCTL
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum,
	Boris Lapshin
In-Reply-To: <1385045738-29726-1-git-send-email-Anton.Nayshtut@wilocity.com>

L2DA command IOCTL introduction. The SIOCBONDL2DACMD is used to control L2DA
bonding logic: change default slave, add and delete map entries.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/bond_main.c | 52 +++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/if_bonding.h | 16 +++++++++++++
 include/uapi/linux/sockios.h    |  1 +
 net/core/dev_ioctl.c            |  2 ++
 net/socket.c                    |  2 ++
 5 files changed, 73 insertions(+)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 33733be..ddaee7b 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1886,6 +1886,55 @@ static int  bond_release_and_destroy(struct net_device *bond_dev,
 	return ret;
 }
 
+static int bond_ioctl_l2da_cmd(struct net_device *bond_dev,
+			       void __user *usr_data)
+{
+	struct bonding *bond = netdev_priv(bond_dev);
+	struct l2da_cmd *u_l2dacmd = (struct l2da_cmd __user *)usr_data;
+	struct l2da_cmd  k_l2dacmd;
+	struct net_device *slave_dev;
+	struct slave *slave;
+	int res = -EFAULT;
+
+	if (copy_from_user(&k_l2dacmd, u_l2dacmd, sizeof(struct l2da_cmd)))
+		return res;
+
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond))
+		goto out;
+
+	switch (k_l2dacmd.cmd) {
+	case BOND_L2DA_CMD_SET_DEFAULT:
+		slave_dev = dev_get_by_name(dev_net(bond_dev),
+					    k_l2dacmd.slave_name);
+		if (slave_dev) {
+			slave = bond_get_slave_by_dev(bond, slave_dev);
+			bond_l2da_set_default_slave(bond, slave);
+			dev_put(slave_dev);
+			res = 0;
+		}
+		break;
+	case BOND_L2DA_CMD_MAP_ADD:
+		slave_dev = dev_get_by_name(dev_net(bond_dev),
+					    k_l2dacmd.slave_name);
+		if (slave_dev) {
+			slave = bond_get_slave_by_dev(bond, slave_dev);
+			res = bond_l2da_set_da_slave(bond, k_l2dacmd.da, slave);
+			dev_put(slave_dev);
+		}
+		break;
+	case BOND_L2DA_CMD_MAP_DEL:
+		res = bond_l2da_del_da(bond, k_l2dacmd.da);
+		break;
+	default:
+		break;
+	}
+out:
+	read_unlock(&bond->lock);
+	return res;
+}
+
 static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
@@ -3262,6 +3311,9 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 		return -EPERM;
 
+	if (cmd == SIOCBONDL2DACMD)
+		return bond_ioctl_l2da_cmd(bond_dev, ifr->ifr_data);
+
 	slave_dev = dev_get_by_name(net, ifr->ifr_slave);
 
 	pr_debug("slave_dev=%p:\n", slave_dev);
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
index b70ff3e..a4d3baf 100644
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -95,6 +95,11 @@
 #define BOND_XMIT_POLICY_ENCAP23	3 /* encapsulated layer 2+3 */
 #define BOND_XMIT_POLICY_ENCAP34	4 /* encapsulated layer 3+4 */
 
+/* l2da commands */
+#define BOND_L2DA_CMD_SET_DEFAULT    0 /* set default slave */
+#define BOND_L2DA_CMD_MAP_ADD        1 /* add DA:iface mapping */
+#define BOND_L2DA_CMD_MAP_DEL        2 /* add DA mapping */
+
 typedef struct ifbond {
 	__s32 bond_mode;
 	__s32 num_slaves;
@@ -117,6 +122,17 @@ struct ad_info {
 	__u8 partner_system[ETH_ALEN];
 };
 
+struct l2da_cmd {
+	__u16 cmd; /* one of the BOND_L2DA_CMD_... commands */
+	__u8 da[ETH_ALEN]; /* Used as IN parameter in:
+			    *   BOND_L2DA_CMD_MAP_ADD
+			    *   BOND_L2DA_CMD_MAP_DEL*/
+	char slave_name[IFNAMSIZ]; /* Used as IN parameter in:
+				    *   BOND_L2DA_CMD_SET_DEFAULT
+				    *   BOND_L2DA_CMD_MAP_ADD
+				    * */
+};
+
 #endif /* _LINUX_IF_BONDING_H */
 
 /*
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index 7997a50..2af055d 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -117,6 +117,7 @@
 #define SIOCBONDSLAVEINFOQUERY 0x8993   /* rtn info about slave state   */
 #define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
 #define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
+#define SIOCBONDL2DACMD        0x8996   /* l2da related command */
 			
 /* bridge calls */
 #define SIOCBRADDBR     0x89a0		/* create new bridge device     */
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 5b7d0e1..3a4716a 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -321,6 +321,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 		    cmd == SIOCBONDSLAVEINFOQUERY ||
 		    cmd == SIOCBONDINFOQUERY ||
 		    cmd == SIOCBONDCHANGEACTIVE ||
+		    cmd == SIOCBONDL2DACMD ||
 		    cmd == SIOCGMIIPHY ||
 		    cmd == SIOCGMIIREG ||
 		    cmd == SIOCSMIIREG ||
@@ -518,6 +519,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 	case SIOCBONDRELEASE:
 	case SIOCBONDSETHWADDR:
 	case SIOCBONDCHANGEACTIVE:
+	case SIOCBONDL2DACMD:
 	case SIOCBRADDIF:
 	case SIOCBRDELIF:
 	case SIOCSHWTSTAMP:
diff --git a/net/socket.c b/net/socket.c
index c226ace..2ed653b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2975,6 +2975,7 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
 	case SIOCBONDRELEASE:
 	case SIOCBONDSETHWADDR:
 	case SIOCBONDCHANGEACTIVE:
+	case SIOCBONDL2DACMD:
 		if (copy_from_user(&kifr, ifr32, sizeof(struct compat_ifreq)))
 			return -EFAULT;
 
@@ -3262,6 +3263,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
 	case SIOCBONDCHANGEACTIVE:
+	case SIOCBONDL2DACMD:
 		return bond_ioctl(net, cmd, argp);
 	case SIOCADDRT:
 	case SIOCDELRT:
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH 4/4] bonding: L2DA query IOCTL
From: Anton Nayshtut @ 2013-11-21 14:55 UTC (permalink / raw)
  To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S. Miller,
	Cong Wang, Nicolas Schichan, Eric Dumazet
  Cc: linux-kernel, netdev, Anton Nayshtut, Erez Kirshenbaum,
	Boris Lapshin
In-Reply-To: <1385045738-29726-1-git-send-email-Anton.Nayshtut@wilocity.com>

L2DA query IOCTL introduction. The SIOCBONDL2DAQUERY is used to get L2DA related
information: default slave and map entries.

Signed-off-by: Anton Nayshtut <Anton.Nayshtut@wilocity.com>
Signed-off-by: Erez Kirshenbaum <Erez.Kirshenbaum@wilocity.com>
Signed-off-by: Boris Lapshin <Boris.Lapshin@wilocity.com>
---
 drivers/net/bonding/bond_main.c | 69 +++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/if_bonding.h | 15 +++++++++
 include/uapi/linux/sockios.h    |  3 +-
 net/core/dev_ioctl.c            |  2 ++
 net/socket.c                    |  2 ++
 5 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index ddaee7b..ecfe349 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1935,6 +1935,59 @@ out:
 	return res;
 }
 
+struct bond_ioctl_l2da_query_clb_ctx {
+	int res;
+	__s32 i;
+	struct l2da_query *l2da_query;
+};
+
+static int bond_ioctl_l2da_query_clb(const unsigned char *da,
+				     struct slave *slave, void *_ctx)
+{
+	struct bond_ioctl_l2da_query_clb_ctx *ctx = _ctx;
+	if (ctx->i == ctx->l2da_query->entry_id) {
+		strcpy(ctx->l2da_query->slave_name, slave->dev->name);
+		memcpy(ctx->l2da_query->da, da, ETH_ALEN);
+		ctx->res = 0;
+		return 1;
+	}
+	++ctx->i;
+	return 0;
+}
+
+static int bond_ioctl_l2da_query(struct net_device *bond_dev,
+				 struct l2da_query *l2da_query)
+{
+	int res = -EINVAL;
+	struct bonding *bond = netdev_priv(bond_dev);
+	struct bond_ioctl_l2da_query_clb_ctx ctx;
+
+	read_lock(&bond->lock);
+
+	if (!bond_is_l2da(bond))
+		goto out;
+
+	switch (l2da_query->query) {
+	case BOND_L2DA_QUERY_DEFAULT:
+		res = bond_l2da_get_default_slave_name(bond,
+					l2da_query->slave_name,
+					sizeof(l2da_query->slave_name));
+		break;
+	case BOND_L2DA_QUERY_MAP:
+		ctx.res        = res;
+		ctx.i          = 0;
+		ctx.l2da_query = l2da_query;
+		bond_l2da_call_foreach(bond, bond_ioctl_l2da_query_clb, &ctx);
+		res = ctx.res;
+		break;
+	default:
+		break;
+	}
+out:
+	read_unlock(&bond->lock);
+	return res;
+}
+
 static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
@@ -3239,6 +3292,8 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 	struct ifbond __user *u_binfo = NULL;
 	struct ifslave k_sinfo;
 	struct ifslave __user *u_sinfo = NULL;
+	struct l2da_query k_l2da_query;
+	struct l2da_query __user *u_l2da_query = NULL;
 	struct mii_ioctl_data *mii = NULL;
 	struct net *net;
 	int res = 0;
@@ -3301,6 +3356,20 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 			return -EFAULT;
 
 		return res;
+	case SIOCBONDL2DAQUERY:
+		u_l2da_query = (struct l2da_query __user *)ifr->ifr_data;
+
+		if (copy_from_user(&k_l2da_query, u_l2da_query,
+				   sizeof(k_l2da_query)))
+			return -EFAULT;
+
+		res = bond_ioctl_l2da_query(bond_dev, &k_l2da_query);
+		if (res == 0 &&
+		    copy_to_user(u_l2da_query, &k_l2da_query,
+				 sizeof(k_l2da_query)))
+			return -EFAULT;
+
+		return res;
 	default:
 		/* Go on */
 		break;
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
index a4d3baf..fdfe491 100644
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -100,6 +100,10 @@
 #define BOND_L2DA_CMD_MAP_ADD        1 /* add DA:iface mapping */
 #define BOND_L2DA_CMD_MAP_DEL        2 /* add DA mapping */
 
+/* l2da queries */
+#define BOND_L2DA_QUERY_DEFAULT      0 /* get default slave */
+#define BOND_L2DA_QUERY_MAP          1 /* get map entry */
+
 typedef struct ifbond {
 	__s32 bond_mode;
 	__s32 num_slaves;
@@ -133,6 +137,17 @@ struct l2da_cmd {
 				    * */
 };
 
+struct l2da_query {
+	__u16 query; /* one of the BOND_L2DA_QUERY_... queries */
+	__s32 entry_id;  /* Used as IN parameter in:
+			  *   BOND_L2DA_QUERY_MAP */
+	__u8 da[ETH_ALEN]; /* Used as OUT parameter in:
+			    *   BOND_L2DA_QUERY_MAP */
+	char slave_name[IFNAMSIZ]; /* Used as OUT parameter in:
+				    *   BOND_L2DA_QUERY_DEFAULT
+				    *   BOND_L2DA_QUERY_MAP */
+};
+
 #endif /* _LINUX_IF_BONDING_H */
 
 /*
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index 2af055d..86b1d0a 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -118,7 +118,8 @@
 #define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
 #define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
 #define SIOCBONDL2DACMD        0x8996   /* l2da related command */
-			
+#define SIOCBONDL2DAQUERY      0x8997   /* l2da related query */
+
 /* bridge calls */
 #define SIOCBRADDBR     0x89a0		/* create new bridge device     */
 #define SIOCBRDELBR     0x89a1		/* remove bridge device         */
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 3a4716a..f189744 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -322,6 +322,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 		    cmd == SIOCBONDINFOQUERY ||
 		    cmd == SIOCBONDCHANGEACTIVE ||
 		    cmd == SIOCBONDL2DACMD ||
+		    cmd == SIOCBONDL2DAQUERY ||
 		    cmd == SIOCGMIIPHY ||
 		    cmd == SIOCGMIIREG ||
 		    cmd == SIOCSMIIREG ||
@@ -528,6 +529,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 		/* fall through */
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
+	case SIOCBONDL2DAQUERY:
 		dev_load(net, ifr.ifr_name);
 		rtnl_lock();
 		ret = dev_ifsioc(net, &ifr, cmd);
diff --git a/net/socket.c b/net/socket.c
index 2ed653b..e4524f5 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2988,6 +2988,7 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
 		return err;
 	case SIOCBONDSLAVEINFOQUERY:
 	case SIOCBONDINFOQUERY:
+	case SIOCBONDL2DAQUERY:
 		uifr = compat_alloc_user_space(sizeof(*uifr));
 		if (copy_in_user(&uifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
 			return -EFAULT;
@@ -3264,6 +3265,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
 	case SIOCBONDINFOQUERY:
 	case SIOCBONDCHANGEACTIVE:
 	case SIOCBONDL2DACMD:
+	case SIOCBONDL2DAQUERY:
 		return bond_ioctl(net, cmd, argp);
 	case SIOCADDRT:
 	case SIOCDELRT:
-- 
1.8.3.1

^ permalink raw reply related

* Re: [PATCH] net: rework recvmsg handler msg_name and msg_namelen logic
From: Eric Dumazet @ 2013-11-21 15:00 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, davem
In-Reply-To: <528E1E2E.20905@gmail.com>

On Thu, 2013-11-21 at 09:52 -0500, Vlad Yasevich wrote:
> On 11/20/2013 07:38 PM, Hannes Frederic Sowa wrote:
> > diff --git a/include/linux/net.h b/include/linux/net.h
> > index b292a04..4bcee94 100644
> > --- a/include/linux/net.h
> > +++ b/include/linux/net.h
> > @@ -164,6 +164,14 @@ struct proto_ops {
> >  #endif
> >  	int		(*sendmsg)   (struct kiocb *iocb, struct socket *sock,
> >  				      struct msghdr *m, size_t total_len);
> > +	/* Notes for implementing recvmsg:
> > +	 * ===============================
> > +	 * msg->msg_namelen should get updated by the recvmsg handlers
> > +	 * iff msg_name != NULL. It is by default 0 to prevent
> > +	 * returning uninitialized memory to user space.  The recvfrom
> > +	 * handlers can assume that msg.msg_name is either NULL or has
> > +	 * a minimum size of sizeof(struct sockaddr_storage).
>              ^^^^^^^
> 
> You meant "maximum", right?

He meant : A protocol can safely assume there are sizeof(struct
sockaddr_storage) bytes available.

So a protocol should use BUILD_BUG_ON() to make sure sockaddr_storage is
big enough.

^ permalink raw reply

* Re: [PATCH net v2] packet: fix use after free race in send path when dev is released
From: Daniel Borkmann @ 2013-11-21 15:02 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: davem, netdev, Salam Noureddine, Ben Greear
In-Reply-To: <1385044842.10637.41.camel@edumazet-glaptop2.roam.corp.google.com>

On 11/21/2013 03:40 PM, Eric Dumazet wrote:
> On Thu, 2013-11-21 at 10:47 +0100, Daniel Borkmann wrote:
>
>> +static void packet_dev_put_deferred(struct rcu_head *head)
>> +{
>> +	struct packet_sock *po = container_of(head, struct packet_sock, rcu);
>> +	struct sock *sk = &po->sk;
>> +
>> +	spin_lock(&po->bind_lock);
>> +	po->ifindex = -1;
>> +
>> +	if (po->prot_hook.dev) {
>> +		dev_put(po->prot_hook.dev);
>> +		po->prot_hook.dev = NULL;
>> +	}
>> +
>> +	spin_unlock(&po->bind_lock);
>> +	sock_put(sk);
>> +}
>
> I dont think this is needed.
>
>>
>>   static int packet_notifier(struct notifier_block *this,
>>   			   unsigned long msg, void *ptr)
>> @@ -3325,13 +3354,13 @@ static int packet_notifier(struct notifier_block *this,
>>   					if (!sock_flag(sk, SOCK_DEAD))
>>   						sk->sk_error_report(sk);
>>   				}
>> +				spin_unlock(&po->bind_lock);
>> +
>>   				if (msg == NETDEV_UNREGISTER) {
>> -					po->ifindex = -1;
>> -					if (po->prot_hook.dev)
>> -						dev_put(po->prot_hook.dev);
>> -					po->prot_hook.dev = NULL;
>> +					sock_hold(sk);
>> +					call_rcu(&po->rcu,
>> +						 packet_dev_put_deferred);
>>   				}
>> -				spin_unlock(&po->bind_lock);
>>   			}
>
> Its not needed because you now take a reference on dev
> in packet_cached_dev_get()

That was also my first thought, but Salam pointed out to me, that in case
we have a situation such as ...

in packet_cached_dev_get():
     rcu_read_lock();
     dev = rcu_dereference(po->cached_dev);      in packet_notifier():
                                                 ---> CPU1: dev_put(po->prot_hook.dev);
---> CPU0:
     if (dev)
	dev_hold(dev);
     rcu_read_unlock();

... we could reach a refcount of 0, before we increase it back to 1. Not sure
if this can actually happen, maybe in preemptible RCU where read-side critical
sections to be preempted? So with this rather paranoid approach we make sure
to avoid such a situation as we wait a grace period when readers finished.

> Otherwise, patch looks good !

Thanks!

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox