1 /* $NetBSD: ipf.c,v 1.3 2011/02/02 02:20:26 rmind 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 "filter.h" 54 55 void ipf_init_filter(char *, char *, int); 56 int ipf_add_filter(u_int32_t, u_int8_t, struct sockaddr *, struct sockaddr *, 57 u_int16_t); 58 int ipf_add_nat(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t, 59 struct sockaddr *, u_int16_t, u_int16_t); 60 int ipf_add_rdr(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t, 61 struct sockaddr *, u_int16_t); 62 int ipf_server_lookup(struct sockaddr *, struct sockaddr *, struct sockaddr *); 63 int ipf_prepare_commit(u_int32_t); 64 int ipf_do_commit(void); 65 int ipf_do_rollback(void); 66 67 const ftp_proxy_ops_t ipf_fprx_ops = { 68 .init_filter = ipf_init_filter, 69 .add_filter = ipf_add_filter, 70 .add_nat = ipf_add_nat, 71 .add_rdr = ipf_add_rdr, 72 .server_lookup = ipf_server_lookup, 73 .prepare_commit = ipf_prepare_commit, 74 .do_commit = ipf_do_commit, 75 .do_rollback = ipf_do_rollback 76 }; 77 78 /* From netinet/in.h, but only _KERNEL_ gets them. */ 79 #define satosin(sa) ((struct sockaddr_in *)(sa)) 80 #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 81 82 static int natfd; 83 const char *netif; 84 85 struct ftp_proxy_nat { 86 struct ipnat ipn; 87 LIST_ENTRY(ftp_proxy_nat) link; 88 }; 89 90 struct ftp_proxy_entry { 91 u_int32_t id; 92 char proxy_tag[IPFTAG_LEN]; 93 int status; 94 LIST_HEAD(, ftp_proxy_nat) nat_entries; 95 LIST_ENTRY(ftp_proxy_entry) link; 96 }; 97 98 LIST_HEAD(, ftp_proxy_entry) ftp_proxy_entries = 99 LIST_HEAD_INITIALIZER(ftp_proxy_entries); 100 101 static struct ftp_proxy_entry * 102 ftp_proxy_entry_create(u_int32_t id) 103 { 104 struct ftp_proxy_entry *fpe; 105 int rv; 106 107 fpe = malloc(sizeof(*fpe)); 108 if (fpe == NULL) 109 return (NULL); 110 111 fpe->id = id; 112 fpe->status = 0; 113 114 rv = snprintf(fpe->proxy_tag, sizeof(fpe->proxy_tag), "ftp_%d", id); 115 if (rv == -1 || rv >= sizeof(fpe->proxy_tag)) { 116 free(fpe); 117 errno = EINVAL; 118 return (NULL); 119 } 120 LIST_INIT(&fpe->nat_entries); 121 LIST_INSERT_HEAD(&ftp_proxy_entries, fpe, link); 122 123 return (fpe); 124 } 125 126 static void 127 ftp_proxy_entry_remove(struct ftp_proxy_entry *fpe) 128 { 129 struct ftp_proxy_nat *fpn; 130 131 while ((fpn = LIST_FIRST(&fpe->nat_entries)) != NULL) { 132 LIST_REMOVE(fpn, link); 133 free(fpn); 134 } 135 136 LIST_REMOVE(fpe, link); 137 free(fpe); 138 } 139 140 static struct ftp_proxy_entry * 141 ftp_proxy_entry_find(u_int32_t id) 142 { 143 struct ftp_proxy_entry *fpe; 144 145 LIST_FOREACH(fpe, &ftp_proxy_entries, link) { 146 if (fpe->id == id) { 147 return fpe; 148 } 149 } 150 return NULL; 151 } 152 153 static int 154 ftp_proxy_entry_add_nat(struct ftp_proxy_entry *fpe, ipnat_t ipn) 155 { 156 struct ftp_proxy_nat *fpn; 157 158 fpn = malloc(sizeof(*fpn)); 159 if (fpn == NULL) 160 return (-1); 161 162 memcpy(&fpn->ipn, &ipn, sizeof(fpn->ipn)); 163 LIST_INSERT_HEAD(&fpe->nat_entries, fpn, link); 164 165 return (0); 166 } 167 168 static int 169 ipfilter_add_nat(ipnat_t ipn) 170 { 171 ipfobj_t obj; 172 173 memset(&obj, 0, sizeof(obj)); 174 obj.ipfo_rev = IPFILTER_VERSION; 175 obj.ipfo_size = sizeof(ipn); 176 obj.ipfo_type = IPFOBJ_IPNAT; 177 obj.ipfo_ptr = &ipn; 178 179 return ioctl(natfd, SIOCADNAT, &obj); 180 } 181 182 static int 183 ipfilter_remove_nat(ipnat_t ipn) 184 { 185 ipfobj_t obj; 186 187 memset(&obj, 0, sizeof(obj)); 188 obj.ipfo_rev = IPFILTER_VERSION; 189 obj.ipfo_size = sizeof(ipn); 190 obj.ipfo_type = IPFOBJ_IPNAT; 191 obj.ipfo_ptr = &ipn; 192 193 return ioctl(natfd, SIOCRMNAT, &obj); 194 } 195 196 int 197 ipf_add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 198 struct sockaddr *dst, u_int16_t d_port) 199 { 200 201 if (!src || !dst || !d_port) { 202 errno = EINVAL; 203 return (-1); 204 } 205 206 /* TODO */ 207 208 return (0); 209 } 210 211 int 212 ipf_add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 213 u_int16_t d_port, struct sockaddr *snat, u_int16_t nat_range_low, 214 u_int16_t nat_range_high) 215 { 216 217 /* TODO */ 218 219 return (0); 220 } 221 222 int 223 ipf_add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 224 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 225 { 226 struct ftp_proxy_entry *fpe = ftp_proxy_entry_find(id); 227 ipnat_t ipn; 228 229 if (fpe == NULL) { 230 errno = ENOENT; 231 return (-1); 232 } 233 234 if (!src || !dst || !d_port || !rdr || !rdr_port || 235 (src->sa_family != rdr->sa_family)) { 236 errno = EINVAL; 237 return (-1); 238 } 239 240 memset(&ipn, 0, sizeof(ipn)); 241 ipn.in_redir = NAT_REDIRECT; 242 ipn.in_v = 4; 243 ipn.in_outip = satosin(dst)->sin_addr.s_addr; 244 ipn.in_outmsk = 0xffffffff; 245 strlcpy(ipn.in_ifnames[0], netif, sizeof(ipn.in_ifnames[0])); 246 strlcpy(ipn.in_ifnames[1], netif, sizeof(ipn.in_ifnames[1])); 247 ipn.in_pmin = htons(d_port); 248 ipn.in_pmax = htons(d_port); 249 ipn.in_inip = satosin(rdr)->sin_addr.s_addr; 250 ipn.in_inmsk = 0xffffffff; 251 ipn.in_pnext = htons(rdr_port); 252 ipn.in_flags = IPN_FIXEDDPORT | IPN_TCP; 253 strlcpy(ipn.in_tag.ipt_tag, fpe->proxy_tag, sizeof(ipn.in_tag.ipt_tag)); 254 255 if (ipfilter_add_nat(ipn) == -1) 256 return (-1); 257 258 if (ftp_proxy_entry_add_nat(fpe, ipn) == -1) 259 return (-1); 260 261 fpe->status = 1; 262 263 return (0); 264 } 265 266 #if 0 267 int 268 ipf_add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 269 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 270 { 271 u_32_t sum1, sum2, sumd; 272 int onoff, error; 273 nat_save_t ns; 274 ipfobj_t obj; 275 nat_t *nat; 276 277 if (!src || !dst || !d_port || !rdr || !rdr_port || 278 (src->sa_family != rdr->sa_family)) { 279 errno = EINVAL; 280 return (-1); 281 } 282 283 memset(&ns, 0, sizeof(ns)); 284 285 nat = &ns.ipn_nat; 286 nat->nat_p = IPPROTO_TCP; 287 nat->nat_dir = NAT_OUTBOUND; 288 nat->nat_redir = NAT_REDIRECT; 289 strlcpy(nat->nat_ifnames[0], netif, sizeof(nat->nat_ifnames[0])); 290 strlcpy(nat->nat_ifnames[1], netif, sizeof(nat->nat_ifnames[1])); 291 292 nat->nat_inip = satosin(rdr)->sin_addr; 293 nat->nat_outip = satosin(dst)->sin_addr; 294 nat->nat_oip = satosin(src)->sin_addr; 295 296 sum1 = LONG_SUM(ntohl(nat->nat_inip.s_addr)) + rdr_port; 297 sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) + d_port; 298 CALC_SUMD(sum1, sum2, sumd); 299 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); 300 nat->nat_sumd[1] = nat->nat_sumd[0]; 301 302 sum1 = LONG_SUM(ntohl(nat->nat_inip.s_addr)); 303 sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); 304 CALC_SUMD(sum1, sum2, sumd); 305 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); 306 307 nat->nat_inport = htons(rdr_port); 308 nat->nat_outport = htons(d_port); 309 nat->nat_oport = satosin(src)->sin_port; 310 311 nat->nat_flags = IPN_TCPUDP; 312 313 memset(&obj, 0, sizeof(obj)); 314 obj.ipfo_rev = IPFILTER_VERSION; 315 obj.ipfo_size = sizeof(ns); 316 obj.ipfo_ptr = &ns; 317 obj.ipfo_type = IPFOBJ_NATSAVE; 318 319 error = 0; 320 onoff = 1; 321 if (ioctl(natfd, SIOCSTLCK, &onoff) == -1) 322 return (-1); 323 if (ioctl(natfd, SIOCSTPUT, &obj) == -1) 324 error = -1; 325 onoff = 0; 326 if (ioctl(natfd, SIOCSTLCK, &onoff) == -1) 327 error = -1; 328 329 return (error); 330 } 331 #endif 332 333 int 334 ipf_do_commit(void) 335 { 336 struct ftp_proxy_entry *fpe, *n; 337 struct ftp_proxy_nat *fpn; 338 339 for (fpe = LIST_FIRST(&ftp_proxy_entries); fpe != NULL; fpe = n) { 340 n = LIST_NEXT(fpe, link); 341 342 /* 343 * If status is nul, then the session is going to be ended. 344 * Remove all nat mappings that were added. 345 */ 346 if (fpe->status == 0) { 347 while ((fpn = LIST_FIRST(&fpe->nat_entries)) != NULL) { 348 if (ipfilter_remove_nat(fpn->ipn) == -1) 349 return (-1); 350 351 LIST_REMOVE(fpn, link); 352 free(fpn); 353 } 354 355 ftp_proxy_entry_remove(fpe); 356 } 357 } 358 359 return (0); 360 } 361 362 int 363 ipf_do_rollback(void) 364 { 365 366 /* TODO ??? */ 367 368 return (0); 369 } 370 371 void 372 ipf_init_filter(char *opt_qname, char *opt_tagname, int opt_verbose) 373 { 374 natfd = open(IPNAT_NAME, O_RDWR); 375 if (natfd == -1) 376 err(EXIT_FAILURE, "cannot open " IPNAT_NAME); 377 } 378 379 int 380 ipf_prepare_commit(u_int32_t id) 381 { 382 struct ftp_proxy_entry *fpe; 383 384 fpe = ftp_proxy_entry_find(id); 385 if (fpe == NULL) { 386 fpe = ftp_proxy_entry_create(id); 387 if (fpe == NULL) 388 return (-1); 389 } 390 fpe->status = 0; 391 392 return (0); 393 } 394 395 int 396 ipf_server_lookup(struct sockaddr *client, struct sockaddr *proxy, 397 struct sockaddr *server) 398 { 399 natlookup_t natlook; 400 ipfobj_t obj; 401 402 /* IPv4-only for now. */ 403 if (client->sa_family != AF_INET) { 404 errno = EPROTONOSUPPORT; 405 return (-1); 406 } 407 408 /* 409 * Build up the ipf object description structure. 410 */ 411 memset((void *)&obj, 0, sizeof(obj)); 412 obj.ipfo_rev = IPFILTER_VERSION; 413 obj.ipfo_size = sizeof(natlook); 414 obj.ipfo_ptr = &natlook; 415 obj.ipfo_type = IPFOBJ_NATLOOKUP; 416 /* 417 * Build up the ipf natlook structure. 418 */ 419 memset((void *)&natlook, 0, sizeof(natlook)); 420 natlook.nl_flags = IPN_TCPUDP; 421 natlook.nl_outip = satosin(client)->sin_addr; 422 natlook.nl_inip = satosin(proxy)->sin_addr; 423 natlook.nl_outport = satosin(client)->sin_port; 424 natlook.nl_inport = satosin(proxy)->sin_port; 425 426 if (ioctl(natfd, SIOCGNATL, &obj) == -1) 427 return (-1); 428 429 /* 430 * Return the real destination address and port number in the sockaddr 431 * passed in. 432 */ 433 memset((void *)server, 0, sizeof(struct sockaddr_in)); 434 satosin(server)->sin_len = sizeof(struct sockaddr_in); 435 satosin(server)->sin_family = AF_INET; 436 satosin(server)->sin_addr = natlook.nl_realip; 437 satosin(server)->sin_port = natlook.nl_realport; 438 439 return (0); 440 } 441