1 /* ebt_among
2 *
3 * Authors:
4 * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
5 *
6 * August, 2003
7 */
8
9 #include <errno.h>
10 #include <ctype.h>
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <xtables.h>
18 #include <arpa/inet.h>
19 #include <netinet/ether.h>
20 #include <netinet/in.h>
21 #include <linux/if_ether.h>
22 #include <linux/netfilter_bridge/ebt_among.h>
23 #include <sys/mman.h>
24 #include <sys/socket.h>
25 #include <sys/stat.h>
26 #include "iptables/nft.h"
27 #include "iptables/nft-bridge.h"
28
29 #define AMONG_DST '1'
30 #define AMONG_SRC '2'
31 #define AMONG_DST_F '3'
32 #define AMONG_SRC_F '4'
33
34 static const struct option bramong_opts[] = {
35 {"among-dst", required_argument, 0, AMONG_DST},
36 {"among-src", required_argument, 0, AMONG_SRC},
37 {"among-dst-file", required_argument, 0, AMONG_DST_F},
38 {"among-src-file", required_argument, 0, AMONG_SRC_F},
39 {0}
40 };
41
bramong_print_help(void)42 static void bramong_print_help(void)
43 {
44 printf(
45 "`among' options:\n"
46 "--among-dst [!] list : matches if ether dst is in list\n"
47 "--among-src [!] list : matches if ether src is in list\n"
48 "--among-dst-file [!] file : obtain dst list from file\n"
49 "--among-src-file [!] file : obtain src list from file\n"
50 "list has form:\n"
51 " xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]"
52 ",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n"
53 "Things in brackets are optional.\n"
54 "If you want to allow two (or more) IP addresses to one MAC address, you\n"
55 "can specify two (or more) pairs with the same MAC, e.g.\n"
56 " 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n"
57 );
58 }
59
60 static void
parse_nft_among_pair(char * buf,struct nft_among_pair * pair,bool have_ip)61 parse_nft_among_pair(char *buf, struct nft_among_pair *pair, bool have_ip)
62 {
63 char *sep = index(buf, '=');
64 struct ether_addr *ether;
65
66 if (sep) {
67 *sep = '\0';
68
69 if (!inet_aton(sep + 1, &pair->in))
70 xtables_error(PARAMETER_PROBLEM,
71 "Invalid IP address '%s'\n", sep + 1);
72 }
73 ether = ether_aton(buf);
74 if (!ether)
75 xtables_error(PARAMETER_PROBLEM,
76 "Invalid MAC address '%s'\n", buf);
77 memcpy(&pair->ether, ether, sizeof(*ether));
78 }
79
80 static void
parse_nft_among_pairs(struct nft_among_pair * pairs,char * buf,size_t cnt,bool have_ip)81 parse_nft_among_pairs(struct nft_among_pair *pairs, char *buf,
82 size_t cnt, bool have_ip)
83 {
84 size_t tmpcnt = 0;
85
86 buf = strtok(buf, ",");
87 while (buf) {
88 struct nft_among_pair pair = {};
89
90 parse_nft_among_pair(buf, &pair, have_ip);
91 nft_among_insert_pair(pairs, &tmpcnt, &pair);
92 buf = strtok(NULL, ",");
93 }
94 }
95
count_nft_among_pairs(char * buf)96 static size_t count_nft_among_pairs(char *buf)
97 {
98 size_t cnt = 0;
99 char *p = buf;
100
101 if (!*buf)
102 return 0;
103
104 do {
105 cnt++;
106 p = index(++p, ',');
107 } while (p);
108
109 return cnt;
110 }
111
nft_among_pairs_have_ip(char * buf)112 static bool nft_among_pairs_have_ip(char *buf)
113 {
114 return !!index(buf, '=');
115 }
116
bramong_parse(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)117 static int bramong_parse(int c, char **argv, int invert,
118 unsigned int *flags, const void *entry,
119 struct xt_entry_match **match)
120 {
121 struct nft_among_data *data = (struct nft_among_data *)(*match)->data;
122 struct xt_entry_match *new_match;
123 bool have_ip, dst = false;
124 size_t new_size, cnt;
125 struct stat stats;
126 int fd = -1, poff;
127 long flen = 0;
128
129 switch (c) {
130 case AMONG_DST_F:
131 dst = true;
132 /* fall through */
133 case AMONG_SRC_F:
134 if ((fd = open(optarg, O_RDONLY)) == -1)
135 xtables_error(PARAMETER_PROBLEM,
136 "Couldn't open file '%s'", optarg);
137 if (fstat(fd, &stats) < 0)
138 xtables_error(PARAMETER_PROBLEM,
139 "fstat(%s) failed: '%s'",
140 optarg, strerror(errno));
141 flen = stats.st_size;
142 /* use mmap because the file will probably be big */
143 optarg = mmap(0, flen, PROT_READ | PROT_WRITE,
144 MAP_PRIVATE, fd, 0);
145 if (optarg == MAP_FAILED)
146 xtables_error(PARAMETER_PROBLEM,
147 "Couldn't map file to memory");
148 if (optarg[flen-1] != '\n')
149 xtables_error(PARAMETER_PROBLEM,
150 "File should end with a newline");
151 if (strchr(optarg, '\n') != optarg+flen-1)
152 xtables_error(PARAMETER_PROBLEM,
153 "File should only contain one line");
154 optarg[flen-1] = '\0';
155 /* fall through */
156 case AMONG_DST:
157 if (c == AMONG_DST)
158 dst = true;
159 /* fall through */
160 case AMONG_SRC:
161 break;
162 default:
163 return 0;
164 }
165
166 cnt = count_nft_among_pairs(optarg);
167 if (cnt == 0)
168 return 0;
169
170 new_size = data->src.cnt + data->dst.cnt + cnt;
171 new_size *= sizeof(struct nft_among_pair);
172 new_size += XT_ALIGN(sizeof(struct xt_entry_match)) +
173 sizeof(struct nft_among_data);
174 new_match = xtables_calloc(1, new_size);
175 memcpy(new_match, *match, (*match)->u.match_size);
176 new_match->u.match_size = new_size;
177
178 data = (struct nft_among_data *)new_match->data;
179 have_ip = nft_among_pairs_have_ip(optarg);
180 poff = nft_among_prepare_data(data, dst, cnt, invert, have_ip);
181 parse_nft_among_pairs(data->pairs + poff, optarg, cnt, have_ip);
182
183 free(*match);
184 *match = new_match;
185
186 if (c == AMONG_DST_F || c == AMONG_SRC_F) {
187 munmap(argv, flen);
188 close(fd);
189 }
190 return 1;
191 }
192
__bramong_print(struct nft_among_pair * pairs,int cnt,bool inv,bool have_ip)193 static void __bramong_print(struct nft_among_pair *pairs,
194 int cnt, bool inv, bool have_ip)
195 {
196 const char *isep = inv ? "! " : "";
197 int i;
198
199 for (i = 0; i < cnt; i++) {
200 printf("%s", isep);
201 isep = ",";
202
203 printf("%s", ether_ntoa(&pairs[i].ether));
204 if (pairs[i].in.s_addr != INADDR_ANY)
205 printf("=%s", inet_ntoa(pairs[i].in));
206 }
207 printf(" ");
208 }
209
bramong_print(const void * ip,const struct xt_entry_match * match,int numeric)210 static void bramong_print(const void *ip, const struct xt_entry_match *match,
211 int numeric)
212 {
213 struct nft_among_data *data = (struct nft_among_data *)match->data;
214
215 if (data->src.cnt) {
216 printf("--among-src ");
217 __bramong_print(data->pairs,
218 data->src.cnt, data->src.inv, data->src.ip);
219 }
220 if (data->dst.cnt) {
221 printf("--among-dst ");
222 __bramong_print(data->pairs + data->src.cnt,
223 data->dst.cnt, data->dst.inv, data->dst.ip);
224 }
225 }
226
227 static struct xtables_match bramong_match = {
228 .name = "among",
229 .revision = 0,
230 .version = XTABLES_VERSION,
231 .family = NFPROTO_BRIDGE,
232 .size = XT_ALIGN(sizeof(struct nft_among_data)),
233 .userspacesize = XT_ALIGN(sizeof(struct nft_among_data)),
234 .help = bramong_print_help,
235 .parse = bramong_parse,
236 .print = bramong_print,
237 .extra_opts = bramong_opts,
238 };
239
_init(void)240 void _init(void)
241 {
242 xtables_register_match(&bramong_match);
243 }
244