@@ -132,10 +132,12 @@ Q: Are all features available with all datapaths?
Feature Linux upstream Linux OVS tree Userspace Hyper-V
========================== ============== ============== ========= =======
Connection tracking 4.3 2.5 2.6 YES
+ Connection tracking for IPv6 YES YES YES YES
Conntrack Fragment Reass. 4.3 2.6 2.12 YES
Conntrack Timeout Policies 5.2 2.12 2.14 NO
Conntrack Zone Limit 4.18 2.10 2.13 YES
Conntrack NAT 4.6 2.6 2.8 YES
+ Conntrack NAT6 4.6 2.6 2.8 YES
Tunnel - LISP NO 2.11 NO NO
Tunnel - STT NO 2.4 NO YES
Tunnel - GRE 3.11 1.0 2.4 YES
@@ -758,6 +758,124 @@ Add tunnels
Till the checksum offload support is complete we recommend
disabling TX/RX offloads for IPV6 on Windows VM.
+Add conntrack for ipv6
+~~~~~~~~~~~~~~~~~~~~~~
+
+The Windows Open vSwitch implementation support conntrack ipv6. To use the
+conntrack ipv6. Using the following commands. Take tcp6(replace Protocol to
+icmp6, udp6 to other protocol) for example.
+
+ ::
+
+ normal scenario
+ Vif38(20::1, ofport:2)->Vif40(20:2, ofport:3)
+ Vif38Name="podvif38"
+ Vif40Name="podvif40"
+ Vif38Port=2
+ Vif38Address="20::1"
+ Vif40Port=3
+ Vif40Address="20::2"
+ Vif40MacAddressCli="00-15-5D-F0-01-0C"
+ Vif38MacAddressCli="00-15-5D-F0-01-0b"
+ Protocol="tcp6"
+ > netsh int ipv6 set neighbors $Vif38Name $Vif40Address $Vif40MacAddressCli
+ > netsh int ipv6 set neighbors $Vif40Name $Vif38Address $Vif38MacAddressCli
+ > ovs-ofctl del-flows --strict br-int "table=0,priority=0"
+ > ovs-ofctl add-flow br-int "table=0,priority=1,ip6,ipv6_dst=$Vif40Address,$Protocol,actions=ct(table=1)"
+ > ovs-ofctl add-flow br-int "table=0,priority=1,ip6,ipv6_dst=$Vif38Address,$Protocol,actions=ct(table=1)"
+ > ovs-ofctl add-flow br-int "table=1,priority=1,ip6,ct_state=+new+trk,$Protocol,actions=ct(commit,table=2)"
+ > ovs-ofctl add-flow br-int "table=1,priority=2,ip6,ct_state=-new+rpl+trk,$Protocol,actions=ct(commit,table=2)"
+ > ovs-ofctl add-flow br-int "table=1,priority=1,ip6,ct_state=+trk+est-new,$Protocol,actions=ct(commit,table=2)"
+ > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port"
+ > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port"
+
+ nat scenario
+ Vif38(20::1, ofport:2) -> nat address(20::9) -> Vif42(21::3, ofport:4)
+ Due to not construct flow to return neighbor mac address, we set the neighbor mac address manually
+ Vif38Name="podvif38"
+ Vif42Name="podvif42"
+ Vif38Ip="20::1"
+ Vif38Port=2
+ Vif42Port=4
+ NatAddress="20::9"
+ NatMacAddress="aa:bb:cc:dd:ee:ff"
+ NatMacAddressForCli="aa-bb-cc-dd-ee-ff"
+ Vif42Ip="21::3"
+ Vif38MacAddress="00:15:5D:F0:01:0B"
+ Vif38MacAddressCli="00-15-5D-F0-01-0B"
+ Vif42MacAddress="00:15:5D:F0:01:0D"
+ Protocol="tcp6"
+ > netsh int ipv6 set neighbors $Vif38Name $NatAddress $NatMacAddressForCli
+ > netsh int ipv6 set neighbors $Vif42Name $Vif38Ip $Vif38MacAddressCli
+ > ovs-ofctl del-flows --strict br-int "table=0,priority=0"
+ > ovs-ofctl add-flow br-int "table=0, priority=2,ipv6,dl_dst=$NatMacAddress,ct_state=-trk,$Protocol actions=ct(table=1,zone=456,nat)"
+ > ovs-ofctl add-flow br-int "table=0, priority=1,ipv6,ct_state=-trk,ip6,$Protocol actions=ct(nat, zone=456,table=1)"
+ > ovs-ofctl add-flow br-int "table=1, ipv6,in_port=$Vif38Port,ipv6_dst=$NatAddress,$Protocol,ct_state=+trk+new, actions=ct(commit,nat(dst=$Vif42Ip),zone=456,exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port"
+ > ovs-ofctl add-flow br-int "table=1, ipv6,ct_state=+dnat,$Protocol,action=resubmit(,2)"
+ > ovs-ofctl add-flow br-int "table=1, ipv6,ct_state=+trk+snat,$Protocol,action=resubmit(,2)"
+ > ovs-ofctl add-flow br-int "table=2, ipv6,in_port=$Vif38Port,ipv6_dst=$Vif42Ip,$Protocol, actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port"
+ > ovs-ofctl add-flow br-int "table=2, ipv6,in_port=$Vif42Port,ct_state=-new+est,ct_mark=1,ct_zone=456,$Protocol,actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif38MacAddress,output:$Vif38Port"
+
+ ::
+
+Ftp is a specific protocol, it contains an related flow, we need to match is related state.
+
+ ::
+
+ normal scenario
+ Vif38(20::1, ofport:2)->Vif40(20:2, ofport:3)
+ Vif38Name="podvif38"
+ Vif40Name="podvif40"
+ Vif38Port=2
+ Vif38Address="20::1"
+ Vif38MacAddressCli="00-15-5D-F0-01-0b"
+ Vif40Port=3
+ Vif40Address="20::2"
+ Vif40MacAddressCli="00-15-5D-F0-01-0C"
+ Protocol="tcp6"
+ > netsh int ipv6 set neighbors $Vif38Name $Vif40Address $Vif40MacAddressCli
+ > netsh int ipv6 set neighbors $Vif40Name $Vif38Address $Vif38MacAddressCli
+ > ovs-ofctl del-flows br-int --strict "table=0,priority=0"
+ > ovs-ofctl add-flow br-int "table=0,priority=1,$Protocol actions=ct(table=1)"
+ > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=+new+trk-est,$Protocol,actions=ct(commit,table=2)"
+ > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=-new+trk+est-rel,$Protocol,actions=ct(commit,table=2)"
+ > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=-new+trk+est+rel,$Protocol,actions=ct(commit,table=2)"
+ > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port"
+ > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port"
+
+ nat scenario
+ Vif38(20::1, ofport:2) -> nat address(20::9) -> Vif42(21::3, ofport:4)
+ Due to not construct flow to return neighbor mac address, we set the neighbor mac address manually
+ Vif38Port=2
+ Vif42Port=4
+ Vif38Name="podvif38"
+ Vif42Name="podvif42"
+ NatAddress="20::9"
+ NatMacAddress="aa:bb:cc:dd:ee:ff"
+ NatMacAddressForCli="aa-bb-cc-dd-ee-ff"
+ Vif42Ip="21::3"
+ Vif38MacAddress="00:15:5D:F0:01:0B"
+ Vif42MacAddress="00:15:5D:F0:01:0D"
+ Protocol="tcp6"
+ > netsh int ipv6 set neighbors $Vif38Name $NatAddress $NatMacAddressForCli
+ > netsh int ipv6 set neighbors $Vif42Name $NatAddress $NatMacAddressForCli
+ > ovs-ofctl del-flows br-int --strict "table=0,priority=0"
+ > ovs-ofctl add-flow br-int "table=0,priority=2,ipv6,dl_dst=$NatMacAddress,ct_state=-trk,$Protocol actions=ct(table=1,zone=456,nat)"
+ > ovs-ofctl add-flow br-int "table=0,priority=1,ipv6,ct_state=-trk,ip6,$Protocol actions=ct(nat, zone=456,table=1)"
+ > ovs-ofctl add-flow br-int "table=1,ipv6,in_port=$Vif38Port,ipv6_dst=$NatAddress,ct_state=+trk+new,$Protocol actions=ct(commit,nat(dst=$Vif42Ip),zone=456,exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port"
+ > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+dnat,$Protocol,action=resubmit(,2)"
+ > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+snat,$Protocol,action=resubmit(,2)"
+ > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+rel,$Protocol,action=resubmit(,2)"
+ > ovs-ofctl add-flow br-int "table=2,ipv6,in_port=$Vif38Port,ipv6_dst=$Vif42Ip,$Protocol, actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,output:$Vif42Port"
+ > ovs-ofctl add-flow br-int "table=2,ipv6,in_port=$Vif42Port,ct_state=-new+est,ct_mark=1,ct_zone=456,$Protocol,actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif38MacAddress,output:$Vif38Port"
+
+ ::
+
+ note::
+
+ Till the checksum offload support is complete we recommend
+ disabling TX/RX offloads for IPV6 on Windows VM.
+
Windows Services
----------------
@@ -1,3 +1,8 @@
+v2.17.0 - 27 Mar 2022
+---------------------
+ - Tcp6, Udp6, Icmp6, ftp6 conntrack support on Windows ovs.
+ * In current OVS we could use ct related state and actions for Ftp6, Udp6,
+ Icmp6, Ftp6 connection.
Post-v2.17.0
---------------------
- libopenvswitch API change:
@@ -1618,6 +1618,180 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
return NDIS_STATUS_SUCCESS;
}
+UINT16
+OvsCalculateICMPv6Checksum(struct in6_addr srcAddr,
+ struct in6_addr dstAddr,
+ uint16_t totalLength,
+ uint16_t protocol,
+ uint16_t *icmpStart,
+ uint16_t length)
+{
+ uint32_t checkSum = 0;
+ uint16_t *srcAddressPtr = (uint16_t *)&srcAddr;
+ uint16_t *dstAddressPtr = (uint16_t *)&dstAddr;
+ uint16_t *value = (uint16_t *)icmpStart;
+ int index = 0;
+
+ checkSum = totalLength + protocol;
+
+ for (int i = 0; i < 8; i++) {
+ checkSum += ntohs(srcAddressPtr[i]);
+ checkSum += ntohs(dstAddressPtr[i]);
+ }
+
+ for (index = length; index > 1; index -= 2) {
+ checkSum += ntohs(*value);
+ value++;
+ }
+
+ if (index > 0) {
+ checkSum += (uint16_t)(*((uint8_t *)value));
+ }
+
+ while ((checkSum >> 16) & 0xffff) {
+ checkSum = (checkSum & 0xffff) + ((checkSum >> 16) & 0xffff);
+ }
+
+ return htons(~((uint16_t)checkSum));
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * OvsUpdateAddressAndPortForIpv6--
+ *
+ * Update ipv6 address in ovsFwdCtx.curNbl.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsUpdateAddressAndPortForIpv6(OvsForwardingContext *ovsFwdCtx,
+ struct in6_addr newAddr, UINT16 newPort,
+ BOOLEAN isSource, BOOLEAN isTx)
+{
+ PUINT8 bufferStart;
+ UINT32 hdrSize;
+ OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
+ IPv6Hdr *ipHdr;
+ TCPHdr *tcpHdr = NULL;
+ UDPHdr *udpHdr = NULL;
+ struct in6_addr *addrField = NULL;
+ UINT16 *portField = NULL;
+ UINT16 *checkField = NULL;
+ BOOLEAN l4Offload = FALSE;
+ NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
+
+ ASSERT(layers->value != 0);
+
+ if (layers->isTcp || layers->isUdp) {
+ hdrSize = layers->l4Offset +
+ layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
+ } else {
+ hdrSize = layers->l3Offset + sizeof (*ipHdr);
+ }
+
+ csumInfo.Value = NET_BUFFER_LIST_INFO(ovsFwdCtx->curNbl,
+ TcpIpChecksumNetBufferListInfo);
+
+ bufferStart = OvsGetHeaderBySize(ovsFwdCtx, hdrSize);
+ if (!bufferStart) {
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ ipHdr = (IPv6Hdr *)(bufferStart + layers->l3Offset);
+
+ if (layers->isTcp) {
+ tcpHdr = (TCPHdr *)(bufferStart + layers->l4Offset);
+ } else if (layers->isUdp) {
+ udpHdr = (UDPHdr *)(bufferStart + layers->l4Offset);
+ }
+
+ if (isSource) {
+ addrField = &ipHdr->saddr;
+ if (tcpHdr) {
+ portField = &tcpHdr->source;
+ checkField = &tcpHdr->check;
+ l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.TcpChecksum :
+ ((BOOLEAN)csumInfo.Receive.TcpChecksumSucceeded ||
+ (BOOLEAN)csumInfo.Receive.TcpChecksumFailed);
+ } else if (udpHdr) {
+ portField = &udpHdr->source;
+ checkField = &udpHdr->check;
+ l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.UdpChecksum :
+ ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded ||
+ (BOOLEAN)csumInfo.Receive.UdpChecksumFailed);
+ }
+ if (isTx && l4Offload) {
+ *checkField = IPv6PseudoChecksum((UINT32 *)&newAddr, (UINT32 *)&ipHdr->daddr,
+ tcpHdr ? IPPROTO_TCP : IPPROTO_UDP,
+ ntohs(ipHdr->payload_len) -
+ (ovsFwdCtx->layers.l4Offset - ovsFwdCtx->layers.l3Offset));
+ }
+ } else {
+ addrField = &ipHdr->daddr;
+ if (tcpHdr) {
+ portField = &tcpHdr->dest;
+ checkField = &tcpHdr->check;
+ l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.TcpChecksum :
+ ((BOOLEAN)csumInfo.Receive.TcpChecksumSucceeded ||
+ (BOOLEAN)csumInfo.Receive.TcpChecksumFailed);
+ } else if (udpHdr) {
+ portField = &udpHdr->dest;
+ checkField = &udpHdr->check;
+ l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.UdpChecksum :
+ ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded ||
+ (BOOLEAN)csumInfo.Receive.UdpChecksumFailed);
+ }
+
+ if (isTx && l4Offload) {
+ *checkField = IPv6PseudoChecksum((UINT32 *)&ipHdr->saddr, (UINT32 *)&newAddr,
+ tcpHdr ? IPPROTO_TCP : IPPROTO_UDP,
+ ntohs(ipHdr->payload_len) -
+ (ovsFwdCtx->layers.l4Offset - ovsFwdCtx->layers.l3Offset));
+ }
+ }
+
+ if (memcmp(addrField, &newAddr, sizeof(struct in6_addr))) {
+ if ((checkField && *checkField != 0) && (!l4Offload || !isTx)) {
+ uint32_t *oldField = (uint32_t *)addrField;
+ uint32_t *newField = (uint32_t *)&newAddr;
+ *checkField = ChecksumUpdate32(*checkField, oldField[0], newField[0]);
+ *checkField = ChecksumUpdate32(*checkField, oldField[1], newField[1]);
+ *checkField = ChecksumUpdate32(*checkField, oldField[2], newField[2]);
+ *checkField = ChecksumUpdate32(*checkField, oldField[3], newField[3]);
+ }
+
+ *addrField = newAddr;
+
+ if (layers->isIcmp) {
+ ICMPHdr *icmp =(ICMPHdr *)(bufferStart + layers->l4Offset);
+ icmp->checksum = 0x00;
+ icmp->checksum = OvsCalculateICMPv6Checksum(ipHdr->saddr, ipHdr->daddr,
+ ntohs(ipHdr->payload_len),
+ IPPROTO_ICMPV6,
+ (uint16_t *)icmp,
+ ntohs(ipHdr->payload_len));
+ }
+ }
+
+ if (portField && *portField != newPort) {
+ if ((checkField) && (!l4Offload || !isTx)) {
+ /* Recompute total checksum. */
+ *checkField = ChecksumUpdate16(*checkField, *portField,
+ newPort);
+ }
+ *portField = newPort;
+ }
+
+ return NDIS_STATUS_SUCCESS;
+}
+
/*
*----------------------------------------------------------------------------
* OvsUpdateIPv4Header --
@@ -133,4 +133,9 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
UINT32 newAddr, UINT16 newPort,
BOOLEAN isSource, BOOLEAN isTx);
+NDIS_STATUS
+OvsUpdateAddressAndPortForIpv6(OvsForwardingContext *ovsFwdCtx,
+ struct in6_addr newAddr, UINT16 newPort,
+ BOOLEAN isSource, BOOLEAN isTx);
+
#endif /* __ACTIONS_H_ */
@@ -14,15 +14,21 @@
* limitations under the License.
*/
+#include <string.h>
#include "Conntrack.h"
#include "PacketParser.h"
+#include "util.h"
/* Eg: 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)*/
#define FTP_PASV_RSP_PREFIX "227"
+#define FTP_EXTEND_PASV_RSP_PREFIX "229"
+#define FTP_EXTEND_ACTIVE_RSP_PREFIX "200"
typedef enum FTP_TYPE {
FTP_TYPE_PASV = 1,
- FTP_TYPE_ACTIVE
+ FTP_TYPE_ACTIVE,
+ FTP_EXTEND_TYPE_PASV,
+ FTP_EXTEND_TYPE_ACTIVE
} FTP_TYPE;
static __inline UINT32
@@ -123,12 +129,11 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
POVS_CT_ENTRY entry,
BOOLEAN request)
{
- NDIS_STATUS status;
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
FTP_TYPE ftpType = 0;
const char *buf;
char temp[256] = { 0 };
char ftpMsg[256] = { 0 };
-
UINT32 len;
TCPHdr tcpStorage;
const TCPHdr *tcp;
@@ -156,6 +161,9 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
if ((len >= 5) && (OvsStrncmp("PORT", ftpMsg, 4) == 0)) {
ftpType = FTP_TYPE_ACTIVE;
req = ftpMsg + 4;
+ } else if ((len >= 5) && (OvsStrncmp("EPRT", ftpMsg, 4) == 0)) {
+ ftpType = FTP_EXTEND_TYPE_ACTIVE;
+ req = ftpMsg + 4;
}
} else {
if ((len >= 4) && (OvsStrncmp(FTP_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) {
@@ -176,6 +184,25 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
/* PASV command without ( */
req = ftpMsg + 3;
}
+ } else if ((len >= 4) && (OvsStrncmp(FTP_EXTEND_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) {
+ ftpType = FTP_EXTEND_TYPE_PASV;
+ /* The ftp extended passive mode only contain port info, ip address
+ * is same with the network protocol used by control connection.
+ * 229 Entering Extended Passive Mode (|||port|)
+ * */
+ char *paren;
+ paren = strchr(ftpMsg, '|');
+ if (paren) {
+ req = paren + 3;
+ } else {
+ /* Not a valid EPSV packet. */
+ return NDIS_STATUS_INVALID_PACKET;
+ }
+
+ if (!(*req > '0' && * req < '9')) {
+ /* Not a valid port number. */
+ return NDIS_STATUS_INVALID_PACKET;
+ }
}
}
@@ -184,28 +211,102 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
return NDIS_STATUS_SUCCESS;
}
- UINT32 arr[6] = {0};
- status = OvsCtExtractNumbers(req, len, arr, 6, ',');
+ struct ct_addr clientIp = {0}, serverIp = {0};
+ UINT16 port = 0;
- if (status != NDIS_STATUS_SUCCESS) {
- return status;
- }
+ if (ftpType == FTP_TYPE_ACTIVE || ftpType == FTP_TYPE_PASV) {
+ UINT32 arr[6] = {0};
+ status = OvsCtExtractNumbers(req, len, arr, 6, ',');
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ return status;
+ }
+
+ UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) |
+ (arr[2] << 8) | arr[3]);
+ port = ntohs(((arr[4] << 8) | arr[5]));
+
+ serverIp.ipv4 = ip;
+ clientIp.ipv4 = key->ipKey.nwDst;
+ } else {
+ if (ftpType == FTP_EXTEND_TYPE_ACTIVE) {
+ /** In ftp active mode, we need to parse string like below:
+ * " |2|20::1|50778|", 2 represent address is ipv6, 1 represent
+ * address family is ipv4, "20::1" is ipv6 address, 50779 is port
+ * client need to listen.
+ * **/
+ char *curHdr = NULL;
+ char *nextHdr = NULL;
+ int index = 0;
+ int isIpv6AddressFamily = 0;
+ char ftpStr[1024] = {0x00};
+
+ RtlCopyMemory(ftpStr, req, strlen(req));
+ for (curHdr = ftpStr; *curHdr != '|'; curHdr++);
+ curHdr = curHdr + 1;;
+ do {
+ /** index == 0 parse address family,
+ * index == 1 parse address,
+ * index == 2 parse port **/
+ for (nextHdr = curHdr; *nextHdr != '|'; nextHdr++);
+ *nextHdr = '\0';
+
+ if (*curHdr == '0' || !curHdr || index > 2) {
+ break;
+ }
+
+ if (index == 0 && *curHdr == '1') {
+ isIpv6AddressFamily = 0;
+ } else if (index == 0 && *curHdr == '2') {
+ isIpv6AddressFamily = 1;
+ }
+
+ if (index == 1 && isIpv6AddressFamily) {
+ OvsIpv6StringToAddress(curHdr, &clientIp.ipv6);
+ }
+
+ if (index == 2) {
+ for (char *tmp = curHdr; *tmp != '\0'; tmp++) {
+ port = port * 10 + (*tmp - '0');
+ }
+ port = htons(port);
+ }
+
+ curHdr = nextHdr + 1;
+ index++;
+ } while (1);
+
+ if (index < 2) { /* Not valid packet due to less than three parameter */
+ return NDIS_STATUS_SUCCESS;
+ }
+ serverIp.ipv6 = key->ipv6Key.ipv6Dst;
+ }
+
+ if (ftpType == FTP_EXTEND_TYPE_PASV) {
+ /* Here used to parse the string "229 Entering Extended Passive Mode (|||50522|),
+ * 50522 is the port we want". */
+ char *tmp = req;
+ while (*tmp != '|' && *tmp != '\0') {
+ port = port * 10 + (*tmp - '0');
+ tmp++;
+ }
- UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) |
- (arr[2] << 8) | arr[3]);
- UINT16 port = ntohs(((arr[4] << 8) | arr[5]));
+ port = htons(port);
+
+ serverIp.ipv6 = key->ipv6Key.ipv6Src;
+ clientIp.ipv6 = key->ipv6Key.ipv6Dst;
+ }
+ }
switch (ftpType) {
case FTP_TYPE_PASV:
/* Ensure that the command states Server's IP address */
- ASSERT(ip == key->ipKey.nwSrc);
-
OvsCtRelatedEntryCreate(key->ipKey.nwProto,
key->l2.dlType,
/* Server's IP */
- ip,
+ serverIp,
/* Use intended client's IP */
- key->ipKey.nwDst,
+ clientIp,
/* Dynamic port opened on server */
port,
/* We don't know the client port */
@@ -217,9 +318,33 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
OvsCtRelatedEntryCreate(key->ipKey.nwProto,
key->l2.dlType,
/* Server's default IP address */
- key->ipKey.nwDst,
+ serverIp,
+ /* Client's IP address */
+ clientIp,
+ /* FTP Data Port is 20 */
+ ntohs(IPPORT_FTP_DATA),
+ /* Port opened up on Client */
+ port,
+ currentTime,
+ entry);
+ break;
+ case FTP_EXTEND_TYPE_PASV:
+ OvsCtRelatedEntryCreate(key->ipv6Key.nwProto,
+ key->l2.dlType,
+ serverIp,
+ clientIp,
+ port,
+ 0,
+ currentTime,
+ entry);
+ break;
+ case FTP_EXTEND_TYPE_ACTIVE:
+ OvsCtRelatedEntryCreate(key->ipv6Key.nwProto,
+ key->l2.dlType,
+ /* Server's default IP address */
+ serverIp,
/* Client's IP address */
- ip,
+ clientIp,
/* FTP Data Port is 20 */
ntohs(IPPORT_FTP_DATA),
/* Port opened up on Client */
@@ -70,6 +70,17 @@ OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp)
|| icmp->type == ICMP4_TIMESTAMP_REQUEST;
}
+BOOLEAN
+OvsConntrackValidateIcmp6Packet(const ICMPHdr *icmp)
+{
+ if (!icmp) {
+ OVS_LOG_TRACE("Invalid ICMP packet detected, header cannot be NULL");
+ return FALSE;
+ }
+
+ return icmp->type == ICMP6_ECHO_REQUEST;
+}
+
OVS_CT_ENTRY *
OvsConntrackCreateIcmpEntry(UINT64 now)
{
@@ -42,12 +42,9 @@ OvsHashNatKey(const OVS_CT_KEY *key)
static __inline BOOLEAN
OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2)
{
- // XXX: Compare IPv6 key as well
#define FIELD_COMPARE(field) \
if (key1->field != key2->field) return FALSE
- FIELD_COMPARE(src.addr.ipv4_aligned);
- FIELD_COMPARE(dst.addr.ipv4_aligned);
FIELD_COMPARE(src.port);
FIELD_COMPARE(dst.port);
FIELD_COMPARE(zone);
@@ -56,6 +53,15 @@ OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2)
FIELD_COMPARE(dst.icmp_type);
FIELD_COMPARE(src.icmp_code);
FIELD_COMPARE(dst.icmp_code);
+
+ if (memcmp(&(key1->src.addr), &(key2->src.addr), sizeof(struct ct_addr))) {
+ return FALSE;
+ }
+
+ if (memcmp(&(key1->dst.addr), &(key2->dst.addr), sizeof(struct ct_addr))) {
+ return FALSE;
+ }
+
return TRUE;
#undef FIELD_COMPARE
}
@@ -215,18 +221,37 @@ OvsNatPacket(OvsForwardingContext *ovsFwdCtx,
} else {
key->ipKey.nwDst = endpoint->addr.ipv4_aligned;
}
- } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)){
- // XXX: IPv6 packet not supported yet.
- return;
+ } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)) {
+ OvsUpdateAddressAndPortForIpv6(ovsFwdCtx,
+ endpoint->addr.ipv6,
+ endpoint->port, isSrcNat,
+ !reverse);
+ if (isSrcNat) {
+ key->ipv6Key.ipv6Src = endpoint->addr.ipv6;
+ } else {
+ key->ipv6Key.ipv6Dst = endpoint->addr.ipv6;
+ }
}
if (natAction & (NAT_ACTION_SRC_PORT | NAT_ACTION_DST_PORT)) {
- if (isSrcNat) {
- if (key->ipKey.l4.tpSrc != 0) {
- key->ipKey.l4.tpSrc = endpoint->port;
+ if (ctKey->dl_type == htons(ETH_TYPE_IPV4)) {
+ if (isSrcNat) {
+ if (key->ipKey.l4.tpSrc != 0) {
+ key->ipKey.l4.tpSrc = endpoint->port;
+ }
+ } else {
+ if (key->ipKey.l4.tpDst != 0) {
+ key->ipKey.l4.tpDst = endpoint->port;
+ }
}
- } else {
- if (key->ipKey.l4.tpDst != 0) {
- key->ipKey.l4.tpDst = endpoint->port;
+ } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)) {
+ if (isSrcNat) {
+ if (key->ipv6Key.l4.tpSrc != 0) {
+ key->ipv6Key.l4.tpSrc = endpoint->port;
+ }
+ } else {
+ if (key->ipv6Key.l4.tpDst != 0) {
+ key->ipv6Key.l4.tpDst = endpoint->port;
+ }
}
}
}
@@ -326,8 +351,8 @@ OvsNatTranslateCtEntry(OVS_CT_ENTRY *entry)
ctAddr.ipv4_aligned = htonl(
ntohl(entry->natInfo.minAddr.ipv4_aligned) + addrIndex);
} else {
- // XXX: IPv6 not supported
- return FALSE;
+ /** Current, only support nat single address**/
+ ctAddr.ipv6_aligned = entry->natInfo.minAddr.ipv6_aligned;
}
port = firstPort;
@@ -379,8 +404,33 @@ OvsNatTranslateCtEntry(OVS_CT_ENTRY *entry)
ctAddr.ipv4_aligned = htonl(
ntohl(ctAddr.ipv4_aligned) + 1);
} else {
- // XXX: IPv6 not supported
- return FALSE;
+ /** When all ports was used, return fails indicate exceed range. **/
+ uint32_t addr[8] = {0};
+ uint16_t *tmpAddr = (uint16_t *)&(ctAddr.ipv6_aligned);
+ for (int m = 0; m < 8; m++) {
+ addr[m] = tmpAddr[m];
+ }
+
+ uint16_t carry = 1, i = 8;
+ while (carry && i)
+ {
+ addr[i-1] += carry;
+ if (addr[i-1] > 0xffff || !addr[i-1])
+ {
+ carry = 1;
+ addr[i-1] &= 0xffff;
+ } else carry = 0;
+ i--;
+ }
+
+ if (carry) {
+ OVS_LOG_INFO("Ipv6 address incremented overflow.");
+ return FALSE;
+ }
+
+ for (int m = 0; m < 8; m++) {
+ tmpAddr[m] = (uint16_t)addr[m];
+ }
}
} else {
ctAddr = entry->natInfo.minAddr;
@@ -38,10 +38,22 @@ static __inline BOOLEAN
OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey)
{
/* FTP PASV - Client initiates the connection from unknown port */
- if ((incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) &&
+ if ((incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.dl_type == htons(ETH_TYPE_IPV4)) &&
+ (incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) &&
(incomingKey.dst.port == entryKey.src.port) &&
(incomingKey.src.addr.ipv4 == entryKey.dst.addr.ipv4) &&
- (incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.nw_proto == entryKey.nw_proto)) {
+ return TRUE;
+ }
+
+ if ((incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.dl_type == htons(ETH_TYPE_IPV6)) &&
+ !memcmp(&(incomingKey.dst.addr.ipv6), &(entryKey.src.addr.ipv6),
+ sizeof(incomingKey.dst.addr.ipv6)) &&
+ (incomingKey.dst.port == entryKey.src.port) &&
+ !memcmp(&(incomingKey.src.addr.ipv6), &(entryKey.dst.addr.ipv6),
+ sizeof(incomingKey.src.addr.ipv6)) &&
(incomingKey.nw_proto == entryKey.nw_proto)) {
return TRUE;
}
@@ -51,10 +63,22 @@ OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey)
* except 20. In this case, the incomingKey's src port is different with
* entryKey's src port.
*/
- if ((incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) &&
+ if ((incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.dl_type == htons(ETH_TYPE_IPV4)) &&
+ (incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) &&
(incomingKey.dst.addr.ipv4 == entryKey.dst.addr.ipv4) &&
(incomingKey.dst.port == entryKey.dst.port) &&
- (incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.nw_proto == entryKey.nw_proto)) {
+ return TRUE;
+ }
+
+ if ((incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.dl_type == htons(ETH_TYPE_IPV6)) &&
+ !memcmp(&(incomingKey.src.addr.ipv6), &(entryKey.src.addr.ipv6),
+ sizeof(incomingKey.src.addr.ipv6)) &&
+ !memcmp(&(incomingKey.dst.addr.ipv6), &(entryKey.dst.addr.ipv6),
+ sizeof(incomingKey.src.addr.ipv6)) &&
+ (incomingKey.dst.port == entryKey.dst.port) &&
(incomingKey.nw_proto == entryKey.nw_proto)) {
return TRUE;
}
@@ -110,8 +134,8 @@ OvsCtRelatedEntryDelete(POVS_CT_REL_ENTRY entry)
NDIS_STATUS
OvsCtRelatedEntryCreate(UINT8 ipProto,
UINT16 dl_type,
- UINT32 serverIp,
- UINT32 clientIp,
+ struct ct_addr serverIp,
+ struct ct_addr clientIp,
UINT16 serverPort,
UINT16 clientPort,
UINT64 currentTime,
@@ -127,13 +151,18 @@ OvsCtRelatedEntryCreate(UINT8 ipProto,
RtlZeroMemory(entry, sizeof(struct OVS_CT_REL_ENTRY));
entry->expiration = currentTime + (CT_INTERVAL_SEC * 60);
- entry->key.src.addr.ipv4 = serverIp;
- entry->key.dst.addr.ipv4 = clientIp;
entry->key.nw_proto = ipProto;
entry->key.dl_type = dl_type;
entry->key.src.port = serverPort;
entry->key.dst.port = clientPort;
entry->parent = parent;
+ if (dl_type == htons(ETH_TYPE_IPV6)) {
+ entry->key.src.addr.ipv6 = serverIp.ipv6;
+ entry->key.dst.addr.ipv6 = clientIp.ipv6;
+ } else {
+ entry->key.src.addr.ipv4 = serverIp.ipv4;
+ entry->key.dst.addr.ipv4 = clientIp.ipv4;
+ }
UINT32 hash = OvsExtractCtRelatedKeyHash(&entry->key);
@@ -189,6 +189,13 @@ OvsCtSetZoneLimit(int zone, ULONG value) {
NdisReleaseSpinLock(&ovsCtZoneLock);
}
+static uint32_t
+OvsCtEndpointHashAdd(uint32_t hash, const struct ct_endpoint *ep)
+{
+ BUILD_ASSERT_DECL(sizeof *ep % 4 == 0);
+ return OvsJhashBytes((UINT32 *)ep, sizeof *ep, hash);
+}
+
/*
*----------------------------------------------------------------------------
* OvsCtHashKey
@@ -199,8 +206,10 @@ UINT32
OvsCtHashKey(const OVS_CT_KEY *key)
{
UINT32 hsrc, hdst, hash;
- hsrc = key->src.addr.ipv4 | ntohl(key->src.port);
- hdst = key->dst.addr.ipv4 | ntohl(key->dst.port);
+ hsrc = ntohl(key->src.port);
+ hdst = ntohl(key->dst.port);
+ hsrc = OvsCtEndpointHashAdd(hsrc, &key->src);
+ hdst = OvsCtEndpointHashAdd(hdst, &key->dst);
hash = hsrc ^ hdst; /* TO identify reverse traffic */
hash = hash | (key->zone + key->nw_proto);
hash = OvsJhashWords((uint32_t*) &hash, 1, hash);
@@ -341,6 +350,26 @@ OvsCtEntryCreate(OvsForwardingContext *fwdCtx,
}
break;
}
+ case IPPROTO_ICMPV6:
+ {
+ ICMPHdr storage;
+ const ICMPHdr *icmp;
+ icmp = OvsGetIcmp(curNbl, layers->l4Offset, &storage);
+ if (!OvsConntrackValidateIcmp6Packet(icmp)) {
+ if(icmp) {
+ OVS_LOG_TRACE("Invalid ICMP packet detected, icmp->type %u",
+ icmp->type);
+ }
+ state = OVS_CS_F_INVALID;
+ break;
+ }
+
+ if (commit) {
+ entry = OvsConntrackCreateIcmpEntry(currentTime);
+ }
+ break;
+ }
+
case IPPROTO_ICMP:
{
ICMPHdr storage;
@@ -435,6 +464,15 @@ OvsCtUpdateEntry(OVS_CT_ENTRY* entry,
NdisReleaseSpinLock(&(entry->lock));
break;
}
+
+ case IPPROTO_ICMPV6:
+ {
+ NdisAcquireSpinLock(&(entry->lock));
+ status = OvsConntrackUpdateIcmpEntry(entry, reply, now);
+ NdisReleaseSpinLock(&(entry->lock));
+ break;
+ }
+
case IPPROTO_UDP:
{
NdisAcquireSpinLock(&(entry->lock));
@@ -528,6 +566,23 @@ OvsDetectCtPacket(OvsForwardingContext *fwdCtx,
}
return NDIS_STATUS_NOT_SUPPORTED;
case ETH_TYPE_IPV6:
+ if (key->ipv6Key.nwProto == IPPROTO_ICMPV6
+ || key->ipv6Key.nwProto == IPPROTO_TCP
+ || key->ipv6Key.nwProto == IPPROTO_UDP) {
+ /** TODO fragment **/
+
+ /** Extract flow key from packet and assign it to
+ * returned parameter. **/
+ status = OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo,
+ &newFlowKey, &fwdCtx->layers,
+ !OvsIphIsZero(&(fwdCtx->tunKey.dst)) ? &(fwdCtx->tunKey) : NULL);
+ if (status != NDIS_STATUS_SUCCESS) {
+ OVS_LOG_ERROR("Extract flow for ipv6 failed Nbl %p", fwdCtx->curNbl);
+ return status;
+ }
+ *key = newFlowKey;
+ return NDIS_STATUS_SUCCESS;
+ }
return NDIS_STATUS_NOT_SUPPORTED;
}
@@ -606,6 +661,54 @@ OvsCtLookup(OvsConntrackKeyLookupCtx *ctx)
return found;
}
+const TCPHdr*
+OvsGetTcpHeader(PNET_BUFFER_LIST nbl,
+ OVS_PACKET_HDR_INFO *layers,
+ VOID *storage,
+ UINT32 *tcpPayloadLen)
+{
+ IPHdr *ipHdr;
+ IPv6Hdr *ipv6Hdr;
+ TCPHdr *tcp;
+ VOID *dest = storage;
+ uint16_t ipv6ExtLength = 0;
+
+ if (layers->isIPv6) {
+ ipv6Hdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl),
+ layers->l4Offset + sizeof(TCPHdr),
+ NULL, 1, 0);
+ if (ipv6Hdr == NULL) {
+ return NULL;
+ }
+
+ tcp = (TCPHdr *)((PCHAR)ipv6Hdr + layers->l4Offset);
+ ipv6Hdr = (IPv6Hdr *)((PCHAR)ipv6Hdr + layers->l3Offset);
+ if (tcp->doff * 4 >= sizeof *tcp) {
+ NdisMoveMemory(dest, tcp, sizeof(TCPHdr));
+ ipv6ExtLength = layers->l4Offset - layers->l3Offset - sizeof(IPv6Hdr);
+ *tcpPayloadLen = (ntohs(ipv6Hdr->payload_len) - ipv6ExtLength - TCP_HDR_LEN(tcp));
+ return storage;
+ }
+ } else {
+ ipHdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl),
+ layers->l4Offset + sizeof(TCPHdr),
+ NULL, 1 /*no align*/, 0);
+ if (ipHdr == NULL) {
+ return NULL;
+ }
+
+ ipHdr = (IPHdr *)((PCHAR)ipHdr + layers->l3Offset);
+ tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4);
+
+ if (tcp->doff * 4 >= sizeof *tcp) {
+ NdisMoveMemory(dest, tcp, sizeof(TCPHdr));
+ *tcpPayloadLen = TCP_DATA_LENGTH(ipHdr, tcp);
+ return storage;
+ }
+ }
+ return NULL;
+}
+
static UINT8
OvsReverseIcmpType(UINT8 type)
{
@@ -622,6 +725,10 @@ OvsReverseIcmpType(UINT8 type)
return ICMP4_INFO_REPLY;
case ICMP4_INFO_REPLY:
return ICMP4_INFO_REQUEST;
+ case ICMP6_ECHO_REQUEST:
+ return ICMP6_ECHO_REPLY;
+ case ICMP6_ECHO_REPLY:
+ return ICMP6_ECHO_REQUEST;
default:
return 0;
}
@@ -692,8 +799,8 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
return NDIS_STATUS_INVALID_PACKET;
}
/* Separate ICMP connection: identified using id */
- ctx->key.dst.icmp_id = icmp->fields.echo.id;
- ctx->key.src.icmp_id = icmp->fields.echo.id;
+ ctx->key.dst.icmp_id = ntohs(icmp->fields.echo.id);
+ ctx->key.src.icmp_id = ntohs(icmp->fields.echo.id);
ctx->key.src.icmp_type = icmp->type;
ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type);
break;
@@ -702,7 +809,6 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
case ICMP4_PARAM_PROB:
case ICMP4_SOURCE_QUENCH:
case ICMP4_REDIRECT: {
- /* XXX Handle inner packet */
ctx->related = TRUE;
break;
}
@@ -717,7 +823,53 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
ctx->key.src.port = flowKey->ipv6Key.l4.tpSrc;
ctx->key.dst.port = flowKey->ipv6Key.l4.tpDst;
- /* XXX Handle ICMPv6 errors*/
+ if (flowKey->ipv6Key.nwProto == IPPROTO_ICMPV6) {
+ ICMPHdr icmpStorage;
+ const ICMPHdr *icmp;
+ icmp = OvsGetIcmp(curNbl, l4Offset, &icmpStorage);
+ ASSERT(icmp);
+
+ switch (icmp->type) {
+ case ICMP6_ECHO_REQUEST:
+ case ICMP6_ECHO_REPLY: {
+ ctx->key.dst.icmp_id = ntohs(icmp->fields.echo.id);
+ ctx->key.src.icmp_id = ntohs(icmp->fields.echo.id);
+ ctx->key.src.icmp_type = icmp->type;
+ ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type);
+ break;
+ }
+ case ICMP6_DST_UNREACH:
+ case ICMP6_TIME_EXCEEDED:
+ case ICMP6_PARAM_PROB:
+ case ICMP6_PACKET_TOO_BIG: {
+ Ipv6Key ipv6Key;
+ OVS_PACKET_HDR_INFO layers;
+ OvsExtractLayers(curNbl, &layers);
+ layers.l3Offset = layers.l7Offset;
+ NDIS_STATUS status = OvsParseIPv6(curNbl, &ipv6Key, &layers);
+ if (status != NDIS_STATUS_SUCCESS) {
+ return NDIS_STATUS_INVALID_PACKET;
+ }
+ ctx->key.src.addr.ipv6 = ipv6Key.ipv6Src;
+ ctx->key.dst.addr.ipv6 = ipv6Key.ipv6Dst;
+ ctx->key.nw_proto = ipv6Key.nwProto;
+ if (ipv6Key.nwProto == SOCKET_IPPROTO_TCP) {
+ OvsParseTcp(curNbl, &(ipv6Key.l4), &layers);
+ } else if (ipv6Key.nwProto == SOCKET_IPPROTO_UDP) {
+ OvsParseUdp(curNbl, &(ipv6Key.l4), &layers);
+ } else if (ipv6Key.nwProto == SOCKET_IPPROTO_SCTP) {
+ OvsParseSctp(curNbl, &ipv6Key.l4, &layers);
+ }
+ ctx->key.src.port = ipv6Key.l4.tpSrc;
+ ctx->key.dst.port = ipv6Key.l4.tpDst;
+ OvsCtKeyReverse(&ctx->key);
+ ctx->related = TRUE;
+ break;
+ }
+ default:
+ ctx->related = FALSE;
+ }
+ }
} else {
return NDIS_STATUS_INVALID_PACKET;
}
@@ -744,6 +896,13 @@ OvsDetectFtpPacket(OvsFlowKey *key) {
ntohs(key->ipKey.l4.tpSrc) == IPPORT_FTP));
}
+static __inline BOOLEAN
+OvsDetectFtp6Packet(OvsFlowKey *key) {
+ return (key->ipv6Key.nwProto == IPPROTO_TCP &&
+ (ntohs(key->ipv6Key.l4.tpDst) == IPPORT_FTP ||
+ ntohs(key->ipv6Key.l4.tpSrc) == IPPORT_FTP));
+}
+
/*
*----------------------------------------------------------------------------
* OvsProcessConntrackEntry
@@ -777,11 +936,22 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
} else {
CT_UPDATE_RES result;
UINT32 bucketIdx;
- result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, layers,
- ctx->reply, currentTime);
+
+ if (layers->isIPv6) {
+ result = OvsCtUpdateEntry(entry, curNbl, key->ipv6Key.nwProto, layers,
+ ctx->reply, currentTime);
+ } else {
+ result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, layers,
+ ctx->reply, currentTime);
+ }
+
switch (result) {
case CT_UPDATE_VALID:
state |= OVS_CS_F_ESTABLISHED;
+ /** when the ct state is established, at least
+ * request/reply two packets go through,
+ * so the ct_state shouldn't contain new.**/
+ state &= ~OVS_CS_F_NEW;
if (ctx->reply) {
state |= OVS_CS_F_REPLY_DIR;
}
@@ -796,9 +966,17 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
OvsCtEntryDelete(ctx->entry, TRUE);
NdisReleaseRWLock(ovsCtBucketLock[bucketIdx], &lockStateTable);
ctx->entry = NULL;
- entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, layers,
- ctx, key, natInfo, commit, currentTime,
- entryCreated);
+
+ if (layers->isIPv6) {
+ entry = OvsCtEntryCreate(fwdCtx, key->ipv6Key.nwProto, layers,
+ ctx, key, natInfo, commit, currentTime,
+ entryCreated);
+ } else {
+ entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, layers,
+ ctx, key, natInfo, commit, currentTime,
+ entryCreated);
+ }
+
if (!entry) {
return NULL;
}
@@ -810,7 +988,8 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
}
if (entry) {
NdisAcquireSpinLock(&(entry->lock));
- if (key->ipKey.nwProto == IPPROTO_TCP) {
+ if ((layers->isIPv6 && key->ipv6Key.nwProto == IPPROTO_TCP) ||
+ (!(layers->isIPv6) && key->ipKey.nwProto == IPPROTO_TCP)) {
/* Update the related bit if there is a parent */
if (entry->parent) {
state |= OVS_CS_F_RELATED;
@@ -938,6 +1117,29 @@ OvsCtUpdateTuple(OvsFlowKey *key, OVS_CT_KEY *ctKey)
htons(ctKey->src.icmp_code);
}
+/*
+ * --------------------------------------------------------------------------
+ * OvsCtUpdateTupleV6 name --
+ * Update origin tuple for ipv6 packet.
+ * --------------------------------------------------------------------------
+ */
+static __inline void
+OvsCtUpdateTupleV6(OvsFlowKey *key, OVS_CT_KEY *ctKey)
+{
+ RtlCopyMemory(&key->ct.tuple_ipv6.ipv6_src, &ctKey->src.addr.ipv6_aligned, sizeof(key->ct.tuple_ipv6.ipv6_src));
+ RtlCopyMemory(&key->ct.tuple_ipv6.ipv6_dst, &ctKey->dst.addr.ipv6_aligned, sizeof(key->ct.tuple_ipv6.ipv6_dst));
+ key->ct.tuple_ipv6.ipv6_proto = ctKey->nw_proto;
+
+ /* Orig tuple Port is overloaded to take in ICMP-Type & Code */
+ /* This mimics the behavior in lib/conntrack.c*/
+ key->ct.tuple_ipv6.src_port = ctKey->nw_proto != IPPROTO_ICMPV6 ?
+ ctKey->src.port :
+ htons(ctKey->src.icmp_type);
+ key->ct.tuple_ipv6.dst_port = ctKey->nw_proto != IPPROTO_ICMPV6 ?
+ ctKey->dst.port :
+ htons(ctKey->src.icmp_code);
+}
+
static __inline NDIS_STATUS
OvsCtExecute_(OvsForwardingContext *fwdCtx,
OvsFlowKey *key,
@@ -954,6 +1156,8 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
BOOLEAN triggerUpdateEvent = FALSE;
BOOLEAN entryCreated = FALSE;
+ BOOLEAN isFtpPacket = FALSE;
+ BOOLEAN isFtpRequestDirection = FALSE;
POVS_CT_ENTRY entry = NULL;
POVS_CT_ENTRY parent = NULL;
PNET_BUFFER_LIST curNbl = fwdCtx->curNbl;
@@ -1004,10 +1208,17 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
return NDIS_STATUS_RESOURCES;
}
/* If no matching entry was found, create one and add New state */
- entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto,
- layers, &ctx,
- key, natInfo, commit, currentTime,
- &entryCreated);
+ if (key->l2.dlType == htons(ETH_TYPE_IPV6)) {
+ entry = OvsCtEntryCreate(fwdCtx, key->ipv6Key.nwProto,
+ layers, &ctx,
+ key, natInfo, commit, currentTime,
+ &entryCreated);
+ } else {
+ entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto,
+ layers, &ctx,
+ key, natInfo, commit, currentTime,
+ &entryCreated);
+ }
}
if (entry == NULL) {
@@ -1030,10 +1241,22 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
OvsCtSetMarkLabel(key, entry, mark, labels, &triggerUpdateEvent);
- if (OvsDetectFtpPacket(key)) {
+ if (layers->isIPv6) {
+ isFtpPacket = OvsDetectFtp6Packet(key);
+ if (ntohs(key->ipv6Key.l4.tpDst) == IPPORT_FTP) {
+ isFtpRequestDirection = TRUE;
+ }
+ } else {
+ isFtpPacket = OvsDetectFtpPacket(key);
+ if (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP) {
+ isFtpRequestDirection = TRUE;
+ }
+ }
+
+ if (isFtpPacket) {
/* FTP parser will always be loaded */
status = OvsCtHandleFtp(curNbl, key, layers, currentTime, entry,
- (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP));
+ isFtpRequestDirection);
if (status != NDIS_STATUS_SUCCESS) {
OVS_LOG_ERROR("Error while parsing the FTP packet");
}
@@ -1064,6 +1287,14 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
} else {
OvsCtUpdateTuple(key, &entry->key);
}
+ } else if (entry->key.dl_type == ntohs(ETH_TYPE_IPV6)) {
+ if (parent != NULL) {
+ OVS_ACQUIRE_SPIN_LOCK(&(parent->lock), irql);
+ OvsCtUpdateTupleV6(key, &parent->key);
+ OVS_RELEASE_SPIN_LOCK(&(parent->lock), irql);
+ } else {
+ OvsCtUpdateTupleV6(key, &entry->key);
+ }
}
if (entryCreated) {
@@ -1400,13 +1631,15 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
PNL_ATTR ctTupleAttrs[__CTA_MAX];
UINT32 attrOffset;
static const NL_POLICY ctTuplePolicy[] = {
- [CTA_TUPLE_IP] = {.type = NL_A_NESTED, .optional = FALSE },
- [CTA_TUPLE_PROTO] = {.type = NL_A_NESTED, .optional = FALSE},
+ [CTA_TUPLE_IP] = { .type = NL_A_NESTED, .optional = FALSE },
+ [CTA_TUPLE_PROTO] = { .type = NL_A_NESTED, .optional = FALSE},
};
static const NL_POLICY ctTupleIpPolicy[] = {
[CTA_IP_V4_SRC] = { .type = NL_A_BE32, .optional = TRUE },
[CTA_IP_V4_DST] = { .type = NL_A_BE32, .optional = TRUE },
+ [CTA_IP_V6_SRC] = { .type = NL_A_BE32,.optional = TRUE },
+ [CTA_IP_V6_DST] = { .type = NL_A_BE32,.optional = TRUE },
};
static const NL_POLICY ctTupleProtoPolicy[] = {
@@ -1415,6 +1648,9 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
[CTA_PROTO_DST_PORT] = { .type = NL_A_BE16, .optional = TRUE },
[CTA_PROTO_ICMP_TYPE] = { .type = NL_A_U8, .optional = TRUE },
[CTA_PROTO_ICMP_CODE] = { .type = NL_A_U8, .optional = TRUE },
+ [CTA_PROTO_ICMPV6_ID] = { .type = NL_A_BE16,.optional = TRUE },
+ [CTA_PROTO_ICMPV6_TYPE] = { .type = NL_A_U8,.optional = TRUE },
+ [CTA_PROTO_ICMPV6_CODE] = { .type = NL_A_U8,.optional = TRUE },
};
if (!ctAttr) {
@@ -1467,8 +1703,11 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE] ) {
ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_TYPE]);
ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE]);
+ } else if (ctTupleProtoAttrs[CTA_PROTO_ICMPV6_TYPE] &&
+ ctTupleProtoAttrs[CTA_PROTO_ICMPV6_CODE] ) {
+ ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMPV6_TYPE]);
+ ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMPV6_CODE]);
}
-
}
}
@@ -1551,15 +1790,15 @@ MapProtoTupleToNl(PNL_BUFFER nlBuf, OVS_CT_KEY *key)
goto done;
}
} else if (key->nw_proto == IPPROTO_ICMPV6) {
- if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, 0)) {
+ if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, htons(key->src.icmp_id))) {
status = NDIS_STATUS_FAILURE;
goto done;
}
- if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, 0)) {
+ if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, key->src.icmp_type)) {
status = NDIS_STATUS_FAILURE;
goto done;
}
- if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, 0)) {
+ if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, key->src.icmp_code)) {
status = NDIS_STATUS_FAILURE;
goto done;
}
@@ -167,33 +167,8 @@ OvsConntrackUpdateExpiration(OVS_CT_ENTRY *ctEntry,
ctEntry->expiration = now + interval;
}
-static const TCPHdr*
-OvsGetTcpHeader(PNET_BUFFER_LIST nbl,
- OVS_PACKET_HDR_INFO *layers,
- VOID *storage,
- UINT32 *tcpPayloadLen)
-{
- IPHdr *ipHdr;
- TCPHdr *tcp;
- VOID *dest = storage;
-
- ipHdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl),
- layers->l4Offset + sizeof(TCPHdr),
- NULL, 1 /*no align*/, 0);
- if (ipHdr == NULL) {
- return NULL;
- }
-
- ipHdr = (IPHdr *)((PCHAR)ipHdr + layers->l3Offset);
- tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4);
- if (tcp->doff * 4 >= sizeof *tcp) {
- NdisMoveMemory(dest, tcp, sizeof(TCPHdr));
- *tcpPayloadLen = TCP_DATA_LENGTH(ipHdr, tcp);
- return storage;
- }
-
- return NULL;
-}
+const TCPHdr* OvsGetTcpHeader(PNET_BUFFER_LIST nbl, OVS_PACKET_HDR_INFO *layers,
+ VOID *storage, UINT32 *tcpPayloadLen);
VOID OvsCleanupConntrack(VOID);
NTSTATUS OvsInitConntrack(POVS_SWITCH_CONTEXT context);
@@ -203,6 +178,7 @@ NDIS_STATUS OvsExecuteConntrackAction(OvsForwardingContext *fwdCtx,
const PNL_ATTR a);
BOOLEAN OvsConntrackValidateTcpPacket(const TCPHdr *tcp);
BOOLEAN OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp);
+BOOLEAN OvsConntrackValidateIcmp6Packet(const ICMPHdr *icmp);
OVS_CT_ENTRY * OvsConntrackCreateTcpEntry(const TCPHdr *tcp,
UINT64 now,
UINT32 tcpPayloadLen);
@@ -235,8 +211,8 @@ NTSTATUS OvsInitCtRelated(POVS_SWITCH_CONTEXT context);
VOID OvsCleanupCtRelated(VOID);
NDIS_STATUS OvsCtRelatedEntryCreate(UINT8 ipProto,
UINT16 dl_type,
- UINT32 serverIp,
- UINT32 clientIp,
+ struct ct_addr serverIp,
+ struct ct_addr clientIp,
UINT16 serverPort,
UINT16 clientPort,
UINT64 currentTime,
@@ -207,6 +207,7 @@ typedef __declspec(align(8)) struct OvsFlowKey {
UINT32 state;
struct ovs_key_ct_labels labels;
struct ovs_key_ct_tuple_ipv4 tuple_ipv4;
+ struct ovs_key_ct_tuple_ipv6 tuple_ipv6;
} ct; /* Connection Tracking Flags */
} OvsFlowKey;
@@ -217,6 +218,7 @@ typedef __declspec(align(8)) struct OvsFlowKey {
#define OVS_ARP_KEY_SIZE (sizeof (ArpKey))
#define OVS_ICMPV6_KEY_SIZE (sizeof (Icmp6Key))
#define OVS_MPLS_KEY_SIZE (sizeof (MplsKey))
+#define OVS_TUPLE_IPV6 (sizeof (struct ovs_key_ct_tuple_ipv6))
typedef struct OvsFlowStats {
Ovs64AlignedU64 packetCount;
@@ -186,6 +186,10 @@ const NL_POLICY nlFlowKeyPolicy[] = {
[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = {.type = NL_A_UNSPEC,
.minLen = sizeof(struct ovs_key_ct_tuple_ipv4),
.maxLen = sizeof(struct ovs_key_ct_tuple_ipv4),
+ .optional = TRUE},
+ [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = {.type = NL_A_UNSPEC,
+ .minLen = OVS_TUPLE_IPV6,
+ .maxLen = OVS_TUPLE_IPV6,
.optional = TRUE}
};
const UINT32 nlFlowKeyPolicyLen = ARRAY_SIZE(nlFlowKeyPolicy);
@@ -1580,6 +1584,13 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
sizeof(struct ovs_key_ct_tuple_ipv4));
}
+ if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) {
+ const struct ovs_key_ct_tuple_ipv6 *tuple_ipv6;
+ tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]);
+ NdisMoveMemory(&destKey->ct.tuple_ipv6, tuple_ipv6,
+ sizeof(struct ovs_key_ct_tuple_ipv6));
+ }
+
/* ===== L2 headers ===== */
if (keyAttrs[OVS_KEY_ATTR_IN_PORT]) {
destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
@@ -2170,6 +2181,13 @@ OvsGetFlowMetadata(OvsFlowKey *key,
sizeof(struct ovs_key_ct_tuple_ipv4));
}
+ if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) {
+ const struct ovs_key_ct_tuple_ipv6 *tuple_ipv6;
+ tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]);
+ NdisMoveMemory(&key->ct.tuple_ipv6, tuple_ipv6,
+ sizeof(struct ovs_key_ct_tuple_ipv6));
+ }
+
return status;
}
@@ -2292,6 +2310,7 @@ OvsExtractLayers(const NET_BUFFER_LIST *packet,
if (icmp) {
layers->l7Offset = layers->l4Offset + sizeof *icmp;
}
+ layers->isIcmp = 1;
}
}
} else {
@@ -2651,6 +2670,8 @@ FlowEqual(OvsFlow *srcFlow,
sizeof(struct ovs_key_ct_labels)) &&
!memcmp(&srcFlow->key.ct.tuple_ipv4, &dstKey->ct.tuple_ipv4,
sizeof(struct ovs_key_ct_tuple_ipv4)) &&
+ !memcmp(&srcFlow->key.ct.tuple_ipv6, &dstKey->ct.tuple_ipv6,
+ sizeof(struct ovs_key_ct_tuple_ipv6)) &&
FlowMemoryEqual((UINT64 *)((UINT8 *)&srcFlow->key + offset),
(UINT64 *) dstStart,
size));
@@ -2763,13 +2784,20 @@ OvsLookupFlow(OVS_DATAPATH *datapath,
0);
*hash = OvsJhashWords((UINT32*)hash, 1, lblHash);
}
- if (key->ct.tuple_ipv4.ipv4_src) {
+ if (key->ct.tuple_ipv4.ipv4_proto) {
UINT32 tupleHash = OvsJhashBytes(
&key->ct.tuple_ipv4,
sizeof(struct ovs_key_ct_tuple_ipv4),
0);
*hash = OvsJhashWords((UINT32*)hash, 1, tupleHash);
}
+
+ if (key->ct.tuple_ipv6.ipv6_proto) {
+ UINT32 tupleHash = OvsJhashBytes(&key->ct.tuple_ipv6,
+ sizeof(struct ovs_key_ct_tuple_ipv6),
+ 0);
+ *hash = OvsJhashWords((UINT32*)hash, 1, tupleHash);
+ }
}
head = &datapath->flowTable[HASH_BUCKET(*hash)];
@@ -2945,7 +2973,9 @@ ReportFlowInfo(OvsFlow *flow,
NdisMoveMemory(&info->key.ct.tuple_ipv4,
&flow->key.ct.tuple_ipv4,
sizeof(struct ovs_key_ct_tuple_ipv4));
-
+ NdisMoveMemory(&info->key.ct.tuple_ipv6,
+ &flow->key.ct.tuple_ipv6,
+ sizeof(struct ovs_key_ct_tuple_ipv6));
return status;
}
@@ -3339,6 +3369,13 @@ OvsProbeSupportedFeature(POVS_MESSAGE msgIn,
OVS_LOG_ERROR("Invalid ct_tuple_ipv4.");
status = STATUS_INVALID_PARAMETER;
}
+ } else if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) {
+ const struct ovs_key_ct_tuple_ipv6 *ct_tuple_ipv6;
+ ct_tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]);
+ if (!ct_tuple_ipv6) {
+ OVS_LOG_ERROR("Invalid ct_tuple_ipv6.");
+ status = STATUS_INVALID_PARAMETER;
+ }
} else {
OVS_LOG_ERROR("Feature not supported.");
status = STATUS_INVALID_PARAMETER;
@@ -239,7 +239,8 @@ typedef union _OVS_PACKET_HDR_INFO {
UINT16 tcpCsumNeeded:1;
UINT16 udpCsumNeeded:1;
UINT16 udpCsumZero:1;
- UINT16 pad:8;
+ UINT16 isIcmp:1;
+ UINT16 pad:7;
} ;
UINT64 value;
} OVS_PACKET_HDR_INFO, *POVS_PACKET_HDR_INFO;
@@ -300,6 +301,11 @@ typedef struct IPv6FragHdr {
UINT32 ident;
} IPv6FragHdr;
+typedef struct IPv6OptHdr {
+ UINT8 nextHdr;
+ UINT8 hdrLen;
+} IPv6OptHdr;
+
typedef struct IPv6NdOptHdr {
UINT8 type;
UINT8 len;
@@ -318,6 +318,7 @@ OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
}
layers->l7Offset = ofs;
+ layers->isIcmp = 1;
return NDIS_STATUS_SUCCESS;
invalid:
@@ -161,3 +161,22 @@ OvsPerCpuDataCleanup()
{
OvsDeferredActionsCleanup();
}
+
+NTSTATUS
+OvsIpv6StringToAddress(const char* ip6String, struct in6_addr *ipv6Addr)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ char *terminator = NULL;
+
+ status = RtlIpv6StringToAddressA(ip6String, &terminator, ipv6Addr);
+ return status;
+}
+
+char *
+OvsIpv6AddressToString(struct in6_addr ipv6Addr, char* ip6String)
+{
+ char *returnedIpv6Str = NULL;
+
+ returnedIpv6Str = RtlIpv6AddressToStringA((&ipv6Addr), ip6String);
+ return returnedIpv6Str;
+}
\ No newline at end of file
@@ -143,6 +143,27 @@ OvsPerCpuDataInit();
VOID
OvsPerCpuDataCleanup();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsIpv6StringToAddress --
+ * The function was used to convert ip6string to binary ipv6 address.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsIpv6StringToAddress(const char* ip6String, struct in6_addr *ipv6Addr);
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsIpv6AddressToString --
+ * The function convert bindary ipv6Addr to string.
+ * --------------------------------------------------------------------------
+ */
+char *
+OvsIpv6AddressToString(struct in6_addr ipv6Addr, char* ip6String);
+
+
static LARGE_INTEGER seed;
/*
@@ -19,6 +19,7 @@
#include <intsafe.h>
#include <ntintsafe.h>
#include <ntstrsafe.h>
+#include <ip2string.h>
#include "Types.h"