datapath-windows: Implement event read handler.
[cascardo/ovs.git] / datapath-windows / ovsext / Datapath.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 /*
18  * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface
19  * alive while we transition over to the netlink based interface.
20  * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c
21  * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c
22  */
23 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 1
24
25 #include "precomp.h"
26 #include "Datapath.h"
27 #include "Jhash.h"
28 #include "Switch.h"
29 #include "Vport.h"
30 #include "Event.h"
31 #include "User.h"
32 #include "PacketIO.h"
33 #include "NetProto.h"
34 #include "Flow.h"
35 #include "User.h"
36
37 #ifdef OVS_DBG_MOD
38 #undef OVS_DBG_MOD
39 #endif
40 #define OVS_DBG_MOD OVS_DBG_DATAPATH
41 #include "Debug.h"
42
43 #define NETLINK_FAMILY_NAME_LEN 48
44
45
46 /*
47  * Netlink messages are grouped by family (aka type), and each family supports
48  * a set of commands, and can be passed both from kernel -> userspace or
49  * vice-versa. To call into the kernel, userspace uses a device operation which
50  * is outside of a netlink message.
51  *
52  * Each command results in the invocation of a handler function to implement the
53  * request functionality.
54  *
55  * Expectedly, only certain combinations of (device operation, netlink family,
56  * command) are valid.
57  *
58  * Here, we implement the basic infrastructure to perform validation on the
59  * incoming message, version checking, and also to invoke the corresponding
60  * handler to do the heavy-lifting.
61  */
62
63 /*
64  * Handler for a given netlink command. Not all the parameters are used by all
65  * the handlers.
66  */
67 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
68                                     UINT32 *replyLen);
69
70 typedef struct _NETLINK_CMD {
71     UINT16 cmd;
72     NetlinkCmdHandler *handler;
73     UINT32 supportedDevOp;      /* Supported device operations. */
74     BOOLEAN validateDpIndex;    /* Does command require a valid DP argument. */
75 } NETLINK_CMD, *PNETLINK_CMD;
76
77 /* A netlink family is a group of commands. */
78 typedef struct _NETLINK_FAMILY {
79     CHAR *name;
80     UINT32 id;
81     UINT8 version;
82     UINT8 pad;
83     UINT16 maxAttr;
84     NETLINK_CMD *cmds;          /* Array of netlink commands and handlers. */
85     UINT16 opsCount;
86 } NETLINK_FAMILY, *PNETLINK_FAMILY;
87
88 /*
89  * Device operations to tag netlink commands with. This is a bitmask since it is
90  * possible that a particular command can be invoked via different device
91  * operations.
92  */
93 #define OVS_READ_DEV_OP          (1 << 0)
94 #define OVS_WRITE_DEV_OP         (1 << 1)
95 #define OVS_TRANSACTION_DEV_OP   (1 << 2)
96 #define OVS_READ_EVENT_DEV_OP    (1 << 3)
97
98 /* Handlers for the various netlink commands. */
99 static NetlinkCmdHandler OvsGetPidCmdHandler,
100                          OvsGetDpCmdHandler,
101                          OvsPendEventCmdHandler,
102                          OvsSubscribeEventCmdHandler,
103                          OvsSetDpCmdHandler,
104                          OvsReadEventCmdHandler,
105                          OvsGetVportCmdHandler;
106
107 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
108                                        UINT32 *replyLen);
109 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
110                                 UINT32 *replyLen);
111 static NTSTATUS HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
112                                     UINT32 *replyLen);
113
114 /*
115  * The various netlink families, along with the supported commands. Most of
116  * these families and commands are part of the openvswitch specification for a
117  * netlink datapath. In addition, each platform can implement a few families
118  * and commands as extensions.
119  */
120
121 /* Netlink control family: this is a Windows specific family. */
122 NETLINK_CMD nlControlFamilyCmdOps[] = {
123     { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
124       .handler         = OvsGetPidCmdHandler,
125       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
126       .validateDpIndex = FALSE,
127     },
128     { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
129       .handler = OvsPendEventCmdHandler,
130       .supportedDevOp = OVS_WRITE_DEV_OP,
131       .validateDpIndex = TRUE,
132     },
133     { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
134       .handler = OvsSubscribeEventCmdHandler,
135       .supportedDevOp = OVS_WRITE_DEV_OP,
136       .validateDpIndex = TRUE,
137     },
138     { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
139       .handler = OvsReadEventCmdHandler,
140       .supportedDevOp = OVS_READ_EVENT_DEV_OP,
141       .validateDpIndex = FALSE,
142     }
143 };
144
145 NETLINK_FAMILY nlControlFamilyOps = {
146     .name     = OVS_WIN_CONTROL_FAMILY,
147     .id       = OVS_WIN_NL_CTRL_FAMILY_ID,
148     .version  = OVS_WIN_CONTROL_VERSION,
149     .maxAttr  = OVS_WIN_CONTROL_ATTR_MAX,
150     .cmds     = nlControlFamilyCmdOps,
151     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
152 };
153
154 /* Netlink datapath family. */
155 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
156     { .cmd             = OVS_DP_CMD_GET,
157       .handler         = OvsGetDpCmdHandler,
158       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
159                          OVS_TRANSACTION_DEV_OP,
160       .validateDpIndex = FALSE
161     },
162     { .cmd             = OVS_DP_CMD_SET,
163       .handler         = OvsSetDpCmdHandler,
164       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
165                          OVS_TRANSACTION_DEV_OP,
166       .validateDpIndex = TRUE
167     }
168 };
169
170 NETLINK_FAMILY nlDatapathFamilyOps = {
171     .name     = OVS_DATAPATH_FAMILY,
172     .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
173     .version  = OVS_DATAPATH_VERSION,
174     .maxAttr  = OVS_DP_ATTR_MAX,
175     .cmds     = nlDatapathFamilyCmdOps,
176     .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
177 };
178
179 /* Netlink packet family. */
180 /* XXX: Add commands here. */
181 NETLINK_FAMILY nlPacketFamilyOps = {
182     .name     = OVS_PACKET_FAMILY,
183     .id       = OVS_WIN_NL_PACKET_FAMILY_ID,
184     .version  = OVS_PACKET_VERSION,
185     .maxAttr  = OVS_PACKET_ATTR_MAX,
186     .cmds     = NULL, /* XXX: placeholder. */
187     .opsCount = 0
188 };
189
190 /* Netlink vport family. */
191 NETLINK_CMD nlVportFamilyCmdOps[] = {
192     { .cmd = OVS_VPORT_CMD_GET,
193       .handler = OvsGetVportCmdHandler,
194       .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
195                         OVS_TRANSACTION_DEV_OP,
196       .validateDpIndex = TRUE
197     }
198 };
199
200 NETLINK_FAMILY nlVportFamilyOps = {
201     .name     = OVS_VPORT_FAMILY,
202     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
203     .version  = OVS_VPORT_VERSION,
204     .maxAttr  = OVS_VPORT_ATTR_MAX,
205     .cmds     = nlVportFamilyCmdOps,
206     .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
207 };
208
209 /* Netlink flow family. */
210 /* XXX: Add commands here. */
211 NETLINK_FAMILY nlFLowFamilyOps = {
212     .name     = OVS_FLOW_FAMILY,
213     .id       = OVS_WIN_NL_FLOW_FAMILY_ID,
214     .version  = OVS_FLOW_VERSION,
215     .maxAttr  = OVS_FLOW_ATTR_MAX,
216     .cmds     = NULL, /* XXX: placeholder. */
217     .opsCount = 0
218 };
219
220 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
221                                    UINT32 bufferLength,
222                                    UINT32 requiredLength,
223                                    PVOID *buffer);
224 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
225                                    POVS_OPEN_INSTANCE instance,
226                                    POVS_MESSAGE ovsMsg,
227                                    NETLINK_FAMILY *nlFamilyOps);
228 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
229                                         NETLINK_FAMILY *nlFamilyOps,
230                                         UINT32 *replyLen);
231 static NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx);
232
233
234 /* Handles to the device object for communication with userspace. */
235 NDIS_HANDLE gOvsDeviceHandle;
236 PDEVICE_OBJECT gOvsDeviceObject;
237
238 _Dispatch_type_(IRP_MJ_CREATE)
239 _Dispatch_type_(IRP_MJ_CLOSE)
240 DRIVER_DISPATCH OvsOpenCloseDevice;
241
242 _Dispatch_type_(IRP_MJ_CLEANUP)
243 DRIVER_DISPATCH OvsCleanupDevice;
244
245 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
246 DRIVER_DISPATCH OvsDeviceControl;
247
248 #ifdef ALLOC_PRAGMA
249 #pragma alloc_text(INIT, OvsCreateDeviceObject)
250 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
251 #pragma alloc_text(PAGE, OvsCleanupDevice)
252 #pragma alloc_text(PAGE, OvsDeviceControl)
253 #endif // ALLOC_PRAGMA
254
255 /*
256  * We might hit this limit easily since userspace opens a netlink descriptor for
257  * each thread, and at least one descriptor per vport. Revisit this later.
258  */
259 #define OVS_MAX_OPEN_INSTANCES 512
260 #define OVS_SYSTEM_DP_NAME     "ovs-system"
261
262 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
263 UINT32 ovsNumberOfOpenInstances;
264 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
265
266 NDIS_SPIN_LOCK ovsCtrlLockObj;
267 PNDIS_SPIN_LOCK gOvsCtrlLock;
268
269
270 VOID
271 OvsInit()
272 {
273     gOvsCtrlLock = &ovsCtrlLockObj;
274     NdisAllocateSpinLock(gOvsCtrlLock);
275     OvsInitEventQueue();
276     OvsUserInit();
277 }
278
279 VOID
280 OvsCleanup()
281 {
282     OvsCleanupEventQueue();
283     if (gOvsCtrlLock) {
284         NdisFreeSpinLock(gOvsCtrlLock);
285         gOvsCtrlLock = NULL;
286     }
287     OvsUserCleanup();
288 }
289
290 VOID
291 OvsAcquireCtrlLock()
292 {
293     NdisAcquireSpinLock(gOvsCtrlLock);
294 }
295
296 VOID
297 OvsReleaseCtrlLock()
298 {
299     NdisReleaseSpinLock(gOvsCtrlLock);
300 }
301
302
303 /*
304  * --------------------------------------------------------------------------
305  * Creates the communication device between user and kernel, and also
306  * initializes the data associated data structures.
307  * --------------------------------------------------------------------------
308  */
309 NDIS_STATUS
310 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
311 {
312     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
313     UNICODE_STRING deviceName;
314     UNICODE_STRING symbolicDeviceName;
315     PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
316     NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
317     OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
318
319     RtlZeroMemory(dispatchTable,
320                   (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
321     dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
322     dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
323     dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
324     dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
325
326     NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
327     NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
328
329     RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
330
331     OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
332                            NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
333                            NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
334                            sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
335
336     deviceAttributes.DeviceName = &deviceName;
337     deviceAttributes.SymbolicName = &symbolicDeviceName;
338     deviceAttributes.MajorFunctions = dispatchTable;
339     deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
340
341     status = NdisRegisterDeviceEx(ovsExtDriverHandle,
342                                   &deviceAttributes,
343                                   &gOvsDeviceObject,
344                                   &gOvsDeviceHandle);
345     if (status != NDIS_STATUS_SUCCESS) {
346         POVS_DEVICE_EXTENSION ovsExt =
347             (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
348         ASSERT(gOvsDeviceObject != NULL);
349         ASSERT(gOvsDeviceHandle != NULL);
350
351         if (ovsExt) {
352             ovsExt->numberOpenInstance = 0;
353         }
354     } else {
355         /* Initialize the associated data structures. */
356         OvsInit();
357     }
358     OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
359     return status;
360 }
361
362
363 VOID
364 OvsDeleteDeviceObject()
365 {
366     if (gOvsDeviceHandle) {
367 #ifdef DBG
368         POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
369                     NdisGetDeviceReservedExtension(gOvsDeviceObject);
370         if (ovsExt) {
371             ASSERT(ovsExt->numberOpenInstance == 0);
372         }
373 #endif
374
375         ASSERT(gOvsDeviceObject);
376         NdisDeregisterDeviceEx(gOvsDeviceHandle);
377         gOvsDeviceHandle = NULL;
378         gOvsDeviceObject = NULL;
379     }
380     OvsCleanup();
381 }
382
383 POVS_OPEN_INSTANCE
384 OvsGetOpenInstance(PFILE_OBJECT fileObject,
385                    UINT32 dpNo)
386 {
387     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
388     ASSERT(instance);
389     ASSERT(instance->fileObject == fileObject);
390     if (gOvsSwitchContext == NULL ||
391         gOvsSwitchContext->dpNo != dpNo) {
392         return NULL;
393     }
394     return instance;
395 }
396
397
398 POVS_OPEN_INSTANCE
399 OvsFindOpenInstance(PFILE_OBJECT fileObject)
400 {
401     UINT32 i, j;
402     for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
403                        j < ovsNumberOfOpenInstances; i++) {
404         if (ovsOpenInstanceArray[i]) {
405             if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
406                 return ovsOpenInstanceArray[i];
407             }
408             j++;
409         }
410     }
411     return NULL;
412 }
413
414 NTSTATUS
415 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
416                    PFILE_OBJECT fileObject)
417 {
418     POVS_OPEN_INSTANCE instance =
419         (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
420     UINT32 i;
421
422     if (instance == NULL) {
423         return STATUS_NO_MEMORY;
424     }
425     OvsAcquireCtrlLock();
426     ASSERT(OvsFindOpenInstance(fileObject) == NULL);
427
428     if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
429         OvsReleaseCtrlLock();
430         OvsFreeMemory(instance);
431         return STATUS_INSUFFICIENT_RESOURCES;
432     }
433     RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
434
435     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
436         if (ovsOpenInstanceArray[i] == NULL) {
437             ovsOpenInstanceArray[i] = instance;
438             instance->cookie = i;
439             break;
440         }
441     }
442     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
443     instance->fileObject = fileObject;
444     ASSERT(fileObject->FsContext == NULL);
445     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
446     if (instance->pid == 0) {
447         /* XXX: check for rollover. */
448     }
449     fileObject->FsContext = instance;
450     OvsReleaseCtrlLock();
451     return STATUS_SUCCESS;
452 }
453
454 static VOID
455 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
456 {
457     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
458     ASSERT(instance);
459     ASSERT(fileObject == instance->fileObject);
460     OvsCleanupEvent(instance);
461     OvsCleanupPacketQueue(instance);
462 }
463
464 VOID
465 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
466 {
467     POVS_OPEN_INSTANCE instance;
468     ASSERT(fileObject->FsContext);
469     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
470     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
471
472     OvsAcquireCtrlLock();
473     fileObject->FsContext = NULL;
474     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
475     ovsOpenInstanceArray[instance->cookie] = NULL;
476     OvsReleaseCtrlLock();
477     ASSERT(instance->eventQueue == NULL);
478     ASSERT (instance->packetQueue == NULL);
479     OvsFreeMemory(instance);
480 }
481
482 NTSTATUS
483 OvsCompleteIrpRequest(PIRP irp,
484                       ULONG_PTR infoPtr,
485                       NTSTATUS status)
486 {
487     irp->IoStatus.Information = infoPtr;
488     irp->IoStatus.Status = status;
489     IoCompleteRequest(irp, IO_NO_INCREMENT);
490     return status;
491 }
492
493
494 NTSTATUS
495 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
496                    PIRP irp)
497 {
498     PIO_STACK_LOCATION irpSp;
499     NTSTATUS status = STATUS_SUCCESS;
500     PFILE_OBJECT fileObject;
501     POVS_DEVICE_EXTENSION ovsExt =
502         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
503
504     ASSERT(deviceObject == gOvsDeviceObject);
505     ASSERT(ovsExt != NULL);
506
507     irpSp = IoGetCurrentIrpStackLocation(irp);
508     fileObject = irpSp->FileObject;
509     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
510                   deviceObject, fileObject,
511                   ovsExt->numberOpenInstance);
512
513     switch (irpSp->MajorFunction) {
514     case IRP_MJ_CREATE:
515         status = OvsAddOpenInstance(ovsExt, fileObject);
516         if (STATUS_SUCCESS == status) {
517             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
518         }
519         break;
520     case IRP_MJ_CLOSE:
521         ASSERT(ovsExt->numberOpenInstance > 0);
522         OvsRemoveOpenInstance(fileObject);
523         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
524         break;
525     default:
526         ASSERT(0);
527     }
528     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
529 }
530
531 _Use_decl_annotations_
532 NTSTATUS
533 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
534                  PIRP irp)
535 {
536
537     PIO_STACK_LOCATION irpSp;
538     PFILE_OBJECT fileObject;
539
540     NTSTATUS status = STATUS_SUCCESS;
541 #ifdef DBG
542     POVS_DEVICE_EXTENSION ovsExt =
543         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
544     if (ovsExt) {
545         ASSERT(ovsExt->numberOpenInstance > 0);
546     }
547 #else
548     UNREFERENCED_PARAMETER(deviceObject);
549 #endif
550     ASSERT(deviceObject == gOvsDeviceObject);
551     irpSp = IoGetCurrentIrpStackLocation(irp);
552     fileObject = irpSp->FileObject;
553
554     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
555
556     OvsCleanupOpenInstance(fileObject);
557
558     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
559 }
560
561
562 /*
563  * --------------------------------------------------------------------------
564  * IOCTL function handler for the device.
565  * --------------------------------------------------------------------------
566  */
567 NTSTATUS
568 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
569                  PIRP irp)
570 {
571
572     PIO_STACK_LOCATION irpSp;
573     NTSTATUS status = STATUS_SUCCESS;
574     PFILE_OBJECT fileObject;
575     PVOID inputBuffer = NULL;
576     PVOID outputBuffer = NULL;
577     UINT32 inputBufferLen, outputBufferLen;
578     UINT32 code, replyLen = 0;
579     POVS_OPEN_INSTANCE instance;
580     UINT32 devOp;
581     OVS_MESSAGE ovsMsgReadOp;
582     POVS_MESSAGE ovsMsg;
583     NETLINK_FAMILY *nlFamilyOps;
584     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
585
586 #ifdef DBG
587     POVS_DEVICE_EXTENSION ovsExt =
588         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
589     ASSERT(deviceObject == gOvsDeviceObject);
590     ASSERT(ovsExt);
591     ASSERT(ovsExt->numberOpenInstance > 0);
592 #else
593     UNREFERENCED_PARAMETER(deviceObject);
594 #endif
595
596     irpSp = IoGetCurrentIrpStackLocation(irp);
597
598     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
599     ASSERT(irpSp->FileObject != NULL);
600
601     fileObject = irpSp->FileObject;
602     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
603     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
604     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
605     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
606     inputBuffer = irp->AssociatedIrp.SystemBuffer;
607
608     /* Concurrent netlink operations are not supported. */
609     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
610         status = STATUS_RESOURCE_IN_USE;
611         goto done;
612     }
613
614     /*
615      * Validate the input/output buffer arguments depending on the type of the
616      * operation.
617      */
618     switch (code) {
619     case OVS_IOCTL_TRANSACT:
620         /* Input buffer is mandatory, output buffer is optional. */
621         if (outputBufferLen != 0) {
622             status = MapIrpOutputBuffer(irp, outputBufferLen,
623                                         sizeof *ovsMsg, &outputBuffer);
624             if (status != STATUS_SUCCESS) {
625                 goto done;
626             }
627             ASSERT(outputBuffer);
628         }
629
630         if (inputBufferLen < sizeof (*ovsMsg)) {
631             status = STATUS_NDIS_INVALID_LENGTH;
632             goto done;
633         }
634
635         ovsMsg = inputBuffer;
636         devOp = OVS_TRANSACTION_DEV_OP;
637         break;
638
639     case OVS_IOCTL_READ_EVENT:
640         /* This IOCTL is used to read events */
641         if (outputBufferLen != 0) {
642             status = MapIrpOutputBuffer(irp, outputBufferLen,
643                                         sizeof *ovsMsg, &outputBuffer);
644             if (status != STATUS_SUCCESS) {
645                 goto done;
646             }
647             ASSERT(outputBuffer);
648         } else {
649             status = STATUS_NDIS_INVALID_LENGTH;
650             goto done;
651         }
652         inputBuffer = NULL;
653         inputBufferLen = 0;
654
655         ovsMsg = &ovsMsgReadOp;
656         ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
657         /* An "artificial" command so we can use NL family function table*/
658         ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
659         devOp = OVS_READ_DEV_OP;
660         break;
661
662     case OVS_IOCTL_READ:
663         /* Output buffer is mandatory. */
664         if (outputBufferLen != 0) {
665             status = MapIrpOutputBuffer(irp, outputBufferLen,
666                                         sizeof *ovsMsg, &outputBuffer);
667             if (status != STATUS_SUCCESS) {
668                 goto done;
669             }
670             ASSERT(outputBuffer);
671         } else {
672             status = STATUS_NDIS_INVALID_LENGTH;
673             goto done;
674         }
675
676         /*
677          * Operate in the mode that read ioctl is similar to ReadFile(). This
678          * might change as the userspace code gets implemented.
679          */
680         inputBuffer = NULL;
681         inputBufferLen = 0;
682
683         /*
684          * For implementing read (ioctl or otherwise), we need to store some
685          * state in the instance to indicate the command that started the dump
686          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
687          * that 'ovsMsgReadOp' is needed only in this function to call into the
688          * appropraite handler. The handler itself can access the state in the
689          * instance.
690          *
691          * In the absence of a dump start, return 0 bytes.
692          */
693         if (instance->dumpState.ovsMsg == NULL) {
694             replyLen = 0;
695             status = STATUS_SUCCESS;
696             goto done;
697         }
698         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
699                       sizeof (ovsMsgReadOp));
700
701         /* Create an NL message for consumption. */
702         ovsMsg = &ovsMsgReadOp;
703         devOp = OVS_READ_DEV_OP;
704
705         break;
706
707     case OVS_IOCTL_WRITE:
708         /* Input buffer is mandatory. */
709         if (inputBufferLen < sizeof (*ovsMsg)) {
710             status = STATUS_NDIS_INVALID_LENGTH;
711             goto done;
712         }
713
714         ovsMsg = inputBuffer;
715         devOp = OVS_WRITE_DEV_OP;
716         break;
717
718     default:
719         status = STATUS_INVALID_DEVICE_REQUEST;
720         goto done;
721     }
722
723     ASSERT(ovsMsg);
724     switch (ovsMsg->nlMsg.nlmsgType) {
725     case OVS_WIN_NL_CTRL_FAMILY_ID:
726         nlFamilyOps = &nlControlFamilyOps;
727         break;
728     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
729         nlFamilyOps = &nlDatapathFamilyOps;
730         break;
731     case OVS_WIN_NL_PACKET_FAMILY_ID:
732     case OVS_WIN_NL_FLOW_FAMILY_ID:
733         status = STATUS_NOT_IMPLEMENTED;
734         goto done;
735     case OVS_WIN_NL_VPORT_FAMILY_ID:
736         nlFamilyOps = &nlVportFamilyOps;
737         break;
738     default:
739         status = STATUS_INVALID_PARAMETER;
740         goto done;
741     }
742
743     /*
744      * For read operation, the netlink command has already been validated
745      * previously.
746      */
747     if (devOp != OVS_READ_DEV_OP) {
748         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
749         if (status != STATUS_SUCCESS) {
750             goto done;
751         }
752     }
753
754     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
755                       inputBuffer, inputBufferLen,
756                       outputBuffer, outputBufferLen,
757                       &usrParamsCtx);
758
759     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
760
761 done:
762     KeMemoryBarrier();
763     instance->inUse = 0;
764     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
765 }
766
767
768 /*
769  * --------------------------------------------------------------------------
770  * Function to validate a netlink command. Only certain combinations of
771  * (device operation, netlink family, command) are valid.
772  * --------------------------------------------------------------------------
773  */
774 static NTSTATUS
775 ValidateNetlinkCmd(UINT32 devOp,
776                    POVS_OPEN_INSTANCE instance,
777                    POVS_MESSAGE ovsMsg,
778                    NETLINK_FAMILY *nlFamilyOps)
779 {
780     NTSTATUS status = STATUS_INVALID_PARAMETER;
781     UINT16 i;
782
783     for (i = 0; i < nlFamilyOps->opsCount; i++) {
784         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
785             /* Validate if the command is valid for the device operation. */
786             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
787                 status = STATUS_INVALID_PARAMETER;
788                 goto done;
789             }
790
791             /* Validate the version. */
792             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
793                 status = STATUS_INVALID_PARAMETER;
794                 goto done;
795             }
796
797             /* Validate the DP for commands that require a DP. */
798             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
799                 OvsAcquireCtrlLock();
800                 if (ovsMsg->ovsHdr.dp_ifindex !=
801                     (INT)gOvsSwitchContext->dpNo) {
802                     status = STATUS_INVALID_PARAMETER;
803                     OvsReleaseCtrlLock();
804                     goto done;
805                 }
806                 OvsReleaseCtrlLock();
807             }
808
809             /* Validate the PID. */
810             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
811                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
812                     status = STATUS_INVALID_PARAMETER;
813                     goto done;
814                 }
815             }
816
817             status = STATUS_SUCCESS;
818             break;
819         }
820     }
821
822 done:
823     return status;
824 }
825
826 /*
827  * --------------------------------------------------------------------------
828  * Function to invoke the netlink command handler.
829  * --------------------------------------------------------------------------
830  */
831 static NTSTATUS
832 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
833                         NETLINK_FAMILY *nlFamilyOps,
834                         UINT32 *replyLen)
835 {
836     NTSTATUS status = STATUS_INVALID_PARAMETER;
837     UINT16 i;
838
839     for (i = 0; i < nlFamilyOps->opsCount; i++) {
840         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
841             NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
842             ASSERT(handler);
843             if (handler) {
844                 status = handler(usrParamsCtx, replyLen);
845             }
846             break;
847         }
848     }
849
850     return status;
851 }
852
853 /*
854  * --------------------------------------------------------------------------
855  *  Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
856  *
857  *  Each handle on the device is assigned a unique PID when the handle is
858  *  created. On platforms that support netlink natively, the PID is available
859  *  to userspace when the netlink socket is created. However, without native
860  *  netlink support on Windows, OVS datapath generates the PID and lets the
861  *  userspace query it.
862  *
863  *  This function implements the query.
864  * --------------------------------------------------------------------------
865  */
866 static NTSTATUS
867 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
868                     UINT32 *replyLen)
869 {
870     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
871     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
872
873     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
874         POVS_OPEN_INSTANCE instance =
875             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
876
877         RtlZeroMemory(msgOut, sizeof *msgOut);
878         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
879         msgOut->nlMsg.nlmsgPid = instance->pid;
880         *replyLen = sizeof *msgOut;
881         /* XXX: We might need to return the DP index as well. */
882     } else {
883         return STATUS_NDIS_INVALID_LENGTH;
884     }
885
886     return STATUS_SUCCESS;
887 }
888
889 /*
890  * --------------------------------------------------------------------------
891  * Utility function to fill up information about the datapath in a reply to
892  * userspace.
893  * Assumes that 'gOvsCtrlLock' lock is acquired.
894  * --------------------------------------------------------------------------
895  */
896 static NTSTATUS
897 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
898               POVS_MESSAGE msgIn,
899               PNL_BUFFER nlBuf)
900 {
901     BOOLEAN writeOk;
902     OVS_MESSAGE msgOutTmp;
903     OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
904     PNL_MSG_HDR nlMsg;
905
906     /* XXX: Add API for nlBuf->bufRemLen. */
907     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof *msgIn);
908
909     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
910     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
911     msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
912     msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
913
914     msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
915     msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
916     msgOutTmp.genlMsg.reserved = 0;
917
918     msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
919
920     writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
921     if (writeOk) {
922         writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
923                                      OVS_SYSTEM_DP_NAME);
924     }
925     if (writeOk) {
926         OVS_DP_STATS dpStats;
927
928         dpStats.n_hit = datapath->hits;
929         dpStats.n_missed = datapath->misses;
930         dpStats.n_lost = datapath->lost;
931         dpStats.n_flows = datapath->nFlows;
932         writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
933                                      (PCHAR)&dpStats, sizeof dpStats);
934     }
935     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
936     nlMsg->nlmsgLen = NlBufSize(nlBuf);
937
938     return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
939 }
940
941 /*
942  * --------------------------------------------------------------------------
943  * Handler for queueing an IRP used for event notification. The IRP is
944  * completed when a port state changes. STATUS_PENDING is returned on
945  * success. User mode keep a pending IRP at all times.
946  * --------------------------------------------------------------------------
947  */
948 static NTSTATUS
949 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
950                        UINT32 *replyLen)
951 {
952     NDIS_STATUS status;
953
954     UNREFERENCED_PARAMETER(replyLen);
955
956     POVS_OPEN_INSTANCE instance =
957         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
958     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
959     OVS_EVENT_POLL poll;
960
961     poll.dpNo = msgIn->ovsHdr.dp_ifindex;
962     status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
963                                &poll, sizeof poll);
964     return status;
965 }
966
967
968 /*
969  * --------------------------------------------------------------------------
970  *  Handler for the subscription for the event queue
971  * --------------------------------------------------------------------------
972  */
973 static NTSTATUS
974 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
975                             UINT32 *replyLen)
976 {
977     NDIS_STATUS status;
978     OVS_EVENT_SUBSCRIBE request;
979     BOOLEAN rc;
980     UINT8 join;
981     PNL_ATTR attrs[2];
982     const NL_POLICY policy[] =  {
983         [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
984         [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
985         };
986
987     UNREFERENCED_PARAMETER(replyLen);
988
989     POVS_OPEN_INSTANCE instance =
990         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
991     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
992
993     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),policy, attrs, 2);
994     if (!rc) {
995         status = STATUS_INVALID_PARAMETER;
996         goto done;
997     }
998
999     /* XXX Ignore the MC group for now */
1000     join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1001     request.dpNo = msgIn->ovsHdr.dp_ifindex;
1002     request.subscribe = join;
1003     request.mask = OVS_EVENT_MASK_ALL;
1004
1005     status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1006                                     sizeof request);
1007 done:
1008     return status;
1009 }
1010
1011
1012 /*
1013  * --------------------------------------------------------------------------
1014  *  Command Handler for 'OVS_DP_CMD_GET'.
1015  *
1016  *  The function handles both the dump based as well as the transaction based
1017  *  'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1018  *  call to setup dump state, as well as subsequent calls to continue dumping
1019  *  data.
1020  * --------------------------------------------------------------------------
1021  */
1022 static NTSTATUS
1023 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1024                    UINT32 *replyLen)
1025 {
1026     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1027         return HandleGetDpTransaction(usrParamsCtx, replyLen);
1028     } else {
1029         return HandleGetDpDump(usrParamsCtx, replyLen);
1030     }
1031 }
1032
1033 /*
1034  * --------------------------------------------------------------------------
1035  *  Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1036  * --------------------------------------------------------------------------
1037  */
1038 static NTSTATUS
1039 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1040                        UINT32 *replyLen)
1041 {
1042     return HandleDpTransaction(usrParamsCtx, replyLen);
1043 }
1044
1045
1046 /*
1047  * --------------------------------------------------------------------------
1048  *  Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1049  * --------------------------------------------------------------------------
1050  */
1051 static NTSTATUS
1052 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1053                 UINT32 *replyLen)
1054 {
1055     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1056     POVS_OPEN_INSTANCE instance =
1057         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1058
1059     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1060         *replyLen = 0;
1061         OvsSetupDumpStart(usrParamsCtx);
1062     } else {
1063         NL_BUFFER nlBuf;
1064         NTSTATUS status;
1065         POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1066
1067         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1068
1069         if (instance->dumpState.ovsMsg == NULL) {
1070             ASSERT(FALSE);
1071             return STATUS_INVALID_DEVICE_STATE;
1072         }
1073
1074         /* Dump state must have been deleted after previous dump operation. */
1075         ASSERT(instance->dumpState.index[0] == 0);
1076         /* Output buffer has been validated while validating read dev op. */
1077         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1078
1079         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1080                   usrParamsCtx->outputLength);
1081
1082         OvsAcquireCtrlLock();
1083         if (!gOvsSwitchContext) {
1084             /* Treat this as a dump done. */
1085             OvsReleaseCtrlLock();
1086             *replyLen = 0;
1087             FreeUserDumpState(instance);
1088             return STATUS_SUCCESS;
1089         }
1090         status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1091         OvsReleaseCtrlLock();
1092
1093         if (status != STATUS_SUCCESS) {
1094             *replyLen = 0;
1095             FreeUserDumpState(instance);
1096             return status;
1097         }
1098
1099         /* Increment the dump index. */
1100         instance->dumpState.index[0] = 1;
1101         *replyLen = msgOut->nlMsg.nlmsgLen;
1102
1103         /* Free up the dump state, since there's no more data to continue. */
1104         FreeUserDumpState(instance);
1105     }
1106
1107     return STATUS_SUCCESS;
1108 }
1109
1110
1111 /*
1112  * --------------------------------------------------------------------------
1113  *  Command Handler for 'OVS_DP_CMD_SET'.
1114  * --------------------------------------------------------------------------
1115  */
1116 static NTSTATUS
1117 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1118                    UINT32 *replyLen)
1119 {
1120     return HandleDpTransaction(usrParamsCtx, replyLen);
1121 }
1122
1123 /*
1124  * --------------------------------------------------------------------------
1125  *  Function for handling transaction based 'OVS_DP_CMD_GET' and
1126  *  'OVS_DP_CMD_SET' commands.
1127  * --------------------------------------------------------------------------
1128  */
1129 static NTSTATUS
1130 HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1131                     UINT32 *replyLen)
1132 {
1133     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1134     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1135     NTSTATUS status = STATUS_SUCCESS;
1136     NL_BUFFER nlBuf;
1137     static const NL_POLICY ovsDatapathSetPolicy[] = {
1138         [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1139         [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1140         [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1141     };
1142     PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1143
1144     /* input buffer has been validated while validating write dev op. */
1145     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1146
1147     /* Parse any attributes in the request. */
1148     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1149         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1150                         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1151                         ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1152             return STATUS_INVALID_PARAMETER;
1153         }
1154
1155         /*
1156         * XXX: Not clear at this stage if there's any role for the
1157         * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1158         * from userspace.
1159         */
1160
1161     } else {
1162         RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1163     }
1164
1165     /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1166     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1167         return STATUS_NDIS_INVALID_LENGTH;
1168     }
1169     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1170
1171     OvsAcquireCtrlLock();
1172     if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1173         if (!gOvsSwitchContext &&
1174             !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1175                               OVS_SYSTEM_DP_NAME)) {
1176             OvsReleaseCtrlLock();
1177             status = STATUS_NOT_FOUND;
1178             goto cleanup;
1179         }
1180     } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1181         OvsReleaseCtrlLock();
1182         status = STATUS_NOT_FOUND;
1183         goto cleanup;
1184     }
1185
1186     status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1187     OvsReleaseCtrlLock();
1188
1189     *replyLen = NlBufSize(&nlBuf);
1190
1191 cleanup:
1192     return status;
1193 }
1194
1195
1196 static NTSTATUS
1197 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1198 {
1199     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1200     POVS_OPEN_INSTANCE instance =
1201         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1202
1203     /* input buffer has been validated while validating write dev op. */
1204     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1205
1206     /* A write operation that does not indicate dump start is invalid. */
1207     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1208         return STATUS_INVALID_PARAMETER;
1209     }
1210     /* XXX: Handle other NLM_F_* flags in the future. */
1211
1212     /*
1213      * This operation should be setting up the dump state. If there's any
1214      * previous state, clear it up so as to set it up afresh.
1215      */
1216     if (instance->dumpState.ovsMsg != NULL) {
1217         FreeUserDumpState(instance);
1218     }
1219
1220     return InitUserDumpState(instance, msgIn);
1221 }
1222
1223 static VOID
1224 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1225                      UINT32 length, UINT16 flags)
1226 {
1227     msgOut->nlMsg.nlmsgType = type;
1228     msgOut->nlMsg.nlmsgFlags = flags;
1229     msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1230     msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1231     msgOut->nlMsg.nlmsgLen = length;
1232
1233     msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1234     msgOut->genlMsg.version = nlDatapathFamilyOps.version;
1235     msgOut->genlMsg.reserved = 0;
1236 }
1237
1238 static VOID
1239 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1240 {
1241     BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1242                 flags);
1243 }
1244
1245 static VOID
1246 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1247 {
1248     BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1249                 sizeof(OVS_MESSAGE_ERROR), 0);
1250
1251     msgOut->errorMsg.error = errorCode;
1252     msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1253 }
1254
1255 static NTSTATUS
1256 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1257                       POVS_MESSAGE msgIn,
1258                       PVOID outBuffer,
1259                       UINT32 outBufLen,
1260                       int dpIfIndex)
1261 {
1262     NL_BUFFER nlBuffer;
1263     OVS_VPORT_FULL_STATS vportStats;
1264     BOOLEAN ok;
1265     OVS_MESSAGE msgOut;
1266     PNL_MSG_HDR nlMsg;
1267
1268     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1269
1270     BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1271     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1272
1273     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1274     if (!ok) {
1275         return STATUS_INSUFFICIENT_RESOURCES;
1276     }
1277
1278     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1279     if (!ok) {
1280         return STATUS_INSUFFICIENT_RESOURCES;
1281     }
1282
1283     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1284     if (!ok) {
1285         return STATUS_INSUFFICIENT_RESOURCES;
1286     }
1287
1288     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1289     if (!ok) {
1290         return STATUS_INSUFFICIENT_RESOURCES;
1291     }
1292
1293     /*
1294      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1295      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1296      * it means we have an array of pids, instead of a single pid.
1297      * ATM we assume we have one pid only.
1298     */
1299
1300     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1301                          vport->upcallPid);
1302     if (!ok) {
1303         return STATUS_INSUFFICIENT_RESOURCES;
1304     }
1305
1306     /*stats*/
1307     vportStats.rxPackets = vport->stats.rxPackets;
1308     vportStats.rxBytes = vport->stats.rxBytes;
1309     vportStats.txPackets = vport->stats.txPackets;
1310     vportStats.txBytes = vport->stats.txBytes;
1311     vportStats.rxErrors = vport->errStats.rxErrors;
1312     vportStats.txErrors = vport->errStats.txErrors;
1313     vportStats.rxDropped = vport->errStats.rxDropped;
1314     vportStats.txDropped = vport->errStats.txDropped;
1315
1316     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1317                             (PCHAR)&vportStats,
1318                             sizeof(OVS_VPORT_FULL_STATS));
1319     if (!ok) {
1320         return STATUS_INSUFFICIENT_RESOURCES;
1321     }
1322
1323     /*
1324      * XXX: when vxlan udp dest port becomes configurable, we will also need
1325      * to add vport options
1326     */
1327
1328     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1329     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1330
1331     return STATUS_SUCCESS;
1332 }
1333
1334 static NTSTATUS
1335 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1336                     UINT32 *replyLen)
1337 {
1338     POVS_MESSAGE msgIn;
1339     POVS_OPEN_INSTANCE instance =
1340         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1341     LOCK_STATE_EX lockState;
1342     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1343
1344     /*
1345      * XXX: this function shares some code with other dump command(s).
1346      * In the future, we will need to refactor the dump functions
1347     */
1348
1349     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1350
1351     if (instance->dumpState.ovsMsg == NULL) {
1352         ASSERT(FALSE);
1353         return STATUS_INVALID_DEVICE_STATE;
1354     }
1355
1356     /* Output buffer has been validated while validating read dev op. */
1357     ASSERT(usrParamsCtx->outputBuffer != NULL);
1358
1359     msgIn = instance->dumpState.ovsMsg;
1360
1361     OvsAcquireCtrlLock();
1362     if (!gOvsSwitchContext) {
1363         /* Treat this as a dump done. */
1364         OvsReleaseCtrlLock();
1365         *replyLen = 0;
1366         FreeUserDumpState(instance);
1367         return STATUS_SUCCESS;
1368     }
1369
1370     /*
1371      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1372      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1373      * it means we have an array of pids, instead of a single pid.
1374      * ATM we assume we have one pid only.
1375     */
1376
1377     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1378
1379     if (gOvsSwitchContext->numVports > 0) {
1380         /* inBucket: the bucket, used for lookup */
1381         UINT32 inBucket = instance->dumpState.index[0];
1382         /* inIndex: index within the given bucket, used for lookup */
1383         UINT32 inIndex = instance->dumpState.index[1];
1384         /* the bucket to be used for the next dump operation */
1385         UINT32 outBucket = 0;
1386         /* the index within the outBucket to be used for the next dump */
1387         UINT32 outIndex = 0;
1388
1389         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1390             PLIST_ENTRY head, link;
1391             head = &(gOvsSwitchContext->portHashArray[i]);
1392             POVS_VPORT_ENTRY vport = NULL;
1393
1394             outIndex = 0;
1395             LIST_FORALL(head, link) {
1396
1397                 /*
1398                  * if one or more dumps were previously done on this same bucket,
1399                  * inIndex will be > 0, so we'll need to reply with the
1400                  * inIndex + 1 vport from the bucket.
1401                 */
1402                 if (outIndex >= inIndex) {
1403                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1404
1405                     if (vport->portNo != 0) {
1406                         OvsCreateMsgFromVport(vport, msgIn,
1407                                               usrParamsCtx->outputBuffer,
1408                                               usrParamsCtx->outputLength,
1409                                               gOvsSwitchContext->dpNo);
1410                         ++outIndex;
1411                         break;
1412                     } else {
1413                         vport = NULL;
1414                     }
1415                 }
1416
1417                 ++outIndex;
1418             }
1419
1420             if (vport) {
1421                 break;
1422             }
1423
1424             /*
1425              * if no vport was found above, check the next bucket, beginning
1426              * with the first (i.e. index 0) elem from within that bucket
1427             */
1428             inIndex = 0;
1429         }
1430
1431         outBucket = i;
1432
1433         /* XXX: what about NLMSG_DONE (as msg type)? */
1434         instance->dumpState.index[0] = outBucket;
1435         instance->dumpState.index[1] = outIndex;
1436     }
1437
1438     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1439
1440     OvsReleaseCtrlLock();
1441
1442     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1443     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1444         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1445         *replyLen = msgOut->nlMsg.nlmsgLen;
1446     } else {
1447         /*
1448          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1449          * it's dump done
1450          */
1451         *replyLen = 0;
1452         /* Free up the dump state, since there's no more data to continue. */
1453         FreeUserDumpState(instance);
1454     }
1455
1456     return STATUS_SUCCESS;
1457 }
1458
1459 static NTSTATUS
1460 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1461             UINT32 *replyLen)
1462 {
1463     NTSTATUS status = STATUS_SUCCESS;
1464     LOCK_STATE_EX lockState;
1465
1466     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1467     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1468     POVS_VPORT_ENTRY vport = NULL;
1469     NL_ERROR nlError = NL_ERROR_SUCCESS;
1470
1471     static const NL_POLICY ovsVportPolicy[] = {
1472         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1473         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1474                                   .minLen = 2,
1475                                   .maxLen = IFNAMSIZ,
1476                                   .optional = TRUE},
1477     };
1478     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1479
1480     /* input buffer has been validated while validating write dev op. */
1481     ASSERT(usrParamsCtx->inputBuffer != NULL);
1482
1483     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1484         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1485         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1486         return STATUS_INVALID_PARAMETER;
1487     }
1488
1489     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1490         return STATUS_INVALID_BUFFER_SIZE;
1491     }
1492
1493     OvsAcquireCtrlLock();
1494     if (!gOvsSwitchContext) {
1495         OvsReleaseCtrlLock();
1496         return STATUS_INVALID_PARAMETER;
1497     }
1498     OvsReleaseCtrlLock();
1499
1500     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1501         vport = OvsFindVportByOvsName(gOvsSwitchContext,
1502             NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1503             NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1504     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1505         vport = OvsFindVportByPortNo(gOvsSwitchContext,
1506             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1507     } else {
1508         nlError = NL_ERROR_INVAL;
1509         goto Cleanup;
1510     }
1511
1512     if (!vport) {
1513         nlError = NL_ERROR_NODEV;
1514         goto Cleanup;
1515     }
1516
1517     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1518     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1519                                    usrParamsCtx->outputLength,
1520                                    gOvsSwitchContext->dpNo);
1521     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1522
1523     *replyLen = msgOut->nlMsg.nlmsgLen;
1524
1525 Cleanup:
1526     if (nlError != NL_ERROR_SUCCESS) {
1527         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1528             usrParamsCtx->outputBuffer;
1529
1530         BuildErrorMsg(msgIn, msgError, nlError);
1531         *replyLen = msgError->nlMsg.nlmsgLen;
1532     }
1533
1534     return STATUS_SUCCESS;
1535 }
1536
1537 /*
1538  * --------------------------------------------------------------------------
1539  *  Handler for the get vport command. The function handles the initial call to
1540  *  setup the dump state, as well as subsequent calls to continue dumping data.
1541  * --------------------------------------------------------------------------
1542 */
1543 static NTSTATUS
1544 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1545                       UINT32 *replyLen)
1546 {
1547     *replyLen = 0;
1548
1549     switch (usrParamsCtx->devOp)
1550     {
1551     case OVS_WRITE_DEV_OP:
1552         return OvsSetupDumpStart(usrParamsCtx);
1553
1554     case OVS_READ_DEV_OP:
1555         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1556
1557     case OVS_TRANSACTION_DEV_OP:
1558         return OvsGetVport(usrParamsCtx, replyLen);
1559
1560     default:
1561         return STATUS_INVALID_DEVICE_REQUEST;
1562     }
1563
1564 }
1565
1566 /*
1567  * --------------------------------------------------------------------------
1568  *  Utility function to map the output buffer in an IRP. The buffer is assumed
1569  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1570  * --------------------------------------------------------------------------
1571  */
1572 static NTSTATUS
1573 MapIrpOutputBuffer(PIRP irp,
1574                    UINT32 bufferLength,
1575                    UINT32 requiredLength,
1576                    PVOID *buffer)
1577 {
1578     ASSERT(irp);
1579     ASSERT(buffer);
1580     ASSERT(bufferLength);
1581     ASSERT(requiredLength);
1582     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1583         return STATUS_INVALID_PARAMETER;
1584     }
1585
1586     if (bufferLength < requiredLength) {
1587         return STATUS_NDIS_INVALID_LENGTH;
1588     }
1589     if (irp->MdlAddress == NULL) {
1590         return STATUS_INVALID_PARAMETER;
1591     }
1592     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1593                                            NormalPagePriority);
1594     if (*buffer == NULL) {
1595         return STATUS_INSUFFICIENT_RESOURCES;
1596     }
1597
1598     return STATUS_SUCCESS;
1599 }
1600
1601 /*
1602  * --------------------------------------------------------------------------
1603  * Utility function to fill up information about the state of a port in a reply
1604  * to* userspace.
1605  * Assumes that 'gOvsCtrlLock' lock is acquired.
1606  * --------------------------------------------------------------------------
1607  */
1608 static NTSTATUS
1609 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1610                 POVS_EVENT_ENTRY eventEntry,
1611                 PNL_BUFFER nlBuf)
1612 {
1613     NTSTATUS status;
1614     BOOLEAN rc;
1615     OVS_MESSAGE msgOutTmp;
1616     PNL_MSG_HDR nlMsg;
1617     POVS_VPORT_ENTRY vport;
1618
1619     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1620
1621     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1622     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
1623
1624     /* driver intiated messages should have zerp seq number*/
1625     msgOutTmp.nlMsg.nlmsgSeq = 0;
1626     msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1627
1628     msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1629     msgOutTmp.genlMsg.reserved = 0;
1630
1631     /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1632     if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1633         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1634     } else if (eventEntry->status &
1635              (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1636         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1637     } else {
1638         ASSERT(FALSE);
1639         return STATUS_UNSUCCESSFUL;
1640     }
1641     msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1642
1643     rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1644     if (!rc) {
1645         status = STATUS_INVALID_BUFFER_SIZE;
1646         goto cleanup;
1647     }
1648
1649     vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1650     if (!vport) {
1651         status = STATUS_DEVICE_DOES_NOT_EXIST;
1652         goto cleanup;
1653     }
1654
1655     rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1656          NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1657          NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1658     if (!rc) {
1659         status = STATUS_INVALID_BUFFER_SIZE;
1660         goto cleanup;
1661     }
1662
1663     /* XXXX Should we add the port stats attributes?*/
1664     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1665     nlMsg->nlmsgLen = NlBufSize(nlBuf);
1666     status = STATUS_SUCCESS;
1667
1668 cleanup:
1669     return status;
1670 }
1671
1672
1673 /*
1674  * --------------------------------------------------------------------------
1675  * Handler for reading events from the driver event queue. This handler is
1676  * executed when user modes issues a socket receive on a socket assocaited
1677  * with the MC group for events.
1678  * XXX user mode should read multiple events in one system call
1679  * --------------------------------------------------------------------------
1680  */
1681 static NTSTATUS
1682 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1683                        UINT32 *replyLen)
1684 {
1685 #ifdef DBG
1686     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1687     POVS_OPEN_INSTANCE instance =
1688         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1689 #endif
1690     NL_BUFFER nlBuf;
1691     NTSTATUS status;
1692     OVS_EVENT_ENTRY eventEntry;
1693
1694     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1695
1696     /* Should never read events with a dump socket */
1697     ASSERT(instance->dumpState.ovsMsg == NULL);
1698
1699     /* Must have an event queue */
1700     ASSERT(instance->eventQueue != NULL);
1701
1702     /* Output buffer has been validated while validating read dev op. */
1703     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1704
1705     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1706
1707     OvsAcquireCtrlLock();
1708     if (!gOvsSwitchContext) {
1709         status = STATUS_SUCCESS;
1710         goto cleanup;
1711     }
1712
1713     /* remove an event entry from the event queue */
1714     status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1715     if (status != STATUS_SUCCESS) {
1716         goto cleanup;
1717     }
1718
1719     status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1720     if (status == NDIS_STATUS_SUCCESS) {
1721         *replyLen = NlBufSize(&nlBuf);
1722     }
1723
1724 cleanup:
1725     OvsReleaseCtrlLock();
1726     return status;
1727 }
1728 #endif /* OVS_USE_NL_INTERFACE */