datapath-windows: Rename files.
[cascardo/ovs.git] / datapath-windows / ovsext / IpHelper.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "precomp.h"
18 #include "IpHelper.h"
19 #include "Switch.h"
20 #include "Jhash.h"
21
22 #ifdef OVS_DBG_MOD
23 #undef OVS_DBG_MOD
24 #endif
25 #define OVS_DBG_MOD OVS_DBG_IPHELPER
26 #include "Debug.h"
27
28 /*
29  * Fow now, we assume only one internal adapter
30  */
31
32 KSTART_ROUTINE             OvsStartIpHelper;
33
34
35 /*
36  * Only when the internal IP is configured and virtual
37  * internal port is connected, the IP helper request can be
38  * queued.
39  */
40 static BOOLEAN             ovsInternalIPConfigured;
41 static UINT32              ovsInternalPortNo;
42 static GUID                ovsInternalNetCfgId;
43 static MIB_IF_ROW2         ovsInternalRow;
44 static MIB_IPINTERFACE_ROW ovsInternalIPRow;
45
46 /* we only keep one internal IP for reference, it will not be used for
47  * determining SRC IP of Tunnel
48  */
49 static UINT32               ovsInternalIP;
50
51
52 /*
53  * FWD_ENTRY -------->  IPFORWARD_ENTRY
54  *      |
55  *      |--------------------------------------> IPENIGH_ENTRY
56  *
57  * IPFORWARD_ENTRY  ------> FWD_ENTRY LIST with same IPFORWARD
58  *
59  * IPNEIGH_ENTRY    ------> FWD_ENTRY LIST with same IPNEIGH
60  *
61  */
62
63 static PLIST_ENTRY          ovsFwdHashTable;    // based on DST IP
64 static PLIST_ENTRY          ovsRouteHashTable;  // based on DST PREFIX
65 static PLIST_ENTRY          ovsNeighHashTable;  // based on DST IP
66 static LIST_ENTRY           ovsSortedIPNeighList;
67 static UINT32               ovsNumFwdEntries;
68
69
70 static PNDIS_RW_LOCK_EX     ovsTableLock;
71 static NDIS_SPIN_LOCK       ovsIpHelperLock;
72
73 static LIST_ENTRY           ovsIpHelperRequestList;
74 static UINT32               ovsNumIpHelperRequests;
75
76 static HANDLE               ipInterfaceNotificationHandle;
77 static HANDLE               ipRouteNotificationHandle;
78 static HANDLE               unicastIPNotificationHandle;
79
80 static OVS_IP_HELPER_THREAD_CONTEXT ovsIpHelperThreadContext;
81
82 static POVS_IPFORWARD_ENTRY OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix);
83 static VOID OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf);
84 static VOID OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr);
85 static VOID OvsCleanupIpHelperRequestList(VOID);
86 static VOID OvsCleanupFwdTable(VOID);
87 static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn);
88
89 static VOID
90 OvsDumpIfRow(PMIB_IF_ROW2 ifRow)
91 {
92     OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d",
93                  ifRow->InterfaceLuid.Info.NetLuidIndex,
94                  ifRow->InterfaceLuid.Info.IfType);
95     OVS_LOG_INFO("InterfaceIndex: %d", ifRow->InterfaceIndex);
96
97     OVS_LOG_INFO("Interface GUID: %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
98                  ifRow->InterfaceGuid.Data1,
99                  ifRow->InterfaceGuid.Data2,
100                  ifRow->InterfaceGuid.Data3,
101                  *(UINT16 *)ifRow->InterfaceGuid.Data4,
102                  ifRow->InterfaceGuid.Data4[2],
103                  ifRow->InterfaceGuid.Data4[3],
104                  ifRow->InterfaceGuid.Data4[4],
105                  ifRow->InterfaceGuid.Data4[5],
106                  ifRow->InterfaceGuid.Data4[6],
107                  ifRow->InterfaceGuid.Data4[7]);
108     OVS_LOG_INFO("Perm MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
109                  ifRow->PermanentPhysicalAddress[0],
110                  ifRow->PermanentPhysicalAddress[1],
111                  ifRow->PermanentPhysicalAddress[2],
112                  ifRow->PermanentPhysicalAddress[3],
113                  ifRow->PermanentPhysicalAddress[4],
114                  ifRow->PermanentPhysicalAddress[5]);
115 }
116
117
118 static VOID
119 OvsDumpIfTable(PMIB_IF_TABLE2 ifTable)
120 {
121     PMIB_IF_ROW2 ifRow;
122     UINT32 i;
123
124     OVS_LOG_INFO("======Number of entries: %d========", ifTable->NumEntries);
125
126     for (i = 0; i < ifTable->NumEntries; i++) {
127         ifRow = &ifTable->Table[i];
128         OvsDumpIfRow(ifRow);
129     }
130 }
131
132
133 NTSTATUS
134 OvsGetIfEntry(GUID *interfaceGuid, PMIB_IF_ROW2 ifEntry)
135 {
136     NTSTATUS status;
137     PMIB_IF_TABLE2 ifTable;
138     UINT32 i;
139
140     if (interfaceGuid == NULL || ifEntry == NULL) {
141         return STATUS_INVALID_PARAMETER;
142     }
143
144     status = GetIfTable2Ex(MibIfTableNormal, &ifTable);
145
146     if (status != STATUS_SUCCESS) {
147         OVS_LOG_INFO("Fail to get if table, status: %x", status);
148         return status;
149     }
150     status = STATUS_NOT_FOUND;
151
152     for (i = 0; i < ifTable->NumEntries; i++) {
153         PMIB_IF_ROW2 ifRow;
154
155         ifRow = &ifTable->Table[i];
156         if (!memcmp(interfaceGuid, &ifRow->InterfaceGuid, sizeof (GUID))) {
157             RtlCopyMemory(ifEntry, ifRow, sizeof (MIB_IF_ROW2));
158             status = STATUS_SUCCESS;
159             OvsDumpIfRow(ifEntry);
160             break;
161         }
162     }
163
164     FreeMibTable(ifTable);
165     return status;
166 }
167
168
169 static VOID
170 OvsDumpIPInterfaceEntry(PMIB_IPINTERFACE_ROW ipRow)
171 {
172     OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d",
173                  ipRow->InterfaceLuid.Info.NetLuidIndex,
174                  ipRow->InterfaceLuid.Info.IfType);
175     OVS_LOG_INFO("InterfaceIndex: %d", ipRow->InterfaceIndex);
176
177     OVS_LOG_INFO("MaxReassembleSize: %u", ipRow->MaxReassemblySize);
178 }
179
180
181 NTSTATUS
182 OvsGetIPInterfaceEntry(NET_LUID luid,
183                        PMIB_IPINTERFACE_ROW ipRow)
184 {
185     NTSTATUS status;
186
187     if (ipRow == NULL) {
188         return STATUS_INVALID_PARAMETER;
189     }
190
191     ipRow->Family = AF_INET;
192     ipRow->InterfaceLuid.Value = luid.Value;
193
194     status = GetIpInterfaceEntry(ipRow);
195
196     if (status != STATUS_SUCCESS) {
197         OVS_LOG_INFO("Fail to get internal IP Interface mib row, status: %x",
198                      status);
199         return status;
200     }
201     OvsDumpIPInterfaceEntry(ipRow);
202     return status;
203 }
204
205
206 static VOID
207 OvsDumpIPEntry(PMIB_UNICASTIPADDRESS_ROW ipRow)
208 {
209     UINT32 ipAddr;
210
211     OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d",
212                  ipRow->InterfaceLuid.Info.NetLuidIndex,
213                  ipRow->InterfaceLuid.Info.IfType);
214
215     OVS_LOG_INFO("InterfaceIndex: %d", ipRow->InterfaceIndex);
216
217     ASSERT(ipRow->Address.si_family == AF_INET);
218
219     ipAddr = ipRow->Address.Ipv4.sin_addr.s_addr;
220     OVS_LOG_INFO("Unicast Address: %d.%d.%d.%d\n",
221                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
222                  (ipAddr >> 16) & 0xff, ipAddr >> 24);
223 }
224
225
226 NTSTATUS
227 OvsGetIPEntry(NET_LUID interfaceLuid,
228               PMIB_UNICASTIPADDRESS_ROW ipEntry)
229 {
230     PMIB_UNICASTIPADDRESS_TABLE ipTable;
231     NTSTATUS status;
232     UINT32 i;
233
234     if (ipEntry == NULL || ipEntry == NULL) {
235         return STATUS_INVALID_PARAMETER;
236     }
237
238     status = GetUnicastIpAddressTable(AF_INET, &ipTable);
239
240     if (status != STATUS_SUCCESS) {
241         OVS_LOG_INFO("Fail to get unicast address table, status: %x", status);
242         return status;
243     }
244
245     status = STATUS_NOT_FOUND;
246
247     for (i = 0; i < ipTable->NumEntries; i++) {
248         PMIB_UNICASTIPADDRESS_ROW ipRow;
249
250         ipRow = &ipTable->Table[i];
251         if (ipRow->InterfaceLuid.Value == interfaceLuid.Value) {
252             RtlCopyMemory(ipEntry, ipRow, sizeof (*ipRow));
253             OvsDumpIPEntry(ipEntry);
254             status = STATUS_SUCCESS;
255             break;
256         }
257     }
258
259     FreeMibTable(ipTable);
260     return status;
261 }
262
263 #ifdef OVS_ENABLE_IPPATH
264 static VOID
265 OvsDumpIPPath(PMIB_IPPATH_ROW ipPath)
266 {
267     UINT32 ipAddr = ipPath->Source.Ipv4.sin_addr.s_addr;
268
269     OVS_LOG_INFO("Source: %d.%d.%d.%d",
270                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
271                  (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
272
273     ipAddr = ipPath->Destination.Ipv4.sin_addr.s_addr;
274     OVS_LOG_INFO("Destination: %d.%d.%d.%d",
275                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
276                  (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
277
278     ipAddr = ipPath->CurrentNextHop.Ipv4.sin_addr.s_addr;
279     OVS_LOG_INFO("NextHop: %d.%d.%d.%d",
280                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
281                  (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
282 }
283
284
285 NTSTATUS
286 OvsGetIPPathEntry(PMIB_IPPATH_ROW ipPath)
287 {
288     NTSTATUS status;
289     UINT32 ipAddr = ipPath->Destination.Ipv4.sin_addr.s_addr;
290
291     status = GetIpPathEntry(ipPath);
292
293     if (status != STATUS_SUCCESS) {
294         OVS_LOG_INFO("Fail to get IP path to %d.%d.%d.%d, status:%x",
295                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
296                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status);
297         return status;
298     }
299     OvsDumpIPPath(ipPath);
300     return status;
301 }
302 #endif
303
304 static VOID
305 OvsDumpRoute(const SOCKADDR_INET *sourceAddress,
306              const SOCKADDR_INET *destinationAddress,
307              PMIB_IPFORWARD_ROW2 route)
308 {
309     UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr;
310
311     OVS_LOG_INFO("Destination: %d.%d.%d.%d",
312                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
313                  (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
314
315     ipAddr = sourceAddress->Ipv4.sin_addr.s_addr;
316     OVS_LOG_INFO("Source: %d.%d.%d.%d",
317                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
318                  (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
319
320     ipAddr = route->NextHop.Ipv4.sin_addr.s_addr;
321     OVS_LOG_INFO("NextHop: %d.%d.%d.%d",
322                  ipAddr & 0xff, (ipAddr >> 8) & 0xff,
323                  (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
324 }
325
326
327 NTSTATUS
328 OvsGetRoute(NET_LUID interfaceLuid,
329             const SOCKADDR_INET *destinationAddress,
330             PMIB_IPFORWARD_ROW2 route,
331             SOCKADDR_INET *sourceAddress)
332 {
333     NTSTATUS status;
334
335     if (destinationAddress == NULL || route == NULL) {
336         return STATUS_INVALID_PARAMETER;
337     }
338
339     status = GetBestRoute2(&interfaceLuid, 0,
340                            NULL, destinationAddress,
341                            0, route, sourceAddress);
342
343     if (status != STATUS_SUCCESS) {
344         UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr;
345         OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x",
346                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
347                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status);
348         return status;
349     }
350
351     OvsDumpRoute(sourceAddress, destinationAddress, route);
352     return status;
353 }
354
355 static VOID
356 OvsDumpIPNeigh(PMIB_IPNET_ROW2 ipNeigh)
357 {
358     UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr;
359
360     OVS_LOG_INFO("Neigh: %d.%d.%d.%d",
361                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
362                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
363     OVS_LOG_INFO("MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
364                  ipNeigh->PhysicalAddress[0],
365                  ipNeigh->PhysicalAddress[1],
366                  ipNeigh->PhysicalAddress[2],
367                  ipNeigh->PhysicalAddress[3],
368                  ipNeigh->PhysicalAddress[4],
369                  ipNeigh->PhysicalAddress[5]);
370 }
371
372
373 NTSTATUS
374 OvsGetIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
375 {
376     NTSTATUS status;
377
378     ASSERT(ipNeigh);
379
380     status = GetIpNetEntry2(ipNeigh);
381
382     if (status != STATUS_SUCCESS) {
383         UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr;
384         OVS_LOG_INFO("Fail to get ARP entry: %d.%d.%d.%d, status: %x",
385                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
386                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status);
387         return status;
388     }
389     if (ipNeigh->State == NlnsReachable ||
390         ipNeigh->State == NlnsPermanent) {
391         OvsDumpIPNeigh(ipNeigh);
392         return STATUS_SUCCESS;
393     }
394     return STATUS_FWP_TCPIP_NOT_READY;
395 }
396
397
398 NTSTATUS
399 OvsResolveIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
400 {
401     NTSTATUS status;
402
403     ASSERT(ipNeigh);
404     status = ResolveIpNetEntry2(ipNeigh, NULL);
405
406     if (status != STATUS_SUCCESS) {
407         UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr;
408         OVS_LOG_INFO("Fail to resolve ARP entry: %d.%d.%d.%d, status: %x",
409                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
410                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status);
411         return status;
412     }
413
414     if (ipNeigh->State == NlnsReachable ||
415         ipNeigh->State == NlnsPermanent) {
416         OvsDumpIPNeigh(ipNeigh);
417         return STATUS_SUCCESS;
418     }
419     return STATUS_FWP_TCPIP_NOT_READY;
420 }
421
422
423 NTSTATUS
424 OvsGetOrResolveIPNeigh(UINT32 ipAddr,
425                        PMIB_IPNET_ROW2 ipNeigh)
426 {
427     NTSTATUS status;
428
429     ASSERT(ipNeigh);
430
431     RtlZeroMemory(ipNeigh, sizeof (*ipNeigh));
432     ipNeigh->InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value;
433     ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex;
434     ipNeigh->Address.si_family = AF_INET;
435     ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr;
436
437     status = OvsGetIPNeighEntry(ipNeigh);
438
439     if (status != STATUS_SUCCESS) {
440         RtlZeroMemory(ipNeigh, sizeof (*ipNeigh));
441         ipNeigh->InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value;
442         ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex;
443         ipNeigh->Address.si_family = AF_INET;
444         ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr;
445         status = OvsResolveIPNeighEntry(ipNeigh);
446     }
447     return status;
448 }
449
450
451 static VOID
452 OvsChangeCallbackIpInterface(PVOID context,
453                              PMIB_IPINTERFACE_ROW ipRow,
454                              MIB_NOTIFICATION_TYPE notificationType)
455 {
456     UNREFERENCED_PARAMETER(context);
457     switch (notificationType) {
458     case MibParameterNotification:
459     case MibAddInstance:
460         if (ipRow->InterfaceLuid.Info.NetLuidIndex ==
461             ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
462             ipRow->InterfaceLuid.Info.IfType ==
463             ovsInternalRow.InterfaceLuid.Info.IfType &&
464             ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
465             /*
466              * Update the IP Interface Row
467              */
468             NdisAcquireSpinLock(&ovsIpHelperLock);
469             RtlCopyMemory(&ovsInternalIPRow, ipRow,
470                           sizeof (PMIB_IPINTERFACE_ROW));
471             ovsInternalIPConfigured = TRUE;
472             NdisReleaseSpinLock(&ovsIpHelperLock);
473         }
474         OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d is %s",
475                      ipRow->InterfaceLuid.Info.NetLuidIndex,
476                      ipRow->InterfaceLuid.Info.IfType,
477                      notificationType == MibAddInstance ? "added" : "modified");
478         break;
479     case MibDeleteInstance:
480         OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d, deleted",
481                      ipRow->InterfaceLuid.Info.NetLuidIndex,
482                      ipRow->InterfaceLuid.Info.IfType);
483         if (ipRow->InterfaceLuid.Info.NetLuidIndex ==
484             ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
485             ipRow->InterfaceLuid.Info.IfType ==
486             ovsInternalRow.InterfaceLuid.Info.IfType &&
487             ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
488
489             NdisAcquireSpinLock(&ovsIpHelperLock);
490             ovsInternalIPConfigured = FALSE;
491             NdisReleaseSpinLock(&ovsIpHelperLock);
492
493             OvsCleanupIpHelperRequestList();
494
495             OvsCleanupFwdTable();
496         }
497
498         break;
499     case MibInitialNotification:
500         OVS_LOG_INFO("Get Initial notification for IP Interface change.");
501     default:
502         return;
503     }
504 }
505
506
507 static VOID
508 OvsChangeCallbackIpRoute(PVOID context,
509                          PMIB_IPFORWARD_ROW2 ipRoute,
510                          MIB_NOTIFICATION_TYPE notificationType)
511 {
512     UINT32 ipAddr, nextHop;
513
514     UNREFERENCED_PARAMETER(context);
515     switch (notificationType) {
516     case MibAddInstance:
517
518         ASSERT(ipRoute);
519         ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr;
520         nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr;
521
522         OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d added",
523                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
524                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff,
525                      ipRoute->DestinationPrefix.PrefixLength,
526                      nextHop & 0xff, (nextHop >> 8) & 0xff,
527                      (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff);
528         break;
529
530     case MibParameterNotification:
531     case MibDeleteInstance:
532         ASSERT(ipRoute);
533         ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr;
534         nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr;
535
536         OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d %s.",
537                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
538                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff,
539                      ipRoute->DestinationPrefix.PrefixLength,
540                      nextHop & 0xff, (nextHop >> 8) & 0xff,
541                      (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff,
542                      notificationType == MibDeleteInstance ? "deleted" :
543                      "modified");
544
545         if (ipRoute->InterfaceLuid.Info.NetLuidIndex ==
546             ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
547             ipRoute->InterfaceLuid.Info.IfType ==
548             ovsInternalRow.InterfaceLuid.Info.IfType &&
549             ipRoute->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
550
551             POVS_IPFORWARD_ENTRY ipf;
552             LOCK_STATE_EX lockState;
553
554             NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
555             ipf = OvsLookupIPForwardEntry(&ipRoute->DestinationPrefix);
556             if (ipf != NULL) {
557                 OvsRemoveIPForwardEntry(ipf);
558             }
559             NdisReleaseRWLock(ovsTableLock, &lockState);
560         }
561         break;
562
563     case MibInitialNotification:
564         OVS_LOG_INFO("Get Initial notification for IP Route change.");
565     default:
566         return;
567     }
568 }
569
570
571 static VOID
572 OvsChangeCallbackUnicastIpAddress(PVOID context,
573                                   PMIB_UNICASTIPADDRESS_ROW unicastRow,
574                                   MIB_NOTIFICATION_TYPE notificationType)
575 {
576     UINT32 ipAddr;
577
578     UNREFERENCED_PARAMETER(context);
579     switch (notificationType) {
580     case MibParameterNotification:
581     case MibAddInstance:
582         ASSERT(unicastRow);
583         ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr;
584         if (unicastRow->InterfaceLuid.Info.NetLuidIndex ==
585             ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
586             unicastRow->InterfaceLuid.Info.IfType ==
587             ovsInternalRow.InterfaceLuid.Info.IfType &&
588             unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
589             ovsInternalIP = ipAddr;
590         }
591         OVS_LOG_INFO("IP Address: %d.%d.%d.%d is %s",
592                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
593                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff,
594                      notificationType == MibAddInstance ? "added": "modified");
595         break;
596
597     case MibDeleteInstance:
598         ASSERT(unicastRow);
599         ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr;
600         OVS_LOG_INFO("IP Address removed: %d.%d.%d.%d",
601                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
602                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
603         if (unicastRow->InterfaceLuid.Info.NetLuidIndex ==
604             ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
605             unicastRow->InterfaceLuid.Info.IfType ==
606             ovsInternalRow.InterfaceLuid.Info.IfType &&
607             unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
608
609             LOCK_STATE_EX lockState;
610             NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
611             OvsRemoveAllFwdEntriesWithSrc(ipAddr);
612             NdisReleaseRWLock(ovsTableLock, &lockState);
613
614         }
615         break;
616
617     case MibInitialNotification:
618         OVS_LOG_INFO("Get Initial notification for Unicast IP Address change.");
619     default:
620         return;
621     }
622 }
623
624
625 static VOID
626 OvsCancelChangeNotification()
627 {
628     if (ipInterfaceNotificationHandle != NULL) {
629         CancelMibChangeNotify2(ipInterfaceNotificationHandle);
630         ipInterfaceNotificationHandle = NULL;
631     }
632     if (ipRouteNotificationHandle != NULL) {
633         CancelMibChangeNotify2(ipRouteNotificationHandle);
634         ipRouteNotificationHandle = NULL;
635     }
636     if (unicastIPNotificationHandle != NULL) {
637         CancelMibChangeNotify2(unicastIPNotificationHandle);
638         unicastIPNotificationHandle = NULL;
639     }
640 }
641
642
643 static NTSTATUS
644 OvsRegisterChangeNotification()
645 {
646     NTSTATUS status;
647
648
649     status = NotifyIpInterfaceChange(AF_INET, OvsChangeCallbackIpInterface,
650                                      NULL, TRUE,
651                                      &ipInterfaceNotificationHandle);
652     if (status != STATUS_SUCCESS) {
653         OVS_LOG_ERROR("Fail to register Notify IP interface change, status:%x.",
654                      status);
655         return status;
656     }
657
658     status = NotifyRouteChange2(AF_INET, OvsChangeCallbackIpRoute, NULL,
659                                 TRUE, &ipRouteNotificationHandle);
660     if (status != STATUS_SUCCESS) {
661         OVS_LOG_ERROR("Fail to regiter ip route change, status: %x.",
662                      status);
663         goto register_cleanup;
664     }
665     status = NotifyUnicastIpAddressChange(AF_INET,
666                                           OvsChangeCallbackUnicastIpAddress,
667                                           NULL, TRUE,
668                                           &unicastIPNotificationHandle);
669     if (status != STATUS_SUCCESS) {
670         OVS_LOG_ERROR("Fail to regiter unicast ip change, status: %x.", status);
671     }
672 register_cleanup:
673     if (status != STATUS_SUCCESS) {
674         OvsCancelChangeNotification();
675     }
676
677     return status;
678 }
679
680
681 static POVS_IPNEIGH_ENTRY
682 OvsLookupIPNeighEntry(UINT32 ipAddr)
683 {
684     PLIST_ENTRY link;
685     POVS_IPNEIGH_ENTRY entry;
686     UINT32 hash = OvsJhashWords(&ipAddr, 1, OVS_HASH_BASIS);
687
688     LIST_FORALL(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], link) {
689         entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, link);
690         if (entry->ipAddr == ipAddr) {
691             return entry;
692         }
693     }
694     return NULL;
695 }
696
697
698 static UINT32
699 OvsHashIPPrefix(PIP_ADDRESS_PREFIX prefix)
700 {
701     UINT64 words = (UINT64)prefix->Prefix.Ipv4.sin_addr.s_addr << 32 |
702                    (UINT32)prefix->PrefixLength;
703     return OvsJhashWords((UINT32 *)&words, 2, OVS_HASH_BASIS);
704 }
705
706
707 static POVS_IPFORWARD_ENTRY
708 OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix)
709 {
710
711     PLIST_ENTRY link;
712     POVS_IPFORWARD_ENTRY ipfEntry;
713     UINT32 hash;
714     ASSERT(prefix->Prefix.si_family == AF_INET);
715
716     hash = RtlUlongByteSwap(prefix->Prefix.Ipv4.sin_addr.s_addr);
717
718     ASSERT(prefix->PrefixLength >= 32 ||
719            (hash & (((UINT32)1 <<  (32 - prefix->PrefixLength)) - 1)) == 0);
720
721     hash = OvsHashIPPrefix(prefix);
722     LIST_FORALL(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK], link) {
723         ipfEntry = CONTAINING_RECORD(link, OVS_IPFORWARD_ENTRY, link);
724         if (ipfEntry->prefix.PrefixLength == prefix->PrefixLength &&
725             ipfEntry->prefix.Prefix.Ipv4.sin_addr.s_addr ==
726             prefix->Prefix.Ipv4.sin_addr.s_addr) {
727             return ipfEntry;
728         }
729     }
730     return NULL;
731 }
732
733
734 static POVS_FWD_ENTRY
735 OvsLookupIPFwdEntry(UINT32 dstIp)
736 {
737     PLIST_ENTRY link;
738     POVS_FWD_ENTRY entry;
739     UINT32 hash = OvsJhashWords(&dstIp, 1, OVS_HASH_BASIS);
740
741     LIST_FORALL(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], link) {
742         entry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link);
743         if (entry->info.dstIpAddr == dstIp) {
744             return entry;
745         }
746     }
747     return NULL;
748 }
749
750
751 NTSTATUS
752 OvsLookupIPFwdInfo(UINT32 dstIp,
753                    POVS_FWD_INFO info)
754 {
755     POVS_FWD_ENTRY entry;
756     LOCK_STATE_EX lockState;
757     NTSTATUS status = STATUS_NOT_FOUND;
758
759     NdisAcquireRWLockRead(ovsTableLock, &lockState, 0);
760     entry = OvsLookupIPFwdEntry(dstIp);
761     if (entry) {
762         info->value[0] = entry->info.value[0];
763         info->value[1] = entry->info.value[1];
764         info->value[2] = entry->info.value[2];
765         status = STATUS_SUCCESS;
766     }
767     NdisReleaseRWLock(ovsTableLock, &lockState);
768     return status;
769 }
770
771
772 static POVS_IPNEIGH_ENTRY
773 OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
774 {
775
776     POVS_IPNEIGH_ENTRY entry;
777     UINT64 timeVal;
778
779     ASSERT(ipNeigh != NULL);
780     entry = (POVS_IPNEIGH_ENTRY)OvsAllocateMemory(sizeof (OVS_IPNEIGH_ENTRY));
781     if (entry == NULL) {
782         return NULL;
783     }
784
785     RtlZeroMemory(entry, sizeof (OVS_IPNEIGH_ENTRY));
786     entry->ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr;
787     KeQuerySystemTime((LARGE_INTEGER *)&timeVal);
788     entry->timeout = timeVal + OVS_IPNEIGH_TIMEOUT;
789     RtlCopyMemory(entry->macAddr, ipNeigh->PhysicalAddress,
790                   MAC_ADDRESS_LEN);
791     InitializeListHead(&entry->fwdList);
792
793     return entry;
794 }
795
796
797 static POVS_IPFORWARD_ENTRY
798 OvsCreateIPForwardEntry(PMIB_IPFORWARD_ROW2 ipRoute)
799 {
800
801     POVS_IPFORWARD_ENTRY entry;
802
803     ASSERT(ipRoute);
804
805     entry =
806        (POVS_IPFORWARD_ENTRY)OvsAllocateMemory(sizeof (OVS_IPFORWARD_ENTRY));
807     if (entry == NULL) {
808         return NULL;
809     }
810
811     RtlZeroMemory(entry, sizeof (OVS_IPFORWARD_ENTRY));
812     RtlCopyMemory(&entry->prefix, &ipRoute->DestinationPrefix,
813                   sizeof (IP_ADDRESS_PREFIX));
814     entry->nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr;
815     InitializeListHead(&entry->fwdList);
816
817     return entry;
818 }
819
820
821 static POVS_FWD_ENTRY
822 OvsCreateFwdEntry(POVS_FWD_INFO fwdInfo)
823 {
824     POVS_FWD_ENTRY entry;
825
826     entry = (POVS_FWD_ENTRY)OvsAllocateMemory(sizeof (OVS_FWD_ENTRY));
827     if (entry == NULL) {
828         return NULL;
829     }
830
831     RtlZeroMemory(entry, sizeof (OVS_FWD_ENTRY));
832     RtlCopyMemory(&entry->info, fwdInfo, sizeof (OVS_FWD_INFO));
833     return entry;
834 }
835
836
837 static VOID
838 OvsRemoveFwdEntry(POVS_FWD_ENTRY fwdEntry)
839 {
840     POVS_IPFORWARD_ENTRY ipf;
841     POVS_IPNEIGH_ENTRY ipn;
842
843     ipf = fwdEntry->ipf;
844     ipn = fwdEntry->ipn;
845
846     RemoveEntryList(&fwdEntry->link);
847     ovsNumFwdEntries--;
848
849     RemoveEntryList(&fwdEntry->ipfLink);
850     ipf->refCount--;
851
852     RemoveEntryList(&fwdEntry->ipnLink);
853     ipn->refCount--;
854
855     if (ipf->refCount == 0) {
856         ASSERT(IsListEmpty(&ipf->fwdList));
857         RemoveEntryList(&ipf->link);
858         OvsFreeMemory(ipf);
859     }
860
861     if (ipn->refCount == 0) {
862         ASSERT(IsListEmpty(&ipn->fwdList));
863         RemoveEntryList(&ipn->link);
864         NdisAcquireSpinLock(&ovsIpHelperLock);
865         RemoveEntryList(&ipn->slink);
866         NdisReleaseSpinLock(&ovsIpHelperLock);
867         OvsFreeMemory(ipn);
868     }
869
870     OvsFreeMemory(fwdEntry);
871 }
872
873
874 static VOID
875 OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf)
876 {
877     POVS_FWD_ENTRY fwdEntry;
878     PLIST_ENTRY link, next;
879
880     ipf->refCount++;
881
882     LIST_FORALL_SAFE(&ipf->fwdList, link, next) {
883         fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink);
884         OvsRemoveFwdEntry(fwdEntry);
885     }
886     ASSERT(ipf->refCount == 1);
887
888     RemoveEntryList(&ipf->link);
889     OvsFreeMemory(ipf);
890 }
891
892
893 static VOID
894 OvsRemoveIPNeighEntry(POVS_IPNEIGH_ENTRY ipn)
895 {
896     PLIST_ENTRY link, next;
897     POVS_FWD_ENTRY fwdEntry;
898
899     ipn->refCount++;
900
901     LIST_FORALL_SAFE(&ipn->fwdList, link, next) {
902         fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink);
903         OvsRemoveFwdEntry(fwdEntry);
904     }
905
906     if (ipn->refCount == 1) {
907         RemoveEntryList(&ipn->link);
908         NdisAcquireSpinLock(&ovsIpHelperLock);
909         RemoveEntryList(&ipn->slink);
910         NdisReleaseSpinLock(&ovsIpHelperLock);
911         OvsFreeMemory(ipn);
912     }
913 }
914
915
916 static VOID
917 OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn)
918 {
919     PLIST_ENTRY link;
920     POVS_IPNEIGH_ENTRY entry;
921
922     if (!IsListEmpty(&ovsSortedIPNeighList)) {
923         link = ovsSortedIPNeighList.Blink;
924         entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink);
925         if (entry->timeout > ipn->timeout) {
926             ipn->timeout++;
927         }
928     }
929     InsertTailList(&ovsSortedIPNeighList, &ipn->slink);
930 }
931
932
933 static VOID
934 OvsAddIPFwdCache(POVS_FWD_ENTRY fwdEntry,
935                  POVS_IPFORWARD_ENTRY ipf,
936                  POVS_IPNEIGH_ENTRY ipn)
937
938 {
939     UINT32 hash;
940
941     if (ipn->refCount == 0) {
942         NdisAcquireSpinLock(&ovsIpHelperLock);
943         OvsAddToSortedNeighList(ipn);
944         NdisReleaseSpinLock(&ovsIpHelperLock);
945         hash = OvsJhashWords(&ipn->ipAddr, 1, OVS_HASH_BASIS);
946         InsertHeadList(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK],
947                        &ipn->link);
948     }
949     if (ipf->refCount == 0) {
950         hash = OvsHashIPPrefix(&ipf->prefix);
951         InsertHeadList(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK],
952                        &ipf->link);
953     }
954
955     InsertHeadList(&ipf->fwdList, &fwdEntry->ipfLink);
956     ipf->refCount++;
957     fwdEntry->ipf = ipf;
958
959     InsertHeadList(&ipn->fwdList, &fwdEntry->ipnLink);
960     ipn->refCount++;
961     fwdEntry->ipn = ipn;
962
963     hash = OvsJhashWords(&fwdEntry->info.dstIpAddr, 1, OVS_HASH_BASIS);
964     InsertHeadList(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK],
965                    &fwdEntry->link);
966     ovsNumFwdEntries++;
967 }
968
969
970 static VOID
971 OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr)
972 {
973     UINT32 i;
974     POVS_FWD_ENTRY fwdEntry;
975     PLIST_ENTRY link, next;
976
977     for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) {
978         LIST_FORALL_SAFE(&ovsFwdHashTable[i], link, next) {
979             fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link);
980             if (fwdEntry->info.srcIpAddr == ipAddr) {
981                 OvsRemoveFwdEntry(fwdEntry);
982             }
983         }
984     }
985 }
986
987
988 static VOID
989 OvsCleanupFwdTable(VOID)
990 {
991     PLIST_ENTRY link, next;
992     POVS_IPNEIGH_ENTRY ipn;
993     UINT32 i;
994     LOCK_STATE_EX lockState;
995
996     NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
997     if (ovsNumFwdEntries) {
998        LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) {
999            ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink);
1000            OvsRemoveIPNeighEntry(ipn);
1001        }
1002     }
1003     for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) {
1004         ASSERT(IsListEmpty(&ovsFwdHashTable[i]));
1005     }
1006     for (i = 0; i < OVS_ROUTE_HASH_TABLE_SIZE; i++) {
1007         ASSERT(IsListEmpty(&ovsRouteHashTable[i]));
1008     }
1009     NdisReleaseRWLock(ovsTableLock, &lockState);
1010 }
1011
1012
1013 static VOID
1014 OvsCleanupIpHelperRequestList(VOID)
1015 {
1016     LIST_ENTRY list;
1017     PLIST_ENTRY next, link;
1018     POVS_IP_HELPER_REQUEST request;
1019
1020     NdisAcquireSpinLock(&ovsIpHelperLock);
1021     if (ovsNumIpHelperRequests == 0) {
1022        NdisReleaseSpinLock(&ovsIpHelperLock);
1023        return;
1024     }
1025
1026     InitializeListHead(&list);
1027     OvsAppendList(&list,  &ovsIpHelperRequestList);
1028     ovsNumIpHelperRequests = 0;
1029     NdisReleaseSpinLock(&ovsIpHelperLock);
1030
1031     LIST_FORALL_SAFE(&list, link, next) {
1032         request = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link);
1033
1034         if (request->command == OVS_IP_HELPER_FWD_REQUEST &&
1035             request->fwdReq.cb) {
1036             request->fwdReq.cb(request->fwdReq.nbl,
1037                                request->fwdReq.inPort,
1038                                &request->fwdReq.tunnelKey,
1039                                request->fwdReq.cbData1,
1040                                request->fwdReq.cbData2,
1041                                STATUS_DEVICE_NOT_READY,
1042                                NULL);
1043         }
1044         OvsFreeMemory(request);
1045     }
1046 }
1047
1048
1049
1050 static VOID
1051 OvsWakeupIPHelper(VOID)
1052 {
1053     KeSetEvent(&ovsIpHelperThreadContext.event, 0, FALSE);
1054 }
1055
1056 VOID
1057 OvsInternalAdapterDown(VOID)
1058 {
1059     NdisAcquireSpinLock(&ovsIpHelperLock);
1060     ovsInternalPortNo = OVS_DEFAULT_PORT_NO;
1061     ovsInternalIPConfigured = FALSE;
1062     NdisReleaseSpinLock(&ovsIpHelperLock);
1063
1064     OvsCleanupIpHelperRequestList();
1065
1066     OvsCleanupFwdTable();
1067 }
1068
1069
1070 VOID
1071 OvsInternalAdapterUp(UINT32 portNo,
1072                      GUID *netCfgInstanceId)
1073 {
1074     POVS_IP_HELPER_REQUEST request;
1075
1076     RtlCopyMemory(&ovsInternalNetCfgId, netCfgInstanceId, sizeof (GUID));
1077     RtlZeroMemory(&ovsInternalRow, sizeof (MIB_IF_ROW2));
1078
1079     request =
1080       (POVS_IP_HELPER_REQUEST)OvsAllocateMemory(sizeof (OVS_IP_HELPER_REQUEST));
1081     if (request == NULL) {
1082         OVS_LOG_ERROR("Fail to initialize Internal Adapter");
1083         return;
1084     }
1085     RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST));
1086     request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_UP;
1087
1088     NdisAcquireSpinLock(&ovsIpHelperLock);
1089     ovsInternalPortNo = portNo;
1090     InsertHeadList(&ovsIpHelperRequestList, &request->link);
1091     ovsNumIpHelperRequests++;
1092     if (ovsNumIpHelperRequests == 1) {
1093         OvsWakeupIPHelper();
1094     }
1095     NdisReleaseSpinLock(&ovsIpHelperLock);
1096 }
1097
1098
1099 static VOID
1100 OvsHandleInternalAdapterUp(POVS_IP_HELPER_REQUEST request)
1101 {
1102     NTSTATUS status;
1103     MIB_UNICASTIPADDRESS_ROW ipEntry;
1104     GUID *netCfgInstanceId = &ovsInternalNetCfgId;
1105
1106     OvsFreeMemory(request);
1107
1108     status = OvsGetIfEntry(&ovsInternalNetCfgId, &ovsInternalRow);
1109
1110     if (status != STATUS_SUCCESS) {
1111         OVS_LOG_ERROR("Fali to get IF entry for internal port with GUID"
1112                       "  %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
1113                       netCfgInstanceId->Data1,
1114                       netCfgInstanceId->Data2,
1115                       netCfgInstanceId->Data3,
1116                       *(UINT16 *)netCfgInstanceId->Data4,
1117                       netCfgInstanceId->Data4[2],
1118                       netCfgInstanceId->Data4[3],
1119                       netCfgInstanceId->Data4[4],
1120                       netCfgInstanceId->Data4[5],
1121                       netCfgInstanceId->Data4[6],
1122                       netCfgInstanceId->Data4[7]);
1123         return;
1124     }
1125
1126     status = OvsGetIPInterfaceEntry(ovsInternalRow.InterfaceLuid,
1127                                     &ovsInternalIPRow);
1128
1129     if (status == STATUS_SUCCESS) {
1130         NdisAcquireSpinLock(&ovsIpHelperLock);
1131         ovsInternalIPConfigured = TRUE;
1132         NdisReleaseSpinLock(&ovsIpHelperLock);
1133     } else {
1134         return;
1135     }
1136
1137     status = OvsGetIPEntry(ovsInternalRow.InterfaceLuid, &ipEntry);
1138     if (status != STATUS_SUCCESS) {
1139         OVS_LOG_INFO("Fali to get IP entry for internal port with GUID"
1140                      "  %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
1141                      netCfgInstanceId->Data1,
1142                      netCfgInstanceId->Data2,
1143                      netCfgInstanceId->Data3,
1144                      *(UINT16 *)netCfgInstanceId->Data4,
1145                      netCfgInstanceId->Data4[2],
1146                      netCfgInstanceId->Data4[3],
1147                      netCfgInstanceId->Data4[4],
1148                      netCfgInstanceId->Data4[5],
1149                      netCfgInstanceId->Data4[6],
1150                      netCfgInstanceId->Data4[7]);
1151     }
1152 }
1153
1154
1155 static NTSTATUS
1156 OvsEnqueueIpHelperRequest(POVS_IP_HELPER_REQUEST request)
1157 {
1158
1159     NdisAcquireSpinLock(&ovsIpHelperLock);
1160
1161     if (ovsInternalPortNo == OVS_DEFAULT_PORT_NO ||
1162         ovsInternalIPConfigured == FALSE) {
1163         NdisReleaseSpinLock(&ovsIpHelperLock);
1164         OvsFreeMemory(request);
1165         return STATUS_NDIS_ADAPTER_NOT_READY;
1166     } else {
1167         InsertHeadList(&ovsIpHelperRequestList, &request->link);
1168         ovsNumIpHelperRequests++;
1169         if (ovsNumIpHelperRequests == 1) {
1170             OvsWakeupIPHelper();
1171         }
1172         NdisReleaseSpinLock(&ovsIpHelperLock);
1173         return STATUS_SUCCESS;
1174     }
1175 }
1176
1177
1178 NTSTATUS
1179 OvsFwdIPHelperRequest(PNET_BUFFER_LIST nbl,
1180                       UINT32 inPort,
1181                       const OvsIPv4TunnelKey *tunnelKey,
1182                       OvsIPHelperCallback cb,
1183                       PVOID cbData1,
1184                       PVOID cbData2)
1185 {
1186     POVS_IP_HELPER_REQUEST request;
1187
1188     request =
1189       (POVS_IP_HELPER_REQUEST)OvsAllocateMemory(sizeof (OVS_IP_HELPER_REQUEST));
1190
1191     if (request == NULL) {
1192         return STATUS_INSUFFICIENT_RESOURCES;
1193     }
1194     request->command = OVS_IP_HELPER_FWD_REQUEST;
1195     request->fwdReq.nbl = nbl;
1196     request->fwdReq.inPort = inPort;
1197     RtlCopyMemory(&request->fwdReq.tunnelKey, tunnelKey,
1198                   sizeof (*tunnelKey));
1199     request->fwdReq.cb = cb;
1200     request->fwdReq.cbData1 = cbData1;
1201     request->fwdReq.cbData2 = cbData2;
1202
1203     return OvsEnqueueIpHelperRequest(request);
1204 }
1205
1206
1207 static VOID
1208 OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST request)
1209 {
1210     SOCKADDR_INET dst, src;
1211     NTSTATUS status = STATUS_SUCCESS;
1212     MIB_IPFORWARD_ROW2 ipRoute;
1213     MIB_IPNET_ROW2 ipNeigh;
1214     OVS_FWD_INFO fwdInfo;
1215     UINT32 ipAddr;
1216     UINT32 srcAddr;
1217     POVS_FWD_ENTRY fwdEntry = NULL;
1218     POVS_IPFORWARD_ENTRY ipf = NULL;
1219     POVS_IPNEIGH_ENTRY ipn = NULL;
1220     LOCK_STATE_EX lockState;
1221     BOOLEAN  newIPF = FALSE;
1222     BOOLEAN  newIPN = FALSE;
1223     BOOLEAN  newFWD = FALSE;
1224
1225     status = OvsLookupIPFwdInfo(request->fwdReq.tunnelKey.dst,
1226                                 &fwdInfo);
1227     if (status == STATUS_SUCCESS) {
1228         goto fwd_handle_nbl;
1229     }
1230
1231     /* find IPRoute */
1232     RtlZeroMemory(&dst, sizeof(dst));
1233     RtlZeroMemory(&src, sizeof(src));
1234     RtlZeroMemory(&ipRoute, sizeof (MIB_IPFORWARD_ROW2));
1235     dst.si_family = AF_INET;
1236     dst.Ipv4.sin_addr.s_addr = request->fwdReq.tunnelKey.dst;
1237
1238     status = OvsGetRoute(ovsInternalRow.InterfaceLuid, &dst, &ipRoute, &src);
1239     if (status != STATUS_SUCCESS) {
1240         goto fwd_handle_nbl;
1241     }
1242     srcAddr = src.Ipv4.sin_addr.s_addr;
1243
1244     /* find IPNeigh */
1245     ipAddr = ipRoute.NextHop.Ipv4.sin_addr.s_addr;
1246     if (ipAddr != 0) {
1247         NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
1248         ipn = OvsLookupIPNeighEntry(ipAddr);
1249         if (ipn) {
1250             goto fwd_request_done;
1251         }
1252         NdisReleaseRWLock(ovsTableLock, &lockState);
1253     }
1254     RtlZeroMemory(&ipNeigh, sizeof (ipNeigh));
1255     ipNeigh.InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value;
1256     if (ipAddr == 0) {
1257         ipAddr = request->fwdReq.tunnelKey.dst;
1258     }
1259     status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh);
1260     if (status != STATUS_SUCCESS) {
1261         goto fwd_handle_nbl;
1262     }
1263
1264     NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
1265
1266 fwd_request_done:
1267
1268     /*
1269      * Initialize ipf
1270      */
1271     ipf = OvsLookupIPForwardEntry(&ipRoute.DestinationPrefix);
1272     if (ipf == NULL) {
1273         ipf = OvsCreateIPForwardEntry(&ipRoute);
1274         if (ipf == NULL) {
1275             NdisReleaseRWLock(ovsTableLock, &lockState);
1276             status = STATUS_INSUFFICIENT_RESOURCES;
1277             goto fwd_handle_nbl;
1278         }
1279         newIPF = TRUE;
1280     } else {
1281         PLIST_ENTRY link;
1282         link = ipf->fwdList.Flink;
1283         fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink);
1284         srcAddr = fwdEntry->info.srcIpAddr;
1285     }
1286
1287     /*
1288      * initialize ipn
1289      */
1290     if (ipn == NULL) {
1291         ipn = OvsLookupIPNeighEntry(ipAddr);
1292         if (ipn == NULL) {
1293             ipn = OvsCreateIPNeighEntry(&ipNeigh);
1294             if (ipn == NULL) {
1295                 NdisReleaseRWLock(ovsTableLock, &lockState);
1296                 status = STATUS_INSUFFICIENT_RESOURCES;
1297                 goto fwd_handle_nbl;
1298             }
1299             newIPN = TRUE;
1300         }
1301     }
1302
1303     /*
1304      * initialize fwdEntry
1305      */
1306     fwdInfo.dstIpAddr = request->fwdReq.tunnelKey.dst;
1307     fwdInfo.srcIpAddr = srcAddr;
1308     RtlCopyMemory(fwdInfo.dstMacAddr, ipn->macAddr, MAC_ADDRESS_LEN);
1309     RtlCopyMemory(fwdInfo.srcMacAddr, ovsInternalRow.PhysicalAddress,
1310                   MAC_ADDRESS_LEN);
1311     fwdInfo.srcPortNo = request->fwdReq.inPort;
1312
1313     fwdEntry = OvsCreateFwdEntry(&fwdInfo);
1314     if (fwdEntry == NULL) {
1315         NdisReleaseRWLock(ovsTableLock, &lockState);
1316         status = STATUS_INSUFFICIENT_RESOURCES;
1317         goto fwd_handle_nbl;
1318     }
1319     newFWD = TRUE;
1320     /*
1321      * Cache the result
1322      */
1323     OvsAddIPFwdCache(fwdEntry, ipf, ipn);
1324     NdisReleaseRWLock(ovsTableLock, &lockState);
1325
1326 fwd_handle_nbl:
1327
1328     if (status != STATUS_SUCCESS) {
1329         if (newFWD) {
1330             ASSERT(fwdEntry != NULL);
1331             OvsFreeMemory(fwdEntry);
1332         }
1333         if (newIPF) {
1334             ASSERT(ipf && ipf->refCount == 0);
1335             OvsFreeMemory(ipf);
1336         }
1337         if (newIPN) {
1338             ASSERT(ipn && ipn->refCount == 0);
1339             OvsFreeMemory(ipn);
1340         }
1341         ipAddr = request->fwdReq.tunnelKey.dst;
1342         OVS_LOG_INFO("Fail to handle IP helper request for dst: %d.%d.%d.%d",
1343                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
1344                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
1345     }
1346     if (request->fwdReq.cb) {
1347         request->fwdReq.cb(request->fwdReq.nbl,
1348                            request->fwdReq.inPort,
1349                            &request->fwdReq.tunnelKey,
1350                            request->fwdReq.cbData1,
1351                            request->fwdReq.cbData2,
1352                            status,
1353                            status == STATUS_SUCCESS ? &fwdInfo : NULL);
1354     }
1355     OvsFreeMemory(request);
1356 }
1357
1358
1359 static VOID
1360 OvsUpdateIPNeighEntry(UINT32 ipAddr,
1361                       PMIB_IPNET_ROW2 ipNeigh,
1362                       NTSTATUS status)
1363 {
1364     UINT64 timeVal;
1365     POVS_IPNEIGH_ENTRY ipn;
1366     LOCK_STATE_EX lockState;
1367     KeQuerySystemTime((LARGE_INTEGER *)&timeVal);
1368     /*
1369      * if mac changed, update all relevant fwdEntry
1370      */
1371     if (status != STATUS_SUCCESS) {
1372         NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
1373     } else {
1374         NdisAcquireRWLockRead(ovsTableLock, &lockState, 0);
1375     }
1376     ipn = OvsLookupIPNeighEntry(ipAddr);
1377     if (ipn == NULL) {
1378         NdisReleaseRWLock(ovsTableLock, &lockState);
1379         return;
1380     }
1381     if (status != STATUS_SUCCESS) {
1382         OvsRemoveIPNeighEntry(ipn);
1383         NdisReleaseRWLock(ovsTableLock, &lockState);
1384         return;
1385     }
1386
1387     if (memcmp((const PVOID)ipn->macAddr,
1388                (const PVOID)ipNeigh->PhysicalAddress,
1389                (size_t)MAC_ADDRESS_LEN)) {
1390         PLIST_ENTRY link;
1391         POVS_FWD_ENTRY fwdEntry;
1392         NdisReleaseRWLock(ovsTableLock, &lockState);
1393         /*
1394          * need update, release and acquire write lock
1395          * This is not the common case.
1396          */
1397
1398         NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
1399         ipn = OvsLookupIPNeighEntry(ipAddr);
1400
1401         if (ipn == NULL) {
1402             NdisReleaseRWLock(ovsTableLock, &lockState);
1403             return;
1404         }
1405
1406         LIST_FORALL(&ipn->fwdList, link) {
1407             fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink);
1408             RtlCopyMemory(fwdEntry->info.dstMacAddr,
1409                           ipNeigh->PhysicalAddress, MAC_ADDRESS_LEN);
1410         }
1411     }
1412     /*
1413      * update timeout and move to the end of
1414      * the sorted list
1415      */
1416
1417     NdisAcquireSpinLock(&ovsIpHelperLock);
1418     RemoveEntryList(&ipn->slink);
1419     ipn->timeout = timeVal + OVS_IPNEIGH_TIMEOUT;
1420     OvsAddToSortedNeighList(ipn);
1421     NdisReleaseSpinLock(&ovsIpHelperLock);
1422     NdisReleaseRWLock(ovsTableLock, &lockState);
1423 }
1424
1425
1426 static VOID
1427 OvsHandleIPNeighTimeout(UINT32 ipAddr)
1428 {
1429     MIB_IPNET_ROW2 ipNeigh;
1430     NTSTATUS status;
1431
1432     status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh);
1433
1434     OvsUpdateIPNeighEntry(ipAddr, &ipNeigh, status);
1435 }
1436
1437
1438 /*
1439  *----------------------------------------------------------------------------
1440  *  IP Helper system threash handle following request
1441  *    1. Intialize Internal port row when internal port is connected
1442  *    2. Handle FWD request
1443  *    3. Handle IP Neigh timeout
1444  *
1445  *    IP Interface, unicast address, and IP route change will be handled
1446  *    by the revelant callback.
1447  *----------------------------------------------------------------------------
1448  */
1449 VOID
1450 OvsStartIpHelper(PVOID data)
1451 {
1452     POVS_IP_HELPER_THREAD_CONTEXT context = (POVS_IP_HELPER_THREAD_CONTEXT)data;
1453     POVS_IP_HELPER_REQUEST req;
1454     POVS_IPNEIGH_ENTRY ipn;
1455     PLIST_ENTRY link;
1456     UINT64   timeVal, timeout;
1457
1458     OVS_LOG_INFO("Start the IP Helper Thread, context: %p", context);
1459
1460     NdisAcquireSpinLock(&ovsIpHelperLock);
1461     while (!context->exit) {
1462
1463         timeout = 0;
1464         while (!IsListEmpty(&ovsIpHelperRequestList)) {
1465             if (context->exit) {
1466                 goto ip_helper_wait;
1467             }
1468             link = ovsIpHelperRequestList.Flink;
1469             RemoveEntryList(link);
1470             NdisReleaseSpinLock(&ovsIpHelperLock);
1471             req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link);
1472             switch (req->command) {
1473             case OVS_IP_HELPER_INTERNAL_ADAPTER_UP:
1474                 OvsHandleInternalAdapterUp(req);
1475                 break;
1476             case OVS_IP_HELPER_FWD_REQUEST:
1477                 OvsHandleFwdRequest(req);
1478                 break;
1479             default:
1480                 OvsFreeMemory(req);
1481             }
1482             NdisAcquireSpinLock(&ovsIpHelperLock);
1483         }
1484
1485         /* for now, let us hold the lock here, if this cause any issue
1486          * we will change to use IpHelper lock only to protect
1487          * IPN
1488          */
1489         while (!IsListEmpty(&ovsSortedIPNeighList)) {
1490             UINT32 ipAddr;
1491             if (context->exit) {
1492                 goto ip_helper_wait;
1493             }
1494             link = ovsSortedIPNeighList.Flink;
1495             ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink);
1496             KeQuerySystemTime((LARGE_INTEGER *)&timeVal);
1497             if (ipn->timeout > timeVal) {
1498                 timeout = ipn->timeout;
1499                 break;
1500             }
1501             ipAddr = ipn->ipAddr;
1502
1503             NdisReleaseSpinLock(&ovsIpHelperLock);
1504
1505             OvsHandleIPNeighTimeout(ipAddr);
1506
1507             NdisAcquireSpinLock(&ovsIpHelperLock);
1508         }
1509         if (!IsListEmpty(&ovsIpHelperRequestList)) {
1510             continue;
1511         }
1512
1513 ip_helper_wait:
1514         if (context->exit) {
1515             break;
1516         }
1517
1518         KeClearEvent(&context->event);
1519         NdisReleaseSpinLock(&ovsIpHelperLock);
1520
1521         KeWaitForSingleObject(&context->event, Executive, KernelMode,
1522                               FALSE, (LARGE_INTEGER *)&timeout);
1523         NdisAcquireSpinLock(&ovsIpHelperLock);
1524     }
1525     NdisReleaseSpinLock(&ovsIpHelperLock);
1526     OvsCleanupFwdTable();
1527     OvsCleanupIpHelperRequestList();
1528
1529     OVS_LOG_INFO("Terminating the OVS IP Helper system thread");
1530
1531     PsTerminateSystemThread(STATUS_SUCCESS);
1532 }
1533
1534
1535 NTSTATUS
1536 OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle)
1537 {
1538     NTSTATUS status;
1539     HANDLE threadHandle;
1540     UINT32 i;
1541
1542     ovsFwdHashTable = (PLIST_ENTRY)OvsAllocateMemory(sizeof(LIST_ENTRY) *
1543                                                      OVS_FWD_HASH_TABLE_SIZE);
1544
1545     ovsRouteHashTable = (PLIST_ENTRY)OvsAllocateMemory(sizeof(LIST_ENTRY) *
1546                                                        OVS_ROUTE_HASH_TABLE_SIZE);
1547
1548     ovsNeighHashTable = (PLIST_ENTRY)OvsAllocateMemory(sizeof(LIST_ENTRY) *
1549                                                        OVS_NEIGH_HASH_TABLE_SIZE);
1550
1551     RtlZeroMemory(&ovsInternalRow, sizeof(MIB_IF_ROW2));
1552     RtlZeroMemory(&ovsInternalIPRow, sizeof (MIB_IPINTERFACE_ROW));
1553     ovsInternalIP = 0;
1554
1555     ovsInternalPortNo = OVS_DEFAULT_PORT_NO;
1556
1557     InitializeListHead(&ovsSortedIPNeighList);
1558
1559     ovsTableLock = NdisAllocateRWLock(ndisFilterHandle);
1560     NdisAllocateSpinLock(&ovsIpHelperLock);
1561
1562     InitializeListHead(&ovsIpHelperRequestList);
1563     ovsNumIpHelperRequests = 0;
1564     ipInterfaceNotificationHandle = NULL;
1565     ipRouteNotificationHandle = NULL;
1566     unicastIPNotificationHandle = NULL;
1567
1568     if (ovsFwdHashTable == NULL ||
1569         ovsRouteHashTable == NULL ||
1570         ovsNeighHashTable == NULL ||
1571         ovsTableLock == NULL) {
1572         status = STATUS_INSUFFICIENT_RESOURCES;
1573         goto init_cleanup;
1574     }
1575
1576     for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) {
1577         InitializeListHead(&ovsFwdHashTable[i]);
1578     }
1579
1580     for (i = 0; i < OVS_ROUTE_HASH_TABLE_SIZE; i++) {
1581         InitializeListHead(&ovsRouteHashTable[i]);
1582     }
1583
1584     for (i = 0; i < OVS_NEIGH_HASH_TABLE_SIZE; i++) {
1585         InitializeListHead(&ovsNeighHashTable[i]);
1586     }
1587
1588
1589     KeInitializeEvent(&ovsIpHelperThreadContext.event, NotificationEvent,
1590                       FALSE);
1591     status = OvsRegisterChangeNotification();
1592     ovsIpHelperThreadContext.exit = 0;
1593     if (status == STATUS_SUCCESS) {
1594         status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE,
1595                                       NULL, NULL, NULL, OvsStartIpHelper,
1596                                       &ovsIpHelperThreadContext);
1597         if (status != STATUS_SUCCESS) {
1598             goto init_cleanup;
1599         }
1600         ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL,
1601                                   KernelMode,
1602                                   &ovsIpHelperThreadContext.threadObject,
1603                                   NULL);
1604         ZwClose(threadHandle);
1605     }
1606
1607 init_cleanup:
1608
1609     if (status != STATUS_SUCCESS) {
1610         OvsCancelChangeNotification();
1611         if (ovsFwdHashTable) {
1612             OvsFreeMemory(ovsFwdHashTable);
1613             ovsFwdHashTable = NULL;
1614         }
1615         if (ovsRouteHashTable) {
1616             OvsFreeMemory(ovsRouteHashTable);
1617             ovsRouteHashTable = NULL;
1618         }
1619         if (ovsNeighHashTable) {
1620             OvsFreeMemory(ovsNeighHashTable);
1621             ovsNeighHashTable = NULL;
1622         }
1623         if (ovsTableLock) {
1624             NdisFreeRWLock(ovsTableLock);
1625             ovsTableLock = NULL;
1626         }
1627         NdisFreeSpinLock(&ovsIpHelperLock);
1628     }
1629     return STATUS_SUCCESS;
1630 }
1631
1632
1633 VOID
1634 OvsCleanupIpHelper(VOID)
1635 {
1636     OvsCancelChangeNotification();
1637
1638     NdisAcquireSpinLock(&ovsIpHelperLock);
1639     ovsIpHelperThreadContext.exit = 1;
1640     OvsWakeupIPHelper();
1641     NdisReleaseSpinLock(&ovsIpHelperLock);
1642
1643     KeWaitForSingleObject(ovsIpHelperThreadContext.threadObject, Executive,
1644                           KernelMode, FALSE, NULL);
1645     ObDereferenceObject(ovsIpHelperThreadContext.threadObject);
1646
1647     OvsFreeMemory(ovsFwdHashTable);
1648     OvsFreeMemory(ovsRouteHashTable);
1649     OvsFreeMemory(ovsNeighHashTable);
1650
1651     NdisFreeRWLock(ovsTableLock);
1652     NdisFreeSpinLock(&ovsIpHelperLock);
1653 }
1654
1655 VOID
1656 OvsCancelFwdIpHelperRequest(PNET_BUFFER_LIST nbl)
1657 {
1658     PLIST_ENTRY link, next;
1659     POVS_IP_HELPER_REQUEST req;
1660     LIST_ENTRY list;
1661     InitializeListHead(&list);
1662
1663     NdisAcquireSpinLock(&ovsIpHelperLock);
1664     LIST_FORALL_SAFE(&ovsIpHelperRequestList, link, next) {
1665         req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link);
1666         if (req->command == OVS_IP_HELPER_FWD_REQUEST &&
1667             (nbl == NULL || req->fwdReq.nbl == nbl)) {
1668             RemoveEntryList(link);
1669             InsertHeadList(&list, link);
1670             if (nbl != NULL) {
1671                 break;
1672             }
1673         }
1674     }
1675     NdisReleaseSpinLock(&ovsIpHelperLock);
1676
1677     LIST_FORALL_SAFE(&list, link, next) {
1678         req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link);
1679         if (req->fwdReq.cb) {
1680             req->fwdReq.cb(req->fwdReq.nbl, req->fwdReq.inPort,
1681                            &req->fwdReq.tunnelKey,
1682                            req->fwdReq.cbData1,
1683                            req->fwdReq.cbData2,
1684                            STATUS_DEVICE_NOT_READY,
1685                            NULL);
1686         }
1687         OvsFreeMemory(req);
1688     }
1689 }