1 /* 2 * Copyright (c) 2015 Fujitsu Ltd. 3 * Copyright (c) International Business Machines Corp., 2001 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 * Author: David L Stevens 19 */ 20 21 #include <stdio.h> 22 #include <unistd.h> 23 #include <errno.h> 24 25 #include <sys/wait.h> 26 #include <sys/socket.h> 27 28 #include <netinet/in.h> 29 #include <netinet/ip6.h> 30 #include <netinet/icmp6.h> 31 32 #include "test.h" 33 #include "safe_macros.h" 34 35 char *TCID = "asapi_05"; 36 37 static void setup(void); 38 39 static void icmp6_ft(void); 40 41 int main(int argc, char *argv[]) 42 { 43 int lc; 44 45 tst_parse_opts(argc, argv, NULL, NULL); 46 47 setup(); 48 49 for (lc = 0; TEST_LOOPING(lc); ++lc) 50 icmp6_ft(); 51 52 tst_exit(); 53 } 54 55 static void setup(void) 56 { 57 TEST_PAUSE; 58 tst_require_root(); 59 } 60 61 enum tt { 62 T_WILLPASS, 63 T_WILLBLOCK, 64 T_SETPASS, 65 T_SETBLOCK, 66 T_SETPASSALL, 67 T_SETBLOCKALL 68 }; 69 70 static struct ftent { 71 char *ft_tname; /* test name, for logging */ 72 unsigned char ft_sndtype; /* send type field */ 73 unsigned char ft_flttype; /* filter type field */ 74 enum tt ft_test; /* what macro to test */ 75 int ft_expected; /* packet should pass? */ 76 } ftab[] = { 77 {"ICMP6_FILTER_SETPASS s 20 f 20", 20, 20, T_SETPASS, 1}, 78 {"ICMP6_FILTER_SETPASS s 20 f 21", 20, 21, T_SETPASS, 0}, 79 {"ICMP6_FILTER_SETBLOCK s 20 f 20", 20, 20, T_SETBLOCK, 0}, 80 {"ICMP6_FILTER_SETBLOCK s 20 f 21", 20, 21, T_SETBLOCK, 1}, 81 {"ICMP6_FILTER_PASSALL s 20", 20, 0, T_SETPASSALL, 1}, 82 {"ICMP6_FILTER_PASSALL s 20", 21, 0, T_SETPASSALL, 1}, 83 {"ICMP6_FILTER_BLOCKALL s 20", 20, 0, T_SETBLOCKALL, 0}, 84 {"ICMP6_FILTER_BLOCKALL s 20", 21, 0, T_SETBLOCKALL, 0}, 85 {"ICMP6_FILTER_WILLBLOCK s 20 f 21", 20, 21, T_WILLBLOCK, 0}, 86 {"ICMP6_FILTER_WILLBLOCK s 20 f 20", 20, 20, T_WILLBLOCK, 1}, 87 {"ICMP6_FILTER_WILLPASS s 20 f 21", 20, 21, T_WILLPASS, 0}, 88 {"ICMP6_FILTER_WILLPASS s 22 f 22", 22, 22, T_WILLPASS, 1}, 89 }; 90 91 #define FTCOUNT ARRAY_SIZE(ftab) 92 93 static int ic6_send1(char *tname, unsigned char type) 94 { 95 struct sockaddr_in6 sin6; 96 struct icmp6_hdr ic6; 97 int s; 98 99 s = SAFE_SOCKET(NULL, AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 100 101 memset(&ic6, 0, sizeof(ic6)); 102 ic6.icmp6_type = type; 103 ic6.icmp6_data32[0] = htonl(getpid()); 104 105 memset(&sin6, 0, sizeof(sin6)); 106 sin6.sin6_family = AF_INET6; 107 sin6.sin6_addr = in6addr_loopback; 108 if (sendto(s, &ic6, sizeof(ic6), 0, (struct sockaddr *)&sin6, 109 sizeof(sin6)) == -1) { 110 tst_resm(TBROK | TERRNO, "%s: sendto failed", tname); 111 return 1; 112 } 113 return 0; 114 } 115 116 static int ic6_recv1(char *tname, int sall, int sf) 117 { 118 fd_set readfds, readfds_saved; 119 struct timeval tv; 120 int maxfd, nfds; 121 int gotall, gotone; 122 int cc; 123 static unsigned char rbuf[2048]; 124 125 tv.tv_sec = 0; 126 tv.tv_usec = 250000; 127 128 FD_ZERO(&readfds_saved); 129 FD_SET(sall, &readfds_saved); 130 FD_SET(sf, &readfds_saved); 131 maxfd = MAX(sall, sf); 132 133 memcpy(&readfds, &readfds_saved, sizeof(readfds)); 134 135 gotall = gotone = 0; 136 /* 137 * Note: this relies on linux-specific behavior (select 138 * updating tv with time elapsed) 139 */ 140 while (!gotall || !gotone) { 141 struct icmp6_hdr *pic6 = (struct icmp6_hdr *)rbuf; 142 143 nfds = select(maxfd + 1, &readfds, 0, 0, &tv); 144 if (nfds == 0) 145 break; /* timed out */ 146 if (nfds < 0) { 147 if (errno == EINTR) 148 continue; 149 tst_resm(TBROK | TERRNO, "%s: select failed", tname); 150 } 151 if (FD_ISSET(sall, &readfds)) { 152 cc = recv(sall, rbuf, sizeof(rbuf), 0); 153 if (cc < 0) { 154 tst_resm(TBROK | TERRNO, 155 "%s: recv(sall, ..) failed", tname); 156 return -1; 157 } 158 /* if packet check succeeds... */ 159 if (htonl(pic6->icmp6_data32[0]) == (uint32_t)getpid()) 160 gotall = 1; 161 } 162 if (FD_ISSET(sf, &readfds)) { 163 cc = recv(sf, rbuf, sizeof(rbuf), 0); 164 if (cc < 0) { 165 tst_resm(TBROK | TERRNO, 166 "%s: recv(sf, ..) failed", tname); 167 return -1; 168 } 169 /* if packet check succeeds... */ 170 if (htonl(pic6->icmp6_data32[0]) == (uint32_t)getpid()) 171 gotone = 1; 172 } 173 memcpy(&readfds, &readfds_saved, sizeof(readfds)); 174 } 175 if (!gotall) { 176 tst_resm(TBROK, "%s: recv all timed out", tname); 177 return -1; 178 } 179 if (gotone) 180 return 1; 181 return 0; 182 } 183 184 /* functional tests */ 185 static void icmp6_ft(void) 186 { 187 struct icmp6_filter i6f; 188 int sall, sf; 189 unsigned int i; 190 191 sall = SAFE_SOCKET(NULL, PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 192 193 ICMP6_FILTER_SETPASSALL(&i6f); 194 if (setsockopt(sall, IPPROTO_ICMPV6, ICMP6_FILTER, &i6f, 195 sizeof(i6f)) < 0) { 196 tst_resm(TBROK | TERRNO, 197 "setsockopt pass all ICMP6_FILTER failed"); 198 } 199 200 sf = SAFE_SOCKET(NULL, PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 201 202 int rv; 203 204 for (i = 0; i < FTCOUNT; ++i) { 205 206 rv = -1; 207 208 switch (ftab[i].ft_test) { 209 case T_SETPASS: 210 ICMP6_FILTER_SETBLOCKALL(&i6f); 211 ICMP6_FILTER_SETPASS(ftab[i].ft_flttype, &i6f); 212 break; 213 case T_SETPASSALL: 214 ICMP6_FILTER_SETPASSALL(&i6f); 215 break; 216 case T_SETBLOCK: 217 ICMP6_FILTER_SETPASSALL(&i6f); 218 ICMP6_FILTER_SETBLOCK(ftab[i].ft_flttype, &i6f); 219 break; 220 case T_SETBLOCKALL: 221 ICMP6_FILTER_SETBLOCKALL(&i6f); 222 break; 223 case T_WILLBLOCK: 224 ICMP6_FILTER_SETPASSALL(&i6f); 225 ICMP6_FILTER_SETBLOCK(ftab[i].ft_flttype, &i6f); 226 rv = ICMP6_FILTER_WILLBLOCK(ftab[i].ft_sndtype, &i6f); 227 break; 228 case T_WILLPASS: 229 ICMP6_FILTER_SETBLOCKALL(&i6f); 230 ICMP6_FILTER_SETPASS(ftab[i].ft_flttype, &i6f); 231 rv = ICMP6_FILTER_WILLPASS(ftab[i].ft_sndtype, &i6f); 232 break; 233 default: 234 tst_resm(TBROK, "%s: unknown test type %d", 235 ftab[i].ft_tname, ftab[i].ft_test); 236 continue; 237 } 238 if (ftab[i].ft_test != T_WILLBLOCK && 239 ftab[i].ft_test != T_WILLPASS) { 240 if (setsockopt(sf, IPPROTO_ICMPV6, ICMP6_FILTER, &i6f, 241 sizeof(i6f)) < 0) { 242 tst_resm(TFAIL | TERRNO, 243 "setsockopt ICMP6_FILTER"); 244 continue; 245 } 246 if (ic6_send1(ftab[i].ft_tname, ftab[i].ft_sndtype)) 247 continue; 248 rv = ic6_recv1(ftab[i].ft_tname, sall, sf); 249 } else { 250 rv = -1; 251 } 252 253 if (rv < 0) 254 continue; 255 if (rv != ftab[i].ft_expected) 256 tst_resm(TFAIL, "%s: rv %d != expected %d", 257 ftab[i].ft_tname, rv, ftab[i].ft_expected); 258 else 259 tst_resm(TPASS, "%s", ftab[i].ft_tname); 260 } 261 } 262