VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/net/netaddrstr2.cpp@ 87362

Last change on this file since 87362 was 87362, checked in by vboxsync, 4 years ago

IPRT: Make RTNetStrToIPv4Cidr() accept prefix specified as netmask,
either dotted-decimal or hex. The inputs we convert here are likely
coming from a user and people have different preferences. Sometimes
they even remember specific different networks in specific formats (I
know I do :). bugref:9330

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: netaddrstr2.cpp 87362 2021-01-21 22:05:41Z vboxsync $ */
2/** @file
3 * IPRT - Network Address String Handling.
4 */
5
6/*
7 * Copyright (C) 2013-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/net.h>
33
34#include <iprt/asm.h>
35#include <iprt/errcore.h>
36#include <iprt/mem.h>
37#include <iprt/string.h>
38#include <iprt/stream.h>
39#include "internal/string.h"
40
41
42DECLHIDDEN(int) rtNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr,
43 char **ppszNext)
44{
45 char *pszNext;
46 int rc;
47
48 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
49 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
50
51 rc = RTStrToUInt8Ex(pcszAddr, &pszNext, 10, &pAddr->au8[0]);
52 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
53 return VERR_INVALID_PARAMETER;
54 if (*pszNext++ != '.')
55 return VERR_INVALID_PARAMETER;
56
57 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
58 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
59 return VERR_INVALID_PARAMETER;
60 if (*pszNext++ != '.')
61 return VERR_INVALID_PARAMETER;
62
63 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
64 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
65 return VERR_INVALID_PARAMETER;
66 if (*pszNext++ != '.')
67 return VERR_INVALID_PARAMETER;
68
69 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
70 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
71 return VERR_INVALID_PARAMETER;
72
73 if (ppszNext != NULL)
74 *ppszNext = pszNext;
75 return rc;
76}
77
78
79RTDECL(int) RTNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr,
80 char **ppszNext)
81{
82 return rtNetStrToIPv4AddrEx(pcszAddr, pAddr, ppszNext);
83}
84RT_EXPORT_SYMBOL(RTNetStrToIPv4AddrEx);
85
86
87RTDECL(int) RTNetStrToIPv4Addr(const char *pcszAddr, PRTNETADDRIPV4 pAddr)
88{
89 char *pszNext;
90 int rc;
91
92 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
93 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
94
95 pcszAddr = RTStrStripL(pcszAddr);
96 rc = rtNetStrToIPv4AddrEx(pcszAddr, pAddr, &pszNext);
97 if (RT_FAILURE(rc) || rc == VWRN_TRAILING_CHARS)
98 return VERR_INVALID_PARAMETER;
99
100 return VINF_SUCCESS;
101}
102RT_EXPORT_SYMBOL(RTNetStrToIPv4Addr);
103
104
105RTDECL(bool) RTNetIsIPv4AddrStr(const char *pcszAddr)
106{
107 RTNETADDRIPV4 addrIPv4;
108 char *pszNext;
109 int rc;
110
111 if (pcszAddr == NULL)
112 return false;
113
114 rc = rtNetStrToIPv4AddrEx(pcszAddr, &addrIPv4, &pszNext);
115 if (rc != VINF_SUCCESS)
116 return false;
117
118 if (*pszNext != '\0')
119 return false;
120
121 return true;
122}
123RT_EXPORT_SYMBOL(RTNetIsIPv4AddrStr);
124
125
126RTDECL(bool) RTNetStrIsIPv4AddrAny(const char *pcszAddr)
127{
128 RTNETADDRIPV4 addrIPv4;
129 char *pszNext;
130 int rc;
131
132 if (pcszAddr == NULL)
133 return false;
134
135 pcszAddr = RTStrStripL(pcszAddr);
136 rc = rtNetStrToIPv4AddrEx(pcszAddr, &addrIPv4, &pszNext);
137 if (RT_FAILURE(rc) || rc == VWRN_TRAILING_CHARS)
138 return false;
139
140 if (addrIPv4.u != 0u) /* INADDR_ANY? */
141 return false;
142
143 return true;
144}
145RT_EXPORT_SYMBOL(RTNetStrIsIPv4AddrAny);
146
147
148RTDECL(int) RTNetMaskToPrefixIPv4(PCRTNETADDRIPV4 pMask, int *piPrefix)
149{
150 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
151
152 if (pMask->u == 0)
153 {
154 if (piPrefix != NULL)
155 *piPrefix = 0;
156 return VINF_SUCCESS;
157 }
158
159 const uint32_t uMask = RT_N2H_U32(pMask->u);
160
161 uint32_t uPrefixMask = UINT32_C(0xffffffff);
162 int iPrefixLen = 32;
163
164 while (iPrefixLen > 0)
165 {
166 if (uMask == uPrefixMask)
167 {
168 if (piPrefix != NULL)
169 *piPrefix = iPrefixLen;
170 return VINF_SUCCESS;
171 }
172
173 --iPrefixLen;
174 uPrefixMask <<= 1;
175 }
176
177 return VERR_INVALID_PARAMETER;
178}
179RT_EXPORT_SYMBOL(RTNetMaskToPrefixIPv4);
180
181
182RTDECL(int) RTNetPrefixToMaskIPv4(int iPrefix, PRTNETADDRIPV4 pMask)
183{
184 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
185
186 if (RT_UNLIKELY(iPrefix < 0 || 32 < iPrefix))
187 return VERR_INVALID_PARAMETER;
188
189 if (RT_LIKELY(iPrefix != 0))
190 pMask->u = RT_H2N_U32(UINT32_C(0xffffffff) << (32 - iPrefix));
191 else /* avoid UB in the shift */
192 pMask->u = 0;
193
194 return VINF_SUCCESS;
195}
196RT_EXPORT_SYMBOL(RTNetPrefixToMaskIPv4);
197
198
199RTDECL(int) RTNetStrToIPv4Cidr(const char *pcszAddr, PRTNETADDRIPV4 pAddr, int *piPrefix)
200{
201 RTNETADDRIPV4 Addr, Mask;
202 uint8_t u8Prefix;
203 char *pszNext;
204 int rc;
205
206 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
207 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
208 AssertPtrReturn(piPrefix, VERR_INVALID_PARAMETER);
209
210 pcszAddr = RTStrStripL(pcszAddr);
211 rc = rtNetStrToIPv4AddrEx(pcszAddr, &Addr, &pszNext);
212 if (RT_FAILURE(rc))
213 return rc;
214
215 /*
216 * If the prefix is missing, treat is as exact (/32) address
217 * specification.
218 */
219 if (*pszNext == '\0' || rc == VWRN_TRAILING_SPACES)
220 {
221 *pAddr = Addr;
222 *piPrefix = 32;
223 return VINF_SUCCESS;
224 }
225
226 /*
227 * Be flexible about the way the prefix is specified after the
228 * slash: accept both the prefix length and the netmask, and for
229 * the latter accept both dotted-decimal and hex. The inputs we
230 * convert here are likely coming from a user and people have
231 * different preferences. Sometimes they just remember specific
232 * different networks in specific formats!
233 */
234 if (*pszNext == '/')
235 ++pszNext;
236 else
237 return VERR_INVALID_PARAMETER;
238
239 /* .../0x... is a hex mask */
240 if (pszNext[0] == '0' && (pszNext[1] == 'x' || pszNext[1] == 'X'))
241 {
242 rc = RTStrToUInt32Ex(pszNext, &pszNext, 16, &Mask.u);
243 if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_SPACES)
244 Mask.u = RT_H2N_U32(Mask.u);
245 else
246 return VERR_INVALID_PARAMETER;
247
248 int iPrefix;
249 rc = RTNetMaskToPrefixIPv4(&Mask, &iPrefix);
250 if (RT_SUCCESS(rc))
251 u8Prefix = (uint8_t)iPrefix;
252 else
253 return VERR_INVALID_PARAMETER;
254 }
255 else
256 {
257 char *pszLookAhead;
258 uint32_t u32;
259 rc = RTStrToUInt32Ex(pszNext, &pszLookAhead, 10, &u32);
260
261 /* single number after the slash is prefix length */
262 if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_SPACES)
263 {
264 if (u32 <= 32)
265 u8Prefix = (uint8_t)u32;
266 else
267 return VERR_INVALID_PARAMETER;
268 }
269 /* a number followed by more stuff, may be a dotted-decimal */
270 else if (rc == VWRN_TRAILING_CHARS)
271 {
272 if (*pszLookAhead != '.') /* don't even bother checking */
273 return VERR_INVALID_PARAMETER;
274
275 rc = rtNetStrToIPv4AddrEx(pszNext, &Mask, &pszNext);
276 if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_SPACES)
277 {
278 int iPrefix;
279 rc = RTNetMaskToPrefixIPv4(&Mask, &iPrefix);
280 if (RT_SUCCESS(rc))
281 u8Prefix = (uint8_t)iPrefix;
282 else
283 return VERR_INVALID_PARAMETER;
284 }
285 else
286 return VERR_INVALID_PARAMETER;
287 }
288 /* failed to convert to number */
289 else
290 return VERR_INVALID_PARAMETER;
291 }
292
293 if (u8Prefix == 0 || u8Prefix > 32)
294 return VERR_INVALID_PARAMETER;
295
296 *pAddr = Addr;
297 *piPrefix = u8Prefix;
298 return VINF_SUCCESS;
299}
300RT_EXPORT_SYMBOL(RTNetStrToIPv4Cidr);
301
302
303static int rtNetStrToHexGroup(const char *pcszValue, char **ppszNext,
304 uint16_t *pu16)
305{
306 char *pszNext;
307 int rc;
308
309 rc = RTStrToUInt16Ex(pcszValue, &pszNext, 16, pu16);
310 if (RT_FAILURE(rc))
311 return rc;
312
313 if ( rc != VINF_SUCCESS
314 && rc != VWRN_TRAILING_CHARS
315 && rc != VWRN_TRAILING_SPACES)
316 {
317 return -rc; /* convert warning to error */
318 }
319
320 /* parser always accepts 0x prefix */
321 if (pcszValue[0] == '0' && (pcszValue[1] == 'x' || pcszValue[1] == 'X'))
322 {
323 if (pu16)
324 *pu16 = 0;
325 if (ppszNext)
326 *ppszNext = (/* UNCONST */ char *)pcszValue + 1; /* to 'x' */
327 return VWRN_TRAILING_CHARS;
328 }
329
330 /* parser accepts leading zeroes "000000f" */
331 if (pszNext - pcszValue > 4)
332 return VERR_PARSE_ERROR;
333
334 if (ppszNext)
335 *ppszNext = pszNext;
336 return rc;
337}
338
339
340/*
341 * This function deals only with the hex-group IPv6 address syntax
342 * proper (with possible embedded IPv4).
343 */
344DECLHIDDEN(int) rtNetStrToIPv6AddrBase(const char *pcszAddr, PRTNETADDRIPV6 pAddrResult,
345 char **ppszNext)
346{
347 RTNETADDRIPV6 ipv6;
348 RTNETADDRIPV4 ipv4;
349 const char *pcszPos;
350 char *pszNext;
351 int iGroup;
352 uint16_t u16;
353 int rc;
354
355 RT_ZERO(ipv6);
356
357 pcszPos = pcszAddr;
358
359 if (pcszPos[0] == ':') /* compressed zero run at the beginning? */
360 {
361 if (pcszPos[1] != ':')
362 return VERR_PARSE_ERROR;
363
364 pcszPos += 2; /* skip over "::" */
365 pszNext = (/* UNCONST */ char *)pcszPos;
366 iGroup = 1;
367 }
368 else
369 {
370 /*
371 * Scan forward until we either get complete address or find
372 * "::" compressed zero run.
373 */
374 pszNext = NULL; /* (MSC incorrectly thinks it may be used unitialized) */
375 for (iGroup = 0; iGroup < 8; ++iGroup)
376 {
377 /* check for embedded IPv4 at the end */
378 if (iGroup == 6)
379 {
380 rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
381 if (rc == VINF_SUCCESS)
382 {
383 ipv6.au32[3] = ipv4.au32[0];
384 iGroup = 8; /* filled 6 and 7 */
385 break; /* we are done */
386 }
387 }
388
389 rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
390 if (RT_FAILURE(rc))
391 return VERR_PARSE_ERROR;
392
393 ipv6.au16[iGroup] = RT_H2N_U16(u16);
394
395 if (iGroup == 7)
396 pcszPos = pszNext;
397 else
398 {
399 /* skip the colon that delimits this group */
400 if (*pszNext != ':')
401 return VERR_PARSE_ERROR;
402 pcszPos = pszNext + 1;
403
404 /* compressed zero run? */
405 if (*pcszPos == ':')
406 {
407 ++pcszPos; /* skip over :: */
408 pszNext += 2; /* skip over :: (in case we are done) */
409 iGroup += 2; /* current field and the zero in the next */
410 break;
411 }
412 }
413 }
414 }
415
416 if (iGroup != 8)
417 {
418 /*
419 * iGroup is the first group that can be filled by the part of
420 * the address after "::".
421 */
422 RTNETADDRIPV6 ipv6Tail;
423 const int iMaybeStart = iGroup;
424 int j;
425
426 RT_ZERO(ipv6Tail);
427
428 /*
429 * We try to accept longest match; we'll shift if necessary.
430 * Unlike the first loop, a failure to parse a group doesn't
431 * mean invalid address.
432 */
433 for (; iGroup < 8; ++iGroup)
434 {
435 /* check for embedded IPv4 at the end */
436 if (iGroup <= 6)
437 {
438 rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
439 if (rc == VINF_SUCCESS)
440 {
441 ipv6Tail.au16[iGroup] = ipv4.au16[0];
442 ipv6Tail.au16[iGroup + 1] = ipv4.au16[1];
443 iGroup = iGroup + 2; /* these two are done */
444 break; /* the rest is trailer */
445 }
446 }
447
448 rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
449 if (RT_FAILURE(rc))
450 break;
451
452 ipv6Tail.au16[iGroup] = RT_H2N_U16(u16);
453
454 if (iGroup == 7)
455 pcszPos = pszNext;
456 else
457 {
458 if (*pszNext != ':')
459 {
460 ++iGroup; /* this one is done */
461 break; /* the rest is trailer */
462 }
463
464 pcszPos = pszNext + 1;
465 }
466 }
467
468 for (j = 7, --iGroup; iGroup >= iMaybeStart; --j, --iGroup)
469 ipv6.au16[j] = ipv6Tail.au16[iGroup];
470 }
471
472 if (pAddrResult != NULL)
473 memcpy(pAddrResult, &ipv6, sizeof(ipv6));
474 if (ppszNext != NULL)
475 *ppszNext = pszNext;
476 return VINF_SUCCESS;
477}
478
479
480DECLHIDDEN(int) rtNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
481 char **ppszZone, char **ppszNext)
482{
483 char *pszNext, *pszZone;
484 int rc;
485
486 rc = rtNetStrToIPv6AddrBase(pcszAddr, pAddr, &pszNext);
487 if (RT_FAILURE(rc))
488 return rc;
489
490 if (*pszNext != '%') /* is there a zone id? */
491 {
492 pszZone = NULL;
493 }
494 else
495 {
496 pszZone = pszNext + 1; /* skip '%' zone id delimiter */
497 if (*pszZone == '\0')
498 return VERR_PARSE_ERROR; /* empty zone id */
499
500 /*
501 * XXX: this is speculative as zone id syntax is
502 * implementation dependent, so we kinda guess here (accepting
503 * unreserved characters from URI syntax).
504 */
505 for (pszNext = pszZone; *pszNext != '\0'; ++pszNext)
506 {
507 const char c = *pszNext;
508 if ( !('0' <= c && c <= '9')
509 && !('a' <= c && c <= 'z')
510 && !('A' <= c && c <= 'Z')
511 && c != '_'
512 && c != '.'
513 && c != '-'
514 && c != '~')
515 {
516 break;
517 }
518 }
519 }
520
521 if (ppszZone != NULL)
522 *ppszZone = pszZone;
523 if (ppszNext != NULL)
524 *ppszNext = pszNext;
525
526 if (*pszNext == '\0') /* all input string consumed */
527 return VINF_SUCCESS;
528 else
529 {
530 while (*pszNext == ' ' || *pszNext == '\t')
531 ++pszNext;
532 if (*pszNext == '\0')
533 return VWRN_TRAILING_SPACES;
534 else
535 return VWRN_TRAILING_CHARS;
536 }
537}
538
539
540RTDECL(int) RTNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
541 char **ppszNext)
542{
543 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
544 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
545
546 return rtNetStrToIPv6AddrBase(pcszAddr, pAddr, ppszNext);
547}
548RT_EXPORT_SYMBOL(RTNetStrToIPv6AddrEx);
549
550
551RTDECL(int) RTNetStrToIPv6Addr(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
552 char **ppszZone)
553{
554 int rc;
555
556 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
557 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
558 AssertPtrReturn(ppszZone, VERR_INVALID_PARAMETER);
559
560 pcszAddr = RTStrStripL(pcszAddr);
561 rc = rtNetStrToIPv6AddrEx(pcszAddr, pAddr, ppszZone, NULL);
562 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
563 return VERR_INVALID_PARAMETER;
564
565 return VINF_SUCCESS;
566}
567RT_EXPORT_SYMBOL(RTNetStrToIPv6Addr);
568
569
570RTDECL(bool) RTNetIsIPv6AddrStr(const char *pcszAddr)
571{
572 RTNETADDRIPV6 addrIPv6;
573 int rc;
574
575 if (pcszAddr == NULL)
576 return false;
577
578 rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, NULL, NULL);
579 if (rc != VINF_SUCCESS)
580 return false;
581
582 return true;
583}
584RT_EXPORT_SYMBOL(RTNetIsIPv6AddrStr);
585
586
587RTDECL(bool) RTNetStrIsIPv6AddrAny(const char *pcszAddr)
588{
589 RTNETADDRIPV6 addrIPv6;
590 char *pszZone, *pszNext;
591 int rc;
592
593 if (pcszAddr == NULL)
594 return false;
595
596 pcszAddr = RTStrStripL(pcszAddr);
597 rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, &pszZone, &pszNext);
598 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
599 return false;
600
601 if (pszZone != NULL)
602 return false;
603
604 if (addrIPv6.s.Lo != 0 || addrIPv6.s.Hi != 0) /* in6addr_any? */
605 return false;
606
607 return true;
608}
609RT_EXPORT_SYMBOL(RTNetStrIsIPv6AddrAny);
610
611
612RTDECL(int) RTNetMaskToPrefixIPv6(PCRTNETADDRIPV6 pMask, int *piPrefix)
613{
614 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
615
616 int iPrefix = 0;
617 unsigned int i;
618
619 for (i = 0; i < RT_ELEMENTS(pMask->au8); ++i)
620 {
621 int iBits;
622 switch (pMask->au8[i])
623 {
624 case 0x00: iBits = 0; break;
625 case 0x80: iBits = 1; break;
626 case 0xc0: iBits = 2; break;
627 case 0xe0: iBits = 3; break;
628 case 0xf0: iBits = 4; break;
629 case 0xf8: iBits = 5; break;
630 case 0xfc: iBits = 6; break;
631 case 0xfe: iBits = 7; break;
632 case 0xff: iBits = 8; break;
633 default: /* non-contiguous mask */
634 return VERR_INVALID_PARAMETER;
635 }
636
637 iPrefix += iBits;
638 if (iBits != 8)
639 break;
640 }
641
642 for (++i; i < RT_ELEMENTS(pMask->au8); ++i)
643 if (pMask->au8[i] != 0)
644 return VERR_INVALID_PARAMETER;
645
646 if (piPrefix != NULL)
647 *piPrefix = iPrefix;
648 return VINF_SUCCESS;
649}
650RT_EXPORT_SYMBOL(RTNetMaskToPrefixIPv6);
651
652
653RTDECL(int) RTNetPrefixToMaskIPv6(int iPrefix, PRTNETADDRIPV6 pMask)
654{
655 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
656
657 if (RT_UNLIKELY(iPrefix < 0 || 128 < iPrefix))
658 return VERR_INVALID_PARAMETER;
659
660 for (unsigned int i = 0; i < RT_ELEMENTS(pMask->au32); ++i)
661 {
662 if (iPrefix == 0)
663 {
664 pMask->au32[i] = 0;
665 }
666 else if (iPrefix >= 32)
667 {
668 pMask->au32[i] = UINT32_C(0xffffffff);
669 iPrefix -= 32;
670 }
671 else
672 {
673 pMask->au32[i] = RT_H2N_U32(UINT32_C(0xffffffff) << (32 - iPrefix));
674 iPrefix = 0;
675 }
676 }
677
678 return VINF_SUCCESS;
679}
680RT_EXPORT_SYMBOL(RTNetPrefixToMaskIPv6);
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette