* Re: [PATCH net-next 00/14] net: provide common RSS key infrastructure
From: David Miller @ 2014-11-16 21:03 UTC (permalink / raw)
To: edumazet
Cc: netdev, Thomas.Lendacky, ariel.elior, mchan, prashant,
rasesh.mody, sathya.perla, subbu.seetharaman, ajit.khaparde,
jesse.brandeburg, jeffrey.t.kirsher, amirv, sshah, sbhatewara
In-Reply-To: <1416147798-16561-1-git-send-email-edumazet@google.com>
From: Eric Dumazet <edumazet@google.com>
Date: Sun, 16 Nov 2014 06:23:04 -0800
> RSS (Receive Side Scaling) uses a 40 bytes key to provide hash for incoming
> packets to select appropriate incoming queue on NIC.
>
> Hash algo (Toeplitz) is also well known and documented by Microsoft
> (search for "Verifying the RSS Hash Calculation")
>
> Problem is that some drivers use a well known key.
> It makes very easy for attackers to target one particular RX queue,
> knowing that number of RX queues is a power of two, or at least some
> small number.
>
> Other drivers use a random value per port, making difficult
> tuning on bonding setups.
>
> Lets add a common infrastructure, so that host gets an unique
> RSS key, and drivers do not have to worry about this.
Looks good, series applied, thanks Eric.
^ permalink raw reply
* Re: [PATCH] qmi_wwan: Add support for HP lt4112 LTE/HSPA+ Gobi 4G Modem
From: David Miller @ 2014-11-16 21:05 UTC (permalink / raw)
To: bjorn; +Cc: mardnh, netdev
In-Reply-To: <87ppcneyy2.fsf@nemi.mork.no>
From: Bjørn Mork <bjorn@mork.no>
Date: Sun, 16 Nov 2014 20:18:45 +0100
> Martin Hauke <mardnh@gmx.de> writes:
>
>> Added the USB VID/PID for the HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e)
>>
>> Signed-off-by: Martin Hauke <mardnh@gmx.de>
>
> Acked-by: Bjørn Mork <bjorn@mork.no>
>
> Dave, please add this to the netdev stable queue as well.
Ok, applied and queued up for -stable.
Thanks.
^ permalink raw reply
* Re: pkt_sched: Fix qdisc len in qdisc_peek_dequeued() [61c9eaf9008] - question
From: Michal Soltys @ 2014-11-16 21:09 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Linux Netdev List
In-Reply-To: <1416161725.17262.92.camel@edumazet-glaptop2.roam.corp.google.com>
Actually, now that I analyzed it more carefully - not having that patch
would cause obvious issues (e.g. hfsc trying to activate already active
leaf in certain scenarios).
So, sorry for the noise.
PS.
Removed Jarek from CC, as that email is no longer valid.
^ permalink raw reply
* Re: [PATCH v2 net-next 6/7] bpf: allow eBPF programs to use maps
From: Alexei Starovoitov @ 2014-11-16 21:24 UTC (permalink / raw)
To: David Miller
Cc: Ingo Molnar, Andy Lutomirski, Daniel Borkmann,
Hannes Frederic Sowa, Eric Dumazet, Linux API,
Network Development, LKML
In-Reply-To: <20141116.140422.570375628237589645.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
On Sun, Nov 16, 2014 at 11:04 AM, David Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org> wrote:
> From: Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
> Date: Thu, 13 Nov 2014 17:36:49 -0800
>
>> +static u64 bpf_map_lookup_elem(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
>> +{
>> + /* verifier checked that R1 contains a valid pointer to bpf_map
>> + * and R2 points to a program stack and map->key_size bytes were
>> + * initialized
>> + */
>> + struct bpf_map *map = (struct bpf_map *) (unsigned long) r1;
>> + void *key = (void *) (unsigned long) r2;
>> + void *value;
>> +
>> + WARN_ON_ONCE(!rcu_read_lock_held());
>> +
>> + value = map->ops->map_lookup_elem(map, key);
>> +
>> + /* lookup() returns either pointer to element value or NULL
>> + * which is the meaning of PTR_TO_MAP_VALUE_OR_NULL type
>> + */
>> + return (unsigned long) value;
>> +}
>
> You should translate this into a true boolean '1' or '0' value so that
> kernel pointers don't propagate to the user or his eBPF programs.
that won't work. eBPF programs have to see all sorts of kernel
pointers. In this case it's a pointer to map element value
or NULL. There are pointers to stack, pointers to map root,
pointers to context, etc. Programs can read pointers from
other data structures. And in the case of tracing they can
pretty much access any kernel memory in read only way.
Just like 'perf probe' filters.
The requirement that _unprivileged_ programs should
not be able to pass all these pointers back to user is
well understood and was discussed in detail several
month back. It's verifier that will prevent leaking of
kernel addresses. Today, the whole thing is for root
only. When the infra is ready for non-root I will add
a pass to verifier, that will kick in only for unprivileged
programs. Verifier already tracks all pointers and
can prevent passing them to user. In this case
verifier knows that register R0 after a call to
bpf_map_lookup_elem() is
"either pointer to element value or NULL",
so it will prevent storing it into any memory or
doing arithmetic on it, so that user space cannot
see the pointer, whereas eBPF program can use
it to access map element value.
^ permalink raw reply
* Re: [PATCH v2 net-next 6/7] bpf: allow eBPF programs to use maps
From: David Miller @ 2014-11-16 21:34 UTC (permalink / raw)
To: ast-uqk4Ao+rVK5Wk0Htik3J/w
Cc: mingo-DgEjT+Ai2ygdnm+yROfE0A, luto-kltTT9wpgjJwATOyAt5JVQ,
dborkman-H+wXaHxf7aLQT0dZR+AlfA,
hannes-tFNcAqjVMyqKXQKiL6tip0B+6BGkLq7r,
edumazet-hpIqsD4AKlfQT0dZR+AlfA, linux-api-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CAMEtUuwrST6wGnBU6UU2NYEubskHYf1XZmZQpkgM+cUc8YD9OA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
From: Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
Date: Sun, 16 Nov 2014 13:24:53 -0800
> The requirement that _unprivileged_ programs should
> not be able to pass all these pointers back to user is
> well understood and was discussed in detail several
> month back. It's verifier that will prevent leaking of
> kernel addresses.
Ok, fair enough.
^ permalink raw reply
* [PATCH net-next] i40e: Reduce stack in i40e_dbg_dump_desc
From: Joe Perches @ 2014-11-16 22:12 UTC (permalink / raw)
To: Jeff Kirsher; +Cc: davem, Shannon Nelson, netdev
In-Reply-To: <5467C3BA.6090003@gmail.com>
Reduce stack use by using kmemdup and not using a very
large struct on stack.
In function ‘i40e_dbg_dump_desc’:
warning: the frame size of 8192 bytes is larger than 2048 bytes [-Wframe-larger-than=]
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 30 +++++++++++++++-----------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index a03f459..232783d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -773,7 +773,7 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n,
{
struct i40e_tx_desc *txd;
union i40e_rx_desc *rxd;
- struct i40e_ring ring;
+ struct i40e_ring *ring;
struct i40e_vsi *vsi;
int i;
@@ -792,29 +792,32 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n,
vsi_seid);
return;
}
- if (is_rx_ring)
- ring = *vsi->rx_rings[ring_id];
- else
- ring = *vsi->tx_rings[ring_id];
+
+ ring = kmemdup(is_rx_ring
+ ? vsi->rx_rings[ring_id] : vsi->tx_rings[ring_id],
+ sizeof(*ring), GFP_KERNEL);
+ if (!ring)
+ return;
+
if (cnt == 2) {
dev_info(&pf->pdev->dev, "vsi = %02i %s ring = %02i\n",
vsi_seid, is_rx_ring ? "rx" : "tx", ring_id);
- for (i = 0; i < ring.count; i++) {
+ for (i = 0; i < ring->count; i++) {
if (!is_rx_ring) {
- txd = I40E_TX_DESC(&ring, i);
+ txd = I40E_TX_DESC(ring, i);
dev_info(&pf->pdev->dev,
" d[%03i] = 0x%016llx 0x%016llx\n",
i, txd->buffer_addr,
txd->cmd_type_offset_bsz);
} else if (sizeof(union i40e_rx_desc) ==
sizeof(union i40e_16byte_rx_desc)) {
- rxd = I40E_RX_DESC(&ring, i);
+ rxd = I40E_RX_DESC(ring, i);
dev_info(&pf->pdev->dev,
" d[%03i] = 0x%016llx 0x%016llx\n",
i, rxd->read.pkt_addr,
rxd->read.hdr_addr);
} else {
- rxd = I40E_RX_DESC(&ring, i);
+ rxd = I40E_RX_DESC(ring, i);
dev_info(&pf->pdev->dev,
" d[%03i] = 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
i, rxd->read.pkt_addr,
@@ -823,26 +826,26 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n,
}
}
} else if (cnt == 3) {
- if (desc_n >= ring.count || desc_n < 0) {
+ if (desc_n >= ring->count || desc_n < 0) {
dev_info(&pf->pdev->dev,
"descriptor %d not found\n", desc_n);
return;
}
if (!is_rx_ring) {
- txd = I40E_TX_DESC(&ring, desc_n);
+ txd = I40E_TX_DESC(ring, desc_n);
dev_info(&pf->pdev->dev,
"vsi = %02i tx ring = %02i d[%03i] = 0x%016llx 0x%016llx\n",
vsi_seid, ring_id, desc_n,
txd->buffer_addr, txd->cmd_type_offset_bsz);
} else if (sizeof(union i40e_rx_desc) ==
sizeof(union i40e_16byte_rx_desc)) {
- rxd = I40E_RX_DESC(&ring, desc_n);
+ rxd = I40E_RX_DESC(ring, desc_n);
dev_info(&pf->pdev->dev,
"vsi = %02i rx ring = %02i d[%03i] = 0x%016llx 0x%016llx\n",
vsi_seid, ring_id, desc_n,
rxd->read.pkt_addr, rxd->read.hdr_addr);
} else {
- rxd = I40E_RX_DESC(&ring, desc_n);
+ rxd = I40E_RX_DESC(ring, desc_n);
dev_info(&pf->pdev->dev,
"vsi = %02i rx ring = %02i d[%03i] = 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
vsi_seid, ring_id, desc_n,
@@ -852,6 +855,7 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n,
} else {
dev_info(&pf->pdev->dev, "dump desc rx/tx <vsi_seid> <ring_id> [<desc_n>]\n");
}
+ kfree(ring);
}
/**
^ permalink raw reply related
* [PATCH net-next] netdevice: Neaten includes and forward declarations
From: Joe Perches @ 2014-11-16 22:21 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20141116.154923.1364136096942964102.davem@davemloft.net>
Use the appropriate #include path for neighbour.h and
add device.h which was indirectly #included by dmaengine.h
Remove unnecessary forward declaration of struct device;
Add comments for other forward struct declarations.
Signed-off-by: Joe Perches <joe@perches.com>
---
include/linux/netdevice.h | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fa6bd7d..33ce566 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -36,6 +36,7 @@
#include <linux/percpu.h>
#include <linux/rculist.h>
+#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/workqueue.h>
#include <linux/dynamic_queue_limits.h>
@@ -49,16 +50,15 @@
#include <net/netprio_cgroup.h>
#include <linux/netdev_features.h>
-#include <linux/neighbour.h>
+
+#include <uapi/linux/neighbour.h>
#include <uapi/linux/netdevice.h>
-struct netpoll_info;
-struct device;
-struct phy_device;
-/* 802.11 specific */
-struct wireless_dev;
-/* 802.15.4 specific */
-struct wpan_dev;
+/* forward struct type declarations */
+struct netpoll_info; /* for net_device_ops */
+struct phy_device; /* for net_device */
+struct wireless_dev; /* for net_device: 802.11 specific */
+struct wpan_dev; /* for net_device: 802.15.4 specific */
void netdev_set_default_ethtool_ops(struct net_device *dev,
const struct ethtool_ops *ops);
^ permalink raw reply related
* Re: [PATCH 1/1 net-next] wireless: remove unnecessary sizeof(u8)
From: Julian Calaby @ 2014-11-16 22:33 UTC (permalink / raw)
To: Fabian Frederick
Cc: linux-kernel@vger.kernel.org, Stefano Brivio, John W. Linville,
Johannes Berg, Emmanuel Grumbach, Intel Linux Wireless,
Larry Finger, Chaoming Li, linux-wireless, b43-dev, netdev
In-Reply-To: <1415998553-12636-1-git-send-email-fabf@skynet.be>
Hi Fabian,
On Sat, Nov 15, 2014 at 7:55 AM, Fabian Frederick <fabf@skynet.be> wrote:
> sizeof(u8) is always 1.
I thought that sizeof(*variable) was preferred over sizeof(type), so
shouldn't these be switched to that format instead?
(I know that this is all no-op, but it should reduce the potential for
highly unlikely bugs in the future. Also, the extra processing is
compile-time not run-time.)
Thanks,
Julian Calaby
> Signed-off-by: Fabian Frederick <fabf@skynet.be>
> ---
> drivers/net/wireless/b43/ppr.c | 2 +-
> drivers/net/wireless/iwlwifi/pcie/trans.c | 2 +-
> drivers/net/wireless/rtlwifi/efuse.c | 16 ++++++++--------
> 3 files changed, 10 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/wireless/b43/ppr.c b/drivers/net/wireless/b43/ppr.c
> index 9a77027..6bc1c6f 100644
> --- a/drivers/net/wireless/b43/ppr.c
> +++ b/drivers/net/wireless/b43/ppr.c
> @@ -28,7 +28,7 @@ void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr)
> memset(ppr, 0, sizeof(*ppr));
>
> /* Compile-time PPR check */
> - BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM * sizeof(u8));
> + BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM);
> }
>
> void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff)
> diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
> index 836725e..f016824 100644
> --- a/drivers/net/wireless/iwlwifi/pcie/trans.c
> +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
> @@ -1157,7 +1157,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
> trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds;
> if (trans_pcie->n_no_reclaim_cmds)
> memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
> - trans_pcie->n_no_reclaim_cmds * sizeof(u8));
> + trans_pcie->n_no_reclaim_cmds);
>
> trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k;
> if (trans_pcie->rx_buf_size_8k)
> diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c
> index 0b4082c..a3135c5 100644
> --- a/drivers/net/wireless/rtlwifi/efuse.c
> +++ b/drivers/net/wireless/rtlwifi/efuse.c
> @@ -251,8 +251,8 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf)
> }
>
> /* allocate memory for efuse_tbl and efuse_word */
> - efuse_tbl = kzalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE] *
> - sizeof(u8), GFP_ATOMIC);
> + efuse_tbl = kzalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE],
> + GFP_ATOMIC);
> if (!efuse_tbl)
> return;
> efuse_word = kzalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC);
> @@ -733,8 +733,8 @@ static int efuse_pg_packet_read(struct ieee80211_hw *hw, u8 offset, u8 *data)
> if (offset > 15)
> return false;
>
> - memset(data, 0xff, PGPKT_DATA_SIZE * sizeof(u8));
> - memset(tmpdata, 0xff, PGPKT_DATA_SIZE * sizeof(u8));
> + memset(data, 0xff, PGPKT_DATA_SIZE);
> + memset(tmpdata, 0xff, PGPKT_DATA_SIZE);
>
> while (continual && (efuse_addr < EFUSE_MAX_SIZE)) {
> if (readstate & PG_STATE_HEADER) {
> @@ -772,7 +772,7 @@ static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr,
> struct rtl_priv *rtlpriv = rtl_priv(hw);
> struct pgpkt_struct tmp_pkt;
> int dataempty = true;
> - u8 originaldata[8 * sizeof(u8)];
> + u8 originaldata[8];
> u8 badworden = 0x0F;
> u8 match_word_en, tmp_word_en;
> u8 tmpindex;
> @@ -881,7 +881,7 @@ static void efuse_write_data_case2(struct ieee80211_hw *hw, u16 *efuse_addr,
> struct pgpkt_struct tmp_pkt;
> u8 pg_header;
> u8 tmp_header;
> - u8 originaldata[8 * sizeof(u8)];
> + u8 originaldata[8];
> u8 tmp_word_cnts;
> u8 badworden = 0x0F;
>
> @@ -904,7 +904,7 @@ static void efuse_write_data_case2(struct ieee80211_hw *hw, u16 *efuse_addr,
>
> tmp_word_cnts = efuse_calculate_word_cnts(tmp_pkt.word_en);
>
> - memset(originaldata, 0xff, 8 * sizeof(u8));
> + memset(originaldata, 0xff, 8);
>
> if (efuse_pg_packet_read(hw, tmp_pkt.offset, originaldata)) {
> badworden = enable_efuse_data_write(hw,
> @@ -962,7 +962,7 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw,
> target_pkt.offset = offset;
> target_pkt.word_en = word_en;
>
> - memset(target_pkt.data, 0xFF, 8 * sizeof(u8));
> + memset(target_pkt.data, 0xFF, 8);
>
> efuse_word_enable_data_read(word_en, data, target_pkt.data);
> target_word_cnts = efuse_calculate_word_cnts(target_pkt.word_en);
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Julian Calaby
Email: julian.calaby@gmail.com
Profile: http://www.google.com/profiles/julian.calaby/
^ permalink raw reply
* [PATCH net-next] enic: support skb->xmit_more
From: Govindarajulu Varadarajan @ 2014-11-16 23:04 UTC (permalink / raw)
To: davem, netdev, ssujith, benve; +Cc: Govindarajulu Varadarajan
Update posted_index only when skb->xmit_more is 0 or tx queue is full.
Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com>
---
drivers/net/ethernet/cisco/enic/enic_main.c | 8 ++++++--
drivers/net/ethernet/cisco/enic/vnic_wq.h | 20 +++++++++++---------
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index 5afe360..52bccfd 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -533,6 +533,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
struct vnic_wq *wq;
unsigned long flags;
unsigned int txq_map;
+ struct netdev_queue *txq;
if (skb->len <= 0) {
dev_kfree_skb_any(skb);
@@ -541,6 +542,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
txq_map = skb_get_queue_mapping(skb) % enic->wq_count;
wq = &enic->wq[txq_map];
+ txq = netdev_get_tx_queue(netdev, skb_get_queue_mapping(skb));
/* Non-TSO sends must fit within ENIC_NON_TSO_MAX_DESC descs,
* which is very likely. In the off chance it's going to take
@@ -558,7 +560,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
if (vnic_wq_desc_avail(wq) <
skb_shinfo(skb)->nr_frags + ENIC_DESC_MAX_SPLITS) {
- netif_tx_stop_queue(netdev_get_tx_queue(netdev, txq_map));
+ netif_tx_stop_queue(txq);
/* This is a hard error, log it */
netdev_err(netdev, "BUG! Tx ring full when queue awake!\n");
spin_unlock_irqrestore(&enic->wq_lock[txq_map], flags);
@@ -568,7 +570,9 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
enic_queue_wq_skb(enic, wq, skb);
if (vnic_wq_desc_avail(wq) < MAX_SKB_FRAGS + ENIC_DESC_MAX_SPLITS)
- netif_tx_stop_queue(netdev_get_tx_queue(netdev, txq_map));
+ netif_tx_stop_queue(txq);
+ if (!skb->xmit_more || netif_xmit_stopped(txq))
+ vnic_wq_doorbell(wq);
spin_unlock_irqrestore(&enic->wq_lock[txq_map], flags);
diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.h b/drivers/net/ethernet/cisco/enic/vnic_wq.h
index 2c6c708..816f1ad 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_wq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_wq.h
@@ -104,6 +104,17 @@ static inline void *vnic_wq_next_desc(struct vnic_wq *wq)
return wq->to_use->desc;
}
+static inline void vnic_wq_doorbell(struct vnic_wq *wq)
+{
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(wq->to_use->index, &wq->ctrl->posted_index);
+}
+
static inline void vnic_wq_post(struct vnic_wq *wq,
void *os_buf, dma_addr_t dma_addr,
unsigned int len, int sop, int eop,
@@ -122,15 +133,6 @@ static inline void vnic_wq_post(struct vnic_wq *wq,
buf->wr_id = wrid;
buf = buf->next;
- if (eop) {
- /* Adding write memory barrier prevents compiler and/or CPU
- * reordering, thus avoiding descriptor posting before
- * descriptor is initialized. Otherwise, hardware can read
- * stale descriptor fields.
- */
- wmb();
- iowrite32(buf->index, &wq->ctrl->posted_index);
- }
wq->to_use = buf;
wq->ring.desc_avail -= desc_skip_cnt;
--
2.1.3
^ permalink raw reply related
* [PATCH] fix #51791 - bug? mac 00:00:00:00:00:00 with natsemi DP83815 after driver load
From: Roland Kletzing @ 2014-11-17 0:17 UTC (permalink / raw)
To: netdev
In-Reply-To: <trinity-1cec0308-6242-413f-a63f-b09cded30a4f-1416096326518@3capp-webde-bs03>
This one should fix Bugzilla #51791 (details below).
Natsemi driver does not read MAC correctly from eeprom, while natsemi-diag from nictools-pci does. Apparently, tt`s a timing issue in the kernel driver.
According to ftp://ftp.gwdg.de/pub/linux/misc/donald.becker/diag/natsemi-diag.c , eeprom_delay(ee_addr) is defined as follows:
/* Delay between EEPROM clock transitions.
This flushes the write buffer to prevent quick double-writes.
*/
#define eeprom_delay(ee_addr) inl(ee_addr); inl(ee_addr)
while in the natsemi linux kernel driver, the delay is done this way :
#define eeprom_delay(ee_addr) readl(ee_addr)
, which results in the MAC being all zero`s.
So i simply added a second readl() to increase delay (instead of turning into inl() as proposed before). This may look a little bit ugly, but it`s fixing the problem for me.
I´m not sure how many natsemi users being left on this planet (probably few), but i guess this change does not do any harm on platforms where the driver does not behave buggy, so please consider adding it to mainline/stable/longterm.
Signed-off-by: Roland Kletzing <devzero@web.de>
---
drivers/net/ethernet/natsemi/natsemi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c
index b83f7c0..246bb91 100644
--- a/drivers/net/ethernet/natsemi/natsemi.c
+++ b/drivers/net/ethernet/natsemi/natsemi.c
@@ -987,7 +987,7 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
deprecated.
*/
-#define eeprom_delay(ee_addr) readl(ee_addr)
+#define eeprom_delay(ee_addr) readl(ee_addr); readl(ee_addr)
#define EE_Write0 (EE_ChipSelect)
#define EE_Write1 (EE_ChipSelect | EE_DataIn)
--
regards
Roland
> Gesendet: Sonntag, 16. November 2014 um 01:05 Uhr
> Von: devzero@web.de
> An: linux-kernel@vger.kernel.org
> Betreff: re: bug? mac 00:00:00:00:00:00 with natsemi DP83815 after driver load
>
> forwarding to lkml, as no response on netdev list so far.
>
> maybe someone has a clue how to properly fix this timing issue. the remaining question is about how to correctly replace readl() with inl() to make it compile cleanly or how eeprom_delay() is being done correctly. inl() seems to be slower to complete which seems to make the driver work, but it seems it needs an I/O port as param but not an memory-adress.
>
> sorry, i`m not good at programming, but now as the problem is "basically" fixed there are just some tiny bits missing for a proper fix. i´m unsure if typecasting ee_addr is the right thing to do (i think it`s not) and if a patch with such typecast would have any chance for being accepted.
>
> ----------------------------
> hi,
> as i`m doing a little project with this older devices, i have come across this issue again and had some fun to dig deeper into it.
>
> it`s a little bit academic, as i can do ifconfig eth0 hw ether..... as a workaround, but it sucks to hack that into startup scripts and i also have seen udev not playing nicely with it.
>
> apparently the problem is being caused by a timing issue in the natsemi driver.
>
> i added some debug printk`s in natsemi.c -> eeprom_read() after each occurrence of eeprom_delay(ee_addr); , and the problem went away.
>
> there is a hint about timing sensitivity in the code:
>
> /* Delay between EEPROM clock transitions.
> No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
> a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
> made udelay() unreliable.
> The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
> deprecated.
> */
>
> looking at the source of natsemi-diag.c made me wonder why that utility is using
>
> #define eeprom_delay(ee_addr) inl(ee_addr)
>
> instead of
>
> #define eeprom_delay(ee_addr) readl(ee_addr)
>
> and apparently, that also fixes the problem (but gives a compile warning):
>
> drivers/net/ethernet/natsemi/natsemi.c: In function âeeprom_readâ:
> drivers/net/ethernet/natsemi/natsemi.c:1019:3: warning: passing argument 1 of âinlâ makes integer from pointer without a cast [enabled by default]
> In file included from include/linux/io.h:22:0,
> from include/linux/pci.h:54,
> from drivers/net/ethernet/natsemi/natsemi.c:38:
>
>
> looking at a more recent version of natsemi-diag.c , i even found this one:
>
> ftp://ftp.gwdg.de/pub/linux/misc/donald.becker/diag/natsemi-diag.c
>
> /* Delay between EEPROM clock transitions.
> This flushes the write buffer to prevent quick double-writes.
> */
> #define eeprom_delay(ee_addr) inl(ee_addr); inl(ee_addr)
>
> The question is how to make a proper fix, as i don`t know what to pass to inl() , as it seems it should not get an mmapped adress but an i/o port instead !?
>
> "The in*() functions return data read from the specified I/O port"
>
> "The read*() functions read data from device memory previously mapped by map_memory()"
>
> regards
> roland
>
> ps: CC driver maintainer from Kernel Maintainers file.
>
>
>
> Roland Kletzing | 17 Dec 13:38 2012
> bug? mac 00:00:00:00:00:00 with natsemi DP83815 after driver load
>
> Hello,
> i recently played with my older evo t20/wyse 3235le thin clients and flashed
> a linux kernel into those, apparently there seems an issue with the natsemi
> driver.
>
> after driver load (natsemi.ko) eth0 has no valid mac adress, dmesg and
> ifconfig shows just zero`s: 00:00:00:00:00:00.
>
> despite that , the nic is working fine for me (in this test setup i set the
> mac manually: ifconfig eth0 hw ether de:ad:be:ef:be:ef )
>
> apparently, the driver fails to read the proper mac from the eeprom, as
> "natsemi-diag -ee" (from nictools-pci in debian squeeze) shows, that there
> is a valid "Ethernet MAC Station Address" stored inside the eeprom. (see
> below)
>
> looks like a driver bug !?
> does anybody have a clue what`s going wrong here?
>
> regards
> roland
>
> #lspci
>
> 00:00.0 Host bridge: Cyrix Corporation PCI Master
> 00:0f.0 Ethernet controller: National Semiconductor Corporation DP83815
> (MacPhyter) Ethernet Controller
> 00:12.0 ISA bridge: Cyrix Corporation 5530 Legacy [Kahlua] (rev 30)
> 00:12.1 Bridge: Cyrix Corporation 5530 SMI [Kahlua]
> 00:12.2 IDE interface: Cyrix Corporation 5530 IDE [Kahlua]
> 00:12.3 Multimedia audio controller: Cyrix Corporation 5530 Audio [Kahlua]
> 00:12.4 VGA compatible controller: Cyrix Corporation 5530 Video [Kahlua]
> 00:13.0 USB Controller: Compaq Computer Corporation ZFMicro Chipset USB (rev
> 06)
>
> #dmesg |egrep "natsemi|eth"
> natsemi dp8381x driver, version 2.1, Sept 11, 2006
> natsemi 0000:00:0f.0: setting latency timer to 64
> natsemi eth0: NatSemi DP8381[56] at 0x4010000 (0000:00:0f.0),
> 00:00:00:00:00:00, IRQ 10, port TP.
> eth0: DSPCFG accepted after 0 usec.
> eth0: link up.
> eth0: Setting full-duplex based on negotiated link capability.
>
> #natsemi-diag -aa
> natsemi-diag.c:v2.08 2/28/2005 Donald Becker (becker <at> scyld.com)
> http://www.scyld.com/diag/index.html
> Index #1: Found a NatSemi DP83815 adapter at 0xf800.
> Natsemi 83815 series with station address de:ad:be:ef:be:ef
> Transceiver setting Autonegotation advertise 10/100 Mbps half and full
> duplex.
> This device appears to be active, so some registers will not be read.
> To see all register values use the '-f' flag.
> NatSemi DP83815 chip registers at 0xf800
> 0x000: 00000004 e805e000 00000002 00000000 ******** 00f1cd65 00000001
> 00000000
> 0x020: 03abd200 d0f01002 00000000 00000000 03abd000 18700010 00000000
> 00000000
> 0x040: ******** 00200000 00000004 0000efbe ffff000b 30303030 00000403
> 00000000
> 0x060: ******** ******** ******** ******** ******** ******** ********
> ********
> 0x080: 00003100 0000786d 00002000 00005c21 000005e1 000045e1 00000005
> 00002801
> 0x0A0: ******** ******** ******** ******** ******** ******** ********
> ********
> 0x0C0: 00000615 00000002 00000000 00000000 00000000 00000000 00000100
> 00000030
> 0x0E0: 00000000 000000bf 00000804 00008200 00000000 00000000 00000000
> 00000000
> Interrupt sources are pending (00000200).
> Tx queue emptied indication.
> Receive mode is 0xc8200000: Normal unicast and hashed multicast.
> Rx filter contents: adde efbe efbe 0000 0000 0000 0000 0000
>
> #natsemi-diag -ee
> natsemi-diag.c:v2.08 2/28/2005 Donald Becker (becker <at> scyld.com)
> http://www.scyld.com/diag/index.html
> Index #1: Found a NatSemi DP83815 adapter at 0xf800.
> Natsemi 83815 series with station address de:ad:be:ef:be:ef
> Transceiver setting Autonegotation advertise 10/100 Mbps half and full
> duplex.
> EEPROM address length 6, 64 words.
> EEPROM contents (64 words):
> 0x00: 100b 0020 0b34 41fb 0000 0000 0000 4000
> 0x08: 0d32 dff4 1905 aa48 0000 0000 129c 4c4c
> 0x10: ca52 2ccc 0cb2 9c6c 0c6c 8c0c 2020 6080
> 0x18: 0800 0000 0000 0000 0000 0000 0000 0000
> 0x20: 0000 0000 0000 0000 0000 0000 0000 0000
> 0x28: 0000 0000 0000 0000 0000 0000 0000 0000
> 0x30: 0000 0000 0000 0000 0000 0000 0000 0000
> 0x38: 0000 0000 0000 0000 0000 0000 0000 e418
> Decoded EEPROM contents:
> PCI Subsystem IDs -- Vendor 0x100b, Device 0x0020.
> PCI timer settings -- minimum grant 11, maximum latency 52.
> Ethernet MAC Station Address 00:80:64:1a:e8:bf.
> Wake-On-LAN password 00:00:00:00:00:00.
> Transceiver setting 0x--f-: advertise 10/100 Mbps half and full duplex.
> Flow control enabled.
> EEPROM active region checksum read as aa48, vs aa48 calculated value.
^ permalink raw reply related
* Re: [PATCH] [bonding]: clear header_ops when last slave detached
From: Wengang @ 2014-11-17 1:19 UTC (permalink / raw)
To: netdev; +Cc: Wengang Wang
In-Reply-To: <1415845156-15461-1-git-send-email-wen.gang.wang@oracle.com>
Hi,
Could anybody please review this patch?
thanks,
wengang
于 2014年11月13日 10:19, Wengang Wang 写道:
> When last slave of a bonding master is removed, the bonding then does not work.
> When packet_snd is called against with a master net_device, it accesses
> header_ops. In case the header_ops is not valid any longer(say module unloaded)
> it will then access an invalid memory address.
> This patch try to fix this issue by clearing header_ops when last slave
> detached.
>
> Signed-off-by: Wengang Wang <wen.gang.wang@oracle.com>
> ---
> drivers/net/bonding/bond_main.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index c9ac06c..84a34fc 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -1728,6 +1728,8 @@ static int __bond_release_one(struct net_device *bond_dev,
> unblock_netpoll_tx();
> synchronize_rcu();
> bond->slave_cnt--;
> + if (!bond->slave_cnt)
> + bond->dev->header_ops = NULL;
>
> if (!bond_has_slaves(bond)) {
> call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
^ permalink raw reply
* ipx: fix locking regression in ipx_sendmsg and ipx_recvmsg
From: Jiri Bohac @ 2014-11-17 1:34 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, netdev; +Cc: Arnd Bergmann
This fixes an old regression introduced by commit
b0d0d915 (ipx: remove the BKL).
When a recvmsg syscall blocks waiting for new data, no data can be sent on the
same socket with sendmsg because ipx_recvmsg() sleeps with the socket locked.
This breaks mars-nwe (NetWare emulator):
- the ncpserv process reads the request using recvmsg
- ncpserv forks and spawns nwconn
- ncpserv calls a (blocking) recvmsg and waits for new requests
- nwconn deadlocks in sendmsg on the same socket
Commit b0d0d915 has simply replaced BKL locking with
lock_sock/release_sock. Unlike now, BKL got unlocked while
sleeping, so a blocking recvmsg did not block a concurrent
sendmsg.
Similarly, a potentially sleeping sendmsg() could block calls to recvmsg().
Only keep the socket locked while actually working with the socket data and
release it prior to calling skb_recv_datagram() / ipxitf_send().
Signed-off-by: Jiri Bohac <jbohac@suse.cz>
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 91729b8..1e0d796 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1703,11 +1703,11 @@ static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock,
/* if (sk->sk_zapped)
return -EIO; */ /* Socket not bound */
if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
- goto out;
+ goto out_release;
/* Max possible packet size limited by 16 bit pktsize in header */
if (len >= 65535 - sizeof(struct ipxhdr))
- goto out;
+ goto out_release;
if (usipx) {
if (!ipxs->port) {
@@ -1718,24 +1718,24 @@ static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock,
#ifdef CONFIG_IPX_INTERN
rc = -ENETDOWN;
if (!ipxs->intrfc)
- goto out; /* Someone zonked the iface */
+ goto out_release; /* Someone zonked the iface */
memcpy(uaddr.sipx_node, ipxs->intrfc->if_node,
IPX_NODE_LEN);
#endif
rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
sizeof(struct sockaddr_ipx));
if (rc)
- goto out;
+ goto out_release;
}
rc = -EINVAL;
if (msg->msg_namelen < sizeof(*usipx) ||
usipx->sipx_family != AF_IPX)
- goto out;
+ goto out_release;
} else {
rc = -ENOTCONN;
if (sk->sk_state != TCP_ESTABLISHED)
- goto out;
+ goto out_release;
usipx = &local_sipx;
usipx->sipx_family = AF_IPX;
@@ -1745,12 +1745,16 @@ static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock,
memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN);
}
+ /* releases sk */
rc = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len,
flags & MSG_DONTWAIT);
if (rc >= 0)
rc = len;
-out:
+ goto out;
+
+out_release:
release_sock(sk);
+out:
return rc;
}
@@ -1776,20 +1780,21 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
#ifdef CONFIG_IPX_INTERN
rc = -ENETDOWN;
if (!ipxs->intrfc)
- goto out; /* Someone zonked the iface */
+ goto out_release; /* Someone zonked the iface */
memcpy(uaddr.sipx_node, ipxs->intrfc->if_node, IPX_NODE_LEN);
#endif /* CONFIG_IPX_INTERN */
rc = __ipx_bind(sock, (struct sockaddr *)&uaddr,
sizeof(struct sockaddr_ipx));
if (rc)
- goto out;
+ goto out_release;
}
rc = -ENOTCONN;
if (sock_flag(sk, SOCK_ZAPPED))
- goto out;
+ goto out_release;
+ release_sock(sk);
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &rc);
if (!skb) {
@@ -1807,8 +1812,10 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
rc = skb_copy_datagram_iovec(skb, sizeof(struct ipxhdr), msg->msg_iov,
copied);
- if (rc)
- goto out_free;
+ if (rc) {
+ skb_free_datagram(sk, skb);
+ goto out;
+ }
if (skb->tstamp.tv64)
sk->sk_stamp = skb->tstamp;
@@ -1822,11 +1829,11 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_namelen = sizeof(*sipx);
}
rc = copied;
+ goto out;
-out_free:
- skb_free_datagram(sk, skb);
-out:
+out_release:
release_sock(sk);
+out:
return rc;
}
diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c
index 67e7ad3..2f082af 100644
--- a/net/ipx/ipx_route.c
+++ b/net/ipx/ipx_route.c
@@ -163,6 +163,7 @@ int ipxrtr_route_skb(struct sk_buff *skb)
/*
* Route an outgoing frame from a socket.
+ * Expects sk to be locked and releases it before returning.
*/
int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
struct iovec *iov, size_t len, int noblock)
@@ -184,7 +185,7 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
rt = ipxrtr_lookup(usipx->sipx_network);
rc = -ENETUNREACH;
if (!rt)
- goto out;
+ goto out_release;
intrfc = rt->ir_intrfc;
}
@@ -242,12 +243,16 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
else
ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));
+ release_sock(sk);
rc = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
rt->ir_router_node : ipx->ipx_dest.node);
+ goto out;
out_put:
ipxitf_put(intrfc);
if (rt)
ipxrtr_put(rt);
+out_release:
+ release_sock(sk);
out:
return rc;
}
--
Jiri Bohac <jbohac@suse.cz>
SUSE Labs, SUSE CZ
^ permalink raw reply related
* Re: [PATCH] bridge: fix netfilter/NF_BR_LOCAL_OUT for own, locally generated queries
From: Linus Lüssing @ 2014-11-17 2:08 UTC (permalink / raw)
To: David S. Miller
Cc: Linus Lüssing, netdev, bridge, Stephen Hemminger, Herbert Xu,
linux-kernel
In-Reply-To: <20141102220145.GA25604@gondor.apana.org.au>
On Mon, Nov 03, 2014 at 06:01:46AM +0800, Herbert Xu wrote:
> On Mon, Sep 22, 2014 at 01:32:44AM +0200, Linus Lüssing wrote:
> > Signed-off-by: Linus Lüssing <linus.luessing@web.de>
>
> Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Hi David,
are there any unanswered questions left?
Cheers, Linus
^ permalink raw reply
* Re: [PATCH] bridge: fix netfilter/NF_BR_LOCAL_OUT for own, locally generated queries
From: Herbert Xu @ 2014-11-17 2:33 UTC (permalink / raw)
To: Linus Lüssing
Cc: Stephen Hemminger, netdev, bridge, David S. Miller, linux-kernel
In-Reply-To: <20141117020816.GD2469@odroid>
On Mon, Nov 17, 2014 at 03:08:16AM +0100, Linus Lüssing wrote:
> On Mon, Nov 03, 2014 at 06:01:46AM +0800, Herbert Xu wrote:
> > On Mon, Sep 22, 2014 at 01:32:44AM +0200, Linus Lüssing wrote:
> > > Signed-off-by: Linus Lüssing <linus.luessing@web.de>
> >
> > Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
>
> Hi David,
>
> are there any unanswered questions left?
Please resubmit your patch as it's no longer in David's queue.
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* linux-next: build failure after merge of the net-next tree
From: Stephen Rothwell @ 2014-11-17 2:34 UTC (permalink / raw)
To: David Miller, netdev
Cc: linux-next, linux-kernel, Jarno Rajahalme, Pravin B Shelar
[-- Attachment #1: Type: text/plain, Size: 2933 bytes --]
Hi all,
After merging the net-next tree, today's linux-next build (x86_64 allmodconfig)
failed like this:
In file included from include/linux/printk.h:6:0,
from include/linux/kernel.h:13,
from net/openvswitch/flow.h:23,
from net/openvswitch/flow_netlink.c:21:
net/openvswitch/flow_netlink.c: In function 'ovs_key_from_nlattrs':
include/linux/kern_levels.h:4:18: error: called object is not a function or function pointer
#define KERN_SOH "\001" /* ASCII Start Of Header */
^
include/linux/kern_levels.h:13:19: note: in expansion of macro 'KERN_SOH'
#define KERN_INFO KERN_SOH "6" /* informational */
^
include/linux/printk.h:247:9: note: in expansion of macro 'KERN_INFO'
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
^
net/openvswitch/datapath.h:205:3: note: in expansion of macro 'pr_info'
pr_info("netlink: " fmt "\n", ##__VA_ARGS__); \
^
net/openvswitch/flow_netlink.c:777:4: note: in expansion of macro 'OVS_NLERR'
OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n",
^
net/openvswitch/datapath.h:205:27: error: expected ')' before string constant
pr_info("netlink: " fmt "\n", ##__VA_ARGS__); \
^
net/openvswitch/flow_netlink.c:19:41: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^
net/openvswitch/datapath.h:205:3: note: in expansion of macro 'pr_info'
pr_info("netlink: " fmt "\n", ##__VA_ARGS__); \
^
net/openvswitch/flow_netlink.c:777:4: note: in expansion of macro 'OVS_NLERR'
OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n",
^
Caused by commit 05da5898a96c ("openvswitch: Add support for
OVS_FLOW_ATTR_PROBE") interacting with commit fecaef85f718
("openvswitch: Validate IPv6 flow key and mask values") from the net
tree.
I applied the following merge fix patch:
From: Stephen Rothwell <sfr@canb.auug.org.au>
Date: Mon, 17 Nov 2014 13:31:33 +1100
Subject: [PATCH] openvswitch: fix up for OVS_NLERR API change
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
net/openvswitch/flow_netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 259b2f1ebd0d..45bdcf596414 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -774,7 +774,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
}
if (ipv6_key->ipv6_label & htonl(0xFFF00000)) {
- OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n",
+ OVS_NLERR(log, "IPv6 flow label %x is out of range (max=%x).\n",
ntohl(ipv6_key->ipv6_label), (1 << 20) - 1);
return -EINVAL;
}
--
2.1.3
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply related
* Re: [PATCH 3/3] sh_eth: Fix dma mapping issue
From: Simon Horman @ 2014-11-17 4:09 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Yoshihiro Kaneko, netdev, David S. Miller, Magnus Damm, linux-sh
In-Reply-To: <54653948.5070606@cogentembedded.com>
Hi Sergei,
On Fri, Nov 14, 2014 at 02:05:44AM +0300, Sergei Shtylyov wrote:
> On 11/13/2014 10:05 AM, Yoshihiro Kaneko wrote:
>
> >From: Mitsuhiro Kimura <mitsuhiro.kimura.kc@renesas.com>
>
> >When CONFIG_DMA_API_DEBUG=y, many DMA error messages reports.
> >In order to use DMA debug, This patch fix following issues.
>
> >Issue 1:
> >If dma_mapping_error function is not called appropriately after
> >DMA mapping, DMA debug will report error message when DMA unmap
> >function is called.
>
> >Issue 2:
> >If skb_reserve function is called after DMA mapping, the relationship
> >between mapping addr and mapping size will be broken.
> >In this case, DMA debug will report error messages when DMA sync
> >function and DMA unmap function are called.
>
> >Issue 3:
> >If the size of frame data is less than ETH_ZLEN, the size is resized
> >to ETH_ZLEN after DMA map function is called.
> >In the TX skb freeing function, dma unmap function is called with that
> >resized value. So, unmap size error will reported.
>
> >Issue 4:
> >In the rx function, DMA map function is called without DMA unmap function
> >is called for RX skb reallocating.
> >It will case the DMA debug error that number of debug entry is full and
> >DMA debug logic is stopped.
>
> The rule of thumb is "fix one issue per patch". Please split accordingly.
>
> >Signed-off-by: Mitsuhiro Kimura <mitsuhiro.kimura.kc@renesas.com>
> >Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com>
>
> Thanks for beating me to it. Fixing these issues has been on my agenda
> for a long time... :-)
as this patch is somewhat involved and as you have pointed out needs a bit
of work I'm wondering if you could take it over.
> >---
> > drivers/net/ethernet/renesas/sh_eth.c | 26 +++++++++++++++++++++++---
> > 1 file changed, 23 insertions(+), 3 deletions(-)
>
> >diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
> >index 0e4a407..23318cf 100644
> >--- a/drivers/net/ethernet/renesas/sh_eth.c
> >+++ b/drivers/net/ethernet/renesas/sh_eth.c
> >@@ -1136,6 +1136,11 @@ static void sh_eth_ring_format(struct net_device *ndev)
> > dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length,
> > DMA_FROM_DEVICE);
> > rxdesc->addr = virt_to_phys(skb->data);
>
> Can't we get rid of these bogus virt_to_phys() calls, while at it?
> dma_map_single() returns a DMA address, no?
>
> >+ if (dma_mapping_error(&ndev->dev, rxdesc->addr)) {
> >+ dev_kfree_skb(mdp->rx_skbuff[i]);
> >+ mdp->rx_skbuff[i] = NULL;
> >+ break;
> >+ }
> > rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
> >
> > /* Rx descriptor address set */
> >@@ -1364,7 +1369,7 @@ static int sh_eth_txfree(struct net_device *ndev)
> > if (mdp->tx_skbuff[entry]) {
> > dma_unmap_single(&ndev->dev, txdesc->addr,
> > txdesc->buffer_length, DMA_TO_DEVICE);
> >- dev_kfree_skb_irq(mdp->tx_skbuff[entry]);
> >+ dev_kfree_skb_any(mdp->tx_skbuff[entry]);
>
> Hm, I'm not sure where is this described in the changelog...
>
> > mdp->tx_skbuff[entry] = NULL;
> > free_num++;
> > }
> >@@ -1466,11 +1471,19 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
> > if (skb == NULL)
> > break; /* Better luck next round. */
> > sh_eth_set_receive_align(skb);
> >+ dma_unmap_single(&ndev->dev, rxdesc->addr,
> >+ rxdesc->buffer_length,
> >+ DMA_FROM_DEVICE);
> > dma_map_single(&ndev->dev, skb->data,
> > rxdesc->buffer_length, DMA_FROM_DEVICE);
> >
> > skb_checksum_none_assert(skb);
> > rxdesc->addr = virt_to_phys(skb->data);
>
> Likewise, can we get rid of this bogu?
>
> >+ if (dma_mapping_error(&ndev->dev, rxdesc->addr)) {
> >+ dev_kfree_skb_any(mdp->rx_skbuff[entry]);
> >+ mdp->rx_skbuff[entry] = NULL;
> >+ break;
> >+ }
> > }
> > if (entry >= mdp->num_rx_ring - 1)
> > rxdesc->status |=
> >@@ -2104,12 +2117,18 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> > if (!mdp->cd->hw_swap)
> > sh_eth_soft_swap(phys_to_virt(ALIGN(txdesc->addr, 4)),
> > skb->len + 2);
> >- txdesc->addr = dma_map_single(&ndev->dev, skb->data, skb->len,
> >- DMA_TO_DEVICE);
> > if (skb->len < ETH_ZLEN)
> > txdesc->buffer_length = ETH_ZLEN;
> > else
> > txdesc->buffer_length = skb->len;
> >+ txdesc->addr = dma_map_single(&ndev->dev, skb->data,
> >+ txdesc->buffer_length,
> >+ DMA_TO_DEVICE);
> >+ if (dma_mapping_error(&ndev->dev, txdesc->addr)) {
> >+ dev_kfree_skb_any(mdp->tx_skbuff[entry]);
> >+ mdp->tx_skbuff[entry] = NULL;
> >+ goto out;
>
> Why not just *return*?!
>
> [...]
>
> WBR, Sergei
>
^ permalink raw reply
* Re: [PATCH 4/4] rhashtable: Add parent argument to mutex_is_held
From: Herbert Xu @ 2014-11-17 4:46 UTC (permalink / raw)
To: Josh Triplett; +Cc: tgraf, netdev, eric.dumazet, paulmck
In-Reply-To: <7DA2F716-1E87-4AC8-A18F-CE5BFB6E6F81@joshtriplett.org>
On Sat, Nov 15, 2014 at 06:37:26PM -0800, Josh Triplett wrote:
> On November 15, 2014 6:22:27 PM PST, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> >Josh Triplett <josh@joshtriplett.org> wrote:
> >>
> >> - Set up the new empty table with the new set of hash parameters.
> >> - synchronize_rcu(). Readers will now search both old and new
> >tables.
> >> - Peel nodes off the ends of the old hash table and add them to the
> >new
> >
> >We currently use a singly linked list in rhashtable. Peeling nodes
> >off the end would mean upgrading to a doubly linked list, which is
> >no different than keeping two lists in terms of cache footprint, no?
>
> No, since each pass just handles one set of nodes from each bucket anyway, you can just do a bit more work in the rehasher instead.
OK let me see if I could implement something like that in rhashtable.
Thanks,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: /proc/net/sockstat invalid memory accounting or memory leak in latest kernels? (trying to debug)
From: Eric Dumazet @ 2014-11-17 5:01 UTC (permalink / raw)
To: Denys Fedoryshchenko; +Cc: Neal Cardwell, Yuchung Cheng, netdev
In-Reply-To: <1416168961.17262.96.camel@edumazet-glaptop2.roam.corp.google.com>
On Sun, 2014-11-16 at 12:16 -0800, Eric Dumazet wrote:
> Thanks Denys !
>
> Could you try following patch ?
>
> Thanks !
Hmm.... I have an updated patch, sorry.
(A memcpy_fromiovec() has to be memcpy_fromiovecend() )
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a3d453b94747..c2bbfcd9c0db 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2998,7 +2998,7 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
{
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_fastopen_request *fo = tp->fastopen_req;
- int syn_loss = 0, space, i, err = 0, iovlen = fo->data->msg_iovlen;
+ int syn_loss = 0, space, err = 0;
struct sk_buff *syn_data = NULL, *data;
unsigned long last_syn_loss = 0;
@@ -3031,25 +3031,19 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
/* limit to order-0 allocations */
space = min_t(size_t, space, SKB_MAX_HEAD(MAX_TCP_HEADER));
- syn_data = skb_copy_expand(syn, MAX_TCP_HEADER, space,
- sk->sk_allocation);
- if (syn_data == NULL)
+ syn_data = sk_stream_alloc_skb(sk, space, sk->sk_allocation);
+ if (!syn_data)
goto fallback;
- for (i = 0; i < iovlen && syn_data->len < space; ++i) {
- struct iovec *iov = &fo->data->msg_iov[i];
- unsigned char __user *from = iov->iov_base;
- int len = iov->iov_len;
-
- if (syn_data->len + len > space)
- len = space - syn_data->len;
- else if (i + 1 == iovlen)
- /* No more data pending in inet_wait_for_connect() */
- fo->data = NULL;
+ syn_data->ip_summed = CHECKSUM_PARTIAL;
+ memcpy(syn_data->cb, syn->cb, sizeof(syn->cb));
+ if (memcpy_fromiovecend(skb_put(syn_data, space),
+ fo->data->msg_iov, 0, space))
+ goto fallback;
- if (skb_add_data(syn_data, from, len))
- goto fallback;
- }
+ /* No more data pending in inet_wait_for_connect() */
+ if (space == fo->size)
+ fo->data = NULL;
/* Queue a data-only packet after the regular SYN for retransmission */
data = pskb_copy(syn_data, sk->sk_allocation);
@@ -3101,13 +3095,10 @@ int tcp_connect(struct sock *sk)
return 0;
}
- buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
- if (unlikely(buff == NULL))
+ buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
+ if (unlikely(!buff))
return -ENOBUFS;
- /* Reserve space for headers. */
- skb_reserve(buff, MAX_TCP_HEADER);
-
tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
tp->retrans_stamp = tcp_time_stamp;
tcp_connect_queue_skb(sk, buff);
^ permalink raw reply related
* linux-next: manual merge of the tiny tree with the net-next tree
From: Stephen Rothwell @ 2014-11-17 5:35 UTC (permalink / raw)
To: Josh Triplett, David Miller, netdev
Cc: linux-next, linux-kernel, Iulia Manda, Pravin B Shelar
[-- Attachment #1: Type: text/plain, Size: 907 bytes --]
Hi Josh,
Today's linux-next merge of the tiny tree got a conflict in
net/openvswitch/Kconfig between commit 8cd4313aa775 ("openvswitch: Fix
build failure") from the net-next tree and commit b043d487e255 ("lib:
Conditionally compile flex_array") from the tiny tree.
I fixed it up (see below) and can carry the fix as necessary (no action
is required).
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
diff --cc net/openvswitch/Kconfig
index b7d818c59423,1d979cecd66e..000000000000
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@@ -4,9 -4,8 +4,10 @@@
config OPENVSWITCH
tristate "Open vSwitch"
+ depends on INET
select LIBCRC32C
+ select NET_MPLS_GSO
+ select FLEX_ARRAY
---help---
Open vSwitch is a multilayer Ethernet switch targeted at virtualized
environments. In addition to supporting a variety of features
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH net-next] enic: support skb->xmit_more
From: Eric Dumazet @ 2014-11-17 5:40 UTC (permalink / raw)
To: Govindarajulu Varadarajan; +Cc: davem, netdev, ssujith, benve
In-Reply-To: <1416179042-1858-1-git-send-email-_govind@gmx.com>
On Mon, 2014-11-17 at 04:34 +0530, Govindarajulu Varadarajan wrote:
> Update posted_index only when skb->xmit_more is 0 or tx queue is full.
>
> Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com>
> ---
> drivers/net/ethernet/cisco/enic/enic_main.c | 8 ++++++--
> drivers/net/ethernet/cisco/enic/vnic_wq.h | 20 +++++++++++---------
> 2 files changed, 17 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
> index 5afe360..52bccfd 100644
> --- a/drivers/net/ethernet/cisco/enic/enic_main.c
> +++ b/drivers/net/ethernet/cisco/enic/enic_main.c
> @@ -533,6 +533,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
> struct vnic_wq *wq;
> unsigned long flags;
> unsigned int txq_map;
> + struct netdev_queue *txq;
>
> if (skb->len <= 0) {
> dev_kfree_skb_any(skb);
> @@ -541,6 +542,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
>
> txq_map = skb_get_queue_mapping(skb) % enic->wq_count;
> wq = &enic->wq[txq_map];
> + txq = netdev_get_tx_queue(netdev, skb_get_queue_mapping(skb));
>
This is strange.
Why dont you use instead :
txq = netdev_get_tx_queue(netdev, txq_map);
> /* Non-TSO sends must fit within ENIC_NON_TSO_MAX_DESC descs,
> * which is very likely. In the off chance it's going to take
> @@ -558,7 +560,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
>
> if (vnic_wq_desc_avail(wq) <
> skb_shinfo(skb)->nr_frags + ENIC_DESC_MAX_SPLITS) {
> - netif_tx_stop_queue(netdev_get_tx_queue(netdev, txq_map));
See here ? Its not equivalent to previous code.
> + netif_tx_stop_queue(txq);
> /* This is a hard error, log it */
> netdev_err(netdev, "BUG! Tx ring full when queue awake!\n");
^ permalink raw reply
* Re: [PATCH 1/1 net-next] wireless: remove unnecessary sizeof(u8)
From: Fabian Frederick @ 2014-11-17 5:56 UTC (permalink / raw)
To: Julian Calaby
Cc: Johannes Berg, linux-wireless, Larry Finger, Chaoming Li, netdev,
linux-kernel@vger.kernel.org, Emmanuel Grumbach, b43-dev,
Stefano Brivio, John W. Linville, Intel Linux Wireless
In-Reply-To: <CAGRGNgWqjsDu_mQK=_agBWjOequb4b7ffgCuxRH=9d72grDYtQ@mail.gmail.com>
> On 16 November 2014 at 23:33 Julian Calaby <julian.calaby@gmail.com> wrote:
>
>
> Hi Fabian,
>
> On Sat, Nov 15, 2014 at 7:55 AM, Fabian Frederick <fabf@skynet.be> wrote:
> > sizeof(u8) is always 1.
>
> I thought that sizeof(*variable) was preferred over sizeof(type), so
> shouldn't these be switched to that format instead?
>
> (I know that this is all no-op, but it should reduce the potential for
> highly unlikely bugs in the future. Also, the extra processing is
> compile-time not run-time.)
>
> Thanks,
Hi Julian,
Of course but char/u8/s8... allocations never use it and result would be the
same:
factor 1 multiplication.
Those rare occurrences (+- 30 in the whole kernel) where we have
sizeof(u8/s8) is ambiguous.
Having a patch removing it gives a pointer...
If the developer meant something else, he will be able to fix it.
Regards,
Fabian
^ permalink raw reply
* Re: [PATCH] bridge: fix netfilter/NF_BR_LOCAL_OUT for own, locally generated queries
From: David Miller @ 2014-11-17 6:12 UTC (permalink / raw)
To: linus.luessing; +Cc: stephen, netdev, bridge, herbert, linux-kernel
In-Reply-To: <20141117020816.GD2469@odroid>
From: Linus Lüssing <linus.luessing@c0d3.blue>
Date: Mon, 17 Nov 2014 03:08:16 +0100
> On Mon, Nov 03, 2014 at 06:01:46AM +0800, Herbert Xu wrote:
>> On Mon, Sep 22, 2014 at 01:32:44AM +0200, Linus Lüssing wrote:
>> > Signed-off-by: Linus Lüssing <linus.luessing@web.de>
>>
>> Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
>
> Hi David,
>
> are there any unanswered questions left?
I expect to get this from the netfilter folks, since it is a
netfilter change. So you should submit it to the netfilter
maintainers.
^ permalink raw reply
* Re: [PATCH 4/4] rhashtable: Add parent argument to mutex_is_held
From: Thomas Graf @ 2014-11-17 6:16 UTC (permalink / raw)
To: Herbert Xu; +Cc: Josh Triplett, netdev, eric.dumazet, paulmck
In-Reply-To: <20141117044657.GA30723@gondor.apana.org.au>
On 11/17/14 at 12:46pm, Herbert Xu wrote:
> On Sat, Nov 15, 2014 at 06:37:26PM -0800, Josh Triplett wrote:
> > On November 15, 2014 6:22:27 PM PST, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > >Josh Triplett <josh@joshtriplett.org> wrote:
> > >>
> > >> - Set up the new empty table with the new set of hash parameters.
> > >> - synchronize_rcu(). Readers will now search both old and new
> > >tables.
> > >> - Peel nodes off the ends of the old hash table and add them to the
> > >new
> > >
> > >We currently use a singly linked list in rhashtable. Peeling nodes
> > >off the end would mean upgrading to a doubly linked list, which is
> > >no different than keeping two lists in terms of cache footprint, no?
> >
> > No, since each pass just handles one set of nodes from each bucket anyway, you can just do a bit more work in the rehasher instead.
>
> OK let me see if I could implement something like that in rhashtable.
This sounds great. Thanks Herbert and Josh!
^ permalink raw reply
* Re: [PATCH 4/4] rhashtable: Add parent argument to mutex_is_held
From: Thomas Graf @ 2014-11-17 6:20 UTC (permalink / raw)
To: Herbert Xu; +Cc: netdev, eric.dumazet, paulmck, josh, David S. Miller
In-Reply-To: <20141115155108.GA21998@gondor.apana.org.au>
On 11/15/14 at 11:51pm, Herbert Xu wrote:
> So I noticed that you got rid of the rehash when you converted
> netlink over to rthashtable. Was this aspect of the conversion
> discussed anywhere? In particular, how do you protect against
> a malicious user that's trying to attack the netlink hash table?
I looked at the trade off between lockless lookups and rehashing and
figured it makes sense to chose making lookups cheaper as sockets can
be constrained per user. However, given the followup-up discussion it
seems we can get both.
Thomas
^ permalink raw reply
* [PATCH net-next v2] ipvlan: Initial check-in of the IPVLAN driver.
From: Mahesh Bandewar @ 2014-11-17 6:27 UTC (permalink / raw)
To: netdev
Cc: Eric Dumazet, Maciej Zenczykowski, Laurent Chavey, Tim Hockin,
David Miller, Brandon Philips, Pavel Emelianov, Mahesh Bandewar
This driver is very similar to the macvlan driver except that it
uses L3 on the frame to determine the logical interface while
functioning as packet dispatcher. It inherits L2 of the master
device hence the packets on wire will have the same L2 for all
the packets originating from all virtual devices off of the same
master device.
This driver was developed keeping the namespace use-case in
mind. Hence most of the examples given here take that as the
base setup where main-device belongs to the default-ns and
virtual devices are assigned to the additional namespaces.
The device operates in two different modes and the difference
in these two modes in primarily in the TX side.
(a) L2 mode : In this mode, the device behaves as a L2 device.
TX processing upto L2 happens on the stack of the virtual device
associated with (namespace). Packets are switched after that
into the main device (default-ns) and queued for xmit.
RX processing is simple and all multicast, broadcast (if
applicable), and unicast belonging to the address(es) are
delivered to the virtual devices.
(b) L3 mode : In this mode, the device behaves like a L3 device.
TX processing upto L3 happens on the stack of the virtual device
associated with (namespace). Packets are switched to the
main-device (default-ns) for the L2 processing. Hence the routing
table of the default-ns will be used in this mode.
RX processins is somewhat similar to the L2 mode except that in
this mode only Unicast packets are delivered to the virtual device
while main-dev will handle all other packets.
The devices can be added using the "ip" command from the iproute2
package -
ip link add link <master> <virtual> type ipvlan mode [ l2 | l3 ]
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Maciej Żenczykowski <maze@google.com>
Cc: Laurent Chavey <chavey@google.com>
Cc: Tim Hockin <thockin@google.com>
Cc: Brandon Philips <brandon.philips@coreos.com>
Cc: Pavel Emelianov <xemul@parallels.com>
---
Documentation/networking/ipvlan.txt | 114 +++++
drivers/net/Kconfig | 18 +
drivers/net/Makefile | 1 +
drivers/net/ipvlan/Makefile | 7 +
drivers/net/ipvlan/ipvlan.h | 157 +++++++
drivers/net/ipvlan/ipvlan_core.c | 630 +++++++++++++++++++++++++++
drivers/net/ipvlan/ipvlan_main.c | 830 ++++++++++++++++++++++++++++++++++++
drivers/net/ipvlan/ipvlan_sysfs.c | 119 ++++++
include/linux/netdevice.h | 4 +
include/uapi/linux/if_link.h | 15 +
10 files changed, 1895 insertions(+)
create mode 100644 Documentation/networking/ipvlan.txt
create mode 100644 drivers/net/ipvlan/Makefile
create mode 100644 drivers/net/ipvlan/ipvlan.h
create mode 100644 drivers/net/ipvlan/ipvlan_core.c
create mode 100644 drivers/net/ipvlan/ipvlan_main.c
create mode 100644 drivers/net/ipvlan/ipvlan_sysfs.c
diff --git a/Documentation/networking/ipvlan.txt b/Documentation/networking/ipvlan.txt
new file mode 100644
index 000000000000..1f5d34cb4841
--- /dev/null
+++ b/Documentation/networking/ipvlan.txt
@@ -0,0 +1,114 @@
+
+ IPVLAN Driver HOWTO
+
+Initial Release:
+ Mahesh Bandewar <maheshb AT google.com>
+
+1. Introduction:
+ This is conceptually very similar to the macvlan driver with one major
+exception of using L3 for mux-ing /demux-ing among slaves. This property makes
+the master device share the L2 with it's slave devices. I have developed this
+driver in conjuntion with network namespaces and not sure if there is use case
+outside of it.
+
+
+2. Building and Installation:
+ In order to build the driver, please select the config item CONFIG_IPVLAN.
+The driver can be built into the kernel (CONFIG_IPVLAN=y) or as a module
+(CONFIG_IPVLAN=m).
+
+
+3. Configuration:
+ There are no module parameters for this driver and it can be configured
+using IProute2/ip utility.
+
+ ip link add link <master-dev> <slave-dev> type ipvlan mode { l2 | L3 }
+
+ e.g. ip link add link ipvl0 eth0 type ipvlan mode l2
+
+
+4. Operating modes:
+ IPvlan has two modes of operation - L2 and L3. For a given master device,
+you can select one of these two modes and all slaves on that master will
+operate in the same (selected) mode. The RX mode is almost identical except
+that in L3 mode the slaves wont receive any multicast / broadcast traffic.
+L3 mode is more restrictive since routing is controlled from the other (mostly)
+default namespace.
+
+4.1 L2 mode:
+ In this mode TX processing happens on the stack instance attached to the
+slave device and packets are switched and queued to the master device to send
+out. In this mode the slaves will RX/TX multicast and broadcast (if applicable)
+as well.
+
+4.2 L3 mode:
+ In this mode TX processing upto L3 happens on the stack instance attached
+to the slave device and packets are switched to the stack instance of the
+master device for the L2 processing and routing from that instance will be
+used before packets are queued on the outbound device. In this mode the slaves
+will not receive nor can send multicast / broadcast traffic.
+
+
+5. Sysfs interface:
+ Currently the mode of operation is available at -
+ /sys/class/net/<master>/ipvlan/mode
+The value can be 0 or 1; where 0 :=> L2, 1 := L3 mode
+
+
+6. What to choose (macvlan vs. ipvlan)?
+ These two devices are very similar in many regards and the specific use
+case could very well define which device to choose. if one of the following
+situations defines your use case then you can choose to use ipvlan -
+ (a) The Linux host that is connected to the external switch / router has
+policy configured that allows only one mac per port.
+ (b) No of virtual devices created on a master exceed the mac capacity and
+puts the NIC in promiscous mode and degraded performance is a concern.
+ (c) If the slave device is to be put into the hostile / untrusted network
+namespace where L2 on the slave could be changed / misused.
+
+
+7. Example configuration:
+
+ +=============================================================+
+ | Host: host1 |
+ | |
+ | +----------------------+ +----------------------+ |
+ | | NS:ns0 | | NS:ns1 | |
+ | | | | | |
+ | | | | | |
+ | | ipvl0 | | ipvl1 | |
+ | +----------#-----------+ +-----------#----------+ |
+ | # # |
+ | ################################ |
+ | # eth0 |
+ +==============================#==============================+
+
+
+ (a) Create two network namespaces - ns0, ns1
+ ip netns add ns0
+ ip netns add ns1
+
+ (b) Create two ipvlan slaves on eth0 (master device)
+ ip link add link eth0 ipvl0 type ipvlan mode l2
+ ip link add link eth0 ipvl1 type ipvlan mode l2
+
+ (c) Assign slaves to the respective network namespaces
+ ip link set dev ipvl0 netns ns0
+ ip link set dev ipvl1 netns ns1
+
+ (d) Now switch to the namespace (ns0 or ns1) to configure the slave devices
+ - For ns0
+ (1) ip netns exec ns0 bash
+ (2) ip link set dev ipvl0 up
+ (3) ip link set dev lo up
+ (4) ip -4 addr add 127.0.0.1 dev lo
+ (5) ip -4 addr add $IPADDR dev ipvl0
+ (6) ip -4 route add default via $ROUTER dev ipvl0
+ - For ns1
+ (1) ip netns exec ns1 bash
+ (2) ip link set dev ipvl1 up
+ (3) ip link set dev lo up
+ (4) ip -4 addr add 127.0.0.1 dev lo
+ (5) ip -4 addr add $IPADDR dev ipvl1
+ (6) ip -4 route add default via $ROUTER dev ipvl1
+
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f9009be3f307..b6d64f546574 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -145,6 +145,24 @@ config MACVTAP
To compile this driver as a module, choose M here: the module
will be called macvtap.
+
+config IPVLAN
+ tristate "IP-VLAN support"
+ ---help---
+ This allows one to create virtual devices off of a main interface
+ and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
+ on packets. All interfaces (including the main interface) share L2
+ making it transparent to the connected L2 switch.
+
+ Ipvlan devices can be added using the "ip" command from the
+ iproute2 package starting with the iproute2-X.Y.ZZ release:
+
+ "ip link add link <main-dev> [ NAME ] type ipvlan"
+
+ To compile this driver as a module, choose M here: the module
+ will be called ipvlan.
+
+
config VXLAN
tristate "Virtual eXtensible Local Area Network (VXLAN)"
depends on INET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 61aefdd1e173..e25fdd7d905e 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,6 +6,7 @@
# Networking Core Drivers
#
obj-$(CONFIG_BONDING) += bonding/
+obj-$(CONFIG_IPVLAN) += ipvlan/
obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
diff --git a/drivers/net/ipvlan/Makefile b/drivers/net/ipvlan/Makefile
new file mode 100644
index 000000000000..2efff4e9bb40
--- /dev/null
+++ b/drivers/net/ipvlan/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Ethernet Ipvlan driver
+#
+
+obj-$(CONFIG_IPVLAN) += ipvlan.o
+
+ipvlan-objs := ipvlan_core.o ipvlan_main.o ipvlan_sysfs.o
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
new file mode 100644
index 000000000000..78bb1ee9bc68
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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.
+ *
+ */
+#ifndef __IPVLAN_H
+#define __IPVLAN_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rculist.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_link.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <net/rtnetlink.h>
+#include <net/gre.h>
+#include <net/route.h>
+#include <net/addrconf.h>
+
+#define IPVLAN_DRV "ipvlan"
+#define IPV_DRV_VER "0.1"
+
+#define IPVLAN_HASH_SIZE (1 << BITS_PER_BYTE)
+#define IPVLAN_HASH_MASK (IPVLAN_HASH_SIZE - 1)
+
+#define IPVLAN_MAC_FILTER_BITS 8
+#define IPVLAN_MAC_FILTER_SIZE (1 << IPVLAN_MAC_FILTER_BITS)
+#define IPVLAN_MAC_FILTER_MASK (IPVLAN_MAC_FILTER_SIZE - 1)
+
+/* Define IPVL_DEBUG and set the appropriate dbg_level for debugging. */
+#ifdef IPVL_DEBUG
+/*
+ * 1 : non-datapath debugging
+ * 2 : Custom
+ * 3 : function enters and exists.
+ * 4 : printk in data path (be careful!)
+ */
+#define IPVL_DBG_LEVEL 1
+#define ipvlan_dbg(level, msg...) do { \
+ if (level <= IPVL_DBG_LEVEL) \
+ printk(KERN_DEBUG msg); \
+ } while (0)
+#else
+#define ipvlan_dbg(level, msg...) do { ; } while (0)
+#endif
+
+typedef enum {
+ IPVL_IPV6 = 0,
+ IPVL_ICMPV6,
+ IPVL_IPV4,
+ IPVL_ARP,
+} ipvl_hdr_type;
+
+struct ipvl_pcpu_stats {
+ u64 rx_pkts;
+ u64 rx_bytes;
+ u64 rx_mcast;
+ u64 tx_pkts;
+ u64 tx_bytes;
+ struct u64_stats_sync syncp;
+ u32 rx_errs;
+ u32 tx_drps;
+};
+
+/* Forward declaration */
+struct ipvl_port;
+
+struct ipvl_dev {
+ struct net_device *dev;
+ struct list_head pnode;
+ struct ipvl_port *port;
+ struct net_device *phy_dev;
+ struct list_head addrs;
+ int ipv4cnt;
+ int ipv6cnt;
+ struct ipvl_pcpu_stats *pcpu_stats;
+ DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
+ netdev_features_t sfeatures;
+ u16 mtu_adj;
+};
+
+struct ipvl_addr {
+ struct ipvl_dev *master; /* Back pointer to master */
+ union {
+ struct in6_addr ip6; /* IPv6 address on logical interface */
+ struct in_addr ip4; /* IPv4 address on logical interface */
+ } ipu;
+#define ip6addr ipu.ip6
+#define ip4addr ipu.ip4
+ struct hlist_node hlnode; /* Hash-table linkage */
+ struct list_head anode; /* logical-interface linkage */
+ struct rcu_head rcu;
+ ipvl_hdr_type atype;
+};
+
+struct ipvl_port {
+ struct net_device *dev;
+ struct hlist_head hlhead[IPVLAN_HASH_SIZE];
+ struct list_head ipvlans;
+ struct rcu_head rcu;
+ int count;
+ struct kobject kobj;
+ u16 mode;
+};
+
+static inline struct ipvl_port *ipvlan_port_get_rcu(const struct net_device *d)
+{
+ return rcu_dereference(d->rx_handler_data);
+}
+
+static inline struct ipvl_port *ipvlan_port_get_rtnl(const struct net_device *d)
+{
+ return rtnl_dereference(d->rx_handler_data);
+}
+
+static inline bool ipvlan_dev_master(struct net_device *d)
+{
+ return d->priv_flags & IFF_IPVLAN_MASTER;
+}
+
+static inline bool ipvlan_dev_slave(struct net_device *d)
+{
+ return d->priv_flags & IFF_IPVLAN_SLAVE;
+}
+
+/* ---- Prototype declarations ---- */
+/* ---- ipvlan_main.c ---- */
+void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev);
+void ipvlan_set_port_mode(struct ipvl_port *port, u32 nval);
+
+/* ---- ipvlan_sysfs.c ---- */
+int ipvlan_add_per_master_sysfs_mode(struct ipvl_port *port,
+ struct net_device *dev);
+void ipvlan_del_per_master_sysfs_mode(struct ipvl_port *port);
+
+/* ---- ipvlan_core.c ---- */
+void ipvlan_init_secret(void);
+unsigned int ipvlan_mac_hash(const unsigned char *addr);
+rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
+int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
+void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
+bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
+struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
+ const void *iaddr, bool is_v6);
+void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync);
+#endif /* __IPVLAN_H */
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
new file mode 100644
index 000000000000..fc814ff311e5
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -0,0 +1,630 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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.
+ *
+ */
+
+#include "ipvlan.h"
+
+static u32 ipvlan_jhash_secret;
+
+void ipvlan_init_secret(void)
+{
+ net_get_random_once(&ipvlan_jhash_secret, sizeof(ipvlan_jhash_secret));
+}
+
+static void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
+ unsigned int len, bool success, bool mcast)
+{
+ if (!ipvlan)
+ return;
+
+ if (likely(success)) {
+ struct ipvl_pcpu_stats *pcptr;
+
+ pcptr = this_cpu_ptr(ipvlan->pcpu_stats);
+ u64_stats_update_begin(&pcptr->syncp);
+ pcptr->rx_pkts++;
+ pcptr->rx_bytes += len;
+ if (mcast)
+ pcptr->rx_mcast++;
+ u64_stats_update_end(&pcptr->syncp);
+ } else {
+ this_cpu_inc(ipvlan->pcpu_stats->rx_errs);
+ }
+}
+
+static u8 ipvlan_get_v6_hash(const void *iaddr)
+{
+ const struct in6_addr *ip6_addr = iaddr;
+
+ return __ipv6_addr_jhash(ip6_addr, ipvlan_jhash_secret)
+ & IPVLAN_HASH_MASK;
+}
+
+static u8 ipvlan_get_v4_hash(const void *iaddr)
+{
+ const struct in_addr *ip4_addr = iaddr;
+ return jhash_1word(ip4_addr->s_addr, ipvlan_jhash_secret)
+ & IPVLAN_HASH_MASK;
+}
+
+struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
+ const void *iaddr, bool is_v6)
+{
+ struct ipvl_addr *addr;
+ u8 hash = is_v6 ? ipvlan_get_v6_hash(iaddr) :
+ ipvlan_get_v4_hash(iaddr);
+
+ hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode) {
+ if (is_v6 && addr->atype == IPVL_IPV6 &&
+ ipv6_addr_equal(&addr->ip6addr, iaddr))
+ return addr;
+ else if (!is_v6 && addr->atype == IPVL_IPV4 &&
+ addr->ip4addr.s_addr ==
+ ((struct in_addr *)iaddr)->s_addr)
+ return addr;
+ }
+ return NULL;
+}
+
+void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr)
+{
+ struct ipvl_port *port = ipvlan->port;
+ u8 hash = (addr->atype == IPVL_IPV6) ?
+ ipvlan_get_v6_hash(&addr->ip6addr) :
+ ipvlan_get_v4_hash(&addr->ip4addr);
+
+ hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
+}
+
+void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync)
+{
+ hlist_del_rcu(&addr->hlnode);
+ if (sync)
+ synchronize_rcu();
+}
+
+bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
+{
+ struct ipvl_port *port = ipvlan->port;
+ struct ipvl_addr *addr;
+
+ list_for_each_entry(addr, &ipvlan->addrs, anode) {
+ if ((is_v6 && addr->atype == IPVL_IPV6 &&
+ ipv6_addr_equal(&addr->ip6addr, iaddr))
+ || (!is_v6 && addr->atype == IPVL_IPV4 &&
+ addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr)
+ )
+ return true;
+ }
+
+ if (ipvlan_ht_addr_lookup(port, iaddr, is_v6))
+ return true;
+
+ return false;
+}
+
+static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type)
+{
+ void *lyr3h = NULL;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_ARP): {
+ struct arphdr *arph;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*arph))))
+ return NULL;
+
+ arph = arp_hdr(skb);
+ *type = IPVL_ARP;
+ lyr3h = arph;
+ break;
+ }
+
+ case htons(ETH_P_IP): {
+ u32 pktlen;
+ struct iphdr *ip4h;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ip4h))))
+ return NULL;
+
+ ip4h = ip_hdr(skb);
+ pktlen = ntohs(ip4h->tot_len);
+ if (ip4h->ihl < 5 || ip4h->version != 4)
+ return NULL;
+ if (skb->len < pktlen || pktlen < (ip4h->ihl * 4))
+ return NULL;
+
+ *type = IPVL_IPV4;
+ lyr3h = ip4h;
+ break;
+ }
+ case htons(ETH_P_IPV6): {
+ struct ipv6hdr *ip6h;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h))))
+ return NULL;
+
+ ip6h = ipv6_hdr(skb);
+ if (ip6h->version != 6)
+ return NULL;
+
+ *type = IPVL_IPV6;
+ lyr3h = ip6h;
+ /* Only Neighbour Solicitation pkts need different treatment */
+ if (ipv6_addr_any(&ip6h->saddr) &&
+ ip6h->nexthdr == NEXTHDR_ICMP) {
+ /* Get to the ICMPv6 header */
+ *type = IPVL_ICMPV6;
+ lyr3h = ip6h + 1;
+ }
+ break;
+ }
+ default:
+ return NULL;
+ }
+
+ return lyr3h;
+}
+
+unsigned int ipvlan_mac_hash(const unsigned char *addr)
+{
+ u32 hash = jhash_1word(__get_unaligned_cpu32(addr+2),
+ ipvlan_jhash_secret);
+ return hash & IPVLAN_MAC_FILTER_MASK;
+}
+
+static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb,
+ const struct ipvl_dev *in_dev, bool local)
+{
+ struct ethhdr *eth = eth_hdr(skb);
+ struct ipvl_dev *ipvlan;
+ struct sk_buff *nskb;
+ unsigned int len;
+ unsigned int mac_hash;
+ int ret;
+
+ /* If it's a PAUSE frame discard it! */
+ if (skb->protocol == htons(ETH_P_PAUSE))
+ return;
+
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ if (local && (ipvlan == in_dev))
+ continue;
+
+ mac_hash = ipvlan_mac_hash(eth->h_dest);
+ if (!test_bit(mac_hash, ipvlan->mac_filters))
+ continue;
+
+ ret = NET_RX_DROP;
+ len = skb->len + ETH_HLEN;
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ goto mcast_acct;
+
+ if (ether_addr_equal(eth->h_dest, ipvlan->phy_dev->broadcast))
+ nskb->pkt_type = PACKET_BROADCAST;
+ else
+ nskb->pkt_type = PACKET_MULTICAST;
+
+ nskb->dev = ipvlan->dev;
+ if (local)
+ ret = dev_forward_skb(ipvlan->dev, nskb);
+ else
+ ret = netif_rx(nskb);
+mcast_acct:
+ ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);
+ }
+
+ /* Locally generated? ...Forward a copy to the main-device as
+ * well. On the RX side we'll ignore it (wont give it to any
+ * of the virtual devices.
+ */
+ if (local) {
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ if (ether_addr_equal(eth->h_dest, port->dev->broadcast))
+ nskb->pkt_type = PACKET_BROADCAST;
+ else
+ nskb->pkt_type = PACKET_MULTICAST;
+
+ dev_forward_skb(port->dev, nskb);
+ }
+ }
+}
+
+static int ipvlan_rcv_frame(struct ipvl_addr *addr, struct sk_buff *skb,
+ bool local)
+{
+ struct ipvl_dev *ipvlan = addr->master;
+ struct net_device *dev = ipvlan->dev;
+ unsigned int len;
+ rx_handler_result_t ret = RX_HANDLER_CONSUMED;
+ bool success = false;
+
+ len = skb->len + ETH_HLEN;
+ if (unlikely(!(dev->flags & IFF_UP))) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+
+ if (local) {
+ if (dev_forward_skb(ipvlan->dev, skb) == NET_RX_SUCCESS)
+ success = true;
+ } else {
+ ret = RX_HANDLER_ANOTHER;
+ success = true;
+ }
+
+out:
+ ipvlan_count_rx(ipvlan, len, success, false);
+ return ret;
+}
+
+static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
+ void *lyr3h, int addr_type,
+ bool use_dest)
+{
+ struct ipvl_addr *addr = NULL;
+
+ if (addr_type == IPVL_IPV6) {
+ struct ipv6hdr *ip6h;
+ struct in6_addr *i6addr;
+
+ ip6h = (struct ipv6hdr *)lyr3h;
+ i6addr = use_dest ? &ip6h->daddr : &ip6h->saddr;
+ addr = ipvlan_ht_addr_lookup(port, i6addr, true);
+ } else if (addr_type == IPVL_ICMPV6) {
+ struct nd_msg *ndmh;
+ struct in6_addr *i6addr;
+ ndmh = (struct nd_msg *)lyr3h;
+
+ /* Make sure that the NeighborSolicitation ICMPv6 packets
+ * are handled to avoid DAD issue.
+ */
+ if (ndmh->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
+ /* Reach the target address */
+ i6addr = &ndmh->target;
+ addr = ipvlan_ht_addr_lookup(port, i6addr, true);
+ }
+ } else if (addr_type == IPVL_IPV4) {
+ struct iphdr *ip4h;
+ __be32 *i4addr;
+
+ ip4h = (struct iphdr *)lyr3h;
+ i4addr = use_dest ? &ip4h->daddr : &ip4h->saddr;
+ addr = ipvlan_ht_addr_lookup(port, i4addr, false);
+ } else if (addr_type == IPVL_ARP) {
+ struct arphdr *arph;
+ unsigned char *arp_ptr;
+ __be32 dip;
+
+ arph = (struct arphdr *)lyr3h;
+ arp_ptr = (unsigned char *)(arph + 1);
+ if (use_dest)
+ /* Skip 2 L2 headers + 1 src L3 (IPv4) header */
+ arp_ptr += (2 * port->dev->addr_len) + 4;
+ else
+ /* Skip L2 header to get to src L3 (IPv4) */
+ arp_ptr += port->dev->addr_len;
+
+ memcpy(&dip, arp_ptr, 4); /* Get the dst IPv4 */
+ addr = ipvlan_ht_addr_lookup(port, &dip, false);
+ }
+
+ return addr;
+}
+
+static int ipvlan_process_v4_outbound(struct sk_buff *skb)
+{
+ const struct iphdr *ip4h = ip_hdr(skb);
+ struct net_device *dev = skb->dev;
+ struct rtable *rt;
+ int err, ret = NET_XMIT_DROP;
+ struct flowi4 fl4 = {
+ .flowi4_oif = dev->iflink,
+ .flowi4_tos = RT_TOS(ip4h->tos),
+ .flowi4_flags = FLOWI_FLAG_ANYSRC,
+ .daddr = ip4h->daddr,
+ .saddr = ip4h->saddr,
+ };
+
+ rt = ip_route_output_flow(dev_net(dev), &fl4, NULL);
+ if (IS_ERR(rt))
+ goto err;
+
+ if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) {
+ ip_rt_put(rt);
+ goto err;
+ }
+ skb_dst_drop(skb);
+ skb_dst_set(skb, &rt->dst);
+ err = ip_local_out(skb);
+ if (unlikely(net_xmit_eval(err)))
+ dev->stats.tx_errors++;
+ else
+ ret = NET_XMIT_SUCCESS;
+ goto out;
+err:
+ dev->stats.tx_errors++;
+ kfree_skb(skb);
+out:
+ return ret;
+}
+
+static int ipvlan_process_v6_outbound(struct sk_buff *skb)
+{
+ const struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct net_device *dev = skb->dev;
+ struct dst_entry *dst;
+ int err, ret = NET_XMIT_DROP;
+ struct flowi6 fl6 = {
+ .flowi6_iif = skb->dev->ifindex,
+ .daddr = ip6h->daddr,
+ .saddr = ip6h->saddr,
+ .flowi6_flags = FLOWI_FLAG_ANYSRC,
+ .flowlabel = ip6_flowinfo(ip6h),
+ .flowi6_mark = skb->mark,
+ .flowi6_proto = ip6h->nexthdr,
+ };
+
+ dst = ip6_route_output(dev_net(dev), NULL, &fl6);
+ if (IS_ERR(dst))
+ goto err;
+
+ skb_dst_drop(skb);
+ skb_dst_set(skb, dst);
+ err = ip6_local_out(skb);
+ if (unlikely(net_xmit_eval(err)))
+ dev->stats.tx_errors++;
+ else
+ ret = NET_XMIT_SUCCESS;
+ goto out;
+err:
+ dev->stats.tx_errors++;
+ kfree_skb(skb);
+out:
+ return ret;
+}
+
+static int ipvlan_process_outbound(struct sk_buff *skb,
+ const struct ipvl_dev *ipvlan)
+{
+ struct ethhdr *ethh = eth_hdr(skb);
+ int ret = NET_XMIT_DROP;
+
+ /* In this mode we dont care about multicast and broadcast traffic */
+ if (is_multicast_ether_addr(ethh->h_dest)) {
+ pr_warn_ratelimited("Dropped {multi|broad}cast of type= [%x]\n",
+ ntohs(skb->protocol));
+ kfree_skb(skb);
+ goto out;
+ }
+
+ /* The ipvlan is a pseudo-L2 device, so the packets that we receive
+ * will have L2; which need to discarded and processed further
+ * in the net-ns of the main-device.
+ */
+ if (skb_mac_header_was_set(skb)) {
+ skb_pull(skb, sizeof(*ethh));
+ skb->mac_header = (typeof(skb->mac_header))~0U;
+ skb_reset_network_header(skb);
+ }
+
+ if (skb->protocol == htons(ETH_P_IPV6))
+ ret = ipvlan_process_v6_outbound(skb);
+ else if (skb->protocol == htons(ETH_P_IP))
+ ret = ipvlan_process_v4_outbound(skb);
+ else {
+ pr_warn_ratelimited("Dropped outbound packet type=%x\n",
+ ntohs(skb->protocol));
+ kfree_skb(skb);
+ }
+out:
+ return ret;
+}
+
+static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
+{
+ const struct ipvl_dev *ipvlan = netdev_priv(dev);
+ void *lyr3h;
+ struct ipvl_addr *addr;
+ int addr_type;
+
+ ipvlan_dbg(4, "L3:Xmit on dev %s,PROT=%x\n", dev->name,
+ ntohs(skb->protocol));
+ lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+ if (!lyr3h)
+ goto out;
+
+ addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
+ if (addr)
+ return ipvlan_rcv_frame(addr, skb, true);
+
+out:
+ /* Send it out */
+ skb->dev = ipvlan->phy_dev;
+ return ipvlan_process_outbound(skb, ipvlan);
+}
+
+static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
+{
+ const struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ethhdr *eth = eth_hdr(skb);
+ struct ipvl_addr *addr;
+ void *lyr3h;
+ int addr_type;
+
+ ipvlan_dbg(4, "L2:Xmit on dev %s,PROT=%x\n", dev->name,
+ ntohs(skb->protocol));
+ if (ether_addr_equal(eth->h_dest, eth->h_source)) {
+ ipvlan_dbg(4, "Comm betn 2 virt devs PROT=%x\n",
+ ntohs(skb->protocol));
+ if ((lyr3h = ipvlan_get_L3_hdr(skb, &addr_type)) == NULL)
+ goto to_default;
+
+ addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
+ if (addr)
+ return ipvlan_rcv_frame(addr, skb, true);
+
+ /* No matching ipvlan dev! Must be on the Physical device */
+to_default:
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_XMIT_DROP;
+
+ /* Packet definitely does not belong to any of the
+ * virtual devices, but the dest is local. So forward
+ * the skb for the main-dev. At the RX side we just return
+ * RX_PASS for it to be processed further on the stack.
+ */
+ return dev_forward_skb(ipvlan->phy_dev, skb);
+
+ } else if (is_multicast_ether_addr(eth->h_dest)) {
+ u8 ip_summed = skb->ip_summed;
+ /* Packet needs to be multicast-ed. */
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ ipvlan_dbg(4, "%s[%d] Mcast Xmit on [%s], PROT=[%x]\n",
+ __func__, __LINE__, dev->name,
+ ntohs(skb->protocol));
+ ipvlan_multicast_frame(ipvlan->port, skb, ipvlan, true);
+ skb->ip_summed = ip_summed;
+ }
+
+ /* Send it out */
+ skb->dev = ipvlan->phy_dev;
+ return dev_queue_xmit(skb);
+}
+
+int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port = ipvlan_port_get_rcu(ipvlan->phy_dev);
+
+ if (!port)
+ goto out;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
+ goto out;
+
+ switch(port->mode) {
+ case IPVLAN_MODE_L2:
+ return ipvlan_xmit_mode_l2(skb, dev);
+ case IPVLAN_MODE_L3:
+ return ipvlan_xmit_mode_l3(skb, dev);
+ }
+
+ /* Should not reach here */
+ BUG();
+out:
+ kfree_skb(skb);
+ return NET_XMIT_DROP;
+}
+
+static bool ipvlan_external_frame(struct sk_buff *skb, struct ipvl_port *port)
+{
+ struct ethhdr *eth = eth_hdr(skb);
+ struct ipvl_addr *addr;
+ void *lyr3h;
+ int addr_type;
+
+ if (ether_addr_equal(eth->h_source, skb->dev->dev_addr)) {
+ if ((lyr3h = ipvlan_get_L3_hdr(skb, &addr_type)) == NULL)
+ return true;
+
+ addr = ipvlan_addr_lookup(port, lyr3h, addr_type, false);
+ if (addr)
+ return false;
+ }
+
+ return true;
+}
+
+static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
+ struct ipvl_port *port)
+{
+ void *lyr3h;
+ int addr_type;
+ struct ipvl_addr *addr;
+ struct sk_buff *skb = *pskb;
+ rx_handler_result_t ret = RX_HANDLER_PASS;
+
+ lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+ if (!lyr3h)
+ goto out;
+
+ addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
+ if (addr) {
+ ipvlan_dbg(4, "%s[%d]L3:Ucast Recv for [%s], PROT=[%x]\n",
+ __func__, __LINE__, addr->master->dev->name,
+ ntohs(skb->protocol));
+ ret = ipvlan_rcv_frame(addr, skb, false);
+ }
+out:
+ return ret;
+}
+
+static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
+ struct ipvl_port *port)
+{
+ struct sk_buff *skb = *pskb;
+ struct ethhdr *eth = eth_hdr(skb);
+ rx_handler_result_t ret = RX_HANDLER_PASS;
+ void *lyr3h;
+ int addr_type;
+
+ /* First Handle broadcast/multicast frames */
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ /* Pass to virtual devs only if they haven't seen the frame. */
+ if (ipvlan_external_frame(skb, port)) {
+ ipvlan_dbg(4, "%s[%d]L2:Mcast Recv:[%s], PROT=[%x]\n",
+ __func__, __LINE__, port->dev->name,
+ ntohs(skb->protocol));
+ ipvlan_multicast_frame(port, skb, NULL, false);
+ }
+ } else if ((lyr3h = ipvlan_get_L3_hdr(skb, &addr_type)) != NULL) {
+ struct ipvl_addr *addr;
+
+ addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
+ if (addr) {
+ ipvlan_dbg(4, "%s[%d]L2:Ucast Recv:[%s], PROT=[%x]\n",
+ __func__, __LINE__, addr->master->dev->name,
+ ntohs(skb->protocol));
+ ret = ipvlan_rcv_frame(addr, skb, false);
+ }
+ }
+
+ return ret;
+}
+
+rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct ipvl_port *port = ipvlan_port_get_rcu(skb->dev);
+
+ if (!port)
+ goto out;
+
+ switch (port->mode) {
+ case IPVLAN_MODE_L2:
+ return ipvlan_handle_mode_l2(pskb, port);
+ case IPVLAN_MODE_L3:
+ return ipvlan_handle_mode_l3(pskb, port);
+ }
+
+ /* Should not reach here */
+ BUG();
+out:
+ return RX_HANDLER_PASS;
+}
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
new file mode 100644
index 000000000000..e81b3ed79fdf
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -0,0 +1,830 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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.
+ *
+ */
+
+#include "ipvlan.h"
+
+void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev)
+{
+ ipvlan->dev->mtu = dev->mtu - ipvlan->mtu_adj;
+}
+
+void ipvlan_set_port_mode(struct ipvl_port *port, u32 nval)
+{
+ struct ipvl_dev *ipvlan;
+
+ if (port->mode != nval) {
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ if (nval == IPVLAN_MODE_L3)
+ ipvlan->dev->flags |= IFF_NOARP;
+ else
+ ipvlan->dev->flags &= ~IFF_NOARP;
+ }
+ port->mode = nval;
+ }
+}
+
+static int ipvlan_port_create(struct net_device *dev)
+{
+ struct ipvl_port *port;
+ int err, idx;
+
+ ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+ if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) {
+ pr_warn("%s[%d]: Returning -EINVAL...\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ if ((port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL)) == NULL) {
+ pr_warn("%s[%d]: Returning -ENOMEM...\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ port->dev = dev;
+ port->mode = IPVLAN_MODE_L3;
+ INIT_LIST_HEAD(&port->ipvlans);
+ for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++)
+ INIT_HLIST_HEAD(&port->hlhead[idx]);
+
+ err = ipvlan_add_per_master_sysfs_mode(port, dev);
+ if (err)
+ goto err;
+
+ err = netdev_rx_handler_register(dev, ipvlan_handle_frame, port);
+ if (err)
+ goto err;
+
+ dev->priv_flags |= IFF_IPVLAN_MASTER;
+ ipvlan_dbg(3, "%s[%d]: Returning (%d)...\n", __func__, __LINE__, err);
+ return 0;
+
+err:
+ kfree_rcu(port, rcu);
+ return err;
+}
+
+static void ipvlan_port_destroy(struct net_device *dev)
+{
+ struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
+
+ dev->priv_flags &= ~IFF_IPVLAN_MASTER;
+ ipvlan_del_per_master_sysfs_mode(port);
+ netdev_rx_handler_unregister(dev);
+ kfree_rcu(port, rcu);
+}
+
+/* ipvlan network devices have devices nesting below it and are a special
+ * "super class" of normal network devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key ipvlan_netdev_xmit_lock_key;
+static struct lock_class_key ipvlan_netdev_addr_lock_key;
+
+#define IPVLAN_FEATURES \
+ (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \
+ NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
+ NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
+
+#define IPVLAN_STATE_MASK \
+ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT))
+
+static void ipvlan_set_lockdep_class_one(struct net_device *dev,
+ struct netdev_queue *txq,
+ void *_unused)
+{
+ lockdep_set_class(&txq->_xmit_lock, &ipvlan_netdev_xmit_lock_key);
+}
+
+static void ipvlan_set_lockdep_class(struct net_device *dev)
+{
+ lockdep_set_class(&dev->addr_list_lock, &ipvlan_netdev_addr_lock_key);
+ netdev_for_each_tx_queue(dev, ipvlan_set_lockdep_class_one, NULL);
+}
+
+/* ---- IPVLAN Netdev Ops ---- */
+static int ipvlan_init(struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ const struct net_device *phy_dev = ipvlan->phy_dev;
+
+ dev->state = (dev->state & ~IPVLAN_STATE_MASK) |
+ (phy_dev->state & IPVLAN_STATE_MASK);
+ dev->features = phy_dev->features & IPVLAN_FEATURES;
+ dev->features |= NETIF_F_LLTX;
+ dev->gso_max_size = phy_dev->gso_max_size;
+ dev->iflink = phy_dev->ifindex;
+ dev->hard_header_len = phy_dev->hard_header_len;
+
+ ipvlan_set_lockdep_class(dev);
+
+ ipvlan->pcpu_stats = alloc_percpu(struct ipvl_pcpu_stats);
+ if (!ipvlan->pcpu_stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ipvlan_uninit(struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port = ipvlan->port;
+
+ if (ipvlan->pcpu_stats)
+ free_percpu(ipvlan->pcpu_stats);
+
+ port->count -= 1;
+ if (!port->count)
+ ipvlan_port_destroy(port->dev);
+}
+
+static int ipvlan_open(struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct net_device *phy_dev = ipvlan->phy_dev;
+ struct ipvl_addr *addr;
+
+ if (ipvlan->port->mode == IPVLAN_MODE_L3)
+ dev->flags |= IFF_NOARP;
+ else
+ dev->flags &= ~IFF_NOARP;
+
+ if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
+ list_for_each_entry(addr, &ipvlan->addrs, anode) {
+ ipvlan_ht_addr_add(ipvlan, addr);
+ }
+ }
+ return dev_uc_add(phy_dev, phy_dev->dev_addr);
+}
+
+static int ipvlan_stop(struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct net_device *phy_dev = ipvlan->phy_dev;
+ struct ipvl_addr *addr;
+
+ dev_uc_unsync(phy_dev, dev);
+ dev_mc_unsync(phy_dev, dev);
+
+ dev_uc_del(phy_dev, phy_dev->dev_addr);
+
+ if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
+ list_for_each_entry(addr, &ipvlan->addrs, anode) {
+ ipvlan_ht_addr_del(addr, !dev->dismantle);
+ }
+ }
+ return 0;
+}
+
+netdev_tx_t ipvlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ const struct ipvl_dev *ipvlan = netdev_priv(dev);
+ int skblen = skb->len;
+ int ret;
+
+ ret = ipvlan_queue_xmit(skb, dev);
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+ struct ipvl_pcpu_stats *pcptr;
+
+ pcptr = this_cpu_ptr(ipvlan->pcpu_stats);
+
+ u64_stats_update_begin(&pcptr->syncp);
+ pcptr->tx_pkts++;
+ pcptr->tx_bytes += skblen;
+ u64_stats_update_end(&pcptr->syncp);
+ } else {
+ this_cpu_inc(ipvlan->pcpu_stats->tx_drps);
+ }
+ return ret;
+}
+
+static netdev_features_t ipvlan_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ return features & (ipvlan->sfeatures | ~IPVLAN_FEATURES);
+}
+
+static void ipvlan_change_rx_flags(struct net_device *dev, int change)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct net_device *phy_dev = ipvlan->phy_dev;
+
+ if (change & IFF_ALLMULTI)
+ dev_set_allmulti(phy_dev, dev->flags & IFF_ALLMULTI? 1 : -1);
+}
+
+static void ipvlan_set_broadcast_mac_filter(struct ipvl_dev *ipvlan, bool set)
+{
+ struct net_device *dev = ipvlan->dev;
+ unsigned int hashbit = ipvlan_mac_hash(dev->broadcast);
+
+ if (set && !test_bit(hashbit, ipvlan->mac_filters)) {
+ /* Set broadcast hash-bit (for IPv4) */
+ __set_bit(hashbit, ipvlan->mac_filters);
+ } else if (!set && test_bit(hashbit, ipvlan->mac_filters)) {
+ /* Reset broadcast hash-bit */
+ __clear_bit(hashbit, ipvlan->mac_filters);
+ }
+}
+
+static void ipvlan_set_multicast_mac_filter(struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+ if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+ bitmap_fill(ipvlan->mac_filters, IPVLAN_MAC_FILTER_SIZE);
+ } else {
+ struct netdev_hw_addr *ha;
+ DECLARE_BITMAP(mc_filters, IPVLAN_MAC_FILTER_SIZE);
+
+ bitmap_zero(mc_filters, IPVLAN_MAC_FILTER_SIZE);
+ netdev_for_each_mc_addr(ha, dev) {
+ __set_bit(ipvlan_mac_hash(ha->addr), mc_filters);
+ }
+ bitmap_copy(ipvlan->mac_filters, mc_filters,
+ IPVLAN_MAC_FILTER_SIZE);
+ }
+ dev_uc_sync(ipvlan->phy_dev, dev);
+ dev_mc_sync(ipvlan->phy_dev, dev);
+}
+
+static struct rtnl_link_stats64 *ipvlan_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+ if (ipvlan->pcpu_stats) {
+ struct ipvl_pcpu_stats *pcptr;
+ u64 rx_pkts, rx_bytes, rx_mcast, tx_pkts, tx_bytes;
+ u32 rx_errs = 0, tx_drps = 0;
+ u32 strt;
+ int idx;
+
+ for_each_possible_cpu(idx) {
+ pcptr = per_cpu_ptr(ipvlan->pcpu_stats, idx);
+ do {
+ strt= u64_stats_fetch_begin_irq(&pcptr->syncp);
+ rx_pkts = pcptr->rx_pkts;
+ rx_bytes = pcptr->rx_bytes;
+ rx_mcast = pcptr->rx_mcast;
+ tx_pkts = pcptr->tx_pkts;
+ tx_bytes = pcptr->tx_bytes;
+ } while(u64_stats_fetch_retry_irq(&pcptr->syncp, strt));
+
+ stats->rx_packets += rx_pkts;
+ stats->rx_bytes += rx_bytes;
+ stats->multicast += rx_mcast;
+ stats->tx_packets += tx_pkts;
+ stats->tx_bytes += tx_bytes;
+
+ /* u32 values are updated without syncp protection. */
+ rx_errs += pcptr->rx_errs;
+ tx_drps += pcptr->tx_drps;
+ }
+ stats->rx_errors = rx_errs;
+ stats->rx_dropped = rx_errs;
+ stats->tx_dropped = tx_drps;
+ }
+ return stats;
+}
+
+static int ipvlan_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct net_device *phy_dev = ipvlan->phy_dev;
+
+ return vlan_vid_add(phy_dev, proto, vid);
+}
+
+static int ipvlan_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct net_device *phy_dev = ipvlan->phy_dev;
+
+ vlan_vid_del(phy_dev, proto, vid);
+ return 0;
+}
+
+static const struct net_device_ops ipvlan_netdev_ops = {
+ .ndo_init = ipvlan_init,
+ .ndo_uninit = ipvlan_uninit,
+ .ndo_open = ipvlan_open,
+ .ndo_stop = ipvlan_stop,
+ .ndo_start_xmit = ipvlan_start_xmit,
+ .ndo_fix_features = ipvlan_fix_features,
+ .ndo_change_rx_flags = ipvlan_change_rx_flags,
+ .ndo_set_rx_mode = ipvlan_set_multicast_mac_filter,
+ .ndo_get_stats64 = ipvlan_get_stats64,
+ .ndo_vlan_rx_add_vid = ipvlan_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ipvlan_vlan_rx_kill_vid,
+};
+
+/* ---- Ethernet Header Ops ---- */
+static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned len)
+{
+ const struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct net_device *phy_dev = ipvlan->phy_dev;
+
+ /* TODO Probably use a different field than dev_addr so that the
+ * mac-address on the virtual device is portable and can be carried
+ * while the packets use the mac-addr on the physical device.
+ */
+ return dev_hard_header(skb, phy_dev, type, daddr,
+ saddr ? : dev->dev_addr, len);
+}
+
+static const struct header_ops ipvlan_header_ops = {
+ .create = ipvlan_hard_header,
+ .rebuild = eth_rebuild_header,
+ .parse = eth_header_parse,
+ .cache = eth_header_cache,
+ .cache_update = eth_header_cache_update,
+};
+
+/* ---- Ethtool ops ---- */
+static int ipvlan_ethtool_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ const struct ipvl_dev *ipvlan = netdev_priv(dev);
+ return __ethtool_get_settings(ipvlan->phy_dev, cmd);
+}
+
+static void ipvlan_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ strlcpy(drvinfo->driver, IPVLAN_DRV, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, IPV_DRV_VER, sizeof(drvinfo->version));
+}
+
+static const struct ethtool_ops ipvlan_ethtool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_settings = ipvlan_ethtool_get_settings,
+ .get_drvinfo = ipvlan_ethtool_get_drvinfo,
+};
+
+/* ---- Link-ops ---- */
+static int ipvlan_nl_changelink(struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev);
+
+ if (data && data[IFLA_IPVLAN_MODE]) {
+ u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
+ ipvlan_set_port_mode(port, nmode);
+ }
+
+ return 0;
+}
+
+static size_t ipvlan_nl_getsize(const struct net_device *dev)
+{
+ return (0
+ + nla_total_size(2) /* IFLA_IPVLAN_MODE */
+ );
+}
+
+static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ if (data && data[IFLA_IPVLAN_MODE]) {
+ u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
+
+ if (mode < IPVLAN_MODE_L2 || mode >= IPVLAN_MODE_MAX)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ipvlan_nl_fillinfo(struct sk_buff *skb,
+ const struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev);
+ int ret = -EINVAL;
+
+ if (!port)
+ goto err;
+
+ ret = -EMSGSIZE;
+ if (nla_put_u16(skb, IFLA_IPVLAN_MODE, port->mode))
+ goto err;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int ipvlan_link_new(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port;
+ struct net_device *phy_dev;
+ int err;
+
+ ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+ if (!tb[IFLA_LINK]) {
+ ipvlan_dbg(3, "%s[%d]: Returning -EINVAL...\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ phy_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
+ if (phy_dev == NULL) {
+ ipvlan_dbg(3, "%s[%d]: Returning -ENODEV...\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
+
+ if (ipvlan_dev_slave(phy_dev)) {
+ struct ipvl_dev *tmp = netdev_priv(phy_dev);
+
+ phy_dev = tmp->phy_dev;
+ } else if (!ipvlan_dev_master(phy_dev)) {
+ err = ipvlan_port_create(phy_dev);
+ if (err < 0) {
+ ipvlan_dbg(3, "%s[%d]: Returning error (%d)...\n",
+ __func__, __LINE__, err);
+ return err;
+ }
+ }
+
+ port = ipvlan_port_get_rtnl(phy_dev);
+ /* Get the mode if specified. */
+ if (data && data[IFLA_IPVLAN_MODE])
+ port->mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
+
+ ipvlan->phy_dev = phy_dev;
+ ipvlan->dev = dev;
+ ipvlan->port = port;
+ ipvlan->sfeatures = IPVLAN_FEATURES;
+ INIT_LIST_HEAD(&ipvlan->addrs);
+ ipvlan->ipv4cnt = 0;
+ ipvlan->ipv6cnt = 0;
+
+ /* Probably put a random address here to be presented to the
+ * world but keep using the physical-dev address for the outgoing
+ * packets.
+ */
+ memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN);
+
+ /* Mark this as a IPVLAN secondary device. */
+ dev->priv_flags |= IFF_IPVLAN_SLAVE;
+
+ port->count += 1;
+ err = register_netdevice(dev);
+ if (err < 0) {
+ ipvlan_dbg(3, "%s[%d]: Returning error...\n",
+ __func__, __LINE__);
+ goto ipvlan_destroy_port;
+ }
+ err = netdev_upper_dev_link(phy_dev, dev);
+ if (err) {
+ ipvlan_dbg(3, "%s[%d]: Returning error (%d)\n",
+ __func__, __LINE__, err);
+ goto ipvlan_destroy_port;
+ }
+
+ list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans);
+ netif_stacked_transfer_operstate(phy_dev, dev);
+ ipvlan_dbg(3, "%s[%d]: Returning success...\n", __func__, __LINE__);
+ return 0;
+
+ipvlan_destroy_port:
+ port->count -= 1;
+ if (!port->count)
+ ipvlan_port_destroy(phy_dev);
+
+ ipvlan_dbg(3, "%s[%d]: Return (after Destroying Port)",
+ __func__, __LINE__);
+ return err;
+}
+
+static void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_addr *addr, *next;
+
+ if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
+ list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
+ ipvlan_ht_addr_del(addr, !dev->dismantle);
+ list_del_rcu(&addr->anode);
+ }
+ }
+ list_del_rcu(&ipvlan->pnode);
+ unregister_netdevice_queue(dev, head);
+ netdev_upper_dev_unlink(ipvlan->phy_dev, dev);
+}
+
+static void ipvlan_link_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+
+ dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
+ dev->priv_flags |= IFF_UNICAST_FLT;
+ dev->netdev_ops = &ipvlan_netdev_ops;
+ dev->destructor = free_netdev;
+ dev->header_ops = &ipvlan_header_ops;
+ dev->ethtool_ops = &ipvlan_ethtool_ops;
+ dev->tx_queue_len = 0;
+}
+
+static const struct nla_policy ipvlan_nl_policy[IFLA_IPVLAN_MAX + 1] =
+{
+ [IFLA_IPVLAN_MODE] = { .type = NLA_U16 },
+};
+
+static struct rtnl_link_ops ipvlan_link_ops = {
+ .kind = "ipvlan",
+ .priv_size = sizeof(struct ipvl_dev),
+
+ .get_size = ipvlan_nl_getsize,
+ .policy = ipvlan_nl_policy,
+ .validate = ipvlan_nl_validate,
+ .fill_info = ipvlan_nl_fillinfo,
+ .changelink = ipvlan_nl_changelink,
+ .maxtype = IFLA_IPVLAN_MAX,
+
+ .setup = ipvlan_link_setup,
+ .newlink = ipvlan_link_new,
+ .dellink = ipvlan_link_delete,
+};
+
+int ipvlan_link_register(struct rtnl_link_ops *ops)
+{
+ return rtnl_link_register(ops);
+}
+
+/* ---- IPVLAN event handling ---- */
+static int ipvlan_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct ipvl_dev *ipvlan, *next;
+ struct ipvl_port *port;
+ LIST_HEAD(lst_kill);
+
+ if (!ipvlan_dev_master(dev))
+ return NOTIFY_DONE;
+
+ port = ipvlan_port_get_rtnl(dev);
+
+ switch (event) {
+ case NETDEV_CHANGE:
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode)
+ netif_stacked_transfer_operstate(ipvlan->phy_dev,
+ ipvlan->dev);
+ break;
+
+ case NETDEV_UNREGISTER:
+ if (dev->reg_state != NETREG_UNREGISTERING)
+ break;
+
+ list_for_each_entry_safe(ipvlan, next, &port->ipvlans,
+ pnode)
+ ipvlan->dev->rtnl_link_ops->dellink(ipvlan->dev,
+ &lst_kill);
+ unregister_netdevice_many(&lst_kill);
+ break;
+
+ case NETDEV_FEAT_CHANGE:
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ ipvlan->dev->features = dev->features & IPVLAN_FEATURES;
+ ipvlan->dev->gso_max_size = dev->gso_max_size;
+ netdev_features_change(ipvlan->dev);
+ }
+ break;
+
+ case NETDEV_CHANGEMTU:
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ ipvlan_adjust_mtu(ipvlan, dev);
+ }
+ break;
+
+ case NETDEV_PRE_TYPE_CHANGE:
+ /* Forbid underlying device to change its type. */
+ return NOTIFY_BAD;
+ }
+ return NOTIFY_DONE;
+}
+
+static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+ struct ipvl_addr *addr;
+
+ if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) {
+ pr_warn("%s[%d]: Failed IPv6=%x:%x:%x:%x address for %s intf\n",
+ __func__, __LINE__, ip6_addr->s6_addr32[0],
+ ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+ ip6_addr->s6_addr32[3], ipvlan->dev->name);
+ return -EINVAL;
+ }
+ if ((addr = kzalloc(sizeof(struct ipvl_addr), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ ipvlan_dbg(1, "%s[%d]: Adding IPv6=%x:%x:%x:%x address for %s intf\n",
+ __func__, __LINE__, ip6_addr->s6_addr32[0],
+ ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+ ip6_addr->s6_addr32[3], ipvlan->dev->name);
+ addr->master = ipvlan;
+ memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr));
+ addr->atype = IPVL_IPV6;
+ list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ ipvlan->ipv6cnt++;
+ ipvlan_ht_addr_add(ipvlan, addr);
+
+ return 0;
+}
+
+static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+ struct ipvl_addr *addr;
+
+ if ((addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true)) ==NULL)
+ return;
+
+ ipvlan_dbg(1,
+ "%s[%d]: Deleting IPv6=%x:%x:%x:%x address for %s intf.\n",
+ __func__, __LINE__, ip6_addr->s6_addr32[0],
+ ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+ ip6_addr->s6_addr32[3], ipvlan->dev->name);
+ /* Delete from the hash-table */
+ ipvlan_ht_addr_del(addr, true);
+ /* Delete from the logical's addr list */
+ list_del_rcu(&addr->anode);
+ ipvlan->ipv6cnt--;
+ WARN_ON(ipvlan->ipv6cnt < 0);
+ kfree_rcu(addr, rcu);
+
+ return;
+}
+
+static int ipvlan_addr6_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct inet6_ifaddr *if6 = (struct inet6_ifaddr *)ptr;
+ struct net_device *dev = (struct net_device *)if6->idev->dev;
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+ ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+ if (!ipvlan_dev_slave(dev))
+ return NOTIFY_DONE;
+
+ if (!ipvlan || !ipvlan->port)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UP:
+ if (ipvlan_add_addr6(ipvlan, &if6->addr))
+ return NOTIFY_BAD;
+ break;
+
+ case NETDEV_DOWN:
+ ipvlan_del_addr6(ipvlan, &if6->addr);
+ break;
+ }
+
+ ipvlan_dbg(3, "%s[%d]: Leaving...\n", __func__, __LINE__);
+ return NOTIFY_OK;
+}
+
+static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
+{
+ struct ipvl_addr *addr;
+
+ if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) {
+ pr_warn("%s[%d]: Failed to add IPv4=%x on %s intf.\n",
+ __func__, __LINE__, ntohl(ip4_addr->s_addr),
+ ipvlan->dev->name);
+ return -EINVAL;
+ }
+ if ((addr = kzalloc(sizeof(struct ipvl_addr), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ ipvlan_dbg(1, "%s[%d]: Adding IPv4=%x address for %s intf.\n",
+ __func__, __LINE__, ip4_addr->s_addr, ipvlan->dev->name);
+ addr->master = ipvlan;
+ memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr));
+ addr->atype = IPVL_IPV4;
+ list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ ipvlan->ipv4cnt++;
+ ipvlan_ht_addr_add(ipvlan, addr);
+ ipvlan_set_broadcast_mac_filter(ipvlan, true);
+
+ return 0;
+}
+
+static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
+{
+ struct ipvl_addr *addr;
+
+ if ((addr= ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false)) ==NULL)
+ return;
+
+ ipvlan_dbg(1, "%s[%d]: Deleting IPv4=%x address for %s intf.\n",
+ __func__, __LINE__, ip4_addr->s_addr, ipvlan->dev->name);
+ /* Delete from the hash-table */
+ ipvlan_ht_addr_del(addr, true);
+ /* Delete from the logical's addr list */
+ list_del_rcu(&addr->anode);
+ ipvlan->ipv4cnt--;
+ WARN_ON(ipvlan->ipv4cnt < 0);
+ if (!ipvlan->ipv4cnt)
+ ipvlan_set_broadcast_mac_filter(ipvlan, false);
+ kfree_rcu(addr, rcu);
+
+ return;
+}
+
+static int ipvlan_addr4_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *if4 = (struct in_ifaddr *)ptr;
+ struct net_device *dev = (struct net_device *)if4->ifa_dev->dev;
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct in_addr ip4_addr;
+
+ ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+ if (!ipvlan_dev_slave(dev))
+ return NOTIFY_DONE;
+
+ if (!ipvlan || !ipvlan->port)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UP:
+ ip4_addr.s_addr = if4->ifa_address;
+ if (ipvlan_add_addr4(ipvlan, &ip4_addr))
+ return NOTIFY_BAD;
+ break;
+
+ case NETDEV_DOWN:
+ ip4_addr.s_addr = if4->ifa_address;
+ ipvlan_del_addr4(ipvlan, &ip4_addr);
+ break;
+ }
+
+ ipvlan_dbg(3, "%s[%d]: Leaving...\n", __func__, __LINE__);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
+ .notifier_call = ipvlan_addr4_event,
+};
+
+static struct notifier_block ipvlan_notifier_block __read_mostly = {
+ .notifier_call = ipvlan_device_event,
+};
+
+static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = {
+ .notifier_call = ipvlan_addr6_event,
+};
+
+static int __init ipvlan_init_module(void)
+{
+ int err;
+
+ ipvlan_init_secret();
+ register_netdevice_notifier(&ipvlan_notifier_block);
+ register_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+ register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+
+ err = ipvlan_link_register(&ipvlan_link_ops);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+ unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+ unregister_netdevice_notifier(&ipvlan_notifier_block);
+ return err;
+}
+
+static void __exit ipvlan_cleanup_module(void)
+{
+ rtnl_link_unregister(&ipvlan_link_ops);
+ unregister_netdevice_notifier(&ipvlan_notifier_block);
+ unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+ unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+}
+
+module_init(ipvlan_init_module);
+module_exit(ipvlan_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mahesh Bandewar <maheshb@google.com>");
+MODULE_DESCRIPTION("Driver for L3 (IPv6/IPv4) based VLANs");
+MODULE_ALIAS_RTNL_LINK("ipvlan");
diff --git a/drivers/net/ipvlan/ipvlan_sysfs.c b/drivers/net/ipvlan/ipvlan_sysfs.c
new file mode 100644
index 000000000000..ce0a6378d435
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_sysfs.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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.
+ *
+ */
+
+#include "ipvlan.h"
+
+/* ---- SysFS entries ---- */
+#define port_of(ko) container_of(ko, struct ipvl_port, kobj)
+#define ipvl_mode_attr_of(_a) container_of(_a, struct ipvl_mode_attr, attr)
+
+
+/* -- For Master mode -- */
+struct ipvl_mode_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct ipvl_port *port, char *buf);
+ ssize_t (*store)(struct ipvl_port *port, const char *buf, size_t len);
+};
+
+static ssize_t ipvlan_show_mode(struct ipvl_port *port, char *buf)
+{
+ return sprintf(buf, "%hu\n", port->mode);
+}
+
+static ssize_t ipvlan_store_mode(struct ipvl_port *port,
+ const char *buf, size_t count)
+{
+ int ret = count;
+ u16 nval;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ if (!port) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (sscanf(buf, "%hu", &nval) != 1) {
+ pr_warn("%s: no mode specified.\n", port->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (nval != 0 && nval != 1) {
+ pr_warn("%s: mode value can only be 0 or 1.\n",
+ port->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ipvlan_set_port_mode(port, nval);
+
+out:
+ rtnl_unlock();
+ return ret;
+}
+
+static struct ipvl_mode_attr mode_attr =
+ __ATTR(mode, S_IRUGO | S_IWUSR, ipvlan_show_mode, ipvlan_store_mode);
+
+static struct attribute *ipvl_mode_attrs[] = {
+ &mode_attr.attr,
+ NULL
+};
+
+static ssize_t ipvlan_sysfs_show_mode(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct ipvl_mode_attr *attribute = ipvl_mode_attr_of(attr);
+ struct ipvl_port *port = port_of(kobj);
+
+ if (!attribute->show)
+ return -EIO;
+
+ return attribute->show(port, buf);
+}
+
+static ssize_t ipvlan_sysfs_store_mode(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipvl_mode_attr *attribute = ipvl_mode_attr_of(attr);
+ struct ipvl_port *port = port_of(kobj);
+
+ if (!attribute->store)
+ return -EIO;
+
+ return attribute->store(port, buf, count);
+}
+
+static struct sysfs_ops ipvl_mode_sysfs_ops = {
+ .show = ipvlan_sysfs_show_mode,
+ .store = ipvlan_sysfs_store_mode,
+};
+
+static struct kobj_type ipvl_master_ktype = {
+#ifdef CONFIG_SYSFS
+ .sysfs_ops = &ipvl_mode_sysfs_ops,
+#endif
+ .default_attrs = ipvl_mode_attrs,
+};
+
+int ipvlan_add_per_master_sysfs_mode(struct ipvl_port *port,
+ struct net_device *dev)
+{
+ return kobject_init_and_add(&port->kobj, &ipvl_master_ktype,
+ &(dev->dev.kobj), "ipvlan");
+}
+
+void ipvlan_del_per_master_sysfs_mode(struct ipvl_port *port)
+{
+ kobject_put(&port->kobj);
+}
+/* ---- END SysFS entries ---- */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index db63cf459ba1..c6bbc8d4b4f8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1228,6 +1228,8 @@ enum netdev_priv_flags {
IFF_LIVE_ADDR_CHANGE = 1<<20,
IFF_MACVLAN = 1<<21,
IFF_XMIT_DST_RELEASE_PERM = 1<<22,
+ IFF_IPVLAN_MASTER = 1<<23,
+ IFF_IPVLAN_SLAVE = 1<<24,
};
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
@@ -1253,6 +1255,8 @@ enum netdev_priv_flags {
#define IFF_LIVE_ADDR_CHANGE IFF_LIVE_ADDR_CHANGE
#define IFF_MACVLAN IFF_MACVLAN
#define IFF_XMIT_DST_RELEASE_PERM IFF_XMIT_DST_RELEASE_PERM
+#define IFF_IPVLAN_MASTER IFF_IPVLAN_MASTER
+#define IFF_IPVLAN_SLAVE IFF_IPVLAN_SLAVE
/**
* struct net_device - The DEVICE structure.
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 7072d8325016..36bddc233633 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -330,6 +330,21 @@ enum macvlan_macaddr_mode {
#define MACVLAN_FLAG_NOPROMISC 1
+/* IPVLAN section */
+enum {
+ IFLA_IPVLAN_UNSPEC,
+ IFLA_IPVLAN_MODE,
+ __IFLA_IPVLAN_MAX
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+enum ipvlan_mode {
+ IPVLAN_MODE_L2 = 0,
+ IPVLAN_MODE_L3,
+ IPVLAN_MODE_MAX
+};
+
/* VXLAN section */
enum {
IFLA_VXLAN_UNSPEC,
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox