1 /* 2 * Check decoding of socket filters. 3 * 4 * Copyright (c) 2017 Dmitry V. Levin <ldv@altlinux.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "tests.h" 31 32 #include <stdio.h> 33 #include <unistd.h> 34 #include <netinet/in.h> 35 #include <sys/socket.h> 36 #include <linux/filter.h> 37 38 #define PRINT_STMT_SYM(pfx, code, k) PRINT_STMT_SYM_(pfx, #code, #k) 39 #define PRINT_STMT_SYM_(pfx, code, k) \ 40 printf("%sBPF_STMT(%s, %s)", pfx, code, k) 41 #define PRINT_STMT_VAL(pfx, code, k) PRINT_STMT_VAL_(pfx, #code, k) 42 #define PRINT_STMT_VAL_(pfx, code, k) \ 43 printf("%sBPF_STMT(%s, %#x)", pfx, code, k) 44 45 #define PRINT_JUMP(pfx, code, k, jt, jf) PRINT_JUMP_(pfx, #code, k, jt, jf) 46 #define PRINT_JUMP_(pfx, code, k, jt, jf) \ 47 printf("%sBPF_JUMP(%s, %#x, %#x, %#x)", pfx, code, k, jt, jf) 48 49 static const struct sock_filter bpf_filter[] = { 50 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_LL_OFF+4), 51 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_NET_OFF+8), 52 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF+SKF_AD_PROTOCOL), 53 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, IPPROTO_UDP, 0, 5), 54 BPF_STMT(BPF_LD|BPF_W|BPF_LEN, 0), 55 BPF_JUMP(BPF_JMP|BPF_K|BPF_JGE, 100, 0, 3), 56 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 42), 57 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 'a', 0, 1), 58 BPF_STMT(BPF_RET|BPF_K, -1U), 59 BPF_STMT(BPF_RET|BPF_K, 0) 60 }; 61 62 static void 63 print_filter(void) 64 { 65 PRINT_STMT_SYM("[", BPF_LD|BPF_B|BPF_ABS, SKF_LL_OFF+4); 66 PRINT_STMT_SYM(", ", BPF_LD|BPF_B|BPF_ABS, SKF_NET_OFF+8); 67 PRINT_STMT_SYM(", ", BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF+SKF_AD_PROTOCOL); 68 PRINT_JUMP(", ", BPF_JMP|BPF_K|BPF_JEQ, IPPROTO_UDP, 0, 5); 69 PRINT_STMT_VAL(", ", BPF_LD|BPF_W|BPF_LEN, 0); 70 PRINT_JUMP(", ", BPF_JMP|BPF_K|BPF_JGE, 100, 0, 3); 71 PRINT_STMT_VAL(", ", BPF_LD|BPF_B|BPF_ABS, 42); 72 PRINT_JUMP(", ", BPF_JMP|BPF_K|BPF_JEQ, 'a', 0, 1); 73 PRINT_STMT_VAL(", ", BPF_RET|BPF_K, -1U); 74 PRINT_STMT_VAL(", ", BPF_RET|BPF_K, 0); 75 putchar(']'); 76 } 77 78 static const char *errstr; 79 80 static int 81 get_filter(int fd, void *val, socklen_t *len) 82 { 83 int rc = getsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, val, len); 84 errstr = sprintrc(rc); 85 return rc; 86 } 87 88 static int 89 set_filter(int fd, void *val, socklen_t len) 90 { 91 int rc = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, val, len); 92 errstr = sprintrc(rc); 93 return rc; 94 } 95 96 int 97 main(void) 98 { 99 int rc; 100 struct sock_filter *const filter = 101 tail_memdup(bpf_filter, sizeof(bpf_filter)); 102 void *const efault = filter + ARRAY_SIZE(bpf_filter); 103 TAIL_ALLOC_OBJECT_CONST_PTR(struct sock_fprog, prog); 104 TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len); 105 106 prog->len = ARRAY_SIZE(bpf_filter); 107 prog->filter = filter; 108 109 int fd = socket(AF_INET, SOCK_DGRAM, 0); 110 if (fd < 0) 111 perror_msg_and_skip("socket AF_INET SOCK_DGRAM"); 112 113 /* query sock_filter program length -> 0 */ 114 *len = BPF_MAXINSNS; 115 rc = get_filter(fd, NULL, len); 116 if (rc) 117 perror_msg_and_skip("getsockopt SOL_SOCKET SO_ATTACH_FILTER"); 118 printf("getsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER" 119 ", NULL, [%u->0]) = 0\n", fd, BPF_MAXINSNS); 120 121 /* getsockopt NULL optlen - EFAULT */ 122 rc = get_filter(fd, NULL, NULL); 123 printf("getsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, NULL, NULL)" 124 " = %s\n", fd, errstr); 125 126 /* attach a filter */ 127 rc = set_filter(fd, prog, sizeof(*prog)); 128 if (rc) 129 perror_msg_and_skip("setsockopt SOL_SOCKET SO_ATTACH_FILTER"); 130 printf("setsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, {len=%u, filter=", 131 fd, prog->len); 132 print_filter(); 133 printf("}, %u) = 0\n", (unsigned int) sizeof(*prog)); 134 135 /* setsockopt optlen is too small - EINVAL */ 136 rc = set_filter(fd, prog, sizeof(*prog) - 4); 137 printf("setsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, %p, %u) = %s\n", 138 fd, prog, (unsigned int) sizeof(*prog) - 4, errstr); 139 140 #ifdef SO_ATTACH_REUSEPORT_CBPF 141 rc = setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, 142 prog, sizeof(*prog)); 143 errstr = sprintrc(rc); 144 printf("setsockopt(%d, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF" 145 ", {len=%u, filter=", fd, prog->len); 146 print_filter(); 147 printf("}, %u) = %s\n", (unsigned int) sizeof(*prog), errstr); 148 #endif 149 150 /* query sock_filter program length -> ARRAY_SIZE(bpf_filter) */ 151 *len = 0; 152 rc = get_filter(fd, efault, len); 153 printf("getsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, %p" 154 ", [0->%u]) = %s\n", 155 fd, efault, (unsigned int) ARRAY_SIZE(bpf_filter), errstr); 156 157 /* getsockopt optlen is too small - EINVAL */ 158 *len = ARRAY_SIZE(bpf_filter) - 1; 159 rc = get_filter(fd, efault, len); 160 printf("getsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, %p" 161 ", [%u]) = %s\n", 162 fd, efault, (unsigned int) ARRAY_SIZE(bpf_filter) - 1, errstr); 163 164 /* getsockopt optval EFAULT */ 165 *len = ARRAY_SIZE(bpf_filter); 166 rc = get_filter(fd, filter + 1, len); 167 printf("getsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, %p" 168 ", [%u]) = %s\n", fd, filter + 1, 169 (unsigned int) ARRAY_SIZE(bpf_filter), errstr); 170 171 /* getsockopt optlen is too large - truncated */ 172 *len = ARRAY_SIZE(bpf_filter) + 1; 173 rc = get_filter(fd, filter, len); 174 printf("getsockopt(%d, SOL_SOCKET, SO_ATTACH_FILTER, ", fd); 175 print_filter(); 176 printf(", [%u->%d]) = %s\n", 177 (unsigned int) ARRAY_SIZE(bpf_filter) + 1, *len, errstr); 178 179 puts("+++ exited with 0 +++"); 180 return 0; 181 } 182