diff --git a/lib/libc/net/linkaddr.3 b/lib/libc/net/linkaddr.3 index c91a50c9b5b9..ceb5c4a79e9a 100644 --- a/lib/libc/net/linkaddr.3 +++ b/lib/libc/net/linkaddr.3 @@ -1,174 +1,180 @@ .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" Donn Seeley at BSDI. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" From: @(#)linkaddr.3 8.1 (Berkeley) 7/28/93 .\" -.Dd May 7, 2025 +.Dd May 9, 2025 .Dt LINK_ADDR 3 .Os .Sh NAME .Nm link_addr , .Nm link_ntoa , .Nm link_ntoa_r .Nd elementary address specification routines for link level access .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In sys/types.h .In sys/socket.h .In net/if_dl.h -.Ft void +.Ft int .Fn link_addr "const char *addr" "struct sockaddr_dl *sdl" .Ft char * .Fn link_ntoa "const struct sockaddr_dl *sdl" .Ft int .Fn link_ntoa_r "const struct sockaddr_dl *sdl" "char *obuf" "size_t *buflen" .Sh DESCRIPTION The routine .Fn link_addr -interprets character strings representing -link-level addresses, returning binary information suitable -for use in system calls. +parses a character string +.Fa addr +representing a link-level address, +and stores the resulting address in the structure pointed to by +.Fa sdl . +A link-level address consists of an optional interface name, followed by +a colon (which is required in all cases), followed by an address +consisting of either a string of hexadecimal digits, or a series of +hexadecimal octets separated by one of the characters +.Sq "." , +.Sq ":" , +or +.Sq - . .Pp The routine .Fn link_ntoa takes a link-level address and returns an .Tn ASCII string representing some of the information present, including the link level address itself, and the interface name or number, if present. The returned string is stored in a static buffer. This facility is experimental and is still subject to change. .Pp The routine .Fn link_ntoa_r behaves like .Fn link_ntoa , except the string is placed in the provided buffer instead of a static buffer. The caller should initialize .Fa buflen to the number of bytes available in .Fa obuf . On return, .Fa buflen is set to the actual number of bytes required for the output buffer, including the NUL terminator. If .Fa obuf is NULL, then .Fa buflen is set as described, but nothing is written. This may be used to determine the required length of the buffer before calling .Fn link_ntoa_r a second time. .Pp For .Fn link_addr , the string .Fa addr may contain an optional network interface identifier of the form .Dq "name unit-number" , suitable for the first argument to .Xr ifconfig 8 , followed in all cases by a colon and an interface address in the form of groups of hexadecimal digits separated by periods. Each group represents a byte of address; address bytes are filled left to right from low order bytes through high order bytes. .Pp .\" A regular expression may make this format clearer: .\" .Bd -literal -offset indent .\" ([a-z]+[0-9]+:)?[0-9a-f]+(\e.[0-9a-f]+)* .\" .Ed .\" .Pp Thus .Li le0:8.0.9.13.d.30 represents an ethernet address to be transmitted on the first Lance ethernet interface. .Sh RETURN VALUES The .Fn link_ntoa function always returns a null terminated string. .Pp The .Fn link_ntoa_r function returns 0 on success, or -1 if the provided buffer was not large enough; in the latter case, the contents of the buffer are indeterminate, but a trailing NUL will always be written if the buffer was at least one byte in size. .Pp The .Fn link_addr -function -has no return value. -(See -.Sx BUGS . ) +function returns 0 on success. +If the address did not appear to be a valid link-level address, -1 is +returned and +.Va errno +is set to indicate the error. .Sh SEE ALSO .Xr getnameinfo 3 .Sh HISTORY The .Fn link_addr and .Fn link_ntoa functions appeared in .Bx 4.3 Reno . The .Fn link_ntoa_r function appeared in .Fx 15.0 . .Sh BUGS The returned values for link_ntoa reside in a static memory area. .Pp -The function -.Fn link_addr -should diagnose improperly formed input, and there should be an unambiguous -way to recognize this. -.Pp If the .Va sdl_len field of the link socket address .Fa sdl is 0, .Fn link_ntoa will not insert a colon before the interface address bytes. If this translated address is given to .Fn link_addr without inserting an initial colon, the latter will not interpret it correctly. diff --git a/lib/libc/net/linkaddr.c b/lib/libc/net/linkaddr.c index d67d2599b2b7..c20f989143eb 100644 --- a/lib/libc/net/linkaddr.c +++ b/lib/libc/net/linkaddr.c @@ -1,229 +1,295 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)linkaddr.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include +#include #include #include -/* States*/ -#define NAMING 0 -#define GOTONE 1 -#define GOTTWO 2 -#define RESET 3 -/* Inputs */ -#define DIGIT (4*0) -#define END (4*1) -#define DELIM (4*2) -#define LETTER (4*3) - -void +int link_addr(const char *addr, struct sockaddr_dl *sdl) { char *cp = sdl->sdl_data; char *cplim = sdl->sdl_len + (char *)sdl; - int byte = 0, state = NAMING, new; + const char *nptr; + size_t newsize; + int error = 0; + char delim = 0; + /* Initialise the sdl to zero, except for sdl_len. */ bzero((char *)&sdl->sdl_family, sdl->sdl_len - 1); sdl->sdl_family = AF_LINK; - do { - state &= ~LETTER; - if ((*addr >= '0') && (*addr <= '9')) { - new = *addr - '0'; - } else if ((*addr >= 'a') && (*addr <= 'f')) { - new = *addr - 'a' + 10; - } else if ((*addr >= 'A') && (*addr <= 'F')) { - new = *addr - 'A' + 10; - } else if (*addr == 0) { - state |= END; - } else if (state == NAMING && - (((*addr >= 'A') && (*addr <= 'Z')) || - ((*addr >= 'a') && (*addr <= 'z')))) - state |= LETTER; - else - state |= DELIM; - addr++; - switch (state /* | INPUT */) { - case NAMING | DIGIT: - case NAMING | LETTER: - *cp++ = addr[-1]; - continue; - case NAMING | DELIM: - state = RESET; - sdl->sdl_nlen = cp - sdl->sdl_data; - continue; - case GOTTWO | DIGIT: - *cp++ = byte; - /* FALLTHROUGH */ - case RESET | DIGIT: - state = GOTONE; - byte = new; - continue; - case GOTONE | DIGIT: - state = GOTTWO; - byte = new + (byte << 4); - continue; - default: /* | DELIM */ - state = RESET; - *cp++ = byte; - byte = 0; - continue; - case GOTONE | END: - case GOTTWO | END: - *cp++ = byte; - /* FALLTHROUGH */ - case RESET | END: + + /* + * Everything up to the first ':' is the interface name. Usually the + * ':' should always be present even if there's no interface name, but + * since this interface was poorly specified in the past, accept a + * missing colon as meaning no interface name. + */ + if ((nptr = strchr(addr, ':')) != NULL) { + size_t namelen = nptr - addr; + + /* Ensure the sdl is large enough to store the name. */ + if (namelen > cplim - cp) { + errno = ENOSPC; + return (-1); + } + + memcpy(cp, addr, namelen); + cp += namelen; + sdl->sdl_nlen = namelen; + /* Skip the interface name and the colon. */ + addr += namelen + 1; + } + + /* + * The remainder of the string should be hex digits representing the + * address, with optional delimiters. Each two hex digits form one + * octet, but octet output can be forced using a delimiter, so we accept + * a long string of hex digits, or a mix of delimited and undelimited + * digits like "1122.3344.5566", or delimited one- or two-digit octets + * like "1.22.3". + * + * If anything fails at this point, exit the loop so we set sdl_alen and + * sdl_len based on whatever we did manage to parse. This preserves + * compatibility with the 4.3BSD version of link_addr, which had no way + * to indicate an error and would just return. + */ +#define DIGIT(c) \ + (((c) >= '0' && (c) <= '9') ? ((c) - '0') \ + : ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) \ + : ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) \ + : (-1)) +#define ISDELIM(c) (((c) == '.' || (c) == ':' || (c) == '-') && \ + (delim == 0 || delim == (c))) + + for (;;) { + int digit, digit2; + + /* + * Treat any leading delimiters as empty bytes. This supports + * the (somewhat obsolete) form of Ethernet addresses with empty + * octets, e.g. "1::3:4:5:6". + */ + while (ISDELIM(*addr) && cp < cplim) { + delim = *addr++; + *cp++ = 0; + } + + /* Did we reach the end of the string? */ + if (*addr == '\0') + break; + + /* + * If not, the next character must be a digit, so make sure we + * have room for at least one more octet. + */ + + if (cp >= cplim) { + error = ENOSPC; break; } - break; - } while (cp < cplim); + + if ((digit = DIGIT(*addr)) == -1) { + error = EINVAL; + break; + } + + ++addr; + + /* If the next character is another digit, consume it. */ + if ((digit2 = DIGIT(*addr)) != -1) { + digit = (digit << 4) | digit2; + ++addr; + } + + if (ISDELIM(*addr)) { + /* + * If the digit is followed by a delimiter, write it + * and consume the delimiter. + */ + delim = *addr++; + *cp++ = digit; + } else if (DIGIT(*addr) != -1) { + /* + * If two digits are followed by a third digit, treat + * the two digits we have as a single octet and + * continue. + */ + *cp++ = digit; + } else if (*addr == '\0') { + /* If the digit is followed by EOS, we're done. */ + *cp++ = digit; + break; + } else { + /* Otherwise, the input was invalid. */ + error = EINVAL; + break; + } + } +#undef DIGIT +#undef ISDELIM + + /* How many bytes did we write to the address? */ sdl->sdl_alen = cp - LLADDR(sdl); - new = cp - (char *)sdl; - if (new > sizeof(*sdl)) - sdl->sdl_len = new; - return; + + /* + * The user might have given us an sdl which is larger than sizeof(sdl); + * in that case, record the actual size of the new sdl. + */ + newsize = cp - (char *)sdl; + if (newsize > sizeof(*sdl)) + sdl->sdl_len = (u_char)newsize; + + if (error == 0) + return (0); + + errno = error; + return (-1); } + char * link_ntoa(const struct sockaddr_dl *sdl) { static char obuf[64]; size_t buflen; _Static_assert(sizeof(obuf) >= IFNAMSIZ + 20, "obuf is too small"); /* * Ignoring the return value of link_ntoa_r() is safe here because it * always writes the terminating NUL. This preserves the traditional * behaviour of link_ntoa(). */ buflen = sizeof(obuf); (void)link_ntoa_r(sdl, obuf, &buflen); return obuf; } int link_ntoa_r(const struct sockaddr_dl *sdl, char *obuf, size_t *buflen) { static const char hexlist[] = "0123456789abcdef"; char *out; const u_char *in, *inlim; int namelen, i, rem; size_t needed; assert(sdl); assert(buflen); /* obuf may be null */ needed = 1; /* 1 for the NUL */ out = obuf; if (obuf) rem = *buflen; else rem = 0; /* * Check if at least n bytes are available in the output buffer, plus 1 for the * trailing NUL. If not, set rem = 0 so we stop writing. * Either way, increment needed by the amount we would have written. */ #define CHECK(n) do { \ if ((SIZE_MAX - (n)) >= needed) \ needed += (n); \ if (rem >= ((n) + 1)) \ rem -= (n); \ else \ rem = 0; \ } while (0) /* * Write the char c to the output buffer, unless the buffer is full. * Note that if obuf is NULL, rem is always zero. */ #define OUT(c) do { \ if (rem > 0) \ *out++ = (c); \ } while (0) namelen = (sdl->sdl_nlen <= IFNAMSIZ) ? sdl->sdl_nlen : IFNAMSIZ; if (namelen > 0) { CHECK(namelen); if (rem > 0) { bcopy(sdl->sdl_data, out, namelen); out += namelen; } if (sdl->sdl_alen > 0) { CHECK(1); OUT(':'); } } in = (const u_char *)LLADDR(sdl); inlim = in + sdl->sdl_alen; while (in < inlim) { if (in != (const u_char *)LLADDR(sdl)) { CHECK(1); OUT('.'); } i = *in++; if (i > 0xf) { CHECK(2); OUT(hexlist[i >> 4]); OUT(hexlist[i & 0xf]); } else { CHECK(1); OUT(hexlist[i]); } } #undef CHECK #undef OUT /* * We always leave enough room for the NUL if possible, but the user * might have passed a NULL or zero-length buffer. */ if (out && *buflen) *out = '\0'; *buflen = needed; return ((rem > 0) ? 0 : -1); } diff --git a/lib/libc/tests/net/link_addr_test.cc b/lib/libc/tests/net/link_addr_test.cc index ea8f64c6723b..b973b924dc13 100644 --- a/lib/libc/tests/net/link_addr_test.cc +++ b/lib/libc/tests/net/link_addr_test.cc @@ -1,382 +1,532 @@ /* * Copyright (c) 2025 Lexi Winter * * SPDX-License-Identifier: ISC */ /* * Tests for link_addr() and link_ntoa(). * * link_addr converts a string representing an (optionally null) interface name * followed by an Ethernet address into a sockaddr_dl. The expected format is * "[ifname]:lladdr". This means if ifname is not specified, the leading colon * is still required. * * link_ntoa does the inverse of link_addr, returning a string representation * of the address. * * Note that the output format of link_ntoa is not valid input for link_addr * since the leading colon may be omitted. This is by design. */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::literals; /* * Define operator== and operator<< for ether_addr so we can use them in * ATF_EXPECT_EQ expressions. */ bool operator==(ether_addr a, ether_addr b) { return (std::ranges::equal(a.octet, b.octet)); } std::ostream & operator<<(std::ostream &s, ether_addr a) { for (unsigned i = 0; i < ETHER_ADDR_LEN; ++i) { if (i > 0) s << ":"; s << std::format("{:02x}", static_cast(a.octet[i])); } return (s); } /* * Create a sockaddr_dl from a string using link_addr(), and ensure the * returned struct looks valid. */ sockaddr_dl make_linkaddr(const std::string &addr) { auto sdl = sockaddr_dl{}; + int ret; sdl.sdl_len = sizeof(sdl); - ::link_addr(addr.c_str(), &sdl); + ret = ::link_addr(addr.c_str(), &sdl); + ATF_REQUIRE_EQ(0, ret); ATF_REQUIRE_EQ(AF_LINK, static_cast(sdl.sdl_family)); ATF_REQUIRE_EQ(true, sdl.sdl_len > 0); ATF_REQUIRE_EQ(true, sdl.sdl_nlen >= 0); return (sdl); } /* * Return the data stored in a sockaddr_dl as a span. */ std::span data(const sockaddr_dl &sdl) { // sdl_len is the entire structure, but we only want the length of the // data area. auto dlen = sdl.sdl_len - offsetof(sockaddr_dl, sdl_data); return {&sdl.sdl_data[0], dlen}; } /* * Return the interface name stored in a sockaddr_dl as a string. */ std::string_view ifname(const sockaddr_dl &sdl) { auto name = data(sdl).subspan(0, sdl.sdl_nlen); return {name.begin(), name.end()}; } /* * Return the Ethernet address stored in a sockaddr_dl as an ether_addr. */ ether_addr addr(const sockaddr_dl &sdl) { ether_addr ret{}; ATF_REQUIRE_EQ(ETHER_ADDR_LEN, sdl.sdl_alen); std::ranges::copy(data(sdl).subspan(sdl.sdl_nlen, ETHER_ADDR_LEN), &ret.octet[0]); return (ret); } /* * Return the link address stored in a sockaddr_dl as a span of octets. */ std::span lladdr(const sockaddr_dl &sdl) { auto data = reinterpret_cast(LLADDR(&sdl)); return {data, data + sdl.sdl_alen}; } /* * Some sample addresses we use for testing. Include at least one address for * each format we want to support. */ struct test_address { std::string input; /* value passed to link_addr */ std::string ntoa; /* expected return from link_ntoa */ ether_addr addr{}; /* expected return from link_addr */ }; std::vector test_addresses{ // No delimiter {"001122334455"s, "0.11.22.33.44.55", ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, // Colon delimiter {"00:11:22:33:44:55"s, "0.11.22.33.44.55", ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, // Dash delimiter {"00-11-22-33-44-55"s, "0.11.22.33.44.55", ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, // Period delimiter (link_ntoa format) {"00.11.22.33.44.55"s, "0.11.22.33.44.55", ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, // Period delimiter (Cisco format) {"0011.2233.4455"s, "0.11.22.33.44.55", ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, // An addresses without leading zeroes. {"0:1:02:30:4:55"s, "0.1.2.30.4.55", ether_addr{0x00, 0x01, 0x02, 0x30, 0x04, 0x55}}, // An address with some uppercase letters. {"AA:B:cC:Dd:e0:1f"s, "aa.b.cc.dd.e0.1f", ether_addr{0xaa, 0x0b, 0xcc, 0xdd, 0xe0, 0x1f}}, // Addresses composed only of letters, to make sure they're not // confused with an interface name. {"aabbccddeeff"s, "aa.bb.cc.dd.ee.ff", ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, {"aa:bb:cc:dd:ee:ff"s, "aa.bb.cc.dd.ee.ff", ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, + + // Address with a blank octet; this is an old form of Ethernet address. + {"00:11::33:44:55"s, "0.11.0.33.44.55", + ether_addr{0x00, 0x11, 0x00, 0x33, 0x44, 0x55}}, }; /* * Test without an interface name. */ ATF_TEST_CASE_WITHOUT_HEAD(basic) ATF_TEST_CASE_BODY(basic) { for (const auto &ta : test_addresses) { // This does basic tests on the returned value. auto sdl = make_linkaddr(":" + ta.input); // Check the ifname and address. ATF_REQUIRE_EQ(""s, ifname(sdl)); ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast(sdl.sdl_alen)); ATF_REQUIRE_EQ(ta.addr, addr(sdl)); // Check link_ntoa returns the expected value. auto ntoa = std::string(::link_ntoa(&sdl)); ATF_REQUIRE_EQ(ta.ntoa, ntoa); } } /* * Test with an interface name. */ ATF_TEST_CASE_WITHOUT_HEAD(ifname) ATF_TEST_CASE_BODY(ifname) { for (const auto &ta : test_addresses) { auto sdl = make_linkaddr("ix0:" + ta.input); ATF_REQUIRE_EQ("ix0", ifname(sdl)); ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast(sdl.sdl_alen)); ATF_REQUIRE_EQ(ta.addr, addr(sdl)); auto ntoa = std::string(::link_ntoa(&sdl)); ATF_REQUIRE_EQ("ix0:" + ta.ntoa, ntoa); } } +/* + * Test with some invalid addresses. + */ +ATF_TEST_CASE_WITHOUT_HEAD(invalid) +ATF_TEST_CASE_BODY(invalid) +{ + auto const invalid_addresses = std::vector{ + // Invalid separator + ":1/2/3"s, + "ix0:1/2/3"s, + + // Multiple different separators + ":1.2-3"s, + "ix0:1.2-3"s, + + // An IP address + ":10.1.2.200/28"s, + "ix0:10.1.2.200/28"s, + + // Valid address followed by garbage + ":1.2.3xxx"s, + ":1.2.3.xxx"s, + "ix0:1.2.3xxx"s, + "ix0:1.2.3.xxx"s, + }; + + for (auto const &addr : invalid_addresses) { + int ret; + + auto sdl = sockaddr_dl{}; + sdl.sdl_len = sizeof(sdl); + + ret = link_addr(addr.c_str(), &sdl); + ATF_REQUIRE_EQ(-1, ret); + } +} + /* * Test some non-Ethernet addresses. */ ATF_TEST_CASE_WITHOUT_HEAD(nonether) ATF_TEST_CASE_BODY(nonether) { sockaddr_dl sdl; /* A short address */ sdl = make_linkaddr(":1:23:cc"); ATF_REQUIRE_EQ("", ifname(sdl)); ATF_REQUIRE_EQ("1.23.cc"s, ::link_ntoa(&sdl)); ATF_REQUIRE_EQ(3, sdl.sdl_alen); ATF_REQUIRE_EQ(true, std::ranges::equal(std::vector{0x01u, 0x23u, 0xccu}, lladdr(sdl))); /* A long address */ sdl = make_linkaddr(":1:23:cc:a:b:c:d:e:f"); ATF_REQUIRE_EQ("", ifname(sdl)); ATF_REQUIRE_EQ("1.23.cc.a.b.c.d.e.f"s, ::link_ntoa(&sdl)); ATF_REQUIRE_EQ(9, sdl.sdl_alen); ATF_REQUIRE_EQ(true, std::ranges::equal( std::vector{0x01u, 0x23u, 0xccu, 0xau, 0xbu, 0xcu, 0xdu, 0xeu, 0xfu}, lladdr(sdl))); } +/* + * Test link_addr behaviour with undersized buffers. + */ +ATF_TEST_CASE_WITHOUT_HEAD(smallbuf) +ATF_TEST_CASE_BODY(smallbuf) +{ + static constexpr auto garbage = std::byte{0xcc}; + auto buf = std::vector(); + sockaddr_dl *sdl; + int ret; + + /* + * Make an sdl with an sdl_data member of the appropriate size, and + * place it in buf. Ensure it's followed by a trailing byte of garbage + * so we can test that link_addr doesn't write past the end. + */ + auto mksdl = [&buf](std::size_t datalen) -> sockaddr_dl * { + auto actual_size = datalen + offsetof(sockaddr_dl, sdl_data); + + buf.resize(actual_size + 1); + std::ranges::fill(buf, garbage); + + auto *sdl = new(reinterpret_cast(&buf[0])) + sockaddr_dl; + sdl->sdl_len = actual_size; + return (sdl); + }; + + /* An sdl large enough to store the interface name */ + sdl = mksdl(3); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ(3, sdl->sdl_nlen); + ATF_REQUIRE_EQ("ix0", ifname(*sdl)); + ATF_REQUIRE_EQ(0, static_cast(sdl->sdl_alen)); + + /* + * For these tests, test both with and without an interface name, since + * that will overflow the buffer in different places. + */ + + /* An empty sdl. Nothing may grow on this cursed ground. */ + + sdl = mksdl(0); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ(0, sdl->sdl_nlen); + ATF_REQUIRE_EQ(0, static_cast(sdl->sdl_alen)); + + sdl = mksdl(0); + ret = link_addr(":1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ(0, sdl->sdl_nlen); + ATF_REQUIRE_EQ(0, static_cast(sdl->sdl_alen)); + + /* + * An sdl large enough to store the interface name and two octets of the + * address. + */ + + sdl = mksdl(5); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ("ix0", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02 }, lladdr(*sdl))); + + sdl = mksdl(2); + ret = link_addr(":1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ("", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02 }, lladdr(*sdl))); + + /* + * An sdl large enough to store the entire address. + */ + + sdl = mksdl(6); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ("ix0", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl))); + + sdl = mksdl(3); + ret = link_addr(":1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ("", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl))); +} + /* * Test an extremely long address which would overflow link_ntoa's internal * buffer. It should handle this by truncating the output. * (Test for SA-16:37.libc / CVE-2016-6559.) */ ATF_TEST_CASE_WITHOUT_HEAD(overlong) ATF_TEST_CASE_BODY(overlong) { auto sdl = make_linkaddr( ":01.02.03.04.05.06.07.08.09.0a.0b.0c.0d.0e.0f." "11.12.13.14.15.16.17.18.19.1a.1b.1c.1d.1e.1f." "22.22.23.24.25.26.27.28.29.2a.2b.2c.2d.2e.2f"); ATF_REQUIRE_EQ( "1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.11.12.13.14.15.16.17.18.19.1a.1b."s, ::link_ntoa(&sdl)); } /* * Test link_ntoa_r, the re-entrant version of link_ntoa(). */ ATF_TEST_CASE_WITHOUT_HEAD(link_ntoa_r) ATF_TEST_CASE_BODY(link_ntoa_r) { static constexpr char garbage = 0x41; std::vector buf; sockaddr_dl sdl; size_t len; int ret; // Return the contents of buf as a string, using the NUL terminator to // determine length. This is to ensure we're using the return value in // the same way C code would, but we do a bit more verification to // elicit a test failure rather than a SEGV if it's broken. auto bufstr = [&buf]() -> std::string_view { // Find the NUL. auto end = std::ranges::find(buf, '\0'); ATF_REQUIRE(end != buf.end()); // Intentionally chopping the NUL off. return {begin(buf), end}; }; // Resize the buffer and set the contents to a known garbage value, so // we don't accidentally have a NUL in the right place when link_ntoa_r // didn't put it there. auto resetbuf = [&buf, &len](std::size_t size) { len = size; buf.resize(len); std::ranges::fill(buf, garbage); }; // Test a short address with a large buffer. sdl = make_linkaddr("ix0:1.2.3"); resetbuf(64); ret = ::link_ntoa_r(&sdl, &buf[0], &len); ATF_REQUIRE_EQ(0, ret); ATF_REQUIRE_EQ(10, len); ATF_REQUIRE_EQ("ix0:1.2.3"s, bufstr()); // Test a buffer which is exactly the right size. sdl = make_linkaddr("ix0:1.2.3"); resetbuf(10); ret = ::link_ntoa_r(&sdl, &buf[0], &len); ATF_REQUIRE_EQ(0, ret); ATF_REQUIRE_EQ(10, len); ATF_REQUIRE_EQ("ix0:1.2.3"sv, bufstr()); // Test various short buffers, using a table of buffer length and the // output we expect. All of these should produce valid but truncated // strings, with a trailing NUL and with buflen set correctly. auto buftests = std::vector>{ {1u, ""sv}, {2u, ""sv}, {3u, ""sv}, {4u, "ix0"sv}, {5u, "ix0:"sv}, {6u, "ix0:1"sv}, {7u, "ix0:1."sv}, {8u, "ix0:1.2"sv}, {9u, "ix0:1.2."sv}, }; for (auto const &[buflen, expected] : buftests) { sdl = make_linkaddr("ix0:1.2.3"); resetbuf(buflen); ret = ::link_ntoa_r(&sdl, &buf[0], &len); ATF_REQUIRE_EQ(-1, ret); ATF_REQUIRE_EQ(10, len); ATF_REQUIRE_EQ(expected, bufstr()); } // Test a NULL buffer, which should just set buflen. sdl = make_linkaddr("ix0:1.2.3"); len = 0; ret = ::link_ntoa_r(&sdl, NULL, &len); ATF_REQUIRE_EQ(-1, ret); ATF_REQUIRE_EQ(10, len); // A NULL buffer with a non-zero length should also be accepted. sdl = make_linkaddr("ix0:1.2.3"); len = 64; ret = ::link_ntoa_r(&sdl, NULL, &len); ATF_REQUIRE_EQ(-1, ret); ATF_REQUIRE_EQ(10, len); // Test a non-NULL buffer, but with a length of zero. sdl = make_linkaddr("ix0:1.2.3"); resetbuf(1); len = 0; ret = ::link_ntoa_r(&sdl, &buf[0], &len); ATF_REQUIRE_EQ(-1, ret); ATF_REQUIRE_EQ(10, len); // Check we really didn't write anything. ATF_REQUIRE_EQ(garbage, buf[0]); // Test a buffer which would be truncated in the middle of a two-digit // hex octet, which should not write the truncated octet at all. sdl = make_linkaddr("ix0:1.22.3"); resetbuf(8); ret = ::link_ntoa_r(&sdl, &buf[0], &len); ATF_REQUIRE_EQ(-1, ret); ATF_REQUIRE_EQ(11, len); ATF_REQUIRE_EQ("ix0:1."sv, bufstr()); } ATF_INIT_TEST_CASES(tcs) { ATF_ADD_TEST_CASE(tcs, basic); ATF_ADD_TEST_CASE(tcs, ifname); + ATF_ADD_TEST_CASE(tcs, smallbuf); + ATF_ADD_TEST_CASE(tcs, invalid); ATF_ADD_TEST_CASE(tcs, nonether); ATF_ADD_TEST_CASE(tcs, overlong); ATF_ADD_TEST_CASE(tcs, link_ntoa_r); } diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c index cd5422c5541c..990226abd435 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -1,197 +1,198 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static struct ifreq link_ridreq; extern char *f_ether; static void link_status(int s __unused, const struct ifaddrs *ifa) { /* XXX no const 'cuz LLADDR is defined wrong */ struct sockaddr_dl *sdl; char *ether_format, *format_char; struct ifreq ifr; int n, rc, sock_hw; static const u_char laggaddr[6] = {0}; sdl = (struct sockaddr_dl *) ifa->ifa_addr; if (sdl == NULL || sdl->sdl_alen == 0) return; if ((sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_L2VLAN || sdl->sdl_type == IFT_BRIDGE) && sdl->sdl_alen == ETHER_ADDR_LEN) { ether_format = ether_ntoa((struct ether_addr *)LLADDR(sdl)); if (f_ether != NULL && strcmp(f_ether, "dash") == 0) { while ((format_char = strchr(ether_format, ':')) != NULL) { *format_char = '-'; } } printf("\tether %s\n", ether_format); } else { n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; printf("\tlladdr %s\n", link_ntoa(sdl) + n); } /* * Best-effort (i.e. failures are silent) to get original * hardware address, as read by NIC driver at attach time. Only * applies to Ethernet NICs (IFT_ETHER). However, laggX * interfaces claim to be IFT_ETHER, and re-type their component * Ethernet NICs as IFT_IEEE8023ADLAG. So, check for both. If * the MAC is zeroed, then it's actually a lagg. */ if ((sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_IEEE8023ADLAG) || sdl->sdl_alen != ETHER_ADDR_LEN) return; strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(ifa->ifa_addr->sa_len)); ifr.ifr_addr.sa_family = AF_LOCAL; if ((sock_hw = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_LOCAL,SOCK_DGRAM)"); return; } rc = ioctl(sock_hw, SIOCGHWADDR, &ifr); close(sock_hw); if (rc != 0) return; /* * If this is definitely a lagg device or the hwaddr * matches the link addr, don't bother. */ if (memcmp(ifr.ifr_addr.sa_data, laggaddr, sdl->sdl_alen) == 0 || memcmp(ifr.ifr_addr.sa_data, LLADDR(sdl), sdl->sdl_alen) == 0) goto pcp; ether_format = ether_ntoa((const struct ether_addr *) &ifr.ifr_addr.sa_data); if (f_ether != NULL && strcmp(f_ether, "dash") == 0) { for (format_char = strchr(ether_format, ':'); format_char != NULL; format_char = strchr(ether_format, ':')) *format_char = '-'; } printf("\thwaddr %s\n", ether_format); pcp: if (ioctl(s, SIOCGLANPCP, (caddr_t)&ifr) == 0 && ifr.ifr_lan_pcp != IFNET_PCP_NONE) printf("\tpcp %d\n", ifr.ifr_lan_pcp); } static void link_getaddr(const char *addr, int which) { char *temp; struct sockaddr_dl sdl; struct sockaddr *sa = &link_ridreq.ifr_addr; if (which != ADDR) errx(1, "can't set link-level netmask or broadcast"); if (!strcmp(addr, "random")) { sdl.sdl_len = sizeof(sdl); sdl.sdl_alen = ETHER_ADDR_LEN; sdl.sdl_nlen = 0; sdl.sdl_family = AF_LINK; arc4random_buf(&sdl.sdl_data, ETHER_ADDR_LEN); /* Non-multicast and claim it is locally administered. */ sdl.sdl_data[0] &= 0xfc; sdl.sdl_data[0] |= 0x02; } else { if ((temp = malloc(strlen(addr) + 2)) == NULL) errx(1, "malloc failed"); temp[0] = ':'; strcpy(temp + 1, addr); sdl.sdl_len = sizeof(sdl); - link_addr(temp, &sdl); + if (link_addr(temp, &sdl) == -1) + errx(1, "malformed link-level address"); free(temp); } if (sdl.sdl_alen > sizeof(sa->sa_data)) errx(1, "malformed link-level address"); sa->sa_family = AF_LINK; sa->sa_len = sdl.sdl_alen; bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); } static struct afswtch af_link = { .af_name = "link", .af_af = AF_LINK, .af_status = link_status, .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, }; static struct afswtch af_ether = { .af_name = "ether", .af_af = AF_LINK, .af_status = link_status, .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, }; static struct afswtch af_lladdr = { .af_name = "lladdr", .af_af = AF_LINK, .af_status = link_status, .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, }; static __constructor void link_ctor(void) { af_register(&af_link); af_register(&af_ether); af_register(&af_lladdr); } diff --git a/sys/net/if_dl.h b/sys/net/if_dl.h index 2c1e9bd9dd87..082ea63f3ff3 100644 --- a/sys/net/if_dl.h +++ b/sys/net/if_dl.h @@ -1,96 +1,96 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_dl.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NET_IF_DL_H_ #define _NET_IF_DL_H_ #include /* * A Link-Level Sockaddr may specify the interface in one of two * ways: either by means of a system-provided index number (computed * anew and possibly differently on every reboot), or by a human-readable * string such as "il0" (for managerial convenience). * * Census taking actions, such as something akin to SIOCGCONF would return * both the index and the human name. * * High volume transactions (such as giving a link-level ``from'' address * in a recvfrom or recvmsg call) may be likely only to provide the indexed * form, (which requires fewer copy operations and less space). * * The form and interpretation of the link-level address is purely a matter * of convention between the device driver and its consumers; however, it is * expected that all drivers for an interface of a given if_type will agree. */ /* * Structure of a Link-Level sockaddr: */ struct sockaddr_dl { u_char sdl_len; /* Total length of sockaddr */ u_char sdl_family; /* AF_LINK */ u_short sdl_index; /* if != 0, system given index for interface */ u_char sdl_type; /* interface type */ u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */ u_char sdl_alen; /* link level address length */ u_char sdl_slen; /* link layer selector length */ char sdl_data[46]; /* minimum work area, can be larger; contains both if name and ll address */ }; #define LLADDR(s) (&(s)->sdl_data[(s)->sdl_nlen]) #define CLLADDR(s) ((const char *)LLADDR(s)) #define LLINDEX(s) ((s)->sdl_index) #ifdef _KERNEL struct ifnet; struct sockaddr_dl *link_alloc_sdl(size_t size, int flags); void link_free_sdl(struct sockaddr *sa); struct sockaddr_dl *link_init_sdl(struct ifnet *ifp, struct sockaddr *paddr, u_char iftypes); #else /* !_KERNEL */ #include __BEGIN_DECLS -void link_addr(const char *, struct sockaddr_dl *); +int link_addr(const char *, struct sockaddr_dl *); char *link_ntoa(const struct sockaddr_dl *); int link_ntoa_r(const struct sockaddr_dl *, char *, size_t *); __END_DECLS #endif /* !_KERNEL */ #endif diff --git a/sys/sys/param.h b/sys/sys/param.h index 119af606d1d5..c3da339aa15d 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -1,375 +1,375 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)param.h 8.3 (Berkeley) 4/4/95 */ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ #include #define BSD 199506 /* System version (year & month). */ #define BSD4_3 1 #define BSD4_4 1 /* * __FreeBSD_version numbers are documented in the Porter's Handbook. * If you bump the version for any reason, you should update the documentation * there. * Currently this lives here in the doc/ repository: * * documentation/content/en/books/porters-handbook/versions/_index.adoc * * scheme is: Rxx * 'R' is in the range 0 to 4 if this is a release branch or * X.0-CURRENT before releng/X.0 is created, otherwise 'R' is * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1305501 /* Master, propagated to newvers */ +#define __FreeBSD_version 1305502 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, * which by definition is always true on FreeBSD. This macro is also defined * on other systems that use the kernel of FreeBSD, such as GNU/kFreeBSD. * * It is tempting to use this macro in userland code when we want to enable * kernel-specific routines, and in fact it's fine to do this in code that * is part of FreeBSD itself. However, be aware that as presence of this * macro is still not widespread (e.g. older FreeBSD versions, 3rd party * compilers, etc), it is STRONGLY DISCOURAGED to check for this macro in * external applications without also checking for __FreeBSD__ as an * alternative. */ #undef __FreeBSD_kernel__ #define __FreeBSD_kernel__ #if defined(_KERNEL) || defined(_WANT_P_OSREL) #define P_OSREL_SIGWAIT 700000 #define P_OSREL_SIGSEGV 700004 #define P_OSREL_MAP_ANON 800104 #define P_OSREL_MAP_FSTRICT 1100036 #define P_OSREL_SHUTDOWN_ENOTCONN 1100077 #define P_OSREL_MAP_GUARD 1200035 #define P_OSREL_WRFSBASE 1200041 #define P_OSREL_CK_CYLGRP 1200046 #define P_OSREL_VMTOTAL64 1200054 #define P_OSREL_CK_SUPERBLOCK 1300000 #define P_OSREL_CK_INODE 1300005 #define P_OSREL_POWERPC_NEW_AUX_ARGS 1300070 #define P_OSREL_TIDPID 1400079 #define P_OSREL_TIDPID_13 1302501 #define P_OSREL_MAJOR(x) ((x) / 100000) #endif #ifndef LOCORE #include #endif /* * Machine-independent constants (some used in following include files). * Redefined constants are from POSIX 1003.1 limits file. * * MAXCOMLEN should be >= sizeof(ac_comm) (see ) */ #include #define MAXCOMLEN 19 /* max command name remembered */ #define MAXINTERP PATH_MAX /* max interpreter file name length */ #define MAXLOGNAME 33 /* max login name length (incl. NUL) */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ #define NGROUPS (NGROUPS_MAX+1) /* max number groups */ #define NOFILE OPEN_MAX /* max open files per process */ #define NOGROUP 65535 /* marker for empty group set member */ #define MAXHOSTNAMELEN 256 /* max hostname size */ #define SPECNAMELEN 255 /* max length of devicename */ /* More types and definitions used throughout the kernel. */ #ifdef _KERNEL #include #include #ifndef LOCORE #include #include #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif #ifndef _KERNEL #ifndef LOCORE /* Signals. */ #include #endif #endif /* Machine type dependent parameters. */ #include #ifndef _KERNEL #include #endif #ifndef DEV_BSHIFT #define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #endif #define DEV_BSIZE (1<>PAGE_SHIFT) #endif /* * btodb() is messy and perhaps slow because `bytes' may be an off_t. We * want to shift an unsigned type to avoid sign extension and we don't * want to widen `bytes' unnecessarily. Assume that the result fits in * a daddr_t. */ #ifndef btodb #define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ (sizeof (bytes) > sizeof(long) \ ? (daddr_t)((unsigned long long)(bytes) >> DEV_BSHIFT) \ : (daddr_t)((unsigned long)(bytes) >> DEV_BSHIFT)) #endif #ifndef dbtob #define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((off_t)(db) << DEV_BSHIFT) #endif #define PRIMASK 0x0ff #define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */ #define PDROP 0x200 /* OR'd with pri to stop re-entry of interlock mutex */ #define NZERO 0 /* default "nice" */ #define NBBY 8 /* number of bits in a byte */ #define NBPW sizeof(int) /* number of bytes per word (integer) */ #define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */ #define NODEV (dev_t)(-1) /* non-existent device */ /* * File system parameters and macros. * * MAXBSIZE - Filesystems are made out of blocks of at most MAXBSIZE bytes * per block. MAXBSIZE may be made larger without effecting * any existing filesystems as long as it does not exceed MAXPHYS, * and may be made smaller at the risk of not being able to use * filesystems which require a block size exceeding MAXBSIZE. * * MAXBCACHEBUF - Maximum size of a buffer in the buffer cache. This must * be >= MAXBSIZE and can be set differently for different * architectures by defining it in . * Making this larger allows NFS to do larger reads/writes. * * BKVASIZE - Nominal buffer space per buffer, in bytes. BKVASIZE is the * minimum KVM memory reservation the kernel is willing to make. * Filesystems can of course request smaller chunks. Actual * backing memory uses a chunk size of a page (PAGE_SIZE). * The default value here can be overridden on a per-architecture * basis by defining it in . * * If you make BKVASIZE too small you risk seriously fragmenting * the buffer KVM map which may slow things down a bit. If you * make it too big the kernel will not be able to optimally use * the KVM memory reserved for the buffer cache and will wind * up with too-few buffers. * * The default is 16384, roughly 2x the block size used by a * normal UFS filesystem. */ #define MAXBSIZE 65536 /* must be power of 2 */ #ifndef MAXBCACHEBUF #define MAXBCACHEBUF MAXBSIZE /* must be a power of 2 >= MAXBSIZE */ #endif #ifndef BKVASIZE #define BKVASIZE 16384 /* must be power of 2 */ #endif #define BKVAMASK (BKVASIZE-1) /* * MAXPATHLEN defines the longest permissible path length after expanding * symbolic links. It is used to allocate a temporary buffer from the buffer * pool in which to do the name expansion, hence should be a power of two, * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the * maximum number of symbolic links that may be expanded in a path name. * It should be set high enough to allow all legitimate uses, but halt * infinite loops reasonably quickly. */ #define MAXPATHLEN PATH_MAX #define MAXSYMLINKS 32 /* Bit map related macros. */ #define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a,i) \ (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a,i) \ ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) /* Macros for counting and rounding. */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #define rounddown(x, y) (((x)/(y))*(y)) #define rounddown2(x, y) __align_down(x, y) /* if y is power of two */ #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #define roundup2(x, y) __align_up(x, y) /* if y is powers of two */ #define powerof2(x) ((((x)-1)&(x))==0) /* Macros for min/max. */ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #ifdef _KERNEL /* * Basic byte order function prototypes for non-inline functions. */ #ifndef LOCORE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS __uint32_t htonl(__uint32_t); __uint16_t htons(__uint16_t); __uint32_t ntohl(__uint32_t); __uint16_t ntohs(__uint16_t); __END_DECLS #endif #endif #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif /* !_BYTEORDER_FUNC_DEFINED */ #endif /* _KERNEL */ /* * Scale factor for scaled integers used to count %cpu time and load avgs. * * The number of CPU `tick's that map to a unique `%age' can be expressed * by the formula (1 / (2 ^ (FSHIFT - 11))). Since the intermediate * calculation is done with 64-bit precision, the maximum load average that can * be calculated is approximately 2^32 / FSCALE. * * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age', * FSHIFT must be at least 11. This gives a maximum load avg of 2 million. */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1<> (PAGE_SHIFT - DEV_BSHIFT)) #define ctodb(db) /* calculates pages to devblks */ \ ((db) << (PAGE_SHIFT - DEV_BSHIFT)) /* * Old spelling of __containerof(). */ #define member2struct(s, m, x) \ ((struct s *)(void *)((char *)(x) - offsetof(struct s, m))) /* * Access a variable length array that has been declared as a fixed * length array. */ #define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset]) #endif /* _SYS_PARAM_H_ */