00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "config.h"
00010
00011 #include <sys/types.h>
00012 #include <sys/ioctl.h>
00013 #include <sys/socket.h>
00014
00015 #include <net/if.h>
00016 #include <netinet/in.h>
00017 #include <net/pfvar.h>
00018
00019 #include <assert.h>
00020 #include <errno.h>
00021 #include <fcntl.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <unistd.h>
00026
00027 #include "dnet.h"
00028
00029
00030
00031
00032 #if defined(DIOCRCLRTABLES)
00033
00034
00035
00036
00037 # define HAVE_PF_CHANGE_GET_TICKET 1
00038
00039
00040
00041
00042 # define PFRA_ADDR(ra) (ra)->addr.v.a.addr.v4.s_addr
00043 # define PFRA_MASK(ra) (ra)->addr.v.a.mask.v4.s_addr
00044 # define pfioc_changerule pfioc_rule
00045 # define oldrule rule
00046 # define newrule rule
00047 #elif defined(DIOCBEGINADDRS)
00048
00049 # define PFRA_ADDR(ra) (ra)->addr.addr.v4.s_addr
00050 # define PFRA_MASK(ra) (ra)->addr.mask.v4.s_addr
00051 #elif defined(PFRULE_FRAGMENT)
00052
00053
00054 # define PFRA_ADDR(ra) (ra)->addr.addr.v4.s_addr
00055 # define PFRA_MASK(ra) (ra)->mask.v4.s_addr
00056 #elif defined (PF_AEQ)
00057
00058
00059 # define PFRA_ADDR(ra) (ra)->addr.v4.s_addr
00060 # define PFRA_MASK(ra) (ra)->mask.v4.s_addr
00061 #else
00062
00063 # define PFRA_ADDR(ra) (ra)->addr
00064 # define PFRA_ADDR(ra) (ra)->mask
00065 #endif
00066
00067 struct fw_handle {
00068 int fd;
00069 };
00070
00071 static void
00072 fr_to_pr(const struct fw_rule *fr, struct pf_rule *pr)
00073 {
00074 memset(pr, 0, sizeof(*pr));
00075
00076 strlcpy(pr->ifname, fr->fw_device, sizeof(pr->ifname));
00077
00078 pr->action = (fr->fw_op == FW_OP_ALLOW) ? PF_PASS : PF_DROP;
00079 pr->direction = (fr->fw_dir == FW_DIR_IN) ? PF_IN : PF_OUT;
00080 pr->proto = fr->fw_proto;
00081
00082 pr->af = AF_INET;
00083 PFRA_ADDR(&pr->src) = fr->fw_src.addr_ip;
00084 addr_btom(fr->fw_src.addr_bits, &(PFRA_MASK(&pr->src)), IP_ADDR_LEN);
00085
00086 PFRA_ADDR(&pr->dst) = fr->fw_dst.addr_ip;
00087 addr_btom(fr->fw_dst.addr_bits, &(PFRA_MASK(&pr->dst)), IP_ADDR_LEN);
00088
00089 switch (fr->fw_proto) {
00090 case IP_PROTO_ICMP:
00091 if (fr->fw_sport[1])
00092 pr->type = (u_char)(fr->fw_sport[0] &
00093 fr->fw_sport[1]) + 1;
00094 if (fr->fw_dport[1])
00095 pr->code = (u_char)(fr->fw_dport[0] &
00096 fr->fw_dport[1]) + 1;
00097 break;
00098 case IP_PROTO_TCP:
00099 case IP_PROTO_UDP:
00100 pr->src.port[0] = htons(fr->fw_sport[0]);
00101 pr->src.port[1] = htons(fr->fw_sport[1]);
00102 if (pr->src.port[0] == pr->src.port[1]) {
00103 pr->src.port_op = PF_OP_EQ;
00104 } else
00105 pr->src.port_op = PF_OP_IRG;
00106
00107 pr->dst.port[0] = htons(fr->fw_dport[0]);
00108 pr->dst.port[1] = htons(fr->fw_dport[1]);
00109 if (pr->dst.port[0] == pr->dst.port[1]) {
00110 pr->dst.port_op = PF_OP_EQ;
00111 } else
00112 pr->dst.port_op = PF_OP_IRG;
00113 break;
00114 }
00115 }
00116
00117 static int
00118 pr_to_fr(const struct pf_rule *pr, struct fw_rule *fr)
00119 {
00120 memset(fr, 0, sizeof(*fr));
00121
00122 strlcpy(fr->fw_device, pr->ifname, sizeof(fr->fw_device));
00123
00124 if (pr->action == PF_DROP)
00125 fr->fw_op = FW_OP_BLOCK;
00126 else if (pr->action == PF_PASS)
00127 fr->fw_op = FW_OP_ALLOW;
00128 else
00129 return (-1);
00130
00131 fr->fw_dir = pr->direction == PF_IN ? FW_DIR_IN : FW_DIR_OUT;
00132 fr->fw_proto = pr->proto;
00133
00134 if (pr->af != AF_INET)
00135 return (-1);
00136
00137 fr->fw_src.addr_type = ADDR_TYPE_IP;
00138 addr_mtob(&(PFRA_MASK(&pr->src)), IP_ADDR_LEN, &fr->fw_src.addr_bits);
00139 fr->fw_src.addr_ip = PFRA_ADDR(&pr->src);
00140
00141 fr->fw_dst.addr_type = ADDR_TYPE_IP;
00142 addr_mtob(&(PFRA_MASK(&pr->dst)), IP_ADDR_LEN, &fr->fw_dst.addr_bits);
00143 fr->fw_dst.addr_ip = PFRA_ADDR(&pr->dst);
00144
00145 switch (fr->fw_proto) {
00146 case IP_PROTO_ICMP:
00147 if (pr->type) {
00148 fr->fw_sport[0] = pr->type - 1;
00149 fr->fw_sport[1] = 0xff;
00150 }
00151 if (pr->code) {
00152 fr->fw_dport[0] = pr->code - 1;
00153 fr->fw_dport[1] = 0xff;
00154 }
00155 break;
00156 case IP_PROTO_TCP:
00157 case IP_PROTO_UDP:
00158 fr->fw_sport[0] = ntohs(pr->src.port[0]);
00159 fr->fw_sport[1] = ntohs(pr->src.port[1]);
00160 if (pr->src.port_op == PF_OP_EQ)
00161 fr->fw_sport[1] = fr->fw_sport[0];
00162
00163 fr->fw_dport[0] = ntohs(pr->dst.port[0]);
00164 fr->fw_dport[1] = ntohs(pr->dst.port[1]);
00165 if (pr->dst.port_op == PF_OP_EQ)
00166 fr->fw_dport[1] = fr->fw_dport[0];
00167 }
00168 return (0);
00169 }
00170
00171 #ifdef HAVE_PF_CHANGE_GET_TICKET
00172 static int
00173 _fw_cmp(const struct fw_rule *a, const struct fw_rule *b)
00174 {
00175 if (strcmp(a->fw_device, b->fw_device) != 0 || a->fw_op != b->fw_op ||
00176 a->fw_dir != b->fw_dir || a->fw_proto != b->fw_proto ||
00177 addr_cmp(&a->fw_src, &b->fw_src) != 0 ||
00178 addr_cmp(&a->fw_dst, &b->fw_dst) != 0 ||
00179 memcmp(a->fw_sport, b->fw_sport, sizeof(a->fw_sport)) != 0 ||
00180 memcmp(a->fw_dport, b->fw_dport, sizeof(a->fw_dport)) != 0)
00181 return (-1);
00182 return (0);
00183 }
00184 #endif
00185
00186 fw_t *
00187 fw_open(void)
00188 {
00189 fw_t *fw;
00190
00191 if ((fw = calloc(1, sizeof(*fw))) != NULL) {
00192 if ((fw->fd = open("/dev/pf", O_RDWR)) < 0)
00193 return (fw_close(fw));
00194 }
00195 return (fw);
00196 }
00197
00198 int
00199 fw_add(fw_t *fw, const struct fw_rule *rule)
00200 {
00201 struct pfioc_changerule pcr;
00202
00203 assert(fw != NULL && rule != NULL);
00204 memset(&pcr, 0, sizeof(pcr));
00205 #ifdef HAVE_PF_CHANGE_GET_TICKET
00206 {
00207 struct fw_rule fr;
00208
00209 if (ioctl(fw->fd, DIOCGETRULES, &pcr) < 0)
00210 return (-1);
00211 while ((int)--pcr.nr >= 0) {
00212 if (ioctl(fw->fd, DIOCGETRULE, &pcr) == 0 &&
00213 pr_to_fr(&pcr.rule, &fr) == 0) {
00214 if (_fw_cmp(rule, &fr) == 0) {
00215 errno = EEXIST;
00216 return (-1);
00217 }
00218 }
00219 }
00220 }
00221 #endif
00222 #ifdef DIOCBEGINADDRS
00223 {
00224 struct pfioc_pooladdr ppa;
00225
00226 if (ioctl(fw->fd, DIOCBEGINADDRS, &ppa) < 0)
00227 return (-1);
00228 pcr.pool_ticket = ppa.ticket;
00229 }
00230 #endif
00231 pcr.action = PF_CHANGE_ADD_TAIL;
00232 fr_to_pr(rule, &pcr.newrule);
00233
00234 return (ioctl(fw->fd, DIOCCHANGERULE, &pcr));
00235 }
00236
00237 int
00238 fw_delete(fw_t *fw, const struct fw_rule *rule)
00239 {
00240 struct pfioc_changerule pcr;
00241
00242 assert(fw != NULL && rule != NULL);
00243 memset(&pcr, 0, sizeof(pcr));
00244 #ifdef HAVE_PF_CHANGE_GET_TICKET
00245 {
00246 struct fw_rule fr;
00247 int found = 0;
00248
00249 if (ioctl(fw->fd, DIOCGETRULES, &pcr) < 0)
00250 return (-1);
00251 while ((int)--pcr.nr >= 0) {
00252 if (ioctl(fw->fd, DIOCGETRULE, &pcr) == 0 &&
00253 pr_to_fr(&pcr.rule, &fr) == 0) {
00254 if (_fw_cmp(rule, &fr) == 0) {
00255 found = 1;
00256 break;
00257 }
00258 }
00259 }
00260 if (!found) {
00261 errno = ENOENT;
00262 return (-1);
00263 }
00264 }
00265 #endif
00266 #ifdef DIOCBEGINADDRS
00267 {
00268 struct pfioc_pooladdr ppa;
00269
00270 if (ioctl(fw->fd, DIOCBEGINADDRS, &ppa) < 0)
00271 return (-1);
00272 pcr.pool_ticket = ppa.ticket;
00273 }
00274 #endif
00275 pcr.action = PF_CHANGE_REMOVE;
00276 fr_to_pr(rule, &pcr.oldrule);
00277
00278 return (ioctl(fw->fd, DIOCCHANGERULE, &pcr));
00279 }
00280
00281 int
00282 fw_loop(fw_t *fw, fw_handler callback, void *arg)
00283 {
00284 struct pfioc_rule pr;
00285 struct fw_rule fr;
00286 uint32_t n, max;
00287 int ret = 0;
00288
00289 memset(&pr, 0, sizeof(pr));
00290 if (ioctl(fw->fd, DIOCGETRULES, &pr) < 0)
00291 return (-1);
00292
00293 for (n = 0, max = pr.nr; n < max; n++) {
00294 pr.nr = n;
00295
00296 if ((ret = ioctl(fw->fd, DIOCGETRULE, &pr)) < 0)
00297 break;
00298 #ifdef PF_TABLE_NAME_SIZE
00299
00300 if (pr.rule.src.addr.type == PF_ADDR_TABLE ||
00301 pr.rule.dst.addr.type == PF_ADDR_TABLE)
00302 continue;
00303 #endif
00304 if (pr_to_fr(&pr.rule, &fr) < 0)
00305 continue;
00306 if ((ret = callback(&fr, arg)) != 0)
00307 break;
00308 }
00309 return (ret);
00310 }
00311
00312 fw_t *
00313 fw_close(fw_t *fw)
00314 {
00315 if (fw != NULL) {
00316 if (fw->fd >= 0)
00317 close(fw->fd);
00318 free(fw);
00319 }
00320 return (NULL);
00321 }