* [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support
@ 2017-10-02 4:36 ` Roopa Prabhu
0 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: netdev, nikolay, stephen, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This series implements arp and nd suppression in the bridge
driver for ethernet vpns. It implements rfc7432, section 10
https://tools.ietf.org/html/rfc7432#section-10
for ethernet VPN deployments. It is similar to the existing
BR_ARP_PROXY flag but has a few semantic differences to conform
to EVPN standard. In case of EVPN, it is mainly used to avoid flooding to
tunnel ports like vxlan/mpls. Unlike the existing flags it suppresses flood
of all neigh discovery packets (arp, nd) to tunnel ports.
Roopa Prabhu (3):
bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd
flood
neigh arp suppress first
bridge: suppress nd messages from going to BR_NEIGH_SUPPRESS ports
include/linux/if_bridge.h | 1 +
include/uapi/linux/if_link.h | 1 +
net/bridge/Makefile | 2 +-
net/bridge/br_arp_nd_proxy.c | 492 +++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_device.c | 18 ++
net/bridge/br_forward.c | 3 +-
net/bridge/br_if.c | 5 +
net/bridge/br_input.c | 73 ++-----
net/bridge/br_netlink.c | 16 +-
net/bridge/br_private.h | 9 +
net/bridge/br_sysfs_if.c | 2 +
11 files changed, 561 insertions(+), 61 deletions(-)
create mode 100644 net/bridge/br_arp_nd_proxy.c
--
2.1.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [Bridge] [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support
@ 2017-10-02 4:36 ` Roopa Prabhu
0 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: nikolay, netdev, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This series implements arp and nd suppression in the bridge
driver for ethernet vpns. It implements rfc7432, section 10
https://tools.ietf.org/html/rfc7432#section-10
for ethernet VPN deployments. It is similar to the existing
BR_ARP_PROXY flag but has a few semantic differences to conform
to EVPN standard. In case of EVPN, it is mainly used to avoid flooding to
tunnel ports like vxlan/mpls. Unlike the existing flags it suppresses flood
of all neigh discovery packets (arp, nd) to tunnel ports.
Roopa Prabhu (3):
bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd
flood
neigh arp suppress first
bridge: suppress nd messages from going to BR_NEIGH_SUPPRESS ports
include/linux/if_bridge.h | 1 +
include/uapi/linux/if_link.h | 1 +
net/bridge/Makefile | 2 +-
net/bridge/br_arp_nd_proxy.c | 492 +++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_device.c | 18 ++
net/bridge/br_forward.c | 3 +-
net/bridge/br_if.c | 5 +
net/bridge/br_input.c | 73 ++-----
net/bridge/br_netlink.c | 16 +-
net/bridge/br_private.h | 9 +
net/bridge/br_sysfs_if.c | 2 +
11 files changed, 561 insertions(+), 61 deletions(-)
create mode 100644 net/bridge/br_arp_nd_proxy.c
--
2.1.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net-next 1/3] bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd flood
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
@ 2017-10-02 4:36 ` Roopa Prabhu
-1 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: netdev, nikolay, stephen, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch adds a new bridge port flag BR_NEIGH_SUPPRESS to
suppress arp and nd flood on bridge ports. It implements
rfc7432, section 10.
https://tools.ietf.org/html/rfc7432#section-10
for ethernet VPN deployments. It is similar to the existing
BR_ARP_PROXY flag but has a few semantic differences to conform
to EVPN standard. In case of EVPN, it is mainly used to
avoid flooding to tunnel ports like vxlan. Unlike the existing
flags it suppresses flood of all neigh discovery packets
(arp, nd) to tunnel ports.
This patch adds netlink and sysfs support to set this bridge port
flag.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
include/linux/if_bridge.h | 1 +
include/uapi/linux/if_link.h | 1 +
net/bridge/Makefile | 2 +-
net/bridge/br_arp_nd_proxy.c | 27 +++++++++++++++++++++++++++
net/bridge/br_forward.c | 3 ++-
net/bridge/br_if.c | 5 +++++
net/bridge/br_netlink.c | 16 +++++++++++++++-
net/bridge/br_private.h | 2 ++
net/bridge/br_sysfs_if.c | 2 ++
9 files changed, 56 insertions(+), 3 deletions(-)
create mode 100644 net/bridge/br_arp_nd_proxy.c
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 3cd18ac..316ee11 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -49,6 +49,7 @@ struct br_ip_list {
#define BR_MULTICAST_TO_UNICAST BIT(12)
#define BR_VLAN_TUNNEL BIT(13)
#define BR_BCAST_FLOOD BIT(14)
+#define BR_NEIGH_SUPPRESS BIT(15)
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 8d062c5..0882e86 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -325,6 +325,7 @@ enum {
IFLA_BRPORT_MCAST_TO_UCAST,
IFLA_BRPORT_VLAN_TUNNEL,
IFLA_BRPORT_BCAST_FLOOD,
+ IFLA_BRPORT_NEIGH_SUPPRESS,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 40b1ede..4aee55f 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_BRIDGE) += bridge.o
bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o br_netlink.o \
- br_netlink_tunnel.o
+ br_netlink_tunnel.o br_arp_nd_proxy.o
bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
new file mode 100644
index 0000000..e191bb5
--- /dev/null
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -0,0 +1,27 @@
+/*
+ * Handle bridge arp/nd proxy/suppress
+ *
+ * Authors:
+ * Roopa Prabhu <roopa@cumulusnetworks.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 <linux/kernel.h>
+#include "br_private.h"
+
+void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
+{
+ struct net_bridge_port *p;
+
+ br->neigh_suppress_enabled = false;
+ list_for_each_entry(p, &br->port_list, list) {
+ if (p->flags & BR_NEIGH_SUPPRESS) {
+ br->neigh_suppress_enabled = true;
+ break;
+ }
+ }
+}
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 48fb174..7a50dc5 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -204,7 +204,8 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb,
/* Do not flood to ports that enable proxy ARP */
if (p->flags & BR_PROXYARP)
continue;
- if ((p->flags & BR_PROXYARP_WIFI) &&
+ if ((p->flags & BR_PROXYARP_WIFI ||
+ p->flags & BR_NEIGH_SUPPRESS) &&
BR_INPUT_SKB_CB(skb)->proxyarp_replied)
continue;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f3aef22..8f615d4 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -310,6 +310,8 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
del_nbp(p);
}
+ br_recalculate_neigh_suppress_enabled(br);
+
br_fdb_delete_by_port(br, NULL, 0, 1);
cancel_delayed_work_sync(&br->gc_work);
@@ -653,4 +655,7 @@ void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
if (mask & BR_AUTO_MASK)
nbp_update_port_count(br);
+
+ if (mask & BR_NEIGH_SUPPRESS)
+ br_recalculate_neigh_suppress_enabled(br);
}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 3bc8907..abe1d8d 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -138,6 +138,7 @@ static inline size_t br_port_info_size(void)
+ nla_total_size(1) /* IFLA_BRPORT_PROXYARP */
+ nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */
+ nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */
+ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+ nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
@@ -208,7 +209,9 @@ static int br_port_fill_attrs(struct sk_buff *skb,
p->topology_change_ack) ||
nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags &
- BR_VLAN_TUNNEL)))
+ BR_VLAN_TUNNEL)) ||
+ nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS, !!(p->flags &
+ BR_NEIGH_SUPPRESS)))
return -EMSGSIZE;
timerval = br_timer_value(&p->message_age_timer);
@@ -618,6 +621,9 @@ static int br_afspec(struct net_bridge *br,
}
}
+ if (p)
+ br_recalculate_neigh_suppress_enabled(p->br);
+
return err;
}
@@ -689,6 +695,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
{
unsigned long old_flags = p->flags;
bool br_vlan_tunnel_old = false;
+ int neigh_suppress_old = 0;
int err;
err = br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
@@ -773,6 +780,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
return err;
}
#endif
+
+ neigh_suppress_old = (p->flags & BR_NEIGH_SUPPRESS);
+ br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS,
+ BR_NEIGH_SUPPRESS);
+ if (neigh_suppress_old != (p->flags & BR_NEIGH_SUPPRESS))
+ br_recalculate_neigh_suppress_enabled(p->br);
+
br_port_flags_change(p, old_flags ^ p->flags);
return 0;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index e870cfc..7dacd83 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -396,6 +396,7 @@ struct net_bridge {
#ifdef CONFIG_NET_SWITCHDEV
int offload_fwd_mark;
#endif
+ bool neigh_suppress_enabled;
};
struct br_input_skb_cb {
@@ -1130,4 +1131,5 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
}
#endif /* CONFIG_NET_SWITCHDEV */
+void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
#endif
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 5d5d413a..2467213 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -174,6 +174,7 @@ BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP);
BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI);
BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD);
BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
+BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -223,6 +224,7 @@ static const struct brport_attribute *brport_attrs[] = {
&brport_attr_proxyarp_wifi,
&brport_attr_multicast_flood,
&brport_attr_broadcast_flood,
+ &brport_attr_neigh_suppress,
NULL
};
--
2.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Bridge] [PATCH net-next 1/3] bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd flood
@ 2017-10-02 4:36 ` Roopa Prabhu
0 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: nikolay, netdev, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch adds a new bridge port flag BR_NEIGH_SUPPRESS to
suppress arp and nd flood on bridge ports. It implements
rfc7432, section 10.
https://tools.ietf.org/html/rfc7432#section-10
for ethernet VPN deployments. It is similar to the existing
BR_ARP_PROXY flag but has a few semantic differences to conform
to EVPN standard. In case of EVPN, it is mainly used to
avoid flooding to tunnel ports like vxlan. Unlike the existing
flags it suppresses flood of all neigh discovery packets
(arp, nd) to tunnel ports.
This patch adds netlink and sysfs support to set this bridge port
flag.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
include/linux/if_bridge.h | 1 +
include/uapi/linux/if_link.h | 1 +
net/bridge/Makefile | 2 +-
net/bridge/br_arp_nd_proxy.c | 27 +++++++++++++++++++++++++++
net/bridge/br_forward.c | 3 ++-
net/bridge/br_if.c | 5 +++++
net/bridge/br_netlink.c | 16 +++++++++++++++-
net/bridge/br_private.h | 2 ++
net/bridge/br_sysfs_if.c | 2 ++
9 files changed, 56 insertions(+), 3 deletions(-)
create mode 100644 net/bridge/br_arp_nd_proxy.c
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 3cd18ac..316ee11 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -49,6 +49,7 @@ struct br_ip_list {
#define BR_MULTICAST_TO_UNICAST BIT(12)
#define BR_VLAN_TUNNEL BIT(13)
#define BR_BCAST_FLOOD BIT(14)
+#define BR_NEIGH_SUPPRESS BIT(15)
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 8d062c5..0882e86 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -325,6 +325,7 @@ enum {
IFLA_BRPORT_MCAST_TO_UCAST,
IFLA_BRPORT_VLAN_TUNNEL,
IFLA_BRPORT_BCAST_FLOOD,
+ IFLA_BRPORT_NEIGH_SUPPRESS,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 40b1ede..4aee55f 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_BRIDGE) += bridge.o
bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o br_netlink.o \
- br_netlink_tunnel.o
+ br_netlink_tunnel.o br_arp_nd_proxy.o
bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
new file mode 100644
index 0000000..e191bb5
--- /dev/null
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -0,0 +1,27 @@
+/*
+ * Handle bridge arp/nd proxy/suppress
+ *
+ * Authors:
+ * Roopa Prabhu <roopa@cumulusnetworks.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 <linux/kernel.h>
+#include "br_private.h"
+
+void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
+{
+ struct net_bridge_port *p;
+
+ br->neigh_suppress_enabled = false;
+ list_for_each_entry(p, &br->port_list, list) {
+ if (p->flags & BR_NEIGH_SUPPRESS) {
+ br->neigh_suppress_enabled = true;
+ break;
+ }
+ }
+}
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 48fb174..7a50dc5 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -204,7 +204,8 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb,
/* Do not flood to ports that enable proxy ARP */
if (p->flags & BR_PROXYARP)
continue;
- if ((p->flags & BR_PROXYARP_WIFI) &&
+ if ((p->flags & BR_PROXYARP_WIFI ||
+ p->flags & BR_NEIGH_SUPPRESS) &&
BR_INPUT_SKB_CB(skb)->proxyarp_replied)
continue;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f3aef22..8f615d4 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -310,6 +310,8 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
del_nbp(p);
}
+ br_recalculate_neigh_suppress_enabled(br);
+
br_fdb_delete_by_port(br, NULL, 0, 1);
cancel_delayed_work_sync(&br->gc_work);
@@ -653,4 +655,7 @@ void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
if (mask & BR_AUTO_MASK)
nbp_update_port_count(br);
+
+ if (mask & BR_NEIGH_SUPPRESS)
+ br_recalculate_neigh_suppress_enabled(br);
}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 3bc8907..abe1d8d 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -138,6 +138,7 @@ static inline size_t br_port_info_size(void)
+ nla_total_size(1) /* IFLA_BRPORT_PROXYARP */
+ nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */
+ nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */
+ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+ nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
@@ -208,7 +209,9 @@ static int br_port_fill_attrs(struct sk_buff *skb,
p->topology_change_ack) ||
nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags &
- BR_VLAN_TUNNEL)))
+ BR_VLAN_TUNNEL)) ||
+ nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS, !!(p->flags &
+ BR_NEIGH_SUPPRESS)))
return -EMSGSIZE;
timerval = br_timer_value(&p->message_age_timer);
@@ -618,6 +621,9 @@ static int br_afspec(struct net_bridge *br,
}
}
+ if (p)
+ br_recalculate_neigh_suppress_enabled(p->br);
+
return err;
}
@@ -689,6 +695,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
{
unsigned long old_flags = p->flags;
bool br_vlan_tunnel_old = false;
+ int neigh_suppress_old = 0;
int err;
err = br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
@@ -773,6 +780,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
return err;
}
#endif
+
+ neigh_suppress_old = (p->flags & BR_NEIGH_SUPPRESS);
+ br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS,
+ BR_NEIGH_SUPPRESS);
+ if (neigh_suppress_old != (p->flags & BR_NEIGH_SUPPRESS))
+ br_recalculate_neigh_suppress_enabled(p->br);
+
br_port_flags_change(p, old_flags ^ p->flags);
return 0;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index e870cfc..7dacd83 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -396,6 +396,7 @@ struct net_bridge {
#ifdef CONFIG_NET_SWITCHDEV
int offload_fwd_mark;
#endif
+ bool neigh_suppress_enabled;
};
struct br_input_skb_cb {
@@ -1130,4 +1131,5 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
}
#endif /* CONFIG_NET_SWITCHDEV */
+void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
#endif
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 5d5d413a..2467213 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -174,6 +174,7 @@ BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP);
BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI);
BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD);
BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
+BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -223,6 +224,7 @@ static const struct brport_attribute *brport_attrs[] = {
&brport_attr_proxyarp_wifi,
&brport_attr_multicast_flood,
&brport_attr_broadcast_flood,
+ &brport_attr_neigh_suppress,
NULL
};
--
2.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 2/3] bridge: suppress arp pkts on BR_NEIGH_SUPPRESS ports
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
@ 2017-10-02 4:36 ` Roopa Prabhu
-1 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: netdev, nikolay, stephen, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch avoids flooding and proxies arp packets
for BR_NEIGH_SUPPRESS ports.
Moves existing br_do_proxy_arp to br_do_proxy_suppress_arp
to support both proxy arp and neigh suppress.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
net/bridge/br_arp_nd_proxy.c | 186 +++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_device.c | 8 ++
net/bridge/br_input.c | 63 ++-------------
net/bridge/br_private.h | 3 +
4 files changed, 202 insertions(+), 58 deletions(-)
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index e191bb5..5153510 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -11,6 +11,13 @@
*/
#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/neighbour.h>
+#include <net/arp.h>
+#include <linux/if_vlan.h>
+#include <linux/inetdevice.h>
+#include <net/addrconf.h>
#include "br_private.h"
void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
@@ -25,3 +32,182 @@ void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
}
}
}
+
+static void br_arp_send(struct net_bridge_port *p, int type, int ptype,
+ __be32 dest_ip, struct net_device *dev,
+ __be32 src_ip, const unsigned char *dest_hw,
+ const unsigned char *src_hw,
+ const unsigned char *target_hw,
+ __be16 vlan_proto, u16 vlan_tci)
+{
+ struct sk_buff *skb;
+
+ netdev_dbg(dev, "arp send dev %s dst %pI4 dst_hw %pM src %pI4 src_hw %pM\n",
+ dev->name, &dest_ip, dest_hw, &src_ip, src_hw);
+
+ if (!vlan_tci) {
+ arp_send(type, ptype, dest_ip, dev, src_ip,
+ dest_hw, src_hw, target_hw);
+ return;
+ }
+
+ skb = arp_create(type, ptype, dest_ip, dev, src_ip,
+ dest_hw, src_hw, target_hw);
+ if (!skb)
+ return;
+
+ if (p) {
+ struct net_bridge_vlan_group *vg;
+ u16 pvid;
+
+ vg = nbp_vlan_group_rcu(p);
+ pvid = br_get_pvid(vg);
+ if (pvid && pvid == vlan_tci)
+ vlan_tci = 0;
+ }
+
+ if (vlan_tci) {
+ skb = vlan_insert_tag_set_proto(skb, vlan_proto,
+ vlan_tci);
+ if (!skb) {
+ net_err_ratelimited("%s: failed to insert VLAN tag\n",
+ __func__);
+ return;
+ }
+ }
+
+ arp_xmit(skb);
+}
+
+static int br_chk_addr_ip(struct net_device *dev, void *data)
+{
+ __be32 ip = *(__be32 *)data;
+ struct in_device *in_dev;
+ __be32 addr = 0;
+
+ in_dev = __in_dev_get_rcu(dev);
+ if (in_dev)
+ addr = inet_confirm_addr(dev_net(dev), in_dev, 0, ip,
+ RT_SCOPE_HOST);
+
+ if (addr == ip)
+ return 1;
+
+ return 0;
+}
+
+static bool br_is_local_ip(struct net_device *dev, __be32 ip)
+{
+ if (br_chk_addr_ip(dev, &ip))
+ return true;
+
+ /* check if ip is configured on upper dev */
+ if (netdev_walk_all_upper_dev_rcu(dev, br_chk_addr_ip, &ip))
+ return true;
+
+ return false;
+}
+
+void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p)
+{
+ struct net_device *dev = br->dev;
+ struct net_device *vlandev = NULL;
+ struct neighbour *n;
+ struct arphdr *parp;
+ u8 *arpptr, *sha;
+ __be32 sip, tip;
+
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
+
+ if ((dev->flags & IFF_NOARP) ||
+ !pskb_may_pull(skb, arp_hdr_len(dev)))
+ return;
+
+ parp = arp_hdr(skb);
+
+ if (parp->ar_pro != htons(ETH_P_IP) ||
+ parp->ar_hln != dev->addr_len ||
+ parp->ar_pln != 4)
+ return;
+
+ arpptr = (u8 *)parp + sizeof(struct arphdr);
+ sha = arpptr;
+ arpptr += dev->addr_len; /* sha */
+ memcpy(&sip, arpptr, sizeof(sip));
+ arpptr += sizeof(sip);
+ arpptr += dev->addr_len; /* tha */
+ memcpy(&tip, arpptr, sizeof(tip));
+
+ if (ipv4_is_loopback(tip) ||
+ ipv4_is_multicast(tip))
+ return;
+
+ if (br->neigh_suppress_enabled) {
+ if (p && (p->flags & BR_NEIGH_SUPPRESS))
+ return;
+ if (ipv4_is_zeronet(sip) || sip == tip) {
+ /* prevent flooding to neigh suppress ports */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+ }
+
+ if (parp->ar_op != htons(ARPOP_REQUEST))
+ return;
+
+ if (vid != 0) {
+ vlandev = __vlan_find_dev_deep_rcu(br->dev, skb->vlan_proto,
+ vid);
+ if (!vlandev)
+ return;
+ } else {
+ vlandev = dev;
+ }
+
+ if (br->neigh_suppress_enabled && br_is_local_ip(vlandev, tip)) {
+ /* its our local ip, so don't proxy reply
+ * and don't forward to neigh suppress ports
+ */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ n = neigh_lookup(&arp_tbl, &tip, vlandev);
+ if (n) {
+ struct net_bridge_fdb_entry *f;
+
+ if (!(n->nud_state & NUD_VALID)) {
+ neigh_release(n);
+ return;
+ }
+
+ f = br_fdb_find_rcu(br, n->ha, vid);
+ if (f) {
+ bool replied = false;
+
+ if (f->dst && ((p->flags & BR_PROXYARP) ||
+ (f->dst->flags & BR_PROXYARP_WIFI) ||
+ (f->dst->flags & BR_NEIGH_SUPPRESS))) {
+ if (!vid)
+ br_arp_send(p, ARPOP_REPLY, ETH_P_ARP,
+ sip, skb->dev, tip, sha,
+ n->ha, sha, 0, 0);
+ else
+ br_arp_send(p, ARPOP_REPLY, ETH_P_ARP,
+ sip, skb->dev, tip, sha,
+ n->ha, sha, skb->vlan_proto,
+ skb_vlan_tag_get(skb));
+ replied = true;
+ }
+
+ /* If we have replied or as long as we know the
+ * mac, indicate to arp replied
+ */
+ if (replied || br->neigh_suppress_enabled)
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ }
+
+ neigh_release(n);
+ }
+}
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index f6b6a92..8961c25 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -39,6 +39,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
const struct nf_br_ops *nf_ops;
const unsigned char *dest;
+ struct ethhdr *eth;
u16 vid = 0;
rcu_read_lock();
@@ -57,11 +58,18 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
BR_INPUT_SKB_CB(skb)->brdev = dev;
skb_reset_mac_header(skb);
+ eth = eth_hdr(skb);
skb_pull(skb, ETH_HLEN);
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid))
goto out;
+ if (IS_ENABLED(CONFIG_INET) && br->neigh_suppress_enabled &&
+ (ntohs(eth->h_proto) == ETH_P_ARP ||
+ ntohs(eth->h_proto) == ETH_P_RARP)) {
+ br_do_proxy_suppress_arp(skb, br, vid, NULL);
+ }
+
dest = eth_hdr(skb)->h_dest;
if (is_broadcast_ether_addr(dest)) {
br_flood(br, skb, BR_PKT_BROADCAST, false, true);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 7637f58..7637a23 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -71,62 +71,6 @@ static int br_pass_frame_up(struct sk_buff *skb)
br_netif_receive_skb);
}
-static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
- u16 vid, struct net_bridge_port *p)
-{
- struct net_device *dev = br->dev;
- struct neighbour *n;
- struct arphdr *parp;
- u8 *arpptr, *sha;
- __be32 sip, tip;
-
- BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
-
- if ((dev->flags & IFF_NOARP) ||
- !pskb_may_pull(skb, arp_hdr_len(dev)))
- return;
-
- parp = arp_hdr(skb);
-
- if (parp->ar_pro != htons(ETH_P_IP) ||
- parp->ar_op != htons(ARPOP_REQUEST) ||
- parp->ar_hln != dev->addr_len ||
- parp->ar_pln != 4)
- return;
-
- arpptr = (u8 *)parp + sizeof(struct arphdr);
- sha = arpptr;
- arpptr += dev->addr_len; /* sha */
- memcpy(&sip, arpptr, sizeof(sip));
- arpptr += sizeof(sip);
- arpptr += dev->addr_len; /* tha */
- memcpy(&tip, arpptr, sizeof(tip));
-
- if (ipv4_is_loopback(tip) ||
- ipv4_is_multicast(tip))
- return;
-
- n = neigh_lookup(&arp_tbl, &tip, dev);
- if (n) {
- struct net_bridge_fdb_entry *f;
-
- if (!(n->nud_state & NUD_VALID)) {
- neigh_release(n);
- return;
- }
-
- f = br_fdb_find_rcu(br, n->ha, vid);
- if (f && ((p->flags & BR_PROXYARP) ||
- (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)))) {
- arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip,
- sha, n->ha, sha);
- BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
- }
-
- neigh_release(n);
- }
-}
-
/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
@@ -171,8 +115,11 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
- if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP))
- br_do_proxy_arp(skb, br, vid, p);
+ if (IS_ENABLED(CONFIG_INET) &&
+ (skb->protocol == htons(ETH_P_ARP) ||
+ skb->protocol == htons(ETH_P_RARP))) {
+ br_do_proxy_suppress_arp(skb, br, vid, p);
+ }
switch (pkt_type) {
case BR_PKT_MULTICAST:
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 7dacd83..3006f0d 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1131,5 +1131,8 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
}
#endif /* CONFIG_NET_SWITCHDEV */
+/* br_arp_nd_proxy.c */
void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
+void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p);
#endif
--
2.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Bridge] [PATCH net-next 2/3] bridge: suppress arp pkts on BR_NEIGH_SUPPRESS ports
@ 2017-10-02 4:36 ` Roopa Prabhu
0 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: nikolay, netdev, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch avoids flooding and proxies arp packets
for BR_NEIGH_SUPPRESS ports.
Moves existing br_do_proxy_arp to br_do_proxy_suppress_arp
to support both proxy arp and neigh suppress.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
net/bridge/br_arp_nd_proxy.c | 186 +++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_device.c | 8 ++
net/bridge/br_input.c | 63 ++-------------
net/bridge/br_private.h | 3 +
4 files changed, 202 insertions(+), 58 deletions(-)
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index e191bb5..5153510 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -11,6 +11,13 @@
*/
#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/neighbour.h>
+#include <net/arp.h>
+#include <linux/if_vlan.h>
+#include <linux/inetdevice.h>
+#include <net/addrconf.h>
#include "br_private.h"
void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
@@ -25,3 +32,182 @@ void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
}
}
}
+
+static void br_arp_send(struct net_bridge_port *p, int type, int ptype,
+ __be32 dest_ip, struct net_device *dev,
+ __be32 src_ip, const unsigned char *dest_hw,
+ const unsigned char *src_hw,
+ const unsigned char *target_hw,
+ __be16 vlan_proto, u16 vlan_tci)
+{
+ struct sk_buff *skb;
+
+ netdev_dbg(dev, "arp send dev %s dst %pI4 dst_hw %pM src %pI4 src_hw %pM\n",
+ dev->name, &dest_ip, dest_hw, &src_ip, src_hw);
+
+ if (!vlan_tci) {
+ arp_send(type, ptype, dest_ip, dev, src_ip,
+ dest_hw, src_hw, target_hw);
+ return;
+ }
+
+ skb = arp_create(type, ptype, dest_ip, dev, src_ip,
+ dest_hw, src_hw, target_hw);
+ if (!skb)
+ return;
+
+ if (p) {
+ struct net_bridge_vlan_group *vg;
+ u16 pvid;
+
+ vg = nbp_vlan_group_rcu(p);
+ pvid = br_get_pvid(vg);
+ if (pvid && pvid == vlan_tci)
+ vlan_tci = 0;
+ }
+
+ if (vlan_tci) {
+ skb = vlan_insert_tag_set_proto(skb, vlan_proto,
+ vlan_tci);
+ if (!skb) {
+ net_err_ratelimited("%s: failed to insert VLAN tag\n",
+ __func__);
+ return;
+ }
+ }
+
+ arp_xmit(skb);
+}
+
+static int br_chk_addr_ip(struct net_device *dev, void *data)
+{
+ __be32 ip = *(__be32 *)data;
+ struct in_device *in_dev;
+ __be32 addr = 0;
+
+ in_dev = __in_dev_get_rcu(dev);
+ if (in_dev)
+ addr = inet_confirm_addr(dev_net(dev), in_dev, 0, ip,
+ RT_SCOPE_HOST);
+
+ if (addr == ip)
+ return 1;
+
+ return 0;
+}
+
+static bool br_is_local_ip(struct net_device *dev, __be32 ip)
+{
+ if (br_chk_addr_ip(dev, &ip))
+ return true;
+
+ /* check if ip is configured on upper dev */
+ if (netdev_walk_all_upper_dev_rcu(dev, br_chk_addr_ip, &ip))
+ return true;
+
+ return false;
+}
+
+void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p)
+{
+ struct net_device *dev = br->dev;
+ struct net_device *vlandev = NULL;
+ struct neighbour *n;
+ struct arphdr *parp;
+ u8 *arpptr, *sha;
+ __be32 sip, tip;
+
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
+
+ if ((dev->flags & IFF_NOARP) ||
+ !pskb_may_pull(skb, arp_hdr_len(dev)))
+ return;
+
+ parp = arp_hdr(skb);
+
+ if (parp->ar_pro != htons(ETH_P_IP) ||
+ parp->ar_hln != dev->addr_len ||
+ parp->ar_pln != 4)
+ return;
+
+ arpptr = (u8 *)parp + sizeof(struct arphdr);
+ sha = arpptr;
+ arpptr += dev->addr_len; /* sha */
+ memcpy(&sip, arpptr, sizeof(sip));
+ arpptr += sizeof(sip);
+ arpptr += dev->addr_len; /* tha */
+ memcpy(&tip, arpptr, sizeof(tip));
+
+ if (ipv4_is_loopback(tip) ||
+ ipv4_is_multicast(tip))
+ return;
+
+ if (br->neigh_suppress_enabled) {
+ if (p && (p->flags & BR_NEIGH_SUPPRESS))
+ return;
+ if (ipv4_is_zeronet(sip) || sip == tip) {
+ /* prevent flooding to neigh suppress ports */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+ }
+
+ if (parp->ar_op != htons(ARPOP_REQUEST))
+ return;
+
+ if (vid != 0) {
+ vlandev = __vlan_find_dev_deep_rcu(br->dev, skb->vlan_proto,
+ vid);
+ if (!vlandev)
+ return;
+ } else {
+ vlandev = dev;
+ }
+
+ if (br->neigh_suppress_enabled && br_is_local_ip(vlandev, tip)) {
+ /* its our local ip, so don't proxy reply
+ * and don't forward to neigh suppress ports
+ */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ n = neigh_lookup(&arp_tbl, &tip, vlandev);
+ if (n) {
+ struct net_bridge_fdb_entry *f;
+
+ if (!(n->nud_state & NUD_VALID)) {
+ neigh_release(n);
+ return;
+ }
+
+ f = br_fdb_find_rcu(br, n->ha, vid);
+ if (f) {
+ bool replied = false;
+
+ if (f->dst && ((p->flags & BR_PROXYARP) ||
+ (f->dst->flags & BR_PROXYARP_WIFI) ||
+ (f->dst->flags & BR_NEIGH_SUPPRESS))) {
+ if (!vid)
+ br_arp_send(p, ARPOP_REPLY, ETH_P_ARP,
+ sip, skb->dev, tip, sha,
+ n->ha, sha, 0, 0);
+ else
+ br_arp_send(p, ARPOP_REPLY, ETH_P_ARP,
+ sip, skb->dev, tip, sha,
+ n->ha, sha, skb->vlan_proto,
+ skb_vlan_tag_get(skb));
+ replied = true;
+ }
+
+ /* If we have replied or as long as we know the
+ * mac, indicate to arp replied
+ */
+ if (replied || br->neigh_suppress_enabled)
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ }
+
+ neigh_release(n);
+ }
+}
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index f6b6a92..8961c25 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -39,6 +39,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
const struct nf_br_ops *nf_ops;
const unsigned char *dest;
+ struct ethhdr *eth;
u16 vid = 0;
rcu_read_lock();
@@ -57,11 +58,18 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
BR_INPUT_SKB_CB(skb)->brdev = dev;
skb_reset_mac_header(skb);
+ eth = eth_hdr(skb);
skb_pull(skb, ETH_HLEN);
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid))
goto out;
+ if (IS_ENABLED(CONFIG_INET) && br->neigh_suppress_enabled &&
+ (ntohs(eth->h_proto) == ETH_P_ARP ||
+ ntohs(eth->h_proto) == ETH_P_RARP)) {
+ br_do_proxy_suppress_arp(skb, br, vid, NULL);
+ }
+
dest = eth_hdr(skb)->h_dest;
if (is_broadcast_ether_addr(dest)) {
br_flood(br, skb, BR_PKT_BROADCAST, false, true);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 7637f58..7637a23 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -71,62 +71,6 @@ static int br_pass_frame_up(struct sk_buff *skb)
br_netif_receive_skb);
}
-static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
- u16 vid, struct net_bridge_port *p)
-{
- struct net_device *dev = br->dev;
- struct neighbour *n;
- struct arphdr *parp;
- u8 *arpptr, *sha;
- __be32 sip, tip;
-
- BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
-
- if ((dev->flags & IFF_NOARP) ||
- !pskb_may_pull(skb, arp_hdr_len(dev)))
- return;
-
- parp = arp_hdr(skb);
-
- if (parp->ar_pro != htons(ETH_P_IP) ||
- parp->ar_op != htons(ARPOP_REQUEST) ||
- parp->ar_hln != dev->addr_len ||
- parp->ar_pln != 4)
- return;
-
- arpptr = (u8 *)parp + sizeof(struct arphdr);
- sha = arpptr;
- arpptr += dev->addr_len; /* sha */
- memcpy(&sip, arpptr, sizeof(sip));
- arpptr += sizeof(sip);
- arpptr += dev->addr_len; /* tha */
- memcpy(&tip, arpptr, sizeof(tip));
-
- if (ipv4_is_loopback(tip) ||
- ipv4_is_multicast(tip))
- return;
-
- n = neigh_lookup(&arp_tbl, &tip, dev);
- if (n) {
- struct net_bridge_fdb_entry *f;
-
- if (!(n->nud_state & NUD_VALID)) {
- neigh_release(n);
- return;
- }
-
- f = br_fdb_find_rcu(br, n->ha, vid);
- if (f && ((p->flags & BR_PROXYARP) ||
- (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)))) {
- arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip,
- sha, n->ha, sha);
- BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
- }
-
- neigh_release(n);
- }
-}
-
/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
@@ -171,8 +115,11 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
- if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP))
- br_do_proxy_arp(skb, br, vid, p);
+ if (IS_ENABLED(CONFIG_INET) &&
+ (skb->protocol == htons(ETH_P_ARP) ||
+ skb->protocol == htons(ETH_P_RARP))) {
+ br_do_proxy_suppress_arp(skb, br, vid, p);
+ }
switch (pkt_type) {
case BR_PKT_MULTICAST:
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 7dacd83..3006f0d 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1131,5 +1131,8 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
}
#endif /* CONFIG_NET_SWITCHDEV */
+/* br_arp_nd_proxy.c */
void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
+void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p);
#endif
--
2.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 3/3] bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
@ 2017-10-02 4:36 ` Roopa Prabhu
-1 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: netdev, nikolay, stephen, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch avoids flooding and proxies ndisc packets
for BR_NEIGH_SUPPRESS ports.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
net/bridge/br_arp_nd_proxy.c | 246 +++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_device.c | 10 ++
net/bridge/br_input.c | 10 ++
net/bridge/br_private.h | 3 +
4 files changed, 269 insertions(+)
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index 5153510..1028e5ab 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -211,3 +211,249 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
neigh_release(n);
}
}
+
+#if IS_ENABLED(CONFIG_IPV6)
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *msg)
+{
+ struct nd_msg *m;
+
+ m = skb_header_pointer(skb, skb_network_offset(skb) +
+ sizeof(struct ipv6hdr), sizeof(*msg), msg);
+ if (!m)
+ return NULL;
+
+ if (m->icmph.icmp6_code != 0 ||
+ (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION &&
+ m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT))
+ return NULL;
+
+ return m;
+}
+
+static void br_nd_send(struct net_bridge_port *p, struct sk_buff *request,
+ struct neighbour *n, __be16 vlan_proto, u16 vlan_tci,
+ struct nd_msg *ns)
+{
+ struct net_device *dev = request->dev;
+ struct sk_buff *reply;
+ struct nd_msg *na;
+ struct ipv6hdr *pip6;
+ u8 *daddr;
+ int na_olen = 8; /* opt hdr + ETH_ALEN for target */
+ int ns_olen;
+ int i, len;
+
+ if (!dev)
+ return;
+
+ len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
+ sizeof(*na) + na_olen + dev->needed_tailroom;
+
+ reply = alloc_skb(len, GFP_ATOMIC);
+ if (!reply)
+ return;
+
+ reply->protocol = htons(ETH_P_IPV6);
+ reply->dev = dev;
+ skb_reserve(reply, LL_RESERVED_SPACE(dev));
+ skb_push(reply, sizeof(struct ethhdr));
+ skb_set_mac_header(reply, 0);
+
+ daddr = eth_hdr(request)->h_source;
+
+ /* Do we need option processing ? */
+ ns_olen = request->len - (skb_network_offset(request) +
+ sizeof(struct ipv6hdr)) - sizeof(*ns);
+ for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
+ if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
+ daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
+ break;
+ }
+ }
+
+ /* Ethernet header */
+ ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
+ ether_addr_copy(eth_hdr(reply)->h_source, n->ha);
+ eth_hdr(reply)->h_proto = htons(ETH_P_IPV6);
+ reply->protocol = htons(ETH_P_IPV6);
+
+ skb_pull(reply, sizeof(struct ethhdr));
+ skb_set_network_header(reply, 0);
+ skb_put(reply, sizeof(struct ipv6hdr));
+
+ /* IPv6 header */
+ pip6 = ipv6_hdr(reply);
+ memset(pip6, 0, sizeof(struct ipv6hdr));
+ pip6->version = 6;
+ pip6->priority = ipv6_hdr(request)->priority;
+ pip6->nexthdr = IPPROTO_ICMPV6;
+ pip6->hop_limit = 255;
+ pip6->daddr = ipv6_hdr(request)->saddr;
+ pip6->saddr = *(struct in6_addr *)n->primary_key;
+
+ skb_pull(reply, sizeof(struct ipv6hdr));
+ skb_set_transport_header(reply, 0);
+
+ na = (struct nd_msg *)skb_put(reply, sizeof(*na) + na_olen);
+
+ /* Neighbor Advertisement */
+ memset(na, 0, sizeof(*na) + na_olen);
+ na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ na->icmph.icmp6_router = 0; /* XXX: should be 1 ? */
+ na->icmph.icmp6_override = 1;
+ na->icmph.icmp6_solicited = 1;
+ na->target = ns->target;
+ ether_addr_copy(&na->opt[2], n->ha);
+ na->opt[0] = ND_OPT_TARGET_LL_ADDR;
+ na->opt[1] = na_olen >> 3;
+
+ na->icmph.icmp6_cksum = csum_ipv6_magic(&pip6->saddr,
+ &pip6->daddr,
+ sizeof(*na) + na_olen,
+ IPPROTO_ICMPV6,
+ csum_partial(na, sizeof(*na) + na_olen, 0));
+
+ pip6->payload_len = htons(sizeof(*na) + na_olen);
+
+ skb_push(reply, sizeof(struct ipv6hdr));
+ skb_push(reply, sizeof(struct ethhdr));
+
+ reply->ip_summed = CHECKSUM_UNNECESSARY;
+
+ if (p) {
+ struct net_bridge_vlan_group *vg;
+ u16 pvid;
+
+ vg = nbp_vlan_group_rcu(p);
+ pvid = br_get_pvid(vg);
+ if (pvid && pvid == vlan_tci)
+ vlan_tci = 0;
+ }
+
+ if (vlan_tci != 0) {
+ reply = vlan_insert_tag_set_proto(reply, vlan_proto, vlan_tci);
+ if (!reply) {
+ net_err_ratelimited("evpn: failed to insert VLAN tag\n");
+ return;
+ }
+ }
+
+ netdev_dbg(dev, "nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n",
+ dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha);
+
+ dev_queue_xmit(reply);
+}
+
+static int br_chk_addr_ip6(struct net_device *dev, void *data)
+{
+ struct in6_addr *addr = (struct in6_addr *)data;
+
+ if (ipv6_chk_addr(dev_net(dev), addr, dev, 0))
+ return 1;
+
+ return 0;
+}
+
+static bool br_is_local_ip6(struct net_device *dev, struct in6_addr *addr)
+
+{
+ if (br_chk_addr_ip6(dev, addr))
+ return true;
+
+ /* check if ip is configured on upper dev */
+ if (netdev_walk_all_upper_dev_rcu(dev, br_chk_addr_ip6, addr))
+ return true;
+
+ return false;
+}
+
+void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p, struct nd_msg *msg)
+{
+ struct net_device *dev = br->dev;
+ struct net_device *vlandev = NULL;
+ struct in6_addr *saddr, *daddr;
+ struct ipv6hdr *iphdr;
+ struct inet6_dev *in6_dev;
+ struct neighbour *n;
+
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
+
+ if (p && (p->flags & BR_NEIGH_SUPPRESS))
+ return;
+
+ if (msg->icmph.icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT &&
+ !msg->icmph.icmp6_solicited) {
+ /* prevent flooding to neigh suppress ports */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
+ return;
+
+ in6_dev = __in6_dev_get(dev);
+ if (!in6_dev)
+ return;
+
+ iphdr = ipv6_hdr(skb);
+ saddr = &iphdr->saddr;
+ daddr = &iphdr->daddr;
+
+ if (ipv6_addr_any(saddr) || !ipv6_addr_cmp(saddr, daddr)) {
+ /* prevent flooding to neigh suppress ports */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ if (vid != 0) {
+ /* build neigh table lookup on the vlan device */
+ vlandev = __vlan_find_dev_deep_rcu(br->dev, skb->vlan_proto,
+ vid);
+ if (!vlandev)
+ return;
+ } else {
+ vlandev = dev;
+ }
+
+ if (br_is_local_ip6(vlandev, &msg->target)) {
+ /* its our own ip, so don't proxy reply
+ * and don't forward to arp suppress ports
+ */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ n = neigh_lookup(ipv6_stub->nd_tbl, &msg->target, vlandev);
+ if (n) {
+ struct net_bridge_fdb_entry *f;
+
+ if (!(n->nud_state & NUD_VALID)) {
+ neigh_release(n);
+ return;
+ }
+
+ f = br_fdb_find_rcu(br, n->ha, vid);
+ if (f) {
+ bool replied = false;
+
+ if (f->dst && (f->dst->flags & BR_NEIGH_SUPPRESS)) {
+ if (vid != 0)
+ br_nd_send(p, skb, n, skb->vlan_proto,
+ skb_vlan_tag_get(skb), msg);
+ else
+ br_nd_send(p, skb, n, 0, 0, msg);
+ replied = true;
+ }
+
+ /* If we have replied or as long as we know the
+ * mac, indicate to NEIGH_SUPPRESS ports that we
+ * have replied
+ */
+ if (replied || br->neigh_suppress_enabled)
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ }
+ neigh_release(n);
+ }
+}
+#endif
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 8961c25..89c5d01 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -68,6 +68,16 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
(ntohs(eth->h_proto) == ETH_P_ARP ||
ntohs(eth->h_proto) == ETH_P_RARP)) {
br_do_proxy_suppress_arp(skb, br, vid, NULL);
+ } else if (IS_ENABLED(CONFIG_IPV6) && br->neigh_suppress_enabled &&
+ skb->protocol == htons(ETH_P_IPV6) &&
+ pskb_may_pull(skb, sizeof(struct ipv6hdr) +
+ sizeof(struct nd_msg)) &&
+ ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
+ struct nd_msg *msg, _msg;
+
+ msg = br_is_nd_neigh_msg(skb, &_msg);
+ if (msg)
+ br_do_suppress_nd(skb, br, vid, NULL, msg);
}
dest = eth_hdr(skb)->h_dest;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 7637a23..491e4dd 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -119,6 +119,16 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
(skb->protocol == htons(ETH_P_ARP) ||
skb->protocol == htons(ETH_P_RARP))) {
br_do_proxy_suppress_arp(skb, br, vid, p);
+ } else if (IS_ENABLED(CONFIG_IPV6) && br->neigh_suppress_enabled &&
+ skb->protocol == htons(ETH_P_IPV6) &&
+ pskb_may_pull(skb, sizeof(struct ipv6hdr) +
+ sizeof(struct nd_msg)) &&
+ ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
+ struct nd_msg *msg, _msg;
+
+ msg = br_is_nd_neigh_msg(skb, &_msg);
+ if (msg)
+ br_do_suppress_nd(skb, br, vid, p, msg);
}
switch (pkt_type) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3006f0d..ff36891 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1135,4 +1135,7 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
u16 vid, struct net_bridge_port *p);
+void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m);
#endif
--
2.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Bridge] [PATCH net-next 3/3] bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports
@ 2017-10-02 4:36 ` Roopa Prabhu
0 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 4:36 UTC (permalink / raw)
To: davem; +Cc: nikolay, netdev, bridge
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch avoids flooding and proxies ndisc packets
for BR_NEIGH_SUPPRESS ports.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
net/bridge/br_arp_nd_proxy.c | 246 +++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_device.c | 10 ++
net/bridge/br_input.c | 10 ++
net/bridge/br_private.h | 3 +
4 files changed, 269 insertions(+)
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index 5153510..1028e5ab 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -211,3 +211,249 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
neigh_release(n);
}
}
+
+#if IS_ENABLED(CONFIG_IPV6)
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *msg)
+{
+ struct nd_msg *m;
+
+ m = skb_header_pointer(skb, skb_network_offset(skb) +
+ sizeof(struct ipv6hdr), sizeof(*msg), msg);
+ if (!m)
+ return NULL;
+
+ if (m->icmph.icmp6_code != 0 ||
+ (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION &&
+ m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT))
+ return NULL;
+
+ return m;
+}
+
+static void br_nd_send(struct net_bridge_port *p, struct sk_buff *request,
+ struct neighbour *n, __be16 vlan_proto, u16 vlan_tci,
+ struct nd_msg *ns)
+{
+ struct net_device *dev = request->dev;
+ struct sk_buff *reply;
+ struct nd_msg *na;
+ struct ipv6hdr *pip6;
+ u8 *daddr;
+ int na_olen = 8; /* opt hdr + ETH_ALEN for target */
+ int ns_olen;
+ int i, len;
+
+ if (!dev)
+ return;
+
+ len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
+ sizeof(*na) + na_olen + dev->needed_tailroom;
+
+ reply = alloc_skb(len, GFP_ATOMIC);
+ if (!reply)
+ return;
+
+ reply->protocol = htons(ETH_P_IPV6);
+ reply->dev = dev;
+ skb_reserve(reply, LL_RESERVED_SPACE(dev));
+ skb_push(reply, sizeof(struct ethhdr));
+ skb_set_mac_header(reply, 0);
+
+ daddr = eth_hdr(request)->h_source;
+
+ /* Do we need option processing ? */
+ ns_olen = request->len - (skb_network_offset(request) +
+ sizeof(struct ipv6hdr)) - sizeof(*ns);
+ for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
+ if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
+ daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
+ break;
+ }
+ }
+
+ /* Ethernet header */
+ ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
+ ether_addr_copy(eth_hdr(reply)->h_source, n->ha);
+ eth_hdr(reply)->h_proto = htons(ETH_P_IPV6);
+ reply->protocol = htons(ETH_P_IPV6);
+
+ skb_pull(reply, sizeof(struct ethhdr));
+ skb_set_network_header(reply, 0);
+ skb_put(reply, sizeof(struct ipv6hdr));
+
+ /* IPv6 header */
+ pip6 = ipv6_hdr(reply);
+ memset(pip6, 0, sizeof(struct ipv6hdr));
+ pip6->version = 6;
+ pip6->priority = ipv6_hdr(request)->priority;
+ pip6->nexthdr = IPPROTO_ICMPV6;
+ pip6->hop_limit = 255;
+ pip6->daddr = ipv6_hdr(request)->saddr;
+ pip6->saddr = *(struct in6_addr *)n->primary_key;
+
+ skb_pull(reply, sizeof(struct ipv6hdr));
+ skb_set_transport_header(reply, 0);
+
+ na = (struct nd_msg *)skb_put(reply, sizeof(*na) + na_olen);
+
+ /* Neighbor Advertisement */
+ memset(na, 0, sizeof(*na) + na_olen);
+ na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ na->icmph.icmp6_router = 0; /* XXX: should be 1 ? */
+ na->icmph.icmp6_override = 1;
+ na->icmph.icmp6_solicited = 1;
+ na->target = ns->target;
+ ether_addr_copy(&na->opt[2], n->ha);
+ na->opt[0] = ND_OPT_TARGET_LL_ADDR;
+ na->opt[1] = na_olen >> 3;
+
+ na->icmph.icmp6_cksum = csum_ipv6_magic(&pip6->saddr,
+ &pip6->daddr,
+ sizeof(*na) + na_olen,
+ IPPROTO_ICMPV6,
+ csum_partial(na, sizeof(*na) + na_olen, 0));
+
+ pip6->payload_len = htons(sizeof(*na) + na_olen);
+
+ skb_push(reply, sizeof(struct ipv6hdr));
+ skb_push(reply, sizeof(struct ethhdr));
+
+ reply->ip_summed = CHECKSUM_UNNECESSARY;
+
+ if (p) {
+ struct net_bridge_vlan_group *vg;
+ u16 pvid;
+
+ vg = nbp_vlan_group_rcu(p);
+ pvid = br_get_pvid(vg);
+ if (pvid && pvid == vlan_tci)
+ vlan_tci = 0;
+ }
+
+ if (vlan_tci != 0) {
+ reply = vlan_insert_tag_set_proto(reply, vlan_proto, vlan_tci);
+ if (!reply) {
+ net_err_ratelimited("evpn: failed to insert VLAN tag\n");
+ return;
+ }
+ }
+
+ netdev_dbg(dev, "nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n",
+ dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha);
+
+ dev_queue_xmit(reply);
+}
+
+static int br_chk_addr_ip6(struct net_device *dev, void *data)
+{
+ struct in6_addr *addr = (struct in6_addr *)data;
+
+ if (ipv6_chk_addr(dev_net(dev), addr, dev, 0))
+ return 1;
+
+ return 0;
+}
+
+static bool br_is_local_ip6(struct net_device *dev, struct in6_addr *addr)
+
+{
+ if (br_chk_addr_ip6(dev, addr))
+ return true;
+
+ /* check if ip is configured on upper dev */
+ if (netdev_walk_all_upper_dev_rcu(dev, br_chk_addr_ip6, addr))
+ return true;
+
+ return false;
+}
+
+void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p, struct nd_msg *msg)
+{
+ struct net_device *dev = br->dev;
+ struct net_device *vlandev = NULL;
+ struct in6_addr *saddr, *daddr;
+ struct ipv6hdr *iphdr;
+ struct inet6_dev *in6_dev;
+ struct neighbour *n;
+
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
+
+ if (p && (p->flags & BR_NEIGH_SUPPRESS))
+ return;
+
+ if (msg->icmph.icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT &&
+ !msg->icmph.icmp6_solicited) {
+ /* prevent flooding to neigh suppress ports */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
+ return;
+
+ in6_dev = __in6_dev_get(dev);
+ if (!in6_dev)
+ return;
+
+ iphdr = ipv6_hdr(skb);
+ saddr = &iphdr->saddr;
+ daddr = &iphdr->daddr;
+
+ if (ipv6_addr_any(saddr) || !ipv6_addr_cmp(saddr, daddr)) {
+ /* prevent flooding to neigh suppress ports */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ if (vid != 0) {
+ /* build neigh table lookup on the vlan device */
+ vlandev = __vlan_find_dev_deep_rcu(br->dev, skb->vlan_proto,
+ vid);
+ if (!vlandev)
+ return;
+ } else {
+ vlandev = dev;
+ }
+
+ if (br_is_local_ip6(vlandev, &msg->target)) {
+ /* its our own ip, so don't proxy reply
+ * and don't forward to arp suppress ports
+ */
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ return;
+ }
+
+ n = neigh_lookup(ipv6_stub->nd_tbl, &msg->target, vlandev);
+ if (n) {
+ struct net_bridge_fdb_entry *f;
+
+ if (!(n->nud_state & NUD_VALID)) {
+ neigh_release(n);
+ return;
+ }
+
+ f = br_fdb_find_rcu(br, n->ha, vid);
+ if (f) {
+ bool replied = false;
+
+ if (f->dst && (f->dst->flags & BR_NEIGH_SUPPRESS)) {
+ if (vid != 0)
+ br_nd_send(p, skb, n, skb->vlan_proto,
+ skb_vlan_tag_get(skb), msg);
+ else
+ br_nd_send(p, skb, n, 0, 0, msg);
+ replied = true;
+ }
+
+ /* If we have replied or as long as we know the
+ * mac, indicate to NEIGH_SUPPRESS ports that we
+ * have replied
+ */
+ if (replied || br->neigh_suppress_enabled)
+ BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
+ }
+ neigh_release(n);
+ }
+}
+#endif
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 8961c25..89c5d01 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -68,6 +68,16 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
(ntohs(eth->h_proto) == ETH_P_ARP ||
ntohs(eth->h_proto) == ETH_P_RARP)) {
br_do_proxy_suppress_arp(skb, br, vid, NULL);
+ } else if (IS_ENABLED(CONFIG_IPV6) && br->neigh_suppress_enabled &&
+ skb->protocol == htons(ETH_P_IPV6) &&
+ pskb_may_pull(skb, sizeof(struct ipv6hdr) +
+ sizeof(struct nd_msg)) &&
+ ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
+ struct nd_msg *msg, _msg;
+
+ msg = br_is_nd_neigh_msg(skb, &_msg);
+ if (msg)
+ br_do_suppress_nd(skb, br, vid, NULL, msg);
}
dest = eth_hdr(skb)->h_dest;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 7637a23..491e4dd 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -119,6 +119,16 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
(skb->protocol == htons(ETH_P_ARP) ||
skb->protocol == htons(ETH_P_RARP))) {
br_do_proxy_suppress_arp(skb, br, vid, p);
+ } else if (IS_ENABLED(CONFIG_IPV6) && br->neigh_suppress_enabled &&
+ skb->protocol == htons(ETH_P_IPV6) &&
+ pskb_may_pull(skb, sizeof(struct ipv6hdr) +
+ sizeof(struct nd_msg)) &&
+ ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
+ struct nd_msg *msg, _msg;
+
+ msg = br_is_nd_neigh_msg(skb, &_msg);
+ if (msg)
+ br_do_suppress_nd(skb, br, vid, p, msg);
}
switch (pkt_type) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3006f0d..ff36891 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1135,4 +1135,7 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
u16 vid, struct net_bridge_port *p);
+void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m);
#endif
--
2.1.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
@ 2017-10-02 14:49 ` Roopa Prabhu
-1 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 14:49 UTC (permalink / raw)
To: davem; +Cc: netdev, Nikolay Aleksandrov, stephen, bridge
On Sun, Oct 1, 2017 at 9:36 PM, Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> From: Roopa Prabhu <roopa@cumulusnetworks.com>
>
> This series implements arp and nd suppression in the bridge
> driver for ethernet vpns. It implements rfc7432, section 10
> https://tools.ietf.org/html/rfc7432#section-10
> for ethernet VPN deployments. It is similar to the existing
> BR_ARP_PROXY flag but has a few semantic differences to conform
> to EVPN standard. In case of EVPN, it is mainly used to avoid flooding to
> tunnel ports like vxlan/mpls. Unlike the existing flags it suppresses flood
> of all neigh discovery packets (arp, nd) to tunnel ports.
>
> Roopa Prabhu (3):
> bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd
> flood
> neigh arp suppress first
> bridge: suppress nd messages from going to BR_NEIGH_SUPPRESS ports
>
pls ignore, shows conflict applying over recent net-next bridge
changes. Will rebase and submit v2.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bridge] [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support
@ 2017-10-02 14:49 ` Roopa Prabhu
0 siblings, 0 replies; 14+ messages in thread
From: Roopa Prabhu @ 2017-10-02 14:49 UTC (permalink / raw)
To: davem; +Cc: Nikolay Aleksandrov, netdev, bridge
On Sun, Oct 1, 2017 at 9:36 PM, Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> From: Roopa Prabhu <roopa@cumulusnetworks.com>
>
> This series implements arp and nd suppression in the bridge
> driver for ethernet vpns. It implements rfc7432, section 10
> https://tools.ietf.org/html/rfc7432#section-10
> for ethernet VPN deployments. It is similar to the existing
> BR_ARP_PROXY flag but has a few semantic differences to conform
> to EVPN standard. In case of EVPN, it is mainly used to avoid flooding to
> tunnel ports like vxlan/mpls. Unlike the existing flags it suppresses flood
> of all neigh discovery packets (arp, nd) to tunnel ports.
>
> Roopa Prabhu (3):
> bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd
> flood
> neigh arp suppress first
> bridge: suppress nd messages from going to BR_NEIGH_SUPPRESS ports
>
pls ignore, shows conflict applying over recent net-next bridge
changes. Will rebase and submit v2.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support
2017-10-02 14:49 ` [Bridge] " Roopa Prabhu
@ 2017-10-02 18:02 ` Stephen Hemminger
-1 siblings, 0 replies; 14+ messages in thread
From: Stephen Hemminger @ 2017-10-02 18:02 UTC (permalink / raw)
To: Roopa Prabhu; +Cc: davem, netdev, Nikolay Aleksandrov, bridge
On Mon, 2 Oct 2017 07:49:09 -0700
Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> On Sun, Oct 1, 2017 at 9:36 PM, Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> > From: Roopa Prabhu <roopa@cumulusnetworks.com>
> >
> > This series implements arp and nd suppression in the bridge
> > driver for ethernet vpns. It implements rfc7432, section 10
> > https://tools.ietf.org/html/rfc7432#section-10
> > for ethernet VPN deployments. It is similar to the existing
> > BR_ARP_PROXY flag but has a few semantic differences to conform
> > to EVPN standard. In case of EVPN, it is mainly used to avoid flooding to
> > tunnel ports like vxlan/mpls. Unlike the existing flags it suppresses flood
> > of all neigh discovery packets (arp, nd) to tunnel ports.
> >
> > Roopa Prabhu (3):
> > bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd
> > flood
> > neigh arp suppress first
> > bridge: suppress nd messages from going to BR_NEIGH_SUPPRESS ports
> >
>
> pls ignore, shows conflict applying over recent net-next bridge
> changes. Will rebase and submit v2.
Ok, but the concept looks good.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bridge] [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support
@ 2017-10-02 18:02 ` Stephen Hemminger
0 siblings, 0 replies; 14+ messages in thread
From: Stephen Hemminger @ 2017-10-02 18:02 UTC (permalink / raw)
To: Roopa Prabhu; +Cc: Nikolay Aleksandrov, netdev, bridge, davem
On Mon, 2 Oct 2017 07:49:09 -0700
Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> On Sun, Oct 1, 2017 at 9:36 PM, Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> > From: Roopa Prabhu <roopa@cumulusnetworks.com>
> >
> > This series implements arp and nd suppression in the bridge
> > driver for ethernet vpns. It implements rfc7432, section 10
> > https://tools.ietf.org/html/rfc7432#section-10
> > for ethernet VPN deployments. It is similar to the existing
> > BR_ARP_PROXY flag but has a few semantic differences to conform
> > to EVPN standard. In case of EVPN, it is mainly used to avoid flooding to
> > tunnel ports like vxlan/mpls. Unlike the existing flags it suppresses flood
> > of all neigh discovery packets (arp, nd) to tunnel ports.
> >
> > Roopa Prabhu (3):
> > bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd
> > flood
> > neigh arp suppress first
> > bridge: suppress nd messages from going to BR_NEIGH_SUPPRESS ports
> >
>
> pls ignore, shows conflict applying over recent net-next bridge
> changes. Will rebase and submit v2.
Ok, but the concept looks good.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 3/3] bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
(?)
@ 2017-10-03 5:53 ` kbuild test robot
-1 siblings, 0 replies; 14+ messages in thread
From: kbuild test robot @ 2017-10-03 5:53 UTC (permalink / raw)
To: Roopa Prabhu; +Cc: kbuild-all, davem, netdev, nikolay, stephen, bridge
[-- Attachment #1: Type: text/plain, Size: 4879 bytes --]
Hi Roopa,
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Roopa-Prabhu/bridge-neigh-msg-proxy-and-flood-suppression-support/20171003-124610
config: tile-allyesconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 4.6.2
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=tile
All errors (new ones prefixed by >>):
net/bridge/br_arp_nd_proxy.c: In function 'br_nd_send':
>> net/bridge/br_arp_nd_proxy.c:310:2: error: implicit declaration of function 'csum_ipv6_magic'
cc1: some warnings being treated as errors
vim +/csum_ipv6_magic +310 net/bridge/br_arp_nd_proxy.c
232
233 static void br_nd_send(struct net_bridge_port *p, struct sk_buff *request,
234 struct neighbour *n, __be16 vlan_proto, u16 vlan_tci,
235 struct nd_msg *ns)
236 {
237 struct net_device *dev = request->dev;
238 struct sk_buff *reply;
239 struct nd_msg *na;
240 struct ipv6hdr *pip6;
241 u8 *daddr;
242 int na_olen = 8; /* opt hdr + ETH_ALEN for target */
243 int ns_olen;
244 int i, len;
245
246 if (!dev)
247 return;
248
249 len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
250 sizeof(*na) + na_olen + dev->needed_tailroom;
251
252 reply = alloc_skb(len, GFP_ATOMIC);
253 if (!reply)
254 return;
255
256 reply->protocol = htons(ETH_P_IPV6);
257 reply->dev = dev;
258 skb_reserve(reply, LL_RESERVED_SPACE(dev));
259 skb_push(reply, sizeof(struct ethhdr));
260 skb_set_mac_header(reply, 0);
261
262 daddr = eth_hdr(request)->h_source;
263
264 /* Do we need option processing ? */
265 ns_olen = request->len - (skb_network_offset(request) +
266 sizeof(struct ipv6hdr)) - sizeof(*ns);
267 for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
268 if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
269 daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
270 break;
271 }
272 }
273
274 /* Ethernet header */
275 ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
276 ether_addr_copy(eth_hdr(reply)->h_source, n->ha);
277 eth_hdr(reply)->h_proto = htons(ETH_P_IPV6);
278 reply->protocol = htons(ETH_P_IPV6);
279
280 skb_pull(reply, sizeof(struct ethhdr));
281 skb_set_network_header(reply, 0);
282 skb_put(reply, sizeof(struct ipv6hdr));
283
284 /* IPv6 header */
285 pip6 = ipv6_hdr(reply);
286 memset(pip6, 0, sizeof(struct ipv6hdr));
287 pip6->version = 6;
288 pip6->priority = ipv6_hdr(request)->priority;
289 pip6->nexthdr = IPPROTO_ICMPV6;
290 pip6->hop_limit = 255;
291 pip6->daddr = ipv6_hdr(request)->saddr;
292 pip6->saddr = *(struct in6_addr *)n->primary_key;
293
294 skb_pull(reply, sizeof(struct ipv6hdr));
295 skb_set_transport_header(reply, 0);
296
297 na = (struct nd_msg *)skb_put(reply, sizeof(*na) + na_olen);
298
299 /* Neighbor Advertisement */
300 memset(na, 0, sizeof(*na) + na_olen);
301 na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
302 na->icmph.icmp6_router = 0; /* XXX: should be 1 ? */
303 na->icmph.icmp6_override = 1;
304 na->icmph.icmp6_solicited = 1;
305 na->target = ns->target;
306 ether_addr_copy(&na->opt[2], n->ha);
307 na->opt[0] = ND_OPT_TARGET_LL_ADDR;
308 na->opt[1] = na_olen >> 3;
309
> 310 na->icmph.icmp6_cksum = csum_ipv6_magic(&pip6->saddr,
311 &pip6->daddr,
312 sizeof(*na) + na_olen,
313 IPPROTO_ICMPV6,
314 csum_partial(na, sizeof(*na) + na_olen, 0));
315
316 pip6->payload_len = htons(sizeof(*na) + na_olen);
317
318 skb_push(reply, sizeof(struct ipv6hdr));
319 skb_push(reply, sizeof(struct ethhdr));
320
321 reply->ip_summed = CHECKSUM_UNNECESSARY;
322
323 if (p) {
324 struct net_bridge_vlan_group *vg;
325 u16 pvid;
326
327 vg = nbp_vlan_group_rcu(p);
328 pvid = br_get_pvid(vg);
329 if (pvid && pvid == vlan_tci)
330 vlan_tci = 0;
331 }
332
333 if (vlan_tci != 0) {
334 reply = vlan_insert_tag_set_proto(reply, vlan_proto, vlan_tci);
335 if (!reply) {
336 net_err_ratelimited("evpn: failed to insert VLAN tag\n");
337 return;
338 }
339 }
340
341 netdev_dbg(dev, "nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n",
342 dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha);
343
344 dev_queue_xmit(reply);
345 }
346
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 50505 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 2/3] bridge: suppress arp pkts on BR_NEIGH_SUPPRESS ports
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
(?)
@ 2017-10-03 20:14 ` kbuild test robot
-1 siblings, 0 replies; 14+ messages in thread
From: kbuild test robot @ 2017-10-03 20:14 UTC (permalink / raw)
To: Roopa Prabhu; +Cc: kbuild-all, davem, netdev, nikolay, stephen, bridge
[-- Attachment #1: Type: text/plain, Size: 1173 bytes --]
Hi Roopa,
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Roopa-Prabhu/bridge-neigh-msg-proxy-and-flood-suppression-support/20171003-124610
config: x86_64-randconfig-i0-10030107 (attached as .config)
compiler: gcc-4.9 (Debian 4.9.4-2) 4.9.4
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
net/bridge/br_arp_nd_proxy.o: In function `br_chk_addr_ip':
>> br_arp_nd_proxy.c:(.text+0x5c): undefined reference to `inet_confirm_addr'
net/bridge/br_arp_nd_proxy.o: In function `br_do_proxy_suppress_arp':
>> br_arp_nd_proxy.c:(.text+0x528): undefined reference to `arp_tbl'
net/bridge/br_arp_nd_proxy.o: In function `br_arp_send.constprop.4':
>> br_arp_nd_proxy.c:(.text.unlikely+0x7d): undefined reference to `arp_send'
>> br_arp_nd_proxy.c:(.text.unlikely+0xaa): undefined reference to `arp_create'
>> br_arp_nd_proxy.c:(.text.unlikely+0x392): undefined reference to `arp_xmit'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 32872 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2017-10-03 20:15 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-02 4:36 [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support Roopa Prabhu
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
2017-10-02 4:36 ` [PATCH net-next 1/3] bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd flood Roopa Prabhu
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
2017-10-02 4:36 ` [PATCH net-next 2/3] bridge: suppress arp pkts on BR_NEIGH_SUPPRESS ports Roopa Prabhu
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
2017-10-03 20:14 ` kbuild test robot
2017-10-02 4:36 ` [PATCH net-next 3/3] bridge: suppress nd " Roopa Prabhu
2017-10-02 4:36 ` [Bridge] " Roopa Prabhu
2017-10-03 5:53 ` kbuild test robot
2017-10-02 14:49 ` [PATCH net-next 0/3] bridge: neigh msg proxy and flood suppression support Roopa Prabhu
2017-10-02 14:49 ` [Bridge] " Roopa Prabhu
2017-10-02 18:02 ` Stephen Hemminger
2017-10-02 18:02 ` [Bridge] " Stephen Hemminger
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.