/* * Check decoding of getsockopt and setsockopt for SOL_NETLINK level. * * Copyright (c) 2017 Dmitry V. Levin <ldv@altlinux.org> * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "tests.h" #include "netlink.h" #include <stdio.h> #ifndef SOL_NETLINK # define SOL_NETLINK 270 #endif static int rc; static const char *errstr; static int get_sockopt(int fd, int name, void *val, socklen_t *len) { rc = getsockopt(fd, SOL_NETLINK, name, val, len); errstr = sprintrc(rc); return rc; } static int set_sockopt(int fd, int name, void *val, socklen_t len) { rc = setsockopt(fd, SOL_NETLINK, name, val, len); errstr = sprintrc(rc); return rc; } int main(void) { static const struct { int val; const char *str; } names[] = { #ifdef NETLINK_ADD_MEMBERSHIP { ARG_STR(NETLINK_ADD_MEMBERSHIP) }, #endif #ifdef NETLINK_DROP_MEMBERSHIP { ARG_STR(NETLINK_DROP_MEMBERSHIP) }, #endif #ifdef NETLINK_PKTINFO { ARG_STR(NETLINK_PKTINFO) }, #endif #ifdef NETLINK_BROADCAST_ERROR { ARG_STR(NETLINK_BROADCAST_ERROR) }, #endif #ifdef NETLINK_NO_ENOBUFS { ARG_STR(NETLINK_NO_ENOBUFS) }, #endif #ifdef NETLINK_RX_RING { ARG_STR(NETLINK_RX_RING) }, #endif #ifdef NETLINK_TX_RING { ARG_STR(NETLINK_TX_RING) }, #endif #ifdef NETLINK_LISTEN_ALL_NSID { ARG_STR(NETLINK_LISTEN_ALL_NSID) }, #endif #ifdef NETLINK_LIST_MEMBERSHIPS { ARG_STR(NETLINK_LIST_MEMBERSHIPS) }, #endif #ifdef NETLINK_CAP_ACK { ARG_STR(NETLINK_CAP_ACK) }, #endif #ifdef NETLINK_EXT_ACK { ARG_STR(NETLINK_EXT_ACK) }, #endif }; TAIL_ALLOC_OBJECT_CONST_PTR(int, val); TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len); void *const efault = val + 1; int fd = socket(AF_NETLINK, SOCK_RAW, 0); if (fd < 0) perror_msg_and_skip("socket AF_NETLINK SOCK_RAW"); unsigned int i; for (i = 0; i < ARRAY_SIZE(names); ++i) { /* getsockopt */ /* classic */ *len = sizeof(*val); get_sockopt(fd, names[i].val, val, len); printf("getsockopt(%d, SOL_NETLINK, %s, ", fd, names[i].str); if (rc) printf("%p", val); else printf("[%d]", *val); printf(", [%d]) = %s\n", *len, errstr); /* optlen larger than necessary - shortened */ *len = sizeof(*val) + 1; get_sockopt(fd, names[i].val, val, len); printf("getsockopt(%d, SOL_NETLINK, %s, ", fd, names[i].str); if (rc) printf("%p", val); else printf("[%d]", *val); printf(", [%d", (int) sizeof(*val) + 1); if ((int) sizeof(*val) + 1 != *len) printf("->%d", *len); printf("]) = %s\n", errstr); /* zero optlen - print returned optlen */ *len = 0; get_sockopt(fd, names[i].val, NULL, len); printf("getsockopt(%d, SOL_NETLINK, %s, NULL, [0", fd, names[i].str); if (*len) printf("->%d", *len); printf("]) = %s\n", errstr); #ifdef NETLINK_LIST_MEMBERSHIPS if (names[i].val != NETLINK_LIST_MEMBERSHIPS) { #endif /* optlen shorter than necessary - print address */ *len = sizeof(*val) - 1; get_sockopt(fd, names[i].val, val, len); printf("getsockopt(%d, SOL_NETLINK, %s, %p, [%d", fd, names[i].str, val, (int) sizeof(*val) - 1); if ((int) sizeof(*val) - 1 != *len) printf("->%d", *len); printf("]) = %s\n", errstr); #ifdef NETLINK_LIST_MEMBERSHIPS } else { /* optlen shorter than required for the first element */ *len = sizeof(*val) - 1; get_sockopt(fd, names[i].val, efault, len); printf("getsockopt(%d, SOL_NETLINK, %s, ", fd, names[i].str); if (rc) printf("%p", efault); else printf("[]"); printf(", [%d", (int) sizeof(*val) - 1); if ((int) sizeof(*val) - 1 != *len) printf("->%d", *len); printf("]) = %s\n", errstr); } #endif /* optval EFAULT - print address */ *len = sizeof(*val); get_sockopt(fd, names[i].val, efault, len); printf("getsockopt(%d, SOL_NETLINK, %s, %p, [%d]) = %s\n", fd, names[i].str, efault, *len, errstr); /* optlen EFAULT - print address */ get_sockopt(fd, names[i].val, val, len + 1); printf("getsockopt(%d, SOL_NETLINK, %s, %p, %p) = %s\n", fd, names[i].str, val, len + 1, errstr); /* setsockopt */ /* classic */ *val = 0xdefaced; set_sockopt(fd, names[i].val, val, sizeof(*val)); printf("setsockopt(%d, SOL_NETLINK, %s, [%d], %d) = %s\n", fd, names[i].str, *val, (int) sizeof(*val), errstr); /* optlen larger than necessary - shortened */ set_sockopt(fd, names[i].val, val, sizeof(*val) + 1); printf("setsockopt(%d, SOL_NETLINK, %s, [%d], %d) = %s\n", fd, names[i].str, *val, (int) sizeof(*val) + 1, errstr); /* optlen < 0 - print address */ set_sockopt(fd, names[i].val, val, -1U); printf("setsockopt(%d, SOL_NETLINK, %s, %p, -1) = %s\n", fd, names[i].str, val, errstr); /* optlen smaller than necessary - print address */ set_sockopt(fd, names[i].val, val, sizeof(*val) - 1); printf("setsockopt(%d, SOL_NETLINK, %s, %p, %d) = %s\n", fd, names[i].str, val, (int) sizeof(*val) - 1, errstr); /* optval EFAULT - print address */ set_sockopt(fd, names[i].val, efault, sizeof(*val)); printf("setsockopt(%d, SOL_NETLINK, %s, %p, %d) = %s\n", fd, names[i].str, efault, (int) sizeof(*val), errstr); } puts("+++ exited with 0 +++"); return 0; }