Skip to content

Commit

Permalink
datapath-windows: Support for IPv6 in TCP segmentation
Browse files Browse the repository at this point in the history
When a packet which needs segmentation is received, the header for
each segment is being calculated, i.e. IP length, checksum, TCP seq,
TCP checksum.

The problem with the current code is that it wrongly assumes that
the Ethernet frame payload is always an IPv4 packet.

This patch checks the EtherType field of the Ethernet frame to see
which protocol is encapsulated in its payload, IPv4 or IPv6, and
calculates the segment's header accordingly.

Signed-off-by: Sorin Vinturis <svinturis@cloudbasesolutions.com>
Co-authored-by: Sairam Venugopal <vsairam@vmware.com>
Reported-by: Sairam Venugopal <vsairam@vmware.com>
Reported-at: openvswitch/ovs-issues#105
Acked-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
  • Loading branch information
Sairam Venugopal authored and blp committed Mar 15, 2016
1 parent b6446b9 commit 884d541
Showing 1 changed file with 80 additions and 39 deletions.
119 changes: 80 additions & 39 deletions datapath-windows/ovsext/BufferMgmt.c
Expand Up @@ -1122,11 +1122,10 @@ static NDIS_STATUS
FixSegmentHeader(PNET_BUFFER nb, UINT16 segmentSize, UINT32 seqNumber,
BOOLEAN lastPacket, UINT16 packetCounter)
{
EthHdr *dstEth;
IPHdr *dstIP;
TCPHdr *dstTCP;
PMDL mdl;
PUINT8 bufferStart;
EthHdr *dstEth = NULL;
TCPHdr *dstTCP = NULL;
PMDL mdl = NULL;
PUINT8 bufferStart = NULL;

mdl = NET_BUFFER_FIRST_MDL(nb);

Expand All @@ -1135,44 +1134,86 @@ FixSegmentHeader(PNET_BUFFER nb, UINT16 segmentSize, UINT32 seqNumber,
return NDIS_STATUS_RESOURCES;
}
dstEth = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(nb));
ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
>= sizeof(EthHdr) + sizeof(IPHdr) + sizeof(TCPHdr));
dstIP = (IPHdr *)((PCHAR)dstEth + sizeof *dstEth);
dstTCP = (TCPHdr *)((PCHAR)dstIP + dstIP->ihl * 4);
ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
>= sizeof(EthHdr) + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP));

/* Fix IP length and checksum */
ASSERT(dstIP->protocol == IPPROTO_TCP);
dstIP->tot_len = htons(segmentSize + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP));
dstIP->id += packetCounter;
dstIP->check = 0;
dstIP->check = IPChecksum((UINT8 *)dstIP, dstIP->ihl * 4, 0);

/* Fix TCP checksum */
dstTCP->seq = htonl(seqNumber);

/*
* Set the TCP FIN and PSH bit only for the last packet
* More information can be found under:
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff568840%28v=vs.85%29.aspx
*/
if (dstTCP->fin) {
dstTCP->fin = lastPacket;
switch (dstEth->Type) {
case ETH_TYPE_IPV4_NBO:
{
IPHdr *dstIP = NULL;

ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
>= sizeof(EthHdr) + sizeof(IPHdr) + sizeof(TCPHdr));
dstIP = (IPHdr *)((PCHAR)dstEth + sizeof(*dstEth));
dstTCP = (TCPHdr *)((PCHAR)dstIP + dstIP->ihl * 4);
ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
>= sizeof(EthHdr) + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP));

/* Fix IP length and checksum */
ASSERT(dstIP->protocol == IPPROTO_TCP);
dstIP->tot_len = htons(segmentSize + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP));
dstIP->id += packetCounter;
dstIP->check = 0;
dstIP->check = IPChecksum((UINT8 *)dstIP, dstIP->ihl * 4, 0);
dstTCP->seq = htonl(seqNumber);

/*
* Set the TCP FIN and PSH bit only for the last packet
* More information can be found under:
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff568840%28v=vs.85%29.aspx
*/
if (dstTCP->fin) {
dstTCP->fin = lastPacket;
}
if (dstTCP->psh) {
dstTCP->psh = lastPacket;
}
UINT16 csumLength = segmentSize + TCP_HDR_LEN(dstTCP);
dstTCP->check = IPPseudoChecksum(&dstIP->saddr,
&dstIP->daddr,
IPPROTO_TCP,
csumLength);
dstTCP->check = CalculateChecksumNB(nb,
csumLength,
sizeof(*dstEth) + dstIP->ihl * 4);
break;
}
case ETH_TYPE_IPV6_NBO:
{
IPv6Hdr *dstIP = NULL;

ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
>= sizeof(EthHdr) + sizeof(IPv6Hdr) + sizeof(TCPHdr));
dstIP = (IPv6Hdr *)((PCHAR)dstEth + sizeof(*dstEth));
dstTCP = (TCPHdr *)((PCHAR)dstIP + sizeof(IPv6Hdr));
ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
>= sizeof(EthHdr) + sizeof(IPv6Hdr) + TCP_HDR_LEN(dstTCP));

/* Fix IP length */
ASSERT(dstIP->nexthdr == IPPROTO_TCP);
dstIP->payload_len = htons(segmentSize + sizeof(IPv6Hdr) + TCP_HDR_LEN(dstTCP));

dstTCP->seq = htonl(seqNumber);
if (dstTCP->fin) {
dstTCP->fin = lastPacket;
}
if (dstTCP->psh) {
dstTCP->psh = lastPacket;
}

UINT16 csumLength = segmentSize + TCP_HDR_LEN(dstTCP);
dstTCP->check = IPv6PseudoChecksum((UINT32*)&dstIP->saddr,
(UINT32*)&dstIP->daddr,
IPPROTO_TCP,
csumLength);
dstTCP->check = CalculateChecksumNB(nb,
csumLength,
sizeof(*dstEth) + sizeof(IPv6Hdr));
break;
}
if (dstTCP->psh) {
dstTCP->psh = lastPacket;
default:
OVS_LOG_ERROR("Invalid eth type: %d\n", dstEth->Type);
ASSERT(! "Invalid eth type");
}

UINT16 csumLength = segmentSize + TCP_HDR_LEN(dstTCP);
dstTCP->check = IPPseudoChecksum(&dstIP->saddr,
&dstIP->daddr,
IPPROTO_TCP,
csumLength);
dstTCP->check = CalculateChecksumNB(nb,
csumLength,
sizeof *dstEth + dstIP->ihl * 4);

return STATUS_SUCCESS;
}

Expand Down

0 comments on commit 884d541

Please sign in to comment.