#include <linux/icmp.h>
#include <linux/in.h>
#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/netdevice.h>
static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
netdev_features_t features,
- bool tx_path)
+ bool tx_path,
+ sa_family_t sa_family)
{
void *iph = skb_network_header(skb);
int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */
__be16 proto = skb->protocol;
char cb[sizeof(skb->cb)];
+ OVS_GSO_CB(skb)->ipv6 = (sa_family == AF_INET6);
/* setup whole inner packet to get protocol. */
__skb_pull(skb, mac_offset);
skb->protocol = __skb_network_protocol(skb);
iph = ip_hdr(skb);
id = ntohs(iph->id);
- skb = tnl_skb_gso_segment(skb, 0, false);
+ skb = tnl_skb_gso_segment(skb, 0, false, AF_INET);
if (!skb || IS_ERR(skb))
return 0;
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
}
EXPORT_SYMBOL_GPL(rpl_ip_local_out);
+static int output_ipv6(struct sk_buff *skb)
+{
+ int ret = NETDEV_TX_OK;
+ int err;
+
+ memset(IP6CB(skb), 0, sizeof (*IP6CB(skb)));
+#undef ip6_local_out
+ err = ip6_local_out(skb);
+ if (unlikely(net_xmit_eval(err)))
+ ret = err;
+
+ return ret;
+}
+
+int rpl_ip6_local_out(struct sk_buff *skb)
+{
+ int ret = NETDEV_TX_OK;
+
+ if (!OVS_GSO_CB(skb)->fix_segment)
+ return output_ipv6(skb);
+
+ if (skb_is_gso(skb)) {
+ skb = tnl_skb_gso_segment(skb, 0, false, AF_INET6);
+ if (!skb || IS_ERR(skb))
+ return 0;
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ int err;
+
+ err = skb_checksum_help(skb);
+ if (unlikely(err))
+ return 0;
+ }
+
+ while (skb) {
+ struct sk_buff *next_skb = skb->next;
+
+ skb->next = NULL;
+ ret = output_ipv6(skb);
+ skb = next_skb;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rpl_ip6_local_out);
#endif /* 3.18 */
/* Keep original tunnel info during userspace action execution. */
struct metadata_dst *fill_md_dst;
#endif
-
+ bool ipv6;
};
#define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)
return skb_inner_mac_header(skb) - skb->data;
}
+#define ip6_local_out rpl_ip6_local_out
+int rpl_ip6_local_out(struct sk_buff *skb);
+
#define skb_reset_inner_headers rpl_skb_reset_inner_headers
static inline void skb_reset_inner_headers(struct sk_buff *skb)
{
int pkt_len, err;
pkt_len = skb->len - skb_inner_network_offset(skb);
- /* TODO: Fix GSO for ipv6 */
#ifdef HAVE_IP6_LOCAL_OUT_SK
err = ip6_local_out_sk(sk, skb);
#else
}
EXPORT_SYMBOL_GPL(rpl_setup_udp_tunnel_sock);
-void ovs_udp_gso(struct sk_buff *skb)
-{
- int udp_offset = skb_transport_offset(skb);
- struct udphdr *uh;
-
- uh = udp_hdr(skb);
- uh->len = htons(skb->len - udp_offset);
-}
-EXPORT_SYMBOL_GPL(ovs_udp_gso);
-
-void ovs_udp_csum_gso(struct sk_buff *skb)
-{
- struct iphdr *iph = ip_hdr(skb);
- int udp_offset = skb_transport_offset(skb);
-
- ovs_udp_gso(skb);
-
- /* csum segment if tunnel sets skb with csum. The cleanest way
- * to do this just to set it up from scratch. */
- skb->ip_summed = CHECKSUM_NONE;
- udp_set_csum(false, skb, iph->saddr, iph->daddr,
- skb->len - udp_offset);
-}
-EXPORT_SYMBOL_GPL(ovs_udp_csum_gso);
-
void rpl_udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk,
struct sk_buff *skb, __be32 src, __be32 dst,
__u8 tos, __u8 ttl, __be16 df, __be16 src_port,
return 0;
}
#endif
+
+void ovs_udp_gso(struct sk_buff *skb)
+{
+ int udp_offset = skb_transport_offset(skb);
+ struct udphdr *uh;
+
+ uh = udp_hdr(skb);
+ uh->len = htons(skb->len - udp_offset);
+}
+EXPORT_SYMBOL_GPL(ovs_udp_gso);
+
+void ovs_udp_csum_gso(struct sk_buff *skb)
+{
+ int udp_offset = skb_transport_offset(skb);
+
+ ovs_udp_gso(skb);
+
+ if (!OVS_GSO_CB(skb)->ipv6) {
+ struct iphdr *iph = ip_hdr(skb);
+
+ /* csum segment if tunnel sets skb with csum. The cleanest way
+ * to do this just to set it up from scratch. */
+ udp_set_csum(false, skb, iph->saddr, iph->daddr,
+ skb->len - udp_offset);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ struct ipv6hdr *ip6h;
+
+ ip6h = ipv6_hdr(skb);
+ udp6_set_csum(false, skb, &ip6h->saddr, &ip6h->daddr,
+ skb->len - udp_offset);
+#endif
+ }
+}
+EXPORT_SYMBOL_GPL(ovs_udp_csum_gso);
+
#endif