+static void ecn_decapsulate(struct sk_buff *skb, u8 tos)
+{
+ if (unlikely(INET_ECN_is_ce(tos))) {
+ __be16 protocol = skb->protocol;
+
+ skb_set_network_header(skb, ETH_HLEN);
+
+ if (protocol == htons(ETH_P_8021Q)) {
+ if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
+ return;
+
+ protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
+ skb_set_network_header(skb, VLAN_ETH_HLEN);
+ }
+
+ if (protocol == htons(ETH_P_IP)) {
+ if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
+ + sizeof(struct iphdr))))
+ return;
+
+ IP_ECN_set_ce(ip_hdr(skb));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (protocol == htons(ETH_P_IPV6)) {
+ if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
+ + sizeof(struct ipv6hdr))))
+ return;
+
+ IP6_ECN_set_ce(ipv6_hdr(skb));
+ }
+#endif
+ }
+}
+
+/**
+ * tnl_rcv - ingress point for generic tunnel code
+ *
+ * @vport: port this packet was received on
+ * @skb: received packet
+ * @tos: ToS from encapsulating IP packet, used to copy ECN bits
+ *
+ * Must be called with rcu_read_lock.
+ *
+ * Packets received by this function are in the following state:
+ * - skb->data points to the inner Ethernet header.
+ * - The inner Ethernet header is in the linear data area.
+ * - skb->csum does not include the inner Ethernet header.
+ * - The layer pointers are undefined.
+ */
+void tnl_rcv(struct vport *vport, struct sk_buff *skb, u8 tos)
+{
+ struct ethhdr *eh;
+
+ skb_reset_mac_header(skb);
+ eh = eth_hdr(skb);
+
+ if (likely(ntohs(eh->h_proto) >= 1536))
+ skb->protocol = eh->h_proto;
+ else
+ skb->protocol = htons(ETH_P_802_2);
+
+ skb_dst_drop(skb);
+ nf_reset(skb);
+ skb_clear_rxhash(skb);
+ secpath_reset(skb);
+
+ ecn_decapsulate(skb, tos);
+ vlan_set_tci(skb, 0);
+
+ if (unlikely(compute_ip_summed(skb, false))) {
+ kfree_skb(skb);
+ return;
+ }
+
+ vport_receive(vport, skb);
+}
+