This patch adds support to segment large GRE packet. This does means that there are two sets of headers for given packet for segmentation. 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 cf9c105..ba89f2f 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 375713a..a716492 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" @@ -449,9 +450,14 @@ netdev_gre_push_header(struct dp_packet *packet, greh = netdev_tnl_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 8fdab82..c2f3f5f 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -212,7 +212,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