1
0

data.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. data.c
  5. Abstract:
  6. This module implements data frame handling functionality for the 802.11
  7. core wireless networking library.
  8. Author:
  9. Chris Stevens 20-Oct-2015
  10. Environment:
  11. Kernel
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include "net80211.h"
  17. //
  18. // ---------------------------------------------------------------- Definitions
  19. //
  20. //
  21. // ------------------------------------------------------ Data Type Definitions
  22. //
  23. //
  24. // ----------------------------------------------- Internal Function Prototypes
  25. //
  26. KSTATUS
  27. Net80211pSendNullDataFrame (
  28. PNET80211_LINK Link,
  29. USHORT FrameControl
  30. );
  31. //
  32. // -------------------------------------------------------------------- Globals
  33. //
  34. //
  35. // ------------------------------------------------------------------ Functions
  36. //
  37. KSTATUS
  38. Net80211pSendDataFrames (
  39. PNET80211_LINK Link,
  40. PNET_PACKET_LIST PacketList,
  41. PNETWORK_ADDRESS SourcePhysicalAddress,
  42. PNETWORK_ADDRESS DestinationPhysicalAddress,
  43. ULONG ProtocolNumber
  44. )
  45. /*++
  46. Routine Description:
  47. This routine adds 802.2 SAP headers and 802.11 data frame headers to the
  48. given packets and sends them down the the device link layer.
  49. Arguments:
  50. Link - Supplies a pointer to the 802.11 link on which to send the data.
  51. PacketList - Supplies a pointer to a list of network packets to send. Data
  52. in these packets may be modified by this routine, but must not be used
  53. once this routine returns.
  54. SourcePhysicalAddress - Supplies a pointer to the source (local) physical
  55. network address.
  56. DestinationPhysicalAddress - Supplies the optional physical address of the
  57. destination, or at least the next hop. If NULL is provided, then the
  58. packets will be sent to the data link layer's broadcast address.
  59. ProtocolNumber - Supplies the protocol number of the data inside the data
  60. link header.
  61. Return Value:
  62. Status code.
  63. --*/
  64. {
  65. PNET80211_BSS_ENTRY Bss;
  66. PLIST_ENTRY CurrentEntry;
  67. BOOL DataPaused;
  68. PNET80211_DATA_FRAME_HEADER Header;
  69. ULONG Index;
  70. PNET8022_LLC_HEADER LlcHeader;
  71. PNET_PACKET_BUFFER Packet;
  72. NET_PACKET_LIST PausedPacketList;
  73. PNET_DEVICE_LINK_SEND Send;
  74. PNET8022_SNAP_EXTENSION SnapExtension;
  75. KSTATUS Status;
  76. //
  77. // Get the active BSS in order to determine the correct receiver address
  78. // and whether or not the data needs to be encrypted.
  79. //
  80. Bss = Net80211pGetBss(Link);
  81. if (Bss == NULL) {
  82. return STATUS_NOT_CONNECTED;
  83. }
  84. //
  85. // Determine if transmission is paused. If it is, then the BSS may no
  86. // longer be active. Fill out as much of the headers as possible and queue
  87. // the packets for later.
  88. //
  89. DataPaused = FALSE;
  90. if ((Link->Flags & NET80211_LINK_FLAG_DATA_PAUSED) != 0) {
  91. DataPaused = TRUE;
  92. NET_INITIALIZE_PACKET_LIST(&PausedPacketList);
  93. }
  94. //
  95. // Fill out the 802.11 headers for these data frames.
  96. //
  97. Status = STATUS_SUCCESS;
  98. CurrentEntry = PacketList->Head.Next;
  99. while (CurrentEntry != &(PacketList->Head)) {
  100. Packet = LIST_VALUE(CurrentEntry, NET_PACKET_BUFFER, ListEntry);
  101. CurrentEntry = CurrentEntry->Next;
  102. //
  103. // Add the 802.2 headers.
  104. //
  105. Packet->DataOffset -= sizeof(NET8022_SNAP_EXTENSION);
  106. SnapExtension = Packet->Buffer + Packet->DataOffset;
  107. RtlZeroMemory(SnapExtension, sizeof(NET8022_SNAP_EXTENSION));
  108. SnapExtension->EthernetType = CPU_TO_NETWORK16((USHORT)ProtocolNumber);
  109. Packet->DataOffset -= sizeof(NET8022_LLC_HEADER);
  110. LlcHeader = Packet->Buffer + Packet->DataOffset;
  111. LlcHeader->DestinationSapAddress = NET8022_SAP_ADDRESS_SNAP_EXTENSION;
  112. LlcHeader->SourceSapAddress = NET8022_SAP_ADDRESS_SNAP_EXTENSION;
  113. LlcHeader->Control = (NET8022_CONTROL_TYPE_UNNUMBERED <<
  114. NET8022_CONTROL_TYPE_SHIFT);
  115. //
  116. // Add 802.11 headers.
  117. //
  118. Packet->DataOffset -= sizeof(NET80211_DATA_FRAME_HEADER);
  119. Header = Packet->Buffer + Packet->DataOffset;
  120. Header->FrameControl = (NET80211_FRAME_CONTROL_PROTOCOL_VERSION <<
  121. NET80211_FRAME_CONTROL_PROTOCOL_VERSION_SHIFT) |
  122. (NET80211_FRAME_TYPE_DATA <<
  123. NET80211_FRAME_CONTROL_TYPE_SHIFT) |
  124. (NET80211_DATA_FRAME_SUBTYPE_DATA <<
  125. NET80211_FRAME_CONTROL_SUBTYPE_SHIFT);
  126. //
  127. // The hardware handles the duration.
  128. //
  129. Header->DurationId = 0;
  130. //
  131. // As the 802.11 core only supports operating in station mode at the
  132. // moment, assume all packets are going out to the DS. As a result, the
  133. // receive address is set to the AP's MAC address (i.e. the BSSID) and
  134. // the real destination is set in the header's third address.
  135. //
  136. Header->FrameControl |= NET80211_FRAME_CONTROL_TO_DS;
  137. if (DestinationPhysicalAddress != NULL) {
  138. RtlCopyMemory(Header->SourceDestinationAddress,
  139. DestinationPhysicalAddress->Address,
  140. NET80211_ADDRESS_SIZE);
  141. } else {
  142. for (Index = 0; Index < NET80211_ADDRESS_SIZE; Index += 1) {
  143. Header->SourceDestinationAddress[Index] = 0xFF;
  144. }
  145. }
  146. RtlCopyMemory(Header->TransmitterAddress,
  147. SourcePhysicalAddress->Address,
  148. NET80211_ADDRESS_SIZE);
  149. //
  150. // If data transmission is paused and this packet should not be forced
  151. // down to the driver, add it to the local list of packets to send
  152. // later. Do not fill out any BSS specific information or the sequence
  153. // number. The BSS may change by the time data transmission is resumed.
  154. //
  155. if ((DataPaused != FALSE) &&
  156. ((Packet->Flags & NET_PACKET_FLAG_FORCE_TRANSMIT) == 0)) {
  157. NET_REMOVE_PACKET_FROM_LIST(Packet, PacketList);
  158. NET_ADD_PACKET_TO_LIST(Packet, &PausedPacketList);
  159. continue;
  160. }
  161. Header->SequenceControl = Net80211pGetSequenceNumber(Link);
  162. Header->SequenceControl <<=
  163. NET80211_SEQUENCE_CONTROL_SEQUENCE_NUMBER_SHIFT;
  164. RtlCopyMemory(Header->ReceiverAddress,
  165. Bss->State.Bssid,
  166. NET80211_ADDRESS_SIZE);
  167. //
  168. // Only encrypt the packet if transmission is not paused. If it is
  169. // paused then this station may be in the middle of acquiring new keys
  170. // for the BSS.
  171. //
  172. if (((Bss->Flags & NET80211_BSS_FLAG_ENCRYPT_DATA) != 0) &&
  173. ((Packet->Flags & NET_PACKET_FLAG_UNENCRYPTED) == 0)) {
  174. Header->FrameControl |= NET80211_FRAME_CONTROL_PROTECTED_FRAME;
  175. Net80211pEncryptPacket(Link, Bss, Packet);
  176. }
  177. }
  178. //
  179. // If any packets were added to the local paused list, then add them to the
  180. // link's list.
  181. //
  182. if ((DataPaused != FALSE) &&
  183. (NET_PACKET_LIST_EMPTY(&PausedPacketList) == FALSE)) {
  184. KeAcquireQueuedLock(Link->Lock);
  185. NET_APPEND_PACKET_LIST(&PausedPacketList,
  186. &(Link->PausedPacketList));
  187. KeReleaseQueuedLock(Link->Lock);
  188. }
  189. //
  190. // Send any remaining packets down to the physical device layer.
  191. //
  192. if (NET_PACKET_LIST_EMPTY(PacketList) == FALSE) {
  193. Send = Link->Properties.Interface.Send;
  194. Status = Send(Link->Properties.DeviceContext, PacketList);
  195. //
  196. // If the link layer returns that the resource is in use it means it
  197. // was too busy to send all of the packets. Release the packets for it
  198. // and convert this into a success status.
  199. //
  200. if (Status == STATUS_RESOURCE_IN_USE) {
  201. NetDestroyBufferList(PacketList);
  202. Status = STATUS_SUCCESS;
  203. }
  204. }
  205. Net80211pBssEntryReleaseReference(Bss);
  206. return Status;
  207. }
  208. VOID
  209. Net80211pProcessDataFrame (
  210. PNET80211_LINK Link,
  211. PNET_PACKET_BUFFER Packet
  212. )
  213. /*++
  214. Routine Description:
  215. This routine processes an 802.11 data frame.
  216. Arguments:
  217. Link - Supplies a pointer to the 802.11 link on which the frame arrived.
  218. Packet - Supplies a pointer to the network packet.
  219. Return Value:
  220. None.
  221. --*/
  222. {
  223. PNET80211_BSS_ENTRY Bss;
  224. ULONG BytesRemaining;
  225. UCHAR DestinationSap;
  226. PNET80211_DATA_FRAME_HEADER Header;
  227. PNET8022_LLC_HEADER LlcHeader;
  228. PNET_NETWORK_ENTRY NetworkEntry;
  229. ULONG NetworkProtocol;
  230. PNET8022_SNAP_EXTENSION SnapExtension;
  231. UCHAR SourceSap;
  232. KSTATUS Status;
  233. //
  234. // Make sure there are at least enough bytes for a data frame header.
  235. //
  236. BytesRemaining = Packet->FooterOffset - Packet->DataOffset;
  237. if (BytesRemaining < sizeof(NET80211_DATA_FRAME_HEADER)) {
  238. RtlDebugPrint("802.2: malformed data packet missing bytes for data "
  239. "frame header. Expected %d bytes, has %d.\n",
  240. sizeof(NET80211_DATA_FRAME_HEADER),
  241. BytesRemaining);
  242. return;
  243. }
  244. //
  245. // If the packet is protected, then decrypt it. The decryption leaves the
  246. // packet's data offset at the start of the decrypted ciphertext.
  247. //
  248. Header = Packet->Buffer + Packet->DataOffset;
  249. if ((Header->FrameControl & NET80211_FRAME_CONTROL_PROTECTED_FRAME) != 0) {
  250. Bss = Net80211pGetBss(Link);
  251. if (Bss == NULL) {
  252. return;
  253. }
  254. Status = Net80211pDecryptPacket(Link, Bss, Packet);
  255. Net80211pBssEntryReleaseReference(Bss);
  256. if (!KSUCCESS(Status)) {
  257. return;
  258. }
  259. //
  260. // Otherwise remove the 802.11 header. It should always be the same size as
  261. // this node does not handle QoS at the moment and is only expecting '
  262. // traffic from the DS.
  263. //
  264. } else {
  265. Packet->DataOffset += sizeof(NET80211_DATA_FRAME_HEADER);
  266. }
  267. //
  268. // Reject packets that do not have enough room for the LLC header.
  269. //
  270. BytesRemaining = Packet->FooterOffset - Packet->DataOffset;
  271. if (BytesRemaining < sizeof(NET8022_LLC_HEADER)) {
  272. RtlDebugPrint("802.2: malformed data packet missing bytes for LLC "
  273. "header. Expected %d bytes, has %d.\n",
  274. sizeof(NET8022_LLC_HEADER),
  275. BytesRemaining);
  276. return;
  277. }
  278. //
  279. // Check the LLC header to look for the SNAP extension and unnumbered
  280. // control type. The 802.11 core does not handle any other packet types.
  281. //
  282. LlcHeader = Packet->Buffer + Packet->DataOffset;
  283. DestinationSap = LlcHeader->DestinationSapAddress;
  284. SourceSap = LlcHeader->SourceSapAddress;
  285. if ((DestinationSap != NET8022_SAP_ADDRESS_SNAP_EXTENSION) ||
  286. (SourceSap != NET8022_SAP_ADDRESS_SNAP_EXTENSION)) {
  287. return;
  288. }
  289. if ((LlcHeader->Control & NET8022_CONTROL_TYPE_MASK) !=
  290. NET8022_CONTROL_TYPE_UNNUMBERED) {
  291. return;
  292. }
  293. Packet->DataOffset += sizeof(NET8022_LLC_HEADER);
  294. //
  295. // Reject packets that do not have enough room for the SNAP extension.
  296. //
  297. BytesRemaining = Packet->FooterOffset - Packet->DataOffset;
  298. if (BytesRemaining < sizeof(NET8022_SNAP_EXTENSION)) {
  299. RtlDebugPrint("802.2: malformed data packet missing bytes for SNAP "
  300. "extension. Expected %d bytes, has %d.\n",
  301. sizeof(NET8022_SNAP_EXTENSION),
  302. BytesRemaining);
  303. return;
  304. }
  305. //
  306. // Get the network protocol out of the SNAP extension.
  307. //
  308. SnapExtension = Packet->Buffer + Packet->DataOffset;
  309. NetworkProtocol = NETWORK_TO_CPU16(SnapExtension->EthernetType);
  310. //
  311. // Get the network layer to deal with this.
  312. //
  313. NetworkEntry = NetGetNetworkEntry(NetworkProtocol);
  314. if (NetworkEntry == NULL) {
  315. RtlDebugPrint("Unknown protocol number 0x%x found in 802.2 header.\n",
  316. NetworkProtocol);
  317. return;
  318. }
  319. Packet->DataOffset += sizeof(NET8022_SNAP_EXTENSION);
  320. NetworkEntry->Interface.ProcessReceivedData(Link->NetworkLink, Packet);
  321. return;
  322. }
  323. VOID
  324. Net80211pPauseDataFrames (
  325. PNET80211_LINK Link
  326. )
  327. /*++
  328. Routine Description:
  329. This routine pauses the outgoing data frame traffic on the given network
  330. link. It assumes that the 802.11 link's queued lock is held.
  331. Arguments:
  332. Link - Supplies a pointer to the 802.11 link on which to pause the outgoing
  333. data frames.
  334. Return Value:
  335. None.
  336. --*/
  337. {
  338. ASSERT(KeIsQueuedLockHeld(Link->Lock) != FALSE);
  339. //
  340. // If associated, send a power save null data frame to the AP in order to
  341. // pause all incoming data traffic.
  342. //
  343. if (Link->ActiveBss != NULL) {
  344. Net80211pSendNullDataFrame(Link,
  345. NET80211_FRAME_CONTROL_POWER_MANAGEMENT);
  346. }
  347. Link->Flags |= NET80211_LINK_FLAG_DATA_PAUSED;
  348. return;
  349. }
  350. VOID
  351. Net80211pResumeDataFrames (
  352. PNET80211_LINK Link
  353. )
  354. /*++
  355. Routine Description:
  356. This routine resumes the outgoing data frame traffic on the given network
  357. link, flushing any packets that were held while the link was paused. It
  358. assumes that the 802.11 link's queued lock is held.
  359. Arguments:
  360. Link - Supplies a pointer to the 802.11 link on which to resume the
  361. outgoing data frames.
  362. Return Value:
  363. None.
  364. --*/
  365. {
  366. PNET80211_BSS_ENTRY Bss;
  367. PLIST_ENTRY CurrentEntry;
  368. PNET80211_DATA_FRAME_HEADER Header;
  369. PNET_PACKET_BUFFER Packet;
  370. NET_PACKET_LIST PacketList;
  371. PNET_DEVICE_LINK_SEND Send;
  372. KSTATUS Status;
  373. ASSERT(KeIsQueuedLockHeld(Link->Lock) != FALSE);
  374. //
  375. // There is nothing to be done if the data frames were not paused.
  376. //
  377. if ((Link->Flags & NET80211_LINK_FLAG_DATA_PAUSED) == 0) {
  378. return;
  379. }
  380. //
  381. // If the link is associated, then send the AP a null data frame indicating
  382. // that the station is coming out of power save mode.
  383. //
  384. if (Link->ActiveBss != NULL) {
  385. Net80211pSendNullDataFrame(Link, 0);
  386. }
  387. //
  388. // Attempt to flush the packets that were queued up.
  389. //
  390. Link->Flags &= ~NET80211_LINK_FLAG_DATA_PAUSED;
  391. if (NET_PACKET_LIST_EMPTY(&(Link->PausedPacketList)) != FALSE) {
  392. return;
  393. }
  394. NET_INITIALIZE_PACKET_LIST(&PacketList);
  395. NET_APPEND_PACKET_LIST(&(Link->PausedPacketList), &PacketList);
  396. //
  397. // With the link lock held, just use the active BSS to fill out and encrypt
  398. // the queued packets.
  399. //
  400. Bss = Link->ActiveBss;
  401. ASSERT(Bss != NULL);
  402. CurrentEntry = PacketList.Head.Next;
  403. while (CurrentEntry != &(PacketList.Head)) {
  404. Packet = LIST_VALUE(CurrentEntry, NET_PACKET_BUFFER, ListEntry);
  405. CurrentEntry = CurrentEntry->Next;
  406. Header = Packet->Buffer + Packet->DataOffset;
  407. RtlCopyMemory(Header->ReceiverAddress,
  408. Bss->State.Bssid,
  409. NET80211_ADDRESS_SIZE);
  410. Header->SequenceControl = Net80211pGetSequenceNumber(Link);
  411. Header->SequenceControl <<=
  412. NET80211_SEQUENCE_CONTROL_SEQUENCE_NUMBER_SHIFT;
  413. if (((Bss->Flags & NET80211_BSS_FLAG_ENCRYPT_DATA) != 0) &&
  414. ((Packet->Flags & NET_PACKET_FLAG_UNENCRYPTED) == 0)) {
  415. Header->FrameControl |= NET80211_FRAME_CONTROL_PROTECTED_FRAME;
  416. Net80211pEncryptPacket(Link, Bss, Packet);
  417. }
  418. }
  419. Send = Link->Properties.Interface.Send;
  420. Status = Send(Link->Properties.DeviceContext, &PacketList);
  421. if (Status == STATUS_RESOURCE_IN_USE) {
  422. NetDestroyBufferList(&PacketList);
  423. }
  424. return;
  425. }
  426. //
  427. // --------------------------------------------------------- Internal Functions
  428. //
  429. KSTATUS
  430. Net80211pSendNullDataFrame (
  431. PNET80211_LINK Link,
  432. USHORT FrameControl
  433. )
  434. /*++
  435. Routine Description:
  436. This routine sends an 802.11 null data frame with the given frame control
  437. bits set. This bypasses the normal data frame submission paths because it
  438. never requires encryption and does not require the 802.2 headers.
  439. Arguments:
  440. Link - Supplies a pointer to the link on which to send the data frame.
  441. FrameControl - Supplies a bitmask of extra frame control values to stick
  442. in the header.
  443. Return Value:
  444. Status code.
  445. --*/
  446. {
  447. PVOID DeviceContext;
  448. ULONG Flags;
  449. PNET80211_DATA_FRAME_HEADER Header;
  450. PNET_PACKET_BUFFER Packet;
  451. NET_PACKET_LIST PacketList;
  452. KSTATUS Status;
  453. NET_INITIALIZE_PACKET_LIST(&PacketList);
  454. //
  455. // Allocate a network packet to send down to the lower layers.
  456. //
  457. Flags = NET_ALLOCATE_BUFFER_FLAG_ADD_DEVICE_LINK_HEADERS |
  458. NET_ALLOCATE_BUFFER_FLAG_ADD_DEVICE_LINK_FOOTERS;
  459. Packet = NULL;
  460. Status = NetAllocateBuffer(sizeof(NET80211_DATA_FRAME_HEADER),
  461. 0,
  462. 0,
  463. Link->NetworkLink,
  464. Flags,
  465. &Packet);
  466. if (!KSUCCESS(Status)) {
  467. goto SendNullDataFrame;
  468. }
  469. //
  470. // Move the offset backwards and fill in the 802.11 management frame header.
  471. //
  472. Packet->DataOffset -= sizeof(NET80211_DATA_FRAME_HEADER);
  473. Header = Packet->Buffer + Packet->DataOffset;
  474. FrameControl &= ~NET80211_FRAME_CONTROL_FROM_DS;
  475. FrameControl |= NET80211_FRAME_CONTROL_TO_DS |
  476. (NET80211_FRAME_CONTROL_PROTOCOL_VERSION <<
  477. NET80211_FRAME_CONTROL_PROTOCOL_VERSION_SHIFT) |
  478. (NET80211_FRAME_TYPE_DATA <<
  479. NET80211_FRAME_CONTROL_TYPE_SHIFT) |
  480. (NET80211_DATA_FRAME_SUBTYPE_NO_DATA <<
  481. NET80211_FRAME_CONTROL_SUBTYPE_SHIFT);
  482. Header->FrameControl = FrameControl;
  483. //
  484. // The hardware handles the duration.
  485. //
  486. Header->DurationId = 0;
  487. //
  488. // Initialize the header's addresses. The receiver and destination address
  489. // are always the BSSID as this is being sent to the AP.
  490. //
  491. ASSERT(Link->ActiveBss != NULL);
  492. RtlCopyMemory(Header->ReceiverAddress,
  493. Link->ActiveBss->State.Bssid,
  494. NET80211_ADDRESS_SIZE);
  495. //
  496. // The source address is always the local link's physical address (i.e. the
  497. // MAC address).
  498. //
  499. RtlCopyMemory(Header->TransmitterAddress,
  500. Link->Properties.PhysicalAddress.Address,
  501. NET80211_ADDRESS_SIZE);
  502. RtlCopyMemory(Header->SourceDestinationAddress,
  503. Link->ActiveBss->State.Bssid,
  504. NET80211_ADDRESS_SIZE);
  505. //
  506. // The header gets the next sequence number for the link. This is only 1
  507. // fragment, so that remains 0.
  508. //
  509. Header->SequenceControl = Net80211pGetSequenceNumber(Link);
  510. Header->SequenceControl <<= NET80211_SEQUENCE_CONTROL_SEQUENCE_NUMBER_SHIFT;
  511. //
  512. // Send the packet off.
  513. //
  514. NET_ADD_PACKET_TO_LIST(Packet, &PacketList);
  515. DeviceContext = Link->Properties.DeviceContext;
  516. Status = Link->Properties.Interface.Send(DeviceContext, &PacketList);
  517. if (!KSUCCESS(Status)) {
  518. goto SendNullDataFrame;
  519. }
  520. SendNullDataFrame:
  521. if (!KSUCCESS(Status)) {
  522. NetDestroyBufferList(&PacketList);
  523. }
  524. return Status;
  525. }