1 /* $NetBSD: ipf.c,v 1.2 2008/06/18 09:06:26 yamt Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc. 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <sys/ioctl.h> 33 #include <sys/file.h> 34 35 #include <net/if.h> 36 37 #include <netinet/in.h> 38 #include <netinet/in_systm.h> 39 #include <netinet/ip_compat.h> 40 #include <netinet/ipl.h> 41 #include <netinet/ip_fil.h> 42 #include <netinet/ip_nat.h> 43 44 #include <arpa/inet.h> 45 46 #include <err.h> 47 #include <errno.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include "ipf.h" 54 55 /* From netinet/in.h, but only _KERNEL_ gets them. */ 56 #define satosin(sa) ((struct sockaddr_in *)(sa)) 57 #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 58 59 static int natfd; 60 const char *netif; 61 62 struct ftp_proxy_nat { 63 struct ipnat ipn; 64 LIST_ENTRY(ftp_proxy_nat) link; 65 }; 66 67 struct ftp_proxy_entry { 68 u_int32_t id; 69 char proxy_tag[IPFTAG_LEN]; 70 int status; 71 LIST_HEAD(, ftp_proxy_nat) nat_entries; 72 LIST_ENTRY(ftp_proxy_entry) link; 73 }; 74 75 LIST_HEAD(, ftp_proxy_entry) ftp_proxy_entries = 76 LIST_HEAD_INITIALIZER(ftp_proxy_entries); 77 78 static struct ftp_proxy_entry * 79 ftp_proxy_entry_create(u_int32_t id) 80 { 81 struct ftp_proxy_entry *fpe; 82 int rv; 83 84 fpe = malloc(sizeof(*fpe)); 85 if (fpe == NULL) 86 return (NULL); 87 88 fpe->id = id; 89 fpe->status = 0; 90 91 rv = snprintf(fpe->proxy_tag, sizeof(fpe->proxy_tag), "ftp_%d", id); 92 if (rv == -1 || rv >= sizeof(fpe->proxy_tag)) { 93 free(fpe); 94 errno = EINVAL; 95 return (NULL); 96 } 97 LIST_INIT(&fpe->nat_entries); 98 LIST_INSERT_HEAD(&ftp_proxy_entries, fpe, link); 99 100 return (fpe); 101 } 102 103 static void 104 ftp_proxy_entry_remove(struct ftp_proxy_entry *fpe) 105 { 106 struct ftp_proxy_nat *fpn; 107 108 while ((fpn = LIST_FIRST(&fpe->nat_entries)) != NULL) { 109 LIST_REMOVE(fpn, link); 110 free(fpn); 111 } 112 113 LIST_REMOVE(fpe, link); 114 free(fpe); 115 } 116 117 static struct ftp_proxy_entry * 118 ftp_proxy_entry_find(u_int32_t id) 119 { 120 struct ftp_proxy_entry *fpe; 121 122 LIST_FOREACH(fpe, &ftp_proxy_entries, link) { 123 if (fpe->id == id) { 124 return fpe; 125 } 126 } 127 return NULL; 128 } 129 130 static int 131 ftp_proxy_entry_add_nat(struct ftp_proxy_entry *fpe, ipnat_t ipn) 132 { 133 struct ftp_proxy_nat *fpn; 134 135 fpn = malloc(sizeof(*fpn)); 136 if (fpn == NULL) 137 return (-1); 138 139 memcpy(&fpn->ipn, &ipn, sizeof(fpn->ipn)); 140 LIST_INSERT_HEAD(&fpe->nat_entries, fpn, link); 141 142 return (0); 143 } 144 145 static int 146 ipfilter_add_nat(ipnat_t ipn) 147 { 148 ipfobj_t obj; 149 150 memset(&obj, 0, sizeof(obj)); 151 obj.ipfo_rev = IPFILTER_VERSION; 152 obj.ipfo_size = sizeof(ipn); 153 obj.ipfo_type = IPFOBJ_IPNAT; 154 obj.ipfo_ptr = &ipn; 155 156 return ioctl(natfd, SIOCADNAT, &obj); 157 } 158 159 static int 160 ipfilter_remove_nat(ipnat_t ipn) 161 { 162 ipfobj_t obj; 163 164 memset(&obj, 0, sizeof(obj)); 165 obj.ipfo_rev = IPFILTER_VERSION; 166 obj.ipfo_size = sizeof(ipn); 167 obj.ipfo_type = IPFOBJ_IPNAT; 168 obj.ipfo_ptr = &ipn; 169 170 return ioctl(natfd, SIOCRMNAT, &obj); 171 } 172 173 int 174 ipf_add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 175 struct sockaddr *dst, u_int16_t d_port) 176 { 177 178 if (!src || !dst || !d_port) { 179 errno = EINVAL; 180 return (-1); 181 } 182 183 /* TODO */ 184 185 return (0); 186 } 187 188 int 189 ipf_add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 190 u_int16_t d_port, struct sockaddr *snat, u_int16_t nat_range_low, 191 u_int16_t nat_range_high) 192 { 193 194 /* TODO */ 195 196 return (0); 197 } 198 199 int 200 ipf_add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 201 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 202 { 203 struct ftp_proxy_entry *fpe = ftp_proxy_entry_find(id); 204 ipnat_t ipn; 205 206 if (fpe == NULL) { 207 errno = ENOENT; 208 return (-1); 209 } 210 211 if (!src || !dst || !d_port || !rdr || !rdr_port || 212 (src->sa_family != rdr->sa_family)) { 213 errno = EINVAL; 214 return (-1); 215 } 216 217 memset(&ipn, 0, sizeof(ipn)); 218 ipn.in_redir = NAT_REDIRECT; 219 ipn.in_v = 4; 220 ipn.in_outip = satosin(dst)->sin_addr.s_addr; 221 ipn.in_outmsk = 0xffffffff; 222 strlcpy(ipn.in_ifnames[0], netif, sizeof(ipn.in_ifnames[0])); 223 strlcpy(ipn.in_ifnames[1], netif, sizeof(ipn.in_ifnames[1])); 224 ipn.in_pmin = htons(d_port); 225 ipn.in_pmax = htons(d_port); 226 ipn.in_inip = satosin(rdr)->sin_addr.s_addr; 227 ipn.in_inmsk = 0xffffffff; 228 ipn.in_pnext = htons(rdr_port); 229 ipn.in_flags = IPN_FIXEDDPORT | IPN_TCP; 230 strlcpy(ipn.in_tag.ipt_tag, fpe->proxy_tag, sizeof(ipn.in_tag.ipt_tag)); 231 232 if (ipfilter_add_nat(ipn) == -1) 233 return (-1); 234 235 if (ftp_proxy_entry_add_nat(fpe, ipn) == -1) 236 return (-1); 237 238 fpe->status = 1; 239 240 return (0); 241 } 242 243 #if 0 244 int 245 ipf_add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 246 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 247 { 248 u_32_t sum1, sum2, sumd; 249 int onoff, error; 250 nat_save_t ns; 251 ipfobj_t obj; 252 nat_t *nat; 253 254 if (!src || !dst || !d_port || !rdr || !rdr_port || 255 (src->sa_family != rdr->sa_family)) { 256 errno = EINVAL; 257 return (-1); 258 } 259 260 memset(&ns, 0, sizeof(ns)); 261 262 nat = &ns.ipn_nat; 263 nat->nat_p = IPPROTO_TCP; 264 nat->nat_dir = NAT_OUTBOUND; 265 nat->nat_redir = NAT_REDIRECT; 266 strlcpy(nat->nat_ifnames[0], netif, sizeof(nat->nat_ifnames[0])); 267 strlcpy(nat->nat_ifnames[1], netif, sizeof(nat->nat_ifnames[1])); 268 269 nat->nat_inip = satosin(rdr)->sin_addr; 270 nat->nat_outip = satosin(dst)->sin_addr; 271 nat->nat_oip = satosin(src)->sin_addr; 272 273 sum1 = LONG_SUM(ntohl(nat->nat_inip.s_addr)) + rdr_port; 274 sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) + d_port; 275 CALC_SUMD(sum1, sum2, sumd); 276 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 277 nat->nat_sumd[1] = nat->nat_sumd[0]; 278 279 sum1 = LONG_SUM(ntohl(nat->nat_inip.s_addr)); 280 sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 281 CALC_SUMD(sum1, sum2, sumd); 282 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 283 284 nat->nat_inport = htons(rdr_port); 285 nat->nat_outport = htons(d_port); 286 nat->nat_oport = satosin(src)->sin_port; 287 288 nat->nat_flags = IPN_TCPUDP; 289 290 memset(&obj, 0, sizeof(obj)); 291 obj.ipfo_rev = IPFILTER_VERSION; 292 obj.ipfo_size = sizeof(ns); 293 obj.ipfo_ptr = &ns; 294 obj.ipfo_type = IPFOBJ_NATSAVE; 295 296 error = 0; 297 onoff = 1; 298 if (ioctl(natfd, SIOCSTLCK, &onoff) == -1) 299 return (-1); 300 if (ioctl(natfd, SIOCSTPUT, &obj) == -1) 301 error = -1; 302 onoff = 0; 303 if (ioctl(natfd, SIOCSTLCK, &onoff) == -1) 304 error = -1; 305 306 return (error); 307 } 308 #endif 309 310 int 311 ipf_do_commit(void) 312 { 313 struct ftp_proxy_entry *fpe, *n; 314 struct ftp_proxy_nat *fpn; 315 316 for (fpe = LIST_FIRST(&ftp_proxy_entries); fpe != NULL; fpe = n) { 317 n = LIST_NEXT(fpe, link); 318 319 /* 320 * If status is nul, then the session is going to be ended. 321 * Remove all nat mappings that were added. 322 */ 323 if (fpe->status == 0) { 324 while ((fpn = LIST_FIRST(&fpe->nat_entries)) != NULL) { 325 if (ipfilter_remove_nat(fpn->ipn) == -1) 326 return (-1); 327 328 LIST_REMOVE(fpn, link); 329 free(fpn); 330 } 331 332 ftp_proxy_entry_remove(fpe); 333 } 334 } 335 336 return (0); 337 } 338 339 int 340 ipf_do_rollback(void) 341 { 342 343 /* TODO ??? */ 344 345 return (0); 346 } 347 348 void 349 ipf_init_filter(char *opt_qname, char *opt_tagname, int opt_verbose) 350 { 351 natfd = open(IPNAT_NAME, O_RDWR); 352 if (natfd == -1) 353 err(EXIT_FAILURE, "cannot open " IPNAT_NAME); 354 } 355 356 int 357 ipf_prepare_commit(u_int32_t id) 358 { 359 struct ftp_proxy_entry *fpe; 360 361 fpe = ftp_proxy_entry_find(id); 362 if (fpe == NULL) { 363 fpe = ftp_proxy_entry_create(id); 364 if (fpe == NULL) 365 return (-1); 366 } 367 fpe->status = 0; 368 369 return (0); 370 } 371 372 int 373 ipf_server_lookup(struct sockaddr *client, struct sockaddr *proxy, 374 struct sockaddr *server) 375 { 376 natlookup_t natlook; 377 ipfobj_t obj; 378 379 /* IPv4-only for now. */ 380 if (client->sa_family != AF_INET) { 381 errno = EPROTONOSUPPORT; 382 return (-1); 383 } 384 385 /* 386 * Build up the ipf object description structure. 387 */ 388 memset((void *)&obj, 0, sizeof(obj)); 389 obj.ipfo_rev = IPFILTER_VERSION; 390 obj.ipfo_size = sizeof(natlook); 391 obj.ipfo_ptr = &natlook; 392 obj.ipfo_type = IPFOBJ_NATLOOKUP; 393 /* 394 * Build up the ipf natlook structure. 395 */ 396 memset((void *)&natlook, 0, sizeof(natlook)); 397 natlook.nl_flags = IPN_TCPUDP; 398 natlook.nl_outip = satosin(client)->sin_addr; 399 natlook.nl_inip = satosin(proxy)->sin_addr; 400 natlook.nl_outport = satosin(client)->sin_port; 401 natlook.nl_inport = satosin(proxy)->sin_port; 402 403 if (ioctl(natfd, SIOCGNATL, &obj) == -1) 404 return (-1); 405 406 /* 407 * Return the real destination address and port number in the sockaddr 408 * passed in. 409 */ 410 memset((void *)server, 0, sizeof(struct sockaddr_in)); 411 satosin(server)->sin_len = sizeof(struct sockaddr_in); 412 satosin(server)->sin_family = AF_INET; 413 satosin(server)->sin_addr = natlook.nl_realip; 414 satosin(server)->sin_port = natlook.nl_realport; 415 416 return (0); 417 } 418