Home | History | Annotate | Line # | Download | only in tftp-proxy
      1 /*	$NetBSD: filter.c,v 1.2 2008/06/18 09:06:25 yamt Exp $ */
      2 /*	$OpenBSD: filter.c,v 1.2 2007/06/23 15:51:21 jcs Exp $ */
      3 
      4 /*
      5  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd (at) sentia.nl>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <syslog.h>
     21 
     22 #include <sys/ioctl.h>
     23 #include <sys/types.h>
     24 #include <sys/socket.h>
     25 
     26 #include <net/if.h>
     27 #include <net/pfvar.h>
     28 #include <netinet/in.h>
     29 #include <netinet/tcp.h>
     30 #include <arpa/inet.h>
     31 
     32 #include <err.h>
     33 #include <errno.h>
     34 #include <fcntl.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <unistd.h>
     39 
     40 #include "filter.h"
     41 
     42 /* From netinet/in.h, but only _KERNEL_ gets them. */
     43 #define satosin(sa)	((struct sockaddr_in *)(sa))
     44 #define satosin6(sa)	((struct sockaddr_in6 *)(sa))
     45 
     46 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
     47 
     48 int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
     49     u_int16_t, u_int8_t);
     50 int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
     51     struct sockaddr_in *, u_int8_t);
     52 int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
     53     struct sockaddr_in6 *, u_int8_t);
     54 
     55 static struct pfioc_pooladdr	pfp;
     56 static struct pfioc_rule	pfr;
     57 static struct pfioc_trans	pft;
     58 static struct pfioc_trans_e	pfte[TRANS_SIZE];
     59 static int dev, rule_log;
     60 static char *qname;
     61 
     62 int
     63 add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
     64     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
     65 {
     66 	if (!src || !dst || !d_port || !proto) {
     67 		errno = EINVAL;
     68 		return (-1);
     69 	}
     70 
     71 	if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1)
     72 		return (-1);
     73 
     74 	pfr.rule.direction = dir;
     75 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
     76 		return (-1);
     77 
     78 	return (0);
     79 }
     80 
     81 int
     82 add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
     83     u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
     84     u_int16_t nat_range_high, u_int8_t proto)
     85 {
     86 	if (!src || !dst || !d_port || !nat || !nat_range_low || !proto ||
     87 	    (src->sa_family != nat->sa_family)) {
     88 		errno = EINVAL;
     89 		return (-1);
     90 	}
     91 
     92 	if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1)
     93 		return (-1);
     94 
     95 	if (nat->sa_family == AF_INET) {
     96 		memcpy(&pfp.addr.addr.v.a.addr.v4,
     97 		    &satosin(nat)->sin_addr.s_addr, 4);
     98 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
     99 	} else {
    100 		memcpy(&pfp.addr.addr.v.a.addr.v6,
    101 		    &satosin6(nat)->sin6_addr.s6_addr, 16);
    102 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
    103 	}
    104 	if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
    105 		return (-1);
    106 
    107 	pfr.rule.rpool.proxy_port[0] = nat_range_low;
    108 	pfr.rule.rpool.proxy_port[1] = nat_range_high;
    109 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
    110 		return (-1);
    111 
    112 	return (0);
    113 }
    114 
    115 int
    116 add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
    117     u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
    118 {
    119 	if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
    120 	    (src->sa_family != rdr->sa_family)) {
    121 		errno = EINVAL;
    122 		return (-1);
    123 	}
    124 
    125 	if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1)
    126 		return (-1);
    127 
    128 	if (rdr->sa_family == AF_INET) {
    129 		memcpy(&pfp.addr.addr.v.a.addr.v4,
    130 		    &satosin(rdr)->sin_addr.s_addr, 4);
    131 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
    132 	} else {
    133 		memcpy(&pfp.addr.addr.v.a.addr.v6,
    134 		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
    135 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
    136 	}
    137 	if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
    138 		return (-1);
    139 
    140 	pfr.rule.rpool.proxy_port[0] = rdr_port;
    141 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
    142 		return (-1);
    143 
    144 	return (0);
    145 }
    146 
    147 int
    148 do_commit(void)
    149 {
    150 	if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
    151 		return (-1);
    152 
    153 	return (0);
    154 }
    155 
    156 int
    157 do_rollback(void)
    158 {
    159 	if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
    160 		return (-1);
    161 
    162 	return (0);
    163 }
    164 
    165 void
    166 init_filter(char *opt_qname, int opt_verbose)
    167 {
    168 	struct pf_status status;
    169 
    170 	qname = opt_qname;
    171 
    172 	if (opt_verbose == 1)
    173 		rule_log = PF_LOG;
    174 	else if (opt_verbose == 2)
    175 		rule_log = PF_LOG_ALL;
    176 
    177 	dev = open("/dev/pf", O_RDWR);
    178 	if (dev == -1) {
    179 		syslog(LOG_ERR, "can't open /dev/pf");
    180 		exit(1);
    181 	}
    182 	if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
    183 		syslog(LOG_ERR, "DIOCGETSTATUS");
    184 		exit(1);
    185 	}
    186 	if (!status.running) {
    187 		syslog(LOG_ERR, "pf is disabled");
    188 		exit(1);
    189 	}
    190 }
    191 
    192 int
    193 prepare_commit(u_int32_t id)
    194 {
    195 	char an[PF_ANCHOR_NAME_SIZE];
    196 	int i;
    197 
    198 	memset(&pft, 0, sizeof pft);
    199 	pft.size = TRANS_SIZE;
    200 	pft.esize = sizeof pfte[0];
    201 	pft.array = pfte;
    202 
    203 	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
    204 	    getpid(), id);
    205 	for (i = 0; i < TRANS_SIZE; i++) {
    206 		memset(&pfte[i], 0, sizeof pfte[0]);
    207 		strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
    208 		switch (i) {
    209 		case TRANS_FILTER:
    210 			pfte[i].rs_num = PF_RULESET_FILTER;
    211 			break;
    212 		case TRANS_NAT:
    213 			pfte[i].rs_num = PF_RULESET_NAT;
    214 			break;
    215 		case TRANS_RDR:
    216 			pfte[i].rs_num = PF_RULESET_RDR;
    217 			break;
    218 		default:
    219 			errno = EINVAL;
    220 			return (-1);
    221 		}
    222 	}
    223 
    224 	if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
    225 		return (-1);
    226 
    227 	return (0);
    228 }
    229 
    230 int
    231 prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
    232     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
    233 {
    234 	char an[PF_ANCHOR_NAME_SIZE];
    235 
    236 	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
    237 	    (src->sa_family != dst->sa_family)) {
    238 	    	errno = EPROTONOSUPPORT;
    239 		return (-1);
    240 	}
    241 
    242 	memset(&pfp, 0, sizeof pfp);
    243 	memset(&pfr, 0, sizeof pfr);
    244 	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
    245 	    getpid(), id);
    246 	strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
    247 	strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
    248 
    249 	switch (rs_num) {
    250 	case PF_RULESET_FILTER:
    251 		pfr.ticket = pfte[TRANS_FILTER].ticket;
    252 		break;
    253 	case PF_RULESET_NAT:
    254 		pfr.ticket = pfte[TRANS_NAT].ticket;
    255 		break;
    256 	case PF_RULESET_RDR:
    257 		pfr.ticket = pfte[TRANS_RDR].ticket;
    258 		break;
    259 	default:
    260 		errno = EINVAL;
    261 		return (-1);
    262 	}
    263 	if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
    264 		return (-1);
    265 	pfr.pool_ticket = pfp.ticket;
    266 
    267 	/* Generic for all rule types. */
    268 	pfr.rule.af = src->sa_family;
    269 	pfr.rule.proto = proto;
    270 	pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
    271 	pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
    272 	if (src->sa_family == AF_INET) {
    273 		memcpy(&pfr.rule.src.addr.v.a.addr.v4,
    274 		    &satosin(src)->sin_addr.s_addr, 4);
    275 		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
    276 		memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
    277 		    &satosin(dst)->sin_addr.s_addr, 4);
    278 		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
    279 	} else {
    280 		memcpy(&pfr.rule.src.addr.v.a.addr.v6,
    281 		    &satosin6(src)->sin6_addr.s6_addr, 16);
    282 		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
    283 		memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
    284 		    &satosin6(dst)->sin6_addr.s6_addr, 16);
    285 		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
    286 	}
    287 	pfr.rule.dst.port_op = PF_OP_EQ;
    288 	pfr.rule.dst.port[0] = htons(d_port);
    289 
    290 	switch (rs_num) {
    291 	case PF_RULESET_FILTER:
    292 		/*
    293 		 * pass quick [log] inet[6] proto tcp \
    294 		 *     from $src to $dst port = $d_port flags S/SAFR keep state
    295 		 *     (max 1) [queue qname]
    296 		 */
    297 		pfr.rule.action = PF_PASS;
    298 		pfr.rule.quick = 1;
    299 		pfr.rule.log = rule_log;
    300 		pfr.rule.keep_state = 1;
    301 		pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
    302 		pfr.rule.flagset = (proto == IPPROTO_TCP ?
    303 		    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
    304 		pfr.rule.max_states = 1;
    305 		if (qname != NULL)
    306 			strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
    307 		break;
    308 	case PF_RULESET_NAT:
    309 		/*
    310 		 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
    311 		 */
    312 		pfr.rule.action = PF_NAT;
    313 		break;
    314 	case PF_RULESET_RDR:
    315 		/*
    316 		 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
    317 		 */
    318 		pfr.rule.action = PF_RDR;
    319 		break;
    320 	default:
    321 		errno = EINVAL;
    322 		return (-1);
    323 	}
    324 
    325 	return (0);
    326 }
    327 
    328 int
    329 server_lookup(struct sockaddr *client, struct sockaddr *proxy,
    330     struct sockaddr *server, u_int8_t proto)
    331 {
    332 	if (client->sa_family == AF_INET)
    333 		return (server_lookup4(satosin(client), satosin(proxy),
    334 		    satosin(server), proto));
    335 
    336 	if (client->sa_family == AF_INET6)
    337 		return (server_lookup6(satosin6(client), satosin6(proxy),
    338 		    satosin6(server), proto));
    339 
    340 	errno = EPROTONOSUPPORT;
    341 	return (-1);
    342 }
    343 
    344 int
    345 server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
    346     struct sockaddr_in *server, u_int8_t proto)
    347 {
    348 	struct pfioc_natlook pnl;
    349 
    350 	memset(&pnl, 0, sizeof pnl);
    351 	pnl.direction = PF_OUT;
    352 	pnl.af = AF_INET;
    353 	pnl.proto = proto;
    354 	memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
    355 	memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
    356 	pnl.sport = client->sin_port;
    357 	pnl.dport = proxy->sin_port;
    358 
    359 	if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
    360 		return (-1);
    361 
    362 	memset(server, 0, sizeof(struct sockaddr_in));
    363 	server->sin_len = sizeof(struct sockaddr_in);
    364 	server->sin_family = AF_INET;
    365 	memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
    366 	    sizeof server->sin_addr.s_addr);
    367 	server->sin_port = pnl.rdport;
    368 
    369 	return (0);
    370 }
    371 
    372 int
    373 server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
    374     struct sockaddr_in6 *server, u_int8_t proto)
    375 {
    376 	struct pfioc_natlook pnl;
    377 
    378 	memset(&pnl, 0, sizeof pnl);
    379 	pnl.direction = PF_OUT;
    380 	pnl.af = AF_INET6;
    381 	pnl.proto = proto;
    382 	memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
    383 	memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
    384 	pnl.sport = client->sin6_port;
    385 	pnl.dport = proxy->sin6_port;
    386 
    387 	if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
    388 		return (-1);
    389 
    390 	memset(server, 0, sizeof(struct sockaddr_in6));
    391 	server->sin6_len = sizeof(struct sockaddr_in6);
    392 	server->sin6_family = AF_INET6;
    393 	memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
    394 	    sizeof server->sin6_addr);
    395 	server->sin6_port = pnl.rdport;
    396 
    397 	return (0);
    398 }
    399