2 * nsiproxy.sys ndis module
4 * Copyright 2003, 2006, 2011 Juan Lang
5 * Copyright 2021 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
37 #ifdef HAVE_NET_IF_ARP_H
38 #include <net/if_arp.h>
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
45 #ifdef HAVE_NETINET_IF_ETHER_H
46 #include <netinet/if_ether.h>
49 #ifdef HAVE_NET_ROUTE_H
50 #include <net/route.h>
53 #ifdef HAVE_SYS_SYSCTL_H
54 #include <sys/sysctl.h>
57 #ifdef HAVE_NET_IF_DL_H
58 #include <net/if_dl.h>
61 #ifdef HAVE_NET_IF_TYPES_H
62 #include <net/if_types.h>
65 #ifdef HAVE_LINUX_WIRELESS_H
66 #include <linux/wireless.h>
72 #define WIN32_NO_STATUS
85 #include "wine/list.h"
86 #include "wine/debug.h"
87 #include "wine/unixlib.h"
89 #include "unix_private.h"
91 WINE_DEFAULT_DEBUG_CHANNEL(nsi
);
99 char if_unix_name
[IFNAMSIZ
];
100 IF_PHYSICAL_ADDRESS if_phys_addr
;
105 static struct list if_list
= LIST_INIT( if_list
);
106 static pthread_mutex_t if_list_lock
= PTHREAD_MUTEX_INITIALIZER
;
108 static struct if_entry
*find_entry_from_index( UINT index
)
110 struct if_entry
*entry
;
112 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
113 if (entry
->if_index
== index
) return entry
;
118 static struct if_entry
*find_entry_from_luid( const NET_LUID
*luid
)
120 struct if_entry
*entry
;
122 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
123 if (entry
->if_luid
.Value
== luid
->Value
) return entry
;
128 #if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR)
129 static NTSTATUS
if_get_physical( const char *name
, UINT
*type
, IF_PHYSICAL_ADDRESS
*phys_addr
)
133 NTSTATUS ret
= STATUS_SUCCESS
;
134 static const struct type_lookup
136 unsigned short ifi_type
;
141 { ARPHRD_LOOPBACK
, MIB_IF_TYPE_LOOPBACK
, 0 },
142 { ARPHRD_ETHER
, MIB_IF_TYPE_ETHERNET
, ETH_ALEN
},
143 { ARPHRD_FDDI
, MIB_IF_TYPE_FDDI
, ETH_ALEN
},
144 { ARPHRD_IEEE802
, MIB_IF_TYPE_TOKENRING
, ETH_ALEN
},
145 { ARPHRD_IEEE802_TR
, MIB_IF_TYPE_TOKENRING
, ETH_ALEN
},
146 { ARPHRD_SLIP
, MIB_IF_TYPE_SLIP
, 0 },
147 { ARPHRD_PPP
, MIB_IF_TYPE_PPP
, 0 }
150 *type
= MIB_IF_TYPE_OTHER
;
151 memset( phys_addr
, 0, sizeof(*phys_addr
) );
153 size
= strlen( name
) + 1;
154 if (size
> sizeof(ifr
.ifr_name
)) return STATUS_NAME_TOO_LONG
;
155 memset( &ifr
, 0, sizeof(ifr
) );
156 memcpy( ifr
.ifr_name
, name
, size
);
158 fd
= socket( PF_INET
, SOCK_DGRAM
, 0 );
159 if (fd
== -1) return STATUS_TOO_MANY_OPENED_FILES
;
161 if (ioctl( fd
, SIOCGIFHWADDR
, &ifr
))
163 ret
= STATUS_DEVICE_DATA_ERROR
;
167 for (i
= 0; i
< ARRAY_SIZE(types
); i
++)
168 if (ifr
.ifr_hwaddr
.sa_family
== types
[i
].ifi_type
)
170 *type
= types
[i
].mib_type
;
171 phys_addr
->Length
= types
[i
].addr_len
;
172 memcpy( phys_addr
->Address
, ifr
.ifr_hwaddr
.sa_data
, phys_addr
->Length
);
176 if (*type
== MIB_IF_TYPE_OTHER
&& !ioctl( fd
, SIOCGIFFLAGS
, &ifr
) && ifr
.ifr_flags
& IFF_POINTOPOINT
)
177 *type
= MIB_IF_TYPE_PPP
;
179 #ifdef HAVE_LINUX_WIRELESS_H
180 if (*type
== MIB_IF_TYPE_ETHERNET
)
184 memset( &pwrq
, 0, sizeof(pwrq
) );
185 memcpy( pwrq
.ifr_name
, name
, size
);
186 if (ioctl( fd
, SIOCGIWNAME
, &pwrq
) != -1)
188 TRACE( "iface %s, wireless protocol %s.\n", debugstr_a(name
), debugstr_a(pwrq
.u
.name
) );
189 *type
= IF_TYPE_IEEE80211
;
199 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
201 static NTSTATUS
if_get_physical( const char *name
, UINT
*type
, IF_PHYSICAL_ADDRESS
*phys_addr
)
203 struct if_msghdr
*ifm
;
204 struct sockaddr_dl
*sdl
;
207 int mib
[] = { CTL_NET
, AF_ROUTE
, 0, AF_LINK
, NET_RT_IFLIST
, 0 }, i
;
208 static const struct type_lookup
214 { IFT_ETHER
, MIB_IF_TYPE_ETHERNET
},
215 { IFT_FDDI
, MIB_IF_TYPE_FDDI
},
216 { IFT_ISO88024
, MIB_IF_TYPE_TOKENRING
},
217 { IFT_ISO88025
, MIB_IF_TYPE_TOKENRING
},
218 { IFT_PPP
, MIB_IF_TYPE_PPP
},
219 { IFT_SLIP
, MIB_IF_TYPE_SLIP
},
220 { IFT_LOOP
, MIB_IF_TYPE_LOOPBACK
}
223 *type
= MIB_IF_TYPE_OTHER
;
224 memset( phys_addr
, 0, sizeof(*phys_addr
) );
226 if (sysctl( mib
, 6, NULL
, &mib_len
, NULL
, 0 ) < 0) return STATUS_TOO_MANY_OPENED_FILES
;
228 buf
= malloc( mib_len
);
229 if (!buf
) return STATUS_NO_MEMORY
;
231 if (sysctl( mib
, 6, buf
, &mib_len
, NULL
, 0 ) < 0)
234 return STATUS_TOO_MANY_OPENED_FILES
;
237 for (p
= buf
; p
< buf
+ mib_len
; p
+= ifm
->ifm_msglen
)
239 ifm
= (struct if_msghdr
*)p
;
240 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
242 if (ifm
->ifm_type
!= RTM_IFINFO
|| (ifm
->ifm_addrs
& RTA_IFP
) == 0) continue;
244 if (sdl
->sdl_family
!= AF_LINK
|| sdl
->sdl_nlen
== 0 ||
245 memcmp( sdl
->sdl_data
, name
, max( sdl
->sdl_nlen
, strlen( name
) ) ))
248 for (i
= 0; i
< ARRAY_SIZE(types
); i
++)
249 if (sdl
->sdl_type
== types
[i
].sdl_type
)
251 *type
= types
[i
].mib_type
;
255 phys_addr
->Length
= sdl
->sdl_alen
;
256 if (phys_addr
->Length
> sizeof(phys_addr
->Address
)) phys_addr
->Length
= 0;
257 memcpy( phys_addr
->Address
, LLADDR(sdl
), phys_addr
->Length
);
262 return STATUS_SUCCESS
;
266 static WCHAR
*strdupAtoW( const char *str
)
271 if (!str
) return ret
;
272 len
= strlen( str
) + 1;
273 ret
= malloc( len
* sizeof(WCHAR
) );
274 if (ret
) ntdll_umbstowcs( str
, len
, ret
, len
);
278 static struct if_entry
*add_entry( UINT index
, char *name
)
280 struct if_entry
*entry
;
281 int name_len
= strlen( name
);
283 if (name_len
>= sizeof(entry
->if_unix_name
)) return NULL
;
284 entry
= malloc( sizeof(*entry
) );
285 if (!entry
) return NULL
;
287 entry
->if_index
= index
;
288 memcpy( entry
->if_unix_name
, name
, name_len
+ 1 );
289 entry
->if_name
= strdupAtoW( name
);
296 if_get_physical( name
, &entry
->if_type
, &entry
->if_phys_addr
);
298 entry
->if_luid
.Info
.Reserved
= 0;
299 entry
->if_luid
.Info
.NetLuidIndex
= index
;
300 entry
->if_luid
.Info
.IfType
= entry
->if_type
;
302 memset( &entry
->if_guid
, 0, sizeof(entry
->if_guid
) );
303 entry
->if_guid
.Data1
= index
;
304 memcpy( entry
->if_guid
.Data4
+ 2, "NetDev", 6 );
306 list_add_tail( &if_list
, &entry
->entry
);
310 static unsigned int update_if_table( void )
312 struct if_nameindex
*indices
= if_nameindex(), *entry
;
313 unsigned int append_count
= 0;
315 for (entry
= indices
; entry
->if_index
; entry
++)
317 if (!find_entry_from_index( entry
->if_index
) && add_entry( entry
->if_index
, entry
->if_name
))
321 if_freenameindex( indices
);
325 static void if_counted_string_init( IF_COUNTED_STRING
*str
, const WCHAR
*value
)
327 str
->Length
= value
? min( lstrlenW( value
), ARRAY_SIZE(str
->String
) - 1 ) * sizeof(WCHAR
) : 0;
328 if (str
->Length
) memcpy( str
->String
, value
, str
->Length
);
329 memset( (char *)str
->String
+ str
->Length
, 0, sizeof(str
->String
) - str
->Length
);
332 static void ifinfo_fill_dynamic( struct if_entry
*entry
, struct nsi_ndis_ifinfo_dynamic
*data
)
334 int fd
, name_len
= strlen( entry
->if_unix_name
);
337 memset( data
, 0, sizeof(*data
) );
339 if (name_len
>= sizeof(req
.ifr_name
)) return;
340 memcpy( req
.ifr_name
, entry
->if_unix_name
, name_len
+ 1 );
342 fd
= socket( PF_INET
, SOCK_DGRAM
, 0 );
343 if (fd
== -1) return;
345 if (!ioctl( fd
, SIOCGIFFLAGS
, &req
))
347 if (req
.ifr_flags
& IFF_UP
) data
->oper_status
= IfOperStatusUp
;
349 else if (req
.ifr_flags
& IFF_DORMANT
) data
->oper_status
= IfOperStatusDormant
;
351 else data
->oper_status
= IfOperStatusDown
;
352 } else data
->oper_status
= IfOperStatusUnknown
;
355 data
->flags
.not_media_conn
= 0;
356 data
->flags
.unk2
= 0;
362 sprintf( filename
, "/sys/class/net/%s/carrier", entry
->if_unix_name
);
363 if (!(fp
= fopen( filename
, "r" ))) data
->media_conn_state
= MediaConnectStateUnknown
;
366 if (fgetc( fp
) == '1') data
->media_conn_state
= MediaConnectStateConnected
;
367 else data
->media_conn_state
= MediaConnectStateDisconnected
;
372 data
->media_conn_state
= MediaConnectStateConnected
;
376 if (!ioctl( fd
, SIOCGIFMTU
, &req
)) data
->mtu
= req
.ifr_mtu
;
385 if ((fp
= fopen( "/proc/net/dev", "r" )))
389 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
391 while (*ptr
&& isspace( *ptr
)) ptr
++;
392 if (!ascii_strncasecmp( ptr
, entry
->if_unix_name
, name_len
) && ptr
[name_len
] == ':')
394 unsigned long long values
[9];
396 sscanf( ptr
, "%llu %llu %llu %llu %*u %*u %*u %llu %llu %llu %llu %llu",
397 values
, values
+ 1, values
+ 2, values
+ 3, values
+ 4,
398 values
+ 5, values
+ 6, values
+ 7, values
+ 8 );
399 data
->in_octets
= values
[0];
400 data
->in_ucast_pkts
= values
[1];
401 data
->in_errors
= values
[2];
402 data
->in_discards
= values
[3];
403 data
->in_mcast_pkts
= values
[4];
404 data
->out_octets
= values
[5];
405 data
->out_ucast_pkts
= values
[6];
406 data
->out_errors
= values
[7];
407 data
->out_discards
= values
[8];
414 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
416 int mib
[] = { CTL_NET
, PF_ROUTE
, 0, AF_INET
, NET_RT_IFLIST
, entry
->if_index
};
418 char *buf
= NULL
, *end
;
419 struct if_msghdr
*ifm
;
420 struct if_data ifdata
;
422 if (sysctl( mib
, ARRAY_SIZE(mib
), NULL
, &needed
, NULL
, 0 ) == -1) goto done
;
423 buf
= malloc( needed
);
425 if (sysctl( mib
, ARRAY_SIZE(mib
), buf
, &needed
, NULL
, 0 ) == -1) goto done
;
426 for (end
= buf
+ needed
; buf
< end
; buf
+= ifm
->ifm_msglen
)
428 ifm
= (struct if_msghdr
*) buf
;
429 if (ifm
->ifm_type
== RTM_IFINFO
)
431 ifdata
= ifm
->ifm_data
;
432 data
->xmit_speed
= data
->rcv_speed
= ifdata
.ifi_baudrate
;
433 data
->in_octets
= ifdata
.ifi_ibytes
;
434 data
->in_errors
= ifdata
.ifi_ierrors
;
435 data
->in_discards
= ifdata
.ifi_iqdrops
;
436 data
->in_ucast_pkts
= ifdata
.ifi_ipackets
;
437 data
->in_mcast_pkts
= ifdata
.ifi_imcasts
;
438 data
->out_octets
= ifdata
.ifi_obytes
;
439 data
->out_ucast_pkts
= ifdata
.ifi_opackets
;
440 data
->out_mcast_pkts
= ifdata
.ifi_omcasts
;
441 data
->out_errors
= ifdata
.ifi_oerrors
;
451 static void ifinfo_fill_entry( struct if_entry
*entry
, NET_LUID
*key
, struct nsi_ndis_ifinfo_rw
*rw
,
452 struct nsi_ndis_ifinfo_dynamic
*dyn
, struct nsi_ndis_ifinfo_static
*stat
)
454 if (key
) memcpy( key
, &entry
->if_luid
, sizeof(entry
->if_luid
) );
458 memset( &rw
->network_guid
, 0, sizeof(entry
->if_guid
) );
459 rw
->admin_status
= MIB_IF_ADMIN_STATUS_UP
;
460 if_counted_string_init( &rw
->alias
, entry
->if_name
);
461 memcpy( &rw
->phys_addr
, &entry
->if_phys_addr
, sizeof(entry
->if_phys_addr
) );
463 if_counted_string_init( &rw
->name2
, NULL
);
467 if (dyn
) ifinfo_fill_dynamic( entry
, dyn
);
471 stat
->if_index
= entry
->if_index
;
472 if_counted_string_init( &stat
->descr
, entry
->if_name
); /* get a more descriptive name */
473 stat
->type
= entry
->if_type
;
474 stat
->access_type
= (entry
->if_type
== MIB_IF_TYPE_LOOPBACK
) ? NET_IF_ACCESS_LOOPBACK
: NET_IF_ACCESS_BROADCAST
;
476 stat
->conn_type
= NET_IF_CONNECTION_DEDICATED
;
477 memcpy( &stat
->if_guid
, &entry
->if_guid
, sizeof(entry
->if_guid
) );
478 stat
->conn_present
= entry
->if_type
!= MIB_IF_TYPE_LOOPBACK
;
479 memcpy( &stat
->perm_phys_addr
, &entry
->if_phys_addr
, sizeof(entry
->if_phys_addr
) );
480 stat
->flags
.hw
= entry
->if_type
!= MIB_IF_TYPE_LOOPBACK
;
481 stat
->flags
.filter
= 0;
483 stat
->media_type
= 0;
484 stat
->phys_medium_type
= 0;
488 static NTSTATUS
ifinfo_enumerate_all( void *key_data
, UINT key_size
, void *rw_data
, UINT rw_size
,
489 void *dynamic_data
, UINT dynamic_size
,
490 void *static_data
, UINT static_size
, UINT_PTR
*count
)
492 struct if_entry
*entry
;
494 NTSTATUS status
= STATUS_SUCCESS
;
495 BOOL want_data
= key_size
|| rw_size
|| dynamic_size
|| static_size
;
497 TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data
, key_size
, rw_data
, rw_size
,
498 dynamic_data
, dynamic_size
, static_data
, static_size
, count
);
500 pthread_mutex_lock( &if_list_lock
);
504 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
508 ifinfo_fill_entry( entry
, key_data
, rw_data
, dynamic_data
, static_data
);
509 key_data
= (BYTE
*)key_data
+ key_size
;
510 rw_data
= (BYTE
*)rw_data
+ rw_size
;
511 dynamic_data
= (BYTE
*)dynamic_data
+ dynamic_size
;
512 static_data
= (BYTE
*)static_data
+ static_size
;
517 pthread_mutex_unlock( &if_list_lock
);
519 if (!want_data
|| num
<= *count
) *count
= num
;
520 else status
= STATUS_BUFFER_OVERFLOW
;
525 static NTSTATUS
ifinfo_get_all_parameters( const void *key
, UINT key_size
, void *rw_data
, UINT rw_size
,
526 void *dynamic_data
, UINT dynamic_size
,
527 void *static_data
, UINT static_size
)
529 struct if_entry
*entry
;
530 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
532 TRACE( "%p %d %p %d %p %d %p %d\n", key
, key_size
, rw_data
, rw_size
,
533 dynamic_data
, dynamic_size
, static_data
, static_size
);
535 pthread_mutex_lock( &if_list_lock
);
537 if (!(entry
= find_entry_from_luid( (const NET_LUID
*)key
)))
540 entry
= find_entry_from_luid( (const NET_LUID
*)key
);
544 ifinfo_fill_entry( entry
, NULL
, rw_data
, dynamic_data
, static_data
);
545 status
= STATUS_SUCCESS
;
548 pthread_mutex_unlock( &if_list_lock
);
553 static NTSTATUS
ifinfo_get_rw_parameter( struct if_entry
*entry
, void *data
, UINT data_size
, UINT data_offset
)
557 case FIELD_OFFSET( struct nsi_ndis_ifinfo_rw
, alias
):
559 IF_COUNTED_STRING
*str
= (IF_COUNTED_STRING
*)data
;
560 if (data_size
!= sizeof(*str
)) return STATUS_INVALID_PARAMETER
;
561 if_counted_string_init( str
, entry
->if_name
);
562 return STATUS_SUCCESS
;
565 FIXME( "Offset %#x not handled\n", data_offset
);
568 return STATUS_INVALID_PARAMETER
;
571 static NTSTATUS
ifinfo_get_static_parameter( struct if_entry
*entry
, void *data
, UINT data_size
, UINT data_offset
)
575 case FIELD_OFFSET( struct nsi_ndis_ifinfo_static
, if_index
):
576 if (data_size
!= sizeof(UINT
)) return STATUS_INVALID_PARAMETER
;
577 *(UINT
*)data
= entry
->if_index
;
578 return STATUS_SUCCESS
;
580 case FIELD_OFFSET( struct nsi_ndis_ifinfo_static
, if_guid
):
581 if (data_size
!= sizeof(GUID
)) return STATUS_INVALID_PARAMETER
;
582 *(GUID
*)data
= entry
->if_guid
;
583 return STATUS_SUCCESS
;
586 FIXME( "Offset %#x not handled\n", data_offset
);
588 return STATUS_INVALID_PARAMETER
;
591 static NTSTATUS
ifinfo_get_parameter( const void *key
, UINT key_size
, UINT param_type
,
592 void *data
, UINT data_size
, UINT data_offset
)
594 struct if_entry
*entry
;
595 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
597 TRACE( "%p %d %d %p %d %d\n", key
, key_size
, param_type
, data
, data_size
, data_offset
);
599 pthread_mutex_lock( &if_list_lock
);
601 if (!(entry
= find_entry_from_luid( (const NET_LUID
*)key
)))
604 entry
= find_entry_from_luid( (const NET_LUID
*)key
);
610 case NSI_PARAM_TYPE_RW
:
611 status
= ifinfo_get_rw_parameter( entry
, data
, data_size
, data_offset
);
613 case NSI_PARAM_TYPE_STATIC
:
614 status
= ifinfo_get_static_parameter( entry
, data
, data_size
, data_offset
);
619 pthread_mutex_unlock( &if_list_lock
);
624 static NTSTATUS
index_luid_get_parameter( const void *key
, UINT key_size
, UINT param_type
,
625 void *data
, UINT data_size
, UINT data_offset
)
627 struct if_entry
*entry
;
628 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
630 TRACE( "%p %d %d %p %d %d\n", key
, key_size
, param_type
, data
, data_size
, data_offset
);
632 if (param_type
!= NSI_PARAM_TYPE_STATIC
|| data_size
!= sizeof(NET_LUID
) || data_offset
!= 0)
633 return STATUS_INVALID_PARAMETER
;
635 pthread_mutex_lock( &if_list_lock
);
637 if (!(entry
= find_entry_from_index( *(UINT
*)key
)))
640 entry
= find_entry_from_index( *(UINT
*)key
);
644 *(NET_LUID
*)data
= entry
->if_luid
;
645 status
= STATUS_SUCCESS
;
647 pthread_mutex_unlock( &if_list_lock
);
651 BOOL
convert_unix_name_to_luid( const char *unix_name
, NET_LUID
*luid
)
653 struct if_entry
*entry
;
657 pthread_mutex_lock( &if_list_lock
);
661 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
663 if (!strcmp( entry
->if_unix_name
, unix_name
))
665 *luid
= entry
->if_luid
;
670 } while (!updated
++ && update_if_table());
673 pthread_mutex_unlock( &if_list_lock
);
678 BOOL
convert_luid_to_unix_name( const NET_LUID
*luid
, const char **unix_name
)
680 struct if_entry
*entry
;
684 pthread_mutex_lock( &if_list_lock
);
688 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
690 if (entry
->if_luid
.Value
== luid
->Value
)
692 *unix_name
= entry
->if_unix_name
;
697 } while (!updated
++ && update_if_table());
700 pthread_mutex_unlock( &if_list_lock
);
705 static const struct module_table tables
[] =
708 NSI_NDIS_IFINFO_TABLE
,
710 sizeof(NET_LUID
), sizeof(struct nsi_ndis_ifinfo_rw
),
711 sizeof(struct nsi_ndis_ifinfo_dynamic
), sizeof(struct nsi_ndis_ifinfo_static
)
713 ifinfo_enumerate_all
,
714 ifinfo_get_all_parameters
,
718 NSI_NDIS_INDEX_LUID_TABLE
,
725 index_luid_get_parameter
730 const struct module ndis_module
=
732 &NPI_MS_NDIS_MODULEID
,