- struct gre_vport *gre_vport = gre_vport_priv(vport);
- const struct mutable_config *mutable = rcu_dereference(gre_vport->mutable);
-
- struct iphdr *old_iph;
- int orig_len;
- struct iphdr iph;
- struct rtable *rt;
- int max_headroom;
- int mtu;
-
- /* Validate the protocol headers before we try to use them. */
- if (skb->protocol == htons(ETH_P_8021Q)) {
- if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
- goto error_free;
-
- skb->protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
- skb_set_network_header(skb, VLAN_ETH_HLEN);
- }
-
- if (skb->protocol == htons(ETH_P_IP)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
- + sizeof(struct iphdr) - skb->data)))
- skb->protocol = 0;
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
- + sizeof(struct ipv6hdr) - skb->data)))
- skb->protocol = 0;
- }
-#endif
- old_iph = ip_hdr(skb);
-
- iph.tos = mutable->port_config.tos;
- if (mutable->port_config.flags & GRE_F_TOS_INHERIT) {
- if (skb->protocol == htons(ETH_P_IP))
- iph.tos = old_iph->tos;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6))
- iph.tos = ipv6_get_dsfield(ipv6_hdr(skb));
-#endif
- }
- iph.tos = ecn_encapsulate(iph.tos, skb);
-
- {
- struct flowi fl = { .nl_u = { .ip4_u =
- { .daddr = mutable->port_config.daddr,
- .saddr = mutable->port_config.saddr,
- .tos = RT_TOS(iph.tos) } },
- .proto = IPPROTO_GRE };
-
- if (ip_route_output_key(&init_net, &rt, &fl))
- goto error_free;
- }
-
- iph.ttl = mutable->port_config.ttl;
- if (mutable->port_config.flags & GRE_F_TTL_INHERIT) {
- if (skb->protocol == htons(ETH_P_IP))
- iph.ttl = old_iph->ttl;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6))
- iph.ttl = ipv6_hdr(skb)->hop_limit;
-#endif
- }
- if (!iph.ttl)
- iph.ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
-
- iph.frag_off = (mutable->port_config.flags & GRE_F_PMTUD) ? htons(IP_DF) : 0;
- if (iph.frag_off)
- mtu = dst_mtu(&rt_dst(rt))
- - ETH_HLEN
- - mutable->tunnel_hlen
- - (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q) ? VLAN_HLEN : 0);
- else
- mtu = mutable->mtu;
-
- if (skb->protocol == htons(ETH_P_IP)) {
- iph.frag_off |= old_iph->frag_off & htons(IP_DF);
- mtu = max(mtu, IP_MIN_MTU);
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6))
- mtu = max(mtu, IPV6_MIN_MTU);
-#endif
-
- iph.version = 4;
- iph.ihl = sizeof(struct iphdr) >> 2;
- iph.protocol = IPPROTO_GRE;
- iph.daddr = rt->rt_dst;
- iph.saddr = rt->rt_src;
-
- nf_reset(skb);
- secpath_reset(skb);
- skb_dst_drop(skb);
- skb_dst_set(skb, &rt_dst(rt));
-
- /* If we are doing GSO on a pskb it is better to make sure that the
- * headroom is correct now. We will only have to copy the portion in
- * the linear data area and GSO will preserve headroom when it creates
- * the segments. This is particularly beneficial on Xen where we get
- * lots of GSO pskbs. Conversely, we delay copying if it is just to
- * get our own writable clone because GSO may do the copy for us. */
- max_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len
- + mutable->tunnel_hlen;
-
- if (skb_headroom(skb) < max_headroom) {
- skb = check_headroom(skb, max_headroom);
- if (unlikely(IS_ERR(skb))) {
- vport_record_error(vport, VPORT_E_TX_DROPPED);
- goto error;
- }
- }
-
- forward_ip_summed(skb);
-
- if (unlikely(vswitch_skb_checksum_setup(skb)))
- goto error_free;
-
- skb = handle_gso(skb);
- if (unlikely(IS_ERR(skb))) {
- vport_record_error(vport, VPORT_E_TX_DROPPED);
- goto error;
- }
-
- /* Process GSO segments. Try to do any work for the entire packet that
- * doesn't involve actually writing to it before this point. */
- orig_len = 0;
- do {
- struct sk_buff *next_skb = skb->next;
- skb->next = NULL;
-
- orig_len += build_packet(vport, mutable, &iph, rt, max_headroom, mtu, skb);