This patch adds support to segment large GRE packet. This does means that there are two sets of headers for given packet. To get offset of inner packet outer_hlen member is added to dp-packet.
Signed-off-by: Pravin B Shelar <pshe...@ovn.org> --- lib/dp-packet-lso.c | 74 +++++++++++++++++++++++++++++++++++++++++++------ lib/dp-packet-lso.h | 2 ++ lib/dp-packet.h | 1 + lib/netdev-native-tnl.c | 12 ++++++-- lib/netdev-vport.c | 5 +++- 5 files changed, 82 insertions(+), 12 deletions(-) diff --git a/lib/dp-packet-lso.c b/lib/dp-packet-lso.c index 14a5ed8..bdcc987 100644 --- a/lib/dp-packet-lso.c +++ b/lib/dp-packet-lso.c @@ -67,6 +67,9 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5); #define TCP_CSUM_OFFSET offsetof(struct tcp_header, tcp_csum) static struct dp_packet * +segment_eth_packet(struct dp_packet *orig, int offset); + +static struct dp_packet * segment_packet__(struct dp_packet *orig, int header_len) { struct dp_packet *seg_list = NULL, *prev = NULL; @@ -74,12 +77,21 @@ segment_packet__(struct dp_packet *orig, int header_len) int offset = header_len; int size = dp_packet_size(orig); struct dp_packet *seg; + unsigned char *src; + src = (unsigned char *) dp_packet_data(orig) - orig->lso.outer_hlen; if (!mss) { - seg_list = dp_packet_clone(orig); - memset(&seg_list->lso, 0, sizeof seg_list->lso); - PACKET_LSO_CTX(seg_list)->next = NULL; - return seg_list; + seg = dp_packet_clone_with_headroom(orig, orig->lso.outer_hlen); + + if (orig->lso.outer_hlen) { + unsigned char *dst; + + dst = (unsigned char *) dp_packet_data(seg) - orig->lso.outer_hlen; + memcpy(dst, src, orig->lso.outer_hlen); + } + memset(&seg->lso, 0, sizeof seg->lso); + PACKET_LSO_CTX(seg)->next = NULL; + return seg; } while (offset < size) { int current_seg_size; @@ -87,8 +99,10 @@ segment_packet__(struct dp_packet *orig, int header_len) current_seg_size = size < (offset + mss) ? (size - offset) : mss; seg = dp_packet_new(0); - dp_packet_put(seg, dp_packet_data(orig), header_len); - + dp_packet_put(seg, src, header_len + orig->lso.outer_hlen); + if (orig->lso.outer_hlen) { + dp_packet_reset_packet(seg, orig->lso.outer_hlen); + } data = (unsigned char *)dp_packet_data(orig) + offset; dp_packet_put(seg, data, current_seg_size); offset += mss; @@ -179,10 +193,54 @@ segment_tcp_packet(struct dp_packet *orig) return seg_list; } +static void +restore_outer_headers(struct dp_packet *p, int hlen, uint8_t l2_pad_size, + uint16_t l2_5_ofs, uint16_t l3_ofs, uint16_t l4_ofs) +{ + dp_packet_reset_packet(p, -hlen); + p->l2_pad_size = l2_pad_size; + p->l2_5_ofs = l2_5_ofs; + p->l3_ofs = l3_ofs; + p->l4_ofs = l4_ofs; +} + +static struct dp_packet * +segment_gre_packet(struct dp_packet *orig) +{ + struct dp_packet *seg_list, *seg; + const struct gre_base_hdr *greh; + uint8_t l2_pad_size = orig->l2_pad_size; + uint16_t l2_5_ofs = orig->l2_5_ofs; + uint16_t l3_ofs = orig->l3_ofs; + uint16_t l4_ofs = orig->l4_ofs; + + seg_list = segment_eth_packet(orig, orig->lso.outer_hlen); + restore_outer_headers(orig, orig->lso.outer_hlen, l2_pad_size, l2_5_ofs, + l3_ofs, l4_ofs); + + FOR_EACH_LSO_SEG(seg_list, seg) { + restore_outer_headers(seg, orig->lso.outer_hlen, l2_pad_size, l2_5_ofs, + l3_ofs, l4_ofs); + + greh = dp_packet_l4(seg); + + if (greh->flags & htons(GRE_CSUM)) { + ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1); + int gre_size = dp_packet_size(seg) - seg->l4_ofs; + + *csum_opt = csum(greh, gre_size); + } + } + return seg_list; +} + static struct dp_packet * segment_l4_packet(struct dp_packet *orig) { - if (orig->lso.type & (DPBUF_LSO_TCPv4 | DPBUF_LSO_TCPv6)) { + if (orig->lso.type & DPBUF_LSO_GRE) { + orig->lso.type &= ~DPBUF_LSO_GRE; + return segment_gre_packet(orig); + } else if (orig->lso.type & (DPBUF_LSO_TCPv4 | DPBUF_LSO_TCPv6)) { return segment_tcp_packet(orig); } else if (orig->lso.type & (DPBUF_LSO_UDPv4 | DPBUF_LSO_UDPv6)) { return segment_udp_packet(orig); @@ -200,7 +258,7 @@ segment_ipv4_packet(struct dp_packet *orig) int ip_offset = 0; bool inc_ip_id = false; - if (orig->lso.type & DPBUF_LSO_TCPv4) { + if (orig->lso.type & (DPBUF_LSO_TCPv4 | DPBUF_LSO_GRE)) { inc_ip_id = true; ip_id = ntohs(orig_iph->ip_id); } diff --git a/lib/dp-packet-lso.h b/lib/dp-packet-lso.h index 09815e8..fdf93a6 100644 --- a/lib/dp-packet-lso.h +++ b/lib/dp-packet-lso.h @@ -31,9 +31,11 @@ #define DPBUF_LSO_TCPv6 (1 << 1) #define DPBUF_LSO_UDPv4 (1 << 2) #define DPBUF_LSO_UDPv6 (1 << 3) +#define DPBUF_LSO_GRE (1 << 4) struct dp_packet_lso_ctx { struct dp_packet *next; /* Used to list lso segments. */ + }; BUILD_ASSERT_DECL(DP_PACKET_CONTEXT_SIZE >= sizeof(struct dp_packet_lso_ctx)); diff --git a/lib/dp-packet.h b/lib/dp-packet.h index e247712..914b37f 100644 --- a/lib/dp-packet.h +++ b/lib/dp-packet.h @@ -66,6 +66,7 @@ struct dp_packet { }; struct { uint16_t mss; + uint16_t outer_hlen; uint8_t type; } lso; }; diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index 9c2dc7e..60cb81f 100644 --- a/lib/netdev-native-tnl.c +++ b/lib/netdev-native-tnl.c @@ -34,6 +34,7 @@ #include "dirs.h" #include "dpif.h" #include "dp-packet.h" +#include "dp-packet-lso.h" #include "entropy.h" #include "flow.h" #include "hash.h" @@ -373,9 +374,14 @@ netdev_gre_push_header(struct dp_packet *packet, greh = push_ip_header(packet, data->header, data->header_len, &ip_tot_size); - if (greh->flags & htons(GRE_CSUM)) { - ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1); - *csum_opt = csum(greh, ip_tot_size); + if (packet->lso.type) { + packet->lso.type |= DPBUF_LSO_GRE; + packet->lso.outer_hlen = data->header_len; + } else { + if (greh->flags & htons(GRE_CSUM)) { + ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1); + *csum_opt = csum(greh, ip_tot_size); + } } } diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 1d2638b..5530818 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -207,7 +207,10 @@ netdev_vport_construct(struct netdev *netdev_) eth_addr_random(&dev->etheraddr); /* Add a default destination port for tunnel ports if none specified. */ - if (!strcmp(type, "geneve")) { + if (!strcmp(type, "gre")) { + netdev_->supported_lso_types = DPBUF_LSO_TCPv4 | DPBUF_LSO_TCPv6 | + DPBUF_LSO_UDPv4 | DPBUF_LSO_UDPv6; + } else if (!strcmp(type, "geneve")) { dev->tnl_cfg.dst_port = htons(GENEVE_DST_PORT); } else if (!strcmp(type, "vxlan")) { dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT); -- 2.5.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev