e73f4d50d24ecb387a0de6b729a331192f2e7215
[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 <windows.h>\r
61 #include <stdlib.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
67 \r
68 C_ASSERT( sizeof(IBAT_PATH_BLOB) == sizeof(ib_path_rec_t) );\r
69 \r
70 namespace IBAT\r
71 {\r
72 \r
73     const IN6_ADDR x_DefaultGid = {0xFE,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0};\r
74 \r
75 class H\r
76 {\r
77 public:\r
78     H( HANDLE h = INVALID_HANDLE_VALUE ) : m_h( h ) {};\r
79     ~H(){ if( m_h != INVALID_HANDLE_VALUE ) CloseHandle( m_h ); }\r
80 \r
81     H& operator =(HANDLE h){ CloseHandle( m_h ); m_h = h; }\r
82     operator HANDLE() const { return m_h; }\r
83 \r
84 private:\r
85     HANDLE m_h;\r
86 };\r
87 \r
88 #if WINVER >= 0x600\r
89 HRESULT\r
90 Resolve(\r
91     __in const struct sockaddr* pSrcAddr,\r
92     __in const struct sockaddr* pDestAddr,\r
93     __out IBAT_PATH_BLOB* pPath\r
94     )\r
95 {\r
96     if( pSrcAddr->sa_family != pDestAddr->sa_family )\r
97         return E_INVALIDARG;\r
98 \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
104 \r
105     bool fLoopback;\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
109     {\r
110         port_in.Address.IpVersion = 4;\r
111         RtlCopyMemory(\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
115 \r
116         fLoopback = ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr ==\r
117             ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr;\r
118     }\r
119     else\r
120     {\r
121         port_in.Address.IpVersion = 6;\r
122         RtlCopyMemory(\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
129             ) == TRUE;\r
130     }\r
131 \r
132     IBAT_PORT_RECORD port_out;\r
133     DWORD size;\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
136 \r
137     if( !fSuccess )\r
138         return HRESULT_FROM_WIN32( GetLastError() );\r
139 \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
144     if( !fLoopback )\r
145     {\r
146         NET_LUID luid;\r
147         DWORD ret;\r
148         do\r
149         {\r
150             DWORD iIf;\r
151             ret = GetBestInterfaceEx( (struct sockaddr*)pSrcAddr, &iIf );\r
152             if( ret != NO_ERROR )\r
153                 return HRESULT_FROM_WIN32( ret );\r
154 \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
158 \r
159         } while( ret != NO_ERROR );\r
160 \r
161         SOCKADDR_INET src;\r
162         MIB_IPNET_ROW2 net = {0};\r
163         net.InterfaceLuid = luid;\r
164         switch( pDestAddr->sa_family )\r
165         {\r
166         case AF_INET:\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
170             break;\r
171 \r
172         case AF_INET6:\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
176             break;\r
177 \r
178         default:\r
179             return E_INVALIDARG;\r
180         }\r
181 \r
182         bool fRetry = true;\r
183     retry:\r
184         ret = GetIpNetEntry2( &net );\r
185         if( ret == ERROR_NOT_FOUND )\r
186         {\r
187             net.State = NlnsUnreachable;\r
188         }\r
189         else if( ret != NO_ERROR )\r
190         {\r
191             return HRESULT_FROM_WIN32( ret );\r
192         }\r
193 \r
194         switch( net.State )\r
195         {\r
196         default:\r
197         case NlnsUnreachable:\r
198             ret = ResolveIpNetEntry2( &net, &src );\r
199             if( ret == ERROR_BAD_NET_NAME && fRetry )\r
200             {\r
201                 fRetry = false;\r
202                 goto retry;\r
203             }\r
204             else if( ret != NO_ERROR )\r
205             {\r
206                 return HRESULT_FROM_WIN32( ret );\r
207             }\r
208             break;\r
209 \r
210         case NlnsReachable:\r
211         case NlnsPermanent:\r
212             break;\r
213 \r
214         case NlnsIncomplete:\r
215             return E_PENDING;\r
216         }\r
217 \r
218         if( net.PhysicalAddressLength > 6 )\r
219             return E_UNEXPECTED;\r
220 \r
221         RtlCopyMemory( mac_in.DestMac, net.PhysicalAddress, IBAT_MAC_LEN );\r
222     }\r
223 \r
224     fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,\r
225         &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );\r
226     if( !fSuccess )\r
227         return HRESULT_FROM_WIN32( GetLastError() );\r
228 \r
229     return S_OK;\r
230 }\r
231 #else   // Back compatibility with Windows Server 2003\r
232 \r
233 \r
234 static HRESULT\r
235 GetDestMac(\r
236     __in struct sockaddr_in* pDestAddr,\r
237     __out BYTE* pDestMac\r
238     )\r
239 {\r
240     DWORD ret;\r
241 \r
242     MIB_IPNETTABLE* pTable = NULL;\r
243     ULONG len = 0;\r
244     do\r
245     {\r
246         ret = GetIpNetTable( pTable, &len, FALSE );\r
247         if( ret != ERROR_INSUFFICIENT_BUFFER )\r
248             break;\r
249 \r
250         if( pTable != NULL )\r
251         {\r
252             HeapFree( GetProcessHeap(), 0, pTable );\r
253         }\r
254 \r
255         pTable = (MIB_IPNETTABLE*)HeapAlloc( GetProcessHeap(), 0, len );\r
256     } while( ret == ERROR_INSUFFICIENT_BUFFER );\r
257 \r
258     if( ret != NO_ERROR )\r
259     {\r
260         if( pTable != NULL )\r
261         {\r
262             HeapFree( GetProcessHeap(), 0, pTable );\r
263         }\r
264         return HRESULT_FROM_WIN32( ret );\r
265     }\r
266 \r
267     ret = ERROR_NOT_SUPPORTED;\r
268     DWORD i;\r
269     for( i = 0; i < pTable->dwNumEntries; i++ )\r
270     {\r
271         if( pTable->table[i].dwType == MIB_IPNET_TYPE_OTHER ||\r
272             pTable->table[i].dwType == MIB_IPNET_TYPE_INVALID )\r
273         {\r
274             continue;\r
275         }\r
276 \r
277         if( pTable->table[i].dwAddr !=\r
278             ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr )\r
279         {\r
280             continue;\r
281         }\r
282 \r
283         if( pTable->table[i].dwPhysAddrLen != IBAT_MAC_LEN )\r
284         {\r
285             continue;\r
286         }\r
287 \r
288         RtlCopyMemory( pDestMac, pTable->table[i].bPhysAddr, IBAT_MAC_LEN );\r
289         ret = S_OK;\r
290         break;\r
291     }\r
292     HeapFree( GetProcessHeap(), 0, pTable );\r
293 \r
294     return HRESULT_FROM_WIN32( ret );\r
295 }\r
296 \r
297 HRESULT\r
298 Resolve(\r
299     __in const struct sockaddr* pSrcAddr,\r
300     __in const struct sockaddr* pDestAddr,\r
301     __out IBAT_PATH_BLOB* pPath\r
302     )\r
303 {\r
304     if( pDestAddr->sa_family != AF_INET )\r
305         return E_NOTIMPL;\r
306 \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
312 \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
316     RtlCopyMemory(\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
320 \r
321     IBAT_PORT_RECORD port_out;\r
322     DWORD size;\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
325 \r
326     if( !fSuccess )\r
327         return HRESULT_FROM_WIN32( GetLastError() );\r
328 \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
333 \r
334     if( ((struct sockaddr_in*)pDestAddr)->sin_addr.s_addr !=\r
335         ((struct sockaddr_in*)pSrcAddr)->sin_addr.s_addr )\r
336     {\r
337         HRESULT hr = GetDestMac( (struct sockaddr_in*)pDestAddr, mac_in.DestMac );\r
338         if( FAILED( hr ) )\r
339         {\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
345                 &len\r
346                 );\r
347             if( ret != NO_ERROR )\r
348                 return HRESULT_FROM_WIN32( ret );\r
349         }\r
350     }\r
351 \r
352     fSuccess = DeviceIoControl( hIbatDev, IOCTL_IBAT_MAC_TO_PATH,\r
353         &mac_in, sizeof(mac_in), pPath, sizeof(*pPath), &size, NULL );\r
354     if( !fSuccess )\r
355         return HRESULT_FROM_WIN32( GetLastError() );\r
356 \r
357     return S_OK;\r
358 }\r
359 \r
360 #endif\r
361 }\r
362 \r
363 extern "C"\r
364 {\r
365 \r
366 HRESULT\r
367 IbatResolve(\r
368     __in const struct sockaddr* pSrcAddr,\r
369     __in const struct sockaddr* pDestAddr,\r
370     __out IBAT_PATH_BLOB* pPath\r
371     )\r
372 {\r
373     return IBAT::Resolve( pSrcAddr, pDestAddr, pPath );\r
374 }\r
375 \r
376 } /* extern "C" */\r