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
);
173 if (*type
== MIB_IF_TYPE_OTHER
&& !ioctl( fd
, SIOCGIFFLAGS
, &ifr
) && ifr
.ifr_flags
& IFF_POINTOPOINT
)
174 *type
= MIB_IF_TYPE_PPP
;
181 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
183 static NTSTATUS
if_get_physical( const char *name
, UINT
*type
, IF_PHYSICAL_ADDRESS
*phys_addr
)
185 struct if_msghdr
*ifm
;
186 struct sockaddr_dl
*sdl
;
189 int mib
[] = { CTL_NET
, AF_ROUTE
, 0, AF_LINK
, NET_RT_IFLIST
, 0 }, i
;
190 static const struct type_lookup
196 { IFT_ETHER
, MIB_IF_TYPE_ETHERNET
},
197 { IFT_FDDI
, MIB_IF_TYPE_FDDI
},
198 { IFT_ISO88024
, MIB_IF_TYPE_TOKENRING
},
199 { IFT_ISO88025
, MIB_IF_TYPE_TOKENRING
},
200 { IFT_PPP
, MIB_IF_TYPE_PPP
},
201 { IFT_SLIP
, MIB_IF_TYPE_SLIP
},
202 { IFT_LOOP
, MIB_IF_TYPE_LOOPBACK
}
205 *type
= MIB_IF_TYPE_OTHER
;
206 memset( phys_addr
, 0, sizeof(*phys_addr
) );
208 if (sysctl( mib
, 6, NULL
, &mib_len
, NULL
, 0 ) < 0) return STATUS_TOO_MANY_OPENED_FILES
;
210 buf
= malloc( mib_len
);
211 if (!buf
) return STATUS_NO_MEMORY
;
213 if (sysctl( mib
, 6, buf
, &mib_len
, NULL
, 0 ) < 0)
216 return STATUS_TOO_MANY_OPENED_FILES
;
219 for (p
= buf
; p
< buf
+ mib_len
; p
+= ifm
->ifm_msglen
)
221 ifm
= (struct if_msghdr
*)p
;
222 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
224 if (ifm
->ifm_type
!= RTM_IFINFO
|| (ifm
->ifm_addrs
& RTA_IFP
) == 0) continue;
226 if (sdl
->sdl_family
!= AF_LINK
|| sdl
->sdl_nlen
== 0 ||
227 memcmp( sdl
->sdl_data
, name
, max( sdl
->sdl_nlen
, strlen( name
) ) ))
230 for (i
= 0; i
< ARRAY_SIZE(types
); i
++)
231 if (sdl
->sdl_type
== types
[i
].sdl_type
)
233 *type
= types
[i
].mib_type
;
237 phys_addr
->Length
= sdl
->sdl_alen
;
238 if (phys_addr
->Length
> sizeof(phys_addr
->Address
)) phys_addr
->Length
= 0;
239 memcpy( phys_addr
->Address
, LLADDR(sdl
), phys_addr
->Length
);
244 return STATUS_SUCCESS
;
248 static WCHAR
*strdupAtoW( const char *str
)
253 if (!str
) return ret
;
254 len
= strlen( str
) + 1;
255 ret
= malloc( len
* sizeof(WCHAR
) );
256 if (ret
) ntdll_umbstowcs( str
, len
, ret
, len
);
260 static struct if_entry
*add_entry( UINT index
, char *name
)
262 struct if_entry
*entry
;
263 int name_len
= strlen( name
);
265 if (name_len
>= sizeof(entry
->if_unix_name
)) return NULL
;
266 entry
= malloc( sizeof(*entry
) );
267 if (!entry
) return NULL
;
269 entry
->if_index
= index
;
270 memcpy( entry
->if_unix_name
, name
, name_len
+ 1 );
271 entry
->if_name
= strdupAtoW( name
);
278 if_get_physical( name
, &entry
->if_type
, &entry
->if_phys_addr
);
280 entry
->if_luid
.Info
.Reserved
= 0;
281 entry
->if_luid
.Info
.NetLuidIndex
= index
;
282 entry
->if_luid
.Info
.IfType
= entry
->if_type
;
284 memset( &entry
->if_guid
, 0, sizeof(entry
->if_guid
) );
285 entry
->if_guid
.Data1
= index
;
286 memcpy( entry
->if_guid
.Data4
+ 2, "NetDev", 6 );
288 list_add_tail( &if_list
, &entry
->entry
);
292 static unsigned int update_if_table( void )
294 struct if_nameindex
*indices
= if_nameindex(), *entry
;
295 unsigned int append_count
= 0;
297 for (entry
= indices
; entry
->if_index
; entry
++)
299 if (!find_entry_from_index( entry
->if_index
) && add_entry( entry
->if_index
, entry
->if_name
))
303 if_freenameindex( indices
);
307 static void if_counted_string_init( IF_COUNTED_STRING
*str
, const WCHAR
*value
)
309 str
->Length
= value
? min( lstrlenW( value
), ARRAY_SIZE(str
->String
) - 1 ) * sizeof(WCHAR
) : 0;
310 if (str
->Length
) memcpy( str
->String
, value
, str
->Length
);
311 memset( (char *)str
->String
+ str
->Length
, 0, sizeof(str
->String
) - str
->Length
);
314 static void ifinfo_fill_dynamic( struct if_entry
*entry
, struct nsi_ndis_ifinfo_dynamic
*data
)
316 int fd
, name_len
= strlen( entry
->if_unix_name
);
319 memset( data
, 0, sizeof(*data
) );
321 if (name_len
>= sizeof(req
.ifr_name
)) return;
322 memcpy( req
.ifr_name
, entry
->if_unix_name
, name_len
+ 1 );
324 fd
= socket( PF_INET
, SOCK_DGRAM
, 0 );
325 if (fd
== -1) return;
327 if (!ioctl( fd
, SIOCGIFFLAGS
, &req
))
329 if (req
.ifr_flags
& IFF_UP
) data
->oper_status
= IfOperStatusUp
;
331 else if (req
.ifr_flags
& IFF_DORMANT
) data
->oper_status
= IfOperStatusDormant
;
333 else data
->oper_status
= IfOperStatusDown
;
334 } else data
->oper_status
= IfOperStatusUnknown
;
337 data
->flags
.not_media_conn
= 0;
338 data
->flags
.unk2
= 0;
344 sprintf( filename
, "/sys/class/net/%s/carrier", entry
->if_unix_name
);
345 if (!(fp
= fopen( filename
, "r" ))) data
->media_conn_state
= MediaConnectStateUnknown
;
348 if (fgetc( fp
) == '1') data
->media_conn_state
= MediaConnectStateConnected
;
349 else data
->media_conn_state
= MediaConnectStateDisconnected
;
354 data
->media_conn_state
= MediaConnectStateConnected
;
358 if (!ioctl( fd
, SIOCGIFMTU
, &req
)) data
->mtu
= req
.ifr_mtu
;
367 if ((fp
= fopen( "/proc/net/dev", "r" )))
371 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
373 while (*ptr
&& isspace( *ptr
)) ptr
++;
374 if (!ascii_strncasecmp( ptr
, entry
->if_unix_name
, name_len
) && ptr
[name_len
] == ':')
376 unsigned long long values
[9];
378 sscanf( ptr
, "%llu %llu %llu %llu %*u %*u %*u %llu %llu %llu %llu %llu",
379 values
, values
+ 1, values
+ 2, values
+ 3, values
+ 4,
380 values
+ 5, values
+ 6, values
+ 7, values
+ 8 );
381 data
->in_octets
= values
[0];
382 data
->in_ucast_pkts
= values
[1];
383 data
->in_errors
= values
[2];
384 data
->in_discards
= values
[3];
385 data
->in_mcast_pkts
= values
[4];
386 data
->out_octets
= values
[5];
387 data
->out_ucast_pkts
= values
[6];
388 data
->out_errors
= values
[7];
389 data
->out_discards
= values
[8];
396 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
398 int mib
[] = { CTL_NET
, PF_ROUTE
, 0, AF_INET
, NET_RT_IFLIST
, entry
->if_index
};
400 char *buf
= NULL
, *end
;
401 struct if_msghdr
*ifm
;
402 struct if_data ifdata
;
404 if (sysctl( mib
, ARRAY_SIZE(mib
), NULL
, &needed
, NULL
, 0 ) == -1) goto done
;
405 buf
= malloc( needed
);
407 if (sysctl( mib
, ARRAY_SIZE(mib
), buf
, &needed
, NULL
, 0 ) == -1) goto done
;
408 for (end
= buf
+ needed
; buf
< end
; buf
+= ifm
->ifm_msglen
)
410 ifm
= (struct if_msghdr
*) buf
;
411 if (ifm
->ifm_type
== RTM_IFINFO
)
413 ifdata
= ifm
->ifm_data
;
414 data
->xmit_speed
= data
->rcv_speed
= ifdata
.ifi_baudrate
;
415 data
->in_octets
= ifdata
.ifi_ibytes
;
416 data
->in_errors
= ifdata
.ifi_ierrors
;
417 data
->in_discards
= ifdata
.ifi_iqdrops
;
418 data
->in_ucast_pkts
= ifdata
.ifi_ipackets
;
419 data
->in_mcast_pkts
= ifdata
.ifi_imcasts
;
420 data
->out_octets
= ifdata
.ifi_obytes
;
421 data
->out_ucast_pkts
= ifdata
.ifi_opackets
;
422 data
->out_mcast_pkts
= ifdata
.ifi_omcasts
;
423 data
->out_errors
= ifdata
.ifi_oerrors
;
433 static void ifinfo_fill_entry( struct if_entry
*entry
, NET_LUID
*key
, struct nsi_ndis_ifinfo_rw
*rw
,
434 struct nsi_ndis_ifinfo_dynamic
*dyn
, struct nsi_ndis_ifinfo_static
*stat
)
436 if (key
) memcpy( key
, &entry
->if_luid
, sizeof(entry
->if_luid
) );
440 memset( &rw
->network_guid
, 0, sizeof(entry
->if_guid
) );
441 rw
->admin_status
= MIB_IF_ADMIN_STATUS_UP
;
442 if_counted_string_init( &rw
->alias
, entry
->if_name
);
443 memcpy( &rw
->phys_addr
, &entry
->if_phys_addr
, sizeof(entry
->if_phys_addr
) );
445 if_counted_string_init( &rw
->name2
, NULL
);
449 if (dyn
) ifinfo_fill_dynamic( entry
, dyn
);
453 stat
->if_index
= entry
->if_index
;
454 if_counted_string_init( &stat
->descr
, entry
->if_name
); /* get a more descriptive name */
455 stat
->type
= entry
->if_type
;
456 stat
->access_type
= (entry
->if_type
== MIB_IF_TYPE_LOOPBACK
) ? NET_IF_ACCESS_LOOPBACK
: NET_IF_ACCESS_BROADCAST
;
458 stat
->conn_type
= NET_IF_CONNECTION_DEDICATED
;
459 memcpy( &stat
->if_guid
, &entry
->if_guid
, sizeof(entry
->if_guid
) );
460 stat
->conn_present
= entry
->if_type
!= MIB_IF_TYPE_LOOPBACK
;
461 memcpy( &stat
->perm_phys_addr
, &entry
->if_phys_addr
, sizeof(entry
->if_phys_addr
) );
462 stat
->flags
.hw
= entry
->if_type
!= MIB_IF_TYPE_LOOPBACK
;
463 stat
->flags
.filter
= 0;
465 stat
->media_type
= 0;
466 stat
->phys_medium_type
= 0;
470 static NTSTATUS
ifinfo_enumerate_all( void *key_data
, UINT key_size
, void *rw_data
, UINT rw_size
,
471 void *dynamic_data
, UINT dynamic_size
,
472 void *static_data
, UINT static_size
, UINT_PTR
*count
)
474 struct if_entry
*entry
;
476 NTSTATUS status
= STATUS_SUCCESS
;
477 BOOL want_data
= key_size
|| rw_size
|| dynamic_size
|| static_size
;
479 TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data
, key_size
, rw_data
, rw_size
,
480 dynamic_data
, dynamic_size
, static_data
, static_size
, count
);
482 pthread_mutex_lock( &if_list_lock
);
486 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
490 ifinfo_fill_entry( entry
, key_data
, rw_data
, dynamic_data
, static_data
);
491 key_data
= (BYTE
*)key_data
+ key_size
;
492 rw_data
= (BYTE
*)rw_data
+ rw_size
;
493 dynamic_data
= (BYTE
*)dynamic_data
+ dynamic_size
;
494 static_data
= (BYTE
*)static_data
+ static_size
;
499 pthread_mutex_unlock( &if_list_lock
);
501 if (!want_data
|| num
<= *count
) *count
= num
;
502 else status
= STATUS_BUFFER_OVERFLOW
;
507 static NTSTATUS
ifinfo_get_all_parameters( const void *key
, UINT key_size
, void *rw_data
, UINT rw_size
,
508 void *dynamic_data
, UINT dynamic_size
,
509 void *static_data
, UINT static_size
)
511 struct if_entry
*entry
;
512 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
514 TRACE( "%p %d %p %d %p %d %p %d\n", key
, key_size
, rw_data
, rw_size
,
515 dynamic_data
, dynamic_size
, static_data
, static_size
);
517 pthread_mutex_lock( &if_list_lock
);
519 if (!(entry
= find_entry_from_luid( (const NET_LUID
*)key
)))
522 entry
= find_entry_from_luid( (const NET_LUID
*)key
);
526 ifinfo_fill_entry( entry
, NULL
, rw_data
, dynamic_data
, static_data
);
527 status
= STATUS_SUCCESS
;
530 pthread_mutex_unlock( &if_list_lock
);
535 static NTSTATUS
ifinfo_get_rw_parameter( struct if_entry
*entry
, void *data
, UINT data_size
, UINT data_offset
)
539 case FIELD_OFFSET( struct nsi_ndis_ifinfo_rw
, alias
):
541 IF_COUNTED_STRING
*str
= (IF_COUNTED_STRING
*)data
;
542 if (data_size
!= sizeof(*str
)) return STATUS_INVALID_PARAMETER
;
543 if_counted_string_init( str
, entry
->if_name
);
544 return STATUS_SUCCESS
;
547 FIXME( "Offset %#x not handled\n", data_offset
);
550 return STATUS_INVALID_PARAMETER
;
553 static NTSTATUS
ifinfo_get_static_parameter( struct if_entry
*entry
, void *data
, UINT data_size
, UINT data_offset
)
557 case FIELD_OFFSET( struct nsi_ndis_ifinfo_static
, if_index
):
558 if (data_size
!= sizeof(UINT
)) return STATUS_INVALID_PARAMETER
;
559 *(UINT
*)data
= entry
->if_index
;
560 return STATUS_SUCCESS
;
562 case FIELD_OFFSET( struct nsi_ndis_ifinfo_static
, if_guid
):
563 if (data_size
!= sizeof(GUID
)) return STATUS_INVALID_PARAMETER
;
564 *(GUID
*)data
= entry
->if_guid
;
565 return STATUS_SUCCESS
;
568 FIXME( "Offset %#x not handled\n", data_offset
);
570 return STATUS_INVALID_PARAMETER
;
573 static NTSTATUS
ifinfo_get_parameter( const void *key
, UINT key_size
, UINT param_type
,
574 void *data
, UINT data_size
, UINT data_offset
)
576 struct if_entry
*entry
;
577 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
579 TRACE( "%p %d %d %p %d %d\n", key
, key_size
, param_type
, data
, data_size
, data_offset
);
581 pthread_mutex_lock( &if_list_lock
);
583 if (!(entry
= find_entry_from_luid( (const NET_LUID
*)key
)))
586 entry
= find_entry_from_luid( (const NET_LUID
*)key
);
592 case NSI_PARAM_TYPE_RW
:
593 status
= ifinfo_get_rw_parameter( entry
, data
, data_size
, data_offset
);
595 case NSI_PARAM_TYPE_STATIC
:
596 status
= ifinfo_get_static_parameter( entry
, data
, data_size
, data_offset
);
601 pthread_mutex_unlock( &if_list_lock
);
606 static NTSTATUS
index_luid_get_parameter( const void *key
, UINT key_size
, UINT param_type
,
607 void *data
, UINT data_size
, UINT data_offset
)
609 struct if_entry
*entry
;
610 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
612 TRACE( "%p %d %d %p %d %d\n", key
, key_size
, param_type
, data
, data_size
, data_offset
);
614 if (param_type
!= NSI_PARAM_TYPE_STATIC
|| data_size
!= sizeof(NET_LUID
) || data_offset
!= 0)
615 return STATUS_INVALID_PARAMETER
;
617 pthread_mutex_lock( &if_list_lock
);
619 if (!(entry
= find_entry_from_index( *(UINT
*)key
)))
622 entry
= find_entry_from_index( *(UINT
*)key
);
626 *(NET_LUID
*)data
= entry
->if_luid
;
627 status
= STATUS_SUCCESS
;
629 pthread_mutex_unlock( &if_list_lock
);
633 BOOL
convert_unix_name_to_luid( const char *unix_name
, NET_LUID
*luid
)
635 struct if_entry
*entry
;
639 pthread_mutex_lock( &if_list_lock
);
643 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
645 if (!strcmp( entry
->if_unix_name
, unix_name
))
647 *luid
= entry
->if_luid
;
652 } while (!updated
++ && update_if_table());
655 pthread_mutex_unlock( &if_list_lock
);
660 BOOL
convert_luid_to_unix_name( const NET_LUID
*luid
, const char **unix_name
)
662 struct if_entry
*entry
;
666 pthread_mutex_lock( &if_list_lock
);
670 LIST_FOR_EACH_ENTRY( entry
, &if_list
, struct if_entry
, entry
)
672 if (entry
->if_luid
.Value
== luid
->Value
)
674 *unix_name
= entry
->if_unix_name
;
679 } while (!updated
++ && update_if_table());
682 pthread_mutex_unlock( &if_list_lock
);
687 static const struct module_table tables
[] =
690 NSI_NDIS_IFINFO_TABLE
,
692 sizeof(NET_LUID
), sizeof(struct nsi_ndis_ifinfo_rw
),
693 sizeof(struct nsi_ndis_ifinfo_dynamic
), sizeof(struct nsi_ndis_ifinfo_static
)
695 ifinfo_enumerate_all
,
696 ifinfo_get_all_parameters
,
700 NSI_NDIS_INDEX_LUID_TABLE
,
707 index_luid_get_parameter
712 const struct module ndis_module
=
714 &NPI_MS_NDIS_MODULEID
,