1/* tables.c is Copyright (c) 2014 Sven Falempin  All Rights Reserved.
2
3   Author's email: sfalempin@citypassenger.com
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; version 2 dated June, 1991, or
8   (at your option) version 3 dated 29 June, 2007.
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
19#include "dnsmasq.h"
20
21#if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
22
23#ifndef __FreeBSD__
24#include <string.h>
25#endif
26
27#include <sys/types.h>
28#include <sys/ioctl.h>
29
30#include <net/if.h>
31#include <netinet/in.h>
32#include <net/pfvar.h>
33
34#include <err.h>
35#include <errno.h>
36#include <fcntl.h>
37
38#define UNUSED(x) (void)(x)
39
40static char *pf_device = "/dev/pf";
41static int dev = -1;
42
43static char *pfr_strerror(int errnum)
44{
45  switch (errnum)
46    {
47    case ESRCH:
48      return "Table does not exist";
49    case ENOENT:
50      return "Anchor or Ruleset does not exist";
51    default:
52      return strerror(errnum);
53    }
54}
55
56static int pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
57{
58  struct pfioc_table io;
59
60  if (size < 0 || (size && tbl == NULL))
61    {
62      errno = EINVAL;
63      return (-1);
64    }
65  bzero(&io, sizeof io);
66  io.pfrio_flags = flags;
67  io.pfrio_buffer = tbl;
68  io.pfrio_esize = sizeof(*tbl);
69  io.pfrio_size = size;
70  if (ioctl(dev, DIOCRADDTABLES, &io))
71    return (-1);
72  if (nadd != NULL)
73    *nadd = io.pfrio_nadd;
74  return (0);
75}
76
77static int fill_addr(const struct all_addr *ipaddr, int flags, struct pfr_addr* addr) {
78  if ( !addr || !ipaddr)
79    {
80      my_syslog(LOG_ERR, _("error: fill_addr missused"));
81      return -1;
82    }
83  bzero(addr, sizeof(*addr));
84#ifdef HAVE_IPV6
85  if (flags & F_IPV6)
86    {
87      addr->pfra_af = AF_INET6;
88      addr->pfra_net = 0x80;
89      memcpy(&(addr->pfra_ip6addr), &(ipaddr->addr), sizeof(struct in6_addr));
90    }
91  else
92#endif
93    {
94      addr->pfra_af = AF_INET;
95      addr->pfra_net = 0x20;
96      addr->pfra_ip4addr.s_addr = ipaddr->addr.addr4.s_addr;
97    }
98  return 1;
99}
100
101/*****************************************************************************/
102
103void ipset_init(void)
104{
105  dev = open( pf_device, O_RDWR);
106  if (dev == -1)
107    {
108      err(1, "%s", pf_device);
109      die (_("failed to access pf devices: %s"), NULL, EC_MISC);
110    }
111}
112
113int add_to_ipset(const char *setname, const struct all_addr *ipaddr,
114		      int flags, int remove)
115{
116  struct pfr_addr addr;
117  struct pfioc_table io;
118  struct pfr_table table;
119  int n = 0, rc = 0;
120
121  if ( dev == -1 )
122    {
123      my_syslog(LOG_ERR, _("warning: no opened pf devices %s"), pf_device);
124      return -1;
125    }
126
127  bzero(&table, sizeof(struct pfr_table));
128  table.pfrt_flags |= PFR_TFLAG_PERSIST;
129  if ( strlen(setname) >= PF_TABLE_NAME_SIZE )
130    {
131      my_syslog(LOG_ERR, _("error: cannot use table name %s"), setname);
132      errno = ENAMETOOLONG;
133      return -1;
134    }
135
136  if ( strlcpy(table.pfrt_name, setname,
137               sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
138    {
139      my_syslog(LOG_ERR, _("error: cannot strlcpy table name %s"), setname);
140      return -1;
141    }
142
143  if ((rc = pfr_add_tables(&table, 1, &n, 0)))
144    {
145      my_syslog(LOG_WARNING, _("warning: pfr_add_tables: %s(%d)"),
146		pfr_strerror(errno),rc);
147      return -1;
148    }
149  table.pfrt_flags &= ~PFR_TFLAG_PERSIST;
150  if (n)
151    my_syslog(LOG_INFO, _("info: table created"));
152
153  fill_addr(ipaddr,flags,&addr);
154  bzero(&io, sizeof(io));
155  io.pfrio_flags = 0;
156  io.pfrio_table = table;
157  io.pfrio_buffer = &addr;
158  io.pfrio_esize = sizeof(addr);
159  io.pfrio_size = 1;
160  if (ioctl(dev, ( remove ? DIOCRDELADDRS : DIOCRADDADDRS ), &io))
161    {
162      my_syslog(LOG_WARNING, _("warning: DIOCR%sADDRS: %s"), ( remove ? "DEL" : "ADD" ), pfr_strerror(errno));
163      return -1;
164    }
165
166  my_syslog(LOG_INFO, _("%d addresses %s"),
167            io.pfrio_nadd, ( remove ? "removed" : "added" ));
168
169  return io.pfrio_nadd;
170}
171
172
173#endif
174