From: Dan Williams <dcbw@redhat.com>
To: Mahesh Bandewar <maheshb@google.com>
Cc: linux-netdev <netdev@vger.kernel.org>, jbenc@redhat.com
Subject: [PATCH] ipvlan: fix up broadcast MAC filtering for ARP and DHCP
Date: Mon, 30 Mar 2015 23:22:50 -0500 [thread overview]
Message-ID: <1427775770.30696.3.camel@redhat.com> (raw)
In-Reply-To: <1427771158.1913.35.camel@redhat.com>
The broadcast MAC is supposed to be allowed whenever the device
has an IPv4 address, otherwise ARP requests get dropped on the
floor. If ndo_set_rx_mode (and thus
ipvlan_set_multicast_mac_filter()) got called after the address
was added, it would blow away the broadcast MAC address in
mac_filters that was added at IPv4 address addition.
Next, ipvlan currently fails DHCP addressing for two reasons:
1) DHCP offers are typically unicast back to the device's MAC
address, and at the IP layer have a destination IP address
matching the new lease address. In ipvlan unicast packets
are checked against existing addresses assigned to the ipvlan
interface, so clearly this fails hard because the ipvlan
interface has no IP addresses yet. Workaround: request
that the server broadcast replies (-B for dhclient) which
don't get checked against the address list.
2) Even when that's done, mac_filters only allows the
broadcast MAC address when the interface has >= 1 IPv4
addresses, so double-fail, and the incoming DHCP offer
gets dropped on the floor again.
To fix these issues, Watch for outgoing DHCP requests and
enable the broadcast MAC address indefinitely when one is seen.
Signed-off-by: Dan Williams <dcbw@redhat.com>
---
NOTE: this patch supercedes my previous two ipvlan patches:
[PATCH 1/2] ipvlan: don't loose broadcast MAC when setting MAC filters
[PATCH 2/2] ipvlan: always allow the broadcast MAC address
drivers/net/ipvlan/ipvlan.h | 2 ++
drivers/net/ipvlan/ipvlan_core.c | 36 ++++++++++++++++++++++++++++++++++--
drivers/net/ipvlan/ipvlan_main.c | 12 +++++++++---
3 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
index 924ea98..7e0b8ff 100644
--- a/drivers/net/ipvlan/ipvlan.h
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -67,6 +67,7 @@ struct ipvl_dev {
struct list_head addrs;
int ipv4cnt;
int ipv6cnt;
+ bool dhcp4_seen;
struct ipvl_pcpu_stats __percpu *pcpu_stats;
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
netdev_features_t sfeatures;
@@ -118,4 +119,5 @@ 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);
+void ipvlan_set_broadcast_mac_filter(struct ipvl_dev *ipvlan, bool set);
#endif /* __IPVLAN_H */
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index 2a17500..6dd992b 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -126,6 +126,12 @@ static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type)
lyr3h = arph;
break;
}
+ case htons(ETH_P_ALL): {
+ /* Raw sockets */
+ if (eth_hdr(skb)->h_proto != htons(ETH_P_IP))
+ break;
+ /* Fall through */
+ }
case htons(ETH_P_IP): {
u32 pktlen;
struct iphdr *ip4h;
@@ -454,16 +460,42 @@ out:
return ipvlan_process_outbound(skb, ipvlan);
}
+#define DHCP_PACKET_MIN_LEN 282
+
+static bool is_dhcp(struct sk_buff *skb)
+{
+ struct iphdr *ip4h = ip_hdr(skb);
+ struct udphdr *udph;
+
+ if (skb->len < DHCP_PACKET_MIN_LEN || ip4h->protocol != IPPROTO_UDP
+ || ip_is_fragment(ip4h))
+ return false;
+
+ /* In the case of RAW sockets the transport header is not set by
+ * the IP stack so we must set it ourselves.
+ */
+ if (skb->transport_header == skb->network_header)
+ skb_set_transport_header(skb, ETH_HLEN + sizeof(struct iphdr));
+ udph = udp_hdr(skb);
+ return udph && udph->dest == htons(67) && udph->source == htons(68);
+}
+
static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
{
- const struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
struct ethhdr *eth = eth_hdr(skb);
struct ipvl_addr *addr;
void *lyr3h;
int addr_type;
+ lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+ if (!ipvlan->dhcp4_seen && lyr3h && addr_type == IPVL_IPV4
+ && is_dhcp(skb)) {
+ ipvlan->dhcp4_seen = true;
+ ipvlan_set_broadcast_mac_filter(ipvlan, true);
+ }
+
if (ether_addr_equal(eth->h_dest, eth->h_source)) {
- lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
if (lyr3h) {
addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
if (addr)
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index 4f4099d..a497fb9 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -167,6 +167,8 @@ static int ipvlan_stop(struct net_device *dev)
dev_uc_del(phy_dev, phy_dev->dev_addr);
+ ipvlan->dhcp4_seen = false;
+
if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
list_for_each_entry(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_del(addr, !dev->dismantle);
@@ -214,7 +216,7 @@ static void ipvlan_change_rx_flags(struct net_device *dev, int change)
dev_set_allmulti(phy_dev, dev->flags & IFF_ALLMULTI? 1 : -1);
}
-static void ipvlan_set_broadcast_mac_filter(struct ipvl_dev *ipvlan, bool set)
+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);
@@ -239,6 +241,9 @@ static void ipvlan_set_multicast_mac_filter(struct net_device *dev)
netdev_for_each_mc_addr(ha, dev)
__set_bit(ipvlan_mac_hash(ha->addr), mc_filters);
+ if (ipvlan->ipv4cnt || ipvlan->dhcp4_seen)
+ __set_bit(ipvlan_mac_hash(dev->broadcast), mc_filters);
+
bitmap_copy(ipvlan->mac_filters, mc_filters,
IPVLAN_MAC_FILTER_SIZE);
}
@@ -467,6 +472,7 @@ static int ipvlan_link_new(struct net *src_net, struct net_device *dev,
INIT_LIST_HEAD(&ipvlan->addrs);
ipvlan->ipv4cnt = 0;
ipvlan->ipv6cnt = 0;
+ ipvlan->dhcp4_seen = false;
/* TODO Probably put random address here to be presented to the
* world but keep using the physical-dev address for the outgoing
@@ -708,8 +714,8 @@ static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
list_del_rcu(&addr->anode);
ipvlan->ipv4cnt--;
WARN_ON(ipvlan->ipv4cnt < 0);
- if (!ipvlan->ipv4cnt)
- ipvlan_set_broadcast_mac_filter(ipvlan, false);
+ if (!ipvlan->ipv4cnt && !ipvlan->dhcp4_seen)
+ ipvlan_set_broadcast_mac_filter(ipvlan, false);
kfree_rcu(addr, rcu);
return;
--
2.1.0
next prev parent reply other threads:[~2015-03-31 4:22 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-03-26 22:41 [PATCH 1/2] ipvlan: don't loose broadcast MAC when setting MAC filters Dan Williams
2015-03-26 22:43 ` [PATCH 2/2] ipvlan: always allow the broadcast MAC address Dan Williams
2015-03-27 17:46 ` Jiri Benc
2015-03-28 0:52 ` Mahesh Bandewar
2015-03-28 5:56 ` Mahesh Bandewar
2015-03-28 18:32 ` Jiri Benc
2015-03-30 14:37 ` Dan Williams
2015-03-30 16:54 ` Mahesh Bandewar
2015-03-30 17:44 ` Dan Williams
2015-03-30 17:56 ` Mahesh Bandewar
2015-03-30 18:13 ` Dan Williams
2015-03-30 18:32 ` Mahesh Bandewar
2015-03-27 17:45 ` [PATCH 1/2] ipvlan: don't loose broadcast MAC when setting MAC filters Jiri Benc
2015-03-30 20:28 ` Mahesh Bandewar
2015-03-30 21:01 ` Dan Williams
2015-03-30 21:11 ` Mahesh Bandewar
2015-03-31 3:05 ` Dan Williams
2015-03-31 4:22 ` Dan Williams [this message]
2015-04-01 20:07 ` [PATCH] ipvlan: fix up broadcast MAC filtering for ARP and DHCP Dan Williams
2015-04-01 20:24 ` Mahesh Bandewar
2015-04-01 20:55 ` Dan Williams
2015-04-02 1:30 ` Mahesh Bandewar
2015-04-02 14:40 ` Dan Williams
2015-04-03 1:39 ` Mahesh Bandewar
2015-04-06 17:17 ` Dan Williams
2015-04-07 18:32 ` Mahesh Bandewar
2015-04-07 19:45 ` Dan Williams
2015-04-09 15:51 ` Dan Williams
2015-04-09 16:01 ` Eric Dumazet
2015-04-09 16:33 ` Mahesh Bandewar
2015-04-09 22:18 ` Dan Williams
2015-04-08 9:37 ` David Laight
2015-04-08 14:12 ` Dan Williams
2015-04-09 1:08 ` Mahesh Bandewar
2015-04-02 18:16 ` David Miller
2015-04-02 18:39 ` Dan Williams
2015-04-02 18:46 ` Dan Williams
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1427775770.30696.3.camel@redhat.com \
--to=dcbw@redhat.com \
--cc=jbenc@redhat.com \
--cc=maheshb@google.com \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).