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