/*
 * Copyright (c) 2014 VMware, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __PACKET_PARSER_H_
#define __PACKET_PARSER_H_ 1

#define MIN_IPV4_HLEN 20

#include "precomp.h"
#include "NetProto.h"

const VOID* OvsGetPacketBytes(const NET_BUFFER_LIST *_pNB, UINT32 len,
                              UINT32 SrcOffset, VOID *storage);
NDIS_STATUS OvsParseIPv6(const NET_BUFFER_LIST *packet, Ipv6Key *key,
                        POVS_PACKET_HDR_INFO layers);
VOID OvsParseTcp(const NET_BUFFER_LIST *packet, L4Key *flow,
                 POVS_PACKET_HDR_INFO layers);
VOID OvsParseUdp(const NET_BUFFER_LIST *packet, L4Key *flow,
                 POVS_PACKET_HDR_INFO layers);
VOID OvsParseSctp(const NET_BUFFER_LIST *packet, L4Key *flow,
                  POVS_PACKET_HDR_INFO layers);
NDIS_STATUS OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
                           Ipv6Key *ipv6Key,
                           Icmp6Key *flow,
                           POVS_PACKET_HDR_INFO layers);

static __inline ULONG
OvsPacketLenNBL(const NET_BUFFER_LIST *_pNB)
{
    INT length = 0;
    NET_BUFFER *nb;

    nb = NET_BUFFER_LIST_FIRST_NB(_pNB);
    ASSERT(nb);
    while(nb) {
        length += NET_BUFFER_DATA_LENGTH(nb);
        nb = NET_BUFFER_NEXT_NB(nb);
    }

    return length;
}

/*
 * Returns the ctl field from the TCP header in 'packet', or 0 if the field
 * can't be read.  The caller must have ensured that 'packet' contains a TCP
 * header.
 *
 * We can't just use TCPHdr, from netProto.h, for this because that
 * breaks the flags down into individual bit-fields.  We can't even use
 * offsetof because that will try to take the address of a bit-field,
 * which C does not allow.
 */
static UINT16
OvsGetTcpCtl(const NET_BUFFER_LIST *packet, // IN
             const POVS_PACKET_HDR_INFO layers) // IN
{
#define TCP_CTL_OFS 12                // Offset of "ctl" field in TCP header.
#define TCP_FLAGS(CTL) ((CTL) & 0x3f) // Obtain TCP flags from CTL.

    const UINT16 *ctl;
    UINT16 storage;

    ctl = OvsGetPacketBytes(packet, sizeof *ctl, layers->l4Offset + TCP_CTL_OFS,
                         &storage);
    return ctl ? *ctl : 0;
}


static UINT8
OvsGetTcpFlags(const NET_BUFFER_LIST *packet,    // IN
               const OvsFlowKey *key,   // IN
               const POVS_PACKET_HDR_INFO layers) // IN
{
    UNREFERENCED_PARAMETER(key); // should be removed later

    if (layers->isTcp) {
        return TCP_FLAGS(OvsGetTcpCtl(packet, layers));
    } else {
        return 0;
    }
}

static const EtherArp *
OvsGetArp(const NET_BUFFER_LIST *packet,
          UINT32 ofs,
          EtherArp *storage)
{
    return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage);
}

static const IPHdr *
OvsGetIp(const NET_BUFFER_LIST *packet,
         UINT32 ofs,
         IPHdr *storage)
{
    const IPHdr *ip = OvsGetPacketBytes(packet, sizeof *ip, ofs, storage);
    if (ip) {
        int ipLen = ip->ihl * 4;
        if (ipLen <  MIN_IPV4_HLEN ||
                ipLen > MAX_IPV4_HLEN ||
                OvsPacketLenNBL(packet) < ofs + ipLen) {
           /* IP header is invalid, flag it */
           return NULL;
        } else {
            return ip;
        }
    }
    return NULL;
}

static const TCPHdr *
OvsGetTcp(const NET_BUFFER_LIST *packet,
          UINT32 ofs,
          TCPHdr *storage)
{
    const TCPHdr *tcp = OvsGetPacketBytes(packet, sizeof *tcp, ofs, storage);
    if (tcp) {
        int tcpLen = tcp->doff * 4;
        if (tcpLen >= sizeof *tcp && OvsPacketLenNBL(packet) >= ofs + tcpLen) {
            return tcp;
        }
    }
    return NULL;
}

static const UDPHdr *
OvsGetUdp(const NET_BUFFER_LIST *packet,
          UINT32 ofs,
          UDPHdr *storage)
{
    return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage);
}

static const SCTPHdr *
OvsGetSctp(const NET_BUFFER_LIST *packet,
           UINT32 ofs,
           SCTPHdr *storage)
{
    return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage);
}

static const ICMPHdr *
OvsGetIcmp(const NET_BUFFER_LIST *packet,
           UINT32 ofs,
           ICMPHdr *storage)
{
    return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage);
}

static const MPLSHdr *
OvsGetMpls(const NET_BUFFER_LIST *packet,
           UINT32 ofs,
           MPLSHdr *storage)
{
    return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage);
}
#endif /* __PACKET_PARSER_H_ */
