ipfw3: layer2 filter with lookup table
[dragonfly.git] / lib / libipfw3 / layer2 / ipfw3_layer2.c
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@gmail.com>
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <err.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sysexits.h>
41
42 #include <net/if.h>
43 #include <net/route.h>
44 #include <net/pfil.h>
45 #include <netinet/in.h>
46
47 #include "../../../sys/net/ipfw3/ip_fw3.h"
48 #include "../../../sbin/ipfw3/ipfw.h"
49 #include "ipfw3_layer2.h"
50
51 /*
52  * Returns the number of bits set (from left) in a contiguous bitmask,
53  * or -1 if the mask is not contiguous.
54  * XXX this needs a proper fix.
55  * This effectively works on masks in big-endian (network) format.
56  * when compiled on little endian architectures.
57  *
58  * First bit is bit 7 of the first byte -- note, for MAC addresses,
59  * the first bit on the wire is bit 0 of the first byte.
60  * len is the max length in bits.
61  */
62 static int
63 contigmask(u_char *p, int len)
64 {
65         int i, n;
66         for (i = 0; i < len ; i++) {
67                 if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
68                         break;
69         }
70         for (n = i + 1; n < len; n++) {
71                 if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
72                         return -1; /* mask not contiguous */
73         }
74         return i;
75 }
76
77 /*
78  * prints a MAC address/mask pair
79  */
80 static void
81 print_mac(u_char *addr, u_char *mask)
82 {
83         int l = contigmask(mask, 48);
84
85         if (l == 0) {
86                 printf(" any");
87         } else {
88                 printf(" %02x:%02x:%02x:%02x:%02x:%02x",
89                         addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
90                 if (l == -1) {
91                         printf("&%02x:%02x:%02x:%02x:%02x:%02x",
92                                 mask[0], mask[1], mask[2],
93                                 mask[3], mask[4], mask[5]);
94                 } else if (l < 48) {
95                         printf("/%d", l);
96                 }
97         }
98 }
99
100 static void
101 get_mac_addr_mask(char *p, u_char *addr, u_char *mask)
102 {
103         int i, l;
104
105         for (i = 0; i < 6; i++)
106                 addr[i] = mask[i] = 0;
107         if (!strcmp(p, "any"))
108                 return;
109
110         for (i = 0; *p && i < 6; i++, p++) {
111                 addr[i] = strtol(p, &p, 16);
112                 if (*p != ':') /* we start with the mask */
113                         break;
114         }
115         if (*p == '/') { /* mask len */
116                 l = strtol(p + 1, &p, 0);
117                 for (i = 0; l > 0; l -= 8, i++)
118                         mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
119         } else if (*p == '&') { /* mask */
120                 for (i = 0, p++; *p && i < 6; i++, p++) {
121                         mask[i] = strtol(p, &p, 16);
122                         if (*p != ':')
123                                 break;
124                 }
125         } else if (*p == '\0') {
126                 for (i = 0; i < 6; i++)
127                         mask[i] = 0xff;
128         }
129         for (i = 0; i < 6; i++)
130                 addr[i] &= mask[i];
131 }
132
133 void
134 parse_layer2(ipfw_insn **cmd, int *ac, char **av[])
135 {
136         (*cmd)->opcode = O_LAYER2_LAYER2;
137         (*cmd)->module = MODULE_LAYER2_ID;
138         (*cmd)->len =  ((*cmd)->len&(F_NOT|F_OR))|LEN_OF_IPFWINSN;
139         NEXT_ARG1;
140 }
141
142 void
143 parse_mac_from(ipfw_insn **cmd, int *ac, char **av[])
144 {
145         NEED(*ac, 2, "mac-from src");
146         NEXT_ARG1;
147         if (strcmp(**av, "table") == 0) {
148                 NEED(*ac, 2, "mac-from table N");
149                 NEXT_ARG1;
150                 (*cmd)->opcode = O_LAYER2_MAC_SRC_LOOKUP;
151                 (*cmd)->module = MODULE_LAYER2_ID;
152                 (*cmd)->len =  (*cmd)->len | F_INSN_SIZE(ipfw_insn);
153                 (*cmd)->arg1 = strtoul(**av, NULL, 10);
154                 NEXT_ARG1;
155         } else {
156                 (*cmd)->opcode = O_LAYER2_MAC_SRC;
157                 (*cmd)->module = MODULE_LAYER2_ID;
158                 (*cmd)->len =  (*cmd)->len | F_INSN_SIZE(ipfw_insn_mac);
159                 ipfw_insn_mac *mac = (ipfw_insn_mac *)(*cmd);
160                 /* src */
161                 get_mac_addr_mask(**av, &(mac->addr[6]), &(mac->mask[6]));
162                 NEXT_ARG1;
163         }
164 }
165
166 void
167 parse_mac_to(ipfw_insn **cmd, int *ac, char **av[])
168 {
169         NEED(*ac, 2, "mac-to dst");
170         NEXT_ARG1;
171         if (strcmp(**av, "table") == 0) {
172                 NEED(*ac, 2, "mac-to table N");
173                 NEXT_ARG1;
174                 (*cmd)->opcode = O_LAYER2_MAC_DST_LOOKUP;
175                 (*cmd)->module = MODULE_LAYER2_ID;
176                 (*cmd)->len =  (*cmd)->len | F_INSN_SIZE(ipfw_insn);
177                 (*cmd)->arg1 = strtoul(**av, NULL, 10);
178                 NEXT_ARG1;
179         } else {
180                 (*cmd)->opcode = O_LAYER2_MAC_DST;
181                 (*cmd)->module = MODULE_LAYER2_ID;
182                 (*cmd)->len =  (*cmd)->len | F_INSN_SIZE(ipfw_insn_mac);
183                 ipfw_insn_mac *mac = (ipfw_insn_mac *)(*cmd);
184                 /* dst */
185                 get_mac_addr_mask(**av, mac->addr, mac->mask);
186                 NEXT_ARG1;
187         }
188 }
189
190
191 void
192 parse_mac(ipfw_insn **cmd, int *ac, char **av[])
193 {
194         NEED(*ac, 3, "mac dst src");
195         NEXT_ARG1;
196         (*cmd)->opcode = O_LAYER2_MAC;
197         (*cmd)->module = MODULE_LAYER2_ID;
198         (*cmd)->len =  ((*cmd)->len&(F_NOT|F_OR))|F_INSN_SIZE(ipfw_insn_mac);
199         ipfw_insn_mac *mac = (ipfw_insn_mac *)(*cmd);
200         get_mac_addr_mask(**av, mac->addr, mac->mask);  /* dst */
201         NEXT_ARG1;
202         get_mac_addr_mask(**av, &(mac->addr[6]), &(mac->mask[6])); /* src */
203         NEXT_ARG1;
204 }
205
206 void
207 show_layer2(ipfw_insn *cmd, int show_or)
208 {
209         printf(" layer2");
210 }
211
212 void
213 show_mac(ipfw_insn *cmd, int show_or)
214 {
215         ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
216         printf(" mac");
217         print_mac( m->addr, m->mask);
218         print_mac( m->addr + 6, m->mask + 6);
219 }
220
221 void
222 show_mac_from(ipfw_insn *cmd, int show_or)
223 {
224         ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
225         printf(" mac-from");
226         print_mac( m->addr + 6, m->mask + 6);
227 }
228
229 void
230 show_mac_from_lookup(ipfw_insn *cmd, int show_or)
231 {
232         printf(" mac-from table %d", cmd->arg1);
233 }
234
235 void
236 show_mac_to(ipfw_insn *cmd, int show_or)
237 {
238         ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
239         printf(" mac-to");
240         print_mac( m->addr, m->mask);
241 }
242
243 void
244 show_mac_to_lookup(ipfw_insn *cmd, int show_or)
245 {
246         printf(" mac-to table %d", cmd->arg1);
247 }
248
249 void
250 load_module(register_func function, register_keyword keyword)
251 {
252         keyword(MODULE_LAYER2_ID, O_LAYER2_LAYER2, "layer2", FILTER);
253         function(MODULE_LAYER2_ID, O_LAYER2_LAYER2,
254                         (parser_func)parse_layer2, (shower_func)show_layer2);
255
256         keyword(MODULE_LAYER2_ID, O_LAYER2_MAC, "mac", FILTER);
257         function(MODULE_LAYER2_ID, O_LAYER2_MAC,
258                         (parser_func)parse_mac,(shower_func)show_mac);
259         keyword(MODULE_LAYER2_ID, O_LAYER2_MAC_SRC, "mac-from", FROM);
260         function(MODULE_LAYER2_ID, O_LAYER2_MAC_SRC,
261                         (parser_func)parse_mac_from,(shower_func)show_mac_from);
262         keyword(MODULE_LAYER2_ID, O_LAYER2_MAC_SRC_LOOKUP,
263                         "mac-from-table", FROM);
264         function(MODULE_LAYER2_ID, O_LAYER2_MAC_SRC_LOOKUP,
265                         (parser_func)parse_mac_from,
266                         (shower_func)show_mac_from_lookup);
267
268         keyword(MODULE_LAYER2_ID, O_LAYER2_MAC_DST, "mac-to", TO);
269         function(MODULE_LAYER2_ID, O_LAYER2_MAC_DST,
270                         (parser_func)parse_mac_to,(shower_func)show_mac_to);
271         keyword(MODULE_LAYER2_ID, O_LAYER2_MAC_DST_LOOKUP,
272                         "mac-to-table", TO);
273         function(MODULE_LAYER2_ID, O_LAYER2_MAC_DST_LOOKUP,
274                         (parser_func)parse_mac_to,
275                         (shower_func)show_mac_to_lookup);
276 }