[IBBUS] __iou/port_was_hibernated() change NTSTATUS --> ib_api_status_t to be consist...
[mirror/winof/.git] / core / ibat / user / ibat.cpp
1 /*\r
2  * Copyright (c) 2008 Microsoft Corporation.  All rights reserved.\r
3  *\r
4  * This software is available to you under the OpenIB.org BSD license\r
5  * below:\r
6  *\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
10  *\r
11  *      - Redistributions of source code must retain the above\r
12  *        copyright notice, this list of conditions and the following\r
13  *        disclaimer.\r
14  *\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
19  *\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
27  * SOFTWARE.\r
28  *\r
29  * $Id:$\r
30  */\r
31 \r
32 //\r
33 // IBAT: InfiniBand Address Translation\r
34 //\r
35 // Description:\r
36 //  Maps source & remote IP addresses (IPv4 and IPv6) to a path record.\r
37 //\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
41 //\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
46 //\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
49 //      GetIpNetTable.\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
53 //      SendARP.\r
54 //\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
57 //  the SA.\r
58 \r
59 #pragma warning( push, 3 )\r
60 #include <wtypes.h>\r
61 \r
62 #include <stdlib.h>\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
68 \r
69 C_ASSERT( sizeof(IBAT_PATH_BLOB) == sizeof(ib_path_rec_t) );\r
70 \r
71 namespace IBAT\r
72 {\r
73 \r
74     const IN6_ADDR x_DefaultGid = {0xFE,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0};\r
75 \r
76 class H\r
77 {\r
78 public:\r
79     H( HANDLE h = INVALID_HANDLE_VALUE ) : m_h( h ) {};\r
80     ~H(){ if( m_h != INVALID_HANDLE_VALUE ) CloseHandle( m_h ); }\r
81 \r
82     H& operator =(HANDLE h){ CloseHandle( m_h ); m_h = h; }\r
83     operator HANDLE() const { return m_h; }\r
84 \r
85 private:\r
86     HANDLE m_h;\r
87 };\r
88 \r
89 #if WINVER >= 0x600\r
90 HRESULT\r
91 Resolve(\r
92     __in const struct sockaddr* pSrcAddr,\r
93     __in const struct sockaddr* pDestAddr,\r
94     __out IBAT_PATH_BLOB* pPath\r
95     )\r
96 {\r
97     if( pSrcAddr->sa_family != pDestAddr->sa_family )\r
98         return E_INVALIDARG;\r
99 \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
105 \r
106     bool fLoopback;\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
110     {\r
111         port_in.Address.IpVersion = 4;\r
112         RtlCopyMemory(\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
116 \r
117         fLoopback = ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr ==\r
118             ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr;\r
119     }\r
120     else\r
121     {\r
122         port_in.Address.IpVersion = 6;\r
123         RtlCopyMemory(\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
130             ) == TRUE;\r
131     }\r
132 \r
133     IBAT_PORT_RECORD port_out;\r
134     DWORD size;\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
137 \r
138     if( !fSuccess )\r
139         return HRESULT_FROM_WIN32( GetLastError() );\r
140 \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
145     if( !fLoopback )\r
146     {\r
147         NET_LUID luid;\r
148         DWORD ret;\r
149         do\r
150         {\r
151             DWORD iIf;\r
152             ret = GetBestInterfaceEx( (struct sockaddr*)pSrcAddr, &iIf );\r
153             if( ret != NO_ERROR )\r
154                 return HRESULT_FROM_WIN32( ret );\r
155 \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
159 \r
160         } while( ret != NO_ERROR );\r
161 \r
162         SOCKADDR_INET src;\r
163         MIB_IPNET_ROW2 net = {0};\r
164         net.InterfaceLuid = luid;\r
165         switch( pDestAddr->sa_family )\r
166         {\r
167         case AF_INET:\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
171             break;\r
172 \r
173         case AF_INET6:\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
177             break;\r
178 \r
179         default:\r
180             return E_INVALIDARG;\r
181         }\r
182 \r
183         bool fRetry = true;\r
184     retry:\r
185         ret = GetIpNetEntry2( &net );\r
186         if( ret == ERROR_NOT_FOUND )\r
187         {\r
188             net.State = NlnsUnreachable;\r
189         }\r
190         else if( ret != NO_ERROR )\r
191         {\r
192             return HRESULT_FROM_WIN32( ret );\r
193         }\r
194 \r
195         switch( net.State )\r
196         {\r
197         default:\r
198         case NlnsUnreachable:\r
199             ret = ResolveIpNetEntry2( &net, &src );\r
200             if( ret == ERROR_BAD_NET_NAME && fRetry )\r
201             {\r
202                 fRetry = false;\r
203                 goto retry;\r
204             }\r
205             else if( ret != NO_ERROR )\r
206             {\r
207                 return HRESULT_FROM_WIN32( ret );\r
208             }\r
209             break;\r
210 \r
211         case NlnsReachable:\r
212         case NlnsPermanent:\r
213             break;\r
214 \r
215         case NlnsIncomplete:\r
216             return E_PENDING;\r
217         }\r
218 \r
219         if( net.PhysicalAddressLength > 6 )\r
220             return E_UNEXPECTED;\r
221 \r
222         RtlCopyMemory( mac_in.DestMac, net.PhysicalAddress, IBAT_MAC_LEN );\r
223     }\r
224 \r
225     fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,\r
226         &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );\r
227     if( !fSuccess )\r
228         return HRESULT_FROM_WIN32( GetLastError() );\r
229 \r
230     return S_OK;\r
231 }\r
232 #else   // Back compatibility with Windows Server 2003\r
233 \r
234 \r
235 static HRESULT\r
236 GetDestMac(\r
237     __in struct sockaddr_in* pDestAddr,\r
238     __out BYTE* pDestMac\r
239     )\r
240 {\r
241     DWORD ret;\r
242 \r
243     MIB_IPNETTABLE* pTable = NULL;\r
244     ULONG len = 0;\r
245     do\r
246     {\r
247         ret = GetIpNetTable( pTable, &len, FALSE );\r
248         if( ret != ERROR_INSUFFICIENT_BUFFER )\r
249             break;\r
250 \r
251         if( pTable != NULL )\r
252         {\r
253             HeapFree( GetProcessHeap(), 0, pTable );\r
254         }\r
255 \r
256         pTable = (MIB_IPNETTABLE*)HeapAlloc( GetProcessHeap(), 0, len );\r
257     } while( ret == ERROR_INSUFFICIENT_BUFFER );\r
258 \r
259     if( ret != NO_ERROR )\r
260     {\r
261         if( pTable != NULL )\r
262         {\r
263             HeapFree( GetProcessHeap(), 0, pTable );\r
264         }\r
265         return HRESULT_FROM_WIN32( ret );\r
266     }\r
267 \r
268     ret = ERROR_NOT_SUPPORTED;\r
269     DWORD i;\r
270     for( i = 0; i < pTable->dwNumEntries; i++ )\r
271     {\r
272         if( pTable->table[i].dwType == MIB_IPNET_TYPE_OTHER ||\r
273             pTable->table[i].dwType == MIB_IPNET_TYPE_INVALID )\r
274         {\r
275             continue;\r
276         }\r
277 \r
278         if( pTable->table[i].dwAddr !=\r
279             ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr )\r
280         {\r
281             continue;\r
282         }\r
283 \r
284         if( pTable->table[i].dwPhysAddrLen != IBAT_MAC_LEN )\r
285         {\r
286             continue;\r
287         }\r
288 \r
289         RtlCopyMemory( pDestMac, pTable->table[i].bPhysAddr, IBAT_MAC_LEN );\r
290         ret = S_OK;\r
291         break;\r
292     }\r
293     HeapFree( GetProcessHeap(), 0, pTable );\r
294 \r
295     return HRESULT_FROM_WIN32( ret );\r
296 }\r
297 \r
298 HRESULT\r
299 Resolve(\r
300     __in const struct sockaddr* pSrcAddr,\r
301     __in const struct sockaddr* pDestAddr,\r
302     __out IBAT_PATH_BLOB* pPath\r
303     )\r
304 {\r
305     if( pDestAddr->sa_family != AF_INET )\r
306         return E_NOTIMPL;\r
307 \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
313 \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
317     RtlCopyMemory(\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
321 \r
322     IBAT_PORT_RECORD port_out;\r
323     DWORD size;\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
326 \r
327     if( !fSuccess )\r
328         return HRESULT_FROM_WIN32( GetLastError() );\r
329 \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
334 \r
335     if( ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr !=\r
336         ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr )\r
337     {\r
338         HRESULT hr = GetDestMac( (struct sockaddr_in*)pDestAddr, mac_in.DestMac );\r
339         if( FAILED( hr ) )\r
340         {\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
346                 &len\r
347                 );\r
348             if( ret != NO_ERROR )\r
349                 return HRESULT_FROM_WIN32( ret );\r
350         }\r
351     }\r
352 \r
353     fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,\r
354         &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );\r
355     if( !fSuccess )\r
356         return HRESULT_FROM_WIN32( GetLastError() );\r
357 \r
358     return S_OK;\r
359 }\r
360 \r
361 #endif\r
362 }\r
363 \r
364 extern "C"\r
365 {\r
366 \r
367 HRESULT\r
368 IbatResolve(\r
369     __in const struct sockaddr* pSrcAddr,\r
370     __in const struct sockaddr* pDestAddr,\r
371     __out IBAT_PATH_BLOB* pPath\r
372     )\r
373 {\r
374     return IBAT::Resolve( pSrcAddr, pDestAddr, pPath );\r
375 }\r
376 \r
377 } /* extern "C" */\r