2 * Copyright (c) 2008 Microsoft Corporation. All rights reserved.
\r
4 * This software is available to you under the OpenIB.org BSD license
\r
7 * Redistribution and use in source and binary forms, with or
\r
8 * without modification, are permitted provided that the following
\r
9 * conditions are met:
\r
11 * - Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\r
15 * - Redistributions in binary form must reproduce the above
\r
16 * copyright notice, this list of conditions and the following
\r
17 * disclaimer in the documentation and/or other materials
\r
18 * provided with the distribution.
\r
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
\r
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
\r
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
33 // IBAT: InfiniBand Address Translation
\r
36 // Maps source & remote IP addresses (IPv4 and IPv6) to a path record.
\r
38 // The mapping requires two steps:
\r
39 // 1. Mapping the remote IP address to the remote Ethernet MAC address
\r
40 // 2. Retrieve the path given the remote Ethernet MAC address from IPoIB
\r
42 // The first step is accomplished as follows on Windows Server 2008:
\r
43 // 1. Lookup the desired MAC from the OS using GetIpNetEntry2
\r
44 // 2. If the remote IP isn't found, resolve the remote IP address
\r
45 // using ResolveIpNetEntry2
\r
47 // The first step is accomplished as follows on Windows Server 2003:
\r
48 // 1. Retrieve the whole IP->MAC mapping table from the OS using
\r
50 // 2. Walk the returned table looking for the destination IP to
\r
51 // find the destination Ethernet MAC address.
\r
52 // 3. If the remote IP isn't found, resolve the remote IP address using
\r
55 // The second step is accomplished by asking IPoIB for the path
\r
56 // given the remote MAC. IPoIB creates the path internally without going to
\r
59 #pragma warning( push, 3 )
\r
63 #include <winioctl.h>
\r
64 #pragma warning( pop )
\r
65 #include "iba/ibat.h"
\r
66 #include <iphlpapi.h>
\r
67 #include "iba/ib_at_ioctl.h"
\r
69 C_ASSERT( sizeof(IBAT_PATH_BLOB) == sizeof(ib_path_rec_t) );
\r
74 const IN6_ADDR x_DefaultGid = {0xFE,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
\r
79 H( HANDLE h = INVALID_HANDLE_VALUE ) : m_h( h ) {};
\r
80 ~H(){ if( m_h != INVALID_HANDLE_VALUE ) CloseHandle( m_h ); }
\r
82 H& operator =(HANDLE h){ CloseHandle( m_h ); m_h = h; }
\r
83 operator HANDLE() const { return m_h; }
\r
92 __in const struct sockaddr* pSrcAddr,
\r
93 __in const struct sockaddr* pDestAddr,
\r
94 __out IBAT_PATH_BLOB* pPath
\r
97 if( pSrcAddr->sa_family != pDestAddr->sa_family )
\r
98 return E_INVALIDARG;
\r
100 H hIbatDev = CreateFileW( IBAT_WIN32_NAME,
\r
101 MAXIMUM_ALLOWED, 0, NULL,
\r
102 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
\r
103 if( hIbatDev == INVALID_HANDLE_VALUE )
\r
104 return HRESULT_FROM_WIN32( GetLastError() );
\r
107 IOCTL_IBAT_IP_TO_PORT_IN port_in;
\r
108 port_in.Version = IBAT_IOCTL_VERSION;
\r
109 if( pSrcAddr->sa_family == AF_INET )
\r
111 port_in.Address.IpVersion = 4;
\r
113 &port_in.Address.Address[12],
\r
114 &((struct sockaddr_in*)pSrcAddr)->sin_addr,
\r
115 sizeof( ((struct sockaddr_in*)pSrcAddr)->sin_addr ) );
\r
117 fLoopback = ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr ==
\r
118 ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr;
\r
122 port_in.Address.IpVersion = 6;
\r
124 port_in.Address.Address,
\r
125 &((struct sockaddr_in6*)pSrcAddr)->sin6_addr,
\r
126 sizeof(port_in.Address.Address) );
\r
127 fLoopback = IN6_ADDR_EQUAL(
\r
128 &((struct sockaddr_in6*)pDestAddr)->sin6_addr,
\r
129 &((struct sockaddr_in6*)pSrcAddr)->sin6_addr
\r
133 IBAT_PORT_RECORD port_out;
\r
135 BOOL fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_IP_TO_PORT,
\r
136 &port_in, sizeof(port_in), &port_out, sizeof(port_out), &size, NULL );
\r
139 return HRESULT_FROM_WIN32( GetLastError() );
\r
141 // Check for loopback.
\r
142 IOCTL_IBAT_MAC_TO_PATH_IN mac_in = {0};
\r
143 mac_in.Version = IBAT_IOCTL_VERSION;
\r
144 mac_in.PortGuid = port_out.PortGuid;
\r
152 ret = GetBestInterfaceEx( (struct sockaddr*)pSrcAddr, &iIf );
\r
153 if( ret != NO_ERROR )
\r
154 return HRESULT_FROM_WIN32( ret );
\r
156 // Interface indexes are not constant, so get the LUID mapping for the
\r
157 // returned interface for use in the rest of the function.
\r
158 ret = ConvertInterfaceIndexToLuid( iIf, &luid );
\r
160 } while( ret != NO_ERROR );
\r
163 MIB_IPNET_ROW2 net = {0};
\r
164 net.InterfaceLuid = luid;
\r
165 switch( pDestAddr->sa_family )
\r
168 net.Address.si_family = src.si_family = AF_INET;
\r
169 net.Address.Ipv4 = *(struct sockaddr_in*)pDestAddr;
\r
170 src.Ipv4 = *(struct sockaddr_in*)pSrcAddr;
\r
174 net.Address.si_family = src.si_family = AF_INET6;
\r
175 net.Address.Ipv6 = *(struct sockaddr_in6*)pDestAddr;
\r
176 src.Ipv6 = *(struct sockaddr_in6*)pSrcAddr;
\r
180 return E_INVALIDARG;
\r
183 bool fRetry = true;
\r
185 ret = GetIpNetEntry2( &net );
\r
186 if( ret == ERROR_NOT_FOUND )
\r
188 net.State = NlnsUnreachable;
\r
190 else if( ret != NO_ERROR )
\r
192 return HRESULT_FROM_WIN32( ret );
\r
195 switch( net.State )
\r
198 case NlnsUnreachable:
\r
199 ret = ResolveIpNetEntry2( &net, &src );
\r
200 if( ret == ERROR_BAD_NET_NAME && fRetry )
\r
205 else if( ret != NO_ERROR )
\r
207 return HRESULT_FROM_WIN32( ret );
\r
211 case NlnsReachable:
\r
212 case NlnsPermanent:
\r
215 case NlnsIncomplete:
\r
219 if( net.PhysicalAddressLength > 6 )
\r
220 return E_UNEXPECTED;
\r
222 RtlCopyMemory( mac_in.DestMac, net.PhysicalAddress, IBAT_MAC_LEN );
\r
225 fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,
\r
226 &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );
\r
228 return HRESULT_FROM_WIN32( GetLastError() );
\r
232 #else // Back compatibility with Windows Server 2003
\r
237 __in struct sockaddr_in* pDestAddr,
\r
238 __out BYTE* pDestMac
\r
243 MIB_IPNETTABLE* pTable = NULL;
\r
247 ret = GetIpNetTable( pTable, &len, FALSE );
\r
248 if( ret != ERROR_INSUFFICIENT_BUFFER )
\r
251 if( pTable != NULL )
\r
253 HeapFree( GetProcessHeap(), 0, pTable );
\r
256 pTable = (MIB_IPNETTABLE*)HeapAlloc( GetProcessHeap(), 0, len );
\r
257 } while( ret == ERROR_INSUFFICIENT_BUFFER );
\r
259 if( ret != NO_ERROR )
\r
261 if( pTable != NULL )
\r
263 HeapFree( GetProcessHeap(), 0, pTable );
\r
265 return HRESULT_FROM_WIN32( ret );
\r
268 ret = ERROR_NOT_SUPPORTED;
\r
270 for( i = 0; i < pTable->dwNumEntries; i++ )
\r
272 if( pTable->table[i].dwType == MIB_IPNET_TYPE_OTHER ||
\r
273 pTable->table[i].dwType == MIB_IPNET_TYPE_INVALID )
\r
278 if( pTable->table[i].dwAddr !=
\r
279 ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr )
\r
284 if( pTable->table[i].dwPhysAddrLen != IBAT_MAC_LEN )
\r
289 RtlCopyMemory( pDestMac, pTable->table[i].bPhysAddr, IBAT_MAC_LEN );
\r
293 HeapFree( GetProcessHeap(), 0, pTable );
\r
295 return HRESULT_FROM_WIN32( ret );
\r
300 __in const struct sockaddr* pSrcAddr,
\r
301 __in const struct sockaddr* pDestAddr,
\r
302 __out IBAT_PATH_BLOB* pPath
\r
305 if( pDestAddr->sa_family != AF_INET )
\r
308 H hIbatDev = CreateFileW( IBAT_WIN32_NAME,
\r
309 MAXIMUM_ALLOWED, 0, NULL,
\r
310 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
\r
311 if( hIbatDev == INVALID_HANDLE_VALUE )
\r
312 return HRESULT_FROM_WIN32( GetLastError() );
\r
314 IOCTL_IBAT_IP_TO_PORT_IN port_in;
\r
315 port_in.Version = IBAT_IOCTL_VERSION;
\r
316 port_in.Address.IpVersion = 4;
\r
318 &port_in.Address.Address[12],
\r
319 &((struct sockaddr_in*)pSrcAddr)->sin_addr,
\r
320 sizeof( ((struct sockaddr_in*)pSrcAddr)->sin_addr ) );
\r
322 IBAT_PORT_RECORD port_out;
\r
324 BOOL fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_IP_TO_PORT,
\r
325 &port_in, sizeof(port_in), &port_out, sizeof(port_out), &size, NULL );
\r
328 return HRESULT_FROM_WIN32( GetLastError() );
\r
330 // Check for loopback.
\r
331 IOCTL_IBAT_MAC_TO_GID_IN mac_in = {0};
\r
332 mac_in.Version = IBAT_IOCTL_VERSION;
\r
333 mac_in.PortGuid = port_out.PortGuid;
\r
335 if( ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr !=
\r
336 ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr )
\r
338 HRESULT hr = GetDestMac( (struct sockaddr_in*)pDestAddr, mac_in.DestMac );
\r
341 ULONG len = sizeof(mac_in.DestMac);
\r
342 DWORD ret = SendARP(
\r
343 ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr,
\r
344 ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr,
\r
345 (ULONG*)mac_in.DestMac,
\r
348 if( ret != NO_ERROR )
\r
349 return HRESULT_FROM_WIN32( ret );
\r
353 fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,
\r
354 &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );
\r
356 return HRESULT_FROM_WIN32( GetLastError() );
\r
369 __in const struct sockaddr* pSrcAddr,
\r
370 __in const struct sockaddr* pDestAddr,
\r
371 __out IBAT_PATH_BLOB* pPath
\r
374 return IBAT::Resolve( pSrcAddr, pDestAddr, pPath );
\r