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
60 #include <windows.h>
\r
62 #include <winioctl.h>
\r
63 #pragma warning( pop )
\r
64 #include "iba/ibat.h"
\r
65 #include <iphlpapi.h>
\r
66 #include "iba/ib_at_ioctl.h"
\r
68 C_ASSERT( sizeof(IBAT_PATH_BLOB) == sizeof(ib_path_rec_t) );
\r
73 const IN6_ADDR x_DefaultGid = {0xFE,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
\r
78 H( HANDLE h = INVALID_HANDLE_VALUE ) : m_h( h ) {};
\r
79 ~H(){ if( m_h != INVALID_HANDLE_VALUE ) CloseHandle( m_h ); }
\r
81 H& operator =(HANDLE h){ CloseHandle( m_h ); m_h = h; }
\r
82 operator HANDLE() const { return m_h; }
\r
91 __in const struct sockaddr* pSrcAddr,
\r
92 __in const struct sockaddr* pDestAddr,
\r
93 __out IBAT_PATH_BLOB* pPath
\r
96 if( pSrcAddr->sa_family != pDestAddr->sa_family )
\r
97 return E_INVALIDARG;
\r
99 H hIbatDev = CreateFileW( IBAT_WIN32_NAME,
\r
100 MAXIMUM_ALLOWED, 0, NULL,
\r
101 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
\r
102 if( hIbatDev == INVALID_HANDLE_VALUE )
\r
103 return HRESULT_FROM_WIN32( GetLastError() );
\r
106 IOCTL_IBAT_IP_TO_PORT_IN port_in;
\r
107 port_in.Version = IBAT_IOCTL_VERSION;
\r
108 if( pSrcAddr->sa_family == AF_INET )
\r
110 port_in.Address.IpVersion = 4;
\r
112 &port_in.Address.Address[12],
\r
113 &((struct sockaddr_in*)pSrcAddr)->sin_addr,
\r
114 sizeof( ((struct sockaddr_in*)pSrcAddr)->sin_addr ) );
\r
116 fLoopback = ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr ==
\r
117 ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr;
\r
121 port_in.Address.IpVersion = 6;
\r
123 port_in.Address.Address,
\r
124 &((struct sockaddr_in6*)pSrcAddr)->sin6_addr,
\r
125 sizeof(port_in.Address.Address) );
\r
126 fLoopback = IN6_ADDR_EQUAL(
\r
127 &((struct sockaddr_in6*)pDestAddr)->sin6_addr,
\r
128 &((struct sockaddr_in6*)pSrcAddr)->sin6_addr
\r
132 IBAT_PORT_RECORD port_out;
\r
134 BOOL fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_IP_TO_PORT,
\r
135 &port_in, sizeof(port_in), &port_out, sizeof(port_out), &size, NULL );
\r
138 return HRESULT_FROM_WIN32( GetLastError() );
\r
140 // Check for loopback.
\r
141 IOCTL_IBAT_MAC_TO_PATH_IN mac_in = {0};
\r
142 mac_in.Version = IBAT_IOCTL_VERSION;
\r
143 mac_in.PortGuid = port_out.PortGuid;
\r
151 ret = GetBestInterfaceEx( (struct sockaddr*)pSrcAddr, &iIf );
\r
152 if( ret != NO_ERROR )
\r
153 return HRESULT_FROM_WIN32( ret );
\r
155 // Interface indexes are not constant, so get the LUID mapping for the
\r
156 // returned interface for use in the rest of the function.
\r
157 ret = ConvertInterfaceIndexToLuid( iIf, &luid );
\r
159 } while( ret != NO_ERROR );
\r
162 MIB_IPNET_ROW2 net = {0};
\r
163 net.InterfaceLuid = luid;
\r
164 switch( pDestAddr->sa_family )
\r
167 net.Address.si_family = src.si_family = AF_INET;
\r
168 net.Address.Ipv4 = *(struct sockaddr_in*)pDestAddr;
\r
169 src.Ipv4 = *(struct sockaddr_in*)pSrcAddr;
\r
173 net.Address.si_family = src.si_family = AF_INET6;
\r
174 net.Address.Ipv6 = *(struct sockaddr_in6*)pDestAddr;
\r
175 src.Ipv6 = *(struct sockaddr_in6*)pSrcAddr;
\r
179 return E_INVALIDARG;
\r
182 bool fRetry = true;
\r
184 ret = GetIpNetEntry2( &net );
\r
185 if( ret == ERROR_NOT_FOUND )
\r
187 net.State = NlnsUnreachable;
\r
189 else if( ret != NO_ERROR )
\r
191 return HRESULT_FROM_WIN32( ret );
\r
194 switch( net.State )
\r
197 case NlnsUnreachable:
\r
198 ret = ResolveIpNetEntry2( &net, &src );
\r
199 if( ret == ERROR_BAD_NET_NAME && fRetry )
\r
204 else if( ret != NO_ERROR )
\r
206 return HRESULT_FROM_WIN32( ret );
\r
210 case NlnsReachable:
\r
211 case NlnsPermanent:
\r
214 case NlnsIncomplete:
\r
218 if( net.PhysicalAddressLength > 6 )
\r
219 return E_UNEXPECTED;
\r
221 RtlCopyMemory( mac_in.DestMac, net.PhysicalAddress, IBAT_MAC_LEN );
\r
224 fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,
\r
225 &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );
\r
227 return HRESULT_FROM_WIN32( GetLastError() );
\r
231 #else // Back compatibility with Windows Server 2003
\r
236 __in struct sockaddr_in* pDestAddr,
\r
237 __out BYTE* pDestMac
\r
242 MIB_IPNETTABLE* pTable = NULL;
\r
246 ret = GetIpNetTable( pTable, &len, FALSE );
\r
247 if( ret != ERROR_INSUFFICIENT_BUFFER )
\r
250 if( pTable != NULL )
\r
252 HeapFree( GetProcessHeap(), 0, pTable );
\r
255 pTable = (MIB_IPNETTABLE*)HeapAlloc( GetProcessHeap(), 0, len );
\r
256 } while( ret == ERROR_INSUFFICIENT_BUFFER );
\r
258 if( ret != NO_ERROR )
\r
260 if( pTable != NULL )
\r
262 HeapFree( GetProcessHeap(), 0, pTable );
\r
264 return HRESULT_FROM_WIN32( ret );
\r
267 ret = ERROR_NOT_SUPPORTED;
\r
269 for( i = 0; i < pTable->dwNumEntries; i++ )
\r
271 if( pTable->table[i].dwType == MIB_IPNET_TYPE_OTHER ||
\r
272 pTable->table[i].dwType == MIB_IPNET_TYPE_INVALID )
\r
277 if( pTable->table[i].dwAddr !=
\r
278 ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr )
\r
283 if( pTable->table[i].dwPhysAddrLen != IBAT_MAC_LEN )
\r
288 RtlCopyMemory( pDestMac, pTable->table[i].bPhysAddr, IBAT_MAC_LEN );
\r
292 HeapFree( GetProcessHeap(), 0, pTable );
\r
294 return HRESULT_FROM_WIN32( ret );
\r
299 __in const struct sockaddr* pSrcAddr,
\r
300 __in const struct sockaddr* pDestAddr,
\r
301 __out IBAT_PATH_BLOB* pPath
\r
304 if( pDestAddr->sa_family != AF_INET )
\r
307 H hIbatDev = CreateFileW( IBAT_WIN32_NAME,
\r
308 MAXIMUM_ALLOWED, 0, NULL,
\r
309 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
\r
310 if( hIbatDev == INVALID_HANDLE_VALUE )
\r
311 return HRESULT_FROM_WIN32( GetLastError() );
\r
313 IOCTL_IBAT_IP_TO_PORT_IN port_in;
\r
314 port_in.Version = IBAT_IOCTL_VERSION;
\r
315 port_in.Address.IpVersion = 4;
\r
317 &port_in.Address.Address[12],
\r
318 &((struct sockaddr_in*)pSrcAddr)->sin_addr,
\r
319 sizeof( ((struct sockaddr_in*)pSrcAddr)->sin_addr ) );
\r
321 IBAT_PORT_RECORD port_out;
\r
323 BOOL fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_IP_TO_PORT,
\r
324 &port_in, sizeof(port_in), &port_out, sizeof(port_out), &size, NULL );
\r
327 return HRESULT_FROM_WIN32( GetLastError() );
\r
329 // Check for loopback.
\r
330 IOCTL_IBAT_MAC_TO_GID_IN mac_in = {0};
\r
331 mac_in.Version = IBAT_IOCTL_VERSION;
\r
332 mac_in.PortGuid = port_out.PortGuid;
\r
334 if( ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr !=
\r
335 ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr )
\r
337 HRESULT hr = GetDestMac( (struct sockaddr_in*)pDestAddr, mac_in.DestMac );
\r
340 ULONG len = sizeof(mac_in.DestMac);
\r
341 DWORD ret = SendARP(
\r
342 ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr,
\r
343 ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr,
\r
344 (ULONG*)mac_in.DestMac,
\r
347 if( ret != NO_ERROR )
\r
348 return HRESULT_FROM_WIN32( ret );
\r
352 fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,
\r
353 &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );
\r
355 return HRESULT_FROM_WIN32( GetLastError() );
\r
368 __in const struct sockaddr* pSrcAddr,
\r
369 __in const struct sockaddr* pDestAddr,
\r
370 __out IBAT_PATH_BLOB* pPath
\r
373 return IBAT::Resolve( pSrcAddr, pDestAddr, pPath );
\r