summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/vrf.c78
1 files changed, 72 insertions, 6 deletions
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index f8d711a84763..259d5cbacf2c 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -1310,6 +1310,61 @@ static void vrf_ip6_input_dst(struct sk_buff *skb, struct net_device *vrf_dev,
skb_dst_set(skb, &rt6->dst);
}
+static int vrf_prepare_mac_header(struct sk_buff *skb,
+ struct net_device *vrf_dev, u16 proto)
+{
+ struct ethhdr *eth;
+ int err;
+
+ /* in general, we do not know if there is enough space in the head of
+ * the packet for hosting the mac header.
+ */
+ err = skb_cow_head(skb, LL_RESERVED_SPACE(vrf_dev));
+ if (unlikely(err))
+ /* no space in the skb head */
+ return -ENOBUFS;
+
+ __skb_push(skb, ETH_HLEN);
+ eth = (struct ethhdr *)skb->data;
+
+ skb_reset_mac_header(skb);
+
+ /* we set the ethernet destination and the source addresses to the
+ * address of the VRF device.
+ */
+ ether_addr_copy(eth->h_dest, vrf_dev->dev_addr);
+ ether_addr_copy(eth->h_source, vrf_dev->dev_addr);
+ eth->h_proto = htons(proto);
+
+ /* the destination address of the Ethernet frame corresponds to the
+ * address set on the VRF interface; therefore, the packet is intended
+ * to be processed locally.
+ */
+ skb->protocol = eth->h_proto;
+ skb->pkt_type = PACKET_HOST;
+
+ skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
+
+ skb_pull_inline(skb, ETH_HLEN);
+
+ return 0;
+}
+
+/* prepare and add the mac header to the packet if it was not set previously.
+ * In this way, packet sniffers such as tcpdump can parse the packet correctly.
+ * If the mac header was already set, the original mac header is left
+ * untouched and the function returns immediately.
+ */
+static int vrf_add_mac_header_if_unset(struct sk_buff *skb,
+ struct net_device *vrf_dev,
+ u16 proto)
+{
+ if (skb_mac_header_was_set(skb))
+ return 0;
+
+ return vrf_prepare_mac_header(skb, vrf_dev, proto);
+}
+
static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
struct sk_buff *skb)
{
@@ -1336,9 +1391,15 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
skb->skb_iif = vrf_dev->ifindex;
if (!list_empty(&vrf_dev->ptype_all)) {
- skb_push(skb, skb->mac_len);
- dev_queue_xmit_nit(skb, vrf_dev);
- skb_pull(skb, skb->mac_len);
+ int err;
+
+ err = vrf_add_mac_header_if_unset(skb, vrf_dev,
+ ETH_P_IPV6);
+ if (likely(!err)) {
+ skb_push(skb, skb->mac_len);
+ dev_queue_xmit_nit(skb, vrf_dev);
+ skb_pull(skb, skb->mac_len);
+ }
}
IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
@@ -1381,9 +1442,14 @@ static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev,
vrf_rx_stats(vrf_dev, skb->len);
if (!list_empty(&vrf_dev->ptype_all)) {
- skb_push(skb, skb->mac_len);
- dev_queue_xmit_nit(skb, vrf_dev);
- skb_pull(skb, skb->mac_len);
+ int err;
+
+ err = vrf_add_mac_header_if_unset(skb, vrf_dev, ETH_P_IP);
+ if (likely(!err)) {
+ skb_push(skb, skb->mac_len);
+ dev_queue_xmit_nit(skb, vrf_dev);
+ skb_pull(skb, skb->mac_len);
+ }
}
skb = vrf_rcv_nfhook(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, vrf_dev);