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>
67 #define NONAMELESSUNION
69 #define WIN32_NO_STATUS
82 #include "wine/list.h"
83 #include "wine/debug.h"
84 #include "wine/unixlib.h"
86 #include "unix_private.h"
88 WINE_DEFAULT_DEBUG_CHANNEL(nsi
);
96 char if_unix_name
[IFNAMSIZ
];
97 IF_PHYSICAL_ADDRESS if_phys_addr
;
102 static struct list if_list
= LIST_INIT( if_list
);
103 static pthread_mutex_t if_list_lock
= PTHREAD_MUTEX_INITIALIZER
;
105 static struct if_entry
*find_entry_from_index( UINT index
)
107 struct if_entry
*entry
;
109 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
110 if (entry
->if_index
== index
) return entry
;
115 static struct if_entry
*find_entry_from_luid( const NET_LUID
*luid
)
117 struct if_entry
*entry
;
119 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
120 if (entry
->if_luid
.Value
== luid
->Value
) return entry
;
125 #if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR)
126 static NTSTATUS
if_get_physical( const char *name
, UINT
*type
, IF_PHYSICAL_ADDRESS
*phys_addr
)
130 NTSTATUS ret
= STATUS_SUCCESS
;
131 static const struct type_lookup
133 unsigned short ifi_type
;
138 { ARPHRD_LOOPBACK
, MIB_IF_TYPE_LOOPBACK
, 0 },
139 { ARPHRD_ETHER
, MIB_IF_TYPE_ETHERNET
, ETH_ALEN
},
140 { ARPHRD_FDDI
, MIB_IF_TYPE_FDDI
, ETH_ALEN
},
141 { ARPHRD_IEEE802
, MIB_IF_TYPE_TOKENRING
, ETH_ALEN
},
142 { ARPHRD_IEEE802_TR
, MIB_IF_TYPE_TOKENRING
, ETH_ALEN
},
143 { ARPHRD_SLIP
, MIB_IF_TYPE_SLIP
, 0 },
144 { ARPHRD_PPP
, MIB_IF_TYPE_PPP
, 0 }
147 *type
= MIB_IF_TYPE_OTHER
;
148 memset( phys_addr
, 0, sizeof(*phys_addr
) );
150 size
= strlen( name
) + 1;
151 if (size
> sizeof(ifr
.ifr_name
)) return STATUS_NAME_TOO_LONG
;
152 memset( &ifr
, 0, sizeof(ifr
) );
153 memcpy( ifr
.ifr_name
, name
, size
);
155 fd
= socket( PF_INET
, SOCK_DGRAM
, 0 );
156 if (fd
== -1) return STATUS_TOO_MANY_OPENED_FILES
;
158 if (ioctl( fd
, SIOCGIFHWADDR
, &ifr
))
160 ret
= STATUS_DEVICE_DATA_ERROR
;
164 for (i
= 0; i
< ARRAY_SIZE(types
); i
++)
165 if (ifr
.ifr_hwaddr
.sa_family
== types
[i
].ifi_type
)
167 *type
= types
[i
].mib_type
;
168 phys_addr
->Length
= types
[i
].addr_len
;
169 memcpy( phys_addr
->Address
, ifr
.ifr_hwaddr
.sa_data
, phys_addr
->Length
);
178 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
180 static NTSTATUS
if_get_physical( const char *name
, UINT
*type
, IF_PHYSICAL_ADDRESS
*phys_addr
)
182 struct if_msghdr
*ifm
;
183 struct sockaddr_dl
*sdl
;
186 int mib
[] = { CTL_NET
, AF_ROUTE
, 0, AF_LINK
, NET_RT_IFLIST
, 0 }, i
;
187 static const struct type_lookup
193 { IFT_ETHER
, MIB_IF_TYPE_ETHERNET
},
194 { IFT_FDDI
, MIB_IF_TYPE_FDDI
},
195 { IFT_ISO88024
, MIB_IF_TYPE_TOKENRING
},
196 { IFT_ISO88025
, MIB_IF_TYPE_TOKENRING
},
197 { IFT_PPP
, MIB_IF_TYPE_PPP
},
198 { IFT_SLIP
, MIB_IF_TYPE_SLIP
},
199 { IFT_LOOP
, MIB_IF_TYPE_LOOPBACK
}
202 *type
= MIB_IF_TYPE_OTHER
;
203 memset( phys_addr
, 0, sizeof(*phys_addr
) );
205 if (sysctl( mib
, 6, NULL
, &mib_len
, NULL
, 0 ) < 0) return STATUS_TOO_MANY_OPENED_FILES
;
207 buf
= malloc( mib_len
);
208 if (!buf
) return STATUS_NO_MEMORY
;
210 if (sysctl( mib
, 6, buf
, &mib_len
, NULL
, 0 ) < 0)
213 return STATUS_TOO_MANY_OPENED_FILES
;
216 for (p
= buf
; p
< buf
+ mib_len
; p
+= ifm
->ifm_msglen
)
218 ifm
= (struct if_msghdr
*)p
;
219 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
221 if (ifm
->ifm_type
!= RTM_IFINFO
|| (ifm
->ifm_addrs
& RTA_IFP
) == 0) continue;
223 if (sdl
->sdl_family
!= AF_LINK
|| sdl
->sdl_nlen
== 0 ||
224 memcmp( sdl
->sdl_data
, name
, max( sdl
->sdl_nlen
, strlen( name
) ) ))
227 for (i
= 0; i
< ARRAY_SIZE(types
); i
++)
228 if (sdl
->sdl_type
== types
[i
].sdl_type
)
230 *type
= types
[i
].mib_type
;
234 phys_addr
->Length
= sdl
->sdl_alen
;
235 if (phys_addr
->Length
> sizeof(phys_addr
->Address
)) phys_addr
->Length
= 0;
236 memcpy( phys_addr
->Address
, LLADDR(sdl
), phys_addr
->Length
);
241 return STATUS_SUCCESS
;
245 static WCHAR
*strdupAtoW( const char *str
)
250 if (!str
) return ret
;
251 len
= strlen( str
) + 1;
252 ret
= malloc( len
* sizeof(WCHAR
) );
253 if (ret
) ntdll_umbstowcs( str
, len
, ret
, len
);
257 static struct if_entry
*add_entry( UINT index
, char *name
)
259 struct if_entry
*entry
;
260 int name_len
= strlen( name
);
262 if (name_len
>= sizeof(entry
->if_unix_name
)) return NULL
;
263 entry
= malloc( sizeof(*entry
) );
264 if (!entry
) return NULL
;
266 entry
->if_index
= index
;
267 memcpy( entry
->if_unix_name
, name
, name_len
+ 1 );
268 entry
->if_name
= strdupAtoW( name
);
275 if_get_physical( name
, &entry
->if_type
, &entry
->if_phys_addr
);
277 entry
->if_luid
.Info
.Reserved
= 0;
278 entry
->if_luid
.Info
.NetLuidIndex
= index
;
279 entry
->if_luid
.Info
.IfType
= entry
->if_type
;
281 memset( &entry
->if_guid
, 0, sizeof(entry
->if_guid
) );
282 entry
->if_guid
.Data1
= index
;
283 memcpy( entry
->if_guid
.Data4
+ 2, "NetDev", 6 );
285 list_add_tail( &if_list
, &entry
->entry
);
289 static unsigned int update_if_table( void )
291 struct if_nameindex
*indices
= if_nameindex(), *entry
;
292 unsigned int append_count
= 0;
294 for (entry
= indices
; entry
->if_index
; entry
++)
296 if (!find_entry_from_index( entry
->if_index
) && add_entry( entry
->if_index
, entry
->if_name
))
300 if_freenameindex( indices
);
304 static void if_counted_string_init( IF_COUNTED_STRING
*str
, const WCHAR
*value
)
306 str
->Length
= value
? min( lstrlenW( value
), ARRAY_SIZE(str
->String
) - 1 ) * sizeof(WCHAR
) : 0;
307 if (str
->Length
) memcpy( str
->String
, value
, str
->Length
);
308 memset( (char *)str
->String
+ str
->Length
, 0, sizeof(str
->String
) - str
->Length
);
311 static void ifinfo_fill_dynamic( struct if_entry
*entry
, struct nsi_ndis_ifinfo_dynamic
*data
)
313 int fd
, name_len
= strlen( entry
->if_unix_name
);
316 memset( data
, 0, sizeof(*data
) );
318 if (name_len
>= sizeof(req
.ifr_name
)) return;
319 memcpy( req
.ifr_name
, entry
->if_unix_name
, name_len
+ 1 );
321 fd
= socket( PF_INET
, SOCK_DGRAM
, 0 );
322 if (fd
== -1) return;
324 if (!ioctl( fd
, SIOCGIFFLAGS
, &req
))
326 if (req
.ifr_flags
& IFF_UP
) data
->oper_status
= IfOperStatusUp
;
328 else if (req
.ifr_flags
& IFF_DORMANT
) data
->oper_status
= IfOperStatusDormant
;
330 else data
->oper_status
= IfOperStatusDown
;
331 } else data
->oper_status
= IfOperStatusUnknown
;
334 data
->flags
.not_media_conn
= 0;
335 data
->flags
.unk2
= 0;
336 data
->media_conn_state
= MediaConnectStateConnected
;
339 if (!ioctl( fd
, SIOCGIFMTU
, &req
)) data
->mtu
= req
.ifr_mtu
;
348 if ((fp
= fopen( "/proc/net/dev", "r" )))
352 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
354 while (*ptr
&& isspace( *ptr
)) ptr
++;
355 if (!ascii_strncasecmp( ptr
, entry
->if_unix_name
, name_len
) && ptr
[name_len
] == ':')
357 unsigned long long values
[9];
359 sscanf( ptr
, "%llu %llu %llu %llu %*u %*u %*u %llu %llu %llu %llu %llu",
360 values
, values
+ 1, values
+ 2, values
+ 3, values
+ 4,
361 values
+ 5, values
+ 6, values
+ 7, values
+ 8 );
362 data
->in_octets
= values
[0];
363 data
->in_ucast_pkts
= values
[1];
364 data
->in_errors
= values
[2];
365 data
->in_discards
= values
[3];
366 data
->in_mcast_pkts
= values
[4];
367 data
->out_octets
= values
[5];
368 data
->out_ucast_pkts
= values
[6];
369 data
->out_errors
= values
[7];
370 data
->out_discards
= values
[8];
377 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
379 int mib
[] = { CTL_NET
, PF_ROUTE
, 0, AF_INET
, NET_RT_IFLIST
, entry
->if_index
};
381 char *buf
= NULL
, *end
;
382 struct if_msghdr
*ifm
;
383 struct if_data ifdata
;
385 if (sysctl( mib
, ARRAY_SIZE(mib
), NULL
, &needed
, NULL
, 0 ) == -1) goto done
;
386 buf
= malloc( needed
);
388 if (sysctl( mib
, ARRAY_SIZE(mib
), buf
, &needed
, NULL
, 0 ) == -1) goto done
;
389 for (end
= buf
+ needed
; buf
< end
; buf
+= ifm
->ifm_msglen
)
391 ifm
= (struct if_msghdr
*) buf
;
392 if (ifm
->ifm_type
== RTM_IFINFO
)
394 ifdata
= ifm
->ifm_data
;
395 data
->xmit_speed
= data
->rcv_speed
= ifdata
.ifi_baudrate
;
396 data
->in_octets
= ifdata
.ifi_ibytes
;
397 data
->in_errors
= ifdata
.ifi_ierrors
;
398 data
->in_discards
= ifdata
.ifi_iqdrops
;
399 data
->in_ucast_pkts
= ifdata
.ifi_ipackets
;
400 data
->in_mcast_pkts
= ifdata
.ifi_imcasts
;
401 data
->out_octets
= ifdata
.ifi_obytes
;
402 data
->out_ucast_pkts
= ifdata
.ifi_opackets
;
403 data
->out_mcast_pkts
= ifdata
.ifi_omcasts
;
404 data
->out_errors
= ifdata
.ifi_oerrors
;
414 static void ifinfo_fill_entry( struct if_entry
*entry
, NET_LUID
*key
, struct nsi_ndis_ifinfo_rw
*rw
,
415 struct nsi_ndis_ifinfo_dynamic
*dyn
, struct nsi_ndis_ifinfo_static
*stat
)
417 if (key
) memcpy( key
, &entry
->if_luid
, sizeof(entry
->if_luid
) );
421 memset( &rw
->network_guid
, 0, sizeof(entry
->if_guid
) );
422 rw
->admin_status
= MIB_IF_ADMIN_STATUS_UP
;
423 if_counted_string_init( &rw
->alias
, entry
->if_name
);
424 memcpy( &rw
->phys_addr
, &entry
->if_phys_addr
, sizeof(entry
->if_phys_addr
) );
426 if_counted_string_init( &rw
->name2
, NULL
);
430 if (dyn
) ifinfo_fill_dynamic( entry
, dyn
);
434 stat
->if_index
= entry
->if_index
;
435 if_counted_string_init( &stat
->descr
, entry
->if_name
); /* get a more descriptive name */
436 stat
->type
= entry
->if_type
;
437 stat
->access_type
= (entry
->if_type
== MIB_IF_TYPE_LOOPBACK
) ? NET_IF_ACCESS_LOOPBACK
: NET_IF_ACCESS_BROADCAST
;
439 stat
->conn_type
= NET_IF_CONNECTION_DEDICATED
;
440 memcpy( &stat
->if_guid
, &entry
->if_guid
, sizeof(entry
->if_guid
) );
441 stat
->conn_present
= entry
->if_type
!= MIB_IF_TYPE_LOOPBACK
;
442 memcpy( &stat
->perm_phys_addr
, &entry
->if_phys_addr
, sizeof(entry
->if_phys_addr
) );
443 stat
->flags
.hw
= entry
->if_type
!= MIB_IF_TYPE_LOOPBACK
;
444 stat
->flags
.filter
= 0;
446 stat
->media_type
= 0;
447 stat
->phys_medium_type
= 0;
451 static NTSTATUS
ifinfo_enumerate_all( void *key_data
, UINT key_size
, void *rw_data
, UINT rw_size
,
452 void *dynamic_data
, UINT dynamic_size
,
453 void *static_data
, UINT static_size
, UINT_PTR
*count
)
455 struct if_entry
*entry
;
457 NTSTATUS status
= STATUS_SUCCESS
;
458 BOOL want_data
= key_size
|| rw_size
|| dynamic_size
|| static_size
;
460 TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data
, key_size
, rw_data
, rw_size
,
461 dynamic_data
, dynamic_size
, static_data
, static_size
, count
);
463 pthread_mutex_lock( &if_list_lock
);
467 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
471 ifinfo_fill_entry( entry
, key_data
, rw_data
, dynamic_data
, static_data
);
472 key_data
= (BYTE
*)key_data
+ key_size
;
473 rw_data
= (BYTE
*)rw_data
+ rw_size
;
474 dynamic_data
= (BYTE
*)dynamic_data
+ dynamic_size
;
475 static_data
= (BYTE
*)static_data
+ static_size
;
480 pthread_mutex_unlock( &if_list_lock
);
482 if (!want_data
|| num
<= *count
) *count
= num
;
483 else status
= STATUS_BUFFER_OVERFLOW
;
488 static NTSTATUS
ifinfo_get_all_parameters( const void *key
, UINT key_size
, void *rw_data
, UINT rw_size
,
489 void *dynamic_data
, UINT dynamic_size
,
490 void *static_data
, UINT static_size
)
492 struct if_entry
*entry
;
493 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
495 TRACE( "%p %d %p %d %p %d %p %d\n", key
, key_size
, rw_data
, rw_size
,
496 dynamic_data
, dynamic_size
, static_data
, static_size
);
498 pthread_mutex_lock( &if_list_lock
);
500 if (!(entry
= find_entry_from_luid( (const NET_LUID
*)key
)))
503 entry
= find_entry_from_luid( (const NET_LUID
*)key
);
507 ifinfo_fill_entry( entry
, NULL
, rw_data
, dynamic_data
, static_data
);
508 status
= STATUS_SUCCESS
;
511 pthread_mutex_unlock( &if_list_lock
);
516 static NTSTATUS
ifinfo_get_rw_parameter( struct if_entry
*entry
, void *data
, UINT data_size
, UINT data_offset
)
520 case FIELD_OFFSET( struct nsi_ndis_ifinfo_rw
, alias
):
522 IF_COUNTED_STRING
*str
= (IF_COUNTED_STRING
*)data
;
523 if (data_size
!= sizeof(*str
)) return STATUS_INVALID_PARAMETER
;
524 if_counted_string_init( str
, entry
->if_name
);
525 return STATUS_SUCCESS
;
528 FIXME( "Offset %#x not handled\n", data_offset
);
531 return STATUS_INVALID_PARAMETER
;
534 static NTSTATUS
ifinfo_get_static_parameter( struct if_entry
*entry
, void *data
, UINT data_size
, UINT data_offset
)
538 case FIELD_OFFSET( struct nsi_ndis_ifinfo_static
, if_index
):
539 if (data_size
!= sizeof(UINT
)) return STATUS_INVALID_PARAMETER
;
540 *(UINT
*)data
= entry
->if_index
;
541 return STATUS_SUCCESS
;
543 case FIELD_OFFSET( struct nsi_ndis_ifinfo_static
, if_guid
):
544 if (data_size
!= sizeof(GUID
)) return STATUS_INVALID_PARAMETER
;
545 *(GUID
*)data
= entry
->if_guid
;
546 return STATUS_SUCCESS
;
549 FIXME( "Offset %#x not handled\n", data_offset
);
551 return STATUS_INVALID_PARAMETER
;
554 static NTSTATUS
ifinfo_get_parameter( const void *key
, UINT key_size
, UINT param_type
,
555 void *data
, UINT data_size
, UINT data_offset
)
557 struct if_entry
*entry
;
558 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
560 TRACE( "%p %d %d %p %d %d\n", key
, key_size
, param_type
, data
, data_size
, data_offset
);
562 pthread_mutex_lock( &if_list_lock
);
564 if (!(entry
= find_entry_from_luid( (const NET_LUID
*)key
)))
567 entry
= find_entry_from_luid( (const NET_LUID
*)key
);
573 case NSI_PARAM_TYPE_RW
:
574 status
= ifinfo_get_rw_parameter( entry
, data
, data_size
, data_offset
);
576 case NSI_PARAM_TYPE_STATIC
:
577 status
= ifinfo_get_static_parameter( entry
, data
, data_size
, data_offset
);
582 pthread_mutex_unlock( &if_list_lock
);
587 static NTSTATUS
index_luid_get_parameter( const void *key
, UINT key_size
, UINT param_type
,
588 void *data
, UINT data_size
, UINT data_offset
)
590 struct if_entry
*entry
;
591 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
593 TRACE( "%p %d %d %p %d %d\n", key
, key_size
, param_type
, data
, data_size
, data_offset
);
595 if (param_type
!= NSI_PARAM_TYPE_STATIC
|| data_size
!= sizeof(NET_LUID
) || data_offset
!= 0)
596 return STATUS_INVALID_PARAMETER
;
598 pthread_mutex_lock( &if_list_lock
);
600 if (!(entry
= find_entry_from_index( *(UINT
*)key
)))
603 entry
= find_entry_from_index( *(UINT
*)key
);
607 *(NET_LUID
*)data
= entry
->if_luid
;
608 status
= STATUS_SUCCESS
;
610 pthread_mutex_unlock( &if_list_lock
);
614 BOOL
convert_unix_name_to_luid( const char *unix_name
, NET_LUID
*luid
)
616 struct if_entry
*entry
;
620 pthread_mutex_lock( &if_list_lock
);
624 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
626 if (!strcmp( entry
->if_unix_name
, unix_name
))
628 *luid
= entry
->if_luid
;
633 } while (!updated
++ && update_if_table());
636 pthread_mutex_unlock( &if_list_lock
);
641 BOOL
convert_luid_to_unix_name( const NET_LUID
*luid
, const char **unix_name
)
643 struct if_entry
*entry
;
647 pthread_mutex_lock( &if_list_lock
);
651 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
653 if (entry
->if_luid
.Value
== luid
->Value
)
655 *unix_name
= entry
->if_unix_name
;
660 } while (!updated
++ && update_if_table());
663 pthread_mutex_unlock( &if_list_lock
);
668 static const struct module_table tables
[] =
671 NSI_NDIS_IFINFO_TABLE
,
673 sizeof(NET_LUID
), sizeof(struct nsi_ndis_ifinfo_rw
),
674 sizeof(struct nsi_ndis_ifinfo_dynamic
), sizeof(struct nsi_ndis_ifinfo_static
)
676 ifinfo_enumerate_all
,
677 ifinfo_get_all_parameters
,
681 NSI_NDIS_INDEX_LUID_TABLE
,
688 index_luid_get_parameter
693 const struct module ndis_module
=
695 &NPI_MS_NDIS_MODULEID
,